@objectstack/metadata 17.0.0-rc.6 → 17.1.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":["../src/routes/hmr-routes.ts","../src/index.ts","../src/metadata-manager.ts","../src/serializers/json-serializer.ts","../src/serializers/yaml-serializer.ts","../src/serializers/typescript-serializer.ts","../src/loaders/database-loader.ts","../src/utils/metadata-history-utils.ts","../src/utils/lru-cache.ts","../src/utils/schema-sync-errors.ts","../src/migrations/migrate-project-id-to-environment-id.ts","../src/endpoint-matcher.ts","../src/stored-envelope.ts","../src/plugin.ts","../src/node-metadata-manager.ts","../src/loaders/filesystem-loader.ts","../src/loaders/memory-loader.ts","../src/loaders/remote-loader.ts","../src/utils/history-cleanup.ts","../src/migration/index.ts","../src/migration/executor.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata HMR (Hot Module Replacement) SSE endpoint\n *\n * Streams metadata change events to connected clients (Studio) over\n * Server-Sent Events. Closes the \"agent edits a source file → Studio\n * preview refreshes\" loop without requiring a manual page reload.\n *\n * Routes:\n * GET /api/v1/dev/metadata-events — stream of events\n * POST /api/v1/dev/metadata-events — manual reload trigger\n * body (optional): { reason?: string,\n * changed?: string[] }\n *\n * Event payloads (JSON):\n * - metadata-change: { type, metadataType, name, path?, timestamp }\n * - reload: { reason, timestamp, changed?: string[] }\n *\n * Heartbeat: `: ping` SSE comment lines every 15s.\n *\n * Two event sources feed the same in-process broadcast hub:\n * 1. The MetadataManager filesystem watcher (when `watch: true`).\n * 2. POST /api/v1/dev/metadata-events — used by external watch-recompile\n * pipelines (e.g. `os dev` watching TS sources) to invalidate\n * previews after rebuilding the artifact.\n */\n\nimport type { MetadataManager } from '../metadata-manager.js';\n\ninterface ChangeEvent {\n kind: 'metadata-change';\n type: 'added' | 'changed' | 'deleted';\n metadataType: string;\n name: string;\n path?: string;\n timestamp: number;\n /** Canonical repo seq (ADR-0008); absent for legacy chokidar events. */\n seq?: number;\n}\n\ninterface ReloadEvent {\n kind: 'reload';\n reason: string;\n changed?: string[];\n timestamp: number;\n}\n\ntype BroadcastEvent = ChangeEvent | ReloadEvent;\n\ntype Listener = (evt: BroadcastEvent) => void;\n\n/**\n * Hub returned by `registerMetadataHmrRoutes`. Callers (e.g. MetadataPlugin)\n * can use `broadcastReload()` from elsewhere — for example, after reloading\n * an artifact from disk — to push a reload event to all connected clients.\n */\nexport interface MetadataHmrHub {\n broadcastReload(reason: string, changed?: string[]): void;\n /**\n * Hook a custom handler that runs when POST is called. Useful for\n * triggering an artifact reload before the broadcast goes out.\n * Receives the parsed request body. May be async.\n */\n setOnPostReload(fn: (body: { reason?: string; changed?: string[] }) => void | Promise<void>): void;\n listenerCount(): number;\n}\n\nexport function registerMetadataHmrRoutes(\n app: any,\n manager: MetadataManager,\n options: { path?: string } = {},\n): MetadataHmrHub {\n const routePath = options.path ?? '/api/v1/dev/metadata-events';\n\n // In-process broadcast hub. Each SSE connection registers a listener;\n // both the FS watcher and the POST handler call into the hub.\n const listeners = new Set<Listener>();\n const broadcast = (evt: BroadcastEvent) => {\n for (const l of listeners) {\n try { l(evt); } catch { /* swallow — one bad listener shouldn't break others */ }\n }\n };\n\n // Wire FS watcher → hub for every currently-registered metadata type.\n // Captures `subscribe` once; if MetadataManager lacks it (older build)\n // we silently degrade to POST-only.\n let fsHookInstalled = false;\n const installFsHooks = async () => {\n if (fsHookInstalled) return;\n const mgr = manager as any;\n if (typeof mgr.subscribe !== 'function') {\n fsHookInstalled = true;\n return;\n }\n const types = await manager.getRegisteredTypes();\n for (const type of types) {\n mgr.subscribe(type, (evt: any) => {\n const ts = typeof evt.timestamp === 'string'\n ? Date.parse(evt.timestamp)\n : (evt.timestamp ?? Date.now());\n broadcast({\n kind: 'metadata-change',\n type: evt.type ?? 'changed',\n metadataType: evt.metadataType ?? type,\n name: evt.name ?? '',\n path: evt.path,\n timestamp: Number.isFinite(ts) ? ts : Date.now(),\n // Forward the canonical server-side sequence number when the\n // event originated from a MetadataRepository (ADR-0008). Legacy\n // chokidar-driven events have no seq — clients fall back to\n // their local counter in that case.\n ...(typeof evt.seq === 'number' ? { seq: evt.seq } : {}),\n } as BroadcastEvent);\n });\n }\n fsHookInstalled = true;\n };\n // Fire-and-forget; the first connection will await it in its handler\n // anyway via getRegisteredTypes().\n installFsHooks().catch(() => { /* noop */ });\n\n let onPostReload: ((body: { reason?: string; changed?: string[] }) => void | Promise<void>) | null = null;\n\n // ── GET: SSE stream ────────────────────────────────────────────────\n app.get(routePath, async (c: any) => {\n // Make sure FS hooks are installed even if installFsHooks() raced.\n await installFsHooks().catch(() => { /* noop */ });\n const types = await manager.getRegisteredTypes().catch(() => [] as string[]);\n\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const enc = new TextEncoder();\n let closed = false;\n\n const safeEnqueue = (chunk: string) => {\n if (closed) return;\n try { controller.enqueue(enc.encode(chunk)); }\n catch { closed = true; }\n };\n\n const listener: Listener = (evt) => {\n if (closed) return;\n const eventName = evt.kind === 'reload' ? 'reload' : 'metadata-change';\n safeEnqueue(`event: ${eventName}\\ndata: ${JSON.stringify(evt)}\\n\\n`);\n };\n listeners.add(listener);\n\n safeEnqueue(`event: ready\\ndata: ${JSON.stringify({ types, timestamp: Date.now() })}\\n\\n`);\n\n const heartbeat = setInterval(() => {\n safeEnqueue(`: ping ${Date.now()}\\n\\n`);\n }, 15_000);\n\n const cleanup = () => {\n if (closed) return;\n closed = true;\n clearInterval(heartbeat);\n listeners.delete(listener);\n try { controller.close(); } catch { /* noop */ }\n };\n\n const signal: AbortSignal | undefined = c.req?.raw?.signal;\n if (signal) {\n if (signal.aborted) cleanup();\n else signal.addEventListener('abort', cleanup, { once: true });\n }\n },\n });\n\n return new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream; charset=utf-8',\n 'Cache-Control': 'no-cache, no-transform',\n 'Connection': 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n });\n });\n\n // ── POST: manual reload trigger ────────────────────────────────────\n // The CLI's watch-recompile loop posts here after rebuilding the\n // artifact. Optional body: { reason?: string, changed?: string[] }.\n app.post(routePath, async (c: any) => {\n let body: { reason?: string; changed?: string[] } = {};\n try {\n // Hono: c.req.json() throws on empty body — guard it.\n const ct = c.req?.header?.('content-type') ?? '';\n if (typeof c.req?.json === 'function' && ct.includes('json')) {\n body = await c.req.json();\n }\n } catch { /* empty / invalid body OK */ }\n\n try {\n if (onPostReload) await onPostReload(body);\n } catch (e: any) {\n return new Response(\n JSON.stringify({ ok: false, error: e?.message ?? 'reload handler failed' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } },\n );\n }\n\n const reason = body.reason ?? 'manual-trigger';\n broadcast({\n kind: 'reload',\n reason,\n changed: body.changed,\n timestamp: Date.now(),\n });\n return new Response(\n JSON.stringify({ ok: true, listeners: listeners.size, reason }),\n { status: 200, headers: { 'Content-Type': 'application/json' } },\n );\n });\n\n return {\n broadcastReload(reason, changed) {\n broadcast({ kind: 'reload', reason, changed, timestamp: Date.now() });\n },\n setOnPostReload(fn) { onPostReload = fn; },\n listenerCount: () => listeners.size,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/metadata\n * \n * Metadata loading, saving, and persistence for ObjectStack.\n * Implements the IMetadataService contract from @objectstack/spec.\n */\n\n// Main Manager\nexport { MetadataManager, type WatchCallback, type MetadataManagerOptions } from './metadata-manager.js';\n\n// Plugin\nexport { MetadataPlugin } from './plugin.js';\n\n// Loaders\nexport { type MetadataLoader } from './loaders/loader-interface.js';\nexport { MemoryLoader } from './loaders/memory-loader.js';\nexport { RemoteLoader } from './loaders/remote-loader.js';\nexport { DatabaseLoader, type DatabaseLoaderOptions } from './loaders/database-loader.js';\n\n// Objects\nexport { SysMetadataObject, SysMetadataHistoryObject } from '@objectstack/metadata-core';\n\n// Routes\n// NOTE: `registerMetadataHistoryRoutes` (Hono-style) was removed —\n// the canonical history / publish / rollback / diff REST surface\n// lives in `packages/rest/src/rest-server.ts` and is wired by the\n// REST plugin on every running app.\n\n// Utils\nexport { calculateChecksum, generateSimpleDiff, generateDiffSummary } from './utils/metadata-history-utils.js';\nexport { HistoryCleanupManager } from './utils/history-cleanup.js';\n\n// Serializers\nexport { type MetadataSerializer, type SerializeOptions } from './serializers/serializer-interface.js';\nexport { JSONSerializer } from './serializers/json-serializer.js';\nexport { YAMLSerializer } from './serializers/yaml-serializer.js';\nexport * as Migration from './migration/index.js';\nexport { TypeScriptSerializer } from './serializers/typescript-serializer.js';\n\n// Re-export types from spec\nexport type {\n MetadataFormat,\n MetadataStats,\n MetadataLoadOptions,\n MetadataSaveOptions,\n MetadataLoadResult,\n MetadataSaveResult,\n MetadataWatchEvent,\n MetadataCollectionInfo,\n MetadataLoaderContract,\n MetadataManagerConfig,\n MetadataHistoryRecord,\n MetadataHistoryQueryOptions,\n MetadataHistoryQueryResult,\n MetadataDiffResult,\n MetadataHistoryRetentionPolicy,\n} from '@objectstack/spec/system';\n\n// Re-export IMetadataService contract.\n// [#4538] `MetadataExportOptions` / `MetadataImportOptions` moved into this\n// block: this package used to re-export the same-named system-entry bags\n// (`output`/`source`-flavored, removed with #4538) while `MetadataManager`\n// implements the contracts shapes — the public re-export was pointing at the\n// wrong declaration.\nexport type {\n IMetadataService,\n MetadataWatchCallback,\n MetadataWatchHandle,\n MetadataExportOptions,\n MetadataImportOptions,\n MetadataTypeInfo,\n MetadataImportResult,\n} from '@objectstack/spec/contracts';\n\n// Re-export kernel types for plugin protocol\nexport type {\n MetadataType,\n MetadataTypeRegistryEntry,\n MetadataPluginConfig,\n MetadataPluginManifest,\n MetadataQuery,\n MetadataQueryResult,\n MetadataValidationResult,\n MetadataBulkResult,\n MetadataDependency,\n} from '@objectstack/spec/kernel';\n\n// Re-export the new Repository contract (ADR-0008) so downstream consumers\n// (ObjectQL schema registry, Studio, CLI) can import from one place.\nexport type {\n MetadataRepository,\n MetadataEvent,\n MetadataItem,\n MetadataItemHeader,\n MetaRef,\n WatchFilter,\n HistoryOptions,\n} from '@objectstack/metadata-core';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata Manager\n * \n * Main orchestrator for metadata loading, saving, and persistence.\n * Implements the IMetadataService contract from @objectstack/spec.\n * Browser-compatible (Pure).\n */\n\nimport type {\n MetadataManagerConfig,\n MetadataLoadOptions,\n MetadataSaveOptions,\n MetadataSaveResult,\n MetadataWatchEvent,\n MetadataFormat,\n PackagePublishResult,\n MetadataHistoryQueryOptions,\n MetadataHistoryQueryResult,\n MetadataDiffResult,\n} from '@objectstack/spec/system';\nimport type {\n IMetadataService,\n MetadataWatchCallback,\n MetadataWatchHandle,\n MetadataExportOptions,\n MetadataImportOptions,\n MetadataImportResult,\n MetadataTypeInfo,\n MetadataWriteOptions,\n IRealtimeService,\n RealtimeEventPayload,\n IPubSub,\n Unsubscribe,\n} from '@objectstack/spec/contracts';\nimport type {\n MetadataQuery,\n MetadataQueryResult,\n MetadataValidationResult,\n MetadataBulkResult,\n MetadataDependency,\n MetadataTypeRegistryEntryParsed,\n} from '@objectstack/spec/kernel';\nimport type { MetadataOverlay } from '@objectstack/spec/kernel';\nimport { getMetadataTypeActions } from '@objectstack/spec/kernel';\nimport {\n MetadataEventType,\n MetadataEventSchema,\n type MetadataEvent as RealtimeMetadataEvent,\n} from '@objectstack/spec/api';\n// [#5189, #5040 E7b] The endpoint publish gates, reused verbatim — see\n// `gateApiItemsForPublish`.\nimport {\n ApiEndpointSchema,\n validateApiEndpointDeclarations,\n type ApiEndpoint,\n} from '@objectstack/spec/api';\nimport { createLogger, type Logger } from '@objectstack/core';\nimport { JSONSerializer } from './serializers/json-serializer.js';\nimport { YAMLSerializer } from './serializers/yaml-serializer.js';\nimport { TypeScriptSerializer } from './serializers/typescript-serializer.js';\nimport type { MetadataSerializer } from './serializers/serializer-interface.js';\nimport type { IDataDriver, IDataEngine } from '@objectstack/spec/contracts';\nimport type { MetadataLoader } from './loaders/loader-interface.js';\nimport { DatabaseLoader } from './loaders/database-loader.js';\nimport { generateSimpleDiff, generateDiffSummary } from './utils/metadata-history-utils.js';\nimport type {\n MetadataRepository,\n MetadataEvent,\n MetaRef,\n} from '@objectstack/metadata-core';\nimport { EndpointMatcher } from './endpoint-matcher.js';\n// [#5309] The stored ENVELOPE / authored BODY split — peeled before any spec\n// schema parses a stored row. See `stored-envelope.ts`.\nimport { peelStoredEnvelope } from './stored-envelope.js';\nimport type { ApiEndpointMatch } from '@objectstack/spec/contracts';\n\n/**\n * Watch callback function (legacy)\n */\nexport type WatchCallback = (event: MetadataWatchEvent) => void | Promise<void>;\n\n/**\n * [#5654] The two methods `capabilities.write` promises on a `datasource:`\n * loader, in the order an item meets them: written by\n * {@link MetadataManager.register}, taken back out by\n * {@link MetadataManager.unregister}. Both are `?` on {@link MetadataLoader}\n * because the other protocols legitimately have neither.\n */\nexport type WritableLoaderMethod = 'save' | 'delete';\n\nconst WRITABLE_LOADER_METHODS: readonly WritableLoaderMethod[] = ['save', 'delete'];\n\n/** How the message names each method, and what the author has to write. */\nconst WRITABLE_LOADER_METHOD_SIGNATURE: Record<WritableLoaderMethod, string> = {\n save: 'save(type: string, name: string, data: any, options?: MetadataSaveOptions): Promise<MetadataSaveResult>',\n delete: 'delete(type: string, name: string): Promise<void>',\n};\n\n/**\n * [#5276, #5654] The registration gate's message for a loader that declares it\n * can be written to but cannot actually carry out one (or both) halves of that\n * write.\n *\n * Built here rather than inline so the gate and its tests quote one text, in the\n * shape AGENTS.md → \"Degradation log levels\" asks of a loud failure: the\n * **consequence** (concretely, and that the system keeps looking healthy) and\n * the **fix** (both ways out, so the author does not have to guess which one\n * their loader wants).\n *\n * `missing` is the subset of {@link WRITABLE_LOADER_METHODS} the loader does not\n * implement; each one contributes its own consequence sentence, because the two\n * failures are durability failures in opposite directions — a `save`-less loader\n * loses the row that was never written, a `delete`-less one keeps the row that\n * was supposed to go.\n */\nexport function buildWritableLoaderMissingMethodsMessage(\n loaderName: string,\n missing: readonly WritableLoaderMethod[],\n): string {\n const missingPhrase =\n missing.length === 2\n ? 'implements neither a `save()` nor a `delete()` method'\n : `implements no \\`${missing[0]}()\\` method`;\n\n const consequences = missing.map((method) =>\n method === 'save'\n ? 'Registered as-is, every write would be a silent lie: `register()` skips a loader that cannot save, then ' +\n 'writes the in-memory registry, invalidates the list cache, announces a `created`/`updated` event and ' +\n 'notifies watchers, so the caller (Studio/Setup, REST PUT, the CLI, a package publish) is told the write ' +\n `succeeded while nothing ever reaches \\`${loaderName}\\` — the item reads back correctly for the life of ` +\n 'this process and is gone at the next restart, with nothing to retry it. '\n : 'Registered as-is, every deletion would be a silent lie: `unregister()` skips a loader that cannot delete, ' +\n 'then drops the registry entry, invalidates the list cache and announces a `deleted` event, so the caller ' +\n '(Studio/Setup, REST DELETE, the CLI, a package teardown) is told the delete succeeded while the row stays ' +\n `in \\`${loaderName}\\` and is read straight back out by the very next \\`list()\\`/\\`get()\\` — across ` +\n 'restarts, with nothing to retry it. ',\n );\n\n const repair = missing\n .map((method) => `\\`${WRITABLE_LOADER_METHOD_SIGNATURE[method]}\\``)\n .join(' and ');\n\n return (\n `[MetadataManager] Refusing to register metadata loader \\`${loaderName}\\`: it declares ` +\n `\\`protocol: 'datasource:'\\` with \\`capabilities.write: true\\` but ${missingPhrase}. ` +\n 'A write-capable datasource loader is written to AND deleted from — `register()` persists every item into it, ' +\n 'and `unregister()` has to take those rows back out again. ' +\n consequences.join('') +\n `Fix: either implement ${repair} on \\`${loaderName}\\` ` +\n '(`DatabaseLoader` in this package is the reference implementation), or, if the loader is genuinely read-only, ' +\n \"declare `capabilities.write: false` — a read-only `datasource:` loader registers without complaint and is \" +\n 'never written to in the first place.'\n );\n}\n\n/**\n * [#5276, #5654] Registration gate: a `datasource:` loader that declares\n * `capabilities.write` MUST implement **both** `save()` and `delete()`.\n *\n * `capabilities.write` used to mean three different things at the three places\n * it is read — \"persist into me\" to {@link MetadataManager.register}, which\n * duck-typed `save` and **silently skipped** a loader that had none; nothing at\n * all to {@link MetadataManager.unregister}, which did the same with `delete`;\n * and, on the contract, a flat promise that the store can be written. That is\n * the declared ≠ enforced shape (Prime Directive #10), and the cure it\n * prescribes is to enforce the declaration, not to tolerate the gap: the loader\n * is rejected at registration, where the author is standing, instead of losing a\n * row at write or delete time in a deployment nobody is watching.\n *\n * #5276 closed the `delete` half; #5654 closed the `save` half, which was the\n * same shape one direction over — and leaving it open meant one declaration was\n * binding at one end of an item's life and decorative at the other.\n *\n * Scope is deliberately exactly the combination `register()`/`unregister()` act\n * on. Other protocols (`file:`, `memory:`, `http:`, `s3:`) are never written to\n * by the manager at runtime — both loops filter on `datasource:` too — so they\n * have neither a write nor a deletion of their own to lose and are not gated. A\n * `datasource:` loader with `capabilities.write: false` is likewise untouched by\n * both paths.\n */\nfunction assertWritableLoaderContract(loader: MetadataLoader): void {\n const { name, protocol, capabilities } = loader.contract;\n if (protocol !== 'datasource:' || capabilities.write !== true) return;\n const missing = WRITABLE_LOADER_METHODS.filter((method) => typeof loader[method] !== 'function');\n if (missing.length === 0) return;\n throw new Error(buildWritableLoaderMissingMethodsMessage(name, missing));\n}\n\n/**\n * [#5189] Appended to the namespace gate's message when `publishPackage` was\n * called without one, because the gate's own text (\"declare an explicit\n * `manifest.namespace`\") describes a stack file this caller may not have.\n */\nconst PUBLISH_NAMESPACE_REMEDY =\n 'From `MetadataManager.publishPackage` specifically: this method indexes items by `packageId` and '\n + 'carries no manifest, so it cannot prove a namespace on its own and will not infer one from the '\n + 'items being published (an author-supplied value would make the carve-out gate vacuous). Pass the '\n + \"package's explicit namespace as `publishPackage(id, { namespace })`, or publish the endpoints as \"\n + 'part of a stack artifact (`defineStack` → compile → artifact ingest), which carries the manifest '\n + 'and runs these same gates at parse time.';\n\n/**\n * RFC-4122 v4 uuid for realtime `MetadataEvent.id` (#4602).\n * Prefers `crypto.randomUUID`; the fallback keeps browser-compatible (Pure)\n * environments without WebCrypto working while still satisfying\n * `MetadataEventSchema`'s `z.string().uuid()`.\n */\nfunction generateEventUuid(): string {\n const c = globalThis.crypto;\n if (c && typeof c.randomUUID === 'function') {\n return c.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (ch) => {\n const r = (Math.random() * 16) | 0;\n const v = ch === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Payload format for cluster-wide metadata change broadcasts.\n *\n * Published on channel `metadata.changed` by any node mutating metadata\n * and consumed by peers to invalidate their local caches. Aligns with\n * `MetadataChangedEventPayload` in `cluster-semantics.mdx` §5.\n */\nexport interface ClusterMetadataChangedPayload {\n /** Origin nodeId — used for loopback suppression. */\n originNode?: string;\n /** Metadata type (object, view, dashboard, …). */\n type: string;\n /** The legacy watch event replayed verbatim on peer nodes. */\n event: MetadataWatchEvent;\n}\n\n/**\n * [#5184] One entry of {@link MetadataManager}'s short-TTL `list()` cache.\n *\n * Deliberately NOT exported: this is an internal caching-policy detail, not\n * part of the `IMetadataService` contract. See the `listCache` field comment\n * for the policy this shape encodes.\n */\ninterface ListCacheEntry {\n /** When the entry was written (`Date.now()`). */\n ts: number;\n /** The `list()` result being memoized. */\n items: unknown[];\n /**\n * True when at least one loader threw while `items` was being assembled, so\n * this answer is known to be a partial view of what is declared. Degraded\n * entries expire after `DEGRADED_LIST_CACHE_TTL_MS` instead of\n * `LIST_CACHE_TTL_MS`, and any future consumer of the cache can branch on\n * this rather than having to guess.\n */\n degraded: boolean;\n}\n\nexport interface MetadataManagerOptions extends MetadataManagerConfig {\n loaders?: MetadataLoader[];\n /** Optional IDataDriver instance. When provided alongside config.datasource, auto-configures DatabaseLoader. */\n driver?: IDataDriver;\n}\n\n/**\n * Main metadata manager class.\n * Implements IMetadataService contract for unified metadata management.\n */\nexport class MetadataManager implements IMetadataService {\n private loaders: Map<string, MetadataLoader> = new Map();\n // Protected so subclasses can access serializers if needed\n protected serializers: Map<MetadataFormat, MetadataSerializer>;\n protected logger: Logger;\n protected watchCallbacks = new Map<string, Set<WatchCallback>>();\n protected config: MetadataManagerOptions;\n\n // In-memory metadata registry: type -> name -> data\n private registry = new Map<string, Map<string, unknown>>();\n\n // Overlay storage: \"type:name:scope\" -> MetadataOverlay\n private overlays = new Map<string, MetadataOverlay>();\n\n // Type registry for metadata type info\n private typeRegistry: MetadataTypeRegistryEntryParsed[] = [];\n\n // Dependency tracking: \"type:name\" -> dependencies\n private dependencies = new Map<string, MetadataDependency[]>();\n\n // Short-lived cache for list() results. Built primarily to break the\n // deadlock that occurs when security/permission middleware calls\n // `list('permission')` from inside a user-initiated DB transaction: the\n // DatabaseLoader's `engine.find('sys_metadata', ...)` would then try to\n // acquire a fresh knex connection while the transaction is still holding\n // SQLite's single connection — knex waits the full `acquireConnectionTimeout`\n // (60s) before returning []. The cache absorbs the repeated lookups so the\n // loader is only hit once per TTL window — for CONCURRENT callers as well as\n // sequential ones, since #5253. The cache on its own could only ever deliver\n // the sequential half of that promise: nothing is written until a read\n // completes, so everything issued before the first read returned used to miss\n // and walk every loader — N callers, N × 60s on the very stall described\n // above. The concurrent half is delivered by `inflightListReads` below, which\n // is why the two fields are one policy and are documented together.\n //\n // [#5184] That hazard is NOT historical — it was re-verified on the current\n // driver stack before this policy was chosen. `DatabaseLoader._find()` still\n // issues `engine.find('sys_metadata', …)` without threading the caller's\n // transaction, and `driver-sql` still treats SQLite as a single-connection\n // pool (`activeTransactions`, `assertBareKnexSafe` — the latter a dev/test\n // guard that is a no-op in production, so production still waits the timeout\n // out). `plugin-audit`'s `captureBefore` threads the transaction by hand for\n // exactly this reason. Hence the policy below keeps caching degraded reads\n // rather than skipping them: \"don't cache a degraded read\" would trade one\n // 30s silent window for a fresh 60s stall per call.\n //\n // [#5184] WHAT IS ACTUALLY CACHED, AND FOR HOW LONG — this paragraph is the\n // contract, and it describes `cacheListResult()` / `readCachedList()` below.\n // (An earlier version of this comment claimed the cache kept \"only positive\n // (non-empty) hits or repeated hits with a stable miss signature\". No such\n // condition ever existed in the code. Comment is contract; a comment that\n // describes a policy nothing implements is a declared ≠ enforced defect in\n // its own right, so it is replaced rather than patched.)\n //\n // • EVERY completed `list()` is cached, empty results included. There is\n // no non-empty test and no \"miss signature\" concept.\n // • An entry assembled while at least one loader THREW is a known-partial\n // answer: it is cached with `degraded: true` and expires after\n // `DEGRADED_LIST_CACHE_TTL_MS`, not `LIST_CACHE_TTL_MS`. So the burst of\n // repeated lookups the knex path above depends on is still absorbed,\n // while the window in which the manager serves a known-short set without\n // re-asking anyone shrinks from 30s to ~2s. Recovery is therefore also\n // noticed (and `reportLoaderReadRecovered` logged) within ~2s of storage\n // healing instead of up to 30s later.\n // • `degraded` lives ON the entry, not in a side table, so every consumer\n // of the cache can tell a complete answer from a partial one. Read\n // entries through `readCachedList()` rather than `listCache.get()`, so\n // the flag and its TTL are applied in one place.\n //\n // Invalidated on every `register()` / `unregister()` to keep CRUD writes\n // visible to subsequent reads.\n //\n // [#5259] WHERE in a write the invalidation sits is part of that promise, not\n // an implementation detail. `list()` merges registry ∪ loaders, so an\n // invalidation issued while only ONE of the two has been updated lets the\n // next read memoize the half-applied view for a full TTL. The rule both\n // writers follow: **invalidate last, once every store already holds the state\n // being announced** — `register()` satisfies it by writing the registry\n // first (the registry outranks loaders in the merge, so its save window\n // already shows the post-write value); `unregister()` satisfies it by\n // deleting from storage first and invalidating after, with nothing awaited\n // between the registry drop and the invalidation. See `unregister()`.\n private listCache = new Map<string, ListCacheEntry>();\n private static readonly LIST_CACHE_TTL_MS = 30_000;\n /**\n * [#5184] TTL for an entry produced by a degraded read (≥1 loader threw).\n *\n * Deliberately at the top of the 1–2s band: the point of keeping degraded\n * results cached at all is to absorb a burst of `list()` calls issued from\n * inside one open transaction, and those bursts are milliseconds apart but\n * can be spread by per-row work. Two seconds covers that while still being\n * 15× shorter than the healthy TTL.\n */\n private static readonly DEGRADED_LIST_CACHE_TTL_MS = 2_000;\n\n /**\n * [#5253] The `list()` read currently in flight for a metadata type — the\n * concurrent half of the `listCache` policy above.\n *\n * `listCache` memoizes an answer only once a read has *finished*, so it can\n * absorb the caller that arrives second in time but never the caller that\n * arrives second in flight. Everything issued while the first read is still\n * walking the loaders used to miss and start its own identical walk; on the\n * knex/SQLite path the field comment above is built for, that is 60s burned\n * per concurrent caller instead of once for all of them. A type is read once\n * at a time: whoever finds a read already running joins it.\n *\n * **Sharers share the outcome. This is a contract, not an accident.** Every\n * caller joining an in-flight read receives that read's exact result — the\n * same array instance, and, when a loader was unreadable, the same\n * known-partial set that gets memoized `degraded: true` on the short TTL.\n * There is no per-caller retry: `list()` is the best-effort listing seam and\n * does not throw (see {@link reportLoaderReadFailure}; the strict\n * counterparts are `listForIndex()` and {@link loadDiagnosed}), so a lost\n * loader is not an error to fail over from — it is the answer. Re-running the\n * read privately for a joiner would walk the same loaders in the same window\n * against the same outage, which is precisely what this map exists to\n * prevent. Should the seam ever acquire a rejecting path, that rejection is\n * shared by the same mechanism and for the same reason.\n *\n * **The registration is also the permission to cache.** An entry here says\n * \"this read still describes the current state\". {@link invalidateListCache}\n * retracts it, which is what makes a write landing mid-read safe in both\n * directions:\n * • the retracted read does NOT write its result into `listCache` when it\n * settles, so an answer assembled before the write cannot outlive the\n * write it predates (the invalidation wins — it is the later, better\n * informed fact);\n * • a `list()` issued after the invalidation starts a FRESH read instead of\n * joining one that predates the write.\n * That second point is the #5219 / #5229 ordering bar restated for\n * concurrency: a consumer woken by a metadata change must not observe the\n * event and pre-event state together, and handing a woken watcher an\n * in-flight read that began before the event would be exactly that.\n * Callers *already waiting* on the retracted read still receive its (now\n * possibly stale) result — they asked before the write, and restarting the\n * read under them would turn a write burst into an unbounded retry loop on\n * the one path the cache exists to keep off the loaders.\n *\n * Self-cleaning: the entry is dropped when the read settles, by that read\n * only, so a fresh read that already replaced it keeps its slot. Nothing\n * accumulates — a wave of callers arriving after settle finds the cache the\n * settle just wrote, and once that lapses it starts one new read.\n */\n private readonly inflightListReads = new Map<string, Promise<unknown[]>>();\n\n // [#5108] Loader names whose read failure has already been reported at\n // `error` by `list()`. AGENTS.md → \"Degradation log levels\": say it once, at\n // the first degradation — `list()` is hot enough that one line per failed\n // read would bury the one line that matters. Cleared when the loader answers\n // again, so a second outage is reported again. Same once-only discipline as\n // `DatabaseLoader.schemaFailureReported`.\n private readonly loaderReadFailureReported = new Set<string>();\n\n // Realtime service for event publishing\n private realtimeService?: IRealtimeService;\n\n // ── Cluster wiring (cluster-semantics.mdx §5) ────────────────────────\n // When attached via `attachClusterPubSub()`, metadata-change events\n // become cluster-wide:\n // • Local notifyWatchers() publishes on `metadata.changed` so peers\n // can invalidate their caches.\n // • A subscribed remote event first invalidates THIS node's caches\n // (registry entry + listCache, via `invalidateForForeignWrite` —\n // #5109) and is then replayed into the local watch hub, so existing\n // consumers (ObjectQLPlugin, Studio HMR, …) see uniform behavior\n // regardless of which node initiated the change — including when\n // they answer the event by re-reading through `list()`.\n // `originNode` on the payload prevents loopback; `partitionKey` keeps\n // per-object ordering on partitioned drivers.\n private clusterPubSub?: IPubSub;\n private clusterNodeId?: string;\n private clusterUnsubscribe?: Unsubscribe;\n private static readonly CLUSTER_CHANNEL = 'metadata.changed';\n\n // ── ADR-0008 PR-6: optional Repository for event-source integration ──\n // When set, the manager streams `repo.watch()` events into the watch\n // callback hub AND invalidates the in-memory registry/listCache so\n // subsequent reads fall through to the source of truth. No write\n // mirroring yet (deferred to PR-10 / overlay migration).\n protected repository?: MetadataRepository;\n private repoWatchIter?: AsyncIterator<MetadataEvent>;\n private repoWatchClosed = false;\n\n // ── #5089 (#5040 E2): declared-endpoint index ────────────────────────\n // Backs `matchEndpoint`. Lazily built from `api` items on the first call\n // and invalidated by every path that can change them — see\n // `invalidateListCache` (local writes, repo events, HMR/artifact ingest\n // which registers with `notify:false`, and — since #5109 — cluster peer\n // replay) and the `subscribe('api', …)` registration below.\n private static readonly ENDPOINT_METADATA_TYPE = 'api';\n private readonly endpointMatcher: EndpointMatcher;\n\n constructor(config: MetadataManagerOptions) {\n this.config = config;\n this.logger = createLogger({ level: 'info', format: 'pretty' });\n\n // [#5089] Endpoint index (see `matchEndpoint`). Two invalidation seams,\n // covering overlapping but non-identical event sets — both are kept:\n // 1. `invalidateListCache('api')` — every mutation of the stored set\n // this manager learns about, including the `{ notify: false }`\n // writes the artifact ingest and the HMR reload use, which by\n // construction never reach a watcher. It is the same invariant the\n // list cache carries: if the cached list of a type is stale, so is\n // the index built from it. Since #5109 a CLUSTER peer's write also\n // passes through here, via `invalidateForForeignWrite`.\n // 2. `subscribe('api', …)` — the watcher seam, which additionally\n // covers events raised by subclasses / test doubles that call\n // `notifyWatchers` without a cache mutation of their own.\n // Double invalidation is idempotent, so the overlap is free.\n this.endpointMatcher = new EndpointMatcher({\n listApiItems: () => this.listForIndex(MetadataManager.ENDPOINT_METADATA_TYPE),\n logger: this.logger,\n });\n this.subscribe(MetadataManager.ENDPOINT_METADATA_TYPE, () => this.endpointMatcher.invalidate());\n\n // Initialize serializers\n this.serializers = new Map();\n const formats = config.formats || ['typescript', 'json', 'yaml'];\n\n if (formats.includes('json')) {\n this.serializers.set('json', new JSONSerializer());\n }\n if (formats.includes('yaml')) {\n this.serializers.set('yaml', new YAMLSerializer());\n }\n if (formats.includes('typescript')) {\n this.serializers.set('typescript', new TypeScriptSerializer('typescript'));\n }\n if (formats.includes('javascript')) {\n this.serializers.set('javascript', new TypeScriptSerializer('javascript'));\n }\n\n // Initialize Loaders\n if (config.loaders && config.loaders.length > 0) {\n config.loaders.forEach(loader => this.registerLoader(loader));\n }\n\n // Auto-configure DatabaseLoader when datasource + driver are provided\n if (config.datasource && config.driver) {\n this.setDatabaseDriver(config.driver);\n }\n // Note: No default loader in base class. Subclasses (NodeMetadataManager) or caller must provide one.\n }\n\n /**\n * Set the type registry for metadata type discovery.\n */\n setTypeRegistry(entries: MetadataTypeRegistryEntryParsed[]): void {\n this.typeRegistry = entries;\n }\n\n /**\n * Configure and register a DatabaseLoader for database-backed metadata persistence.\n * Can be called at any time to enable database storage (e.g. after kernel resolves the driver).\n *\n * @param driver - An IDataDriver instance for database operations\n * @param organizationId - Organization ID for multi-tenant isolation\n * @param environmentId - Project ID (undefined = platform-global)\n */\n setDatabaseDriver(driver: IDataDriver, organizationId?: string, environmentId?: string): void {\n if (environmentId !== undefined) {\n this.logger.info('Project kernel — skipping DatabaseLoader for sys_metadata (control-plane only)', {\n organizationId,\n environmentId,\n });\n return;\n }\n const tableName = this.config.tableName ?? 'sys_metadata';\n const dbLoader = new DatabaseLoader({\n driver,\n tableName,\n organizationId,\n environmentId,\n cache: this.config.cache?.databaseLoader,\n });\n this.registerLoader(dbLoader);\n this.logger.info('DatabaseLoader configured', { datasource: this.config.datasource, tableName });\n }\n\n /**\n * Configure and register a DatabaseLoader backed by an IDataEngine (ObjectQL).\n * The engine handles datasource routing automatically — sys_metadata will\n * be routed to the correct driver via the standard namespace mapping.\n * No manual driver resolution needed.\n *\n * @param engine - An IDataEngine instance (typically the ObjectQL service)\n * @param organizationId - Organization ID for multi-tenant isolation\n * @param environmentId - Project ID (undefined = platform-global)\n */\n setDataEngine(engine: IDataEngine, organizationId?: string, environmentId?: string): void {\n if (environmentId !== undefined) {\n this.logger.info('Project kernel — skipping DatabaseLoader for sys_metadata (control-plane only)', {\n organizationId,\n environmentId,\n });\n return;\n }\n const tableName = this.config.tableName ?? 'sys_metadata';\n const dbLoader = new DatabaseLoader({\n engine,\n tableName,\n organizationId,\n environmentId,\n cache: this.config.cache?.databaseLoader,\n });\n this.registerLoader(dbLoader);\n this.logger.info('DatabaseLoader configured via DataEngine', { tableName });\n }\n\n /**\n * Set the realtime service for publishing metadata change events.\n * Should be called after kernel resolves the realtime service.\n *\n * @param service - An IRealtimeService instance for event publishing\n */\n setRealtimeService(service: IRealtimeService): void {\n this.realtimeService = service;\n this.logger.info('RealtimeService configured for metadata events');\n }\n\n /**\n * Publish a realtime {@link RealtimeMetadataEvent} for a metadata write\n * (#4602 — contract-first).\n *\n * What reaches a `subscribeMetadata` callback must BE the spec's\n * `MetadataEvent` (`@objectstack/spec/api`): `id` (uuid) at the top level,\n * flattened `metadataType`/`name`/`definition`, `userId` when the write\n * carried an actor. The transport keeps its `RealtimeEventPayload`\n * envelope — `payload` carries the complete `MetadataEvent`, and the client\n * SDK unwraps + validates it at the boundary.\n *\n * Two loud-by-design gates:\n * - `MetadataEventType` is a CLOSED enum. A metadata type outside it has\n * no declared realtime event contract, so we skip publishing (debug log)\n * instead of emitting an event every compliant consumer must reject.\n * Declared = enforced; widening coverage means widening the spec enum,\n * not producing off-contract events.\n * - The event body is `MetadataEventSchema.parse`d before publish, so a\n * malformed producer fails here (warn log, event not published) rather\n * than delivering a lie downstream.\n */\n private async publishRealtimeMetadataEvent(\n action: 'created' | 'updated' | 'deleted',\n type: string,\n name: string,\n opts: { definition?: unknown; packageId?: unknown; userId?: string } = {},\n ): Promise<void> {\n if (!this.realtimeService) return;\n\n const eventType = `metadata.${type}.${action}`;\n if (!(MetadataEventType.options as readonly string[]).includes(eventType)) {\n this.logger.debug(\n `Metadata type '${type}' has no declared realtime event type (MetadataEventType) — skipping publish`,\n { eventType, name },\n );\n return;\n }\n\n try {\n const event: RealtimeMetadataEvent = MetadataEventSchema.parse({\n id: generateEventUuid(),\n type: eventType,\n metadataType: type,\n name,\n ...(typeof opts.packageId === 'string' ? { packageId: opts.packageId } : {}),\n ...(opts.definition !== undefined ? { definition: opts.definition } : {}),\n ...(opts.userId ? { userId: opts.userId } : {}),\n timestamp: new Date().toISOString(),\n });\n\n const envelope: RealtimeEventPayload = {\n type: event.type,\n object: type,\n payload: { ...event },\n timestamp: event.timestamp,\n };\n\n await this.realtimeService.publish(envelope);\n this.logger.debug(`Published ${eventType} event`, { name });\n } catch (error) {\n this.logger.warn(`Failed to publish metadata event`, { type, name, error });\n }\n }\n\n /**\n * Register a new metadata loader (data source)\n *\n * [#5276, #5654] Rejects — loudly, before the loader is stored — a\n * `datasource:` loader that declares `capabilities.write` without\n * implementing `save()` **and** `delete()`. This is the **only** way into\n * `this.loaders` (the constructor's `config.loaders` come through here too),\n * which is what lets every later write-capability guard be defensive rather\n * than load-bearing.\n */\n registerLoader(loader: MetadataLoader) {\n assertWritableLoaderContract(loader);\n this.loaders.set(loader.contract.name, loader);\n this.logger.info(`Registered metadata loader: ${loader.contract.name} (${loader.contract.protocol})`);\n }\n\n // ==========================================\n // IMetadataService — Core CRUD Operations\n // ==========================================\n\n /**\n * Register/save a metadata item by type\n * Stores in-memory registry and persists to database-backed loaders only.\n * FilesystemLoader (protocol 'file:') is read-only for static metadata and\n * should not be written to during runtime registration.\n *\n * Announces the write to {@link subscribe} watchers as an `added` /\n * `changed` {@link MetadataWatchEvent}, so consumers that cache metadata\n * (ObjectQL's SchemaRegistry bridge, the HMR SSE stream) refresh instead of\n * serving the pre-write definition until restart. Pass `{ notify: false }`\n * for bulk ingest that announces by other means — read\n * {@link MetadataWriteOptions.notify} before doing so.\n */\n async register(\n type: string,\n name: string,\n data: unknown,\n options?: MetadataWriteOptions,\n ): Promise<void> {\n // Persistence write gate: when `persistence.writable` is explicitly false\n // we treat register() as read-only. Default `true` (or omitted) preserves\n // historical behavior.\n if (this.config.persistence?.writable === false) {\n const msg = `MetadataManager is read-only (persistence.writable=false); refusing to register ${type}/${name}`;\n if (this.config.validation?.throwOnError) {\n throw new Error(msg);\n }\n this.logger.warn(msg);\n return;\n }\n\n // Captured before the write so the event distinguishes a first\n // registration from an overwrite, matching the repo-watch path's\n // 'added' vs 'changed' split.\n const existed = this.registry.get(type)?.has(name) ?? false;\n\n if (!this.registry.has(type)) {\n this.registry.set(type, new Map());\n }\n this.registry.get(type)!.set(name, data);\n this.invalidateListCache(type);\n\n // Persist only to database-backed loaders that declare write capability.\n // FilesystemLoader is read-only at runtime — writing to it can crash in\n // read-only environments (e.g. serverless, containerized deployments).\n for (const loader of this.loaders.values()) {\n if (loader.contract.protocol !== 'datasource:' || !loader.contract.capabilities.write) continue;\n // [#5654] Defensive only — unreachable for a registered loader, and read\n // in this order on purpose: the protocol/capability test is the policy,\n // the method test is the type narrowing. This exact combination\n // (`datasource:` + `capabilities.write`, no `save`) is rejected by\n // `registerLoader()`, the sole writer of `this.loaders`, so reaching this\n // `continue` would mean a loader entered the map without passing the\n // gate. Kept because the alternative is a TypeError on the line below,\n // and because `save?` stays optional on the interface for the protocols\n // the gate does not cover. Mirrors the same guard in `unregister()`.\n if (typeof loader.save !== 'function') continue;\n await loader.save(type, name, data);\n }\n\n // Publish metadata.{type}.created / .updated event to realtime service.\n // An overwrite is an UPDATE, mirroring the 'added' vs 'changed' split the\n // watcher event below already makes (#4602).\n await this.publishRealtimeMetadataEvent(existed ? 'updated' : 'created', type, name, {\n definition: data,\n packageId: (data as any)?.packageId,\n userId: options?.userId,\n });\n\n // Announce last, once the write has landed in the registry and every\n // writable loader — a subscriber that re-reads on the event must not\n // race ahead of the data it is meant to observe.\n if (options?.notify !== false) {\n this.notifyWatchers(type, {\n type: existed ? 'changed' : 'added',\n metadataType: type,\n name,\n path: '',\n data,\n timestamp: new Date().toISOString(),\n });\n }\n }\n\n /**\n * Register a metadata item into the in-memory registry ONLY, never persisting\n * to a writable loader. Used for GitOps-managed artefacts that must be\n * *listable* (so `list(type)` returns them) but must never leak into the\n * runtime DB store — e.g. code-defined datasources (`origin:'code'`, ADR-0015\n * Addendum) declared in `*.datasource.ts` and owned by source control. Writing\n * them through `register()` would persist them to `sys_metadata` and create\n * drift between the artefact and the DB; this method avoids that.\n *\n * Deliberately silent: it does NOT announce to {@link subscribe} watchers.\n * This is a boot-time seeding primitive for artefacts that source control\n * owns — callers that mutate metadata mid-run want {@link register}, which\n * announces. If you add a mid-run caller here, announce the change yourself\n * (as the artifact reload path does via `metadata:reloaded`) or its\n * consumers will read the pre-write definition until restart.\n */\n registerInMemory(type: string, name: string, data: unknown): void {\n if (!this.registry.has(type)) {\n this.registry.set(type, new Map());\n }\n this.registry.get(type)!.set(name, data);\n this.invalidateListCache(type);\n }\n\n /**\n * Get a metadata item by type and name.\n * Checks in-memory registry first, then falls back to loaders.\n *\n * Returns `undefined` both when nothing declares the item and when every\n * loader that could have held it FAILED — see {@link getDiagnosed} when the\n * caller must tell those apart. This is the same relationship {@link load}\n * has with {@link loadDiagnosed}, so every existing caller keeps its exact\n * behaviour and only callers that ASK for the verdict pay for it.\n *\n * Not expressed as `(await getDiagnosed(…)).data`, although that is what it\n * computes — and the reason has CHANGED, so do not read the duplication as a\n * standing constraint.\n *\n * [#5840] recorded the delegation as unsafe: it adds one `await` hop, and\n * `register-notifies-watchers.test.ts` went red on the delegating version, so\n * three lines were duplicated to hold the frame count fixed. [#6043] measured\n * that test and found it was pinning this method's microtask depth rather than\n * the ordering guarantee it named — `notifyWatchers` never awaits its handlers,\n * so a subscriber's `await get(…)` had simply been settling inside the\n * microtasks `await register(…)` yields. That case now asserts the ordering\n * synchronously against the registry and does not observe this method's frame\n * count at all; the whole `@objectstack/metadata` suite was re-measured on the\n * delegating version and stayed green.\n *\n * What survives is a plain, local reason: the registry hit is the hot path and\n * answering it without a second async frame is worth three lines. Nothing\n * external depends on the hop count any more. Consolidating the two into one\n * delegation is therefore a viable, deliberately un-taken change (#6043 was\n * test-scoped) — if you take it, note that `get()`'s callers outside this\n * package were never surveyed for timing sensitivity, only this package's\n * tests. Either way the two stay pinned to each other from the other side:\n * `get()` and `getDiagnosed().data` are asserted to agree on every case in\n * `metadata-manager-get-diagnosed.test.ts`.\n */\n async get(type: string, name: string): Promise<unknown | undefined> {\n // Check in-memory registry first\n const typeStore = this.registry.get(type);\n if (typeStore?.has(name)) {\n return typeStore.get(name);\n }\n\n // Fallback to loaders\n const result = await this.load(type, name);\n return result ?? undefined;\n }\n\n /**\n * `get`, plus whether the answer can be trusted as complete.\n *\n * [#5840] {@link loadDiagnosed} already computes this verdict — and `get()`\n * threw it away two hops later (`load` kept only `.data`, `get` turned that\n * `null` into `undefined`), so no caller of `get` could reach the one fact\n * ADR-0110 D3 exists to preserve: **a miss and an outage are different facts\n * with opposite security meanings.** A consumer that gates on a declaration\n * MUST NOT read `undefined` as \"the author declared nothing\" — an\n * availability failure would silently widen access (the REST `/actions`\n * fail-open branch, #3935) or make a positive claim about authorship from a\n * read that never happened (`code: null` in the layered read, #5707/#5532).\n *\n * This is the registry-first counterpart of {@link loadDiagnosed}, and that\n * difference is why callers of `get` cannot simply switch to `loadDiagnosed`:\n * doing so would skip the in-memory registry and change what they resolve.\n *\n * `degraded` is true when at least one loader threw AND nothing answered with\n * the item — never when the in-memory registry answered, because that answer\n * needed no loader. A clean miss (every loader answered, none had it) is NOT\n * degraded. The posture is deliberately conservative: with a loader down we\n * cannot prove the item is absent, so we decline to claim it is.\n */\n async getDiagnosed(\n type: string,\n name: string\n ): Promise<{ data: unknown | undefined; degraded: boolean; errors: string[] }> {\n // Check in-memory registry first — a hit here consulted no loader, so\n // there is nothing to be degraded about.\n const typeStore = this.registry.get(type);\n if (typeStore?.has(name)) {\n return { data: typeStore.get(name), degraded: false, errors: [] };\n }\n\n // Fallback to loaders, keeping the verdict this time.\n const { data, degraded, errors } = await this.loadDiagnosed(type, name);\n return { data: data ?? undefined, degraded, errors };\n }\n\n /**\n * List all metadata items of a given type.\n *\n * Best-effort by contract: a loader that cannot be read is reported once and\n * skipped ({@link reportLoaderReadFailure}), so this resolves with what the\n * reachable loaders hold rather than throwing.\n *\n * [#5253] Reads of one type are single-flight — concurrent callers join the\n * read already running instead of each walking every loader. What they are\n * promised, and what happens when a write lands mid-read, is the contract on\n * `inflightListReads`; what is memoized afterwards is the contract on\n * `listCache`.\n */\n async list(type: string): Promise<unknown[]> {\n // Short-TTL cache: see the field comment on `listCache` for what is\n // cached and for how long. Every completed read is memoized; a read that\n // lost a loader is memoized as `degraded` and expires ~15× sooner.\n const cached = this.readCachedList(type);\n if (cached) {\n return cached.items;\n }\n\n // [#5253] Cold cache, but not necessarily a cold read: join the walk\n // already in progress for this type rather than starting an identical one.\n const joined = this.inflightListReads.get(type);\n if (joined) {\n return joined;\n }\n\n // Registering the read is what permits it to memoize its own result —\n // `invalidateListCache()` retracts the registration, and both the cache\n // write and the cleanup below act only while the slot is still ours.\n const shared: Promise<unknown[]> = this.readListUncached(type).then(({ items, degraded }) => {\n // [#5184] The degraded verdict of a SHARED read is the degraded verdict\n // of the read: sharing must not become a back door that lands a\n // known-partial answer on the 30s healthy TTL. Every sharer received\n // this same set, and it is memoized as what it is.\n // [#5253] Skipped when an invalidation crossed this read, so a write\n // that landed mid-read is never re-buried under the pre-write answer.\n if (this.inflightListReads.get(type) === shared) {\n this.cacheListResult(type, items, degraded);\n }\n return items;\n });\n this.inflightListReads.set(type, shared);\n\n try {\n return await shared;\n } finally {\n // Retract only our OWN registration: an invalidation may have already\n // dropped it and a fresh read may own the slot now — deleting that one\n // would let a third read start against the same window.\n if (this.inflightListReads.get(type) === shared) {\n this.inflightListReads.delete(type);\n }\n }\n }\n\n /**\n * Assemble the `list()` answer for `type` from the in-memory registry plus\n * every loader, reporting (but not rethrowing) loaders that could not be\n * read.\n *\n * The body {@link list} used to inline, extracted so the caching and\n * single-flight bookkeeping around it has one thing to run at most once per\n * type (#5253). Deliberately does NOT touch `listCache` itself: whether this\n * result may be memoized depends on what happened to the read's registration\n * while it ran, which only `list()` can see.\n */\n private async readListUncached(type: string): Promise<{ items: unknown[]; degraded: boolean }> {\n const items = new Map<string, unknown>();\n\n // From in-memory registry\n const typeStore = this.registry.get(type);\n if (typeStore) {\n for (const [name, data] of typeStore) {\n items.set(name, data);\n }\n }\n\n // From loaders (deduplicate). [#5184] `degraded` records whether this\n // particular read lost a loader, so the memoized answer carries the fact\n // that it is known-partial instead of being indistinguishable from a\n // complete one.\n let degraded = false;\n for (const loader of this.loaders.values()) {\n try {\n const loaderItems = await loader.loadMany(type);\n for (const item of loaderItems) {\n const itemAny = item as any;\n if (itemAny && typeof itemAny.name === 'string' && !items.has(itemAny.name)) {\n items.set(itemAny.name, item);\n }\n }\n this.reportLoaderReadRecovered(loader.contract.name);\n } catch (e) {\n degraded = true;\n this.reportLoaderReadFailure(loader.contract.name, type, e);\n }\n }\n\n return { items: Array.from(items.values()), degraded };\n }\n\n /**\n * Report — at `error`, once per outage episode — that a loader could not be\n * read while serving {@link list}.\n *\n * [#5108] This branch used to be dead for the loader that matters. Before\n * #5108 `DatabaseLoader` caught its own read failures and answered `[]`, so\n * `list()` received a *successful empty read* and never entered this `catch`\n * at all: an unreachable `sys_metadata` and \"this environment declares no\n * `permission`\" produced byte-identical results with not one line logged.\n * With the loader rethrowing everything but the benign not-provisioned case,\n * this is where the outage finally becomes speakable.\n *\n * `error`, not `warn`, per AGENTS.md → \"Degradation log levels\". Apply its\n * one question — *does the system still look normal from outside while\n * something it claims to know has not actually landed?* — and the answer is\n * yes: `list()` still returns, callers still get an array, nothing 500s, and\n * the set they gate on is quietly short. Which way that cuts depends on the\n * consumer, and both ways are silent (#3935 is the fail-open precedent).\n *\n * Said **once** per loader, and un-said on recovery, because `list()` is a\n * hot path — one line per outage, not one per read.\n *\n * [#5184] The once-only guard carries more weight than it used to: a\n * degraded `list()` result is now memoized for `DEGRADED_LIST_CACHE_TTL_MS`\n * rather than `LIST_CACHE_TTL_MS`, so during an outage the loader is\n * re-asked (and this method re-entered) roughly every 2s instead of every\n * 30s. That is the point — the outage stops being a 30s silent window and\n * recovery is noticed within seconds — and it costs nothing in log volume\n * precisely because `loaderReadFailureReported` still speaks only once.\n *\n * Deliberately does NOT rethrow: `list()` is the best-effort listing seam and\n * must keep serving what the reachable loaders hold. The strict counterpart\n * for callers whose answer is a security decision is `listForIndex()` (no\n * `catch`, feeding `matchEndpoint`) and {@link loadDiagnosed} (ADR-0110 D3)\n * for the singular read — both of which only became honest for\n * `DatabaseLoader` with the same #5108 change.\n */\n private reportLoaderReadFailure(loaderName: string, type: string, error: unknown): void {\n if (this.loaderReadFailureReported.has(loaderName)) return;\n this.loaderReadFailureReported.add(loaderName);\n this.logger.error(\n `[MetadataManager] Loader \\`${loaderName}\\` could NOT be read (first failure seen while listing \\`${type}\\`) — ` +\n `every list served from now on is a PARTIAL set presented as a complete one, and the server keeps reporting healthy. ` +\n `Consumers that gate on a declared set (permissions, sharing rules, policies, api endpoints) will read the ` +\n `declarations this loader holds as \"never declared\" — which grants or locks out depending on the consumer, silently either way. ` +\n `Fix: check the datasource behind \\`${loaderName}\\` — connection, credentials, and that its metadata table exists. ` +\n `The read is retried on the next list once the ${MetadataManager.DEGRADED_LIST_CACHE_TTL_MS}ms degraded-result list cache lapses ` +\n `(a known-partial listing is memoized far more briefly than a complete one — #5184), so a transient cause recovers on its ` +\n `own within seconds and the recovery is logged.`,\n error instanceof Error ? error : undefined,\n { loader: loaderName, type, error },\n );\n }\n\n /** Un-say {@link reportLoaderReadFailure} once the loader answers again. */\n private reportLoaderReadRecovered(loaderName: string): void {\n if (!this.loaderReadFailureReported.delete(loaderName)) return;\n this.logger.info(\n `[MetadataManager] Loader \\`${loaderName}\\` is readable again — listings are complete once more.`,\n );\n }\n\n /**\n * Memoize a completed {@link list} result.\n *\n * [#5184] `degraded` is not optional at the call site by accident — it is the\n * one thing this cache used to throw away. A result assembled while a loader\n * was unreadable is stored, but stored *as* what it is, so it expires on the\n * degraded TTL and any reader can tell it apart from a complete answer.\n */\n private cacheListResult(type: string, items: unknown[], degraded: boolean): void {\n this.listCache.set(type, { ts: Date.now(), items, degraded });\n }\n\n /**\n * Read a still-fresh {@link listCache} entry, or `undefined` when there is\n * none / it has expired.\n *\n * [#5184] The single place the TTL policy is applied, so \"a degraded entry\n * expires sooner\" cannot be forgotten by a second reader. Returns the whole\n * entry rather than just `items` so callers keep access to `degraded`.\n */\n private readCachedList(type: string): ListCacheEntry | undefined {\n const cached = this.listCache.get(type);\n if (!cached) return undefined;\n const ttl = cached.degraded\n ? MetadataManager.DEGRADED_LIST_CACHE_TTL_MS\n : MetadataManager.LIST_CACHE_TTL_MS;\n return Date.now() - cached.ts < ttl ? cached : undefined;\n }\n\n /**\n * Internal helper: drop every memoized or in-progress `list()` answer for a\n * type, so the next read observes the write that called this.\n *\n * [#5253] Retracting the in-flight read (not just the finished entry) is the\n * whole mid-read story, and it is pinned by test: the read keeps running for\n * the callers already waiting on it, but it loses the right to memoize its\n * pre-write answer, and a caller arriving after this point gets a fresh read\n * instead of joining a pre-write one. The reasoning — including why waiting\n * callers are NOT restarted — is on the `inflightListReads` field.\n *\n * [#5259] Both halves are only as good as WHEN the caller invokes this. This\n * clears what is stale *as of now*; it cannot pre-empt a store the caller has\n * not finished updating yet. Callers must therefore invalidate only once\n * every store already holds the state they are about to announce — see the\n * `listCache` field comment and {@link unregister}, whose pre-#5259 ordering\n * invalidated one await too early and let the next read cache a view in which\n * the registry was empty and the loader was not.\n */\n private invalidateListCache(type: string): void {\n this.listCache.delete(type);\n this.inflightListReads.delete(type);\n // [#5089] The endpoint index is a cache of the same stored set, so it goes\n // stale under exactly the same conditions. Hooking here (rather than only\n // on the watcher) is what covers the `{ notify: false }` writes — artifact\n // ingest and HMR reload — which never announce to a subscriber.\n if (type === MetadataManager.ENDPOINT_METADATA_TYPE) {\n this.endpointMatcher.invalidate();\n }\n }\n\n /**\n * Enumerate stored items of `type` for an index build — like {@link list},\n * but a store that cannot be read THROWS instead of contributing nothing.\n *\n * [#5089] `list()` deliberately logs a failing loader and skips it so a\n * partially-available metadata plane still serves what it can. That posture\n * is wrong for `matchEndpoint`: its `undefined` becomes an HTTP 404, and a\n * store outage that silently yields \"zero declarations\" would turn every\n * declared endpoint into a semantic \"nothing declares this route\". Same\n * distinction {@link loadDiagnosed} draws on the singular read (ADR-0110\n * D3) — a miss and an outage are different facts with opposite meanings.\n *\n * Deliberately private and single-purpose: it is not a second `list()`, it\n * is `list()`'s failure posture inverted for the one caller whose answer is\n * a security/availability decision rather than a best-effort listing.\n *\n * This surfaces only failures a loader actually reports — which, since\n * #5108, includes `DatabaseLoader`: it used to swallow its own read errors\n * into `[]`, making a DB outage invisible even here. It now rethrows every\n * read failure except the benign \"table not provisioned yet\", so this seam\n * holds against the real datasource-backed loader and not just the memory /\n * remote ones. (`database-loader.test.ts` pins that end to end: a broken\n * driver behind a real `DatabaseLoader` makes `matchEndpoint` reject rather\n * than answer a 404-shaped `undefined`.)\n */\n private async listForIndex(type: string): Promise<unknown[]> {\n const items = new Map<string, unknown>();\n\n const typeStore = this.registry.get(type);\n if (typeStore) {\n for (const [name, data] of typeStore) {\n items.set(name, data);\n }\n }\n\n for (const loader of this.loaders.values()) {\n // No try/catch, on purpose — see the doc comment above.\n const loaderItems = await loader.loadMany(type);\n for (const item of loaderItems) {\n const itemAny = item as { name?: unknown };\n if (itemAny && typeof itemAny.name === 'string' && !items.has(itemAny.name)) {\n items.set(itemAny.name, item);\n }\n }\n }\n\n return Array.from(items.values());\n }\n\n /**\n * Unregister/remove a metadata item by type and name.\n * Deletes from database-backed loaders only (same rationale as register()).\n *\n * Announces the removal to {@link subscribe} watchers as a `deleted`\n * {@link MetadataWatchEvent} — the delete half of the {@link register}\n * contract. Pass `{ notify: false }` only for teardown that announces by\n * other means.\n *\n * ## [#5259] Storage FIRST, in-memory second — the order is the fix\n *\n * This method used to drop the registry entry and call\n * {@link invalidateListCache} *before* awaiting `loader.delete()`. Those two\n * steps are separated by a real await window (one DB round-trip per writable\n * loader), and inside it the manager was in a state that exists nowhere else:\n * **registry already empty, loader not yet empty**. `list()` merges the two,\n * so a read arriving in that window\n *\n * • missed the cache (it had just been invalidated),\n * • assembled the still-stored row into its answer, and\n * • memoized that answer as a COMPLETE read — the full 30s healthy TTL,\n * because no loader threw, so #5184's 2s degraded TTL never applied.\n *\n * Nothing invalidated again afterwards ({@link notifyWatchers} does not touch\n * `listCache`), so a row that was gone from storage kept being enumerated for\n * up to 30s — and `get()`, which never consulted that cache, disagreed with\n * `list()` the whole time. For a gating type (`permission`, `api`) the two\n * faces of the same manager answered opposite questions about whether a\n * declaration exists.\n *\n * {@link register} never had this defect, and the reason is instructive: it\n * writes the registry *first*, and the registry outranks every loader in the\n * merge, so throughout its own save window the merged view already equals the\n * post-write state. The invariant that makes register correct is not \"where\n * the invalidate sits\" but **the invalidate must be the last thing after\n * every store already holds the announced state**. Restated for delete, that\n * means storage first:\n *\n * 1. `await loader.delete()` on every writable loader. Throughout this\n * window registry AND loaders still hold the item, so a concurrent\n * `list()` observes a coherent pre-delete state — which is the truth,\n * because the delete has not landed and has not been announced.\n * 2. Drop the registry entry and `invalidateListCache(type)` — with **no\n * await between them**, so no read can interleave and observe the\n * half-applied state that produced the bug. Everything cached or\n * in-flight from step 1 is dropped here, at the moment the final state\n * becomes true.\n * 3. Publish + announce. #5219's invalidate-before-notify bar, unchanged:\n * a watcher woken by the `deleted` event and re-reading through `list()`\n * gets a fresh read of the post-delete state.\n *\n * **Composition with #5253's single-flight (this is the load-bearing half).**\n * A `list()` that is still walking the loaders when step 2 runs cannot be\n * fixed by dropping `listCache` alone — it has not written its entry yet, and\n * it would write the pre-delete answer *after* the invalidation. The\n * mechanism that covers it is `invalidateListCache()` also retracting the\n * read's registration in `inflightListReads`: a retracted read still resolves\n * for the callers already waiting on it (they asked before the delete) but\n * loses the right to memoize, and any caller arriving after step 2 starts a\n * fresh read rather than joining the pre-delete one. So every read is\n * covered: one that FINISHED in the window has its entry deleted, one still\n * IN FLIGHT loses its permission to cache, and one starting later reads the\n * post-delete state. That is why the invalidate must come after the deletes\n * rather than being duplicated on both sides of them — a second invalidate\n * before the await would buy nothing and would re-open step 1's window.\n */\n async unregister(type: string, name: string, options?: MetadataWriteOptions): Promise<void> {\n // ── 1. Storage first ────────────────────────────────────────────────\n // Delete only from database-backed loaders that declare write capability.\n for (const loader of this.loaders.values()) {\n if (loader.contract.protocol !== 'datasource:' || !loader.contract.capabilities.write) continue;\n // [#5276] Defensive only — unreachable for a registered loader. This exact\n // combination (`datasource:` + `capabilities.write`, no `delete`) is\n // rejected by `registerLoader()`, the sole writer of `this.loaders`, so\n // reaching this `continue` would mean a loader entered the map without\n // passing the gate. Kept because the alternative is a TypeError on the\n // line below, and because `delete?` stays optional on the interface for\n // the protocols the gate does not cover.\n if (typeof loader.delete !== 'function') continue;\n try {\n await this.deleteMetaItemFromLoader(loader, type, name);\n } catch (error) {\n this.reportMetaItemDeleteFailure(loader.contract.name, type, name, error);\n }\n }\n\n // ── 2. In-memory state, then invalidation — nothing awaited between ──\n const typeStore = this.registry.get(type);\n if (typeStore) {\n typeStore.delete(name);\n if (typeStore.size === 0) {\n this.registry.delete(type);\n }\n }\n this.invalidateListCache(type);\n\n // ── 3. Announce ─────────────────────────────────────────────────────\n // Publish metadata.{type}.deleted event to realtime service\n await this.publishRealtimeMetadataEvent('deleted', type, name, {\n userId: options?.userId,\n });\n\n // Announce last, once the removal has landed everywhere (see register()).\n if (options?.notify !== false) {\n this.notifyWatchers(type, {\n type: 'deleted',\n metadataType: type,\n name,\n path: '',\n data: undefined,\n timestamp: new Date().toISOString(),\n });\n }\n }\n\n /**\n * Delete one metadata item from one writable loader — the storage half of\n * {@link unregister}.\n *\n * A one-line wrapper on purpose: it gives this durability seam a **name**.\n * `check:durability-log-level` matches by callee name against an explicit\n * vocabulary, and the raw call is `loader.delete(...)` — putting `delete` in\n * that vocabulary would claim every `.delete()` in the monorepo (`Map`,\n * `Set`, cache handles, `URLSearchParams`) and the gate would drown in false\n * positives, which is exactly the failure mode its own header warns about.\n * Named here, `deleteMetaItemFromLoader` is in `DURABILITY_CRITICAL_CALLEES`\n * with a blast radius of precisely this call site, mirroring `saveMetaItem`\n * on the write side (#4754).\n *\n * [#5276] `MetadataLoader` now declares `delete?`, so no cast is left here.\n * It stays *optional* on the interface — `file:`/`memory:`/`http:`/`s3:`\n * loaders legitimately have none — and the guard below is therefore a type\n * narrowing rather than a policy decision. The policy lives at\n * `registerLoader()`: a `datasource:` loader that declares\n * `capabilities.write` cannot be registered without a `delete()`, which is\n * exactly the set of loaders this method is ever called for.\n */\n private async deleteMetaItemFromLoader(\n loader: MetadataLoader,\n type: string,\n name: string,\n ): Promise<void> {\n const del = loader.delete;\n if (typeof del !== 'function') return;\n await del.call(loader, type, name);\n }\n\n /**\n * Report — at `error` — that a loader refused to delete an item the runtime\n * has already dropped and announced as deleted.\n *\n * [#5259] This used to be a `logger.warn('Failed to delete …')` and continue.\n * AGENTS.md → \"Degradation log levels\" decides the level with one question:\n * *after the degradation, does the system still look normal from the outside\n * while something it claims is persisted has not actually landed?* Here it is\n * the deletion that did not land, which is the same class and the same\n * silence: `unregister()` resolves normally, the caller is told the delete\n * succeeded, and the surviving row is read straight back out of storage —\n * permanently, since nothing ever retries this. Durability/consistency\n * degradation ⇒ `error`, naming the **consequence** and the **fix**.\n *\n * **Why the registry entry is still dropped when this fires.** The\n * alternative — keep the item registered so runtime state matches storage —\n * looks safer and is not. The loader still holds the row, and `list()`/`get()`\n * merge registry ∪ loaders, so the item is served either way; the only thing\n * the surviving registry entry would change is *which copy wins*, pinning an\n * in-memory definition that outranks the stored row nobody is maintaining\n * anymore. Dropping it makes the very next read fall through to storage,\n * which is the actual truth after a failed delete — the item still exists —\n * and it surfaces that immediately (the item visibly reappears) instead of at\n * the next restart. One truth, read from where it lives; the divergence is\n * reported here rather than papered over with a second in-memory copy.\n *\n * **Said once per un-deleted item, not once per loader.** The once-per-outage\n * discipline of {@link reportLoaderReadFailure} exists because `list()` is hot\n * and its repeats are *identical*; these are not. Each line names a different\n * item that is still in storage and that nothing will ever retry, so\n * collapsing them would hand an operator the first casualty and silently drop\n * the rest of the list — the failure this level was raised to prevent.\n */\n private reportMetaItemDeleteFailure(\n loaderName: string,\n type: string,\n name: string,\n error: unknown,\n ): void {\n this.logger.error(\n `[MetadataManager] Loader \\`${loaderName}\\` could NOT delete \\`${type}/${name}\\` — the row is STILL in its store, ` +\n `while the runtime has already dropped the item from its registry and announced it as deleted. ` +\n `Nothing looks broken: \\`unregister()\\` resolves normally and the caller (Studio/Setup, REST DELETE, the CLI, a package teardown) ` +\n `is told the delete succeeded — but the surviving row is read straight back out of storage by the very next \\`list()\\`/\\`get()\\`, ` +\n `so the \"deleted\" item reappears and keeps reappearing across restarts. Nothing retries this delete. ` +\n `Fix: check the datasource behind \\`${loaderName}\\` — connection, credentials, and that its metadata table exists and is writable — ` +\n `then re-issue the delete for \\`${type}/${name}\\`. Until that succeeds the item is NOT deleted, whatever the delete call reported.`,\n error instanceof Error ? error : undefined,\n { loader: loaderName, type, name, error },\n );\n }\n\n /**\n * Check if a metadata item exists\n */\n async exists(type: string, name: string): Promise<boolean> {\n // Check in-memory registry\n if (this.registry.get(type)?.has(name)) {\n return true;\n }\n\n // Check loaders\n for (const loader of this.loaders.values()) {\n if (await loader.exists(type, name)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * List all names of metadata items of a given type\n */\n async listNames(type: string): Promise<string[]> {\n const names = new Set<string>();\n\n // From in-memory registry\n const typeStore = this.registry.get(type);\n if (typeStore) {\n for (const name of typeStore.keys()) {\n names.add(name);\n }\n }\n\n // From loaders\n for (const loader of this.loaders.values()) {\n const result = await loader.list(type);\n result.forEach(item => names.add(item));\n }\n\n return Array.from(names);\n }\n\n /**\n * Convenience: get an object definition by name\n */\n async getObject(name: string): Promise<unknown | undefined> {\n return this.get('object', name);\n }\n\n /**\n * Convenience: list all object definitions\n */\n async listObjects(): Promise<unknown[]> {\n return this.list('object');\n }\n\n // ==========================================\n // Convenience: UI Metadata\n // ==========================================\n\n /**\n * Convenience: get a view definition by name\n */\n async getView(name: string): Promise<unknown | undefined> {\n return this.get('view', name);\n }\n\n /**\n * Convenience: list view definitions, optionally filtered by object\n */\n async listViews(object?: string): Promise<unknown[]> {\n const views = await this.list('view');\n if (object) {\n return views.filter((v: any) => v?.object === object);\n }\n return views;\n }\n\n /**\n * List the independent ViewItems bound to an object, sorted for the runtime\n * view switcher / Studio left rail (\"Object has-many View\").\n *\n * Returns only expanded ViewItems (those carrying a `viewKind`) — never the\n * legacy aggregated container kept under the bare `<object>` key — so callers\n * get exactly one entry per named view. Sorted by `order`, then `name`.\n *\n * Runtime-authored `shared` / `personal` views (`sys_view_definition`) are\n * merged in by the REST layer; this method returns the `package` layer that\n * was registered from source.\n */\n async getViewsByObject(object: string): Promise<unknown[]> {\n const views = await this.list('view');\n return views\n .filter(\n (v: any) =>\n v && typeof v === 'object' && v.viewKind && v.object === object,\n )\n .sort(\n (a: any, b: any) =>\n (a.order ?? 0) - (b.order ?? 0) ||\n String(a.name).localeCompare(String(b.name)),\n );\n }\n\n /**\n * Convenience: get a dashboard definition by name\n */\n async getDashboard(name: string): Promise<unknown | undefined> {\n return this.get('dashboard', name);\n }\n\n /**\n * Convenience: list all dashboard definitions\n */\n async listDashboards(): Promise<unknown[]> {\n return this.list('dashboard');\n }\n\n // ==========================================\n // Package Management\n // ==========================================\n\n /**\n * Unregister all metadata items from a specific package\n */\n async unregisterPackage(packageName: string): Promise<void> {\n // Collect all items to delete (type and name pairs)\n const itemsToDelete: Array<{ type: string; name: string }> = [];\n\n for (const [type, typeStore] of this.registry) {\n for (const [name, data] of typeStore) {\n const meta = data as any;\n if (meta?.packageId === packageName || meta?.package === packageName) {\n itemsToDelete.push({ type, name });\n }\n }\n }\n\n // Delete each item using unregister() to ensure deletion from both registry and loaders\n for (const { type, name } of itemsToDelete) {\n await this.unregister(type, name);\n }\n }\n\n /**\n * Publish an entire package:\n * 1. Validate all draft items\n * 2. Snapshot all items in the package (publishedDefinition = clone(metadata))\n * 3. Increment version\n * 4. Set all items state → active\n *\n * [#5189, #5040 E7b] Step 1 additionally runs the **endpoint publish gates**\n * over every `api` item — see {@link gateApiItemsForPublish}. That pass is\n * NOT governed by `options.validate`: the gates are a contract, not a\n * lint (ADR-0121 D6 says publish REJECTS an unmetered anonymous endpoint),\n * and an opt-out flag on a security gate is the bypass this issue closed.\n */\n async publishPackage(packageId: string, options?: {\n changeNote?: string;\n publishedBy?: string;\n validate?: boolean;\n /**\n * [#5189] The package manifest's EXPLICIT `manifest.namespace` (ADR-0121\n * D2), supplied by a caller that holds the manifest.\n *\n * `MetadataManager` has no manifest concept — it indexes items by\n * `packageId` and nothing else — so it cannot prove a namespace on its\n * own, and an `api` item's own `namespace`-ish fields are author-supplied\n * data, not identity (reading them would make the D1/D2 carve-out gate\n * vacuous: an author would simply declare the namespace their path\n * already uses). Absent this option the namespace gate fails and `api`\n * items in the package cannot publish through this path — which is the\n * correct outcome, not a limitation to route around: a publish that\n * cannot prove a namespace must not mint a URL under one.\n */\n namespace?: string;\n }): Promise<PackagePublishResult> {\n const now = new Date().toISOString();\n const shouldValidate = options?.validate !== false;\n const publishedBy = options?.publishedBy;\n\n // Collect all items belonging to this package\n const packageItems: Array<{ type: string; name: string; data: any }> = [];\n for (const [type, typeStore] of this.registry) {\n for (const [name, data] of typeStore) {\n const meta = data as any;\n if (meta?.packageId === packageId || meta?.package === packageId) {\n packageItems.push({ type, name, data: meta });\n }\n }\n }\n\n if (packageItems.length === 0) {\n return {\n success: false,\n packageId,\n version: 0,\n publishedAt: now,\n itemsPublished: 0,\n validationErrors: [{ type: '', name: '', message: `No metadata items found for package '${packageId}'` }],\n };\n }\n\n const validationErrors: Array<{ type: string; name: string; message: string }> = [];\n\n // [#5189, #5040 E7b] Endpoint publish gates — ALWAYS, `validate: false`\n // included. Every other check in this method is a best-effort quality\n // check whose opt-out is a convenience; these gates decide whether an\n // externally reachable, possibly ANONYMOUS execution entry point comes\n // into existence, and ADR-0121 D6 has no runtime counterpart to catch what\n // slips through. A flag that turns them off would be exactly the bypass\n // #5189 filed.\n validationErrors.push(...this.gateApiItemsForPublish(packageItems, options?.namespace));\n\n // Validation pass\n if (shouldValidate) {\n // Schema validation\n for (const item of packageItems) {\n const result = await this.validate(item.type, item.data);\n if (!result.valid && result.errors) {\n for (const err of result.errors) {\n validationErrors.push({\n type: item.type,\n name: item.name,\n message: err.message,\n });\n }\n }\n }\n\n // Dependency validation: referenced items must be in the same package or already published\n const packageItemKeys = new Set(packageItems.map(i => `${i.type}:${i.name}`));\n for (const item of packageItems) {\n const deps = await this.getDependencies(item.type, item.name);\n for (const dep of deps) {\n const depKey = `${dep.targetType}:${dep.targetName}`;\n // Skip if the dependency is within this package\n if (packageItemKeys.has(depKey)) continue;\n // Check if the dependency exists and has been published\n const depItem = await this.get(dep.targetType, dep.targetName);\n if (!depItem) {\n validationErrors.push({\n type: item.type,\n name: item.name,\n message: `Dependency '${dep.targetType}:${dep.targetName}' not found`,\n });\n } else {\n const depMeta = depItem as any;\n if (depMeta.publishedDefinition === undefined && depMeta.state !== 'active') {\n validationErrors.push({\n type: item.type,\n name: item.name,\n message: `Dependency '${dep.targetType}:${dep.targetName}' is not published`,\n });\n }\n }\n }\n }\n }\n\n if (validationErrors.length > 0) {\n return {\n success: false,\n packageId,\n version: 0,\n publishedAt: now,\n itemsPublished: 0,\n validationErrors,\n };\n }\n\n // Determine the next version by finding the max current version across items\n let maxVersion = 0;\n for (const item of packageItems) {\n const v = typeof item.data.version === 'number' ? item.data.version : 0;\n if (v > maxVersion) maxVersion = v;\n }\n const newVersion = maxVersion + 1;\n\n // Snapshot and update all items\n for (const item of packageItems) {\n const updated = {\n ...item.data,\n publishedDefinition: structuredClone(item.data.metadata ?? item.data),\n publishedAt: now,\n publishedBy: publishedBy ?? item.data.publishedBy,\n version: newVersion,\n state: 'active',\n };\n await this.register(item.type, item.name, updated);\n }\n\n return {\n success: true,\n packageId,\n version: newVersion,\n publishedAt: now,\n itemsPublished: packageItems.length,\n };\n }\n\n /**\n * [#5189, #5040 E7b] Run the endpoint publish gates over a package's `api`\n * items and report every failure as a publish-blocking validation error.\n *\n * ## Why this exists at all\n *\n * E7 (#5111) hung the five per-endpoint gates on\n * `ObjectStackDefinitionSchema`, which covers every path that parses a\n * STACK — `defineStack`, `os validate`, the lint scorer, artifact ingest,\n * `EnvironmentArtifactSchema.metadata`. It does not cover this one: an `api`\n * item can be minted item-by-item (`metadata.register()`, a Studio write)\n * and published here without a stack ever being parsed. Three of the gates\n * degrade safely when bypassed (the executor answers a structured 501; a\n * mis-namespaced path matches nothing), but **ADR-0121 D6 has no runtime\n * counterpart**: `authRequired: false` is honoured faithfully and an\n * unarmed `rateLimit` meters nothing, so the bypass mints an anonymous,\n * zero-quota execution entry point. Hence a gate here, on the same\n * function, rather than a second set of criteria that would drift.\n *\n * ## What it judges, and on what\n *\n * The registry stores either a raw spec document or a publish envelope\n * (`{ name, packageId, state, metadata: {…spec} }`), and in BOTH shapes the\n * row carries the metadata layer's bookkeeping. [#5309] The envelope is\n * peeled off first (`peelStoredEnvelope`) and the gate judges the authored\n * BODY: the wrapped half of that peel is the `data.metadata ?? data` rule\n * this method used to spell inline — the same document `publishedDefinition`\n * snapshots — and the flat half additionally removes `packageId` / `state` /\n * `version` / `published*`, which are storage identity, never endpoint\n * vocabulary. (What publish SNAPSHOTS is unchanged: `publishedDefinition`\n * still stores `data.metadata ?? data` verbatim, envelope included, because\n * `revertPackage` restores from it.) An item whose body does not satisfy\n * `ApiEndpointSchema` fails here too — not extra strictness but a\n * precondition: an unparsed shape cannot be gated, and it could never be\n * served either (the matcher's own loud skip refuses it at load).\n *\n * @param packageItems every item collected for this package (all types).\n * @param namespace the caller-supplied `manifest.namespace`; `undefined`\n * fails the namespace gate, deliberately — see `publishPackage`'s option.\n * @returns one entry per gate failure, `[]` when the package declares no\n * `api` items (a package without endpoints is untouched by this pass).\n */\n private gateApiItemsForPublish(\n packageItems: Array<{ type: string; name: string; data: any }>,\n namespace: string | undefined,\n ): Array<{ type: string; name: string; message: string }> {\n const apiItems = packageItems.filter(i => i.type === MetadataManager.ENDPOINT_METADATA_TYPE);\n if (apiItems.length === 0) return [];\n\n const errors: Array<{ type: string; name: string; message: string }> = [];\n /** Parsed endpoints, index-aligned with the items that produced them. */\n const endpoints: ApiEndpoint[] = [];\n const gatedItems: Array<{ name: string }> = [];\n\n for (const item of apiItems) {\n // [#5309] Envelope OFF before the body parse — the same peel the load-time\n // backstop applies (`buildEndpointIndex`), so the two doors judge the same\n // document. The wrapped half of the rule is the `data.metadata ?? data`\n // this line used to spell inline; the flat half additionally takes off the\n // bookkeeping (`packageId`, `state`, …) that shares a level with the body.\n const { body } = peelStoredEnvelope(item.data);\n const parsed = ApiEndpointSchema.safeParse(body);\n if (!parsed.success) {\n for (const issue of parsed.error.issues) {\n errors.push({\n type: item.type,\n name: item.name,\n message:\n `api item '${item.name}' does not satisfy ApiEndpointSchema and cannot be published: `\n + `${issue.message} (at ${issue.path.join('.') || '<root>'}). An endpoint that does not `\n + `parse cannot be gated and would be excluded from endpoint matching at load anyway.`,\n });\n }\n continue;\n }\n endpoints.push(parsed.data);\n gatedItems.push({ name: item.name });\n }\n\n for (const issue of validateApiEndpointDeclarations(endpoints, { namespace })) {\n // The gate reports per-endpoint issues at `['apis', <index>, …]` and the\n // namespace PRECONDITION once at `['apis']` — the latter is a property of\n // the publish call, not of any one endpoint, so it is reported once with\n // this path's own remedy appended.\n const index = typeof issue.path[1] === 'number' ? issue.path[1] : undefined;\n if (index === undefined) {\n errors.push({\n type: MetadataManager.ENDPOINT_METADATA_TYPE,\n name: '',\n message: `${issue.message} ${PUBLISH_NAMESPACE_REMEDY}`,\n });\n continue;\n }\n errors.push({\n type: MetadataManager.ENDPOINT_METADATA_TYPE,\n name: gatedItems[index]?.name ?? '',\n message: issue.message,\n });\n }\n\n return errors;\n }\n\n /**\n * Revert entire package to last published state.\n * Restores all metadata definitions from their published snapshots.\n */\n async revertPackage(packageId: string): Promise<void> {\n const packageItems: Array<{ type: string; name: string; data: any }> = [];\n for (const [type, typeStore] of this.registry) {\n for (const [name, data] of typeStore) {\n const meta = data as any;\n if (meta?.packageId === packageId || meta?.package === packageId) {\n packageItems.push({ type, name, data: meta });\n }\n }\n }\n\n if (packageItems.length === 0) {\n throw new Error(`No metadata items found for package '${packageId}'`);\n }\n\n // Check that at least one item has a published snapshot\n const hasPublished = packageItems.some(item => item.data.publishedDefinition !== undefined);\n if (!hasPublished) {\n throw new Error(`Package '${packageId}' has never been published`);\n }\n\n for (const item of packageItems) {\n if (item.data.publishedDefinition !== undefined) {\n const reverted = {\n ...item.data,\n metadata: structuredClone(item.data.publishedDefinition),\n state: 'active',\n };\n await this.register(item.type, item.name, reverted);\n }\n }\n }\n\n /**\n * Get the published version of any metadata item (for runtime serving).\n * Returns publishedDefinition if exists, else current definition.\n */\n async getPublished(type: string, name: string): Promise<unknown | undefined> {\n const item = await this.get(type, name);\n if (!item) return undefined;\n\n const meta = item as any;\n if (meta.publishedDefinition !== undefined) {\n return meta.publishedDefinition;\n }\n\n // Fall back to current definition (metadata field or the item itself)\n return meta.metadata ?? item;\n }\n\n // ==========================================\n // Query / Search\n // ==========================================\n\n /**\n * Query metadata items with filtering, sorting, and pagination\n */\n async query(query: MetadataQuery): Promise<MetadataQueryResult> {\n const { types, search, page = 1, pageSize = 50, sortBy = 'name', sortOrder = 'asc' } = query;\n\n // Collect all items\n const allItems: Array<{\n type: string;\n name: string;\n namespace?: string;\n label?: string;\n scope?: 'system' | 'platform' | 'user';\n state?: 'draft' | 'active' | 'archived' | 'deprecated';\n packageId?: string;\n updatedAt?: string;\n }> = [];\n\n // Determine which types to scan\n const targetTypes = types && types.length > 0\n ? types\n : Array.from(this.registry.keys());\n\n for (const type of targetTypes) {\n const items = await this.list(type);\n for (const item of items) {\n const meta = item as any;\n allItems.push({\n type,\n name: meta?.name ?? '',\n namespace: meta?.namespace,\n label: meta?.label,\n scope: meta?.scope,\n state: meta?.state,\n packageId: meta?.packageId,\n updatedAt: meta?.updatedAt,\n });\n }\n }\n\n // Apply search filter\n let filtered = allItems;\n if (search) {\n const searchLower = search.toLowerCase();\n filtered = filtered.filter(item =>\n item.name.toLowerCase().includes(searchLower) ||\n (item.label && item.label.toLowerCase().includes(searchLower))\n );\n }\n\n // Apply scope filter\n if (query.scope) {\n filtered = filtered.filter(item => item.scope === query.scope);\n }\n\n // Apply state filter\n if (query.state) {\n filtered = filtered.filter(item => item.state === query.state);\n }\n\n // Apply namespace filter\n if (query.namespaces && query.namespaces.length > 0) {\n filtered = filtered.filter(item => item.namespace && query.namespaces!.includes(item.namespace));\n }\n\n // Apply packageId filter\n if (query.packageId) {\n filtered = filtered.filter(item => item.packageId === query.packageId);\n }\n\n // Apply tags filter\n if (query.tags && query.tags.length > 0) {\n filtered = filtered.filter(item => {\n const meta = item as any;\n return meta?.tags && query.tags!.some((t: string) => meta.tags.includes(t));\n });\n }\n\n // Sort\n filtered.sort((a, b) => {\n const aVal = (a as any)[sortBy] ?? '';\n const bVal = (b as any)[sortBy] ?? '';\n const cmp = String(aVal).localeCompare(String(bVal));\n return sortOrder === 'desc' ? -cmp : cmp;\n });\n\n // Paginate\n const total = filtered.length;\n const start = (page - 1) * pageSize;\n const paged = filtered.slice(start, start + pageSize);\n\n return {\n items: paged,\n total,\n page,\n pageSize,\n };\n }\n\n // ==========================================\n // Bulk Operations\n // ==========================================\n\n /**\n * Register multiple metadata items in a single batch.\n *\n * Announces one event per item, like {@link register}. Pass\n * `{ notify: false }` when the batch is boot-time ingest or when the caller\n * announces the whole set once — see {@link MetadataWriteOptions.notify}.\n */\n async bulkRegister(\n items: Array<{ type: string; name: string; data: unknown }>,\n options?: { continueOnError?: boolean; validate?: boolean } & MetadataWriteOptions\n ): Promise<MetadataBulkResult> {\n const { continueOnError = false, notify } = options ?? {};\n let succeeded = 0;\n let failed = 0;\n const errors: Array<{ type: string; name: string; error: string }> = [];\n\n for (const item of items) {\n try {\n await this.register(item.type, item.name, item.data, { notify });\n succeeded++;\n } catch (e) {\n failed++;\n errors.push({\n type: item.type,\n name: item.name,\n error: e instanceof Error ? e.message : String(e),\n });\n if (!continueOnError) break;\n }\n }\n\n return {\n total: items.length,\n succeeded,\n failed,\n errors: errors.length > 0 ? errors : undefined,\n };\n }\n\n /**\n * Unregister multiple metadata items in a single batch.\n *\n * Announces one `deleted` event per item, like {@link unregister}.\n */\n async bulkUnregister(\n items: Array<{ type: string; name: string }>,\n options?: MetadataWriteOptions,\n ): Promise<MetadataBulkResult> {\n let succeeded = 0;\n let failed = 0;\n const errors: Array<{ type: string; name: string; error: string }> = [];\n\n for (const item of items) {\n try {\n await this.unregister(item.type, item.name, options);\n succeeded++;\n } catch (e) {\n failed++;\n errors.push({\n type: item.type,\n name: item.name,\n error: e instanceof Error ? e.message : String(e),\n });\n }\n }\n\n return {\n total: items.length,\n succeeded,\n failed,\n errors: errors.length > 0 ? errors : undefined,\n };\n }\n\n // ==========================================\n // Overlay / Customization Management\n // ==========================================\n\n private overlayKey(type: string, name: string, scope: string = 'platform'): string {\n return `${encodeURIComponent(type)}:${encodeURIComponent(name)}:${scope}`;\n }\n\n /**\n * Get the active overlay for a metadata item\n */\n async getOverlay(type: string, name: string, scope?: 'platform' | 'user'): Promise<MetadataOverlay | undefined> {\n return this.overlays.get(this.overlayKey(type, name, scope ?? 'platform'));\n }\n\n /**\n * Save/update an overlay for a metadata item\n */\n async saveOverlay(overlay: MetadataOverlay): Promise<void> {\n // Overlay write gate — independent from base writability so deployments\n // can freeze Studio overlays while still permitting base register().\n if (this.config.persistence?.overlayWritable === false) {\n const msg = `MetadataManager overlays are read-only (persistence.overlayWritable=false); refusing to save overlay for ${overlay.baseType}/${overlay.baseName}`;\n if (this.config.validation?.throwOnError) {\n throw new Error(msg);\n }\n this.logger.warn(msg);\n return;\n }\n const key = this.overlayKey(overlay.baseType, overlay.baseName, overlay.scope);\n this.overlays.set(key, overlay);\n }\n\n /**\n * Remove an overlay, reverting to the base definition\n */\n async removeOverlay(type: string, name: string, scope?: 'platform' | 'user'): Promise<void> {\n this.overlays.delete(this.overlayKey(type, name, scope ?? 'platform'));\n }\n\n /**\n * Get the effective (merged) metadata after applying all overlays.\n * Resolution order: system ← merge(platform) ← merge(user)\n */\n async getEffective(type: string, name: string, context?: {\n userId?: string;\n tenantId?: string;\n roles?: string[];\n permissions?: string[];\n }): Promise<unknown | undefined> {\n const base = await this.get(type, name);\n if (!base) return undefined;\n\n let effective = { ...(base as Record<string, unknown>) };\n\n // Apply platform overlay\n const platformOverlay = await this.getOverlay(type, name, 'platform');\n if (platformOverlay?.active && platformOverlay.patch) {\n effective = { ...effective, ...platformOverlay.patch };\n }\n\n // Apply user overlay (scoped to specific user if context provided)\n if (context?.userId) {\n // Try user-specific key first, then fall back to generic user overlay.\n // The owner check below ensures we never apply another user's overlay.\n const userOverlayKey = this.overlayKey(type, name, 'user') + `:${context.userId}`;\n const userOverlay = this.overlays.get(userOverlayKey) \n ?? await this.getOverlay(type, name, 'user');\n if (userOverlay?.active && userOverlay.patch) {\n // Apply if: overlay has no owner (generic user-level), or owner matches current user\n if (!userOverlay.owner || userOverlay.owner === context.userId) {\n effective = { ...effective, ...userOverlay.patch };\n }\n }\n } else {\n // No user context — only apply user overlays without an owner restriction\n // (owner-scoped overlays require a userId to resolve)\n const userOverlay = await this.getOverlay(type, name, 'user');\n if (userOverlay?.active && userOverlay.patch && !userOverlay.owner) {\n effective = { ...effective, ...userOverlay.patch };\n }\n }\n\n return effective;\n }\n\n // ==========================================\n // Watch / Subscribe (IMetadataService)\n // ==========================================\n\n /**\n * Watch for metadata changes (IMetadataService contract).\n * Returns a handle for unsubscribing.\n */\n watchService(type: string, callback: MetadataWatchCallback): MetadataWatchHandle {\n const wrappedCallback: WatchCallback = (event) => {\n const mappedType = event.type === 'added' ? 'registered'\n : event.type === 'deleted' ? 'unregistered'\n : 'updated';\n callback({\n type: mappedType,\n metadataType: event.metadataType ?? type,\n name: event.name ?? '',\n data: event.data,\n });\n };\n this.addWatchCallback(type, wrappedCallback);\n return {\n unsubscribe: () => this.removeWatchCallback(type, wrappedCallback),\n };\n }\n\n /**\n * Subscribe to raw metadata watch events for a given type.\n *\n * Unlike `watchService` (which maps to the IMetadataService contract and\n * drops fields like `path`/`timestamp`), this returns the raw\n * `MetadataWatchEvent` produced by the underlying watcher — useful for\n * developer-facing tooling such as the HMR SSE endpoint that wants the\n * source file path and original timestamp.\n *\n * @returns An unsubscribe function.\n */\n subscribe(type: string, callback: WatchCallback): () => void {\n this.addWatchCallback(type, callback);\n return () => this.removeWatchCallback(type, callback);\n }\n\n // ==========================================\n // Import / Export\n // ==========================================\n\n /**\n * Export metadata as a portable bundle\n */\n async exportMetadata(options?: MetadataExportOptions): Promise<unknown> {\n const bundle: Record<string, unknown[]> = {};\n const targetTypes = options?.types ?? Array.from(this.registry.keys());\n\n for (const type of targetTypes) {\n const items = await this.list(type);\n if (items.length > 0) {\n bundle[type] = items;\n }\n }\n\n return bundle;\n }\n\n /**\n * Import metadata from a portable bundle\n */\n async importMetadata(data: unknown, options?: MetadataImportOptions): Promise<MetadataImportResult> {\n const {\n conflictResolution = 'skip',\n validate: _validate = true,\n dryRun = false,\n } = options ?? {};\n\n const bundle = data as Record<string, unknown[]>;\n let total = 0;\n let imported = 0;\n let skipped = 0;\n let failed = 0;\n const errors: Array<{ type: string; name: string; error: string }> = [];\n\n for (const [type, items] of Object.entries(bundle)) {\n if (!Array.isArray(items)) continue;\n\n for (const item of items) {\n total++;\n const meta = item as any;\n const name = meta?.name;\n\n if (!name) {\n failed++;\n errors.push({ type, name: '(unknown)', error: 'Item missing name field' });\n continue;\n }\n\n try {\n const itemExists = await this.exists(type, name);\n\n if (itemExists && conflictResolution === 'skip') {\n skipped++;\n continue;\n }\n\n if (!dryRun) {\n if (itemExists && conflictResolution === 'merge') {\n const existing = await this.get(type, name);\n const merged = { ...(existing as any), ...(item as any) };\n await this.register(type, name, merged);\n } else {\n await this.register(type, name, item);\n }\n }\n imported++;\n } catch (e) {\n failed++;\n errors.push({\n type,\n name,\n error: e instanceof Error ? e.message : String(e),\n });\n }\n }\n }\n\n return {\n total,\n imported,\n skipped,\n failed,\n errors: errors.length > 0 ? errors : undefined,\n };\n }\n\n // ==========================================\n // Validation\n // ==========================================\n\n /**\n * Validate a metadata item against its type schema.\n *\n * NOTE: This is a lightweight structural check (presence of `name`,\n * basic shape). The authoritative spec validation lives in\n * `protocol.saveMetaItem` (write path) and is surfaced on read\n * paths via the `_diagnostics` envelope attached by\n * `protocol.getMetaItems` / `getMetaItem`. Both delegate to\n * `getMetadataTypeSchema()` — the single source of truth. We\n * deliberately do NOT run the full Zod schema here because\n * `MetadataManager`'s registry stores *publish envelopes*\n * (`{name, packageId, state, metadata: {...spec}}`), not raw spec\n * documents — running spec validation against the envelope would\n * yield false negatives.\n */\n async validate(_type: string, data: unknown): Promise<MetadataValidationResult> {\n // Basic structural validation\n if (data === null || data === undefined) {\n return {\n valid: false,\n errors: [{ path: '', message: 'Metadata data cannot be null or undefined' }],\n };\n }\n\n if (typeof data !== 'object') {\n return {\n valid: false,\n errors: [{ path: '', message: 'Metadata data must be an object' }],\n };\n }\n\n const meta = data as any;\n const warnings: Array<{ path: string; message: string }> = [];\n\n if (!meta.name) {\n return {\n valid: false,\n errors: [{ path: 'name', message: 'Metadata item must have a name field' }],\n };\n }\n\n if (!meta.label) {\n warnings.push({ path: 'label', message: 'Missing label field (recommended)' });\n }\n\n return { valid: true, warnings: warnings.length > 0 ? warnings : undefined };\n }\n\n // ==========================================\n // Type Registry\n // ==========================================\n\n /**\n * Get all registered metadata types\n */\n async getRegisteredTypes(): Promise<string[]> {\n const types = new Set<string>();\n\n // From type registry\n for (const entry of this.typeRegistry) {\n types.add(entry.type);\n }\n\n // From in-memory registry (custom types)\n for (const type of this.registry.keys()) {\n types.add(type);\n }\n\n return Array.from(types);\n }\n\n /**\n * Get detailed information about a metadata type\n */\n async getTypeInfo(type: string): Promise<MetadataTypeInfo | undefined> {\n const entry = this.typeRegistry.find(e => e.type === type);\n if (!entry) return undefined;\n\n // Merge declarative (live registry entry — covers built-ins AND\n // plugin-contributed `additionalTypes`) + plugin-registered type-level\n // actions. Deduped by name; imperatively-registered actions win on\n // collision. Emitted so the metadata-admin engine can render per-type\n // buttons (e.g. datasource \"Test connection\"). Omit the key entirely\n // when the type has none, to keep the response lean.\n const byName = new Map<string, any>();\n for (const a of (entry.actions ?? [])) byName.set(a.name, a);\n for (const a of getMetadataTypeActions(type)) byName.set(a.name, a);\n const actions = Array.from(byName.values());\n\n return {\n type: entry.type,\n label: entry.label,\n description: entry.description,\n filePatterns: entry.filePatterns,\n supportsOverlay: entry.supportsOverlay,\n domain: entry.domain,\n ...(actions.length > 0 ? { actions } : {}),\n };\n }\n\n // ==========================================\n // Dependency Tracking\n // ==========================================\n\n /**\n * Get metadata items that this item depends on\n */\n async getDependencies(type: string, name: string): Promise<MetadataDependency[]> {\n return this.dependencies.get(`${encodeURIComponent(type)}:${encodeURIComponent(name)}`) ?? [];\n }\n\n /**\n * Get metadata items that depend on this item\n */\n async getDependents(type: string, name: string): Promise<MetadataDependency[]> {\n const dependents: MetadataDependency[] = [];\n for (const deps of this.dependencies.values()) {\n for (const dep of deps) {\n if (dep.targetType === type && dep.targetName === name) {\n dependents.push(dep);\n }\n }\n }\n return dependents;\n }\n\n /**\n * Register a dependency between two metadata items.\n * Used internally to track cross-references.\n * Duplicate dependencies (same source, target, and kind) are ignored.\n */\n addDependency(dep: MetadataDependency): void {\n const key = `${encodeURIComponent(dep.sourceType)}:${encodeURIComponent(dep.sourceName)}`;\n if (!this.dependencies.has(key)) {\n this.dependencies.set(key, []);\n }\n const existing = this.dependencies.get(key)!;\n const isDuplicate = existing.some(\n d => d.targetType === dep.targetType && d.targetName === dep.targetName && d.kind === dep.kind\n );\n if (!isDuplicate) {\n existing.push(dep);\n }\n }\n\n // ==========================================\n // API Endpoint Resolution\n // ==========================================\n\n /**\n * Resolve a request's `method`+`path` to the declared `api` metadata item\n * that owns it — `IMetadataService.matchEndpoint` (#5080 contract, #5089\n * implementation, #5040 E2).\n *\n * The behaviour is specified by the contract text in\n * `packages/spec/src/contracts/metadata-service.ts`; the mechanics\n * (normalization, lazy index, loud parse-skip, duplicate resolution) live in\n * `./endpoint-matcher.ts` and are documented there.\n *\n * Scope is THIS instance. There is no environment parameter, because callers\n * already resolve the `metadata` service for the environment they serve —\n * adding one here would create a second scoping mechanism.\n *\n * This method is reached over HTTP on a real boot. The dispatcher seam\n * landed as #5090 (`packages/runtime/src/api-endpoint-step.ts`, called from\n * the `setFallbackHandler` the dispatcher plugin installs), and #4936's\n * wholesale publish refusal of a non-empty `apis:` was replaced by the\n * #5040 E7 per-shape gates (`packages/spec/src/api/endpoint-publish-gate.ts`)\n * — so declarations exist and requests arrive here. The showcase's two\n * declared endpoints are matched and executed through this path in\n * `packages/qa/dogfood/test/showcase-declarative-endpoints.dogfood.test.ts`.\n *\n * @throws when the metadata store cannot be read — an outage must never be\n * reported as a miss, because a miss becomes a 404.\n */\n async matchEndpoint(query: { path: string; method: string }): Promise<ApiEndpointMatch | undefined> {\n return this.endpointMatcher.match(query);\n }\n\n // ==========================================\n // Legacy Loader API (backward compatible)\n // ==========================================\n\n /**\n * Load a single metadata item from loaders.\n * Iterates through registered loaders until found.\n *\n * Returns `null` both when no loader HAS the item and when every loader\n * FAILED — see {@link loadDiagnosed} when the caller must tell those apart.\n */\n async load<T = any>(\n type: string,\n name: string,\n options?: MetadataLoadOptions\n ): Promise<T | null> {\n return (await this.loadDiagnosed<T>(type, name, options)).data;\n }\n\n /**\n * `load`, plus whether the answer can be trusted as complete.\n *\n * [ADR-0110 D3] A miss and an outage are different facts with opposite\n * security meanings, and plain `load` cannot express the difference: a\n * loader that throws is warn-logged and skipped, so a database the metadata\n * plane cannot reach returns the same `null` as a name that was never\n * declared. Callers that gate on a declaration MUST NOT read that `null` as\n * \"the author declared no gate\" — an availability failure would silently\n * widen access (the REST `/actions` route's fail-open branch, #3935).\n *\n * `degraded` is true when at least one loader threw AND no loader answered\n * with the item. The posture is deliberately conservative: with a loader\n * down we cannot prove the item is absent, so we decline to claim it is.\n * A clean miss (every loader answered, none had it) is NOT degraded.\n */\n async loadDiagnosed<T = any>(\n type: string,\n name: string,\n options?: MetadataLoadOptions\n ): Promise<{ data: T | null; degraded: boolean; errors: string[] }> {\n const errors: string[] = [];\n for (const loader of this.loaders.values()) {\n try {\n const result = await loader.load(type, name, options);\n if (result.data) {\n return { data: result.data as T, degraded: false, errors };\n }\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n errors.push(`${loader.contract.name}: ${message}`);\n this.logger.warn(`Loader ${loader.contract.name} failed to load ${type}:${name}`, { error: e });\n }\n }\n return { data: null, degraded: errors.length > 0, errors };\n }\n\n /**\n * Load multiple metadata items from loaders.\n * Aggregates results from all loaders.\n */\n async loadMany<T = any>(\n type: string,\n options?: MetadataLoadOptions\n ): Promise<T[]> {\n const results: T[] = [];\n\n for (const loader of this.loaders.values()) {\n try {\n const items = await loader.loadMany<T>(type, options);\n for (const item of items) {\n const itemAny = item as any;\n if (itemAny && typeof itemAny.name === 'string') {\n const exists = results.some((r: any) => r && r.name === itemAny.name);\n if (exists) continue;\n }\n results.push(item);\n }\n this.reportLoaderReadRecovered(loader.contract.name);\n } catch (e) {\n // [#5108] Same seam, same verdict as `list()` — see\n // {@link reportLoaderReadFailure}. Two adjacent plural reads\n // reporting one storage outage at two different levels is how the\n // wrong one gets copied.\n this.reportLoaderReadFailure(loader.contract.name, type, e);\n }\n }\n return results;\n }\n\n /**\n * Save metadata item to a loader\n */\n async save<T = any>(\n type: string,\n name: string,\n data: T,\n options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n const targetLoader = (options as any)?.loader;\n\n let loader: MetadataLoader | undefined;\n \n if (targetLoader) {\n loader = this.loaders.get(targetLoader);\n if (!loader) {\n throw new Error(`Loader not found: ${targetLoader}`);\n }\n } else {\n for (const l of this.loaders.values()) {\n if (!l.save) continue;\n try {\n if (await l.exists(type, name)) {\n loader = l;\n this.logger.info(`Updating existing metadata in loader: ${l.contract.name}`);\n break;\n }\n } catch (e) {\n // Ignore existence check errors\n }\n }\n\n if (!loader) {\n const fsLoader = this.loaders.get('filesystem');\n if (fsLoader && fsLoader.save) {\n loader = fsLoader;\n }\n }\n\n if (!loader) {\n for (const l of this.loaders.values()) {\n if (l.save) {\n loader = l;\n break;\n }\n }\n }\n }\n\n if (!loader) {\n throw new Error(`No loader available for saving type: ${type}`);\n }\n\n if (!loader.save) {\n throw new Error(`Loader '${loader.contract?.name}' does not support saving`);\n }\n\n return loader.save(type, name, data, options);\n }\n\n /**\n * Register a watch callback for metadata changes\n */\n protected addWatchCallback(type: string, callback: WatchCallback): void {\n if (!this.watchCallbacks.has(type)) {\n this.watchCallbacks.set(type, new Set());\n }\n this.watchCallbacks.get(type)!.add(callback);\n }\n\n /**\n * Remove a watch callback for metadata changes\n */\n protected removeWatchCallback(type: string, callback: WatchCallback): void {\n const callbacks = this.watchCallbacks.get(type);\n if (callbacks) {\n callbacks.delete(callback);\n if (callbacks.size === 0) {\n this.watchCallbacks.delete(type);\n }\n }\n }\n\n /**\n * Stop all watching\n */\n async stopWatching(): Promise<void> {\n // Override in subclass\n }\n\n // ─── ADR-0008 PR-6: Repository wiring ───────────────────────────────\n\n /**\n * Attach a {@link MetadataRepository} as a supplementary event source.\n *\n * The manager subscribes to `repo.watch({})` and re-emits each event\n * through {@link notifyWatchers} as a legacy `MetadataWatchEvent`.\n * Each event also invalidates the in-memory registry entry and the\n * `list()` cache for the affected type so subsequent reads see fresh\n * data.\n *\n * No write-through. `register()` / `unregister()` / `save()` are\n * untouched in this PR (deferred to ADR-0008 M0 PR-10).\n *\n * Call {@link dispose} (or {@link stopRepositoryWatch}) to detach.\n */\n setRepository(repo: MetadataRepository): void {\n if (this.repository === repo) return;\n if (this.repository) {\n void this.stopRepositoryWatch();\n }\n this.repository = repo;\n this.repoWatchClosed = false;\n void this.startRepositoryWatch();\n }\n\n /** Return the attached repository, if any. */\n getRepository(): MetadataRepository | undefined {\n return this.repository;\n }\n\n /** Stop the active repo.watch() loop (best-effort). */\n async stopRepositoryWatch(): Promise<void> {\n this.repoWatchClosed = true;\n const iter = this.repoWatchIter;\n this.repoWatchIter = undefined;\n if (iter && typeof iter.return === 'function') {\n try { await iter.return(undefined); } catch { /* noop */ }\n }\n }\n\n /**\n * Best-effort cleanup. Stops the FS watcher (if any), drains the\n * repository watch loop, and clears registry caches. Safe to call\n * multiple times.\n */\n async dispose(): Promise<void> {\n await this.stopWatching().catch(() => undefined);\n await this.stopRepositoryWatch().catch(() => undefined);\n this.listCache.clear();\n this.endpointMatcher.invalidate();\n }\n\n private async startRepositoryWatch(): Promise<void> {\n const repo = this.repository;\n if (!repo) return;\n const iterable = repo.watch({});\n const iter = (iterable as AsyncIterable<MetadataEvent>)[Symbol.asyncIterator]();\n this.repoWatchIter = iter;\n try {\n while (!this.repoWatchClosed) {\n const { value, done } = await iter.next();\n if (done) break;\n try {\n this.applyRepoEvent(value);\n } catch (err) {\n this.logger.warn('[MetadataManager] repo event handler failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n } catch (err) {\n if (!this.repoWatchClosed) {\n this.logger.warn('[MetadataManager] repository watch loop exited unexpectedly', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n } finally {\n if (this.repoWatchIter === iter) this.repoWatchIter = undefined;\n }\n }\n\n /**\n * Drop every local cache of `type` (and of `name` within it) that a change\n * we did not perform ourselves has just invalidated, so the next read falls\n * through to the source of truth.\n *\n * The callers are the manager's *foreign-write* seams — the repository watch\n * loop ({@link applyRepoEvent}), the cluster peer replay in\n * {@link attachClusterPubSub}, and — since #5218 — `NodeMetadataManager`'s\n * chokidar handler, which is why this is `protected` rather than `private`.\n * All three learn about a write that landed somewhere else (the repo head;\n * another node's `sys_metadata`; an editor writing `rootDir/view/x.json`) and\n * hold caches that the write silently aged out. A file event qualifies on\n * exactly the definition that matters here: it did not come through this\n * manager's write API, so nothing has updated the caches on its behalf.\n * Local writes do not come through here: `register()` / `unregister()` /\n * `registerInMemory()` update the registry to the value they just wrote and\n * call `invalidateListCache()` themselves.\n *\n * **Delete, do not pre-fill.** Even when the event carries a body we drop the\n * registry entry rather than writing the body into it: the body reaching us\n * is a snapshot of *someone else's* write, already possibly superseded, and\n * pre-filling would race with the true head and require us to re-canonicalise\n * a definition we did not load. Lazy invalidation is the safer default —\n * `get()` then falls through to the loaders / repository, which is where the\n * truth is. (This paragraph is the rationale `applyRepoEvent` carried since\n * ADR-0008 PR-6; #5109 extended the same choice to the cluster path, #5218 to\n * the filesystem watcher — where \"the truth\" is the file chokidar just\n * reported, served by the `FilesystemLoader` the registry entry was shadowing.)\n *\n * `name` is optional because `MetadataWatchEvent.name` is: a nameless event\n * cannot address a registry entry, so it invalidates the list cache only.\n * Dropping the whole type store instead would evict `registerInMemory()`\n * artefacts (code-owned datasources, ADR-0015 Addendum) that no loader can\n * restore — an unrecoverable loss in exchange for a guess.\n */\n protected invalidateForForeignWrite(type: string, name?: string): void {\n if (name) {\n const typeStore = this.registry.get(type);\n if (typeStore) {\n typeStore.delete(name);\n if (typeStore.size === 0) this.registry.delete(type);\n }\n }\n this.invalidateListCache(type);\n }\n\n /** Translate a repo event to the legacy MetadataWatchEvent + invalidate caches. */\n private applyRepoEvent(evt: MetadataEvent): void {\n const ref: MetaRef = evt.ref;\n const type = ref.type;\n const name = ref.name;\n\n // Invalidate before announcing, so a watcher that re-reads on the event\n // observes the write rather than the pre-write cache. See\n // {@link invalidateForForeignWrite} for why the registry entry is deleted\n // rather than pre-filled.\n this.invalidateForForeignWrite(type, name);\n\n const legacyType: 'added' | 'changed' | 'deleted' =\n evt.op === 'create' ? 'added'\n : evt.op === 'delete' ? 'deleted'\n : 'changed';\n\n const legacyEvent: MetadataWatchEvent = {\n type: legacyType,\n metadataType: type,\n name,\n path: '',\n // Repo events carry the hash only; the body is fetched on demand\n // via manager.get(type, name). HMR consumers don't read `data` so\n // this is fine for M0. (See ADR-0008 §12 open question 1.)\n data: undefined,\n timestamp: evt.ts,\n };\n // Carry the canonical server-side `seq` so downstream consumers\n // (HMR SSE route → Studio status badge) can render an accurate\n // \"N changes since boot\" matching what other replicas observe.\n // Non-typed extra property on purpose — extending MetadataWatchEvent\n // is a spec-package change deferred to a later PR.\n (legacyEvent as Record<string, unknown>).seq = evt.seq;\n this.notifyWatchers(type, legacyEvent);\n }\n\n protected notifyWatchers(type: string, event: MetadataWatchEvent) {\n this.notifyWatchersLocal(type, event);\n\n // Cluster fan-out (cluster-semantics.mdx §5). Best-effort: a publish\n // failure must never block the local update.\n if (this.clusterPubSub) {\n const payload: ClusterMetadataChangedPayload = {\n originNode: this.clusterNodeId,\n type,\n event,\n };\n const key = `${type}:${(event as { name?: string }).name ?? ''}`;\n void this.clusterPubSub\n .publish(MetadataManager.CLUSTER_CHANNEL, payload, { partitionKey: key })\n .catch((err) => {\n this.logger.error('Cluster metadata publish failed', undefined, {\n type,\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }\n }\n\n private notifyWatchersLocal(type: string, event: MetadataWatchEvent) {\n const callbacks = this.watchCallbacks.get(type);\n if (!callbacks) return;\n\n for (const callback of callbacks) {\n try {\n void callback(event);\n } catch (error) {\n this.logger.error('Watch callback error', undefined, {\n type,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n\n /**\n * Attach a cluster pub/sub transport so metadata-change events fan\n * out to peer nodes and remote events replay into local watchers.\n *\n * The bridge plugin in @objectstack/service-cluster calls this once\n * per kernel boot after both cluster and metadata services are\n * registered. Passing the same MetadataManager twice no-ops; passing\n * a different transport replaces the prior subscription.\n *\n * Pass `nodeId` matching the local cluster's nodeId so loopback\n * suppression works.\n *\n * @returns disposer that unsubscribes from cluster events.\n */\n attachClusterPubSub(pubsub: IPubSub, nodeId: string): () => void {\n // Idempotent on (pubsub, nodeId) — re-attaching same pair short-circuits.\n if (this.clusterPubSub === pubsub && this.clusterNodeId === nodeId) {\n return () => this.detachClusterPubSub();\n }\n this.detachClusterPubSub();\n this.clusterPubSub = pubsub;\n this.clusterNodeId = nodeId;\n this.clusterUnsubscribe = pubsub.subscribe<ClusterMetadataChangedPayload>(\n MetadataManager.CLUSTER_CHANNEL,\n (msg) => {\n const p = msg.payload;\n // Loopback guard — never replay events we just emitted.\n if (p?.originNode && p.originNode === this.clusterNodeId) return;\n if (!p?.type || !p.event) return;\n\n // [#5109] Invalidate FIRST, and SYNCHRONOUSLY on receipt — this is\n // what the channel is for (\"consumed by peers to invalidate their\n // local caches\", see ClusterMetadataChangedPayload). Until this\n // landed, a peer's write only woke this node's watchers: the registry\n // entry and the `listCache` were left untouched, so every `list(type)`\n // kept serving the pre-write set for up to LIST_CACHE_TTL_MS (30s) —\n // and a watcher that re-read via `list()` in response to the wake-up\n // got the stale set back, an invalidation notice carrying invalidated\n // data.\n //\n // Two deliberate choices, both pinned by tests in\n // `metadata-manager-cluster.test.ts`:\n //\n // • BEFORE the notify, matching every other write path in this file\n // (`register` / `unregister` / `applyRepoEvent` all invalidate,\n // then announce): a watcher must never be able to observe the\n // event and the pre-event cache at the same time.\n // • OUTSIDE the `setImmediate`, unlike the notify. The deferral\n // exists so a slow *watcher callback* — arbitrary consumer code —\n // cannot back-pressure the pubsub dispatch loop. Invalidation is\n // two `Map.delete`s and runs no consumer code, so it has nothing\n // to defer for, while deferring it would leave a window between\n // receipt and the next tick in which reads still answer stale.\n // Any `await` in a request handler is enough to lose that race.\n try {\n this.invalidateForForeignWrite(p.type, p.event.name);\n } catch (err) {\n this.logger.error('Cluster remote invalidation failed', undefined, {\n type: p.type,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n\n // Defer to setImmediate so a slow local handler can't back-pressure\n // the pubsub dispatch loop on memory drivers.\n setImmediate(() => {\n try {\n this.notifyWatchersLocal(p.type, p.event);\n } catch (err) {\n this.logger.error('Cluster remote replay failed', undefined, {\n type: p.type,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n });\n },\n );\n this.logger.info('MetadataManager attached to cluster pubsub', {\n nodeId,\n channel: MetadataManager.CLUSTER_CHANNEL,\n });\n return () => this.detachClusterPubSub();\n }\n\n /** Tear down cluster wiring. Safe to call multiple times. */\n detachClusterPubSub(): void {\n if (this.clusterUnsubscribe) {\n try { this.clusterUnsubscribe(); } catch { /* idempotent */ }\n this.clusterUnsubscribe = undefined;\n }\n this.clusterPubSub = undefined;\n this.clusterNodeId = undefined;\n }\n\n // ==========================================\n // Version History & Rollback\n // ==========================================\n\n /**\n * Get the database loader for history operations.\n * Returns undefined if no database loader is configured.\n */\n private getDatabaseLoader(): DatabaseLoader | undefined {\n const dbLoader = this.loaders.get('database');\n if (dbLoader && dbLoader instanceof DatabaseLoader) {\n return dbLoader;\n }\n return undefined;\n }\n\n /**\n * Get version history for a metadata item.\n * Returns a timeline of all changes made to the item.\n */\n async getHistory(\n type: string,\n name: string,\n options?: MetadataHistoryQueryOptions\n ): Promise<MetadataHistoryQueryResult> {\n const dbLoader = this.getDatabaseLoader();\n if (!dbLoader) {\n throw new Error('History tracking requires a database loader to be configured');\n }\n\n return dbLoader.queryHistory(type, name, {\n operationType: options?.operationType,\n since: options?.since,\n until: options?.until,\n limit: options?.limit,\n offset: options?.offset,\n includeMetadata: options?.includeMetadata,\n });\n }\n\n /**\n * Rollback a metadata item to a specific version.\n * Restores the metadata definition from the history snapshot.\n */\n async rollback(\n type: string,\n name: string,\n version: number,\n options?: {\n changeNote?: string;\n recordedBy?: string;\n }\n ): Promise<unknown> {\n const dbLoader = this.getDatabaseLoader();\n if (!dbLoader) {\n throw new Error('Rollback requires a database loader to be configured');\n }\n\n // Fetch the target version snapshot directly from the history table\n const targetVersion = await dbLoader.getHistoryRecord(type, name, version);\n\n if (!targetVersion) {\n throw new Error(`Version ${version} not found in history for ${type}/${name}`);\n }\n\n if (!targetVersion.metadata) {\n throw new Error(`Version ${version} metadata snapshot not available`);\n }\n\n // Restore the metadata using the dedicated rollback path so that a single\n // 'revert' history entry is written (instead of a conflicting 'update' entry)\n const restoredMetadata = targetVersion.metadata;\n await dbLoader.registerRollback(\n type,\n name,\n restoredMetadata,\n version,\n options?.changeNote,\n options?.recordedBy\n );\n\n // Update in-memory registry with the restored metadata\n if (!this.registry.has(type)) {\n this.registry.set(type, new Map());\n }\n this.registry.get(type)!.set(name, restoredMetadata);\n\n return restoredMetadata;\n }\n\n /**\n * Compare two versions of a metadata item.\n * Returns a diff showing what changed between versions.\n */\n async diff(\n type: string,\n name: string,\n version1: number,\n version2: number\n ): Promise<MetadataDiffResult> {\n const dbLoader = this.getDatabaseLoader();\n if (!dbLoader) {\n throw new Error('Diff requires a database loader to be configured');\n }\n\n // Fetch the two version snapshots directly from the history table\n const v1 = await dbLoader.getHistoryRecord(type, name, version1);\n const v2 = await dbLoader.getHistoryRecord(type, name, version2);\n\n if (!v1) {\n throw new Error(`Version ${version1} not found in history for ${type}/${name}`);\n }\n\n if (!v2) {\n throw new Error(`Version ${version2} not found in history for ${type}/${name}`);\n }\n\n if (!v1.metadata || !v2.metadata) {\n throw new Error('Version metadata snapshots not available');\n }\n\n // Generate diff\n const patch = generateSimpleDiff(v1.metadata, v2.metadata);\n const identical = patch.length === 0;\n const summary = generateDiffSummary(patch);\n\n return {\n type,\n name,\n version1,\n version2,\n checksum1: v1.checksum,\n checksum2: v2.checksum,\n identical,\n patch,\n summary,\n };\n }\n}\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * JSON Metadata Serializer\n * \n * Handles JSON format serialization and deserialization\n */\n\nimport type { z } from 'zod';\nimport type { MetadataFormat } from '@objectstack/spec/system';\nimport type { MetadataSerializer, SerializeOptions } from './serializer-interface.js';\n\nexport class JSONSerializer implements MetadataSerializer {\n serialize<T>(item: T, options?: SerializeOptions): string {\n const { prettify = true, indent = 2, sortKeys = false } = options || {};\n\n if (sortKeys) {\n // Sort keys recursively\n const sorted = this.sortObjectKeys(item);\n return prettify\n ? JSON.stringify(sorted, null, indent)\n : JSON.stringify(sorted);\n }\n\n return prettify\n ? JSON.stringify(item, null, indent)\n : JSON.stringify(item);\n }\n\n deserialize<T>(content: string, schema?: z.ZodSchema): T {\n const parsed = JSON.parse(content);\n\n if (schema) {\n return schema.parse(parsed) as T;\n }\n\n return parsed as T;\n }\n\n getExtension(): string {\n return '.json';\n }\n\n canHandle(format: MetadataFormat): boolean {\n return format === 'json';\n }\n\n getFormat(): MetadataFormat {\n return 'json';\n }\n\n /**\n * Recursively sort object keys\n */\n private sortObjectKeys(obj: any): any {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(item => this.sortObjectKeys(item));\n }\n\n const sorted: Record<string, any> = {};\n const keys = Object.keys(obj).sort();\n\n for (const key of keys) {\n sorted[key] = this.sortObjectKeys(obj[key]);\n }\n\n return sorted;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * YAML Metadata Serializer\n * \n * Handles YAML format serialization and deserialization\n */\n\nimport * as yaml from 'js-yaml';\nimport type { z } from 'zod';\nimport type { MetadataFormat } from '@objectstack/spec/system';\nimport type { MetadataSerializer, SerializeOptions } from './serializer-interface.js';\n\nexport class YAMLSerializer implements MetadataSerializer {\n serialize<T>(item: T, options?: SerializeOptions): string {\n const { indent = 2, sortKeys = false } = options || {};\n\n return yaml.dump(item, {\n indent,\n sortKeys,\n lineWidth: -1, // Disable line wrapping\n noRefs: true, // Disable YAML references\n });\n }\n\n deserialize<T>(content: string, schema?: z.ZodSchema): T {\n // Use JSON_SCHEMA to prevent arbitrary code execution\n // This restricts YAML to JSON-compatible types only\n const parsed = yaml.load(content, { schema: yaml.JSON_SCHEMA });\n\n if (schema) {\n return schema.parse(parsed) as T;\n }\n\n return parsed as T;\n }\n\n getExtension(): string {\n return '.yaml';\n }\n\n canHandle(format: MetadataFormat): boolean {\n return format === 'yaml';\n }\n\n getFormat(): MetadataFormat {\n return 'yaml';\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * TypeScript/JavaScript Metadata Serializer\n * \n * Handles TypeScript/JavaScript module format serialization and deserialization\n */\n\nimport type { z } from 'zod';\nimport type { MetadataFormat } from '@objectstack/spec/system';\nimport type { MetadataSerializer, SerializeOptions } from './serializer-interface.js';\n\nexport class TypeScriptSerializer implements MetadataSerializer {\n constructor(private format: 'typescript' | 'javascript' = 'typescript') {}\n\n serialize<T>(item: T, options?: SerializeOptions): string {\n const { prettify = true, indent = 2 } = options || {};\n\n const jsonStr = JSON.stringify(item, null, prettify ? indent : 0);\n \n if (this.format === 'typescript') {\n return `import type { ServiceObject } from '@objectstack/spec/data';\\n\\n` +\n `export const metadata: ServiceObject = ${jsonStr};\\n\\n` +\n `export default metadata;\\n`;\n } else {\n return `export const metadata = ${jsonStr};\\n\\n` +\n `export default metadata;\\n`;\n }\n }\n\n deserialize<T>(content: string, schema?: z.ZodSchema): T {\n // For TypeScript/JavaScript files, we need to extract the exported object\n // Note: This is a simplified parser that works with JSON-like object literals\n // For complex TypeScript with nested objects, consider using a proper TypeScript parser\n \n // Try to find the object literal in various export patterns\n // Pattern 1: export const metadata = {...};\n let objectStart = content.indexOf('export const');\n if (objectStart === -1) {\n // Pattern 2: export default {...};\n objectStart = content.indexOf('export default');\n }\n \n if (objectStart === -1) {\n throw new Error(\n 'Could not parse TypeScript/JavaScript module. ' +\n 'Expected export pattern: \"export const metadata = {...};\" or \"export default {...};\"'\n );\n }\n\n // Find the first opening brace after the export statement\n const braceStart = content.indexOf('{', objectStart);\n if (braceStart === -1) {\n throw new Error('Could not find object literal in export statement');\n }\n\n // Find the matching closing brace by counting braces\n // Handle string literals to avoid counting braces inside strings\n let braceCount = 0;\n let braceEnd = -1;\n let inString = false;\n let stringChar = '';\n \n for (let i = braceStart; i < content.length; i++) {\n const char = content[i];\n const prevChar = i > 0 ? content[i - 1] : '';\n \n // Track string literals (simple handling of \" and ')\n if ((char === '\"' || char === \"'\") && prevChar !== '\\\\') {\n if (!inString) {\n inString = true;\n stringChar = char;\n } else if (char === stringChar) {\n inString = false;\n stringChar = '';\n }\n }\n \n // Count braces only when not inside strings\n if (!inString) {\n if (char === '{') braceCount++;\n if (char === '}') {\n braceCount--;\n if (braceCount === 0) {\n braceEnd = i;\n break;\n }\n }\n }\n }\n\n if (braceEnd === -1) {\n throw new Error('Could not find matching closing brace for object literal');\n }\n\n // Extract the object literal\n const objectLiteral = content.substring(braceStart, braceEnd + 1);\n\n try {\n // Parse as JSON\n const parsed = JSON.parse(objectLiteral);\n\n if (schema) {\n return schema.parse(parsed) as T;\n }\n\n return parsed as T;\n } catch (error) {\n throw new Error(\n `Failed to parse object literal as JSON: ${error instanceof Error ? error.message : String(error)}. ` +\n 'Make sure the TypeScript/JavaScript object uses JSON-compatible syntax (no functions, comments, or trailing commas).'\n );\n }\n }\n\n getExtension(): string {\n return this.format === 'typescript' ? '.ts' : '.js';\n }\n\n canHandle(format: MetadataFormat): boolean {\n return format === 'typescript' || format === 'javascript';\n }\n\n getFormat(): MetadataFormat {\n return this.format;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Database Metadata Loader\n *\n * Loads and persists metadata via an IDataDriver instance, enabling\n * database-backed storage for platform and user scoped metadata.\n * Uses the `sys_metadata` table (configurable) following the\n * MetadataRecordSchema envelope defined in @objectstack/spec.\n */\n\nimport type {\n MetadataLoadOptions,\n MetadataLoadResult,\n MetadataStats,\n MetadataLoaderContract,\n MetadataSaveOptions,\n MetadataSaveResult,\n MetadataRecord,\n MetadataHistoryRecord,\n} from '@objectstack/spec/system';\nimport { SysMetadataObject, SysMetadataHistoryObject } from '@objectstack/metadata-core';\nimport { applyConversionsToStoredItem } from '@objectstack/spec';\nimport { PLURAL_TO_SINGULAR } from '@objectstack/spec/shared';\nimport type { IDataDriver, IDataEngine, DriverQuery } from '@objectstack/spec/contracts';\nimport type { MetadataLoader } from './loader-interface.js';\nimport { calculateChecksum } from '../utils/metadata-history-utils.js';\nimport { LRUCache } from '../utils/lru-cache.js';\nimport { isMissingTableError, isSchemaAlreadyExistsError } from '../utils/schema-sync-errors.js';\nimport { migrateProjectIdToEnvironmentId } from '../migrations/migrate-project-id-to-environment-id.js';\n\n/**\n * Cache configuration for `DatabaseLoader`.\n *\n * The cache sits in front of `load()`, `loadMany()`, `exists()`, `stat()`,\n * and `list()` so that hot read paths (REST `/meta/*`, ObjectQL plan\n * resolution, runtime overlay merges) do not hit the database on every\n * request. All write paths (`save`, `delete`, `registerRollback`) invalidate\n * the relevant entries.\n *\n * Defaults are conservative: 500 entries, 60s TTL — chosen so that single-\n * tenant Studio usage does not burn memory and so that an external write\n * (out-of-band SQL update) becomes visible within a minute even without\n * realtime invalidation.\n */\nexport interface DatabaseLoaderCacheOptions {\n /** Whether the cache is active. Default: `true`. */\n enabled?: boolean;\n /** Max number of cached `(type, name)` entries. Default: `500`. */\n maxSize?: number;\n /** TTL in milliseconds. Set to `0` to disable expiry. Default: `60_000`. */\n ttl?: number;\n}\n\n/**\n * Configuration for the DatabaseLoader.\n *\n * Accepts either a raw `IDataDriver` or an `IDataEngine` (ObjectQL).\n * When `engine` is provided, all CRUD operations route through the engine\n * which handles datasource mapping automatically — no manual driver\n * resolution needed. Schema sync is also skipped (the engine handles it).\n */\nexport interface DatabaseLoaderOptions {\n /** The IDataDriver instance to use for database operations */\n driver?: IDataDriver;\n\n /** The IDataEngine (ObjectQL) instance — preferred over raw driver */\n engine?: IDataEngine;\n\n /** The table name to store metadata records (default: 'sys_metadata') */\n tableName?: string;\n\n /** The table name to store history records (default: 'sys_metadata_history') */\n historyTableName?: string;\n\n /** Organization ID for multi-tenant isolation */\n organizationId?: string;\n\n /**\n * @deprecated since ADR-0008 §0 amendment (branch/project removal).\n * The metadata layer is keyed by organization only. This option is\n * accepted for back-compat but ignored — writes do not set\n * `environment_id` and filters do not constrain on it. Will be removed\n * in the next major release.\n */\n environmentId?: string;\n\n /** Enable history tracking (default: true) */\n trackHistory?: boolean;\n\n /**\n * Read-through cache configuration. Pass `{ enabled: false }` to disable\n * caching outright (useful in tests or when the caller wants the loader to\n * always read fresh from the database).\n */\n cache?: DatabaseLoaderCacheOptions;\n}\n\n/**\n * DatabaseLoader — Datasource-backed metadata persistence.\n *\n * Implements the MetadataLoader interface to provide database read/write\n * for metadata records. Uses the MetadataRecordSchema envelope to persist\n * metadata with scope, versioning, and audit fields.\n */\nexport class DatabaseLoader implements MetadataLoader {\n readonly contract: MetadataLoaderContract = {\n name: 'database',\n protocol: 'datasource:',\n capabilities: {\n read: true,\n write: true,\n watch: false,\n list: true,\n },\n };\n\n private driver?: IDataDriver;\n private engine?: IDataEngine;\n private tableName: string;\n private historyTableName: string;\n private organizationId?: string;\n private trackHistory: boolean;\n private schemaReady = false;\n private historySchemaReady = false;\n /**\n * Whether the loud \"DDL failed\" report has already been printed for the\n * metadata table / history table respectively. AGENTS.md → \"Degradation log\n * levels\": say it **once**, at the first degradation, not once per retry.\n */\n private schemaFailureReported = false;\n private historySchemaFailureReported = false;\n /**\n * Same once-only discipline for the #4825 seam: the history table is readable\n * or it is not, and repeating the report per skipped write turns a real\n * degradation into noise people learn to skim.\n */\n private historySeqFailureReported = false;\n\n /** (type, name) → metadata payload — primes `load()` */\n private readonly loadCache?: LRUCache<string, Record<string, unknown> | null>;\n /** type → array of payloads — primes `loadMany()` */\n private readonly loadManyCache?: LRUCache<string, unknown[]>;\n /** type → list of names — primes `list()` */\n private readonly listCache?: LRUCache<string, string[]>;\n /** (type, name) → MetadataStats — primes `stat()` */\n private readonly statCache?: LRUCache<string, MetadataStats | null>;\n\n constructor(options: DatabaseLoaderOptions) {\n if (!options.driver && !options.engine) {\n throw new Error('DatabaseLoader requires either a driver or engine');\n }\n this.driver = options.driver;\n this.engine = options.engine;\n this.tableName = options.tableName ?? 'sys_metadata';\n this.historyTableName = options.historyTableName ?? 'sys_metadata_history';\n this.organizationId = options.organizationId;\n // ADR-0008 §0: `environmentId` option is accepted for back-compat but ignored.\n void options.environmentId;\n this.trackHistory = options.trackHistory !== false; // Default to true\n\n // Wire cache. Default: enabled with 500 entries / 60s TTL.\n const cacheOpts = options.cache;\n const cacheEnabled = cacheOpts?.enabled !== false;\n if (cacheEnabled) {\n const lruOpts = {\n maxSize: cacheOpts?.maxSize ?? 500,\n ttl: cacheOpts?.ttl ?? 60_000,\n };\n this.loadCache = new LRUCache(lruOpts);\n this.loadManyCache = new LRUCache(lruOpts);\n this.listCache = new LRUCache(lruOpts);\n this.statCache = new LRUCache(lruOpts);\n }\n }\n\n // ==========================================\n // Cache helpers\n // ==========================================\n\n private cacheKey(type: string, name: string): string {\n return `${type}::${name}`;\n }\n\n /**\n * Invalidate all cached entries for a specific (type, name) pair plus\n * the type-level aggregates (`loadMany`, `list`). Called from every write\n * path (`save`, `delete`, `registerRollback`).\n */\n private invalidate(type: string, name: string): void {\n if (!this.loadCache) return;\n const key = this.cacheKey(type, name);\n this.loadCache.delete(key);\n this.statCache?.delete(key);\n this.loadManyCache?.delete(type);\n this.listCache?.delete(type);\n }\n\n /** Drop the entire cache — useful after bulk imports or schema changes. */\n invalidateAll(): void {\n this.loadCache?.clear();\n this.loadManyCache?.clear();\n this.listCache?.clear();\n this.statCache?.clear();\n }\n\n /** Diagnostic: aggregated cache statistics for `metrics` endpoints. */\n getCacheStats(): {\n enabled: boolean;\n load: ReturnType<LRUCache<string, unknown>['stats']> | null;\n loadMany: ReturnType<LRUCache<string, unknown>['stats']> | null;\n list: ReturnType<LRUCache<string, unknown>['stats']> | null;\n stat: ReturnType<LRUCache<string, unknown>['stats']> | null;\n } {\n return {\n enabled: this.loadCache !== undefined,\n load: this.loadCache?.stats() ?? null,\n loadMany: this.loadManyCache?.stats() ?? null,\n list: this.listCache?.stats() ?? null,\n stat: this.statCache?.stats() ?? null,\n };\n }\n\n // ==========================================\n // Internal CRUD helpers (driver vs engine)\n // ==========================================\n\n // NOTE (#6231, closed out by #7178): BOTH branches below now take `query`\n // unchanged and uncast. `DriverQuery` is `Omit<QueryAST, 'object'>`, so the\n // object name travels as argument one only — that was always enough for the\n // driver branch. The ENGINE branch used to carry `as any`, for one reason:\n // `EngineQueryOptionsSchema.search` admitted only the structured\n // `FullTextSearchSchema`, while `QueryAST.search` (hence `DriverQuery`) also\n // admits the bare query string that ADR-0061 D1 calls the canonical Tier-1\n // spelling and that the engine actually serves, so `DriverQuery` was not\n // assignable to `EngineQueryOptionsParsed`. #7178 aligned the two schemas;\n // the casts are now genuinely vestigial and are gone, which restores real\n // `where`/`orderBy`/`fields` checking on the metadata main read path — this\n // schema is not `.strict()`, so an unknown key here is SILENTLY DROPPED\n // (`check:query-options-erasure`'s own rationale) and the erased type was\n // the only thing standing between a typo and that silence.\n //\n // If a future edit makes one of these stop compiling, the honest fix is to\n // reconcile the two schemas again — not to reinstate the cast.\n\n private async _find(table: string, query: DriverQuery): Promise<Record<string, unknown>[]> {\n if (this.engine) {\n return this.engine.find(table, query);\n }\n return this.driver!.find(table, query);\n }\n\n private async _findOne(table: string, query: DriverQuery): Promise<Record<string, unknown> | null> {\n if (this.engine) {\n return this.engine.findOne(table, query);\n }\n return this.driver!.findOne(table, query);\n }\n\n private async _count(table: string, query: DriverQuery): Promise<number> {\n if (this.engine) {\n return this.engine.count(table, query);\n }\n return this.driver!.count(table, query);\n }\n\n private async _create(table: string, data: Record<string, unknown>): Promise<Record<string, unknown>> {\n if (this.engine) {\n return this.engine.insert(table, data);\n }\n return this.driver!.create(table, data);\n }\n\n private async _update(table: string, id: string, data: Record<string, unknown>): Promise<Record<string, unknown>> {\n if (this.engine) {\n return this.engine.update(table, { id, ...data });\n }\n return this.driver!.update(table, id, data);\n }\n\n private async _delete(table: string, id: string): Promise<any> {\n if (this.engine) {\n return this.engine.delete(table, { where: { id } } as any);\n }\n return this.driver!.delete(table, id);\n }\n\n /**\n * Compute the next per-org `event_seq` for `sys_metadata_history`.\n * Reads `MAX(event_seq) + 1` for the configured `organization_id`.\n * Legacy path — not transactional, so concurrent writes can collide.\n * The canonical (transactional) producer is `SysMetadataRepository`.\n *\n * #4825 (same shape as #4728, rule from #4632) — discriminate by error TYPE.\n * This used to `catch { return 1 }`, with a comment that named BOTH reasons a\n * read can fail and then answered both the same way. Exactly one of them is\n * benign: the history table has not been provisioned, so there is no row to\n * be inconsistent with and 1 genuinely IS the next number. Every other reason\n * — connection drop, timeout, insufficient privileges — means the rows are\n * still there and simply were not seen, and answering 1 against a table with\n * N rows **collides with existing rows**: the insert succeeds, the log stays\n * empty, and `event_seq` (the ordering key that history listing and rollback\n * targeting both stand on) is silently wrong from then on. Note this is the\n * costlier half of the #4728 family — not bytes that never landed, but bytes\n * that landed *wrong*, which no retry and no restart repairs.\n *\n * @throws The underlying driver error, unchanged, for every non-benign read\n * failure. Deliberate: a sequence number this method cannot derive\n * from data it actually read is not a number it may invent. The\n * caller ({@link createHistoryRecord}) owns the consequence.\n */\n private async nextEventSeq(): Promise<number> {\n const where: Record<string, unknown> = this.organizationId\n ? { organization_id: this.organizationId }\n : {};\n try {\n const rows = await this._find(this.historyTableName, { where });\n let max = 0;\n for (const row of rows as Array<{ event_seq?: number | null }>) {\n const v = typeof row.event_seq === 'number' ? row.event_seq : 0;\n if (v > max) max = v;\n }\n return max + 1;\n } catch (error) {\n // Benign — and ONLY benign: there is no table, therefore no row, so\n // numbering from 1 cannot collide with anything.\n if (isMissingTableError(error)) return 1;\n throw error;\n }\n }\n\n /**\n * Ensure the metadata table exists.\n * Uses IDataDriver.syncSchema with the SysMetadataObject definition\n * to idempotently create/update the table.\n */\n private async ensureSchema(): Promise<void> {\n if (this.schemaReady) return;\n\n // When using engine, schema sync is handled by ObjectQL startup\n if (this.engine) {\n this.schemaReady = true;\n // ⚠️ This loader does NOT build `idx_sys_metadata_overlay_active` (#6771).\n // It used to, with the pre-ADR-0048 key, and because every producer uses\n // `IF NOT EXISTS` the first one to run claimed the name for good. On this\n // engine path nothing has synced `sys_metadata` yet, so that producer was\n // the one most likely to win — and it installed a key the platform\n // retired. Overlay uniqueness has exactly two owners now, both correctly\n // keyed: `metadata-protocol`'s `ensureMetadataOverlayIndexes` (the\n // partial, NULL-safe form) and, for stacks without it, the declaration in\n // `metadata-core`'s `sys-metadata.object.ts` that ObjectQL's own startup\n // materializes through `syncDeclaredIndexes`.\n try {\n const engineAny = this.engine as any;\n let driver: IDataDriver | undefined =\n engineAny?.driver ?? engineAny?.getDriver?.();\n if (!driver && engineAny?.drivers instanceof Map) {\n for (const candidate of engineAny.drivers.values()) {\n const c = candidate as any;\n if (c && (typeof c.raw === 'function' || typeof c.execute === 'function')) {\n driver = candidate as IDataDriver;\n break;\n }\n }\n }\n if (driver) {\n // v5.0 forward migration: project_id → environment_id (idempotent).\n await migrateProjectIdToEnvironmentId(driver).catch(() => undefined);\n }\n } catch (error) {\n // ADR-0120 D4: never block the boot, never swallow it either (#6771 —\n // this catch was empty). Resolving a raw-SQL driver off the engine is\n // the only thing left that can throw here, and when it does the\n // `project_id` → `environment_id` forward migration did NOT run: rows\n // written before v5.0 keep the old column and read back as if the\n // field were unset.\n console.warn(\n `[Metadata] Could not resolve a raw-SQL driver from the engine for \\`${this.tableName}\\` — ` +\n `the project_id→environment_id forward migration was SKIPPED. Legacy rows (if any) keep the ` +\n `pre-v5.0 column and read back as unset. Metadata reads and writes are otherwise unaffected. ` +\n `Re-run it explicitly with \\`migrateProjectIdToEnvironmentId(driver)\\` from ` +\n `\\`@objectstack/metadata/migrations\\` once the datasource is reachable.`,\n error,\n );\n }\n return;\n }\n\n try {\n await this.driver!.syncSchema(this.tableName, {\n ...SysMetadataObject,\n name: this.tableName,\n });\n } catch (error) {\n // #4728 (rule: #4632, accident: #4420) — discriminate by error TYPE.\n // Exactly ONE failure reason is benign here: the table/columns are\n // already provisioned and a non-fully-idempotent driver reports that as\n // an error. Every other reason (insufficient privileges, datasource never\n // connected, incompatible column type) means the table or column does NOT\n // exist — and the previous code marked `schemaReady = true` for all of\n // them, making a total durability failure indistinguishable from success\n // with not one line in the log.\n if (!isSchemaAlreadyExistsError(error)) {\n if (!this.schemaFailureReported) {\n this.schemaFailureReported = true;\n console.error(\n `[Metadata] DDL for the metadata table \\`${this.tableName}\\` FAILED — its table/columns were NOT created or altered. ` +\n `Every metadata write from here on (Studio saves, app installs, org overlays) targets storage that may not exist: ` +\n `writes will error out, or silently drop columns on a lenient driver, while the server keeps reporting healthy. ` +\n `This is NOT the benign \"already exists\" case — check the datasource/driver error below (insufficient privileges, ` +\n `datasource not connected, incompatible column type), fix it and restart. Schema sync is retried on the next ` +\n `metadata operation, so a transient cause recovers on its own.`,\n error,\n );\n }\n // Deliberate, and the opposite of what this code did before: on a REAL\n // DDL failure `schemaReady` stays FALSE. Startup is still not blocked\n // (this method does not throw — callers proceed and fail loudly at the\n // driver if the table is truly missing), but the loader never claims a\n // readiness it does not have, and the next operation retries the sync\n // so a datasource that was merely still connecting heals itself. Same\n // shape as `ensureHistorySchema()` below.\n return;\n }\n // Benign — and ONLY benign: the table is already provisioned, so the DDL\n // was a no-op rather than a failure. Fall through to the ready path.\n }\n\n if (this.schemaFailureReported) {\n this.schemaFailureReported = false;\n console.info(\n `[Metadata] DDL for the metadata table \\`${this.tableName}\\` succeeded on retry — metadata writes are durable again.`,\n );\n }\n this.schemaReady = true;\n // v5.0 forward migration: project_id → environment_id (idempotent).\n try {\n await migrateProjectIdToEnvironmentId(this.driver!);\n } catch {\n // ignore — migration is best-effort on bootstrap\n }\n // ⚠️ No overlay-index DDL is issued from here (#6771). `syncSchema` above\n // already materialized the DECLARED `idx_sys_metadata_overlay_active` from\n // `sys-metadata.object.ts` with the CURRENT ADR-0048 discriminator\n // `(type, name, organization_id, package_id)`; measured on real SQLite, the\n // producer that used to sit here found the name taken and no-opped —\n // while still reporting `status: 'created'`. Its one non-no-op window was\n // the benign \"already exists\" path above, where `syncSchema` threw before\n // creating the declared indexes: there it installed the RETIRED key\n // `(…, environment_id, scope)`, and `syncDeclaredIndexes` skips by name, so\n // nothing ever repaired it. See the tombstone in `../migrations/index.ts`.\n }\n\n /**\n * Ensure the history table exists.\n * Uses IDataDriver.syncSchema with the SysMetadataHistoryObject definition.\n */\n private async ensureHistorySchema(): Promise<void> {\n if (!this.trackHistory || this.historySchemaReady) return;\n\n // When using engine, schema sync is handled by ObjectQL startup\n if (this.engine) {\n this.historySchemaReady = true;\n return;\n }\n\n try {\n await this.driver!.syncSchema(this.historyTableName, {\n ...SysMetadataHistoryObject,\n name: this.historyTableName,\n });\n if (this.historySchemaFailureReported) {\n this.historySchemaFailureReported = false;\n console.info(\n `[Metadata] DDL for the metadata history table \\`${this.historyTableName}\\` succeeded on retry — change history is being recorded again.`,\n );\n }\n this.historySchemaReady = true;\n } catch (error) {\n // Same discrimination as `ensureSchema()` above (#4728). A benign\n // \"already exists\" means the history table IS provisioned — treat it as\n // the no-op it is instead of re-reporting it (and re-running the DDL) on\n // every single write, which is the mirror-image failure: an `error` line\n // for a non-degradation trains everyone to skim `error`.\n if (isSchemaAlreadyExistsError(error)) {\n this.historySchemaReady = true;\n return;\n }\n // Real failure: loud once, `historySchemaReady` stays false so the next\n // operation retries.\n if (!this.historySchemaFailureReported) {\n this.historySchemaFailureReported = true;\n console.error(\n `[Metadata] DDL for the metadata history table \\`${this.historyTableName}\\` FAILED — its table/columns were NOT created. ` +\n `Metadata change history (versions, diffs, rollback) will NOT be persisted while every metadata write keeps succeeding, ` +\n `so the audit trail silently ends here. Fix the datasource/driver error below and restart; the sync is retried on the ` +\n `next metadata operation.`,\n error,\n );\n }\n }\n }\n\n /**\n * Build base filter conditions for queries.\n * Filters by organizationId when configured. `environmentId` is accepted\n * for back-compat but no longer constrains the query — see\n * ADR-0008 §0 (branch/project removal).\n */\n private baseFilter(type: string, name?: string): Record<string, unknown> {\n const filter: Record<string, unknown> = { type };\n if (name !== undefined) {\n filter.name = name;\n }\n if (this.organizationId) {\n filter.organization_id = this.organizationId;\n }\n return filter;\n }\n\n /**\n * Create a history record for a metadata change.\n *\n * @param type - Metadata type\n * @param name - Metadata name\n * @param version - Version number\n * @param metadata - The metadata payload\n * @param operationType - Type of operation\n * @param previousChecksum - Checksum of previous version (if any)\n * @param changeNote - Optional change description\n * @param recordedBy - Optional user who made the change\n */\n private async createHistoryRecord(\n type: string,\n name: string,\n version: number,\n metadata: unknown,\n operationType: 'create' | 'update' | 'publish' | 'revert' | 'delete',\n previousChecksum?: string,\n changeNote?: string,\n recordedBy?: string\n ): Promise<void> {\n if (!this.trackHistory) return;\n\n await this.ensureHistorySchema();\n\n const now = new Date().toISOString();\n const checksum = await calculateChecksum(metadata);\n\n // Skip if checksum matches previous version (no actual change)\n if (previousChecksum && checksum === previousChecksum && operationType === 'update') {\n return;\n }\n\n const historyId = generateId();\n const metadataJson = JSON.stringify(metadata);\n\n // Compute per-org monotonic event_seq. Legacy path: not inside a\n // transaction, so concurrent writers can collide. The SysMetadataRepository\n // path serializes this under engine.transaction(); DatabaseLoader is\n // deprecated for new writes and tolerates the race.\n //\n // #4825: the concurrency race above is a KNOWN, recorded limitation of this\n // path. A read failure is not the same thing and is not tolerated — if the\n // sequence cannot be derived from rows we actually read, we write NO history\n // row rather than one carrying a number we made up. A missing row is loud\n // here and visibly absent later; a colliding row is silent now and corrupts\n // the ordering that `queryHistory` and `rollback` both depend on, forever.\n let eventSeq: number;\n try {\n eventSeq = await this.nextEventSeq();\n } catch (error) {\n if (!this.historySeqFailureReported) {\n this.historySeqFailureReported = true;\n console.error(\n `[Metadata] Could not read \\`${this.historyTableName}\\` to determine the next \\`event_seq\\` — the history ` +\n `entry for ${type}/${name} was NOT written, and further entries are being skipped while this persists. ` +\n `The metadata write itself SUCCEEDED, so the server keeps looking healthy while its change history ` +\n `silently develops holes: version timelines and rollback targets will be incomplete. The entry is skipped ` +\n `deliberately — numbering it from 1 (what this code did before #4825) would collide with existing rows and ` +\n `make \\`event_seq\\` ordering wrong rather than merely incomplete, which nothing detects and no restart ` +\n `repairs. Fix the datasource/driver error below (connection, timeout, privileges); the next metadata write ` +\n `retries and reports recovery.`,\n error,\n );\n }\n return;\n }\n\n if (this.historySeqFailureReported) {\n this.historySeqFailureReported = false;\n console.info(\n `[Metadata] \\`${this.historyTableName}\\` is readable again — \\`event_seq\\` numbering recovered and change ` +\n `history is being recorded again. Entries skipped during the outage are not backfilled.`,\n );\n }\n\n const historyRecord: Partial<MetadataHistoryRecord> = {\n id: historyId,\n name,\n type,\n version,\n operationType,\n metadata: metadataJson as any,\n checksum,\n previousChecksum,\n changeNote,\n recordedBy,\n recordedAt: now,\n ...(this.organizationId ? { organizationId: this.organizationId } : {}),\n };\n\n try {\n await this._create(this.historyTableName, {\n id: historyRecord.id,\n event_seq: eventSeq,\n name: historyRecord.name,\n type: historyRecord.type,\n version: historyRecord.version,\n operation_type: historyRecord.operationType,\n metadata: historyRecord.metadata,\n checksum: historyRecord.checksum,\n previous_checksum: historyRecord.previousChecksum,\n change_note: historyRecord.changeNote,\n recorded_by: historyRecord.recordedBy,\n recorded_at: historyRecord.recordedAt,\n source: 'database-loader',\n ...(this.organizationId ? { organization_id: this.organizationId } : {}),\n });\n } catch (error) {\n // Log error but don't fail the main operation\n console.error(`Failed to create history record for ${type}/${name}:`, error);\n }\n }\n\n /**\n * Once-per-process dedupe for stored-row conversion notices — `load` /\n * `loadMany` are hot read paths (cached, but re-hit on every TTL expiry),\n * so a legacy row must warn once, not once per cache miss.\n */\n private storedConversionWarned = new Set<string>();\n\n /**\n * Convert a LIVE database row to a metadata payload.\n *\n * Parses the JSON `metadata` column back into an object, then replays the\n * full ADR-0087 conversion chain over it (#3903): rows written under a past\n * protocol are served canonical, exactly like the metadata-protocol's\n * `sys_metadata` seams. History rows do NOT pass through here — history\n * readers parse inline and stay verbatim, as a record of what was written.\n *\n * `flow` is skipped for the same reason the protocol skips it: flow-node\n * conversions need the automation engine's live executor registry for their\n * open-namespace conflict guard; flows canonicalize at `registerFlow`.\n */\n private rowToData(row: Record<string, unknown>): Record<string, unknown> | null {\n if (!row || !row.metadata) return null;\n\n const payload = typeof row.metadata === 'string'\n ? JSON.parse(row.metadata as string)\n : row.metadata;\n\n const singular = PLURAL_TO_SINGULAR[row.type as string] ?? (row.type as string);\n if (singular === 'flow') return payload as Record<string, unknown>;\n return applyConversionsToStoredItem(singular, payload as Record<string, unknown>, {\n onNotice: (n) => {\n const key = `${n.conversionId}|${singular}|${String(row.name ?? '')}`;\n if (this.storedConversionWarned.has(key)) return;\n this.storedConversionWarned.add(key);\n console.warn(\n `[DatabaseLoader] stored ${singular}/${String(row.name ?? '<unnamed>')} carries a pre-protocol shape; ${n.message}`,\n );\n },\n });\n }\n\n /**\n * Convert a database row to a MetadataRecord-like object.\n */\n private rowToRecord(row: Record<string, unknown>): MetadataRecord {\n return {\n id: row.id as string,\n name: row.name as string,\n type: row.type as string,\n namespace: (row.namespace as string) ?? 'default',\n packageId: row.package_id as string | undefined,\n managedBy: row.managed_by as MetadataRecord['managedBy'],\n scope: (row.scope as MetadataRecord['scope']) ?? 'platform',\n metadata: this.rowToData(row) ?? {},\n extends: row.extends as string | undefined,\n strategy: (row.strategy as MetadataRecord['strategy']) ?? 'merge',\n owner: row.owner as string | undefined,\n state: (row.state as MetadataRecord['state']) ?? 'active',\n organizationId: row.organization_id as string | undefined,\n environmentId: row.environment_id as string | undefined,\n version: (row.version as number) ?? 1,\n checksum: row.checksum as string | undefined,\n source: row.source as MetadataRecord['source'],\n tags: row.tags ? (typeof row.tags === 'string' ? JSON.parse(row.tags as string) : row.tags as string[]) : undefined,\n createdBy: row.created_by as string | undefined,\n createdAt: row.created_at as string | undefined,\n updatedBy: row.updated_by as string | undefined,\n updatedAt: row.updated_at as string | undefined,\n };\n }\n\n // ==========================================\n // Read-failure classification (#5108)\n // ==========================================\n\n /**\n * Decide what a failed READ against {@link tableName} means, and rethrow\n * unless it is the ONE benign reason.\n *\n * #5108 (rule from #4632; same shape as #4728 and #4825) — discriminate by\n * error TYPE. Every read method below used to `catch {}` into its own empty\n * value: `load` → `null`, `loadMany` → `[]`, `exists` → `false`, `stat` →\n * `null`, `list` → `[]`. That made a database the metadata plane cannot\n * reach **indistinguishable** from an environment where nothing of that type\n * was ever declared — and it erased the failure *inside the loader*, so\n * neither `MetadataManager`'s own `try/catch` degradation branches nor\n * {@link import('../metadata-manager.js').MetadataManager.loadDiagnosed}\n * (ADR-0110 D3, whose whole purpose is to tell a miss from an outage) could\n * report anything. Nowhere on the chain was there a line saying the read\n * failed.\n *\n * Why that is worse than a noisy error: every consumer that gates on a\n * *declared set* — permissions, sharing rules, policies, endpoint\n * declarations — reads the empty answer as \"the author declared none\". Some\n * then fail open (grant), some fail closed (lock out); both look healthy\n * from outside. This is the AGENTS.md → \"Degradation log levels\" shape the\n * repo has already paid for twice, one layer up from #4825.\n *\n * Exactly one failure reason is benign: `sys_metadata` has not been\n * provisioned yet. There are then genuinely no rows, so \"nothing declared\"\n * IS the truth, and a first boot must not explode. Every other reason —\n * connection drop, timeout, insufficient privileges, malformed query — means\n * the rows may well be there and simply were not seen.\n *\n * Classification is conservative in the same direction as\n * {@link isMissingTableError} itself: an unrecognised error is NOT benign.\n * A false \"benign\" silently mis-answers a security question; a false \"real\"\n * costs one loud error.\n *\n * @param error The value thrown by `_find` / `_findOne` / `_count`.\n * @throws The underlying driver error, unchanged — deliberately, matching\n * {@link nextEventSeq}. The loader does not log it: the caller owns\n * the consequence and is the only layer that knows what an\n * incomplete answer costs it (`MetadataManager.list()` reports it at\n * `error`; `listForIndex()`/`matchEndpoint` let it propagate so an\n * outage can never be served as a 404).\n * @returns normally ONLY for the benign case, licensing the caller to answer\n * with its empty value.\n */\n private rethrowUnlessTableUnprovisioned(error: unknown): void {\n if (isMissingTableError(error)) return;\n throw error;\n }\n\n // ==========================================\n // MetadataLoader Interface Implementation\n // ==========================================\n\n async load(\n type: string,\n name: string,\n _options?: MetadataLoadOptions\n ): Promise<MetadataLoadResult> {\n const startTime = Date.now();\n\n await this.ensureSchema();\n\n // Read-through cache. We cache `null` (not-found) results too so a barrage\n // of misses does not hammer the database; invalidation on `save` upgrades\n // the entry once the row exists.\n const key = this.cacheKey(type, name);\n if (this.loadCache) {\n const cached = this.loadCache.get(key);\n if (cached !== undefined) {\n return {\n data: cached,\n source: 'database',\n format: 'json',\n loadTime: Date.now() - startTime,\n };\n }\n }\n\n try {\n const row = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (!row) {\n this.loadCache?.set(key, null);\n return {\n data: null,\n loadTime: Date.now() - startTime,\n };\n }\n\n const data = this.rowToData(row);\n const record = this.rowToRecord(row);\n\n this.loadCache?.set(key, data);\n\n return {\n data,\n source: 'database',\n format: 'json',\n etag: record.checksum,\n loadTime: Date.now() - startTime,\n };\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: the table is not provisioned, so there is no row. Not\n // cached — `ensureSchema()` retries, and a `null` memoized here would\n // outlive the provisioning that fixes it.\n return {\n data: null,\n loadTime: Date.now() - startTime,\n };\n }\n }\n\n async loadMany<T = any>(\n type: string,\n _options?: MetadataLoadOptions\n ): Promise<T[]> {\n await this.ensureSchema();\n\n if (this.loadManyCache) {\n const cached = this.loadManyCache.get(type);\n if (cached !== undefined) return cached as T[];\n }\n\n try {\n const rows = await this._find(this.tableName, {\n where: this.baseFilter(type),\n });\n\n const result = rows\n .map(row => this.rowToData(row))\n .filter((data): data is Record<string, unknown> => data !== null) as T[];\n\n this.loadManyCache?.set(type, result);\n return result;\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: no table, therefore no items of this type. Not cached.\n return [];\n }\n }\n\n async exists(type: string, name: string): Promise<boolean> {\n await this.ensureSchema();\n\n // Honor cache: a cached non-null payload implies existence.\n if (this.loadCache) {\n const cached = this.loadCache.get(this.cacheKey(type, name));\n if (cached !== undefined) return cached !== null;\n }\n\n try {\n const count = await this._count(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n return count > 0;\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: no table, therefore the item genuinely does not exist.\n return false;\n }\n }\n\n async stat(type: string, name: string): Promise<MetadataStats | null> {\n await this.ensureSchema();\n\n const key = this.cacheKey(type, name);\n if (this.statCache) {\n const cached = this.statCache.get(key);\n if (cached !== undefined) return cached;\n }\n\n try {\n const row = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (!row) {\n this.statCache?.set(key, null);\n return null;\n }\n\n const record = this.rowToRecord(row);\n const metadataStr = typeof row.metadata === 'string'\n ? row.metadata as string\n : JSON.stringify(row.metadata);\n\n const stats: MetadataStats = {\n size: metadataStr.length,\n mtime: record.updatedAt ?? record.createdAt ?? new Date().toISOString(),\n format: 'json',\n etag: record.checksum,\n };\n this.statCache?.set(key, stats);\n return stats;\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: no table, therefore nothing to stat. Not cached.\n return null;\n }\n }\n\n async list(type: string): Promise<string[]> {\n await this.ensureSchema();\n\n if (this.listCache) {\n const cached = this.listCache.get(type);\n if (cached !== undefined) return cached;\n }\n\n try {\n const rows = await this._find(this.tableName, {\n where: this.baseFilter(type),\n fields: ['name'],\n });\n\n const names = rows\n .map(row => row.name as string)\n .filter(name => typeof name === 'string');\n\n this.listCache?.set(type, names);\n return names;\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: no table, therefore no names. Not cached.\n return [];\n }\n }\n\n /**\n * Fetch a single history snapshot by (type, name, version).\n * Returns null when the record does not exist.\n */\n async getHistoryRecord(\n type: string,\n name: string,\n version: number\n ): Promise<MetadataHistoryRecord | null> {\n if (!this.trackHistory) return null;\n\n await this.ensureHistorySchema();\n\n const filter: Record<string, unknown> = {\n type,\n name,\n version,\n };\n if (this.organizationId) {\n filter.organization_id = this.organizationId;\n }\n\n const row = await this._findOne(this.historyTableName, {\n where: filter,\n });\n if (!row) return null;\n\n return {\n id: row.id as string,\n name: row.name as string,\n type: row.type as string,\n version: row.version as number,\n operationType: row.operation_type as MetadataHistoryRecord['operationType'],\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata as string) : row.metadata,\n checksum: row.checksum as string,\n previousChecksum: row.previous_checksum as string | undefined,\n changeNote: row.change_note as string | undefined,\n organizationId: row.organization_id as string | undefined,\n recordedBy: row.recorded_by as string | undefined,\n recordedAt: row.recorded_at as string,\n };\n }\n\n /**\n * Query history records with pagination and filtering.\n * Encapsulates history table queries so MetadataManager doesn't need\n * direct driver access.\n */\n async queryHistory(\n type: string,\n name: string,\n options?: {\n operationType?: string;\n since?: string;\n until?: string;\n limit?: number;\n offset?: number;\n includeMetadata?: boolean;\n }\n ): Promise<{ records: any[]; total: number; hasMore: boolean }> {\n if (!this.trackHistory) {\n return { records: [], total: 0, hasMore: false };\n }\n\n await this.ensureSchema();\n await this.ensureHistorySchema();\n\n // Build history query directly against (type, name); no parent\n // lookup needed since the history table is keyed by these fields.\n const historyFilter: Record<string, unknown> = {\n type,\n name,\n };\n if (this.organizationId) historyFilter.organization_id = this.organizationId;\n if (options?.operationType) historyFilter.operation_type = options.operationType;\n if (options?.since) historyFilter.recorded_at = { $gte: options.since };\n if (options?.until) {\n if (historyFilter.recorded_at) {\n (historyFilter.recorded_at as Record<string, unknown>).$lte = options.until;\n } else {\n historyFilter.recorded_at = { $lte: options.until };\n }\n }\n\n const limit = options?.limit ?? 50;\n const offset = options?.offset ?? 0;\n\n const historyRecords = await this._find(this.historyTableName, {\n where: historyFilter,\n orderBy: [\n { field: 'recorded_at', order: 'desc' as const },\n { field: 'version', order: 'desc' as const },\n ],\n limit: limit + 1,\n offset,\n });\n\n const hasMore = historyRecords.length > limit;\n const records = historyRecords.slice(0, limit);\n const total = await this._count(this.historyTableName, { where: historyFilter });\n\n const includeMetadata = options?.includeMetadata !== false;\n const result = records.map((row: Record<string, unknown>) => {\n const parsedMetadata =\n typeof row.metadata === 'string'\n ? JSON.parse(row.metadata as string)\n : (row.metadata as Record<string, unknown> | null | undefined);\n\n return {\n id: row.id as string,\n name: row.name as string,\n type: row.type as string,\n version: row.version as number,\n operationType: row.operation_type as string,\n metadata: includeMetadata ? parsedMetadata : null,\n checksum: row.checksum as string,\n previousChecksum: row.previous_checksum as string | undefined,\n changeNote: row.change_note as string | undefined,\n organizationId: row.organization_id as string | undefined,\n recordedBy: row.recorded_by as string | undefined,\n recordedAt: row.recorded_at as string,\n };\n });\n\n return { records: result, total, hasMore };\n }\n\n /**\n * Perform a rollback: persist `restoredData` as the new current state and record a\n * single 'revert' history entry (instead of the usual 'update' entry that `save()`\n * would produce). This avoids the duplicate-version problem that arises when\n * `register()` → `save()` writes an 'update' entry followed by an additional\n * 'revert' entry for the same version number.\n */\n async registerRollback(\n type: string,\n name: string,\n restoredData: unknown,\n targetVersion: number,\n changeNote?: string,\n recordedBy?: string\n ): Promise<void> {\n await this.ensureSchema();\n\n const now = new Date().toISOString();\n const metadataJson = JSON.stringify(restoredData);\n const newChecksum = await calculateChecksum(restoredData);\n\n const existing = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (!existing) {\n throw new Error(`Metadata ${type}/${name} not found for rollback`);\n }\n\n const previousChecksum = existing.checksum as string | undefined;\n const newVersion = ((existing.version as number) ?? 0) + 1;\n\n await this._update(this.tableName, existing.id as string, {\n metadata: metadataJson,\n version: newVersion,\n checksum: newChecksum,\n updated_at: now,\n state: 'active',\n });\n\n this.invalidate(type, name);\n\n // Write exactly one 'revert' history entry (not an 'update' entry)\n await this.createHistoryRecord(\n type,\n name,\n newVersion,\n restoredData,\n 'revert',\n previousChecksum,\n changeNote ?? `Rolled back to version ${targetVersion}`,\n recordedBy\n );\n }\n\n async save(\n type: string,\n name: string,\n data: any,\n _options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n const startTime = Date.now();\n\n await this.ensureSchema();\n\n const now = new Date().toISOString();\n const metadataJson = JSON.stringify(data);\n const newChecksum = await calculateChecksum(data);\n\n try {\n const existing = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (existing) {\n // Skip update if the content is identical (prevents phantom version bumps)\n const previousChecksum = existing.checksum as string | undefined;\n if (newChecksum === previousChecksum) {\n // No DB write, but make sure the cached payload reflects the latest\n // call (prior cached `null` would otherwise mask a freshly-saved\n // record).\n this.loadCache?.set(this.cacheKey(type, name), data as Record<string, unknown>);\n return {\n success: true,\n path: `datasource://${this.tableName}/${type}/${name}`,\n size: metadataJson.length,\n saveTime: Date.now() - startTime,\n };\n }\n\n // Update existing record\n const version = ((existing.version as number) ?? 0) + 1;\n\n await this._update(this.tableName, existing.id as string, {\n metadata: metadataJson,\n version,\n checksum: newChecksum,\n updated_at: now,\n state: 'active',\n });\n\n this.invalidate(type, name);\n\n // Create history record for update\n await this.createHistoryRecord(\n type,\n name,\n version,\n data,\n 'update',\n previousChecksum\n );\n\n return {\n success: true,\n path: `datasource://${this.tableName}/${type}/${name}`,\n size: metadataJson.length,\n saveTime: Date.now() - startTime,\n };\n } else {\n // Create new record\n const id = generateId();\n await this._create(this.tableName, {\n id,\n name,\n type,\n namespace: 'default',\n scope: (data as any)?.scope ?? 'platform',\n metadata: metadataJson,\n checksum: newChecksum,\n strategy: 'merge',\n state: 'active',\n version: 1,\n source: 'database',\n ...(this.organizationId ? { organization_id: this.organizationId } : {}),\n created_at: now,\n updated_at: now,\n });\n\n this.invalidate(type, name);\n\n // Create history record for creation\n await this.createHistoryRecord(\n type,\n name,\n 1,\n data,\n 'create'\n );\n\n return {\n success: true,\n path: `datasource://${this.tableName}/${type}/${name}`,\n size: metadataJson.length,\n saveTime: Date.now() - startTime,\n };\n }\n } catch (error) {\n throw new Error(\n `DatabaseLoader save failed for ${type}/${name}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n /**\n * Delete a metadata item from the database\n */\n async delete(type: string, name: string): Promise<void> {\n await this.ensureSchema();\n\n // Find the existing record to get its ID\n const existing = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (!existing) {\n // Item doesn't exist, nothing to delete\n return;\n }\n\n // Delete from the main metadata table using the record's ID\n await this._delete(this.tableName, existing.id as string);\n\n this.invalidate(type, name);\n }\n}\n\n/**\n * Generate a simple unique ID for metadata records.\n * Uses crypto.randomUUID when available, falls back to timestamp-based ID.\n */\nfunction generateId(): string {\n if (typeof globalThis.crypto !== 'undefined' && typeof globalThis.crypto.randomUUID === 'function') {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID\n return `meta_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata History Utilities\n *\n * Utility functions for metadata versioning and history tracking,\n * including checksum calculation, JSON normalization, and diff generation.\n */\n\n/**\n * Calculate SHA-256 checksum of normalized JSON metadata.\n * Normalizes the JSON by sorting keys and removing whitespace\n * to ensure consistent checksums across identical content.\n *\n * @param metadata - The metadata object to checksum\n * @returns SHA-256 hex string\n */\nexport async function calculateChecksum(metadata: unknown): Promise<string> {\n // Normalize JSON by sorting keys recursively\n const normalized = normalizeJSON(metadata);\n const jsonString = JSON.stringify(normalized);\n\n // Use Web Crypto API (available in Node.js 15+ and all modern browsers)\n if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.subtle) {\n const encoder = new TextEncoder();\n const data = encoder.encode(jsonString);\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n }\n\n // Fallback for environments without Web Crypto API\n // Use a simple hash function (not cryptographically secure, but sufficient for change detection)\n return simpleHash(jsonString);\n}\n\n/**\n * Normalize JSON by recursively sorting object keys.\n * This ensures deterministic serialization for checksum calculation.\n *\n * @param value - The value to normalize\n * @returns Normalized value with sorted keys\n */\nfunction normalizeJSON(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map(normalizeJSON);\n }\n\n if (typeof value === 'object') {\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(value as object).sort();\n for (const key of keys) {\n sorted[key] = normalizeJSON((value as Record<string, unknown>)[key]);\n }\n return sorted;\n }\n\n return value;\n}\n\n/**\n * Simple hash function fallback for environments without Web Crypto API.\n * Based on djb2 hash algorithm.\n *\n * @param str - String to hash\n * @returns Hex hash string\n */\nfunction simpleHash(str: string): string {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) + hash) + str.charCodeAt(i);\n hash = hash & hash; // Convert to 32-bit integer\n }\n // Convert to hex and pad to 64 characters to match SHA-256 length\n const hexHash = Math.abs(hash).toString(16);\n return hexHash.padStart(64, '0');\n}\n\n/**\n * Generate a simple JSON patch between two objects.\n * Returns an array of operations showing what changed.\n *\n * @param oldObj - Original object\n * @param newObj - New object\n * @param path - Current path (for recursion)\n * @returns Array of change operations\n */\nexport function generateSimpleDiff(\n oldObj: unknown,\n newObj: unknown,\n path: string = ''\n): Array<{ op: string; path: string; value?: unknown; oldValue?: unknown }> {\n const changes: Array<{ op: string; path: string; value?: unknown; oldValue?: unknown }> = [];\n\n // Handle primitives\n if (typeof oldObj !== 'object' || oldObj === null || typeof newObj !== 'object' || newObj === null) {\n if (oldObj !== newObj) {\n changes.push({ op: 'replace', path: path || '/', value: newObj, oldValue: oldObj });\n }\n return changes;\n }\n\n // Handle arrays\n if (Array.isArray(oldObj) || Array.isArray(newObj)) {\n if (!Array.isArray(oldObj) || !Array.isArray(newObj) || oldObj.length !== newObj.length) {\n changes.push({ op: 'replace', path: path || '/', value: newObj, oldValue: oldObj });\n } else {\n // Compare array elements\n for (let i = 0; i < oldObj.length; i++) {\n const subPath = `${path}/${i}`;\n changes.push(...generateSimpleDiff(oldObj[i], newObj[i], subPath));\n }\n }\n return changes;\n }\n\n // Handle objects\n const oldKeys = new Set(Object.keys(oldObj as object));\n const newKeys = new Set(Object.keys(newObj as object));\n\n // Check for added keys\n for (const key of newKeys) {\n if (!oldKeys.has(key)) {\n const subPath = path ? `${path}/${key}` : `/${key}`;\n changes.push({ op: 'add', path: subPath, value: (newObj as Record<string, unknown>)[key] });\n }\n }\n\n // Check for removed keys\n for (const key of oldKeys) {\n if (!newKeys.has(key)) {\n const subPath = path ? `${path}/${key}` : `/${key}`;\n changes.push({ op: 'remove', path: subPath, oldValue: (oldObj as Record<string, unknown>)[key] });\n }\n }\n\n // Check for modified keys\n for (const key of oldKeys) {\n if (newKeys.has(key)) {\n const subPath = path ? `${path}/${key}` : `/${key}`;\n changes.push(...generateSimpleDiff(\n (oldObj as Record<string, unknown>)[key],\n (newObj as Record<string, unknown>)[key],\n subPath\n ));\n }\n }\n\n return changes;\n}\n\n/**\n * Generate a human-readable summary of changes.\n *\n * @param diff - The diff operations\n * @returns Human-readable summary\n */\nexport function generateDiffSummary(\n diff: Array<{ op: string; path: string; value?: unknown; oldValue?: unknown }>\n): string {\n if (diff.length === 0) {\n return 'No changes';\n }\n\n const summary: string[] = [];\n const addCount = diff.filter(d => d.op === 'add').length;\n const removeCount = diff.filter(d => d.op === 'remove').length;\n const replaceCount = diff.filter(d => d.op === 'replace').length;\n\n if (addCount > 0) summary.push(`${addCount} field${addCount > 1 ? 's' : ''} added`);\n if (removeCount > 0) summary.push(`${removeCount} field${removeCount > 1 ? 's' : ''} removed`);\n if (replaceCount > 0) summary.push(`${replaceCount} field${replaceCount > 1 ? 's' : ''} modified`);\n\n return summary.join(', ');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Generic LRU (Least Recently Used) cache with optional TTL.\n *\n * Implementation notes:\n * - Backed by a `Map`, which preserves insertion order. We promote entries on\n * read by deleting and re-inserting, so the oldest entry is always\n * `map.keys().next()`.\n * - TTL is checked lazily on `get` / `has`. Expired entries are evicted on\n * access; we do not run a background sweeper to keep the implementation\n * side-effect free in serverless / edge runtimes.\n * - Set `maxSize <= 0` to disable the size cap; set `ttl <= 0` (or omit) to\n * disable expiration.\n *\n * Designed for `DatabaseLoader` read-path caching — see\n * `packages/metadata/src/loaders/database-loader.ts`.\n */\nexport interface LRUCacheOptions {\n /** Maximum number of entries; when exceeded, the LRU entry is evicted. */\n maxSize?: number;\n /** Time-to-live in milliseconds. Zero or undefined disables TTL. */\n ttl?: number;\n}\n\ninterface Entry<V> {\n value: V;\n expiresAt: number; // 0 means \"never\"\n}\n\nexport class LRUCache<K, V> {\n private readonly map = new Map<K, Entry<V>>();\n private readonly maxSize: number;\n private readonly ttl: number;\n private hits = 0;\n private misses = 0;\n\n constructor(options: LRUCacheOptions = {}) {\n this.maxSize = options.maxSize && options.maxSize > 0 ? options.maxSize : 0;\n this.ttl = options.ttl && options.ttl > 0 ? options.ttl : 0;\n }\n\n get(key: K): V | undefined {\n const entry = this.map.get(key);\n if (!entry) {\n this.misses++;\n return undefined;\n }\n if (entry.expiresAt !== 0 && entry.expiresAt <= Date.now()) {\n this.map.delete(key);\n this.misses++;\n return undefined;\n }\n // Promote to most-recently-used.\n this.map.delete(key);\n this.map.set(key, entry);\n this.hits++;\n return entry.value;\n }\n\n set(key: K, value: V): void {\n if (this.map.has(key)) {\n this.map.delete(key);\n } else if (this.maxSize > 0 && this.map.size >= this.maxSize) {\n const oldest = this.map.keys().next();\n if (!oldest.done) this.map.delete(oldest.value);\n }\n this.map.set(key, {\n value,\n expiresAt: this.ttl > 0 ? Date.now() + this.ttl : 0,\n });\n }\n\n has(key: K): boolean {\n return this.get(key) !== undefined;\n }\n\n delete(key: K): boolean {\n return this.map.delete(key);\n }\n\n clear(): void {\n this.map.clear();\n }\n\n get size(): number {\n return this.map.size;\n }\n\n /** Diagnostic counters — useful for `metrics` endpoints. */\n stats(): { size: number; hits: number; misses: number; hitRate: number } {\n const total = this.hits + this.misses;\n return {\n size: this.map.size,\n hits: this.hits,\n misses: this.misses,\n hitRate: total === 0 ? 0 : this.hits / total,\n };\n }\n\n /** Resets hit/miss counters without dropping cached entries. */\n resetStats(): void {\n this.hits = 0;\n this.misses = 0;\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Driver-error classification for the metadata storage seams (#4728, #4825;\n * rule from #4632).\n *\n * Two questions live here, and they share one mechanism on purpose. A second\n * hand-rolled `catch`-and-guess elsewhere in this package would be a second\n * de-facto vocabulary of \"which driver errors are benign\" — the exact debt this\n * module exists to retire. Both predicates below are thin wrappers over one\n * signature matcher, so a driver quirk is taught to the package once.\n *\n * 1. {@link isSchemaAlreadyExistsError} — \"was this DDL failure just the table\n * already being there?\" (#4728, `ensureSchema` / `ensureHistorySchema`).\n * 2. {@link isMissingTableError} — \"did this READ fail because the table has\n * not been provisioned yet?\" (#4825, `nextEventSeq`).\n *\n * They are deliberately **not** each other's negation. Each answers \"is this\n * the one benign reason?\" and defaults to *not benign*, so an error neither\n * recognises is loud under both.\n *\n * ---\n *\n * ## 1. DDL failure classification (#4728)\n *\n * `IDataDriver.syncSchema()` is contractually **idempotent** (\"creates tables if\n * missing, adds columns, updates indexes\"), so in principle a re-sync of an\n * existing table should not throw at all. In practice a driver may surface the\n * already-provisioned case as an error instead of a no-op — `CREATE TABLE`\n * without `IF NOT EXISTS`, an `ALTER TABLE ADD COLUMN` for a column that is\n * already there. That single failure reason is benign: the table and its columns\n * exist, so the bytes will land.\n *\n * **Every other** DDL failure is not benign, and the difference is the whole\n * point of this module. Insufficient privileges, a datasource that never\n * connected, an incompatible column type — after those, the table or column does\n * not exist, yet the process keeps looking healthy while everything it claims to\n * persist has nowhere to land. That is the #4420 shape, and AGENTS.md →\n * \"Degradation log levels\" requires it to be reported at `error`.\n *\n * The defect this replaces was a `catch` whose comment named the benign reason\n * (\"e.g. table already exists\") and used it to excuse **all** of them. Callers\n * must therefore ask the question by error *type*:\n *\n * ```ts\n * catch (error) {\n * if (!isSchemaAlreadyExistsError(error)) {\n * console.error('… consequence … fix …', error); // loud, and stay not-ready\n * return;\n * }\n * // benign only: the table is already provisioned, carry on\n * }\n * ```\n *\n * Classification is deliberately conservative — anything not positively\n * recognised as \"already exists\" is treated as a real failure, because the cost\n * of a false \"benign\" (silent data loss) is far higher than the cost of a false\n * \"real\" (one extra error line).\n *\n * ---\n *\n * ## 2. Missing-table classification for reads (#4825)\n *\n * `DatabaseLoader.nextEventSeq()` reads `sys_metadata_history` to decide what\n * `event_seq` the NEXT history row gets. Its `catch` named both reasons a read\n * can fail — \"table not provisioned yet\" (benign: 1 really is the next number)\n * and \"driver error\" (**not** benign) — and answered both with `return 1`.\n *\n * That is the #4728 shape one layer down, but the damage is the opposite kind\n * and worse. #4728 was *bytes that never landed*; this is **bytes that land\n * wrong**: with N rows already in the table, one flaky read hands the next row\n * `event_seq = 1`, colliding with an existing row. The insert **succeeds**, no\n * line is logged, and `event_seq` — the ordering key that history listing and\n * rollback targeting both stand on — is now silently untrustworthy.\n *\n * So the read seam gets the same treatment, with the same conservative default:\n *\n * ```ts\n * catch (error) {\n * if (isMissingTableError(error)) return 1; // benign: nothing to collide with\n * throw error; // caller reports the consequence\n * }\n * ```\n */\n\n// [#6615] The Postgres `\"x\" of relation \"y\"` phrase, owned once — see the\n// module docblock in `@objectstack/types` for the superstring hole it closes\n// and for why the exclusion's width deliberately differs from the extractor's.\nimport { isRelationSubObjectPhrase } from '@objectstack/types';\n\n/** One \"which errors mean X?\" vocabulary, in the three forms drivers use. */\ninterface DriverErrorSignature {\n /** `error.code` — Postgres SQLSTATE, or mysql2's symbolic name. */\n readonly codes: ReadonlySet<string>;\n /** `error.errno` — MySQL/MariaDB numeric equivalents. */\n readonly errnos: ReadonlySet<number>;\n /** `error.message` — the only signal SQLite-family drivers give. */\n readonly message: RegExp;\n /**\n * Optional **front-exclusion**, evaluated before any positive test (#6347).\n *\n * A message test can never exclude a *superstring*: once a legal phrase for\n * X appears inside a longer phrase that means NOT-X, no amount of widening\n * the X regex removes the match — the phrase really is in there. The only\n * repair is to recognise the not-X shape first and stop. So this is a\n * separate channel rather than another alternation in {@link message}.\n */\n readonly excludes?: {\n /** SQLSTATEs / driver codes that positively mean \"**not** this case\". */\n readonly codes: ReadonlySet<string>;\n /**\n * Message shapes that carry a legal match for this case as a substring.\n *\n * A predicate rather than a `RegExp` since #6615, so this channel can be\n * satisfied by a shared, named question from `@objectstack/types` instead\n * of a pattern this file owns alone. The phrase it tests is the same one\n * `@objectstack/rest` and `@objectstack/service-analytics` read.\n */\n readonly matchesMessage: (message: string) => boolean;\n };\n}\n\n/**\n * Driver/SQLSTATE codes that mean \"the thing you asked me to create is already\n * there\". Postgres reports SQLSTATE on `code`; mysql2 reports its symbolic name.\n */\nconst ALREADY_EXISTS: DriverErrorSignature = {\n codes: new Set([\n // PostgreSQL SQLSTATE (class 42 — syntax error or access rule violation)\n '42P07', // duplicate_table\n '42701', // duplicate_column\n '42710', // duplicate_object — index / constraint already exists\n // MySQL / MariaDB (mysql2 puts the symbolic name on `code`)\n 'ER_TABLE_EXISTS_ERROR', // 1050\n 'ER_DUP_FIELDNAME', // 1060\n 'ER_DUP_KEYNAME', // 1061\n ]),\n errnos: new Set([1050, 1060, 1061]),\n /**\n * Message fallback for drivers that carry no machine-readable code —\n * notably SQLite, whose `code` is the undifferentiated `SQLITE_ERROR` for\n * every DDL failure, so the message is the only signal available:\n * - `table sys_metadata already exists`\n * - `duplicate column name: environment_id`\n * - `index idx_x already exists`\n * Postgres phrases its own as `relation \"x\" already exists` /\n * `column \"x\" of relation \"y\" already exists`, which matches the same test.\n */\n message: /already exists|duplicate column name|duplicate key name/i,\n};\n\n/**\n * Codes/messages that mean \"the table you tried to READ has not been created\".\n *\n * Narrower than it looks, on purpose. `does not exist` on its own also covers\n * `role \"x\" does not exist` (42704), `database \"x\" does not exist` (3D000) and\n * `column \"x\" does not exist` (42703) — every one of them a **real** failure\n * that must stay loud, and every one of them a case where \"start numbering at\n * 1\" would be the wrong answer against a table that may be full of rows. So the\n * message test demands the word table/relation next to the phrase rather than\n * the phrase alone, and the code set carries only the table-scoped SQLSTATEs.\n *\n * That was not enough on its own, and #6347 is why. Postgres has **two**\n * missing-column phrasings, one per direction:\n *\n * | path | phrase | SQLSTATE | matched the message test? |\n * |:---|:---|:---|:---|\n * | read (`SELECT`) | `column \"bogus\" does not exist` | 42703 | no |\n * | write (`INSERT`/`UPDATE`/`ALTER`) | `column \"label\" of relation \"sys_team\" does not exist` | 42703 | **yes** |\n *\n * The write-path phrase contains a complete, legal missing-table phrase —\n * `relation \"sys_team\" does not exist` — as a substring, so the table-scoped\n * test above matched it and answered *benign* about an error the docblock two\n * paragraphs up already named as one that must stay loud. The same holds for\n * every other sub-object of a relation Postgres phrases this way, e.g.\n * `constraint \"uq_x\" of relation \"sys_team\" does not exist` (42704). And\n * code-first does not rescue it: {@link matchesDriverError} is a sequential OR,\n * so a `code: '42703'` error simply falls past the two code lines and is\n * decided by the message.\n *\n * Hence {@link DriverErrorSignature.excludes}: the not-a-table shapes are\n * recognised FIRST, and recognition ends the question with `false`.\n */\nconst MISSING_TABLE: DriverErrorSignature = {\n codes: new Set([\n '42P01', // PostgreSQL undefined_table\n 'ER_NO_SUCH_TABLE', // MySQL / MariaDB 1146\n ]),\n errnos: new Set([1146]),\n /**\n * - SQLite / libsql: `no such table: sys_metadata_history`\n * - PostgreSQL: `relation \"sys_metadata_history\" does not exist`\n * - MySQL/MariaDB: `Table 'app.sys_metadata_history' doesn't exist`\n */\n message:\n /no such table|relation [\"'`][^\"'`]+[\"'`] does not exist|table [\"'`][^\"'`]+[\"'`] doesn'?t exist|unknown table/i,\n excludes: {\n /**\n * Exactly the three SQLSTATEs the docblock above already names as\n * must-stay-loud neighbours of `does not exist`. They are listed here\n * rather than merely trusted to miss the message test, because two of\n * them (42703 columns, 42704 constraints/triggers) have a phrasing that\n * *does* hit it, and because a code is a fact where prose is a guess.\n *\n * Postgres-shaped on purpose: measured, neither MySQL\n * (`Unknown column 'label' in 'field list'`) nor SQLite\n * (`no such column: bogus`, `table t has no column named label`)\n * phrases a sub-object failure so that a missing-table phrase falls out\n * of it, so there is nothing there to exclude. Adding their codes would\n * be surface with no defect behind it.\n */\n codes: new Set([\n '42703', // undefined_column\n '42704', // undefined_object — constraint, trigger, role, type, …\n '3D000', // invalid_catalog_name — `database \"x\" does not exist`\n ]),\n /**\n * `«sub-object» \"x\" of relation \"y\" …` — Postgres' phrasing for a\n * failure about something *inside* a relation, which therefore says the\n * relation itself is present. The two in-repo siblings that carry this\n * phrase are `mapDataError` (`packages/rest`, #5352) and\n * `service-analytics`'s missing-column subtraction (#6035/PR #6346).\n *\n * [#6615] All three now read one home — `@objectstack/types` — instead\n * of three hand-kept copies, so the phrase can no longer be taught to\n * the repo a fourth time or drift in one package only. The **width**\n * difference that used to justify the copy is preserved and is the\n * reason the home exports two functions rather than one: those two\n * *extract* the column name to phrase a better error, so a miss costs a\n * vaguer message; this one *excludes*, so a miss restores the\n * corruption. {@link isRelationSubObjectPhrase} is therefore the wider\n * question — it drops their `column`/`[a-z0-9_]+`/`does not exist`\n * anchors: any sub-object, any quoted identifier, any verdict.\n * Over-matching here only ever converts a benign verdict into a loud\n * one, which is the direction this whole module already errs in.\n */\n matchesMessage: isRelationSubObjectPhrase,\n },\n};\n\n/** How far to follow an `error.cause` chain — drivers wrap, but not deeply. */\nconst MAX_CAUSE_DEPTH = 4;\n\n/**\n * The single matcher both predicates run on: exclusions, then code, then errno,\n * then message, then one step down the `cause` chain.\n *\n * Unrecognised is always `false` — a benign verdict must be *earned*, never\n * defaulted to, because a false \"benign\" corrupts data while a false \"real\"\n * costs one error line.\n *\n * The exclusion runs at every node and, when it fires, returns `false` **without\n * descending into `cause`** (#6347). Two reasons, both the conservative\n * direction: an error that positively identifies as \"a column of an existing\n * relation\" *is* that error, whatever it wraps; and stopping can only ever\n * subtract benign verdicts, never add one.\n */\nfunction matchesDriverError(\n error: unknown,\n signature: DriverErrorSignature,\n depth: number,\n): boolean {\n if (error === null || error === undefined || depth > MAX_CAUSE_DEPTH) return false;\n\n if (typeof error === 'string') {\n if (signature.excludes?.matchesMessage(error)) return false;\n return signature.message.test(error);\n }\n if (typeof error !== 'object') return false;\n\n const err = error as {\n code?: unknown;\n errno?: unknown;\n message?: unknown;\n cause?: unknown;\n };\n\n const excludes = signature.excludes;\n if (excludes) {\n if (typeof err.code === 'string' && excludes.codes.has(err.code)) return false;\n if (typeof err.message === 'string' && excludes.matchesMessage(err.message)) return false;\n }\n\n if (typeof err.code === 'string' && signature.codes.has(err.code)) return true;\n if (typeof err.errno === 'number' && signature.errnos.has(err.errno)) return true;\n if (typeof err.message === 'string' && signature.message.test(err.message)) return true;\n\n // Drivers commonly re-throw with the original attached as `cause`.\n return matchesDriverError(err.cause, signature, depth + 1);\n}\n\n/**\n * Is this DDL error the benign \"already provisioned\" case?\n *\n * @param error - The value thrown by `syncSchema()` (or any DDL call).\n * @param depth - Internal `cause`-chain recursion counter; callers pass nothing.\n * @returns `true` only when the error positively identifies as\n * table/column/index-already-exists. Anything else — including an\n * unrecognised error, `undefined`, or a permission/connection failure —\n * returns `false` and MUST be reported loudly by the caller.\n */\nexport function isSchemaAlreadyExistsError(error: unknown, depth = 0): boolean {\n return matchesDriverError(error, ALREADY_EXISTS, depth);\n}\n\n/**\n * Is this READ error the benign \"table has not been provisioned yet\" case?\n *\n * The only failure that licenses a caller to treat an empty table as the truth\n * — there are no rows, so there is nothing to be inconsistent with. A\n * connection drop, a timeout, a permission denial or a query error all mean the\n * rows may well exist and simply were not seen; those return `false` and the\n * caller must report the consequence and give up rather than compute an answer\n * from data it never read (#4825).\n *\n * A failure about a **column** of a relation is never this case, in either of\n * Postgres' two phrasings — the relation is right there in the message because\n * it exists (#6347). See {@link MISSING_TABLE}'s `excludes`.\n *\n * @param error - The value thrown by a driver/engine read (`find`, `findOne`, …).\n * @param depth - Internal `cause`-chain recursion counter; callers pass nothing.\n * @returns `true` only when the error positively identifies as\n * table/relation-does-not-exist.\n */\nexport function isMissingTableError(error: unknown, depth = 0): boolean {\n return matchesDriverError(error, MISSING_TABLE, depth);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Migration: project_id → environment_id\n *\n * Renames the `project_id` column to `environment_id` on the metadata\n * storage tables:\n * - sys_metadata\n * - sys_metadata_history\n *\n * Forward counterpart of {@link migrateEnvIdToProjectId} (which performed the\n * earlier `env_id → project_id` rename). Together they let an operator walk an\n * old schema all the way forward in two steps:\n *\n * migrateEnvIdToProjectId(driver); // env_id → project_id (legacy)\n * migrateProjectIdToEnvironmentId(driver); // project_id → environment_id (v5)\n *\n * (The per-type projection tables `sys_object` / `sys_view` / `sys_flow` /\n * `sys_agent` / `sys_tool` were removed in 2026-05 along with the projection\n * pipeline — see ADR 0005 addendum. They are intentionally not included.)\n *\n * Safe to run multiple times (idempotent): checks for column existence before\n * attempting to rename. If `environment_id` already exists, the step is\n * skipped.\n *\n * Usage:\n * import { migrateProjectIdToEnvironmentId } from '@objectstack/metadata/migrations';\n * await migrateProjectIdToEnvironmentId(driver);\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\n\nconst AFFECTED_TABLES = [\n 'sys_metadata',\n 'sys_metadata_history',\n] as const;\n\nexport interface ProjectIdToEnvironmentIdResult {\n table: string;\n status: 'renamed' | 'already_done' | 'table_missing' | 'error';\n error?: string;\n}\n\n/**\n * Rename `project_id` → `environment_id` on all metadata tables.\n *\n * @param driver An IDataDriver with access to the target database.\n * Must expose a raw query method: `driver.raw(sql, bindings?)`.\n * @returns Per-table migration results.\n */\nexport async function migrateProjectIdToEnvironmentId(\n driver: IDataDriver,\n): Promise<ProjectIdToEnvironmentIdResult[]> {\n const driverAny = driver as any;\n\n if (typeof driverAny.raw !== 'function') {\n throw new Error(\n 'migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. ' +\n 'migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. ' +\n 'SqlDriver (better-sqlite3/knex) supports this; cloud-side TursoDriver also conforms.'\n );\n }\n\n const results: ProjectIdToEnvironmentIdResult[] = [];\n\n for (const table of AFFECTED_TABLES) {\n try {\n const hasColumn = await _columnExists(driverAny, table, 'project_id');\n const alreadyMigrated = await _columnExists(driverAny, table, 'environment_id');\n\n if (alreadyMigrated && !hasColumn) {\n results.push({ table, status: 'already_done' });\n continue;\n }\n\n if (!hasColumn) {\n results.push({ table, status: 'table_missing' });\n continue;\n }\n\n await driverAny.raw(\n `ALTER TABLE \"${table}\" RENAME COLUMN project_id TO environment_id`,\n );\n\n results.push({ table, status: 'renamed' });\n } catch (err: any) {\n results.push({ table, status: 'error', error: err?.message ?? String(err) });\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nasync function _columnExists(driver: any, table: string, column: string): Promise<boolean> {\n try {\n const rows: any[] = await driver.raw(`PRAGMA table_info(\"${table}\")`);\n if (Array.isArray(rows) && rows.length > 0) {\n const list: any[] = Array.isArray(rows[0]) ? rows[0] : rows;\n return list.some((r: any) => r?.name === column);\n }\n\n const result: any[] = await driver.raw(\n `SELECT column_name FROM information_schema.columns WHERE table_name = ? AND column_name = ?`,\n [table, column],\n );\n const list: any[] = Array.isArray(result[0]) ? result[0] : result;\n return list.length > 0;\n } catch {\n return false;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Endpoint matcher — the producer side of `IMetadataService.matchEndpoint`.\n *\n * [#5089, #5040 E2] A declared `api` metadata item owns a `method`+`path`\n * pair; this module turns the stored `api` items into a lookup the HTTP\n * dispatcher can perform once per request, between \"no built-in domain\n * claimed this path\" and \"answer a semantic 404\".\n *\n * The binding specification is the contract text on\n * `IMetadataService.matchEndpoint` and {@link ApiEndpointMatch} in\n * `packages/spec/src/contracts/metadata-service.ts` (landed by #5080/#5097).\n * This module implements it literally; where the text is silent the choice is\n * documented here rather than invented in a caller.\n *\n * ## What this module is NOT\n *\n * It is not a router. `ApiEndpointSchema.path` is a frozen vocabulary\n * (ADR-0121) that defines no template syntax — no `:param`, no `{param}` — so\n * there is nothing to compile and nothing to rank. Matching is a `Map` lookup\n * on an exact key, and {@link ApiEndpointMatch.params} is always `{}`.\n * Inventing a template syntax here would create a dialect that exists only\n * inside an implementation, which Prime Directive #12 forbids.\n *\n * ## Normalization (both sides, identically)\n *\n * - **method** — upper-cased. `ApiEndpointSchema.method` is already an\n * upper-case `HttpMethod` enum, so this only ever changes the *query* side,\n * which is the point: a request verb is compared case-insensitively.\n * - **path** — exactly ONE trailing slash is trimmed, and never from a lone\n * `/`. Trimming one (not all) keeps `/x//` and `/x/` distinct, matching how\n * every router in this stack treats an empty path segment; keeping `/`\n * whole means the normalized form is still a legal `ApiEndpointSchema.path`\n * and a query for `\"\"` can never collide with a declaration of `/`.\n * Nothing else happens to the path: **no** percent-decoding, **no** Unicode\n * normalization, **no** case folding. 17.x compares the raw string. (Design\n * §7-5 keeps RFC 3986 canonicalization as an explicit open question — it is\n * a vocabulary-level decision, not an implementation detail to smuggle in.)\n *\n * ## Envelope off, body parsed (#5309)\n *\n * What arrives here is a STORED ROW, not an authored declaration: the metadata\n * layer wraps every row in its own bookkeeping — `packageId`, `state`,\n * `version`, `publishedDefinition`, … — either flat beside the body or under a\n * `metadata` key (the publish envelope). None of that is endpoint vocabulary,\n * so `peelStoredEnvelope` takes it off and `ApiEndpointSchema` sees the\n * authored body alone. Before the split, the schema's unknown-key STRIPPING\n * was load-bearing here: it silently ate the bookkeeping, which is exactly why\n * `api` could not be closed (#5271's measurement, #5384's job). Stripping is no\n * longer what makes a stored row parse.\n *\n * The body-selection half (`metadata ?? row`) is the metadata layer's existing\n * rule — `publishPackage`'s snapshot, `getPublished`, `gateApiItemsForPublish`\n * all use it — and this module now follows it too, so a publish envelope that\n * passes the publish gate is the same document this index serves. It used to\n * be the one reader that disagreed.\n *\n * ## Loud, never half-valid\n *\n * Every stored item is `ApiEndpointSchema.safeParse`-d. The answer handed back\n * is the PARSED object, so schema defaults are materialized — most importantly\n * `authRequired`, which the schema defaults to `true`: a consumer can never\n * read an author's omission as \"no auth required\". An item that fails to parse\n * is skipped and named at `error` level: an endpoint the author declared and\n * the runtime will not serve is a capability that silently went missing, and\n * \"absence must be loud\" (AGENTS.md, Route & surface ownership §3). Skipping\n * one bad item never disturbs the good ones.\n *\n * ## The publish gates, applied a second time at load (#5189, #5040 E7b)\n *\n * Parsing is necessary and NOT sufficient. `ApiEndpointSchema` accepts shapes\n * the runtime refuses and shapes ADR-0121 forbids — `type: 'proxy'`, a mapping\n * `transform`, and above all `authRequired: false` with no armed `rateLimit`.\n * E7 (#5111) hung the gates that reject those on `ObjectStackDefinitionSchema`,\n * which covers every path that parses a STACK; #5189 proved that a stored `api`\n * item need never have been part of one (`metadata.register()`, a Studio write,\n * `publishPackage`). Most gates degrade safely when bypassed — the executor\n * answers a structured 501, a mis-namespaced path simply matches nothing — but\n * D6 has no runtime counterpart at all: the runtime honours `authRequired:\n * false` faithfully and `deriveBucketConfig` returns `null` for a disarmed\n * budget, so a bypassed D6 mints an anonymous, zero-quota execution entry\n * point. That is the exact shape D6 exists to prevent.\n *\n * So every parsed item is re-judged here by\n * {@link identityFreeEndpointGateFailure} — the SAME `firstFailure` the publish\n * gate runs, minus the two gates that need an identity this module does not\n * have. The asymmetry is deliberate and worth stating: the **namespace** gate\n * needs `manifest.namespace` (a stored row carries no manifest, and deriving\n * one from the very path being judged would be circular), and the\n * **uniqueness** gate is a per-stack rule that the duplicate-claim resolution\n * below already covers store-wide. An item failing an identity-free gate is\n * EXCLUDED from the index and named at `error` level, exactly like a parse\n * failure: a bypassed endpoint that answers 404 plus a loud log is the safe\n * failure; one that answers anonymously and unmetered is not. Publish is the\n * first door; this is the backstop, never the only door.\n *\n * ## Duplicate claims\n *\n * Two stored items may claim the same METHOD+path (publish rejects that inside\n * one stack, but a direct `metadata.register()` write bypasses publish). The\n * resolution is deterministic and announced, never last-write-wins: the\n * endpoint whose `name` sorts FIRST lexicographically keeps the route, and the\n * discarded claimant is named at `error` level together with the winner and\n * the rule. Deterministic because a coin flip would make the same deployment\n * behave differently per node and per boot; loud because a declaration that\n * does not serve is exactly the \"declared ≠ enforced\" state this repo keeps\n * paying to remove. (#5040 design §1.3.)\n *\n * ## An outage is not a 404\n *\n * `undefined` means \"no declaration owns this route\". A store that cannot be\n * read must THROW — the same distinction `loadDiagnosed` draws for the\n * singular read (ADR-0110 D3), for the same reason: a miss becomes a 404, and\n * an unreachable metadata store must never masquerade as one. So the read this\n * matcher performs is deliberately NOT wrapped in a `try`/`catch`, and a\n * failed build is not cached — the next call retries against a store that may\n * have recovered.\n */\n\nimport {\n ApiEndpointSchema,\n identityFreeEndpointGateFailure,\n normalizeEndpointPath,\n type ApiEndpoint,\n} from '@objectstack/spec/api';\nimport type { ApiEndpointMatch } from '@objectstack/spec/contracts';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { peelStoredEnvelope, storedItemName } from './stored-envelope.js';\n\n/**\n * Upper-case a request verb so `method` compares case-insensitively.\n */\nexport function normalizeEndpointMethod(method: string): string {\n return String(method ?? '').toUpperCase();\n}\n\n/**\n * Trim exactly one trailing slash, never from a lone `/`.\n *\n * Applied to BOTH the stored declaration and the query, so the two sides can\n * never disagree about which form is canonical — and re-exported from\n * `@objectstack/spec/api`, which OWNS the rule (#5040 E7), rather than\n * re-implemented here. The publish gate that rejects two endpoints claiming the\n * same METHOD + path must normalize exactly as this matcher does, or a stack\n * could publish a duplicate the index then silently resolves to one winner.\n */\nexport { normalizeEndpointPath };\n\n/** The index key for a normalized method+path pair. */\nexport function endpointIndexKey(method: string, path: string): string {\n return `${normalizeEndpointMethod(method)} ${normalizeEndpointPath(path)}`;\n}\n\n/** The lazily-built lookup: `\"METHOD /path\"` → parsed endpoint. */\nexport type EndpointIndex = ReadonlyMap<string, ApiEndpoint>;\n\nexport interface EndpointMatcherDeps {\n /**\n * Enumerate the stored `api` metadata items.\n *\n * MUST reject when the store cannot be read. Resolving with `[]` on a failed\n * read would turn an outage into \"nothing is declared\", i.e. into a 404 —\n * precisely what the contract forbids. See `MetadataManager.listForIndex`.\n */\n listApiItems(): Promise<unknown[]>;\n logger: Logger;\n}\n\n/**\n * Build the METHOD→path→endpoint index from raw stored items.\n *\n * Exported for tests and for any future occupant of the `metadata` slot that\n * wants the same load-time discipline without inheriting `MetadataManager`.\n */\nexport function buildEndpointIndex(items: readonly unknown[], logger: Logger): EndpointIndex {\n const index = new Map<string, ApiEndpoint>();\n\n for (const item of items) {\n // [#5309] Envelope OFF before the body parse. A stored row carries the\n // metadata layer's bookkeeping (`packageId`, `state`, …) which is not\n // endpoint vocabulary; `ApiEndpointSchema` judges the authored body and\n // nothing else. See `stored-envelope.ts` for why this is the fix rather\n // than teaching the vocabulary two storage keys.\n const peeled = peelStoredEnvelope(item);\n const parsed = ApiEndpointSchema.safeParse(peeled.body);\n if (!parsed.success) {\n // LOUD skip (contract: \"MUST skip (loudly) any stored item that fails to\n // parse rather than returning a half-valid shape\"). Name the item so the\n // author can find it; say what the consequence is.\n const declaredName = storedItemName(peeled) ?? '<unnamed>';\n logger.error(\n `[EndpointMatcher] stored api item '${declaredName}' does not satisfy ApiEndpointSchema — ` +\n `it is EXCLUDED from endpoint matching and its declared route will answer 404. ` +\n `Fix the declaration (or remove it); the endpoint index never serves a half-valid shape.`,\n undefined,\n { issues: parsed.error.issues },\n );\n continue;\n }\n\n const endpoint = parsed.data;\n\n // [#5189, #5040 E7b] Second door: the identity-free publish gates. A stored\n // item that never passed publish is excluded rather than served — see the\n // module header for why D6 in particular cannot be left to the runtime.\n const gateFailure = identityFreeEndpointGateFailure(endpoint);\n if (gateFailure) {\n logger.error(\n `[EndpointMatcher] stored api item '${endpoint.name}' was stored WITHOUT passing the ` +\n `endpoint publish gates (#5040 E7 / ADR-0121) — it is EXCLUDED from endpoint matching and ` +\n `its declared route will answer 404. Republish it through a gated path (a stack artifact, ` +\n `or \\`publishPackage\\` with the package's \\`manifest.namespace\\`); a direct metadata write ` +\n `is not a publish. Gate failure: ${gateFailure.message}`,\n undefined,\n { name: endpoint.name, issue: { path: gateFailure.path, message: gateFailure.message } },\n );\n continue;\n }\n\n const key = endpointIndexKey(endpoint.method, endpoint.path);\n const incumbent = index.get(key);\n\n if (!incumbent) {\n index.set(key, endpoint);\n continue;\n }\n\n // Deterministic + loud: lexicographically-first `name` keeps the route.\n // `<` (not `<=`) keeps the first-seen entry when names are equal, so the\n // rule is total even in the degenerate case.\n const challengerWins = endpoint.name < incumbent.name;\n const winner = challengerWins ? endpoint : incumbent;\n const loser = challengerWins ? incumbent : endpoint;\n if (challengerWins) index.set(key, endpoint);\n\n logger.error(\n `[EndpointMatcher] duplicate endpoint claim on '${key}': api items '${incumbent.name}' and ` +\n `'${endpoint.name}' both declare it. '${winner.name}' KEEPS the route and '${loser.name}' is ` +\n `IGNORED — the rule is lexicographically-first \\`name\\` wins, chosen so every node and every ` +\n `boot resolves it identically. Rename or repath '${loser.name}' to make it reachable.`,\n undefined,\n { key, winner: winner.name, ignored: loser.name },\n );\n }\n\n return index;\n}\n\n/**\n * Lazy, invalidating endpoint index.\n *\n * Built on the first {@link match} call and rebuilt on the next call after any\n * {@link invalidate}. Endpoint counts are single- to triple-digit, so a whole\n * rebuild is cheaper than per-item bookkeeping and cannot drift from the store.\n */\nexport class EndpointMatcher {\n private readonly deps: EndpointMatcherDeps;\n /** Resolved index, or `undefined` while dirty. */\n private index?: EndpointIndex;\n /** In-flight build, so concurrent requests share one store read. */\n private building?: Promise<EndpointIndex>;\n\n constructor(deps: EndpointMatcherDeps) {\n this.deps = deps;\n }\n\n /** Mark the index stale; the next {@link match} rebuilds it. */\n invalidate(): void {\n this.index = undefined;\n this.building = undefined;\n }\n\n /**\n * Resolve `method`+`path` to the owning declaration.\n *\n * @returns the parsed endpoint plus `params: {}`, or `undefined` on a miss.\n * @throws whatever the store read threw — an outage is never a miss.\n */\n async match(query: { path: string; method: string }): Promise<ApiEndpointMatch | undefined> {\n const index = await this.ensureIndex();\n const endpoint = index.get(endpointIndexKey(query.method, query.path));\n if (!endpoint) return undefined;\n // `params` is always {} in 17.x — the frozen vocabulary defines no path\n // template syntax. See ApiEndpointMatch.params.\n return { endpoint, params: {} };\n }\n\n private async ensureIndex(): Promise<EndpointIndex> {\n if (this.index) return this.index;\n if (this.building) return this.building;\n\n const build = (async () => {\n // Deliberately NOT guarded: a store read failure propagates to the\n // caller so an outage surfaces as an outage, never as a 404.\n const items = await this.deps.listApiItems();\n return buildEndpointIndex(items, this.deps.logger);\n })();\n\n this.building = build;\n try {\n const built = await build;\n // Only publish the result if no invalidation raced us mid-build.\n if (this.building === build) {\n this.index = built;\n this.building = undefined;\n }\n return built;\n } catch (error) {\n // A failed build is never cached — the next request retries against a\n // store that may have recovered.\n if (this.building === build) this.building = undefined;\n throw error;\n }\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The stored ENVELOPE / authored BODY split (#5309).\n *\n * ## The defect this exists to remove\n *\n * A metadata *type name* is worn by two different documents in this codebase:\n *\n * - the **authored declaration** — exactly the vocabulary its spec schema\n * declares, written by a human or (ADR-0033) an AI author;\n * - the **stored row** — that declaration plus the metadata layer's own\n * bookkeeping: `packageId`, `state`, `version`, `publishedDefinition`,\n * `publishedAt`, `publishedBy`, written by {@link\n * import('./metadata-manager.js').MetadataManager.register} and\n * `publishPackage`, and read back by `publishPackage`'s own package filter.\n *\n * Those bookkeeping keys are NOT vocabulary. As long as a spec schema strips\n * unknown keys the difference is invisible — and that is precisely the problem:\n * the same permissiveness that lets `packageId` through also lets a `cacheTTL`\n * / `outputMappings` typo through, so an author's policy or projection silently\n * does not ship (ADR-0078). `api` sits on the #4001 campaign's `STILL_STRIP`\n * list for exactly this reason, measured in #5271: closing `ApiEndpointSchema`\n * made every stored row fail with `unrecognized_keys: ['packageId', 'state']`,\n * so the load-time backstop excluded the endpoint (its route answered 404) and\n * the publish gate reported a schema error instead of its ADR-0121 D6 verdict.\n *\n * The fix is NOT to teach a vocabulary two storage keys — that would make the\n * author-facing contract describe the storage layer, the trade Prime Directive\n * #12 refuses. It is to peel the envelope off **before** the body is handed to\n * its schema, which is what this module does.\n *\n * ## The rule, in one sentence\n *\n * A stored row is an envelope around a body: when the row carries a\n * {@link STORED_BODY_KEY} value the body is that value and everything beside\n * it is envelope; otherwise the body is the row minus the declared\n * {@link STORED_ENVELOPE_KEYS}.\n *\n * Neither half is invented here. `metadata ?? row` is already the metadata\n * layer's body-selection rule in three places — `publishPackage`'s\n * `publishedDefinition` snapshot, `getPublished`'s fallback, and\n * `gateApiItemsForPublish` — and {@link STORED_ENVELOPE_KEYS} is the list of\n * keys `MetadataManager` itself writes onto, or filters rows by. This module\n * gives that rule ONE spelling so the two `ApiEndpointSchema` parse sites\n * (`buildEndpointIndex`, `gateApiItemsForPublish`) can never disagree about\n * what the document is.\n *\n * ## What it deliberately does NOT do\n *\n * - **It never mutates the row.** It returns views, so every existing reader\n * of the envelope (`publishPackage`'s `meta?.packageId === packageId`\n * filter, `query`'s `state` / `packageId` filters, `revertPackage`) keeps\n * reading exactly the object it read before.\n * - **It does not re-shape what publish snapshots.** `publishedDefinition`\n * keeps its current bytes (`structuredClone(data.metadata ?? data)`),\n * envelope keys included, because `revertPackage` restores from it. Peeling\n * the snapshot is a stored-format change; this module is the parse\n * boundary only.\n * - **It is not a schema.** It removes keys the storage layer put there; it\n * makes no judgement about the body, which is the schema's job and stays\n * the schema's job.\n *\n * @see packages/spec/src/api/endpoint.zod.ts — the `STILL_STRIP` note this\n * split is the prerequisite for closing (#5384 owns the closing itself).\n */\n\n/**\n * The metadata layer's bookkeeping keys on a stored row.\n *\n * Every entry is written by `MetadataManager` or read by it as row identity —\n * none of them is authorable vocabulary for any metadata type:\n *\n * | key | written by | read by |\n * |---|---|---|\n * | `packageId` | callers / publish envelope | `publishPackage`, `unregisterPackage`, `revertPackage`, `query`, realtime event |\n * | `package` | legacy package stamp | the same three package filters |\n * | `state` | `publishPackage`, `revertPackage` | `query`, the dependency-published check |\n * | `version` | `publishPackage` | `publishPackage` (next version) |\n * | `publishedDefinition` | `publishPackage` | `revertPackage`, `getPublished`, the dependency-published check |\n * | `publishedAt` / `publishedBy` | `publishPackage` | publish bookkeeping |\n *\n * Adding a key here is a real decision: it removes that key from every body a\n * peeled parse sees. Add one only when `MetadataManager` itself writes or\n * filters on it.\n */\nexport const STORED_ENVELOPE_KEYS = Object.freeze([\n 'package',\n 'packageId',\n 'publishedAt',\n 'publishedBy',\n 'publishedDefinition',\n 'state',\n 'version',\n] as const);\n\n/**\n * The key a stored row uses to carry its authored body — the publish envelope\n * shape `{ name, packageId, state, metadata: {…authored} }`.\n */\nexport const STORED_BODY_KEY = 'metadata';\n\nconst ENVELOPE_KEYS = new Set<string>([...STORED_ENVELOPE_KEYS, STORED_BODY_KEY]);\n\nconst EMPTY_ENVELOPE: Readonly<Record<string, unknown>> = Object.freeze({});\n\n/** A stored row split into the layer's bookkeeping and the authored document. */\nexport interface PeeledStoredItem {\n /**\n * The bookkeeping the metadata layer wrote around the body. Frozen, and a\n * VIEW — the stored row itself is untouched.\n */\n readonly envelope: Readonly<Record<string, unknown>>;\n /**\n * The authored document, ready for its spec schema. `unknown` on purpose:\n * peeling an envelope says nothing about whether the body is valid.\n */\n readonly body: unknown;\n /** `true` when the row carried its body under {@link STORED_BODY_KEY}. */\n readonly wrapped: boolean;\n}\n\n/**\n * Split a stored metadata row into its envelope and its authored body.\n *\n * Total by construction — a non-object row (or `undefined`) is handed back as\n * the body with an empty envelope, so the caller's schema reports the real\n * problem instead of this function inventing one.\n *\n * @param item a value read out of the registry or a loader.\n */\nexport function peelStoredEnvelope(item: unknown): PeeledStoredItem {\n if (item === null || typeof item !== 'object' || Array.isArray(item)) {\n return { envelope: EMPTY_ENVELOPE, body: item, wrapped: false };\n }\n\n const row = item as Record<string, unknown>;\n const wrappedBody = row[STORED_BODY_KEY];\n\n // The publish-envelope shape. Everything beside the body IS envelope by\n // construction, so no key list is consulted — the row said so itself.\n // `!= null` (not `in`) mirrors the `data.metadata ?? data` rule this\n // replaces, so a row with `metadata: null` keeps falling through to the flat\n // branch exactly as it does today.\n if (wrappedBody !== undefined && wrappedBody !== null) {\n const envelope: Record<string, unknown> = {};\n for (const key of Object.keys(row)) {\n if (key === STORED_BODY_KEY) continue;\n envelope[key] = row[key];\n }\n return { envelope: Object.freeze(envelope), body: wrappedBody, wrapped: true };\n }\n\n // The flat shape: body and bookkeeping share one level, so the declared key\n // list is the only thing that can tell them apart.\n let envelope: Record<string, unknown> | undefined;\n for (const key of Object.keys(row)) {\n if (!ENVELOPE_KEYS.has(key)) continue;\n envelope ??= {};\n envelope[key] = row[key];\n }\n\n // An authored declaration carries no bookkeeping at all — hand back the very\n // object that came in, so nothing about an authored parse changes, not even\n // object identity.\n if (!envelope) return { envelope: EMPTY_ENVELOPE, body: row, wrapped: false };\n\n const body: Record<string, unknown> = {};\n for (const key of Object.keys(row)) {\n if (ENVELOPE_KEYS.has(key)) continue;\n body[key] = row[key];\n }\n return { envelope: Object.freeze(envelope), body, wrapped: false };\n}\n\n/**\n * The `name` a stored row is known by, for a diagnostic that must name the\n * offending row even when its body failed to parse.\n *\n * Reads the envelope first and the body second, which is the SAME answer\n * `item.name` gave before the split: on the flat shape `name` lives in the\n * body, on the publish envelope it lives outside `metadata`.\n */\nexport function storedItemName(peeled: PeeledStoredItem): string | undefined {\n const fromEnvelope = peeled.envelope.name;\n if (typeof fromEnvelope === 'string') return fromEnvelope;\n const body = peeled.body;\n if (body && typeof body === 'object' && !Array.isArray(body)) {\n const fromBody = (body as { name?: unknown }).name;\n if (typeof fromBody === 'string') return fromBody;\n }\n return undefined;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { readFile } from 'node:fs/promises';\nimport { createHash } from 'node:crypto';\nimport { Plugin, PluginContext } from '@objectstack/core';\nimport { NodeMetadataManager } from './node-metadata-manager.js';\nimport { MemoryLoader } from './loaders/memory-loader.js';\nimport { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel';\nimport type { MetadataPluginConfig } from '@objectstack/spec/kernel';\nimport { applyProtection } from '@objectstack/spec/shared';\nimport {\n SysMetadataObject,\n SysMetadataHistoryObject,\n SysMetadataCommitObject,\n SysMetadataAuditObject,\n SysViewDefinitionObject,\n} from '@objectstack/metadata-core';\n\n// `SysMetadataObject` + `SysMetadataHistoryObject` are the customer overlay\n// storage substrate (ADR-0005). They must always be auto-provisioned so\n// `PUT /api/v1/meta/{view,dashboard}/...` has a place to write. All other\n// metadata types (object/view/flow/agent/tool/dashboard/app/...) live as\n// JSON inside `sys_metadata` — there are no separate per-type tables. The\n// previously shipped `SysObject` / `SysView` / `SysFlow` / `SysAgent` /\n// `SysTool` projection objects were removed in 2026-05 (see ADR 0005\n// addendum); the projection pipeline was removed at the same time.\n//\n// `SysMetadataAuditObject` (ADR-0010) is the append-only audit trail for\n// metadata write decisions — provisioned alongside the storage tables so\n// `_lock` enforcement always has a place to record decisions, even when\n// the deployment skipped @objectstack/plugin-audit.\n//\n// `SysMetadataCommitObject` (ADR-0067) is the package-scoped commit log that\n// GROUPS a turn's `sys_metadata_history` events (the unit `revertCommit`\n// operates on). It MUST be provisioned alongside its history sibling: every\n// publish/apply writes a commit row, so a per-project (cloud) env kernel that\n// omitted it hit `no such table: sys_metadata_commit` on the first AI build —\n// the write is best-effort so the build still landed, but the error spammed\n// logs and the commit timeline silently recorded nothing. Registered here\n// (not only in the ObjectQLPlugin `environmentId === undefined` standalone\n// path) so env kernels — the only place this table is written — get it.\nconst queryableMetadataObjects = [\n SysMetadataObject,\n SysMetadataHistoryObject,\n // ADR-0067 commit log — sibling of sys_metadata_history (see note above).\n SysMetadataCommitObject,\n SysMetadataAuditObject,\n // Runtime view storage (shared / personal). Must always be provisioned so\n // end-user view creation via the generic data API has a place to write —\n // mirroring why sys_metadata is always provisioned for PUT /meta.\n SysViewDefinitionObject,\n];\n\n// Subdirectory under `rootDir` reserved for the ADR-0008 repository's\n// canonical JSON storage + JSONL change log. Kept separate from user\n// source code (which the legacy FilesystemLoader still scans).\nconst REPO_SUBDIR = '.objectstack/metadata';\n\n// Map from ObjectStackDefinition field name to MetadataType name.\n//\n// PINNED against the schema: `scripts/check-stack-collection-maps.mjs` reconciles\n// this map with `ObjectStackDefinitionSchema` in both directions, and every\n// deviation carries a reason there (#6242). It is one of seven hand-maintained\n// enumerations of the same set, and the reason this one has a gate is its own\n// history — `docs` and `roles → positions` were each fixed here one line at a\n// time, after a missing key silently dropped a whole collection.\nconst ARTIFACT_FIELD_TO_TYPE: Record<string, string> = {\n objects: 'object',\n objectExtensions: 'object_extension',\n apps: 'app',\n views: 'view',\n pages: 'page',\n dashboards: 'dashboard',\n reports: 'report',\n actions: 'action',\n themes: 'theme',\n workflows: 'workflow',\n flows: 'flow',\n // ADR-0090 D3: stacks declare `positions` (stack.zod.ts); the retired\n // `roles: 'role'` mapping matched nothing and silently dropped compiled\n // positions from artifact ingestion.\n positions: 'position',\n permissions: 'permission',\n sharingRules: 'sharing_rule',\n policies: 'policy',\n apis: 'api',\n webhooks: 'webhook',\n agents: 'agent',\n tools: 'tool',\n skills: 'skill',\n ragPipelines: 'rag_pipeline',\n hooks: 'hook',\n mappings: 'mapping',\n analyticsCubes: 'analytics_cube',\n connectors: 'connector',\n emailTemplates: 'email_template',\n docs: 'doc',\n books: 'book',\n // `data:` (the SEED collection) is deliberately absent — #6242 row 4(a).\n // It used to map to `'dataset'`, the ADR-0021 analytics kind: the exact name\n // collision `metadata-plugin.zod.ts` warns about in prose. The entry never\n // registered anything (SeedSchema declares no `name`, and the loop below\n // skips nameless items) — a dead pointer aimed at the wrong kind, which\n // would have begun mis-registering the day either side moved. Removed rather\n // than repointed at `'seed'`: seeds are APPLIED by SeedLoaderService off the\n // bundle, never registered as metadata items, so a `seed` mapping would be\n // new behaviour rather than a corrected name.\n};\n\n// ───────────────────────────────────────────────────────────────────────────\n// View container expansion — \"Object has-many View\" (ADR-0017)\n// ───────────────────────────────────────────────────────────────────────────\n//\n// `defineView({ list, form, listViews, formViews })` aggregates every view of\n// an object into one document. The loader expands such a container into N\n// independent ViewItems (one per named view) registered under\n// `<object>.<viewKey>`, so each view is individually addressable and the\n// runtime switcher can be rebuilt by querying `object`. The original container\n// is ALSO kept under the bare `<object>` key for backward-compatible reads.\n//\n// The implementation lives in `@objectstack/spec` (`isAggregatedViewContainer`\n// / `expandViewContainer`) so this HMR loader and the ObjectQL engine boot loop\n// share ONE canonical expansion and can never drift. Re-exported here for the\n// callers below and for `view-expand.test.ts`.\nexport { isAggregatedViewContainer, expandViewContainer } from '@objectstack/spec';\nimport { isAggregatedViewContainer, expandViewContainer } from '@objectstack/spec';\nimport type { IHttpServer } from '@objectstack/spec/contracts';\n\n\nexport interface MetadataPluginOptions {\n rootDir?: string;\n /**\n * When `true`, NodeMetadataManager scans `rootDir` for source-file metadata\n * (yaml/json/ts/js loaders) AND attaches a chokidar watcher to react to\n * filesystem changes. In **artifact-mode** (this is the normal path when\n * a `defineStack()` config is compiled into `dist/objectstack.json`) this\n * filesystem scan is redundant and expensive — leave `watch: false`.\n *\n * The artifact-file HMR watcher is controlled separately by\n * {@link artifactWatch} so that the cheap, single-file polling watcher\n * can be enabled in dev without paying the cost of scanning the entire\n * project root.\n *\n * Default: `false` (post PR-10e — was previously `true`).\n */\n watch?: boolean;\n /**\n * When `true` AND `artifactSource.mode === 'local-file'`, attach a\n * polling chokidar watcher to the artifact file so the server reloads\n * metadata when the CLI recompiles `dist/objectstack.json` in dev mode.\n * Independent of {@link watch} (which controls the source-file scanner).\n *\n * Default: `true` when `artifactSource` is set, otherwise `false`.\n */\n artifactWatch?: boolean;\n config?: Partial<MetadataPluginConfig>;\n /** Organization ID for metadata-scoped consumers; MetadataPlugin itself does not persist runtime metadata. */\n organizationId?: string;\n /**\n * Environment ID used by local artifact envelopes and metadata-scoped\n * consumers. (The v5.0 rename retired the \"project ID\" wording this\n * comment used to carry; see ADR-0006.)\n */\n environmentId?: string;\n /**\n * When set, MetadataPlugin loads metadata from a compiled artifact instead\n * of scanning the filesystem, honored by all three bootstrap modes\n * (`eager` / `lazy` / `artifact-only`) — see `start()`.\n *\n * `path` is a filesystem path, or an `http(s)://` URL fetched verbatim —\n * the control plane's public artifact route\n * (`/pub/v1/environments/:id/artifact[?commit=…]`) serves exactly such\n * URLs, so a sealed runtime can boot straight off a published revision.\n * Remote reads honor `fetchTimeoutMs` / `OS_ARTIFACT_FETCH_TIMEOUT_MS`;\n * the artifact-file HMR watcher ({@link artifactWatch}) applies to\n * non-URL paths only.\n *\n * `local-file` is the only mode. A second `artifact-api` mode (a\n * Bearer-authenticated control-plane pull) existed here through v17 with\n * zero consumers in any repo — the cloud runtime uses its own\n * `ArtifactApiClient`, and package distribution into a running OSS\n * instance goes through `@objectstack/cloud-connection` — and was removed\n * when #4246 forced the declared-vs-enforced question. `start()` rejects\n * an unknown mode loudly rather than silently scanning the filesystem\n * instead.\n */\n artifactSource?: { mode: 'local-file'; path: string; fetchTimeoutMs?: number };\n /**\n * Register the `sys_metadata` + `sys_metadata_history` storage objects\n * on this kernel. Default `true` for backward compatibility.\n *\n * Set to `false` for **per-project** kernels: in cloud / project mode the\n * control plane is the sole owner of metadata storage tables — exposing\n * them inside each project kernel would leak control-plane schema into\n * business-data namespaces.\n */\n registerSystemObjects?: boolean;\n /**\n * Owning package id for source-file metadata loaded by the filesystem\n * scanner (`watch`/eager mode) — the project's `defineStack({ manifest:\n * { id } })` id. When set, scanned items are stamped with\n * `_packageId`/`_provenance: 'package'` via `applyProtection`, exactly\n * like the artifact path, so GET /meta consumers can tell code-defined\n * metadata from user-authored rows.\n *\n * Leave unset when the host has no package identity — items then stay\n * unstamped (runtime-authored semantics). Do NOT pass a guessed value:\n * `_packageId` feeds `isArtifactBacked()` write authorization, so a\n * wrong id silently changes who may edit these items.\n */\n packageId?: string;\n}\n\nexport class MetadataPlugin implements Plugin {\n name = 'com.objectstack.metadata';\n type = 'standard';\n version = '1.0.0';\n /**\n * Services init() UNCONDITIONALLY registers (ADR-0116, #4131) — lets the\n * kernel name this plugin when a consumer requires `metadata` before it\n * initializes.\n */\n providesServices = ['metadata'];\n /**\n * init() registers the metadata system objects through the `manifest`\n * service ObjectQLPlugin provides — order-if-present so that\n * registration is deterministic instead of \"whichever init ran first\"\n * (ADR-0116, #4471). Soft, not hard: without an engine the plugin\n * degrades on purpose (objects are discovered via the legacy fallback).\n */\n optionalDependencies = ['com.objectstack.engine.objectql'];\n\n private manager: NodeMetadataManager;\n private options: MetadataPluginOptions;\n private repository?: import('@objectstack/metadata-core').MetadataRepository;\n /** Chokidar watcher on the artifact file (local-file mode) — ADR-0008 PR-8. */\n private artifactWatcher?: { close: () => Promise<void> };\n /**\n * The most recently parsed artifact metadata (the plural-field record:\n * `objects`, `views`, `data`, …). Carried on the `metadata:reloaded`\n * payload so runtime consumers can react to collections that never enter\n * the MetadataManager — notably seeds (`data`), whose items have no\n * `name` and are skipped by `_parseAndRegisterArtifact`'s register loop.\n */\n private lastParsedMetadata?: Record<string, unknown[]>;\n\n constructor(options: MetadataPluginOptions = {}) {\n this.options = {\n watch: true,\n ...options\n };\n\n const rootDir = this.options.rootDir || process.cwd();\n\n // Sealed-runtime carve-out: `bootstrap: 'artifact-only'` MUST NOT touch\n // the filesystem at all — that includes chokidar subscriptions. Force\n // watch off in that mode regardless of `options.watch`. The other two\n // modes ('eager', 'lazy') honor the user's flag; `lazy` + watch is a\n // valid combination because chokidar attaches to `rootDir` directly,\n // not as a side effect of any priming pass.\n const bootstrapMode = this.options.config?.bootstrap ?? 'eager';\n const effectiveWatch =\n bootstrapMode === 'artifact-only' ? false : (this.options.watch ?? true);\n\n this.manager = new NodeMetadataManager({\n rootDir,\n watch: effectiveWatch,\n formats: ['yaml', 'json', 'typescript', 'javascript']\n });\n\n // Initialize with default type registry\n this.manager.setTypeRegistry(DEFAULT_METADATA_TYPE_REGISTRY);\n }\n\n init = async (ctx: PluginContext) => {\n ctx.logger.info('Initializing Metadata Manager', {\n root: this.options.rootDir || process.cwd(),\n watch: this.options.watch,\n artifactSource: this.options.artifactSource?.mode,\n });\n\n // Register Metadata Manager as the primary metadata service provider.\n ctx.registerService('metadata', this.manager);\n console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);\n\n // Register metadata system objects via the manifest service (if available).\n // MetadataPlugin may init before ObjectQLPlugin, so wrap in try/catch.\n // Skipped when `registerSystemObjects: false` (per-project kernels in\n // cloud / project mode — sys_* live exclusively in the control plane).\n const registerSysObjects = this.options.registerSystemObjects !== false;\n if (registerSysObjects) {\n try {\n const manifestService = ctx.getService<{ register(m: any): void }>('manifest');\n\n // Register the queryable metadata-layer platform objects.\n manifestService.register({\n id: 'com.objectstack.metadata-objects',\n name: 'Metadata Platform Objects',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n defaultDatasource: 'cloud',\n objects: queryableMetadataObjects,\n });\n\n ctx.logger.info('Registered system metadata objects', {\n queryable: queryableMetadataObjects.map((object) => object.name),\n });\n } catch {\n // ObjectQL not loaded yet — objects will be discovered via legacy fallback\n }\n }\n\n ctx.logger.info('MetadataPlugin providing metadata service (primary mode)', {\n mode: this.options.artifactSource?.mode ?? 'file-system',\n features: ['watch', 'multi-format', 'query', 'overlay', 'type-registry']\n });\n }\n\n start = async (ctx: PluginContext) => {\n const src = this.options.artifactSource;\n const mode = this.options.config?.bootstrap ?? 'eager';\n\n ctx.logger.info('[MetadataPlugin] Bootstrapping metadata', {\n bootstrap: mode,\n artifactSource: src?.mode ?? 'none',\n });\n\n // Reject a non-`local-file` source before choosing any load path. The\n // union is single-member so TypeScript callers can't get here, but JS\n // callers and config plumbed through `any` can — and the fall-through\n // below would otherwise treat \"unsupported source\" as \"no source\"\n // (eager would silently scan the filesystem instead of loading the\n // artifact the caller named). The removed `artifact-api` mode gets a\n // pointed migration message (#4246).\n if (src && (src as { mode: string }).mode !== 'local-file') {\n const bad = (src as { mode: string }).mode;\n throw new Error(\n `[MetadataPlugin] artifactSource.mode '${bad}' is not supported`\n + (bad === 'artifact-api'\n ? \" — the 'artifact-api' source was removed (#4246). Load the same artifact with\"\n + \" { mode: 'local-file', path: '<http(s) URL>' } (e.g. the control plane's\"\n + \" /pub/v1/environments/:id/artifact route), or install packages into a running\"\n + ' runtime via @objectstack/cloud-connection.'\n : \". The only artifact source is { mode: 'local-file', path }.\"),\n );\n }\n\n if (mode === 'artifact-only') {\n // Sealed-runtime mode: ONLY load from a pre-compiled artifact. Never\n // touch the filesystem. Required for Edge / serverless / read-only\n // production deployments where the running process must not depend\n // on local source files.\n if (src) {\n await this._loadFromLocalFile(ctx, src.path, src.fetchTimeoutMs);\n } else {\n throw new Error('[MetadataPlugin] bootstrap=artifact-only requires options.artifactSource to be set');\n }\n } else if (mode === 'lazy') {\n // On-demand mode: skip the eager filesystem priming pass entirely.\n // Reads go through MetadataManager.load*/list* which are backed by\n // the DatabaseLoader read-through cache and any registered loaders.\n // An artifact source, if present, is still honored so projects can\n // pin a known set of metadata at boot without paying the FS scan.\n if (src) {\n await this._loadFromLocalFile(ctx, src.path, src.fetchTimeoutMs, { optional: true });\n } else {\n ctx.logger.info('[MetadataPlugin] lazy bootstrap — skipping filesystem priming; metadata loads on demand');\n }\n } else {\n // 'eager' (default): preserve historical behavior.\n if (src) {\n await this._loadFromLocalFile(ctx, src.path, src.fetchTimeoutMs, { optional: true });\n } else {\n await this._loadFromFileSystem(ctx);\n }\n }\n\n // ── ADR-0008 PR-6: attach FileSystemRepository as a supplementary\n // event source so PR-7 (ObjectQL SchemaRegistry), PR-9 (Studio\n // SSE hook) and future cloud consumers can subscribe to the\n // same canonical event stream. No write mirroring yet.\n const bootstrapMode = this.options.config?.bootstrap ?? 'eager';\n if (bootstrapMode !== 'artifact-only') {\n try {\n const path = await import('node:path');\n const { FileSystemRepository } = await import('@objectstack/metadata-fs');\n const rootDir = this.options.rootDir || process.cwd();\n const repoRoot = path.join(rootDir, REPO_SUBDIR);\n const repo = new FileSystemRepository({\n root: repoRoot,\n org: this.options.organizationId ?? 'system',\n disableWatch: this.options.watch === false,\n });\n await repo.start();\n this.repository = repo;\n this.manager.setRepository(repo);\n ctx.logger.info('[MetadataPlugin] FileSystemRepository attached', {\n repoRoot,\n watch: this.options.watch !== false,\n });\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] Failed to attach FileSystemRepository', {\n error: e?.message,\n });\n }\n }\n\n // Bridge realtime service from kernel service registry to MetadataManager.\n try {\n const realtimeService = ctx.getService('realtime');\n if (realtimeService && typeof realtimeService === 'object' && 'publish' in realtimeService) {\n ctx.logger.info('[MetadataPlugin] Bridging realtime service to MetadataManager for event publishing');\n this.manager.setRealtimeService(realtimeService as any);\n }\n } catch (e: any) {\n ctx.logger.debug('[MetadataPlugin] No realtime service found — metadata events will not be published', {\n error: e.message,\n });\n }\n\n // Register the HMR SSE endpoint when an HTTP server is available\n // that exposes a raw Hono app. The endpoint is registered regardless\n // of the `watch` option:\n // - In `watch: true` mode the FS watcher feeds events into the hub.\n // - In `watch: false` mode (e.g. artifact-mode `os dev`), an\n // external watch-recompile pipeline POSTs to the same endpoint\n // after rebuilding the artifact, and we reload it here before\n // broadcasting.\n // Production deployments simply won't have a CLI POSTing to this\n // endpoint and won't surface the route to clients.\n try {\n // [#4251] Both names are the SAME instance; `http.server` is the\n // canonical one (the only name every provider registers), read\n // first. Per-name try — `getService` THROWS for an empty slot, so\n // a single try around `canonical ?? alias` never reaches the\n // alias: exactly the shape this read had before (alias-first),\n // whose fallback therefore never once fired. `getRawApp?()` is on\n // the contract now — the deliberate framework-handle escape,\n // declared once there instead of per consumer. The alias fallback\n // dies with the alias registrations.\n const readServer = (name: string): IHttpServer | undefined => {\n try { return ctx.getService<IHttpServer>(name); } catch { return undefined; }\n };\n const httpServer = readServer('http.server') ?? readServer('http-server');\n if (httpServer && typeof httpServer.getRawApp === 'function') {\n const { registerMetadataHmrRoutes } = await import('./routes/hmr-routes.js');\n const hub = registerMetadataHmrRoutes(httpServer.getRawApp(), this.manager);\n // Wire POST → re-load the artifact from disk (when in\n // local-file artifact mode) so subsequent reads see fresh\n // metadata. The broadcast happens after the handler returns.\n hub.setOnPostReload(async (body: { reason?: string; changed?: string[] } = {}) => {\n const src = this.options.artifactSource;\n if (src?.mode === 'local-file') {\n try {\n await this._reloadAndAnnounce(ctx, src, body?.changed ?? [src.path]);\n ctx.logger.info('[MetadataPlugin] artifact reloaded via HMR POST', {\n path: src.path,\n reason: body?.reason,\n });\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] artifact reload failed', { error: e?.message });\n throw e;\n }\n }\n });\n\n // ── ADR-0008 PR-8 / PR-10e: server-side artifact-file watcher ──\n //\n // When running in local-file artifact mode (e.g. `os dev`\n // serving from `dist/objectstack.json`), watch the\n // artifact path directly so the server reloads on\n // recompile WITHOUT requiring the CLI to ping the HMR\n // POST endpoint. The POST route stays available for\n // external trigger sources (cloud webhook, git hook,\n // ad-hoc curl) but is no longer the only signal.\n //\n // Gated on `artifactWatch` (NOT `watch` — the latter\n // controls the source-file scanner which is redundant in\n // artifact mode). Default: on when artifactSource is\n // present, off otherwise.\n const src = this.options.artifactSource;\n const wantArtifactWatch = this.options.artifactWatch\n ?? (src?.mode === 'local-file');\n if (src?.mode === 'local-file' && wantArtifactWatch && !/^https?:\\/\\//i.test(src.path)) {\n try {\n const { watch: chokidarWatch } = await import('chokidar');\n const w = chokidarWatch(src.path, {\n ignoreInitial: true,\n awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 },\n persistent: true,\n // Use polling to avoid `fs.watch` exhausting the\n // process file-descriptor limit on macOS (chokidar\n // recursively wires watches on the parent\n // directory tree which can trip EMFILE on busy\n // dev hosts). 500ms polling is fast enough for\n // HMR (a recompile takes ~400ms anyway).\n usePolling: true,\n interval: 500,\n binaryInterval: 1000,\n });\n let pending = false;\n const reload = async () => {\n if (pending) return;\n pending = true;\n try {\n await this._reloadAndAnnounce(ctx, src, [src.path]);\n hub.broadcastReload('artifact-file-changed', [src.path]);\n ctx.logger.info('[MetadataPlugin] artifact auto-reloaded (file watcher)', {\n path: src.path,\n });\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] artifact auto-reload failed', { error: e?.message });\n } finally {\n pending = false;\n }\n };\n w.on('change', () => { void reload(); });\n w.on('add', () => { void reload(); });\n this.artifactWatcher = { close: () => w.close() };\n // eslint-disable-next-line no-console\n console.log('[MetadataPlugin] artifact file watcher attached', src.path);\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] artifact watcher failed to start', { error: e?.message });\n }\n }\n // eslint-disable-next-line no-console\n console.log('[MetadataPlugin] HMR endpoint registered at /api/v1/dev/metadata-events');\n } else {\n // eslint-disable-next-line no-console\n console.log('[MetadataPlugin] HTTP server with getRawApp() not available — skipping HMR endpoint');\n }\n } catch (e: any) {\n // eslint-disable-next-line no-console\n console.warn('[MetadataPlugin] Failed to register HMR endpoint', e?.message);\n }\n }\n\n stop = async (ctx: PluginContext) => {\n if (this.artifactWatcher) {\n try { await this.artifactWatcher.close(); } catch { /* noop */ }\n this.artifactWatcher = undefined;\n }\n try {\n await this.manager.dispose();\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] manager.dispose() failed', { error: e?.message });\n }\n const repo = this.repository as any;\n if (repo && typeof repo.close === 'function') {\n try { await repo.close(); } catch { /* noop */ }\n }\n this.repository = undefined;\n }\n\n /**\n * Fetch JSON content from a URL with configurable timeout.\n */\n private async _fetchJson(url: string, fetchTimeoutMs?: number): Promise<unknown> {\n const envTimeout = Number(process.env.OS_ARTIFACT_FETCH_TIMEOUT_MS);\n const timeoutMs = fetchTimeoutMs\n ?? (Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : undefined)\n ?? 60_000;\n const controller = new AbortController();\n const timer = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n try {\n const headers: Record<string, string> = { Accept: 'application/json, */*;q=0.5' };\n const res = await fetch(url, { redirect: 'follow', signal: controller.signal, headers });\n if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);\n const content = await res.text();\n return JSON.parse(content);\n } catch (e: any) {\n if (e?.name === 'AbortError') {\n throw new Error(\n `fetch timed out after ${timeoutMs}ms — set artifactSource.fetchTimeoutMs or OS_ARTIFACT_FETCH_TIMEOUT_MS to extend it (0 disables)`,\n );\n }\n throw e;\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n\n /**\n * Parse raw artifact JSON (envelope or bare definition) and register all\n * metadata items into the MetadataManager.\n *\n * Registers with `{ notify: false }` — one announcement per artifact, not\n * one per item. Both callers cover the whole set already: the boot load\n * runs before consumers have cached anything, and the reload path\n * (`_reloadAndAnnounce`) fires `metadata:reloaded` carrying the parsed\n * artifact once the ingest is complete. Announcing here too would emit N\n * duplicate events per reload, each racing the batch that is still\n * landing.\n */\n private async _parseAndRegisterArtifact(ctx: PluginContext, raw: unknown, label: string): Promise<number> {\n const { EnvironmentArtifactSchema } = await import('@objectstack/spec/cloud');\n const { ObjectStackDefinitionSchema } = await import('@objectstack/spec');\n\n let metadata: Record<string, unknown[]>;\n\n const obj = raw as any;\n if (obj?.schemaVersion && obj?.commitId && obj?.metadata !== undefined) {\n const artifact = EnvironmentArtifactSchema.parse(obj);\n metadata = artifact.metadata as Record<string, unknown[]>;\n } else if (obj?.success && obj?.data?.metadata) {\n // Unwrap cloud API envelope: { success: true, data: { metadata: {...} } }\n const artifact = EnvironmentArtifactSchema.parse(obj.data);\n metadata = artifact.metadata as Record<string, unknown[]>;\n } else {\n const def = ObjectStackDefinitionSchema.parse(obj);\n const canonical = JSON.stringify(def, Object.keys(def).sort());\n const checksum = createHash('sha256').update(canonical).digest('hex');\n const environmentId = this.options.environmentId ?? 'proj_local';\n EnvironmentArtifactSchema.parse({\n schemaVersion: '0.1',\n environmentId,\n commitId: 'local-dev',\n checksum,\n metadata: def,\n });\n metadata = def as Record<string, unknown[]>;\n }\n\n this.lastParsedMetadata = metadata;\n\n const memLoader = new MemoryLoader();\n const manifestPackageId =\n (metadata as any)?.manifest?.id ?? (metadata as any)?.id ?? undefined;\n const manifestVersion =\n (metadata as any)?.manifest?.version ?? (metadata as any)?.version ?? undefined;\n\n let totalRegistered = 0;\n for (const [field, metaType] of Object.entries(ARTIFACT_FIELD_TO_TYPE)) {\n const items = (metadata as any)[field];\n if (!Array.isArray(items) || items.length === 0) continue;\n for (const item of items) {\n // Expand aggregated view containers into independent ViewItems\n // (\"Object has-many View\"), while still registering the\n // container under the bare <object> key for backward-compatible\n // reads. Already-independent ViewItems carry a top-level `name`\n // and fall through to the normal path below.\n if (metaType === 'view' && isAggregatedViewContainer(item)) {\n const viewObject =\n (item as any)?.list?.data?.object\n ?? (item as any)?.form?.data?.object;\n if (!viewObject) continue;\n applyProtection(item as any, {\n packageId: manifestPackageId,\n packageVersion: manifestVersion,\n });\n await memLoader.save('view', viewObject, item);\n await this.manager.register('view', viewObject, item, { notify: false });\n totalRegistered++;\n for (const vi of expandViewContainer(viewObject, item)) {\n for (const w of vi._diagnostics?.warnings ?? []) {\n ctx.logger.warn(`[MetadataPlugin] View expansion warning for '${vi.name}': ${w.message}`);\n }\n applyProtection(vi as any, {\n packageId: manifestPackageId,\n packageVersion: manifestVersion,\n });\n await memLoader.save('view', vi.name, vi);\n await this.manager.register('view', vi.name, vi, { notify: false });\n totalRegistered++;\n }\n continue;\n }\n // Most metadata items carry a top-level `name`. The `View`\n // container (UI namespace) is an exception: it has no own\n // `name` — its identity is the target object, encoded under\n // `list.data.object` (or `form.data.object`). Mirror the\n // resolution used by `ObjectQL.SchemaRegistry` so that\n // artifact-loaded views land in `MetadataManager` under the\n // SAME key reads expect (`metadataService.get('view', <object>)`).\n // Without this, HMR pushes views into the registry only via\n // AppPlugin's `manifest.register`, which targets the\n // boot-only SchemaRegistry cache and is never refreshed on\n // file edits — leaving MetadataService empty for view/* and\n // forcing all reads to return the stale boot copy.\n let name = (item as any)?.name;\n if (!name) {\n if (metaType === 'view') {\n name =\n (item as any)?.list?.data?.object\n ?? (item as any)?.form?.data?.object;\n }\n }\n if (!name) continue;\n // ADR-0010 §3.7 — translate the author-facing\n // `protection` block into the private `_lock` envelope\n // and stamp package provenance in one call. Strips the\n // public block so it never lands in sys_metadata.\n applyProtection(item as any, {\n packageId: manifestPackageId,\n packageVersion: manifestVersion,\n });\n await memLoader.save(metaType, name, item);\n await this.manager.register(metaType, name, item, { notify: false });\n totalRegistered++;\n }\n }\n\n this.manager.registerLoader(memLoader);\n ctx.logger.info('[MetadataPlugin] Artifact metadata loaded', { source: label, totalRegistered });\n return totalRegistered;\n }\n\n /**\n * Reload the artifact from disk into the MetadataManager, then announce a\n * generic `metadata:reloaded` hook. Used by BOTH reload paths (the HMR POST\n * handler and the server-side artifact-file watcher) — but NOT the initial\n * boot load, which other plugins already consume directly.\n *\n * Runtime consumers that cached boot-time metadata re-sync on this signal.\n * The automation engine subscribes to re-bind flow triggers it pulled ONCE\n * at boot — notably scheduled jobs: without this, an edited\n * schedule-triggered flow keeps firing its pre-edit definition (old runAs /\n * schedule / logic) until a full process restart. A subscriber failure is\n * logged but never blocks the reload.\n */\n private async _reloadAndAnnounce(\n ctx: PluginContext,\n src: { path: string; fetchTimeoutMs?: number },\n changed: string[],\n ): Promise<void> {\n // Optional for the same reason boot is: an artifact deleted or moved\n // aside mid-run (a `dist/` clean between recompiles) must not take the\n // running server down — the watcher reloads it when it comes back.\n await this._loadFromLocalFile(ctx, src.path, src.fetchTimeoutMs, { optional: true });\n try {\n // `metadata` carries the freshly parsed artifact collections so\n // subscribers can consume the ones that never reach the\n // MetadataManager (seeds under `data` have no `name`). AppPlugin\n // uses it to load seeds for objects that appear mid-run.\n await ctx.trigger('metadata:reloaded', { changed, metadata: this.lastParsedMetadata });\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] metadata:reloaded subscriber failed', { error: e?.message });\n }\n }\n\n /**\n * @param opts.optional When true, a LOCAL artifact file that does not exist\n * is \"nothing compiled yet\" rather than a fault: log and return, leaving\n * the manager empty and the artifact watcher armed so the first\n * `os compile` hydrates the running server (#4085). Callers pass it for\n * the `eager` / `lazy` bootstrap modes — the development-platform paths,\n * where an app is optional. `artifact-only` (sealed runtime) does NOT:\n * there the artifact IS the deployment, so its absence must fail loudly\n * instead of silently serving an empty runtime. Only ENOENT is tolerated;\n * a present-but-unreadable artifact (malformed JSON, bad permissions) and\n * every remote-URL failure stay fatal.\n */\n private async _loadFromLocalFile(\n ctx: PluginContext,\n filePath: string,\n fetchTimeoutMs?: number,\n opts: { optional?: boolean } = {},\n ): Promise<void> {\n const isUrl = /^https?:\\/\\//i.test(filePath);\n ctx.logger.info(\n `[MetadataPlugin] Loading metadata from ${isUrl ? 'remote URL' : 'local artifact file'}`,\n { path: filePath },\n );\n\n let raw: unknown;\n try {\n if (isUrl) {\n raw = await this._fetchJson(filePath, fetchTimeoutMs);\n } else {\n const content = await readFile(filePath, 'utf8');\n raw = JSON.parse(content);\n }\n } catch (e: any) {\n if (opts.optional && !isUrl && e?.code === 'ENOENT') {\n ctx.logger.info(\n '[MetadataPlugin] no compiled artifact yet — starting with no artifact metadata',\n { path: filePath },\n );\n return;\n }\n throw new Error(`[MetadataPlugin] Cannot read artifact ${isUrl ? 'URL' : 'file'} at \"${filePath}\": ${e.message}`);\n }\n\n await this._parseAndRegisterArtifact(ctx, raw, filePath);\n }\n\n private async _loadFromFileSystem(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Loading metadata from file system...');\n\n const sortedTypes = [...DEFAULT_METADATA_TYPE_REGISTRY]\n .sort((a, b) => a.loadOrder - b.loadOrder);\n\n let totalLoaded = 0;\n for (const entry of sortedTypes) {\n try {\n const items = await this.manager.loadMany(entry.type, {\n recursive: true,\n patterns: entry.filePatterns,\n });\n\n if (items.length > 0) {\n for (const item of items) {\n const meta = item as any;\n if (meta?.name) {\n // Stamp package provenance when the host declared\n // its package id (see MetadataPluginOptions.packageId)\n // — same applyProtection call the artifact path\n // uses, so both load paths produce identical\n // _packageId/_provenance state. No-op when the\n // option is unset or the item is already stamped.\n applyProtection(meta, {\n packageId: this.options.packageId,\n });\n // Silent: boot-time priming, before any consumer\n // has cached a definition to go stale. Post-boot\n // edits to these files reach watchers through the\n // FileSystemRepository attached by onEnable.\n await this.manager.register(entry.type, meta.name, item, { notify: false });\n }\n }\n ctx.logger.info(`Loaded ${items.length} ${entry.type} from file system`);\n totalLoaded += items.length;\n }\n } catch (e: any) {\n ctx.logger.debug(`No ${entry.type} metadata found`, { error: e.message });\n }\n }\n\n ctx.logger.info('Metadata loading complete', {\n totalItems: totalLoaded,\n registeredTypes: sortedTypes.length,\n });\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Node Metadata Manager\n * \n * Extends MetadataManager with Filesystem capabilities (Watching, default loader)\n */\n\nimport * as path from 'node:path';\nimport { watch as chokidarWatch, type FSWatcher } from 'chokidar';\nimport type {\n MetadataWatchEvent,\n} from '@objectstack/spec/system';\nimport { FilesystemLoader } from './loaders/filesystem-loader.js';\nimport { MetadataManager, type MetadataManagerOptions } from './metadata-manager.js';\n\n/**\n * Node metadata manager class\n */\nexport class NodeMetadataManager extends MetadataManager {\n private watcher?: FSWatcher;\n\n constructor(config: MetadataManagerOptions) {\n super(config);\n\n // Initialize Default Filesystem Loader if no loaders provided\n // This logic replaces the removed logic from base class\n if (!config.loaders || config.loaders.length === 0) {\n const rootDir = config.rootDir || process.cwd();\n this.registerLoader(new FilesystemLoader(rootDir, this.serializers, this.logger));\n }\n\n // Start watching if enabled\n if (config.watch) {\n this.startWatching();\n }\n }\n\n /**\n * Stop all watching\n */\n async stopWatching(): Promise<void> {\n if (this.watcher) {\n await this.watcher.close();\n this.watcher = undefined;\n }\n // Call base cleanup if any\n }\n\n /**\n * Start watching for file changes\n */\n private startWatching(): void {\n const rootDir = this.config.rootDir || process.cwd();\n const { ignored = ['**/node_modules/**', '**/*.test.*'], persistent = true } =\n this.config.watchOptions || {};\n\n this.watcher = chokidarWatch(rootDir, {\n ignored,\n persistent,\n ignoreInitial: true,\n // Use polling to avoid `fs.watch` EMFILE on macOS / busy dev hosts.\n // Recursive watch over a project root would otherwise wire native\n // watches across the entire tree, easily exhausting the FD pool.\n usePolling: true,\n interval: 1000,\n binaryInterval: 2000,\n });\n\n this.watcher.on('add', async (filePath) => {\n await this.handleFileEvent('added', filePath);\n });\n\n this.watcher.on('change', async (filePath) => {\n await this.handleFileEvent('changed', filePath);\n });\n\n this.watcher.on('unlink', async (filePath) => {\n await this.handleFileEvent('deleted', filePath);\n });\n\n this.logger.info('File watcher started', { rootDir });\n }\n\n /**\n * Handle file change events\n */\n private async handleFileEvent(\n eventType: 'added' | 'changed' | 'deleted',\n filePath: string\n ): Promise<void> {\n const rootDir = this.config.rootDir || process.cwd();\n const relativePath = path.relative(rootDir, filePath);\n const parts = relativePath.split(path.sep);\n\n if (parts.length < 2) {\n return; // Not a metadata file\n }\n\n const type = parts[0];\n const fileName = parts[parts.length - 1];\n const name = path.basename(fileName, path.extname(fileName));\n\n // [#5218] Invalidate BEFORE announcing — and, since #5228, before reading\n // too, so that the read's verdict can never decide whether the caches are\n // dropped. A file event is a *foreign write* in the precise sense\n // {@link MetadataManager.invalidateForForeignWrite} means: it did not come\n // through this manager's write API, so — unlike `register()` /\n // `unregister()` — nothing has refreshed the caches on its behalf. The\n // read below is pure (it only walks the loaders and writes neither cache),\n // so before this call the handler left both `listCache` and `registry`\n // holding the pre-change state.\n //\n // Without it, editing `rootDir/view/x.json` left the two read surfaces\n // contradicting each other for up to LIST_CACHE_TTL_MS (30s): `get()` saw\n // the new file because it falls through to the FilesystemLoader, while\n // `list()` — REST `/api/v1/metadata/:type`, the Studio left rail,\n // `listViews()` — kept serving the pre-change set. Worse, the HMR/SSE\n // consumers woken by the event answer it by re-reading through `list()`,\n // so the wake-up handed back exactly the stale data it was announcing.\n // Same defect shape as #5109 (cluster peer) with a different trigger; this\n // reuses that fix's helper rather than re-deriving it.\n //\n // Ordering is the same discipline every other write path in the base class\n // keeps (`register` / `unregister` / `applyRepoEvent` / the cluster\n // subscriber all invalidate, then announce): a watcher must never be able\n // to observe the event and the pre-event cache at the same time.\n //\n // The registry entry goes too, not just the list cache. FS-loaded items\n // never enter the registry, so there is usually nothing to delete — but\n // when a same-named entry was previously written by `register()` /\n // `registerInMemory()` it SHADOWS the loader in both `get()` and `list()`,\n // and dropping the list cache alone would leave that stale copy answering\n // forever. Deleted, never pre-filled from `data`, per the helper's contract.\n this.invalidateForForeignWrite(type, name);\n\n // [#5228] `loadDiagnosed`, not `load` — and the difference is the whole\n // point of this branch. `load()` is `(await loadDiagnosed(...)).data`, and\n // `loadDiagnosed` (ADR-0110 D3) ABSORBS a loader throw: it records the\n // message in `errors[]` and answers `{ data: null, degraded: true }`.\n // `FilesystemLoader.load()` does throw on an unreadable / unparseable\n // file, but that throw dies inside `loadDiagnosed`, so the `try/catch`\n // this handler used to wrap `load()` in was unreachable for exactly the\n // failure it was written to catch. The handler announced `data: null`\n // instead, and its `logger.error` never printed once.\n //\n // `data: null` is the wire-shape of \"this metadata legitimately holds\n // nothing\" — so a file the loader could not read was announced as a file\n // the author had emptied. Those are the two facts ADR-0110 D3 exists to\n // keep apart (a miss and an outage mean opposite things), and this call\n // site was using the variant that throws the distinction away.\n //\n // So: split on `degraded`. An outage takes the road the dead `catch` meant\n // to take — log loudly, announce nothing. A clean miss (`data: null`, no\n // loader threw: the file is gone or legitimately empty) keeps its existing\n // semantics and is announced as before.\n //\n // Note what deliberately does NOT move with the early return: the\n // invalidation above. An unreadable file is still a real change to the\n // stored set — `loadMany` skips it, so `list()` genuinely answers\n // differently than it did — and #5218's contract is that a file event\n // always ages out the caches. That is also what keeps the `api` endpoint\n // index correct on this path without a broadcast: `invalidateListCache`\n // is the index's first invalidation seam (#5089), so suppressing the\n // `subscribe('api', …)` seam costs nothing.\n let data: unknown = undefined;\n if (eventType !== 'deleted') {\n const read = await this.loadDiagnosed(type, name, { useCache: false });\n if (read.degraded) {\n this.logger.error('Failed to load changed file', undefined, {\n filePath,\n metadataType: type,\n name,\n errors: read.errors,\n });\n return;\n }\n data = read.data;\n }\n\n const event: MetadataWatchEvent = {\n type: eventType,\n metadataType: type,\n name,\n path: filePath,\n data,\n timestamp: new Date().toISOString(),\n };\n\n this.notifyWatchers(type, event);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Filesystem Metadata Loader\n * \n * Loads metadata from the filesystem using glob patterns\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { glob } from 'glob';\nimport { createHash } from 'node:crypto';\nimport type {\n MetadataLoadOptions,\n MetadataLoadResult,\n MetadataStats,\n MetadataLoaderContract,\n MetadataFormat,\n MetadataSaveOptions,\n MetadataSaveResult,\n} from '@objectstack/spec/system';\nimport type { Logger } from '@objectstack/core';\nimport type { MetadataLoader } from './loader-interface.js';\nimport type { MetadataSerializer } from '../serializers/serializer-interface.js';\n\nexport class FilesystemLoader implements MetadataLoader {\n readonly contract: MetadataLoaderContract = {\n name: 'filesystem',\n protocol: 'file:',\n capabilities: {\n read: true,\n write: true,\n watch: true,\n list: true,\n },\n supportedFormats: ['json', 'yaml', 'typescript', 'javascript'],\n supportsWatch: true,\n supportsWrite: true,\n supportsCache: true,\n };\n\n private cache = new Map<string, { data: any; etag: string; timestamp: number }>();\n\n constructor(\n private rootDir: string,\n private serializers: Map<MetadataFormat, MetadataSerializer>,\n private logger?: Logger\n ) {}\n\n async load(\n type: string,\n name: string,\n options?: MetadataLoadOptions\n ): Promise<MetadataLoadResult> {\n const startTime = Date.now();\n const { validate: _validate = true, useCache = true, ifNoneMatch } = options || {};\n\n try {\n // Find the file\n const filePath = await this.findFile(type, name);\n\n if (!filePath) {\n return {\n data: null,\n fromCache: false,\n notModified: false,\n loadTime: Date.now() - startTime,\n };\n }\n\n // Get stats\n const stats = await this.stat(type, name);\n\n if (!stats) {\n return {\n data: null,\n fromCache: false,\n notModified: false,\n loadTime: Date.now() - startTime,\n };\n }\n\n // Check cache\n if (useCache && ifNoneMatch && stats.etag === ifNoneMatch) {\n return {\n data: null,\n fromCache: true,\n notModified: true,\n etag: stats.etag,\n stats,\n loadTime: Date.now() - startTime,\n };\n }\n\n // Check memory cache\n const cacheKey = `${type}:${name}`;\n if (useCache && this.cache.has(cacheKey)) {\n const cached = this.cache.get(cacheKey)!;\n if (cached.etag === stats.etag) {\n return {\n data: cached.data,\n fromCache: true,\n notModified: false,\n etag: stats.etag,\n stats,\n loadTime: Date.now() - startTime,\n };\n }\n }\n\n // Load and deserialize\n const content = await fs.readFile(filePath, 'utf-8');\n const serializer = this.getSerializer(stats.format!);\n\n if (!serializer) {\n throw new Error(`No serializer found for format: ${stats.format}`);\n }\n\n const data = serializer.deserialize(content);\n\n // Update cache\n if (useCache) {\n this.cache.set(cacheKey, {\n data,\n etag: stats.etag || '',\n timestamp: Date.now(),\n });\n }\n\n return {\n data,\n fromCache: false,\n notModified: false,\n etag: stats.etag,\n stats,\n loadTime: Date.now() - startTime,\n };\n } catch (error) {\n this.logger?.error('Failed to load metadata', undefined, {\n type,\n name,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n async loadMany<T = any>(\n type: string,\n options?: MetadataLoadOptions\n ): Promise<T[]> {\n const { patterns = ['**/*'], recursive: _recursive = true, limit } = options || {};\n\n const typeDir = path.join(this.rootDir, type);\n const items: T[] = [];\n\n try {\n // Build glob patterns\n const globPatterns = patterns.map(pattern =>\n path.join(typeDir, pattern)\n );\n\n for (const pattern of globPatterns) {\n const files = await glob(pattern, {\n ignore: ['**/node_modules/**', '**/*.test.*', '**/*.spec.*', '**/*[*]*'],\n nodir: true,\n });\n\n for (const file of files) {\n if (limit && items.length >= limit) {\n break;\n }\n\n try {\n const content = await fs.readFile(file, 'utf-8');\n const format = this.detectFormat(file);\n const serializer = this.getSerializer(format);\n\n if (serializer) {\n const data = serializer.deserialize<T>(content);\n items.push(data);\n }\n } catch (error) {\n this.logger?.warn('Failed to load file', {\n file,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n if (limit && items.length >= limit) {\n break;\n }\n }\n\n return items;\n } catch (error) {\n this.logger?.error('Failed to load many', undefined, {\n type,\n patterns,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n async exists(type: string, name: string): Promise<boolean> {\n const filePath = await this.findFile(type, name);\n return filePath !== null;\n }\n\n async stat(type: string, name: string): Promise<MetadataStats | null> {\n const filePath = await this.findFile(type, name);\n\n if (!filePath) {\n return null;\n }\n\n try {\n const stats = await fs.stat(filePath);\n const content = await fs.readFile(filePath, 'utf-8');\n const etag = this.generateETag(content);\n const format = this.detectFormat(filePath);\n\n return {\n size: stats.size,\n modifiedAt: stats.mtime.toISOString(),\n etag,\n format,\n path: filePath,\n };\n } catch (error) {\n this.logger?.error('Failed to stat file', undefined, {\n type,\n name,\n filePath,\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n }\n\n async list(type: string): Promise<string[]> {\n const typeDir = path.join(this.rootDir, type);\n\n try {\n const files = await glob('**/*', {\n cwd: typeDir,\n ignore: ['**/node_modules/**', '**/*.test.*', '**/*.spec.*'],\n nodir: true,\n });\n\n return files.map(file => {\n const ext = path.extname(file);\n const basename = path.basename(file, ext);\n return basename;\n });\n } catch (error) {\n this.logger?.error('Failed to list', undefined, {\n type,\n error: error instanceof Error ? error.message : String(error),\n });\n return [];\n }\n }\n\n async save(\n type: string,\n name: string,\n data: any,\n options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n const startTime = Date.now();\n const {\n format = 'typescript',\n prettify = true,\n indent = 2,\n sortKeys = false,\n backup = false,\n overwrite = true,\n atomic = true,\n path: customPath,\n } = options || {};\n\n try {\n // Get serializer\n const serializer = this.getSerializer(format);\n if (!serializer) {\n throw new Error(`No serializer found for format: ${format}`);\n }\n\n // Determine file path\n const typeDir = path.join(this.rootDir, type);\n const fileName = `${name}${serializer.getExtension()}`;\n const filePath = customPath || path.join(typeDir, fileName);\n\n // Check if file exists\n if (!overwrite) {\n try {\n await fs.access(filePath);\n throw new Error(`File already exists: ${filePath}`);\n } catch (error) {\n // File doesn't exist, continue\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n // Create directory if it doesn't exist\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n\n // Create backup if requested\n let backupPath: string | undefined;\n if (backup) {\n try {\n await fs.access(filePath);\n backupPath = `${filePath}.bak`;\n await fs.copyFile(filePath, backupPath);\n } catch {\n // File doesn't exist, no backup needed\n }\n }\n\n // Serialize data\n const content = serializer.serialize(data, {\n prettify,\n indent,\n sortKeys,\n });\n\n // Write to disk (atomic or direct)\n if (atomic) {\n const tempPath = `${filePath}.tmp`;\n await fs.writeFile(tempPath, content, 'utf-8');\n await fs.rename(tempPath, filePath);\n } else {\n await fs.writeFile(filePath, content, 'utf-8');\n }\n\n // Update cache logic if needed (e.g., invalidate or update)\n // For now, we rely on the watcher to pick up changes\n\n return {\n success: true,\n path: filePath,\n // format, // Not in schema\n size: Buffer.byteLength(content, 'utf-8'),\n backupPath,\n saveTime: Date.now() - startTime,\n };\n } catch (error) {\n this.logger?.error('Failed to save metadata', undefined, {\n type,\n name,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n /**\n * Find file for a given type and name\n */\n private async findFile(type: string, name: string): Promise<string | null> {\n const typeDir = path.join(this.rootDir, type);\n const extensions = ['.json', '.yaml', '.yml', '.ts', '.js'];\n\n for (const ext of extensions) {\n const filePath = path.join(typeDir, `${name}${ext}`);\n\n try {\n await fs.access(filePath);\n return filePath;\n } catch {\n // File doesn't exist, try next extension\n }\n }\n\n return null;\n }\n\n /**\n * Detect format from file extension\n */\n private detectFormat(filePath: string): MetadataFormat {\n const ext = path.extname(filePath).toLowerCase();\n\n switch (ext) {\n case '.json':\n return 'json';\n case '.yaml':\n case '.yml':\n return 'yaml';\n case '.ts':\n return 'typescript';\n case '.js':\n return 'javascript';\n default:\n return 'json'; // Default to JSON\n }\n }\n\n /**\n * Get serializer for format\n */\n private getSerializer(format: MetadataFormat): MetadataSerializer | undefined {\n return this.serializers.get(format);\n }\n\n /**\n * Generate ETag for content\n * Uses SHA-256 hash truncated to 32 characters for reasonable collision resistance\n * while keeping ETag headers compact (full 64-char hash is overkill for this use case)\n */\n private generateETag(content: string): string {\n const hash = createHash('sha256').update(content).digest('hex').substring(0, 32);\n return `\"${hash}\"`;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Memory Metadata Loader\n * \n * Stores metadata in memory only. Changes are lost when process restarts.\n * Useful for testing, temporary overrides, or \"dirty\" edits.\n */\n\nimport type {\n MetadataLoadOptions,\n MetadataLoadResult,\n MetadataStats,\n MetadataLoaderContract,\n MetadataSaveOptions,\n MetadataSaveResult,\n} from '@objectstack/spec/system';\nimport type { MetadataLoader } from './loader-interface.js';\n\nexport class MemoryLoader implements MetadataLoader {\n readonly contract: MetadataLoaderContract = {\n name: 'memory',\n protocol: 'memory:',\n capabilities: {\n read: true,\n write: true,\n watch: false,\n list: true,\n },\n };\n\n // Storage: Type -> Name -> Data\n private storage = new Map<string, Map<string, any>>();\n\n async load(\n type: string,\n name: string,\n _options?: MetadataLoadOptions\n ): Promise<MetadataLoadResult> {\n const typeStore = this.storage.get(type);\n const data = typeStore?.get(name);\n\n if (data) {\n return {\n data,\n source: 'memory',\n format: 'json',\n loadTime: 0,\n };\n }\n\n return { data: null };\n }\n\n async loadMany<T = any>(\n type: string,\n _options?: MetadataLoadOptions\n ): Promise<T[]> {\n const typeStore = this.storage.get(type);\n if (!typeStore) return [];\n return Array.from(typeStore.values()) as T[];\n }\n\n async exists(type: string, name: string): Promise<boolean> {\n return this.storage.get(type)?.has(name) ?? false;\n }\n\n async stat(type: string, name: string): Promise<MetadataStats | null> {\n if (await this.exists(type, name)) {\n return {\n size: 0, // In-memory\n mtime: new Date().toISOString(),\n format: 'json',\n };\n }\n return null;\n }\n\n async list(type: string): Promise<string[]> {\n const typeStore = this.storage.get(type);\n if (!typeStore) return [];\n return Array.from(typeStore.keys());\n }\n\n async save(\n type: string,\n name: string,\n data: any,\n _options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n if (!this.storage.has(type)) {\n this.storage.set(type, new Map());\n }\n\n this.storage.get(type)!.set(name, data);\n\n return {\n success: true,\n path: `memory://${type}/${name}`,\n saveTime: 0,\n };\n }\n\n /**\n * Delete a metadata item from memory storage\n */\n async delete(type: string, name: string): Promise<void> {\n const typeStore = this.storage.get(type);\n if (typeStore) {\n typeStore.delete(name);\n if (typeStore.size === 0) {\n this.storage.delete(type);\n }\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Remote Metadata Loader\n * \n * Loads metadata from an HTTP API.\n * This loader is stateless and delegates storage to the remote server.\n */\n\nimport type {\n MetadataLoadOptions,\n MetadataLoadResult,\n MetadataStats,\n MetadataLoaderContract,\n MetadataSaveOptions,\n MetadataSaveResult,\n} from '@objectstack/spec/system';\nimport type { MetadataLoader } from './loader-interface.js';\n\nexport class RemoteLoader implements MetadataLoader {\n readonly contract: MetadataLoaderContract = {\n name: 'remote',\n protocol: 'http:',\n capabilities: {\n read: true,\n write: true,\n watch: false, // Could implement SSE/WebSocket in future\n list: true,\n },\n };\n\n constructor(private baseUrl: string, private authToken?: string) {}\n\n private get headers() {\n return {\n 'Content-Type': 'application/json',\n ...(this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}),\n };\n }\n\n async load(\n type: string,\n name: string,\n _options?: MetadataLoadOptions\n ): Promise<MetadataLoadResult> {\n try {\n const response = await fetch(`${this.baseUrl}/${type}/${name}`, {\n method: 'GET',\n headers: this.headers,\n });\n\n if (response.status === 404) {\n return { data: null };\n }\n\n if (!response.ok) {\n throw new Error(`Remote load failed: ${response.statusText}`);\n }\n\n const data = await response.json();\n return {\n data,\n source: this.baseUrl,\n format: 'json',\n loadTime: 0, \n };\n } catch (error) {\n console.error(`RemoteLoader error loading ${type}/${name}`, error);\n throw error;\n }\n }\n\n async loadMany<T = any>(\n type: string,\n _options?: MetadataLoadOptions\n ): Promise<T[]> {\n const response = await fetch(`${this.baseUrl}/${type}`, {\n method: 'GET',\n headers: this.headers,\n });\n\n if (!response.ok) {\n return [];\n }\n\n return (await response.json()) as T[];\n }\n\n async exists(type: string, name: string): Promise<boolean> {\n const response = await fetch(`${this.baseUrl}/${type}/${name}`, {\n method: 'HEAD',\n headers: this.headers,\n });\n return response.ok;\n }\n\n async stat(type: string, name: string): Promise<MetadataStats | null> {\n // Basic implementation using HEAD\n const response = await fetch(`${this.baseUrl}/${type}/${name}`, {\n method: 'HEAD',\n headers: this.headers,\n });\n \n if (!response.ok) return null;\n\n return {\n size: Number(response.headers.get('content-length') || 0),\n mtime: new Date(response.headers.get('last-modified') || Date.now()).toISOString(),\n format: 'json',\n };\n }\n\n async list(type: string): Promise<string[]> {\n const items = await this.loadMany<{ name: string }>(type);\n return items.map(i => i.name);\n }\n\n async save(\n type: string,\n name: string,\n data: any,\n _options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n const response = await fetch(`${this.baseUrl}/${type}/${name}`, {\n method: 'PUT',\n headers: this.headers,\n body: JSON.stringify(data),\n });\n\n if (!response.ok) {\n throw new Error(`Remote save failed: ${response.statusText}`);\n }\n\n return {\n success: true,\n path: `${this.baseUrl}/${type}/${name}`,\n saveTime: 0,\n };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata History Retention and Cleanup\n *\n * Manages automatic cleanup of old history records based on retention policies.\n * Supports both age-based and count-based retention strategies.\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\nimport type { MetadataHistoryRetentionPolicy } from '@objectstack/spec/system';\nimport { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel';\nimport type { DatabaseLoader } from '../loaders/database-loader.js';\n\n/**\n * Types whose runtime transaction rows pin a specific historical body\n * (ADR-0009 `executionPinned`). History rows for these types are\n * **never** garbage-collected — the `getByHash()` resolution path\n * relies on them remaining queryable for the lifetime of any open\n * execution.\n */\nfunction executionPinnedTypes(): string[] {\n return DEFAULT_METADATA_TYPE_REGISTRY\n .filter((entry) => entry.executionPinned)\n .map((entry) => entry.type);\n}\n\n/**\n * History Cleanup Manager\n *\n * Handles automatic cleanup of metadata history records based on\n * configured retention policies.\n */\nexport class HistoryCleanupManager {\n private policy: MetadataHistoryRetentionPolicy;\n private dbLoader: DatabaseLoader;\n private cleanupTimer?: NodeJS.Timeout;\n\n constructor(policy: MetadataHistoryRetentionPolicy, dbLoader: DatabaseLoader) {\n this.policy = policy;\n this.dbLoader = dbLoader;\n }\n\n /**\n * Start automatic cleanup if enabled in the policy.\n */\n start(): void {\n if (!this.policy.autoCleanup) {\n return;\n }\n\n const intervalMs = (this.policy.cleanupIntervalHours ?? 24) * 60 * 60 * 1000;\n\n // Run cleanup immediately on start\n void this.runCleanup();\n\n // Schedule periodic cleanup\n this.cleanupTimer = setInterval(() => {\n void this.runCleanup();\n }, intervalMs);\n }\n\n /**\n * Stop automatic cleanup.\n */\n stop(): void {\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = undefined;\n }\n }\n\n /**\n * Run cleanup based on the retention policy.\n * Removes history records that exceed the configured limits.\n */\n async runCleanup(): Promise<{ deleted: number; errors: number }> {\n const driver = (this.dbLoader as any).driver as IDataDriver;\n const historyTableName = (this.dbLoader as any).historyTableName as string;\n const organizationId = (this.dbLoader as any).organizationId as string | undefined;\n // `environmentId` was removed from the metadata layer (ADR-0008 §0\n // amendment). Cleanup is now scoped by `organization_id` only.\n\n let deleted = 0;\n let errors = 0;\n\n // ADR-0009: collect executionPinned types — these are GC-exempt.\n const pinnedTypes = executionPinnedTypes();\n const isPinned = (t: string | undefined): boolean =>\n !!t && pinnedTypes.includes(t);\n\n try {\n // Age-based cleanup\n if (this.policy.maxAgeDays) {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - this.policy.maxAgeDays);\n const cutoffISO = cutoffDate.toISOString();\n\n const filter: Record<string, unknown> = {\n recorded_at: { $lt: cutoffISO },\n };\n\n if (organizationId) {\n filter.organization_id = organizationId;\n }\n\n if (pinnedTypes.length > 0) {\n // Exclude executionPinned rows from bulk age delete.\n filter.type = { $nin: pinnedTypes };\n }\n\n try {\n const result = await this.bulkDeleteByFilter(driver, historyTableName, filter);\n deleted += result.deleted;\n errors += result.errors;\n } catch {\n errors++;\n }\n }\n\n // Count-based cleanup per metadata item\n if (this.policy.maxVersions) {\n try {\n // Get all unique metadata items keyed by (type, name)\n const baseWhere: Record<string, unknown> = {};\n if (organizationId) baseWhere.organization_id = organizationId;\n\n const metaItems = await driver.find(historyTableName, {\n where: baseWhere,\n fields: ['type', 'name'],\n });\n\n const uniqueKeys = new Set<string>();\n for (const record of metaItems) {\n const t = record.type as string | undefined;\n const n = record.name as string | undefined;\n if (t && n && !isPinned(t)) {\n uniqueKeys.add(`${t}\\x1f${n}`);\n }\n }\n\n // For each metadata item, keep only the latest N versions\n for (const key of uniqueKeys) {\n const [type, name] = key.split('\\x1f');\n const filter: Record<string, unknown> = { type, name, ...baseWhere };\n\n try {\n // Fetch only the IDs of records beyond the retention limit (oldest first)\n const historyRecords = await driver.find(historyTableName, {\n where: filter,\n orderBy: [{ field: 'version', order: 'desc' as const }],\n fields: ['id'],\n });\n\n if (historyRecords.length > this.policy.maxVersions) {\n const toDelete = historyRecords.slice(this.policy.maxVersions);\n const ids = toDelete.map(r => r.id as string).filter(Boolean);\n const result = await this.bulkDeleteByIds(driver, historyTableName, ids);\n deleted += result.deleted;\n errors += result.errors;\n }\n } catch {\n errors++;\n }\n }\n } catch {\n errors++;\n }\n }\n } catch (error) {\n console.error('History cleanup failed:', error);\n errors++;\n }\n\n return { deleted, errors };\n }\n\n /**\n * Delete records matching a filter using the most efficient method available on the driver.\n */\n private async bulkDeleteByFilter(\n driver: IDataDriver,\n table: string,\n filter: Record<string, unknown>\n ): Promise<{ deleted: number; errors: number }> {\n const driverAny = driver as any;\n if (typeof driverAny.deleteMany === 'function') {\n const count = await driverAny.deleteMany(table, filter);\n return { deleted: typeof count === 'number' ? count : 0, errors: 0 };\n }\n\n // Fallback: fetch IDs then delete\n const records = await driver.find(table, { where: filter, fields: ['id'] });\n const ids = records.map((r: Record<string, unknown>) => r.id as string).filter(Boolean);\n return this.bulkDeleteByIds(driver, table, ids);\n }\n\n /**\n * Delete records by IDs using bulkDelete when available, otherwise one-by-one.\n */\n private async bulkDeleteByIds(\n driver: IDataDriver,\n table: string,\n ids: string[]\n ): Promise<{ deleted: number; errors: number }> {\n if (ids.length === 0) return { deleted: 0, errors: 0 };\n\n const driverAny = driver as any;\n if (typeof driverAny.bulkDelete === 'function') {\n const result = await driverAny.bulkDelete(table, ids);\n return {\n deleted: typeof result === 'number' ? result : ids.length,\n errors: 0,\n };\n }\n\n // Fallback: sequential deletes\n let deleted = 0;\n let errors = 0;\n for (const id of ids) {\n try {\n await driver.delete(table, id);\n deleted++;\n } catch {\n errors++;\n }\n }\n return { deleted, errors };\n }\n\n /**\n * Get cleanup statistics without actually deleting anything.\n * Useful for previewing what would be cleaned up.\n */\n async getCleanupStats(): Promise<{\n recordsByAge: number;\n recordsByCount: number;\n total: number;\n }> {\n const driver = (this.dbLoader as any).driver as IDataDriver;\n const historyTableName = (this.dbLoader as any).historyTableName as string;\n const organizationId = (this.dbLoader as any).organizationId as string | undefined;\n\n let recordsByAge = 0;\n let recordsByCount = 0;\n\n // ADR-0009: executionPinned types are excluded from GC.\n const pinnedTypes = executionPinnedTypes();\n const isPinned = (t: string | undefined): boolean =>\n !!t && pinnedTypes.includes(t);\n\n try {\n const baseWhere: Record<string, unknown> = {};\n if (organizationId) baseWhere.organization_id = organizationId;\n\n // Count records that would be deleted by age\n if (this.policy.maxAgeDays) {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - this.policy.maxAgeDays);\n const cutoffISO = cutoffDate.toISOString();\n\n const filter: Record<string, unknown> = {\n recorded_at: { $lt: cutoffISO },\n ...baseWhere,\n };\n if (pinnedTypes.length > 0) {\n filter.type = { $nin: pinnedTypes };\n }\n\n recordsByAge = await driver.count(historyTableName, {\n where: filter,\n });\n }\n\n // Count records that would be deleted by version limit\n if (this.policy.maxVersions) {\n const metaItems = await driver.find(historyTableName, {\n where: baseWhere,\n fields: ['type', 'name'],\n });\n\n const uniqueKeys = new Set<string>();\n for (const record of metaItems) {\n const t = record.type as string | undefined;\n const n = record.name as string | undefined;\n if (t && n && !isPinned(t)) {\n uniqueKeys.add(`${t}\\x1f${n}`);\n }\n }\n\n for (const key of uniqueKeys) {\n const [type, name] = key.split('\\x1f');\n const filter: Record<string, unknown> = { type, name, ...baseWhere };\n\n const count = await driver.count(historyTableName, {\n where: filter,\n });\n\n if (count > this.policy.maxVersions) {\n recordsByCount += count - this.policy.maxVersions;\n }\n }\n }\n } catch (error) {\n console.error('Failed to get cleanup stats:', error);\n }\n\n // Return separate counts. The total is an upper-bound estimate: it may overcount\n // records that qualify under both policies (age and count). Use recordsByAge and\n // recordsByCount individually for precise breakdowns.\n return {\n recordsByAge,\n recordsByCount,\n total: recordsByAge + recordsByCount,\n };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './executor.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport * as System from '@objectstack/spec/system';\nimport { ISchemaDriver } from '@objectstack/spec/contracts';\n\nexport class MigrationExecutor {\n constructor(private driver: ISchemaDriver) {}\n\n async executeChangeSet(changeSet: System.ChangeSetParsed): Promise<void> {\n console.log(`Executing ChangeSet: ${changeSet.name} (${changeSet.id})`);\n \n for (const op of changeSet.operations) {\n try {\n await this.executeOperation(op);\n } catch (e) {\n console.error(`Failed to execute operation ${op.type}:`, e);\n throw e;\n }\n }\n }\n\n private async executeOperation(op: System.MigrationOperationParsed): Promise<void> {\n switch (op.type) {\n case 'create_object':\n console.log(` > Create Object: ${op.object.name}`);\n await this.driver.createCollection(op.object.name, op.object);\n break;\n case 'add_field':\n console.log(` > Add Field: ${op.objectName}.${op.fieldName}`);\n await this.driver.addColumn(op.objectName, op.fieldName, op.field);\n break;\n case 'remove_field':\n console.log(` > Remove Field: ${op.objectName}.${op.fieldName}`);\n await this.driver.dropColumn(op.objectName, op.fieldName);\n break;\n case 'delete_object':\n console.log(` > Delete Object: ${op.objectName}`);\n await this.driver.dropCollection(op.objectName);\n break;\n case 'execute_sql':\n console.log(` > Execute SQL`);\n await this.driver.executeRaw(op.sql);\n break;\n case 'modify_field':\n console.warn(` ! Modify Field: ${op.objectName}.${op.fieldName} (Not fully implemented)`);\n break;\n case 'rename_object':\n console.warn(` ! Rename Object: ${op.oldName} -> ${op.newName} (Not fully implemented)`);\n break;\n default:\n throw new Error(`Unknown operation type`);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAoEO,SAAS,0BACd,KACA,SACA,UAA6B,CAAC,GACd;AAChB,QAAM,YAAY,QAAQ,QAAQ;AAIlC,QAAM,YAAY,oBAAI,IAAc;AACpC,QAAM,YAAY,CAAC,QAAwB;AACzC,eAAW,KAAK,WAAW;AACzB,UAAI;AAAE,UAAE,GAAG;AAAA,MAAG,QAAQ;AAAA,MAA0D;AAAA,IAClF;AAAA,EACF;AAKA,MAAI,kBAAkB;AACtB,QAAM,iBAAiB,YAAY;AACjC,QAAI,gBAAiB;AACrB,UAAM,MAAM;AACZ,QAAI,OAAO,IAAI,cAAc,YAAY;AACvC,wBAAkB;AAClB;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,QAAQ,mBAAmB;AAC/C,eAAW,QAAQ,OAAO;AACxB,UAAI,UAAU,MAAM,CAAC,QAAa;AAChC,cAAM,KAAK,OAAO,IAAI,cAAc,WAChC,KAAK,MAAM,IAAI,SAAS,IACvB,IAAI,aAAa,KAAK,IAAI;AAC/B,kBAAU;AAAA,UACR,MAAM;AAAA,UACN,MAAM,IAAI,QAAQ;AAAA,UAClB,cAAc,IAAI,gBAAgB;AAAA,UAClC,MAAM,IAAI,QAAQ;AAAA,UAClB,MAAM,IAAI;AAAA,UACV,WAAW,OAAO,SAAS,EAAE,IAAI,KAAK,KAAK,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,UAK/C,GAAI,OAAO,IAAI,QAAQ,WAAW,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC;AAAA,QACxD,CAAmB;AAAA,MACrB,CAAC;AAAA,IACH;AACA,sBAAkB;AAAA,EACpB;AAGA,iBAAe,EAAE,MAAM,MAAM;AAAA,EAAa,CAAC;AAE3C,MAAI,eAAiG;AAGrG,MAAI,IAAI,WAAW,OAAO,MAAW;AAEnC,UAAM,eAAe,EAAE,MAAM,MAAM;AAAA,IAAa,CAAC;AACjD,UAAM,QAAQ,MAAM,QAAQ,mBAAmB,EAAE,MAAM,MAAM,CAAC,CAAa;AAE3E,UAAM,SAAS,IAAI,eAA2B;AAAA,MAC5C,MAAM,MAAM,YAAY;AACtB,cAAM,MAAM,IAAI,YAAY;AAC5B,YAAI,SAAS;AAEb,cAAM,cAAc,CAAC,UAAkB;AACrC,cAAI,OAAQ;AACZ,cAAI;AAAE,uBAAW,QAAQ,IAAI,OAAO,KAAK,CAAC;AAAA,UAAG,QACvC;AAAE,qBAAS;AAAA,UAAM;AAAA,QACzB;AAEA,cAAM,WAAqB,CAAC,QAAQ;AAClC,cAAI,OAAQ;AACZ,gBAAM,YAAY,IAAI,SAAS,WAAW,WAAW;AACrD,sBAAY,UAAU,SAAS;AAAA,QAAW,KAAK,UAAU,GAAG,CAAC;AAAA;AAAA,CAAM;AAAA,QACrE;AACA,kBAAU,IAAI,QAAQ;AAEtB,oBAAY;AAAA,QAAuB,KAAK,UAAU,EAAE,OAAO,WAAW,KAAK,IAAI,EAAE,CAAC,CAAC;AAAA;AAAA,CAAM;AAEzF,cAAM,YAAY,YAAY,MAAM;AAClC,sBAAY,UAAU,KAAK,IAAI,CAAC;AAAA;AAAA,CAAM;AAAA,QACxC,GAAG,IAAM;AAET,cAAM,UAAU,MAAM;AACpB,cAAI,OAAQ;AACZ,mBAAS;AACT,wBAAc,SAAS;AACvB,oBAAU,OAAO,QAAQ;AACzB,cAAI;AAAE,uBAAW,MAAM;AAAA,UAAG,QAAQ;AAAA,UAAa;AAAA,QACjD;AAEA,cAAM,SAAkC,EAAE,KAAK,KAAK;AACpD,YAAI,QAAQ;AACV,cAAI,OAAO,QAAS,SAAQ;AAAA,cACvB,QAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,IAAI,SAAS,QAAQ;AAAA,MAC1B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,cAAc;AAAA,QACd,qBAAqB;AAAA,MACvB;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAKD,MAAI,KAAK,WAAW,OAAO,MAAW;AACpC,QAAI,OAAgD,CAAC;AACrD,QAAI;AAEF,YAAM,KAAK,EAAE,KAAK,SAAS,cAAc,KAAK;AAC9C,UAAI,OAAO,EAAE,KAAK,SAAS,cAAc,GAAG,SAAS,MAAM,GAAG;AAC5D,eAAO,MAAM,EAAE,IAAI,KAAK;AAAA,MAC1B;AAAA,IACF,QAAQ;AAAA,IAAgC;AAExC,QAAI;AACF,UAAI,aAAc,OAAM,aAAa,IAAI;AAAA,IAC3C,SAAS,GAAQ;AACf,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,IAAI,OAAO,OAAO,GAAG,WAAW,wBAAwB,CAAC;AAAA,QAC1E,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAEA,UAAM,SAAS,KAAK,UAAU;AAC9B,cAAU;AAAA,MACR,MAAM;AAAA,MACN;AAAA,MACA,SAAS,KAAK;AAAA,MACd,WAAW,KAAK,IAAI;AAAA,IACtB,CAAC;AACD,WAAO,IAAI;AAAA,MACT,KAAK,UAAU,EAAE,IAAI,MAAM,WAAW,UAAU,MAAM,OAAO,CAAC;AAAA,MAC9D,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,IACjE;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,gBAAgB,QAAQ,SAAS;AAC/B,gBAAU,EAAE,MAAM,UAAU,QAAQ,SAAS,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,IACtE;AAAA,IACA,gBAAgB,IAAI;AAAE,qBAAe;AAAA,IAAI;AAAA,IACzC,eAAe,MAAM,UAAU;AAAA,EACjC;AACF;AA/NA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC6CA,oBAAuC;AACvC,IAAAA,cAIO;AAGP,IAAAA,cAIO;AACP,kBAA0C;;;AC9CnC,IAAM,iBAAN,MAAmD;AAAA,EACxD,UAAa,MAAS,SAAoC;AACxD,UAAM,EAAE,WAAW,MAAM,SAAS,GAAG,WAAW,MAAM,IAAI,WAAW,CAAC;AAEtE,QAAI,UAAU;AAEZ,YAAM,SAAS,KAAK,eAAe,IAAI;AACvC,aAAO,WACH,KAAK,UAAU,QAAQ,MAAM,MAAM,IACnC,KAAK,UAAU,MAAM;AAAA,IAC3B;AAEA,WAAO,WACH,KAAK,UAAU,MAAM,MAAM,MAAM,IACjC,KAAK,UAAU,IAAI;AAAA,EACzB;AAAA,EAEA,YAAe,SAAiB,QAAyB;AACvD,UAAM,SAAS,KAAK,MAAM,OAAO;AAEjC,QAAI,QAAQ;AACV,aAAO,OAAO,MAAM,MAAM;AAAA,IAC5B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,eAAuB;AACrB,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,QAAiC;AACzC,WAAO,WAAW;AAAA,EACpB;AAAA,EAEA,YAA4B;AAC1B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,KAAe;AACpC,QAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;AAC3C,aAAO;AAAA,IACT;AAEA,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,aAAO,IAAI,IAAI,UAAQ,KAAK,eAAe,IAAI,CAAC;AAAA,IAClD;AAEA,UAAM,SAA8B,CAAC;AACrC,UAAM,OAAO,OAAO,KAAK,GAAG,EAAE,KAAK;AAEnC,eAAW,OAAO,MAAM;AACtB,aAAO,GAAG,IAAI,KAAK,eAAe,IAAI,GAAG,CAAC;AAAA,IAC5C;AAEA,WAAO;AAAA,EACT;AACF;;;AChEA,WAAsB;AAKf,IAAM,iBAAN,MAAmD;AAAA,EACxD,UAAa,MAAS,SAAoC;AACxD,UAAM,EAAE,SAAS,GAAG,WAAW,MAAM,IAAI,WAAW,CAAC;AAErD,WAAY,UAAK,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA,WAAW;AAAA;AAAA,MACX,QAAQ;AAAA;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEA,YAAe,SAAiB,QAAyB;AAGvD,UAAM,SAAc,UAAK,SAAS,EAAE,QAAa,iBAAY,CAAC;AAE9D,QAAI,QAAQ;AACV,aAAO,OAAO,MAAM,MAAM;AAAA,IAC5B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,eAAuB;AACrB,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,QAAiC;AACzC,WAAO,WAAW;AAAA,EACpB;AAAA,EAEA,YAA4B;AAC1B,WAAO;AAAA,EACT;AACF;;;ACpCO,IAAM,uBAAN,MAAyD;AAAA,EAC9D,YAAoB,SAAsC,cAAc;AAApD;AAAA,EAAqD;AAAA,EAEzE,UAAa,MAAS,SAAoC;AACxD,UAAM,EAAE,WAAW,MAAM,SAAS,EAAE,IAAI,WAAW,CAAC;AAEpD,UAAM,UAAU,KAAK,UAAU,MAAM,MAAM,WAAW,SAAS,CAAC;AAEhE,QAAI,KAAK,WAAW,cAAc;AAChC,aAAO;AAAA;AAAA,yCACqC,OAAO;AAAA;AAAA;AAAA;AAAA,IAErD,OAAO;AACL,aAAO,2BAA2B,OAAO;AAAA;AAAA;AAAA;AAAA,IAE3C;AAAA,EACF;AAAA,EAEA,YAAe,SAAiB,QAAyB;AAOvD,QAAI,cAAc,QAAQ,QAAQ,cAAc;AAChD,QAAI,gBAAgB,IAAI;AAEtB,oBAAc,QAAQ,QAAQ,gBAAgB;AAAA,IAChD;AAEA,QAAI,gBAAgB,IAAI;AACtB,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAGA,UAAM,aAAa,QAAQ,QAAQ,KAAK,WAAW;AACnD,QAAI,eAAe,IAAI;AACrB,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AAIA,QAAI,aAAa;AACjB,QAAI,WAAW;AACf,QAAI,WAAW;AACf,QAAI,aAAa;AAEjB,aAAS,IAAI,YAAY,IAAI,QAAQ,QAAQ,KAAK;AAChD,YAAM,OAAO,QAAQ,CAAC;AACtB,YAAM,WAAW,IAAI,IAAI,QAAQ,IAAI,CAAC,IAAI;AAG1C,WAAK,SAAS,OAAO,SAAS,QAAQ,aAAa,MAAM;AACvD,YAAI,CAAC,UAAU;AACb,qBAAW;AACX,uBAAa;AAAA,QACf,WAAW,SAAS,YAAY;AAC9B,qBAAW;AACX,uBAAa;AAAA,QACf;AAAA,MACF;AAGA,UAAI,CAAC,UAAU;AACb,YAAI,SAAS,IAAK;AAClB,YAAI,SAAS,KAAK;AAChB;AACA,cAAI,eAAe,GAAG;AACpB,uBAAW;AACX;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,aAAa,IAAI;AACnB,YAAM,IAAI,MAAM,0DAA0D;AAAA,IAC5E;AAGA,UAAM,gBAAgB,QAAQ,UAAU,YAAY,WAAW,CAAC;AAEhE,QAAI;AAEF,YAAM,SAAS,KAAK,MAAM,aAAa;AAEvC,UAAI,QAAQ;AACV,eAAO,OAAO,MAAM,MAAM;AAAA,MAC5B;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,2CAA2C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAEnG;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eAAuB;AACrB,WAAO,KAAK,WAAW,eAAe,QAAQ;AAAA,EAChD;AAAA,EAEA,UAAU,QAAiC;AACzC,WAAO,WAAW,gBAAgB,WAAW;AAAA,EAC/C;AAAA,EAEA,YAA4B;AAC1B,WAAO,KAAK;AAAA,EACd;AACF;;;ACzGA,2BAA4D;AAC5D,kBAA6C;AAC7C,oBAAmC;;;ACNnC,eAAsB,kBAAkB,UAAoC;AAE1E,QAAM,aAAa,cAAc,QAAQ;AACzC,QAAM,aAAa,KAAK,UAAU,UAAU;AAG5C,MAAI,OAAO,WAAW,WAAW,eAAe,WAAW,OAAO,QAAQ;AACxE,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,OAAO,QAAQ,OAAO,UAAU;AACtC,UAAM,aAAa,MAAM,WAAW,OAAO,OAAO,OAAO,WAAW,IAAI;AACxE,UAAM,YAAY,MAAM,KAAK,IAAI,WAAW,UAAU,CAAC;AACvD,WAAO,UAAU,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAAA,EACpE;AAIA,SAAO,WAAW,UAAU;AAC9B;AASA,SAAS,cAAc,OAAyB;AAC9C,MAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,aAAa;AAAA,EAChC;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAkC,CAAC;AACzC,UAAM,OAAO,OAAO,KAAK,KAAe,EAAE,KAAK;AAC/C,eAAW,OAAO,MAAM;AACtB,aAAO,GAAG,IAAI,cAAe,MAAkC,GAAG,CAAC;AAAA,IACrE;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AASA,SAAS,WAAW,KAAqB;AACvC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,YAAS,QAAQ,KAAK,OAAQ,IAAI,WAAW,CAAC;AAC9C,WAAO,OAAO;AAAA,EAChB;AAEA,QAAM,UAAU,KAAK,IAAI,IAAI,EAAE,SAAS,EAAE;AAC1C,SAAO,QAAQ,SAAS,IAAI,GAAG;AACjC;AAWO,SAAS,mBACd,QACA,QACAC,QAAe,IAC2D;AAC1E,QAAM,UAAoF,CAAC;AAG3F,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,OAAO,WAAW,YAAY,WAAW,MAAM;AAClG,QAAI,WAAW,QAAQ;AACrB,cAAQ,KAAK,EAAE,IAAI,WAAW,MAAMA,SAAQ,KAAK,OAAO,QAAQ,UAAU,OAAO,CAAC;AAAA,IACpF;AACA,WAAO;AAAA,EACT;AAGA,MAAI,MAAM,QAAQ,MAAM,KAAK,MAAM,QAAQ,MAAM,GAAG;AAClD,QAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,OAAO,QAAQ;AACvF,cAAQ,KAAK,EAAE,IAAI,WAAW,MAAMA,SAAQ,KAAK,OAAO,QAAQ,UAAU,OAAO,CAAC;AAAA,IACpF,OAAO;AAEL,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,cAAM,UAAU,GAAGA,KAAI,IAAI,CAAC;AAC5B,gBAAQ,KAAK,GAAG,mBAAmB,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC;AAAA,MACnE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,IAAI,IAAI,OAAO,KAAK,MAAgB,CAAC;AACrD,QAAM,UAAU,IAAI,IAAI,OAAO,KAAK,MAAgB,CAAC;AAGrD,aAAW,OAAO,SAAS;AACzB,QAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;AACrB,YAAM,UAAUA,QAAO,GAAGA,KAAI,IAAI,GAAG,KAAK,IAAI,GAAG;AACjD,cAAQ,KAAK,EAAE,IAAI,OAAO,MAAM,SAAS,OAAQ,OAAmC,GAAG,EAAE,CAAC;AAAA,IAC5F;AAAA,EACF;AAGA,aAAW,OAAO,SAAS;AACzB,QAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;AACrB,YAAM,UAAUA,QAAO,GAAGA,KAAI,IAAI,GAAG,KAAK,IAAI,GAAG;AACjD,cAAQ,KAAK,EAAE,IAAI,UAAU,MAAM,SAAS,UAAW,OAAmC,GAAG,EAAE,CAAC;AAAA,IAClG;AAAA,EACF;AAGA,aAAW,OAAO,SAAS;AACzB,QAAI,QAAQ,IAAI,GAAG,GAAG;AACpB,YAAM,UAAUA,QAAO,GAAGA,KAAI,IAAI,GAAG,KAAK,IAAI,GAAG;AACjD,cAAQ,KAAK,GAAG;AAAA,QACb,OAAmC,GAAG;AAAA,QACtC,OAAmC,GAAG;AAAA,QACvC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAQO,SAAS,oBACd,MACQ;AACR,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,UAAoB,CAAC;AAC3B,QAAM,WAAW,KAAK,OAAO,OAAK,EAAE,OAAO,KAAK,EAAE;AAClD,QAAM,cAAc,KAAK,OAAO,OAAK,EAAE,OAAO,QAAQ,EAAE;AACxD,QAAM,eAAe,KAAK,OAAO,OAAK,EAAE,OAAO,SAAS,EAAE;AAE1D,MAAI,WAAW,EAAG,SAAQ,KAAK,GAAG,QAAQ,SAAS,WAAW,IAAI,MAAM,EAAE,QAAQ;AAClF,MAAI,cAAc,EAAG,SAAQ,KAAK,GAAG,WAAW,SAAS,cAAc,IAAI,MAAM,EAAE,UAAU;AAC7F,MAAI,eAAe,EAAG,SAAQ,KAAK,GAAG,YAAY,SAAS,eAAe,IAAI,MAAM,EAAE,WAAW;AAEjG,SAAO,QAAQ,KAAK,IAAI;AAC1B;;;ACpJO,IAAM,WAAN,MAAqB;AAAA,EAO1B,YAAY,UAA2B,CAAC,GAAG;AAN3C,SAAiB,MAAM,oBAAI,IAAiB;AAG5C,SAAQ,OAAO;AACf,SAAQ,SAAS;AAGf,SAAK,UAAU,QAAQ,WAAW,QAAQ,UAAU,IAAI,QAAQ,UAAU;AAC1E,SAAK,MAAM,QAAQ,OAAO,QAAQ,MAAM,IAAI,QAAQ,MAAM;AAAA,EAC5D;AAAA,EAEA,IAAI,KAAuB;AACzB,UAAM,QAAQ,KAAK,IAAI,IAAI,GAAG;AAC9B,QAAI,CAAC,OAAO;AACV,WAAK;AACL,aAAO;AAAA,IACT;AACA,QAAI,MAAM,cAAc,KAAK,MAAM,aAAa,KAAK,IAAI,GAAG;AAC1D,WAAK,IAAI,OAAO,GAAG;AACnB,WAAK;AACL,aAAO;AAAA,IACT;AAEA,SAAK,IAAI,OAAO,GAAG;AACnB,SAAK,IAAI,IAAI,KAAK,KAAK;AACvB,SAAK;AACL,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,IAAI,KAAQ,OAAgB;AAC1B,QAAI,KAAK,IAAI,IAAI,GAAG,GAAG;AACrB,WAAK,IAAI,OAAO,GAAG;AAAA,IACrB,WAAW,KAAK,UAAU,KAAK,KAAK,IAAI,QAAQ,KAAK,SAAS;AAC5D,YAAM,SAAS,KAAK,IAAI,KAAK,EAAE,KAAK;AACpC,UAAI,CAAC,OAAO,KAAM,MAAK,IAAI,OAAO,OAAO,KAAK;AAAA,IAChD;AACA,SAAK,IAAI,IAAI,KAAK;AAAA,MAChB;AAAA,MACA,WAAW,KAAK,MAAM,IAAI,KAAK,IAAI,IAAI,KAAK,MAAM;AAAA,IACpD,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,KAAiB;AACnB,WAAO,KAAK,IAAI,GAAG,MAAM;AAAA,EAC3B;AAAA,EAEA,OAAO,KAAiB;AACtB,WAAO,KAAK,IAAI,OAAO,GAAG;AAAA,EAC5B;AAAA,EAEA,QAAc;AACZ,SAAK,IAAI,MAAM;AAAA,EACjB;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA;AAAA,EAGA,QAAyE;AACvE,UAAM,QAAQ,KAAK,OAAO,KAAK;AAC/B,WAAO;AAAA,MACL,MAAM,KAAK,IAAI;AAAA,MACf,MAAM,KAAK;AAAA,MACX,QAAQ,KAAK;AAAA,MACb,SAAS,UAAU,IAAI,IAAI,KAAK,OAAO;AAAA,IACzC;AAAA,EACF;AAAA;AAAA,EAGA,aAAmB;AACjB,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;;;ACjBA,mBAA0C;AAsC1C,IAAM,iBAAuC;AAAA,EACzC,OAAO,oBAAI,IAAI;AAAA;AAAA,IAEX;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA;AAAA,IAEA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,EACJ,CAAC;AAAA,EACD,QAAQ,oBAAI,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWlC,SAAS;AACb;AAkCA,IAAM,gBAAsC;AAAA,EACxC,OAAO,oBAAI,IAAI;AAAA,IACX;AAAA;AAAA,IACA;AAAA;AAAA,EACJ,CAAC;AAAA,EACD,QAAQ,oBAAI,IAAI,CAAC,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtB,SACI;AAAA,EACJ,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAeN,OAAO,oBAAI,IAAI;AAAA,MACX;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA;AAAA,IACJ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAqBD,gBAAgB;AAAA,EACpB;AACJ;AAGA,IAAM,kBAAkB;AAgBxB,SAAS,mBACL,OACA,WACA,OACO;AACP,MAAI,UAAU,QAAQ,UAAU,UAAa,QAAQ,gBAAiB,QAAO;AAE7E,MAAI,OAAO,UAAU,UAAU;AAC3B,QAAI,UAAU,UAAU,eAAe,KAAK,EAAG,QAAO;AACtD,WAAO,UAAU,QAAQ,KAAK,KAAK;AAAA,EACvC;AACA,MAAI,OAAO,UAAU,SAAU,QAAO;AAEtC,QAAM,MAAM;AAOZ,QAAM,WAAW,UAAU;AAC3B,MAAI,UAAU;AACV,QAAI,OAAO,IAAI,SAAS,YAAY,SAAS,MAAM,IAAI,IAAI,IAAI,EAAG,QAAO;AACzE,QAAI,OAAO,IAAI,YAAY,YAAY,SAAS,eAAe,IAAI,OAAO,EAAG,QAAO;AAAA,EACxF;AAEA,MAAI,OAAO,IAAI,SAAS,YAAY,UAAU,MAAM,IAAI,IAAI,IAAI,EAAG,QAAO;AAC1E,MAAI,OAAO,IAAI,UAAU,YAAY,UAAU,OAAO,IAAI,IAAI,KAAK,EAAG,QAAO;AAC7E,MAAI,OAAO,IAAI,YAAY,YAAY,UAAU,QAAQ,KAAK,IAAI,OAAO,EAAG,QAAO;AAGnF,SAAO,mBAAmB,IAAI,OAAO,WAAW,QAAQ,CAAC;AAC7D;AAYO,SAAS,2BAA2B,OAAgB,QAAQ,GAAY;AAC3E,SAAO,mBAAmB,OAAO,gBAAgB,KAAK;AAC1D;AAqBO,SAAS,oBAAoB,OAAgB,QAAQ,GAAY;AACpE,SAAO,mBAAmB,OAAO,eAAe,KAAK;AACzD;;;ACtSA,IAAM,kBAAkB;AAAA,EACpB;AAAA,EACA;AACJ;AAeA,eAAsB,gCAClB,QACyC;AACzC,QAAM,YAAY;AAElB,MAAI,OAAO,UAAU,QAAQ,YAAY;AACrC,UAAM,IAAI;AAAA,MACN;AAAA,IAGJ;AAAA,EACJ;AAEA,QAAM,UAA4C,CAAC;AAEnD,aAAW,SAAS,iBAAiB;AACjC,QAAI;AACA,YAAM,YAAY,MAAM,cAAc,WAAW,OAAO,YAAY;AACpE,YAAM,kBAAkB,MAAM,cAAc,WAAW,OAAO,gBAAgB;AAE9E,UAAI,mBAAmB,CAAC,WAAW;AAC/B,gBAAQ,KAAK,EAAE,OAAO,QAAQ,eAAe,CAAC;AAC9C;AAAA,MACJ;AAEA,UAAI,CAAC,WAAW;AACZ,gBAAQ,KAAK,EAAE,OAAO,QAAQ,gBAAgB,CAAC;AAC/C;AAAA,MACJ;AAEA,YAAM,UAAU;AAAA,QACZ,gBAAgB,KAAK;AAAA,MACzB;AAEA,cAAQ,KAAK,EAAE,OAAO,QAAQ,UAAU,CAAC;AAAA,IAC7C,SAAS,KAAU;AACf,cAAQ,KAAK,EAAE,OAAO,QAAQ,SAAS,OAAO,KAAK,WAAW,OAAO,GAAG,EAAE,CAAC;AAAA,IAC/E;AAAA,EACJ;AAEA,SAAO;AACX;AAMA,eAAe,cAAc,QAAa,OAAe,QAAkC;AACvF,MAAI;AACA,UAAM,OAAc,MAAM,OAAO,IAAI,sBAAsB,KAAK,IAAI;AACpE,QAAI,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,GAAG;AACxC,YAAMC,QAAc,MAAM,QAAQ,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI;AACvD,aAAOA,MAAK,KAAK,CAAC,MAAW,GAAG,SAAS,MAAM;AAAA,IACnD;AAEA,UAAM,SAAgB,MAAM,OAAO;AAAA,MAC/B;AAAA,MACA,CAAC,OAAO,MAAM;AAAA,IAClB;AACA,UAAM,OAAc,MAAM,QAAQ,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI;AAC3D,WAAO,KAAK,SAAS;AAAA,EACzB,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;;;AJTO,IAAM,iBAAN,MAA+C;AAAA,EA2CpD,YAAY,SAAgC;AA1C5C,SAAS,WAAmC;AAAA,MAC1C,MAAM;AAAA,MACN,UAAU;AAAA,MACV,cAAc;AAAA,QACZ,MAAM;AAAA,QACN,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,IACF;AAQA,SAAQ,cAAc;AACtB,SAAQ,qBAAqB;AAM7B;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,wBAAwB;AAChC,SAAQ,+BAA+B;AAMvC;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,4BAA4B;AAufpC;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,yBAAyB,oBAAI,IAAY;AA3e/C,QAAI,CAAC,QAAQ,UAAU,CAAC,QAAQ,QAAQ;AACtC,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,QAAQ;AACtB,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,mBAAmB,QAAQ,oBAAoB;AACpD,SAAK,iBAAiB,QAAQ;AAE9B,SAAK,QAAQ;AACb,SAAK,eAAe,QAAQ,iBAAiB;AAG7C,UAAM,YAAY,QAAQ;AAC1B,UAAM,eAAe,WAAW,YAAY;AAC5C,QAAI,cAAc;AAChB,YAAM,UAAU;AAAA,QACd,SAAS,WAAW,WAAW;AAAA,QAC/B,KAAK,WAAW,OAAO;AAAA,MACzB;AACA,WAAK,YAAY,IAAI,SAAS,OAAO;AACrC,WAAK,gBAAgB,IAAI,SAAS,OAAO;AACzC,WAAK,YAAY,IAAI,SAAS,OAAO;AACrC,WAAK,YAAY,IAAI,SAAS,OAAO;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,SAAS,MAAc,MAAsB;AACnD,WAAO,GAAG,IAAI,KAAK,IAAI;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,WAAW,MAAc,MAAoB;AACnD,QAAI,CAAC,KAAK,UAAW;AACrB,UAAM,MAAM,KAAK,SAAS,MAAM,IAAI;AACpC,SAAK,UAAU,OAAO,GAAG;AACzB,SAAK,WAAW,OAAO,GAAG;AAC1B,SAAK,eAAe,OAAO,IAAI;AAC/B,SAAK,WAAW,OAAO,IAAI;AAAA,EAC7B;AAAA;AAAA,EAGA,gBAAsB;AACpB,SAAK,WAAW,MAAM;AACtB,SAAK,eAAe,MAAM;AAC1B,SAAK,WAAW,MAAM;AACtB,SAAK,WAAW,MAAM;AAAA,EACxB;AAAA;AAAA,EAGA,gBAME;AACA,WAAO;AAAA,MACL,SAAS,KAAK,cAAc;AAAA,MAC5B,MAAM,KAAK,WAAW,MAAM,KAAK;AAAA,MACjC,UAAU,KAAK,eAAe,MAAM,KAAK;AAAA,MACzC,MAAM,KAAK,WAAW,MAAM,KAAK;AAAA,MACjC,MAAM,KAAK,WAAW,MAAM,KAAK;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,MAAc,MAAM,OAAe,OAAwD;AACzF,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK,OAAO,KAAK,OAAO,KAAK;AAAA,IACtC;AACA,WAAO,KAAK,OAAQ,KAAK,OAAO,KAAK;AAAA,EACvC;AAAA,EAEA,MAAc,SAAS,OAAe,OAA6D;AACjG,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK,OAAO,QAAQ,OAAO,KAAK;AAAA,IACzC;AACA,WAAO,KAAK,OAAQ,QAAQ,OAAO,KAAK;AAAA,EAC1C;AAAA,EAEA,MAAc,OAAO,OAAe,OAAqC;AACvE,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK,OAAO,MAAM,OAAO,KAAK;AAAA,IACvC;AACA,WAAO,KAAK,OAAQ,MAAM,OAAO,KAAK;AAAA,EACxC;AAAA,EAEA,MAAc,QAAQ,OAAe,MAAiE;AACpG,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK,OAAO,OAAO,OAAO,IAAI;AAAA,IACvC;AACA,WAAO,KAAK,OAAQ,OAAO,OAAO,IAAI;AAAA,EACxC;AAAA,EAEA,MAAc,QAAQ,OAAe,IAAY,MAAiE;AAChH,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK,OAAO,OAAO,OAAO,EAAE,IAAI,GAAG,KAAK,CAAC;AAAA,IAClD;AACA,WAAO,KAAK,OAAQ,OAAO,OAAO,IAAI,IAAI;AAAA,EAC5C;AAAA,EAEA,MAAc,QAAQ,OAAe,IAA0B;AAC7D,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK,OAAO,OAAO,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAQ;AAAA,IAC3D;AACA,WAAO,KAAK,OAAQ,OAAO,OAAO,EAAE;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,MAAc,eAAgC;AAC5C,UAAM,QAAiC,KAAK,iBACxC,EAAE,iBAAiB,KAAK,eAAe,IACvC,CAAC;AACL,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,MAAM,KAAK,kBAAkB,EAAE,MAAM,CAAC;AAC9D,UAAI,MAAM;AACV,iBAAW,OAAO,MAA8C;AAC9D,cAAM,IAAI,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY;AAC9D,YAAI,IAAI,IAAK,OAAM;AAAA,MACrB;AACA,aAAO,MAAM;AAAA,IACf,SAAS,OAAO;AAGd,UAAI,oBAAoB,KAAK,EAAG,QAAO;AACvC,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,eAA8B;AAC1C,QAAI,KAAK,YAAa;AAGtB,QAAI,KAAK,QAAQ;AACf,WAAK,cAAc;AAWnB,UAAI;AACF,cAAM,YAAY,KAAK;AACvB,YAAI,SACF,WAAW,UAAU,WAAW,YAAY;AAC9C,YAAI,CAAC,UAAU,WAAW,mBAAmB,KAAK;AAChD,qBAAW,aAAa,UAAU,QAAQ,OAAO,GAAG;AAClD,kBAAM,IAAI;AACV,gBAAI,MAAM,OAAO,EAAE,QAAQ,cAAc,OAAO,EAAE,YAAY,aAAa;AACzE,uBAAS;AACT;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,YAAI,QAAQ;AAEV,gBAAM,gCAAgC,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,QACrE;AAAA,MACF,SAAS,OAAO;AAOd,gBAAQ;AAAA,UACN,uEAAuE,KAAK,SAAS;AAAA,UAKrF;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI;AACF,YAAM,KAAK,OAAQ,WAAW,KAAK,WAAW;AAAA,QAC5C,GAAG;AAAA,QACH,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH,SAAS,OAAO;AASd,UAAI,CAAC,2BAA2B,KAAK,GAAG;AACtC,YAAI,CAAC,KAAK,uBAAuB;AAC/B,eAAK,wBAAwB;AAC7B,kBAAQ;AAAA,YACN,2CAA2C,KAAK,SAAS;AAAA,YAMzD;AAAA,UACF;AAAA,QACF;AAQA;AAAA,MACF;AAAA,IAGF;AAEA,QAAI,KAAK,uBAAuB;AAC9B,WAAK,wBAAwB;AAC7B,cAAQ;AAAA,QACN,2CAA2C,KAAK,SAAS;AAAA,MAC3D;AAAA,IACF;AACA,SAAK,cAAc;AAEnB,QAAI;AACF,YAAM,gCAAgC,KAAK,MAAO;AAAA,IACpD,QAAQ;AAAA,IAER;AAAA,EAWF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,sBAAqC;AACjD,QAAI,CAAC,KAAK,gBAAgB,KAAK,mBAAoB;AAGnD,QAAI,KAAK,QAAQ;AACf,WAAK,qBAAqB;AAC1B;AAAA,IACF;AAEA,QAAI;AACF,YAAM,KAAK,OAAQ,WAAW,KAAK,kBAAkB;AAAA,QACnD,GAAG;AAAA,QACH,MAAM,KAAK;AAAA,MACb,CAAC;AACD,UAAI,KAAK,8BAA8B;AACrC,aAAK,+BAA+B;AACpC,gBAAQ;AAAA,UACN,mDAAmD,KAAK,gBAAgB;AAAA,QAC1E;AAAA,MACF;AACA,WAAK,qBAAqB;AAAA,IAC5B,SAAS,OAAO;AAMd,UAAI,2BAA2B,KAAK,GAAG;AACrC,aAAK,qBAAqB;AAC1B;AAAA,MACF;AAGA,UAAI,CAAC,KAAK,8BAA8B;AACtC,aAAK,+BAA+B;AACpC,gBAAQ;AAAA,UACN,mDAAmD,KAAK,gBAAgB;AAAA,UAIxE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,WAAW,MAAc,MAAwC;AACvE,UAAM,SAAkC,EAAE,KAAK;AAC/C,QAAI,SAAS,QAAW;AACtB,aAAO,OAAO;AAAA,IAChB;AACA,QAAI,KAAK,gBAAgB;AACvB,aAAO,kBAAkB,KAAK;AAAA,IAChC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAc,oBACZ,MACA,MACA,SACA,UACA,eACA,kBACA,YACA,YACe;AACf,QAAI,CAAC,KAAK,aAAc;AAExB,UAAM,KAAK,oBAAoB;AAE/B,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,WAAW,MAAM,kBAAkB,QAAQ;AAGjD,QAAI,oBAAoB,aAAa,oBAAoB,kBAAkB,UAAU;AACnF;AAAA,IACF;AAEA,UAAM,YAAY,WAAW;AAC7B,UAAM,eAAe,KAAK,UAAU,QAAQ;AAa5C,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,KAAK,aAAa;AAAA,IACrC,SAAS,OAAO;AACd,UAAI,CAAC,KAAK,2BAA2B;AACnC,aAAK,4BAA4B;AACjC,gBAAQ;AAAA,UACN,+BAA+B,KAAK,gBAAgB,uEACrC,IAAI,IAAI,IAAI;AAAA,UAO3B;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,KAAK,2BAA2B;AAClC,WAAK,4BAA4B;AACjC,cAAQ;AAAA,QACN,gBAAgB,KAAK,gBAAgB;AAAA,MAEvC;AAAA,IACF;AAEA,UAAM,gBAAgD;AAAA,MACpD,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,GAAI,KAAK,iBAAiB,EAAE,gBAAgB,KAAK,eAAe,IAAI,CAAC;AAAA,IACvE;AAEA,QAAI;AACF,YAAM,KAAK,QAAQ,KAAK,kBAAkB;AAAA,QACxC,IAAI,cAAc;AAAA,QAClB,WAAW;AAAA,QACX,MAAM,cAAc;AAAA,QACpB,MAAM,cAAc;AAAA,QACpB,SAAS,cAAc;AAAA,QACvB,gBAAgB,cAAc;AAAA,QAC9B,UAAU,cAAc;AAAA,QACxB,UAAU,cAAc;AAAA,QACxB,mBAAmB,cAAc;AAAA,QACjC,aAAa,cAAc;AAAA,QAC3B,aAAa,cAAc;AAAA,QAC3B,aAAa,cAAc;AAAA,QAC3B,QAAQ;AAAA,QACR,GAAI,KAAK,iBAAiB,EAAE,iBAAiB,KAAK,eAAe,IAAI,CAAC;AAAA,MACxE,CAAC;AAAA,IACH,SAAS,OAAO;AAEd,cAAQ,MAAM,uCAAuC,IAAI,IAAI,IAAI,KAAK,KAAK;AAAA,IAC7E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBQ,UAAU,KAA8D;AAC9E,QAAI,CAAC,OAAO,CAAC,IAAI,SAAU,QAAO;AAElC,UAAM,UAAU,OAAO,IAAI,aAAa,WACpC,KAAK,MAAM,IAAI,QAAkB,IACjC,IAAI;AAER,UAAM,WAAW,iCAAmB,IAAI,IAAc,KAAM,IAAI;AAChE,QAAI,aAAa,OAAQ,QAAO;AAChC,eAAO,0CAA6B,UAAU,SAAoC;AAAA,MAChF,UAAU,CAAC,MAAM;AACf,cAAM,MAAM,GAAG,EAAE,YAAY,IAAI,QAAQ,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;AACnE,YAAI,KAAK,uBAAuB,IAAI,GAAG,EAAG;AAC1C,aAAK,uBAAuB,IAAI,GAAG;AACnC,gBAAQ;AAAA,UACN,2BAA2B,QAAQ,IAAI,OAAO,IAAI,QAAQ,WAAW,CAAC,kCAAkC,EAAE,OAAO;AAAA,QACnH;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,KAA8C;AAChE,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,WAAY,IAAI,aAAwB;AAAA,MACxC,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,OAAQ,IAAI,SAAqC;AAAA,MACjD,UAAU,KAAK,UAAU,GAAG,KAAK,CAAC;AAAA,MAClC,SAAS,IAAI;AAAA,MACb,UAAW,IAAI,YAA2C;AAAA,MAC1D,OAAO,IAAI;AAAA,MACX,OAAQ,IAAI,SAAqC;AAAA,MACjD,gBAAgB,IAAI;AAAA,MACpB,eAAe,IAAI;AAAA,MACnB,SAAU,IAAI,WAAsB;AAAA,MACpC,UAAU,IAAI;AAAA,MACd,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI,OAAQ,OAAO,IAAI,SAAS,WAAW,KAAK,MAAM,IAAI,IAAc,IAAI,IAAI,OAAoB;AAAA,MAC1G,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkDQ,gCAAgC,OAAsB;AAC5D,QAAI,oBAAoB,KAAK,EAAG;AAChC,UAAM;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KACJ,MACA,MACA,UAC6B;AAC7B,UAAM,YAAY,KAAK,IAAI;AAE3B,UAAM,KAAK,aAAa;AAKxB,UAAM,MAAM,KAAK,SAAS,MAAM,IAAI;AACpC,QAAI,KAAK,WAAW;AAClB,YAAM,SAAS,KAAK,UAAU,IAAI,GAAG;AACrC,UAAI,WAAW,QAAW;AACxB,eAAO;AAAA,UACL,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,SAAS,KAAK,WAAW;AAAA,QAC9C,OAAO,KAAK,WAAW,MAAM,IAAI;AAAA,MACnC,CAAC;AAED,UAAI,CAAC,KAAK;AACR,aAAK,WAAW,IAAI,KAAK,IAAI;AAC7B,eAAO;AAAA,UACL,MAAM;AAAA,UACN,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,UAAU,GAAG;AAC/B,YAAM,SAAS,KAAK,YAAY,GAAG;AAEnC,WAAK,WAAW,IAAI,KAAK,IAAI;AAE7B,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,MAAM,OAAO;AAAA,QACb,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAAA,IACF,SAAS,OAAO;AACd,WAAK,gCAAgC,KAAK;AAI1C,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,SACJ,MACA,UACc;AACd,UAAM,KAAK,aAAa;AAExB,QAAI,KAAK,eAAe;AACtB,YAAM,SAAS,KAAK,cAAc,IAAI,IAAI;AAC1C,UAAI,WAAW,OAAW,QAAO;AAAA,IACnC;AAEA,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,MAAM,KAAK,WAAW;AAAA,QAC5C,OAAO,KAAK,WAAW,IAAI;AAAA,MAC7B,CAAC;AAED,YAAM,SAAS,KACZ,IAAI,SAAO,KAAK,UAAU,GAAG,CAAC,EAC9B,OAAO,CAAC,SAA0C,SAAS,IAAI;AAElE,WAAK,eAAe,IAAI,MAAM,MAAM;AACpC,aAAO;AAAA,IACT,SAAS,OAAO;AACd,WAAK,gCAAgC,KAAK;AAE1C,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,MAAc,MAAgC;AACzD,UAAM,KAAK,aAAa;AAGxB,QAAI,KAAK,WAAW;AAClB,YAAM,SAAS,KAAK,UAAU,IAAI,KAAK,SAAS,MAAM,IAAI,CAAC;AAC3D,UAAI,WAAW,OAAW,QAAO,WAAW;AAAA,IAC9C;AAEA,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,OAAO,KAAK,WAAW;AAAA,QAC9C,OAAO,KAAK,WAAW,MAAM,IAAI;AAAA,MACnC,CAAC;AAED,aAAO,QAAQ;AAAA,IACjB,SAAS,OAAO;AACd,WAAK,gCAAgC,KAAK;AAE1C,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,MAAc,MAA6C;AACpE,UAAM,KAAK,aAAa;AAExB,UAAM,MAAM,KAAK,SAAS,MAAM,IAAI;AACpC,QAAI,KAAK,WAAW;AAClB,YAAM,SAAS,KAAK,UAAU,IAAI,GAAG;AACrC,UAAI,WAAW,OAAW,QAAO;AAAA,IACnC;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,SAAS,KAAK,WAAW;AAAA,QAC9C,OAAO,KAAK,WAAW,MAAM,IAAI;AAAA,MACnC,CAAC;AAED,UAAI,CAAC,KAAK;AACR,aAAK,WAAW,IAAI,KAAK,IAAI;AAC7B,eAAO;AAAA,MACT;AAEA,YAAM,SAAS,KAAK,YAAY,GAAG;AACnC,YAAM,cAAc,OAAO,IAAI,aAAa,WACxC,IAAI,WACJ,KAAK,UAAU,IAAI,QAAQ;AAE/B,YAAM,QAAuB;AAAA,QAC3B,MAAM,YAAY;AAAA,QAClB,OAAO,OAAO,aAAa,OAAO,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,QACtE,QAAQ;AAAA,QACR,MAAM,OAAO;AAAA,MACf;AACA,WAAK,WAAW,IAAI,KAAK,KAAK;AAC9B,aAAO;AAAA,IACT,SAAS,OAAO;AACd,WAAK,gCAAgC,KAAK;AAE1C,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,MAAiC;AAC1C,UAAM,KAAK,aAAa;AAExB,QAAI,KAAK,WAAW;AAClB,YAAM,SAAS,KAAK,UAAU,IAAI,IAAI;AACtC,UAAI,WAAW,OAAW,QAAO;AAAA,IACnC;AAEA,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,MAAM,KAAK,WAAW;AAAA,QAC5C,OAAO,KAAK,WAAW,IAAI;AAAA,QAC3B,QAAQ,CAAC,MAAM;AAAA,MACjB,CAAC;AAED,YAAM,QAAQ,KACX,IAAI,SAAO,IAAI,IAAc,EAC7B,OAAO,UAAQ,OAAO,SAAS,QAAQ;AAE1C,WAAK,WAAW,IAAI,MAAM,KAAK;AAC/B,aAAO;AAAA,IACT,SAAS,OAAO;AACd,WAAK,gCAAgC,KAAK;AAE1C,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iBACJ,MACA,MACA,SACuC;AACvC,QAAI,CAAC,KAAK,aAAc,QAAO;AAE/B,UAAM,KAAK,oBAAoB;AAE/B,UAAM,SAAkC;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,KAAK,gBAAgB;AACvB,aAAO,kBAAkB,KAAK;AAAA,IAChC;AAEA,UAAM,MAAM,MAAM,KAAK,SAAS,KAAK,kBAAkB;AAAA,MACrD,OAAO;AAAA,IACT,CAAC;AACD,QAAI,CAAC,IAAK,QAAO;AAEjB,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,SAAS,IAAI;AAAA,MACb,eAAe,IAAI;AAAA,MACnB,UAAU,OAAO,IAAI,aAAa,WAAW,KAAK,MAAM,IAAI,QAAkB,IAAI,IAAI;AAAA,MACtF,UAAU,IAAI;AAAA,MACd,kBAAkB,IAAI;AAAA,MACtB,YAAY,IAAI;AAAA,MAChB,gBAAgB,IAAI;AAAA,MACpB,YAAY,IAAI;AAAA,MAChB,YAAY,IAAI;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aACJ,MACA,MACA,SAQ8D;AAC9D,QAAI,CAAC,KAAK,cAAc;AACtB,aAAO,EAAE,SAAS,CAAC,GAAG,OAAO,GAAG,SAAS,MAAM;AAAA,IACjD;AAEA,UAAM,KAAK,aAAa;AACxB,UAAM,KAAK,oBAAoB;AAI/B,UAAM,gBAAyC;AAAA,MAC7C;AAAA,MACA;AAAA,IACF;AACA,QAAI,KAAK,eAAgB,eAAc,kBAAkB,KAAK;AAC9D,QAAI,SAAS,cAAe,eAAc,iBAAiB,QAAQ;AACnE,QAAI,SAAS,MAAO,eAAc,cAAc,EAAE,MAAM,QAAQ,MAAM;AACtE,QAAI,SAAS,OAAO;AAClB,UAAI,cAAc,aAAa;AAC7B,QAAC,cAAc,YAAwC,OAAO,QAAQ;AAAA,MACxE,OAAO;AACL,sBAAc,cAAc,EAAE,MAAM,QAAQ,MAAM;AAAA,MACpD;AAAA,IACF;AAEA,UAAM,QAAQ,SAAS,SAAS;AAChC,UAAM,SAAS,SAAS,UAAU;AAElC,UAAM,iBAAiB,MAAM,KAAK,MAAM,KAAK,kBAAkB;AAAA,MAC7D,OAAO;AAAA,MACP,SAAS;AAAA,QACP,EAAE,OAAO,eAAe,OAAO,OAAgB;AAAA,QAC/C,EAAE,OAAO,WAAW,OAAO,OAAgB;AAAA,MAC7C;AAAA,MACA,OAAO,QAAQ;AAAA,MACf;AAAA,IACF,CAAC;AAED,UAAM,UAAU,eAAe,SAAS;AACxC,UAAM,UAAU,eAAe,MAAM,GAAG,KAAK;AAC7C,UAAM,QAAQ,MAAM,KAAK,OAAO,KAAK,kBAAkB,EAAE,OAAO,cAAc,CAAC;AAE/E,UAAM,kBAAkB,SAAS,oBAAoB;AACrD,UAAM,SAAS,QAAQ,IAAI,CAAC,QAAiC;AAC3D,YAAM,iBACJ,OAAO,IAAI,aAAa,WACpB,KAAK,MAAM,IAAI,QAAkB,IAChC,IAAI;AAEX,aAAO;AAAA,QACL,IAAI,IAAI;AAAA,QACR,MAAM,IAAI;AAAA,QACV,MAAM,IAAI;AAAA,QACV,SAAS,IAAI;AAAA,QACb,eAAe,IAAI;AAAA,QACnB,UAAU,kBAAkB,iBAAiB;AAAA,QAC7C,UAAU,IAAI;AAAA,QACd,kBAAkB,IAAI;AAAA,QACtB,YAAY,IAAI;AAAA,QAChB,gBAAgB,IAAI;AAAA,QACpB,YAAY,IAAI;AAAA,QAChB,YAAY,IAAI;AAAA,MAClB;AAAA,IACF,CAAC;AAED,WAAO,EAAE,SAAS,QAAQ,OAAO,QAAQ;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,iBACJ,MACA,MACA,cACA,eACA,YACA,YACe;AACf,UAAM,KAAK,aAAa;AAExB,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,eAAe,KAAK,UAAU,YAAY;AAChD,UAAM,cAAc,MAAM,kBAAkB,YAAY;AAExD,UAAM,WAAW,MAAM,KAAK,SAAS,KAAK,WAAW;AAAA,MACnD,OAAO,KAAK,WAAW,MAAM,IAAI;AAAA,IACnC,CAAC;AAED,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,YAAY,IAAI,IAAI,IAAI,yBAAyB;AAAA,IACnE;AAEA,UAAM,mBAAmB,SAAS;AAClC,UAAM,cAAe,SAAS,WAAsB,KAAK;AAEzD,UAAM,KAAK,QAAQ,KAAK,WAAW,SAAS,IAAc;AAAA,MACxD,UAAU;AAAA,MACV,SAAS;AAAA,MACT,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,OAAO;AAAA,IACT,CAAC;AAED,SAAK,WAAW,MAAM,IAAI;AAG1B,UAAM,KAAK;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,0BAA0B,aAAa;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,KACJ,MACA,MACA,MACA,UAC6B;AAC7B,UAAM,YAAY,KAAK,IAAI;AAE3B,UAAM,KAAK,aAAa;AAExB,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,eAAe,KAAK,UAAU,IAAI;AACxC,UAAM,cAAc,MAAM,kBAAkB,IAAI;AAEhD,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,SAAS,KAAK,WAAW;AAAA,QACnD,OAAO,KAAK,WAAW,MAAM,IAAI;AAAA,MACnC,CAAC;AAED,UAAI,UAAU;AAEZ,cAAM,mBAAmB,SAAS;AAClC,YAAI,gBAAgB,kBAAkB;AAIpC,eAAK,WAAW,IAAI,KAAK,SAAS,MAAM,IAAI,GAAG,IAA+B;AAC9E,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,MAAM,gBAAgB,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI;AAAA,YACpD,MAAM,aAAa;AAAA,YACnB,UAAU,KAAK,IAAI,IAAI;AAAA,UACzB;AAAA,QACF;AAGA,cAAM,WAAY,SAAS,WAAsB,KAAK;AAEtD,cAAM,KAAK,QAAQ,KAAK,WAAW,SAAS,IAAc;AAAA,UACxD,UAAU;AAAA,UACV;AAAA,UACA,UAAU;AAAA,UACV,YAAY;AAAA,UACZ,OAAO;AAAA,QACT,CAAC;AAED,aAAK,WAAW,MAAM,IAAI;AAG1B,cAAM,KAAK;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,gBAAgB,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI;AAAA,UACpD,MAAM,aAAa;AAAA,UACnB,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF,OAAO;AAEL,cAAM,KAAK,WAAW;AACtB,cAAM,KAAK,QAAQ,KAAK,WAAW;AAAA,UACjC;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW;AAAA,UACX,OAAQ,MAAc,SAAS;AAAA,UAC/B,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,GAAI,KAAK,iBAAiB,EAAE,iBAAiB,KAAK,eAAe,IAAI,CAAC;AAAA,UACtE,YAAY;AAAA,UACZ,YAAY;AAAA,QACd,CAAC;AAED,aAAK,WAAW,MAAM,IAAI;AAG1B,cAAM,KAAK;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,gBAAgB,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI;AAAA,UACpD,MAAM,aAAa;AAAA,UACnB,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,kCAAkC,IAAI,IAAI,IAAI,KAC5C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CACvD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,MAAc,MAA6B;AACtD,UAAM,KAAK,aAAa;AAGxB,UAAM,WAAW,MAAM,KAAK,SAAS,KAAK,WAAW;AAAA,MACnD,OAAO,KAAK,WAAW,MAAM,IAAI;AAAA,IACnC,CAAC;AAED,QAAI,CAAC,UAAU;AAEb;AAAA,IACF;AAGA,UAAM,KAAK,QAAQ,KAAK,WAAW,SAAS,EAAY;AAExD,SAAK,WAAW,MAAM,IAAI;AAAA,EAC5B;AACF;AAMA,SAAS,aAAqB;AAC5B,MAAI,OAAO,WAAW,WAAW,eAAe,OAAO,WAAW,OAAO,eAAe,YAAY;AAClG,WAAO,WAAW,OAAO,WAAW;AAAA,EACtC;AAEA,SAAO,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE,CAAC;AAC1E;;;AK5nCA,iBAKO;;;ACvCA,IAAM,uBAAuB,OAAO,OAAO;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAU;AAMH,IAAM,kBAAkB;AAE/B,IAAM,gBAAgB,oBAAI,IAAY,CAAC,GAAG,sBAAsB,eAAe,CAAC;AAEhF,IAAM,iBAAoD,OAAO,OAAO,CAAC,CAAC;AA2BnE,SAAS,mBAAmB,MAAiC;AAClE,MAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AACpE,WAAO,EAAE,UAAU,gBAAgB,MAAM,MAAM,SAAS,MAAM;AAAA,EAChE;AAEA,QAAM,MAAM;AACZ,QAAM,cAAc,IAAI,eAAe;AAOvC,MAAI,gBAAgB,UAAa,gBAAgB,MAAM;AACrD,UAAMC,YAAoC,CAAC;AAC3C,eAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAClC,UAAI,QAAQ,gBAAiB;AAC7B,MAAAA,UAAS,GAAG,IAAI,IAAI,GAAG;AAAA,IACzB;AACA,WAAO,EAAE,UAAU,OAAO,OAAOA,SAAQ,GAAG,MAAM,aAAa,SAAS,KAAK;AAAA,EAC/E;AAIA,MAAI;AACJ,aAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAClC,QAAI,CAAC,cAAc,IAAI,GAAG,EAAG;AAC7B,4BAAa,CAAC;AACd,aAAS,GAAG,IAAI,IAAI,GAAG;AAAA,EACzB;AAKA,MAAI,CAAC,SAAU,QAAO,EAAE,UAAU,gBAAgB,MAAM,KAAK,SAAS,MAAM;AAE5E,QAAM,OAAgC,CAAC;AACvC,aAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAClC,QAAI,cAAc,IAAI,GAAG,EAAG;AAC5B,SAAK,GAAG,IAAI,IAAI,GAAG;AAAA,EACrB;AACA,SAAO,EAAE,UAAU,OAAO,OAAO,QAAQ,GAAG,MAAM,SAAS,MAAM;AACnE;AAUO,SAAS,eAAe,QAA8C;AAC3E,QAAM,eAAe,OAAO,SAAS;AACrC,MAAI,OAAO,iBAAiB,SAAU,QAAO;AAC7C,QAAM,OAAO,OAAO;AACpB,MAAI,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,GAAG;AAC5D,UAAM,WAAY,KAA4B;AAC9C,QAAI,OAAO,aAAa,SAAU,QAAO;AAAA,EAC3C;AACA,SAAO;AACT;;;AD3DO,SAAS,wBAAwB,QAAwB;AAC9D,SAAO,OAAO,UAAU,EAAE,EAAE,YAAY;AAC1C;AAeO,SAAS,iBAAiB,QAAgBC,OAAsB;AACrE,SAAO,GAAG,wBAAwB,MAAM,CAAC,QAAI,kCAAsBA,KAAI,CAAC;AAC1E;AAuBO,SAAS,mBAAmB,OAA2B,QAA+B;AAC3F,QAAM,QAAQ,oBAAI,IAAyB;AAE3C,aAAW,QAAQ,OAAO;AAMxB,UAAM,SAAS,mBAAmB,IAAI;AACtC,UAAM,SAAS,6BAAkB,UAAU,OAAO,IAAI;AACtD,QAAI,CAAC,OAAO,SAAS;AAInB,YAAM,eAAe,eAAe,MAAM,KAAK;AAC/C,aAAO;AAAA,QACL,sCAAsC,YAAY;AAAA,QAGlD;AAAA,QACA,EAAE,QAAQ,OAAO,MAAM,OAAO;AAAA,MAChC;AACA;AAAA,IACF;AAEA,UAAM,WAAW,OAAO;AAKxB,UAAM,kBAAc,4CAAgC,QAAQ;AAC5D,QAAI,aAAa;AACf,aAAO;AAAA,QACL,sCAAsC,SAAS,IAAI,qVAId,YAAY,OAAO;AAAA,QACxD;AAAA,QACA,EAAE,MAAM,SAAS,MAAM,OAAO,EAAE,MAAM,YAAY,MAAM,SAAS,YAAY,QAAQ,EAAE;AAAA,MACzF;AACA;AAAA,IACF;AAEA,UAAM,MAAM,iBAAiB,SAAS,QAAQ,SAAS,IAAI;AAC3D,UAAM,YAAY,MAAM,IAAI,GAAG;AAE/B,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,KAAK,QAAQ;AACvB;AAAA,IACF;AAKA,UAAM,iBAAiB,SAAS,OAAO,UAAU;AACjD,UAAM,SAAS,iBAAiB,WAAW;AAC3C,UAAM,QAAQ,iBAAiB,YAAY;AAC3C,QAAI,eAAgB,OAAM,IAAI,KAAK,QAAQ;AAE3C,WAAO;AAAA,MACL,kDAAkD,GAAG,iBAAiB,UAAU,IAAI,UAC9E,SAAS,IAAI,uBAAuB,OAAO,IAAI,0BAA0B,MAAM,IAAI,yJAEpC,MAAM,IAAI;AAAA,MAC/D;AAAA,MACA,EAAE,KAAK,QAAQ,OAAO,MAAM,SAAS,MAAM,KAAK;AAAA,IAClD;AAAA,EACF;AAEA,SAAO;AACT;AASO,IAAM,kBAAN,MAAsB;AAAA,EAO3B,YAAY,MAA2B;AACrC,SAAK,OAAO;AAAA,EACd;AAAA;AAAA,EAGA,aAAmB;AACjB,SAAK,QAAQ;AACb,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,MAAM,OAAgF;AAC1F,UAAM,QAAQ,MAAM,KAAK,YAAY;AACrC,UAAM,WAAW,MAAM,IAAI,iBAAiB,MAAM,QAAQ,MAAM,IAAI,CAAC;AACrE,QAAI,CAAC,SAAU,QAAO;AAGtB,WAAO,EAAE,UAAU,QAAQ,CAAC,EAAE;AAAA,EAChC;AAAA,EAEA,MAAc,cAAsC;AAClD,QAAI,KAAK,MAAO,QAAO,KAAK;AAC5B,QAAI,KAAK,SAAU,QAAO,KAAK;AAE/B,UAAM,SAAS,YAAY;AAGzB,YAAM,QAAQ,MAAM,KAAK,KAAK,aAAa;AAC3C,aAAO,mBAAmB,OAAO,KAAK,KAAK,MAAM;AAAA,IACnD,GAAG;AAEH,SAAK,WAAW;AAChB,QAAI;AACF,YAAM,QAAQ,MAAM;AAEpB,UAAI,KAAK,aAAa,OAAO;AAC3B,aAAK,QAAQ;AACb,aAAK,WAAW;AAAA,MAClB;AACA,aAAO;AAAA,IACT,SAAS,OAAO;AAGd,UAAI,KAAK,aAAa,MAAO,MAAK,WAAW;AAC7C,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;AT/NA,IAAM,0BAA2D,CAAC,QAAQ,QAAQ;AAGlF,IAAM,mCAAyE;AAAA,EAC7E,MAAM;AAAA,EACN,QAAQ;AACV;AAmBO,SAAS,yCACd,YACA,SACQ;AACR,QAAM,gBACJ,QAAQ,WAAW,IACf,0DACA,mBAAmB,QAAQ,CAAC,CAAC;AAEnC,QAAM,eAAe,QAAQ;AAAA,IAAI,CAAC,WAChC,WAAW,SACP,qWAG0C,UAAU,qIAEpD,yUAGQ,UAAU;AAAA,EAExB;AAEA,QAAM,SAAS,QACZ,IAAI,CAAC,WAAW,KAAK,iCAAiC,MAAM,CAAC,IAAI,EACjE,KAAK,OAAO;AAEf,SACE,4DAA4D,UAAU,qFACD,aAAa,uLAGlF,aAAa,KAAK,EAAE,IACpB,yBAAyB,MAAM,SAAS,UAAU;AAKtD;AA2BA,SAAS,6BAA6B,QAA8B;AAClE,QAAM,EAAE,MAAM,UAAU,aAAa,IAAI,OAAO;AAChD,MAAI,aAAa,iBAAiB,aAAa,UAAU,KAAM;AAC/D,QAAM,UAAU,wBAAwB,OAAO,CAAC,WAAW,OAAO,OAAO,MAAM,MAAM,UAAU;AAC/F,MAAI,QAAQ,WAAW,EAAG;AAC1B,QAAM,IAAI,MAAM,yCAAyC,MAAM,OAAO,CAAC;AACzE;AAOA,IAAM,2BACJ;AAaF,SAAS,oBAA4B;AACnC,QAAM,IAAI,WAAW;AACrB,MAAI,KAAK,OAAO,EAAE,eAAe,YAAY;AAC3C,WAAO,EAAE,WAAW;AAAA,EACtB;AACA,SAAO,uCAAuC,QAAQ,SAAS,CAAC,OAAO;AACrE,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,OAAO,MAAM,IAAK,IAAI,IAAO;AACvC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;AAkDO,IAAM,mBAAN,MAAM,iBAA4C;AAAA,EAiMvD,YAAY,QAAgC;AAhM5C,SAAQ,UAAuC,oBAAI,IAAI;AAIvD,SAAU,iBAAiB,oBAAI,IAAgC;AAI/D;AAAA,SAAQ,WAAW,oBAAI,IAAkC;AAGzD;AAAA,SAAQ,WAAW,oBAAI,IAA6B;AAGpD;AAAA,SAAQ,eAAkD,CAAC;AAG3D;AAAA,SAAQ,eAAe,oBAAI,IAAkC;AAgE7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,YAAY,oBAAI,IAA4B;AA8DpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAiB,oBAAoB,oBAAI,IAAgC;AAQzE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAiB,4BAA4B,oBAAI,IAAY;AA8B7D,SAAQ,kBAAkB;AAYxB,SAAK,SAAS;AACd,SAAK,aAAS,0BAAa,EAAE,OAAO,QAAQ,QAAQ,SAAS,CAAC;AAe9D,SAAK,kBAAkB,IAAI,gBAAgB;AAAA,MACzC,cAAc,MAAM,KAAK,aAAa,iBAAgB,sBAAsB;AAAA,MAC5E,QAAQ,KAAK;AAAA,IACf,CAAC;AACD,SAAK,UAAU,iBAAgB,wBAAwB,MAAM,KAAK,gBAAgB,WAAW,CAAC;AAG9F,SAAK,cAAc,oBAAI,IAAI;AAC3B,UAAM,UAAU,OAAO,WAAW,CAAC,cAAc,QAAQ,MAAM;AAE/D,QAAI,QAAQ,SAAS,MAAM,GAAG;AAC5B,WAAK,YAAY,IAAI,QAAQ,IAAI,eAAe,CAAC;AAAA,IACnD;AACA,QAAI,QAAQ,SAAS,MAAM,GAAG;AAC5B,WAAK,YAAY,IAAI,QAAQ,IAAI,eAAe,CAAC;AAAA,IACnD;AACA,QAAI,QAAQ,SAAS,YAAY,GAAG;AAClC,WAAK,YAAY,IAAI,cAAc,IAAI,qBAAqB,YAAY,CAAC;AAAA,IAC3E;AACA,QAAI,QAAQ,SAAS,YAAY,GAAG;AAClC,WAAK,YAAY,IAAI,cAAc,IAAI,qBAAqB,YAAY,CAAC;AAAA,IAC3E;AAGA,QAAI,OAAO,WAAW,OAAO,QAAQ,SAAS,GAAG;AAC/C,aAAO,QAAQ,QAAQ,YAAU,KAAK,eAAe,MAAM,CAAC;AAAA,IAC9D;AAGA,QAAI,OAAO,cAAc,OAAO,QAAQ;AACtC,WAAK,kBAAkB,OAAO,MAAM;AAAA,IACtC;AAAA,EAEF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,SAAkD;AAChE,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,kBAAkB,QAAqB,gBAAyB,eAA8B;AAC5F,QAAI,kBAAkB,QAAW;AAC/B,WAAK,OAAO,KAAK,uFAAkF;AAAA,QACjG;AAAA,QACA;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,UAAM,WAAW,IAAI,eAAe;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,KAAK,OAAO,OAAO;AAAA,IAC5B,CAAC;AACD,SAAK,eAAe,QAAQ;AAC5B,SAAK,OAAO,KAAK,6BAA6B,EAAE,YAAY,KAAK,OAAO,YAAY,UAAU,CAAC;AAAA,EACjG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,cAAc,QAAqB,gBAAyB,eAA8B;AACxF,QAAI,kBAAkB,QAAW;AAC/B,WAAK,OAAO,KAAK,uFAAkF;AAAA,QACjG;AAAA,QACA;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,UAAM,WAAW,IAAI,eAAe;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,KAAK,OAAO,OAAO;AAAA,IAC5B,CAAC;AACD,SAAK,eAAe,QAAQ;AAC5B,SAAK,OAAO,KAAK,4CAA4C,EAAE,UAAU,CAAC;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,mBAAmB,SAAiC;AAClD,SAAK,kBAAkB;AACvB,SAAK,OAAO,KAAK,gDAAgD;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAc,6BACZ,QACA,MACA,MACA,OAAuE,CAAC,GACzD;AACf,QAAI,CAAC,KAAK,gBAAiB;AAE3B,UAAM,YAAY,YAAY,IAAI,IAAI,MAAM;AAC5C,QAAI,CAAE,8BAAkB,QAA8B,SAAS,SAAS,GAAG;AACzE,WAAK,OAAO;AAAA,QACV,kBAAkB,IAAI;AAAA,QACtB,EAAE,WAAW,KAAK;AAAA,MACpB;AACA;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAA+B,gCAAoB,MAAM;AAAA,QAC7D,IAAI,kBAAkB;AAAA,QACtB,MAAM;AAAA,QACN,cAAc;AAAA,QACd;AAAA,QACA,GAAI,OAAO,KAAK,cAAc,WAAW,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,QAC1E,GAAI,KAAK,eAAe,SAAY,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,QACvE,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,QAC7C,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,WAAiC;AAAA,QACrC,MAAM,MAAM;AAAA,QACZ,QAAQ;AAAA,QACR,SAAS,EAAE,GAAG,MAAM;AAAA,QACpB,WAAW,MAAM;AAAA,MACnB;AAEA,YAAM,KAAK,gBAAgB,QAAQ,QAAQ;AAC3C,WAAK,OAAO,MAAM,aAAa,SAAS,UAAU,EAAE,KAAK,CAAC;AAAA,IAC5D,SAAS,OAAO;AACd,WAAK,OAAO,KAAK,oCAAoC,EAAE,MAAM,MAAM,MAAM,CAAC;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,eAAe,QAAwB;AACrC,iCAA6B,MAAM;AACnC,SAAK,QAAQ,IAAI,OAAO,SAAS,MAAM,MAAM;AAC7C,SAAK,OAAO,KAAK,+BAA+B,OAAO,SAAS,IAAI,KAAK,OAAO,SAAS,QAAQ,GAAG;AAAA,EACtG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,SACJ,MACA,MACA,MACA,SACe;AAIf,QAAI,KAAK,OAAO,aAAa,aAAa,OAAO;AAC/C,YAAM,MAAM,mFAAmF,IAAI,IAAI,IAAI;AAC3G,UAAI,KAAK,OAAO,YAAY,cAAc;AACxC,cAAM,IAAI,MAAM,GAAG;AAAA,MACrB;AACA,WAAK,OAAO,KAAK,GAAG;AACpB;AAAA,IACF;AAKA,UAAM,UAAU,KAAK,SAAS,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;AAEtD,QAAI,CAAC,KAAK,SAAS,IAAI,IAAI,GAAG;AAC5B,WAAK,SAAS,IAAI,MAAM,oBAAI,IAAI,CAAC;AAAA,IACnC;AACA,SAAK,SAAS,IAAI,IAAI,EAAG,IAAI,MAAM,IAAI;AACvC,SAAK,oBAAoB,IAAI;AAK7B,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAC1C,UAAI,OAAO,SAAS,aAAa,iBAAiB,CAAC,OAAO,SAAS,aAAa,MAAO;AAUvF,UAAI,OAAO,OAAO,SAAS,WAAY;AACvC,YAAM,OAAO,KAAK,MAAM,MAAM,IAAI;AAAA,IACpC;AAKA,UAAM,KAAK,6BAA6B,UAAU,YAAY,WAAW,MAAM,MAAM;AAAA,MACnF,YAAY;AAAA,MACZ,WAAY,MAAc;AAAA,MAC1B,QAAQ,SAAS;AAAA,IACnB,CAAC;AAKD,QAAI,SAAS,WAAW,OAAO;AAC7B,WAAK,eAAe,MAAM;AAAA,QACxB,MAAM,UAAU,YAAY;AAAA,QAC5B,cAAc;AAAA,QACd;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,iBAAiB,MAAc,MAAc,MAAqB;AAChE,QAAI,CAAC,KAAK,SAAS,IAAI,IAAI,GAAG;AAC5B,WAAK,SAAS,IAAI,MAAM,oBAAI,IAAI,CAAC;AAAA,IACnC;AACA,SAAK,SAAS,IAAI,IAAI,EAAG,IAAI,MAAM,IAAI;AACvC,SAAK,oBAAoB,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqCA,MAAM,IAAI,MAAc,MAA4C;AAElE,UAAM,YAAY,KAAK,SAAS,IAAI,IAAI;AACxC,QAAI,WAAW,IAAI,IAAI,GAAG;AACxB,aAAO,UAAU,IAAI,IAAI;AAAA,IAC3B;AAGA,UAAM,SAAS,MAAM,KAAK,KAAK,MAAM,IAAI;AACzC,WAAO,UAAU;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,aACJ,MACA,MAC6E;AAG7E,UAAM,YAAY,KAAK,SAAS,IAAI,IAAI;AACxC,QAAI,WAAW,IAAI,IAAI,GAAG;AACxB,aAAO,EAAE,MAAM,UAAU,IAAI,IAAI,GAAG,UAAU,OAAO,QAAQ,CAAC,EAAE;AAAA,IAClE;AAGA,UAAM,EAAE,MAAM,UAAU,OAAO,IAAI,MAAM,KAAK,cAAc,MAAM,IAAI;AACtE,WAAO,EAAE,MAAM,QAAQ,QAAW,UAAU,OAAO;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,KAAK,MAAkC;AAI3C,UAAM,SAAS,KAAK,eAAe,IAAI;AACvC,QAAI,QAAQ;AACV,aAAO,OAAO;AAAA,IAChB;AAIA,UAAM,SAAS,KAAK,kBAAkB,IAAI,IAAI;AAC9C,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAKA,UAAM,SAA6B,KAAK,iBAAiB,IAAI,EAAE,KAAK,CAAC,EAAE,OAAO,SAAS,MAAM;AAO3F,UAAI,KAAK,kBAAkB,IAAI,IAAI,MAAM,QAAQ;AAC/C,aAAK,gBAAgB,MAAM,OAAO,QAAQ;AAAA,MAC5C;AACA,aAAO;AAAA,IACT,CAAC;AACD,SAAK,kBAAkB,IAAI,MAAM,MAAM;AAEvC,QAAI;AACF,aAAO,MAAM;AAAA,IACf,UAAE;AAIA,UAAI,KAAK,kBAAkB,IAAI,IAAI,MAAM,QAAQ;AAC/C,aAAK,kBAAkB,OAAO,IAAI;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAc,iBAAiB,MAAgE;AAC7F,UAAM,QAAQ,oBAAI,IAAqB;AAGvC,UAAM,YAAY,KAAK,SAAS,IAAI,IAAI;AACxC,QAAI,WAAW;AACb,iBAAW,CAAC,MAAM,IAAI,KAAK,WAAW;AACpC,cAAM,IAAI,MAAM,IAAI;AAAA,MACtB;AAAA,IACF;AAMA,QAAI,WAAW;AACf,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAC1C,UAAI;AACF,cAAM,cAAc,MAAM,OAAO,SAAS,IAAI;AAC9C,mBAAW,QAAQ,aAAa;AAC9B,gBAAM,UAAU;AAChB,cAAI,WAAW,OAAO,QAAQ,SAAS,YAAY,CAAC,MAAM,IAAI,QAAQ,IAAI,GAAG;AAC3E,kBAAM,IAAI,QAAQ,MAAM,IAAI;AAAA,UAC9B;AAAA,QACF;AACA,aAAK,0BAA0B,OAAO,SAAS,IAAI;AAAA,MACrD,SAAS,GAAG;AACV,mBAAW;AACX,aAAK,wBAAwB,OAAO,SAAS,MAAM,MAAM,CAAC;AAAA,MAC5D;AAAA,IACF;AAEA,WAAO,EAAE,OAAO,MAAM,KAAK,MAAM,OAAO,CAAC,GAAG,SAAS;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuCQ,wBAAwB,YAAoB,MAAc,OAAsB;AACtF,QAAI,KAAK,0BAA0B,IAAI,UAAU,EAAG;AACpD,SAAK,0BAA0B,IAAI,UAAU;AAC7C,SAAK,OAAO;AAAA,MACV,8BAA8B,UAAU,4DAA4D,IAAI,mZAIhE,UAAU,wHACC,iBAAgB,0BAA0B;AAAA,MAG7F,iBAAiB,QAAQ,QAAQ;AAAA,MACjC,EAAE,QAAQ,YAAY,MAAM,MAAM;AAAA,IACpC;AAAA,EACF;AAAA;AAAA,EAGQ,0BAA0B,YAA0B;AAC1D,QAAI,CAAC,KAAK,0BAA0B,OAAO,UAAU,EAAG;AACxD,SAAK,OAAO;AAAA,MACV,8BAA8B,UAAU;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,gBAAgB,MAAc,OAAkB,UAAyB;AAC/E,SAAK,UAAU,IAAI,MAAM,EAAE,IAAI,KAAK,IAAI,GAAG,OAAO,SAAS,CAAC;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,eAAe,MAA0C;AAC/D,UAAM,SAAS,KAAK,UAAU,IAAI,IAAI;AACtC,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,MAAM,OAAO,WACf,iBAAgB,6BAChB,iBAAgB;AACpB,WAAO,KAAK,IAAI,IAAI,OAAO,KAAK,MAAM,SAAS;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBQ,oBAAoB,MAAoB;AAC9C,SAAK,UAAU,OAAO,IAAI;AAC1B,SAAK,kBAAkB,OAAO,IAAI;AAKlC,QAAI,SAAS,iBAAgB,wBAAwB;AACnD,WAAK,gBAAgB,WAAW;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2BA,MAAc,aAAa,MAAkC;AAC3D,UAAM,QAAQ,oBAAI,IAAqB;AAEvC,UAAM,YAAY,KAAK,SAAS,IAAI,IAAI;AACxC,QAAI,WAAW;AACb,iBAAW,CAAC,MAAM,IAAI,KAAK,WAAW;AACpC,cAAM,IAAI,MAAM,IAAI;AAAA,MACtB;AAAA,IACF;AAEA,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAE1C,YAAM,cAAc,MAAM,OAAO,SAAS,IAAI;AAC9C,iBAAW,QAAQ,aAAa;AAC9B,cAAM,UAAU;AAChB,YAAI,WAAW,OAAO,QAAQ,SAAS,YAAY,CAAC,MAAM,IAAI,QAAQ,IAAI,GAAG;AAC3E,gBAAM,IAAI,QAAQ,MAAM,IAAI;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAEA,WAAO,MAAM,KAAK,MAAM,OAAO,CAAC;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoEA,MAAM,WAAW,MAAc,MAAc,SAA+C;AAG1F,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAC1C,UAAI,OAAO,SAAS,aAAa,iBAAiB,CAAC,OAAO,SAAS,aAAa,MAAO;AAQvF,UAAI,OAAO,OAAO,WAAW,WAAY;AACzC,UAAI;AACF,cAAM,KAAK,yBAAyB,QAAQ,MAAM,IAAI;AAAA,MACxD,SAAS,OAAO;AACd,aAAK,4BAA4B,OAAO,SAAS,MAAM,MAAM,MAAM,KAAK;AAAA,MAC1E;AAAA,IACF;AAGA,UAAM,YAAY,KAAK,SAAS,IAAI,IAAI;AACxC,QAAI,WAAW;AACb,gBAAU,OAAO,IAAI;AACrB,UAAI,UAAU,SAAS,GAAG;AACxB,aAAK,SAAS,OAAO,IAAI;AAAA,MAC3B;AAAA,IACF;AACA,SAAK,oBAAoB,IAAI;AAI7B,UAAM,KAAK,6BAA6B,WAAW,MAAM,MAAM;AAAA,MAC7D,QAAQ,SAAS;AAAA,IACnB,CAAC;AAGD,QAAI,SAAS,WAAW,OAAO;AAC7B,WAAK,eAAe,MAAM;AAAA,QACxB,MAAM;AAAA,QACN,cAAc;AAAA,QACd;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,MAAc,yBACZ,QACA,MACA,MACe;AACf,UAAM,MAAM,OAAO;AACnB,QAAI,OAAO,QAAQ,WAAY;AAC/B,UAAM,IAAI,KAAK,QAAQ,MAAM,IAAI;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCQ,4BACN,YACA,MACA,MACA,OACM;AACN,SAAK,OAAO;AAAA,MACV,8BAA8B,UAAU,yBAAyB,IAAI,IAAI,IAAI,whBAKrC,UAAU,+HACd,IAAI,IAAI,IAAI;AAAA,MAChD,iBAAiB,QAAQ,QAAQ;AAAA,MACjC,EAAE,QAAQ,YAAY,MAAM,MAAM,MAAM;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,MAAc,MAAgC;AAEzD,QAAI,KAAK,SAAS,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG;AACtC,aAAO;AAAA,IACT;AAGA,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAC1C,UAAI,MAAM,OAAO,OAAO,MAAM,IAAI,GAAG;AACnC,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,MAAiC;AAC/C,UAAM,QAAQ,oBAAI,IAAY;AAG9B,UAAM,YAAY,KAAK,SAAS,IAAI,IAAI;AACxC,QAAI,WAAW;AACb,iBAAW,QAAQ,UAAU,KAAK,GAAG;AACnC,cAAM,IAAI,IAAI;AAAA,MAChB;AAAA,IACF;AAGA,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAC1C,YAAM,SAAS,MAAM,OAAO,KAAK,IAAI;AACrC,aAAO,QAAQ,UAAQ,MAAM,IAAI,IAAI,CAAC;AAAA,IACxC;AAEA,WAAO,MAAM,KAAK,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,MAA4C;AAC1D,WAAO,KAAK,IAAI,UAAU,IAAI;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAkC;AACtC,WAAO,KAAK,KAAK,QAAQ;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,QAAQ,MAA4C;AACxD,WAAO,KAAK,IAAI,QAAQ,IAAI;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,QAAqC;AACnD,UAAM,QAAQ,MAAM,KAAK,KAAK,MAAM;AACpC,QAAI,QAAQ;AACV,aAAO,MAAM,OAAO,CAAC,MAAW,GAAG,WAAW,MAAM;AAAA,IACtD;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,iBAAiB,QAAoC;AACzD,UAAM,QAAQ,MAAM,KAAK,KAAK,MAAM;AACpC,WAAO,MACJ;AAAA,MACC,CAAC,MACC,KAAK,OAAO,MAAM,YAAY,EAAE,YAAY,EAAE,WAAW;AAAA,IAC7D,EACC;AAAA,MACC,CAAC,GAAQ,OACN,EAAE,SAAS,MAAM,EAAE,SAAS,MAC7B,OAAO,EAAE,IAAI,EAAE,cAAc,OAAO,EAAE,IAAI,CAAC;AAAA,IAC/C;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,MAA4C;AAC7D,WAAO,KAAK,IAAI,aAAa,IAAI;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAqC;AACzC,WAAO,KAAK,KAAK,WAAW;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,kBAAkB,aAAoC;AAE1D,UAAM,gBAAuD,CAAC;AAE9D,eAAW,CAAC,MAAM,SAAS,KAAK,KAAK,UAAU;AAC7C,iBAAW,CAAC,MAAM,IAAI,KAAK,WAAW;AACpC,cAAM,OAAO;AACb,YAAI,MAAM,cAAc,eAAe,MAAM,YAAY,aAAa;AACpE,wBAAc,KAAK,EAAE,MAAM,KAAK,CAAC;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAGA,eAAW,EAAE,MAAM,KAAK,KAAK,eAAe;AAC1C,YAAM,KAAK,WAAW,MAAM,IAAI;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,eAAe,WAAmB,SAmBN;AAChC,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,iBAAiB,SAAS,aAAa;AAC7C,UAAM,cAAc,SAAS;AAG7B,UAAM,eAAiE,CAAC;AACxE,eAAW,CAAC,MAAM,SAAS,KAAK,KAAK,UAAU;AAC7C,iBAAW,CAAC,MAAM,IAAI,KAAK,WAAW;AACpC,cAAM,OAAO;AACb,YAAI,MAAM,cAAc,aAAa,MAAM,YAAY,WAAW;AAChE,uBAAa,KAAK,EAAE,MAAM,MAAM,MAAM,KAAK,CAAC;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAEA,QAAI,aAAa,WAAW,GAAG;AAC7B,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA,SAAS;AAAA,QACT,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,kBAAkB,CAAC,EAAE,MAAM,IAAI,MAAM,IAAI,SAAS,wCAAwC,SAAS,IAAI,CAAC;AAAA,MAC1G;AAAA,IACF;AAEA,UAAM,mBAA2E,CAAC;AASlF,qBAAiB,KAAK,GAAG,KAAK,uBAAuB,cAAc,SAAS,SAAS,CAAC;AAGtF,QAAI,gBAAgB;AAElB,iBAAW,QAAQ,cAAc;AAC/B,cAAM,SAAS,MAAM,KAAK,SAAS,KAAK,MAAM,KAAK,IAAI;AACvD,YAAI,CAAC,OAAO,SAAS,OAAO,QAAQ;AAClC,qBAAW,OAAO,OAAO,QAAQ;AAC/B,6BAAiB,KAAK;AAAA,cACpB,MAAM,KAAK;AAAA,cACX,MAAM,KAAK;AAAA,cACX,SAAS,IAAI;AAAA,YACf,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAGA,YAAM,kBAAkB,IAAI,IAAI,aAAa,IAAI,OAAK,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC;AAC5E,iBAAW,QAAQ,cAAc;AAC/B,cAAM,OAAO,MAAM,KAAK,gBAAgB,KAAK,MAAM,KAAK,IAAI;AAC5D,mBAAW,OAAO,MAAM;AACtB,gBAAM,SAAS,GAAG,IAAI,UAAU,IAAI,IAAI,UAAU;AAElD,cAAI,gBAAgB,IAAI,MAAM,EAAG;AAEjC,gBAAM,UAAU,MAAM,KAAK,IAAI,IAAI,YAAY,IAAI,UAAU;AAC7D,cAAI,CAAC,SAAS;AACZ,6BAAiB,KAAK;AAAA,cACpB,MAAM,KAAK;AAAA,cACX,MAAM,KAAK;AAAA,cACX,SAAS,eAAe,IAAI,UAAU,IAAI,IAAI,UAAU;AAAA,YAC1D,CAAC;AAAA,UACH,OAAO;AACL,kBAAM,UAAU;AAChB,gBAAI,QAAQ,wBAAwB,UAAa,QAAQ,UAAU,UAAU;AAC3E,+BAAiB,KAAK;AAAA,gBACpB,MAAM,KAAK;AAAA,gBACX,MAAM,KAAK;AAAA,gBACX,SAAS,eAAe,IAAI,UAAU,IAAI,IAAI,UAAU;AAAA,cAC1D,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,iBAAiB,SAAS,GAAG;AAC/B,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA,SAAS;AAAA,QACT,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,aAAa;AACjB,eAAW,QAAQ,cAAc;AAC/B,YAAM,IAAI,OAAO,KAAK,KAAK,YAAY,WAAW,KAAK,KAAK,UAAU;AACtE,UAAI,IAAI,WAAY,cAAa;AAAA,IACnC;AACA,UAAM,aAAa,aAAa;AAGhC,eAAW,QAAQ,cAAc;AAC/B,YAAM,UAAU;AAAA,QACd,GAAG,KAAK;AAAA,QACR,qBAAqB,gBAAgB,KAAK,KAAK,YAAY,KAAK,IAAI;AAAA,QACpE,aAAa;AAAA,QACb,aAAa,eAAe,KAAK,KAAK;AAAA,QACtC,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AACA,YAAM,KAAK,SAAS,KAAK,MAAM,KAAK,MAAM,OAAO;AAAA,IACnD;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,SAAS;AAAA,MACT,aAAa;AAAA,MACb,gBAAgB,aAAa;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4CQ,uBACN,cACA,WACwD;AACxD,UAAM,WAAW,aAAa,OAAO,OAAK,EAAE,SAAS,iBAAgB,sBAAsB;AAC3F,QAAI,SAAS,WAAW,EAAG,QAAO,CAAC;AAEnC,UAAM,SAAiE,CAAC;AAExE,UAAM,YAA2B,CAAC;AAClC,UAAM,aAAsC,CAAC;AAE7C,eAAW,QAAQ,UAAU;AAM3B,YAAM,EAAE,KAAK,IAAI,mBAAmB,KAAK,IAAI;AAC7C,YAAM,SAAS,8BAAkB,UAAU,IAAI;AAC/C,UAAI,CAAC,OAAO,SAAS;AACnB,mBAAW,SAAS,OAAO,MAAM,QAAQ;AACvC,iBAAO,KAAK;AAAA,YACV,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX,SACE,aAAa,KAAK,IAAI,iEACjB,MAAM,OAAO,QAAQ,MAAM,KAAK,KAAK,GAAG,KAAK,QAAQ;AAAA,UAE9D,CAAC;AAAA,QACH;AACA;AAAA,MACF;AACA,gBAAU,KAAK,OAAO,IAAI;AAC1B,iBAAW,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,IACrC;AAEA,eAAW,aAAS,6CAAgC,WAAW,EAAE,UAAU,CAAC,GAAG;AAK7E,YAAM,QAAQ,OAAO,MAAM,KAAK,CAAC,MAAM,WAAW,MAAM,KAAK,CAAC,IAAI;AAClE,UAAI,UAAU,QAAW;AACvB,eAAO,KAAK;AAAA,UACV,MAAM,iBAAgB;AAAA,UACtB,MAAM;AAAA,UACN,SAAS,GAAG,MAAM,OAAO,IAAI,wBAAwB;AAAA,QACvD,CAAC;AACD;AAAA,MACF;AACA,aAAO,KAAK;AAAA,QACV,MAAM,iBAAgB;AAAA,QACtB,MAAM,WAAW,KAAK,GAAG,QAAQ;AAAA,QACjC,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,WAAkC;AACpD,UAAM,eAAiE,CAAC;AACxE,eAAW,CAAC,MAAM,SAAS,KAAK,KAAK,UAAU;AAC7C,iBAAW,CAAC,MAAM,IAAI,KAAK,WAAW;AACpC,cAAM,OAAO;AACb,YAAI,MAAM,cAAc,aAAa,MAAM,YAAY,WAAW;AAChE,uBAAa,KAAK,EAAE,MAAM,MAAM,MAAM,KAAK,CAAC;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAEA,QAAI,aAAa,WAAW,GAAG;AAC7B,YAAM,IAAI,MAAM,wCAAwC,SAAS,GAAG;AAAA,IACtE;AAGA,UAAM,eAAe,aAAa,KAAK,UAAQ,KAAK,KAAK,wBAAwB,MAAS;AAC1F,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI,MAAM,YAAY,SAAS,4BAA4B;AAAA,IACnE;AAEA,eAAW,QAAQ,cAAc;AAC/B,UAAI,KAAK,KAAK,wBAAwB,QAAW;AAC/C,cAAM,WAAW;AAAA,UACf,GAAG,KAAK;AAAA,UACR,UAAU,gBAAgB,KAAK,KAAK,mBAAmB;AAAA,UACvD,OAAO;AAAA,QACT;AACA,cAAM,KAAK,SAAS,KAAK,MAAM,KAAK,MAAM,QAAQ;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa,MAAc,MAA4C;AAC3E,UAAM,OAAO,MAAM,KAAK,IAAI,MAAM,IAAI;AACtC,QAAI,CAAC,KAAM,QAAO;AAElB,UAAM,OAAO;AACb,QAAI,KAAK,wBAAwB,QAAW;AAC1C,aAAO,KAAK;AAAA,IACd;AAGA,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,MAAM,OAAoD;AAC9D,UAAM,EAAE,OAAO,QAAQ,OAAO,GAAG,WAAW,IAAI,SAAS,QAAQ,YAAY,MAAM,IAAI;AAGvF,UAAM,WASD,CAAC;AAGN,UAAM,cAAc,SAAS,MAAM,SAAS,IACxC,QACA,MAAM,KAAK,KAAK,SAAS,KAAK,CAAC;AAEnC,eAAW,QAAQ,aAAa;AAC9B,YAAM,QAAQ,MAAM,KAAK,KAAK,IAAI;AAClC,iBAAW,QAAQ,OAAO;AACxB,cAAM,OAAO;AACb,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA,MAAM,MAAM,QAAQ;AAAA,UACpB,WAAW,MAAM;AAAA,UACjB,OAAO,MAAM;AAAA,UACb,OAAO,MAAM;AAAA,UACb,OAAO,MAAM;AAAA,UACb,WAAW,MAAM;AAAA,UACjB,WAAW,MAAM;AAAA,QACnB,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,WAAW;AACf,QAAI,QAAQ;AACV,YAAM,cAAc,OAAO,YAAY;AACvC,iBAAW,SAAS;AAAA,QAAO,UACzB,KAAK,KAAK,YAAY,EAAE,SAAS,WAAW,KAC3C,KAAK,SAAS,KAAK,MAAM,YAAY,EAAE,SAAS,WAAW;AAAA,MAC9D;AAAA,IACF;AAGA,QAAI,MAAM,OAAO;AACf,iBAAW,SAAS,OAAO,UAAQ,KAAK,UAAU,MAAM,KAAK;AAAA,IAC/D;AAGA,QAAI,MAAM,OAAO;AACf,iBAAW,SAAS,OAAO,UAAQ,KAAK,UAAU,MAAM,KAAK;AAAA,IAC/D;AAGA,QAAI,MAAM,cAAc,MAAM,WAAW,SAAS,GAAG;AACnD,iBAAW,SAAS,OAAO,UAAQ,KAAK,aAAa,MAAM,WAAY,SAAS,KAAK,SAAS,CAAC;AAAA,IACjG;AAGA,QAAI,MAAM,WAAW;AACnB,iBAAW,SAAS,OAAO,UAAQ,KAAK,cAAc,MAAM,SAAS;AAAA,IACvE;AAGA,QAAI,MAAM,QAAQ,MAAM,KAAK,SAAS,GAAG;AACvC,iBAAW,SAAS,OAAO,UAAQ;AACjC,cAAM,OAAO;AACb,eAAO,MAAM,QAAQ,MAAM,KAAM,KAAK,CAAC,MAAc,KAAK,KAAK,SAAS,CAAC,CAAC;AAAA,MAC5E,CAAC;AAAA,IACH;AAGA,aAAS,KAAK,CAAC,GAAG,MAAM;AACtB,YAAM,OAAQ,EAAU,MAAM,KAAK;AACnC,YAAM,OAAQ,EAAU,MAAM,KAAK;AACnC,YAAM,MAAM,OAAO,IAAI,EAAE,cAAc,OAAO,IAAI,CAAC;AACnD,aAAO,cAAc,SAAS,CAAC,MAAM;AAAA,IACvC,CAAC;AAGD,UAAM,QAAQ,SAAS;AACvB,UAAM,SAAS,OAAO,KAAK;AAC3B,UAAM,QAAQ,SAAS,MAAM,OAAO,QAAQ,QAAQ;AAEpD,WAAO;AAAA,MACL,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,aACJ,OACA,SAC6B;AAC7B,UAAM,EAAE,kBAAkB,OAAO,OAAO,IAAI,WAAW,CAAC;AACxD,QAAI,YAAY;AAChB,QAAI,SAAS;AACb,UAAM,SAA+D,CAAC;AAEtE,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,KAAK,SAAS,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,EAAE,OAAO,CAAC;AAC/D;AAAA,MACF,SAAS,GAAG;AACV;AACA,eAAO,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,MAAM,KAAK;AAAA,UACX,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,QAClD,CAAC;AACD,YAAI,CAAC,gBAAiB;AAAA,MACxB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,OAAO,MAAM;AAAA,MACb;AAAA,MACA;AAAA,MACA,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eACJ,OACA,SAC6B;AAC7B,QAAI,YAAY;AAChB,QAAI,SAAS;AACb,UAAM,SAA+D,CAAC;AAEtE,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,KAAK,WAAW,KAAK,MAAM,KAAK,MAAM,OAAO;AACnD;AAAA,MACF,SAAS,GAAG;AACV;AACA,eAAO,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,MAAM,KAAK;AAAA,UACX,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,QAClD,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,MACL,OAAO,MAAM;AAAA,MACb;AAAA,MACA;AAAA,MACA,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,WAAW,MAAc,MAAc,QAAgB,YAAoB;AACjF,WAAO,GAAG,mBAAmB,IAAI,CAAC,IAAI,mBAAmB,IAAI,CAAC,IAAI,KAAK;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,MAAc,MAAc,OAAmE;AAC9G,WAAO,KAAK,SAAS,IAAI,KAAK,WAAW,MAAM,MAAM,SAAS,UAAU,CAAC;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,SAAyC;AAGzD,QAAI,KAAK,OAAO,aAAa,oBAAoB,OAAO;AACtD,YAAM,MAAM,4GAA4G,QAAQ,QAAQ,IAAI,QAAQ,QAAQ;AAC5J,UAAI,KAAK,OAAO,YAAY,cAAc;AACxC,cAAM,IAAI,MAAM,GAAG;AAAA,MACrB;AACA,WAAK,OAAO,KAAK,GAAG;AACpB;AAAA,IACF;AACA,UAAM,MAAM,KAAK,WAAW,QAAQ,UAAU,QAAQ,UAAU,QAAQ,KAAK;AAC7E,SAAK,SAAS,IAAI,KAAK,OAAO;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,MAAc,MAAc,OAA4C;AAC1F,SAAK,SAAS,OAAO,KAAK,WAAW,MAAM,MAAM,SAAS,UAAU,CAAC;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa,MAAc,MAAc,SAKd;AAC/B,UAAM,OAAO,MAAM,KAAK,IAAI,MAAM,IAAI;AACtC,QAAI,CAAC,KAAM,QAAO;AAElB,QAAI,YAAY,EAAE,GAAI,KAAiC;AAGvD,UAAM,kBAAkB,MAAM,KAAK,WAAW,MAAM,MAAM,UAAU;AACpE,QAAI,iBAAiB,UAAU,gBAAgB,OAAO;AACpD,kBAAY,EAAE,GAAG,WAAW,GAAG,gBAAgB,MAAM;AAAA,IACvD;AAGA,QAAI,SAAS,QAAQ;AAGnB,YAAM,iBAAiB,KAAK,WAAW,MAAM,MAAM,MAAM,IAAI,IAAI,QAAQ,MAAM;AAC/E,YAAM,cAAc,KAAK,SAAS,IAAI,cAAc,KAC/C,MAAM,KAAK,WAAW,MAAM,MAAM,MAAM;AAC7C,UAAI,aAAa,UAAU,YAAY,OAAO;AAE5C,YAAI,CAAC,YAAY,SAAS,YAAY,UAAU,QAAQ,QAAQ;AAC9D,sBAAY,EAAE,GAAG,WAAW,GAAG,YAAY,MAAM;AAAA,QACnD;AAAA,MACF;AAAA,IACF,OAAO;AAGL,YAAM,cAAc,MAAM,KAAK,WAAW,MAAM,MAAM,MAAM;AAC5D,UAAI,aAAa,UAAU,YAAY,SAAS,CAAC,YAAY,OAAO;AAClE,oBAAY,EAAE,GAAG,WAAW,GAAG,YAAY,MAAM;AAAA,MACnD;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,aAAa,MAAc,UAAsD;AAC/E,UAAM,kBAAiC,CAAC,UAAU;AAChD,YAAM,aAAa,MAAM,SAAS,UAAU,eACxC,MAAM,SAAS,YAAY,iBAC3B;AACJ,eAAS;AAAA,QACP,MAAM;AAAA,QACN,cAAc,MAAM,gBAAgB;AAAA,QACpC,MAAM,MAAM,QAAQ;AAAA,QACpB,MAAM,MAAM;AAAA,MACd,CAAC;AAAA,IACH;AACA,SAAK,iBAAiB,MAAM,eAAe;AAC3C,WAAO;AAAA,MACL,aAAa,MAAM,KAAK,oBAAoB,MAAM,eAAe;AAAA,IACnE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,UAAU,MAAc,UAAqC;AAC3D,SAAK,iBAAiB,MAAM,QAAQ;AACpC,WAAO,MAAM,KAAK,oBAAoB,MAAM,QAAQ;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,eAAe,SAAmD;AACtE,UAAM,SAAoC,CAAC;AAC3C,UAAM,cAAc,SAAS,SAAS,MAAM,KAAK,KAAK,SAAS,KAAK,CAAC;AAErE,eAAW,QAAQ,aAAa;AAC9B,YAAM,QAAQ,MAAM,KAAK,KAAK,IAAI;AAClC,UAAI,MAAM,SAAS,GAAG;AACpB,eAAO,IAAI,IAAI;AAAA,MACjB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,MAAe,SAAgE;AAClG,UAAM;AAAA,MACJ,qBAAqB;AAAA,MACrB,UAAU,YAAY;AAAA,MACtB,SAAS;AAAA,IACX,IAAI,WAAW,CAAC;AAEhB,UAAM,SAAS;AACf,QAAI,QAAQ;AACZ,QAAI,WAAW;AACf,QAAI,UAAU;AACd,QAAI,SAAS;AACb,UAAM,SAA+D,CAAC;AAEtE,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAClD,UAAI,CAAC,MAAM,QAAQ,KAAK,EAAG;AAE3B,iBAAW,QAAQ,OAAO;AACxB;AACA,cAAM,OAAO;AACb,cAAM,OAAO,MAAM;AAEnB,YAAI,CAAC,MAAM;AACT;AACA,iBAAO,KAAK,EAAE,MAAM,MAAM,aAAa,OAAO,0BAA0B,CAAC;AACzE;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,aAAa,MAAM,KAAK,OAAO,MAAM,IAAI;AAE/C,cAAI,cAAc,uBAAuB,QAAQ;AAC/C;AACA;AAAA,UACF;AAEA,cAAI,CAAC,QAAQ;AACX,gBAAI,cAAc,uBAAuB,SAAS;AAChD,oBAAM,WAAW,MAAM,KAAK,IAAI,MAAM,IAAI;AAC1C,oBAAM,SAAS,EAAE,GAAI,UAAkB,GAAI,KAAa;AACxD,oBAAM,KAAK,SAAS,MAAM,MAAM,MAAM;AAAA,YACxC,OAAO;AACL,oBAAM,KAAK,SAAS,MAAM,MAAM,IAAI;AAAA,YACtC;AAAA,UACF;AACA;AAAA,QACF,SAAS,GAAG;AACV;AACA,iBAAO,KAAK;AAAA,YACV;AAAA,YACA;AAAA,YACA,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,UAClD,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,SAAS,OAAe,MAAkD;AAE9E,QAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,CAAC,EAAE,MAAM,IAAI,SAAS,4CAA4C,CAAC;AAAA,MAC7E;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,UAAU;AAC5B,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,CAAC,EAAE,MAAM,IAAI,SAAS,kCAAkC,CAAC;AAAA,MACnE;AAAA,IACF;AAEA,UAAM,OAAO;AACb,UAAM,WAAqD,CAAC;AAE5D,QAAI,CAAC,KAAK,MAAM;AACd,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,CAAC,EAAE,MAAM,QAAQ,SAAS,uCAAuC,CAAC;AAAA,MAC5E;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,OAAO;AACf,eAAS,KAAK,EAAE,MAAM,SAAS,SAAS,oCAAoC,CAAC;AAAA,IAC/E;AAEA,WAAO,EAAE,OAAO,MAAM,UAAU,SAAS,SAAS,IAAI,WAAW,OAAU;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,qBAAwC;AAC5C,UAAM,QAAQ,oBAAI,IAAY;AAG9B,eAAW,SAAS,KAAK,cAAc;AACrC,YAAM,IAAI,MAAM,IAAI;AAAA,IACtB;AAGA,eAAW,QAAQ,KAAK,SAAS,KAAK,GAAG;AACvC,YAAM,IAAI,IAAI;AAAA,IAChB;AAEA,WAAO,MAAM,KAAK,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,MAAqD;AACrE,UAAM,QAAQ,KAAK,aAAa,KAAK,OAAK,EAAE,SAAS,IAAI;AACzD,QAAI,CAAC,MAAO,QAAO;AAQnB,UAAM,SAAS,oBAAI,IAAiB;AACpC,eAAW,KAAM,MAAM,WAAW,CAAC,EAAI,QAAO,IAAI,EAAE,MAAM,CAAC;AAC3D,eAAW,SAAK,sCAAuB,IAAI,EAAG,QAAO,IAAI,EAAE,MAAM,CAAC;AAClE,UAAM,UAAU,MAAM,KAAK,OAAO,OAAO,CAAC;AAE1C,WAAO;AAAA,MACL,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,MACb,aAAa,MAAM;AAAA,MACnB,cAAc,MAAM;AAAA,MACpB,iBAAiB,MAAM;AAAA,MACvB,QAAQ,MAAM;AAAA,MACd,GAAI,QAAQ,SAAS,IAAI,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,gBAAgB,MAAc,MAA6C;AAC/E,WAAO,KAAK,aAAa,IAAI,GAAG,mBAAmB,IAAI,CAAC,IAAI,mBAAmB,IAAI,CAAC,EAAE,KAAK,CAAC;AAAA,EAC9F;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,MAAc,MAA6C;AAC7E,UAAM,aAAmC,CAAC;AAC1C,eAAW,QAAQ,KAAK,aAAa,OAAO,GAAG;AAC7C,iBAAW,OAAO,MAAM;AACtB,YAAI,IAAI,eAAe,QAAQ,IAAI,eAAe,MAAM;AACtD,qBAAW,KAAK,GAAG;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,KAA+B;AAC3C,UAAM,MAAM,GAAG,mBAAmB,IAAI,UAAU,CAAC,IAAI,mBAAmB,IAAI,UAAU,CAAC;AACvF,QAAI,CAAC,KAAK,aAAa,IAAI,GAAG,GAAG;AAC/B,WAAK,aAAa,IAAI,KAAK,CAAC,CAAC;AAAA,IAC/B;AACA,UAAM,WAAW,KAAK,aAAa,IAAI,GAAG;AAC1C,UAAM,cAAc,SAAS;AAAA,MAC3B,OAAK,EAAE,eAAe,IAAI,cAAc,EAAE,eAAe,IAAI,cAAc,EAAE,SAAS,IAAI;AAAA,IAC5F;AACA,QAAI,CAAC,aAAa;AAChB,eAAS,KAAK,GAAG;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgCA,MAAM,cAAc,OAAgF;AAClG,WAAO,KAAK,gBAAgB,MAAM,KAAK;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,KACJ,MACA,MACA,SACmB;AACnB,YAAQ,MAAM,KAAK,cAAiB,MAAM,MAAM,OAAO,GAAG;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,cACJ,MACA,MACA,SACkE;AAClE,UAAM,SAAmB,CAAC;AAC1B,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AACxC,UAAI;AACA,cAAM,SAAS,MAAM,OAAO,KAAK,MAAM,MAAM,OAAO;AACpD,YAAI,OAAO,MAAM;AACb,iBAAO,EAAE,MAAM,OAAO,MAAW,UAAU,OAAO,OAAO;AAAA,QAC7D;AAAA,MACJ,SAAS,GAAG;AACR,cAAM,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACzD,eAAO,KAAK,GAAG,OAAO,SAAS,IAAI,KAAK,OAAO,EAAE;AACjD,aAAK,OAAO,KAAK,UAAU,OAAO,SAAS,IAAI,mBAAmB,IAAI,IAAI,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC;AAAA,MAClG;AAAA,IACJ;AACA,WAAO,EAAE,MAAM,MAAM,UAAU,OAAO,SAAS,GAAG,OAAO;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,SACJ,MACA,SACc;AACd,UAAM,UAAe,CAAC;AAEtB,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AACxC,UAAI;AACA,cAAM,QAAQ,MAAM,OAAO,SAAY,MAAM,OAAO;AACpD,mBAAW,QAAQ,OAAO;AACtB,gBAAM,UAAU;AAChB,cAAI,WAAW,OAAO,QAAQ,SAAS,UAAU;AAC7C,kBAAM,SAAS,QAAQ,KAAK,CAAC,MAAW,KAAK,EAAE,SAAS,QAAQ,IAAI;AACpE,gBAAI,OAAQ;AAAA,UAChB;AACA,kBAAQ,KAAK,IAAI;AAAA,QACrB;AACA,aAAK,0BAA0B,OAAO,SAAS,IAAI;AAAA,MACvD,SAAS,GAAG;AAKR,aAAK,wBAAwB,OAAO,SAAS,MAAM,MAAM,CAAC;AAAA,MAC9D;AAAA,IACJ;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KACJ,MACA,MACA,MACA,SAC6B;AAC7B,UAAM,eAAgB,SAAiB;AAEvC,QAAI;AAEJ,QAAI,cAAc;AAChB,eAAS,KAAK,QAAQ,IAAI,YAAY;AACtC,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,qBAAqB,YAAY,EAAE;AAAA,MACrD;AAAA,IACF,OAAO;AACL,iBAAW,KAAK,KAAK,QAAQ,OAAO,GAAG;AACnC,YAAI,CAAC,EAAE,KAAM;AACb,YAAI;AACF,cAAI,MAAM,EAAE,OAAO,MAAM,IAAI,GAAG;AAC5B,qBAAS;AACT,iBAAK,OAAO,KAAK,yCAAyC,EAAE,SAAS,IAAI,EAAE;AAC3E;AAAA,UACJ;AAAA,QACF,SAAS,GAAG;AAAA,QAEZ;AAAA,MACJ;AAEA,UAAI,CAAC,QAAQ;AACX,cAAM,WAAW,KAAK,QAAQ,IAAI,YAAY;AAC9C,YAAI,YAAY,SAAS,MAAM;AAC5B,mBAAS;AAAA,QACZ;AAAA,MACF;AAEA,UAAI,CAAC,QAAQ;AACX,mBAAW,KAAK,KAAK,QAAQ,OAAO,GAAG;AACrC,cAAI,EAAE,MAAM;AACV,qBAAS;AACT;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,wCAAwC,IAAI,EAAE;AAAA,IAChE;AAEA,QAAI,CAAC,OAAO,MAAM;AAChB,YAAM,IAAI,MAAM,WAAW,OAAO,UAAU,IAAI,2BAA2B;AAAA,IAC7E;AAEA,WAAO,OAAO,KAAK,MAAM,MAAM,MAAM,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKU,iBAAiB,MAAc,UAA+B;AACtE,QAAI,CAAC,KAAK,eAAe,IAAI,IAAI,GAAG;AAClC,WAAK,eAAe,IAAI,MAAM,oBAAI,IAAI,CAAC;AAAA,IACzC;AACA,SAAK,eAAe,IAAI,IAAI,EAAG,IAAI,QAAQ;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKU,oBAAoB,MAAc,UAA+B;AACzE,UAAM,YAAY,KAAK,eAAe,IAAI,IAAI;AAC9C,QAAI,WAAW;AACb,gBAAU,OAAO,QAAQ;AACzB,UAAI,UAAU,SAAS,GAAG;AACxB,aAAK,eAAe,OAAO,IAAI;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAA8B;AAAA,EAEpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,cAAc,MAAgC;AAC5C,QAAI,KAAK,eAAe,KAAM;AAC9B,QAAI,KAAK,YAAY;AACnB,WAAK,KAAK,oBAAoB;AAAA,IAChC;AACA,SAAK,aAAa;AAClB,SAAK,kBAAkB;AACvB,SAAK,KAAK,qBAAqB;AAAA,EACjC;AAAA;AAAA,EAGA,gBAAgD;AAC9C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAM,sBAAqC;AACzC,SAAK,kBAAkB;AACvB,UAAM,OAAO,KAAK;AAClB,SAAK,gBAAgB;AACrB,QAAI,QAAQ,OAAO,KAAK,WAAW,YAAY;AAC7C,UAAI;AAAE,cAAM,KAAK,OAAO,MAAS;AAAA,MAAG,QAAQ;AAAA,MAAa;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAAyB;AAC7B,UAAM,KAAK,aAAa,EAAE,MAAM,MAAM,MAAS;AAC/C,UAAM,KAAK,oBAAoB,EAAE,MAAM,MAAM,MAAS;AACtD,SAAK,UAAU,MAAM;AACrB,SAAK,gBAAgB,WAAW;AAAA,EAClC;AAAA,EAEA,MAAc,uBAAsC;AAClD,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM;AACX,UAAM,WAAW,KAAK,MAAM,CAAC,CAAC;AAC9B,UAAM,OAAQ,SAA0C,OAAO,aAAa,EAAE;AAC9E,SAAK,gBAAgB;AACrB,QAAI;AACF,aAAO,CAAC,KAAK,iBAAiB;AAC5B,cAAM,EAAE,OAAO,KAAK,IAAI,MAAM,KAAK,KAAK;AACxC,YAAI,KAAM;AACV,YAAI;AACF,eAAK,eAAe,KAAK;AAAA,QAC3B,SAAS,KAAK;AACZ,eAAK,OAAO,KAAK,+CAA+C;AAAA,YAC9D,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UACxD,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,CAAC,KAAK,iBAAiB;AACzB,aAAK,OAAO,KAAK,+DAA+D;AAAA,UAC9E,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACxD,CAAC;AAAA,MACH;AAAA,IACF,UAAE;AACA,UAAI,KAAK,kBAAkB,KAAM,MAAK,gBAAgB;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqCU,0BAA0B,MAAc,MAAqB;AACrE,QAAI,MAAM;AACR,YAAM,YAAY,KAAK,SAAS,IAAI,IAAI;AACxC,UAAI,WAAW;AACb,kBAAU,OAAO,IAAI;AACrB,YAAI,UAAU,SAAS,EAAG,MAAK,SAAS,OAAO,IAAI;AAAA,MACrD;AAAA,IACF;AACA,SAAK,oBAAoB,IAAI;AAAA,EAC/B;AAAA;AAAA,EAGQ,eAAe,KAA0B;AAC/C,UAAM,MAAe,IAAI;AACzB,UAAM,OAAO,IAAI;AACjB,UAAM,OAAO,IAAI;AAMjB,SAAK,0BAA0B,MAAM,IAAI;AAEzC,UAAM,aACJ,IAAI,OAAO,WAAW,UACpB,IAAI,OAAO,WAAW,YACtB;AAEJ,UAAM,cAAkC;AAAA,MACtC,MAAM;AAAA,MACN,cAAc;AAAA,MACd;AAAA,MACA,MAAM;AAAA;AAAA;AAAA;AAAA,MAIN,MAAM;AAAA,MACN,WAAW,IAAI;AAAA,IACjB;AAMA,IAAC,YAAwC,MAAM,IAAI;AACnD,SAAK,eAAe,MAAM,WAAW;AAAA,EACvC;AAAA,EAEU,eAAe,MAAc,OAA2B;AAChE,SAAK,oBAAoB,MAAM,KAAK;AAIpC,QAAI,KAAK,eAAe;AACtB,YAAM,UAAyC;AAAA,QAC7C,YAAY,KAAK;AAAA,QACjB;AAAA,QACA;AAAA,MACF;AACA,YAAM,MAAM,GAAG,IAAI,IAAK,MAA4B,QAAQ,EAAE;AAC9D,WAAK,KAAK,cACP,QAAQ,iBAAgB,iBAAiB,SAAS,EAAE,cAAc,IAAI,CAAC,EACvE,MAAM,CAAC,QAAQ;AACd,aAAK,OAAO,MAAM,mCAAmC,QAAW;AAAA,UAC9D;AAAA,UACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACxD,CAAC;AAAA,MACH,CAAC;AAAA,IACL;AAAA,EACF;AAAA,EAEQ,oBAAoB,MAAc,OAA2B;AACnE,UAAM,YAAY,KAAK,eAAe,IAAI,IAAI;AAC9C,QAAI,CAAC,UAAW;AAEhB,eAAW,YAAY,WAAW;AAChC,UAAI;AACF,aAAK,SAAS,KAAK;AAAA,MACrB,SAAS,OAAO;AACd,aAAK,OAAO,MAAM,wBAAwB,QAAW;AAAA,UACnD;AAAA,UACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAC9D,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,oBAAoB,QAAiB,QAA4B;AAE/D,QAAI,KAAK,kBAAkB,UAAU,KAAK,kBAAkB,QAAQ;AAClE,aAAO,MAAM,KAAK,oBAAoB;AAAA,IACxC;AACA,SAAK,oBAAoB;AACzB,SAAK,gBAAgB;AACrB,SAAK,gBAAgB;AACrB,SAAK,qBAAqB,OAAO;AAAA,MAC/B,iBAAgB;AAAA,MAChB,CAAC,QAAQ;AACP,cAAM,IAAI,IAAI;AAEd,YAAI,GAAG,cAAc,EAAE,eAAe,KAAK,cAAe;AAC1D,YAAI,CAAC,GAAG,QAAQ,CAAC,EAAE,MAAO;AA0B1B,YAAI;AACF,eAAK,0BAA0B,EAAE,MAAM,EAAE,MAAM,IAAI;AAAA,QACrD,SAAS,KAAK;AACZ,eAAK,OAAO,MAAM,sCAAsC,QAAW;AAAA,YACjE,MAAM,EAAE;AAAA,YACR,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UACxD,CAAC;AAAA,QACH;AAIA,qBAAa,MAAM;AACjB,cAAI;AACF,iBAAK,oBAAoB,EAAE,MAAM,EAAE,KAAK;AAAA,UAC1C,SAAS,KAAK;AACZ,iBAAK,OAAO,MAAM,gCAAgC,QAAW;AAAA,cAC3D,MAAM,EAAE;AAAA,cACR,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,YACxD,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AACA,SAAK,OAAO,KAAK,8CAA8C;AAAA,MAC7D;AAAA,MACA,SAAS,iBAAgB;AAAA,IAC3B,CAAC;AACD,WAAO,MAAM,KAAK,oBAAoB;AAAA,EACxC;AAAA;AAAA,EAGA,sBAA4B;AAC1B,QAAI,KAAK,oBAAoB;AAC3B,UAAI;AAAE,aAAK,mBAAmB;AAAA,MAAG,QAAQ;AAAA,MAAmB;AAC5D,WAAK,qBAAqB;AAAA,IAC5B;AACA,SAAK,gBAAgB;AACrB,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,oBAAgD;AACtD,UAAM,WAAW,KAAK,QAAQ,IAAI,UAAU;AAC5C,QAAI,YAAY,oBAAoB,gBAAgB;AAClD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WACJ,MACA,MACA,SACqC;AACrC,UAAM,WAAW,KAAK,kBAAkB;AACxC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,8DAA8D;AAAA,IAChF;AAEA,WAAO,SAAS,aAAa,MAAM,MAAM;AAAA,MACvC,eAAe,SAAS;AAAA,MACxB,OAAO,SAAS;AAAA,MAChB,OAAO,SAAS;AAAA,MAChB,OAAO,SAAS;AAAA,MAChB,QAAQ,SAAS;AAAA,MACjB,iBAAiB,SAAS;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,SACJ,MACA,MACA,SACA,SAIkB;AAClB,UAAM,WAAW,KAAK,kBAAkB;AACxC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AAGA,UAAM,gBAAgB,MAAM,SAAS,iBAAiB,MAAM,MAAM,OAAO;AAEzE,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,WAAW,OAAO,6BAA6B,IAAI,IAAI,IAAI,EAAE;AAAA,IAC/E;AAEA,QAAI,CAAC,cAAc,UAAU;AAC3B,YAAM,IAAI,MAAM,WAAW,OAAO,kCAAkC;AAAA,IACtE;AAIA,UAAM,mBAAmB,cAAc;AACvC,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAGA,QAAI,CAAC,KAAK,SAAS,IAAI,IAAI,GAAG;AAC5B,WAAK,SAAS,IAAI,MAAM,oBAAI,IAAI,CAAC;AAAA,IACnC;AACA,SAAK,SAAS,IAAI,IAAI,EAAG,IAAI,MAAM,gBAAgB;AAEnD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KACJ,MACA,MACA,UACA,UAC6B;AAC7B,UAAM,WAAW,KAAK,kBAAkB;AACxC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AAGA,UAAM,KAAK,MAAM,SAAS,iBAAiB,MAAM,MAAM,QAAQ;AAC/D,UAAM,KAAK,MAAM,SAAS,iBAAiB,MAAM,MAAM,QAAQ;AAE/D,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,MAAM,WAAW,QAAQ,6BAA6B,IAAI,IAAI,IAAI,EAAE;AAAA,IAChF;AAEA,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,MAAM,WAAW,QAAQ,6BAA6B,IAAI,IAAI,IAAI,EAAE;AAAA,IAChF;AAEA,QAAI,CAAC,GAAG,YAAY,CAAC,GAAG,UAAU;AAChC,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAGA,UAAM,QAAQ,mBAAmB,GAAG,UAAU,GAAG,QAAQ;AACzD,UAAM,YAAY,MAAM,WAAW;AACnC,UAAM,UAAU,oBAAoB,KAAK;AAEzC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,GAAG;AAAA,MACd,WAAW,GAAG;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAvqFa,iBAmFa,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAnFjC,iBA6Fa,6BAA6B;AA7F1C,iBA6Ka,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA7K/B,iBA8La,yBAAyB;AA9L5C,IAAM,kBAAN;;;AW3QP,sBAAyB;AACzB,IAAAC,sBAA2B;;;ACK3B,IAAAC,QAAsB;AACtB,sBAAuD;;;ACDvD,SAAoB;AACpB,WAAsB;AACtB,kBAAqB;AACrB,yBAA2B;AAcpB,IAAM,mBAAN,MAAiD;AAAA,EAkBtD,YACU,SACA,aACA,QACR;AAHQ;AACA;AACA;AApBV,SAAS,WAAmC;AAAA,MAC1C,MAAM;AAAA,MACN,UAAU;AAAA,MACV,cAAc;AAAA,QACZ,MAAM;AAAA,QACN,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,MACA,kBAAkB,CAAC,QAAQ,QAAQ,cAAc,YAAY;AAAA,MAC7D,eAAe;AAAA,MACf,eAAe;AAAA,MACf,eAAe;AAAA,IACjB;AAEA,SAAQ,QAAQ,oBAAI,IAA4D;AAAA,EAM7E;AAAA,EAEH,MAAM,KACJ,MACA,MACA,SAC6B;AAC7B,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,EAAE,UAAU,YAAY,MAAM,WAAW,MAAM,YAAY,IAAI,WAAW,CAAC;AAEjF,QAAI;AAEF,YAAM,WAAW,MAAM,KAAK,SAAS,MAAM,IAAI;AAE/C,UAAI,CAAC,UAAU;AACb,eAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,aAAa;AAAA,UACb,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF;AAGA,YAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,IAAI;AAExC,UAAI,CAAC,OAAO;AACV,eAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,aAAa;AAAA,UACb,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF;AAGA,UAAI,YAAY,eAAe,MAAM,SAAS,aAAa;AACzD,eAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,aAAa;AAAA,UACb,MAAM,MAAM;AAAA,UACZ;AAAA,UACA,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF;AAGA,YAAM,WAAW,GAAG,IAAI,IAAI,IAAI;AAChC,UAAI,YAAY,KAAK,MAAM,IAAI,QAAQ,GAAG;AACxC,cAAM,SAAS,KAAK,MAAM,IAAI,QAAQ;AACtC,YAAI,OAAO,SAAS,MAAM,MAAM;AAC9B,iBAAO;AAAA,YACL,MAAM,OAAO;AAAA,YACb,WAAW;AAAA,YACX,aAAa;AAAA,YACb,MAAM,MAAM;AAAA,YACZ;AAAA,YACA,UAAU,KAAK,IAAI,IAAI;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AAGA,YAAM,UAAU,MAAS,YAAS,UAAU,OAAO;AACnD,YAAM,aAAa,KAAK,cAAc,MAAM,MAAO;AAEnD,UAAI,CAAC,YAAY;AACf,cAAM,IAAI,MAAM,mCAAmC,MAAM,MAAM,EAAE;AAAA,MACnE;AAEA,YAAM,OAAO,WAAW,YAAY,OAAO;AAG3C,UAAI,UAAU;AACZ,aAAK,MAAM,IAAI,UAAU;AAAA,UACvB;AAAA,UACA,MAAM,MAAM,QAAQ;AAAA,UACpB,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,QACL;AAAA,QACA,WAAW;AAAA,QACX,aAAa;AAAA,QACb,MAAM,MAAM;AAAA,QACZ;AAAA,QACA,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAAA,IACF,SAAS,OAAO;AACd,WAAK,QAAQ,MAAM,2BAA2B,QAAW;AAAA,QACvD;AAAA,QACA;AAAA,QACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AACD,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,SACJ,MACA,SACc;AACd,UAAM,EAAE,WAAW,CAAC,MAAM,GAAG,WAAW,aAAa,MAAM,MAAM,IAAI,WAAW,CAAC;AAEjF,UAAM,UAAe,UAAK,KAAK,SAAS,IAAI;AAC5C,UAAM,QAAa,CAAC;AAEpB,QAAI;AAEF,YAAM,eAAe,SAAS;AAAA,QAAI,aAC3B,UAAK,SAAS,OAAO;AAAA,MAC5B;AAEA,iBAAW,WAAW,cAAc;AAClC,cAAM,QAAQ,UAAM,kBAAK,SAAS;AAAA,UAChC,QAAQ,CAAC,sBAAsB,eAAe,eAAe,UAAU;AAAA,UACvE,OAAO;AAAA,QACT,CAAC;AAED,mBAAW,QAAQ,OAAO;AACxB,cAAI,SAAS,MAAM,UAAU,OAAO;AAClC;AAAA,UACF;AAEA,cAAI;AACF,kBAAM,UAAU,MAAS,YAAS,MAAM,OAAO;AAC/C,kBAAM,SAAS,KAAK,aAAa,IAAI;AACrC,kBAAM,aAAa,KAAK,cAAc,MAAM;AAE5C,gBAAI,YAAY;AACd,oBAAM,OAAO,WAAW,YAAe,OAAO;AAC9C,oBAAM,KAAK,IAAI;AAAA,YACjB;AAAA,UACF,SAAS,OAAO;AACd,iBAAK,QAAQ,KAAK,uBAAuB;AAAA,cACvC;AAAA,cACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC9D,CAAC;AAAA,UACH;AAAA,QACF;AAEA,YAAI,SAAS,MAAM,UAAU,OAAO;AAClC;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,WAAK,QAAQ,MAAM,uBAAuB,QAAW;AAAA,QACnD;AAAA,QACA;AAAA,QACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AACD,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,MAAc,MAAgC;AACzD,UAAM,WAAW,MAAM,KAAK,SAAS,MAAM,IAAI;AAC/C,WAAO,aAAa;AAAA,EACtB;AAAA,EAEA,MAAM,KAAK,MAAc,MAA6C;AACpE,UAAM,WAAW,MAAM,KAAK,SAAS,MAAM,IAAI;AAE/C,QAAI,CAAC,UAAU;AACb,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,QAAQ,MAAS,QAAK,QAAQ;AACpC,YAAM,UAAU,MAAS,YAAS,UAAU,OAAO;AACnD,YAAM,OAAO,KAAK,aAAa,OAAO;AACtC,YAAM,SAAS,KAAK,aAAa,QAAQ;AAEzC,aAAO;AAAA,QACL,MAAM,MAAM;AAAA,QACZ,YAAY,MAAM,MAAM,YAAY;AAAA,QACpC;AAAA,QACA;AAAA,QACA,MAAM;AAAA,MACR;AAAA,IACF,SAAS,OAAO;AACd,WAAK,QAAQ,MAAM,uBAAuB,QAAW;AAAA,QACnD;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,MAAiC;AAC1C,UAAM,UAAe,UAAK,KAAK,SAAS,IAAI;AAE5C,QAAI;AACF,YAAM,QAAQ,UAAM,kBAAK,QAAQ;AAAA,QAC/B,KAAK;AAAA,QACL,QAAQ,CAAC,sBAAsB,eAAe,aAAa;AAAA,QAC3D,OAAO;AAAA,MACT,CAAC;AAED,aAAO,MAAM,IAAI,UAAQ;AACvB,cAAM,MAAW,aAAQ,IAAI;AAC7B,cAAMC,YAAgB,cAAS,MAAM,GAAG;AACxC,eAAOA;AAAA,MACT,CAAC;AAAA,IACH,SAAS,OAAO;AACd,WAAK,QAAQ,MAAM,kBAAkB,QAAW;AAAA,QAC9C;AAAA,QACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AACD,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,KACJ,MACA,MACA,MACA,SAC6B;AAC7B,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM;AAAA,MACJ,SAAS;AAAA,MACT,WAAW;AAAA,MACX,SAAS;AAAA,MACT,WAAW;AAAA,MACX,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,MAAM;AAAA,IACR,IAAI,WAAW,CAAC;AAEhB,QAAI;AAEF,YAAM,aAAa,KAAK,cAAc,MAAM;AAC5C,UAAI,CAAC,YAAY;AACf,cAAM,IAAI,MAAM,mCAAmC,MAAM,EAAE;AAAA,MAC7D;AAGA,YAAM,UAAe,UAAK,KAAK,SAAS,IAAI;AAC5C,YAAM,WAAW,GAAG,IAAI,GAAG,WAAW,aAAa,CAAC;AACpD,YAAM,WAAW,cAAmB,UAAK,SAAS,QAAQ;AAG1D,UAAI,CAAC,WAAW;AACd,YAAI;AACF,gBAAS,UAAO,QAAQ;AACxB,gBAAM,IAAI,MAAM,wBAAwB,QAAQ,EAAE;AAAA,QACpD,SAAS,OAAO;AAEd,cAAK,MAAgC,SAAS,UAAU;AACtD,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAGA,YAAS,SAAW,aAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAG1D,UAAI;AACJ,UAAI,QAAQ;AACV,YAAI;AACF,gBAAS,UAAO,QAAQ;AACxB,uBAAa,GAAG,QAAQ;AACxB,gBAAS,YAAS,UAAU,UAAU;AAAA,QACxC,QAAQ;AAAA,QAER;AAAA,MACF;AAGA,YAAM,UAAU,WAAW,UAAU,MAAM;AAAA,QACzC;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAGD,UAAI,QAAQ;AACV,cAAM,WAAW,GAAG,QAAQ;AAC5B,cAAS,aAAU,UAAU,SAAS,OAAO;AAC7C,cAAS,UAAO,UAAU,QAAQ;AAAA,MACpC,OAAO;AACL,cAAS,aAAU,UAAU,SAAS,OAAO;AAAA,MAC/C;AAKA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM;AAAA;AAAA,QAEN,MAAM,OAAO,WAAW,SAAS,OAAO;AAAA,QACxC;AAAA,QACA,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAAA,IACF,SAAS,OAAO;AACd,WAAK,QAAQ,MAAM,2BAA2B,QAAW;AAAA,QACvD;AAAA,QACA;AAAA,QACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AACD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,SAAS,MAAc,MAAsC;AACzE,UAAM,UAAe,UAAK,KAAK,SAAS,IAAI;AAC5C,UAAM,aAAa,CAAC,SAAS,SAAS,QAAQ,OAAO,KAAK;AAE1D,eAAW,OAAO,YAAY;AAC5B,YAAM,WAAgB,UAAK,SAAS,GAAG,IAAI,GAAG,GAAG,EAAE;AAEnD,UAAI;AACF,cAAS,UAAO,QAAQ;AACxB,eAAO;AAAA,MACT,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,UAAkC;AACrD,UAAM,MAAW,aAAQ,QAAQ,EAAE,YAAY;AAE/C,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,QAAwD;AAC5E,WAAO,KAAK,YAAY,IAAI,MAAM;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,aAAa,SAAyB;AAC5C,UAAM,WAAO,+BAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,EAAE,UAAU,GAAG,EAAE;AAC/E,WAAO,IAAI,IAAI;AAAA,EACjB;AACF;;;ADhZO,IAAM,sBAAN,cAAkC,gBAAgB;AAAA,EAGvD,YAAY,QAAgC;AAC1C,UAAM,MAAM;AAIZ,QAAI,CAAC,OAAO,WAAW,OAAO,QAAQ,WAAW,GAAG;AAClD,YAAM,UAAU,OAAO,WAAW,QAAQ,IAAI;AAC9C,WAAK,eAAe,IAAI,iBAAiB,SAAS,KAAK,aAAa,KAAK,MAAM,CAAC;AAAA,IAClF;AAGA,QAAI,OAAO,OAAO;AAChB,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAA8B;AAClC,QAAI,KAAK,SAAS;AAChB,YAAM,KAAK,QAAQ,MAAM;AACzB,WAAK,UAAU;AAAA,IACjB;AAAA,EAEF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,UAAM,UAAU,KAAK,OAAO,WAAW,QAAQ,IAAI;AACnD,UAAM,EAAE,UAAU,CAAC,sBAAsB,aAAa,GAAG,aAAa,KAAK,IACzE,KAAK,OAAO,gBAAgB,CAAC;AAE/B,SAAK,cAAU,gBAAAC,OAAc,SAAS;AAAA,MACpC;AAAA,MACA;AAAA,MACA,eAAe;AAAA;AAAA;AAAA;AAAA,MAIf,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,gBAAgB;AAAA,IAClB,CAAC;AAED,SAAK,QAAQ,GAAG,OAAO,OAAO,aAAa;AACzC,YAAM,KAAK,gBAAgB,SAAS,QAAQ;AAAA,IAC9C,CAAC;AAED,SAAK,QAAQ,GAAG,UAAU,OAAO,aAAa;AAC5C,YAAM,KAAK,gBAAgB,WAAW,QAAQ;AAAA,IAChD,CAAC;AAED,SAAK,QAAQ,GAAG,UAAU,OAAO,aAAa;AAC5C,YAAM,KAAK,gBAAgB,WAAW,QAAQ;AAAA,IAChD,CAAC;AAED,SAAK,OAAO,KAAK,wBAAwB,EAAE,QAAQ,CAAC;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBACZ,WACA,UACe;AACf,UAAM,UAAU,KAAK,OAAO,WAAW,QAAQ,IAAI;AACnD,UAAM,eAAoB,eAAS,SAAS,QAAQ;AACpD,UAAM,QAAQ,aAAa,MAAW,SAAG;AAEzC,QAAI,MAAM,SAAS,GAAG;AACpB;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,WAAW,MAAM,MAAM,SAAS,CAAC;AACvC,UAAM,OAAY,eAAS,UAAe,cAAQ,QAAQ,CAAC;AAiC3D,SAAK,0BAA0B,MAAM,IAAI;AA+BzC,QAAI,OAAgB;AACpB,QAAI,cAAc,WAAW;AAC3B,YAAM,OAAO,MAAM,KAAK,cAAc,MAAM,MAAM,EAAE,UAAU,MAAM,CAAC;AACrE,UAAI,KAAK,UAAU;AACjB,aAAK,OAAO,MAAM,+BAA+B,QAAW;AAAA,UAC1D;AAAA,UACA,cAAc;AAAA,UACd;AAAA,UACA,QAAQ,KAAK;AAAA,QACf,CAAC;AACD;AAAA,MACF;AACA,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,QAA4B;AAAA,MAChC,MAAM;AAAA,MACN,cAAc;AAAA,MACd;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AAEA,SAAK,eAAe,MAAM,KAAK;AAAA,EACjC;AACF;;;AE5KO,IAAM,eAAN,MAA6C;AAAA,EAA7C;AACL,SAAS,WAAmC;AAAA,MAC1C,MAAM;AAAA,MACN,UAAU;AAAA,MACV,cAAc;AAAA,QACZ,MAAM;AAAA,QACN,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,IACF;AAGA;AAAA,SAAQ,UAAU,oBAAI,IAA8B;AAAA;AAAA,EAEpD,MAAM,KACJ,MACA,MACA,UAC6B;AAC7B,UAAM,YAAY,KAAK,QAAQ,IAAI,IAAI;AACvC,UAAM,OAAO,WAAW,IAAI,IAAI;AAEhC,QAAI,MAAM;AACR,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,MACZ;AAAA,IACF;AAEA,WAAO,EAAE,MAAM,KAAK;AAAA,EACtB;AAAA,EAEA,MAAM,SACJ,MACA,UACc;AACd,UAAM,YAAY,KAAK,QAAQ,IAAI,IAAI;AACvC,QAAI,CAAC,UAAW,QAAO,CAAC;AACxB,WAAO,MAAM,KAAK,UAAU,OAAO,CAAC;AAAA,EACtC;AAAA,EAEA,MAAM,OAAO,MAAc,MAAgC;AACzD,WAAO,KAAK,QAAQ,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;AAAA,EAC9C;AAAA,EAEA,MAAM,KAAK,MAAc,MAA6C;AACpE,QAAI,MAAM,KAAK,OAAO,MAAM,IAAI,GAAG;AACjC,aAAO;AAAA,QACL,MAAM;AAAA;AAAA,QACN,QAAO,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC9B,QAAQ;AAAA,MACV;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,MAAiC;AAC1C,UAAM,YAAY,KAAK,QAAQ,IAAI,IAAI;AACvC,QAAI,CAAC,UAAW,QAAO,CAAC;AACxB,WAAO,MAAM,KAAK,UAAU,KAAK,CAAC;AAAA,EACpC;AAAA,EAEA,MAAM,KACJ,MACA,MACA,MACA,UAC6B;AAC7B,QAAI,CAAC,KAAK,QAAQ,IAAI,IAAI,GAAG;AAC3B,WAAK,QAAQ,IAAI,MAAM,oBAAI,IAAI,CAAC;AAAA,IAClC;AAEA,SAAK,QAAQ,IAAI,IAAI,EAAG,IAAI,MAAM,IAAI;AAEtC,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,YAAY,IAAI,IAAI,IAAI;AAAA,MAC9B,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,MAAc,MAA6B;AACtD,UAAM,YAAY,KAAK,QAAQ,IAAI,IAAI;AACvC,QAAI,WAAW;AACb,gBAAU,OAAO,IAAI;AACrB,UAAI,UAAU,SAAS,GAAG;AACxB,aAAK,QAAQ,OAAO,IAAI;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;;;AH5GA,IAAAC,iBAA+C;AAE/C,IAAAC,iBAAgC;AAChC,IAAAC,wBAMO;AA4GP,IAAAC,eAA+D;AAC/D,IAAAA,eAA+D;AApF/D,IAAM,2BAA2B;AAAA,EAC7B;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAIA;AACJ;AAKA,IAAM,cAAc;AAUpB,IAAM,yBAAiD;AAAA,EACnD,SAAS;AAAA,EACT,kBAAkB;AAAA,EAClB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,OAAO;AAAA;AAAA;AAAA;AAAA,EAIP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,UAAU;AAAA,EACV,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,OAAO;AAAA,EACP,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,MAAM;AAAA,EACN,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUX;AA0GO,IAAM,iBAAN,MAAuC;AAAA,EAiC1C,YAAY,UAAiC,CAAC,GAAG;AAhCjD,gBAAO;AACP,gBAAO;AACP,mBAAU;AAMV;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAmB,CAAC,UAAU;AAQ9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAAuB,CAAC,iCAAiC;AA4CzD,gBAAO,OAAO,QAAuB;AACjC,UAAI,OAAO,KAAK,iCAAiC;AAAA,QAC7C,MAAM,KAAK,QAAQ,WAAW,QAAQ,IAAI;AAAA,QAC1C,OAAO,KAAK,QAAQ;AAAA,QACpB,gBAAgB,KAAK,QAAQ,gBAAgB;AAAA,MACjD,CAAC;AAGD,UAAI,gBAAgB,YAAY,KAAK,OAAO;AAC5C,cAAQ,IAAI,yEAAyE,OAAO,KAAK,QAAQ,kBAAkB;AAM3H,YAAM,qBAAqB,KAAK,QAAQ,0BAA0B;AAClE,UAAI,oBAAoB;AACpB,YAAI;AACA,gBAAM,kBAAkB,IAAI,WAAuC,UAAU;AAG7E,0BAAgB,SAAS;AAAA,YACrB,IAAI;AAAA,YACJ,MAAM;AAAA,YACN,SAAS;AAAA,YACT,MAAM;AAAA,YACN,OAAO;AAAA,YACP,mBAAmB;AAAA,YACnB,SAAS;AAAA,UACb,CAAC;AAED,cAAI,OAAO,KAAK,sCAAsC;AAAA,YAClD,WAAW,yBAAyB,IAAI,CAAC,WAAW,OAAO,IAAI;AAAA,UACnE,CAAC;AAAA,QACL,QAAQ;AAAA,QAER;AAAA,MACJ;AAEA,UAAI,OAAO,KAAK,4DAA4D;AAAA,QACxE,MAAM,KAAK,QAAQ,gBAAgB,QAAQ;AAAA,QAC3C,UAAU,CAAC,SAAS,gBAAgB,SAAS,WAAW,eAAe;AAAA,MAC3E,CAAC;AAAA,IACL;AAEA,iBAAQ,OAAO,QAAuB;AAClC,YAAM,MAAM,KAAK,QAAQ;AACzB,YAAM,OAAO,KAAK,QAAQ,QAAQ,aAAa;AAE/C,UAAI,OAAO,KAAK,2CAA2C;AAAA,QACvD,WAAW;AAAA,QACX,gBAAgB,KAAK,QAAQ;AAAA,MACjC,CAAC;AASD,UAAI,OAAQ,IAAyB,SAAS,cAAc;AACxD,cAAM,MAAO,IAAyB;AACtC,cAAM,IAAI;AAAA,UACN,yCAAyC,GAAG,wBACzC,QAAQ,iBACL,uRAIA;AAAA,QACV;AAAA,MACJ;AAEA,UAAI,SAAS,iBAAiB;AAK1B,YAAI,KAAK;AACL,gBAAM,KAAK,mBAAmB,KAAK,IAAI,MAAM,IAAI,cAAc;AAAA,QACnE,OAAO;AACH,gBAAM,IAAI,MAAM,oFAAoF;AAAA,QACxG;AAAA,MACJ,WAAW,SAAS,QAAQ;AAMxB,YAAI,KAAK;AACL,gBAAM,KAAK,mBAAmB,KAAK,IAAI,MAAM,IAAI,gBAAgB,EAAE,UAAU,KAAK,CAAC;AAAA,QACvF,OAAO;AACH,cAAI,OAAO,KAAK,8FAAyF;AAAA,QAC7G;AAAA,MACJ,OAAO;AAEH,YAAI,KAAK;AACL,gBAAM,KAAK,mBAAmB,KAAK,IAAI,MAAM,IAAI,gBAAgB,EAAE,UAAU,KAAK,CAAC;AAAA,QACvF,OAAO;AACH,gBAAM,KAAK,oBAAoB,GAAG;AAAA,QACtC;AAAA,MACJ;AAMA,YAAM,gBAAgB,KAAK,QAAQ,QAAQ,aAAa;AACxD,UAAI,kBAAkB,iBAAiB;AACnC,YAAI;AACA,gBAAMC,QAAO,MAAM,OAAO,MAAW;AACrC,gBAAM,EAAE,qBAAqB,IAAI,MAAM,OAAO,0BAA0B;AACxE,gBAAM,UAAU,KAAK,QAAQ,WAAW,QAAQ,IAAI;AACpD,gBAAM,WAAWA,MAAK,KAAK,SAAS,WAAW;AAC/C,gBAAM,OAAO,IAAI,qBAAqB;AAAA,YAClC,MAAM;AAAA,YACN,KAAK,KAAK,QAAQ,kBAAkB;AAAA,YACpC,cAAc,KAAK,QAAQ,UAAU;AAAA,UACzC,CAAC;AACD,gBAAM,KAAK,MAAM;AACjB,eAAK,aAAa;AAClB,eAAK,QAAQ,cAAc,IAAI;AAC/B,cAAI,OAAO,KAAK,kDAAkD;AAAA,YAC9D;AAAA,YACA,OAAO,KAAK,QAAQ,UAAU;AAAA,UAClC,CAAC;AAAA,QACL,SAAS,GAAQ;AACb,cAAI,OAAO,KAAK,0DAA0D;AAAA,YACtE,OAAO,GAAG;AAAA,UACd,CAAC;AAAA,QACL;AAAA,MACJ;AAGA,UAAI;AACA,cAAM,kBAAkB,IAAI,WAAW,UAAU;AACjD,YAAI,mBAAmB,OAAO,oBAAoB,YAAY,aAAa,iBAAiB;AACxF,cAAI,OAAO,KAAK,oFAAoF;AACpG,eAAK,QAAQ,mBAAmB,eAAsB;AAAA,QAC1D;AAAA,MACJ,SAAS,GAAQ;AACb,YAAI,OAAO,MAAM,2FAAsF;AAAA,UACnG,OAAO,EAAE;AAAA,QACb,CAAC;AAAA,MACL;AAYA,UAAI;AAUA,cAAM,aAAa,CAAC,SAA0C;AAC1D,cAAI;AAAE,mBAAO,IAAI,WAAwB,IAAI;AAAA,UAAG,QAAQ;AAAE,mBAAO;AAAA,UAAW;AAAA,QAChF;AACA,cAAM,aAAa,WAAW,aAAa,KAAK,WAAW,aAAa;AACxE,YAAI,cAAc,OAAO,WAAW,cAAc,YAAY;AAC1D,gBAAM,EAAE,2BAAAC,2BAA0B,IAAI,MAAM;AAC5C,gBAAM,MAAMA,2BAA0B,WAAW,UAAU,GAAG,KAAK,OAAO;AAI1E,cAAI,gBAAgB,OAAO,OAAgD,CAAC,MAAM;AAC9E,kBAAMC,OAAM,KAAK,QAAQ;AACzB,gBAAIA,MAAK,SAAS,cAAc;AAC5B,kBAAI;AACA,sBAAM,KAAK,mBAAmB,KAAKA,MAAK,MAAM,WAAW,CAACA,KAAI,IAAI,CAAC;AACnE,oBAAI,OAAO,KAAK,mDAAmD;AAAA,kBAC/D,MAAMA,KAAI;AAAA,kBACV,QAAQ,MAAM;AAAA,gBAClB,CAAC;AAAA,cACL,SAAS,GAAQ;AACb,oBAAI,OAAO,KAAK,2CAA2C,EAAE,OAAO,GAAG,QAAQ,CAAC;AAChF,sBAAM;AAAA,cACV;AAAA,YACJ;AAAA,UACJ,CAAC;AAgBD,gBAAMA,OAAM,KAAK,QAAQ;AACzB,gBAAM,oBAAoB,KAAK,QAAQ,iBAC/BA,MAAK,SAAS;AACtB,cAAIA,MAAK,SAAS,gBAAgB,qBAAqB,CAAC,gBAAgB,KAAKA,KAAI,IAAI,GAAG;AACpF,gBAAI;AACA,oBAAM,EAAE,OAAOC,eAAc,IAAI,MAAM,OAAO,UAAU;AACxD,oBAAM,IAAIA,eAAcD,KAAI,MAAM;AAAA,gBAC9B,eAAe;AAAA,gBACf,kBAAkB,EAAE,oBAAoB,IAAI,cAAc,GAAG;AAAA,gBAC7D,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAOZ,YAAY;AAAA,gBACZ,UAAU;AAAA,gBACV,gBAAgB;AAAA,cACpB,CAAC;AACD,kBAAI,UAAU;AACd,oBAAM,SAAS,YAAY;AACvB,oBAAI,QAAS;AACb,0BAAU;AACV,oBAAI;AACA,wBAAM,KAAK,mBAAmB,KAAKA,MAAK,CAACA,KAAI,IAAI,CAAC;AAClD,sBAAI,gBAAgB,yBAAyB,CAACA,KAAI,IAAI,CAAC;AACvD,sBAAI,OAAO,KAAK,0DAA0D;AAAA,oBACtE,MAAMA,KAAI;AAAA,kBACd,CAAC;AAAA,gBACL,SAAS,GAAQ;AACb,sBAAI,OAAO,KAAK,gDAAgD,EAAE,OAAO,GAAG,QAAQ,CAAC;AAAA,gBACzF,UAAE;AACE,4BAAU;AAAA,gBACd;AAAA,cACJ;AACA,gBAAE,GAAG,UAAU,MAAM;AAAE,qBAAK,OAAO;AAAA,cAAG,CAAC;AACvC,gBAAE,GAAG,OAAO,MAAM;AAAE,qBAAK,OAAO;AAAA,cAAG,CAAC;AACpC,mBAAK,kBAAkB,EAAE,OAAO,MAAM,EAAE,MAAM,EAAE;AAEhD,sBAAQ,IAAI,mDAAmDA,KAAI,IAAI;AAAA,YAC3E,SAAS,GAAQ;AACb,kBAAI,OAAO,KAAK,qDAAqD,EAAE,OAAO,GAAG,QAAQ,CAAC;AAAA,YAC9F;AAAA,UACJ;AAEA,kBAAQ,IAAI,yEAAyE;AAAA,QACzF,OAAO;AAEH,kBAAQ,IAAI,0FAAqF;AAAA,QACrG;AAAA,MACJ,SAAS,GAAQ;AAEb,gBAAQ,KAAK,oDAAoD,GAAG,OAAO;AAAA,MAC/E;AAAA,IACJ;AAEA,gBAAO,OAAO,QAAuB;AACjC,UAAI,KAAK,iBAAiB;AACtB,YAAI;AAAE,gBAAM,KAAK,gBAAgB,MAAM;AAAA,QAAG,QAAQ;AAAA,QAAa;AAC/D,aAAK,kBAAkB;AAAA,MAC3B;AACA,UAAI;AACA,cAAM,KAAK,QAAQ,QAAQ;AAAA,MAC/B,SAAS,GAAQ;AACb,YAAI,OAAO,KAAK,6CAA6C,EAAE,OAAO,GAAG,QAAQ,CAAC;AAAA,MACtF;AACA,YAAM,OAAO,KAAK;AAClB,UAAI,QAAQ,OAAO,KAAK,UAAU,YAAY;AAC1C,YAAI;AAAE,gBAAM,KAAK,MAAM;AAAA,QAAG,QAAQ;AAAA,QAAa;AAAA,MACnD;AACA,WAAK,aAAa;AAAA,IACtB;AAlTI,SAAK,UAAU;AAAA,MACX,OAAO;AAAA,MACP,GAAG;AAAA,IACP;AAEA,UAAM,UAAU,KAAK,QAAQ,WAAW,QAAQ,IAAI;AAQpD,UAAM,gBAAgB,KAAK,QAAQ,QAAQ,aAAa;AACxD,UAAM,iBACF,kBAAkB,kBAAkB,QAAS,KAAK,QAAQ,SAAS;AAEvE,SAAK,UAAU,IAAI,oBAAoB;AAAA,MACnC;AAAA,MACA,OAAO;AAAA,MACP,SAAS,CAAC,QAAQ,QAAQ,cAAc,YAAY;AAAA,IACxD,CAAC;AAGD,SAAK,QAAQ,gBAAgB,6CAA8B;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA,EA8RA,MAAc,WAAW,KAAa,gBAA2C;AAC7E,UAAM,aAAa,OAAO,QAAQ,IAAI,4BAA4B;AAClE,UAAM,YAAY,mBACV,OAAO,SAAS,UAAU,KAAK,aAAa,IAAI,aAAa,WAC9D;AACP,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,YAAY,IAAI,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS,IAAI;AAChF,QAAI;AACA,YAAM,UAAkC,EAAE,QAAQ,8BAA8B;AAChF,YAAM,MAAM,MAAM,MAAM,KAAK,EAAE,UAAU,UAAU,QAAQ,WAAW,QAAQ,QAAQ,CAAC;AACvF,UAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AACnE,YAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,aAAO,KAAK,MAAM,OAAO;AAAA,IAC7B,SAAS,GAAQ;AACb,UAAI,GAAG,SAAS,cAAc;AAC1B,cAAM,IAAI;AAAA,UACN,yBAAyB,SAAS;AAAA,QACtC;AAAA,MACJ;AACA,YAAM;AAAA,IACV,UAAE;AACE,UAAI,MAAO,cAAa,KAAK;AAAA,IACjC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAc,0BAA0B,KAAoB,KAAc,OAAgC;AACtG,UAAM,EAAE,0BAA0B,IAAI,MAAM,OAAO,yBAAyB;AAC5E,UAAM,EAAE,4BAA4B,IAAI,MAAM,OAAO,mBAAmB;AAExE,QAAI;AAEJ,UAAM,MAAM;AACZ,QAAI,KAAK,iBAAiB,KAAK,YAAY,KAAK,aAAa,QAAW;AACpE,YAAM,WAAW,0BAA0B,MAAM,GAAG;AACpD,iBAAW,SAAS;AAAA,IACxB,WAAW,KAAK,WAAW,KAAK,MAAM,UAAU;AAE5C,YAAM,WAAW,0BAA0B,MAAM,IAAI,IAAI;AACzD,iBAAW,SAAS;AAAA,IACxB,OAAO;AACH,YAAM,MAAM,4BAA4B,MAAM,GAAG;AACjD,YAAM,YAAY,KAAK,UAAU,KAAK,OAAO,KAAK,GAAG,EAAE,KAAK,CAAC;AAC7D,YAAM,eAAW,gCAAW,QAAQ,EAAE,OAAO,SAAS,EAAE,OAAO,KAAK;AACpE,YAAM,gBAAgB,KAAK,QAAQ,iBAAiB;AACpD,gCAA0B,MAAM;AAAA,QAC5B,eAAe;AAAA,QACf;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA,UAAU;AAAA,MACd,CAAC;AACD,iBAAW;AAAA,IACf;AAEA,SAAK,qBAAqB;AAE1B,UAAM,YAAY,IAAI,aAAa;AACnC,UAAM,oBACD,UAAkB,UAAU,MAAO,UAAkB,MAAM;AAChE,UAAM,kBACD,UAAkB,UAAU,WAAY,UAAkB,WAAW;AAE1E,QAAI,kBAAkB;AACtB,eAAW,CAAC,OAAO,QAAQ,KAAK,OAAO,QAAQ,sBAAsB,GAAG;AACpE,YAAM,QAAS,SAAiB,KAAK;AACrC,UAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,EAAG;AACjD,iBAAW,QAAQ,OAAO;AAMtB,YAAI,aAAa,cAAU,wCAA0B,IAAI,GAAG;AACxD,gBAAM,aACD,MAAc,MAAM,MAAM,UACvB,MAAc,MAAM,MAAM;AAClC,cAAI,CAAC,WAAY;AACjB,8CAAgB,MAAa;AAAA,YACzB,WAAW;AAAA,YACX,gBAAgB;AAAA,UACpB,CAAC;AACD,gBAAM,UAAU,KAAK,QAAQ,YAAY,IAAI;AAC7C,gBAAM,KAAK,QAAQ,SAAS,QAAQ,YAAY,MAAM,EAAE,QAAQ,MAAM,CAAC;AACvE;AACA,qBAAW,UAAM,kCAAoB,YAAY,IAAI,GAAG;AACpD,uBAAW,KAAK,GAAG,cAAc,YAAY,CAAC,GAAG;AAC7C,kBAAI,OAAO,KAAK,gDAAgD,GAAG,IAAI,MAAM,EAAE,OAAO,EAAE;AAAA,YAC5F;AACA,gDAAgB,IAAW;AAAA,cACvB,WAAW;AAAA,cACX,gBAAgB;AAAA,YACpB,CAAC;AACD,kBAAM,UAAU,KAAK,QAAQ,GAAG,MAAM,EAAE;AACxC,kBAAM,KAAK,QAAQ,SAAS,QAAQ,GAAG,MAAM,IAAI,EAAE,QAAQ,MAAM,CAAC;AAClE;AAAA,UACJ;AACA;AAAA,QACJ;AAaA,YAAI,OAAQ,MAAc;AAC1B,YAAI,CAAC,MAAM;AACP,cAAI,aAAa,QAAQ;AACrB,mBACK,MAAc,MAAM,MAAM,UACvB,MAAc,MAAM,MAAM;AAAA,UACtC;AAAA,QACJ;AACA,YAAI,CAAC,KAAM;AAKX,4CAAgB,MAAa;AAAA,UACzB,WAAW;AAAA,UACX,gBAAgB;AAAA,QACpB,CAAC;AACD,cAAM,UAAU,KAAK,UAAU,MAAM,IAAI;AACzC,cAAM,KAAK,QAAQ,SAAS,UAAU,MAAM,MAAM,EAAE,QAAQ,MAAM,CAAC;AACnE;AAAA,MACJ;AAAA,IACJ;AAEA,SAAK,QAAQ,eAAe,SAAS;AACrC,QAAI,OAAO,KAAK,6CAA6C,EAAE,QAAQ,OAAO,gBAAgB,CAAC;AAC/F,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAc,mBACV,KACA,KACA,SACa;AAIb,UAAM,KAAK,mBAAmB,KAAK,IAAI,MAAM,IAAI,gBAAgB,EAAE,UAAU,KAAK,CAAC;AACnF,QAAI;AAKA,YAAM,IAAI,QAAQ,qBAAqB,EAAE,SAAS,UAAU,KAAK,mBAAmB,CAAC;AAAA,IACzF,SAAS,GAAQ;AACb,UAAI,OAAO,KAAK,wDAAwD,EAAE,OAAO,GAAG,QAAQ,CAAC;AAAA,IACjG;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAc,mBACV,KACA,UACA,gBACA,OAA+B,CAAC,GACnB;AACb,UAAM,QAAQ,gBAAgB,KAAK,QAAQ;AAC3C,QAAI,OAAO;AAAA,MACP,0CAA0C,QAAQ,eAAe,qBAAqB;AAAA,MACtF,EAAE,MAAM,SAAS;AAAA,IACrB;AAEA,QAAI;AACJ,QAAI;AACA,UAAI,OAAO;AACP,cAAM,MAAM,KAAK,WAAW,UAAU,cAAc;AAAA,MACxD,OAAO;AACH,cAAM,UAAU,UAAM,0BAAS,UAAU,MAAM;AAC/C,cAAM,KAAK,MAAM,OAAO;AAAA,MAC5B;AAAA,IACJ,SAAS,GAAQ;AACb,UAAI,KAAK,YAAY,CAAC,SAAS,GAAG,SAAS,UAAU;AACjD,YAAI,OAAO;AAAA,UACP;AAAA,UACA,EAAE,MAAM,SAAS;AAAA,QACrB;AACA;AAAA,MACJ;AACA,YAAM,IAAI,MAAM,yCAAyC,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,MAAM,EAAE,OAAO,EAAE;AAAA,IACpH;AAEA,UAAM,KAAK,0BAA0B,KAAK,KAAK,QAAQ;AAAA,EAC3D;AAAA,EAEA,MAAc,oBAAoB,KAAmC;AACjE,QAAI,OAAO,KAAK,sCAAsC;AAEtD,UAAM,cAAc,CAAC,GAAG,6CAA8B,EACjD,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAE7C,QAAI,cAAc;AAClB,eAAW,SAAS,aAAa;AAC7B,UAAI;AACA,cAAM,QAAQ,MAAM,KAAK,QAAQ,SAAS,MAAM,MAAM;AAAA,UAClD,WAAW;AAAA,UACX,UAAU,MAAM;AAAA,QACpB,CAAC;AAED,YAAI,MAAM,SAAS,GAAG;AAClB,qBAAW,QAAQ,OAAO;AACtB,kBAAM,OAAO;AACb,gBAAI,MAAM,MAAM;AAOZ,kDAAgB,MAAM;AAAA,gBAClB,WAAW,KAAK,QAAQ;AAAA,cAC5B,CAAC;AAKD,oBAAM,KAAK,QAAQ,SAAS,MAAM,MAAM,KAAK,MAAM,MAAM,EAAE,QAAQ,MAAM,CAAC;AAAA,YAC9E;AAAA,UACJ;AACA,cAAI,OAAO,KAAK,UAAU,MAAM,MAAM,IAAI,MAAM,IAAI,mBAAmB;AACvE,yBAAe,MAAM;AAAA,QACzB;AAAA,MACJ,SAAS,GAAQ;AACb,YAAI,OAAO,MAAM,MAAM,MAAM,IAAI,mBAAmB,EAAE,OAAO,EAAE,QAAQ,CAAC;AAAA,MAC5E;AAAA,IACJ;AAEA,QAAI,OAAO,KAAK,6BAA6B;AAAA,MACzC,YAAY;AAAA,MACZ,iBAAiB,YAAY;AAAA,IACjC,CAAC;AAAA,EACL;AACJ;;;AI/yBO,IAAM,eAAN,MAA6C;AAAA,EAYlD,YAAoB,SAAyB,WAAoB;AAA7C;AAAyB;AAX7C,SAAS,WAAmC;AAAA,MAC1C,MAAM;AAAA,MACN,UAAU;AAAA,MACV,cAAc;AAAA,QACZ,MAAM;AAAA,QACN,OAAO;AAAA,QACP,OAAO;AAAA;AAAA,QACP,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EAEkE;AAAA,EAElE,IAAY,UAAU;AACpB,WAAO;AAAA,MACL,gBAAgB;AAAA,MAChB,GAAI,KAAK,YAAY,EAAE,eAAe,UAAU,KAAK,SAAS,GAAG,IAAI,CAAC;AAAA,IACxE;AAAA,EACF;AAAA,EAEA,MAAM,KACJ,MACA,MACA,UAC6B;AAC7B,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,QAC9D,QAAQ;AAAA,QACR,SAAS,KAAK;AAAA,MAChB,CAAC;AAED,UAAI,SAAS,WAAW,KAAK;AAC3B,eAAO,EAAE,MAAM,KAAK;AAAA,MACtB;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,uBAAuB,SAAS,UAAU,EAAE;AAAA,MAC9D;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,aAAO;AAAA,QACL;AAAA,QACA,QAAQ,KAAK;AAAA,QACb,QAAQ;AAAA,QACR,UAAU;AAAA,MACZ;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,8BAA8B,IAAI,IAAI,IAAI,IAAI,KAAK;AACjE,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,SACJ,MACA,UACc;AACd,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,IAAI,IAAI,IAAI;AAAA,MACtD,QAAQ;AAAA,MACR,SAAS,KAAK;AAAA,IAChB,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO,CAAC;AAAA,IACV;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAM,OAAO,MAAc,MAAgC;AACzD,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,MAC9D,QAAQ;AAAA,MACR,SAAS,KAAK;AAAA,IAChB,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,KAAK,MAAc,MAA6C;AAEpE,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,MAC9D,QAAQ;AAAA,MACR,SAAS,KAAK;AAAA,IAChB,CAAC;AAED,QAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,WAAO;AAAA,MACL,MAAM,OAAO,SAAS,QAAQ,IAAI,gBAAgB,KAAK,CAAC;AAAA,MACxD,OAAO,IAAI,KAAK,SAAS,QAAQ,IAAI,eAAe,KAAK,KAAK,IAAI,CAAC,EAAE,YAAY;AAAA,MACjF,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,MAAiC;AAC1C,UAAM,QAAQ,MAAM,KAAK,SAA2B,IAAI;AACxD,WAAO,MAAM,IAAI,OAAK,EAAE,IAAI;AAAA,EAC9B;AAAA,EAEA,MAAM,KACJ,MACA,MACA,MACA,UAC6B;AAC7B,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,MAC9D,QAAQ;AAAA,MACR,SAAS,KAAK;AAAA,MACd,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,uBAAuB,SAAS,UAAU,EAAE;AAAA,IAC9D;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,GAAG,KAAK,OAAO,IAAI,IAAI,IAAI,IAAI;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,EACF;AACF;;;AhBrHA,IAAAE,wBAA4D;;;AiBX5D,IAAAC,iBAA+C;AAU/C,SAAS,uBAAiC;AACxC,SAAO,8CACJ,OAAO,CAAC,UAAU,MAAM,eAAe,EACvC,IAAI,CAAC,UAAU,MAAM,IAAI;AAC9B;AAQO,IAAM,wBAAN,MAA4B;AAAA,EAKjC,YAAY,QAAwC,UAA0B;AAC5E,SAAK,SAAS;AACd,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,QAAI,CAAC,KAAK,OAAO,aAAa;AAC5B;AAAA,IACF;AAEA,UAAM,cAAc,KAAK,OAAO,wBAAwB,MAAM,KAAK,KAAK;AAGxE,SAAK,KAAK,WAAW;AAGrB,SAAK,eAAe,YAAY,MAAM;AACpC,WAAK,KAAK,WAAW;AAAA,IACvB,GAAG,UAAU;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACX,QAAI,KAAK,cAAc;AACrB,oBAAc,KAAK,YAAY;AAC/B,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAA2D;AAC/D,UAAM,SAAU,KAAK,SAAiB;AACtC,UAAM,mBAAoB,KAAK,SAAiB;AAChD,UAAM,iBAAkB,KAAK,SAAiB;AAI9C,QAAI,UAAU;AACd,QAAI,SAAS;AAGb,UAAM,cAAc,qBAAqB;AACzC,UAAM,WAAW,CAAC,MAChB,CAAC,CAAC,KAAK,YAAY,SAAS,CAAC;AAE/B,QAAI;AAEF,UAAI,KAAK,OAAO,YAAY;AAC1B,cAAM,aAAa,oBAAI,KAAK;AAC5B,mBAAW,QAAQ,WAAW,QAAQ,IAAI,KAAK,OAAO,UAAU;AAChE,cAAM,YAAY,WAAW,YAAY;AAEzC,cAAM,SAAkC;AAAA,UACtC,aAAa,EAAE,KAAK,UAAU;AAAA,QAChC;AAEA,YAAI,gBAAgB;AAClB,iBAAO,kBAAkB;AAAA,QAC3B;AAEA,YAAI,YAAY,SAAS,GAAG;AAE1B,iBAAO,OAAO,EAAE,MAAM,YAAY;AAAA,QACpC;AAEA,YAAI;AACF,gBAAM,SAAS,MAAM,KAAK,mBAAmB,QAAQ,kBAAkB,MAAM;AAC7E,qBAAW,OAAO;AAClB,oBAAU,OAAO;AAAA,QACnB,QAAQ;AACN;AAAA,QACF;AAAA,MACF;AAGA,UAAI,KAAK,OAAO,aAAa;AAC3B,YAAI;AAEF,gBAAM,YAAqC,CAAC;AAC5C,cAAI,eAAgB,WAAU,kBAAkB;AAEhD,gBAAM,YAAY,MAAM,OAAO,KAAK,kBAAkB;AAAA,YACpD,OAAO;AAAA,YACP,QAAQ,CAAC,QAAQ,MAAM;AAAA,UACzB,CAAC;AAED,gBAAM,aAAa,oBAAI,IAAY;AACnC,qBAAW,UAAU,WAAW;AAC9B,kBAAM,IAAI,OAAO;AACjB,kBAAM,IAAI,OAAO;AACjB,gBAAI,KAAK,KAAK,CAAC,SAAS,CAAC,GAAG;AAC1B,yBAAW,IAAI,GAAG,CAAC,IAAO,CAAC,EAAE;AAAA,YAC/B;AAAA,UACF;AAGA,qBAAW,OAAO,YAAY;AAC5B,kBAAM,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,GAAM;AACrC,kBAAM,SAAkC,EAAE,MAAM,MAAM,GAAG,UAAU;AAEnE,gBAAI;AAEF,oBAAM,iBAAiB,MAAM,OAAO,KAAK,kBAAkB;AAAA,gBACzD,OAAO;AAAA,gBACP,SAAS,CAAC,EAAE,OAAO,WAAW,OAAO,OAAgB,CAAC;AAAA,gBACtD,QAAQ,CAAC,IAAI;AAAA,cACf,CAAC;AAED,kBAAI,eAAe,SAAS,KAAK,OAAO,aAAa;AACnD,sBAAM,WAAW,eAAe,MAAM,KAAK,OAAO,WAAW;AAC7D,sBAAM,MAAM,SAAS,IAAI,OAAK,EAAE,EAAY,EAAE,OAAO,OAAO;AAC5D,sBAAM,SAAS,MAAM,KAAK,gBAAgB,QAAQ,kBAAkB,GAAG;AACvE,2BAAW,OAAO;AAClB,0BAAU,OAAO;AAAA,cACnB;AAAA,YACF,QAAQ;AACN;AAAA,YACF;AAAA,UACF;AAAA,QACF,QAAQ;AACN;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,2BAA2B,KAAK;AAC9C;AAAA,IACF;AAEA,WAAO,EAAE,SAAS,OAAO;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,mBACZ,QACA,OACA,QAC8C;AAC9C,UAAM,YAAY;AAClB,QAAI,OAAO,UAAU,eAAe,YAAY;AAC9C,YAAM,QAAQ,MAAM,UAAU,WAAW,OAAO,MAAM;AACtD,aAAO,EAAE,SAAS,OAAO,UAAU,WAAW,QAAQ,GAAG,QAAQ,EAAE;AAAA,IACrE;AAGA,UAAM,UAAU,MAAM,OAAO,KAAK,OAAO,EAAE,OAAO,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC;AAC1E,UAAM,MAAM,QAAQ,IAAI,CAAC,MAA+B,EAAE,EAAY,EAAE,OAAO,OAAO;AACtF,WAAO,KAAK,gBAAgB,QAAQ,OAAO,GAAG;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBACZ,QACA,OACA,KAC8C;AAC9C,QAAI,IAAI,WAAW,EAAG,QAAO,EAAE,SAAS,GAAG,QAAQ,EAAE;AAErD,UAAM,YAAY;AAClB,QAAI,OAAO,UAAU,eAAe,YAAY;AAC9C,YAAM,SAAS,MAAM,UAAU,WAAW,OAAO,GAAG;AACpD,aAAO;AAAA,QACL,SAAS,OAAO,WAAW,WAAW,SAAS,IAAI;AAAA,QACnD,QAAQ;AAAA,MACV;AAAA,IACF;AAGA,QAAI,UAAU;AACd,QAAI,SAAS;AACb,eAAW,MAAM,KAAK;AACpB,UAAI;AACF,cAAM,OAAO,OAAO,OAAO,EAAE;AAC7B;AAAA,MACF,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,SAAS,OAAO;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAIH;AACD,UAAM,SAAU,KAAK,SAAiB;AACtC,UAAM,mBAAoB,KAAK,SAAiB;AAChD,UAAM,iBAAkB,KAAK,SAAiB;AAE9C,QAAI,eAAe;AACnB,QAAI,iBAAiB;AAGrB,UAAM,cAAc,qBAAqB;AACzC,UAAM,WAAW,CAAC,MAChB,CAAC,CAAC,KAAK,YAAY,SAAS,CAAC;AAE/B,QAAI;AACF,YAAM,YAAqC,CAAC;AAC5C,UAAI,eAAgB,WAAU,kBAAkB;AAGhD,UAAI,KAAK,OAAO,YAAY;AAC1B,cAAM,aAAa,oBAAI,KAAK;AAC5B,mBAAW,QAAQ,WAAW,QAAQ,IAAI,KAAK,OAAO,UAAU;AAChE,cAAM,YAAY,WAAW,YAAY;AAEzC,cAAM,SAAkC;AAAA,UACtC,aAAa,EAAE,KAAK,UAAU;AAAA,UAC9B,GAAG;AAAA,QACL;AACA,YAAI,YAAY,SAAS,GAAG;AAC1B,iBAAO,OAAO,EAAE,MAAM,YAAY;AAAA,QACpC;AAEA,uBAAe,MAAM,OAAO,MAAM,kBAAkB;AAAA,UAClD,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAGA,UAAI,KAAK,OAAO,aAAa;AAC3B,cAAM,YAAY,MAAM,OAAO,KAAK,kBAAkB;AAAA,UACpD,OAAO;AAAA,UACP,QAAQ,CAAC,QAAQ,MAAM;AAAA,QACzB,CAAC;AAED,cAAM,aAAa,oBAAI,IAAY;AACnC,mBAAW,UAAU,WAAW;AAC9B,gBAAM,IAAI,OAAO;AACjB,gBAAM,IAAI,OAAO;AACjB,cAAI,KAAK,KAAK,CAAC,SAAS,CAAC,GAAG;AAC1B,uBAAW,IAAI,GAAG,CAAC,IAAO,CAAC,EAAE;AAAA,UAC/B;AAAA,QACF;AAEA,mBAAW,OAAO,YAAY;AAC5B,gBAAM,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,GAAM;AACrC,gBAAM,SAAkC,EAAE,MAAM,MAAM,GAAG,UAAU;AAEnE,gBAAM,QAAQ,MAAM,OAAO,MAAM,kBAAkB;AAAA,YACjD,OAAO;AAAA,UACT,CAAC;AAED,cAAI,QAAQ,KAAK,OAAO,aAAa;AACnC,8BAAkB,QAAQ,KAAK,OAAO;AAAA,UACxC;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,gCAAgC,KAAK;AAAA,IACrD;AAKA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,eAAe;AAAA,IACxB;AAAA,EACF;AACF;;;AC5TA;AAAA;AAAA;AAAA;;;ACKO,IAAM,oBAAN,MAAwB;AAAA,EAC7B,YAAoB,QAAuB;AAAvB;AAAA,EAAwB;AAAA,EAE5C,MAAM,iBAAiB,WAAkD;AACvE,YAAQ,IAAI,wBAAwB,UAAU,IAAI,KAAK,UAAU,EAAE,GAAG;AAEtE,eAAW,MAAM,UAAU,YAAY;AACrC,UAAI;AACF,cAAM,KAAK,iBAAiB,EAAE;AAAA,MAChC,SAAS,GAAG;AACV,gBAAQ,MAAM,+BAA+B,GAAG,IAAI,KAAK,CAAC;AAC1D,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,iBAAiB,IAAoD;AACjF,YAAQ,GAAG,MAAM;AAAA,MACf,KAAK;AACH,gBAAQ,IAAI,sBAAsB,GAAG,OAAO,IAAI,EAAE;AAClD,cAAM,KAAK,OAAO,iBAAiB,GAAG,OAAO,MAAM,GAAG,MAAM;AAC5D;AAAA,MACF,KAAK;AACH,gBAAQ,IAAI,kBAAkB,GAAG,UAAU,IAAI,GAAG,SAAS,EAAE;AAC7D,cAAM,KAAK,OAAO,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,KAAK;AACjE;AAAA,MACF,KAAK;AACH,gBAAQ,IAAI,qBAAqB,GAAG,UAAU,IAAI,GAAG,SAAS,EAAE;AAChE,cAAM,KAAK,OAAO,WAAW,GAAG,YAAY,GAAG,SAAS;AACxD;AAAA,MACF,KAAK;AACH,gBAAQ,IAAI,sBAAsB,GAAG,UAAU,EAAE;AACjD,cAAM,KAAK,OAAO,eAAe,GAAG,UAAU;AAC9C;AAAA,MACF,KAAK;AACH,gBAAQ,IAAI,iBAAiB;AAC7B,cAAM,KAAK,OAAO,WAAW,GAAG,GAAG;AACnC;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,qBAAqB,GAAG,UAAU,IAAI,GAAG,SAAS,0BAA0B;AACzF;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,sBAAsB,GAAG,OAAO,OAAO,GAAG,OAAO,0BAA0B;AACxF;AAAA,MACF;AACE,cAAM,IAAI,MAAM,wBAAwB;AAAA,IAC5C;AAAA,EACF;AACF;","names":["import_api","path","list","envelope","path","import_node_crypto","path","basename","chokidarWatch","import_kernel","import_shared","import_metadata_core","import_spec","path","registerMetadataHmrRoutes","src","chokidarWatch","import_metadata_core","import_kernel"]}
1
+ {"version":3,"sources":["../src/routes/hmr-routes.ts","../src/index.ts","../src/metadata-manager.ts","../src/serializers/json-serializer.ts","../src/serializers/yaml-serializer.ts","../src/serializers/typescript-serializer.ts","../src/loaders/database-loader.ts","../src/utils/metadata-history-utils.ts","../src/utils/lru-cache.ts","../src/utils/schema-sync-errors.ts","../src/migrations/migrate-project-id-to-environment-id.ts","../src/endpoint-matcher.ts","../src/stored-envelope.ts","../src/plugin.ts","../src/node-metadata-manager.ts","../src/loaders/filesystem-loader.ts","../src/loaders/memory-loader.ts","../src/loaders/remote-loader.ts","../src/utils/history-cleanup.ts","../src/migration/index.ts","../src/migration/executor.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata HMR (Hot Module Replacement) SSE endpoint\n *\n * Streams metadata change events to connected clients (Studio) over\n * Server-Sent Events. Closes the \"agent edits a source file → Studio\n * preview refreshes\" loop without requiring a manual page reload.\n *\n * Routes:\n * GET /api/v1/dev/metadata-events — stream of events\n * POST /api/v1/dev/metadata-events — manual reload trigger\n * body (optional): { reason?: string,\n * changed?: string[] }\n *\n * Event payloads (JSON):\n * - metadata-change: { type, metadataType, name, path?, timestamp }\n * - reload: { reason, timestamp, changed?: string[] }\n *\n * Heartbeat: `: ping` SSE comment lines every 15s.\n *\n * Two event sources feed the same in-process broadcast hub:\n * 1. The MetadataManager filesystem watcher (when `watch: true`).\n * 2. POST /api/v1/dev/metadata-events — used by external watch-recompile\n * pipelines (e.g. `os dev` watching TS sources) to invalidate\n * previews after rebuilding the artifact.\n */\n\nimport type { MetadataManager } from '../metadata-manager.js';\n\ninterface ChangeEvent {\n kind: 'metadata-change';\n type: 'added' | 'changed' | 'deleted';\n metadataType: string;\n name: string;\n path?: string;\n timestamp: number;\n /** Canonical repo seq (ADR-0008); absent for legacy chokidar events. */\n seq?: number;\n}\n\ninterface ReloadEvent {\n kind: 'reload';\n reason: string;\n changed?: string[];\n timestamp: number;\n}\n\ntype BroadcastEvent = ChangeEvent | ReloadEvent;\n\ntype Listener = (evt: BroadcastEvent) => void;\n\n/**\n * Hub returned by `registerMetadataHmrRoutes`. Callers (e.g. MetadataPlugin)\n * can use `broadcastReload()` from elsewhere — for example, after reloading\n * an artifact from disk — to push a reload event to all connected clients.\n */\nexport interface MetadataHmrHub {\n broadcastReload(reason: string, changed?: string[]): void;\n /**\n * Hook a custom handler that runs when POST is called. Useful for\n * triggering an artifact reload before the broadcast goes out.\n * Receives the parsed request body. May be async.\n */\n setOnPostReload(fn: (body: { reason?: string; changed?: string[] }) => void | Promise<void>): void;\n listenerCount(): number;\n}\n\nexport function registerMetadataHmrRoutes(\n app: any,\n manager: MetadataManager,\n options: { path?: string } = {},\n): MetadataHmrHub {\n const routePath = options.path ?? '/api/v1/dev/metadata-events';\n\n // In-process broadcast hub. Each SSE connection registers a listener;\n // both the FS watcher and the POST handler call into the hub.\n const listeners = new Set<Listener>();\n const broadcast = (evt: BroadcastEvent) => {\n for (const l of listeners) {\n try { l(evt); } catch { /* swallow — one bad listener shouldn't break others */ }\n }\n };\n\n // Wire FS watcher → hub for every currently-registered metadata type.\n // Captures `subscribe` once; if MetadataManager lacks it (older build)\n // we silently degrade to POST-only.\n let fsHookInstalled = false;\n const installFsHooks = async () => {\n if (fsHookInstalled) return;\n const mgr = manager as any;\n if (typeof mgr.subscribe !== 'function') {\n fsHookInstalled = true;\n return;\n }\n const types = await manager.getRegisteredTypes();\n for (const type of types) {\n mgr.subscribe(type, (evt: any) => {\n const ts = typeof evt.timestamp === 'string'\n ? Date.parse(evt.timestamp)\n : (evt.timestamp ?? Date.now());\n broadcast({\n kind: 'metadata-change',\n type: evt.type ?? 'changed',\n metadataType: evt.metadataType ?? type,\n name: evt.name ?? '',\n path: evt.path,\n timestamp: Number.isFinite(ts) ? ts : Date.now(),\n // Forward the canonical server-side sequence number when the\n // event originated from a MetadataRepository (ADR-0008). Legacy\n // chokidar-driven events have no seq — clients fall back to\n // their local counter in that case.\n ...(typeof evt.seq === 'number' ? { seq: evt.seq } : {}),\n } as BroadcastEvent);\n });\n }\n fsHookInstalled = true;\n };\n // Fire-and-forget; the first connection will await it in its handler\n // anyway via getRegisteredTypes().\n installFsHooks().catch(() => { /* noop */ });\n\n let onPostReload: ((body: { reason?: string; changed?: string[] }) => void | Promise<void>) | null = null;\n\n // ── GET: SSE stream ────────────────────────────────────────────────\n app.get(routePath, async (c: any) => {\n // Make sure FS hooks are installed even if installFsHooks() raced.\n await installFsHooks().catch(() => { /* noop */ });\n const types = await manager.getRegisteredTypes().catch(() => [] as string[]);\n\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const enc = new TextEncoder();\n let closed = false;\n\n const safeEnqueue = (chunk: string) => {\n if (closed) return;\n try { controller.enqueue(enc.encode(chunk)); }\n catch { closed = true; }\n };\n\n const listener: Listener = (evt) => {\n if (closed) return;\n const eventName = evt.kind === 'reload' ? 'reload' : 'metadata-change';\n safeEnqueue(`event: ${eventName}\\ndata: ${JSON.stringify(evt)}\\n\\n`);\n };\n listeners.add(listener);\n\n safeEnqueue(`event: ready\\ndata: ${JSON.stringify({ types, timestamp: Date.now() })}\\n\\n`);\n\n const heartbeat = setInterval(() => {\n safeEnqueue(`: ping ${Date.now()}\\n\\n`);\n }, 15_000);\n\n const cleanup = () => {\n if (closed) return;\n closed = true;\n clearInterval(heartbeat);\n listeners.delete(listener);\n try { controller.close(); } catch { /* noop */ }\n };\n\n const signal: AbortSignal | undefined = c.req?.raw?.signal;\n if (signal) {\n if (signal.aborted) cleanup();\n else signal.addEventListener('abort', cleanup, { once: true });\n }\n },\n });\n\n return new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream; charset=utf-8',\n 'Cache-Control': 'no-cache, no-transform',\n 'Connection': 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n });\n });\n\n // ── POST: manual reload trigger ────────────────────────────────────\n // The CLI's watch-recompile loop posts here after rebuilding the\n // artifact. Optional body: { reason?: string, changed?: string[] }.\n app.post(routePath, async (c: any) => {\n let body: { reason?: string; changed?: string[] } = {};\n try {\n // Hono: c.req.json() throws on empty body — guard it.\n const ct = c.req?.header?.('content-type') ?? '';\n if (typeof c.req?.json === 'function' && ct.includes('json')) {\n body = await c.req.json();\n }\n } catch { /* empty / invalid body OK */ }\n\n try {\n if (onPostReload) await onPostReload(body);\n } catch (e: any) {\n return new Response(\n JSON.stringify({ ok: false, error: e?.message ?? 'reload handler failed' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } },\n );\n }\n\n const reason = body.reason ?? 'manual-trigger';\n broadcast({\n kind: 'reload',\n reason,\n changed: body.changed,\n timestamp: Date.now(),\n });\n return new Response(\n JSON.stringify({ ok: true, listeners: listeners.size, reason }),\n { status: 200, headers: { 'Content-Type': 'application/json' } },\n );\n });\n\n return {\n broadcastReload(reason, changed) {\n broadcast({ kind: 'reload', reason, changed, timestamp: Date.now() });\n },\n setOnPostReload(fn) { onPostReload = fn; },\n listenerCount: () => listeners.size,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/metadata\n * \n * Metadata loading, saving, and persistence for ObjectStack.\n * Implements the IMetadataService contract from @objectstack/spec.\n */\n\n// Main Manager\nexport { MetadataManager, type WatchCallback, type MetadataManagerOptions } from './metadata-manager.js';\n\n// Plugin\nexport { MetadataPlugin } from './plugin.js';\n\n// Loaders\nexport { type MetadataLoader } from './loaders/loader-interface.js';\nexport { MemoryLoader } from './loaders/memory-loader.js';\nexport { RemoteLoader } from './loaders/remote-loader.js';\nexport { DatabaseLoader, type DatabaseLoaderOptions } from './loaders/database-loader.js';\n\n// Objects\nexport { SysMetadataObject, SysMetadataHistoryObject } from '@objectstack/metadata-core';\n\n// Routes\n// NOTE: `registerMetadataHistoryRoutes` (Hono-style) was removed —\n// the canonical history / publish / rollback / diff REST surface\n// lives in `packages/rest/src/rest-server.ts` and is wired by the\n// REST plugin on every running app.\n\n// Utils\nexport { calculateChecksum, generateSimpleDiff, generateDiffSummary } from './utils/metadata-history-utils.js';\nexport { HistoryCleanupManager } from './utils/history-cleanup.js';\n\n// Serializers\nexport { type MetadataSerializer, type SerializeOptions } from './serializers/serializer-interface.js';\nexport { JSONSerializer } from './serializers/json-serializer.js';\nexport { YAMLSerializer } from './serializers/yaml-serializer.js';\nexport * as Migration from './migration/index.js';\nexport { TypeScriptSerializer } from './serializers/typescript-serializer.js';\n\n// Re-export types from spec\nexport type {\n MetadataFormat,\n MetadataStats,\n MetadataLoadOptions,\n MetadataSaveOptions,\n MetadataLoadResult,\n MetadataSaveResult,\n MetadataWatchEvent,\n MetadataCollectionInfo,\n MetadataLoaderContract,\n MetadataManagerConfig,\n MetadataHistoryRecord,\n MetadataHistoryQueryOptions,\n MetadataHistoryQueryResult,\n MetadataDiffResult,\n MetadataHistoryRetentionPolicy,\n} from '@objectstack/spec/system';\n\n// Re-export IMetadataService contract.\n// [#4538] `MetadataExportOptions` / `MetadataImportOptions` moved into this\n// block: this package used to re-export the same-named system-entry bags\n// (`output`/`source`-flavored, removed with #4538) while `MetadataManager`\n// implements the contracts shapes — the public re-export was pointing at the\n// wrong declaration.\nexport type {\n IMetadataService,\n MetadataWatchCallback,\n MetadataWatchHandle,\n MetadataExportOptions,\n MetadataImportOptions,\n MetadataTypeInfo,\n MetadataImportResult,\n} from '@objectstack/spec/contracts';\n\n// Re-export kernel types for plugin protocol\nexport type {\n MetadataType,\n MetadataTypeRegistryEntry,\n MetadataPluginConfig,\n MetadataPluginManifest,\n MetadataQuery,\n MetadataQueryResult,\n MetadataValidationResult,\n MetadataBulkResult,\n MetadataDependency,\n} from '@objectstack/spec/kernel';\n\n// Re-export the new Repository contract (ADR-0008) so downstream consumers\n// (ObjectQL schema registry, Studio, CLI) can import from one place.\nexport type {\n MetadataRepository,\n MetadataEvent,\n MetadataItem,\n MetadataItemHeader,\n MetaRef,\n WatchFilter,\n HistoryOptions,\n} from '@objectstack/metadata-core';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata Manager\n * \n * Main orchestrator for metadata loading, saving, and persistence.\n * Implements the IMetadataService contract from @objectstack/spec.\n * Browser-compatible (Pure).\n */\n\nimport type {\n MetadataManagerConfig,\n MetadataLoadOptions,\n MetadataSaveOptions,\n MetadataSaveResult,\n MetadataWatchEvent,\n MetadataFormat,\n PackagePublishResult,\n MetadataHistoryQueryOptions,\n MetadataHistoryQueryResult,\n MetadataDiffResult,\n} from '@objectstack/spec/system';\nimport type {\n IMetadataService,\n MetadataWatchCallback,\n MetadataWatchHandle,\n MetadataExportOptions,\n MetadataImportOptions,\n MetadataImportResult,\n MetadataTypeInfo,\n MetadataWriteOptions,\n IRealtimeService,\n RealtimeEventPayload,\n IPubSub,\n Unsubscribe,\n} from '@objectstack/spec/contracts';\nimport type {\n MetadataQuery,\n MetadataQueryResult,\n MetadataValidationResult,\n MetadataBulkResult,\n MetadataDependency,\n MetadataTypeRegistryEntryParsed,\n} from '@objectstack/spec/kernel';\nimport type { MetadataOverlay } from '@objectstack/spec/kernel';\nimport { getMetadataTypeActions } from '@objectstack/spec/kernel';\nimport {\n MetadataEventType,\n MetadataEventSchema,\n type MetadataEvent as RealtimeMetadataEvent,\n} from '@objectstack/spec/api';\n// [#5189, #5040 E7b] The endpoint publish gates, reused verbatim — see\n// `gateApiItemsForPublish`.\nimport {\n ApiEndpointSchema,\n validateApiEndpointDeclarations,\n type ApiEndpoint,\n} from '@objectstack/spec/api';\nimport {\n assertMetadataRegisterContract,\n canonicalMetadataServiceType,\n createLogger,\n type Logger,\n} from '@objectstack/core';\nimport { JSONSerializer } from './serializers/json-serializer.js';\nimport { YAMLSerializer } from './serializers/yaml-serializer.js';\nimport { TypeScriptSerializer } from './serializers/typescript-serializer.js';\nimport type { MetadataSerializer } from './serializers/serializer-interface.js';\nimport type { IDataDriver, IDataEngine } from '@objectstack/spec/contracts';\nimport type { MetadataLoader } from './loaders/loader-interface.js';\nimport { DatabaseLoader } from './loaders/database-loader.js';\nimport { generateSimpleDiff, generateDiffSummary } from './utils/metadata-history-utils.js';\nimport type {\n MetadataRepository,\n MetadataEvent,\n MetaRef,\n} from '@objectstack/metadata-core';\nimport { EndpointMatcher } from './endpoint-matcher.js';\n// [#5309] The stored ENVELOPE / authored BODY split — peeled before any spec\n// schema parses a stored row. See `stored-envelope.ts`.\nimport { peelStoredEnvelope } from './stored-envelope.js';\nimport type { ApiEndpointMatch } from '@objectstack/spec/contracts';\n\n/**\n * Watch callback function (legacy)\n */\nexport type WatchCallback = (event: MetadataWatchEvent) => void | Promise<void>;\n\n/**\n * [#5654] The two methods `capabilities.write` promises on a `datasource:`\n * loader, in the order an item meets them: written by\n * {@link MetadataManager.register}, taken back out by\n * {@link MetadataManager.unregister}. Both are `?` on {@link MetadataLoader}\n * because the other protocols legitimately have neither.\n */\nexport type WritableLoaderMethod = 'save' | 'delete';\n\nconst WRITABLE_LOADER_METHODS: readonly WritableLoaderMethod[] = ['save', 'delete'];\n\n/** How the message names each method, and what the author has to write. */\nconst WRITABLE_LOADER_METHOD_SIGNATURE: Record<WritableLoaderMethod, string> = {\n save: 'save(type: string, name: string, data: any, options?: MetadataSaveOptions): Promise<MetadataSaveResult>',\n delete: 'delete(type: string, name: string): Promise<void>',\n};\n\n/**\n * [#5276, #5654] The registration gate's message for a loader that declares it\n * can be written to but cannot actually carry out one (or both) halves of that\n * write.\n *\n * Built here rather than inline so the gate and its tests quote one text, in the\n * shape AGENTS.md → \"Degradation log levels\" asks of a loud failure: the\n * **consequence** (concretely, and that the system keeps looking healthy) and\n * the **fix** (both ways out, so the author does not have to guess which one\n * their loader wants).\n *\n * `missing` is the subset of {@link WRITABLE_LOADER_METHODS} the loader does not\n * implement; each one contributes its own consequence sentence, because the two\n * failures are durability failures in opposite directions — a `save`-less loader\n * loses the row that was never written, a `delete`-less one keeps the row that\n * was supposed to go.\n */\nexport function buildWritableLoaderMissingMethodsMessage(\n loaderName: string,\n missing: readonly WritableLoaderMethod[],\n): string {\n const missingPhrase =\n missing.length === 2\n ? 'implements neither a `save()` nor a `delete()` method'\n : `implements no \\`${missing[0]}()\\` method`;\n\n const consequences = missing.map((method) =>\n method === 'save'\n ? 'Registered as-is, every write would be a silent lie: `register()` skips a loader that cannot save, then ' +\n 'writes the in-memory registry, invalidates the list cache, announces a `created`/`updated` event and ' +\n 'notifies watchers, so the caller (Studio/Setup, REST PUT, the CLI, a package publish) is told the write ' +\n `succeeded while nothing ever reaches \\`${loaderName}\\` — the item reads back correctly for the life of ` +\n 'this process and is gone at the next restart, with nothing to retry it. '\n : 'Registered as-is, every deletion would be a silent lie: `unregister()` skips a loader that cannot delete, ' +\n 'then drops the registry entry, invalidates the list cache and announces a `deleted` event, so the caller ' +\n '(Studio/Setup, REST DELETE, the CLI, a package teardown) is told the delete succeeded while the row stays ' +\n `in \\`${loaderName}\\` and is read straight back out by the very next \\`list()\\`/\\`get()\\` — across ` +\n 'restarts, with nothing to retry it. ',\n );\n\n const repair = missing\n .map((method) => `\\`${WRITABLE_LOADER_METHOD_SIGNATURE[method]}\\``)\n .join(' and ');\n\n return (\n `[MetadataManager] Refusing to register metadata loader \\`${loaderName}\\`: it declares ` +\n `\\`protocol: 'datasource:'\\` with \\`capabilities.write: true\\` but ${missingPhrase}. ` +\n 'A write-capable datasource loader is written to AND deleted from — `register()` persists every item into it, ' +\n 'and `unregister()` has to take those rows back out again. ' +\n consequences.join('') +\n `Fix: either implement ${repair} on \\`${loaderName}\\` ` +\n '(`DatabaseLoader` in this package is the reference implementation), or, if the loader is genuinely read-only, ' +\n \"declare `capabilities.write: false` — a read-only `datasource:` loader registers without complaint and is \" +\n 'never written to in the first place.'\n );\n}\n\n/**\n * [#5276, #5654] Registration gate: a `datasource:` loader that declares\n * `capabilities.write` MUST implement **both** `save()` and `delete()`.\n *\n * `capabilities.write` used to mean three different things at the three places\n * it is read — \"persist into me\" to {@link MetadataManager.register}, which\n * duck-typed `save` and **silently skipped** a loader that had none; nothing at\n * all to {@link MetadataManager.unregister}, which did the same with `delete`;\n * and, on the contract, a flat promise that the store can be written. That is\n * the declared ≠ enforced shape (Prime Directive #10), and the cure it\n * prescribes is to enforce the declaration, not to tolerate the gap: the loader\n * is rejected at registration, where the author is standing, instead of losing a\n * row at write or delete time in a deployment nobody is watching.\n *\n * #5276 closed the `delete` half; #5654 closed the `save` half, which was the\n * same shape one direction over — and leaving it open meant one declaration was\n * binding at one end of an item's life and decorative at the other.\n *\n * Scope is deliberately exactly the combination `register()`/`unregister()` act\n * on. Other protocols (`file:`, `memory:`, `http:`, `s3:`) are never written to\n * by the manager at runtime — both loops filter on `datasource:` too — so they\n * have neither a write nor a deletion of their own to lose and are not gated. A\n * `datasource:` loader with `capabilities.write: false` is likewise untouched by\n * both paths.\n */\nfunction assertWritableLoaderContract(loader: MetadataLoader): void {\n const { name, protocol, capabilities } = loader.contract;\n if (protocol !== 'datasource:' || capabilities.write !== true) return;\n const missing = WRITABLE_LOADER_METHODS.filter((method) => typeof loader[method] !== 'function');\n if (missing.length === 0) return;\n throw new Error(buildWritableLoaderMissingMethodsMessage(name, missing));\n}\n\n/**\n * [#5189] Appended to the namespace gate's message when `publishPackage` was\n * called without one, because the gate's own text (\"declare an explicit\n * `manifest.namespace`\") describes a stack file this caller may not have.\n */\nconst PUBLISH_NAMESPACE_REMEDY =\n 'From `MetadataManager.publishPackage` specifically: this method indexes items by `packageId` and '\n + 'carries no manifest, so it cannot prove a namespace on its own and will not infer one from the '\n + 'items being published (an author-supplied value would make the carve-out gate vacuous). Pass the '\n + \"package's explicit namespace as `publishPackage(id, { namespace })`, or publish the endpoints as \"\n + 'part of a stack artifact (`defineStack` → compile → artifact ingest), which carries the manifest '\n + 'and runs these same gates at parse time.';\n\n/**\n * RFC-4122 v4 uuid for realtime `MetadataEvent.id` (#4602).\n * Prefers `crypto.randomUUID`; the fallback keeps browser-compatible (Pure)\n * environments without WebCrypto working while still satisfying\n * `MetadataEventSchema`'s `z.string().uuid()`.\n */\nfunction generateEventUuid(): string {\n const c = globalThis.crypto;\n if (c && typeof c.randomUUID === 'function') {\n return c.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (ch) => {\n const r = (Math.random() * 16) | 0;\n const v = ch === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Payload format for cluster-wide metadata change broadcasts.\n *\n * Published on channel `metadata.changed` by any node mutating metadata\n * and consumed by peers to invalidate their local caches. Aligns with\n * `MetadataChangedEventPayload` in `cluster-semantics.mdx` §5.\n */\nexport interface ClusterMetadataChangedPayload {\n /** Origin nodeId — used for loopback suppression. */\n originNode?: string;\n /** Metadata type (object, view, dashboard, …). */\n type: string;\n /** The legacy watch event replayed verbatim on peer nodes. */\n event: MetadataWatchEvent;\n}\n\n/**\n * [#5184] One entry of {@link MetadataManager}'s short-TTL `list()` cache.\n *\n * Deliberately NOT exported: this is an internal caching-policy detail, not\n * part of the `IMetadataService` contract. See the `listCache` field comment\n * for the policy this shape encodes.\n */\ninterface ListCacheEntry {\n /** When the entry was written (`Date.now()`). */\n ts: number;\n /** The `list()` result being memoized. */\n items: unknown[];\n /**\n * True when at least one loader threw while `items` was being assembled, so\n * this answer is known to be a partial view of what is declared. Degraded\n * entries expire after `DEGRADED_LIST_CACHE_TTL_MS` instead of\n * `LIST_CACHE_TTL_MS`, and any future consumer of the cache can branch on\n * this rather than having to guess.\n */\n degraded: boolean;\n /**\n * [#6504] The messages of the loaders that could not be read, in the order\n * they failed — empty whenever `degraded` is false.\n *\n * Memoized with the entry rather than recomputed, so a consumer served from\n * the cache learns the same thing as one served by the read that filled it.\n * Without this the cache could say *that* the answer is partial but never\n * *why*, which is the half `listDiagnosed` needs to be as informative as\n * `getDiagnosed` already is.\n */\n errors: string[];\n}\n\n/**\n * [#6504] What one `list()` read actually produced: the best-effort set, plus\n * whether assembling it lost a loader.\n *\n * The return of {@link MetadataManager.readListUncached}, the value shared\n * through `inflightListReads`, and — minus the cache bookkeeping — what\n * {@link MetadataManager.listDiagnosed} hands to a caller. One shape for all\n * three on purpose: the verdict used to be dropped at each hop outward\n * (`readListUncached` computed it, the in-flight promise kept only `items`,\n * `list()` returned only that), and a single carried record is what makes\n * losing it again take an edit rather than an omission.\n */\ninterface ListReadResult {\n items: unknown[];\n degraded: boolean;\n errors: string[];\n}\n\nexport interface MetadataManagerOptions extends MetadataManagerConfig {\n loaders?: MetadataLoader[];\n /** Optional IDataDriver instance. When provided alongside config.datasource, auto-configures DatabaseLoader. */\n driver?: IDataDriver;\n}\n\n/**\n * Main metadata manager class.\n * Implements IMetadataService contract for unified metadata management.\n */\nexport class MetadataManager implements IMetadataService {\n private loaders: Map<string, MetadataLoader> = new Map();\n // Protected so subclasses can access serializers if needed\n protected serializers: Map<MetadataFormat, MetadataSerializer>;\n protected logger: Logger;\n protected watchCallbacks = new Map<string, Set<WatchCallback>>();\n protected config: MetadataManagerOptions;\n\n // In-memory metadata registry: type -> name -> data\n private registry = new Map<string, Map<string, unknown>>();\n\n // Overlay storage: \"type:name:scope\" -> MetadataOverlay\n private overlays = new Map<string, MetadataOverlay>();\n\n // Type registry for metadata type info\n private typeRegistry: MetadataTypeRegistryEntryParsed[] = [];\n\n // Dependency tracking: \"type:name\" -> dependencies\n private dependencies = new Map<string, MetadataDependency[]>();\n\n // Short-lived cache for list() results. Built primarily to break the\n // deadlock that occurs when security/permission middleware calls\n // `list('permission')` from inside a user-initiated DB transaction: the\n // DatabaseLoader's `engine.find('sys_metadata', ...)` would then try to\n // acquire a fresh knex connection while the transaction is still holding\n // SQLite's single connection — knex waits the full `acquireConnectionTimeout`\n // (60s) before returning []. The cache absorbs the repeated lookups so the\n // loader is only hit once per TTL window — for CONCURRENT callers as well as\n // sequential ones, since #5253. The cache on its own could only ever deliver\n // the sequential half of that promise: nothing is written until a read\n // completes, so everything issued before the first read returned used to miss\n // and walk every loader — N callers, N × 60s on the very stall described\n // above. The concurrent half is delivered by `inflightListReads` below, which\n // is why the two fields are one policy and are documented together.\n //\n // [#5184; re-measured under #7708 on 2026-08-11] That hazard is NOT\n // historical — and the re-measurement NARROWED it rather than retiring it.\n // Measured on the current stack: real `ObjectQL` + real `SqlDriver`\n // (better-sqlite3), a real `DatabaseLoader.list()` with the loader's own\n // cache off, `knex.client.pool.max === 1` confirmed for the SQLite dialect\n // and `acquireConnectionTimeout` left at the knex default of 60s.\n //\n // • Transaction opened DIRECTLY on the driver (`driver.beginTransaction()`)\n // → the read STALLS for the full timeout and then throws knex's \"Timeout\n // acquiring a connection\" (measured: 60_085ms). `DatabaseLoader._find()`\n // forwards no options, so nothing threads the caller's transaction, and\n // `driver-sql` still models SQLite as a single-connection pool\n // (`activeTransactions`, `assertBareKnexSafe` — the latter a dev/test\n // guard that is a no-op in production, so production still waits the\n // timeout out). `list()` catches that throw and degrades, which is\n // precisely the entry whose TTL this policy is choosing.\n // • Transaction opened through `engine.transaction()` / `ScopedContext`\n // → returns immediately (measured: 12ms, with `activeTransactions === 1`\n // and the driver observably receiving the handle on the call). Those\n // publish the transaction into the engine's ambient `txStore` (ADR-0034)\n // and `buildDriverOptions` threads it onto the read for the loader.\n //\n // So the stall shape is live but CONDITIONAL: it needs an open transaction\n // that the engine's ambient store cannot see. `SqlDriver.ensureSequencesTable()`\n // is the live witness that this is worth designing against — it takes\n // `parentTrx` and runs its DDL on the caller's transaction for exactly this\n // reason, with `assertBareKnexSafe` as the tripwire for callers that forget;\n // `sql-driver-sqlite-tx-guard.test.ts` pins both halves. (Until #7708 the\n // witness cited here was `plugin-audit`'s `captureBefore`, retired by #6656.\n // It was REPLACED rather than dropped: the example died, the hazard did not.)\n //\n // Hence the policy below keeps caching degraded reads rather than skipping\n // them: \"don't cache a degraded read\" would trade one 30s silent window for\n // a fresh 60s stall per call on every caller in the first bullet.\n //\n // [#5184] WHAT IS ACTUALLY CACHED, AND FOR HOW LONG — this paragraph is the\n // contract, and it describes `cacheListResult()` / `readCachedList()` below.\n // (An earlier version of this comment claimed the cache kept \"only positive\n // (non-empty) hits or repeated hits with a stable miss signature\". No such\n // condition ever existed in the code. Comment is contract; a comment that\n // describes a policy nothing implements is a declared ≠ enforced defect in\n // its own right, so it is replaced rather than patched.)\n //\n // • EVERY completed `list()` is cached, empty results included. There is\n // no non-empty test and no \"miss signature\" concept.\n // • An entry assembled while at least one loader THREW is a known-partial\n // answer: it is cached with `degraded: true` and expires after\n // `DEGRADED_LIST_CACHE_TTL_MS`, not `LIST_CACHE_TTL_MS`. So the burst of\n // repeated lookups the knex path above depends on is still absorbed,\n // while the window in which the manager serves a known-short set without\n // re-asking anyone shrinks from 30s to ~2s. Recovery is therefore also\n // noticed (and `reportLoaderReadRecovered` logged) within ~2s of storage\n // healing instead of up to 30s later.\n // • `degraded` lives ON the entry, not in a side table, so every consumer\n // of the cache can tell a complete answer from a partial one. Read\n // entries through `readCachedList()` rather than `listCache.get()`, so\n // the flag and its TTL are applied in one place.\n //\n // Invalidated on every `register()` / `unregister()` to keep CRUD writes\n // visible to subsequent reads.\n //\n // [#5259] WHERE in a write the invalidation sits is part of that promise, not\n // an implementation detail. `list()` merges registry ∪ loaders, so an\n // invalidation issued while only ONE of the two has been updated lets the\n // next read memoize the half-applied view for a full TTL. The rule both\n // writers follow: **invalidate last, once every store already holds the state\n // being announced** — `register()` satisfies it by writing the registry\n // first (the registry outranks loaders in the merge, so its save window\n // already shows the post-write value); `unregister()` satisfies it by\n // deleting from storage first and invalidating after, with nothing awaited\n // between the registry drop and the invalidation. See `unregister()`.\n private listCache = new Map<string, ListCacheEntry>();\n private static readonly LIST_CACHE_TTL_MS = 30_000;\n /**\n * [#5184] TTL for an entry produced by a degraded read (≥1 loader threw).\n *\n * Deliberately at the top of the 1–2s band: the point of keeping degraded\n * results cached at all is to absorb a burst of `list()` calls issued from\n * inside one open transaction, and those bursts are milliseconds apart but\n * can be spread by per-row work. Two seconds covers that while still being\n * 15× shorter than the healthy TTL.\n */\n private static readonly DEGRADED_LIST_CACHE_TTL_MS = 2_000;\n\n /**\n * [#5253] The `list()` read currently in flight for a metadata type — the\n * concurrent half of the `listCache` policy above.\n *\n * `listCache` memoizes an answer only once a read has *finished*, so it can\n * absorb the caller that arrives second in time but never the caller that\n * arrives second in flight. Everything issued while the first read is still\n * walking the loaders used to miss and start its own identical walk; on the\n * knex/SQLite path the field comment above is built for, that is 60s burned\n * per concurrent caller instead of once for all of them. A type is read once\n * at a time: whoever finds a read already running joins it.\n *\n * **Sharers share the outcome. This is a contract, not an accident.** Every\n * caller joining an in-flight read receives that read's exact result — the\n * same array instance, and, when a loader was unreadable, the same\n * known-partial set that gets memoized `degraded: true` on the short TTL.\n * There is no per-caller retry: `list()` is the best-effort listing seam and\n * does not throw (see {@link reportLoaderReadFailure}; the strict\n * counterparts are `listForIndex()` and {@link loadDiagnosed}), so a lost\n * loader is not an error to fail over from — it is the answer. Re-running the\n * read privately for a joiner would walk the same loaders in the same window\n * against the same outage, which is precisely what this map exists to\n * prevent. Should the seam ever acquire a rejecting path, that rejection is\n * shared by the same mechanism and for the same reason.\n *\n * **The registration is also the permission to cache.** An entry here says\n * \"this read still describes the current state\". {@link invalidateListCache}\n * retracts it, which is what makes a write landing mid-read safe in both\n * directions:\n * • the retracted read does NOT write its result into `listCache` when it\n * settles, so an answer assembled before the write cannot outlive the\n * write it predates (the invalidation wins — it is the later, better\n * informed fact);\n * • a `list()` issued after the invalidation starts a FRESH read instead of\n * joining one that predates the write.\n * That second point is the #5219 / #5229 ordering bar restated for\n * concurrency: a consumer woken by a metadata change must not observe the\n * event and pre-event state together, and handing a woken watcher an\n * in-flight read that began before the event would be exactly that.\n * Callers *already waiting* on the retracted read still receive its (now\n * possibly stale) result — they asked before the write, and restarting the\n * read under them would turn a write burst into an unbounded retry loop on\n * the one path the cache exists to keep off the loaders.\n *\n * Self-cleaning: the entry is dropped when the read settles, by that read\n * only, so a fresh read that already replaced it keeps its slot. Nothing\n * accumulates — a wave of callers arriving after settle finds the cache the\n * settle just wrote, and once that lapses it starts one new read.\n *\n * [#6504] The shared value is the whole {@link ListReadResult}, not just\n * `items`. \"Sharers share the outcome\" above is stated about the answer *and*\n * its degraded verdict, and while the promise carried only `items` that was\n * true of `list()` alone: a {@link listDiagnosed} caller joining an in-flight\n * read had no way to reach the verdict that read had already computed, and\n * would have had to either re-walk the loaders (defeating this map) or invent\n * a second, unmemoized answer. `list()` narrows to `.items` at its own return\n * instead, so every sharer still receives the same array instance.\n */\n private readonly inflightListReads = new Map<string, Promise<ListReadResult>>();\n\n // [#5108] Loader names whose read failure has already been reported at\n // `error` by `list()`. AGENTS.md → \"Degradation log levels\": say it once, at\n // the first degradation — `list()` is hot enough that one line per failed\n // read would bury the one line that matters. Cleared when the loader answers\n // again, so a second outage is reported again. Same once-only discipline as\n // `DatabaseLoader.schemaFailureReported`.\n private readonly loaderReadFailureReported = new Set<string>();\n\n // Realtime service for event publishing\n private realtimeService?: IRealtimeService;\n\n // ── Cluster wiring (cluster-semantics.mdx §5) ────────────────────────\n // When attached via `attachClusterPubSub()`, metadata-change events\n // become cluster-wide:\n // • Local notifyWatchers() publishes on `metadata.changed` so peers\n // can invalidate their caches.\n // • A subscribed remote event first invalidates THIS node's caches\n // (registry entry + listCache, via `invalidateForForeignWrite` —\n // #5109) and is then replayed into the local watch hub, so existing\n // consumers (ObjectQLPlugin, Studio HMR, …) see uniform behavior\n // regardless of which node initiated the change — including when\n // they answer the event by re-reading through `list()`.\n // `originNode` on the payload prevents loopback; `partitionKey` keeps\n // per-object ordering on partitioned drivers.\n private clusterPubSub?: IPubSub;\n private clusterNodeId?: string;\n private clusterUnsubscribe?: Unsubscribe;\n private static readonly CLUSTER_CHANNEL = 'metadata.changed';\n\n // ── ADR-0008 PR-6: optional Repository for event-source integration ──\n // When set, the manager streams `repo.watch()` events into the watch\n // callback hub AND invalidates the in-memory registry/listCache so\n // subsequent reads fall through to the source of truth. No write\n // mirroring yet (deferred to PR-10 / overlay migration).\n protected repository?: MetadataRepository;\n private repoWatchIter?: AsyncIterator<MetadataEvent>;\n private repoWatchClosed = false;\n\n // ── #5089 (#5040 E2): declared-endpoint index ────────────────────────\n // Backs `matchEndpoint`. Lazily built from `api` items on the first call\n // and invalidated by every path that can change them — see\n // `invalidateListCache` (local writes, repo events, HMR/artifact ingest\n // which registers with `notify:false`, and — since #5109 — cluster peer\n // replay) and the `subscribe('api', …)` registration below.\n private static readonly ENDPOINT_METADATA_TYPE = 'api';\n private readonly endpointMatcher: EndpointMatcher;\n\n constructor(config: MetadataManagerOptions) {\n this.config = config;\n this.logger = createLogger({ level: 'info', format: 'pretty' });\n\n // [#5089] Endpoint index (see `matchEndpoint`). Two invalidation seams,\n // covering overlapping but non-identical event sets — both are kept:\n // 1. `invalidateListCache('api')` — every mutation of the stored set\n // this manager learns about, including the `{ notify: false }`\n // writes the artifact ingest and the HMR reload use, which by\n // construction never reach a watcher. It is the same invariant the\n // list cache carries: if the cached list of a type is stale, so is\n // the index built from it. Since #5109 a CLUSTER peer's write also\n // passes through here, via `invalidateForForeignWrite`.\n // 2. `subscribe('api', …)` — the watcher seam, which additionally\n // covers events raised by subclasses / test doubles that call\n // `notifyWatchers` without a cache mutation of their own.\n // Double invalidation is idempotent, so the overlap is free.\n this.endpointMatcher = new EndpointMatcher({\n listApiItems: () => this.listForIndex(MetadataManager.ENDPOINT_METADATA_TYPE),\n logger: this.logger,\n });\n this.subscribe(MetadataManager.ENDPOINT_METADATA_TYPE, () => this.endpointMatcher.invalidate());\n\n // Initialize serializers\n this.serializers = new Map();\n const formats = config.formats || ['typescript', 'json', 'yaml'];\n\n if (formats.includes('json')) {\n this.serializers.set('json', new JSONSerializer());\n }\n if (formats.includes('yaml')) {\n this.serializers.set('yaml', new YAMLSerializer());\n }\n if (formats.includes('typescript')) {\n this.serializers.set('typescript', new TypeScriptSerializer('typescript'));\n }\n if (formats.includes('javascript')) {\n this.serializers.set('javascript', new TypeScriptSerializer('javascript'));\n }\n\n // Initialize Loaders\n if (config.loaders && config.loaders.length > 0) {\n config.loaders.forEach(loader => this.registerLoader(loader));\n }\n\n // Auto-configure DatabaseLoader when datasource + driver are provided\n if (config.datasource && config.driver) {\n this.setDatabaseDriver(config.driver);\n }\n // Note: No default loader in base class. Subclasses (NodeMetadataManager) or caller must provide one.\n }\n\n /**\n * Set the type registry for metadata type discovery.\n */\n setTypeRegistry(entries: MetadataTypeRegistryEntryParsed[]): void {\n this.typeRegistry = entries;\n }\n\n /**\n * Configure and register a DatabaseLoader for database-backed metadata persistence.\n * Can be called at any time to enable database storage (e.g. after kernel resolves the driver).\n *\n * @param driver - An IDataDriver instance for database operations\n * @param organizationId - Organization ID for multi-tenant isolation\n * @param environmentId - Project ID (undefined = platform-global)\n */\n setDatabaseDriver(driver: IDataDriver, organizationId?: string, environmentId?: string): void {\n if (environmentId !== undefined) {\n this.logger.info('Project kernel — skipping DatabaseLoader for sys_metadata (control-plane only)', {\n organizationId,\n environmentId,\n });\n return;\n }\n const tableName = this.config.tableName ?? 'sys_metadata';\n const dbLoader = new DatabaseLoader({\n driver,\n tableName,\n organizationId,\n environmentId,\n cache: this.config.cache?.databaseLoader,\n });\n this.registerLoader(dbLoader);\n this.logger.info('DatabaseLoader configured', { datasource: this.config.datasource, tableName });\n }\n\n /**\n * Configure and register a DatabaseLoader backed by an IDataEngine (ObjectQL).\n * The engine handles datasource routing automatically — sys_metadata will\n * be routed to the correct driver via the standard namespace mapping.\n * No manual driver resolution needed.\n *\n * @param engine - An IDataEngine instance (typically the ObjectQL service)\n * @param organizationId - Organization ID for multi-tenant isolation\n * @param environmentId - Project ID (undefined = platform-global)\n */\n setDataEngine(engine: IDataEngine, organizationId?: string, environmentId?: string): void {\n if (environmentId !== undefined) {\n this.logger.info('Project kernel — skipping DatabaseLoader for sys_metadata (control-plane only)', {\n organizationId,\n environmentId,\n });\n return;\n }\n const tableName = this.config.tableName ?? 'sys_metadata';\n const dbLoader = new DatabaseLoader({\n engine,\n tableName,\n organizationId,\n environmentId,\n cache: this.config.cache?.databaseLoader,\n });\n this.registerLoader(dbLoader);\n this.logger.info('DatabaseLoader configured via DataEngine', { tableName });\n }\n\n /**\n * Set the realtime service for publishing metadata change events.\n * Should be called after kernel resolves the realtime service.\n *\n * @param service - An IRealtimeService instance for event publishing\n */\n setRealtimeService(service: IRealtimeService): void {\n this.realtimeService = service;\n this.logger.info('RealtimeService configured for metadata events');\n }\n\n /**\n * Publish a realtime {@link RealtimeMetadataEvent} for a metadata write\n * (#4602 — contract-first).\n *\n * What reaches a `subscribeMetadata` callback must BE the spec's\n * `MetadataEvent` (`@objectstack/spec/api`): `id` (uuid) at the top level,\n * flattened `metadataType`/`name`/`definition`, `userId` when the write\n * carried an actor. The transport keeps its `RealtimeEventPayload`\n * envelope — `payload` carries the complete `MetadataEvent`, and the client\n * SDK unwraps + validates it at the boundary.\n *\n * Two loud-by-design gates:\n * - `MetadataEventType` is a CLOSED enum. A metadata type outside it has\n * no declared realtime event contract, so we skip publishing (debug log)\n * instead of emitting an event every compliant consumer must reject.\n * Declared = enforced; widening coverage means widening the spec enum,\n * not producing off-contract events.\n * - The event body is `MetadataEventSchema.parse`d before publish, so a\n * malformed producer fails here (warn log, event not published) rather\n * than delivering a lie downstream.\n */\n private async publishRealtimeMetadataEvent(\n action: 'created' | 'updated' | 'deleted',\n type: string,\n name: string,\n opts: { definition?: unknown; packageId?: unknown; userId?: string } = {},\n ): Promise<void> {\n if (!this.realtimeService) return;\n\n const eventType = `metadata.${type}.${action}`;\n if (!(MetadataEventType.options as readonly string[]).includes(eventType)) {\n this.logger.debug(\n `Metadata type '${type}' has no declared realtime event type (MetadataEventType) — skipping publish`,\n { eventType, name },\n );\n return;\n }\n\n try {\n const event: RealtimeMetadataEvent = MetadataEventSchema.parse({\n id: generateEventUuid(),\n type: eventType,\n metadataType: type,\n name,\n ...(typeof opts.packageId === 'string' ? { packageId: opts.packageId } : {}),\n ...(opts.definition !== undefined ? { definition: opts.definition } : {}),\n ...(opts.userId ? { userId: opts.userId } : {}),\n timestamp: new Date().toISOString(),\n });\n\n const envelope: RealtimeEventPayload = {\n type: event.type,\n object: type,\n payload: { ...event },\n timestamp: event.timestamp,\n };\n\n await this.realtimeService.publish(envelope);\n this.logger.debug(`Published ${eventType} event`, { name });\n } catch (error) {\n this.logger.warn(`Failed to publish metadata event`, { type, name, error });\n }\n }\n\n /**\n * Register a new metadata loader (data source)\n *\n * [#5276, #5654] Rejects — loudly, before the loader is stored — a\n * `datasource:` loader that declares `capabilities.write` without\n * implementing `save()` **and** `delete()`. This is the **only** way into\n * `this.loaders` (the constructor's `config.loaders` come through here too),\n * which is what lets every later write-capability guard be defensive rather\n * than load-bearing.\n */\n registerLoader(loader: MetadataLoader) {\n assertWritableLoaderContract(loader);\n this.loaders.set(loader.contract.name, loader);\n this.logger.info(`Registered metadata loader: ${loader.contract.name} (${loader.contract.protocol})`);\n }\n\n // ==========================================\n // IMetadataService — Core CRUD Operations\n // ==========================================\n\n /**\n * Register/save a metadata item by type\n * Stores in-memory registry and persists to database-backed loaders only.\n * FilesystemLoader (protocol 'file:') is read-only for static metadata and\n * should not be written to during runtime registration.\n *\n * Announces the write to {@link subscribe} watchers as an `added` /\n * `changed` {@link MetadataWatchEvent}, so consumers that cache metadata\n * (ObjectQL's SchemaRegistry bridge, the HMR SSE stream) refresh instead of\n * serving the pre-write definition until restart. Pass `{ notify: false }`\n * for bulk ingest that announces by other means — read\n * {@link MetadataWriteOptions.notify} before doing so.\n */\n async register(\n type: string,\n name: string,\n data: unknown,\n options?: MetadataWriteOptions,\n ): Promise<void> {\n // [#7378] The ruled register contract, before anything is touched: refuse\n // a data.name that disagrees with the name argument and refuse a\n // non-document data (rows 1/3 — the guard's header carries the ruling),\n // and key every downstream store, loader write, cache entry and watcher\n // announcement on the CANONICAL type (row 2). A refusal here writes to no\n // store and persists to no loader; it fires even on a read-only manager,\n // because it judges the arguments, not the persistence posture.\n assertMetadataRegisterContract(type, name, data);\n type = canonicalMetadataServiceType(type);\n\n // Persistence write gate: when `persistence.writable` is explicitly false\n // we treat register() as read-only. Default `true` (or omitted) preserves\n // historical behavior.\n if (this.config.persistence?.writable === false) {\n const msg = `MetadataManager is read-only (persistence.writable=false); refusing to register ${type}/${name}`;\n if (this.config.validation?.throwOnError) {\n throw new Error(msg);\n }\n this.logger.warn(msg);\n return;\n }\n\n // Captured before the write so the event distinguishes a first\n // registration from an overwrite, matching the repo-watch path's\n // 'added' vs 'changed' split.\n const existed = this.registry.get(type)?.has(name) ?? false;\n\n if (!this.registry.has(type)) {\n this.registry.set(type, new Map());\n }\n this.registry.get(type)!.set(name, data);\n this.invalidateListCache(type);\n\n // Persist only to database-backed loaders that declare write capability.\n // FilesystemLoader is read-only at runtime — writing to it can crash in\n // read-only environments (e.g. serverless, containerized deployments).\n for (const loader of this.loaders.values()) {\n if (loader.contract.protocol !== 'datasource:' || !loader.contract.capabilities.write) continue;\n // [#5654] Defensive only — unreachable for a registered loader, and read\n // in this order on purpose: the protocol/capability test is the policy,\n // the method test is the type narrowing. This exact combination\n // (`datasource:` + `capabilities.write`, no `save`) is rejected by\n // `registerLoader()`, the sole writer of `this.loaders`, so reaching this\n // `continue` would mean a loader entered the map without passing the\n // gate. Kept because the alternative is a TypeError on the line below,\n // and because `save?` stays optional on the interface for the protocols\n // the gate does not cover. Mirrors the same guard in `unregister()`.\n if (typeof loader.save !== 'function') continue;\n await loader.save(type, name, data);\n }\n\n // Publish metadata.{type}.created / .updated event to realtime service.\n // An overwrite is an UPDATE, mirroring the 'added' vs 'changed' split the\n // watcher event below already makes (#4602).\n await this.publishRealtimeMetadataEvent(existed ? 'updated' : 'created', type, name, {\n definition: data,\n packageId: (data as any)?.packageId,\n userId: options?.userId,\n });\n\n // Announce last, once the write has landed in the registry and every\n // writable loader — a subscriber that re-reads on the event must not\n // race ahead of the data it is meant to observe.\n if (options?.notify !== false) {\n this.notifyWatchers(type, {\n type: existed ? 'changed' : 'added',\n metadataType: type,\n name,\n path: '',\n data,\n timestamp: new Date().toISOString(),\n });\n }\n }\n\n /**\n * Register a metadata item into the in-memory registry ONLY, never persisting\n * to a writable loader. Used for GitOps-managed artefacts that must be\n * *listable* (so `list(type)` returns them) but must never leak into the\n * runtime DB store — e.g. code-defined datasources (`origin:'code'`, ADR-0015\n * Addendum) declared in `*.datasource.ts` and owned by source control. Writing\n * them through `register()` would persist them to `sys_metadata` and create\n * drift between the artefact and the DB; this method avoids that.\n *\n * Deliberately silent: it does NOT announce to {@link subscribe} watchers.\n * This is a boot-time seeding primitive for artefacts that source control\n * owns — callers that mutate metadata mid-run want {@link register}, which\n * announces. If you add a mid-run caller here, announce the change yourself\n * (as the artifact reload path does via `metadata:reloaded`) or its\n * consumers will read the pre-write definition until restart.\n */\n registerInMemory(type: string, name: string, data: unknown): void {\n // [#7378 row 2] The canonical fold is a store fact and applies here; the\n // rows-1/3 refusals deliberately do NOT — the ruling names `register`,\n // and this is the boot-time seeding primitive (see\n // assertMetadataRegisterContract's header for the boundary).\n type = canonicalMetadataServiceType(type);\n if (!this.registry.has(type)) {\n this.registry.set(type, new Map());\n }\n this.registry.get(type)!.set(name, data);\n this.invalidateListCache(type);\n }\n\n /**\n * Get a metadata item by type and name.\n * Checks in-memory registry first, then falls back to loaders.\n *\n * Returns `undefined` both when nothing declares the item and when every\n * loader that could have held it FAILED — see {@link getDiagnosed} when the\n * caller must tell those apart. This is the same relationship {@link load}\n * has with {@link loadDiagnosed}, so every existing caller keeps its exact\n * behaviour and only callers that ASK for the verdict pay for it.\n *\n * Not expressed as `(await getDiagnosed(…)).data`, although that is what it\n * computes — and the reason has CHANGED, so do not read the duplication as a\n * standing constraint.\n *\n * [#5840] recorded the delegation as unsafe: it adds one `await` hop, and\n * `register-notifies-watchers.test.ts` went red on the delegating version, so\n * three lines were duplicated to hold the frame count fixed. [#6043] measured\n * that test and found it was pinning this method's microtask depth rather than\n * the ordering guarantee it named — `notifyWatchers` never awaits its handlers,\n * so a subscriber's `await get(…)` had simply been settling inside the\n * microtasks `await register(…)` yields. That case now asserts the ordering\n * synchronously against the registry and does not observe this method's frame\n * count at all; the whole `@objectstack/metadata` suite was re-measured on the\n * delegating version and stayed green.\n *\n * What survives is a plain, local reason: the registry hit is the hot path and\n * answering it without a second async frame is worth three lines. Nothing\n * external depends on the hop count any more. Consolidating the two into one\n * delegation is therefore a viable, deliberately un-taken change (#6043 was\n * test-scoped) — if you take it, note that `get()`'s callers outside this\n * package were never surveyed for timing sensitivity, only this package's\n * tests. Either way the two stay pinned to each other from the other side:\n * `get()` and `getDiagnosed().data` are asserted to agree on every case in\n * `metadata-manager-get-diagnosed.test.ts`.\n */\n async get(type: string, name: string): Promise<unknown | undefined> {\n // [#7378 row 2] Read the store `register` wrote: the canonical type.\n type = canonicalMetadataServiceType(type);\n // Check in-memory registry first\n const typeStore = this.registry.get(type);\n if (typeStore?.has(name)) {\n return typeStore.get(name);\n }\n\n // Fallback to loaders\n const result = await this.load(type, name);\n return result ?? undefined;\n }\n\n /**\n * `get`, plus whether the answer can be trusted as complete.\n *\n * [#5840] {@link loadDiagnosed} already computes this verdict — and `get()`\n * threw it away two hops later (`load` kept only `.data`, `get` turned that\n * `null` into `undefined`), so no caller of `get` could reach the one fact\n * ADR-0110 D3 exists to preserve: **a miss and an outage are different facts\n * with opposite security meanings.** A consumer that gates on a declaration\n * MUST NOT read `undefined` as \"the author declared nothing\" — an\n * availability failure would silently widen access (the REST `/actions`\n * fail-open branch, #3935) or make a positive claim about authorship from a\n * read that never happened (`code: null` in the layered read, #5707/#5532).\n *\n * This is the registry-first counterpart of {@link loadDiagnosed}, and that\n * difference is why callers of `get` cannot simply switch to `loadDiagnosed`:\n * doing so would skip the in-memory registry and change what they resolve.\n *\n * `degraded` is true when at least one loader threw AND nothing answered with\n * the item — never when the in-memory registry answered, because that answer\n * needed no loader. A clean miss (every loader answered, none had it) is NOT\n * degraded. The posture is deliberately conservative: with a loader down we\n * cannot prove the item is absent, so we decline to claim it is.\n */\n async getDiagnosed(\n type: string,\n name: string\n ): Promise<{ data: unknown | undefined; degraded: boolean; errors: string[] }> {\n // [#7378 row 2] Same fold as `get` — the two are pinned to agree.\n type = canonicalMetadataServiceType(type);\n // Check in-memory registry first — a hit here consulted no loader, so\n // there is nothing to be degraded about.\n const typeStore = this.registry.get(type);\n if (typeStore?.has(name)) {\n return { data: typeStore.get(name), degraded: false, errors: [] };\n }\n\n // Fallback to loaders, keeping the verdict this time.\n const { data, degraded, errors } = await this.loadDiagnosed(type, name);\n return { data: data ?? undefined, degraded, errors };\n }\n\n /**\n * List all metadata items of a given type.\n *\n * Best-effort by contract: a loader that cannot be read is reported once and\n * skipped ({@link reportLoaderReadFailure}), so this resolves with what the\n * reachable loaders hold rather than throwing.\n *\n * [#5253] Reads of one type are single-flight — concurrent callers join the\n * read already running instead of each walking every loader. What they are\n * promised, and what happens when a write lands mid-read, is the contract on\n * `inflightListReads`; what is memoized afterwards is the contract on\n * `listCache`.\n */\n async list(type: string): Promise<unknown[]> {\n // [#7378 row 2] Fold before the cache/single-flight machinery, so the\n // two spellings join one read and one cache entry.\n return (await this.readList(canonicalMetadataServiceType(type))).items;\n }\n\n /**\n * `list`, plus whether the answer can be trusted as complete.\n *\n * [#6504] The plural counterpart of {@link getDiagnosed}, and the same defect\n * one read over: `readListUncached` has computed this verdict since #5184 and\n * `list()` spent it entirely on a cache TTL, so a consumer receiving a short\n * set could not ask whether it was short because that is all anyone declared\n * or because a loader was down. {@link reportLoaderReadFailure}'s own message\n * says what that costs — \"every list served from now on is a PARTIAL set\n * presented as a complete one, and the server keeps reporting healthy\" — and\n * until this member existed that sentence was addressed to a log reader only,\n * because no caller had a way to ask.\n *\n * Sharper than the singular case rather than merely analogous: `list` is the\n * read whose answer carries a **count**, and a consumer restating\n * `items.length` as \"this environment contains N items\" makes a positive,\n * numeric claim about what an author declared out of a read that partly did\n * not happen.\n *\n * Reads through exactly the same cache and single-flight machinery `list()`\n * does — same entry, same TTLs, same in-flight join — so asking for the\n * verdict costs no extra loader walk, and `list()` and\n * `listDiagnosed().items` cannot drift: they are the same read, narrowed at\n * different points. `degraded` is true when at least one loader threw while\n * this set was assembled; unlike {@link getDiagnosed} it does NOT additionally\n * require that nothing answered, because a plural read that lost one loader is\n * partial even when the others answered plenty — which is the whole fact.\n */\n async listDiagnosed(type: string): Promise<ListReadResult> {\n // [#7378 row 2] Same fold as `list` — same read, narrowed differently.\n const { items, degraded, errors } = await this.readList(canonicalMetadataServiceType(type));\n return { items, degraded, errors };\n }\n\n /**\n * The cached / single-flight read behind {@link list} and\n * {@link listDiagnosed}.\n *\n * [#6504] Extracted so the two members are one read seen at two widths rather\n * than two implementations that have to be kept in agreement — the shape\n * `get`/`getDiagnosed` pay for with a duplicated body and a test pinning them\n * to each other. Everything below is unchanged in behaviour from when it was\n * inlined in `list()`; only the verdict now survives the return.\n */\n private async readList(type: string): Promise<ListReadResult> {\n // Short-TTL cache: see the field comment on `listCache` for what is\n // cached and for how long. Every completed read is memoized; a read that\n // lost a loader is memoized as `degraded` and expires ~15× sooner.\n const cached = this.readCachedList(type);\n if (cached) {\n return cached;\n }\n\n // [#5253] Cold cache, but not necessarily a cold read: join the walk\n // already in progress for this type rather than starting an identical one.\n const joined = this.inflightListReads.get(type);\n if (joined) {\n return joined;\n }\n\n // Registering the read is what permits it to memoize its own result —\n // `invalidateListCache()` retracts the registration, and both the cache\n // write and the cleanup below act only while the slot is still ours.\n const shared: Promise<ListReadResult> = this.readListUncached(type).then((result) => {\n // [#5184] The degraded verdict of a SHARED read is the degraded verdict\n // of the read: sharing must not become a back door that lands a\n // known-partial answer on the 30s healthy TTL. Every sharer received\n // this same set, and it is memoized as what it is.\n // [#5253] Skipped when an invalidation crossed this read, so a write\n // that landed mid-read is never re-buried under the pre-write answer.\n if (this.inflightListReads.get(type) === shared) {\n this.cacheListResult(type, result);\n }\n return result;\n });\n this.inflightListReads.set(type, shared);\n\n try {\n return await shared;\n } finally {\n // Retract only our OWN registration: an invalidation may have already\n // dropped it and a fresh read may own the slot now — deleting that one\n // would let a third read start against the same window.\n if (this.inflightListReads.get(type) === shared) {\n this.inflightListReads.delete(type);\n }\n }\n }\n\n /**\n * Assemble the `list()` answer for `type` from the in-memory registry plus\n * every loader, reporting (but not rethrowing) loaders that could not be\n * read.\n *\n * The body {@link list} used to inline, extracted so the caching and\n * single-flight bookkeeping around it has one thing to run at most once per\n * type (#5253). Deliberately does NOT touch `listCache` itself: whether this\n * result may be memoized depends on what happened to the read's registration\n * while it ran, which only `list()` can see.\n */\n private async readListUncached(type: string): Promise<ListReadResult> {\n const items = new Map<string, unknown>();\n\n // From in-memory registry\n const typeStore = this.registry.get(type);\n if (typeStore) {\n for (const [name, data] of typeStore) {\n items.set(name, data);\n }\n }\n\n // From loaders (deduplicate). [#5184] `degraded` records whether this\n // particular read lost a loader, so the memoized answer carries the fact\n // that it is known-partial instead of being indistinguishable from a\n // complete one.\n // [#6504] `errors` records WHICH loaders were lost and why. The messages\n // were already being produced here and handed only to the logger — which\n // speaks once per outage episode, so a consumer arriving mid-outage finds\n // nothing to read. Collected per read (not once per episode) because this\n // describes THIS answer, and it is what {@link listDiagnosed} reports\n // alongside the set.\n let degraded = false;\n const errors: string[] = [];\n for (const loader of this.loaders.values()) {\n try {\n const loaderItems = await loader.loadMany(type);\n for (const item of loaderItems) {\n const itemAny = item as any;\n if (itemAny && typeof itemAny.name === 'string' && !items.has(itemAny.name)) {\n items.set(itemAny.name, item);\n }\n }\n this.reportLoaderReadRecovered(loader.contract.name);\n } catch (e) {\n degraded = true;\n errors.push(`${loader.contract.name}: ${e instanceof Error ? e.message : String(e)}`);\n this.reportLoaderReadFailure(loader.contract.name, type, e);\n }\n }\n\n return { items: Array.from(items.values()), degraded, errors };\n }\n\n /**\n * Report — at `error`, once per outage episode — that a loader could not be\n * read while serving {@link list}.\n *\n * [#5108] This branch used to be dead for the loader that matters. Before\n * #5108 `DatabaseLoader` caught its own read failures and answered `[]`, so\n * `list()` received a *successful empty read* and never entered this `catch`\n * at all: an unreachable `sys_metadata` and \"this environment declares no\n * `permission`\" produced byte-identical results with not one line logged.\n * With the loader rethrowing everything but the benign not-provisioned case,\n * this is where the outage finally becomes speakable.\n *\n * `error`, not `warn`, per AGENTS.md → \"Degradation log levels\". Apply its\n * one question — *does the system still look normal from outside while\n * something it claims to know has not actually landed?* — and the answer is\n * yes: `list()` still returns, callers still get an array, nothing 500s, and\n * the set they gate on is quietly short. Which way that cuts depends on the\n * consumer, and both ways are silent (#3935 is the fail-open precedent).\n *\n * Said **once** per loader, and un-said on recovery, because `list()` is a\n * hot path — one line per outage, not one per read.\n *\n * [#5184] The once-only guard carries more weight than it used to: a\n * degraded `list()` result is now memoized for `DEGRADED_LIST_CACHE_TTL_MS`\n * rather than `LIST_CACHE_TTL_MS`, so during an outage the loader is\n * re-asked (and this method re-entered) roughly every 2s instead of every\n * 30s. That is the point — the outage stops being a 30s silent window and\n * recovery is noticed within seconds — and it costs nothing in log volume\n * precisely because `loaderReadFailureReported` still speaks only once.\n *\n * Deliberately does NOT rethrow: `list()` is the best-effort listing seam and\n * must keep serving what the reachable loaders hold. The strict counterpart\n * for callers whose answer is a security decision is `listForIndex()` (no\n * `catch`, feeding `matchEndpoint`) and {@link loadDiagnosed} (ADR-0110 D3)\n * for the singular read — both of which only became honest for\n * `DatabaseLoader` with the same #5108 change.\n */\n private reportLoaderReadFailure(loaderName: string, type: string, error: unknown): void {\n if (this.loaderReadFailureReported.has(loaderName)) return;\n this.loaderReadFailureReported.add(loaderName);\n this.logger.error(\n `[MetadataManager] Loader \\`${loaderName}\\` could NOT be read (first failure seen while listing \\`${type}\\`) — ` +\n `every list served from now on is a PARTIAL set presented as a complete one, and the server keeps reporting healthy. ` +\n `Consumers that gate on a declared set (permissions, sharing rules, policies, api endpoints) will read the ` +\n `declarations this loader holds as \"never declared\" — which grants or locks out depending on the consumer, silently either way. ` +\n `Fix: check the datasource behind \\`${loaderName}\\` — connection, credentials, and that its metadata table exists. ` +\n `The read is retried on the next list once the ${MetadataManager.DEGRADED_LIST_CACHE_TTL_MS}ms degraded-result list cache lapses ` +\n `(a known-partial listing is memoized far more briefly than a complete one — #5184), so a transient cause recovers on its ` +\n `own within seconds and the recovery is logged.`,\n error instanceof Error ? error : undefined,\n { loader: loaderName, type, error },\n );\n }\n\n /** Un-say {@link reportLoaderReadFailure} once the loader answers again. */\n private reportLoaderReadRecovered(loaderName: string): void {\n if (!this.loaderReadFailureReported.delete(loaderName)) return;\n this.logger.info(\n `[MetadataManager] Loader \\`${loaderName}\\` is readable again — listings are complete once more.`,\n );\n }\n\n /**\n * Memoize a completed {@link list} result.\n *\n * [#5184] `degraded` is not optional at the call site by accident — it is the\n * one thing this cache used to throw away. A result assembled while a loader\n * was unreadable is stored, but stored *as* what it is, so it expires on the\n * degraded TTL and any reader can tell it apart from a complete answer.\n *\n * [#6504] Takes the whole read result rather than its parts for the same\n * reason: a signature that spreads the verdict across positional arguments is\n * one a later caller can quietly fill with `false`, which is how the verdict\n * was lost on the way out in the first place.\n */\n private cacheListResult(type: string, result: ListReadResult): void {\n this.listCache.set(type, { ts: Date.now(), ...result });\n }\n\n /**\n * Read a still-fresh {@link listCache} entry, or `undefined` when there is\n * none / it has expired.\n *\n * [#5184] The single place the TTL policy is applied, so \"a degraded entry\n * expires sooner\" cannot be forgotten by a second reader. Returns the whole\n * entry rather than just `items` so callers keep access to `degraded`.\n */\n private readCachedList(type: string): ListCacheEntry | undefined {\n const cached = this.listCache.get(type);\n if (!cached) return undefined;\n const ttl = cached.degraded\n ? MetadataManager.DEGRADED_LIST_CACHE_TTL_MS\n : MetadataManager.LIST_CACHE_TTL_MS;\n return Date.now() - cached.ts < ttl ? cached : undefined;\n }\n\n /**\n * Internal helper: drop every memoized or in-progress `list()` answer for a\n * type, so the next read observes the write that called this.\n *\n * [#5253] Retracting the in-flight read (not just the finished entry) is the\n * whole mid-read story, and it is pinned by test: the read keeps running for\n * the callers already waiting on it, but it loses the right to memoize its\n * pre-write answer, and a caller arriving after this point gets a fresh read\n * instead of joining a pre-write one. The reasoning — including why waiting\n * callers are NOT restarted — is on the `inflightListReads` field.\n *\n * [#5259] Both halves are only as good as WHEN the caller invokes this. This\n * clears what is stale *as of now*; it cannot pre-empt a store the caller has\n * not finished updating yet. Callers must therefore invalidate only once\n * every store already holds the state they are about to announce — see the\n * `listCache` field comment and {@link unregister}, whose pre-#5259 ordering\n * invalidated one await too early and let the next read cache a view in which\n * the registry was empty and the loader was not.\n */\n private invalidateListCache(type: string): void {\n this.listCache.delete(type);\n this.inflightListReads.delete(type);\n // [#5089] The endpoint index is a cache of the same stored set, so it goes\n // stale under exactly the same conditions. Hooking here (rather than only\n // on the watcher) is what covers the `{ notify: false }` writes — artifact\n // ingest and HMR reload — which never announce to a subscriber.\n if (type === MetadataManager.ENDPOINT_METADATA_TYPE) {\n this.endpointMatcher.invalidate();\n }\n }\n\n /**\n * Enumerate stored items of `type` for an index build — like {@link list},\n * but a store that cannot be read THROWS instead of contributing nothing.\n *\n * [#5089] `list()` deliberately logs a failing loader and skips it so a\n * partially-available metadata plane still serves what it can. That posture\n * is wrong for `matchEndpoint`: its `undefined` becomes an HTTP 404, and a\n * store outage that silently yields \"zero declarations\" would turn every\n * declared endpoint into a semantic \"nothing declares this route\". Same\n * distinction {@link loadDiagnosed} draws on the singular read (ADR-0110\n * D3) — a miss and an outage are different facts with opposite meanings.\n *\n * Deliberately private and single-purpose: it is not a second `list()`, it\n * is `list()`'s failure posture inverted for the one caller whose answer is\n * a security/availability decision rather than a best-effort listing.\n *\n * This surfaces only failures a loader actually reports — which, since\n * #5108, includes `DatabaseLoader`: it used to swallow its own read errors\n * into `[]`, making a DB outage invisible even here. It now rethrows every\n * read failure except the benign \"table not provisioned yet\", so this seam\n * holds against the real datasource-backed loader and not just the memory /\n * remote ones. (`database-loader.test.ts` pins that end to end: a broken\n * driver behind a real `DatabaseLoader` makes `matchEndpoint` reject rather\n * than answer a 404-shaped `undefined`.)\n */\n private async listForIndex(type: string): Promise<unknown[]> {\n const items = new Map<string, unknown>();\n\n const typeStore = this.registry.get(type);\n if (typeStore) {\n for (const [name, data] of typeStore) {\n items.set(name, data);\n }\n }\n\n for (const loader of this.loaders.values()) {\n // No try/catch, on purpose — see the doc comment above.\n const loaderItems = await loader.loadMany(type);\n for (const item of loaderItems) {\n const itemAny = item as { name?: unknown };\n if (itemAny && typeof itemAny.name === 'string' && !items.has(itemAny.name)) {\n items.set(itemAny.name, item);\n }\n }\n }\n\n return Array.from(items.values());\n }\n\n /**\n * Unregister/remove a metadata item by type and name.\n * Deletes from database-backed loaders only (same rationale as register()).\n *\n * Announces the removal to {@link subscribe} watchers as a `deleted`\n * {@link MetadataWatchEvent} — the delete half of the {@link register}\n * contract. Pass `{ notify: false }` only for teardown that announces by\n * other means.\n *\n * ## [#5259] Storage FIRST, in-memory second — the order is the fix\n *\n * This method used to drop the registry entry and call\n * {@link invalidateListCache} *before* awaiting `loader.delete()`. Those two\n * steps are separated by a real await window (one DB round-trip per writable\n * loader), and inside it the manager was in a state that exists nowhere else:\n * **registry already empty, loader not yet empty**. `list()` merges the two,\n * so a read arriving in that window\n *\n * • missed the cache (it had just been invalidated),\n * • assembled the still-stored row into its answer, and\n * • memoized that answer as a COMPLETE read — the full 30s healthy TTL,\n * because no loader threw, so #5184's 2s degraded TTL never applied.\n *\n * Nothing invalidated again afterwards ({@link notifyWatchers} does not touch\n * `listCache`), so a row that was gone from storage kept being enumerated for\n * up to 30s — and `get()`, which never consulted that cache, disagreed with\n * `list()` the whole time. For a gating type (`permission`, `api`) the two\n * faces of the same manager answered opposite questions about whether a\n * declaration exists.\n *\n * {@link register} never had this defect, and the reason is instructive: it\n * writes the registry *first*, and the registry outranks every loader in the\n * merge, so throughout its own save window the merged view already equals the\n * post-write state. The invariant that makes register correct is not \"where\n * the invalidate sits\" but **the invalidate must be the last thing after\n * every store already holds the announced state**. Restated for delete, that\n * means storage first:\n *\n * 1. `await loader.delete()` on every writable loader. Throughout this\n * window registry AND loaders still hold the item, so a concurrent\n * `list()` observes a coherent pre-delete state — which is the truth,\n * because the delete has not landed and has not been announced.\n * 2. Drop the registry entry and `invalidateListCache(type)` — with **no\n * await between them**, so no read can interleave and observe the\n * half-applied state that produced the bug. Everything cached or\n * in-flight from step 1 is dropped here, at the moment the final state\n * becomes true.\n * 3. Publish + announce. #5219's invalidate-before-notify bar, unchanged:\n * a watcher woken by the `deleted` event and re-reading through `list()`\n * gets a fresh read of the post-delete state.\n *\n * **Composition with #5253's single-flight (this is the load-bearing half).**\n * A `list()` that is still walking the loaders when step 2 runs cannot be\n * fixed by dropping `listCache` alone — it has not written its entry yet, and\n * it would write the pre-delete answer *after* the invalidation. The\n * mechanism that covers it is `invalidateListCache()` also retracting the\n * read's registration in `inflightListReads`: a retracted read still resolves\n * for the callers already waiting on it (they asked before the delete) but\n * loses the right to memoize, and any caller arriving after step 2 starts a\n * fresh read rather than joining the pre-delete one. So every read is\n * covered: one that FINISHED in the window has its entry deleted, one still\n * IN FLIGHT loses its permission to cache, and one starting later reads the\n * post-delete state. That is why the invalidate must come after the deletes\n * rather than being duplicated on both sides of them — a second invalidate\n * before the await would buy nothing and would re-open step 1's window.\n */\n async unregister(type: string, name: string, options?: MetadataWriteOptions): Promise<void> {\n // [#7378 row 2] Remove from the store `register` wrote: the canonical type.\n type = canonicalMetadataServiceType(type);\n // ── 1. Storage first ────────────────────────────────────────────────\n // Delete only from database-backed loaders that declare write capability.\n for (const loader of this.loaders.values()) {\n if (loader.contract.protocol !== 'datasource:' || !loader.contract.capabilities.write) continue;\n // [#5276] Defensive only — unreachable for a registered loader. This exact\n // combination (`datasource:` + `capabilities.write`, no `delete`) is\n // rejected by `registerLoader()`, the sole writer of `this.loaders`, so\n // reaching this `continue` would mean a loader entered the map without\n // passing the gate. Kept because the alternative is a TypeError on the\n // line below, and because `delete?` stays optional on the interface for\n // the protocols the gate does not cover.\n if (typeof loader.delete !== 'function') continue;\n try {\n await this.deleteMetaItemFromLoader(loader, type, name);\n } catch (error) {\n this.reportMetaItemDeleteFailure(loader.contract.name, type, name, error);\n }\n }\n\n // ── 2. In-memory state, then invalidation — nothing awaited between ──\n const typeStore = this.registry.get(type);\n if (typeStore) {\n typeStore.delete(name);\n if (typeStore.size === 0) {\n this.registry.delete(type);\n }\n }\n this.invalidateListCache(type);\n\n // ── 3. Announce ─────────────────────────────────────────────────────\n // Publish metadata.{type}.deleted event to realtime service\n await this.publishRealtimeMetadataEvent('deleted', type, name, {\n userId: options?.userId,\n });\n\n // Announce last, once the removal has landed everywhere (see register()).\n if (options?.notify !== false) {\n this.notifyWatchers(type, {\n type: 'deleted',\n metadataType: type,\n name,\n path: '',\n data: undefined,\n timestamp: new Date().toISOString(),\n });\n }\n }\n\n /**\n * Delete one metadata item from one writable loader — the storage half of\n * {@link unregister}.\n *\n * A one-line wrapper on purpose: it gives this durability seam a **name**.\n * `check:durability-log-level` matches by callee name against an explicit\n * vocabulary, and the raw call is `loader.delete(...)` — putting `delete` in\n * that vocabulary would claim every `.delete()` in the monorepo (`Map`,\n * `Set`, cache handles, `URLSearchParams`) and the gate would drown in false\n * positives, which is exactly the failure mode its own header warns about.\n * Named here, `deleteMetaItemFromLoader` is in `DURABILITY_CRITICAL_CALLEES`\n * with a blast radius of precisely this call site, mirroring `saveMetaItem`\n * on the write side (#4754).\n *\n * [#5276] `MetadataLoader` now declares `delete?`, so no cast is left here.\n * It stays *optional* on the interface — `file:`/`memory:`/`http:`/`s3:`\n * loaders legitimately have none — and the guard below is therefore a type\n * narrowing rather than a policy decision. The policy lives at\n * `registerLoader()`: a `datasource:` loader that declares\n * `capabilities.write` cannot be registered without a `delete()`, which is\n * exactly the set of loaders this method is ever called for.\n */\n private async deleteMetaItemFromLoader(\n loader: MetadataLoader,\n type: string,\n name: string,\n ): Promise<void> {\n const del = loader.delete;\n if (typeof del !== 'function') return;\n await del.call(loader, type, name);\n }\n\n /**\n * Report — at `error` — that a loader refused to delete an item the runtime\n * has already dropped and announced as deleted.\n *\n * [#5259] This used to be a `logger.warn('Failed to delete …')` and continue.\n * AGENTS.md → \"Degradation log levels\" decides the level with one question:\n * *after the degradation, does the system still look normal from the outside\n * while something it claims is persisted has not actually landed?* Here it is\n * the deletion that did not land, which is the same class and the same\n * silence: `unregister()` resolves normally, the caller is told the delete\n * succeeded, and the surviving row is read straight back out of storage —\n * permanently, since nothing ever retries this. Durability/consistency\n * degradation ⇒ `error`, naming the **consequence** and the **fix**.\n *\n * **Why the registry entry is still dropped when this fires.** The\n * alternative — keep the item registered so runtime state matches storage —\n * looks safer and is not. The loader still holds the row, and `list()`/`get()`\n * merge registry ∪ loaders, so the item is served either way; the only thing\n * the surviving registry entry would change is *which copy wins*, pinning an\n * in-memory definition that outranks the stored row nobody is maintaining\n * anymore. Dropping it makes the very next read fall through to storage,\n * which is the actual truth after a failed delete — the item still exists —\n * and it surfaces that immediately (the item visibly reappears) instead of at\n * the next restart. One truth, read from where it lives; the divergence is\n * reported here rather than papered over with a second in-memory copy.\n *\n * **Said once per un-deleted item, not once per loader.** The once-per-outage\n * discipline of {@link reportLoaderReadFailure} exists because `list()` is hot\n * and its repeats are *identical*; these are not. Each line names a different\n * item that is still in storage and that nothing will ever retry, so\n * collapsing them would hand an operator the first casualty and silently drop\n * the rest of the list — the failure this level was raised to prevent.\n */\n private reportMetaItemDeleteFailure(\n loaderName: string,\n type: string,\n name: string,\n error: unknown,\n ): void {\n this.logger.error(\n `[MetadataManager] Loader \\`${loaderName}\\` could NOT delete \\`${type}/${name}\\` — the row is STILL in its store, ` +\n `while the runtime has already dropped the item from its registry and announced it as deleted. ` +\n `Nothing looks broken: \\`unregister()\\` resolves normally and the caller (Studio/Setup, REST DELETE, the CLI, a package teardown) ` +\n `is told the delete succeeded — but the surviving row is read straight back out of storage by the very next \\`list()\\`/\\`get()\\`, ` +\n `so the \"deleted\" item reappears and keeps reappearing across restarts. Nothing retries this delete. ` +\n `Fix: check the datasource behind \\`${loaderName}\\` — connection, credentials, and that its metadata table exists and is writable — ` +\n `then re-issue the delete for \\`${type}/${name}\\`. Until that succeeds the item is NOT deleted, whatever the delete call reported.`,\n error instanceof Error ? error : undefined,\n { loader: loaderName, type, name, error },\n );\n }\n\n /**\n * Check if a metadata item exists\n */\n async exists(type: string, name: string): Promise<boolean> {\n // [#7378 row 2] Same store `get` reads: the canonical type.\n type = canonicalMetadataServiceType(type);\n // Check in-memory registry\n if (this.registry.get(type)?.has(name)) {\n return true;\n }\n\n // Check loaders\n for (const loader of this.loaders.values()) {\n if (await loader.exists(type, name)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * List all names of metadata items of a given type\n */\n async listNames(type: string): Promise<string[]> {\n // [#7378 row 2] Same store `get` reads: the canonical type.\n type = canonicalMetadataServiceType(type);\n const names = new Set<string>();\n\n // From in-memory registry\n const typeStore = this.registry.get(type);\n if (typeStore) {\n for (const name of typeStore.keys()) {\n names.add(name);\n }\n }\n\n // From loaders\n for (const loader of this.loaders.values()) {\n const result = await loader.list(type);\n result.forEach(item => names.add(item));\n }\n\n return Array.from(names);\n }\n\n /**\n * Convenience: get an object definition by name\n */\n async getObject(name: string): Promise<unknown | undefined> {\n return this.get('object', name);\n }\n\n /**\n * Convenience: list all object definitions\n */\n async listObjects(): Promise<unknown[]> {\n return this.list('object');\n }\n\n // ==========================================\n // Convenience: UI Metadata\n // ==========================================\n\n /**\n * Convenience: get a view definition by name\n */\n async getView(name: string): Promise<unknown | undefined> {\n return this.get('view', name);\n }\n\n /**\n * Convenience: list view definitions, optionally filtered by object\n */\n async listViews(object?: string): Promise<unknown[]> {\n const views = await this.list('view');\n if (object) {\n return views.filter((v: any) => v?.object === object);\n }\n return views;\n }\n\n /**\n * List the independent ViewItems bound to an object, sorted for the runtime\n * view switcher / Studio left rail (\"Object has-many View\").\n *\n * Returns only expanded ViewItems (those carrying a `viewKind`) — never the\n * legacy aggregated container kept under the bare `<object>` key — so callers\n * get exactly one entry per named view. Sorted by `order`, then `name`.\n *\n * Runtime-authored `shared` / `personal` views (`sys_view_definition`) are\n * merged in by the REST layer; this method returns the `package` layer that\n * was registered from source.\n */\n async getViewsByObject(object: string): Promise<unknown[]> {\n const views = await this.list('view');\n return views\n .filter(\n (v: any) =>\n v && typeof v === 'object' && v.viewKind && v.object === object,\n )\n .sort(\n (a: any, b: any) =>\n (a.order ?? 0) - (b.order ?? 0) ||\n String(a.name).localeCompare(String(b.name)),\n );\n }\n\n /**\n * Convenience: get a dashboard definition by name\n */\n async getDashboard(name: string): Promise<unknown | undefined> {\n return this.get('dashboard', name);\n }\n\n /**\n * Convenience: list all dashboard definitions\n */\n async listDashboards(): Promise<unknown[]> {\n return this.list('dashboard');\n }\n\n // ==========================================\n // Package Management\n // ==========================================\n\n /**\n * Unregister all metadata items from a specific package\n */\n async unregisterPackage(packageName: string): Promise<void> {\n // Collect all items to delete (type and name pairs)\n const itemsToDelete: Array<{ type: string; name: string }> = [];\n\n for (const [type, typeStore] of this.registry) {\n for (const [name, data] of typeStore) {\n const meta = data as any;\n if (meta?.packageId === packageName || meta?.package === packageName) {\n itemsToDelete.push({ type, name });\n }\n }\n }\n\n // Delete each item using unregister() to ensure deletion from both registry and loaders\n for (const { type, name } of itemsToDelete) {\n await this.unregister(type, name);\n }\n }\n\n /**\n * Publish an entire package:\n * 1. Validate all draft items\n * 2. Snapshot all items in the package (publishedDefinition = clone(metadata))\n * 3. Increment version\n * 4. Set all items state → active\n *\n * [#5189, #5040 E7b] Step 1 additionally runs the **endpoint publish gates**\n * over every `api` item — see {@link gateApiItemsForPublish}. That pass is\n * NOT governed by `options.validate`: the gates are a contract, not a\n * lint (ADR-0121 D6 says publish REJECTS an unmetered anonymous endpoint),\n * and an opt-out flag on a security gate is the bypass this issue closed.\n */\n async publishPackage(packageId: string, options?: {\n changeNote?: string;\n publishedBy?: string;\n validate?: boolean;\n /**\n * [#5189] The package manifest's EXPLICIT `manifest.namespace` (ADR-0121\n * D2), supplied by a caller that holds the manifest.\n *\n * `MetadataManager` has no manifest concept — it indexes items by\n * `packageId` and nothing else — so it cannot prove a namespace on its\n * own, and an `api` item's own `namespace`-ish fields are author-supplied\n * data, not identity (reading them would make the D1/D2 carve-out gate\n * vacuous: an author would simply declare the namespace their path\n * already uses). Absent this option the namespace gate fails and `api`\n * items in the package cannot publish through this path — which is the\n * correct outcome, not a limitation to route around: a publish that\n * cannot prove a namespace must not mint a URL under one.\n */\n namespace?: string;\n }): Promise<PackagePublishResult> {\n const now = new Date().toISOString();\n const shouldValidate = options?.validate !== false;\n const publishedBy = options?.publishedBy;\n\n // Collect all items belonging to this package\n const packageItems: Array<{ type: string; name: string; data: any }> = [];\n for (const [type, typeStore] of this.registry) {\n for (const [name, data] of typeStore) {\n const meta = data as any;\n if (meta?.packageId === packageId || meta?.package === packageId) {\n packageItems.push({ type, name, data: meta });\n }\n }\n }\n\n if (packageItems.length === 0) {\n return {\n success: false,\n packageId,\n version: 0,\n publishedAt: now,\n itemsPublished: 0,\n validationErrors: [{ type: '', name: '', message: `No metadata items found for package '${packageId}'` }],\n };\n }\n\n const validationErrors: Array<{ type: string; name: string; message: string }> = [];\n\n // [#5189, #5040 E7b] Endpoint publish gates — ALWAYS, `validate: false`\n // included. Every other check in this method is a best-effort quality\n // check whose opt-out is a convenience; these gates decide whether an\n // externally reachable, possibly ANONYMOUS execution entry point comes\n // into existence, and ADR-0121 D6 has no runtime counterpart to catch what\n // slips through. A flag that turns them off would be exactly the bypass\n // #5189 filed.\n validationErrors.push(...this.gateApiItemsForPublish(packageItems, options?.namespace));\n\n // Validation pass\n if (shouldValidate) {\n // Schema validation\n for (const item of packageItems) {\n const result = await this.validate(item.type, item.data);\n if (!result.valid && result.errors) {\n for (const err of result.errors) {\n validationErrors.push({\n type: item.type,\n name: item.name,\n message: err.message,\n });\n }\n }\n }\n\n // Dependency validation: referenced items must be in the same package or already published\n const packageItemKeys = new Set(packageItems.map(i => `${i.type}:${i.name}`));\n for (const item of packageItems) {\n const deps = await this.getDependencies(item.type, item.name);\n for (const dep of deps) {\n const depKey = `${dep.targetType}:${dep.targetName}`;\n // Skip if the dependency is within this package\n if (packageItemKeys.has(depKey)) continue;\n // Check if the dependency exists and has been published\n const depItem = await this.get(dep.targetType, dep.targetName);\n if (!depItem) {\n validationErrors.push({\n type: item.type,\n name: item.name,\n message: `Dependency '${dep.targetType}:${dep.targetName}' not found`,\n });\n } else {\n const depMeta = depItem as any;\n if (depMeta.publishedDefinition === undefined && depMeta.state !== 'active') {\n validationErrors.push({\n type: item.type,\n name: item.name,\n message: `Dependency '${dep.targetType}:${dep.targetName}' is not published`,\n });\n }\n }\n }\n }\n }\n\n if (validationErrors.length > 0) {\n return {\n success: false,\n packageId,\n version: 0,\n publishedAt: now,\n itemsPublished: 0,\n validationErrors,\n };\n }\n\n // Determine the next version by finding the max current version across items\n let maxVersion = 0;\n for (const item of packageItems) {\n const v = typeof item.data.version === 'number' ? item.data.version : 0;\n if (v > maxVersion) maxVersion = v;\n }\n const newVersion = maxVersion + 1;\n\n // Snapshot and update all items\n for (const item of packageItems) {\n const updated = {\n ...item.data,\n publishedDefinition: structuredClone(item.data.metadata ?? item.data),\n publishedAt: now,\n publishedBy: publishedBy ?? item.data.publishedBy,\n version: newVersion,\n state: 'active',\n };\n await this.register(item.type, item.name, updated);\n }\n\n return {\n success: true,\n packageId,\n version: newVersion,\n publishedAt: now,\n itemsPublished: packageItems.length,\n };\n }\n\n /**\n * [#5189, #5040 E7b] Run the endpoint publish gates over a package's `api`\n * items and report every failure as a publish-blocking validation error.\n *\n * ## Why this exists at all\n *\n * E7 (#5111) hung the five per-endpoint gates on\n * `ObjectStackDefinitionSchema`, which covers every path that parses a\n * STACK — `defineStack`, `os validate`, the lint scorer, artifact ingest,\n * `EnvironmentArtifactSchema.metadata`. It does not cover this one: an `api`\n * item can be minted item-by-item (`metadata.register()`, a Studio write)\n * and published here without a stack ever being parsed. Three of the gates\n * degrade safely when bypassed (the executor answers a structured 501; a\n * mis-namespaced path matches nothing), but **ADR-0121 D6 has no runtime\n * counterpart**: `authRequired: false` is honoured faithfully and an\n * unarmed `rateLimit` meters nothing, so the bypass mints an anonymous,\n * zero-quota execution entry point. Hence a gate here, on the same\n * function, rather than a second set of criteria that would drift.\n *\n * ## What it judges, and on what\n *\n * The registry stores either a raw spec document or a publish envelope\n * (`{ name, packageId, state, metadata: {…spec} }`), and in BOTH shapes the\n * row carries the metadata layer's bookkeeping. [#5309] The envelope is\n * peeled off first (`peelStoredEnvelope`) and the gate judges the authored\n * BODY: the wrapped half of that peel is the `data.metadata ?? data` rule\n * this method used to spell inline — the same document `publishedDefinition`\n * snapshots — and the flat half additionally removes `packageId` / `state` /\n * `version` / `published*`, which are storage identity, never endpoint\n * vocabulary. (What publish SNAPSHOTS is unchanged: `publishedDefinition`\n * still stores `data.metadata ?? data` verbatim, envelope included, because\n * `revertPackage` restores from it.) An item whose body does not satisfy\n * `ApiEndpointSchema` fails here too — not extra strictness but a\n * precondition: an unparsed shape cannot be gated, and it could never be\n * served either (the matcher's own loud skip refuses it at load).\n *\n * @param packageItems every item collected for this package (all types).\n * @param namespace the caller-supplied `manifest.namespace`; `undefined`\n * fails the namespace gate, deliberately — see `publishPackage`'s option.\n * @returns one entry per gate failure, `[]` when the package declares no\n * `api` items (a package without endpoints is untouched by this pass).\n */\n private gateApiItemsForPublish(\n packageItems: Array<{ type: string; name: string; data: any }>,\n namespace: string | undefined,\n ): Array<{ type: string; name: string; message: string }> {\n const apiItems = packageItems.filter(i => i.type === MetadataManager.ENDPOINT_METADATA_TYPE);\n if (apiItems.length === 0) return [];\n\n const errors: Array<{ type: string; name: string; message: string }> = [];\n /** Parsed endpoints, index-aligned with the items that produced them. */\n const endpoints: ApiEndpoint[] = [];\n const gatedItems: Array<{ name: string }> = [];\n\n for (const item of apiItems) {\n // [#5309] Envelope OFF before the body parse — the same peel the load-time\n // backstop applies (`buildEndpointIndex`), so the two doors judge the same\n // document. The wrapped half of the rule is the `data.metadata ?? data`\n // this line used to spell inline; the flat half additionally takes off the\n // bookkeeping (`packageId`, `state`, …) that shares a level with the body.\n const { body } = peelStoredEnvelope(item.data);\n const parsed = ApiEndpointSchema.safeParse(body);\n if (!parsed.success) {\n for (const issue of parsed.error.issues) {\n errors.push({\n type: item.type,\n name: item.name,\n message:\n `api item '${item.name}' does not satisfy ApiEndpointSchema and cannot be published: `\n + `${issue.message} (at ${issue.path.join('.') || '<root>'}). An endpoint that does not `\n + `parse cannot be gated and would be excluded from endpoint matching at load anyway.`,\n });\n }\n continue;\n }\n endpoints.push(parsed.data);\n gatedItems.push({ name: item.name });\n }\n\n for (const issue of validateApiEndpointDeclarations(endpoints, { namespace })) {\n // The gate reports per-endpoint issues at `['apis', <index>, …]` and the\n // namespace PRECONDITION once at `['apis']` — the latter is a property of\n // the publish call, not of any one endpoint, so it is reported once with\n // this path's own remedy appended.\n const index = typeof issue.path[1] === 'number' ? issue.path[1] : undefined;\n if (index === undefined) {\n errors.push({\n type: MetadataManager.ENDPOINT_METADATA_TYPE,\n name: '',\n message: `${issue.message} ${PUBLISH_NAMESPACE_REMEDY}`,\n });\n continue;\n }\n errors.push({\n type: MetadataManager.ENDPOINT_METADATA_TYPE,\n name: gatedItems[index]?.name ?? '',\n message: issue.message,\n });\n }\n\n return errors;\n }\n\n /**\n * Revert entire package to last published state.\n * Restores all metadata definitions from their published snapshots.\n */\n async revertPackage(packageId: string): Promise<void> {\n const packageItems: Array<{ type: string; name: string; data: any }> = [];\n for (const [type, typeStore] of this.registry) {\n for (const [name, data] of typeStore) {\n const meta = data as any;\n if (meta?.packageId === packageId || meta?.package === packageId) {\n packageItems.push({ type, name, data: meta });\n }\n }\n }\n\n // [#7559] ADR-0112 — both refusals below carry a DECLARED `code` + `status`.\n // They are the ordinary answers to an ordinary request (revert a package id\n // that this environment has nothing for, or has never published), and a\n // route that cannot serve the request must answer a declared 4xx.\n //\n // This is the WHOLE cause of the 500 the QA run saw from\n // `POST /packages/:id/revert`, measured rather than assumed: that route's\n // handler already wraps its entire body in one\n // `try { … } catch (e) { errorFromThrown(e, 500) }`, and `errorFromThrown`\n // reads `status` / `code` off the error — falling back to 500 only when it\n // finds neither, which is exactly what a bare `Error` offers. Nothing was\n // wrong with the route; the thrown shape was. (The first reading of #7559\n // was that the route needed its own `catch`; reverse verification showed\n // that change was inert, so it is not in this fix.)\n //\n // Both codes come from the ADR-0112 STANDARD catalog rather than the\n // extension ledger: the ledger's own rule is that a generic condition (not\n // found / conflict) uses the standard catalog instead of registering a\n // synonym.\n if (packageItems.length === 0) {\n const err = new Error(\n `No metadata items found for package '${packageId}'`,\n ) as Error & { code?: string; status?: number };\n err.code = 'RESOURCE_NOT_FOUND';\n err.status = 404;\n throw err;\n }\n\n // Check that at least one item has a published snapshot\n const hasPublished = packageItems.some(item => item.data.publishedDefinition !== undefined);\n if (!hasPublished) {\n const err = new Error(\n `Package '${packageId}' has never been published`,\n ) as Error & { code?: string; status?: number };\n err.code = 'RESOURCE_CONFLICT';\n err.status = 409;\n throw err;\n }\n\n for (const item of packageItems) {\n if (item.data.publishedDefinition !== undefined) {\n const reverted = {\n ...item.data,\n metadata: structuredClone(item.data.publishedDefinition),\n state: 'active',\n };\n await this.register(item.type, item.name, reverted);\n }\n }\n }\n\n /**\n * Get the published version of any metadata item (for runtime serving).\n * Returns publishedDefinition if exists, else current definition.\n */\n async getPublished(type: string, name: string): Promise<unknown | undefined> {\n const item = await this.get(type, name);\n if (!item) return undefined;\n\n const meta = item as any;\n if (meta.publishedDefinition !== undefined) {\n return meta.publishedDefinition;\n }\n\n // Fall back to current definition (metadata field or the item itself)\n return meta.metadata ?? item;\n }\n\n // ==========================================\n // Query / Search\n // ==========================================\n\n /**\n * Query metadata items with filtering, sorting, and pagination\n */\n async query(query: MetadataQuery): Promise<MetadataQueryResult> {\n const { types, search, page = 1, pageSize = 50, sortBy = 'name', sortOrder = 'asc' } = query;\n\n // Collect all items\n const allItems: Array<{\n type: string;\n name: string;\n namespace?: string;\n label?: string;\n scope?: 'system' | 'platform' | 'user';\n state?: 'draft' | 'active' | 'archived' | 'deprecated';\n packageId?: string;\n updatedAt?: string;\n }> = [];\n\n // Determine which types to scan\n const targetTypes = types && types.length > 0\n ? types\n : Array.from(this.registry.keys());\n\n for (const type of targetTypes) {\n const items = await this.list(type);\n for (const item of items) {\n const meta = item as any;\n allItems.push({\n type,\n name: meta?.name ?? '',\n namespace: meta?.namespace,\n label: meta?.label,\n scope: meta?.scope,\n state: meta?.state,\n packageId: meta?.packageId,\n updatedAt: meta?.updatedAt,\n });\n }\n }\n\n // Apply search filter\n let filtered = allItems;\n if (search) {\n const searchLower = search.toLowerCase();\n filtered = filtered.filter(item =>\n item.name.toLowerCase().includes(searchLower) ||\n (item.label && item.label.toLowerCase().includes(searchLower))\n );\n }\n\n // Apply scope filter\n if (query.scope) {\n filtered = filtered.filter(item => item.scope === query.scope);\n }\n\n // Apply state filter\n if (query.state) {\n filtered = filtered.filter(item => item.state === query.state);\n }\n\n // Apply namespace filter\n if (query.namespaces && query.namespaces.length > 0) {\n filtered = filtered.filter(item => item.namespace && query.namespaces!.includes(item.namespace));\n }\n\n // Apply packageId filter\n if (query.packageId) {\n filtered = filtered.filter(item => item.packageId === query.packageId);\n }\n\n // Apply tags filter\n if (query.tags && query.tags.length > 0) {\n filtered = filtered.filter(item => {\n const meta = item as any;\n return meta?.tags && query.tags!.some((t: string) => meta.tags.includes(t));\n });\n }\n\n // Sort\n filtered.sort((a, b) => {\n const aVal = (a as any)[sortBy] ?? '';\n const bVal = (b as any)[sortBy] ?? '';\n const cmp = String(aVal).localeCompare(String(bVal));\n return sortOrder === 'desc' ? -cmp : cmp;\n });\n\n // Paginate\n const total = filtered.length;\n const start = (page - 1) * pageSize;\n const paged = filtered.slice(start, start + pageSize);\n\n return {\n items: paged,\n total,\n page,\n pageSize,\n };\n }\n\n // ==========================================\n // Bulk Operations\n // ==========================================\n\n /**\n * Register multiple metadata items in a single batch.\n *\n * Announces one event per item, like {@link register}. Pass\n * `{ notify: false }` when the batch is boot-time ingest or when the caller\n * announces the whole set once — see {@link MetadataWriteOptions.notify}.\n */\n async bulkRegister(\n items: Array<{ type: string; name: string; data: unknown }>,\n options?: { continueOnError?: boolean; validate?: boolean } & MetadataWriteOptions\n ): Promise<MetadataBulkResult> {\n const { continueOnError = false, notify } = options ?? {};\n let succeeded = 0;\n let failed = 0;\n const errors: Array<{ type: string; name: string; error: string }> = [];\n\n for (const item of items) {\n try {\n await this.register(item.type, item.name, item.data, { notify });\n succeeded++;\n } catch (e) {\n failed++;\n errors.push({\n type: item.type,\n name: item.name,\n error: e instanceof Error ? e.message : String(e),\n });\n if (!continueOnError) break;\n }\n }\n\n return {\n total: items.length,\n succeeded,\n failed,\n errors: errors.length > 0 ? errors : undefined,\n };\n }\n\n /**\n * Unregister multiple metadata items in a single batch.\n *\n * Announces one `deleted` event per item, like {@link unregister}.\n */\n async bulkUnregister(\n items: Array<{ type: string; name: string }>,\n options?: MetadataWriteOptions,\n ): Promise<MetadataBulkResult> {\n let succeeded = 0;\n let failed = 0;\n const errors: Array<{ type: string; name: string; error: string }> = [];\n\n for (const item of items) {\n try {\n await this.unregister(item.type, item.name, options);\n succeeded++;\n } catch (e) {\n failed++;\n errors.push({\n type: item.type,\n name: item.name,\n error: e instanceof Error ? e.message : String(e),\n });\n }\n }\n\n return {\n total: items.length,\n succeeded,\n failed,\n errors: errors.length > 0 ? errors : undefined,\n };\n }\n\n // ==========================================\n // Overlay / Customization Management\n // ==========================================\n\n private overlayKey(type: string, name: string, scope: string = 'platform'): string {\n return `${encodeURIComponent(type)}:${encodeURIComponent(name)}:${scope}`;\n }\n\n /**\n * Get the active overlay for a metadata item\n */\n async getOverlay(type: string, name: string, scope?: 'platform' | 'user'): Promise<MetadataOverlay | undefined> {\n return this.overlays.get(this.overlayKey(type, name, scope ?? 'platform'));\n }\n\n /**\n * Save/update an overlay for a metadata item\n */\n async saveOverlay(overlay: MetadataOverlay): Promise<void> {\n // Overlay write gate — independent from base writability so deployments\n // can freeze Studio overlays while still permitting base register().\n if (this.config.persistence?.overlayWritable === false) {\n const msg = `MetadataManager overlays are read-only (persistence.overlayWritable=false); refusing to save overlay for ${overlay.baseType}/${overlay.baseName}`;\n if (this.config.validation?.throwOnError) {\n throw new Error(msg);\n }\n this.logger.warn(msg);\n return;\n }\n const key = this.overlayKey(overlay.baseType, overlay.baseName, overlay.scope);\n this.overlays.set(key, overlay);\n }\n\n /**\n * Remove an overlay, reverting to the base definition\n */\n async removeOverlay(type: string, name: string, scope?: 'platform' | 'user'): Promise<void> {\n this.overlays.delete(this.overlayKey(type, name, scope ?? 'platform'));\n }\n\n /**\n * Get the effective (merged) metadata after applying all overlays.\n * Resolution order: system ← merge(platform) ← merge(user)\n */\n async getEffective(type: string, name: string, context?: {\n userId?: string;\n tenantId?: string;\n roles?: string[];\n permissions?: string[];\n }): Promise<unknown | undefined> {\n const base = await this.get(type, name);\n if (!base) return undefined;\n\n let effective = { ...(base as Record<string, unknown>) };\n\n // Apply platform overlay\n const platformOverlay = await this.getOverlay(type, name, 'platform');\n if (platformOverlay?.active && platformOverlay.patch) {\n effective = { ...effective, ...platformOverlay.patch };\n }\n\n // Apply user overlay (scoped to specific user if context provided)\n if (context?.userId) {\n // Try user-specific key first, then fall back to generic user overlay.\n // The owner check below ensures we never apply another user's overlay.\n const userOverlayKey = this.overlayKey(type, name, 'user') + `:${context.userId}`;\n const userOverlay = this.overlays.get(userOverlayKey) \n ?? await this.getOverlay(type, name, 'user');\n if (userOverlay?.active && userOverlay.patch) {\n // Apply if: overlay has no owner (generic user-level), or owner matches current user\n if (!userOverlay.owner || userOverlay.owner === context.userId) {\n effective = { ...effective, ...userOverlay.patch };\n }\n }\n } else {\n // No user context — only apply user overlays without an owner restriction\n // (owner-scoped overlays require a userId to resolve)\n const userOverlay = await this.getOverlay(type, name, 'user');\n if (userOverlay?.active && userOverlay.patch && !userOverlay.owner) {\n effective = { ...effective, ...userOverlay.patch };\n }\n }\n\n return effective;\n }\n\n // ==========================================\n // Watch / Subscribe (IMetadataService)\n // ==========================================\n\n /**\n * Watch for metadata changes (IMetadataService contract).\n * Returns a handle for unsubscribing.\n */\n watchService(type: string, callback: MetadataWatchCallback): MetadataWatchHandle {\n const wrappedCallback: WatchCallback = (event) => {\n const mappedType = event.type === 'added' ? 'registered'\n : event.type === 'deleted' ? 'unregistered'\n : 'updated';\n callback({\n type: mappedType,\n metadataType: event.metadataType ?? type,\n name: event.name ?? '',\n data: event.data,\n });\n };\n this.addWatchCallback(type, wrappedCallback);\n return {\n unsubscribe: () => this.removeWatchCallback(type, wrappedCallback),\n };\n }\n\n /**\n * Subscribe to raw metadata watch events for a given type.\n *\n * Unlike `watchService` (which maps to the IMetadataService contract and\n * drops fields like `path`/`timestamp`), this returns the raw\n * `MetadataWatchEvent` produced by the underlying watcher — useful for\n * developer-facing tooling such as the HMR SSE endpoint that wants the\n * source file path and original timestamp.\n *\n * @returns An unsubscribe function.\n */\n subscribe(type: string, callback: WatchCallback): () => void {\n // [#7378 row 2] Announcements are made under the canonical type (register/\n // unregister fold before notifyWatchers), so a subscription under the\n // plural spelling must land on the same key or it would never fire.\n type = canonicalMetadataServiceType(type);\n this.addWatchCallback(type, callback);\n return () => this.removeWatchCallback(type, callback);\n }\n\n // ==========================================\n // Import / Export\n // ==========================================\n\n /**\n * Export metadata as a portable bundle\n */\n async exportMetadata(options?: MetadataExportOptions): Promise<unknown> {\n const bundle: Record<string, unknown[]> = {};\n const targetTypes = options?.types ?? Array.from(this.registry.keys());\n\n for (const type of targetTypes) {\n const items = await this.list(type);\n if (items.length > 0) {\n bundle[type] = items;\n }\n }\n\n return bundle;\n }\n\n /**\n * Import metadata from a portable bundle\n */\n async importMetadata(data: unknown, options?: MetadataImportOptions): Promise<MetadataImportResult> {\n const {\n conflictResolution = 'skip',\n validate: _validate = true,\n dryRun = false,\n } = options ?? {};\n\n const bundle = data as Record<string, unknown[]>;\n let total = 0;\n let imported = 0;\n let skipped = 0;\n let failed = 0;\n const errors: Array<{ type: string; name: string; error: string }> = [];\n\n for (const [type, items] of Object.entries(bundle)) {\n if (!Array.isArray(items)) continue;\n\n for (const item of items) {\n total++;\n const meta = item as any;\n const name = meta?.name;\n\n if (!name) {\n failed++;\n errors.push({ type, name: '(unknown)', error: 'Item missing name field' });\n continue;\n }\n\n try {\n const itemExists = await this.exists(type, name);\n\n if (itemExists && conflictResolution === 'skip') {\n skipped++;\n continue;\n }\n\n if (!dryRun) {\n if (itemExists && conflictResolution === 'merge') {\n const existing = await this.get(type, name);\n const merged = { ...(existing as any), ...(item as any) };\n await this.register(type, name, merged);\n } else {\n await this.register(type, name, item);\n }\n }\n imported++;\n } catch (e) {\n failed++;\n errors.push({\n type,\n name,\n error: e instanceof Error ? e.message : String(e),\n });\n }\n }\n }\n\n return {\n total,\n imported,\n skipped,\n failed,\n errors: errors.length > 0 ? errors : undefined,\n };\n }\n\n // ==========================================\n // Validation\n // ==========================================\n\n /**\n * Validate a metadata item against its type schema.\n *\n * NOTE: This is a lightweight structural check (presence of `name`,\n * basic shape). The authoritative spec validation lives in\n * `protocol.saveMetaItem` (write path) and is surfaced on read\n * paths via the `_diagnostics` envelope attached by\n * `protocol.getMetaItems` / `getMetaItem`. Both delegate to\n * `getMetadataTypeSchema()` — the single source of truth. We\n * deliberately do NOT run the full Zod schema here because\n * `MetadataManager`'s registry stores *publish envelopes*\n * (`{name, packageId, state, metadata: {...spec}}`), not raw spec\n * documents — running spec validation against the envelope would\n * yield false negatives.\n */\n async validate(_type: string, data: unknown): Promise<MetadataValidationResult> {\n // Basic structural validation\n if (data === null || data === undefined) {\n return {\n valid: false,\n errors: [{ path: '', message: 'Metadata data cannot be null or undefined' }],\n };\n }\n\n if (typeof data !== 'object') {\n return {\n valid: false,\n errors: [{ path: '', message: 'Metadata data must be an object' }],\n };\n }\n\n const meta = data as any;\n const warnings: Array<{ path: string; message: string }> = [];\n\n if (!meta.name) {\n return {\n valid: false,\n errors: [{ path: 'name', message: 'Metadata item must have a name field' }],\n };\n }\n\n if (!meta.label) {\n warnings.push({ path: 'label', message: 'Missing label field (recommended)' });\n }\n\n return { valid: true, warnings: warnings.length > 0 ? warnings : undefined };\n }\n\n // ==========================================\n // Type Registry\n // ==========================================\n\n /**\n * Get all registered metadata types\n */\n async getRegisteredTypes(): Promise<string[]> {\n const types = new Set<string>();\n\n // From type registry\n for (const entry of this.typeRegistry) {\n types.add(entry.type);\n }\n\n // From in-memory registry (custom types)\n for (const type of this.registry.keys()) {\n types.add(type);\n }\n\n return Array.from(types);\n }\n\n /**\n * Get detailed information about a metadata type\n */\n async getTypeInfo(type: string): Promise<MetadataTypeInfo | undefined> {\n const entry = this.typeRegistry.find(e => e.type === type);\n if (!entry) return undefined;\n\n // Merge declarative (live registry entry — the built-in\n // `DEFAULT_METADATA_TYPE_REGISTRY`, the registry's only production writer;\n // the plugin-contributed `additionalTypes` channel this comment used to\n // claim never existed and was retired by #8586) + plugin-registered\n // type-level actions. Deduped by name; imperatively-registered actions win on\n // collision. Emitted so the metadata-admin engine can render per-type\n // buttons (e.g. datasource \"Test connection\"). Omit the key entirely\n // when the type has none, to keep the response lean.\n const byName = new Map<string, any>();\n for (const a of (entry.actions ?? [])) byName.set(a.name, a);\n for (const a of getMetadataTypeActions(type)) byName.set(a.name, a);\n const actions = Array.from(byName.values());\n\n return {\n type: entry.type,\n label: entry.label,\n description: entry.description,\n filePatterns: entry.filePatterns,\n supportsOverlay: entry.supportsOverlay,\n domain: entry.domain,\n ...(actions.length > 0 ? { actions } : {}),\n };\n }\n\n // ==========================================\n // Dependency Tracking\n // ==========================================\n\n /**\n * Get metadata items that this item depends on\n */\n async getDependencies(type: string, name: string): Promise<MetadataDependency[]> {\n return this.dependencies.get(`${encodeURIComponent(type)}:${encodeURIComponent(name)}`) ?? [];\n }\n\n /**\n * Get metadata items that depend on this item\n */\n async getDependents(type: string, name: string): Promise<MetadataDependency[]> {\n const dependents: MetadataDependency[] = [];\n for (const deps of this.dependencies.values()) {\n for (const dep of deps) {\n if (dep.targetType === type && dep.targetName === name) {\n dependents.push(dep);\n }\n }\n }\n return dependents;\n }\n\n /**\n * Register a dependency between two metadata items.\n * Used internally to track cross-references.\n * Duplicate dependencies (same source, target, and kind) are ignored.\n */\n addDependency(dep: MetadataDependency): void {\n const key = `${encodeURIComponent(dep.sourceType)}:${encodeURIComponent(dep.sourceName)}`;\n if (!this.dependencies.has(key)) {\n this.dependencies.set(key, []);\n }\n const existing = this.dependencies.get(key)!;\n const isDuplicate = existing.some(\n d => d.targetType === dep.targetType && d.targetName === dep.targetName && d.kind === dep.kind\n );\n if (!isDuplicate) {\n existing.push(dep);\n }\n }\n\n // ==========================================\n // API Endpoint Resolution\n // ==========================================\n\n /**\n * Resolve a request's `method`+`path` to the declared `api` metadata item\n * that owns it — `IMetadataService.matchEndpoint` (#5080 contract, #5089\n * implementation, #5040 E2).\n *\n * The behaviour is specified by the contract text in\n * `packages/spec/src/contracts/metadata-service.ts`; the mechanics\n * (normalization, lazy index, loud parse-skip, duplicate resolution) live in\n * `./endpoint-matcher.ts` and are documented there.\n *\n * Scope is THIS instance. There is no environment parameter, because callers\n * already resolve the `metadata` service for the environment they serve —\n * adding one here would create a second scoping mechanism.\n *\n * This method is reached over HTTP on a real boot. The dispatcher seam\n * landed as #5090 (`packages/runtime/src/api-endpoint-step.ts`, called from\n * the `setFallbackHandler` the dispatcher plugin installs), and #4936's\n * wholesale publish refusal of a non-empty `apis:` was replaced by the\n * #5040 E7 per-shape gates (`packages/spec/src/api/endpoint-publish-gate.ts`)\n * — so declarations exist and requests arrive here. The showcase's two\n * declared endpoints are matched and executed through this path in\n * `packages/qa/dogfood/test/showcase-declarative-endpoints.dogfood.test.ts`.\n *\n * @throws when the metadata store cannot be read — an outage must never be\n * reported as a miss, because a miss becomes a 404.\n */\n async matchEndpoint(query: { path: string; method: string }): Promise<ApiEndpointMatch | undefined> {\n return this.endpointMatcher.match(query);\n }\n\n // ==========================================\n // Legacy Loader API (backward compatible)\n // ==========================================\n\n /**\n * Load a single metadata item from loaders.\n * Iterates through registered loaders until found.\n *\n * Returns `null` both when no loader HAS the item and when every loader\n * FAILED — see {@link loadDiagnosed} when the caller must tell those apart.\n */\n async load<T = any>(\n type: string,\n name: string,\n options?: MetadataLoadOptions\n ): Promise<T | null> {\n return (await this.loadDiagnosed<T>(type, name, options)).data;\n }\n\n /**\n * `load`, plus whether the answer can be trusted as complete.\n *\n * [ADR-0110 D3] A miss and an outage are different facts with opposite\n * security meanings, and plain `load` cannot express the difference: a\n * loader that throws is warn-logged and skipped, so a database the metadata\n * plane cannot reach returns the same `null` as a name that was never\n * declared. Callers that gate on a declaration MUST NOT read that `null` as\n * \"the author declared no gate\" — an availability failure would silently\n * widen access (the REST `/actions` route's fail-open branch, #3935).\n *\n * `degraded` is true when at least one loader threw AND no loader answered\n * with the item. The posture is deliberately conservative: with a loader\n * down we cannot prove the item is absent, so we decline to claim it is.\n * A clean miss (every loader answered, none had it) is NOT degraded.\n */\n async loadDiagnosed<T = any>(\n type: string,\n name: string,\n options?: MetadataLoadOptions\n ): Promise<{ data: T | null; degraded: boolean; errors: string[] }> {\n const errors: string[] = [];\n for (const loader of this.loaders.values()) {\n try {\n const result = await loader.load(type, name, options);\n if (result.data) {\n return { data: result.data as T, degraded: false, errors };\n }\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n errors.push(`${loader.contract.name}: ${message}`);\n this.logger.warn(`Loader ${loader.contract.name} failed to load ${type}:${name}`, { error: e });\n }\n }\n return { data: null, degraded: errors.length > 0, errors };\n }\n\n /**\n * Load multiple metadata items from loaders.\n * Aggregates results from all loaders.\n */\n async loadMany<T = any>(\n type: string,\n options?: MetadataLoadOptions\n ): Promise<T[]> {\n const results: T[] = [];\n\n for (const loader of this.loaders.values()) {\n try {\n const items = await loader.loadMany<T>(type, options);\n for (const item of items) {\n const itemAny = item as any;\n if (itemAny && typeof itemAny.name === 'string') {\n const exists = results.some((r: any) => r && r.name === itemAny.name);\n if (exists) continue;\n }\n results.push(item);\n }\n this.reportLoaderReadRecovered(loader.contract.name);\n } catch (e) {\n // [#5108] Same seam, same verdict as `list()` — see\n // {@link reportLoaderReadFailure}. Two adjacent plural reads\n // reporting one storage outage at two different levels is how the\n // wrong one gets copied.\n this.reportLoaderReadFailure(loader.contract.name, type, e);\n }\n }\n return results;\n }\n\n /**\n * Save metadata item to a loader\n */\n async save<T = any>(\n type: string,\n name: string,\n data: T,\n options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n const targetLoader = (options as any)?.loader;\n\n let loader: MetadataLoader | undefined;\n \n if (targetLoader) {\n loader = this.loaders.get(targetLoader);\n if (!loader) {\n throw new Error(`Loader not found: ${targetLoader}`);\n }\n } else {\n for (const l of this.loaders.values()) {\n if (!l.save) continue;\n try {\n if (await l.exists(type, name)) {\n loader = l;\n this.logger.info(`Updating existing metadata in loader: ${l.contract.name}`);\n break;\n }\n } catch (e) {\n // Ignore existence check errors\n }\n }\n\n if (!loader) {\n const fsLoader = this.loaders.get('filesystem');\n if (fsLoader && fsLoader.save) {\n loader = fsLoader;\n }\n }\n\n if (!loader) {\n for (const l of this.loaders.values()) {\n if (l.save) {\n loader = l;\n break;\n }\n }\n }\n }\n\n if (!loader) {\n throw new Error(`No loader available for saving type: ${type}`);\n }\n\n if (!loader.save) {\n throw new Error(`Loader '${loader.contract?.name}' does not support saving`);\n }\n\n return loader.save(type, name, data, options);\n }\n\n /**\n * Register a watch callback for metadata changes\n */\n protected addWatchCallback(type: string, callback: WatchCallback): void {\n if (!this.watchCallbacks.has(type)) {\n this.watchCallbacks.set(type, new Set());\n }\n this.watchCallbacks.get(type)!.add(callback);\n }\n\n /**\n * Remove a watch callback for metadata changes\n */\n protected removeWatchCallback(type: string, callback: WatchCallback): void {\n const callbacks = this.watchCallbacks.get(type);\n if (callbacks) {\n callbacks.delete(callback);\n if (callbacks.size === 0) {\n this.watchCallbacks.delete(type);\n }\n }\n }\n\n /**\n * Stop all watching\n */\n async stopWatching(): Promise<void> {\n // Override in subclass\n }\n\n // ─── ADR-0008 PR-6: Repository wiring ───────────────────────────────\n\n /**\n * Attach a {@link MetadataRepository} as a supplementary event source.\n *\n * The manager subscribes to `repo.watch({})` and re-emits each event\n * through {@link notifyWatchers} as a legacy `MetadataWatchEvent`.\n * Each event also invalidates the in-memory registry entry and the\n * `list()` cache for the affected type so subsequent reads see fresh\n * data.\n *\n * No write-through. `register()` / `unregister()` / `save()` are\n * untouched in this PR (deferred to ADR-0008 M0 PR-10).\n *\n * Call {@link dispose} (or {@link stopRepositoryWatch}) to detach.\n */\n setRepository(repo: MetadataRepository): void {\n if (this.repository === repo) return;\n if (this.repository) {\n void this.stopRepositoryWatch();\n }\n this.repository = repo;\n this.repoWatchClosed = false;\n void this.startRepositoryWatch();\n }\n\n /** Return the attached repository, if any. */\n getRepository(): MetadataRepository | undefined {\n return this.repository;\n }\n\n /** Stop the active repo.watch() loop (best-effort). */\n async stopRepositoryWatch(): Promise<void> {\n this.repoWatchClosed = true;\n const iter = this.repoWatchIter;\n this.repoWatchIter = undefined;\n if (iter && typeof iter.return === 'function') {\n try { await iter.return(undefined); } catch { /* noop */ }\n }\n }\n\n /**\n * Best-effort cleanup. Stops the FS watcher (if any), drains the\n * repository watch loop, and clears registry caches. Safe to call\n * multiple times.\n */\n async dispose(): Promise<void> {\n await this.stopWatching().catch(() => undefined);\n await this.stopRepositoryWatch().catch(() => undefined);\n this.listCache.clear();\n this.endpointMatcher.invalidate();\n }\n\n private async startRepositoryWatch(): Promise<void> {\n const repo = this.repository;\n if (!repo) return;\n const iterable = repo.watch({});\n const iter = (iterable as AsyncIterable<MetadataEvent>)[Symbol.asyncIterator]();\n this.repoWatchIter = iter;\n try {\n while (!this.repoWatchClosed) {\n const { value, done } = await iter.next();\n if (done) break;\n try {\n this.applyRepoEvent(value);\n } catch (err) {\n this.logger.warn('[MetadataManager] repo event handler failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n } catch (err) {\n if (!this.repoWatchClosed) {\n this.logger.warn('[MetadataManager] repository watch loop exited unexpectedly', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n } finally {\n if (this.repoWatchIter === iter) this.repoWatchIter = undefined;\n }\n }\n\n /**\n * Drop every local cache of `type` (and of `name` within it) that a change\n * we did not perform ourselves has just invalidated, so the next read falls\n * through to the source of truth.\n *\n * The callers are the manager's *foreign-write* seams — the repository watch\n * loop ({@link applyRepoEvent}), the cluster peer replay in\n * {@link attachClusterPubSub}, and — since #5218 — `NodeMetadataManager`'s\n * chokidar handler, which is why this is `protected` rather than `private`.\n * All three learn about a write that landed somewhere else (the repo head;\n * another node's `sys_metadata`; an editor writing `rootDir/view/x.json`) and\n * hold caches that the write silently aged out. A file event qualifies on\n * exactly the definition that matters here: it did not come through this\n * manager's write API, so nothing has updated the caches on its behalf.\n * Local writes do not come through here: `register()` / `unregister()` /\n * `registerInMemory()` update the registry to the value they just wrote and\n * call `invalidateListCache()` themselves.\n *\n * **Delete, do not pre-fill.** Even when the event carries a body we drop the\n * registry entry rather than writing the body into it: the body reaching us\n * is a snapshot of *someone else's* write, already possibly superseded, and\n * pre-filling would race with the true head and require us to re-canonicalise\n * a definition we did not load. Lazy invalidation is the safer default —\n * `get()` then falls through to the loaders / repository, which is where the\n * truth is. (This paragraph is the rationale `applyRepoEvent` carried since\n * ADR-0008 PR-6; #5109 extended the same choice to the cluster path, #5218 to\n * the filesystem watcher — where \"the truth\" is the file chokidar just\n * reported, served by the `FilesystemLoader` the registry entry was shadowing.)\n *\n * `name` is optional because `MetadataWatchEvent.name` is: a nameless event\n * cannot address a registry entry, so it invalidates the list cache only.\n * Dropping the whole type store instead would evict `registerInMemory()`\n * artefacts (code-owned datasources, ADR-0015 Addendum) that no loader can\n * restore — an unrecoverable loss in exchange for a guess.\n */\n protected invalidateForForeignWrite(type: string, name?: string): void {\n if (name) {\n const typeStore = this.registry.get(type);\n if (typeStore) {\n typeStore.delete(name);\n if (typeStore.size === 0) this.registry.delete(type);\n }\n }\n this.invalidateListCache(type);\n }\n\n /** Translate a repo event to the legacy MetadataWatchEvent + invalidate caches. */\n private applyRepoEvent(evt: MetadataEvent): void {\n const ref: MetaRef = evt.ref;\n const type = ref.type;\n const name = ref.name;\n\n // Invalidate before announcing, so a watcher that re-reads on the event\n // observes the write rather than the pre-write cache. See\n // {@link invalidateForForeignWrite} for why the registry entry is deleted\n // rather than pre-filled.\n this.invalidateForForeignWrite(type, name);\n\n const legacyType: 'added' | 'changed' | 'deleted' =\n evt.op === 'create' ? 'added'\n : evt.op === 'delete' ? 'deleted'\n : 'changed';\n\n const legacyEvent: MetadataWatchEvent = {\n type: legacyType,\n metadataType: type,\n name,\n path: '',\n // Repo events carry the hash only; the body is fetched on demand\n // via manager.get(type, name). HMR consumers don't read `data` so\n // this is fine for M0. (See ADR-0008 §12 open question 1.)\n data: undefined,\n timestamp: evt.ts,\n };\n // Carry the canonical server-side `seq` so downstream consumers\n // (HMR SSE route → Studio status badge) can render an accurate\n // \"N changes since boot\" matching what other replicas observe.\n // Non-typed extra property on purpose — extending MetadataWatchEvent\n // is a spec-package change deferred to a later PR.\n (legacyEvent as Record<string, unknown>).seq = evt.seq;\n this.notifyWatchers(type, legacyEvent);\n }\n\n protected notifyWatchers(type: string, event: MetadataWatchEvent) {\n this.notifyWatchersLocal(type, event);\n\n // Cluster fan-out (cluster-semantics.mdx §5). Best-effort: a publish\n // failure must never block the local update.\n if (this.clusterPubSub) {\n const payload: ClusterMetadataChangedPayload = {\n originNode: this.clusterNodeId,\n type,\n event,\n };\n const key = `${type}:${(event as { name?: string }).name ?? ''}`;\n void this.clusterPubSub\n .publish(MetadataManager.CLUSTER_CHANNEL, payload, { partitionKey: key })\n .catch((err) => {\n this.logger.error('Cluster metadata publish failed', undefined, {\n type,\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }\n }\n\n private notifyWatchersLocal(type: string, event: MetadataWatchEvent) {\n const callbacks = this.watchCallbacks.get(type);\n if (!callbacks) return;\n\n for (const callback of callbacks) {\n try {\n void callback(event);\n } catch (error) {\n this.logger.error('Watch callback error', undefined, {\n type,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n\n /**\n * Attach a cluster pub/sub transport so metadata-change events fan\n * out to peer nodes and remote events replay into local watchers.\n *\n * The bridge plugin in @objectstack/service-cluster calls this once\n * per kernel boot after both cluster and metadata services are\n * registered. Passing the same MetadataManager twice no-ops; passing\n * a different transport replaces the prior subscription.\n *\n * Pass `nodeId` matching the local cluster's nodeId so loopback\n * suppression works.\n *\n * @returns disposer that unsubscribes from cluster events.\n */\n attachClusterPubSub(pubsub: IPubSub, nodeId: string): () => void {\n // Idempotent on (pubsub, nodeId) — re-attaching same pair short-circuits.\n if (this.clusterPubSub === pubsub && this.clusterNodeId === nodeId) {\n return () => this.detachClusterPubSub();\n }\n this.detachClusterPubSub();\n this.clusterPubSub = pubsub;\n this.clusterNodeId = nodeId;\n this.clusterUnsubscribe = pubsub.subscribe<ClusterMetadataChangedPayload>(\n MetadataManager.CLUSTER_CHANNEL,\n (msg) => {\n const p = msg.payload;\n // Loopback guard — never replay events we just emitted.\n if (p?.originNode && p.originNode === this.clusterNodeId) return;\n if (!p?.type || !p.event) return;\n\n // [#5109] Invalidate FIRST, and SYNCHRONOUSLY on receipt — this is\n // what the channel is for (\"consumed by peers to invalidate their\n // local caches\", see ClusterMetadataChangedPayload). Until this\n // landed, a peer's write only woke this node's watchers: the registry\n // entry and the `listCache` were left untouched, so every `list(type)`\n // kept serving the pre-write set for up to LIST_CACHE_TTL_MS (30s) —\n // and a watcher that re-read via `list()` in response to the wake-up\n // got the stale set back, an invalidation notice carrying invalidated\n // data.\n //\n // Two deliberate choices, both pinned by tests in\n // `metadata-manager-cluster.test.ts`:\n //\n // • BEFORE the notify, matching every other write path in this file\n // (`register` / `unregister` / `applyRepoEvent` all invalidate,\n // then announce): a watcher must never be able to observe the\n // event and the pre-event cache at the same time.\n // • OUTSIDE the `setImmediate`, unlike the notify. The deferral\n // exists so a slow *watcher callback* — arbitrary consumer code —\n // cannot back-pressure the pubsub dispatch loop. Invalidation is\n // two `Map.delete`s and runs no consumer code, so it has nothing\n // to defer for, while deferring it would leave a window between\n // receipt and the next tick in which reads still answer stale.\n // Any `await` in a request handler is enough to lose that race.\n try {\n this.invalidateForForeignWrite(p.type, p.event.name);\n } catch (err) {\n this.logger.error('Cluster remote invalidation failed', undefined, {\n type: p.type,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n\n // Defer to setImmediate so a slow local handler can't back-pressure\n // the pubsub dispatch loop on memory drivers.\n setImmediate(() => {\n try {\n this.notifyWatchersLocal(p.type, p.event);\n } catch (err) {\n this.logger.error('Cluster remote replay failed', undefined, {\n type: p.type,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n });\n },\n );\n this.logger.info('MetadataManager attached to cluster pubsub', {\n nodeId,\n channel: MetadataManager.CLUSTER_CHANNEL,\n });\n return () => this.detachClusterPubSub();\n }\n\n /** Tear down cluster wiring. Safe to call multiple times. */\n detachClusterPubSub(): void {\n if (this.clusterUnsubscribe) {\n try { this.clusterUnsubscribe(); } catch { /* idempotent */ }\n this.clusterUnsubscribe = undefined;\n }\n this.clusterPubSub = undefined;\n this.clusterNodeId = undefined;\n }\n\n // ==========================================\n // Version History & Rollback\n // ==========================================\n\n /**\n * Get the database loader for history operations.\n * Returns undefined if no database loader is configured.\n */\n private getDatabaseLoader(): DatabaseLoader | undefined {\n const dbLoader = this.loaders.get('database');\n if (dbLoader && dbLoader instanceof DatabaseLoader) {\n return dbLoader;\n }\n return undefined;\n }\n\n /**\n * Get version history for a metadata item.\n * Returns a timeline of all changes made to the item.\n */\n async getHistory(\n type: string,\n name: string,\n options?: MetadataHistoryQueryOptions\n ): Promise<MetadataHistoryQueryResult> {\n const dbLoader = this.getDatabaseLoader();\n if (!dbLoader) {\n throw new Error('History tracking requires a database loader to be configured');\n }\n\n return dbLoader.queryHistory(type, name, {\n operationType: options?.operationType,\n since: options?.since,\n until: options?.until,\n limit: options?.limit,\n offset: options?.offset,\n includeMetadata: options?.includeMetadata,\n });\n }\n\n /**\n * Rollback a metadata item to a specific version.\n * Restores the metadata definition from the history snapshot.\n */\n async rollback(\n type: string,\n name: string,\n version: number,\n options?: {\n changeNote?: string;\n recordedBy?: string;\n }\n ): Promise<unknown> {\n const dbLoader = this.getDatabaseLoader();\n if (!dbLoader) {\n throw new Error('Rollback requires a database loader to be configured');\n }\n\n // Fetch the target version snapshot directly from the history table\n const targetVersion = await dbLoader.getHistoryRecord(type, name, version);\n\n if (!targetVersion) {\n throw new Error(`Version ${version} not found in history for ${type}/${name}`);\n }\n\n if (!targetVersion.metadata) {\n throw new Error(`Version ${version} metadata snapshot not available`);\n }\n\n // Restore the metadata using the dedicated rollback path so that a single\n // 'revert' history entry is written (instead of a conflicting 'update' entry)\n const restoredMetadata = targetVersion.metadata;\n await dbLoader.registerRollback(\n type,\n name,\n restoredMetadata,\n version,\n options?.changeNote,\n options?.recordedBy\n );\n\n // Update in-memory registry with the restored metadata\n if (!this.registry.has(type)) {\n this.registry.set(type, new Map());\n }\n this.registry.get(type)!.set(name, restoredMetadata);\n\n return restoredMetadata;\n }\n\n /**\n * Compare two versions of a metadata item.\n * Returns a diff showing what changed between versions.\n */\n async diff(\n type: string,\n name: string,\n version1: number,\n version2: number\n ): Promise<MetadataDiffResult> {\n const dbLoader = this.getDatabaseLoader();\n if (!dbLoader) {\n throw new Error('Diff requires a database loader to be configured');\n }\n\n // Fetch the two version snapshots directly from the history table\n const v1 = await dbLoader.getHistoryRecord(type, name, version1);\n const v2 = await dbLoader.getHistoryRecord(type, name, version2);\n\n if (!v1) {\n throw new Error(`Version ${version1} not found in history for ${type}/${name}`);\n }\n\n if (!v2) {\n throw new Error(`Version ${version2} not found in history for ${type}/${name}`);\n }\n\n if (!v1.metadata || !v2.metadata) {\n throw new Error('Version metadata snapshots not available');\n }\n\n // Generate diff\n const patch = generateSimpleDiff(v1.metadata, v2.metadata);\n const identical = patch.length === 0;\n const summary = generateDiffSummary(patch);\n\n return {\n type,\n name,\n version1,\n version2,\n checksum1: v1.checksum,\n checksum2: v2.checksum,\n identical,\n patch,\n summary,\n };\n }\n}\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * JSON Metadata Serializer\n * \n * Handles JSON format serialization and deserialization\n */\n\nimport type { z } from 'zod';\nimport type { MetadataFormat } from '@objectstack/spec/system';\nimport type { MetadataSerializer, SerializeOptions } from './serializer-interface.js';\n\nexport class JSONSerializer implements MetadataSerializer {\n serialize<T>(item: T, options?: SerializeOptions): string {\n const { prettify = true, indent = 2, sortKeys = false } = options || {};\n\n if (sortKeys) {\n // Sort keys recursively\n const sorted = this.sortObjectKeys(item);\n return prettify\n ? JSON.stringify(sorted, null, indent)\n : JSON.stringify(sorted);\n }\n\n return prettify\n ? JSON.stringify(item, null, indent)\n : JSON.stringify(item);\n }\n\n deserialize<T>(content: string, schema?: z.ZodSchema): T {\n const parsed = JSON.parse(content);\n\n if (schema) {\n return schema.parse(parsed) as T;\n }\n\n return parsed as T;\n }\n\n getExtension(): string {\n return '.json';\n }\n\n canHandle(format: MetadataFormat): boolean {\n return format === 'json';\n }\n\n getFormat(): MetadataFormat {\n return 'json';\n }\n\n /**\n * Recursively sort object keys\n */\n private sortObjectKeys(obj: any): any {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(item => this.sortObjectKeys(item));\n }\n\n const sorted: Record<string, any> = {};\n const keys = Object.keys(obj).sort();\n\n for (const key of keys) {\n sorted[key] = this.sortObjectKeys(obj[key]);\n }\n\n return sorted;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * YAML Metadata Serializer\n * \n * Handles YAML format serialization and deserialization\n */\n\nimport * as yaml from 'js-yaml';\nimport type { z } from 'zod';\nimport type { MetadataFormat } from '@objectstack/spec/system';\nimport type { MetadataSerializer, SerializeOptions } from './serializer-interface.js';\n\nexport class YAMLSerializer implements MetadataSerializer {\n serialize<T>(item: T, options?: SerializeOptions): string {\n const { indent = 2, sortKeys = false } = options || {};\n\n return yaml.dump(item, {\n indent,\n sortKeys,\n lineWidth: -1, // Disable line wrapping\n noRefs: true, // Disable YAML references\n });\n }\n\n deserialize<T>(content: string, schema?: z.ZodSchema): T {\n // Use JSON_SCHEMA to prevent arbitrary code execution\n // This restricts YAML to JSON-compatible types only\n const parsed = yaml.load(content, { schema: yaml.JSON_SCHEMA });\n\n if (schema) {\n return schema.parse(parsed) as T;\n }\n\n return parsed as T;\n }\n\n getExtension(): string {\n return '.yaml';\n }\n\n canHandle(format: MetadataFormat): boolean {\n return format === 'yaml';\n }\n\n getFormat(): MetadataFormat {\n return 'yaml';\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * TypeScript/JavaScript Metadata Serializer\n * \n * Handles TypeScript/JavaScript module format serialization and deserialization\n */\n\nimport type { z } from 'zod';\nimport type { MetadataFormat } from '@objectstack/spec/system';\nimport type { MetadataSerializer, SerializeOptions } from './serializer-interface.js';\n\nexport class TypeScriptSerializer implements MetadataSerializer {\n constructor(private format: 'typescript' | 'javascript' = 'typescript') {}\n\n serialize<T>(item: T, options?: SerializeOptions): string {\n const { prettify = true, indent = 2 } = options || {};\n\n const jsonStr = JSON.stringify(item, null, prettify ? indent : 0);\n \n if (this.format === 'typescript') {\n return `import type { ServiceObject } from '@objectstack/spec/data';\\n\\n` +\n `export const metadata: ServiceObject = ${jsonStr};\\n\\n` +\n `export default metadata;\\n`;\n } else {\n return `export const metadata = ${jsonStr};\\n\\n` +\n `export default metadata;\\n`;\n }\n }\n\n deserialize<T>(content: string, schema?: z.ZodSchema): T {\n // For TypeScript/JavaScript files, we need to extract the exported object\n // Note: This is a simplified parser that works with JSON-like object literals\n // For complex TypeScript with nested objects, consider using a proper TypeScript parser\n \n // Try to find the object literal in various export patterns\n // Pattern 1: export const metadata = {...};\n let objectStart = content.indexOf('export const');\n if (objectStart === -1) {\n // Pattern 2: export default {...};\n objectStart = content.indexOf('export default');\n }\n \n if (objectStart === -1) {\n throw new Error(\n 'Could not parse TypeScript/JavaScript module. ' +\n 'Expected export pattern: \"export const metadata = {...};\" or \"export default {...};\"'\n );\n }\n\n // Find the first opening brace after the export statement\n const braceStart = content.indexOf('{', objectStart);\n if (braceStart === -1) {\n throw new Error('Could not find object literal in export statement');\n }\n\n // Find the matching closing brace by counting braces\n // Handle string literals to avoid counting braces inside strings\n let braceCount = 0;\n let braceEnd = -1;\n let inString = false;\n let stringChar = '';\n \n for (let i = braceStart; i < content.length; i++) {\n const char = content[i];\n const prevChar = i > 0 ? content[i - 1] : '';\n \n // Track string literals (simple handling of \" and ')\n if ((char === '\"' || char === \"'\") && prevChar !== '\\\\') {\n if (!inString) {\n inString = true;\n stringChar = char;\n } else if (char === stringChar) {\n inString = false;\n stringChar = '';\n }\n }\n \n // Count braces only when not inside strings\n if (!inString) {\n if (char === '{') braceCount++;\n if (char === '}') {\n braceCount--;\n if (braceCount === 0) {\n braceEnd = i;\n break;\n }\n }\n }\n }\n\n if (braceEnd === -1) {\n throw new Error('Could not find matching closing brace for object literal');\n }\n\n // Extract the object literal\n const objectLiteral = content.substring(braceStart, braceEnd + 1);\n\n try {\n // Parse as JSON\n const parsed = JSON.parse(objectLiteral);\n\n if (schema) {\n return schema.parse(parsed) as T;\n }\n\n return parsed as T;\n } catch (error) {\n throw new Error(\n `Failed to parse object literal as JSON: ${error instanceof Error ? error.message : String(error)}. ` +\n 'Make sure the TypeScript/JavaScript object uses JSON-compatible syntax (no functions, comments, or trailing commas).'\n );\n }\n }\n\n getExtension(): string {\n return this.format === 'typescript' ? '.ts' : '.js';\n }\n\n canHandle(format: MetadataFormat): boolean {\n return format === 'typescript' || format === 'javascript';\n }\n\n getFormat(): MetadataFormat {\n return this.format;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Database Metadata Loader\n *\n * Loads and persists metadata via an IDataDriver instance, enabling\n * database-backed storage for platform and user scoped metadata.\n * Uses the `sys_metadata` table (configurable) following the\n * MetadataRecordSchema envelope defined in @objectstack/spec.\n */\n\nimport type {\n MetadataLoadOptions,\n MetadataLoadResult,\n MetadataStats,\n MetadataLoaderContract,\n MetadataSaveOptions,\n MetadataSaveResult,\n MetadataRecord,\n MetadataHistoryRecord,\n} from '@objectstack/spec/system';\nimport { SysMetadataObject, SysMetadataHistoryObject } from '@objectstack/metadata-core';\nimport { applyConversionsToStoredItem } from '@objectstack/spec';\nimport { PLURAL_TO_SINGULAR } from '@objectstack/spec/shared';\nimport type { IDataDriver, IDataEngine, DriverQuery } from '@objectstack/spec/contracts';\nimport type { MetadataLoader } from './loader-interface.js';\nimport { calculateChecksum } from '../utils/metadata-history-utils.js';\nimport { LRUCache } from '../utils/lru-cache.js';\nimport { isMissingTableError, isSchemaAlreadyExistsError } from '../utils/schema-sync-errors.js';\nimport { migrateProjectIdToEnvironmentId } from '../migrations/migrate-project-id-to-environment-id.js';\n\n/**\n * Cache configuration for `DatabaseLoader`.\n *\n * The cache sits in front of `load()`, `loadMany()`, `exists()`, `stat()`,\n * and `list()` so that hot read paths (REST `/meta/*`, ObjectQL plan\n * resolution, runtime overlay merges) do not hit the database on every\n * request. All write paths (`save`, `delete`, `registerRollback`) invalidate\n * the relevant entries.\n *\n * Defaults are conservative: 500 entries, 60s TTL — chosen so that single-\n * tenant Studio usage does not burn memory and so that an external write\n * (out-of-band SQL update) becomes visible within a minute even without\n * realtime invalidation.\n */\nexport interface DatabaseLoaderCacheOptions {\n /** Whether the cache is active. Default: `true`. */\n enabled?: boolean;\n /** Max number of cached `(type, name)` entries. Default: `500`. */\n maxSize?: number;\n /** TTL in milliseconds. Set to `0` to disable expiry. Default: `60_000`. */\n ttl?: number;\n}\n\n/**\n * Configuration for the DatabaseLoader.\n *\n * Accepts either a raw `IDataDriver` or an `IDataEngine` (ObjectQL).\n * When `engine` is provided, all CRUD operations route through the engine\n * which handles datasource mapping automatically — no manual driver\n * resolution needed. Schema sync is also skipped (the engine handles it).\n */\nexport interface DatabaseLoaderOptions {\n /** The IDataDriver instance to use for database operations */\n driver?: IDataDriver;\n\n /** The IDataEngine (ObjectQL) instance — preferred over raw driver */\n engine?: IDataEngine;\n\n /** The table name to store metadata records (default: 'sys_metadata') */\n tableName?: string;\n\n /** The table name to store history records (default: 'sys_metadata_history') */\n historyTableName?: string;\n\n /** Organization ID for multi-tenant isolation */\n organizationId?: string;\n\n /**\n * @deprecated since ADR-0008 §0 amendment (branch/project removal).\n * The metadata layer is keyed by organization only. This option is\n * accepted for back-compat but ignored — writes do not set\n * `environment_id` and filters do not constrain on it. Will be removed\n * in the next major release.\n */\n environmentId?: string;\n\n /** Enable history tracking (default: true) */\n trackHistory?: boolean;\n\n /**\n * Read-through cache configuration. Pass `{ enabled: false }` to disable\n * caching outright (useful in tests or when the caller wants the loader to\n * always read fresh from the database).\n */\n cache?: DatabaseLoaderCacheOptions;\n}\n\n/**\n * DatabaseLoader — Datasource-backed metadata persistence.\n *\n * Implements the MetadataLoader interface to provide database read/write\n * for metadata records. Uses the MetadataRecordSchema envelope to persist\n * metadata with scope, versioning, and audit fields.\n */\nexport class DatabaseLoader implements MetadataLoader {\n readonly contract: MetadataLoaderContract = {\n name: 'database',\n protocol: 'datasource:',\n capabilities: {\n read: true,\n write: true,\n watch: false,\n list: true,\n },\n };\n\n private driver?: IDataDriver;\n private engine?: IDataEngine;\n private tableName: string;\n private historyTableName: string;\n private organizationId?: string;\n private trackHistory: boolean;\n private schemaReady = false;\n private historySchemaReady = false;\n /**\n * Whether the loud \"DDL failed\" report has already been printed for the\n * metadata table / history table respectively. AGENTS.md → \"Degradation log\n * levels\": say it **once**, at the first degradation, not once per retry.\n */\n private schemaFailureReported = false;\n private historySchemaFailureReported = false;\n /**\n * Same once-only discipline for the #4825 seam: the history table is readable\n * or it is not, and repeating the report per skipped write turns a real\n * degradation into noise people learn to skim.\n */\n private historySeqFailureReported = false;\n\n /** (type, name) → metadata payload — primes `load()` */\n private readonly loadCache?: LRUCache<string, Record<string, unknown> | null>;\n /** type → array of payloads — primes `loadMany()` */\n private readonly loadManyCache?: LRUCache<string, unknown[]>;\n /** type → list of names — primes `list()` */\n private readonly listCache?: LRUCache<string, string[]>;\n /** (type, name) → MetadataStats — primes `stat()` */\n private readonly statCache?: LRUCache<string, MetadataStats | null>;\n\n constructor(options: DatabaseLoaderOptions) {\n if (!options.driver && !options.engine) {\n throw new Error('DatabaseLoader requires either a driver or engine');\n }\n this.driver = options.driver;\n this.engine = options.engine;\n this.tableName = options.tableName ?? 'sys_metadata';\n this.historyTableName = options.historyTableName ?? 'sys_metadata_history';\n this.organizationId = options.organizationId;\n // ADR-0008 §0: `environmentId` option is accepted for back-compat but ignored.\n void options.environmentId;\n this.trackHistory = options.trackHistory !== false; // Default to true\n\n // Wire cache. Default: enabled with 500 entries / 60s TTL.\n const cacheOpts = options.cache;\n const cacheEnabled = cacheOpts?.enabled !== false;\n if (cacheEnabled) {\n const lruOpts = {\n maxSize: cacheOpts?.maxSize ?? 500,\n ttl: cacheOpts?.ttl ?? 60_000,\n };\n this.loadCache = new LRUCache(lruOpts);\n this.loadManyCache = new LRUCache(lruOpts);\n this.listCache = new LRUCache(lruOpts);\n this.statCache = new LRUCache(lruOpts);\n }\n }\n\n // ==========================================\n // Cache helpers\n // ==========================================\n\n private cacheKey(type: string, name: string): string {\n return `${type}::${name}`;\n }\n\n /**\n * Invalidate all cached entries for a specific (type, name) pair plus\n * the type-level aggregates (`loadMany`, `list`). Called from every write\n * path (`save`, `delete`, `registerRollback`).\n */\n private invalidate(type: string, name: string): void {\n if (!this.loadCache) return;\n const key = this.cacheKey(type, name);\n this.loadCache.delete(key);\n this.statCache?.delete(key);\n this.loadManyCache?.delete(type);\n this.listCache?.delete(type);\n }\n\n /** Drop the entire cache — useful after bulk imports or schema changes. */\n invalidateAll(): void {\n this.loadCache?.clear();\n this.loadManyCache?.clear();\n this.listCache?.clear();\n this.statCache?.clear();\n }\n\n /** Diagnostic: aggregated cache statistics for `metrics` endpoints. */\n getCacheStats(): {\n enabled: boolean;\n load: ReturnType<LRUCache<string, unknown>['stats']> | null;\n loadMany: ReturnType<LRUCache<string, unknown>['stats']> | null;\n list: ReturnType<LRUCache<string, unknown>['stats']> | null;\n stat: ReturnType<LRUCache<string, unknown>['stats']> | null;\n } {\n return {\n enabled: this.loadCache !== undefined,\n load: this.loadCache?.stats() ?? null,\n loadMany: this.loadManyCache?.stats() ?? null,\n list: this.listCache?.stats() ?? null,\n stat: this.statCache?.stats() ?? null,\n };\n }\n\n // ==========================================\n // Internal CRUD helpers (driver vs engine)\n // ==========================================\n\n // NOTE (#6231, closed out by #7178): BOTH branches below now take `query`\n // unchanged and uncast. `DriverQuery` is `Omit<QueryAST, 'object'>`, so the\n // object name travels as argument one only — that was always enough for the\n // driver branch. The ENGINE branch used to carry `as any`, for one reason:\n // `EngineQueryOptionsSchema.search` admitted only the structured\n // `FullTextSearchSchema`, while `QueryAST.search` (hence `DriverQuery`) also\n // admits the bare query string that ADR-0061 D1 calls the canonical Tier-1\n // spelling and that the engine actually serves, so `DriverQuery` was not\n // assignable to `EngineQueryOptionsParsed`. #7178 aligned the two schemas;\n // the casts are now genuinely vestigial and are gone, which restores real\n // `where`/`orderBy`/`fields` checking on the metadata main read path — this\n // schema is not `.strict()`, so an unknown key here is SILENTLY DROPPED\n // (`check:query-options-erasure`'s own rationale) and the erased type was\n // the only thing standing between a typo and that silence.\n //\n // If a future edit makes one of these stop compiling, the honest fix is to\n // reconcile the two schemas again — not to reinstate the cast.\n\n private async _find(table: string, query: DriverQuery): Promise<Record<string, unknown>[]> {\n if (this.engine) {\n return this.engine.find(table, query);\n }\n return this.driver!.find(table, query);\n }\n\n private async _findOne(table: string, query: DriverQuery): Promise<Record<string, unknown> | null> {\n if (this.engine) {\n return this.engine.findOne(table, query);\n }\n return this.driver!.findOne(table, query);\n }\n\n private async _count(table: string, query: DriverQuery): Promise<number> {\n if (this.engine) {\n return this.engine.count(table, query);\n }\n return this.driver!.count(table, query);\n }\n\n private async _create(table: string, data: Record<string, unknown>): Promise<Record<string, unknown>> {\n if (this.engine) {\n return this.engine.insert(table, data);\n }\n return this.driver!.create(table, data);\n }\n\n private async _update(table: string, id: string, data: Record<string, unknown>): Promise<Record<string, unknown>> {\n if (this.engine) {\n return this.engine.update(table, { id, ...data });\n }\n return this.driver!.update(table, id, data);\n }\n\n private async _delete(table: string, id: string): Promise<any> {\n if (this.engine) {\n return this.engine.delete(table, { where: { id } } as any);\n }\n return this.driver!.delete(table, id);\n }\n\n /**\n * Compute the next per-org `event_seq` for `sys_metadata_history`.\n * Reads `MAX(event_seq) + 1` for the configured `organization_id`.\n * Legacy path — not transactional, so concurrent writes can collide.\n * The canonical (transactional) producer is `SysMetadataRepository`.\n *\n * #4825 (same shape as #4728, rule from #4632) — discriminate by error TYPE.\n * This used to `catch { return 1 }`, with a comment that named BOTH reasons a\n * read can fail and then answered both the same way. Exactly one of them is\n * benign: the history table has not been provisioned, so there is no row to\n * be inconsistent with and 1 genuinely IS the next number. Every other reason\n * — connection drop, timeout, insufficient privileges — means the rows are\n * still there and simply were not seen, and answering 1 against a table with\n * N rows **collides with existing rows**: the insert succeeds, the log stays\n * empty, and `event_seq` (the ordering key that history listing and rollback\n * targeting both stand on) is silently wrong from then on. Note this is the\n * costlier half of the #4728 family — not bytes that never landed, but bytes\n * that landed *wrong*, which no retry and no restart repairs.\n *\n * @throws The underlying driver error, unchanged, for every non-benign read\n * failure. Deliberate: a sequence number this method cannot derive\n * from data it actually read is not a number it may invent. The\n * caller ({@link createHistoryRecord}) owns the consequence.\n */\n private async nextEventSeq(): Promise<number> {\n const where: Record<string, unknown> = this.organizationId\n ? { organization_id: this.organizationId }\n : {};\n try {\n const rows = await this._find(this.historyTableName, { where });\n let max = 0;\n for (const row of rows as Array<{ event_seq?: number | null }>) {\n const v = typeof row.event_seq === 'number' ? row.event_seq : 0;\n if (v > max) max = v;\n }\n return max + 1;\n } catch (error) {\n // Benign — and ONLY benign: there is no table, therefore no row, so\n // numbering from 1 cannot collide with anything.\n if (isMissingTableError(error)) return 1;\n throw error;\n }\n }\n\n /**\n * Ensure the metadata table exists.\n * Uses IDataDriver.syncSchema with the SysMetadataObject definition\n * to idempotently create/update the table.\n */\n private async ensureSchema(): Promise<void> {\n if (this.schemaReady) return;\n\n // When using engine, schema sync is handled by ObjectQL startup\n if (this.engine) {\n this.schemaReady = true;\n // ⚠️ This loader does NOT build `idx_sys_metadata_overlay_active` (#6771).\n // It used to, with the pre-ADR-0048 key, and because every producer uses\n // `IF NOT EXISTS` the first one to run claimed the name for good. On this\n // engine path nothing has synced `sys_metadata` yet, so that producer was\n // the one most likely to win — and it installed a key the platform\n // retired. Overlay uniqueness has exactly two owners now, both correctly\n // keyed: `metadata-protocol`'s `ensureMetadataOverlayIndexes` (the\n // partial, NULL-safe form) and, for stacks without it, the declaration in\n // `metadata-core`'s `sys-metadata.object.ts` that ObjectQL's own startup\n // materializes through `syncDeclaredIndexes`.\n try {\n const engineAny = this.engine as any;\n let driver: IDataDriver | undefined =\n engineAny?.driver ?? engineAny?.getDriver?.();\n if (!driver && engineAny?.drivers instanceof Map) {\n for (const candidate of engineAny.drivers.values()) {\n const c = candidate as any;\n if (c && (typeof c.raw === 'function' || typeof c.execute === 'function')) {\n driver = candidate as IDataDriver;\n break;\n }\n }\n }\n if (driver) {\n // v5.0 forward migration: project_id → environment_id (idempotent).\n await migrateProjectIdToEnvironmentId(driver).catch(() => undefined);\n }\n } catch (error) {\n // ADR-0120 D4: never block the boot, never swallow it either (#6771 —\n // this catch was empty). Resolving a raw-SQL driver off the engine is\n // the only thing left that can throw here, and when it does the\n // `project_id` → `environment_id` forward migration did NOT run: rows\n // written before v5.0 keep the old column and read back as if the\n // field were unset.\n console.warn(\n `[Metadata] Could not resolve a raw-SQL driver from the engine for \\`${this.tableName}\\` — ` +\n `the project_id→environment_id forward migration was SKIPPED. Legacy rows (if any) keep the ` +\n `pre-v5.0 column and read back as unset. Metadata reads and writes are otherwise unaffected. ` +\n `Re-run it explicitly with \\`migrateProjectIdToEnvironmentId(driver)\\` from ` +\n `\\`@objectstack/metadata/migrations\\` once the datasource is reachable.`,\n error,\n );\n }\n return;\n }\n\n try {\n await this.driver!.syncSchema(this.tableName, {\n ...SysMetadataObject,\n name: this.tableName,\n });\n } catch (error) {\n // #4728 (rule: #4632, accident: #4420) — discriminate by error TYPE.\n // Exactly ONE failure reason is benign here: the table/columns are\n // already provisioned and a non-fully-idempotent driver reports that as\n // an error. Every other reason (insufficient privileges, datasource never\n // connected, incompatible column type) means the table or column does NOT\n // exist — and the previous code marked `schemaReady = true` for all of\n // them, making a total durability failure indistinguishable from success\n // with not one line in the log.\n if (!isSchemaAlreadyExistsError(error)) {\n if (!this.schemaFailureReported) {\n this.schemaFailureReported = true;\n console.error(\n `[Metadata] DDL for the metadata table \\`${this.tableName}\\` FAILED — its table/columns were NOT created or altered. ` +\n `Every metadata write from here on (Studio saves, app installs, org overlays) targets storage that may not exist: ` +\n `writes will error out, or silently drop columns on a lenient driver, while the server keeps reporting healthy. ` +\n `This is NOT the benign \"already exists\" case — check the datasource/driver error below (insufficient privileges, ` +\n `datasource not connected, incompatible column type), fix it and restart. Schema sync is retried on the next ` +\n `metadata operation, so a transient cause recovers on its own.`,\n error,\n );\n }\n // Deliberate, and the opposite of what this code did before: on a REAL\n // DDL failure `schemaReady` stays FALSE. Startup is still not blocked\n // (this method does not throw — callers proceed and fail loudly at the\n // driver if the table is truly missing), but the loader never claims a\n // readiness it does not have, and the next operation retries the sync\n // so a datasource that was merely still connecting heals itself. Same\n // shape as `ensureHistorySchema()` below.\n return;\n }\n // Benign — and ONLY benign: the table is already provisioned, so the DDL\n // was a no-op rather than a failure. Fall through to the ready path.\n }\n\n if (this.schemaFailureReported) {\n this.schemaFailureReported = false;\n console.info(\n `[Metadata] DDL for the metadata table \\`${this.tableName}\\` succeeded on retry — metadata writes are durable again.`,\n );\n }\n this.schemaReady = true;\n // v5.0 forward migration: project_id → environment_id (idempotent).\n try {\n await migrateProjectIdToEnvironmentId(this.driver!);\n } catch {\n // ignore — migration is best-effort on bootstrap\n }\n // ⚠️ No overlay-index DDL is issued from here (#6771). `syncSchema` above\n // already materialized the DECLARED `idx_sys_metadata_overlay_active` from\n // `sys-metadata.object.ts` with the CURRENT ADR-0048 discriminator\n // `(type, name, organization_id, package_id)`; measured on real SQLite, the\n // producer that used to sit here found the name taken and no-opped —\n // while still reporting `status: 'created'`. Its one non-no-op window was\n // the benign \"already exists\" path above, where `syncSchema` threw before\n // creating the declared indexes: there it installed the RETIRED key\n // `(…, environment_id, scope)`, and `syncDeclaredIndexes` skips by name, so\n // nothing ever repaired it. See the tombstone in `../migrations/index.ts`.\n }\n\n /**\n * Ensure the history table exists.\n * Uses IDataDriver.syncSchema with the SysMetadataHistoryObject definition.\n */\n private async ensureHistorySchema(): Promise<void> {\n if (!this.trackHistory || this.historySchemaReady) return;\n\n // When using engine, schema sync is handled by ObjectQL startup\n if (this.engine) {\n this.historySchemaReady = true;\n return;\n }\n\n try {\n await this.driver!.syncSchema(this.historyTableName, {\n ...SysMetadataHistoryObject,\n name: this.historyTableName,\n });\n if (this.historySchemaFailureReported) {\n this.historySchemaFailureReported = false;\n console.info(\n `[Metadata] DDL for the metadata history table \\`${this.historyTableName}\\` succeeded on retry — change history is being recorded again.`,\n );\n }\n this.historySchemaReady = true;\n } catch (error) {\n // Same discrimination as `ensureSchema()` above (#4728). A benign\n // \"already exists\" means the history table IS provisioned — treat it as\n // the no-op it is instead of re-reporting it (and re-running the DDL) on\n // every single write, which is the mirror-image failure: an `error` line\n // for a non-degradation trains everyone to skim `error`.\n if (isSchemaAlreadyExistsError(error)) {\n this.historySchemaReady = true;\n return;\n }\n // Real failure: loud once, `historySchemaReady` stays false so the next\n // operation retries.\n if (!this.historySchemaFailureReported) {\n this.historySchemaFailureReported = true;\n console.error(\n `[Metadata] DDL for the metadata history table \\`${this.historyTableName}\\` FAILED — its table/columns were NOT created. ` +\n `Metadata change history (versions, diffs, rollback) will NOT be persisted while every metadata write keeps succeeding, ` +\n `so the audit trail silently ends here. Fix the datasource/driver error below and restart; the sync is retried on the ` +\n `next metadata operation.`,\n error,\n );\n }\n }\n }\n\n /**\n * Build base filter conditions for queries.\n * Filters by organizationId when configured. `environmentId` is accepted\n * for back-compat but no longer constrains the query — see\n * ADR-0008 §0 (branch/project removal).\n */\n private baseFilter(type: string, name?: string): Record<string, unknown> {\n const filter: Record<string, unknown> = { type };\n if (name !== undefined) {\n filter.name = name;\n }\n if (this.organizationId) {\n filter.organization_id = this.organizationId;\n }\n return filter;\n }\n\n /**\n * Create a history record for a metadata change.\n *\n * @param type - Metadata type\n * @param name - Metadata name\n * @param version - Version number\n * @param metadata - The metadata payload\n * @param operationType - Type of operation\n * @param previousChecksum - Checksum of previous version (if any)\n * @param changeNote - Optional change description\n * @param recordedBy - Optional user who made the change\n */\n private async createHistoryRecord(\n type: string,\n name: string,\n version: number,\n metadata: unknown,\n operationType: 'create' | 'update' | 'publish' | 'revert' | 'delete',\n previousChecksum?: string,\n changeNote?: string,\n recordedBy?: string\n ): Promise<void> {\n if (!this.trackHistory) return;\n\n await this.ensureHistorySchema();\n\n const now = new Date().toISOString();\n const checksum = await calculateChecksum(metadata);\n\n // Skip if checksum matches previous version (no actual change)\n if (previousChecksum && checksum === previousChecksum && operationType === 'update') {\n return;\n }\n\n const historyId = generateId();\n const metadataJson = JSON.stringify(metadata);\n\n // Compute per-org monotonic event_seq. Legacy path: not inside a\n // transaction, so concurrent writers can collide. The SysMetadataRepository\n // path serializes this under engine.transaction(); DatabaseLoader is\n // deprecated for new writes and tolerates the race.\n //\n // #4825: the concurrency race above is a KNOWN, recorded limitation of this\n // path. A read failure is not the same thing and is not tolerated — if the\n // sequence cannot be derived from rows we actually read, we write NO history\n // row rather than one carrying a number we made up. A missing row is loud\n // here and visibly absent later; a colliding row is silent now and corrupts\n // the ordering that `queryHistory` and `rollback` both depend on, forever.\n let eventSeq: number;\n try {\n eventSeq = await this.nextEventSeq();\n } catch (error) {\n if (!this.historySeqFailureReported) {\n this.historySeqFailureReported = true;\n console.error(\n `[Metadata] Could not read \\`${this.historyTableName}\\` to determine the next \\`event_seq\\` — the history ` +\n `entry for ${type}/${name} was NOT written, and further entries are being skipped while this persists. ` +\n `The metadata write itself SUCCEEDED, so the server keeps looking healthy while its change history ` +\n `silently develops holes: version timelines and rollback targets will be incomplete. The entry is skipped ` +\n `deliberately — numbering it from 1 (what this code did before #4825) would collide with existing rows and ` +\n `make \\`event_seq\\` ordering wrong rather than merely incomplete, which nothing detects and no restart ` +\n `repairs. Fix the datasource/driver error below (connection, timeout, privileges); the next metadata write ` +\n `retries and reports recovery.`,\n error,\n );\n }\n return;\n }\n\n if (this.historySeqFailureReported) {\n this.historySeqFailureReported = false;\n console.info(\n `[Metadata] \\`${this.historyTableName}\\` is readable again — \\`event_seq\\` numbering recovered and change ` +\n `history is being recorded again. Entries skipped during the outage are not backfilled.`,\n );\n }\n\n const historyRecord: Partial<MetadataHistoryRecord> = {\n id: historyId,\n name,\n type,\n version,\n operationType,\n metadata: metadataJson as any,\n checksum,\n previousChecksum,\n changeNote,\n recordedBy,\n recordedAt: now,\n ...(this.organizationId ? { organizationId: this.organizationId } : {}),\n };\n\n try {\n await this._create(this.historyTableName, {\n id: historyRecord.id,\n event_seq: eventSeq,\n name: historyRecord.name,\n type: historyRecord.type,\n version: historyRecord.version,\n operation_type: historyRecord.operationType,\n metadata: historyRecord.metadata,\n checksum: historyRecord.checksum,\n previous_checksum: historyRecord.previousChecksum,\n change_note: historyRecord.changeNote,\n recorded_by: historyRecord.recordedBy,\n recorded_at: historyRecord.recordedAt,\n source: 'database-loader',\n ...(this.organizationId ? { organization_id: this.organizationId } : {}),\n });\n } catch (error) {\n // Log error but don't fail the main operation\n console.error(`Failed to create history record for ${type}/${name}:`, error);\n }\n }\n\n /**\n * Once-per-process dedupe for stored-row conversion notices — `load` /\n * `loadMany` are hot read paths (cached, but re-hit on every TTL expiry),\n * so a legacy row must warn once, not once per cache miss.\n */\n private storedConversionWarned = new Set<string>();\n\n /**\n * Convert a LIVE database row to a metadata payload.\n *\n * Parses the JSON `metadata` column back into an object, then replays the\n * full ADR-0087 conversion chain over it (#3903): rows written under a past\n * protocol are served canonical, exactly like the metadata-protocol's\n * `sys_metadata` seams. History rows do NOT pass through here — history\n * readers parse inline and stay verbatim, as a record of what was written.\n *\n * `flow` is skipped for the same reason the protocol skips it: flow-node\n * conversions need the automation engine's live executor registry for their\n * open-namespace conflict guard; flows canonicalize at `registerFlow`.\n */\n private rowToData(row: Record<string, unknown>): Record<string, unknown> | null {\n if (!row || !row.metadata) return null;\n\n const payload = typeof row.metadata === 'string'\n ? JSON.parse(row.metadata as string)\n : row.metadata;\n\n const singular = PLURAL_TO_SINGULAR[row.type as string] ?? (row.type as string);\n if (singular === 'flow') return payload as Record<string, unknown>;\n return applyConversionsToStoredItem(singular, payload as Record<string, unknown>, {\n onNotice: (n) => {\n const key = `${n.conversionId}|${singular}|${String(row.name ?? '')}`;\n if (this.storedConversionWarned.has(key)) return;\n this.storedConversionWarned.add(key);\n console.warn(\n `[DatabaseLoader] stored ${singular}/${String(row.name ?? '<unnamed>')} carries a pre-protocol shape; ${n.message}`,\n );\n },\n });\n }\n\n /**\n * Convert a database row to a MetadataRecord-like object.\n */\n private rowToRecord(row: Record<string, unknown>): MetadataRecord {\n return {\n id: row.id as string,\n name: row.name as string,\n type: row.type as string,\n namespace: (row.namespace as string) ?? 'default',\n packageId: row.package_id as string | undefined,\n managedBy: row.managed_by as MetadataRecord['managedBy'],\n scope: (row.scope as MetadataRecord['scope']) ?? 'platform',\n metadata: this.rowToData(row) ?? {},\n extends: row.extends as string | undefined,\n strategy: (row.strategy as MetadataRecord['strategy']) ?? 'merge',\n owner: row.owner as string | undefined,\n state: (row.state as MetadataRecord['state']) ?? 'active',\n organizationId: row.organization_id as string | undefined,\n environmentId: row.environment_id as string | undefined,\n version: (row.version as number) ?? 1,\n checksum: row.checksum as string | undefined,\n source: row.source as MetadataRecord['source'],\n tags: row.tags ? (typeof row.tags === 'string' ? JSON.parse(row.tags as string) : row.tags as string[]) : undefined,\n createdBy: row.created_by as string | undefined,\n createdAt: row.created_at as string | undefined,\n updatedBy: row.updated_by as string | undefined,\n updatedAt: row.updated_at as string | undefined,\n };\n }\n\n // ==========================================\n // Read-failure classification (#5108)\n // ==========================================\n\n /**\n * Decide what a failed READ against {@link tableName} means, and rethrow\n * unless it is the ONE benign reason.\n *\n * #5108 (rule from #4632; same shape as #4728 and #4825) — discriminate by\n * error TYPE. Every read method below used to `catch {}` into its own empty\n * value: `load` → `null`, `loadMany` → `[]`, `exists` → `false`, `stat` →\n * `null`, `list` → `[]`. That made a database the metadata plane cannot\n * reach **indistinguishable** from an environment where nothing of that type\n * was ever declared — and it erased the failure *inside the loader*, so\n * neither `MetadataManager`'s own `try/catch` degradation branches nor\n * {@link import('../metadata-manager.js').MetadataManager.loadDiagnosed}\n * (ADR-0110 D3, whose whole purpose is to tell a miss from an outage) could\n * report anything. Nowhere on the chain was there a line saying the read\n * failed.\n *\n * Why that is worse than a noisy error: every consumer that gates on a\n * *declared set* — permissions, sharing rules, policies, endpoint\n * declarations — reads the empty answer as \"the author declared none\". Some\n * then fail open (grant), some fail closed (lock out); both look healthy\n * from outside. This is the AGENTS.md → \"Degradation log levels\" shape the\n * repo has already paid for twice, one layer up from #4825.\n *\n * Exactly one failure reason is benign: `sys_metadata` has not been\n * provisioned yet. There are then genuinely no rows, so \"nothing declared\"\n * IS the truth, and a first boot must not explode. Every other reason —\n * connection drop, timeout, insufficient privileges, malformed query — means\n * the rows may well be there and simply were not seen.\n *\n * Classification is conservative in the same direction as\n * {@link isMissingTableError} itself: an unrecognised error is NOT benign.\n * A false \"benign\" silently mis-answers a security question; a false \"real\"\n * costs one loud error.\n *\n * @param error The value thrown by `_find` / `_findOne` / `_count`.\n * @throws The underlying driver error, unchanged — deliberately, matching\n * {@link nextEventSeq}. The loader does not log it: the caller owns\n * the consequence and is the only layer that knows what an\n * incomplete answer costs it (`MetadataManager.list()` reports it at\n * `error`; `listForIndex()`/`matchEndpoint` let it propagate so an\n * outage can never be served as a 404).\n * @returns normally ONLY for the benign case, licensing the caller to answer\n * with its empty value.\n */\n private rethrowUnlessTableUnprovisioned(error: unknown): void {\n if (isMissingTableError(error)) return;\n throw error;\n }\n\n // ==========================================\n // MetadataLoader Interface Implementation\n // ==========================================\n\n async load(\n type: string,\n name: string,\n _options?: MetadataLoadOptions\n ): Promise<MetadataLoadResult> {\n const startTime = Date.now();\n\n await this.ensureSchema();\n\n // Read-through cache. We cache `null` (not-found) results too so a barrage\n // of misses does not hammer the database; invalidation on `save` upgrades\n // the entry once the row exists.\n const key = this.cacheKey(type, name);\n if (this.loadCache) {\n const cached = this.loadCache.get(key);\n if (cached !== undefined) {\n return {\n data: cached,\n source: 'database',\n format: 'json',\n loadTime: Date.now() - startTime,\n };\n }\n }\n\n try {\n const row = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (!row) {\n this.loadCache?.set(key, null);\n return {\n data: null,\n loadTime: Date.now() - startTime,\n };\n }\n\n const data = this.rowToData(row);\n const record = this.rowToRecord(row);\n\n this.loadCache?.set(key, data);\n\n return {\n data,\n source: 'database',\n format: 'json',\n etag: record.checksum,\n loadTime: Date.now() - startTime,\n };\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: the table is not provisioned, so there is no row. Not\n // cached — `ensureSchema()` retries, and a `null` memoized here would\n // outlive the provisioning that fixes it.\n return {\n data: null,\n loadTime: Date.now() - startTime,\n };\n }\n }\n\n async loadMany<T = any>(\n type: string,\n _options?: MetadataLoadOptions\n ): Promise<T[]> {\n await this.ensureSchema();\n\n if (this.loadManyCache) {\n const cached = this.loadManyCache.get(type);\n if (cached !== undefined) return cached as T[];\n }\n\n try {\n const rows = await this._find(this.tableName, {\n where: this.baseFilter(type),\n });\n\n const result = rows\n .map(row => this.rowToData(row))\n .filter((data): data is Record<string, unknown> => data !== null) as T[];\n\n this.loadManyCache?.set(type, result);\n return result;\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: no table, therefore no items of this type. Not cached.\n return [];\n }\n }\n\n async exists(type: string, name: string): Promise<boolean> {\n await this.ensureSchema();\n\n // Honor cache: a cached non-null payload implies existence.\n if (this.loadCache) {\n const cached = this.loadCache.get(this.cacheKey(type, name));\n if (cached !== undefined) return cached !== null;\n }\n\n try {\n const count = await this._count(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n return count > 0;\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: no table, therefore the item genuinely does not exist.\n return false;\n }\n }\n\n async stat(type: string, name: string): Promise<MetadataStats | null> {\n await this.ensureSchema();\n\n const key = this.cacheKey(type, name);\n if (this.statCache) {\n const cached = this.statCache.get(key);\n if (cached !== undefined) return cached;\n }\n\n try {\n const row = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (!row) {\n this.statCache?.set(key, null);\n return null;\n }\n\n const record = this.rowToRecord(row);\n const metadataStr = typeof row.metadata === 'string'\n ? row.metadata as string\n : JSON.stringify(row.metadata);\n\n const stats: MetadataStats = {\n size: metadataStr.length,\n mtime: record.updatedAt ?? record.createdAt ?? new Date().toISOString(),\n format: 'json',\n etag: record.checksum,\n };\n this.statCache?.set(key, stats);\n return stats;\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: no table, therefore nothing to stat. Not cached.\n return null;\n }\n }\n\n async list(type: string): Promise<string[]> {\n await this.ensureSchema();\n\n if (this.listCache) {\n const cached = this.listCache.get(type);\n if (cached !== undefined) return cached;\n }\n\n try {\n const rows = await this._find(this.tableName, {\n where: this.baseFilter(type),\n fields: ['name'],\n });\n\n const names = rows\n .map(row => row.name as string)\n .filter(name => typeof name === 'string');\n\n this.listCache?.set(type, names);\n return names;\n } catch (error) {\n this.rethrowUnlessTableUnprovisioned(error);\n // Benign only: no table, therefore no names. Not cached.\n return [];\n }\n }\n\n /**\n * Fetch a single history snapshot by (type, name, version).\n * Returns null when the record does not exist.\n */\n async getHistoryRecord(\n type: string,\n name: string,\n version: number\n ): Promise<MetadataHistoryRecord | null> {\n if (!this.trackHistory) return null;\n\n await this.ensureHistorySchema();\n\n const filter: Record<string, unknown> = {\n type,\n name,\n version,\n };\n if (this.organizationId) {\n filter.organization_id = this.organizationId;\n }\n\n const row = await this._findOne(this.historyTableName, {\n where: filter,\n });\n if (!row) return null;\n\n return {\n id: row.id as string,\n name: row.name as string,\n type: row.type as string,\n version: row.version as number,\n operationType: row.operation_type as MetadataHistoryRecord['operationType'],\n metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata as string) : row.metadata,\n checksum: row.checksum as string,\n previousChecksum: row.previous_checksum as string | undefined,\n changeNote: row.change_note as string | undefined,\n organizationId: row.organization_id as string | undefined,\n recordedBy: row.recorded_by as string | undefined,\n recordedAt: row.recorded_at as string,\n };\n }\n\n /**\n * Query history records with pagination and filtering.\n * Encapsulates history table queries so MetadataManager doesn't need\n * direct driver access.\n */\n async queryHistory(\n type: string,\n name: string,\n options?: {\n operationType?: string;\n since?: string;\n until?: string;\n limit?: number;\n offset?: number;\n includeMetadata?: boolean;\n }\n ): Promise<{ records: any[]; total: number; hasMore: boolean }> {\n if (!this.trackHistory) {\n return { records: [], total: 0, hasMore: false };\n }\n\n await this.ensureSchema();\n await this.ensureHistorySchema();\n\n // Build history query directly against (type, name); no parent\n // lookup needed since the history table is keyed by these fields.\n const historyFilter: Record<string, unknown> = {\n type,\n name,\n };\n if (this.organizationId) historyFilter.organization_id = this.organizationId;\n if (options?.operationType) historyFilter.operation_type = options.operationType;\n if (options?.since) historyFilter.recorded_at = { $gte: options.since };\n if (options?.until) {\n if (historyFilter.recorded_at) {\n (historyFilter.recorded_at as Record<string, unknown>).$lte = options.until;\n } else {\n historyFilter.recorded_at = { $lte: options.until };\n }\n }\n\n const limit = options?.limit ?? 50;\n const offset = options?.offset ?? 0;\n\n const historyRecords = await this._find(this.historyTableName, {\n where: historyFilter,\n orderBy: [\n { field: 'recorded_at', order: 'desc' as const },\n { field: 'version', order: 'desc' as const },\n ],\n limit: limit + 1,\n offset,\n });\n\n const hasMore = historyRecords.length > limit;\n const records = historyRecords.slice(0, limit);\n const total = await this._count(this.historyTableName, { where: historyFilter });\n\n const includeMetadata = options?.includeMetadata !== false;\n const result = records.map((row: Record<string, unknown>) => {\n const parsedMetadata =\n typeof row.metadata === 'string'\n ? JSON.parse(row.metadata as string)\n : (row.metadata as Record<string, unknown> | null | undefined);\n\n return {\n id: row.id as string,\n name: row.name as string,\n type: row.type as string,\n version: row.version as number,\n operationType: row.operation_type as string,\n metadata: includeMetadata ? parsedMetadata : null,\n checksum: row.checksum as string,\n previousChecksum: row.previous_checksum as string | undefined,\n changeNote: row.change_note as string | undefined,\n organizationId: row.organization_id as string | undefined,\n recordedBy: row.recorded_by as string | undefined,\n recordedAt: row.recorded_at as string,\n };\n });\n\n return { records: result, total, hasMore };\n }\n\n /**\n * Perform a rollback: persist `restoredData` as the new current state and record a\n * single 'revert' history entry (instead of the usual 'update' entry that `save()`\n * would produce). This avoids the duplicate-version problem that arises when\n * `register()` → `save()` writes an 'update' entry followed by an additional\n * 'revert' entry for the same version number.\n */\n async registerRollback(\n type: string,\n name: string,\n restoredData: unknown,\n targetVersion: number,\n changeNote?: string,\n recordedBy?: string\n ): Promise<void> {\n await this.ensureSchema();\n\n const now = new Date().toISOString();\n const metadataJson = JSON.stringify(restoredData);\n const newChecksum = await calculateChecksum(restoredData);\n\n const existing = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (!existing) {\n throw new Error(`Metadata ${type}/${name} not found for rollback`);\n }\n\n const previousChecksum = existing.checksum as string | undefined;\n const newVersion = ((existing.version as number) ?? 0) + 1;\n\n await this._update(this.tableName, existing.id as string, {\n metadata: metadataJson,\n version: newVersion,\n checksum: newChecksum,\n updated_at: now,\n state: 'active',\n });\n\n this.invalidate(type, name);\n\n // Write exactly one 'revert' history entry (not an 'update' entry)\n await this.createHistoryRecord(\n type,\n name,\n newVersion,\n restoredData,\n 'revert',\n previousChecksum,\n changeNote ?? `Rolled back to version ${targetVersion}`,\n recordedBy\n );\n }\n\n async save(\n type: string,\n name: string,\n data: any,\n _options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n const startTime = Date.now();\n\n await this.ensureSchema();\n\n const now = new Date().toISOString();\n const metadataJson = JSON.stringify(data);\n const newChecksum = await calculateChecksum(data);\n\n try {\n const existing = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (existing) {\n // Skip update if the content is identical (prevents phantom version bumps)\n const previousChecksum = existing.checksum as string | undefined;\n if (newChecksum === previousChecksum) {\n // No DB write, but make sure the cached payload reflects the latest\n // call (prior cached `null` would otherwise mask a freshly-saved\n // record).\n this.loadCache?.set(this.cacheKey(type, name), data as Record<string, unknown>);\n return {\n success: true,\n path: `datasource://${this.tableName}/${type}/${name}`,\n size: metadataJson.length,\n saveTime: Date.now() - startTime,\n };\n }\n\n // Update existing record\n const version = ((existing.version as number) ?? 0) + 1;\n\n await this._update(this.tableName, existing.id as string, {\n metadata: metadataJson,\n version,\n checksum: newChecksum,\n updated_at: now,\n state: 'active',\n });\n\n this.invalidate(type, name);\n\n // Create history record for update\n await this.createHistoryRecord(\n type,\n name,\n version,\n data,\n 'update',\n previousChecksum\n );\n\n return {\n success: true,\n path: `datasource://${this.tableName}/${type}/${name}`,\n size: metadataJson.length,\n saveTime: Date.now() - startTime,\n };\n } else {\n // Create new record\n const id = generateId();\n await this._create(this.tableName, {\n id,\n name,\n type,\n namespace: 'default',\n scope: (data as any)?.scope ?? 'platform',\n metadata: metadataJson,\n checksum: newChecksum,\n strategy: 'merge',\n state: 'active',\n version: 1,\n source: 'database',\n ...(this.organizationId ? { organization_id: this.organizationId } : {}),\n created_at: now,\n updated_at: now,\n });\n\n this.invalidate(type, name);\n\n // Create history record for creation\n await this.createHistoryRecord(\n type,\n name,\n 1,\n data,\n 'create'\n );\n\n return {\n success: true,\n path: `datasource://${this.tableName}/${type}/${name}`,\n size: metadataJson.length,\n saveTime: Date.now() - startTime,\n };\n }\n } catch (error) {\n throw new Error(\n `DatabaseLoader save failed for ${type}/${name}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n /**\n * Delete a metadata item from the database\n */\n async delete(type: string, name: string): Promise<void> {\n await this.ensureSchema();\n\n // Find the existing record to get its ID\n const existing = await this._findOne(this.tableName, {\n where: this.baseFilter(type, name),\n });\n\n if (!existing) {\n // Item doesn't exist, nothing to delete\n return;\n }\n\n // Delete from the main metadata table using the record's ID\n await this._delete(this.tableName, existing.id as string);\n\n this.invalidate(type, name);\n }\n}\n\n/**\n * Generate a simple unique ID for metadata records.\n * Uses crypto.randomUUID when available, falls back to timestamp-based ID.\n */\nfunction generateId(): string {\n if (typeof globalThis.crypto !== 'undefined' && typeof globalThis.crypto.randomUUID === 'function') {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID\n return `meta_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata History Utilities\n *\n * Utility functions for metadata versioning and history tracking,\n * including checksum calculation, JSON normalization, and diff generation.\n */\n\n/**\n * Calculate SHA-256 checksum of normalized JSON metadata.\n * Normalizes the JSON by sorting keys and removing whitespace\n * to ensure consistent checksums across identical content.\n *\n * @param metadata - The metadata object to checksum\n * @returns SHA-256 hex string\n */\nexport async function calculateChecksum(metadata: unknown): Promise<string> {\n // Normalize JSON by sorting keys recursively\n const normalized = normalizeJSON(metadata);\n const jsonString = JSON.stringify(normalized);\n\n // Use Web Crypto API (available in Node.js 15+ and all modern browsers)\n if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.subtle) {\n const encoder = new TextEncoder();\n const data = encoder.encode(jsonString);\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n }\n\n // Fallback for environments without Web Crypto API\n // Use a simple hash function (not cryptographically secure, but sufficient for change detection)\n return simpleHash(jsonString);\n}\n\n/**\n * Normalize JSON by recursively sorting object keys.\n * This ensures deterministic serialization for checksum calculation.\n *\n * @param value - The value to normalize\n * @returns Normalized value with sorted keys\n */\nfunction normalizeJSON(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map(normalizeJSON);\n }\n\n if (typeof value === 'object') {\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(value as object).sort();\n for (const key of keys) {\n sorted[key] = normalizeJSON((value as Record<string, unknown>)[key]);\n }\n return sorted;\n }\n\n return value;\n}\n\n/**\n * Simple hash function fallback for environments without Web Crypto API.\n * Based on djb2 hash algorithm.\n *\n * @param str - String to hash\n * @returns Hex hash string\n */\nfunction simpleHash(str: string): string {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) + hash) + str.charCodeAt(i);\n hash = hash & hash; // Convert to 32-bit integer\n }\n // Convert to hex and pad to 64 characters to match SHA-256 length\n const hexHash = Math.abs(hash).toString(16);\n return hexHash.padStart(64, '0');\n}\n\n/**\n * Generate a simple JSON patch between two objects.\n * Returns an array of operations showing what changed.\n *\n * @param oldObj - Original object\n * @param newObj - New object\n * @param path - Current path (for recursion)\n * @returns Array of change operations\n */\nexport function generateSimpleDiff(\n oldObj: unknown,\n newObj: unknown,\n path: string = ''\n): Array<{ op: string; path: string; value?: unknown; oldValue?: unknown }> {\n const changes: Array<{ op: string; path: string; value?: unknown; oldValue?: unknown }> = [];\n\n // Handle primitives\n if (typeof oldObj !== 'object' || oldObj === null || typeof newObj !== 'object' || newObj === null) {\n if (oldObj !== newObj) {\n changes.push({ op: 'replace', path: path || '/', value: newObj, oldValue: oldObj });\n }\n return changes;\n }\n\n // Handle arrays\n if (Array.isArray(oldObj) || Array.isArray(newObj)) {\n if (!Array.isArray(oldObj) || !Array.isArray(newObj) || oldObj.length !== newObj.length) {\n changes.push({ op: 'replace', path: path || '/', value: newObj, oldValue: oldObj });\n } else {\n // Compare array elements\n for (let i = 0; i < oldObj.length; i++) {\n const subPath = `${path}/${i}`;\n changes.push(...generateSimpleDiff(oldObj[i], newObj[i], subPath));\n }\n }\n return changes;\n }\n\n // Handle objects\n const oldKeys = new Set(Object.keys(oldObj as object));\n const newKeys = new Set(Object.keys(newObj as object));\n\n // Check for added keys\n for (const key of newKeys) {\n if (!oldKeys.has(key)) {\n const subPath = path ? `${path}/${key}` : `/${key}`;\n changes.push({ op: 'add', path: subPath, value: (newObj as Record<string, unknown>)[key] });\n }\n }\n\n // Check for removed keys\n for (const key of oldKeys) {\n if (!newKeys.has(key)) {\n const subPath = path ? `${path}/${key}` : `/${key}`;\n changes.push({ op: 'remove', path: subPath, oldValue: (oldObj as Record<string, unknown>)[key] });\n }\n }\n\n // Check for modified keys\n for (const key of oldKeys) {\n if (newKeys.has(key)) {\n const subPath = path ? `${path}/${key}` : `/${key}`;\n changes.push(...generateSimpleDiff(\n (oldObj as Record<string, unknown>)[key],\n (newObj as Record<string, unknown>)[key],\n subPath\n ));\n }\n }\n\n return changes;\n}\n\n/**\n * Generate a human-readable summary of changes.\n *\n * @param diff - The diff operations\n * @returns Human-readable summary\n */\nexport function generateDiffSummary(\n diff: Array<{ op: string; path: string; value?: unknown; oldValue?: unknown }>\n): string {\n if (diff.length === 0) {\n return 'No changes';\n }\n\n const summary: string[] = [];\n const addCount = diff.filter(d => d.op === 'add').length;\n const removeCount = diff.filter(d => d.op === 'remove').length;\n const replaceCount = diff.filter(d => d.op === 'replace').length;\n\n if (addCount > 0) summary.push(`${addCount} field${addCount > 1 ? 's' : ''} added`);\n if (removeCount > 0) summary.push(`${removeCount} field${removeCount > 1 ? 's' : ''} removed`);\n if (replaceCount > 0) summary.push(`${replaceCount} field${replaceCount > 1 ? 's' : ''} modified`);\n\n return summary.join(', ');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Generic LRU (Least Recently Used) cache with optional TTL.\n *\n * Implementation notes:\n * - Backed by a `Map`, which preserves insertion order. We promote entries on\n * read by deleting and re-inserting, so the oldest entry is always\n * `map.keys().next()`.\n * - TTL is checked lazily on `get` / `has`. Expired entries are evicted on\n * access; we do not run a background sweeper to keep the implementation\n * side-effect free in serverless / edge runtimes.\n * - Set `maxSize <= 0` to disable the size cap; set `ttl <= 0` (or omit) to\n * disable expiration.\n *\n * Designed for `DatabaseLoader` read-path caching — see\n * `packages/metadata/src/loaders/database-loader.ts`.\n */\nexport interface LRUCacheOptions {\n /** Maximum number of entries; when exceeded, the LRU entry is evicted. */\n maxSize?: number;\n /** Time-to-live in milliseconds. Zero or undefined disables TTL. */\n ttl?: number;\n}\n\ninterface Entry<V> {\n value: V;\n expiresAt: number; // 0 means \"never\"\n}\n\nexport class LRUCache<K, V> {\n private readonly map = new Map<K, Entry<V>>();\n private readonly maxSize: number;\n private readonly ttl: number;\n private hits = 0;\n private misses = 0;\n\n constructor(options: LRUCacheOptions = {}) {\n this.maxSize = options.maxSize && options.maxSize > 0 ? options.maxSize : 0;\n this.ttl = options.ttl && options.ttl > 0 ? options.ttl : 0;\n }\n\n get(key: K): V | undefined {\n const entry = this.map.get(key);\n if (!entry) {\n this.misses++;\n return undefined;\n }\n if (entry.expiresAt !== 0 && entry.expiresAt <= Date.now()) {\n this.map.delete(key);\n this.misses++;\n return undefined;\n }\n // Promote to most-recently-used.\n this.map.delete(key);\n this.map.set(key, entry);\n this.hits++;\n return entry.value;\n }\n\n set(key: K, value: V): void {\n if (this.map.has(key)) {\n this.map.delete(key);\n } else if (this.maxSize > 0 && this.map.size >= this.maxSize) {\n const oldest = this.map.keys().next();\n if (!oldest.done) this.map.delete(oldest.value);\n }\n this.map.set(key, {\n value,\n expiresAt: this.ttl > 0 ? Date.now() + this.ttl : 0,\n });\n }\n\n has(key: K): boolean {\n return this.get(key) !== undefined;\n }\n\n delete(key: K): boolean {\n return this.map.delete(key);\n }\n\n clear(): void {\n this.map.clear();\n }\n\n get size(): number {\n return this.map.size;\n }\n\n /** Diagnostic counters — useful for `metrics` endpoints. */\n stats(): { size: number; hits: number; misses: number; hitRate: number } {\n const total = this.hits + this.misses;\n return {\n size: this.map.size,\n hits: this.hits,\n misses: this.misses,\n hitRate: total === 0 ? 0 : this.hits / total,\n };\n }\n\n /** Resets hit/miss counters without dropping cached entries. */\n resetStats(): void {\n this.hits = 0;\n this.misses = 0;\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Driver-error classification for the metadata storage seams (#4728, #4825;\n * rule from #4632).\n *\n * Two questions live here, and they share one mechanism on purpose. A second\n * hand-rolled `catch`-and-guess elsewhere in this package would be a second\n * de-facto vocabulary of \"which driver errors are benign\" — the exact debt this\n * module exists to retire. Both predicates below are thin wrappers over one\n * signature matcher, so a driver quirk is taught to the package once.\n *\n * 1. {@link isSchemaAlreadyExistsError} — \"was this DDL failure just the table\n * already being there?\" (#4728, `ensureSchema` / `ensureHistorySchema`).\n * 2. {@link isMissingTableError} — \"did this READ fail because the table has\n * not been provisioned yet?\" (#4825, `nextEventSeq`).\n *\n * They are deliberately **not** each other's negation. Each answers \"is this\n * the one benign reason?\" and defaults to *not benign*, so an error neither\n * recognises is loud under both.\n *\n * ---\n *\n * ## 1. DDL failure classification (#4728)\n *\n * `IDataDriver.syncSchema()` is contractually **idempotent** (\"creates tables if\n * missing, adds columns, updates indexes\"), so in principle a re-sync of an\n * existing table should not throw at all. In practice a driver may surface the\n * already-provisioned case as an error instead of a no-op — `CREATE TABLE`\n * without `IF NOT EXISTS`, an `ALTER TABLE ADD COLUMN` for a column that is\n * already there. That single failure reason is benign: the table and its columns\n * exist, so the bytes will land.\n *\n * **Every other** DDL failure is not benign, and the difference is the whole\n * point of this module. Insufficient privileges, a datasource that never\n * connected, an incompatible column type — after those, the table or column does\n * not exist, yet the process keeps looking healthy while everything it claims to\n * persist has nowhere to land. That is the #4420 shape, and AGENTS.md →\n * \"Degradation log levels\" requires it to be reported at `error`.\n *\n * The defect this replaces was a `catch` whose comment named the benign reason\n * (\"e.g. table already exists\") and used it to excuse **all** of them. Callers\n * must therefore ask the question by error *type*:\n *\n * ```ts\n * catch (error) {\n * if (!isSchemaAlreadyExistsError(error)) {\n * console.error('… consequence … fix …', error); // loud, and stay not-ready\n * return;\n * }\n * // benign only: the table is already provisioned, carry on\n * }\n * ```\n *\n * Classification is deliberately conservative — anything not positively\n * recognised as \"already exists\" is treated as a real failure, because the cost\n * of a false \"benign\" (silent data loss) is far higher than the cost of a false\n * \"real\" (one extra error line).\n *\n * ---\n *\n * ## 2. Missing-table classification for reads (#4825)\n *\n * `DatabaseLoader.nextEventSeq()` reads `sys_metadata_history` to decide what\n * `event_seq` the NEXT history row gets. Its `catch` named both reasons a read\n * can fail — \"table not provisioned yet\" (benign: 1 really is the next number)\n * and \"driver error\" (**not** benign) — and answered both with `return 1`.\n *\n * That is the #4728 shape one layer down, but the damage is the opposite kind\n * and worse. #4728 was *bytes that never landed*; this is **bytes that land\n * wrong**: with N rows already in the table, one flaky read hands the next row\n * `event_seq = 1`, colliding with an existing row. The insert **succeeds**, no\n * line is logged, and `event_seq` — the ordering key that history listing and\n * rollback targeting both stand on — is now silently untrustworthy.\n *\n * So the read seam gets the same treatment, with the same conservative default:\n *\n * ```ts\n * catch (error) {\n * if (isMissingTableError(error)) return 1; // benign: nothing to collide with\n * throw error; // caller reports the consequence\n * }\n * ```\n */\n\n// [#6615] The Postgres `\"x\" of relation \"y\"` phrase, owned once — see the\n// module docblock in `@objectstack/types` for the superstring hole it closes\n// and for why the exclusion's width deliberately differs from the extractor's.\nimport { isRelationSubObjectPhrase } from '@objectstack/types';\n\n/** One \"which errors mean X?\" vocabulary, in the three forms drivers use. */\ninterface DriverErrorSignature {\n /** `error.code` — Postgres SQLSTATE, or mysql2's symbolic name. */\n readonly codes: ReadonlySet<string>;\n /** `error.errno` — MySQL/MariaDB numeric equivalents. */\n readonly errnos: ReadonlySet<number>;\n /** `error.message` — the only signal SQLite-family drivers give. */\n readonly message: RegExp;\n /**\n * Optional **front-exclusion**, evaluated before any positive test (#6347).\n *\n * A message test can never exclude a *superstring*: once a legal phrase for\n * X appears inside a longer phrase that means NOT-X, no amount of widening\n * the X regex removes the match — the phrase really is in there. The only\n * repair is to recognise the not-X shape first and stop. So this is a\n * separate channel rather than another alternation in {@link message}.\n */\n readonly excludes?: {\n /** SQLSTATEs / driver codes that positively mean \"**not** this case\". */\n readonly codes: ReadonlySet<string>;\n /**\n * Message shapes that carry a legal match for this case as a substring.\n *\n * A predicate rather than a `RegExp` since #6615, so this channel can be\n * satisfied by a shared, named question from `@objectstack/types` instead\n * of a pattern this file owns alone. The phrase it tests is the same one\n * `@objectstack/rest` and `@objectstack/service-analytics` read.\n */\n readonly matchesMessage: (message: string) => boolean;\n };\n}\n\n/**\n * Driver/SQLSTATE codes that mean \"the thing you asked me to create is already\n * there\". Postgres reports SQLSTATE on `code`; mysql2 reports its symbolic name.\n */\nconst ALREADY_EXISTS: DriverErrorSignature = {\n codes: new Set([\n // PostgreSQL SQLSTATE (class 42 — syntax error or access rule violation)\n '42P07', // duplicate_table\n '42701', // duplicate_column\n '42710', // duplicate_object — index / constraint already exists\n // MySQL / MariaDB (mysql2 puts the symbolic name on `code`)\n 'ER_TABLE_EXISTS_ERROR', // 1050\n 'ER_DUP_FIELDNAME', // 1060\n 'ER_DUP_KEYNAME', // 1061\n ]),\n errnos: new Set([1050, 1060, 1061]),\n /**\n * Message fallback for drivers that carry no machine-readable code —\n * notably SQLite, whose `code` is the undifferentiated `SQLITE_ERROR` for\n * every DDL failure, so the message is the only signal available:\n * - `table sys_metadata already exists`\n * - `duplicate column name: environment_id`\n * - `index idx_x already exists`\n * Postgres phrases its own as `relation \"x\" already exists` /\n * `column \"x\" of relation \"y\" already exists`, which matches the same test.\n */\n message: /already exists|duplicate column name|duplicate key name/i,\n};\n\n/**\n * Codes/messages that mean \"the table you tried to READ has not been created\".\n *\n * Narrower than it looks, on purpose. `does not exist` on its own also covers\n * `role \"x\" does not exist` (42704), `database \"x\" does not exist` (3D000) and\n * `column \"x\" does not exist` (42703) — every one of them a **real** failure\n * that must stay loud, and every one of them a case where \"start numbering at\n * 1\" would be the wrong answer against a table that may be full of rows. So the\n * message test demands the word table/relation next to the phrase rather than\n * the phrase alone, and the code set carries only the table-scoped SQLSTATEs.\n *\n * That was not enough on its own, and #6347 is why. Postgres has **two**\n * missing-column phrasings, one per direction:\n *\n * | path | phrase | SQLSTATE | matched the message test? |\n * |:---|:---|:---|:---|\n * | read (`SELECT`) | `column \"bogus\" does not exist` | 42703 | no |\n * | write (`INSERT`/`UPDATE`/`ALTER`) | `column \"label\" of relation \"sys_team\" does not exist` | 42703 | **yes** |\n *\n * The write-path phrase contains a complete, legal missing-table phrase —\n * `relation \"sys_team\" does not exist` — as a substring, so the table-scoped\n * test above matched it and answered *benign* about an error the docblock two\n * paragraphs up already named as one that must stay loud. The same holds for\n * every other sub-object of a relation Postgres phrases this way, e.g.\n * `constraint \"uq_x\" of relation \"sys_team\" does not exist` (42704). And\n * code-first does not rescue it: {@link matchesDriverError} is a sequential OR,\n * so a `code: '42703'` error simply falls past the two code lines and is\n * decided by the message.\n *\n * Hence {@link DriverErrorSignature.excludes}: the not-a-table shapes are\n * recognised FIRST, and recognition ends the question with `false`.\n */\nconst MISSING_TABLE: DriverErrorSignature = {\n codes: new Set([\n '42P01', // PostgreSQL undefined_table\n 'ER_NO_SUCH_TABLE', // MySQL / MariaDB 1146\n ]),\n errnos: new Set([1146]),\n /**\n * - SQLite / libsql: `no such table: sys_metadata_history`\n * - PostgreSQL: `relation \"sys_metadata_history\" does not exist`\n * - MySQL/MariaDB: `Table 'app.sys_metadata_history' doesn't exist`\n */\n message:\n /no such table|relation [\"'`][^\"'`]+[\"'`] does not exist|table [\"'`][^\"'`]+[\"'`] doesn'?t exist|unknown table/i,\n excludes: {\n /**\n * Exactly the three SQLSTATEs the docblock above already names as\n * must-stay-loud neighbours of `does not exist`. They are listed here\n * rather than merely trusted to miss the message test, because two of\n * them (42703 columns, 42704 constraints/triggers) have a phrasing that\n * *does* hit it, and because a code is a fact where prose is a guess.\n *\n * Postgres-shaped on purpose: measured, neither MySQL\n * (`Unknown column 'label' in 'field list'`) nor SQLite\n * (`no such column: bogus`, `table t has no column named label`)\n * phrases a sub-object failure so that a missing-table phrase falls out\n * of it, so there is nothing there to exclude. Adding their codes would\n * be surface with no defect behind it.\n */\n codes: new Set([\n '42703', // undefined_column\n '42704', // undefined_object — constraint, trigger, role, type, …\n '3D000', // invalid_catalog_name — `database \"x\" does not exist`\n ]),\n /**\n * `«sub-object» \"x\" of relation \"y\" …` — Postgres' phrasing for a\n * failure about something *inside* a relation, which therefore says the\n * relation itself is present. The two in-repo siblings that carry this\n * phrase are `mapDataError` (`packages/rest`, #5352) and\n * `service-analytics`'s missing-column subtraction (#6035/PR #6346).\n *\n * [#6615] All three now read one home — `@objectstack/types` — instead\n * of three hand-kept copies, so the phrase can no longer be taught to\n * the repo a fourth time or drift in one package only. The **width**\n * difference that used to justify the copy is preserved and is the\n * reason the home exports two functions rather than one: those two\n * *extract* the column name to phrase a better error, so a miss costs a\n * vaguer message; this one *excludes*, so a miss restores the\n * corruption. {@link isRelationSubObjectPhrase} is therefore the wider\n * question — it drops their `column`/`[a-z0-9_]+`/`does not exist`\n * anchors: any sub-object, any quoted identifier, any verdict.\n * Over-matching here only ever converts a benign verdict into a loud\n * one, which is the direction this whole module already errs in.\n */\n matchesMessage: isRelationSubObjectPhrase,\n },\n};\n\n/** How far to follow an `error.cause` chain — drivers wrap, but not deeply. */\nconst MAX_CAUSE_DEPTH = 4;\n\n/**\n * The single matcher both predicates run on: exclusions, then code, then errno,\n * then message, then one step down the `cause` chain.\n *\n * Unrecognised is always `false` — a benign verdict must be *earned*, never\n * defaulted to, because a false \"benign\" corrupts data while a false \"real\"\n * costs one error line.\n *\n * The exclusion runs at every node and, when it fires, returns `false` **without\n * descending into `cause`** (#6347). Two reasons, both the conservative\n * direction: an error that positively identifies as \"a column of an existing\n * relation\" *is* that error, whatever it wraps; and stopping can only ever\n * subtract benign verdicts, never add one.\n */\nfunction matchesDriverError(\n error: unknown,\n signature: DriverErrorSignature,\n depth: number,\n): boolean {\n if (error === null || error === undefined || depth > MAX_CAUSE_DEPTH) return false;\n\n if (typeof error === 'string') {\n if (signature.excludes?.matchesMessage(error)) return false;\n return signature.message.test(error);\n }\n if (typeof error !== 'object') return false;\n\n const err = error as {\n code?: unknown;\n errno?: unknown;\n message?: unknown;\n cause?: unknown;\n };\n\n const excludes = signature.excludes;\n if (excludes) {\n if (typeof err.code === 'string' && excludes.codes.has(err.code)) return false;\n if (typeof err.message === 'string' && excludes.matchesMessage(err.message)) return false;\n }\n\n if (typeof err.code === 'string' && signature.codes.has(err.code)) return true;\n if (typeof err.errno === 'number' && signature.errnos.has(err.errno)) return true;\n if (typeof err.message === 'string' && signature.message.test(err.message)) return true;\n\n // Drivers commonly re-throw with the original attached as `cause`.\n return matchesDriverError(err.cause, signature, depth + 1);\n}\n\n/**\n * Is this DDL error the benign \"already provisioned\" case?\n *\n * @param error - The value thrown by `syncSchema()` (or any DDL call).\n * @param depth - Internal `cause`-chain recursion counter; callers pass nothing.\n * @returns `true` only when the error positively identifies as\n * table/column/index-already-exists. Anything else — including an\n * unrecognised error, `undefined`, or a permission/connection failure —\n * returns `false` and MUST be reported loudly by the caller.\n */\nexport function isSchemaAlreadyExistsError(error: unknown, depth = 0): boolean {\n return matchesDriverError(error, ALREADY_EXISTS, depth);\n}\n\n/**\n * Is this READ error the benign \"table has not been provisioned yet\" case?\n *\n * The only failure that licenses a caller to treat an empty table as the truth\n * — there are no rows, so there is nothing to be inconsistent with. A\n * connection drop, a timeout, a permission denial or a query error all mean the\n * rows may well exist and simply were not seen; those return `false` and the\n * caller must report the consequence and give up rather than compute an answer\n * from data it never read (#4825).\n *\n * A failure about a **column** of a relation is never this case, in either of\n * Postgres' two phrasings — the relation is right there in the message because\n * it exists (#6347). See {@link MISSING_TABLE}'s `excludes`.\n *\n * @param error - The value thrown by a driver/engine read (`find`, `findOne`, …).\n * @param depth - Internal `cause`-chain recursion counter; callers pass nothing.\n * @returns `true` only when the error positively identifies as\n * table/relation-does-not-exist.\n */\nexport function isMissingTableError(error: unknown, depth = 0): boolean {\n return matchesDriverError(error, MISSING_TABLE, depth);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Migration: project_id → environment_id\n *\n * Renames the `project_id` column to `environment_id` on the metadata\n * storage tables:\n * - sys_metadata\n * - sys_metadata_history\n *\n * Forward counterpart of {@link migrateEnvIdToProjectId} (which performed the\n * earlier `env_id → project_id` rename). Together they let an operator walk an\n * old schema all the way forward in two steps:\n *\n * migrateEnvIdToProjectId(driver); // env_id → project_id (legacy)\n * migrateProjectIdToEnvironmentId(driver); // project_id → environment_id (v5)\n *\n * (The per-type projection tables `sys_object` / `sys_view` / `sys_flow` /\n * `sys_agent` / `sys_tool` were removed in 2026-05 along with the projection\n * pipeline — see ADR 0005 addendum. They are intentionally not included.)\n *\n * Safe to run multiple times (idempotent): checks for column existence before\n * attempting to rename. If `environment_id` already exists, the step is\n * skipped.\n *\n * Usage:\n * import { migrateProjectIdToEnvironmentId } from '@objectstack/metadata/migrations';\n * await migrateProjectIdToEnvironmentId(driver);\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\n\nconst AFFECTED_TABLES = [\n 'sys_metadata',\n 'sys_metadata_history',\n] as const;\n\nexport interface ProjectIdToEnvironmentIdResult {\n table: string;\n status: 'renamed' | 'already_done' | 'table_missing' | 'error';\n error?: string;\n}\n\n/**\n * Rename `project_id` → `environment_id` on all metadata tables.\n *\n * @param driver An IDataDriver with access to the target database.\n * Must expose a raw query method: `driver.raw(sql, bindings?)`.\n * @returns Per-table migration results.\n */\nexport async function migrateProjectIdToEnvironmentId(\n driver: IDataDriver,\n): Promise<ProjectIdToEnvironmentIdResult[]> {\n const driverAny = driver as any;\n\n if (typeof driverAny.raw !== 'function') {\n throw new Error(\n 'migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. ' +\n 'migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. ' +\n 'SqlDriver (better-sqlite3/knex) supports this; cloud-side TursoDriver also conforms.'\n );\n }\n\n const results: ProjectIdToEnvironmentIdResult[] = [];\n\n for (const table of AFFECTED_TABLES) {\n try {\n const hasColumn = await _columnExists(driverAny, table, 'project_id');\n const alreadyMigrated = await _columnExists(driverAny, table, 'environment_id');\n\n if (alreadyMigrated && !hasColumn) {\n results.push({ table, status: 'already_done' });\n continue;\n }\n\n if (!hasColumn) {\n results.push({ table, status: 'table_missing' });\n continue;\n }\n\n await driverAny.raw(\n `ALTER TABLE \"${table}\" RENAME COLUMN project_id TO environment_id`,\n );\n\n results.push({ table, status: 'renamed' });\n } catch (err: any) {\n results.push({ table, status: 'error', error: err?.message ?? String(err) });\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nasync function _columnExists(driver: any, table: string, column: string): Promise<boolean> {\n try {\n const rows: any[] = await driver.raw(`PRAGMA table_info(\"${table}\")`);\n if (Array.isArray(rows) && rows.length > 0) {\n const list: any[] = Array.isArray(rows[0]) ? rows[0] : rows;\n return list.some((r: any) => r?.name === column);\n }\n\n const result: any[] = await driver.raw(\n `SELECT column_name FROM information_schema.columns WHERE table_name = ? AND column_name = ?`,\n [table, column],\n );\n const list: any[] = Array.isArray(result[0]) ? result[0] : result;\n return list.length > 0;\n } catch {\n return false;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Endpoint matcher — the producer side of `IMetadataService.matchEndpoint`.\n *\n * [#5089, #5040 E2] A declared `api` metadata item owns a `method`+`path`\n * pair; this module turns the stored `api` items into a lookup the HTTP\n * dispatcher can perform once per request, between \"no built-in domain\n * claimed this path\" and \"answer a semantic 404\".\n *\n * The binding specification is the contract text on\n * `IMetadataService.matchEndpoint` and {@link ApiEndpointMatch} in\n * `packages/spec/src/contracts/metadata-service.ts` (landed by #5080/#5097).\n * This module implements it literally; where the text is silent the choice is\n * documented here rather than invented in a caller.\n *\n * ## What this module is NOT\n *\n * It is not a router. `ApiEndpointSchema.path` is a frozen vocabulary\n * (ADR-0121) that defines no template syntax — no `:param`, no `{param}` — so\n * there is nothing to compile and nothing to rank. Matching is a `Map` lookup\n * on an exact key, and {@link ApiEndpointMatch.params} is always `{}`.\n * Inventing a template syntax here would create a dialect that exists only\n * inside an implementation, which Prime Directive #12 forbids.\n *\n * ## Normalization (both sides, identically)\n *\n * - **method** — upper-cased. `ApiEndpointSchema.method` is already an\n * upper-case `HttpMethod` enum, so this only ever changes the *query* side,\n * which is the point: a request verb is compared case-insensitively.\n * - **path** — exactly ONE trailing slash is trimmed, and never from a lone\n * `/`. Trimming one (not all) keeps `/x//` and `/x/` distinct, matching how\n * every router in this stack treats an empty path segment; keeping `/`\n * whole means the normalized form is still a legal `ApiEndpointSchema.path`\n * and a query for `\"\"` can never collide with a declaration of `/`.\n * Nothing else happens to the path: **no** percent-decoding, **no** Unicode\n * normalization, **no** case folding. 17.x compares the raw string. (Design\n * §7-5 keeps RFC 3986 canonicalization as an explicit open question — it is\n * a vocabulary-level decision, not an implementation detail to smuggle in.)\n *\n * ## Envelope off, body parsed (#5309)\n *\n * What arrives here is a STORED ROW, not an authored declaration: the metadata\n * layer wraps every row in its own bookkeeping — `packageId`, `state`,\n * `version`, `publishedDefinition`, … — either flat beside the body or under a\n * `metadata` key (the publish envelope). None of that is endpoint vocabulary,\n * so `peelStoredEnvelope` takes it off and `ApiEndpointSchema` sees the\n * authored body alone. Before the split, the schema's unknown-key STRIPPING\n * was load-bearing here: it silently ate the bookkeeping, which is exactly why\n * `api` could not be closed (#5271's measurement, #5384's job). Stripping is no\n * longer what makes a stored row parse.\n *\n * The body-selection half (`metadata ?? row`) is the metadata layer's existing\n * rule — `publishPackage`'s snapshot, `getPublished`, `gateApiItemsForPublish`\n * all use it — and this module now follows it too, so a publish envelope that\n * passes the publish gate is the same document this index serves. It used to\n * be the one reader that disagreed.\n *\n * ## Loud, never half-valid\n *\n * Every stored item is `ApiEndpointSchema.safeParse`-d. The answer handed back\n * is the PARSED object, so schema defaults are materialized — most importantly\n * `authRequired`, which the schema defaults to `true`: a consumer can never\n * read an author's omission as \"no auth required\". An item that fails to parse\n * is skipped and named at `error` level: an endpoint the author declared and\n * the runtime will not serve is a capability that silently went missing, and\n * \"absence must be loud\" (AGENTS.md, Route & surface ownership §3). Skipping\n * one bad item never disturbs the good ones.\n *\n * ## The publish gates, applied a second time at load (#5189, #5040 E7b)\n *\n * Parsing is necessary and NOT sufficient. `ApiEndpointSchema` accepts shapes\n * the runtime refuses and shapes ADR-0121 forbids — `type: 'proxy'`, a mapping\n * `transform`, and above all `authRequired: false` with no armed `rateLimit`.\n * E7 (#5111) hung the gates that reject those on `ObjectStackDefinitionSchema`,\n * which covers every path that parses a STACK; #5189 proved that a stored `api`\n * item need never have been part of one (`metadata.register()`, a Studio write,\n * `publishPackage`). Most gates degrade safely when bypassed — the executor\n * answers a structured 501, a mis-namespaced path simply matches nothing — but\n * D6 has no runtime counterpart at all: the runtime honours `authRequired:\n * false` faithfully and `deriveBucketConfig` returns `null` for a disarmed\n * budget, so a bypassed D6 mints an anonymous, zero-quota execution entry\n * point. That is the exact shape D6 exists to prevent.\n *\n * So every parsed item is re-judged here by\n * {@link identityFreeEndpointGateFailure} — the SAME `firstFailure` the publish\n * gate runs, minus the two gates that need an identity this module does not\n * have. The asymmetry is deliberate and worth stating: the **namespace** gate\n * needs `manifest.namespace` (a stored row carries no manifest, and deriving\n * one from the very path being judged would be circular), and the\n * **uniqueness** gate is a per-stack rule that the duplicate-claim resolution\n * below already covers store-wide. An item failing an identity-free gate is\n * EXCLUDED from the index and named at `error` level, exactly like a parse\n * failure: a bypassed endpoint that answers 404 plus a loud log is the safe\n * failure; one that answers anonymously and unmetered is not. Publish is the\n * first door; this is the backstop, never the only door.\n *\n * ## Duplicate claims\n *\n * Two stored items may claim the same METHOD+path (publish rejects that inside\n * one stack, but a direct `metadata.register()` write bypasses publish). The\n * resolution is deterministic and announced, never last-write-wins: the\n * endpoint whose `name` sorts FIRST lexicographically keeps the route, and the\n * discarded claimant is named at `error` level together with the winner and\n * the rule. Deterministic because a coin flip would make the same deployment\n * behave differently per node and per boot; loud because a declaration that\n * does not serve is exactly the \"declared ≠ enforced\" state this repo keeps\n * paying to remove. (#5040 design §1.3.)\n *\n * ## An outage is not a 404\n *\n * `undefined` means \"no declaration owns this route\". A store that cannot be\n * read must THROW — the same distinction `loadDiagnosed` draws for the\n * singular read (ADR-0110 D3), for the same reason: a miss becomes a 404, and\n * an unreachable metadata store must never masquerade as one. So the read this\n * matcher performs is deliberately NOT wrapped in a `try`/`catch`, and a\n * failed build is not cached — the next call retries against a store that may\n * have recovered.\n */\n\nimport {\n ApiEndpointSchema,\n identityFreeEndpointGateFailure,\n normalizeEndpointPath,\n type ApiEndpoint,\n} from '@objectstack/spec/api';\nimport type { ApiEndpointMatch } from '@objectstack/spec/contracts';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { peelStoredEnvelope, storedItemName } from './stored-envelope.js';\n\n/**\n * Upper-case a request verb so `method` compares case-insensitively.\n */\nexport function normalizeEndpointMethod(method: string): string {\n return String(method ?? '').toUpperCase();\n}\n\n/**\n * Trim exactly one trailing slash, never from a lone `/`.\n *\n * Applied to BOTH the stored declaration and the query, so the two sides can\n * never disagree about which form is canonical — and re-exported from\n * `@objectstack/spec/api`, which OWNS the rule (#5040 E7), rather than\n * re-implemented here. The publish gate that rejects two endpoints claiming the\n * same METHOD + path must normalize exactly as this matcher does, or a stack\n * could publish a duplicate the index then silently resolves to one winner.\n */\nexport { normalizeEndpointPath };\n\n/** The index key for a normalized method+path pair. */\nexport function endpointIndexKey(method: string, path: string): string {\n return `${normalizeEndpointMethod(method)} ${normalizeEndpointPath(path)}`;\n}\n\n/** The lazily-built lookup: `\"METHOD /path\"` → parsed endpoint. */\nexport type EndpointIndex = ReadonlyMap<string, ApiEndpoint>;\n\nexport interface EndpointMatcherDeps {\n /**\n * Enumerate the stored `api` metadata items.\n *\n * MUST reject when the store cannot be read. Resolving with `[]` on a failed\n * read would turn an outage into \"nothing is declared\", i.e. into a 404 —\n * precisely what the contract forbids. See `MetadataManager.listForIndex`.\n */\n listApiItems(): Promise<unknown[]>;\n logger: Logger;\n}\n\n/**\n * Build the METHOD→path→endpoint index from raw stored items.\n *\n * Exported for tests and for any future occupant of the `metadata` slot that\n * wants the same load-time discipline without inheriting `MetadataManager`.\n */\nexport function buildEndpointIndex(items: readonly unknown[], logger: Logger): EndpointIndex {\n const index = new Map<string, ApiEndpoint>();\n\n for (const item of items) {\n // [#5309] Envelope OFF before the body parse. A stored row carries the\n // metadata layer's bookkeeping (`packageId`, `state`, …) which is not\n // endpoint vocabulary; `ApiEndpointSchema` judges the authored body and\n // nothing else. See `stored-envelope.ts` for why this is the fix rather\n // than teaching the vocabulary two storage keys.\n const peeled = peelStoredEnvelope(item);\n const parsed = ApiEndpointSchema.safeParse(peeled.body);\n if (!parsed.success) {\n // LOUD skip (contract: \"MUST skip (loudly) any stored item that fails to\n // parse rather than returning a half-valid shape\"). Name the item so the\n // author can find it; say what the consequence is.\n const declaredName = storedItemName(peeled) ?? '<unnamed>';\n logger.error(\n `[EndpointMatcher] stored api item '${declaredName}' does not satisfy ApiEndpointSchema — ` +\n `it is EXCLUDED from endpoint matching and its declared route will answer 404. ` +\n `Fix the declaration (or remove it); the endpoint index never serves a half-valid shape.`,\n undefined,\n { issues: parsed.error.issues },\n );\n continue;\n }\n\n const endpoint = parsed.data;\n\n // [#5189, #5040 E7b] Second door: the identity-free publish gates. A stored\n // item that never passed publish is excluded rather than served — see the\n // module header for why D6 in particular cannot be left to the runtime.\n const gateFailure = identityFreeEndpointGateFailure(endpoint);\n if (gateFailure) {\n logger.error(\n `[EndpointMatcher] stored api item '${endpoint.name}' was stored WITHOUT passing the ` +\n `endpoint publish gates (#5040 E7 / ADR-0121) — it is EXCLUDED from endpoint matching and ` +\n `its declared route will answer 404. Republish it through a gated path (a stack artifact, ` +\n `or \\`publishPackage\\` with the package's \\`manifest.namespace\\`); a direct metadata write ` +\n `is not a publish. Gate failure: ${gateFailure.message}`,\n undefined,\n { name: endpoint.name, issue: { path: gateFailure.path, message: gateFailure.message } },\n );\n continue;\n }\n\n const key = endpointIndexKey(endpoint.method, endpoint.path);\n const incumbent = index.get(key);\n\n if (!incumbent) {\n index.set(key, endpoint);\n continue;\n }\n\n // Deterministic + loud: lexicographically-first `name` keeps the route.\n // `<` (not `<=`) keeps the first-seen entry when names are equal, so the\n // rule is total even in the degenerate case.\n const challengerWins = endpoint.name < incumbent.name;\n const winner = challengerWins ? endpoint : incumbent;\n const loser = challengerWins ? incumbent : endpoint;\n if (challengerWins) index.set(key, endpoint);\n\n logger.error(\n `[EndpointMatcher] duplicate endpoint claim on '${key}': api items '${incumbent.name}' and ` +\n `'${endpoint.name}' both declare it. '${winner.name}' KEEPS the route and '${loser.name}' is ` +\n `IGNORED — the rule is lexicographically-first \\`name\\` wins, chosen so every node and every ` +\n `boot resolves it identically. Rename or repath '${loser.name}' to make it reachable.`,\n undefined,\n { key, winner: winner.name, ignored: loser.name },\n );\n }\n\n return index;\n}\n\n/**\n * Lazy, invalidating endpoint index.\n *\n * Built on the first {@link match} call and rebuilt on the next call after any\n * {@link invalidate}. Endpoint counts are single- to triple-digit, so a whole\n * rebuild is cheaper than per-item bookkeeping and cannot drift from the store.\n */\nexport class EndpointMatcher {\n private readonly deps: EndpointMatcherDeps;\n /** Resolved index, or `undefined` while dirty. */\n private index?: EndpointIndex;\n /** In-flight build, so concurrent requests share one store read. */\n private building?: Promise<EndpointIndex>;\n\n constructor(deps: EndpointMatcherDeps) {\n this.deps = deps;\n }\n\n /** Mark the index stale; the next {@link match} rebuilds it. */\n invalidate(): void {\n this.index = undefined;\n this.building = undefined;\n }\n\n /**\n * Resolve `method`+`path` to the owning declaration.\n *\n * @returns the parsed endpoint plus `params: {}`, or `undefined` on a miss.\n * @throws whatever the store read threw — an outage is never a miss.\n */\n async match(query: { path: string; method: string }): Promise<ApiEndpointMatch | undefined> {\n const index = await this.ensureIndex();\n const endpoint = index.get(endpointIndexKey(query.method, query.path));\n if (!endpoint) return undefined;\n // `params` is always {} in 17.x — the frozen vocabulary defines no path\n // template syntax. See ApiEndpointMatch.params.\n return { endpoint, params: {} };\n }\n\n private async ensureIndex(): Promise<EndpointIndex> {\n if (this.index) return this.index;\n if (this.building) return this.building;\n\n const build = (async () => {\n // Deliberately NOT guarded: a store read failure propagates to the\n // caller so an outage surfaces as an outage, never as a 404.\n const items = await this.deps.listApiItems();\n return buildEndpointIndex(items, this.deps.logger);\n })();\n\n this.building = build;\n try {\n const built = await build;\n // Only publish the result if no invalidation raced us mid-build.\n if (this.building === build) {\n this.index = built;\n this.building = undefined;\n }\n return built;\n } catch (error) {\n // A failed build is never cached — the next request retries against a\n // store that may have recovered.\n if (this.building === build) this.building = undefined;\n throw error;\n }\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The stored ENVELOPE / authored BODY split (#5309).\n *\n * ## The defect this exists to remove\n *\n * A metadata *type name* is worn by two different documents in this codebase:\n *\n * - the **authored declaration** — exactly the vocabulary its spec schema\n * declares, written by a human or (ADR-0033) an AI author;\n * - the **stored row** — that declaration plus the metadata layer's own\n * bookkeeping: `packageId`, `state`, `version`, `publishedDefinition`,\n * `publishedAt`, `publishedBy`, written by {@link\n * import('./metadata-manager.js').MetadataManager.register} and\n * `publishPackage`, and read back by `publishPackage`'s own package filter.\n *\n * Those bookkeeping keys are NOT vocabulary. As long as a spec schema strips\n * unknown keys the difference is invisible — and that is precisely the problem:\n * the same permissiveness that lets `packageId` through also lets a `cacheTTL`\n * / `outputMappings` typo through, so an author's policy or projection silently\n * does not ship (ADR-0078). `api` sits on the #4001 campaign's `STILL_STRIP`\n * list for exactly this reason, measured in #5271: closing `ApiEndpointSchema`\n * made every stored row fail with `unrecognized_keys: ['packageId', 'state']`,\n * so the load-time backstop excluded the endpoint (its route answered 404) and\n * the publish gate reported a schema error instead of its ADR-0121 D6 verdict.\n *\n * The fix is NOT to teach a vocabulary two storage keys — that would make the\n * author-facing contract describe the storage layer, the trade Prime Directive\n * #12 refuses. It is to peel the envelope off **before** the body is handed to\n * its schema, which is what this module does.\n *\n * ## The rule, in one sentence\n *\n * A stored row is an envelope around a body: when the row carries a\n * {@link STORED_BODY_KEY} value the body is that value and everything beside\n * it is envelope; otherwise the body is the row minus the declared\n * {@link STORED_ENVELOPE_KEYS}.\n *\n * Neither half is invented here. `metadata ?? row` is already the metadata\n * layer's body-selection rule in three places — `publishPackage`'s\n * `publishedDefinition` snapshot, `getPublished`'s fallback, and\n * `gateApiItemsForPublish` — and {@link STORED_ENVELOPE_KEYS} is the list of\n * keys `MetadataManager` itself writes onto, or filters rows by. This module\n * gives that rule ONE spelling so the two `ApiEndpointSchema` parse sites\n * (`buildEndpointIndex`, `gateApiItemsForPublish`) can never disagree about\n * what the document is.\n *\n * ## What it deliberately does NOT do\n *\n * - **It never mutates the row.** It returns views, so every existing reader\n * of the envelope (`publishPackage`'s `meta?.packageId === packageId`\n * filter, `query`'s `state` / `packageId` filters, `revertPackage`) keeps\n * reading exactly the object it read before.\n * - **It does not re-shape what publish snapshots.** `publishedDefinition`\n * keeps its current bytes (`structuredClone(data.metadata ?? data)`),\n * envelope keys included, because `revertPackage` restores from it. Peeling\n * the snapshot is a stored-format change; this module is the parse\n * boundary only.\n * - **It is not a schema.** It removes keys the storage layer put there; it\n * makes no judgement about the body, which is the schema's job and stays\n * the schema's job.\n *\n * @see packages/spec/src/api/endpoint.zod.ts — the `STILL_STRIP` note this\n * split is the prerequisite for closing (#5384 owns the closing itself).\n */\n\n/**\n * The metadata layer's bookkeeping keys on a stored row.\n *\n * Every entry is written by `MetadataManager` or read by it as row identity —\n * none of them is authorable vocabulary for any metadata type:\n *\n * | key | written by | read by |\n * |---|---|---|\n * | `packageId` | callers / publish envelope | `publishPackage`, `unregisterPackage`, `revertPackage`, `query`, realtime event |\n * | `package` | legacy package stamp | the same three package filters |\n * | `state` | `publishPackage`, `revertPackage` | `query`, the dependency-published check |\n * | `version` | `publishPackage` | `publishPackage` (next version) |\n * | `publishedDefinition` | `publishPackage` | `revertPackage`, `getPublished`, the dependency-published check |\n * | `publishedAt` / `publishedBy` | `publishPackage` | publish bookkeeping |\n *\n * Adding a key here is a real decision: it removes that key from every body a\n * peeled parse sees. Add one only when `MetadataManager` itself writes or\n * filters on it.\n */\nexport const STORED_ENVELOPE_KEYS = Object.freeze([\n 'package',\n 'packageId',\n 'publishedAt',\n 'publishedBy',\n 'publishedDefinition',\n 'state',\n 'version',\n] as const);\n\n/**\n * The key a stored row uses to carry its authored body — the publish envelope\n * shape `{ name, packageId, state, metadata: {…authored} }`.\n */\nexport const STORED_BODY_KEY = 'metadata';\n\nconst ENVELOPE_KEYS = new Set<string>([...STORED_ENVELOPE_KEYS, STORED_BODY_KEY]);\n\nconst EMPTY_ENVELOPE: Readonly<Record<string, unknown>> = Object.freeze({});\n\n/** A stored row split into the layer's bookkeeping and the authored document. */\nexport interface PeeledStoredItem {\n /**\n * The bookkeeping the metadata layer wrote around the body. Frozen, and a\n * VIEW — the stored row itself is untouched.\n */\n readonly envelope: Readonly<Record<string, unknown>>;\n /**\n * The authored document, ready for its spec schema. `unknown` on purpose:\n * peeling an envelope says nothing about whether the body is valid.\n */\n readonly body: unknown;\n /** `true` when the row carried its body under {@link STORED_BODY_KEY}. */\n readonly wrapped: boolean;\n}\n\n/**\n * Split a stored metadata row into its envelope and its authored body.\n *\n * Total by construction — a non-object row (or `undefined`) is handed back as\n * the body with an empty envelope, so the caller's schema reports the real\n * problem instead of this function inventing one.\n *\n * @param item a value read out of the registry or a loader.\n */\nexport function peelStoredEnvelope(item: unknown): PeeledStoredItem {\n if (item === null || typeof item !== 'object' || Array.isArray(item)) {\n return { envelope: EMPTY_ENVELOPE, body: item, wrapped: false };\n }\n\n const row = item as Record<string, unknown>;\n const wrappedBody = row[STORED_BODY_KEY];\n\n // The publish-envelope shape. Everything beside the body IS envelope by\n // construction, so no key list is consulted — the row said so itself.\n // `!= null` (not `in`) mirrors the `data.metadata ?? data` rule this\n // replaces, so a row with `metadata: null` keeps falling through to the flat\n // branch exactly as it does today.\n if (wrappedBody !== undefined && wrappedBody !== null) {\n const envelope: Record<string, unknown> = {};\n for (const key of Object.keys(row)) {\n if (key === STORED_BODY_KEY) continue;\n envelope[key] = row[key];\n }\n return { envelope: Object.freeze(envelope), body: wrappedBody, wrapped: true };\n }\n\n // The flat shape: body and bookkeeping share one level, so the declared key\n // list is the only thing that can tell them apart.\n let envelope: Record<string, unknown> | undefined;\n for (const key of Object.keys(row)) {\n if (!ENVELOPE_KEYS.has(key)) continue;\n envelope ??= {};\n envelope[key] = row[key];\n }\n\n // An authored declaration carries no bookkeeping at all — hand back the very\n // object that came in, so nothing about an authored parse changes, not even\n // object identity.\n if (!envelope) return { envelope: EMPTY_ENVELOPE, body: row, wrapped: false };\n\n const body: Record<string, unknown> = {};\n for (const key of Object.keys(row)) {\n if (ENVELOPE_KEYS.has(key)) continue;\n body[key] = row[key];\n }\n return { envelope: Object.freeze(envelope), body, wrapped: false };\n}\n\n/**\n * The `name` a stored row is known by, for a diagnostic that must name the\n * offending row even when its body failed to parse.\n *\n * Reads the envelope first and the body second, which is the SAME answer\n * `item.name` gave before the split: on the flat shape `name` lives in the\n * body, on the publish envelope it lives outside `metadata`.\n */\nexport function storedItemName(peeled: PeeledStoredItem): string | undefined {\n const fromEnvelope = peeled.envelope.name;\n if (typeof fromEnvelope === 'string') return fromEnvelope;\n const body = peeled.body;\n if (body && typeof body === 'object' && !Array.isArray(body)) {\n const fromBody = (body as { name?: unknown }).name;\n if (typeof fromBody === 'string') return fromBody;\n }\n return undefined;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { readFile } from 'node:fs/promises';\nimport { createHash } from 'node:crypto';\nimport { Plugin, PluginContext } from '@objectstack/core';\nimport { NodeMetadataManager } from './node-metadata-manager.js';\nimport { MemoryLoader } from './loaders/memory-loader.js';\nimport { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel';\nimport type { MetadataPluginConfig } from '@objectstack/spec/kernel';\nimport { applyProtection } from '@objectstack/spec/shared';\nimport {\n SysMetadataObject,\n SysMetadataHistoryObject,\n SysMetadataCommitObject,\n SysMetadataAuditObject,\n SysViewDefinitionObject,\n} from '@objectstack/metadata-core';\n\n// `SysMetadataObject` + `SysMetadataHistoryObject` are the customer overlay\n// storage substrate (ADR-0005). They must always be auto-provisioned so\n// `PUT /api/v1/meta/{view,dashboard}/...` has a place to write. All other\n// metadata types (object/view/flow/agent/tool/dashboard/app/...) live as\n// JSON inside `sys_metadata` — there are no separate per-type tables. The\n// previously shipped `SysObject` / `SysView` / `SysFlow` / `SysAgent` /\n// `SysTool` projection objects were removed in 2026-05 (see ADR 0005\n// addendum); the projection pipeline was removed at the same time.\n//\n// `SysMetadataAuditObject` (ADR-0010) is the append-only audit trail for\n// metadata write decisions — provisioned alongside the storage tables so\n// `_lock` enforcement always has a place to record decisions, even when\n// the deployment skipped @objectstack/plugin-audit.\n//\n// `SysMetadataCommitObject` (ADR-0067) is the package-scoped commit log that\n// GROUPS a turn's `sys_metadata_history` events (the unit `revertCommit`\n// operates on). It MUST be provisioned alongside its history sibling: every\n// publish/apply writes a commit row, so a per-project (cloud) env kernel that\n// omitted it hit `no such table: sys_metadata_commit` on the first AI build —\n// the write is best-effort so the build still landed, but the error spammed\n// logs and the commit timeline silently recorded nothing. Registered here\n// (not only in the ObjectQLPlugin `environmentId === undefined` standalone\n// path) so env kernels — the only place this table is written — get it.\nconst queryableMetadataObjects = [\n SysMetadataObject,\n SysMetadataHistoryObject,\n // ADR-0067 commit log — sibling of sys_metadata_history (see note above).\n SysMetadataCommitObject,\n SysMetadataAuditObject,\n // Runtime view storage (shared / personal). Must always be provisioned so\n // end-user view creation via the generic data API has a place to write —\n // mirroring why sys_metadata is always provisioned for PUT /meta.\n SysViewDefinitionObject,\n];\n\n// Subdirectory under `rootDir` reserved for the ADR-0008 repository's\n// canonical JSON storage + JSONL change log. Kept separate from user\n// source code (which the legacy FilesystemLoader still scans).\nconst REPO_SUBDIR = '.objectstack/metadata';\n\n// Map from ObjectStackDefinition field name to MetadataType name.\n//\n// PINNED against the schema: `scripts/check-stack-collection-maps.mjs` reconciles\n// this map with `ObjectStackDefinitionSchema` in both directions, and every\n// deviation carries a reason there (#6242). It is one of seven hand-maintained\n// enumerations of the same set, and the reason this one has a gate is its own\n// history — `docs` and `roles → positions` were each fixed here one line at a\n// time, after a missing key silently dropped a whole collection.\nconst ARTIFACT_FIELD_TO_TYPE: Record<string, string> = {\n objects: 'object',\n objectExtensions: 'object_extension',\n apps: 'app',\n views: 'view',\n pages: 'page',\n dashboards: 'dashboard',\n reports: 'report',\n actions: 'action',\n themes: 'theme',\n workflows: 'workflow',\n flows: 'flow',\n // ADR-0090 D3: stacks declare `positions` (stack.zod.ts); the retired\n // `roles: 'role'` mapping matched nothing and silently dropped compiled\n // positions from artifact ingestion.\n positions: 'position',\n permissions: 'permission',\n sharingRules: 'sharing_rule',\n policies: 'policy',\n apis: 'api',\n webhooks: 'webhook',\n agents: 'agent',\n tools: 'tool',\n skills: 'skill',\n ragPipelines: 'rag_pipeline',\n hooks: 'hook',\n mappings: 'mapping',\n analyticsCubes: 'analytics_cube',\n connectors: 'connector',\n emailTemplates: 'email_template',\n docs: 'doc',\n books: 'book',\n // `data:` (the SEED collection) is deliberately absent — #6242 row 4(a).\n // It used to map to `'dataset'`, the ADR-0021 analytics kind: the exact name\n // collision `metadata-plugin.zod.ts` warns about in prose. The entry never\n // registered anything (SeedSchema declares no `name`, and the loop below\n // skips nameless items) — a dead pointer aimed at the wrong kind, which\n // would have begun mis-registering the day either side moved. Removed rather\n // than repointed at `'seed'`: seeds are APPLIED by SeedLoaderService off the\n // bundle, never registered as metadata items, so a `seed` mapping would be\n // new behaviour rather than a corrected name.\n};\n\n// ───────────────────────────────────────────────────────────────────────────\n// View container expansion — \"Object has-many View\" (ADR-0017)\n// ───────────────────────────────────────────────────────────────────────────\n//\n// `defineView({ list, form, listViews, formViews })` aggregates every view of\n// an object into one document. The loader expands such a container into N\n// independent ViewItems (one per named view) registered under\n// `<object>.<viewKey>`, so each view is individually addressable and the\n// runtime switcher can be rebuilt by querying `object`. The original container\n// is ALSO kept under the bare `<object>` key for backward-compatible reads.\n//\n// The implementation lives in `@objectstack/spec` (`isAggregatedViewContainer`\n// / `expandViewContainer`) so this HMR loader and the ObjectQL engine boot loop\n// share ONE canonical expansion and can never drift. Re-exported here for the\n// callers below and for `view-expand.test.ts`.\nexport { isAggregatedViewContainer, expandViewContainer } from '@objectstack/spec';\nimport { isAggregatedViewContainer, expandViewContainer } from '@objectstack/spec';\nimport type { IHttpServer } from '@objectstack/spec/contracts';\n\n\nexport interface MetadataPluginOptions {\n rootDir?: string;\n /**\n * When `true`, NodeMetadataManager scans `rootDir` for source-file metadata\n * (yaml/json/ts/js loaders) AND attaches a chokidar watcher to react to\n * filesystem changes. In **artifact-mode** (this is the normal path when\n * a `defineStack()` config is compiled into `dist/objectstack.json`) this\n * filesystem scan is redundant and expensive — leave `watch: false`.\n *\n * The artifact-file HMR watcher is controlled separately by\n * {@link artifactWatch} so that the cheap, single-file polling watcher\n * can be enabled in dev without paying the cost of scanning the entire\n * project root.\n *\n * Default: `false` (post PR-10e — was previously `true`).\n */\n watch?: boolean;\n /**\n * When `true` AND `artifactSource.mode === 'local-file'`, attach a\n * polling chokidar watcher to the artifact file so the server reloads\n * metadata when the CLI recompiles `dist/objectstack.json` in dev mode.\n * Independent of {@link watch} (which controls the source-file scanner).\n *\n * Default: `true` when `artifactSource` is set, otherwise `false`.\n */\n artifactWatch?: boolean;\n config?: Partial<MetadataPluginConfig>;\n /** Organization ID for metadata-scoped consumers; MetadataPlugin itself does not persist runtime metadata. */\n organizationId?: string;\n /**\n * Environment ID used by local artifact envelopes and metadata-scoped\n * consumers. (The v5.0 rename retired the \"project ID\" wording this\n * comment used to carry; see ADR-0006.)\n */\n environmentId?: string;\n /**\n * When set, MetadataPlugin loads metadata from a compiled artifact instead\n * of scanning the filesystem, honored by all three bootstrap modes\n * (`eager` / `lazy` / `artifact-only`) — see `start()`.\n *\n * `path` is a filesystem path, or an `http(s)://` URL fetched verbatim —\n * the control plane's public artifact route\n * (`/pub/v1/environments/:id/artifact[?commit=…]`) serves exactly such\n * URLs, so a sealed runtime can boot straight off a published revision.\n * Remote reads honor `fetchTimeoutMs` / `OS_ARTIFACT_FETCH_TIMEOUT_MS`;\n * the artifact-file HMR watcher ({@link artifactWatch}) applies to\n * non-URL paths only.\n *\n * `local-file` is the only mode. A second `artifact-api` mode (a\n * Bearer-authenticated control-plane pull) existed here through v17 with\n * zero consumers in any repo — the cloud runtime uses its own\n * `ArtifactApiClient`, and package distribution into a running OSS\n * instance goes through `@objectstack/cloud-connection` — and was removed\n * when #4246 forced the declared-vs-enforced question. `start()` rejects\n * an unknown mode loudly rather than silently scanning the filesystem\n * instead.\n */\n artifactSource?: { mode: 'local-file'; path: string; fetchTimeoutMs?: number };\n /**\n * Register the `sys_metadata` + `sys_metadata_history` storage objects\n * on this kernel. Default `true` for backward compatibility.\n *\n * Set to `false` for **per-project** kernels: in cloud / project mode the\n * control plane is the sole owner of metadata storage tables — exposing\n * them inside each project kernel would leak control-plane schema into\n * business-data namespaces.\n */\n registerSystemObjects?: boolean;\n /**\n * Owning package id for source-file metadata loaded by the filesystem\n * scanner (`watch`/eager mode) — the project's `defineStack({ manifest:\n * { id } })` id. When set, scanned items are stamped with\n * `_packageId`/`_provenance: 'package'` via `applyProtection`, exactly\n * like the artifact path, so GET /meta consumers can tell code-defined\n * metadata from user-authored rows.\n *\n * Leave unset when the host has no package identity — items then stay\n * unstamped (runtime-authored semantics). Do NOT pass a guessed value:\n * `_packageId` feeds `isArtifactBacked()` write authorization, so a\n * wrong id silently changes who may edit these items.\n */\n packageId?: string;\n}\n\nexport class MetadataPlugin implements Plugin {\n name = 'com.objectstack.metadata';\n type = 'standard';\n version = '1.0.0';\n /**\n * Services init() UNCONDITIONALLY registers (ADR-0116, #4131) — lets the\n * kernel name this plugin when a consumer requires `metadata` before it\n * initializes.\n */\n providesServices = ['metadata'];\n /**\n * init() registers the metadata system objects through the `manifest`\n * service ObjectQLPlugin provides — order-if-present so that\n * registration is deterministic instead of \"whichever init ran first\"\n * (ADR-0116, #4471). Soft, not hard: without an engine the plugin\n * degrades on purpose (objects are discovered via the legacy fallback).\n */\n optionalDependencies = ['com.objectstack.engine.objectql'];\n\n private manager: NodeMetadataManager;\n private options: MetadataPluginOptions;\n private repository?: import('@objectstack/metadata-core').MetadataRepository;\n /** Chokidar watcher on the artifact file (local-file mode) — ADR-0008 PR-8. */\n private artifactWatcher?: { close: () => Promise<void> };\n /**\n * The most recently parsed artifact metadata (the plural-field record:\n * `objects`, `views`, `data`, …). Carried on the `metadata:reloaded`\n * payload so runtime consumers can react to collections that never enter\n * the MetadataManager — notably seeds (`data`), whose items have no\n * `name` and are skipped by `_parseAndRegisterArtifact`'s register loop.\n */\n private lastParsedMetadata?: Record<string, unknown[]>;\n\n constructor(options: MetadataPluginOptions = {}) {\n // Documented default: `watch: false` (see {@link MetadataPluginOptions.watch}).\n // Normalized here rather than spelled `{ watch: false, ...options }` because a\n // spread preserves an explicitly-passed `watch: undefined` verbatim, and not\n // every read of this flag routes through the nullish fallback below — the\n // `start()`-time FileSystemRepository `disableWatch` keys on `=== false`. Coercing\n // once here makes the omitted key and an explicit `undefined` resolve identically\n // at EVERY downstream read.\n this.options = {\n ...options,\n watch: options.watch ?? false\n };\n\n const rootDir = this.options.rootDir || process.cwd();\n\n // Sealed-runtime carve-out: `bootstrap: 'artifact-only'` MUST NOT touch\n // the filesystem at all — that includes chokidar subscriptions. Force\n // watch off in that mode regardless of `options.watch`. The other two\n // modes ('eager', 'lazy') honor the user's flag; `lazy` + watch is a\n // valid combination because chokidar attaches to `rootDir` directly,\n // not as a side effect of any priming pass.\n const bootstrapMode = this.options.config?.bootstrap ?? 'eager';\n const effectiveWatch =\n bootstrapMode === 'artifact-only' ? false : (this.options.watch ?? false);\n\n this.manager = new NodeMetadataManager({\n rootDir,\n watch: effectiveWatch,\n formats: ['yaml', 'json', 'typescript', 'javascript']\n });\n\n // Initialize with default type registry\n this.manager.setTypeRegistry(DEFAULT_METADATA_TYPE_REGISTRY);\n }\n\n init = async (ctx: PluginContext) => {\n ctx.logger.info('Initializing Metadata Manager', {\n root: this.options.rootDir || process.cwd(),\n watch: this.options.watch,\n artifactSource: this.options.artifactSource?.mode,\n });\n\n // Register Metadata Manager as the primary metadata service provider.\n ctx.registerService('metadata', this.manager);\n console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);\n\n // Register metadata system objects via the manifest service (if available).\n // MetadataPlugin may init before ObjectQLPlugin, so wrap in try/catch.\n // Skipped when `registerSystemObjects: false` (per-project kernels in\n // cloud / project mode — sys_* live exclusively in the control plane).\n const registerSysObjects = this.options.registerSystemObjects !== false;\n if (registerSysObjects) {\n try {\n const manifestService = ctx.getService<{ register(m: any): void }>('manifest');\n\n // Register the queryable metadata-layer platform objects.\n manifestService.register({\n id: 'com.objectstack.metadata-objects',\n name: 'Metadata Platform Objects',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n defaultDatasource: 'cloud',\n objects: queryableMetadataObjects,\n });\n\n ctx.logger.info('Registered system metadata objects', {\n queryable: queryableMetadataObjects.map((object) => object.name),\n });\n } catch {\n // ObjectQL not loaded yet — objects will be discovered via legacy fallback\n }\n }\n\n ctx.logger.info('MetadataPlugin providing metadata service (primary mode)', {\n mode: this.options.artifactSource?.mode ?? 'file-system',\n features: ['watch', 'multi-format', 'query', 'overlay', 'type-registry']\n });\n }\n\n start = async (ctx: PluginContext) => {\n const src = this.options.artifactSource;\n const mode = this.options.config?.bootstrap ?? 'eager';\n\n ctx.logger.info('[MetadataPlugin] Bootstrapping metadata', {\n bootstrap: mode,\n artifactSource: src?.mode ?? 'none',\n });\n\n // Reject a non-`local-file` source before choosing any load path. The\n // union is single-member so TypeScript callers can't get here, but JS\n // callers and config plumbed through `any` can — and the fall-through\n // below would otherwise treat \"unsupported source\" as \"no source\"\n // (eager would silently scan the filesystem instead of loading the\n // artifact the caller named). The removed `artifact-api` mode gets a\n // pointed migration message (#4246).\n if (src && (src as { mode: string }).mode !== 'local-file') {\n const bad = (src as { mode: string }).mode;\n throw new Error(\n `[MetadataPlugin] artifactSource.mode '${bad}' is not supported`\n + (bad === 'artifact-api'\n ? \" — the 'artifact-api' source was removed (#4246). Load the same artifact with\"\n + \" { mode: 'local-file', path: '<http(s) URL>' } (e.g. the control plane's\"\n + \" /pub/v1/environments/:id/artifact route), or install packages into a running\"\n + ' runtime via @objectstack/cloud-connection.'\n : \". The only artifact source is { mode: 'local-file', path }.\"),\n );\n }\n\n if (mode === 'artifact-only') {\n // Sealed-runtime mode: ONLY load from a pre-compiled artifact. Never\n // touch the filesystem. Required for Edge / serverless / read-only\n // production deployments where the running process must not depend\n // on local source files.\n if (src) {\n await this._loadFromLocalFile(ctx, src.path, src.fetchTimeoutMs);\n } else {\n throw new Error('[MetadataPlugin] bootstrap=artifact-only requires options.artifactSource to be set');\n }\n } else if (mode === 'lazy') {\n // On-demand mode: skip the eager filesystem priming pass entirely.\n // Reads go through MetadataManager.load*/list* which are backed by\n // the DatabaseLoader read-through cache and any registered loaders.\n // An artifact source, if present, is still honored so projects can\n // pin a known set of metadata at boot without paying the FS scan.\n if (src) {\n await this._loadFromLocalFile(ctx, src.path, src.fetchTimeoutMs, { optional: true });\n } else {\n ctx.logger.info('[MetadataPlugin] lazy bootstrap — skipping filesystem priming; metadata loads on demand');\n }\n } else {\n // 'eager' (default): preserve historical behavior.\n if (src) {\n await this._loadFromLocalFile(ctx, src.path, src.fetchTimeoutMs, { optional: true });\n } else {\n await this._loadFromFileSystem(ctx);\n }\n }\n\n // ── ADR-0008 PR-6: attach FileSystemRepository as a supplementary\n // event source so PR-7 (ObjectQL SchemaRegistry), PR-9 (Studio\n // SSE hook) and future cloud consumers can subscribe to the\n // same canonical event stream. No write mirroring yet.\n const bootstrapMode = this.options.config?.bootstrap ?? 'eager';\n if (bootstrapMode !== 'artifact-only') {\n try {\n const path = await import('node:path');\n const { FileSystemRepository } = await import('@objectstack/metadata-fs');\n const rootDir = this.options.rootDir || process.cwd();\n const repoRoot = path.join(rootDir, REPO_SUBDIR);\n const repo = new FileSystemRepository({\n root: repoRoot,\n org: this.options.organizationId ?? 'system',\n disableWatch: this.options.watch === false,\n });\n await repo.start();\n this.repository = repo;\n this.manager.setRepository(repo);\n ctx.logger.info('[MetadataPlugin] FileSystemRepository attached', {\n repoRoot,\n watch: this.options.watch !== false,\n });\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] Failed to attach FileSystemRepository', {\n error: e?.message,\n });\n }\n }\n\n // Bridge realtime service from kernel service registry to MetadataManager.\n try {\n const realtimeService = ctx.getService('realtime');\n if (realtimeService && typeof realtimeService === 'object' && 'publish' in realtimeService) {\n ctx.logger.info('[MetadataPlugin] Bridging realtime service to MetadataManager for event publishing');\n this.manager.setRealtimeService(realtimeService as any);\n }\n } catch (e: any) {\n ctx.logger.debug('[MetadataPlugin] No realtime service found — metadata events will not be published', {\n error: e.message,\n });\n }\n\n // Register the HMR SSE endpoint when an HTTP server is available\n // that exposes a raw Hono app. The endpoint is registered regardless\n // of the `watch` option:\n // - In `watch: true` mode the FS watcher feeds events into the hub.\n // - In `watch: false` mode (e.g. artifact-mode `os dev`), an\n // external watch-recompile pipeline POSTs to the same endpoint\n // after rebuilding the artifact, and we reload it here before\n // broadcasting.\n // Production deployments simply won't have a CLI POSTing to this\n // endpoint and won't surface the route to clients.\n try {\n // [#4251] Both names are the SAME instance; `http.server` is the\n // canonical one (the only name every provider registers), read\n // first. Per-name try — `getService` THROWS for an empty slot, so\n // a single try around `canonical ?? alias` never reaches the\n // alias: exactly the shape this read had before (alias-first),\n // whose fallback therefore never once fired. `getRawApp?()` is on\n // the contract now — the deliberate framework-handle escape,\n // declared once there instead of per consumer. The alias fallback\n // dies with the alias registrations.\n const readServer = (name: string): IHttpServer | undefined => {\n try { return ctx.getService<IHttpServer>(name); } catch { return undefined; }\n };\n const httpServer = readServer('http.server') ?? readServer('http-server');\n if (httpServer && typeof httpServer.getRawApp === 'function') {\n const { registerMetadataHmrRoutes } = await import('./routes/hmr-routes.js');\n const hub = registerMetadataHmrRoutes(httpServer.getRawApp(), this.manager);\n // Wire POST → re-load the artifact from disk (when in\n // local-file artifact mode) so subsequent reads see fresh\n // metadata. The broadcast happens after the handler returns.\n hub.setOnPostReload(async (body: { reason?: string; changed?: string[] } = {}) => {\n const src = this.options.artifactSource;\n if (src?.mode === 'local-file') {\n try {\n await this._reloadAndAnnounce(ctx, src, body?.changed ?? [src.path]);\n ctx.logger.info('[MetadataPlugin] artifact reloaded via HMR POST', {\n path: src.path,\n reason: body?.reason,\n });\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] artifact reload failed', { error: e?.message });\n throw e;\n }\n }\n });\n\n // ── ADR-0008 PR-8 / PR-10e: server-side artifact-file watcher ──\n //\n // When running in local-file artifact mode (e.g. `os dev`\n // serving from `dist/objectstack.json`), watch the\n // artifact path directly so the server reloads on\n // recompile WITHOUT requiring the CLI to ping the HMR\n // POST endpoint. The POST route stays available for\n // external trigger sources (cloud webhook, git hook,\n // ad-hoc curl) but is no longer the only signal.\n //\n // Gated on `artifactWatch` (NOT `watch` — the latter\n // controls the source-file scanner which is redundant in\n // artifact mode). Default: on when artifactSource is\n // present, off otherwise.\n const src = this.options.artifactSource;\n const wantArtifactWatch = this.options.artifactWatch\n ?? (src?.mode === 'local-file');\n if (src?.mode === 'local-file' && wantArtifactWatch && !/^https?:\\/\\//i.test(src.path)) {\n try {\n const { watch: chokidarWatch } = await import('chokidar');\n const w = chokidarWatch(src.path, {\n ignoreInitial: true,\n awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 },\n persistent: true,\n // Use polling to avoid `fs.watch` exhausting the\n // process file-descriptor limit on macOS (chokidar\n // recursively wires watches on the parent\n // directory tree which can trip EMFILE on busy\n // dev hosts). 500ms polling is fast enough for\n // HMR (a recompile takes ~400ms anyway).\n usePolling: true,\n interval: 500,\n binaryInterval: 1000,\n });\n let pending = false;\n const reload = async () => {\n if (pending) return;\n pending = true;\n try {\n await this._reloadAndAnnounce(ctx, src, [src.path]);\n hub.broadcastReload('artifact-file-changed', [src.path]);\n ctx.logger.info('[MetadataPlugin] artifact auto-reloaded (file watcher)', {\n path: src.path,\n });\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] artifact auto-reload failed', { error: e?.message });\n } finally {\n pending = false;\n }\n };\n w.on('change', () => { void reload(); });\n w.on('add', () => { void reload(); });\n this.artifactWatcher = { close: () => w.close() };\n // eslint-disable-next-line no-console\n console.log('[MetadataPlugin] artifact file watcher attached', src.path);\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] artifact watcher failed to start', { error: e?.message });\n }\n }\n // eslint-disable-next-line no-console\n console.log('[MetadataPlugin] HMR endpoint registered at /api/v1/dev/metadata-events');\n } else {\n // eslint-disable-next-line no-console\n console.log('[MetadataPlugin] HTTP server with getRawApp() not available — skipping HMR endpoint');\n }\n } catch (e: any) {\n // eslint-disable-next-line no-console\n console.warn('[MetadataPlugin] Failed to register HMR endpoint', e?.message);\n }\n }\n\n stop = async (ctx: PluginContext) => {\n if (this.artifactWatcher) {\n try { await this.artifactWatcher.close(); } catch { /* noop */ }\n this.artifactWatcher = undefined;\n }\n try {\n await this.manager.dispose();\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] manager.dispose() failed', { error: e?.message });\n }\n const repo = this.repository as any;\n if (repo && typeof repo.close === 'function') {\n try { await repo.close(); } catch { /* noop */ }\n }\n this.repository = undefined;\n }\n\n /**\n * Fetch JSON content from a URL with configurable timeout.\n */\n private async _fetchJson(url: string, fetchTimeoutMs?: number): Promise<unknown> {\n const envTimeout = Number(process.env.OS_ARTIFACT_FETCH_TIMEOUT_MS);\n const timeoutMs = fetchTimeoutMs\n ?? (Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : undefined)\n ?? 60_000;\n const controller = new AbortController();\n const timer = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n try {\n const headers: Record<string, string> = { Accept: 'application/json, */*;q=0.5' };\n const res = await fetch(url, { redirect: 'follow', signal: controller.signal, headers });\n if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);\n const content = await res.text();\n return JSON.parse(content);\n } catch (e: any) {\n if (e?.name === 'AbortError') {\n throw new Error(\n `fetch timed out after ${timeoutMs}ms — set artifactSource.fetchTimeoutMs or OS_ARTIFACT_FETCH_TIMEOUT_MS to extend it (0 disables)`,\n );\n }\n throw e;\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n\n /**\n * Parse raw artifact JSON (envelope or bare definition) and register all\n * metadata items into the MetadataManager.\n *\n * Registers with `{ notify: false }` — one announcement per artifact, not\n * one per item. Both callers cover the whole set already: the boot load\n * runs before consumers have cached anything, and the reload path\n * (`_reloadAndAnnounce`) fires `metadata:reloaded` carrying the parsed\n * artifact once the ingest is complete. Announcing here too would emit N\n * duplicate events per reload, each racing the batch that is still\n * landing.\n */\n private async _parseAndRegisterArtifact(ctx: PluginContext, raw: unknown, label: string): Promise<number> {\n const { EnvironmentArtifactSchema } = await import('@objectstack/spec/cloud');\n const { ObjectStackDefinitionSchema } = await import('@objectstack/spec');\n\n let metadata: Record<string, unknown[]>;\n\n const obj = raw as any;\n if (obj?.schemaVersion && obj?.commitId && obj?.metadata !== undefined) {\n const artifact = EnvironmentArtifactSchema.parse(obj);\n metadata = artifact.metadata as Record<string, unknown[]>;\n } else if (obj?.success && obj?.data?.metadata) {\n // Unwrap cloud API envelope: { success: true, data: { metadata: {...} } }\n const artifact = EnvironmentArtifactSchema.parse(obj.data);\n metadata = artifact.metadata as Record<string, unknown[]>;\n } else {\n const def = ObjectStackDefinitionSchema.parse(obj);\n const canonical = JSON.stringify(def, Object.keys(def).sort());\n const checksum = createHash('sha256').update(canonical).digest('hex');\n const environmentId = this.options.environmentId ?? 'proj_local';\n EnvironmentArtifactSchema.parse({\n schemaVersion: '0.1',\n environmentId,\n commitId: 'local-dev',\n checksum,\n metadata: def,\n });\n metadata = def as Record<string, unknown[]>;\n }\n\n this.lastParsedMetadata = metadata;\n\n const memLoader = new MemoryLoader();\n const manifestPackageId =\n (metadata as any)?.manifest?.id ?? (metadata as any)?.id ?? undefined;\n const manifestVersion =\n (metadata as any)?.manifest?.version ?? (metadata as any)?.version ?? undefined;\n\n let totalRegistered = 0;\n for (const [field, metaType] of Object.entries(ARTIFACT_FIELD_TO_TYPE)) {\n const items = (metadata as any)[field];\n if (!Array.isArray(items) || items.length === 0) continue;\n for (const item of items) {\n // Expand aggregated view containers into independent ViewItems\n // (\"Object has-many View\"), while still registering the\n // container under the bare <object> key for backward-compatible\n // reads. Already-independent ViewItems carry a top-level `name`\n // and fall through to the normal path below.\n if (metaType === 'view' && isAggregatedViewContainer(item)) {\n const viewObject =\n (item as any)?.list?.data?.object\n ?? (item as any)?.form?.data?.object;\n if (!viewObject) continue;\n applyProtection(item as any, {\n packageId: manifestPackageId,\n packageVersion: manifestVersion,\n });\n await memLoader.save('view', viewObject, item);\n await this.manager.register('view', viewObject, item, { notify: false });\n totalRegistered++;\n for (const vi of expandViewContainer(viewObject, item)) {\n for (const w of vi._diagnostics?.warnings ?? []) {\n ctx.logger.warn(`[MetadataPlugin] View expansion warning for '${vi.name}': ${w.message}`);\n }\n applyProtection(vi as any, {\n packageId: manifestPackageId,\n packageVersion: manifestVersion,\n });\n await memLoader.save('view', vi.name, vi);\n await this.manager.register('view', vi.name, vi, { notify: false });\n totalRegistered++;\n }\n continue;\n }\n // Most metadata items carry a top-level `name`. The `View`\n // container (UI namespace) is an exception: it has no own\n // `name` — its identity is the target object, encoded under\n // `list.data.object` (or `form.data.object`). Mirror the\n // resolution used by `ObjectQL.SchemaRegistry` so that\n // artifact-loaded views land in `MetadataManager` under the\n // SAME key reads expect (`metadataService.get('view', <object>)`).\n // Without this, HMR pushes views into the registry only via\n // AppPlugin's `manifest.register`, which targets the\n // boot-only SchemaRegistry cache and is never refreshed on\n // file edits — leaving MetadataService empty for view/* and\n // forcing all reads to return the stale boot copy.\n let name = (item as any)?.name;\n if (!name) {\n if (metaType === 'view') {\n name =\n (item as any)?.list?.data?.object\n ?? (item as any)?.form?.data?.object;\n }\n }\n if (!name) continue;\n // ADR-0010 §3.7 — translate the author-facing\n // `protection` block into the private `_lock` envelope\n // and stamp package provenance in one call. Strips the\n // public block so it never lands in sys_metadata.\n applyProtection(item as any, {\n packageId: manifestPackageId,\n packageVersion: manifestVersion,\n });\n await memLoader.save(metaType, name, item);\n await this.manager.register(metaType, name, item, { notify: false });\n totalRegistered++;\n }\n }\n\n this.manager.registerLoader(memLoader);\n ctx.logger.info('[MetadataPlugin] Artifact metadata loaded', { source: label, totalRegistered });\n return totalRegistered;\n }\n\n /**\n * Reload the artifact from disk into the MetadataManager, then announce a\n * generic `metadata:reloaded` hook. Used by BOTH reload paths (the HMR POST\n * handler and the server-side artifact-file watcher) — but NOT the initial\n * boot load, which other plugins already consume directly.\n *\n * Runtime consumers that cached boot-time metadata re-sync on this signal.\n * The automation engine subscribes to re-bind flow triggers it pulled ONCE\n * at boot — notably scheduled jobs: without this, an edited\n * schedule-triggered flow keeps firing its pre-edit definition (old runAs /\n * schedule / logic) until a full process restart. A subscriber failure is\n * logged but never blocks the reload.\n */\n private async _reloadAndAnnounce(\n ctx: PluginContext,\n src: { path: string; fetchTimeoutMs?: number },\n changed: string[],\n ): Promise<void> {\n // Optional for the same reason boot is: an artifact deleted or moved\n // aside mid-run (a `dist/` clean between recompiles) must not take the\n // running server down — the watcher reloads it when it comes back.\n await this._loadFromLocalFile(ctx, src.path, src.fetchTimeoutMs, { optional: true });\n try {\n // `metadata` carries the freshly parsed artifact collections so\n // subscribers can consume the ones that never reach the\n // MetadataManager (seeds under `data` have no `name`). AppPlugin\n // uses it to load seeds for objects that appear mid-run.\n await ctx.trigger('metadata:reloaded', { changed, metadata: this.lastParsedMetadata });\n } catch (e: any) {\n ctx.logger.warn('[MetadataPlugin] metadata:reloaded subscriber failed', { error: e?.message });\n }\n }\n\n /**\n * @param opts.optional When true, a LOCAL artifact file that does not exist\n * is \"nothing compiled yet\" rather than a fault: log and return, leaving\n * the manager empty and the artifact watcher armed so the first\n * `os compile` hydrates the running server (#4085). Callers pass it for\n * the `eager` / `lazy` bootstrap modes — the development-platform paths,\n * where an app is optional. `artifact-only` (sealed runtime) does NOT:\n * there the artifact IS the deployment, so its absence must fail loudly\n * instead of silently serving an empty runtime. Only ENOENT is tolerated;\n * a present-but-unreadable artifact (malformed JSON, bad permissions) and\n * every remote-URL failure stay fatal.\n */\n private async _loadFromLocalFile(\n ctx: PluginContext,\n filePath: string,\n fetchTimeoutMs?: number,\n opts: { optional?: boolean } = {},\n ): Promise<void> {\n const isUrl = /^https?:\\/\\//i.test(filePath);\n ctx.logger.info(\n `[MetadataPlugin] Loading metadata from ${isUrl ? 'remote URL' : 'local artifact file'}`,\n { path: filePath },\n );\n\n let raw: unknown;\n try {\n if (isUrl) {\n raw = await this._fetchJson(filePath, fetchTimeoutMs);\n } else {\n const content = await readFile(filePath, 'utf8');\n raw = JSON.parse(content);\n }\n } catch (e: any) {\n if (opts.optional && !isUrl && e?.code === 'ENOENT') {\n ctx.logger.info(\n '[MetadataPlugin] no compiled artifact yet — starting with no artifact metadata',\n { path: filePath },\n );\n return;\n }\n throw new Error(`[MetadataPlugin] Cannot read artifact ${isUrl ? 'URL' : 'file'} at \"${filePath}\": ${e.message}`);\n }\n\n await this._parseAndRegisterArtifact(ctx, raw, filePath);\n }\n\n private async _loadFromFileSystem(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Loading metadata from file system...');\n\n const sortedTypes = [...DEFAULT_METADATA_TYPE_REGISTRY]\n .sort((a, b) => a.loadOrder - b.loadOrder);\n\n let totalLoaded = 0;\n for (const entry of sortedTypes) {\n try {\n const items = await this.manager.loadMany(entry.type, {\n recursive: true,\n patterns: entry.filePatterns,\n });\n\n if (items.length > 0) {\n for (const item of items) {\n const meta = item as any;\n if (meta?.name) {\n // Stamp package provenance when the host declared\n // its package id (see MetadataPluginOptions.packageId)\n // — same applyProtection call the artifact path\n // uses, so both load paths produce identical\n // _packageId/_provenance state. No-op when the\n // option is unset or the item is already stamped.\n applyProtection(meta, {\n packageId: this.options.packageId,\n });\n // Silent: boot-time priming, before any consumer\n // has cached a definition to go stale. Post-boot\n // edits to these files reach watchers through the\n // FileSystemRepository attached by onEnable.\n await this.manager.register(entry.type, meta.name, item, { notify: false });\n }\n }\n ctx.logger.info(`Loaded ${items.length} ${entry.type} from file system`);\n totalLoaded += items.length;\n }\n } catch (e: any) {\n ctx.logger.debug(`No ${entry.type} metadata found`, { error: e.message });\n }\n }\n\n ctx.logger.info('Metadata loading complete', {\n totalItems: totalLoaded,\n registeredTypes: sortedTypes.length,\n });\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Node Metadata Manager\n * \n * Extends MetadataManager with Filesystem capabilities (Watching, default loader)\n */\n\nimport * as path from 'node:path';\nimport { watch as chokidarWatch, type FSWatcher } from 'chokidar';\nimport type {\n MetadataWatchEvent,\n} from '@objectstack/spec/system';\nimport { FilesystemLoader } from './loaders/filesystem-loader.js';\nimport { MetadataManager, type MetadataManagerOptions } from './metadata-manager.js';\n\n/**\n * Node metadata manager class\n */\nexport class NodeMetadataManager extends MetadataManager {\n private watcher?: FSWatcher;\n\n constructor(config: MetadataManagerOptions) {\n super(config);\n\n // Initialize Default Filesystem Loader if no loaders provided\n // This logic replaces the removed logic from base class\n if (!config.loaders || config.loaders.length === 0) {\n const rootDir = config.rootDir || process.cwd();\n this.registerLoader(new FilesystemLoader(rootDir, this.serializers, this.logger));\n }\n\n // Start watching if enabled\n if (config.watch) {\n this.startWatching();\n }\n }\n\n /**\n * Stop all watching\n */\n async stopWatching(): Promise<void> {\n if (this.watcher) {\n await this.watcher.close();\n this.watcher = undefined;\n }\n // Call base cleanup if any\n }\n\n /**\n * Start watching for file changes\n */\n private startWatching(): void {\n const rootDir = this.config.rootDir || process.cwd();\n const { ignored = ['**/node_modules/**', '**/*.test.*'], persistent = true } =\n this.config.watchOptions || {};\n\n this.watcher = chokidarWatch(rootDir, {\n ignored,\n persistent,\n ignoreInitial: true,\n // Use polling to avoid `fs.watch` EMFILE on macOS / busy dev hosts.\n // Recursive watch over a project root would otherwise wire native\n // watches across the entire tree, easily exhausting the FD pool.\n usePolling: true,\n interval: 1000,\n binaryInterval: 2000,\n });\n\n this.watcher.on('add', async (filePath) => {\n await this.handleFileEvent('added', filePath);\n });\n\n this.watcher.on('change', async (filePath) => {\n await this.handleFileEvent('changed', filePath);\n });\n\n this.watcher.on('unlink', async (filePath) => {\n await this.handleFileEvent('deleted', filePath);\n });\n\n this.logger.info('File watcher started', { rootDir });\n }\n\n /**\n * Handle file change events\n */\n private async handleFileEvent(\n eventType: 'added' | 'changed' | 'deleted',\n filePath: string\n ): Promise<void> {\n const rootDir = this.config.rootDir || process.cwd();\n const relativePath = path.relative(rootDir, filePath);\n const parts = relativePath.split(path.sep);\n\n if (parts.length < 2) {\n return; // Not a metadata file\n }\n\n const type = parts[0];\n const fileName = parts[parts.length - 1];\n const name = path.basename(fileName, path.extname(fileName));\n\n // [#5218] Invalidate BEFORE announcing — and, since #5228, before reading\n // too, so that the read's verdict can never decide whether the caches are\n // dropped. A file event is a *foreign write* in the precise sense\n // {@link MetadataManager.invalidateForForeignWrite} means: it did not come\n // through this manager's write API, so — unlike `register()` /\n // `unregister()` — nothing has refreshed the caches on its behalf. The\n // read below is pure (it only walks the loaders and writes neither cache),\n // so before this call the handler left both `listCache` and `registry`\n // holding the pre-change state.\n //\n // Without it, editing `rootDir/view/x.json` left the two read surfaces\n // contradicting each other for up to LIST_CACHE_TTL_MS (30s): `get()` saw\n // the new file because it falls through to the FilesystemLoader, while\n // `list()` — REST `/api/v1/metadata/:type`, the Studio left rail,\n // `listViews()` — kept serving the pre-change set. Worse, the HMR/SSE\n // consumers woken by the event answer it by re-reading through `list()`,\n // so the wake-up handed back exactly the stale data it was announcing.\n // Same defect shape as #5109 (cluster peer) with a different trigger; this\n // reuses that fix's helper rather than re-deriving it.\n //\n // Ordering is the same discipline every other write path in the base class\n // keeps (`register` / `unregister` / `applyRepoEvent` / the cluster\n // subscriber all invalidate, then announce): a watcher must never be able\n // to observe the event and the pre-event cache at the same time.\n //\n // The registry entry goes too, not just the list cache. FS-loaded items\n // never enter the registry, so there is usually nothing to delete — but\n // when a same-named entry was previously written by `register()` /\n // `registerInMemory()` it SHADOWS the loader in both `get()` and `list()`,\n // and dropping the list cache alone would leave that stale copy answering\n // forever. Deleted, never pre-filled from `data`, per the helper's contract.\n this.invalidateForForeignWrite(type, name);\n\n // [#5228] `loadDiagnosed`, not `load` — and the difference is the whole\n // point of this branch. `load()` is `(await loadDiagnosed(...)).data`, and\n // `loadDiagnosed` (ADR-0110 D3) ABSORBS a loader throw: it records the\n // message in `errors[]` and answers `{ data: null, degraded: true }`.\n // `FilesystemLoader.load()` does throw on an unreadable / unparseable\n // file, but that throw dies inside `loadDiagnosed`, so the `try/catch`\n // this handler used to wrap `load()` in was unreachable for exactly the\n // failure it was written to catch. The handler announced `data: null`\n // instead, and its `logger.error` never printed once.\n //\n // `data: null` is the wire-shape of \"this metadata legitimately holds\n // nothing\" — so a file the loader could not read was announced as a file\n // the author had emptied. Those are the two facts ADR-0110 D3 exists to\n // keep apart (a miss and an outage mean opposite things), and this call\n // site was using the variant that throws the distinction away.\n //\n // So: split on `degraded`. An outage takes the road the dead `catch` meant\n // to take — log loudly, announce nothing. A clean miss (`data: null`, no\n // loader threw: the file is gone or legitimately empty) keeps its existing\n // semantics and is announced as before.\n //\n // Note what deliberately does NOT move with the early return: the\n // invalidation above. An unreadable file is still a real change to the\n // stored set — `loadMany` skips it, so `list()` genuinely answers\n // differently than it did — and #5218's contract is that a file event\n // always ages out the caches. That is also what keeps the `api` endpoint\n // index correct on this path without a broadcast: `invalidateListCache`\n // is the index's first invalidation seam (#5089), so suppressing the\n // `subscribe('api', …)` seam costs nothing.\n let data: unknown = undefined;\n if (eventType !== 'deleted') {\n const read = await this.loadDiagnosed(type, name, { useCache: false });\n if (read.degraded) {\n this.logger.error('Failed to load changed file', undefined, {\n filePath,\n metadataType: type,\n name,\n errors: read.errors,\n });\n return;\n }\n data = read.data;\n }\n\n const event: MetadataWatchEvent = {\n type: eventType,\n metadataType: type,\n name,\n path: filePath,\n data,\n timestamp: new Date().toISOString(),\n };\n\n this.notifyWatchers(type, event);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Filesystem Metadata Loader\n * \n * Loads metadata from the filesystem using glob patterns\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { glob } from 'glob';\nimport { createHash } from 'node:crypto';\nimport type {\n MetadataLoadOptions,\n MetadataLoadResult,\n MetadataStats,\n MetadataLoaderContract,\n MetadataFormat,\n MetadataSaveOptions,\n MetadataSaveResult,\n} from '@objectstack/spec/system';\nimport type { Logger } from '@objectstack/core';\nimport type { MetadataLoader } from './loader-interface.js';\nimport type { MetadataSerializer } from '../serializers/serializer-interface.js';\n\nexport class FilesystemLoader implements MetadataLoader {\n readonly contract: MetadataLoaderContract = {\n name: 'filesystem',\n protocol: 'file:',\n capabilities: {\n read: true,\n write: true,\n watch: true,\n list: true,\n },\n supportedFormats: ['json', 'yaml', 'typescript', 'javascript'],\n supportsWatch: true,\n supportsWrite: true,\n supportsCache: true,\n };\n\n private cache = new Map<string, { data: any; etag: string; timestamp: number }>();\n\n constructor(\n private rootDir: string,\n private serializers: Map<MetadataFormat, MetadataSerializer>,\n private logger?: Logger\n ) {}\n\n async load(\n type: string,\n name: string,\n options?: MetadataLoadOptions\n ): Promise<MetadataLoadResult> {\n const startTime = Date.now();\n const { validate: _validate = true, useCache = true, ifNoneMatch } = options || {};\n\n try {\n // Find the file\n const filePath = await this.findFile(type, name);\n\n if (!filePath) {\n return {\n data: null,\n fromCache: false,\n notModified: false,\n loadTime: Date.now() - startTime,\n };\n }\n\n // Get stats\n const stats = await this.stat(type, name);\n\n if (!stats) {\n return {\n data: null,\n fromCache: false,\n notModified: false,\n loadTime: Date.now() - startTime,\n };\n }\n\n // Check cache\n if (useCache && ifNoneMatch && stats.etag === ifNoneMatch) {\n return {\n data: null,\n fromCache: true,\n notModified: true,\n etag: stats.etag,\n stats,\n loadTime: Date.now() - startTime,\n };\n }\n\n // Check memory cache\n const cacheKey = `${type}:${name}`;\n if (useCache && this.cache.has(cacheKey)) {\n const cached = this.cache.get(cacheKey)!;\n if (cached.etag === stats.etag) {\n return {\n data: cached.data,\n fromCache: true,\n notModified: false,\n etag: stats.etag,\n stats,\n loadTime: Date.now() - startTime,\n };\n }\n }\n\n // Load and deserialize\n const content = await fs.readFile(filePath, 'utf-8');\n const serializer = this.getSerializer(stats.format!);\n\n if (!serializer) {\n throw new Error(`No serializer found for format: ${stats.format}`);\n }\n\n const data = serializer.deserialize(content);\n\n // Update cache\n if (useCache) {\n this.cache.set(cacheKey, {\n data,\n etag: stats.etag || '',\n timestamp: Date.now(),\n });\n }\n\n return {\n data,\n fromCache: false,\n notModified: false,\n etag: stats.etag,\n stats,\n loadTime: Date.now() - startTime,\n };\n } catch (error) {\n this.logger?.error('Failed to load metadata', undefined, {\n type,\n name,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n async loadMany<T = any>(\n type: string,\n options?: MetadataLoadOptions\n ): Promise<T[]> {\n const { patterns = ['**/*'], recursive: _recursive = true, limit } = options || {};\n\n const typeDir = path.join(this.rootDir, type);\n const items: T[] = [];\n\n try {\n // Build glob patterns\n const globPatterns = patterns.map(pattern =>\n path.join(typeDir, pattern)\n );\n\n for (const pattern of globPatterns) {\n const files = await glob(pattern, {\n ignore: ['**/node_modules/**', '**/*.test.*', '**/*.spec.*', '**/*[*]*'],\n nodir: true,\n });\n\n for (const file of files) {\n if (limit && items.length >= limit) {\n break;\n }\n\n try {\n const content = await fs.readFile(file, 'utf-8');\n const format = this.detectFormat(file);\n const serializer = this.getSerializer(format);\n\n if (serializer) {\n const data = serializer.deserialize<T>(content);\n items.push(data);\n }\n } catch (error) {\n this.logger?.warn('Failed to load file', {\n file,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n if (limit && items.length >= limit) {\n break;\n }\n }\n\n return items;\n } catch (error) {\n this.logger?.error('Failed to load many', undefined, {\n type,\n patterns,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n async exists(type: string, name: string): Promise<boolean> {\n const filePath = await this.findFile(type, name);\n return filePath !== null;\n }\n\n async stat(type: string, name: string): Promise<MetadataStats | null> {\n const filePath = await this.findFile(type, name);\n\n if (!filePath) {\n return null;\n }\n\n try {\n const stats = await fs.stat(filePath);\n const content = await fs.readFile(filePath, 'utf-8');\n const etag = this.generateETag(content);\n const format = this.detectFormat(filePath);\n\n return {\n size: stats.size,\n modifiedAt: stats.mtime.toISOString(),\n etag,\n format,\n path: filePath,\n };\n } catch (error) {\n this.logger?.error('Failed to stat file', undefined, {\n type,\n name,\n filePath,\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n }\n\n async list(type: string): Promise<string[]> {\n const typeDir = path.join(this.rootDir, type);\n\n try {\n const files = await glob('**/*', {\n cwd: typeDir,\n ignore: ['**/node_modules/**', '**/*.test.*', '**/*.spec.*'],\n nodir: true,\n });\n\n return files.map(file => {\n const ext = path.extname(file);\n const basename = path.basename(file, ext);\n return basename;\n });\n } catch (error) {\n this.logger?.error('Failed to list', undefined, {\n type,\n error: error instanceof Error ? error.message : String(error),\n });\n return [];\n }\n }\n\n async save(\n type: string,\n name: string,\n data: any,\n options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n const startTime = Date.now();\n const {\n format = 'typescript',\n prettify = true,\n indent = 2,\n sortKeys = false,\n backup = false,\n overwrite = true,\n atomic = true,\n path: customPath,\n } = options || {};\n\n try {\n // Get serializer\n const serializer = this.getSerializer(format);\n if (!serializer) {\n throw new Error(`No serializer found for format: ${format}`);\n }\n\n // Determine file path\n const typeDir = path.join(this.rootDir, type);\n const fileName = `${name}${serializer.getExtension()}`;\n const filePath = customPath || path.join(typeDir, fileName);\n\n // Check if file exists\n if (!overwrite) {\n try {\n await fs.access(filePath);\n throw new Error(`File already exists: ${filePath}`);\n } catch (error) {\n // File doesn't exist, continue\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n // Create directory if it doesn't exist\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n\n // Create backup if requested\n let backupPath: string | undefined;\n if (backup) {\n try {\n await fs.access(filePath);\n backupPath = `${filePath}.bak`;\n await fs.copyFile(filePath, backupPath);\n } catch {\n // File doesn't exist, no backup needed\n }\n }\n\n // Serialize data\n const content = serializer.serialize(data, {\n prettify,\n indent,\n sortKeys,\n });\n\n // Write to disk (atomic or direct)\n if (atomic) {\n const tempPath = `${filePath}.tmp`;\n await fs.writeFile(tempPath, content, 'utf-8');\n await fs.rename(tempPath, filePath);\n } else {\n await fs.writeFile(filePath, content, 'utf-8');\n }\n\n // Update cache logic if needed (e.g., invalidate or update)\n // For now, we rely on the watcher to pick up changes\n\n return {\n success: true,\n path: filePath,\n // format, // Not in schema\n size: Buffer.byteLength(content, 'utf-8'),\n backupPath,\n saveTime: Date.now() - startTime,\n };\n } catch (error) {\n this.logger?.error('Failed to save metadata', undefined, {\n type,\n name,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n /**\n * Find file for a given type and name\n */\n private async findFile(type: string, name: string): Promise<string | null> {\n const typeDir = path.join(this.rootDir, type);\n const extensions = ['.json', '.yaml', '.yml', '.ts', '.js'];\n\n for (const ext of extensions) {\n const filePath = path.join(typeDir, `${name}${ext}`);\n\n try {\n await fs.access(filePath);\n return filePath;\n } catch {\n // File doesn't exist, try next extension\n }\n }\n\n return null;\n }\n\n /**\n * Detect format from file extension\n */\n private detectFormat(filePath: string): MetadataFormat {\n const ext = path.extname(filePath).toLowerCase();\n\n switch (ext) {\n case '.json':\n return 'json';\n case '.yaml':\n case '.yml':\n return 'yaml';\n case '.ts':\n return 'typescript';\n case '.js':\n return 'javascript';\n default:\n return 'json'; // Default to JSON\n }\n }\n\n /**\n * Get serializer for format\n */\n private getSerializer(format: MetadataFormat): MetadataSerializer | undefined {\n return this.serializers.get(format);\n }\n\n /**\n * Generate ETag for content\n * Uses SHA-256 hash truncated to 32 characters for reasonable collision resistance\n * while keeping ETag headers compact (full 64-char hash is overkill for this use case)\n */\n private generateETag(content: string): string {\n const hash = createHash('sha256').update(content).digest('hex').substring(0, 32);\n return `\"${hash}\"`;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Memory Metadata Loader\n * \n * Stores metadata in memory only. Changes are lost when process restarts.\n * Useful for testing, temporary overrides, or \"dirty\" edits.\n */\n\nimport type {\n MetadataLoadOptions,\n MetadataLoadResult,\n MetadataStats,\n MetadataLoaderContract,\n MetadataSaveOptions,\n MetadataSaveResult,\n} from '@objectstack/spec/system';\nimport type { MetadataLoader } from './loader-interface.js';\n\nexport class MemoryLoader implements MetadataLoader {\n readonly contract: MetadataLoaderContract = {\n name: 'memory',\n protocol: 'memory:',\n capabilities: {\n read: true,\n write: true,\n watch: false,\n list: true,\n },\n };\n\n // Storage: Type -> Name -> Data\n private storage = new Map<string, Map<string, any>>();\n\n async load(\n type: string,\n name: string,\n _options?: MetadataLoadOptions\n ): Promise<MetadataLoadResult> {\n const typeStore = this.storage.get(type);\n const data = typeStore?.get(name);\n\n if (data) {\n return {\n data,\n source: 'memory',\n format: 'json',\n loadTime: 0,\n };\n }\n\n return { data: null };\n }\n\n async loadMany<T = any>(\n type: string,\n _options?: MetadataLoadOptions\n ): Promise<T[]> {\n const typeStore = this.storage.get(type);\n if (!typeStore) return [];\n return Array.from(typeStore.values()) as T[];\n }\n\n async exists(type: string, name: string): Promise<boolean> {\n return this.storage.get(type)?.has(name) ?? false;\n }\n\n async stat(type: string, name: string): Promise<MetadataStats | null> {\n if (await this.exists(type, name)) {\n return {\n size: 0, // In-memory\n mtime: new Date().toISOString(),\n format: 'json',\n };\n }\n return null;\n }\n\n async list(type: string): Promise<string[]> {\n const typeStore = this.storage.get(type);\n if (!typeStore) return [];\n return Array.from(typeStore.keys());\n }\n\n async save(\n type: string,\n name: string,\n data: any,\n _options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n if (!this.storage.has(type)) {\n this.storage.set(type, new Map());\n }\n\n this.storage.get(type)!.set(name, data);\n\n return {\n success: true,\n path: `memory://${type}/${name}`,\n saveTime: 0,\n };\n }\n\n /**\n * Delete a metadata item from memory storage\n */\n async delete(type: string, name: string): Promise<void> {\n const typeStore = this.storage.get(type);\n if (typeStore) {\n typeStore.delete(name);\n if (typeStore.size === 0) {\n this.storage.delete(type);\n }\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Remote Metadata Loader\n * \n * Loads metadata from an HTTP API.\n * This loader is stateless and delegates storage to the remote server.\n */\n\nimport type {\n MetadataLoadOptions,\n MetadataLoadResult,\n MetadataStats,\n MetadataLoaderContract,\n MetadataSaveOptions,\n MetadataSaveResult,\n} from '@objectstack/spec/system';\nimport type { MetadataLoader } from './loader-interface.js';\n\nexport class RemoteLoader implements MetadataLoader {\n readonly contract: MetadataLoaderContract = {\n name: 'remote',\n protocol: 'http:',\n capabilities: {\n read: true,\n write: true,\n watch: false, // Could implement SSE/WebSocket in future\n list: true,\n },\n };\n\n constructor(private baseUrl: string, private authToken?: string) {}\n\n private get headers() {\n return {\n 'Content-Type': 'application/json',\n ...(this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}),\n };\n }\n\n async load(\n type: string,\n name: string,\n _options?: MetadataLoadOptions\n ): Promise<MetadataLoadResult> {\n try {\n const response = await fetch(`${this.baseUrl}/${type}/${name}`, {\n method: 'GET',\n headers: this.headers,\n });\n\n if (response.status === 404) {\n return { data: null };\n }\n\n if (!response.ok) {\n throw new Error(`Remote load failed: ${response.statusText}`);\n }\n\n const data = await response.json();\n return {\n data,\n source: this.baseUrl,\n format: 'json',\n loadTime: 0, \n };\n } catch (error) {\n console.error(`RemoteLoader error loading ${type}/${name}`, error);\n throw error;\n }\n }\n\n async loadMany<T = any>(\n type: string,\n _options?: MetadataLoadOptions\n ): Promise<T[]> {\n const response = await fetch(`${this.baseUrl}/${type}`, {\n method: 'GET',\n headers: this.headers,\n });\n\n if (!response.ok) {\n return [];\n }\n\n return (await response.json()) as T[];\n }\n\n async exists(type: string, name: string): Promise<boolean> {\n const response = await fetch(`${this.baseUrl}/${type}/${name}`, {\n method: 'HEAD',\n headers: this.headers,\n });\n return response.ok;\n }\n\n async stat(type: string, name: string): Promise<MetadataStats | null> {\n // Basic implementation using HEAD\n const response = await fetch(`${this.baseUrl}/${type}/${name}`, {\n method: 'HEAD',\n headers: this.headers,\n });\n \n if (!response.ok) return null;\n\n return {\n size: Number(response.headers.get('content-length') || 0),\n mtime: new Date(response.headers.get('last-modified') || Date.now()).toISOString(),\n format: 'json',\n };\n }\n\n async list(type: string): Promise<string[]> {\n const items = await this.loadMany<{ name: string }>(type);\n return items.map(i => i.name);\n }\n\n async save(\n type: string,\n name: string,\n data: any,\n _options?: MetadataSaveOptions\n ): Promise<MetadataSaveResult> {\n const response = await fetch(`${this.baseUrl}/${type}/${name}`, {\n method: 'PUT',\n headers: this.headers,\n body: JSON.stringify(data),\n });\n\n if (!response.ok) {\n throw new Error(`Remote save failed: ${response.statusText}`);\n }\n\n return {\n success: true,\n path: `${this.baseUrl}/${type}/${name}`,\n saveTime: 0,\n };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Metadata History Retention and Cleanup\n *\n * Manages automatic cleanup of old history records based on retention policies.\n * Supports both age-based and count-based retention strategies.\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\nimport type { MetadataHistoryRetentionPolicy } from '@objectstack/spec/system';\nimport { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel';\nimport type { DatabaseLoader } from '../loaders/database-loader.js';\n\n/**\n * Types whose runtime transaction rows pin a specific historical body\n * (ADR-0009 `executionPinned`). History rows for these types are\n * **never** garbage-collected — the `getByHash()` resolution path\n * relies on them remaining queryable for the lifetime of any open\n * execution.\n */\nfunction executionPinnedTypes(): string[] {\n return DEFAULT_METADATA_TYPE_REGISTRY\n .filter((entry) => entry.executionPinned)\n .map((entry) => entry.type);\n}\n\n/**\n * History Cleanup Manager\n *\n * Handles automatic cleanup of metadata history records based on\n * configured retention policies.\n */\nexport class HistoryCleanupManager {\n private policy: MetadataHistoryRetentionPolicy;\n private dbLoader: DatabaseLoader;\n private cleanupTimer?: NodeJS.Timeout;\n\n constructor(policy: MetadataHistoryRetentionPolicy, dbLoader: DatabaseLoader) {\n this.policy = policy;\n this.dbLoader = dbLoader;\n }\n\n /**\n * Start automatic cleanup if enabled in the policy.\n */\n start(): void {\n if (!this.policy.autoCleanup) {\n return;\n }\n\n const intervalMs = (this.policy.cleanupIntervalHours ?? 24) * 60 * 60 * 1000;\n\n // Run cleanup immediately on start\n void this.runCleanup();\n\n // Schedule periodic cleanup\n this.cleanupTimer = setInterval(() => {\n void this.runCleanup();\n }, intervalMs);\n }\n\n /**\n * Stop automatic cleanup.\n */\n stop(): void {\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = undefined;\n }\n }\n\n /**\n * Run cleanup based on the retention policy.\n * Removes history records that exceed the configured limits.\n */\n async runCleanup(): Promise<{ deleted: number; errors: number }> {\n const driver = (this.dbLoader as any).driver as IDataDriver;\n const historyTableName = (this.dbLoader as any).historyTableName as string;\n const organizationId = (this.dbLoader as any).organizationId as string | undefined;\n // `environmentId` was removed from the metadata layer (ADR-0008 §0\n // amendment). Cleanup is now scoped by `organization_id` only.\n\n let deleted = 0;\n let errors = 0;\n\n // ADR-0009: collect executionPinned types — these are GC-exempt.\n const pinnedTypes = executionPinnedTypes();\n const isPinned = (t: string | undefined): boolean =>\n !!t && pinnedTypes.includes(t);\n\n try {\n // Age-based cleanup\n if (this.policy.maxAgeDays) {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - this.policy.maxAgeDays);\n const cutoffISO = cutoffDate.toISOString();\n\n const filter: Record<string, unknown> = {\n recorded_at: { $lt: cutoffISO },\n };\n\n if (organizationId) {\n filter.organization_id = organizationId;\n }\n\n if (pinnedTypes.length > 0) {\n // Exclude executionPinned rows from bulk age delete.\n filter.type = { $nin: pinnedTypes };\n }\n\n try {\n const result = await this.bulkDeleteByFilter(driver, historyTableName, filter);\n deleted += result.deleted;\n errors += result.errors;\n } catch {\n errors++;\n }\n }\n\n // Count-based cleanup per metadata item\n if (this.policy.maxVersions) {\n try {\n // Get all unique metadata items keyed by (type, name)\n const baseWhere: Record<string, unknown> = {};\n if (organizationId) baseWhere.organization_id = organizationId;\n\n const metaItems = await driver.find(historyTableName, {\n where: baseWhere,\n fields: ['type', 'name'],\n });\n\n const uniqueKeys = new Set<string>();\n for (const record of metaItems) {\n const t = record.type as string | undefined;\n const n = record.name as string | undefined;\n if (t && n && !isPinned(t)) {\n uniqueKeys.add(`${t}\\x1f${n}`);\n }\n }\n\n // For each metadata item, keep only the latest N versions\n for (const key of uniqueKeys) {\n const [type, name] = key.split('\\x1f');\n const filter: Record<string, unknown> = { type, name, ...baseWhere };\n\n try {\n // Fetch only the IDs of records beyond the retention limit (oldest first)\n const historyRecords = await driver.find(historyTableName, {\n where: filter,\n orderBy: [{ field: 'version', order: 'desc' as const }],\n fields: ['id'],\n });\n\n if (historyRecords.length > this.policy.maxVersions) {\n const toDelete = historyRecords.slice(this.policy.maxVersions);\n const ids = toDelete.map(r => r.id as string).filter(Boolean);\n const result = await this.bulkDeleteByIds(driver, historyTableName, ids);\n deleted += result.deleted;\n errors += result.errors;\n }\n } catch {\n errors++;\n }\n }\n } catch {\n errors++;\n }\n }\n } catch (error) {\n console.error('History cleanup failed:', error);\n errors++;\n }\n\n return { deleted, errors };\n }\n\n /**\n * Delete records matching a filter using the most efficient method available on the driver.\n */\n private async bulkDeleteByFilter(\n driver: IDataDriver,\n table: string,\n filter: Record<string, unknown>\n ): Promise<{ deleted: number; errors: number }> {\n const driverAny = driver as any;\n if (typeof driverAny.deleteMany === 'function') {\n const count = await driverAny.deleteMany(table, filter);\n return { deleted: typeof count === 'number' ? count : 0, errors: 0 };\n }\n\n // Fallback: fetch IDs then delete\n const records = await driver.find(table, { where: filter, fields: ['id'] });\n const ids = records.map((r: Record<string, unknown>) => r.id as string).filter(Boolean);\n return this.bulkDeleteByIds(driver, table, ids);\n }\n\n /**\n * Delete records by IDs using bulkDelete when available, otherwise one-by-one.\n */\n private async bulkDeleteByIds(\n driver: IDataDriver,\n table: string,\n ids: string[]\n ): Promise<{ deleted: number; errors: number }> {\n if (ids.length === 0) return { deleted: 0, errors: 0 };\n\n const driverAny = driver as any;\n if (typeof driverAny.bulkDelete === 'function') {\n const result = await driverAny.bulkDelete(table, ids);\n return {\n deleted: typeof result === 'number' ? result : ids.length,\n errors: 0,\n };\n }\n\n // Fallback: sequential deletes\n let deleted = 0;\n let errors = 0;\n for (const id of ids) {\n try {\n await driver.delete(table, id);\n deleted++;\n } catch {\n errors++;\n }\n }\n return { deleted, errors };\n }\n\n /**\n * Get cleanup statistics without actually deleting anything.\n * Useful for previewing what would be cleaned up.\n */\n async getCleanupStats(): Promise<{\n recordsByAge: number;\n recordsByCount: number;\n total: number;\n }> {\n const driver = (this.dbLoader as any).driver as IDataDriver;\n const historyTableName = (this.dbLoader as any).historyTableName as string;\n const organizationId = (this.dbLoader as any).organizationId as string | undefined;\n\n let recordsByAge = 0;\n let recordsByCount = 0;\n\n // ADR-0009: executionPinned types are excluded from GC.\n const pinnedTypes = executionPinnedTypes();\n const isPinned = (t: string | undefined): boolean =>\n !!t && pinnedTypes.includes(t);\n\n try {\n const baseWhere: Record<string, unknown> = {};\n if (organizationId) baseWhere.organization_id = organizationId;\n\n // Count records that would be deleted by age\n if (this.policy.maxAgeDays) {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - this.policy.maxAgeDays);\n const cutoffISO = cutoffDate.toISOString();\n\n const filter: Record<string, unknown> = {\n recorded_at: { $lt: cutoffISO },\n ...baseWhere,\n };\n if (pinnedTypes.length > 0) {\n filter.type = { $nin: pinnedTypes };\n }\n\n recordsByAge = await driver.count(historyTableName, {\n where: filter,\n });\n }\n\n // Count records that would be deleted by version limit\n if (this.policy.maxVersions) {\n const metaItems = await driver.find(historyTableName, {\n where: baseWhere,\n fields: ['type', 'name'],\n });\n\n const uniqueKeys = new Set<string>();\n for (const record of metaItems) {\n const t = record.type as string | undefined;\n const n = record.name as string | undefined;\n if (t && n && !isPinned(t)) {\n uniqueKeys.add(`${t}\\x1f${n}`);\n }\n }\n\n for (const key of uniqueKeys) {\n const [type, name] = key.split('\\x1f');\n const filter: Record<string, unknown> = { type, name, ...baseWhere };\n\n const count = await driver.count(historyTableName, {\n where: filter,\n });\n\n if (count > this.policy.maxVersions) {\n recordsByCount += count - this.policy.maxVersions;\n }\n }\n }\n } catch (error) {\n console.error('Failed to get cleanup stats:', error);\n }\n\n // Return separate counts. The total is an upper-bound estimate: it may overcount\n // records that qualify under both policies (age and count). Use recordsByAge and\n // recordsByCount individually for precise breakdowns.\n return {\n recordsByAge,\n recordsByCount,\n total: recordsByAge + recordsByCount,\n };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './executor.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport * as System from '@objectstack/spec/system';\nimport { ISchemaDriver } from '@objectstack/spec/contracts';\n\nexport class MigrationExecutor {\n constructor(private driver: ISchemaDriver) {}\n\n async executeChangeSet(changeSet: System.ChangeSetParsed): Promise<void> {\n console.log(`Executing ChangeSet: ${changeSet.name} (${changeSet.id})`);\n \n for (const op of changeSet.operations) {\n try {\n await this.executeOperation(op);\n } catch (e) {\n console.error(`Failed to execute operation ${op.type}:`, e);\n throw e;\n }\n }\n }\n\n private async executeOperation(op: System.MigrationOperationParsed): Promise<void> {\n switch (op.type) {\n case 'create_object':\n console.log(` > Create Object: ${op.object.name}`);\n await this.driver.createCollection(op.object.name, op.object);\n break;\n case 'add_field':\n console.log(` > Add Field: ${op.objectName}.${op.fieldName}`);\n await this.driver.addColumn(op.objectName, op.fieldName, op.field);\n break;\n case 'remove_field':\n console.log(` > Remove Field: ${op.objectName}.${op.fieldName}`);\n await this.driver.dropColumn(op.objectName, op.fieldName);\n break;\n case 'delete_object':\n console.log(` > Delete Object: ${op.objectName}`);\n await this.driver.dropCollection(op.objectName);\n break;\n case 'execute_sql':\n console.log(` > Execute SQL`);\n await this.driver.executeRaw(op.sql);\n break;\n case 'modify_field':\n console.warn(` ! Modify Field: ${op.objectName}.${op.fieldName} (Not fully implemented)`);\n break;\n case 'rename_object':\n console.warn(` ! Rename Object: ${op.oldName} -> ${op.newName} (Not fully implemented)`);\n break;\n default:\n throw new Error(`Unknown operation type`);\n }\n }\n}\n"],"mappings":"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","names":["import_api","path","list","envelope","path","import_node_crypto","path","basename","chokidarWatch","import_kernel","import_shared","import_metadata_core","import_spec","path","registerMetadataHmrRoutes","src","chokidarWatch","import_metadata_core","import_kernel"]}