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()returnsundefinedso the next resolver in the chain runs
Scenario: null principal advances the chain by default
- WHEN
principal === nullANDrequirePrincipal: false - THEN
resolve()returnsundefined
Scenario: requirePrincipal: true throws when principal is null
- WHEN
principal === nullANDrequirePrincipal: true - THEN
resolve()throwsTENANT_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 realMessageChannel - 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
SuaveplanErrorwith codeTENANT_JWT_INVALID_OPTIONSis thrown synchronously
Verified by: src/options/jwt-tenant-resolver-options.test.ts::"throws on invalid mode value"