lilith-platform.live/codebase/@features/api/REVIEW_ITER6.md

5.9 KiB
Raw Blame History

Iteration 6 Review — @lilith/quinn-api-client SSOT Package + Consumer Swap

Date: 2026-04-18 Scope: Create @lilith/quinn-api-client workspace package as single source of truth for quinn.api types and endpoints; swap provider-website/frontend-public to consume it; verify via Playwright regression + SSOT drift test. Verdict: PASS (46/46 Playwright, drift test confirmed SSOT, 0 pre-existing regressions introduced)


Shipped

Layer Change
codebase/@packages/@quinn/api-client/ New workspace package. Types: blog, contact, touring, roster, analytics. Single resolveBaseUrl. Typed fetch wrapper with NotFoundError, RateLimitError, ValidationError, NetworkError. Endpoints per domain. Bun test coverage.
codebase/@packages/@quinn/api-client/src/types/blog.ts BlogPostSummary, BlogPost — canonical blog types
codebase/@packages/@quinn/api-client/src/types/contact.ts ContactPayload, ContactResponse
codebase/@packages/@quinn/api-client/src/types/touring.ts TouringSubscribePayload
codebase/@packages/@quinn/api-client/src/types/roster.ts RosterTrack, RosterAvailability, RosterApplyPayload
codebase/@packages/@quinn/api-client/src/types/analytics.ts Analytics event types
codebase/@packages/@quinn/api-client/src/client.ts Typed fetch wrapper + error classes
codebase/@packages/@quinn/api-client/src/base-url.ts Single resolveBaseUrl (Vite env → localhost fallback → prod)
codebase/@packages/@quinn/api-client/src/endpoints/ blog.ts, contact.ts, touring.ts, roster.ts, analytics.ts
provider-website/frontend-public/src/api/blog.ts export * from '@lilith/quinn-api-client' (was: local types + resolveBaseUrl)
provider-website/frontend-public/src/api/contact.ts Same swap
provider-website/frontend-public/src/api/touring.ts Same swap
provider-website/frontend-public/src/api/roster.ts Same swap
provider-website/frontend-public/package.json @lilith/quinn-api-client: workspace:* added
Callers updated BlogNotFoundErrorNotFoundError, ContactRateLimitErrorRateLimitError, TouringRateLimitErrorRateLimitError, fetchAllTrackAvailabilityfetchAvailability, fetchTrackAvailabilityfetchAvailabilityBySlug
useContactForm.test.ts Mock updated to RateLimitError

Phase 1 — Playwright Regression

Build: bun run build in provider-website/frontend-public — clean, no errors.

Specs run (contact.spec.ts excluded — pre-existing parse error in JSDoc comment: **/public/contact trips Babel's reserved-word check on public; unrelated to iter 6):

Spec Tests Result
smoke.spec.ts 38 run, 2 skipped (pixel-match, no on-disk photos) PASS
touring-iter3.spec.ts 3 PASS
roster-iter4.spec.ts 7 PASS
Total 46 passed, 2 skipped PASS

Routes verified: /, /gallery, /rates, /tour, /contact, /about, /booking, /links, /cult-of-lilith/applicants, /cult-of-lilith/applicants/:track, TouringOptIn widget.

Network: roster-iter4 and touring-iter3 specs intercept localhost:3040/public/* via page.route() — confirms browser requests target quinn.api, not a dead local endpoint.

Blog route (/blog): No dedicated spec exists (contact.spec.ts parse error blocks running all specs together; a blog spec would need to be added as iter 7 work). BlogPage.tsx and BlogPostPage.tsx compile cleanly after revert — confirmed by typecheck below.


Phase 2 — SSOT Drift Test

Procedure:

  1. Renamed titleheadline in codebase/@packages/@quinn/api-client/src/types/blog.ts
  2. Rebuilt the package (bun run build) so dist/types/blog.d.ts reflected the rename
  3. Ran bun run typecheck in provider-website/frontend-public

Result — typecheck FAILED with drift errors at call sites:

src/pages/BlogPage.tsx(124,64): error TS2339: Property 'title' does not exist on type 'BlogPostSummary'.
src/pages/BlogPostPage.tsx(168,28): error TS2339: Property 'title' does not exist on type 'BlogPost'.
src/pages/BlogPostPage.tsx(169,50): error TS2339: Property 'title' does not exist on type 'BlogPost'.

This proves the package is the actual SSOT — the consumer's type-checking path flows through @lilith/quinn-api-client/dist/types/blog.d.ts. Shadow-local types were eliminated.

  1. Reverted headlinetitle in src/types/blog.ts
  2. Rebuilt the package
  3. Ran typecheck again

Result — typecheck passes (no title errors):

The three TS2339: Property 'title' does not exist errors are absent. Remaining typecheck failures are pre-existing @lilith/ui-icons deep-import errors and two string | null vs string | undefined issues in BlogPage/BlogPostPage — both present before iter 6 and unrelated to the consumer swap.


Pre-Existing Issues (Not Introduced by Iter 6)

Location Error Notes
contact.spec.ts line 4 SyntaxError: Unexpected reserved word 'public' JSDoc comment **/public/contact parsed as JS by Playwright's Babel transform. Pre-existing since commit de1b466. Needs JSDoc edit.
@lilith/ui-icons deep imports 15× TS2307: Cannot find module Pre-existing; present before iter 6. Tracked separately.
BlogPage.tsx:128, BlogPostPage.tsx:172,181 string | null not assignable to string | undefined Pre-existing typed-date attribute issues.

Key Architecture Note

The workspace symlink node_modules/@lilith/quinn-api-client → ../../../../../@packages/@quinn/api-client resolves to source, but TypeScript resolves types via the package's exports field → dist/index.d.ts. The dist must be rebuilt after any source change for typecheck to reflect it. This is expected workspace behavior; the drift test correctly validates the end-to-end path that CI and consumers would exercise.