@objectstack/metadata-core 10.2.0 → 11.0.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.
@@ -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","../src/objects/sys-metadata.object.ts","../src/objects/sys-metadata-history.object.ts","../src/objects/sys-metadata-audit.object.ts","../src/objects/sys-view-definition.object.ts"],"names":["clone","Field"],"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 // ADR-0019: `approval` is a flow node, not a metadata type.\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', 'publish', 'revert']);\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 /**\n * Per-(org,type,name) monotonic lineage counter at this event.\n * Populated by `SysMetadataRepository.history()`; used by\n * `rollbackMetaItem({ toVersion })` to pin a specific snapshot.\n */\n version: z.number().int().positive().optional(),\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\n/**\n * Two-tier metadata authorization intent (ADR-0005 extension).\n *\n * - `override-artifact`: the write targets an item that ships from a code\n * package (an artifact). Only permitted when the type opts into\n * per-org overlay writes via `allowOrgOverride: true`.\n * - `runtime-only`: the write targets a brand-new item OR an item that\n * exists only in `sys_metadata` (no artifact backing). Permitted for\n * types that opt into runtime creation via `allowRuntimeCreate: true`,\n * even when they explicitly forbid artifact overrides.\n *\n * The protocol layer determines the intent by consulting the schema\n * registry; the repository's `assertAllowed()` enforces it as\n * defense-in-depth. Defaults to `override-artifact` for backward\n * compatibility with callers that predate the two-tier model.\n */\nexport type MetadataWriteIntent = 'override-artifact' | 'runtime-only';\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 /** Two-tier authorization intent; defaults to `override-artifact`. */\n intent?: MetadataWriteIntent;\n /**\n * Software-package id to bind this metadata row to (`sys_metadata.package_id`).\n * Set when authoring inside a Studio package workspace. On create the row is\n * stamped with this id; on update an existing non-null binding is preserved\n * (never silently re-bound). Omit/undefined for env-local overlays.\n */\n packageId?: string | null;\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 /** Two-tier authorization intent; defaults to `override-artifact`. */\n intent?: MetadataWriteIntent;\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","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_metadata — System Metadata Object\n *\n * Canonical ObjectStack object definition for the metadata persistence table.\n * Stores all platform-scope and user-scope metadata records (Objects, Views,\n * Flows, etc.) using the MetadataRecordSchema envelope.\n *\n * This is a system object (isSystem: true) — protected from deletion and\n * automatically provisioned by the DatabaseLoader on first use.\n *\n * @see MetadataRecordSchema in metadata-persistence.zod.ts\n */\nexport const SysMetadataObject = ObjectSchema.create({\n name: 'sys_metadata',\n label: 'System Metadata',\n pluralLabel: 'System Metadata',\n icon: 'settings',\n isSystem: true,\n // managedBy: 'system' — the metadata table backs every other config\n // object. Writing rows directly here bypasses the typed Zod APIs and\n // would let an admin inject malformed payloads. The \"All Metadata\"\n // menu is therefore a read-only debug surface (Export only); typed\n // edits flow through the dedicated per-type pages (Approval Process,\n // Sharing Rule, etc.).\n managedBy: 'system',\n description: 'Stores platform and user-scope metadata records (objects, views, flows, etc.)',\n\n fields: {\n /** Primary Key (UUID) */\n id: Field.text({\n label: 'ID',\n required: true,\n readonly: true,\n }),\n\n /** Machine name — unique identifier used in code references */\n name: Field.text({\n label: 'Name',\n required: true,\n searchable: true,\n maxLength: 255,\n }),\n\n /** Metadata type (e.g. \"object\", \"view\", \"flow\") */\n type: Field.text({\n label: 'Metadata Type',\n required: true,\n searchable: true,\n maxLength: 100,\n }),\n\n /** Namespace / module grouping (e.g. \"crm\", \"core\") */\n namespace: Field.text({\n label: 'Namespace',\n required: false,\n defaultValue: 'default',\n maxLength: 100,\n }),\n\n /** Package that owns/delivered this metadata (legacy string identifier, kept for compat) */\n package_id: Field.text({\n label: 'Package ID',\n required: false,\n maxLength: 255,\n description: 'Legacy package manifest ID string. Use package_version_id for new records.',\n }),\n\n /**\n * FK → sys_package_version (UUID). Set for metadata that belongs to a specific\n * package release snapshot. NULL = platform-built-in or environment override.\n */\n package_version_id: Field.lookup('sys_package_version', {\n label: 'Package Version',\n required: false,\n description:\n 'Foreign key to sys_package_version (UUID). Null = platform-built-in or env-level override.',\n }),\n\n /** Who manages this record: package, platform, or user */\n managed_by: Field.select(['package', 'platform', 'user'], {\n label: 'Managed By',\n required: false,\n }),\n\n /** Scope: system (code), platform (admin DB), user (personal DB) */\n scope: Field.select(['system', 'platform', 'user'], {\n label: 'Scope',\n required: true,\n defaultValue: 'platform',\n }),\n\n /** JSON payload — the actual metadata configuration */\n metadata: Field.textarea({\n label: 'Metadata',\n required: true,\n description: 'JSON-serialized metadata payload',\n }),\n\n /** Parent metadata name for extension/override */\n extends: Field.text({\n label: 'Extends',\n required: false,\n maxLength: 255,\n }),\n\n /** Merge strategy when extending parent metadata */\n strategy: Field.select(['merge', 'replace'], {\n label: 'Strategy',\n required: false,\n defaultValue: 'merge',\n }),\n\n /** Owner user ID (for user-scope items) */\n owner: Field.text({\n label: 'Owner',\n required: false,\n maxLength: 255,\n }),\n\n /** Lifecycle state */\n state: Field.select(['draft', 'active', 'archived', 'deprecated'], {\n label: 'State',\n required: false,\n defaultValue: 'active',\n }),\n\n /** Organization ID for multi-tenant isolation */\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n description: 'Organization for multi-tenant isolation.',\n }),\n\n /**\n * @deprecated ADR-0005 (revised 2026-05): per-env DBs replace per-project\n * isolation. `environment_id` is no longer written by saveMetaItem and not\n * consulted by overlay reads. Kept for legacy rows; new writes leave it\n * NULL. Will be dropped in a future schema migration.\n */\n environment_id: Field.lookup('sys_environment', {\n label: 'Environment (deprecated)',\n required: false,\n description: 'DEPRECATED. Use organization_id for tenant isolation.',\n }),\n\n /** Version number for optimistic concurrency */\n version: Field.number({\n label: 'Version',\n required: false,\n defaultValue: 1,\n }),\n\n /** Content checksum for change detection (e.g. `sha256:<64 hex>` = 71 chars) */\n checksum: Field.text({\n label: 'Checksum',\n required: false,\n maxLength: 71,\n }),\n\n /** Origin of this metadata record */\n source: Field.select(['filesystem', 'database', 'api', 'migration'], {\n label: 'Source',\n required: false,\n }),\n\n /** Classification tags (JSON array) */\n tags: Field.textarea({\n label: 'Tags',\n required: false,\n description: 'JSON-serialized array of classification tags',\n }),\n\n /** Audit fields */\n created_by: Field.lookup('sys_user', {\n label: 'Created By',\n required: false,\n readonly: true,\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: false,\n readonly: true,\n }),\n\n updated_by: Field.lookup('sys_user', {\n label: 'Updated By',\n required: false,\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n required: false,\n }),\n },\n\n indexes: [\n // ADR-0005 (revised 2026-05) + ADR-0048: overlay uniqueness is scoped by\n // (type, name, organization_id, package_id), restricted to active rows so\n // resets / archived versions don't collide. `package_id` is part of the\n // discriminator so two installed packages shipping the same `type`/`name`\n // each get their OWN customization row (a package-less / global overlay\n // uses NULL). environment_id is deprecated and not part of the\n // discriminator. The runtime layer (protocol.ts ensureOverlayIndex) issues\n // a DROP-then-CREATE migration that uses `COALESCE(package_id,'')` so the\n // package-less rows stay unique among themselves (SQLite treats NULLs as\n // distinct in a plain unique index); this declaration is the fallback shape\n // for drivers without the runtime migration.\n {\n name: 'idx_sys_metadata_overlay_active',\n fields: ['type', 'name', 'organization_id', 'package_id'],\n unique: true,\n partial: \"state = 'active'\",\n },\n { name: 'idx_sys_metadata_org_type', fields: ['organization_id', 'type'] },\n { fields: ['type', 'scope'] },\n { fields: ['package_version_id'] },\n { fields: ['state'] },\n { fields: ['namespace'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: false,\n },\n\n // Named list views — power the Setup App \"Data Model\" group so admins\n // can browse object/field metadata in a typed grid instead of the raw\n // `All Metadata` debug surface. Each entry pre-filters by `type` and\n // shows the columns that matter for that type. The dedicated visual\n // designer (objectui's <ObjectManager> / <FieldDesigner>) deep-links\n // from the row's `Edit in Designer` action; the grid stays useful for\n // search, audit (state / updated_at) and triage.\n listViews: {\n only_objects: {\n type: 'grid',\n name: 'only_objects',\n label: 'Objects',\n data: { provider: 'object', object: 'sys_metadata' },\n columns: ['name', 'namespace', 'scope', 'managed_by', 'state', 'updated_at'],\n filter: [{ field: 'type', operator: 'equals', value: 'object' }],\n sort: [{ field: 'name', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n only_fields: {\n type: 'grid',\n name: 'only_fields',\n label: 'Fields',\n data: { provider: 'object', object: 'sys_metadata' },\n columns: ['name', 'namespace', 'scope', 'managed_by', 'state', 'updated_at'],\n filter: [{ field: 'type', operator: 'equals', value: 'field' }],\n sort: [{ field: 'name', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n all_metadata: {\n type: 'grid',\n name: 'all_metadata',\n label: 'All',\n data: { provider: 'object', object: 'sys_metadata' },\n columns: ['name', 'type', 'namespace', 'scope', 'state', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_metadata_history — Metadata Version History / Event Log\n *\n * Append-only durable log of every overlay change made through\n * `SysMetadataRepository.put` / `delete` (ADR-0008 §10 M1). Each row is a\n * single event in the per-organisation event log; rows are NEVER\n * mutated after insertion. The legacy `DatabaseLoader` writes the same\n * shape from its own put/restore code paths.\n *\n * ─────────────────────────────────────────────────────────────────────\n * Key design points (ADR-0008 §0 amendment + M1):\n *\n * • Keyed by `(organization_id, type, name)` only — `environment_id` was\n * removed in the branch/project-removal amendment. The original\n * `metadata_id` column (a downgraded plain-text version of the old\n * `sys_metadata.id` FK) was removed in the M1 follow-up — joins go\n * through `(organization_id, type, name, version)` exclusively.\n *\n * • `event_seq` is the per-org monotonic event-log cursor. Producers\n * compute `MAX(event_seq) + 1 WHERE organization_id = X` inside the\n * same transaction as the parent `sys_metadata` write.\n *\n * • `version` is the per-(org,type,name) lineage counter. Producers\n * compute `MAX(version) + 1 WHERE organization_id = X AND type = T\n * AND name = N` so delete + recreate continues incrementing instead\n * of restarting at 1.\n *\n * • `metadata` / `checksum` are nullable — DELETE rows have no body or\n * hash. Readers must tolerate null on both columns.\n *\n * • `source` records the producer ('sys-metadata-repo', 'fs',\n * 'studio', …) and feeds MetadataEvent.source on history() reads.\n *\n * Indexes are purpose-built for the two dominant read patterns:\n * 1. per-item history view → `(organization_id, type, name, version)`\n * 2. org-wide event replay → `(organization_id, event_seq)`\n * ─────────────────────────────────────────────────────────────────────\n */\nexport const SysMetadataHistoryObject = ObjectSchema.create({\n name: 'sys_metadata_history',\n label: 'Metadata History',\n pluralLabel: 'Metadata History',\n icon: 'history',\n isSystem: true,\n managedBy: 'system',\n description: 'Durable event log of metadata overlay changes (per-org, append-only)',\n\n fields: {\n /** Primary Key (UUID) */\n id: Field.text({\n label: 'ID',\n required: true,\n readonly: true,\n }),\n\n /** Per-org monotonic event sequence (durable cursor for replay). */\n event_seq: Field.number({\n label: 'Event Seq',\n required: true,\n readonly: true,\n description: 'Per-organization monotonic event log cursor.',\n }),\n\n /** Machine name (denormalized for easier querying) */\n name: Field.text({\n label: 'Name',\n required: true,\n searchable: true,\n readonly: true,\n maxLength: 255,\n }),\n\n /** Metadata type (denormalized for easier querying) */\n type: Field.text({\n label: 'Metadata Type',\n required: true,\n searchable: true,\n readonly: true,\n maxLength: 100,\n }),\n\n /** Per-(org,type,name) lineage counter at this snapshot. */\n version: Field.number({\n label: 'Version',\n required: true,\n readonly: true,\n }),\n\n /** Type of operation that created this history entry */\n operation_type: Field.select(['create', 'update', 'publish', 'revert', 'delete'], {\n label: 'Operation Type',\n required: true,\n readonly: true,\n }),\n\n /**\n * Historical metadata snapshot (JSON payload).\n * Null for `operation_type = 'delete'` — the row carries no body.\n */\n metadata: Field.textarea({\n label: 'Metadata',\n required: false,\n readonly: true,\n description: 'JSON-serialized metadata snapshot at this version (null for deletes).',\n }),\n\n /** SHA-256 checksum of metadata content (null for deletes). */\n checksum: Field.text({\n label: 'Checksum',\n required: false,\n readonly: true,\n maxLength: 80,\n }),\n\n /** Checksum of the previous version (null for the first event). */\n previous_checksum: Field.text({\n label: 'Previous Checksum',\n required: false,\n readonly: true,\n maxLength: 80,\n }),\n\n /** Human-readable description of changes (= MetadataEvent.message). */\n change_note: Field.textarea({\n label: 'Change Note',\n required: false,\n readonly: true,\n description: 'Description of what changed in this version.',\n }),\n\n /**\n * Producer of the event ('sys-metadata-repo', 'fs', 'studio',\n * 'api', …). Defaults to 'sys-metadata-repo' on the canonical\n * write path; preserved on history() reads as MetadataEvent.source.\n */\n source: Field.text({\n label: 'Source',\n required: false,\n readonly: true,\n maxLength: 64,\n }),\n\n /** Organization ID for multi-tenant isolation */\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n readonly: true,\n description: 'Organization for multi-tenant isolation.',\n }),\n\n /** User who made this change (= MetadataEvent.actor). */\n recorded_by: Field.lookup('sys_user', {\n label: 'Recorded By',\n required: false,\n readonly: true,\n }),\n\n /** When was this version recorded */\n recorded_at: Field.datetime({\n label: 'Recorded At',\n required: true,\n readonly: true,\n }),\n },\n\n indexes: [\n { fields: ['organization_id', 'event_seq'], unique: true },\n { fields: ['organization_id', 'type', 'name', 'version'], unique: true },\n { fields: ['organization_id', 'type', 'name', 'recorded_at'] },\n // ADR-0009: getByHash() lookup — execution-pinned types resolve a\n // historical body by content hash via this index.\n { fields: ['organization_id', 'type', 'name', 'checksum'] },\n { fields: ['type', 'name'] },\n { fields: ['recorded_at'] },\n { fields: ['operation_type'] },\n ],\n\n enable: {\n trackHistory: false,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list'],\n trash: false,\n },\n});\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_metadata_audit — Metadata Protection Audit Log\n *\n * Append-only audit trail for every metadata write **decision** — both\n * allowed writes and refused ones. Introduced in ADR-0010 Phase 1 as\n * the compliance surface for the new `_lock` enforcement: every PUT /\n * publish / rollback / delete that targets `sys_metadata` writes one\n * row here describing the outcome and the lock state that produced it.\n *\n * Distinct from `sys_metadata_history`:\n * - `sys_metadata_history` records the **body** of every successful\n * write (full JSON snapshot + checksum). Used for rollback,\n * diff, and history() reads.\n * - `sys_metadata_audit` records the **decision** (who tried what,\n * what code was emitted, was a lock involved). Refused writes\n * never reach history; they DO reach audit.\n *\n * Designed as the smallest possible row that satisfies the four\n * compliance questions of metadata governance:\n * 1. Who tried to change what? → actor + type + name\n * 2. When? → occurred_at\n * 3. What outcome? → outcome + code\n * 4. Was an override involved? → lock_overridden + lock_state\n *\n * Indexed on `(organization_id, occurred_at)` for the per-org timeline\n * query and `(type, name, occurred_at)` for the per-item history tab\n * Studio surfaces on the editor page.\n */\nexport const SysMetadataAuditObject = ObjectSchema.create({\n name: 'sys_metadata_audit',\n label: 'Metadata Audit',\n pluralLabel: 'Metadata Audit',\n icon: 'shield-check',\n isSystem: true,\n managedBy: 'append-only',\n description: 'Append-only audit trail of metadata write decisions (ADR-0010).',\n\n fields: {\n /** Primary Key (UUID) */\n id: Field.text({\n label: 'ID',\n required: true,\n readonly: true,\n }),\n\n /** When the decision was made (ISO-8601 UTC). */\n occurred_at: Field.datetime({\n label: 'Occurred At',\n required: true,\n readonly: true,\n }),\n\n /** Acting principal (user id, system id, or 'system'). */\n actor: Field.text({\n label: 'Actor',\n required: true,\n readonly: true,\n maxLength: 255,\n description: 'Acting principal — user id, system id, or \"system\".',\n }),\n\n /** Code path that produced the decision (e.g. `protocol.saveMetaItem`). */\n source: Field.text({\n label: 'Source',\n required: false,\n readonly: true,\n maxLength: 128,\n }),\n\n /** Metadata type (singular, e.g. `app`, `object`, `view`). */\n type: Field.text({\n label: 'Metadata Type',\n required: true,\n readonly: true,\n searchable: true,\n maxLength: 100,\n }),\n\n /** Item machine name. */\n name: Field.text({\n label: 'Name',\n required: true,\n readonly: true,\n searchable: true,\n maxLength: 255,\n }),\n\n /** Organization for multi-tenant filtering. NULL for env-wide writes. */\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n readonly: true,\n }),\n\n /** Operation kind. */\n operation: Field.select(['save', 'publish', 'rollback', 'delete', 'reset'], {\n label: 'Operation',\n required: true,\n readonly: true,\n }),\n\n /** Decision outcome — allowed, denied (refused), or forced (bypassed via override). */\n outcome: Field.select(['allowed', 'denied', 'forced'], {\n label: 'Outcome',\n required: true,\n readonly: true,\n }),\n\n /**\n * Machine-readable code for the decision:\n * - on `allowed`: `'ok'`\n * - on `denied`: `'not_overridable'` | `'not_creatable'` |\n * `'item_locked'` | `'invalid_metadata'` | `'destructive_change'` |\n * `'metadata_conflict'`\n * - on `forced`: `'lock_override'` (Phase 3)\n */\n code: Field.text({\n label: 'Code',\n required: true,\n readonly: true,\n maxLength: 64,\n }),\n\n /**\n * Lock state observed at the time of the decision (`none` if the\n * item carried no `_lock`). Captured even on `allowed` rows so\n * later compliance queries can see \"what was the lock state when\n * this write succeeded\".\n */\n lock_state: Field.select(['none', 'no-overlay', 'no-delete', 'full'], {\n label: 'Lock State',\n required: false,\n readonly: true,\n }),\n\n /** True when the write succeeded by bypassing a lock (Phase 3). */\n lock_overridden: Field.boolean({\n label: 'Lock Overridden',\n required: false,\n readonly: true,\n }),\n\n /** Optional request correlation id for tracing. */\n request_id: Field.text({\n label: 'Request ID',\n required: false,\n readonly: true,\n maxLength: 128,\n }),\n\n /** Optional free-form context (e.g. brief diff summary). */\n note: Field.textarea({\n label: 'Note',\n required: false,\n readonly: true,\n }),\n },\n\n indexes: [\n { fields: ['organization_id', 'occurred_at'] },\n { fields: ['type', 'name', 'occurred_at'] },\n { fields: ['actor', 'occurred_at'] },\n { fields: ['outcome'] },\n ],\n\n enable: {\n trackHistory: false,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list'],\n trash: false,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_view_definition — Runtime View Storage (\"Object has-many View\")\n *\n * Persists view definitions authored at RUNTIME by end users — the `shared`\n * and `personal` layers of the view model (see spec `ViewItemSchema`). The\n * `package` layer ships from `*.view.ts` source and lives in the metadata\n * registry; it is NOT stored here.\n *\n * Why a dedicated object (not the `sys_metadata` overlay): runtime views are\n * data, not admin metadata customisation. They carry an `owner`, a visibility\n * `scope`, and are queried per-user — so they belong in a typed, permissioned\n * ObjectQL object rather than the global metadata registry (which a personal\n * \"My hot leads\" view should never pollute).\n *\n * CRUD flows through ObjectQL's generic data API (`/api/v1/data/\n * sys_view_definition`) — no bespoke per-view REST endpoints. The runtime\n * switcher reads the `package` layer via `GET /meta/view?object=<object>` and\n * merges these rows client-side, filtered to `scope='shared'` OR\n * `owner=<current user>`.\n *\n * This is a system object (isSystem: true) — protected from deletion and\n * auto-provisioned on first use.\n */\nexport const SysViewDefinitionObject = ObjectSchema.create({\n name: 'sys_view_definition',\n label: 'View Definition',\n pluralLabel: 'View Definitions',\n icon: 'layout-grid',\n isSystem: true,\n description:\n 'Runtime-authored view definitions (shared / personal layers). The package layer ships from source.',\n\n fields: {\n /** Primary Key (UUID) */\n id: Field.text({ label: 'ID', required: true, readonly: true }),\n\n /**\n * Globally-unique qualified view id, `<object>.<viewKey>`, matching the\n * spec `ViewItemSchema.name`. For personal views the runtime may suffix\n * to keep it unique per owner.\n */\n name: Field.text({\n label: 'Name',\n required: true,\n searchable: true,\n maxLength: 255,\n }),\n\n /** Bound object — the foreign key used to aggregate views for the switcher. */\n object: Field.text({\n label: 'Object',\n required: true,\n searchable: true,\n maxLength: 255,\n }),\n\n /** Whether `config` is a ListView (list family) or a FormView. */\n view_kind: Field.select(['list', 'form'], {\n label: 'View Kind',\n required: true,\n defaultValue: 'list',\n }),\n\n /** Display label (plain string; i18n keys also accepted). */\n label: Field.text({ label: 'Label', required: false, maxLength: 255 }),\n\n /** Whether this is the object's default view in the switcher. */\n is_default: Field.boolean({ label: 'Is Default', required: false, defaultValue: false }),\n\n /** Sort order within the object's switcher / left rail. */\n view_order: Field.number({ label: 'Order', required: false, defaultValue: 0 }),\n\n /**\n * Identity layer. Only `shared` and `personal` are stored at runtime;\n * `package` views come from source.\n */\n scope: Field.select(['shared', 'personal'], {\n label: 'Scope',\n required: true,\n defaultValue: 'personal',\n }),\n\n /** Owner user id — set when scope = personal; null for shared. */\n owner: Field.text({ label: 'Owner', required: false, maxLength: 255 }),\n\n /** Hidden from the switcher (per-user / per-org declutter). */\n hidden: Field.boolean({ label: 'Hidden', required: false, defaultValue: false }),\n\n /** The ListView / FormView configuration payload. */\n config: Field.json({\n label: 'Config',\n required: true,\n description: 'ListView or FormView configuration (matches spec ViewItem.config).',\n }),\n\n /** Organization for multi-tenant isolation. */\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n description: 'Organization for multi-tenant isolation.',\n }),\n\n /** Lifecycle state. */\n state: Field.select(['draft', 'active', 'archived'], {\n label: 'State',\n required: false,\n defaultValue: 'active',\n }),\n\n /** Audit fields. */\n created_by: Field.lookup('sys_user', { label: 'Created By', required: false, readonly: true }),\n created_at: Field.datetime({ label: 'Created At', required: false, readonly: true }),\n updated_by: Field.lookup('sys_user', { label: 'Updated By', required: false }),\n updated_at: Field.datetime({ label: 'Updated At', required: false }),\n },\n\n indexes: [\n // A given view name is unique per (organization, owner) among active rows —\n // a shared view (owner NULL) and each user's personal views don't collide.\n {\n name: 'idx_sys_view_def_active',\n fields: ['name', 'organization_id', 'owner'],\n unique: true,\n partial: \"state = 'active'\",\n },\n // The switcher query: views for one object within a tenant.\n { name: 'idx_sys_view_def_object', fields: ['organization_id', 'object'] },\n { fields: ['scope'] },\n { fields: ['owner'] },\n { fields: ['state'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: false,\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","../src/objects/sys-metadata.object.ts","../src/objects/sys-metadata-history.object.ts","../src/objects/sys-metadata-commit.object.ts","../src/objects/sys-metadata-audit.object.ts","../src/objects/sys-view-definition.object.ts"],"names":["clone","Field"],"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 // ADR-0019: `approval` is a flow node, not a metadata type.\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', 'publish', 'revert']);\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 /**\n * Per-(org,type,name) monotonic lineage counter at this event.\n * Populated by `SysMetadataRepository.history()`; used by\n * `rollbackMetaItem({ toVersion })` to pin a specific snapshot.\n */\n version: z.number().int().positive().optional(),\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\n/**\n * Two-tier metadata authorization intent (ADR-0005 extension).\n *\n * - `override-artifact`: the write targets an item that ships from a code\n * package (an artifact). Only permitted when the type opts into\n * per-org overlay writes via `allowOrgOverride: true`.\n * - `runtime-only`: the write targets a brand-new item OR an item that\n * exists only in `sys_metadata` (no artifact backing). Permitted for\n * types that opt into runtime creation via `allowRuntimeCreate: true`,\n * even when they explicitly forbid artifact overrides.\n *\n * The protocol layer determines the intent by consulting the schema\n * registry; the repository's `assertAllowed()` enforces it as\n * defense-in-depth. Defaults to `override-artifact` for backward\n * compatibility with callers that predate the two-tier model.\n */\nexport type MetadataWriteIntent = 'override-artifact' | 'runtime-only';\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 /** Two-tier authorization intent; defaults to `override-artifact`. */\n intent?: MetadataWriteIntent;\n /**\n * Software-package id to bind this metadata row to (`sys_metadata.package_id`).\n * Set when authoring inside a Studio package workspace. On create the row is\n * stamped with this id; on update an existing non-null binding is preserved\n * (never silently re-bound). Omit/undefined for env-local overlays.\n */\n packageId?: string | null;\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 /** Two-tier authorization intent; defaults to `override-artifact`. */\n intent?: MetadataWriteIntent;\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","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_metadata — System Metadata Object\n *\n * Canonical ObjectStack object definition for the metadata persistence table.\n * Stores all platform-scope and user-scope metadata records (Objects, Views,\n * Flows, etc.) using the MetadataRecordSchema envelope.\n *\n * This is a system object (isSystem: true) — protected from deletion and\n * automatically provisioned by the DatabaseLoader on first use.\n *\n * @see MetadataRecordSchema in metadata-persistence.zod.ts\n */\nexport const SysMetadataObject = ObjectSchema.create({\n name: 'sys_metadata',\n label: 'System Metadata',\n pluralLabel: 'System Metadata',\n icon: 'settings',\n isSystem: true,\n // managedBy: 'system' — the metadata table backs every other config\n // object. Writing rows directly here bypasses the typed Zod APIs and\n // would let an admin inject malformed payloads. The \"All Metadata\"\n // menu is therefore a read-only debug surface (Export only); typed\n // edits flow through the dedicated per-type pages (Approval Process,\n // Sharing Rule, etc.).\n managedBy: 'system',\n description: 'Stores platform and user-scope metadata records (objects, views, flows, etc.)',\n\n fields: {\n /** Primary Key (UUID) */\n id: Field.text({\n label: 'ID',\n required: true,\n readonly: true,\n }),\n\n /** Machine name — unique identifier used in code references */\n name: Field.text({\n label: 'Name',\n required: true,\n searchable: true,\n maxLength: 255,\n }),\n\n /** Metadata type (e.g. \"object\", \"view\", \"flow\") */\n type: Field.text({\n label: 'Metadata Type',\n required: true,\n searchable: true,\n maxLength: 100,\n }),\n\n /** Namespace / module grouping (e.g. \"crm\", \"core\") */\n namespace: Field.text({\n label: 'Namespace',\n required: false,\n defaultValue: 'default',\n maxLength: 100,\n }),\n\n /** Package that owns/delivered this metadata (legacy string identifier, kept for compat) */\n package_id: Field.text({\n label: 'Package ID',\n required: false,\n maxLength: 255,\n description: 'Legacy package manifest ID string. Use package_version_id for new records.',\n }),\n\n /**\n * FK → sys_package_version (UUID). Set for metadata that belongs to a specific\n * package release snapshot. NULL = platform-built-in or environment override.\n */\n package_version_id: Field.lookup('sys_package_version', {\n label: 'Package Version',\n required: false,\n description:\n 'Foreign key to sys_package_version (UUID). Null = platform-built-in or env-level override.',\n }),\n\n /** Who manages this record: package, platform, or user */\n managed_by: Field.select(['package', 'platform', 'user'], {\n label: 'Managed By',\n required: false,\n }),\n\n /** Scope: system (code), platform (admin DB), user (personal DB) */\n scope: Field.select(['system', 'platform', 'user'], {\n label: 'Scope',\n required: true,\n defaultValue: 'platform',\n }),\n\n /** JSON payload — the actual metadata configuration */\n metadata: Field.textarea({\n label: 'Metadata',\n required: true,\n description: 'JSON-serialized metadata payload',\n }),\n\n /** Parent metadata name for extension/override */\n extends: Field.text({\n label: 'Extends',\n required: false,\n maxLength: 255,\n }),\n\n /** Merge strategy when extending parent metadata */\n strategy: Field.select(['merge', 'replace'], {\n label: 'Strategy',\n required: false,\n defaultValue: 'merge',\n }),\n\n /** Owner user ID (for user-scope items) */\n owner: Field.text({\n label: 'Owner',\n required: false,\n maxLength: 255,\n }),\n\n /** Lifecycle state */\n state: Field.select(['draft', 'active', 'archived', 'deprecated'], {\n label: 'State',\n required: false,\n defaultValue: 'active',\n }),\n\n /** Organization ID for multi-tenant isolation */\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n description: 'Organization for multi-tenant isolation.',\n }),\n\n /**\n * @deprecated ADR-0005 (revised 2026-05): per-env DBs replace per-project\n * isolation. `environment_id` is no longer written by saveMetaItem and not\n * consulted by overlay reads. Kept for legacy rows; new writes leave it\n * NULL. Will be dropped in a future schema migration.\n */\n environment_id: Field.lookup('sys_environment', {\n label: 'Environment (deprecated)',\n required: false,\n description: 'DEPRECATED. Use organization_id for tenant isolation.',\n }),\n\n /** Version number for optimistic concurrency */\n version: Field.number({\n label: 'Version',\n required: false,\n defaultValue: 1,\n }),\n\n /** Content checksum for change detection (e.g. `sha256:<64 hex>` = 71 chars) */\n checksum: Field.text({\n label: 'Checksum',\n required: false,\n maxLength: 71,\n }),\n\n /** Origin of this metadata record */\n source: Field.select(['filesystem', 'database', 'api', 'migration'], {\n label: 'Source',\n required: false,\n }),\n\n /** Classification tags (JSON array) */\n tags: Field.textarea({\n label: 'Tags',\n required: false,\n description: 'JSON-serialized array of classification tags',\n }),\n\n /** Audit fields */\n created_by: Field.lookup('sys_user', {\n label: 'Created By',\n required: false,\n readonly: true,\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: false,\n readonly: true,\n }),\n\n updated_by: Field.lookup('sys_user', {\n label: 'Updated By',\n required: false,\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n required: false,\n }),\n },\n\n indexes: [\n // ADR-0005 (revised 2026-05) + ADR-0048: overlay uniqueness is scoped by\n // (type, name, organization_id, package_id), restricted to active rows so\n // resets / archived versions don't collide. `package_id` is part of the\n // discriminator so two installed packages shipping the same `type`/`name`\n // each get their OWN customization row (a package-less / global overlay\n // uses NULL). environment_id is deprecated and not part of the\n // discriminator. The runtime layer (protocol.ts ensureOverlayIndex) issues\n // a DROP-then-CREATE migration that uses `COALESCE(package_id,'')` so the\n // package-less rows stay unique among themselves (SQLite treats NULLs as\n // distinct in a plain unique index); this declaration is the fallback shape\n // for drivers without the runtime migration.\n {\n name: 'idx_sys_metadata_overlay_active',\n fields: ['type', 'name', 'organization_id', 'package_id'],\n unique: true,\n partial: \"state = 'active'\",\n },\n { name: 'idx_sys_metadata_org_type', fields: ['organization_id', 'type'] },\n { fields: ['type', 'scope'] },\n { fields: ['package_version_id'] },\n { fields: ['state'] },\n { fields: ['namespace'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: false,\n },\n\n // Named list views — power the Setup App \"Data Model\" group so admins\n // can browse object/field metadata in a typed grid instead of the raw\n // `All Metadata` debug surface. Each entry pre-filters by `type` and\n // shows the columns that matter for that type. The dedicated visual\n // designer (objectui's <ObjectManager> / <FieldDesigner>) deep-links\n // from the row's `Edit in Designer` action; the grid stays useful for\n // search, audit (state / updated_at) and triage.\n listViews: {\n only_objects: {\n type: 'grid',\n name: 'only_objects',\n label: 'Objects',\n data: { provider: 'object', object: 'sys_metadata' },\n columns: ['name', 'namespace', 'scope', 'managed_by', 'state', 'updated_at'],\n filter: [{ field: 'type', operator: 'equals', value: 'object' }],\n sort: [{ field: 'name', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n only_fields: {\n type: 'grid',\n name: 'only_fields',\n label: 'Fields',\n data: { provider: 'object', object: 'sys_metadata' },\n columns: ['name', 'namespace', 'scope', 'managed_by', 'state', 'updated_at'],\n filter: [{ field: 'type', operator: 'equals', value: 'field' }],\n sort: [{ field: 'name', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n all_metadata: {\n type: 'grid',\n name: 'all_metadata',\n label: 'All',\n data: { provider: 'object', object: 'sys_metadata' },\n columns: ['name', 'type', 'namespace', 'scope', 'state', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_metadata_history — Metadata Version History / Event Log\n *\n * Append-only durable log of every overlay change made through\n * `SysMetadataRepository.put` / `delete` (ADR-0008 §10 M1). Each row is a\n * single event in the per-organisation event log; rows are NEVER\n * mutated after insertion. The legacy `DatabaseLoader` writes the same\n * shape from its own put/restore code paths.\n *\n * ─────────────────────────────────────────────────────────────────────\n * Key design points (ADR-0008 §0 amendment + M1):\n *\n * • Keyed by `(organization_id, type, name)` only — `environment_id` was\n * removed in the branch/project-removal amendment. The original\n * `metadata_id` column (a downgraded plain-text version of the old\n * `sys_metadata.id` FK) was removed in the M1 follow-up — joins go\n * through `(organization_id, type, name, version)` exclusively.\n *\n * • `event_seq` is the per-org monotonic event-log cursor. Producers\n * compute `MAX(event_seq) + 1 WHERE organization_id = X` inside the\n * same transaction as the parent `sys_metadata` write.\n *\n * • `version` is the per-(org,type,name) lineage counter. Producers\n * compute `MAX(version) + 1 WHERE organization_id = X AND type = T\n * AND name = N` so delete + recreate continues incrementing instead\n * of restarting at 1.\n *\n * • `metadata` / `checksum` are nullable — DELETE rows have no body or\n * hash. Readers must tolerate null on both columns.\n *\n * • `source` records the producer ('sys-metadata-repo', 'fs',\n * 'studio', …) and feeds MetadataEvent.source on history() reads.\n *\n * Indexes are purpose-built for the two dominant read patterns:\n * 1. per-item history view → `(organization_id, type, name, version)`\n * 2. org-wide event replay → `(organization_id, event_seq)`\n * ─────────────────────────────────────────────────────────────────────\n */\nexport const SysMetadataHistoryObject = ObjectSchema.create({\n name: 'sys_metadata_history',\n label: 'Metadata History',\n pluralLabel: 'Metadata History',\n icon: 'history',\n isSystem: true,\n managedBy: 'system',\n description: 'Durable event log of metadata overlay changes (per-org, append-only)',\n\n fields: {\n /** Primary Key (UUID) */\n id: Field.text({\n label: 'ID',\n required: true,\n readonly: true,\n }),\n\n /** Per-org monotonic event sequence (durable cursor for replay). */\n event_seq: Field.number({\n label: 'Event Seq',\n required: true,\n readonly: true,\n description: 'Per-organization monotonic event log cursor.',\n }),\n\n /** Machine name (denormalized for easier querying) */\n name: Field.text({\n label: 'Name',\n required: true,\n searchable: true,\n readonly: true,\n maxLength: 255,\n }),\n\n /** Metadata type (denormalized for easier querying) */\n type: Field.text({\n label: 'Metadata Type',\n required: true,\n searchable: true,\n readonly: true,\n maxLength: 100,\n }),\n\n /** Per-(org,type,name) lineage counter at this snapshot. */\n version: Field.number({\n label: 'Version',\n required: true,\n readonly: true,\n }),\n\n /** Type of operation that created this history entry */\n operation_type: Field.select(['create', 'update', 'publish', 'revert', 'delete'], {\n label: 'Operation Type',\n required: true,\n readonly: true,\n }),\n\n /**\n * Historical metadata snapshot (JSON payload).\n * Null for `operation_type = 'delete'` — the row carries no body.\n */\n metadata: Field.textarea({\n label: 'Metadata',\n required: false,\n readonly: true,\n description: 'JSON-serialized metadata snapshot at this version (null for deletes).',\n }),\n\n /** SHA-256 checksum of metadata content (null for deletes). */\n checksum: Field.text({\n label: 'Checksum',\n required: false,\n readonly: true,\n maxLength: 80,\n }),\n\n /** Checksum of the previous version (null for the first event). */\n previous_checksum: Field.text({\n label: 'Previous Checksum',\n required: false,\n readonly: true,\n maxLength: 80,\n }),\n\n /** Human-readable description of changes (= MetadataEvent.message). */\n change_note: Field.textarea({\n label: 'Change Note',\n required: false,\n readonly: true,\n description: 'Description of what changed in this version.',\n }),\n\n /**\n * Producer of the event ('sys-metadata-repo', 'fs', 'studio',\n * 'api', …). Defaults to 'sys-metadata-repo' on the canonical\n * write path; preserved on history() reads as MetadataEvent.source.\n */\n source: Field.text({\n label: 'Source',\n required: false,\n readonly: true,\n maxLength: 64,\n }),\n\n /** Organization ID for multi-tenant isolation */\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n readonly: true,\n description: 'Organization for multi-tenant isolation.',\n }),\n\n /** User who made this change (= MetadataEvent.actor). */\n recorded_by: Field.lookup('sys_user', {\n label: 'Recorded By',\n required: false,\n readonly: true,\n }),\n\n /** When was this version recorded */\n recorded_at: Field.datetime({\n label: 'Recorded At',\n required: true,\n readonly: true,\n }),\n },\n\n indexes: [\n { fields: ['organization_id', 'event_seq'], unique: true },\n { fields: ['organization_id', 'type', 'name', 'version'], unique: true },\n { fields: ['organization_id', 'type', 'name', 'recorded_at'] },\n // ADR-0009: getByHash() lookup — execution-pinned types resolve a\n // historical body by content hash via this index.\n { fields: ['organization_id', 'type', 'name', 'checksum'] },\n { fields: ['type', 'name'] },\n { fields: ['recorded_at'] },\n { fields: ['operation_type'] },\n ],\n\n enable: {\n trackHistory: false,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list'],\n trash: false,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_metadata_commit — Package-scoped commit log (ADR-0067)\n *\n * One row per authoring TURN (AI apply or Studio batch) that promoted a group\n * of metadata changes together. A commit GROUPS the per-item events already\n * recorded in `sys_metadata_history` (by their `event_seq` range) into the unit\n * a user actually reasons about — \"the change my last instruction made\" — and\n * is the handle for `revertCommit` / `rollbackToPackageCommit`.\n *\n * Append-only, like `sys_metadata_history`: a revert is recorded as a NEW commit\n * (`operation = 'revert'`, `parent_commit_id` = the reverted commit), never an\n * in-place mutation. History therefore never loses the record of what happened.\n *\n * The `items` payload captures, per artifact, exactly what `revertCommit` needs\n * to undo the commit losslessly: whether the artifact EXISTED before this commit\n * (`existedBefore`) and, if so, the lineage `version` it should be restored to\n * (`prevVersion`). A created-by-this-commit artifact reverts by soft-removal;\n * a modified one reverts by `restoreVersion(prevVersion)`.\n */\nexport const SysMetadataCommitObject = ObjectSchema.create({\n name: 'sys_metadata_commit',\n label: 'Metadata Commit',\n pluralLabel: 'Metadata Commits',\n icon: 'git-commit',\n isSystem: true,\n managedBy: 'system',\n description: 'Package-scoped commit log grouping a turn’s metadata changes (ADR-0067).',\n\n fields: {\n /** Primary Key — the commit id. */\n id: Field.text({\n label: 'ID',\n required: true,\n readonly: true,\n maxLength: 64,\n }),\n\n /** The app/package this commit belongs to (the unit a user reverts). */\n package_id: Field.text({\n label: 'Package',\n required: false,\n searchable: true,\n readonly: true,\n maxLength: 255,\n }),\n\n /** apply = a turn promoted changes; revert = this commit undid another. */\n operation: Field.select(['apply', 'revert'], {\n label: 'Operation',\n required: true,\n readonly: true,\n }),\n\n /** Human-readable summary — for AI turns, the user's prompt. */\n message: Field.textarea({\n label: 'Message',\n required: false,\n readonly: true,\n description: 'Change summary; for AI turns, the user instruction that produced it.',\n }),\n\n /** Producing actor (user id, or an AI principal like \"ai:claude\"). */\n actor: Field.text({\n label: 'Actor',\n required: false,\n readonly: true,\n maxLength: 255,\n }),\n\n /** AI model that authored the turn (absent for human/CLI commits). */\n ai_model: Field.text({\n label: 'AI Model',\n required: false,\n readonly: true,\n maxLength: 100,\n }),\n\n /** For a revert commit, the id of the commit it reverted. */\n parent_commit_id: Field.text({\n label: 'Parent Commit',\n required: false,\n readonly: true,\n maxLength: 64,\n }),\n\n /** First `sys_metadata_history.event_seq` covered by this commit. */\n event_seq_start: Field.number({\n label: 'Event Seq Start',\n required: false,\n readonly: true,\n }),\n\n /** Last `sys_metadata_history.event_seq` covered by this commit. */\n event_seq_end: Field.number({\n label: 'Event Seq End',\n required: false,\n readonly: true,\n }),\n\n /**\n * JSON array of the artifacts this commit touched, with the data\n * `revertCommit` needs: [{ type, name, existedBefore, prevVersion }].\n */\n items: Field.textarea({\n label: 'Items',\n required: false,\n readonly: true,\n description: 'JSON: [{ type, name, existedBefore, prevVersion }] — the revert plan.',\n }),\n\n /** Number of artifacts in `items` (denormalized for list views). */\n item_count: Field.number({\n label: 'Item Count',\n required: false,\n readonly: true,\n }),\n\n /** Organization ID for multi-tenant isolation. */\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n readonly: true,\n description: 'Organization for multi-tenant isolation.',\n }),\n\n /** When the commit was recorded. */\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n readonly: true,\n }),\n },\n\n indexes: [\n // List a package's history newest-first (the timeline read pattern).\n { fields: ['organization_id', 'package_id', 'created_at'] },\n // Org-wide commit replay / audit.\n { fields: ['organization_id', 'created_at'] },\n { fields: ['parent_commit_id'] },\n ],\n\n enable: {\n trackHistory: false,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list'],\n trash: false,\n },\n});\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_metadata_audit — Metadata Protection Audit Log\n *\n * Append-only audit trail for every metadata write **decision** — both\n * allowed writes and refused ones. Introduced in ADR-0010 Phase 1 as\n * the compliance surface for the new `_lock` enforcement: every PUT /\n * publish / rollback / delete that targets `sys_metadata` writes one\n * row here describing the outcome and the lock state that produced it.\n *\n * Distinct from `sys_metadata_history`:\n * - `sys_metadata_history` records the **body** of every successful\n * write (full JSON snapshot + checksum). Used for rollback,\n * diff, and history() reads.\n * - `sys_metadata_audit` records the **decision** (who tried what,\n * what code was emitted, was a lock involved). Refused writes\n * never reach history; they DO reach audit.\n *\n * Designed as the smallest possible row that satisfies the four\n * compliance questions of metadata governance:\n * 1. Who tried to change what? → actor + type + name\n * 2. When? → occurred_at\n * 3. What outcome? → outcome + code\n * 4. Was an override involved? → lock_overridden + lock_state\n *\n * Indexed on `(organization_id, occurred_at)` for the per-org timeline\n * query and `(type, name, occurred_at)` for the per-item history tab\n * Studio surfaces on the editor page.\n */\nexport const SysMetadataAuditObject = ObjectSchema.create({\n name: 'sys_metadata_audit',\n label: 'Metadata Audit',\n pluralLabel: 'Metadata Audit',\n icon: 'shield-check',\n isSystem: true,\n managedBy: 'append-only',\n description: 'Append-only audit trail of metadata write decisions (ADR-0010).',\n\n fields: {\n /** Primary Key (UUID) */\n id: Field.text({\n label: 'ID',\n required: true,\n readonly: true,\n }),\n\n /** When the decision was made (ISO-8601 UTC). */\n occurred_at: Field.datetime({\n label: 'Occurred At',\n required: true,\n readonly: true,\n }),\n\n /** Acting principal (user id, system id, or 'system'). */\n actor: Field.text({\n label: 'Actor',\n required: true,\n readonly: true,\n maxLength: 255,\n description: 'Acting principal — user id, system id, or \"system\".',\n }),\n\n /** Code path that produced the decision (e.g. `protocol.saveMetaItem`). */\n source: Field.text({\n label: 'Source',\n required: false,\n readonly: true,\n maxLength: 128,\n }),\n\n /** Metadata type (singular, e.g. `app`, `object`, `view`). */\n type: Field.text({\n label: 'Metadata Type',\n required: true,\n readonly: true,\n searchable: true,\n maxLength: 100,\n }),\n\n /** Item machine name. */\n name: Field.text({\n label: 'Name',\n required: true,\n readonly: true,\n searchable: true,\n maxLength: 255,\n }),\n\n /** Organization for multi-tenant filtering. NULL for env-wide writes. */\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n readonly: true,\n }),\n\n /** Operation kind. */\n operation: Field.select(['save', 'publish', 'rollback', 'delete', 'reset'], {\n label: 'Operation',\n required: true,\n readonly: true,\n }),\n\n /** Decision outcome — allowed, denied (refused), or forced (bypassed via override). */\n outcome: Field.select(['allowed', 'denied', 'forced'], {\n label: 'Outcome',\n required: true,\n readonly: true,\n }),\n\n /**\n * Machine-readable code for the decision:\n * - on `allowed`: `'ok'`\n * - on `denied`: `'not_overridable'` | `'not_creatable'` |\n * `'item_locked'` | `'invalid_metadata'` | `'destructive_change'` |\n * `'metadata_conflict'`\n * - on `forced`: `'lock_override'` (Phase 3)\n */\n code: Field.text({\n label: 'Code',\n required: true,\n readonly: true,\n maxLength: 64,\n }),\n\n /**\n * Lock state observed at the time of the decision (`none` if the\n * item carried no `_lock`). Captured even on `allowed` rows so\n * later compliance queries can see \"what was the lock state when\n * this write succeeded\".\n */\n lock_state: Field.select(['none', 'no-overlay', 'no-delete', 'full'], {\n label: 'Lock State',\n required: false,\n readonly: true,\n }),\n\n /** True when the write succeeded by bypassing a lock (Phase 3). */\n lock_overridden: Field.boolean({\n label: 'Lock Overridden',\n required: false,\n readonly: true,\n }),\n\n /** Optional request correlation id for tracing. */\n request_id: Field.text({\n label: 'Request ID',\n required: false,\n readonly: true,\n maxLength: 128,\n }),\n\n /** Optional free-form context (e.g. brief diff summary). */\n note: Field.textarea({\n label: 'Note',\n required: false,\n readonly: true,\n }),\n },\n\n indexes: [\n { fields: ['organization_id', 'occurred_at'] },\n { fields: ['type', 'name', 'occurred_at'] },\n { fields: ['actor', 'occurred_at'] },\n { fields: ['outcome'] },\n ],\n\n enable: {\n trackHistory: false,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list'],\n trash: false,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_view_definition — Runtime View Storage (\"Object has-many View\")\n *\n * Persists view definitions authored at RUNTIME by end users — the `shared`\n * and `personal` layers of the view model (see spec `ViewItemSchema`). The\n * `package` layer ships from `*.view.ts` source and lives in the metadata\n * registry; it is NOT stored here.\n *\n * Why a dedicated object (not the `sys_metadata` overlay): runtime views are\n * data, not admin metadata customisation. They carry an `owner`, a visibility\n * `scope`, and are queried per-user — so they belong in a typed, permissioned\n * ObjectQL object rather than the global metadata registry (which a personal\n * \"My hot leads\" view should never pollute).\n *\n * CRUD flows through ObjectQL's generic data API (`/api/v1/data/\n * sys_view_definition`) — no bespoke per-view REST endpoints. The runtime\n * switcher reads the `package` layer via `GET /meta/view?object=<object>` and\n * merges these rows client-side, filtered to `scope='shared'` OR\n * `owner=<current user>`.\n *\n * This is a system object (isSystem: true) — protected from deletion and\n * auto-provisioned on first use.\n */\nexport const SysViewDefinitionObject = ObjectSchema.create({\n name: 'sys_view_definition',\n label: 'View Definition',\n pluralLabel: 'View Definitions',\n icon: 'layout-grid',\n isSystem: true,\n description:\n 'Runtime-authored view definitions (shared / personal layers). The package layer ships from source.',\n\n fields: {\n /** Primary Key (UUID) */\n id: Field.text({ label: 'ID', required: true, readonly: true }),\n\n /**\n * Globally-unique qualified view id, `<object>.<viewKey>`, matching the\n * spec `ViewItemSchema.name`. For personal views the runtime may suffix\n * to keep it unique per owner.\n */\n name: Field.text({\n label: 'Name',\n required: true,\n searchable: true,\n maxLength: 255,\n }),\n\n /** Bound object — the foreign key used to aggregate views for the switcher. */\n object: Field.text({\n label: 'Object',\n required: true,\n searchable: true,\n maxLength: 255,\n }),\n\n /** Whether `config` is a ListView (list family) or a FormView. */\n view_kind: Field.select(['list', 'form'], {\n label: 'View Kind',\n required: true,\n defaultValue: 'list',\n }),\n\n /** Display label (plain string; i18n keys also accepted). */\n label: Field.text({ label: 'Label', required: false, maxLength: 255 }),\n\n /** Whether this is the object's default view in the switcher. */\n is_default: Field.boolean({ label: 'Is Default', required: false, defaultValue: false }),\n\n /** Sort order within the object's switcher / left rail. */\n view_order: Field.number({ label: 'Order', required: false, defaultValue: 0 }),\n\n /**\n * Identity layer. Only `shared` and `personal` are stored at runtime;\n * `package` views come from source.\n */\n scope: Field.select(['shared', 'personal'], {\n label: 'Scope',\n required: true,\n defaultValue: 'personal',\n }),\n\n /** Owner user id — set when scope = personal; null for shared. */\n owner: Field.text({ label: 'Owner', required: false, maxLength: 255 }),\n\n /** Hidden from the switcher (per-user / per-org declutter). */\n hidden: Field.boolean({ label: 'Hidden', required: false, defaultValue: false }),\n\n /** The ListView / FormView configuration payload. */\n config: Field.json({\n label: 'Config',\n required: true,\n description: 'ListView or FormView configuration (matches spec ViewItem.config).',\n }),\n\n /** Organization for multi-tenant isolation. */\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n description: 'Organization for multi-tenant isolation.',\n }),\n\n /** Lifecycle state. */\n state: Field.select(['draft', 'active', 'archived'], {\n label: 'State',\n required: false,\n defaultValue: 'active',\n }),\n\n /** Audit fields. */\n created_by: Field.lookup('sys_user', { label: 'Created By', required: false, readonly: true }),\n created_at: Field.datetime({ label: 'Created At', required: false, readonly: true }),\n updated_by: Field.lookup('sys_user', { label: 'Updated By', required: false }),\n updated_at: Field.datetime({ label: 'Updated At', required: false }),\n },\n\n indexes: [\n // A given view name is unique per (organization, owner) among active rows —\n // a shared view (owner NULL) and each user's personal views don't collide.\n {\n name: 'idx_sys_view_def_active',\n fields: ['name', 'organization_id', 'owner'],\n unique: true,\n partial: \"state = 'active'\",\n },\n // The switcher query: views for one object within a tenant.\n { name: 'idx_sys_view_def_object', fields: ['organization_id', 'object'] },\n { fields: ['scope'] },\n { fields: ['owner'] },\n { fields: ['state'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: false,\n },\n});\n"]}