Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
<!-- markdownlint-disable MD024 -->
# 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: false` AND `add(...)` is called
- **THEN** the call throws `TENANT_PROVIDER_MEMORY_IMMUTABLE` AND 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 real `MessageChannel` AND `listAsync()` 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"`
<!-- markdownlint-enable MD024 -->