chore(canary-phase14): Frontend API client complete
Phase 14 deliverables (audited PASS):
- Zod 4 schemas mirror backend handler/DTO byte-shapes (verified
against backend/internal/token/{handler,dto,types}.go,
backend/internal/event/{dto,entity}.go, and
backend/internal/middleware/turnstile.go — not just the spec).
- api/types/token.ts: tokenResponseSchema, manageTokenViewSchema,
artifactSchema (z.discriminatedUnion on kind), typeDescriptorSchema,
createTokenRequestSchema with superRefine for telegram/webhook
required_if semantics, per-type metadata helpers.
- api/types/event.ts: eventResponseSchema (with GeoView, NotifyStatus
enum, nullable user_agent/referer/notified_at) and
manageResponseSchema covering cursor pagination.
- api/types/error.ts: apiErrorEnvelopeSchema + successEnvelope<T>
factory; single source of truth for envelope shape.
- core/api/errors.ts refactored: ApiError carries canary error codes
(VALIDATION_ERROR, TURNSTILE_FAILED, BAD_CURSOR, BAD_PARAM, ...)
and a Record<string,string> fields map. No more FastAPI-style
parsing. Module augmentation flows ApiError as defaultError to all
TanStack hooks. Obsolete template api.config.ts removed.
- api/client.ts: axios instance over VITE_API_URL (fallback /api),
request interceptor adds CF-Turnstile-Response header from
pluggable provider when present, response interceptor routes axios
errors through Zod-backed transformAxiosError -> ApiError.
apiGet/apiPost/apiDelete typed via ZodType<T>; PARSE_ERROR carries
the failing field path + message for debuggability.
- 4 hooks: useTokenTypes (static, no polling), useCreateToken
(invalidates manage on success), useManageToken (URI-encoded id,
splayed cursor/limit query key, frequent polling), useDeleteToken
(removeQueries on success).
Implicit contract documented here for the next AI: the
cf_turnstile_response body field is intentionally optional in
createTokenRequestSchema because backend middleware/turnstile.go
reads the CF-Turnstile-Response HEADER first (set via the request
interceptor). Operators wiring the Turnstile widget should call
setTurnstileTokenProvider(() => widgetToken) on app init; the form
need not carry the token in the body.
Audited: superpowers:code-reviewer + general-purpose spec adherence
— PASS on first dispatch. Audit findings S-1/S-3/S-4/S-7 fixed
pre-rollup in 7c0a504d. I-1 (config_schema gap in
backend TypeDescriptor — spec stale vs code) and I-2 (turnstile
body-field optional) flagged for future phases; not Phase 14 issues.
OPERATOR PAUSE BOUNDARY (spec §16.3, plan line 2314). No UI/
component/page/styling code in this phase. Phase 15 resumes with
infrastructure (nginx + telemetry).
This commit is contained in:
parent
7c0a504d0b
commit
37cdf97133