# `memory` ## Purpose Ships the default `MemoryTenantStore` — a synchronous in-memory implementation of the `TenantStore` interface suitable for tests, ephemeral demos, and very small ops deployments where a database is overkill. The store enforces slug uniqueness, performs soft-deletes (by flipping `status` to `"deleted"` rather than evicting the row), and assigns monotonic ids so test snapshots stay stable. ## Features - Slug uniqueness enforced on `create` and `update`; collisions raise `TenantPluginError(TENANT_SLUG_TAKEN)` (HTTP 409). - Missing-id updates raise `TenantPluginError(TENANT_NOT_FOUND)` (HTTP 404). - `delete` is soft (sets `status: "deleted"`) and idempotent — calling twice or against a missing id is a no-op. - `listAll` supports optional `status` filter plus `limit` / `offset` pagination. - `clear()` and `size` test helpers for assertion convenience. ## Usage: Basic Example ```ts import { MemoryTenantStore } from "@suaveplan/api-plugin-tenant"; const store = new MemoryTenantStore(); const tenant = await store.create({ name: "Acme", slug: "acme" }); console.log(tenant.id); // "tenant_1" console.log(await store.getBySlug("acme")); ``` ## Usage: Advanced Example ```ts import { MemoryTenantStore } from "@suaveplan/api-plugin-tenant"; const store = new MemoryTenantStore(); await store.create({ name: "Acme", slug: "acme", domains: ["acme.example.com"], enabledPlugins: ["billing", "vault"], settings: { features: { betaUI: true } }, }); await store.create({ name: "Bravo", slug: "bravo", status: "pending" }); const active = await store.listAll({ status: "active", limit: 50 }); const byDomain = await store.getByDomain("acme.example.com"); await store.delete(byDomain!.id); const after = await store.getById(byDomain!.id); console.assert(after?.status === "deleted"); ``` ## API Reference - `class MemoryTenantStore implements TenantStore` — constructor takes no arguments. - `getById(id)` / `getBySlug(slug)` / `getByDomain(domain)` — `Promise`. - `listAll(query?)` — `Promise`; optional filter on `status`, `limit`, `offset`. - `create(input)` — `Promise`; throws `TENANT_SLUG_TAKEN` on duplicate slug. - `update(id, patch)` — `Promise`; throws `TENANT_NOT_FOUND` for unknown ids or `TENANT_SLUG_TAKEN` if the patched slug collides. - `delete(id)` — `Promise`; soft + idempotent. - `size` — `number` of stored entries (deleted included). - `clear()` — `void`; drop everything and reset the id sequence. ## Implementation Notes The id sequence is monotonic and starts at 1 so test fixtures stay deterministic across runs (no `crypto.randomUUID` calls). Backing storage is a single `Map` so all operations are O(n) for `listAll` and `getBySlug` / `getByDomain` — perfectly fine for the test scale this store targets. Operators running thousands of tenants should swap in the SurrealDB or Postgres implementations from the companion packages. The store does NOT broadcast change events; the plugin emits its own OTel counter via `tenantService` instead.