Files
suaveplan/openspec/archive/2026-05-26-api-tenant-provider-memory/proposal.md
2026-05-27 23:29:53 +01:00

58 lines
3.5 KiB
Markdown

# `@suaveplan/api-tenant-provider-memory` — In-memory TenantProvider
**Package**: `@suaveplan/api-tenant-provider-memory`
**Status**: PROPOSED
**Type**: new-package
**Stage**: 4 (Base adapters) — see umbrella `openspec/changes/api-plugin-foundation/`
## Why
Per design doc §5.4, `perTenant: true` cron jobs require the host to enumerate tenants. `tenant.provider` is the capability that exposes that enumeration. Shipping a v1 in-memory reference implementation matters for two reasons:
1. It pins the contract — every later DB-backed adapter (Stage 8 or later) implements the same `TenantProvider` interface, so retrofitting tenant enumeration into a stable contract doesn't have to happen later (§5.4 last sentence).
2. It's the default for the testing kit (`createTestHost` Stage 5) and for any deployment whose tenant list is small + static.
The provider returns an iterable/async-iterable of `TenantContext`. Designed to be both production-grade for static deployments AND a reference for the more elaborate Stage-8 DB-backed adapter.
Stage 4 invariant: ships direct AND worker-proxy surfaces; both pass `runTenantProviderConformance` (§5.2a, §13.4). (Conformance suite is owned by `api-conformance-suites` this stage.)
## What changes
Net-new package depending on `@suaveplan/api-tenant-provider` (contract), `@suaveplan/api-types`, `@suaveplan/api-worker-rpc`, `@suaveplan/error`, `zod`.
- ADDED: `createMemoryTenantProvider(opts)` — direct-surface factory returning a `TenantProvider`.
- ADDED: `createMemoryTenantProviderWorkerProxy(port, opts)` — proxy-surface factory.
- ADDED: `MemoryTenantProviderOptions` — Zod schema with `tenants: TenantContext[]` (initial set), `mutable: boolean` (default true — allow runtime `add`/`remove`/`update`).
- ADDED: Mutation API (when `mutable: true`): `add(tenant)`, `remove(tenantId)`, `update(tenantId, patch)`.
- ADDED: `list()` returns an array snapshot; `listAsync()` returns an `AsyncIterable<TenantContext>` for symmetry with future DB-backed adapter.
- ADDED: `get(tenantId)` returns `TenantContext | undefined`.
- ADDED: `TENANT_PROVIDER_MEMORY_*` error codes (`TENANT_PROVIDER_MEMORY_DUPLICATE_ID`, `TENANT_PROVIDER_MEMORY_NOT_FOUND`, `TENANT_PROVIDER_MEMORY_IMMUTABLE`, `TENANT_PROVIDER_MEMORY_INVALID_OPTIONS`).
## Out of scope
- DB-backed enumeration — Stage 8 (or later) `api-tenant-provider-postgres` / `api-tenant-provider-surrealdb`.
- Hierarchical tenants — flat model only at this stage.
- Tenant-level feature flags — `@suaveplan/feature-flags` consumes the provider, not the other way round.
## Source mapping
Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`.
| Requirement | Design doc section |
|---|---|
| `tenant.provider` ships in v1 | §5.4 |
| In-memory reference impl | §5.4 |
| Worker-proxy variant | §5.2a |
| Conformance both surfaces | §13.4 |
## Verification approach
- `src/factory/create-memory-tenant-provider.test.ts` — list, get, mutation, duplicate-id rejection, immutability when `mutable: false`.
- `src/factory/create-memory-tenant-provider.spec-conformance.test.ts` — `runTenantProviderConformance("memory-direct", ...)`.
- `src/proxy/create-memory-tenant-provider-worker-proxy.test.ts` — proxy unit tests; async iteration across MessagePort.
- `src/proxy/create-memory-tenant-provider-worker-proxy.spec-conformance.test.ts` — `runTenantProviderConformance("memory-worker-proxy", ...)`.
## Migration impact
None — additive only.