3.4 KiB
Capability Spec Delta: api-tenant-provider-memory (api-tenant-provider-memory)
Package: @suaveplan/api-tenant-provider-memory
Status: PROPOSED
ADDED Requirements
Requirement: Enumerate tenants from in-memory state
The package SHALL implement TenantProvider with list() (sync snapshot), listAsync() (async iterable), and get(tenantId). Initial state comes from options.tenants; runtime mutations are gated by options.mutable.
Scenario: list returns the initial set
- WHEN the provider is constructed with
tenants: [A, B, C] - THEN
list()returns an array of length 3 containing A, B, C in insertion order
Scenario: async iterator yields every tenant exactly once
- WHEN
for await (const t of provider.listAsync())runs against an initial set of 3 tenants - THEN exactly 3 tenants are yielded AND each ID appears exactly once
Scenario: get returns undefined for unknown tenant
- WHEN
get("unknown")is called - THEN the result is
undefined(no throw)
Verified by: src/factory/create-memory-tenant-provider.test.ts::"list returns initial set in insertion order" AND src/factory/create-memory-tenant-provider.test.ts::"listAsync yields every tenant exactly once" AND src/factory/create-memory-tenant-provider.test.ts::"get returns undefined for unknown tenant id"
Requirement: Mutation API gated by mutable flag
When options.mutable: true, the provider SHALL expose add(tenant), remove(tenantId), update(tenantId, patch). When mutable: false, those methods MUST throw TENANT_PROVIDER_MEMORY_IMMUTABLE.
Scenario: add rejects duplicate id
- WHEN
add({ id: "acme", ... })is called twice with the same id - THEN the second call throws
TENANT_PROVIDER_MEMORY_DUPLICATE_ID
Scenario: remove of unknown tenant throws
- WHEN
remove("not-there")is called - THEN the call throws
TENANT_PROVIDER_MEMORY_NOT_FOUND
Scenario: mutation blocked when immutable
- WHEN the provider is constructed with
mutable: falseANDadd(...)is called - THEN the call throws
TENANT_PROVIDER_MEMORY_IMMUTABLEAND state remains unchanged
Verified by: src/factory/create-memory-tenant-provider.test.ts::"add rejects duplicate id" AND src/factory/create-memory-tenant-provider.test.ts::"remove of unknown tenant throws" AND src/factory/create-memory-tenant-provider.test.ts::"mutation blocked when immutable"
Requirement: Direct and worker-proxy surfaces both pass conformance
The proxy surface SHALL support listAsync() across the MessagePort (chunked yield).
Scenario: direct conforms
- WHEN
runTenantProviderConformance("memory-direct", () => createMemoryTenantProvider(...))runs - THEN every invariant passes
Scenario: worker-proxy conforms with async iteration
- WHEN
runTenantProviderConformance("memory-worker-proxy", ...)runs over a realMessageChannelANDlistAsync()is iterated to completion against a 1_000-tenant fixture - THEN every invariant passes AND every tenant id is yielded exactly once on the proxy side
Verified by: src/factory/create-memory-tenant-provider.spec-conformance.test.ts::"memory-direct conforms to TenantProvider contract" AND src/proxy/create-memory-tenant-provider-worker-proxy.spec-conformance.test.ts::"memory-worker-proxy conforms with async iteration across MessagePort"