import { M as MetaRef, a as MetadataRepository, b as MetadataItem, P as PutOptions, c as PutResult, D as DeleteOptions, d as DeleteResult, L as ListFilter, e as MetadataItemHeader, H as HistoryOptions, f as MetadataEvent, W as WatchFilter } from './repository-Dl3EudaY.js'; export { g as LAYER_SOURCE, h as MetaRefSchema, i as MetadataEventSchema, j as MetadataItemSchema, k as MetadataOp, l as MetadataOpSchema, m as MetadataType, n as MetadataTypeSchema, o as MetadataWriteIntent, r as refKey } from './repository-Dl3EudaY.js'; import * as _objectstack_spec_data from '@objectstack/spec/data'; import { AuditProvenanceField } from '@objectstack/spec/data'; export { AUDIT_FIELD_DEFS, InjectedColumnProvenance, OWNER_FIELD_DEF, OWNING_BUSINESS_UNIT_FIELD_DEF, TENANT_SCOPE_FIELD_DEF, injectedSystemColumnDefs, platformProvisionsStorage, resolveInjectedColumnProvenance, unprovisionedInjectedColumns } from '@objectstack/spec/data'; import 'zod'; /** * Typed errors thrown by Repository implementations. Implementations must * use these exact classes (or subclasses) so callers can `instanceof` * across package boundaries. */ declare class MetadataError extends Error { readonly code: string; constructor(code: string, message: string); } /** * Thrown when a `put` or `delete` operation's `parentVersion` does not * match the current HEAD. Maps to HTTP 412 Precondition Failed. */ declare class ConflictError extends MetadataError { readonly ref: MetaRef; readonly expectedParent: string | null; readonly actualHead: string | null; constructor(ref: MetaRef, expectedParent: string | null, actualHead: string | null); } /** Thrown when a read targets a missing item. Maps to HTTP 404. */ declare class NotFoundError extends MetadataError { readonly ref: MetaRef; constructor(ref: MetaRef); } /** * Thrown when a `put`'s spec fails Zod validation against the canonical * schema for the metadata type. Maps to HTTP 422. */ declare class SchemaValidationError extends MetadataError { readonly ref: MetaRef; readonly issues: unknown; constructor(ref: MetaRef, issues: unknown); } /** Thrown for parent_branch / fork / merge edge cases. */ declare class BranchError extends MetadataError { constructor(message: string); } /** Stable JSON serialisation. See module-level doc for guarantees. */ declare function canonicalize(value: unknown): string; /** * Compute the canonical sha256 hash of a spec, returned as * `"sha256:<64-hex>"`. Equal hashes imply equal canonical forms. */ declare function hashSpec(value: unknown): string; /** * `InMemoryRepository` — reference implementation of `MetadataRepository` * backed by plain JS Maps. Used by: * * - tests (parameterized contract-test suite) * - edge / serverless runtimes (no FS, no DB) * - `LayeredRepository` fallbacks * * State model * ─────────── * items : refKey → MetadataItem (current head) * logs : org → MetadataEvent[] (append-only, monotonic per org) * seqs : org → number * * `watch()` is implemented over a simple subscriber list. Each subscriber * receives a deep-copy of the event so they cannot mutate the log. */ interface InMemoryRepositoryOptions { /** Optional clock injection for deterministic tests. Default: Date.now. */ now?: () => Date; } declare class InMemoryRepository implements MetadataRepository { private readonly items; /** Per-org event log. */ private readonly logs; /** Next seq per org. */ private readonly seqs; private readonly subscribers; private readonly now; constructor(opts?: InMemoryRepositoryOptions); get(ref: MetaRef): Promise; getByHash(ref: MetaRef, hash: string): Promise; put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise; delete(ref: MetaRef, opts: DeleteOptions): Promise; list(filter: ListFilter): AsyncIterable; history(ref: MetaRef, opts?: HistoryOptions): AsyncIterable; watch(filter: WatchFilter, since?: number): AsyncIterable; private bumpSeq; private appendEvent; private orgKeysMatching; } /** * `MetadataCache` — bounded, event-invalidated LRU sitting in front of a * `MetadataRepository`. See ADR-0008 §2.5. * * Design contract * ─────────────── * * 1. **Lazy fill.** Cache entries are only created on first read miss. * No bulk preload — that would defeat the whole point of being * bounded. * 2. **Event-driven invalidation.** The cache subscribes to * `repo.watch({...})` and drops or replaces affected entries * whenever the repository emits an event. Stale reads are bounded * by the event-propagation latency of the underlying repo. * 3. **Bounded.** Both `maxEntries` and `maxBytes` are enforced; LRU * eviction happens on `set()` when either limit is exceeded. * 4. **Coherent under races.** Concurrent `get()`s for the same key * coalesce onto a single backend fetch (the "thundering herd" * fix). If an invalidation event arrives during an in-flight * fetch, the resulting value is discarded — the next read fetches * fresh. * 5. **Negative caching.** A miss (repo returned `null`) is also * cached, with a smaller TTL semantics — it stays until an event * for that ref arrives. This makes "does X exist?" cheap during * tight loops without compromising correctness. */ interface MetadataCacheOptions { /** Maximum number of entries to keep. Default: 1024. */ maxEntries?: number; /** * Maximum approximate body size in bytes. Default: 8 MiB. Each entry's * size is estimated from `JSON.stringify(item.body).length`. */ maxBytes?: number; /** * Watch filter. Only events matching this filter invalidate cache * entries. Default: no filter (all events). */ watchFilter?: WatchFilter; } interface CacheStats { entries: number; bytes: number; hits: number; misses: number; invalidations: number; /** Reads that arrived while a fetch was already in-flight for the same key. */ coalesced: number; } declare class MetadataCache { private readonly repo; private readonly maxEntries; private readonly maxBytes; private readonly watchFilter; /** LRU is implemented via insertion-order Map; touch on get/set. */ private readonly entries; private bytes; /** De-duplicate concurrent fetches for the same key. */ private readonly inflight; /** * Generation counter incremented on every invalidation. Each in-flight * fetch captures the gen at start; if the gen changes before it * resolves, the result is NOT cached. */ private readonly fetchGen; private watchIterator; private watchClosed; private watchLoop; private readonly stats; constructor(repo: MetadataRepository, opts?: MetadataCacheOptions); /** * Start the background watch subscription. Idempotent; safe to call * multiple times. Caller is responsible for calling `close()` when * the cache is no longer needed. */ start(): void; /** Tear down the watch subscription and clear in-flight tracking. */ close(): Promise; /** Read with cache. Coalesces concurrent reads for the same key. */ get(ref: MetaRef): Promise; /** Drop a single entry by ref. */ invalidate(ref: MetaRef): void; /** Drop the entire cache (e.g. on a reset). */ clear(): void; getStats(): Readonly; private applyEvent; private cacheSet; private evictIfNeeded; private bumpGen; } /** * `LayeredRepository` — composes N child `MetadataRepository`s into a * single read-through stack. See ADR-0008 §10 PR-5. * * Read semantics * ────────────── * - `get(ref)` walks the layers top-to-bottom; first non-null wins. * - `list()` deduplicates by `refKey(ref)`, preferring the top layer. * - `history()` and `watch()` merge events from all layers, each * tagged with the source layer label in `evt.source` * (`