Skip to content

feat(api-contract): add @chatbotx.io/api-contract package and implement public routers from it - #1069

Open
realcodesiman wants to merge 5 commits into
mainfrom
feat/api-contract-package
Open

feat(api-contract): add @chatbotx.io/api-contract package and implement public routers from it#1069
realcodesiman wants to merge 5 commits into
mainfrom
feat/api-contract-package

Conversation

@realcodesiman

@realcodesiman realcodesiman commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Introduces packages/api-contract (46 files), a stable, versioned contract package (implement(contract) pattern) for the public v1 workspace-token API surface, replacing per-feature ad-hoc .route({...}) builder calls. A contract carries every field the OpenAPI generator needs (route, input, output, errors) independent of the handler implementation, so MCP tool names (derived from operationId) and hard-coded Postman/CLI paths stop drifting silently with unrelated refactors.
  • Rewires 18 of the public router's workspace-token API modules onto the new contracts.
  • Left three untouched: tags/api/token-auth.ts still calls tagService methods (create/update/deleteMany) that don't exist until PR-5's repository migration; contacts and webhooks (generic, not external-webhooks) aren't part of this contract migration at all yet — no contract module exists for either.

⚠️ Branch dependency

Stacked on #1065 (PR-1), #1066 (PR-2), and #1068 (PR-3) — merge those first for this diff to shrink to its own ~3k lines.

Bugs found and fixed during review (before this PR was opened)

  • DB-import leak: inbox/resource.ts and whatsapp-message-template/resource.ts imported schema-only symbols from the bare @chatbotx.io/business package root, which transitively pulls in service modules that instantiate a Postgres Pool at module scope — undermining the whole point of a pure, testable-without-a-database contract package. Added dedicated schema-only subpath exports (business/inbox/schema, business/integration-whatsapp/schema) instead. Verified by tracing the full transitive import chain and by importing the package's entrypoint under a stripped env -i environment with zero errors.
  • Silent validation regression: custom-field and bot-field create/update inputs used a bare z.string() for the name field instead of the shared zodFieldName() validator, dropping the guard against naming a field with the reserved bot_field: prefix (which would collide with the reference-token format the remap engine relies on). This bug pre-existed on the source migration branch too — not something introduced by extraction. Both now import zodFieldName() from @chatbotx.io/flow-config, matching the existing builder-side schemas exactly. Verified empirically: the schema now rejects a bot_field:-prefixed name and accepts a normal one.

Notes for reviewers

  • apps/builder/__tests__/public-spec-snapshot.test.ts covers only the tags contract (/v1/tags) — its own docstring is scoped correctly, but be aware it is not yet a full-surface OpenAPI drift guard for the other ~19 contract modules this PR adds/rewires. Worth a follow-up to extend it to the merged public spec.
  • Also fixes a stale packages/public-apis mention in AGENTS.md's package table and deletes the now-obsolete public-api-tooling skill doc.

Test plan

  • `pnpm lint` — clean
  • `pnpm --filter builder check-types`, `pnpm --filter @chatbotx.io/api-contract check-types`, `pnpm --filter @chatbotx.io/business check-types` — all clean
  • `pnpm --filter builder test` — 2029/2029 passing
  • `pnpm check:circular` — no new circular deps (69 pre-existing, unchanged, even after adding the `flow-config` dependency)
  • `invariant-guard` agent review — found the two bugs above (both fixed and re-verified)
  • `CI=true pnpm install --no-frozen-lockfile` run after adding the new workspace package and its `flow-config` dependency

…s to api/private.ts

Pure mechanical rename across ~61 feature directories, extracted fresh off
main instead of cherry-picked from the migration branch since many of those
paths also carry unrelated logic changes there. Excludes ads-campaign,
which no longer exists on the source branch.
Adds a tokenHash column and moves workspace bearer-token lookup to hash-first
with a plaintext fallback for the deploy-to-migration gap. The fallback and
the token column removal are deliberately deferred to a follow-up once the
legacy-plaintext warning log has been silent for a release.

Also generalizes channel-api-rate-limit.ts into api-rate-limit.ts (adds a
scope param) and pulls authorize-workspace-access.ts forward as shared
owner-quota/trial-gate infrastructure, since the token-auth middleware needs
both.
…ernal router

Consolidates the three oRPC auth-stack exports onto one instanceof-based error
mapper (was three duplicated error.name checks), adding an ActionValidationError
-> 422 mapping. Wires the owner-quota/trial gate from authorize-workspace-access.ts
into workspaceAuthorizedMidddleware so an oRPC mutation can't bypass the gate a
server action already enforces for the same operation (invariant #14: read/delete
stays open on an expired workspace).

Splits the OpenAPI REST surface so /api/[[...rest]] only ever serves publicRouter
(workspace-token / channel-token authed procedures) instead of the full
session-authed router. A procedure missing from publicRouter now 404s instead of
silently answering to a session cookie. A dev-only /api-internal mirrors the old
full-router behavior for local Scalar debugging and 404s in production; the
builder UI is unaffected since it calls the untouched /rpc route, not /api.
…nt public routers from it

Introduces a stable, versioned contract package (implement(contract) pattern)
for the public v1 workspace-token API surface, replacing per-feature ad-hoc
.route({...}) builder calls. A contract carries every field the OpenAPI
generator needs (route, input, output, errors) independent of the handler
implementation, so MCP tool names (derived from operationId) and hard-coded
Postman/CLI paths stop drifting silently with unrelated refactors.

Rewires 18 of the public router's workspace-token API modules onto the new
contracts. Left three untouched: tags/api/token-auth.ts still calls
tagService methods (create/update/deleteMany) that don't exist until a later
repository-migration PR, and contacts/webhooks aren't part of this contract
migration at all yet (no contract module exists for them).

Fixes a DB-import leak found during review: two contract resource files
imported schema-only symbols from the bare @chatbotx.io/business package
root, which transitively pulls in service modules that instantiate a
Postgres Pool at module scope -- undermining the package's whole point of
being testable without a live database. Added dedicated schema-only subpath
exports (inbox/schema, integration-whatsapp/schema) instead.

Fixes a second regression found during review: custom-field and bot-field
create/update inputs used a bare z.string() for the name field instead of
the shared zodFieldName() validator, silently dropping the guard against
naming a field with the reserved "bot_field:" prefix (which would collide
with the reference-token format used by the remap engine). Both now import
zodFieldName() from @chatbotx.io/flow-config, matching the pre-existing
builder-side schemas exactly.

Also fixes a stale packages/public-apis mention in AGENTS.md's package table
and deletes the now-obsolete public-api-tooling skill doc.
@realcodesiman
realcodesiman force-pushed the feat/api-contract-package branch from 2f6c823 to 688b31a Compare August 30, 2026 23:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant