@objectstack/metadata-core 6.8.0 → 6.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ var MetadataTypeSchema = _zod.z.enum([
24
24
  "flow",
25
25
  "workflow",
26
26
  "approval",
27
+ "job",
27
28
  "agent",
28
29
  "tool",
29
30
  "skill",
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/framework/framework/packages/metadata-core/dist/index.cjs","../src/types.ts","../src/repository.ts","../src/in-memory-repository.ts","../src/cache.ts","../src/layered-repository.ts"],"names":["clone"],"mappings":"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","file":"/home/runner/work/framework/framework/packages/metadata-core/dist/index.cjs","sourcesContent":[null,"// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata Repository types — see ADR-0008 §2.\n *\n * All shapes are defined as Zod schemas so the same definition serves\n * runtime validation and static typing (`z.infer<typeof X>`).\n */\n\nimport { z } from 'zod';\n\n// ─── Metadata type registry ───────────────────────────────────────────\n\n/**\n * Canonical metadata type names. Aligned with the `MetadataTypeSchema`\n * enum in `@objectstack/spec/kernel/metadata-plugin.zod.ts`. New types are\n * added here in lockstep with that file.\n */\nexport const MetadataTypeSchema = z.enum([\n 'object',\n 'field',\n 'trigger',\n 'validation',\n 'hook',\n 'view',\n 'page',\n 'dashboard',\n 'app',\n 'action',\n 'flow',\n 'workflow',\n 'approval',\n 'agent',\n 'tool',\n 'skill',\n 'report',\n 'translation',\n 'role',\n 'profile',\n 'permission',\n 'policy',\n 'api',\n 'endpoint',\n 'datasource',\n 'cube',\n 'settings',\n 'router',\n 'function',\n 'service',\n 'email_template',\n]).describe('Canonical metadata type name');\n\nexport type MetadataType = z.infer<typeof MetadataTypeSchema>;\n\n// ─── MetaRef ──────────────────────────────────────────────────────────\n\n/**\n * Fully-qualified reference to a metadata item. Identity is `(org, type, name)`.\n *\n * Per ADR-0008 v2 (2026-05) the metadata layer no longer carries `project`\n * or `branch`. Project survives only as an **artifact packaging concept**\n * (the unit a CLI/CI run compiles into `dist/objectstack.json`); it does\n * not appear in the runtime customization scope. Branching belongs to Git\n * (or your VCS of choice) and never propagated cleanly into the runtime\n * model — so it has been removed entirely.\n *\n * Higher layers may default `org='system'` for built-ins.\n *\n * `version` is optional: omit to mean \"HEAD\", supply to pin.\n */\nexport const MetaRefSchema = z.object({\n org: z.string().min(1).describe('Tenant/org identifier; \"system\" for built-ins'),\n type: MetadataTypeSchema,\n name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Snake_case machine name'),\n version: z.string().optional().describe('Optional version pin (content hash); omit for HEAD'),\n});\n\nexport type MetaRef = z.infer<typeof MetaRefSchema>;\n\n/**\n * Construct a stable string key from a MetaRef (excluding `version`,\n * which is mutable). Used as cache keys and log indexes.\n */\nexport function refKey(ref: Pick<MetaRef, 'org' | 'type' | 'name'>): string {\n return `${ref.org}/${ref.type}/${ref.name}`;\n}\n\n// ─── Item & header ────────────────────────────────────────────────────\n\n/**\n * Full metadata item as stored / returned by the Repository.\n *\n * `body` is the **canonical, Zod-normalised** spec (with defaults filled\n * in). `hash` is `sha256(canonicalize(body))`. Equal hashes imply equal\n * specs.\n */\nexport const MetadataItemSchema = z.object({\n ref: MetaRefSchema,\n body: z.record(z.string(), z.unknown()).describe('Canonical Zod-normalised spec'),\n hash: z.string().regex(/^sha256:[0-9a-f]{64}$/).describe('sha256(canonicalize(body))'),\n parentHash: z.string().nullable().describe('Hash this version was derived from; null for first version'),\n authoredBy: z.string().describe('Identity of the writer (user id, \"cli\", \"ai:claude\", …)'),\n authoredAt: z.string().describe('ISO-8601 timestamp'),\n message: z.string().optional().describe('Optional commit message'),\n seq: z.number().int().nonnegative().describe('Sequence number this write produced in the org log'),\n schemaVersion: z.string().optional().describe('Zod schema version that wrote this spec (M3 codemod hook)'),\n});\n\nexport type MetadataItem = z.infer<typeof MetadataItemSchema>;\n\n/** Lightweight header for listing — `body` omitted. */\nexport type MetadataItemHeader = Omit<MetadataItem, 'body'>;\n\n// ─── Change log event ─────────────────────────────────────────────────\n\nexport const MetadataOpSchema = z.enum(['create', 'update', 'delete', 'rename']);\nexport type MetadataOp = z.infer<typeof MetadataOpSchema>;\n\n/**\n * The single event payload broadcast by the change log. ADR-0008 §2.4.\n *\n * For `rename`, `previousName` carries the old machine name. For\n * `delete`, `hash` is null. The payload is intentionally small —\n * consumers re-fetch via the cache when they need the full body.\n */\nexport const MetadataEventSchema = z.object({\n seq: z.number().int().nonnegative(),\n op: MetadataOpSchema,\n ref: MetaRefSchema,\n hash: z.string().nullable(),\n parentHash: z.string().nullable(),\n previousName: z.string().optional().describe('Set on op=\"rename\"'),\n actor: z.string(),\n message: z.string().optional(),\n ts: z.string(),\n source: z.string().describe('Origin label: \"fs\", \"studio\", \"rest\", \"ai\", \"git-import\", …'),\n});\n\nexport type MetadataEvent = z.infer<typeof MetadataEventSchema>;\n\n// ─── Operation options ────────────────────────────────────────────────\n\nexport interface PutOptions {\n /**\n * Hash this writer believed was at HEAD. `null` means \"creating, expect\n * absence\". A mismatch throws ConflictError.\n */\n parentVersion: string | null;\n /** Identity of the writer; mirrored to MetadataEvent.actor. */\n actor: string;\n /** Optional human-readable commit message. */\n message?: string;\n /** Optional label for the change log \"source\" column. */\n source?: string;\n}\n\nexport interface PutResult {\n /** New content hash assigned to the spec. */\n version: string;\n /** Sequence number of the emitted MetadataEvent. */\n seq: number;\n /** The committed item (canonicalised). */\n item: MetadataItem;\n}\n\nexport interface DeleteOptions {\n parentVersion: string;\n actor: string;\n message?: string;\n source?: string;\n}\n\nexport interface DeleteResult {\n seq: number;\n}\n\nexport interface ListFilter {\n org?: string;\n type?: MetadataType;\n /** Substring match on `name`; case-sensitive. */\n nameContains?: string;\n /** Pagination cursor; opaque string from a previous response. */\n cursor?: string;\n /** Page size; implementations may clamp. */\n limit?: number;\n}\n\nexport interface WatchFilter {\n org?: string;\n type?: MetadataType;\n /** When omitted, match all names within the scope. */\n name?: string;\n}\n\nexport interface HistoryOptions {\n /** Lower bound (exclusive) for pagination. */\n sinceSeq?: number;\n limit?: number;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The `MetadataRepository` interface — single point of pluggability for\n * the metadata storage backend. See ADR-0008 §2.6.\n *\n * Implementations:\n *\n * - `InMemoryRepository` (this package, for tests & edge)\n * - `FileSystemRepository` (`@objectstack/metadata`)\n * - `LayeredRepository` (`@objectstack/metadata`)\n * - `PostgresRepository` (`@objectstack/metadata-postgres`, M1)\n *\n * Implementation contract — what every backend MUST guarantee:\n *\n * 1. **Atomic put.** A successful `put()` either fully applies (item\n * visible to subsequent `get` AND an event present in the log) or\n * does not apply at all. No half-states.\n * 2. **Monotonic seq per org.** `seq` is strictly increasing within\n * `org`. Different orgs have independent sequences. (Repositories\n * scoped to a single org may treat the entire repo as one log.)\n * 3. **Optimistic locking.** `put` and `delete` throw `ConflictError`\n * when `parentVersion` does not match the current HEAD.\n * 4. **Canonical hashing.** `item.hash === hashSpec(item.body)` — always.\n * 5. **Event ordering.** Subscribers to `watch()` receive events in\n * monotonically-increasing `seq` order with no gaps.\n * 6. **Resumability.** `watch(_, since)` MUST replay all events with\n * `seq > since` before delivering live events.\n * 7. **Tombstones, not holes.** `delete` produces a `delete` event;\n * `get` returns null but `history` still shows the lineage.\n */\n\nimport type {\n MetaRef,\n MetadataItem,\n MetadataItemHeader,\n MetadataEvent,\n PutOptions,\n PutResult,\n DeleteOptions,\n DeleteResult,\n ListFilter,\n WatchFilter,\n HistoryOptions,\n} from './types.js';\n\nexport interface MetadataRepository {\n /** Read HEAD or a pinned version. Returns null if absent. */\n get(ref: MetaRef): Promise<MetadataItem | null>;\n\n /**\n * Resolve a historical version by content hash (ADR-0009).\n *\n * Returns the `MetadataItem` whose canonical sha256 equals `hash`\n * for the given ref, or `null` if no such version is recorded.\n *\n * Implementations MUST search history (not just HEAD) so that\n * `executionPinned` types remain resolvable through definition\n * upgrades. For non-`executionPinned` types, implementations MAY\n * return `null` if they have GC'd the corresponding history row.\n */\n getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null>;\n\n /**\n * Write a new version. Atomic.\n * @throws ConflictError if `parentVersion` does not match HEAD.\n * @throws SchemaValidationError if `spec` fails Zod normalisation.\n */\n put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult>;\n\n /**\n * Soft-delete (tombstone). `parentVersion` is required.\n * @throws ConflictError on parent mismatch.\n */\n delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult>;\n\n /** Enumerate items matching a filter. Implementations may stream. */\n list(filter: ListFilter): AsyncIterable<MetadataItemHeader>;\n\n /** Per-item history; events in monotonic `seq` order. */\n history(ref: MetaRef, opts?: HistoryOptions): AsyncIterable<MetadataEvent>;\n\n /**\n * Live event stream. The iterator MUST:\n *\n * - Replay all events with `seq > since` before yielding any new event.\n * - Stay open until the consumer breaks the loop.\n * - Survive transient backend disconnects (implementation's choice\n * how to resume — Postgres LISTEN reconnect, JSONL tail, etc.).\n */\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent>;\n}\n\n/**\n * Sentinel symbol used by `LayeredRepository` (M0 PR-5) to label which\n * underlying layer emitted an event. Defined here so the contract is\n * shared.\n */\nexport const LAYER_SOURCE = Symbol.for('objectstack.metadata.layer-source');\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `InMemoryRepository` — reference implementation of `MetadataRepository`\n * backed by plain JS Maps. Used by:\n *\n * - tests (parameterized contract-test suite)\n * - edge / serverless runtimes (no FS, no DB)\n * - `LayeredRepository` fallbacks\n *\n * State model\n * ───────────\n * items : refKey → MetadataItem (current head)\n * logs : org → MetadataEvent[] (append-only, monotonic per org)\n * seqs : org → number\n *\n * `watch()` is implemented over a simple subscriber list. Each subscriber\n * receives a deep-copy of the event so they cannot mutate the log.\n */\n\nimport {\n type MetaRef,\n type MetadataItem,\n type MetadataItemHeader,\n type MetadataEvent,\n type PutOptions,\n type PutResult,\n type DeleteOptions,\n type DeleteResult,\n type ListFilter,\n type WatchFilter,\n type HistoryOptions,\n type MetadataType,\n refKey,\n} from './types.js';\nimport { hashSpec } from './canonicalize.js';\nimport { ConflictError } from './errors.js';\nimport type { MetadataRepository } from './repository.js';\n\nconst orgKey = (ref: Pick<MetaRef, 'org'>): string => ref.org;\n\nconst matchesFilter = (\n ref: MetaRef,\n filter: { org?: string; type?: MetadataType; name?: string },\n): boolean => {\n if (filter.org && filter.org !== ref.org) return false;\n if (filter.type && filter.type !== ref.type) return false;\n if (filter.name && filter.name !== ref.name) return false;\n return true;\n};\n\ninterface Subscriber {\n filter: WatchFilter;\n push: (evt: MetadataEvent) => void;\n closed: boolean;\n}\n\nexport interface InMemoryRepositoryOptions {\n /** Optional clock injection for deterministic tests. Default: Date.now. */\n now?: () => Date;\n}\n\nexport class InMemoryRepository implements MetadataRepository {\n private readonly items = new Map<string, MetadataItem>();\n /** Per-org event log. */\n private readonly logs = new Map<string, MetadataEvent[]>();\n /** Next seq per org. */\n private readonly seqs = new Map<string, number>();\n private readonly subscribers = new Set<Subscriber>();\n private readonly now: () => Date;\n\n constructor(opts: InMemoryRepositoryOptions = {}) {\n this.now = opts.now ?? (() => new Date());\n }\n\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n const item = this.items.get(refKey(ref));\n if (!item) return null;\n if (ref.version && item.hash !== ref.version) {\n // History lookup is not supported in this minimal impl — only HEAD.\n // A future revision may walk the log to reconstruct an old version.\n return null;\n }\n return clone(item);\n }\n\n async getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null> {\n // InMemoryRepository keeps only HEAD bodies; historical bodies are\n // not retained. Resolve only if the requested hash IS HEAD.\n const item = this.items.get(refKey(ref));\n if (!item || item.hash !== hash) return null;\n return clone(item);\n }\n\n async put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult> {\n const key = refKey(ref);\n const current = this.items.get(key);\n const currentHead = current?.hash ?? null;\n\n if ((opts.parentVersion ?? null) !== currentHead) {\n throw new ConflictError(ref, opts.parentVersion ?? null, currentHead);\n }\n\n const hash = hashSpec(spec);\n\n // No-op write — same content. Still consumes nothing; no event emitted.\n if (current && current.hash === hash) {\n return { version: hash, seq: current.seq, item: clone(current) };\n }\n\n const seq = this.bumpSeq(ref);\n const ts = this.now().toISOString();\n\n const item: MetadataItem = {\n ref: { ...ref, version: undefined },\n body: clonePlain(spec) as Record<string, unknown>,\n hash,\n parentHash: currentHead,\n authoredBy: opts.actor,\n authoredAt: ts,\n message: opts.message,\n seq,\n };\n\n this.items.set(key, item);\n\n const evt: MetadataEvent = {\n seq,\n op: current ? 'update' : 'create',\n ref: { ...ref, version: undefined },\n hash,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'in-memory',\n };\n this.appendEvent(ref, evt);\n\n return { version: hash, seq, item: clone(item) };\n }\n\n async delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult> {\n const key = refKey(ref);\n const current = this.items.get(key);\n const currentHead = current?.hash ?? null;\n if (currentHead !== opts.parentVersion) {\n throw new ConflictError(ref, opts.parentVersion, currentHead);\n }\n\n this.items.delete(key);\n const seq = this.bumpSeq(ref);\n const ts = this.now().toISOString();\n const evt: MetadataEvent = {\n seq,\n op: 'delete',\n ref: { ...ref, version: undefined },\n hash: null,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'in-memory',\n };\n this.appendEvent(ref, evt);\n return { seq };\n }\n\n async *list(filter: ListFilter): AsyncIterable<MetadataItemHeader> {\n const limit = filter.limit ?? Infinity;\n let yielded = 0;\n for (const item of this.items.values()) {\n if (!matchesFilter(item.ref, filter)) continue;\n if (filter.nameContains && !item.ref.name.includes(filter.nameContains)) continue;\n const { body, ...header } = item;\n void body;\n yield clone(header) as MetadataItemHeader;\n if (++yielded >= limit) return;\n }\n }\n\n async *history(ref: MetaRef, opts: HistoryOptions = {}): AsyncIterable<MetadataEvent> {\n const log = this.logs.get(orgKey(ref)) ?? [];\n const since = opts.sinceSeq ?? -1;\n const limit = opts.limit ?? Infinity;\n let yielded = 0;\n for (const evt of log) {\n if (evt.seq <= since) continue;\n if (evt.ref.type !== ref.type || evt.ref.name !== ref.name) continue;\n yield clone(evt);\n if (++yielded >= limit) return;\n }\n }\n\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent> {\n // Implemented as a manual async iterator (not a generator) so we can\n // implement `return()` to unblock a pending wait. Async generators\n // do NOT run their `finally` block when paused on an unresolved\n // `await` — see https://github.com/tc39/proposal-async-iteration.\n const queue: MetadataEvent[] = [];\n let waiter: ((evt: IteratorResult<MetadataEvent>) => void) | null = null;\n let closed = false;\n const delivered = new Set<string>();\n const evtKey = (e: MetadataEvent) => `${orgKey(e.ref)}#${e.seq}`;\n\n const subscriber: Subscriber = {\n filter,\n closed: false,\n push: (evt) => {\n if (subscriber.closed) return;\n if (waiter) {\n const k = evtKey(evt);\n if (delivered.has(k)) return;\n delivered.add(k);\n const w = waiter;\n waiter = null;\n w({ value: clone(evt), done: false });\n } else {\n queue.push(evt);\n }\n },\n };\n\n // Build the replay buffer BEFORE registering the subscriber to avoid\n // a race window? No — we register first then build replay, dedup'ing\n // by seq when we hand off to live so any event that arrives during\n // replay isn't double-delivered.\n this.subscribers.add(subscriber);\n\n const replay: MetadataEvent[] = [];\n for (const ok of this.orgKeysMatching(filter)) {\n const log = this.logs.get(ok) ?? [];\n for (const evt of log) {\n if (typeof since === 'number' && evt.seq <= since) continue;\n if (!matchesFilter(evt.ref, filter)) continue;\n replay.push(evt);\n }\n }\n replay.sort((a, b) => a.seq - b.seq);\n let replayIdx = 0;\n\n const drainQueueOrReplay = (): IteratorResult<MetadataEvent> | null => {\n while (replayIdx < replay.length) {\n const evt = replay[replayIdx++]!;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n while (queue.length > 0) {\n const evt = queue.shift()!;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n return null;\n };\n\n const close = (): IteratorResult<MetadataEvent> => {\n if (!closed) {\n closed = true;\n subscriber.closed = true;\n this.subscribers.delete(subscriber);\n if (waiter) {\n const w = waiter;\n waiter = null;\n w({ value: undefined, done: true });\n }\n }\n return { value: undefined, done: true };\n };\n\n const iterator: AsyncIterator<MetadataEvent> = {\n next: () => {\n if (closed) return Promise.resolve({ value: undefined, done: true });\n const immediate = drainQueueOrReplay();\n if (immediate) return Promise.resolve(immediate);\n return new Promise<IteratorResult<MetadataEvent>>((resolve) => {\n waiter = resolve;\n });\n },\n return: () => Promise.resolve(close()),\n throw: (err) => {\n close();\n return Promise.reject(err);\n },\n };\n\n return {\n [Symbol.asyncIterator]: () => iterator,\n };\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private bumpSeq(ref: Pick<MetaRef, 'org'>): number {\n const ok = orgKey(ref);\n const next = (this.seqs.get(ok) ?? 0) + 1;\n this.seqs.set(ok, next);\n return next;\n }\n\n private appendEvent(ref: Pick<MetaRef, 'org'>, evt: MetadataEvent): void {\n const ok = orgKey(ref);\n const log = this.logs.get(ok) ?? [];\n log.push(evt);\n this.logs.set(ok, log);\n // Broadcast\n for (const sub of this.subscribers) {\n if (sub.closed) continue;\n if (!matchesFilter(evt.ref, sub.filter)) continue;\n sub.push(evt);\n }\n }\n\n private orgKeysMatching(filter: WatchFilter): string[] {\n const keys: string[] = [];\n for (const ok of this.logs.keys()) {\n if (filter.org && filter.org !== ok) continue;\n keys.push(ok);\n }\n return keys;\n }\n}\n\n/** Deep clone via JSON; safe for `MetadataItem` / `MetadataEvent`. */\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\n/** Clone a value that the caller passed in; rejects functions/symbols. */\nfunction clonePlain(value: unknown): unknown {\n return JSON.parse(JSON.stringify(value));\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `MetadataCache` — bounded, event-invalidated LRU sitting in front of a\n * `MetadataRepository`. See ADR-0008 §2.5.\n *\n * Design contract\n * ───────────────\n *\n * 1. **Lazy fill.** Cache entries are only created on first read miss.\n * No bulk preload — that would defeat the whole point of being\n * bounded.\n * 2. **Event-driven invalidation.** The cache subscribes to\n * `repo.watch({...})` and drops or replaces affected entries\n * whenever the repository emits an event. Stale reads are bounded\n * by the event-propagation latency of the underlying repo.\n * 3. **Bounded.** Both `maxEntries` and `maxBytes` are enforced; LRU\n * eviction happens on `set()` when either limit is exceeded.\n * 4. **Coherent under races.** Concurrent `get()`s for the same key\n * coalesce onto a single backend fetch (the \"thundering herd\"\n * fix). If an invalidation event arrives during an in-flight\n * fetch, the resulting value is discarded — the next read fetches\n * fresh.\n * 5. **Negative caching.** A miss (repo returned `null`) is also\n * cached, with a smaller TTL semantics — it stays until an event\n * for that ref arrives. This makes \"does X exist?\" cheap during\n * tight loops without compromising correctness.\n */\n\nimport type { MetaRef, MetadataItem, MetadataEvent, WatchFilter } from './types.js';\nimport { refKey } from './types.js';\nimport type { MetadataRepository } from './repository.js';\n\nexport interface MetadataCacheOptions {\n /** Maximum number of entries to keep. Default: 1024. */\n maxEntries?: number;\n /**\n * Maximum approximate body size in bytes. Default: 8 MiB. Each entry's\n * size is estimated from `JSON.stringify(item.body).length`.\n */\n maxBytes?: number;\n /**\n * Watch filter. Only events matching this filter invalidate cache\n * entries. Default: no filter (all events).\n */\n watchFilter?: WatchFilter;\n}\n\ninterface CacheEntry {\n /** Null = negative cache (item is known absent). */\n item: MetadataItem | null;\n size: number;\n /** Hit count, just for diagnostics. */\n hits: number;\n}\n\nexport interface CacheStats {\n entries: number;\n bytes: number;\n hits: number;\n misses: number;\n invalidations: number;\n /** Reads that arrived while a fetch was already in-flight for the same key. */\n coalesced: number;\n}\n\nexport class MetadataCache {\n private readonly repo: MetadataRepository;\n private readonly maxEntries: number;\n private readonly maxBytes: number;\n private readonly watchFilter: WatchFilter;\n\n /** LRU is implemented via insertion-order Map; touch on get/set. */\n private readonly entries = new Map<string, CacheEntry>();\n private bytes = 0;\n\n /** De-duplicate concurrent fetches for the same key. */\n private readonly inflight = new Map<string, Promise<MetadataItem | null>>();\n /**\n * Generation counter incremented on every invalidation. Each in-flight\n * fetch captures the gen at start; if the gen changes before it\n * resolves, the result is NOT cached.\n */\n private readonly fetchGen = new Map<string, number>();\n\n private watchIterator: AsyncIterator<MetadataEvent> | null = null;\n private watchClosed = false;\n private watchLoop: Promise<void> | null = null;\n\n private readonly stats: CacheStats = {\n entries: 0,\n bytes: 0,\n hits: 0,\n misses: 0,\n invalidations: 0,\n coalesced: 0,\n };\n\n constructor(repo: MetadataRepository, opts: MetadataCacheOptions = {}) {\n this.repo = repo;\n this.maxEntries = opts.maxEntries ?? 1024;\n this.maxBytes = opts.maxBytes ?? 8 * 1024 * 1024;\n this.watchFilter = opts.watchFilter ?? {};\n }\n\n /**\n * Start the background watch subscription. Idempotent; safe to call\n * multiple times. Caller is responsible for calling `close()` when\n * the cache is no longer needed.\n */\n start(): void {\n if (this.watchIterator || this.watchClosed) return;\n const iter = this.repo.watch(this.watchFilter)[Symbol.asyncIterator]();\n this.watchIterator = iter;\n this.watchLoop = (async () => {\n try {\n while (!this.watchClosed) {\n const { value, done } = await iter.next();\n if (done) return;\n this.applyEvent(value);\n }\n } catch {\n // Repository tore down its event stream; cache is left in a\n // best-effort state. Consumers can detect this via stats or by\n // restarting the cache.\n }\n })();\n }\n\n /** Tear down the watch subscription and clear in-flight tracking. */\n async close(): Promise<void> {\n this.watchClosed = true;\n if (this.watchIterator?.return) {\n try {\n await this.watchIterator.return(undefined);\n } catch {\n // ignore\n }\n }\n this.watchIterator = null;\n if (this.watchLoop) {\n try {\n await this.watchLoop;\n } catch {\n // ignore\n }\n this.watchLoop = null;\n }\n this.inflight.clear();\n }\n\n /** Read with cache. Coalesces concurrent reads for the same key. */\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n const key = refKey(ref);\n const cached = this.entries.get(key);\n if (cached) {\n // Touch (LRU bump).\n this.entries.delete(key);\n this.entries.set(key, cached);\n cached.hits += 1;\n this.stats.hits += 1;\n return cached.item ? clone(cached.item) : null;\n }\n\n const existing = this.inflight.get(key);\n if (existing) {\n this.stats.coalesced += 1;\n const item = await existing;\n return item ? clone(item) : null;\n }\n\n this.stats.misses += 1;\n const gen = (this.fetchGen.get(key) ?? 0);\n const promise = this.repo\n .get(ref)\n .then((item) => {\n // If the cache was invalidated for this key during the fetch,\n // skip caching the (possibly stale) result.\n if ((this.fetchGen.get(key) ?? 0) === gen) {\n this.cacheSet(key, item);\n }\n return item;\n })\n .finally(() => {\n this.inflight.delete(key);\n });\n\n this.inflight.set(key, promise);\n const item = await promise;\n return item ? clone(item) : null;\n }\n\n /** Drop a single entry by ref. */\n invalidate(ref: MetaRef): void {\n const key = refKey(ref);\n this.bumpGen(key);\n const removed = this.entries.get(key);\n if (removed) {\n this.bytes -= removed.size;\n this.entries.delete(key);\n }\n this.stats.invalidations += 1;\n }\n\n /** Drop the entire cache (e.g. on a reset). */\n clear(): void {\n this.entries.clear();\n this.bytes = 0;\n // Bump every in-flight key so we don't accidentally cache stale data.\n for (const key of this.inflight.keys()) this.bumpGen(key);\n }\n\n getStats(): Readonly<CacheStats> {\n return {\n ...this.stats,\n entries: this.entries.size,\n bytes: this.bytes,\n };\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private applyEvent(evt: MetadataEvent): void {\n // Any op (create/update/delete/rename) invalidates the affected key.\n // We don't try to be clever (e.g. preloading the new value) — the\n // next reader will pull the fresh body through `get`.\n const ref = evt.ref;\n this.invalidate(ref);\n if (evt.op === 'rename' && evt.previousName) {\n this.invalidate({ ...ref, name: evt.previousName });\n }\n }\n\n private cacheSet(key: string, item: MetadataItem | null): void {\n const size = item ? estimateSize(item) : 0;\n const existing = this.entries.get(key);\n if (existing) this.bytes -= existing.size;\n this.entries.set(key, { item, size, hits: 0 });\n this.bytes += size;\n this.evictIfNeeded();\n }\n\n private evictIfNeeded(): void {\n // Map preserves insertion order; oldest first. Touch on `get` moves\n // entries to the end → eviction from the front evicts the LRU.\n while (\n this.entries.size > this.maxEntries ||\n this.bytes > this.maxBytes\n ) {\n const first = this.entries.keys().next();\n if (first.done) break;\n const key = first.value;\n const entry = this.entries.get(key)!;\n this.bytes -= entry.size;\n this.entries.delete(key);\n }\n }\n\n private bumpGen(key: string): void {\n this.fetchGen.set(key, (this.fetchGen.get(key) ?? 0) + 1);\n }\n}\n\nfunction estimateSize(item: MetadataItem): number {\n // Rough estimate — JS strings are 2 bytes per char, but JSON.stringify\n // length is a fine proxy. Add a fixed overhead for the wrapper fields.\n try {\n return JSON.stringify(item.body).length * 2 + 256;\n } catch {\n return 1024; // fallback for unstringifiable bodies\n }\n}\n\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `LayeredRepository` — composes N child `MetadataRepository`s into a\n * single read-through stack. See ADR-0008 §10 PR-5.\n *\n * Read semantics\n * ──────────────\n * - `get(ref)` walks the layers top-to-bottom; first non-null wins.\n * - `list()` deduplicates by `refKey(ref)`, preferring the top layer.\n * - `history()` and `watch()` merge events from all layers, each\n * tagged with the source layer label in `evt.source`\n * (`<label>:<original-source>`).\n *\n * Write semantics\n * ───────────────\n * - `put` / `delete` are routed to the topmost writable layer.\n * - A layer is writable unless `readOnly: true` in its config.\n * - The write target's existing HEAD is used for the parent check —\n * i.e. if a key exists only in a lower layer, writing produces a\n * create on the top layer (an \"overlay\" of the lower one).\n *\n * Event tagging\n * ─────────────\n * - Each emitted event carries `source` rewritten as\n * `<layer.label>:<source>` so subscribers can tell which layer\n * produced the change. Original event content is otherwise\n * unchanged.\n *\n * This implementation never re-orders events relative to seq within a\n * single layer, but seqs across layers may interleave. Downstream\n * consumers (Cache, SchemaRegistry) only need monotonicity within a\n * (layer, branch) tuple — they treat each (layer.label, ref) as a\n * separate stream.\n */\n\nimport {\n type MetadataRepository,\n type MetaRef,\n type MetadataItem,\n type MetadataItemHeader,\n type MetadataEvent,\n type PutOptions,\n type PutResult,\n type DeleteOptions,\n type DeleteResult,\n type ListFilter,\n type WatchFilter,\n type HistoryOptions,\n refKey,\n} from './index.js';\n\nexport interface LayerConfig {\n /** Stable identifier for this layer (e.g. \"user\", \"team\", \"system\"). */\n label: string;\n /** The backing repository. */\n repo: MetadataRepository;\n /** When true, this layer rejects writes (used for built-ins). */\n readOnly?: boolean;\n}\n\nexport interface LayeredRepositoryOptions {\n /**\n * Layers in priority order, highest first. The first writable layer\n * receives all writes.\n */\n layers: LayerConfig[];\n}\n\nconst tagSource = (label: string, evt: MetadataEvent): MetadataEvent => ({\n ...evt,\n source: `${label}:${evt.source}`,\n});\n\nexport class LayeredRepository implements MetadataRepository {\n private readonly layers: LayerConfig[];\n /** Index of the first writable layer; -1 if none. */\n private readonly writableIdx: number;\n\n constructor(opts: LayeredRepositoryOptions) {\n if (!opts.layers.length) {\n throw new Error('LayeredRepository requires at least one layer');\n }\n this.layers = opts.layers;\n this.writableIdx = opts.layers.findIndex((l) => !l.readOnly);\n }\n\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n for (const layer of this.layers) {\n const item = await layer.repo.get(ref);\n if (item) return item;\n }\n return null;\n }\n\n async getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null> {\n // Probe layers top→bottom; first layer that resolves the hash wins.\n // executionPinned types are durable in the storage layer\n // (`SysMetadataRepository`); FS / in-memory layers only resolve\n // hash == HEAD as a fallback.\n for (const layer of this.layers) {\n const item = await layer.repo.getByHash(ref, hash);\n if (item) return item;\n }\n return null;\n }\n\n async put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult> {\n if (this.writableIdx < 0) {\n throw new Error('LayeredRepository: no writable layer configured');\n }\n return this.layers[this.writableIdx]!.repo.put(ref, spec, opts);\n }\n\n async delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult> {\n if (this.writableIdx < 0) {\n throw new Error('LayeredRepository: no writable layer configured');\n }\n return this.layers[this.writableIdx]!.repo.delete(ref, opts);\n }\n\n async *list(filter: ListFilter): AsyncIterable<MetadataItemHeader> {\n // Yield headers from top→bottom, deduplicating by refKey.\n const seen = new Set<string>();\n const limit = filter.limit ?? Infinity;\n let yielded = 0;\n for (const layer of this.layers) {\n for await (const header of layer.repo.list(filter)) {\n const key = refKey(header.ref);\n if (seen.has(key)) continue;\n seen.add(key);\n yield header;\n if (++yielded >= limit) return;\n }\n }\n }\n\n async *history(ref: MetaRef, opts: HistoryOptions = {}): AsyncIterable<MetadataEvent> {\n // Merge histories from all layers, tagged. Order preserves each\n // layer's monotonic seq, but events across layers may interleave.\n // We collect everything then sort by ts as a best-effort total order.\n const events: MetadataEvent[] = [];\n for (const layer of this.layers) {\n for await (const evt of layer.repo.history(ref, opts)) {\n events.push(tagSource(layer.label, evt));\n }\n }\n events.sort((a, b) => (a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0));\n const limit = opts.limit ?? Infinity;\n let yielded = 0;\n for (const evt of events) {\n yield evt;\n if (++yielded >= limit) return;\n }\n }\n\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent> {\n // Fan out to all child watchers; multiplex into a single iterator.\n return multiplexWatch(this.layers, filter, since);\n }\n}\n\n/**\n * Multiplex N async iterables of MetadataEvent into one, tagging each\n * event's `source` with its layer label. Implemented as a manual\n * AsyncIterator so we can correctly cancel all child iterators when the\n * consumer breaks out.\n */\nfunction multiplexWatch(\n layers: LayerConfig[],\n filter: WatchFilter,\n since: number | undefined,\n): AsyncIterable<MetadataEvent> {\n return {\n [Symbol.asyncIterator]() {\n const children = layers.map((layer) => ({\n label: layer.label,\n iter: layer.repo.watch(filter, since)[Symbol.asyncIterator](),\n pending: null as Promise<{ label: string; result: IteratorResult<MetadataEvent> }> | null,\n done: false,\n }));\n let closed = false;\n\n const pumpAll = () => {\n for (const c of children) {\n if (c.done || c.pending) continue;\n c.pending = c.iter.next().then((result) => ({ label: c.label, result }));\n }\n };\n\n const closeAll = async () => {\n if (closed) return;\n closed = true;\n await Promise.all(\n children.map(async (c) => {\n try { await c.iter.return?.(undefined); } catch { /* ignore */ }\n }),\n );\n };\n\n return {\n async next(): Promise<IteratorResult<MetadataEvent>> {\n while (!closed) {\n pumpAll();\n const inflight = children.filter((c) => c.pending);\n if (!inflight.length) return { value: undefined, done: true };\n const winner = await Promise.race(inflight.map((c) => c.pending!));\n const target = children.find((c) => c.label === winner.label)!;\n target.pending = null;\n if (winner.result.done) {\n target.done = true;\n continue;\n }\n return {\n value: tagSource(winner.label, winner.result.value as MetadataEvent),\n done: false,\n };\n }\n return { value: undefined, done: true };\n },\n async return() {\n await closeAll();\n return { value: undefined, done: true };\n },\n async throw(err) {\n await closeAll();\n throw err;\n },\n } as AsyncIterator<MetadataEvent>;\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/framework/framework/packages/metadata-core/dist/index.cjs","../src/types.ts","../src/repository.ts","../src/in-memory-repository.ts","../src/cache.ts","../src/layered-repository.ts"],"names":["clone"],"mappings":"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","file":"/home/runner/work/framework/framework/packages/metadata-core/dist/index.cjs","sourcesContent":[null,"// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata Repository types — see ADR-0008 §2.\n *\n * All shapes are defined as Zod schemas so the same definition serves\n * runtime validation and static typing (`z.infer<typeof X>`).\n */\n\nimport { z } from 'zod';\n\n// ─── Metadata type registry ───────────────────────────────────────────\n\n/**\n * Canonical metadata type names. Aligned with the `MetadataTypeSchema`\n * enum in `@objectstack/spec/kernel/metadata-plugin.zod.ts`. New types are\n * added here in lockstep with that file.\n */\nexport const MetadataTypeSchema = z.enum([\n 'object',\n 'field',\n 'trigger',\n 'validation',\n 'hook',\n 'view',\n 'page',\n 'dashboard',\n 'app',\n 'action',\n 'flow',\n 'workflow',\n 'approval',\n 'job',\n 'agent',\n 'tool',\n 'skill',\n 'report',\n 'translation',\n 'role',\n 'profile',\n 'permission',\n 'policy',\n 'api',\n 'endpoint',\n 'datasource',\n 'cube',\n 'settings',\n 'router',\n 'function',\n 'service',\n 'email_template',\n]).describe('Canonical metadata type name');\n\nexport type MetadataType = z.infer<typeof MetadataTypeSchema>;\n\n// ─── MetaRef ──────────────────────────────────────────────────────────\n\n/**\n * Fully-qualified reference to a metadata item. Identity is `(org, type, name)`.\n *\n * Per ADR-0008 v2 (2026-05) the metadata layer no longer carries `project`\n * or `branch`. Project survives only as an **artifact packaging concept**\n * (the unit a CLI/CI run compiles into `dist/objectstack.json`); it does\n * not appear in the runtime customization scope. Branching belongs to Git\n * (or your VCS of choice) and never propagated cleanly into the runtime\n * model — so it has been removed entirely.\n *\n * Higher layers may default `org='system'` for built-ins.\n *\n * `version` is optional: omit to mean \"HEAD\", supply to pin.\n */\nexport const MetaRefSchema = z.object({\n org: z.string().min(1).describe('Tenant/org identifier; \"system\" for built-ins'),\n type: MetadataTypeSchema,\n name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Snake_case machine name'),\n version: z.string().optional().describe('Optional version pin (content hash); omit for HEAD'),\n});\n\nexport type MetaRef = z.infer<typeof MetaRefSchema>;\n\n/**\n * Construct a stable string key from a MetaRef (excluding `version`,\n * which is mutable). Used as cache keys and log indexes.\n */\nexport function refKey(ref: Pick<MetaRef, 'org' | 'type' | 'name'>): string {\n return `${ref.org}/${ref.type}/${ref.name}`;\n}\n\n// ─── Item & header ────────────────────────────────────────────────────\n\n/**\n * Full metadata item as stored / returned by the Repository.\n *\n * `body` is the **canonical, Zod-normalised** spec (with defaults filled\n * in). `hash` is `sha256(canonicalize(body))`. Equal hashes imply equal\n * specs.\n */\nexport const MetadataItemSchema = z.object({\n ref: MetaRefSchema,\n body: z.record(z.string(), z.unknown()).describe('Canonical Zod-normalised spec'),\n hash: z.string().regex(/^sha256:[0-9a-f]{64}$/).describe('sha256(canonicalize(body))'),\n parentHash: z.string().nullable().describe('Hash this version was derived from; null for first version'),\n authoredBy: z.string().describe('Identity of the writer (user id, \"cli\", \"ai:claude\", …)'),\n authoredAt: z.string().describe('ISO-8601 timestamp'),\n message: z.string().optional().describe('Optional commit message'),\n seq: z.number().int().nonnegative().describe('Sequence number this write produced in the org log'),\n schemaVersion: z.string().optional().describe('Zod schema version that wrote this spec (M3 codemod hook)'),\n});\n\nexport type MetadataItem = z.infer<typeof MetadataItemSchema>;\n\n/** Lightweight header for listing — `body` omitted. */\nexport type MetadataItemHeader = Omit<MetadataItem, 'body'>;\n\n// ─── Change log event ─────────────────────────────────────────────────\n\nexport const MetadataOpSchema = z.enum(['create', 'update', 'delete', 'rename']);\nexport type MetadataOp = z.infer<typeof MetadataOpSchema>;\n\n/**\n * The single event payload broadcast by the change log. ADR-0008 §2.4.\n *\n * For `rename`, `previousName` carries the old machine name. For\n * `delete`, `hash` is null. The payload is intentionally small —\n * consumers re-fetch via the cache when they need the full body.\n */\nexport const MetadataEventSchema = z.object({\n seq: z.number().int().nonnegative(),\n op: MetadataOpSchema,\n ref: MetaRefSchema,\n hash: z.string().nullable(),\n parentHash: z.string().nullable(),\n previousName: z.string().optional().describe('Set on op=\"rename\"'),\n actor: z.string(),\n message: z.string().optional(),\n ts: z.string(),\n source: z.string().describe('Origin label: \"fs\", \"studio\", \"rest\", \"ai\", \"git-import\", …'),\n});\n\nexport type MetadataEvent = z.infer<typeof MetadataEventSchema>;\n\n// ─── Operation options ────────────────────────────────────────────────\n\nexport interface PutOptions {\n /**\n * Hash this writer believed was at HEAD. `null` means \"creating, expect\n * absence\". A mismatch throws ConflictError.\n */\n parentVersion: string | null;\n /** Identity of the writer; mirrored to MetadataEvent.actor. */\n actor: string;\n /** Optional human-readable commit message. */\n message?: string;\n /** Optional label for the change log \"source\" column. */\n source?: string;\n}\n\nexport interface PutResult {\n /** New content hash assigned to the spec. */\n version: string;\n /** Sequence number of the emitted MetadataEvent. */\n seq: number;\n /** The committed item (canonicalised). */\n item: MetadataItem;\n}\n\nexport interface DeleteOptions {\n parentVersion: string;\n actor: string;\n message?: string;\n source?: string;\n}\n\nexport interface DeleteResult {\n seq: number;\n}\n\nexport interface ListFilter {\n org?: string;\n type?: MetadataType;\n /** Substring match on `name`; case-sensitive. */\n nameContains?: string;\n /** Pagination cursor; opaque string from a previous response. */\n cursor?: string;\n /** Page size; implementations may clamp. */\n limit?: number;\n}\n\nexport interface WatchFilter {\n org?: string;\n type?: MetadataType;\n /** When omitted, match all names within the scope. */\n name?: string;\n}\n\nexport interface HistoryOptions {\n /** Lower bound (exclusive) for pagination. */\n sinceSeq?: number;\n limit?: number;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The `MetadataRepository` interface — single point of pluggability for\n * the metadata storage backend. See ADR-0008 §2.6.\n *\n * Implementations:\n *\n * - `InMemoryRepository` (this package, for tests & edge)\n * - `FileSystemRepository` (`@objectstack/metadata`)\n * - `LayeredRepository` (`@objectstack/metadata`)\n * - `PostgresRepository` (`@objectstack/metadata-postgres`, M1)\n *\n * Implementation contract — what every backend MUST guarantee:\n *\n * 1. **Atomic put.** A successful `put()` either fully applies (item\n * visible to subsequent `get` AND an event present in the log) or\n * does not apply at all. No half-states.\n * 2. **Monotonic seq per org.** `seq` is strictly increasing within\n * `org`. Different orgs have independent sequences. (Repositories\n * scoped to a single org may treat the entire repo as one log.)\n * 3. **Optimistic locking.** `put` and `delete` throw `ConflictError`\n * when `parentVersion` does not match the current HEAD.\n * 4. **Canonical hashing.** `item.hash === hashSpec(item.body)` — always.\n * 5. **Event ordering.** Subscribers to `watch()` receive events in\n * monotonically-increasing `seq` order with no gaps.\n * 6. **Resumability.** `watch(_, since)` MUST replay all events with\n * `seq > since` before delivering live events.\n * 7. **Tombstones, not holes.** `delete` produces a `delete` event;\n * `get` returns null but `history` still shows the lineage.\n */\n\nimport type {\n MetaRef,\n MetadataItem,\n MetadataItemHeader,\n MetadataEvent,\n PutOptions,\n PutResult,\n DeleteOptions,\n DeleteResult,\n ListFilter,\n WatchFilter,\n HistoryOptions,\n} from './types.js';\n\nexport interface MetadataRepository {\n /** Read HEAD or a pinned version. Returns null if absent. */\n get(ref: MetaRef): Promise<MetadataItem | null>;\n\n /**\n * Resolve a historical version by content hash (ADR-0009).\n *\n * Returns the `MetadataItem` whose canonical sha256 equals `hash`\n * for the given ref, or `null` if no such version is recorded.\n *\n * Implementations MUST search history (not just HEAD) so that\n * `executionPinned` types remain resolvable through definition\n * upgrades. For non-`executionPinned` types, implementations MAY\n * return `null` if they have GC'd the corresponding history row.\n */\n getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null>;\n\n /**\n * Write a new version. Atomic.\n * @throws ConflictError if `parentVersion` does not match HEAD.\n * @throws SchemaValidationError if `spec` fails Zod normalisation.\n */\n put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult>;\n\n /**\n * Soft-delete (tombstone). `parentVersion` is required.\n * @throws ConflictError on parent mismatch.\n */\n delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult>;\n\n /** Enumerate items matching a filter. Implementations may stream. */\n list(filter: ListFilter): AsyncIterable<MetadataItemHeader>;\n\n /** Per-item history; events in monotonic `seq` order. */\n history(ref: MetaRef, opts?: HistoryOptions): AsyncIterable<MetadataEvent>;\n\n /**\n * Live event stream. The iterator MUST:\n *\n * - Replay all events with `seq > since` before yielding any new event.\n * - Stay open until the consumer breaks the loop.\n * - Survive transient backend disconnects (implementation's choice\n * how to resume — Postgres LISTEN reconnect, JSONL tail, etc.).\n */\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent>;\n}\n\n/**\n * Sentinel symbol used by `LayeredRepository` (M0 PR-5) to label which\n * underlying layer emitted an event. Defined here so the contract is\n * shared.\n */\nexport const LAYER_SOURCE = Symbol.for('objectstack.metadata.layer-source');\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `InMemoryRepository` — reference implementation of `MetadataRepository`\n * backed by plain JS Maps. Used by:\n *\n * - tests (parameterized contract-test suite)\n * - edge / serverless runtimes (no FS, no DB)\n * - `LayeredRepository` fallbacks\n *\n * State model\n * ───────────\n * items : refKey → MetadataItem (current head)\n * logs : org → MetadataEvent[] (append-only, monotonic per org)\n * seqs : org → number\n *\n * `watch()` is implemented over a simple subscriber list. Each subscriber\n * receives a deep-copy of the event so they cannot mutate the log.\n */\n\nimport {\n type MetaRef,\n type MetadataItem,\n type MetadataItemHeader,\n type MetadataEvent,\n type PutOptions,\n type PutResult,\n type DeleteOptions,\n type DeleteResult,\n type ListFilter,\n type WatchFilter,\n type HistoryOptions,\n type MetadataType,\n refKey,\n} from './types.js';\nimport { hashSpec } from './canonicalize.js';\nimport { ConflictError } from './errors.js';\nimport type { MetadataRepository } from './repository.js';\n\nconst orgKey = (ref: Pick<MetaRef, 'org'>): string => ref.org;\n\nconst matchesFilter = (\n ref: MetaRef,\n filter: { org?: string; type?: MetadataType; name?: string },\n): boolean => {\n if (filter.org && filter.org !== ref.org) return false;\n if (filter.type && filter.type !== ref.type) return false;\n if (filter.name && filter.name !== ref.name) return false;\n return true;\n};\n\ninterface Subscriber {\n filter: WatchFilter;\n push: (evt: MetadataEvent) => void;\n closed: boolean;\n}\n\nexport interface InMemoryRepositoryOptions {\n /** Optional clock injection for deterministic tests. Default: Date.now. */\n now?: () => Date;\n}\n\nexport class InMemoryRepository implements MetadataRepository {\n private readonly items = new Map<string, MetadataItem>();\n /** Per-org event log. */\n private readonly logs = new Map<string, MetadataEvent[]>();\n /** Next seq per org. */\n private readonly seqs = new Map<string, number>();\n private readonly subscribers = new Set<Subscriber>();\n private readonly now: () => Date;\n\n constructor(opts: InMemoryRepositoryOptions = {}) {\n this.now = opts.now ?? (() => new Date());\n }\n\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n const item = this.items.get(refKey(ref));\n if (!item) return null;\n if (ref.version && item.hash !== ref.version) {\n // History lookup is not supported in this minimal impl — only HEAD.\n // A future revision may walk the log to reconstruct an old version.\n return null;\n }\n return clone(item);\n }\n\n async getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null> {\n // InMemoryRepository keeps only HEAD bodies; historical bodies are\n // not retained. Resolve only if the requested hash IS HEAD.\n const item = this.items.get(refKey(ref));\n if (!item || item.hash !== hash) return null;\n return clone(item);\n }\n\n async put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult> {\n const key = refKey(ref);\n const current = this.items.get(key);\n const currentHead = current?.hash ?? null;\n\n if ((opts.parentVersion ?? null) !== currentHead) {\n throw new ConflictError(ref, opts.parentVersion ?? null, currentHead);\n }\n\n const hash = hashSpec(spec);\n\n // No-op write — same content. Still consumes nothing; no event emitted.\n if (current && current.hash === hash) {\n return { version: hash, seq: current.seq, item: clone(current) };\n }\n\n const seq = this.bumpSeq(ref);\n const ts = this.now().toISOString();\n\n const item: MetadataItem = {\n ref: { ...ref, version: undefined },\n body: clonePlain(spec) as Record<string, unknown>,\n hash,\n parentHash: currentHead,\n authoredBy: opts.actor,\n authoredAt: ts,\n message: opts.message,\n seq,\n };\n\n this.items.set(key, item);\n\n const evt: MetadataEvent = {\n seq,\n op: current ? 'update' : 'create',\n ref: { ...ref, version: undefined },\n hash,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'in-memory',\n };\n this.appendEvent(ref, evt);\n\n return { version: hash, seq, item: clone(item) };\n }\n\n async delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult> {\n const key = refKey(ref);\n const current = this.items.get(key);\n const currentHead = current?.hash ?? null;\n if (currentHead !== opts.parentVersion) {\n throw new ConflictError(ref, opts.parentVersion, currentHead);\n }\n\n this.items.delete(key);\n const seq = this.bumpSeq(ref);\n const ts = this.now().toISOString();\n const evt: MetadataEvent = {\n seq,\n op: 'delete',\n ref: { ...ref, version: undefined },\n hash: null,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'in-memory',\n };\n this.appendEvent(ref, evt);\n return { seq };\n }\n\n async *list(filter: ListFilter): AsyncIterable<MetadataItemHeader> {\n const limit = filter.limit ?? Infinity;\n let yielded = 0;\n for (const item of this.items.values()) {\n if (!matchesFilter(item.ref, filter)) continue;\n if (filter.nameContains && !item.ref.name.includes(filter.nameContains)) continue;\n const { body, ...header } = item;\n void body;\n yield clone(header) as MetadataItemHeader;\n if (++yielded >= limit) return;\n }\n }\n\n async *history(ref: MetaRef, opts: HistoryOptions = {}): AsyncIterable<MetadataEvent> {\n const log = this.logs.get(orgKey(ref)) ?? [];\n const since = opts.sinceSeq ?? -1;\n const limit = opts.limit ?? Infinity;\n let yielded = 0;\n for (const evt of log) {\n if (evt.seq <= since) continue;\n if (evt.ref.type !== ref.type || evt.ref.name !== ref.name) continue;\n yield clone(evt);\n if (++yielded >= limit) return;\n }\n }\n\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent> {\n // Implemented as a manual async iterator (not a generator) so we can\n // implement `return()` to unblock a pending wait. Async generators\n // do NOT run their `finally` block when paused on an unresolved\n // `await` — see https://github.com/tc39/proposal-async-iteration.\n const queue: MetadataEvent[] = [];\n let waiter: ((evt: IteratorResult<MetadataEvent>) => void) | null = null;\n let closed = false;\n const delivered = new Set<string>();\n const evtKey = (e: MetadataEvent) => `${orgKey(e.ref)}#${e.seq}`;\n\n const subscriber: Subscriber = {\n filter,\n closed: false,\n push: (evt) => {\n if (subscriber.closed) return;\n if (waiter) {\n const k = evtKey(evt);\n if (delivered.has(k)) return;\n delivered.add(k);\n const w = waiter;\n waiter = null;\n w({ value: clone(evt), done: false });\n } else {\n queue.push(evt);\n }\n },\n };\n\n // Build the replay buffer BEFORE registering the subscriber to avoid\n // a race window? No — we register first then build replay, dedup'ing\n // by seq when we hand off to live so any event that arrives during\n // replay isn't double-delivered.\n this.subscribers.add(subscriber);\n\n const replay: MetadataEvent[] = [];\n for (const ok of this.orgKeysMatching(filter)) {\n const log = this.logs.get(ok) ?? [];\n for (const evt of log) {\n if (typeof since === 'number' && evt.seq <= since) continue;\n if (!matchesFilter(evt.ref, filter)) continue;\n replay.push(evt);\n }\n }\n replay.sort((a, b) => a.seq - b.seq);\n let replayIdx = 0;\n\n const drainQueueOrReplay = (): IteratorResult<MetadataEvent> | null => {\n while (replayIdx < replay.length) {\n const evt = replay[replayIdx++]!;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n while (queue.length > 0) {\n const evt = queue.shift()!;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n return null;\n };\n\n const close = (): IteratorResult<MetadataEvent> => {\n if (!closed) {\n closed = true;\n subscriber.closed = true;\n this.subscribers.delete(subscriber);\n if (waiter) {\n const w = waiter;\n waiter = null;\n w({ value: undefined, done: true });\n }\n }\n return { value: undefined, done: true };\n };\n\n const iterator: AsyncIterator<MetadataEvent> = {\n next: () => {\n if (closed) return Promise.resolve({ value: undefined, done: true });\n const immediate = drainQueueOrReplay();\n if (immediate) return Promise.resolve(immediate);\n return new Promise<IteratorResult<MetadataEvent>>((resolve) => {\n waiter = resolve;\n });\n },\n return: () => Promise.resolve(close()),\n throw: (err) => {\n close();\n return Promise.reject(err);\n },\n };\n\n return {\n [Symbol.asyncIterator]: () => iterator,\n };\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private bumpSeq(ref: Pick<MetaRef, 'org'>): number {\n const ok = orgKey(ref);\n const next = (this.seqs.get(ok) ?? 0) + 1;\n this.seqs.set(ok, next);\n return next;\n }\n\n private appendEvent(ref: Pick<MetaRef, 'org'>, evt: MetadataEvent): void {\n const ok = orgKey(ref);\n const log = this.logs.get(ok) ?? [];\n log.push(evt);\n this.logs.set(ok, log);\n // Broadcast\n for (const sub of this.subscribers) {\n if (sub.closed) continue;\n if (!matchesFilter(evt.ref, sub.filter)) continue;\n sub.push(evt);\n }\n }\n\n private orgKeysMatching(filter: WatchFilter): string[] {\n const keys: string[] = [];\n for (const ok of this.logs.keys()) {\n if (filter.org && filter.org !== ok) continue;\n keys.push(ok);\n }\n return keys;\n }\n}\n\n/** Deep clone via JSON; safe for `MetadataItem` / `MetadataEvent`. */\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\n/** Clone a value that the caller passed in; rejects functions/symbols. */\nfunction clonePlain(value: unknown): unknown {\n return JSON.parse(JSON.stringify(value));\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `MetadataCache` — bounded, event-invalidated LRU sitting in front of a\n * `MetadataRepository`. See ADR-0008 §2.5.\n *\n * Design contract\n * ───────────────\n *\n * 1. **Lazy fill.** Cache entries are only created on first read miss.\n * No bulk preload — that would defeat the whole point of being\n * bounded.\n * 2. **Event-driven invalidation.** The cache subscribes to\n * `repo.watch({...})` and drops or replaces affected entries\n * whenever the repository emits an event. Stale reads are bounded\n * by the event-propagation latency of the underlying repo.\n * 3. **Bounded.** Both `maxEntries` and `maxBytes` are enforced; LRU\n * eviction happens on `set()` when either limit is exceeded.\n * 4. **Coherent under races.** Concurrent `get()`s for the same key\n * coalesce onto a single backend fetch (the \"thundering herd\"\n * fix). If an invalidation event arrives during an in-flight\n * fetch, the resulting value is discarded — the next read fetches\n * fresh.\n * 5. **Negative caching.** A miss (repo returned `null`) is also\n * cached, with a smaller TTL semantics — it stays until an event\n * for that ref arrives. This makes \"does X exist?\" cheap during\n * tight loops without compromising correctness.\n */\n\nimport type { MetaRef, MetadataItem, MetadataEvent, WatchFilter } from './types.js';\nimport { refKey } from './types.js';\nimport type { MetadataRepository } from './repository.js';\n\nexport interface MetadataCacheOptions {\n /** Maximum number of entries to keep. Default: 1024. */\n maxEntries?: number;\n /**\n * Maximum approximate body size in bytes. Default: 8 MiB. Each entry's\n * size is estimated from `JSON.stringify(item.body).length`.\n */\n maxBytes?: number;\n /**\n * Watch filter. Only events matching this filter invalidate cache\n * entries. Default: no filter (all events).\n */\n watchFilter?: WatchFilter;\n}\n\ninterface CacheEntry {\n /** Null = negative cache (item is known absent). */\n item: MetadataItem | null;\n size: number;\n /** Hit count, just for diagnostics. */\n hits: number;\n}\n\nexport interface CacheStats {\n entries: number;\n bytes: number;\n hits: number;\n misses: number;\n invalidations: number;\n /** Reads that arrived while a fetch was already in-flight for the same key. */\n coalesced: number;\n}\n\nexport class MetadataCache {\n private readonly repo: MetadataRepository;\n private readonly maxEntries: number;\n private readonly maxBytes: number;\n private readonly watchFilter: WatchFilter;\n\n /** LRU is implemented via insertion-order Map; touch on get/set. */\n private readonly entries = new Map<string, CacheEntry>();\n private bytes = 0;\n\n /** De-duplicate concurrent fetches for the same key. */\n private readonly inflight = new Map<string, Promise<MetadataItem | null>>();\n /**\n * Generation counter incremented on every invalidation. Each in-flight\n * fetch captures the gen at start; if the gen changes before it\n * resolves, the result is NOT cached.\n */\n private readonly fetchGen = new Map<string, number>();\n\n private watchIterator: AsyncIterator<MetadataEvent> | null = null;\n private watchClosed = false;\n private watchLoop: Promise<void> | null = null;\n\n private readonly stats: CacheStats = {\n entries: 0,\n bytes: 0,\n hits: 0,\n misses: 0,\n invalidations: 0,\n coalesced: 0,\n };\n\n constructor(repo: MetadataRepository, opts: MetadataCacheOptions = {}) {\n this.repo = repo;\n this.maxEntries = opts.maxEntries ?? 1024;\n this.maxBytes = opts.maxBytes ?? 8 * 1024 * 1024;\n this.watchFilter = opts.watchFilter ?? {};\n }\n\n /**\n * Start the background watch subscription. Idempotent; safe to call\n * multiple times. Caller is responsible for calling `close()` when\n * the cache is no longer needed.\n */\n start(): void {\n if (this.watchIterator || this.watchClosed) return;\n const iter = this.repo.watch(this.watchFilter)[Symbol.asyncIterator]();\n this.watchIterator = iter;\n this.watchLoop = (async () => {\n try {\n while (!this.watchClosed) {\n const { value, done } = await iter.next();\n if (done) return;\n this.applyEvent(value);\n }\n } catch {\n // Repository tore down its event stream; cache is left in a\n // best-effort state. Consumers can detect this via stats or by\n // restarting the cache.\n }\n })();\n }\n\n /** Tear down the watch subscription and clear in-flight tracking. */\n async close(): Promise<void> {\n this.watchClosed = true;\n if (this.watchIterator?.return) {\n try {\n await this.watchIterator.return(undefined);\n } catch {\n // ignore\n }\n }\n this.watchIterator = null;\n if (this.watchLoop) {\n try {\n await this.watchLoop;\n } catch {\n // ignore\n }\n this.watchLoop = null;\n }\n this.inflight.clear();\n }\n\n /** Read with cache. Coalesces concurrent reads for the same key. */\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n const key = refKey(ref);\n const cached = this.entries.get(key);\n if (cached) {\n // Touch (LRU bump).\n this.entries.delete(key);\n this.entries.set(key, cached);\n cached.hits += 1;\n this.stats.hits += 1;\n return cached.item ? clone(cached.item) : null;\n }\n\n const existing = this.inflight.get(key);\n if (existing) {\n this.stats.coalesced += 1;\n const item = await existing;\n return item ? clone(item) : null;\n }\n\n this.stats.misses += 1;\n const gen = (this.fetchGen.get(key) ?? 0);\n const promise = this.repo\n .get(ref)\n .then((item) => {\n // If the cache was invalidated for this key during the fetch,\n // skip caching the (possibly stale) result.\n if ((this.fetchGen.get(key) ?? 0) === gen) {\n this.cacheSet(key, item);\n }\n return item;\n })\n .finally(() => {\n this.inflight.delete(key);\n });\n\n this.inflight.set(key, promise);\n const item = await promise;\n return item ? clone(item) : null;\n }\n\n /** Drop a single entry by ref. */\n invalidate(ref: MetaRef): void {\n const key = refKey(ref);\n this.bumpGen(key);\n const removed = this.entries.get(key);\n if (removed) {\n this.bytes -= removed.size;\n this.entries.delete(key);\n }\n this.stats.invalidations += 1;\n }\n\n /** Drop the entire cache (e.g. on a reset). */\n clear(): void {\n this.entries.clear();\n this.bytes = 0;\n // Bump every in-flight key so we don't accidentally cache stale data.\n for (const key of this.inflight.keys()) this.bumpGen(key);\n }\n\n getStats(): Readonly<CacheStats> {\n return {\n ...this.stats,\n entries: this.entries.size,\n bytes: this.bytes,\n };\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private applyEvent(evt: MetadataEvent): void {\n // Any op (create/update/delete/rename) invalidates the affected key.\n // We don't try to be clever (e.g. preloading the new value) — the\n // next reader will pull the fresh body through `get`.\n const ref = evt.ref;\n this.invalidate(ref);\n if (evt.op === 'rename' && evt.previousName) {\n this.invalidate({ ...ref, name: evt.previousName });\n }\n }\n\n private cacheSet(key: string, item: MetadataItem | null): void {\n const size = item ? estimateSize(item) : 0;\n const existing = this.entries.get(key);\n if (existing) this.bytes -= existing.size;\n this.entries.set(key, { item, size, hits: 0 });\n this.bytes += size;\n this.evictIfNeeded();\n }\n\n private evictIfNeeded(): void {\n // Map preserves insertion order; oldest first. Touch on `get` moves\n // entries to the end → eviction from the front evicts the LRU.\n while (\n this.entries.size > this.maxEntries ||\n this.bytes > this.maxBytes\n ) {\n const first = this.entries.keys().next();\n if (first.done) break;\n const key = first.value;\n const entry = this.entries.get(key)!;\n this.bytes -= entry.size;\n this.entries.delete(key);\n }\n }\n\n private bumpGen(key: string): void {\n this.fetchGen.set(key, (this.fetchGen.get(key) ?? 0) + 1);\n }\n}\n\nfunction estimateSize(item: MetadataItem): number {\n // Rough estimate — JS strings are 2 bytes per char, but JSON.stringify\n // length is a fine proxy. Add a fixed overhead for the wrapper fields.\n try {\n return JSON.stringify(item.body).length * 2 + 256;\n } catch {\n return 1024; // fallback for unstringifiable bodies\n }\n}\n\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `LayeredRepository` — composes N child `MetadataRepository`s into a\n * single read-through stack. See ADR-0008 §10 PR-5.\n *\n * Read semantics\n * ──────────────\n * - `get(ref)` walks the layers top-to-bottom; first non-null wins.\n * - `list()` deduplicates by `refKey(ref)`, preferring the top layer.\n * - `history()` and `watch()` merge events from all layers, each\n * tagged with the source layer label in `evt.source`\n * (`<label>:<original-source>`).\n *\n * Write semantics\n * ───────────────\n * - `put` / `delete` are routed to the topmost writable layer.\n * - A layer is writable unless `readOnly: true` in its config.\n * - The write target's existing HEAD is used for the parent check —\n * i.e. if a key exists only in a lower layer, writing produces a\n * create on the top layer (an \"overlay\" of the lower one).\n *\n * Event tagging\n * ─────────────\n * - Each emitted event carries `source` rewritten as\n * `<layer.label>:<source>` so subscribers can tell which layer\n * produced the change. Original event content is otherwise\n * unchanged.\n *\n * This implementation never re-orders events relative to seq within a\n * single layer, but seqs across layers may interleave. Downstream\n * consumers (Cache, SchemaRegistry) only need monotonicity within a\n * (layer, branch) tuple — they treat each (layer.label, ref) as a\n * separate stream.\n */\n\nimport {\n type MetadataRepository,\n type MetaRef,\n type MetadataItem,\n type MetadataItemHeader,\n type MetadataEvent,\n type PutOptions,\n type PutResult,\n type DeleteOptions,\n type DeleteResult,\n type ListFilter,\n type WatchFilter,\n type HistoryOptions,\n refKey,\n} from './index.js';\n\nexport interface LayerConfig {\n /** Stable identifier for this layer (e.g. \"user\", \"team\", \"system\"). */\n label: string;\n /** The backing repository. */\n repo: MetadataRepository;\n /** When true, this layer rejects writes (used for built-ins). */\n readOnly?: boolean;\n}\n\nexport interface LayeredRepositoryOptions {\n /**\n * Layers in priority order, highest first. The first writable layer\n * receives all writes.\n */\n layers: LayerConfig[];\n}\n\nconst tagSource = (label: string, evt: MetadataEvent): MetadataEvent => ({\n ...evt,\n source: `${label}:${evt.source}`,\n});\n\nexport class LayeredRepository implements MetadataRepository {\n private readonly layers: LayerConfig[];\n /** Index of the first writable layer; -1 if none. */\n private readonly writableIdx: number;\n\n constructor(opts: LayeredRepositoryOptions) {\n if (!opts.layers.length) {\n throw new Error('LayeredRepository requires at least one layer');\n }\n this.layers = opts.layers;\n this.writableIdx = opts.layers.findIndex((l) => !l.readOnly);\n }\n\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n for (const layer of this.layers) {\n const item = await layer.repo.get(ref);\n if (item) return item;\n }\n return null;\n }\n\n async getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null> {\n // Probe layers top→bottom; first layer that resolves the hash wins.\n // executionPinned types are durable in the storage layer\n // (`SysMetadataRepository`); FS / in-memory layers only resolve\n // hash == HEAD as a fallback.\n for (const layer of this.layers) {\n const item = await layer.repo.getByHash(ref, hash);\n if (item) return item;\n }\n return null;\n }\n\n async put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult> {\n if (this.writableIdx < 0) {\n throw new Error('LayeredRepository: no writable layer configured');\n }\n return this.layers[this.writableIdx]!.repo.put(ref, spec, opts);\n }\n\n async delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult> {\n if (this.writableIdx < 0) {\n throw new Error('LayeredRepository: no writable layer configured');\n }\n return this.layers[this.writableIdx]!.repo.delete(ref, opts);\n }\n\n async *list(filter: ListFilter): AsyncIterable<MetadataItemHeader> {\n // Yield headers from top→bottom, deduplicating by refKey.\n const seen = new Set<string>();\n const limit = filter.limit ?? Infinity;\n let yielded = 0;\n for (const layer of this.layers) {\n for await (const header of layer.repo.list(filter)) {\n const key = refKey(header.ref);\n if (seen.has(key)) continue;\n seen.add(key);\n yield header;\n if (++yielded >= limit) return;\n }\n }\n }\n\n async *history(ref: MetaRef, opts: HistoryOptions = {}): AsyncIterable<MetadataEvent> {\n // Merge histories from all layers, tagged. Order preserves each\n // layer's monotonic seq, but events across layers may interleave.\n // We collect everything then sort by ts as a best-effort total order.\n const events: MetadataEvent[] = [];\n for (const layer of this.layers) {\n for await (const evt of layer.repo.history(ref, opts)) {\n events.push(tagSource(layer.label, evt));\n }\n }\n events.sort((a, b) => (a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0));\n const limit = opts.limit ?? Infinity;\n let yielded = 0;\n for (const evt of events) {\n yield evt;\n if (++yielded >= limit) return;\n }\n }\n\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent> {\n // Fan out to all child watchers; multiplex into a single iterator.\n return multiplexWatch(this.layers, filter, since);\n }\n}\n\n/**\n * Multiplex N async iterables of MetadataEvent into one, tagging each\n * event's `source` with its layer label. Implemented as a manual\n * AsyncIterator so we can correctly cancel all child iterators when the\n * consumer breaks out.\n */\nfunction multiplexWatch(\n layers: LayerConfig[],\n filter: WatchFilter,\n since: number | undefined,\n): AsyncIterable<MetadataEvent> {\n return {\n [Symbol.asyncIterator]() {\n const children = layers.map((layer) => ({\n label: layer.label,\n iter: layer.repo.watch(filter, since)[Symbol.asyncIterator](),\n pending: null as Promise<{ label: string; result: IteratorResult<MetadataEvent> }> | null,\n done: false,\n }));\n let closed = false;\n\n const pumpAll = () => {\n for (const c of children) {\n if (c.done || c.pending) continue;\n c.pending = c.iter.next().then((result) => ({ label: c.label, result }));\n }\n };\n\n const closeAll = async () => {\n if (closed) return;\n closed = true;\n await Promise.all(\n children.map(async (c) => {\n try { await c.iter.return?.(undefined); } catch { /* ignore */ }\n }),\n );\n };\n\n return {\n async next(): Promise<IteratorResult<MetadataEvent>> {\n while (!closed) {\n pumpAll();\n const inflight = children.filter((c) => c.pending);\n if (!inflight.length) return { value: undefined, done: true };\n const winner = await Promise.race(inflight.map((c) => c.pending!));\n const target = children.find((c) => c.label === winner.label)!;\n target.pending = null;\n if (winner.result.done) {\n target.done = true;\n continue;\n }\n return {\n value: tagSource(winner.label, winner.result.value as MetadataEvent),\n done: false,\n };\n }\n return { value: undefined, done: true };\n },\n async return() {\n await closeAll();\n return { value: undefined, done: true };\n },\n async throw(err) {\n await closeAll();\n throw err;\n },\n } as AsyncIterator<MetadataEvent>;\n },\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MetaRef, k as MetadataRepository, f as MetadataItem, P as PutOptions, n as PutResult, D as DeleteOptions, a as DeleteResult, b as ListFilter, g as MetadataItemHeader, H as HistoryOptions, d as MetadataEvent, W as WatchFilter } from './repository-BL4L5ta6.cjs';
2
- export { L as LAYER_SOURCE, c as MetaRefSchema, e as MetadataEventSchema, h as MetadataItemSchema, i as MetadataOp, j as MetadataOpSchema, l as MetadataType, m as MetadataTypeSchema, r as refKey } from './repository-BL4L5ta6.cjs';
1
+ import { M as MetaRef, k as MetadataRepository, f as MetadataItem, P as PutOptions, n as PutResult, D as DeleteOptions, a as DeleteResult, b as ListFilter, g as MetadataItemHeader, H as HistoryOptions, d as MetadataEvent, W as WatchFilter } from './repository-DsvcAzZw.cjs';
2
+ export { L as LAYER_SOURCE, c as MetaRefSchema, e as MetadataEventSchema, h as MetadataItemSchema, i as MetadataOp, j as MetadataOpSchema, l as MetadataType, m as MetadataTypeSchema, r as refKey } from './repository-DsvcAzZw.cjs';
3
3
  import 'zod';
4
4
 
5
5
  /**
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MetaRef, k as MetadataRepository, f as MetadataItem, P as PutOptions, n as PutResult, D as DeleteOptions, a as DeleteResult, b as ListFilter, g as MetadataItemHeader, H as HistoryOptions, d as MetadataEvent, W as WatchFilter } from './repository-BL4L5ta6.js';
2
- export { L as LAYER_SOURCE, c as MetaRefSchema, e as MetadataEventSchema, h as MetadataItemSchema, i as MetadataOp, j as MetadataOpSchema, l as MetadataType, m as MetadataTypeSchema, r as refKey } from './repository-BL4L5ta6.js';
1
+ import { M as MetaRef, k as MetadataRepository, f as MetadataItem, P as PutOptions, n as PutResult, D as DeleteOptions, a as DeleteResult, b as ListFilter, g as MetadataItemHeader, H as HistoryOptions, d as MetadataEvent, W as WatchFilter } from './repository-DsvcAzZw.js';
2
+ export { L as LAYER_SOURCE, c as MetaRefSchema, e as MetadataEventSchema, h as MetadataItemSchema, i as MetadataOp, j as MetadataOpSchema, l as MetadataType, m as MetadataTypeSchema, r as refKey } from './repository-DsvcAzZw.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ var MetadataTypeSchema = z.enum([
24
24
  "flow",
25
25
  "workflow",
26
26
  "approval",
27
+ "job",
27
28
  "agent",
28
29
  "tool",
29
30
  "skill",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/repository.ts","../src/in-memory-repository.ts","../src/cache.ts","../src/layered-repository.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata Repository types — see ADR-0008 §2.\n *\n * All shapes are defined as Zod schemas so the same definition serves\n * runtime validation and static typing (`z.infer<typeof X>`).\n */\n\nimport { z } from 'zod';\n\n// ─── Metadata type registry ───────────────────────────────────────────\n\n/**\n * Canonical metadata type names. Aligned with the `MetadataTypeSchema`\n * enum in `@objectstack/spec/kernel/metadata-plugin.zod.ts`. New types are\n * added here in lockstep with that file.\n */\nexport const MetadataTypeSchema = z.enum([\n 'object',\n 'field',\n 'trigger',\n 'validation',\n 'hook',\n 'view',\n 'page',\n 'dashboard',\n 'app',\n 'action',\n 'flow',\n 'workflow',\n 'approval',\n 'agent',\n 'tool',\n 'skill',\n 'report',\n 'translation',\n 'role',\n 'profile',\n 'permission',\n 'policy',\n 'api',\n 'endpoint',\n 'datasource',\n 'cube',\n 'settings',\n 'router',\n 'function',\n 'service',\n 'email_template',\n]).describe('Canonical metadata type name');\n\nexport type MetadataType = z.infer<typeof MetadataTypeSchema>;\n\n// ─── MetaRef ──────────────────────────────────────────────────────────\n\n/**\n * Fully-qualified reference to a metadata item. Identity is `(org, type, name)`.\n *\n * Per ADR-0008 v2 (2026-05) the metadata layer no longer carries `project`\n * or `branch`. Project survives only as an **artifact packaging concept**\n * (the unit a CLI/CI run compiles into `dist/objectstack.json`); it does\n * not appear in the runtime customization scope. Branching belongs to Git\n * (or your VCS of choice) and never propagated cleanly into the runtime\n * model — so it has been removed entirely.\n *\n * Higher layers may default `org='system'` for built-ins.\n *\n * `version` is optional: omit to mean \"HEAD\", supply to pin.\n */\nexport const MetaRefSchema = z.object({\n org: z.string().min(1).describe('Tenant/org identifier; \"system\" for built-ins'),\n type: MetadataTypeSchema,\n name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Snake_case machine name'),\n version: z.string().optional().describe('Optional version pin (content hash); omit for HEAD'),\n});\n\nexport type MetaRef = z.infer<typeof MetaRefSchema>;\n\n/**\n * Construct a stable string key from a MetaRef (excluding `version`,\n * which is mutable). Used as cache keys and log indexes.\n */\nexport function refKey(ref: Pick<MetaRef, 'org' | 'type' | 'name'>): string {\n return `${ref.org}/${ref.type}/${ref.name}`;\n}\n\n// ─── Item & header ────────────────────────────────────────────────────\n\n/**\n * Full metadata item as stored / returned by the Repository.\n *\n * `body` is the **canonical, Zod-normalised** spec (with defaults filled\n * in). `hash` is `sha256(canonicalize(body))`. Equal hashes imply equal\n * specs.\n */\nexport const MetadataItemSchema = z.object({\n ref: MetaRefSchema,\n body: z.record(z.string(), z.unknown()).describe('Canonical Zod-normalised spec'),\n hash: z.string().regex(/^sha256:[0-9a-f]{64}$/).describe('sha256(canonicalize(body))'),\n parentHash: z.string().nullable().describe('Hash this version was derived from; null for first version'),\n authoredBy: z.string().describe('Identity of the writer (user id, \"cli\", \"ai:claude\", …)'),\n authoredAt: z.string().describe('ISO-8601 timestamp'),\n message: z.string().optional().describe('Optional commit message'),\n seq: z.number().int().nonnegative().describe('Sequence number this write produced in the org log'),\n schemaVersion: z.string().optional().describe('Zod schema version that wrote this spec (M3 codemod hook)'),\n});\n\nexport type MetadataItem = z.infer<typeof MetadataItemSchema>;\n\n/** Lightweight header for listing — `body` omitted. */\nexport type MetadataItemHeader = Omit<MetadataItem, 'body'>;\n\n// ─── Change log event ─────────────────────────────────────────────────\n\nexport const MetadataOpSchema = z.enum(['create', 'update', 'delete', 'rename']);\nexport type MetadataOp = z.infer<typeof MetadataOpSchema>;\n\n/**\n * The single event payload broadcast by the change log. ADR-0008 §2.4.\n *\n * For `rename`, `previousName` carries the old machine name. For\n * `delete`, `hash` is null. The payload is intentionally small —\n * consumers re-fetch via the cache when they need the full body.\n */\nexport const MetadataEventSchema = z.object({\n seq: z.number().int().nonnegative(),\n op: MetadataOpSchema,\n ref: MetaRefSchema,\n hash: z.string().nullable(),\n parentHash: z.string().nullable(),\n previousName: z.string().optional().describe('Set on op=\"rename\"'),\n actor: z.string(),\n message: z.string().optional(),\n ts: z.string(),\n source: z.string().describe('Origin label: \"fs\", \"studio\", \"rest\", \"ai\", \"git-import\", …'),\n});\n\nexport type MetadataEvent = z.infer<typeof MetadataEventSchema>;\n\n// ─── Operation options ────────────────────────────────────────────────\n\nexport interface PutOptions {\n /**\n * Hash this writer believed was at HEAD. `null` means \"creating, expect\n * absence\". A mismatch throws ConflictError.\n */\n parentVersion: string | null;\n /** Identity of the writer; mirrored to MetadataEvent.actor. */\n actor: string;\n /** Optional human-readable commit message. */\n message?: string;\n /** Optional label for the change log \"source\" column. */\n source?: string;\n}\n\nexport interface PutResult {\n /** New content hash assigned to the spec. */\n version: string;\n /** Sequence number of the emitted MetadataEvent. */\n seq: number;\n /** The committed item (canonicalised). */\n item: MetadataItem;\n}\n\nexport interface DeleteOptions {\n parentVersion: string;\n actor: string;\n message?: string;\n source?: string;\n}\n\nexport interface DeleteResult {\n seq: number;\n}\n\nexport interface ListFilter {\n org?: string;\n type?: MetadataType;\n /** Substring match on `name`; case-sensitive. */\n nameContains?: string;\n /** Pagination cursor; opaque string from a previous response. */\n cursor?: string;\n /** Page size; implementations may clamp. */\n limit?: number;\n}\n\nexport interface WatchFilter {\n org?: string;\n type?: MetadataType;\n /** When omitted, match all names within the scope. */\n name?: string;\n}\n\nexport interface HistoryOptions {\n /** Lower bound (exclusive) for pagination. */\n sinceSeq?: number;\n limit?: number;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The `MetadataRepository` interface — single point of pluggability for\n * the metadata storage backend. See ADR-0008 §2.6.\n *\n * Implementations:\n *\n * - `InMemoryRepository` (this package, for tests & edge)\n * - `FileSystemRepository` (`@objectstack/metadata`)\n * - `LayeredRepository` (`@objectstack/metadata`)\n * - `PostgresRepository` (`@objectstack/metadata-postgres`, M1)\n *\n * Implementation contract — what every backend MUST guarantee:\n *\n * 1. **Atomic put.** A successful `put()` either fully applies (item\n * visible to subsequent `get` AND an event present in the log) or\n * does not apply at all. No half-states.\n * 2. **Monotonic seq per org.** `seq` is strictly increasing within\n * `org`. Different orgs have independent sequences. (Repositories\n * scoped to a single org may treat the entire repo as one log.)\n * 3. **Optimistic locking.** `put` and `delete` throw `ConflictError`\n * when `parentVersion` does not match the current HEAD.\n * 4. **Canonical hashing.** `item.hash === hashSpec(item.body)` — always.\n * 5. **Event ordering.** Subscribers to `watch()` receive events in\n * monotonically-increasing `seq` order with no gaps.\n * 6. **Resumability.** `watch(_, since)` MUST replay all events with\n * `seq > since` before delivering live events.\n * 7. **Tombstones, not holes.** `delete` produces a `delete` event;\n * `get` returns null but `history` still shows the lineage.\n */\n\nimport type {\n MetaRef,\n MetadataItem,\n MetadataItemHeader,\n MetadataEvent,\n PutOptions,\n PutResult,\n DeleteOptions,\n DeleteResult,\n ListFilter,\n WatchFilter,\n HistoryOptions,\n} from './types.js';\n\nexport interface MetadataRepository {\n /** Read HEAD or a pinned version. Returns null if absent. */\n get(ref: MetaRef): Promise<MetadataItem | null>;\n\n /**\n * Resolve a historical version by content hash (ADR-0009).\n *\n * Returns the `MetadataItem` whose canonical sha256 equals `hash`\n * for the given ref, or `null` if no such version is recorded.\n *\n * Implementations MUST search history (not just HEAD) so that\n * `executionPinned` types remain resolvable through definition\n * upgrades. For non-`executionPinned` types, implementations MAY\n * return `null` if they have GC'd the corresponding history row.\n */\n getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null>;\n\n /**\n * Write a new version. Atomic.\n * @throws ConflictError if `parentVersion` does not match HEAD.\n * @throws SchemaValidationError if `spec` fails Zod normalisation.\n */\n put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult>;\n\n /**\n * Soft-delete (tombstone). `parentVersion` is required.\n * @throws ConflictError on parent mismatch.\n */\n delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult>;\n\n /** Enumerate items matching a filter. Implementations may stream. */\n list(filter: ListFilter): AsyncIterable<MetadataItemHeader>;\n\n /** Per-item history; events in monotonic `seq` order. */\n history(ref: MetaRef, opts?: HistoryOptions): AsyncIterable<MetadataEvent>;\n\n /**\n * Live event stream. The iterator MUST:\n *\n * - Replay all events with `seq > since` before yielding any new event.\n * - Stay open until the consumer breaks the loop.\n * - Survive transient backend disconnects (implementation's choice\n * how to resume — Postgres LISTEN reconnect, JSONL tail, etc.).\n */\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent>;\n}\n\n/**\n * Sentinel symbol used by `LayeredRepository` (M0 PR-5) to label which\n * underlying layer emitted an event. Defined here so the contract is\n * shared.\n */\nexport const LAYER_SOURCE = Symbol.for('objectstack.metadata.layer-source');\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `InMemoryRepository` — reference implementation of `MetadataRepository`\n * backed by plain JS Maps. Used by:\n *\n * - tests (parameterized contract-test suite)\n * - edge / serverless runtimes (no FS, no DB)\n * - `LayeredRepository` fallbacks\n *\n * State model\n * ───────────\n * items : refKey → MetadataItem (current head)\n * logs : org → MetadataEvent[] (append-only, monotonic per org)\n * seqs : org → number\n *\n * `watch()` is implemented over a simple subscriber list. Each subscriber\n * receives a deep-copy of the event so they cannot mutate the log.\n */\n\nimport {\n type MetaRef,\n type MetadataItem,\n type MetadataItemHeader,\n type MetadataEvent,\n type PutOptions,\n type PutResult,\n type DeleteOptions,\n type DeleteResult,\n type ListFilter,\n type WatchFilter,\n type HistoryOptions,\n type MetadataType,\n refKey,\n} from './types.js';\nimport { hashSpec } from './canonicalize.js';\nimport { ConflictError } from './errors.js';\nimport type { MetadataRepository } from './repository.js';\n\nconst orgKey = (ref: Pick<MetaRef, 'org'>): string => ref.org;\n\nconst matchesFilter = (\n ref: MetaRef,\n filter: { org?: string; type?: MetadataType; name?: string },\n): boolean => {\n if (filter.org && filter.org !== ref.org) return false;\n if (filter.type && filter.type !== ref.type) return false;\n if (filter.name && filter.name !== ref.name) return false;\n return true;\n};\n\ninterface Subscriber {\n filter: WatchFilter;\n push: (evt: MetadataEvent) => void;\n closed: boolean;\n}\n\nexport interface InMemoryRepositoryOptions {\n /** Optional clock injection for deterministic tests. Default: Date.now. */\n now?: () => Date;\n}\n\nexport class InMemoryRepository implements MetadataRepository {\n private readonly items = new Map<string, MetadataItem>();\n /** Per-org event log. */\n private readonly logs = new Map<string, MetadataEvent[]>();\n /** Next seq per org. */\n private readonly seqs = new Map<string, number>();\n private readonly subscribers = new Set<Subscriber>();\n private readonly now: () => Date;\n\n constructor(opts: InMemoryRepositoryOptions = {}) {\n this.now = opts.now ?? (() => new Date());\n }\n\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n const item = this.items.get(refKey(ref));\n if (!item) return null;\n if (ref.version && item.hash !== ref.version) {\n // History lookup is not supported in this minimal impl — only HEAD.\n // A future revision may walk the log to reconstruct an old version.\n return null;\n }\n return clone(item);\n }\n\n async getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null> {\n // InMemoryRepository keeps only HEAD bodies; historical bodies are\n // not retained. Resolve only if the requested hash IS HEAD.\n const item = this.items.get(refKey(ref));\n if (!item || item.hash !== hash) return null;\n return clone(item);\n }\n\n async put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult> {\n const key = refKey(ref);\n const current = this.items.get(key);\n const currentHead = current?.hash ?? null;\n\n if ((opts.parentVersion ?? null) !== currentHead) {\n throw new ConflictError(ref, opts.parentVersion ?? null, currentHead);\n }\n\n const hash = hashSpec(spec);\n\n // No-op write — same content. Still consumes nothing; no event emitted.\n if (current && current.hash === hash) {\n return { version: hash, seq: current.seq, item: clone(current) };\n }\n\n const seq = this.bumpSeq(ref);\n const ts = this.now().toISOString();\n\n const item: MetadataItem = {\n ref: { ...ref, version: undefined },\n body: clonePlain(spec) as Record<string, unknown>,\n hash,\n parentHash: currentHead,\n authoredBy: opts.actor,\n authoredAt: ts,\n message: opts.message,\n seq,\n };\n\n this.items.set(key, item);\n\n const evt: MetadataEvent = {\n seq,\n op: current ? 'update' : 'create',\n ref: { ...ref, version: undefined },\n hash,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'in-memory',\n };\n this.appendEvent(ref, evt);\n\n return { version: hash, seq, item: clone(item) };\n }\n\n async delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult> {\n const key = refKey(ref);\n const current = this.items.get(key);\n const currentHead = current?.hash ?? null;\n if (currentHead !== opts.parentVersion) {\n throw new ConflictError(ref, opts.parentVersion, currentHead);\n }\n\n this.items.delete(key);\n const seq = this.bumpSeq(ref);\n const ts = this.now().toISOString();\n const evt: MetadataEvent = {\n seq,\n op: 'delete',\n ref: { ...ref, version: undefined },\n hash: null,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'in-memory',\n };\n this.appendEvent(ref, evt);\n return { seq };\n }\n\n async *list(filter: ListFilter): AsyncIterable<MetadataItemHeader> {\n const limit = filter.limit ?? Infinity;\n let yielded = 0;\n for (const item of this.items.values()) {\n if (!matchesFilter(item.ref, filter)) continue;\n if (filter.nameContains && !item.ref.name.includes(filter.nameContains)) continue;\n const { body, ...header } = item;\n void body;\n yield clone(header) as MetadataItemHeader;\n if (++yielded >= limit) return;\n }\n }\n\n async *history(ref: MetaRef, opts: HistoryOptions = {}): AsyncIterable<MetadataEvent> {\n const log = this.logs.get(orgKey(ref)) ?? [];\n const since = opts.sinceSeq ?? -1;\n const limit = opts.limit ?? Infinity;\n let yielded = 0;\n for (const evt of log) {\n if (evt.seq <= since) continue;\n if (evt.ref.type !== ref.type || evt.ref.name !== ref.name) continue;\n yield clone(evt);\n if (++yielded >= limit) return;\n }\n }\n\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent> {\n // Implemented as a manual async iterator (not a generator) so we can\n // implement `return()` to unblock a pending wait. Async generators\n // do NOT run their `finally` block when paused on an unresolved\n // `await` — see https://github.com/tc39/proposal-async-iteration.\n const queue: MetadataEvent[] = [];\n let waiter: ((evt: IteratorResult<MetadataEvent>) => void) | null = null;\n let closed = false;\n const delivered = new Set<string>();\n const evtKey = (e: MetadataEvent) => `${orgKey(e.ref)}#${e.seq}`;\n\n const subscriber: Subscriber = {\n filter,\n closed: false,\n push: (evt) => {\n if (subscriber.closed) return;\n if (waiter) {\n const k = evtKey(evt);\n if (delivered.has(k)) return;\n delivered.add(k);\n const w = waiter;\n waiter = null;\n w({ value: clone(evt), done: false });\n } else {\n queue.push(evt);\n }\n },\n };\n\n // Build the replay buffer BEFORE registering the subscriber to avoid\n // a race window? No — we register first then build replay, dedup'ing\n // by seq when we hand off to live so any event that arrives during\n // replay isn't double-delivered.\n this.subscribers.add(subscriber);\n\n const replay: MetadataEvent[] = [];\n for (const ok of this.orgKeysMatching(filter)) {\n const log = this.logs.get(ok) ?? [];\n for (const evt of log) {\n if (typeof since === 'number' && evt.seq <= since) continue;\n if (!matchesFilter(evt.ref, filter)) continue;\n replay.push(evt);\n }\n }\n replay.sort((a, b) => a.seq - b.seq);\n let replayIdx = 0;\n\n const drainQueueOrReplay = (): IteratorResult<MetadataEvent> | null => {\n while (replayIdx < replay.length) {\n const evt = replay[replayIdx++]!;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n while (queue.length > 0) {\n const evt = queue.shift()!;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n return null;\n };\n\n const close = (): IteratorResult<MetadataEvent> => {\n if (!closed) {\n closed = true;\n subscriber.closed = true;\n this.subscribers.delete(subscriber);\n if (waiter) {\n const w = waiter;\n waiter = null;\n w({ value: undefined, done: true });\n }\n }\n return { value: undefined, done: true };\n };\n\n const iterator: AsyncIterator<MetadataEvent> = {\n next: () => {\n if (closed) return Promise.resolve({ value: undefined, done: true });\n const immediate = drainQueueOrReplay();\n if (immediate) return Promise.resolve(immediate);\n return new Promise<IteratorResult<MetadataEvent>>((resolve) => {\n waiter = resolve;\n });\n },\n return: () => Promise.resolve(close()),\n throw: (err) => {\n close();\n return Promise.reject(err);\n },\n };\n\n return {\n [Symbol.asyncIterator]: () => iterator,\n };\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private bumpSeq(ref: Pick<MetaRef, 'org'>): number {\n const ok = orgKey(ref);\n const next = (this.seqs.get(ok) ?? 0) + 1;\n this.seqs.set(ok, next);\n return next;\n }\n\n private appendEvent(ref: Pick<MetaRef, 'org'>, evt: MetadataEvent): void {\n const ok = orgKey(ref);\n const log = this.logs.get(ok) ?? [];\n log.push(evt);\n this.logs.set(ok, log);\n // Broadcast\n for (const sub of this.subscribers) {\n if (sub.closed) continue;\n if (!matchesFilter(evt.ref, sub.filter)) continue;\n sub.push(evt);\n }\n }\n\n private orgKeysMatching(filter: WatchFilter): string[] {\n const keys: string[] = [];\n for (const ok of this.logs.keys()) {\n if (filter.org && filter.org !== ok) continue;\n keys.push(ok);\n }\n return keys;\n }\n}\n\n/** Deep clone via JSON; safe for `MetadataItem` / `MetadataEvent`. */\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\n/** Clone a value that the caller passed in; rejects functions/symbols. */\nfunction clonePlain(value: unknown): unknown {\n return JSON.parse(JSON.stringify(value));\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `MetadataCache` — bounded, event-invalidated LRU sitting in front of a\n * `MetadataRepository`. See ADR-0008 §2.5.\n *\n * Design contract\n * ───────────────\n *\n * 1. **Lazy fill.** Cache entries are only created on first read miss.\n * No bulk preload — that would defeat the whole point of being\n * bounded.\n * 2. **Event-driven invalidation.** The cache subscribes to\n * `repo.watch({...})` and drops or replaces affected entries\n * whenever the repository emits an event. Stale reads are bounded\n * by the event-propagation latency of the underlying repo.\n * 3. **Bounded.** Both `maxEntries` and `maxBytes` are enforced; LRU\n * eviction happens on `set()` when either limit is exceeded.\n * 4. **Coherent under races.** Concurrent `get()`s for the same key\n * coalesce onto a single backend fetch (the \"thundering herd\"\n * fix). If an invalidation event arrives during an in-flight\n * fetch, the resulting value is discarded — the next read fetches\n * fresh.\n * 5. **Negative caching.** A miss (repo returned `null`) is also\n * cached, with a smaller TTL semantics — it stays until an event\n * for that ref arrives. This makes \"does X exist?\" cheap during\n * tight loops without compromising correctness.\n */\n\nimport type { MetaRef, MetadataItem, MetadataEvent, WatchFilter } from './types.js';\nimport { refKey } from './types.js';\nimport type { MetadataRepository } from './repository.js';\n\nexport interface MetadataCacheOptions {\n /** Maximum number of entries to keep. Default: 1024. */\n maxEntries?: number;\n /**\n * Maximum approximate body size in bytes. Default: 8 MiB. Each entry's\n * size is estimated from `JSON.stringify(item.body).length`.\n */\n maxBytes?: number;\n /**\n * Watch filter. Only events matching this filter invalidate cache\n * entries. Default: no filter (all events).\n */\n watchFilter?: WatchFilter;\n}\n\ninterface CacheEntry {\n /** Null = negative cache (item is known absent). */\n item: MetadataItem | null;\n size: number;\n /** Hit count, just for diagnostics. */\n hits: number;\n}\n\nexport interface CacheStats {\n entries: number;\n bytes: number;\n hits: number;\n misses: number;\n invalidations: number;\n /** Reads that arrived while a fetch was already in-flight for the same key. */\n coalesced: number;\n}\n\nexport class MetadataCache {\n private readonly repo: MetadataRepository;\n private readonly maxEntries: number;\n private readonly maxBytes: number;\n private readonly watchFilter: WatchFilter;\n\n /** LRU is implemented via insertion-order Map; touch on get/set. */\n private readonly entries = new Map<string, CacheEntry>();\n private bytes = 0;\n\n /** De-duplicate concurrent fetches for the same key. */\n private readonly inflight = new Map<string, Promise<MetadataItem | null>>();\n /**\n * Generation counter incremented on every invalidation. Each in-flight\n * fetch captures the gen at start; if the gen changes before it\n * resolves, the result is NOT cached.\n */\n private readonly fetchGen = new Map<string, number>();\n\n private watchIterator: AsyncIterator<MetadataEvent> | null = null;\n private watchClosed = false;\n private watchLoop: Promise<void> | null = null;\n\n private readonly stats: CacheStats = {\n entries: 0,\n bytes: 0,\n hits: 0,\n misses: 0,\n invalidations: 0,\n coalesced: 0,\n };\n\n constructor(repo: MetadataRepository, opts: MetadataCacheOptions = {}) {\n this.repo = repo;\n this.maxEntries = opts.maxEntries ?? 1024;\n this.maxBytes = opts.maxBytes ?? 8 * 1024 * 1024;\n this.watchFilter = opts.watchFilter ?? {};\n }\n\n /**\n * Start the background watch subscription. Idempotent; safe to call\n * multiple times. Caller is responsible for calling `close()` when\n * the cache is no longer needed.\n */\n start(): void {\n if (this.watchIterator || this.watchClosed) return;\n const iter = this.repo.watch(this.watchFilter)[Symbol.asyncIterator]();\n this.watchIterator = iter;\n this.watchLoop = (async () => {\n try {\n while (!this.watchClosed) {\n const { value, done } = await iter.next();\n if (done) return;\n this.applyEvent(value);\n }\n } catch {\n // Repository tore down its event stream; cache is left in a\n // best-effort state. Consumers can detect this via stats or by\n // restarting the cache.\n }\n })();\n }\n\n /** Tear down the watch subscription and clear in-flight tracking. */\n async close(): Promise<void> {\n this.watchClosed = true;\n if (this.watchIterator?.return) {\n try {\n await this.watchIterator.return(undefined);\n } catch {\n // ignore\n }\n }\n this.watchIterator = null;\n if (this.watchLoop) {\n try {\n await this.watchLoop;\n } catch {\n // ignore\n }\n this.watchLoop = null;\n }\n this.inflight.clear();\n }\n\n /** Read with cache. Coalesces concurrent reads for the same key. */\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n const key = refKey(ref);\n const cached = this.entries.get(key);\n if (cached) {\n // Touch (LRU bump).\n this.entries.delete(key);\n this.entries.set(key, cached);\n cached.hits += 1;\n this.stats.hits += 1;\n return cached.item ? clone(cached.item) : null;\n }\n\n const existing = this.inflight.get(key);\n if (existing) {\n this.stats.coalesced += 1;\n const item = await existing;\n return item ? clone(item) : null;\n }\n\n this.stats.misses += 1;\n const gen = (this.fetchGen.get(key) ?? 0);\n const promise = this.repo\n .get(ref)\n .then((item) => {\n // If the cache was invalidated for this key during the fetch,\n // skip caching the (possibly stale) result.\n if ((this.fetchGen.get(key) ?? 0) === gen) {\n this.cacheSet(key, item);\n }\n return item;\n })\n .finally(() => {\n this.inflight.delete(key);\n });\n\n this.inflight.set(key, promise);\n const item = await promise;\n return item ? clone(item) : null;\n }\n\n /** Drop a single entry by ref. */\n invalidate(ref: MetaRef): void {\n const key = refKey(ref);\n this.bumpGen(key);\n const removed = this.entries.get(key);\n if (removed) {\n this.bytes -= removed.size;\n this.entries.delete(key);\n }\n this.stats.invalidations += 1;\n }\n\n /** Drop the entire cache (e.g. on a reset). */\n clear(): void {\n this.entries.clear();\n this.bytes = 0;\n // Bump every in-flight key so we don't accidentally cache stale data.\n for (const key of this.inflight.keys()) this.bumpGen(key);\n }\n\n getStats(): Readonly<CacheStats> {\n return {\n ...this.stats,\n entries: this.entries.size,\n bytes: this.bytes,\n };\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private applyEvent(evt: MetadataEvent): void {\n // Any op (create/update/delete/rename) invalidates the affected key.\n // We don't try to be clever (e.g. preloading the new value) — the\n // next reader will pull the fresh body through `get`.\n const ref = evt.ref;\n this.invalidate(ref);\n if (evt.op === 'rename' && evt.previousName) {\n this.invalidate({ ...ref, name: evt.previousName });\n }\n }\n\n private cacheSet(key: string, item: MetadataItem | null): void {\n const size = item ? estimateSize(item) : 0;\n const existing = this.entries.get(key);\n if (existing) this.bytes -= existing.size;\n this.entries.set(key, { item, size, hits: 0 });\n this.bytes += size;\n this.evictIfNeeded();\n }\n\n private evictIfNeeded(): void {\n // Map preserves insertion order; oldest first. Touch on `get` moves\n // entries to the end → eviction from the front evicts the LRU.\n while (\n this.entries.size > this.maxEntries ||\n this.bytes > this.maxBytes\n ) {\n const first = this.entries.keys().next();\n if (first.done) break;\n const key = first.value;\n const entry = this.entries.get(key)!;\n this.bytes -= entry.size;\n this.entries.delete(key);\n }\n }\n\n private bumpGen(key: string): void {\n this.fetchGen.set(key, (this.fetchGen.get(key) ?? 0) + 1);\n }\n}\n\nfunction estimateSize(item: MetadataItem): number {\n // Rough estimate — JS strings are 2 bytes per char, but JSON.stringify\n // length is a fine proxy. Add a fixed overhead for the wrapper fields.\n try {\n return JSON.stringify(item.body).length * 2 + 256;\n } catch {\n return 1024; // fallback for unstringifiable bodies\n }\n}\n\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `LayeredRepository` — composes N child `MetadataRepository`s into a\n * single read-through stack. See ADR-0008 §10 PR-5.\n *\n * Read semantics\n * ──────────────\n * - `get(ref)` walks the layers top-to-bottom; first non-null wins.\n * - `list()` deduplicates by `refKey(ref)`, preferring the top layer.\n * - `history()` and `watch()` merge events from all layers, each\n * tagged with the source layer label in `evt.source`\n * (`<label>:<original-source>`).\n *\n * Write semantics\n * ───────────────\n * - `put` / `delete` are routed to the topmost writable layer.\n * - A layer is writable unless `readOnly: true` in its config.\n * - The write target's existing HEAD is used for the parent check —\n * i.e. if a key exists only in a lower layer, writing produces a\n * create on the top layer (an \"overlay\" of the lower one).\n *\n * Event tagging\n * ─────────────\n * - Each emitted event carries `source` rewritten as\n * `<layer.label>:<source>` so subscribers can tell which layer\n * produced the change. Original event content is otherwise\n * unchanged.\n *\n * This implementation never re-orders events relative to seq within a\n * single layer, but seqs across layers may interleave. Downstream\n * consumers (Cache, SchemaRegistry) only need monotonicity within a\n * (layer, branch) tuple — they treat each (layer.label, ref) as a\n * separate stream.\n */\n\nimport {\n type MetadataRepository,\n type MetaRef,\n type MetadataItem,\n type MetadataItemHeader,\n type MetadataEvent,\n type PutOptions,\n type PutResult,\n type DeleteOptions,\n type DeleteResult,\n type ListFilter,\n type WatchFilter,\n type HistoryOptions,\n refKey,\n} from './index.js';\n\nexport interface LayerConfig {\n /** Stable identifier for this layer (e.g. \"user\", \"team\", \"system\"). */\n label: string;\n /** The backing repository. */\n repo: MetadataRepository;\n /** When true, this layer rejects writes (used for built-ins). */\n readOnly?: boolean;\n}\n\nexport interface LayeredRepositoryOptions {\n /**\n * Layers in priority order, highest first. The first writable layer\n * receives all writes.\n */\n layers: LayerConfig[];\n}\n\nconst tagSource = (label: string, evt: MetadataEvent): MetadataEvent => ({\n ...evt,\n source: `${label}:${evt.source}`,\n});\n\nexport class LayeredRepository implements MetadataRepository {\n private readonly layers: LayerConfig[];\n /** Index of the first writable layer; -1 if none. */\n private readonly writableIdx: number;\n\n constructor(opts: LayeredRepositoryOptions) {\n if (!opts.layers.length) {\n throw new Error('LayeredRepository requires at least one layer');\n }\n this.layers = opts.layers;\n this.writableIdx = opts.layers.findIndex((l) => !l.readOnly);\n }\n\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n for (const layer of this.layers) {\n const item = await layer.repo.get(ref);\n if (item) return item;\n }\n return null;\n }\n\n async getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null> {\n // Probe layers top→bottom; first layer that resolves the hash wins.\n // executionPinned types are durable in the storage layer\n // (`SysMetadataRepository`); FS / in-memory layers only resolve\n // hash == HEAD as a fallback.\n for (const layer of this.layers) {\n const item = await layer.repo.getByHash(ref, hash);\n if (item) return item;\n }\n return null;\n }\n\n async put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult> {\n if (this.writableIdx < 0) {\n throw new Error('LayeredRepository: no writable layer configured');\n }\n return this.layers[this.writableIdx]!.repo.put(ref, spec, opts);\n }\n\n async delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult> {\n if (this.writableIdx < 0) {\n throw new Error('LayeredRepository: no writable layer configured');\n }\n return this.layers[this.writableIdx]!.repo.delete(ref, opts);\n }\n\n async *list(filter: ListFilter): AsyncIterable<MetadataItemHeader> {\n // Yield headers from top→bottom, deduplicating by refKey.\n const seen = new Set<string>();\n const limit = filter.limit ?? Infinity;\n let yielded = 0;\n for (const layer of this.layers) {\n for await (const header of layer.repo.list(filter)) {\n const key = refKey(header.ref);\n if (seen.has(key)) continue;\n seen.add(key);\n yield header;\n if (++yielded >= limit) return;\n }\n }\n }\n\n async *history(ref: MetaRef, opts: HistoryOptions = {}): AsyncIterable<MetadataEvent> {\n // Merge histories from all layers, tagged. Order preserves each\n // layer's monotonic seq, but events across layers may interleave.\n // We collect everything then sort by ts as a best-effort total order.\n const events: MetadataEvent[] = [];\n for (const layer of this.layers) {\n for await (const evt of layer.repo.history(ref, opts)) {\n events.push(tagSource(layer.label, evt));\n }\n }\n events.sort((a, b) => (a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0));\n const limit = opts.limit ?? Infinity;\n let yielded = 0;\n for (const evt of events) {\n yield evt;\n if (++yielded >= limit) return;\n }\n }\n\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent> {\n // Fan out to all child watchers; multiplex into a single iterator.\n return multiplexWatch(this.layers, filter, since);\n }\n}\n\n/**\n * Multiplex N async iterables of MetadataEvent into one, tagging each\n * event's `source` with its layer label. Implemented as a manual\n * AsyncIterator so we can correctly cancel all child iterators when the\n * consumer breaks out.\n */\nfunction multiplexWatch(\n layers: LayerConfig[],\n filter: WatchFilter,\n since: number | undefined,\n): AsyncIterable<MetadataEvent> {\n return {\n [Symbol.asyncIterator]() {\n const children = layers.map((layer) => ({\n label: layer.label,\n iter: layer.repo.watch(filter, since)[Symbol.asyncIterator](),\n pending: null as Promise<{ label: string; result: IteratorResult<MetadataEvent> }> | null,\n done: false,\n }));\n let closed = false;\n\n const pumpAll = () => {\n for (const c of children) {\n if (c.done || c.pending) continue;\n c.pending = c.iter.next().then((result) => ({ label: c.label, result }));\n }\n };\n\n const closeAll = async () => {\n if (closed) return;\n closed = true;\n await Promise.all(\n children.map(async (c) => {\n try { await c.iter.return?.(undefined); } catch { /* ignore */ }\n }),\n );\n };\n\n return {\n async next(): Promise<IteratorResult<MetadataEvent>> {\n while (!closed) {\n pumpAll();\n const inflight = children.filter((c) => c.pending);\n if (!inflight.length) return { value: undefined, done: true };\n const winner = await Promise.race(inflight.map((c) => c.pending!));\n const target = children.find((c) => c.label === winner.label)!;\n target.pending = null;\n if (winner.result.done) {\n target.done = true;\n continue;\n }\n return {\n value: tagSource(winner.label, winner.result.value as MetadataEvent),\n done: false,\n };\n }\n return { value: undefined, done: true };\n },\n async return() {\n await closeAll();\n return { value: undefined, done: true };\n },\n async throw(err) {\n await closeAll();\n throw err;\n },\n } as AsyncIterator<MetadataEvent>;\n },\n };\n}\n"],"mappings":"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","names":["clone","item"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/repository.ts","../src/in-memory-repository.ts","../src/cache.ts","../src/layered-repository.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata Repository types — see ADR-0008 §2.\n *\n * All shapes are defined as Zod schemas so the same definition serves\n * runtime validation and static typing (`z.infer<typeof X>`).\n */\n\nimport { z } from 'zod';\n\n// ─── Metadata type registry ───────────────────────────────────────────\n\n/**\n * Canonical metadata type names. Aligned with the `MetadataTypeSchema`\n * enum in `@objectstack/spec/kernel/metadata-plugin.zod.ts`. New types are\n * added here in lockstep with that file.\n */\nexport const MetadataTypeSchema = z.enum([\n 'object',\n 'field',\n 'trigger',\n 'validation',\n 'hook',\n 'view',\n 'page',\n 'dashboard',\n 'app',\n 'action',\n 'flow',\n 'workflow',\n 'approval',\n 'job',\n 'agent',\n 'tool',\n 'skill',\n 'report',\n 'translation',\n 'role',\n 'profile',\n 'permission',\n 'policy',\n 'api',\n 'endpoint',\n 'datasource',\n 'cube',\n 'settings',\n 'router',\n 'function',\n 'service',\n 'email_template',\n]).describe('Canonical metadata type name');\n\nexport type MetadataType = z.infer<typeof MetadataTypeSchema>;\n\n// ─── MetaRef ──────────────────────────────────────────────────────────\n\n/**\n * Fully-qualified reference to a metadata item. Identity is `(org, type, name)`.\n *\n * Per ADR-0008 v2 (2026-05) the metadata layer no longer carries `project`\n * or `branch`. Project survives only as an **artifact packaging concept**\n * (the unit a CLI/CI run compiles into `dist/objectstack.json`); it does\n * not appear in the runtime customization scope. Branching belongs to Git\n * (or your VCS of choice) and never propagated cleanly into the runtime\n * model — so it has been removed entirely.\n *\n * Higher layers may default `org='system'` for built-ins.\n *\n * `version` is optional: omit to mean \"HEAD\", supply to pin.\n */\nexport const MetaRefSchema = z.object({\n org: z.string().min(1).describe('Tenant/org identifier; \"system\" for built-ins'),\n type: MetadataTypeSchema,\n name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Snake_case machine name'),\n version: z.string().optional().describe('Optional version pin (content hash); omit for HEAD'),\n});\n\nexport type MetaRef = z.infer<typeof MetaRefSchema>;\n\n/**\n * Construct a stable string key from a MetaRef (excluding `version`,\n * which is mutable). Used as cache keys and log indexes.\n */\nexport function refKey(ref: Pick<MetaRef, 'org' | 'type' | 'name'>): string {\n return `${ref.org}/${ref.type}/${ref.name}`;\n}\n\n// ─── Item & header ────────────────────────────────────────────────────\n\n/**\n * Full metadata item as stored / returned by the Repository.\n *\n * `body` is the **canonical, Zod-normalised** spec (with defaults filled\n * in). `hash` is `sha256(canonicalize(body))`. Equal hashes imply equal\n * specs.\n */\nexport const MetadataItemSchema = z.object({\n ref: MetaRefSchema,\n body: z.record(z.string(), z.unknown()).describe('Canonical Zod-normalised spec'),\n hash: z.string().regex(/^sha256:[0-9a-f]{64}$/).describe('sha256(canonicalize(body))'),\n parentHash: z.string().nullable().describe('Hash this version was derived from; null for first version'),\n authoredBy: z.string().describe('Identity of the writer (user id, \"cli\", \"ai:claude\", …)'),\n authoredAt: z.string().describe('ISO-8601 timestamp'),\n message: z.string().optional().describe('Optional commit message'),\n seq: z.number().int().nonnegative().describe('Sequence number this write produced in the org log'),\n schemaVersion: z.string().optional().describe('Zod schema version that wrote this spec (M3 codemod hook)'),\n});\n\nexport type MetadataItem = z.infer<typeof MetadataItemSchema>;\n\n/** Lightweight header for listing — `body` omitted. */\nexport type MetadataItemHeader = Omit<MetadataItem, 'body'>;\n\n// ─── Change log event ─────────────────────────────────────────────────\n\nexport const MetadataOpSchema = z.enum(['create', 'update', 'delete', 'rename']);\nexport type MetadataOp = z.infer<typeof MetadataOpSchema>;\n\n/**\n * The single event payload broadcast by the change log. ADR-0008 §2.4.\n *\n * For `rename`, `previousName` carries the old machine name. For\n * `delete`, `hash` is null. The payload is intentionally small —\n * consumers re-fetch via the cache when they need the full body.\n */\nexport const MetadataEventSchema = z.object({\n seq: z.number().int().nonnegative(),\n op: MetadataOpSchema,\n ref: MetaRefSchema,\n hash: z.string().nullable(),\n parentHash: z.string().nullable(),\n previousName: z.string().optional().describe('Set on op=\"rename\"'),\n actor: z.string(),\n message: z.string().optional(),\n ts: z.string(),\n source: z.string().describe('Origin label: \"fs\", \"studio\", \"rest\", \"ai\", \"git-import\", …'),\n});\n\nexport type MetadataEvent = z.infer<typeof MetadataEventSchema>;\n\n// ─── Operation options ────────────────────────────────────────────────\n\nexport interface PutOptions {\n /**\n * Hash this writer believed was at HEAD. `null` means \"creating, expect\n * absence\". A mismatch throws ConflictError.\n */\n parentVersion: string | null;\n /** Identity of the writer; mirrored to MetadataEvent.actor. */\n actor: string;\n /** Optional human-readable commit message. */\n message?: string;\n /** Optional label for the change log \"source\" column. */\n source?: string;\n}\n\nexport interface PutResult {\n /** New content hash assigned to the spec. */\n version: string;\n /** Sequence number of the emitted MetadataEvent. */\n seq: number;\n /** The committed item (canonicalised). */\n item: MetadataItem;\n}\n\nexport interface DeleteOptions {\n parentVersion: string;\n actor: string;\n message?: string;\n source?: string;\n}\n\nexport interface DeleteResult {\n seq: number;\n}\n\nexport interface ListFilter {\n org?: string;\n type?: MetadataType;\n /** Substring match on `name`; case-sensitive. */\n nameContains?: string;\n /** Pagination cursor; opaque string from a previous response. */\n cursor?: string;\n /** Page size; implementations may clamp. */\n limit?: number;\n}\n\nexport interface WatchFilter {\n org?: string;\n type?: MetadataType;\n /** When omitted, match all names within the scope. */\n name?: string;\n}\n\nexport interface HistoryOptions {\n /** Lower bound (exclusive) for pagination. */\n sinceSeq?: number;\n limit?: number;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The `MetadataRepository` interface — single point of pluggability for\n * the metadata storage backend. See ADR-0008 §2.6.\n *\n * Implementations:\n *\n * - `InMemoryRepository` (this package, for tests & edge)\n * - `FileSystemRepository` (`@objectstack/metadata`)\n * - `LayeredRepository` (`@objectstack/metadata`)\n * - `PostgresRepository` (`@objectstack/metadata-postgres`, M1)\n *\n * Implementation contract — what every backend MUST guarantee:\n *\n * 1. **Atomic put.** A successful `put()` either fully applies (item\n * visible to subsequent `get` AND an event present in the log) or\n * does not apply at all. No half-states.\n * 2. **Monotonic seq per org.** `seq` is strictly increasing within\n * `org`. Different orgs have independent sequences. (Repositories\n * scoped to a single org may treat the entire repo as one log.)\n * 3. **Optimistic locking.** `put` and `delete` throw `ConflictError`\n * when `parentVersion` does not match the current HEAD.\n * 4. **Canonical hashing.** `item.hash === hashSpec(item.body)` — always.\n * 5. **Event ordering.** Subscribers to `watch()` receive events in\n * monotonically-increasing `seq` order with no gaps.\n * 6. **Resumability.** `watch(_, since)` MUST replay all events with\n * `seq > since` before delivering live events.\n * 7. **Tombstones, not holes.** `delete` produces a `delete` event;\n * `get` returns null but `history` still shows the lineage.\n */\n\nimport type {\n MetaRef,\n MetadataItem,\n MetadataItemHeader,\n MetadataEvent,\n PutOptions,\n PutResult,\n DeleteOptions,\n DeleteResult,\n ListFilter,\n WatchFilter,\n HistoryOptions,\n} from './types.js';\n\nexport interface MetadataRepository {\n /** Read HEAD or a pinned version. Returns null if absent. */\n get(ref: MetaRef): Promise<MetadataItem | null>;\n\n /**\n * Resolve a historical version by content hash (ADR-0009).\n *\n * Returns the `MetadataItem` whose canonical sha256 equals `hash`\n * for the given ref, or `null` if no such version is recorded.\n *\n * Implementations MUST search history (not just HEAD) so that\n * `executionPinned` types remain resolvable through definition\n * upgrades. For non-`executionPinned` types, implementations MAY\n * return `null` if they have GC'd the corresponding history row.\n */\n getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null>;\n\n /**\n * Write a new version. Atomic.\n * @throws ConflictError if `parentVersion` does not match HEAD.\n * @throws SchemaValidationError if `spec` fails Zod normalisation.\n */\n put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult>;\n\n /**\n * Soft-delete (tombstone). `parentVersion` is required.\n * @throws ConflictError on parent mismatch.\n */\n delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult>;\n\n /** Enumerate items matching a filter. Implementations may stream. */\n list(filter: ListFilter): AsyncIterable<MetadataItemHeader>;\n\n /** Per-item history; events in monotonic `seq` order. */\n history(ref: MetaRef, opts?: HistoryOptions): AsyncIterable<MetadataEvent>;\n\n /**\n * Live event stream. The iterator MUST:\n *\n * - Replay all events with `seq > since` before yielding any new event.\n * - Stay open until the consumer breaks the loop.\n * - Survive transient backend disconnects (implementation's choice\n * how to resume — Postgres LISTEN reconnect, JSONL tail, etc.).\n */\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent>;\n}\n\n/**\n * Sentinel symbol used by `LayeredRepository` (M0 PR-5) to label which\n * underlying layer emitted an event. Defined here so the contract is\n * shared.\n */\nexport const LAYER_SOURCE = Symbol.for('objectstack.metadata.layer-source');\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `InMemoryRepository` — reference implementation of `MetadataRepository`\n * backed by plain JS Maps. Used by:\n *\n * - tests (parameterized contract-test suite)\n * - edge / serverless runtimes (no FS, no DB)\n * - `LayeredRepository` fallbacks\n *\n * State model\n * ───────────\n * items : refKey → MetadataItem (current head)\n * logs : org → MetadataEvent[] (append-only, monotonic per org)\n * seqs : org → number\n *\n * `watch()` is implemented over a simple subscriber list. Each subscriber\n * receives a deep-copy of the event so they cannot mutate the log.\n */\n\nimport {\n type MetaRef,\n type MetadataItem,\n type MetadataItemHeader,\n type MetadataEvent,\n type PutOptions,\n type PutResult,\n type DeleteOptions,\n type DeleteResult,\n type ListFilter,\n type WatchFilter,\n type HistoryOptions,\n type MetadataType,\n refKey,\n} from './types.js';\nimport { hashSpec } from './canonicalize.js';\nimport { ConflictError } from './errors.js';\nimport type { MetadataRepository } from './repository.js';\n\nconst orgKey = (ref: Pick<MetaRef, 'org'>): string => ref.org;\n\nconst matchesFilter = (\n ref: MetaRef,\n filter: { org?: string; type?: MetadataType; name?: string },\n): boolean => {\n if (filter.org && filter.org !== ref.org) return false;\n if (filter.type && filter.type !== ref.type) return false;\n if (filter.name && filter.name !== ref.name) return false;\n return true;\n};\n\ninterface Subscriber {\n filter: WatchFilter;\n push: (evt: MetadataEvent) => void;\n closed: boolean;\n}\n\nexport interface InMemoryRepositoryOptions {\n /** Optional clock injection for deterministic tests. Default: Date.now. */\n now?: () => Date;\n}\n\nexport class InMemoryRepository implements MetadataRepository {\n private readonly items = new Map<string, MetadataItem>();\n /** Per-org event log. */\n private readonly logs = new Map<string, MetadataEvent[]>();\n /** Next seq per org. */\n private readonly seqs = new Map<string, number>();\n private readonly subscribers = new Set<Subscriber>();\n private readonly now: () => Date;\n\n constructor(opts: InMemoryRepositoryOptions = {}) {\n this.now = opts.now ?? (() => new Date());\n }\n\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n const item = this.items.get(refKey(ref));\n if (!item) return null;\n if (ref.version && item.hash !== ref.version) {\n // History lookup is not supported in this minimal impl — only HEAD.\n // A future revision may walk the log to reconstruct an old version.\n return null;\n }\n return clone(item);\n }\n\n async getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null> {\n // InMemoryRepository keeps only HEAD bodies; historical bodies are\n // not retained. Resolve only if the requested hash IS HEAD.\n const item = this.items.get(refKey(ref));\n if (!item || item.hash !== hash) return null;\n return clone(item);\n }\n\n async put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult> {\n const key = refKey(ref);\n const current = this.items.get(key);\n const currentHead = current?.hash ?? null;\n\n if ((opts.parentVersion ?? null) !== currentHead) {\n throw new ConflictError(ref, opts.parentVersion ?? null, currentHead);\n }\n\n const hash = hashSpec(spec);\n\n // No-op write — same content. Still consumes nothing; no event emitted.\n if (current && current.hash === hash) {\n return { version: hash, seq: current.seq, item: clone(current) };\n }\n\n const seq = this.bumpSeq(ref);\n const ts = this.now().toISOString();\n\n const item: MetadataItem = {\n ref: { ...ref, version: undefined },\n body: clonePlain(spec) as Record<string, unknown>,\n hash,\n parentHash: currentHead,\n authoredBy: opts.actor,\n authoredAt: ts,\n message: opts.message,\n seq,\n };\n\n this.items.set(key, item);\n\n const evt: MetadataEvent = {\n seq,\n op: current ? 'update' : 'create',\n ref: { ...ref, version: undefined },\n hash,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'in-memory',\n };\n this.appendEvent(ref, evt);\n\n return { version: hash, seq, item: clone(item) };\n }\n\n async delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult> {\n const key = refKey(ref);\n const current = this.items.get(key);\n const currentHead = current?.hash ?? null;\n if (currentHead !== opts.parentVersion) {\n throw new ConflictError(ref, opts.parentVersion, currentHead);\n }\n\n this.items.delete(key);\n const seq = this.bumpSeq(ref);\n const ts = this.now().toISOString();\n const evt: MetadataEvent = {\n seq,\n op: 'delete',\n ref: { ...ref, version: undefined },\n hash: null,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'in-memory',\n };\n this.appendEvent(ref, evt);\n return { seq };\n }\n\n async *list(filter: ListFilter): AsyncIterable<MetadataItemHeader> {\n const limit = filter.limit ?? Infinity;\n let yielded = 0;\n for (const item of this.items.values()) {\n if (!matchesFilter(item.ref, filter)) continue;\n if (filter.nameContains && !item.ref.name.includes(filter.nameContains)) continue;\n const { body, ...header } = item;\n void body;\n yield clone(header) as MetadataItemHeader;\n if (++yielded >= limit) return;\n }\n }\n\n async *history(ref: MetaRef, opts: HistoryOptions = {}): AsyncIterable<MetadataEvent> {\n const log = this.logs.get(orgKey(ref)) ?? [];\n const since = opts.sinceSeq ?? -1;\n const limit = opts.limit ?? Infinity;\n let yielded = 0;\n for (const evt of log) {\n if (evt.seq <= since) continue;\n if (evt.ref.type !== ref.type || evt.ref.name !== ref.name) continue;\n yield clone(evt);\n if (++yielded >= limit) return;\n }\n }\n\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent> {\n // Implemented as a manual async iterator (not a generator) so we can\n // implement `return()` to unblock a pending wait. Async generators\n // do NOT run their `finally` block when paused on an unresolved\n // `await` — see https://github.com/tc39/proposal-async-iteration.\n const queue: MetadataEvent[] = [];\n let waiter: ((evt: IteratorResult<MetadataEvent>) => void) | null = null;\n let closed = false;\n const delivered = new Set<string>();\n const evtKey = (e: MetadataEvent) => `${orgKey(e.ref)}#${e.seq}`;\n\n const subscriber: Subscriber = {\n filter,\n closed: false,\n push: (evt) => {\n if (subscriber.closed) return;\n if (waiter) {\n const k = evtKey(evt);\n if (delivered.has(k)) return;\n delivered.add(k);\n const w = waiter;\n waiter = null;\n w({ value: clone(evt), done: false });\n } else {\n queue.push(evt);\n }\n },\n };\n\n // Build the replay buffer BEFORE registering the subscriber to avoid\n // a race window? No — we register first then build replay, dedup'ing\n // by seq when we hand off to live so any event that arrives during\n // replay isn't double-delivered.\n this.subscribers.add(subscriber);\n\n const replay: MetadataEvent[] = [];\n for (const ok of this.orgKeysMatching(filter)) {\n const log = this.logs.get(ok) ?? [];\n for (const evt of log) {\n if (typeof since === 'number' && evt.seq <= since) continue;\n if (!matchesFilter(evt.ref, filter)) continue;\n replay.push(evt);\n }\n }\n replay.sort((a, b) => a.seq - b.seq);\n let replayIdx = 0;\n\n const drainQueueOrReplay = (): IteratorResult<MetadataEvent> | null => {\n while (replayIdx < replay.length) {\n const evt = replay[replayIdx++]!;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n while (queue.length > 0) {\n const evt = queue.shift()!;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n return null;\n };\n\n const close = (): IteratorResult<MetadataEvent> => {\n if (!closed) {\n closed = true;\n subscriber.closed = true;\n this.subscribers.delete(subscriber);\n if (waiter) {\n const w = waiter;\n waiter = null;\n w({ value: undefined, done: true });\n }\n }\n return { value: undefined, done: true };\n };\n\n const iterator: AsyncIterator<MetadataEvent> = {\n next: () => {\n if (closed) return Promise.resolve({ value: undefined, done: true });\n const immediate = drainQueueOrReplay();\n if (immediate) return Promise.resolve(immediate);\n return new Promise<IteratorResult<MetadataEvent>>((resolve) => {\n waiter = resolve;\n });\n },\n return: () => Promise.resolve(close()),\n throw: (err) => {\n close();\n return Promise.reject(err);\n },\n };\n\n return {\n [Symbol.asyncIterator]: () => iterator,\n };\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private bumpSeq(ref: Pick<MetaRef, 'org'>): number {\n const ok = orgKey(ref);\n const next = (this.seqs.get(ok) ?? 0) + 1;\n this.seqs.set(ok, next);\n return next;\n }\n\n private appendEvent(ref: Pick<MetaRef, 'org'>, evt: MetadataEvent): void {\n const ok = orgKey(ref);\n const log = this.logs.get(ok) ?? [];\n log.push(evt);\n this.logs.set(ok, log);\n // Broadcast\n for (const sub of this.subscribers) {\n if (sub.closed) continue;\n if (!matchesFilter(evt.ref, sub.filter)) continue;\n sub.push(evt);\n }\n }\n\n private orgKeysMatching(filter: WatchFilter): string[] {\n const keys: string[] = [];\n for (const ok of this.logs.keys()) {\n if (filter.org && filter.org !== ok) continue;\n keys.push(ok);\n }\n return keys;\n }\n}\n\n/** Deep clone via JSON; safe for `MetadataItem` / `MetadataEvent`. */\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\n/** Clone a value that the caller passed in; rejects functions/symbols. */\nfunction clonePlain(value: unknown): unknown {\n return JSON.parse(JSON.stringify(value));\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `MetadataCache` — bounded, event-invalidated LRU sitting in front of a\n * `MetadataRepository`. See ADR-0008 §2.5.\n *\n * Design contract\n * ───────────────\n *\n * 1. **Lazy fill.** Cache entries are only created on first read miss.\n * No bulk preload — that would defeat the whole point of being\n * bounded.\n * 2. **Event-driven invalidation.** The cache subscribes to\n * `repo.watch({...})` and drops or replaces affected entries\n * whenever the repository emits an event. Stale reads are bounded\n * by the event-propagation latency of the underlying repo.\n * 3. **Bounded.** Both `maxEntries` and `maxBytes` are enforced; LRU\n * eviction happens on `set()` when either limit is exceeded.\n * 4. **Coherent under races.** Concurrent `get()`s for the same key\n * coalesce onto a single backend fetch (the \"thundering herd\"\n * fix). If an invalidation event arrives during an in-flight\n * fetch, the resulting value is discarded — the next read fetches\n * fresh.\n * 5. **Negative caching.** A miss (repo returned `null`) is also\n * cached, with a smaller TTL semantics — it stays until an event\n * for that ref arrives. This makes \"does X exist?\" cheap during\n * tight loops without compromising correctness.\n */\n\nimport type { MetaRef, MetadataItem, MetadataEvent, WatchFilter } from './types.js';\nimport { refKey } from './types.js';\nimport type { MetadataRepository } from './repository.js';\n\nexport interface MetadataCacheOptions {\n /** Maximum number of entries to keep. Default: 1024. */\n maxEntries?: number;\n /**\n * Maximum approximate body size in bytes. Default: 8 MiB. Each entry's\n * size is estimated from `JSON.stringify(item.body).length`.\n */\n maxBytes?: number;\n /**\n * Watch filter. Only events matching this filter invalidate cache\n * entries. Default: no filter (all events).\n */\n watchFilter?: WatchFilter;\n}\n\ninterface CacheEntry {\n /** Null = negative cache (item is known absent). */\n item: MetadataItem | null;\n size: number;\n /** Hit count, just for diagnostics. */\n hits: number;\n}\n\nexport interface CacheStats {\n entries: number;\n bytes: number;\n hits: number;\n misses: number;\n invalidations: number;\n /** Reads that arrived while a fetch was already in-flight for the same key. */\n coalesced: number;\n}\n\nexport class MetadataCache {\n private readonly repo: MetadataRepository;\n private readonly maxEntries: number;\n private readonly maxBytes: number;\n private readonly watchFilter: WatchFilter;\n\n /** LRU is implemented via insertion-order Map; touch on get/set. */\n private readonly entries = new Map<string, CacheEntry>();\n private bytes = 0;\n\n /** De-duplicate concurrent fetches for the same key. */\n private readonly inflight = new Map<string, Promise<MetadataItem | null>>();\n /**\n * Generation counter incremented on every invalidation. Each in-flight\n * fetch captures the gen at start; if the gen changes before it\n * resolves, the result is NOT cached.\n */\n private readonly fetchGen = new Map<string, number>();\n\n private watchIterator: AsyncIterator<MetadataEvent> | null = null;\n private watchClosed = false;\n private watchLoop: Promise<void> | null = null;\n\n private readonly stats: CacheStats = {\n entries: 0,\n bytes: 0,\n hits: 0,\n misses: 0,\n invalidations: 0,\n coalesced: 0,\n };\n\n constructor(repo: MetadataRepository, opts: MetadataCacheOptions = {}) {\n this.repo = repo;\n this.maxEntries = opts.maxEntries ?? 1024;\n this.maxBytes = opts.maxBytes ?? 8 * 1024 * 1024;\n this.watchFilter = opts.watchFilter ?? {};\n }\n\n /**\n * Start the background watch subscription. Idempotent; safe to call\n * multiple times. Caller is responsible for calling `close()` when\n * the cache is no longer needed.\n */\n start(): void {\n if (this.watchIterator || this.watchClosed) return;\n const iter = this.repo.watch(this.watchFilter)[Symbol.asyncIterator]();\n this.watchIterator = iter;\n this.watchLoop = (async () => {\n try {\n while (!this.watchClosed) {\n const { value, done } = await iter.next();\n if (done) return;\n this.applyEvent(value);\n }\n } catch {\n // Repository tore down its event stream; cache is left in a\n // best-effort state. Consumers can detect this via stats or by\n // restarting the cache.\n }\n })();\n }\n\n /** Tear down the watch subscription and clear in-flight tracking. */\n async close(): Promise<void> {\n this.watchClosed = true;\n if (this.watchIterator?.return) {\n try {\n await this.watchIterator.return(undefined);\n } catch {\n // ignore\n }\n }\n this.watchIterator = null;\n if (this.watchLoop) {\n try {\n await this.watchLoop;\n } catch {\n // ignore\n }\n this.watchLoop = null;\n }\n this.inflight.clear();\n }\n\n /** Read with cache. Coalesces concurrent reads for the same key. */\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n const key = refKey(ref);\n const cached = this.entries.get(key);\n if (cached) {\n // Touch (LRU bump).\n this.entries.delete(key);\n this.entries.set(key, cached);\n cached.hits += 1;\n this.stats.hits += 1;\n return cached.item ? clone(cached.item) : null;\n }\n\n const existing = this.inflight.get(key);\n if (existing) {\n this.stats.coalesced += 1;\n const item = await existing;\n return item ? clone(item) : null;\n }\n\n this.stats.misses += 1;\n const gen = (this.fetchGen.get(key) ?? 0);\n const promise = this.repo\n .get(ref)\n .then((item) => {\n // If the cache was invalidated for this key during the fetch,\n // skip caching the (possibly stale) result.\n if ((this.fetchGen.get(key) ?? 0) === gen) {\n this.cacheSet(key, item);\n }\n return item;\n })\n .finally(() => {\n this.inflight.delete(key);\n });\n\n this.inflight.set(key, promise);\n const item = await promise;\n return item ? clone(item) : null;\n }\n\n /** Drop a single entry by ref. */\n invalidate(ref: MetaRef): void {\n const key = refKey(ref);\n this.bumpGen(key);\n const removed = this.entries.get(key);\n if (removed) {\n this.bytes -= removed.size;\n this.entries.delete(key);\n }\n this.stats.invalidations += 1;\n }\n\n /** Drop the entire cache (e.g. on a reset). */\n clear(): void {\n this.entries.clear();\n this.bytes = 0;\n // Bump every in-flight key so we don't accidentally cache stale data.\n for (const key of this.inflight.keys()) this.bumpGen(key);\n }\n\n getStats(): Readonly<CacheStats> {\n return {\n ...this.stats,\n entries: this.entries.size,\n bytes: this.bytes,\n };\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private applyEvent(evt: MetadataEvent): void {\n // Any op (create/update/delete/rename) invalidates the affected key.\n // We don't try to be clever (e.g. preloading the new value) — the\n // next reader will pull the fresh body through `get`.\n const ref = evt.ref;\n this.invalidate(ref);\n if (evt.op === 'rename' && evt.previousName) {\n this.invalidate({ ...ref, name: evt.previousName });\n }\n }\n\n private cacheSet(key: string, item: MetadataItem | null): void {\n const size = item ? estimateSize(item) : 0;\n const existing = this.entries.get(key);\n if (existing) this.bytes -= existing.size;\n this.entries.set(key, { item, size, hits: 0 });\n this.bytes += size;\n this.evictIfNeeded();\n }\n\n private evictIfNeeded(): void {\n // Map preserves insertion order; oldest first. Touch on `get` moves\n // entries to the end → eviction from the front evicts the LRU.\n while (\n this.entries.size > this.maxEntries ||\n this.bytes > this.maxBytes\n ) {\n const first = this.entries.keys().next();\n if (first.done) break;\n const key = first.value;\n const entry = this.entries.get(key)!;\n this.bytes -= entry.size;\n this.entries.delete(key);\n }\n }\n\n private bumpGen(key: string): void {\n this.fetchGen.set(key, (this.fetchGen.get(key) ?? 0) + 1);\n }\n}\n\nfunction estimateSize(item: MetadataItem): number {\n // Rough estimate — JS strings are 2 bytes per char, but JSON.stringify\n // length is a fine proxy. Add a fixed overhead for the wrapper fields.\n try {\n return JSON.stringify(item.body).length * 2 + 256;\n } catch {\n return 1024; // fallback for unstringifiable bodies\n }\n}\n\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `LayeredRepository` — composes N child `MetadataRepository`s into a\n * single read-through stack. See ADR-0008 §10 PR-5.\n *\n * Read semantics\n * ──────────────\n * - `get(ref)` walks the layers top-to-bottom; first non-null wins.\n * - `list()` deduplicates by `refKey(ref)`, preferring the top layer.\n * - `history()` and `watch()` merge events from all layers, each\n * tagged with the source layer label in `evt.source`\n * (`<label>:<original-source>`).\n *\n * Write semantics\n * ───────────────\n * - `put` / `delete` are routed to the topmost writable layer.\n * - A layer is writable unless `readOnly: true` in its config.\n * - The write target's existing HEAD is used for the parent check —\n * i.e. if a key exists only in a lower layer, writing produces a\n * create on the top layer (an \"overlay\" of the lower one).\n *\n * Event tagging\n * ─────────────\n * - Each emitted event carries `source` rewritten as\n * `<layer.label>:<source>` so subscribers can tell which layer\n * produced the change. Original event content is otherwise\n * unchanged.\n *\n * This implementation never re-orders events relative to seq within a\n * single layer, but seqs across layers may interleave. Downstream\n * consumers (Cache, SchemaRegistry) only need monotonicity within a\n * (layer, branch) tuple — they treat each (layer.label, ref) as a\n * separate stream.\n */\n\nimport {\n type MetadataRepository,\n type MetaRef,\n type MetadataItem,\n type MetadataItemHeader,\n type MetadataEvent,\n type PutOptions,\n type PutResult,\n type DeleteOptions,\n type DeleteResult,\n type ListFilter,\n type WatchFilter,\n type HistoryOptions,\n refKey,\n} from './index.js';\n\nexport interface LayerConfig {\n /** Stable identifier for this layer (e.g. \"user\", \"team\", \"system\"). */\n label: string;\n /** The backing repository. */\n repo: MetadataRepository;\n /** When true, this layer rejects writes (used for built-ins). */\n readOnly?: boolean;\n}\n\nexport interface LayeredRepositoryOptions {\n /**\n * Layers in priority order, highest first. The first writable layer\n * receives all writes.\n */\n layers: LayerConfig[];\n}\n\nconst tagSource = (label: string, evt: MetadataEvent): MetadataEvent => ({\n ...evt,\n source: `${label}:${evt.source}`,\n});\n\nexport class LayeredRepository implements MetadataRepository {\n private readonly layers: LayerConfig[];\n /** Index of the first writable layer; -1 if none. */\n private readonly writableIdx: number;\n\n constructor(opts: LayeredRepositoryOptions) {\n if (!opts.layers.length) {\n throw new Error('LayeredRepository requires at least one layer');\n }\n this.layers = opts.layers;\n this.writableIdx = opts.layers.findIndex((l) => !l.readOnly);\n }\n\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n for (const layer of this.layers) {\n const item = await layer.repo.get(ref);\n if (item) return item;\n }\n return null;\n }\n\n async getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null> {\n // Probe layers top→bottom; first layer that resolves the hash wins.\n // executionPinned types are durable in the storage layer\n // (`SysMetadataRepository`); FS / in-memory layers only resolve\n // hash == HEAD as a fallback.\n for (const layer of this.layers) {\n const item = await layer.repo.getByHash(ref, hash);\n if (item) return item;\n }\n return null;\n }\n\n async put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult> {\n if (this.writableIdx < 0) {\n throw new Error('LayeredRepository: no writable layer configured');\n }\n return this.layers[this.writableIdx]!.repo.put(ref, spec, opts);\n }\n\n async delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult> {\n if (this.writableIdx < 0) {\n throw new Error('LayeredRepository: no writable layer configured');\n }\n return this.layers[this.writableIdx]!.repo.delete(ref, opts);\n }\n\n async *list(filter: ListFilter): AsyncIterable<MetadataItemHeader> {\n // Yield headers from top→bottom, deduplicating by refKey.\n const seen = new Set<string>();\n const limit = filter.limit ?? Infinity;\n let yielded = 0;\n for (const layer of this.layers) {\n for await (const header of layer.repo.list(filter)) {\n const key = refKey(header.ref);\n if (seen.has(key)) continue;\n seen.add(key);\n yield header;\n if (++yielded >= limit) return;\n }\n }\n }\n\n async *history(ref: MetaRef, opts: HistoryOptions = {}): AsyncIterable<MetadataEvent> {\n // Merge histories from all layers, tagged. Order preserves each\n // layer's monotonic seq, but events across layers may interleave.\n // We collect everything then sort by ts as a best-effort total order.\n const events: MetadataEvent[] = [];\n for (const layer of this.layers) {\n for await (const evt of layer.repo.history(ref, opts)) {\n events.push(tagSource(layer.label, evt));\n }\n }\n events.sort((a, b) => (a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0));\n const limit = opts.limit ?? Infinity;\n let yielded = 0;\n for (const evt of events) {\n yield evt;\n if (++yielded >= limit) return;\n }\n }\n\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent> {\n // Fan out to all child watchers; multiplex into a single iterator.\n return multiplexWatch(this.layers, filter, since);\n }\n}\n\n/**\n * Multiplex N async iterables of MetadataEvent into one, tagging each\n * event's `source` with its layer label. Implemented as a manual\n * AsyncIterator so we can correctly cancel all child iterators when the\n * consumer breaks out.\n */\nfunction multiplexWatch(\n layers: LayerConfig[],\n filter: WatchFilter,\n since: number | undefined,\n): AsyncIterable<MetadataEvent> {\n return {\n [Symbol.asyncIterator]() {\n const children = layers.map((layer) => ({\n label: layer.label,\n iter: layer.repo.watch(filter, since)[Symbol.asyncIterator](),\n pending: null as Promise<{ label: string; result: IteratorResult<MetadataEvent> }> | null,\n done: false,\n }));\n let closed = false;\n\n const pumpAll = () => {\n for (const c of children) {\n if (c.done || c.pending) continue;\n c.pending = c.iter.next().then((result) => ({ label: c.label, result }));\n }\n };\n\n const closeAll = async () => {\n if (closed) return;\n closed = true;\n await Promise.all(\n children.map(async (c) => {\n try { await c.iter.return?.(undefined); } catch { /* ignore */ }\n }),\n );\n };\n\n return {\n async next(): Promise<IteratorResult<MetadataEvent>> {\n while (!closed) {\n pumpAll();\n const inflight = children.filter((c) => c.pending);\n if (!inflight.length) return { value: undefined, done: true };\n const winner = await Promise.race(inflight.map((c) => c.pending!));\n const target = children.find((c) => c.label === winner.label)!;\n target.pending = null;\n if (winner.result.done) {\n target.done = true;\n continue;\n }\n return {\n value: tagSource(winner.label, winner.result.value as MetadataEvent),\n done: false,\n };\n }\n return { value: undefined, done: true };\n },\n async return() {\n await closeAll();\n return { value: undefined, done: true };\n },\n async throw(err) {\n await closeAll();\n throw err;\n },\n } as AsyncIterator<MetadataEvent>;\n },\n };\n}\n"],"mappings":"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","names":["clone","item"]}
@@ -27,6 +27,7 @@ declare const MetadataTypeSchema: z.ZodEnum<{
27
27
  flow: "flow";
28
28
  workflow: "workflow";
29
29
  approval: "approval";
30
+ job: "job";
30
31
  agent: "agent";
31
32
  tool: "tool";
32
33
  skill: "skill";
@@ -77,6 +78,7 @@ declare const MetaRefSchema: z.ZodObject<{
77
78
  flow: "flow";
78
79
  workflow: "workflow";
79
80
  approval: "approval";
81
+ job: "job";
80
82
  agent: "agent";
81
83
  tool: "tool";
82
84
  skill: "skill";
@@ -129,6 +131,7 @@ declare const MetadataItemSchema: z.ZodObject<{
129
131
  flow: "flow";
130
132
  workflow: "workflow";
131
133
  approval: "approval";
134
+ job: "job";
132
135
  agent: "agent";
133
136
  tool: "tool";
134
137
  skill: "skill";
@@ -201,6 +204,7 @@ declare const MetadataEventSchema: z.ZodObject<{
201
204
  flow: "flow";
202
205
  workflow: "workflow";
203
206
  approval: "approval";
207
+ job: "job";
204
208
  agent: "agent";
205
209
  tool: "tool";
206
210
  skill: "skill";
@@ -27,6 +27,7 @@ declare const MetadataTypeSchema: z.ZodEnum<{
27
27
  flow: "flow";
28
28
  workflow: "workflow";
29
29
  approval: "approval";
30
+ job: "job";
30
31
  agent: "agent";
31
32
  tool: "tool";
32
33
  skill: "skill";
@@ -77,6 +78,7 @@ declare const MetaRefSchema: z.ZodObject<{
77
78
  flow: "flow";
78
79
  workflow: "workflow";
79
80
  approval: "approval";
81
+ job: "job";
80
82
  agent: "agent";
81
83
  tool: "tool";
82
84
  skill: "skill";
@@ -129,6 +131,7 @@ declare const MetadataItemSchema: z.ZodObject<{
129
131
  flow: "flow";
130
132
  workflow: "workflow";
131
133
  approval: "approval";
134
+ job: "job";
132
135
  agent: "agent";
133
136
  tool: "tool";
134
137
  skill: "skill";
@@ -201,6 +204,7 @@ declare const MetadataEventSchema: z.ZodObject<{
201
204
  flow: "flow";
202
205
  workflow: "workflow";
203
206
  approval: "approval";
207
+ job: "job";
204
208
  agent: "agent";
205
209
  tool: "tool";
206
210
  skill: "skill";
@@ -1,4 +1,4 @@
1
- import { k as MetadataRepository } from './repository-BL4L5ta6.cjs';
1
+ import { k as MetadataRepository } from './repository-DsvcAzZw.cjs';
2
2
  import 'zod';
3
3
 
4
4
  interface ContractSuiteOptions {
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { k as MetadataRepository } from './repository-BL4L5ta6.js';
1
+ import { k as MetadataRepository } from './repository-DsvcAzZw.js';
2
2
  import 'zod';
3
3
 
4
4
  interface ContractSuiteOptions {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/metadata-core",
3
- "version": "6.8.0",
3
+ "version": "6.9.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Metadata Repository contracts: types, canonicalization, errors, interface (ADR-0008).",
6
6
  "type": "module",