Skip to content

fix(api): publish the contract and correct the 404 declaration (#113, #112) - #117

Merged
maehr merged 3 commits into
textrefs:stagingfrom
maehr:fix/api-contract-publication
Aug 31, 2026
Merged

maehr merged 3 commits into
textrefs:stagingfrom
maehr:fix/api-contract-publication

Conversation

@maehr

@maehr maehr commented Aug 31, 2026

Copy link
Copy Markdown
Member

Two findings from the API audit in #111. Both edit api/openapi.yaml, so they land together. Review commit by commit.

fix(api): declare the 404 response as text/html (#113, finding G3)

Every record path declared 404 as application/json against the Error schema. The site is static, so the host cannot build a per-path error body. It serves the site's HTML 404 page instead. The contract promised a body that the site never sends.

  • The shared NotFound response now declares text/html. All 12 record paths reach it through $ref, so one edit covers them.
  • The Error schema stays, with a comment that says why: it describes the error body of the dynamic resolver that info.description already announces. It is the only unreferenced schema.

Evidence: GitHub Pages answers a missing path with content-type: text/html; charset=utf-8.

fix(api): publish the OpenAPI contract at /api/openapi.yaml (#112, finding G1)

astro.config.mjs:55 advertised the contract file. The URL returned 404, because starlight-openapi renders HTML at /api/ and copies nothing.

  • src/pages/api/openapi.yaml.ts serves the file. ?raw inlines the source at build time, so the body cannot drift from a copy in public/.
  • The route does not collide with the /api/ index.
  • info.description records the served media type, in the same paragraph that records the .jsonl case. GitHub Pages sends text/yaml for a .yaml file — confirmed against two live Pages deployments (prometheus-community.github.io, grafana.github.io).

Verification

  • npm run verify:fast passes.
  • cmp api/openapi.yaml dist/api/openapi.yaml exits 0: the served body is byte-identical.

After deploy, confirm both with one request each:

curl -sI https://textrefs.org/api/openapi.yaml               # 200, text/yaml
curl -sI https://textrefs.org/id/work/does-not-exist.json    # 404, text/html

Closes #113
Closes #112

🤖 Generated with Claude Code

https://claude.ai/code/session_01LTNUyMDUnkyBLHWac3qLjY

maehr and others added 2 commits August 31, 2026 15:47
The site is static. The host cannot build a per-path JSON error body, so
every miss returns the site's own 404 page. The shared `NotFound` response
declared `application/json` against the `Error` schema, which no request
ever receives. All 12 record paths reach that response through `$ref`, so
one edit corrects all of them.

Keep the `Error` schema and mark it reserved for the dynamic resolver that
`info.description` announces. It is the only unreferenced schema, and the
comment says why it stays.

Closes textrefs#113

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LTNUyMDUnkyBLHWac3qLjY
The site description advertised the contract file, but only the rendered
HTML at `/api/` existed. `starlight-openapi` reads `api/openapi.yaml` at
build time and copies nothing, so the advertised URL returned 404. A client
could read the docs, but could not generate code, run contract tests, or
import the API into a tool.

Serve the file from a static endpoint. `?raw` inlines the source at build
time, so the served body stays byte-identical and cannot drift from a copy.
GitHub Pages sends `text/yaml` for the `.yaml` extension; the contract now
records that, as it already records the `.jsonl` case.

Closes textrefs#112

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LTNUyMDUnkyBLHWac3qLjY
Copilot AI lite review requested due to automatic review settings August 31, 2026 13:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

`RegistryObject` and `Error` are the only schemas that no response uses.
`Error` already carries its reason. This adds the second one: the union
names what every dump line and every `@graph` member is, so a client can
validate a record before it knows the record's type.

Part of textrefs#113

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LTNUyMDUnkyBLHWac3qLjY
@maehr

maehr commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

Review complete: no blocking findings.\n\nThe shared HTML 404 response matches the static-host behavior, the raw-import endpoint avoids a second contract copy, and the generated dist/api/openapi.yaml is byte-identical to api/openapi.yaml in my local build. npm run verify:fast passes on the checked-out head, and the full GitHub checks are green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants