Skip to content

refactor(mapgen-studio): centralize ORPCError data cast, drop dead success fallbacks, and narrow router to contract-derived type#1615

Merged
mateicanavra merged 1 commit into
mainfrom
design/rigor
Jun 12, 2026
Merged

refactor(mapgen-studio): centralize ORPCError data cast, drop dead success fallbacks, and narrow router to contract-derived type#1615
mateicanavra merged 1 commit into
mainfrom
design/rigor

Conversation

@mateicanavra

Copy link
Copy Markdown
Owner

This PR centralizes and tightens the type-precision around the oRPC failure-translation seam in Mapgen Studio, with no behavior or wire-format changes.

Shared readErrorData<T>() accessor

A new readErrorData<T>() function is added to src/lib/orpc.ts that narrows ORPCError.data (typed unknown because the contract declares no errorMap) to Partial<T> | undefined. Both features/civ7Setup/api.ts and features/runInGame/api.ts previously duplicated the same inline (err.data ?? undefined) as { … } cast to recover side fields (observedAt, details) from thrown errors. They now use the shared accessor instead, while keeping their existing per-field runtime guards unchanged, so produced failure envelopes are byte-identical.

Dead success-path fallbacks removed

fetchCiv7SetupConfig and fetchCiv7SavedSetupConfigs applied ?? new Date().toISOString() and ?? "" fallbacks to observedAt and directory on their success returns. Since the civ7.setupConfig and civ7.savedConfigs contract output schemas declare these fields as required, the fallbacks could never fire and falsely implied the fields might be absent. They are removed. The error-path observedAt, which legitimately may be absent, continues to flow through readErrorData and its runtime guard.

Router type narrowed from AnyRouter to contract-derived Router<StudioContract, …>

createStudioRouter was annotated : AnyRouter, discarding the StudioContract pinning the effect-orpc router carries. It is now annotated as Router<StudioContract, Record<never, never>> (the initial context is fully provided by the injected ManagedRuntime). Inferring the raw EnhancedRouter<…> return type was rejected because it references effect-orpc internals and would trip TS2742 in the emitted declarations. StudioRouter is updated to ReturnType<typeof createStudioRouter>, and RPCHandler continues to accept it since AnyRouter is its lower bound.

mateicanavra commented Jun 12, 2026

Copy link
Copy Markdown
Owner Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

Copy link
Copy Markdown

Railway preview (MapGen Studio): not provisioned for this PR.

Policy (Graphite stacks): previews are created only for the top-of-stack PR by default.

  • To force a preview for this PR: add label railway-preview
  • To suppress a preview: add label no-railway-preview

Debug: {"isClosed":false,"isFork":false,"suppress":false,"force":false,"isTopOfStack":false,"hasToken":true,"draft":false}

This was referenced Jun 12, 2026

mateicanavra commented Jun 12, 2026

Copy link
Copy Markdown
Owner Author

Merge activity

  • Jun 12, 8:50 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jun 12, 9:13 PM UTC: Graphite rebased this pull request as part of a merge.
  • Jun 12, 9:13 PM UTC: @mateicanavra merged this pull request with Graphite.

@mateicanavra
mateicanavra changed the base branch from design/data-model to graphite-base/1615 June 12, 2026 21:10
@mateicanavra
mateicanavra changed the base branch from graphite-base/1615 to main June 12, 2026 21:11
Pure rigor + cleanup, no behavior change (mapgen-studio-rigor):

- Add one shared typed `readErrorData<T>()` accessor in src/lib/orpc.ts that
  narrows the thrown `ORPCError.data` (typed `unknown`, no contract errorMap) to
  `Partial<T> | undefined`, and use it in features/civ7Setup/api.ts and
  features/runInGame/api.ts in place of the scattered inline
  `(err.data ?? undefined) as { … }` casts. Per-field runtime guards unchanged,
  so the produced failure envelopes are byte-identical.
- Drop the dead `?? new Date().toISOString()` (observedAt) and `?? ""` (directory)
  fallbacks on the civ7Setup success returns; civ7.setupConfig / civ7.savedConfigs
  declare those fields REQUIRED on their success output. The error-path observedAt
  (which may legitimately be absent) keeps its guard.
- Narrow createStudioRouter / StudioRouter from `AnyRouter` to the contract-derived
  `Router<StudioContract, Record<never, never>>` (initial context fully provided by
  the injected ManagedRuntime). Annotated, not inferred, so the tsup DTS emit stays
  portable (no TS2742); RPCHandler still accepts it.
- Intentional "why" comments on the new accessor and the router narrowing.

Verify: bun run check (mapgen-studio + studio-server tsc clean); bun run build
(vite + worker-bundle + studio-server DTS) ; bun run test (138 green); dev server
renders with no console errors; openspec validate mapgen-studio-rigor --strict.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mateicanavra
mateicanavra merged commit a1631a2 into main Jun 12, 2026
1 check passed
This was referenced Jun 12, 2026
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