Skip to content

feat(api-types): @codespar/api-types — shared Zod contract for api.codespar.dev - #12

Merged
fabianocruz merged 1 commit into
mainfrom
feat/api-types-package
Apr 24, 2026
Merged

fabianocruz merged 1 commit into
mainfrom
feat/api-types-package

Conversation

@fabianocruz

Copy link
Copy Markdown
Member

Summary

  • Adds a new workspace package @codespar/api-types (v0.1.0) — Zod schemas + inferred TS types for every /v1/* REST shape the managed-tier API exposes today (api-keys, projects, connections, servers, sessions).
  • Single source of truth for both codespar-enterprise (backend) and codespar-web (dashboard) to import, replacing today's duplicated hand-rolled interfaces on both sides.
  • Drop-in: zero existing package changes in this PR. Adoption lands in two follow-ups (enterprise routes + web Server Actions).

Why

Two regressions in the last 48h traced back to type duplication between web and backend:

  • web #155POST /v1/api-keys response omits revoked_at; web type assumed present; undefined !== null rendered a freshly-created key as Revoked.
  • web #156GET /v1/api-keys returns { keys }; web parsed data.api_keys; list came up empty on refresh.

Both are regression-pinned in the test suite (see src/index.test.ts). Parsing responses at the fetch boundary with these schemas would have caught each one at runtime with a useful ZodError instead of silently rendering a broken UI.

Coverage

Module Shapes
api-keys CreateApiKeyRequest, CreatedApiKey, ApiKeyRow, ListApiKeysResponse
projects CreateProjectRequest, UpdateProjectRequest, ProjectRow, ListProjectsResponse
connections CreateConnectionRequest, ConnectionRow, ListConnectionsResponse, RotateWebhookSecretRequest/Response
servers ServerRow, ListServersResponse, ServerAuthSchemaResponse
sessions CreateSessionRequest, SessionRow, SessionDetail, ListSessionsResponse, ToolCallRow, ListToolCallsResponse, ExecuteToolRequest/Response

Design choices

  • Zod + z.infer, not hand-written types. Compile-time catches the easy drift; runtime .parse() at the fetch boundary catches the rest. Same schema serves both.
  • TimestampSchema = z.string().datetime({ offset: true }). Fastify auto-serializes Date to ISO-8601 — the wire always carries strings, never JS Date objects. Codified once so no route has to remember.
  • Separate package, not folded into @codespar/types. @codespar/types is the session runtime contract used by the public SDK. REST admin shapes (ApiKeyRow, etc.) are a different audience — dashboards and internal tools, not agent authors. Keeping them apart avoids leaking internal-facing types into the SDK's public surface.

Test plan

  • npm run build -w @codespar/api-types — clean tsc
  • npm run test -w @codespar/api-types — 14/14 pass
  • npm run build at monorepo root — 17/17 packages build clean
  • npm test at monorepo root — 33/33 pass
  • Publish @codespar/api-types@0.1.0 to npm after merge
  • Follow-up PR in codespar-enterprise — adopt in routes, validate responses
  • Follow-up PR in codespar-web — delete duplicated types, import from @codespar/api-types

🤖 Generated with Claude Code

…pi.codespar.dev

Single source of truth for every /v1/* REST shape (api-keys, projects,
connections, servers, sessions). Ships Zod schemas + inferred TS types so
both the enterprise backend and the web dashboard can import the same
contract instead of hand-rolling mirror interfaces.

Two bugs in codespar-web last week traced back to that duplication:
- #155: POST /v1/api-keys omitted revoked_at; web assumed present,
  undefined !== null rendered a freshly-created key as "Revoked"
- #156: GET /v1/api-keys returns { keys }; web parsed data.api_keys,
  list came up empty

Both regressions are captured in the test suite so the schemas can't
drift back into the broken shapes. Next PRs adopt these types in
codespar-enterprise routes + codespar-web Server Actions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@fabianocruz
fabianocruz merged commit 2c477db into main Apr 24, 2026
1 check passed
fabianocruz added a commit that referenced this pull request Apr 24, 2026
## Summary

- Follow-up to #12: the initial `ConnectionRowSchema` claimed `org_id`
and `project_id`, but the backend's `serializeConnection()` strips both
before sending. Any runtime `.parse()` of a real `/v1/connections`
response would have failed.
- Drops both fields + updates the test.

## Why it matters

Discovered while adopting the schema in codespar-enterprise#41 — the fix
was written but landed on the `feat/api-types-package` branch *after*
#12 was squash-merged. This PR brings main to the correct shape before
publishing `@codespar/api-types@0.1.0` to npm.

## Test plan

- [x] `npm run build -w @codespar/api-types` — clean
- [x] `npm run test -w @codespar/api-types` — 14/14 pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant