Files
2026-05-27 23:29:53 +01:00

2.7 KiB

Capability Spec Delta: api-tenant-resolver-jwt (api-tenant-resolver-jwt)

Package: @suaveplan/api-tenant-resolver-jwt Status: PROPOSED

ADDED Requirements

Requirement: Resolve tenant from Principal

The package SHALL implement TenantResolver such that resolve(request, principal) returns principal.tenantId when present, returns undefined (chain-advance sentinel) when principal is non-null but has no tenantId, and returns undefined when principal is null (unless requirePrincipal: true).

Scenario: principal with tenantId resolves directly

  • WHEN principal = { id: "u1", tenantId: "acme", scopes: [], claims: {} }
  • THEN resolve() returns "acme"

Scenario: principal without tenantId advances the chain

  • WHEN principal = { id: "u1", scopes: [], claims: {} } (no tenantId)
  • THEN resolve() returns undefined so the next resolver in the chain runs

Scenario: null principal advances the chain by default

  • WHEN principal === null AND requirePrincipal: false
  • THEN resolve() returns undefined

Scenario: requirePrincipal: true throws when principal is null

  • WHEN principal === null AND requirePrincipal: true
  • THEN resolve() throws TENANT_JWT_PRINCIPAL_REQUIRED

Verified by: src/factory/create-jwt-tenant-resolver.test.ts::"returns principal.tenantId when present" AND src/factory/create-jwt-tenant-resolver.test.ts::"returns undefined when tenantId missing" AND src/factory/create-jwt-tenant-resolver.test.ts::"throws when requirePrincipal and principal is null"


Requirement: Direct and worker-proxy surfaces both pass conformance

Scenario: direct conforms

  • WHEN runTenantResolverConformance("jwt-direct", () => createJwtTenantResolver(...)) runs
  • THEN every invariant passes

Scenario: worker-proxy conforms

  • WHEN runTenantResolverConformance("jwt-worker-proxy", ...) runs over a real MessageChannel
  • THEN every invariant passes

Verified by: src/factory/create-jwt-tenant-resolver.spec-conformance.test.ts::"jwt-direct conforms to TenantResolver contract" AND src/proxy/create-jwt-tenant-resolver-worker-proxy.spec-conformance.test.ts::"jwt-worker-proxy conforms to TenantResolver contract"


Requirement: Options Zod-validated

Scenario: invalid options throw at construction

  • WHEN createJwtTenantResolver({ mode: "bogus" as never })
  • THEN a SuaveplanError with code TENANT_JWT_INVALID_OPTIONS is thrown synchronously

Verified by: src/options/jwt-tenant-resolver-options.test.ts::"throws on invalid mode value"