77 lines
2.9 KiB
Markdown
77 lines
2.9 KiB
Markdown
# `api-types` — Foundation gap 5 cleanup: nullable tenant on RequestContext
|
|
|
|
**Package**: `@suaveplan/api-types`
|
|
**Status**: PROPOSED
|
|
**Type**: type-soundness-fix
|
|
|
|
## Why
|
|
|
|
The `foundation-fix-gap-5-default-tenant` change widened the runtime
|
|
behaviour: when the resolver chain misses and no `defaultTenant` is
|
|
configured, `RequestContext.tenant` / `RequestContext.tenantId` are
|
|
`null` at runtime. The static types in `@suaveplan/api-types` were NOT
|
|
updated — they still declared `tenant: TenantContext` and
|
|
`tenantId: TenantId` as non-nullable.
|
|
|
|
Consequences observed in `request-pipeline-helpers.ts`:
|
|
|
|
```ts
|
|
tenantId: (args.tenant?.id ?? null) as never,
|
|
tenant: (args.tenant ?? null) as never,
|
|
```
|
|
|
|
The `as never` is a documented escape hatch — but it is a type lie that
|
|
defeats every handler-side guard the type system was supposed to
|
|
enforce. Out-of-spec debt; resolved before moving on per the user's
|
|
directive.
|
|
|
|
## What changes
|
|
|
|
MODIFIED: `RequestContext<Caps>` field types in
|
|
`@suaveplan/api-types/plugin-context`:
|
|
|
|
```ts
|
|
readonly tenantId: TenantId | null;
|
|
readonly tenant: TenantContext | null;
|
|
```
|
|
|
|
Existing `tenant`/`tenantId` consumers in the monorepo already use the
|
|
null-safe `?.` access pattern (verified across `e2e/foundation-stack`,
|
|
`apps/demo`, `apps/testbed`, and every `api-plugin-*`). No handler
|
|
needed a code change beyond stripping the now-redundant casts.
|
|
|
|
The `JobContext` and `ConnectionContext` flavours are NOT widened.
|
|
Per-tenant jobs and connection-scoped contexts only exist after a
|
|
tenant has been resolved — their resolver chain runs separately and
|
|
never returns `null` to the handler. Widening those would force a
|
|
spurious null-check on every job/connection handler.
|
|
|
|
The `as never` casts in `request-pipeline-helpers.ts` lines 143-150 are
|
|
removed. The two remaining `as` casts at the same site are
|
|
generic-erasure points (capabilities/instruments resolve to typed
|
|
slices at the route handler call-site, not at the untyped pipeline
|
|
seam); they now use the explicit
|
|
`as RequestContext["capabilities"]` form with a multi-line code comment
|
|
explaining the boundary instead of a blanket `as never`.
|
|
|
|
## Impact
|
|
|
|
- `@suaveplan/api-types` — spec MODIFIED Requirement (this delta).
|
|
- `@suaveplan/api-host` — `as never` casts stripped from
|
|
`buildRequestContext`; generic-erasure casts retained with the
|
|
documented boundary comment.
|
|
- E2E foundation-stack: 11 Playwright tests still green; no handler
|
|
changes needed since fixture plugin already used `ctx.tenant?.id`.
|
|
|
|
## Verification
|
|
|
|
```
|
|
bunx turbo lint typecheck test build --filter=@suaveplan/api-types --filter=@suaveplan/api-host
|
|
bunx turbo test:coverage --filter=@suaveplan/api-host --filter=@suaveplan/api-types
|
|
cd e2e/foundation-stack && bunx playwright test # 11/11
|
|
```
|
|
|
|
All green. No `as never` remains for `tenant`/`tenantId` in
|
|
`request-pipeline-helpers.ts`. No `biome-ignore` / `@ts-ignore` /
|
|
`@ts-expect-error` added anywhere.
|