@objectstack/metadata 17.3.0 → 17.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +532 -0
- package/README.md +6 -4
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.cjs +299 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +227 -60
- package/dist/index.d.ts +227 -60
- package/dist/index.js +294 -35
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.cjs +104 -22
- package/dist/migrations/index.cjs.map +1 -1
- package/dist/migrations/index.d.cts +40 -1
- package/dist/migrations/index.d.ts +40 -1
- package/dist/migrations/index.js +107 -22
- package/dist/migrations/index.js.map +1 -1
- package/dist/node.cjs +299 -36
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +24 -1
- package/dist/node.d.ts +24 -1
- package/dist/node.js +294 -35
- package/dist/node.js.map +1 -1
- package/dist/view-container.cjs +37 -0
- package/dist/view-container.cjs.map +1 -0
- package/dist/view-container.d.cts +76 -0
- package/dist/view-container.d.ts +76 -0
- package/dist/view-container.js +12 -0
- package/dist/view-container.js.map +1 -0
- package/package.json +18 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/migrations/driver-exec.ts","../../src/migrations/migrate-env-id-to-project-id.ts","../../src/migrations/migrate-project-id-to-environment-id.ts","../../src/migrations/drop-projection-tables.ts","../../src/migrations/migrate-sys-notification-to-event.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * How the migrations in this directory obtain a raw-SQL entry point.\n *\n * Every helper here used to guard on — and drive through — `driver.raw(sql,\n * bindings?)`. **No data driver in this repo defines `raw`.** Measured on\n * `origin/main`, the only `raw(` member anywhere outside a test double is\n * `packages/verify/src/harness.ts`, an HTTP harness whose signature is\n * `(path, init)`. `SqlDriver` keeps its knex handle `protected`, so\n * `driver.raw` is `undefined` there too, and `SqliteWasmDriver` inherits that.\n * The result was a published, operator-documented migration path that refused\n * every driver the platform ships — quietly, because\n * `migrateSysNotificationToEvent` *returns* `{ status: 'error' }` rather than\n * throwing, and the message blamed the operator's driver instead of saying the\n * migration did not run.\n *\n * ## Why `execute` is tried FIRST\n *\n * `IDataDriver` (`@objectstack/spec/contracts`, `data-driver.ts`) declares\n *\n * ```ts\n * execute(command: unknown, parameters?: unknown[], options?: DriverOptions): Promise<unknown>;\n * ```\n *\n * — **non-optional**, with bound parameters as the second POSITIONAL argument,\n * which is the exact shape `raw(sql, bindings?)` was being called in. `raw` has\n * never appeared on that interface. So `execute` is not merely the surface the\n * shipped drivers happen to have; it is the only raw-execution surface the\n * contract guarantees at all, and a driver that satisfies `IDataDriver` always\n * has it. Trying it first is therefore the order that matches the declaration.\n *\n * ⚠️ `IDataEngine.execute?(command, options?)` (`data-engine.ts`) is a DIFFERENT\n * member on a different interface — its second parameter is an options bag, not\n * bindings. These helpers take an `IDataDriver`, so `data-driver.ts` governs.\n * Do not reason about this call from the engine declaration.\n *\n * ## Prior art, and why the order had to be chosen rather than copied\n *\n * `packages/metadata-protocol/src/migrations/` already resolves both surfaces\n * instead of assuming one — twice, and **in opposite orders**:\n * `partial-index-probe.ts` tries `raw` first, `seed-tenancy-backfill.ts` tries\n * `execute` first. `metadata-protocol/src/protocol.ts` (`ensureOverlayIndex`)\n * is a third, raw-first. One operation with three implementations and two\n * behaviours resolves to the declaration-bound side, so this directory adopts\n * `execute`-first uniformly across all four of its members.\n *\n * `raw` is kept as a fallback rather than dropped: nothing in this repo defines\n * it, but a host or a third-party driver may, and removing a surface that\n * currently works is not what this repair is for. The refusal below therefore\n * fires only for a driver that has NEITHER.\n *\n * ## Known limitation, deliberately not papered over here\n *\n * Two shipped drivers satisfy `typeof driver.execute === 'function'` without\n * being able to run SQL: `MemoryDriver.execute` logs a warning and returns\n * `null` for every command, and `MongoDbDriver.execute` returns a string\n * command back verbatim. Both are selected by the probe below and then answer\n * every column probe with \"absent\", so a migration reports `not_applicable` /\n * `table_missing` instead of refusing. `IDataDriver` exposes no capability flag\n * that would separate \"implements the escape hatch\" from \"can run SQL\"\n * (`DriverCapabilities` has no such member), so distinguishing them is a\n * contract question, not something to guess at with a driver-name sniff.\n * Filed separately.\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\n\n/**\n * A raw-SQL entry point resolved off a driver. `bindings` are passed\n * positionally, matching `IDataDriver.execute`'s declared `parameters`.\n */\nexport type DriverExec = (sql: string, bindings?: readonly unknown[]) => Promise<any>;\n\n/**\n * Resolve the raw-SQL entry point of `driver`, or `undefined` when it offers\n * neither surface.\n *\n * Callers that must refuse should pair this with {@link driverExecRefusal} so\n * every member of this directory states the same remedy.\n */\nexport function resolveDriverExec(driver: IDataDriver | null | undefined): DriverExec | undefined {\n const candidate = driver as any;\n if (!candidate) return undefined;\n // Declared surface first — see the header.\n if (typeof candidate.execute === 'function') {\n return (sql, bindings) => candidate.execute(sql, bindings ? [...bindings] : []);\n }\n if (typeof candidate.raw === 'function') {\n return (sql, bindings) => candidate.raw(sql, bindings ? [...bindings] : []);\n }\n return undefined;\n}\n\n/**\n * The single refusal sentence used by every migration in this directory, for a\n * driver that offers neither surface.\n *\n * Assembled in one place because the wording carries pinned properties: the\n * remedy is stated exactly ONCE (a guard here once concatenated its instruction\n * twice), the two sentences stay separated rather than running together, and a\n * conforming driver is named so the operator has something to act on.\n */\nexport function driverExecRefusal(helper: string): string {\n return (\n `${helper}: driver must expose an .execute(sql, bindings?) or .raw(sql, bindings?) method. ` +\n 'SqlDriver (better-sqlite3/knex) exposes .execute(), as does its SqliteWasmDriver subclass; ' +\n 'cloud-side TursoDriver also conforms.'\n );\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Migration: env_id → project_id\n *\n * Renames the `env_id` column to `project_id` on the metadata storage tables:\n * - sys_metadata\n * - sys_metadata_history\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 `project_id` already exists, the step is skipped.\n *\n * Usage:\n * import { migrateEnvIdToProjectId } from '@objectstack/metadata/migrations';\n * await migrateEnvIdToProjectId(driver);\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\n\nimport { type DriverExec, driverExecRefusal, resolveDriverExec } from './driver-exec.js';\n\nconst AFFECTED_TABLES = [\n 'sys_metadata',\n 'sys_metadata_history',\n] as const;\n\nexport interface MigrationResult {\n table: string;\n status: 'renamed' | 'already_done' | 'table_missing' | 'error';\n error?: string;\n}\n\n/**\n * Rename `env_id` → `project_id` on all metadata tables.\n *\n * @param driver An IDataDriver with access to the target database. Raw SQL is\n * issued through the surface `IDataDriver` declares —\n * `execute(sql, bindings?)` — falling back to\n * `raw(sql, bindings?)`; see `./driver-exec.ts`.\n * @returns Per-table migration results.\n */\nexport async function migrateEnvIdToProjectId(driver: IDataDriver): Promise<MigrationResult[]> {\n const exec = resolveDriverExec(driver);\n\n if (!exec) {\n throw new Error(driverExecRefusal('migrateEnvIdToProjectId'));\n }\n\n const results: MigrationResult[] = [];\n\n for (const table of AFFECTED_TABLES) {\n try {\n // Detect dialect: SQLite uses PRAGMA, others use information_schema.\n const hasColumn = await _columnExists(exec, table, 'env_id');\n const alreadyMigrated = await _columnExists(exec, table, 'project_id');\n\n if (alreadyMigrated && !hasColumn) {\n results.push({ table, status: 'already_done' });\n continue;\n }\n\n if (!hasColumn) {\n // Neither column exists — table might not exist yet.\n results.push({ table, status: 'table_missing' });\n continue;\n }\n\n // Perform the rename. SQLite ≥ 3.25.0 supports ALTER TABLE RENAME COLUMN.\n await exec(`ALTER TABLE \"${table}\" RENAME COLUMN env_id TO project_id`);\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(exec: DriverExec, table: string, column: string): Promise<boolean> {\n try {\n // SQLite: PRAGMA table_info returns rows with `name` column.\n const rows: any[] = await exec(`PRAGMA table_info(\"${table}\")`);\n if (Array.isArray(rows) && rows.length > 0) {\n // knex wraps PRAGMA result; handle both `rows` and `rows[0]` shapes.\n const list: any[] = Array.isArray(rows[0]) ? rows[0] : rows;\n return list.some((r: any) => r?.name === column);\n }\n\n // Fallback for non-SQLite: query information_schema.\n const result: any[] = await exec(\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 * Migration: project_id → environment_id\n *\n * Renames the `project_id` column to `environment_id` on the metadata storage\n * tables — but only on the tables whose CURRENT declaration actually knows\n * `environment_id`.\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 * ─────────────────────────────────────────────────────────────────────\n * Why the table list is DERIVED and not written out (#13205)\n *\n * This migration is the terminal step of that chain: its target column is\n * the CURRENT declared shape, so \"should this table be renamed?\" is not an\n * independent fact — it is `does this object still declare environment_id?`.\n * Written out by hand, the two drifted apart: `sys_metadata_history` stayed\n * on the list after the branch/project-removal amendment (M1) removed\n * `environment_id` from its declaration, so against a database whose\n * physical `sys_metadata_history` still carried `project_id` this migration\n * renamed it to a column NO declaration knows about — minting exactly the\n * orphan column class the metadata drift audit exists to remove.\n *\n * The old guard could not catch it: the loop gates on `project_id` existing\n * PHYSICALLY (`_columnExists`), which says nothing about the target column\n * being DECLARED. So the list is now computed from the declarations in\n * `@objectstack/metadata-core` (already a dependency of this package — no\n * new edge), and a candidate that does not declare the target column is\n * reported as `skipped_not_declared` rather than dropped silently: an\n * operator reading the result sees the table was considered and why nothing\n * happened, instead of having to guess whether it was forgotten again.\n *\n * ⚠️ The sibling `migrate-env-id-to-project-id.ts` is deliberately NOT\n * changed this way. Its target (`project_id`) is an INTERMEDIATE column that\n * no current declaration carries by design — gating it on today's\n * declarations would disable the chain's first step entirely. The rule\n * \"target must be declared\" is sound only for the terminal migration.\n * ─────────────────────────────────────────────────────────────────────\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\nimport { type DriverExec, driverExecRefusal, resolveDriverExec } from './driver-exec.js';\nimport { SysMetadataObject, SysMetadataHistoryObject } from '@objectstack/metadata-core';\n\n/** The column this migration RENAMES AWAY FROM. */\nconst SOURCE_COLUMN = 'project_id';\n\n/** The column this migration PRODUCES. Must be declared, or the rename mints an orphan. */\nconst TARGET_COLUMN = 'environment_id';\n\n/**\n * Every metadata storage table this migration considers. Membership here says\n * \"this table has, historically, carried the tenancy column\" — whether the\n * rename actually runs is decided by {@link AFFECTED_TABLES} below, from the\n * declaration.\n */\nconst CANDIDATE_OBJECTS = [SysMetadataObject, SysMetadataHistoryObject] as const;\n\nfunction declaresColumn(object: { fields?: Record<string, unknown> }, column: string): boolean {\n return Object.prototype.hasOwnProperty.call(object.fields ?? {}, column);\n}\n\n/** Candidate table names, in declaration order. */\nconst CANDIDATE_TABLES: readonly string[] = CANDIDATE_OBJECTS.map((o) => o.name);\n\n/**\n * The tables this migration will actually rename: the candidates whose CURRENT\n * declaration carries {@link TARGET_COLUMN}.\n *\n * Exported for the pin in `migrate-project-id-to-environment-id.test.ts` (not\n * re-exported from `./index.ts` — this is not package surface).\n */\nexport const AFFECTED_TABLES: readonly string[] = CANDIDATE_OBJECTS\n .filter((o) => declaresColumn(o, TARGET_COLUMN))\n .map((o) => o.name);\n\nexport interface ProjectIdToEnvironmentIdResult {\n table: string;\n /**\n * `skipped_not_declared` — the table is a known metadata storage table, but\n * its current declaration has no `environment_id`, so renaming into it\n * would create a column nothing declares. Nothing was executed.\n */\n status: 'renamed' | 'already_done' | 'table_missing' | 'skipped_not_declared' | 'error';\n error?: string;\n}\n\n/**\n * Rename `project_id` → `environment_id` on all metadata tables that still\n * declare `environment_id`.\n *\n * @param driver An IDataDriver with access to the target database. Raw SQL is\n * issued through the surface `IDataDriver` declares —\n * `execute(sql, bindings?)` — falling back to\n * `raw(sql, bindings?)`; see `./driver-exec.ts`.\n * @returns Per-table migration results — one entry per candidate table,\n * including the ones skipped for lacking the declared target.\n */\nexport async function migrateProjectIdToEnvironmentId(\n driver: IDataDriver,\n): Promise<ProjectIdToEnvironmentIdResult[]> {\n const exec = resolveDriverExec(driver);\n\n if (!exec) {\n throw new Error(driverExecRefusal('migrateProjectIdToEnvironmentId'));\n }\n\n const results: ProjectIdToEnvironmentIdResult[] = [];\n\n for (const table of CANDIDATE_TABLES) {\n // The declared-target gate, ahead of every physical probe: a table whose\n // declaration lost `environment_id` must never be renamed INTO it, no\n // matter what the physical schema still carries (#13205).\n if (!AFFECTED_TABLES.includes(table)) {\n results.push({ table, status: 'skipped_not_declared' });\n continue;\n }\n\n try {\n const hasColumn = await _columnExists(exec, table, SOURCE_COLUMN);\n const alreadyMigrated = await _columnExists(exec, table, TARGET_COLUMN);\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 exec(\n `ALTER TABLE \"${table}\" RENAME COLUMN ${SOURCE_COLUMN} TO ${TARGET_COLUMN}`,\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(exec: DriverExec, table: string, column: string): Promise<boolean> {\n try {\n const rows: any[] = await exec(`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 exec(\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 * Migration: drop deprecated metadata projection tables.\n *\n * In 2026-05 the per-type projection tables (`sys_object` / `sys_view` /\n * `sys_flow` / `sys_agent` / `sys_tool`) and the corresponding\n * `MetadataProjector` were removed (see ADR 0005 addendum). All metadata\n * now lives as JSON inside `sys_metadata` — these projection tables are\n * dead weight on any existing database.\n *\n * This migration drops them if present. It is idempotent and safe to run\n * on databases that never had them (the `DROP TABLE IF EXISTS` is a no-op).\n *\n * Usage:\n * import { dropProjectionTables } from '@objectstack/metadata/migrations';\n * await dropProjectionTables(driver);\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\n\nimport { driverExecRefusal, resolveDriverExec } from './driver-exec.js';\n\nconst DEPRECATED_TABLES = [\n 'sys_object',\n 'sys_view',\n 'sys_flow',\n 'sys_agent',\n 'sys_tool',\n] as const;\n\nexport interface DropProjectionResult {\n table: string;\n status: 'dropped' | 'not_present' | 'error';\n error?: string;\n}\n\n/**\n * Drop the deprecated per-type metadata projection tables.\n *\n * @param driver An `IDataDriver`. Raw SQL is issued through the surface\n * `IDataDriver` declares — `execute(sql, bindings?)` — falling\n * back to `raw(sql, bindings?)`; see `./driver-exec.ts`.\n * @returns Per-table results.\n */\nexport async function dropProjectionTables(driver: IDataDriver): Promise<DropProjectionResult[]> {\n const exec = resolveDriverExec(driver);\n if (!exec) {\n throw new Error(driverExecRefusal('dropProjectionTables'));\n }\n\n const results: DropProjectionResult[] = [];\n for (const table of DEPRECATED_TABLES) {\n try {\n await exec(`DROP TABLE IF EXISTS ${table}`);\n results.push({ table, status: 'dropped' });\n } catch (error) {\n results.push({\n table,\n status: 'error',\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n return results;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Migration: sys_notification (per-user inbox) → notification event (ADR-0030)\n *\n * ADR-0030 re-models `sys_notification` from a per-user *inbox* into the L2\n * *event* (one row per `emit`). This migration preserves users' existing bell\n * notifications across the cut-over by splitting each legacy row into the new\n * layered model:\n *\n * legacy sys_notification row (recipient_id, type, title, body, url,\n * actor_name, is_read, read_at, …)\n * │\n * ├─► sys_inbox_message (L5 in-app materialization, keyed by user)\n * ├─► sys_notification_receipt (L5 read-state: 'read' if is_read else 'delivered')\n * └─► the sys_notification row itself is rewritten to the event shape\n * (topic ← type, payload ← {title,body,url,actor_name}) and its legacy\n * inbox columns are cleared.\n *\n * Idempotent: it acts only on rows that still carry the legacy shape\n * (`recipient_id IS NOT NULL`); a second run is a no-op. Safe when the legacy\n * columns were never present (a fresh install created directly in the new\n * shape) — it reports `not_applicable`.\n *\n * Usage:\n * import { migrateSysNotificationToEvent } from '@objectstack/metadata/migrations';\n * await migrateSysNotificationToEvent({ driver, data });\n *\n * `driver` provides raw access to read legacy columns the re-modeled schema no\n * longer projects and to clear them — through the surface `IDataDriver`\n * declares, `execute(sql, bindings?)`, falling back to `raw(sql, bindings?)`\n * (see `./driver-exec.ts`); `data` (IDataEngine) performs the\n * structured inbox/receipt writes and the event rewrite so ids, JSON fields and\n * tenant stamping are handled uniformly across drivers.\n */\n\nimport type { IDataDriver, IDataEngine } from '@objectstack/spec/contracts';\n\nimport { type DriverExec, driverExecRefusal, resolveDriverExec } from './driver-exec.js';\n\nconst EVENT_OBJECT = 'sys_notification';\nconst INBOX_OBJECT = 'sys_inbox_message';\nconst RECEIPT_OBJECT = 'sys_notification_receipt';\n\n/** Legacy inbox columns cleared once a row is rewritten to the event shape. */\nconst LEGACY_COLUMNS = [\n 'recipient_id',\n 'type',\n 'title',\n 'body',\n 'url',\n 'actor_name',\n 'is_read',\n 'read_at',\n] as const;\n\nexport interface SysNotificationMigrationResult {\n status: 'migrated' | 'already_done' | 'not_applicable' | 'error';\n /** Number of legacy rows split into inbox + receipt + event. */\n migrated: number;\n error?: string;\n}\n\nexport interface SysNotificationMigrationOptions {\n driver: IDataDriver;\n data: IDataEngine;\n /** Defaults to `() => new Date().toISOString()`. */\n now?(): string;\n}\n\nexport async function migrateSysNotificationToEvent(\n opts: SysNotificationMigrationOptions,\n): Promise<SysNotificationMigrationResult> {\n const { data } = opts;\n const now = opts.now ?? (() => new Date().toISOString());\n\n const exec = resolveDriverExec(opts.driver);\n if (!exec) {\n return {\n status: 'error',\n migrated: 0,\n error: driverExecRefusal('migrateSysNotificationToEvent'),\n };\n }\n\n // No legacy `recipient_id` column → the table never held the inbox shape.\n if (!(await columnExists(exec, EVENT_OBJECT, 'recipient_id'))) {\n return { status: 'not_applicable', migrated: 0 };\n }\n\n // Only null-out columns that actually exist on this deployment.\n const presentLegacy: string[] = [];\n for (const col of LEGACY_COLUMNS) {\n if (await columnExists(exec, EVENT_OBJECT, col)) presentLegacy.push(col);\n }\n\n let migrated = 0;\n try {\n const rows = await selectLegacyRows(exec);\n if (rows.length === 0) return { status: 'already_done', migrated: 0 };\n\n for (const row of rows) {\n const id = String(row.id);\n const recipientId = row.recipient_id != null ? String(row.recipient_id) : null;\n if (!recipientId) continue; // defensive — guarded by the SELECT filter\n const orgId = row.organization_id != null ? String(row.organization_id) : null;\n const createdAt = row.created_at != null ? canonicalTimestampText(row.created_at) : now();\n const title = row.title != null ? String(row.title) : (row.type != null ? String(row.type) : 'Notification');\n const isRead = row.is_read === true || row.is_read === 1 || row.is_read === '1';\n // One topic for both the inbox row and the rewritten event, so the\n // materialization and its L2 event never disagree (empty/null legacy\n // `type` → 'legacy').\n const eventTopic = row.type != null && String(row.type).length > 0 ? String(row.type) : 'legacy';\n\n // L5 in-app materialization.\n await data.insert(INBOX_OBJECT, {\n user_id: recipientId,\n notification_id: id,\n topic: eventTopic,\n title,\n body_md: row.body ?? null,\n severity: 'info',\n action_url: row.url ?? null,\n organization_id: orgId,\n created_at: createdAt,\n });\n\n // L5 receipt (read-state spine).\n await data.insert(RECEIPT_OBJECT, {\n notification_id: id,\n delivery_id: null,\n user_id: recipientId,\n channel: 'inbox',\n state: isRead ? 'read' : 'delivered',\n at: isRead && row.read_at != null ? canonicalTimestampText(row.read_at) : createdAt,\n organization_id: orgId,\n created_at: createdAt,\n });\n\n // Rewrite the row itself to the L2 event shape (engine handles JSON).\n await data.update(\n EVENT_OBJECT,\n {\n id,\n topic: eventTopic,\n severity: 'info',\n payload: {\n title: row.title ?? null,\n body: row.body ?? null,\n url: row.url ?? null,\n actorName: row.actor_name ?? null,\n },\n },\n { where: { id } },\n );\n\n // Clear the legacy inbox columns so the row no longer matches the\n // migration filter (idempotency) and carries no stale recipient.\n if (presentLegacy.length > 0) {\n const setClause = presentLegacy.map((c) => `\"${c}\" = NULL`).join(', ');\n await exec(`UPDATE \"${EVENT_OBJECT}\" SET ${setClause} WHERE id = ?`, [id]);\n }\n\n migrated += 1;\n }\n\n return { status: 'migrated', migrated };\n } catch (err: any) {\n return { status: 'error', migrated, error: err?.message ?? String(err) };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * The canonical text spelling of a timestamp read back out of the legacy table.\n *\n * `selectLegacyRows` reads through `driver.raw`/`execute`, which hands the\n * dialect client's own materialisation straight back — that door does not run\n * `formatOutput`, so none of its repairs apply here on any dialect:\n *\n * - `created_at` is a BUILTIN audit column, so it is never in `datetimeFields`\n * and no declared-field coercion reaches it; `formatOutput` repairs it only\n * inside its `if (this.isSqlite)` arm (`repairNaiveUtcAuditTimestamp` over\n * `AUDIT_TIMESTAMP_COLUMNS`).\n * - `read_at` is a LEGACY column ADR-0030 removed from the object, so it is\n * not declared either — it can never enter `datetimeFields`, and it is not\n * an audit column, so no arm of `formatOutput` could reach it even at the\n * record read door.\n *\n * On SQLite both arrive as canonical ISO text and `String()` is the identity —\n * which is why every test in this directory stayed green. On Postgres and\n * MySQL an instant column materialises as a JS `Date`\n * (`withPostgresCalendarDayAsText` leaves the instant types alone deliberately;\n * pinned in `sql-driver-13567-audit-stamp-materialisation.test.ts`), and\n * `String(date)` spells\n *\n * Sun Aug 30 2026 18:19:25 GMT+0800 (China Standard Time)\n *\n * — whole seconds in the MIGRATING HOST's zone, with the milliseconds gone.\n * This migration is one-way and this value is WRITTEN, so that spelling is what\n * the platform would carry afterwards: either accepted and stored skewed and\n * de-precisioned, or rejected outright, since the trailing zone name is in no\n * dialect's timestamp grammar (#13998).\n *\n * Canonicalising HERE, at the consumer that writes, is deliberate and is the\n * only shape that could also repair an already-migrated deployment (#13973\n * option A). It is not a tolerant alias: `Date` and ISO text are two\n * materialisations of ONE instant, not two spellings of a key. Matches the\n * repo's existing correct form at `metadata-protocol/src/protocol.ts` (the\n * `occurred_at` read in `readMetadataAuditEvents`); anything that is neither a\n * string nor a `Date` keeps its previous `String()` rendering unchanged rather\n * than having a unit guessed for it on a one-way write path.\n */\nfunction canonicalTimestampText(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value instanceof Date) return value.toISOString();\n return String(value);\n}\n\nasync function selectLegacyRows(exec: DriverExec): Promise<any[]> {\n const result: any[] = await exec(\n `SELECT id, recipient_id, type, title, body, url, actor_name, is_read, read_at, created_at, organization_id ` +\n `FROM \"${EVENT_OBJECT}\" WHERE recipient_id IS NOT NULL`,\n );\n // knex wraps some results as `[rows]`; normalize both shapes.\n if (Array.isArray(result) && result.length > 0 && Array.isArray(result[0])) {\n return result[0];\n }\n return Array.isArray(result) ? result : [];\n}\n\nasync function columnExists(exec: DriverExec, table: string, column: string): Promise<boolean> {\n // SQLite path: PRAGMA table_info. On Postgres/others this raises a syntax\n // error — swallow it *locally* and fall through to information_schema (the\n // outer-catch version of this would never reach the fallback, making the\n // migration silently no-op on every non-SQLite DB).\n try {\n const rows: any = await exec(`PRAGMA table_info(\"${table}\")`);\n const list: any[] = Array.isArray(rows)\n ? (Array.isArray(rows[0]) ? rows[0] : rows)\n : [];\n if (list.length > 0 && list.some((r: any) => r?.name != null)) {\n return list.some((r: any) => r?.name === column);\n }\n } catch {\n /* not SQLite — fall through to information_schema */\n }\n // Postgres / others.\n try {\n const result: any = await exec(\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)\n ? (Array.isArray(result[0]) ? result[0] : result)\n : [];\n return list.length > 0;\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["list","AFFECTED_TABLES","_columnExists","list"]}
|
|
1
|
+
{"version":3,"sources":["../../src/migrations/driver-exec.ts","../../src/migrations/migrate-env-id-to-project-id.ts","../../src/migrations/migrate-project-id-to-environment-id.ts","../../src/migrations/drop-projection-tables.ts","../../src/migrations/migrate-sys-notification-to-event.ts"],"mappings":"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","names":["list","AFFECTED_TABLES","_columnExists","list"]}
|
package/dist/node.cjs
CHANGED
|
@@ -197,6 +197,9 @@ var init_hmr_routes = __esm({
|
|
|
197
197
|
// src/node.ts
|
|
198
198
|
var node_exports = {};
|
|
199
199
|
__export(node_exports, {
|
|
200
|
+
AMBIGUOUS_METADATA_STEM_CODE: () => AMBIGUOUS_METADATA_STEM_CODE,
|
|
201
|
+
AMBIGUOUS_METADATA_STEM_STATUS: () => AMBIGUOUS_METADATA_STEM_STATUS,
|
|
202
|
+
AmbiguousMetadataStemError: () => AmbiguousMetadataStemError,
|
|
200
203
|
DatabaseLoader: () => DatabaseLoader,
|
|
201
204
|
FilesystemLoader: () => FilesystemLoader,
|
|
202
205
|
HistoryCleanupManager: () => HistoryCleanupManager,
|
|
@@ -214,7 +217,8 @@ __export(node_exports, {
|
|
|
214
217
|
calculateChecksum: () => calculateChecksum,
|
|
215
218
|
deriveViewContainerObject: () => deriveViewContainerObject,
|
|
216
219
|
generateDiffSummary: () => generateDiffSummary,
|
|
217
|
-
generateSimpleDiff: () => generateSimpleDiff
|
|
220
|
+
generateSimpleDiff: () => generateSimpleDiff,
|
|
221
|
+
isAmbiguousMetadataStemError: () => isAmbiguousMetadataStemError
|
|
218
222
|
});
|
|
219
223
|
module.exports = __toCommonJS(node_exports);
|
|
220
224
|
|
|
@@ -642,7 +646,7 @@ async function _columnExists(exec, table, column) {
|
|
|
642
646
|
// src/loaders/database-loader.ts
|
|
643
647
|
function canonicalIsoInstant(value) {
|
|
644
648
|
if (value === null || value === void 0) return void 0;
|
|
645
|
-
if (value instanceof Date) return value.toISOString();
|
|
649
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? void 0 : value.toISOString();
|
|
646
650
|
if (typeof value === "string") return value;
|
|
647
651
|
return String(value);
|
|
648
652
|
}
|
|
@@ -698,7 +702,7 @@ var DatabaseLoader = class {
|
|
|
698
702
|
if (cacheEnabled) {
|
|
699
703
|
const lruOpts = {
|
|
700
704
|
maxSize: cacheOpts?.maxSize ?? 500,
|
|
701
|
-
ttl: cacheOpts?.
|
|
705
|
+
ttl: cacheOpts?.ttlMs ?? 6e4
|
|
702
706
|
};
|
|
703
707
|
this.loadCache = new LRUCache(lruOpts);
|
|
704
708
|
this.loadManyCache = new LRUCache(lruOpts);
|
|
@@ -1548,6 +1552,33 @@ function generateId() {
|
|
|
1548
1552
|
return `meta_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
1549
1553
|
}
|
|
1550
1554
|
|
|
1555
|
+
// src/loaders/ambiguous-metadata-stem.ts
|
|
1556
|
+
var AMBIGUOUS_METADATA_STEM_CODE = "AMBIGUOUS_METADATA_STEM";
|
|
1557
|
+
var AMBIGUOUS_METADATA_STEM_STATUS = 500;
|
|
1558
|
+
var AMBIGUOUS_METADATA_STEM_BRAND = /* @__PURE__ */ Symbol.for("objectstack.metadata.ambiguousStem");
|
|
1559
|
+
var _a, _b;
|
|
1560
|
+
var AmbiguousMetadataStemError = class extends (_b = Error, _a = AMBIGUOUS_METADATA_STEM_BRAND, _b) {
|
|
1561
|
+
constructor(type, stem, paths) {
|
|
1562
|
+
const sorted = [...paths].sort();
|
|
1563
|
+
super(
|
|
1564
|
+
`Ambiguous metadata name \`${stem}\` for type \`${type}\`: ${sorted.length} files resolve to the same name \u2014 ${sorted.map((p) => `\`${p}\``).join(", ")}. Only the first would ever be served (extension precedence: .json, .yaml, .yml, .ts, .js), so the others are listed and unreachable. Delete or rename all but one.`
|
|
1565
|
+
);
|
|
1566
|
+
/** Brand — see the module doc on why this is not `instanceof`. */
|
|
1567
|
+
this[_a] = true;
|
|
1568
|
+
/** ADR-0112 wire code. */
|
|
1569
|
+
this.code = AMBIGUOUS_METADATA_STEM_CODE;
|
|
1570
|
+
/** HTTP status a transport should answer. */
|
|
1571
|
+
this.status = AMBIGUOUS_METADATA_STEM_STATUS;
|
|
1572
|
+
this.name = "AmbiguousMetadataStemError";
|
|
1573
|
+
this.type = type;
|
|
1574
|
+
this.stem = stem;
|
|
1575
|
+
this.paths = sorted;
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
function isAmbiguousMetadataStemError(err) {
|
|
1579
|
+
return typeof err === "object" && err !== null && err[AMBIGUOUS_METADATA_STEM_BRAND] === true;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1551
1582
|
// src/endpoint-matcher.ts
|
|
1552
1583
|
var import_api = require("@objectstack/spec/api");
|
|
1553
1584
|
|
|
@@ -1698,6 +1729,8 @@ var EndpointMatcher = class {
|
|
|
1698
1729
|
// src/view-container-expansion.ts
|
|
1699
1730
|
var import_spec2 = require("@objectstack/spec");
|
|
1700
1731
|
var import_shared2 = require("@objectstack/spec/shared");
|
|
1732
|
+
|
|
1733
|
+
// src/view-container.ts
|
|
1701
1734
|
function deriveViewContainerObject(container) {
|
|
1702
1735
|
if (!container || typeof container !== "object") return void 0;
|
|
1703
1736
|
const c = container;
|
|
@@ -1705,6 +1738,8 @@ function deriveViewContainerObject(container) {
|
|
|
1705
1738
|
const byName = typeof c.name === "string" && c.name ? c.name : void 0;
|
|
1706
1739
|
return own ?? c?.list?.data?.object ?? c?.form?.data?.object ?? byName;
|
|
1707
1740
|
}
|
|
1741
|
+
|
|
1742
|
+
// src/view-container-expansion.ts
|
|
1708
1743
|
function expandRuntimeViewContainer(data) {
|
|
1709
1744
|
if (!(0, import_spec2.isAggregatedViewContainer)(data)) return [];
|
|
1710
1745
|
const container = data;
|
|
@@ -2410,6 +2445,9 @@ var _MetadataManager = class _MetadataManager {
|
|
|
2410
2445
|
await this.admitLoaderItems(loader, type, items);
|
|
2411
2446
|
this.reportLoaderReadRecovered(loader.contract.name);
|
|
2412
2447
|
} catch (e) {
|
|
2448
|
+
if (isAmbiguousMetadataStemError(e)) {
|
|
2449
|
+
throw e;
|
|
2450
|
+
}
|
|
2413
2451
|
degraded = true;
|
|
2414
2452
|
errors.push(`${loader.contract.name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
2415
2453
|
this.reportLoaderReadFailure(loader.contract.name, type, e);
|
|
@@ -2747,6 +2785,30 @@ var _MetadataManager = class _MetadataManager {
|
|
|
2747
2785
|
}
|
|
2748
2786
|
/**
|
|
2749
2787
|
* List all names of metadata items of a given type
|
|
2788
|
+
*
|
|
2789
|
+
* ## [#14423] One loader's fault does not take the whole enumeration down
|
|
2790
|
+
*
|
|
2791
|
+
* This loop used to be bare — `const result = await loader.list(type)` with
|
|
2792
|
+
* no `try`, while the two sibling plural reads (`list()` via
|
|
2793
|
+
* {@link admitLoaderItems}, and {@link loadMany}) have carried a per-loader
|
|
2794
|
+
* `catch` since #5108. That asymmetry is the defect, independent of any one
|
|
2795
|
+
* caller: the SAME storage outage was swallowed by one plural read and
|
|
2796
|
+
* thrown out of the other, so which answer a caller got depended only on
|
|
2797
|
+
* which method it happened to call. A caller reading both — the action
|
|
2798
|
+
* governance audit is one — saw `loadMany` report a short-but-successful
|
|
2799
|
+
* set and `listNames` throw, and had no way to tell that one fact was
|
|
2800
|
+
* behind both.
|
|
2801
|
+
*
|
|
2802
|
+
* Same shape as `loadMany`'s, deliberately, down to the helpers: the outage
|
|
2803
|
+
* is spoken once per loader through {@link reportLoaderReadFailure} and
|
|
2804
|
+
* un-said through {@link reportLoaderReadRecovered}. ⛔ Not a third spelling
|
|
2805
|
+
* for "a loader faulted" — a second vocabulary for one event is how the two
|
|
2806
|
+
* reads drifted apart in the first place.
|
|
2807
|
+
*
|
|
2808
|
+
* The degradation is the same one `list()` documents and is graded the same
|
|
2809
|
+
* way (AGENTS.md → "Degradation log levels"): the caller still gets an
|
|
2810
|
+
* array, nothing 500s, and the set is quietly short — so it is reported at
|
|
2811
|
+
* `error`, by the shared helper, rather than being re-graded here.
|
|
2750
2812
|
*/
|
|
2751
2813
|
async listNames(type) {
|
|
2752
2814
|
type = (0, import_core.canonicalMetadataServiceType)(type);
|
|
@@ -2758,8 +2820,16 @@ var _MetadataManager = class _MetadataManager {
|
|
|
2758
2820
|
}
|
|
2759
2821
|
}
|
|
2760
2822
|
for (const loader of this.loaders.values()) {
|
|
2761
|
-
|
|
2762
|
-
|
|
2823
|
+
try {
|
|
2824
|
+
const result = await loader.list(type);
|
|
2825
|
+
result.forEach((item) => names.add(item));
|
|
2826
|
+
this.reportLoaderReadRecovered(loader.contract.name);
|
|
2827
|
+
} catch (e) {
|
|
2828
|
+
if (isAmbiguousMetadataStemError(e)) {
|
|
2829
|
+
throw e;
|
|
2830
|
+
}
|
|
2831
|
+
this.reportLoaderReadFailure(loader.contract.name, type, e);
|
|
2832
|
+
}
|
|
2763
2833
|
}
|
|
2764
2834
|
return Array.from(names);
|
|
2765
2835
|
}
|
|
@@ -3619,6 +3689,99 @@ var _MetadataManager = class _MetadataManager {
|
|
|
3619
3689
|
}
|
|
3620
3690
|
return results;
|
|
3621
3691
|
}
|
|
3692
|
+
/**
|
|
3693
|
+
* [#14423] {@link loadMany}, read under the identity the STORE holds each
|
|
3694
|
+
* item by — the keyed plural read, beside the unkeyed one.
|
|
3695
|
+
*
|
|
3696
|
+
* ## Why a second method and not a widened `loadMany`
|
|
3697
|
+
*
|
|
3698
|
+
* `loadMany` keys nothing: it returns bodies, and every consumer that needs
|
|
3699
|
+
* an identity reads `body.name` off them. #14205 already ruled what identity
|
|
3700
|
+
* IS — the key the store holds the item under (`register(type, name, data)`
|
|
3701
|
+
* takes it as the ARGUMENT, and a body is not required to name itself) — so
|
|
3702
|
+
* `body.name` is a guess that happens to be right for most items and drops
|
|
3703
|
+
* the rest ENTIRELY: an item whose body carries no `name` is served by
|
|
3704
|
+
* `load(type, name)` and is not nameable from `loadMany`'s answer at all.
|
|
3705
|
+
*
|
|
3706
|
+
* Widening `loadMany`'s return would fix that and break every consumer of a
|
|
3707
|
+
* published shape (the ones counted on this card all read `body.name` as the
|
|
3708
|
+
* identity). So this is additive: `loadMany`'s return shape is untouched,
|
|
3709
|
+
* and a caller that needs the key asks for the key.
|
|
3710
|
+
*
|
|
3711
|
+
* ## What it reads — the same population `loadMany` reads
|
|
3712
|
+
*
|
|
3713
|
+
* Loaders only, deliberately, so this is `loadMany` keyed and nothing more.
|
|
3714
|
+
* It is NOT `list()`/{@link listNames}, which also merge the in-memory
|
|
3715
|
+
* `register()` registry; a caller wanting that set has those. Reading the
|
|
3716
|
+
* loaders alone is also what makes this the enumerable twin of
|
|
3717
|
+
* {@link loadDiagnosed}, which walks the same loaders by name — that pairing
|
|
3718
|
+
* is the point on the audit side of #14423, where an enumeration and a
|
|
3719
|
+
* by-name read that disagree about a population make one subsystem accuse
|
|
3720
|
+
* another of a defect neither has.
|
|
3721
|
+
*
|
|
3722
|
+
* ## Delegate first, fall back second — and why that order is not a style
|
|
3723
|
+
*
|
|
3724
|
+
* Per loader: {@link MetadataLoader.loadManyKeyed} where the loader offers
|
|
3725
|
+
* one, else its `list()` + a per-name `load()`. Measured, on
|
|
3726
|
+
* `DatabaseLoader`: the keyed method shares `loadMany`'s single query
|
|
3727
|
+
* (`{find:1, findOne:0}` — zero extra cost), while enumerate-then-read-each
|
|
3728
|
+
* on that same loader is a real N+1 (`{find:1, findOne:5}` for five items).
|
|
3729
|
+
* The fallback exists for loaders that cannot produce keys at all
|
|
3730
|
+
* (`RemoteLoader`'s wire format carries bodies only), and it recovers the
|
|
3731
|
+
* nameless item the pre-#14205 `loadMany`-and-key-by-`body.name` fallback
|
|
3732
|
+
* drops — which is why it is `list()` + `load()` and not `loadMany()`.
|
|
3733
|
+
*
|
|
3734
|
+
* ## Failure posture
|
|
3735
|
+
*
|
|
3736
|
+
* Per-loader `try`/`catch`, the same seam and the same helpers as
|
|
3737
|
+
* {@link loadMany} and `list()` — one loader's outage does not take the
|
|
3738
|
+
* enumeration down, and it is reported once through
|
|
3739
|
+
* {@link reportLoaderReadFailure} rather than in a third vocabulary.
|
|
3740
|
+
* Earlier loaders win a key collision, mirroring `list()`.
|
|
3741
|
+
*/
|
|
3742
|
+
async loadManyKeyed(type, options) {
|
|
3743
|
+
const items = /* @__PURE__ */ new Map();
|
|
3744
|
+
for (const loader of this.loaders.values()) {
|
|
3745
|
+
try {
|
|
3746
|
+
await this.admitKeyedLoaderItems(loader, type, items, options);
|
|
3747
|
+
this.reportLoaderReadRecovered(loader.contract.name);
|
|
3748
|
+
} catch (e) {
|
|
3749
|
+
this.reportLoaderReadFailure(loader.contract.name, type, e);
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
return Array.from(items, ([name, data]) => ({ name, data }));
|
|
3753
|
+
}
|
|
3754
|
+
/**
|
|
3755
|
+
* Merge ONE loader's answer for `type` into `items`, keyed by that loader's
|
|
3756
|
+
* own key for each item — {@link loadManyKeyed}'s per-loader body.
|
|
3757
|
+
*
|
|
3758
|
+
* Distinct from {@link admitLoaderItems} on exactly one axis, and that axis
|
|
3759
|
+
* is the whole of #14423: the fallback for a loader with no
|
|
3760
|
+
* `loadManyKeyed`. `admitLoaderItems` falls back to `loadMany` keyed by
|
|
3761
|
+
* `data.name` — the pre-#14205 behaviour, verbatim, which drops a nameless
|
|
3762
|
+
* body. Here the fallback is `list()` + a per-name `load()`, so a loader
|
|
3763
|
+
* that cannot enumerate keys and bodies together still answers with both.
|
|
3764
|
+
*
|
|
3765
|
+
* Read failures are NOT caught here — the caller owns that verdict, as in
|
|
3766
|
+
* {@link admitLoaderItems}.
|
|
3767
|
+
*/
|
|
3768
|
+
async admitKeyedLoaderItems(loader, type, items, options) {
|
|
3769
|
+
if (typeof loader.loadManyKeyed === "function") {
|
|
3770
|
+
const keyed = await loader.loadManyKeyed(type, options);
|
|
3771
|
+
for (const entry of keyed) {
|
|
3772
|
+
if (!entry || typeof entry.name !== "string" || entry.name === "") continue;
|
|
3773
|
+
if (items.has(entry.name)) continue;
|
|
3774
|
+
items.set(entry.name, entry.data);
|
|
3775
|
+
}
|
|
3776
|
+
return;
|
|
3777
|
+
}
|
|
3778
|
+
for (const name of await loader.list(type)) {
|
|
3779
|
+
if (typeof name !== "string" || name === "" || items.has(name)) continue;
|
|
3780
|
+
const result = await loader.load(type, name, options);
|
|
3781
|
+
if (result?.data == null) continue;
|
|
3782
|
+
items.set(name, result.data);
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3622
3785
|
/**
|
|
3623
3786
|
* Save metadata item to a loader
|
|
3624
3787
|
*/
|
|
@@ -4251,33 +4414,34 @@ var _FilesystemLoader = class _FilesystemLoader {
|
|
|
4251
4414
|
const globPatterns = patterns.map(
|
|
4252
4415
|
(pattern) => path.join(typeDir, pattern)
|
|
4253
4416
|
);
|
|
4417
|
+
const files = [];
|
|
4254
4418
|
for (const pattern of globPatterns) {
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
const content = await fs.readFile(file, "utf-8");
|
|
4265
|
-
const format = this.detectFormat(file);
|
|
4266
|
-
const serializer = this.getSerializer(format);
|
|
4267
|
-
if (serializer) {
|
|
4268
|
-
const data = serializer.deserialize(content);
|
|
4269
|
-
items.push({ file, data });
|
|
4270
|
-
}
|
|
4271
|
-
} catch (error) {
|
|
4272
|
-
this.logger?.warn("Failed to load file", {
|
|
4273
|
-
file,
|
|
4274
|
-
error: error instanceof Error ? error.message : String(error)
|
|
4275
|
-
});
|
|
4276
|
-
}
|
|
4277
|
-
}
|
|
4419
|
+
files.push(
|
|
4420
|
+
...await (0, import_glob.glob)(pattern, {
|
|
4421
|
+
ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*", "**/*[*]*"],
|
|
4422
|
+
nodir: true
|
|
4423
|
+
})
|
|
4424
|
+
);
|
|
4425
|
+
}
|
|
4426
|
+
this.resolvableNames(type, typeDir, files);
|
|
4427
|
+
for (const file of files) {
|
|
4278
4428
|
if (limit && items.length >= limit) {
|
|
4279
4429
|
break;
|
|
4280
4430
|
}
|
|
4431
|
+
try {
|
|
4432
|
+
const content = await fs.readFile(file, "utf-8");
|
|
4433
|
+
const format = this.detectFormat(file);
|
|
4434
|
+
const serializer = this.getSerializer(format);
|
|
4435
|
+
if (serializer) {
|
|
4436
|
+
const data = serializer.deserialize(content);
|
|
4437
|
+
items.push({ file, data });
|
|
4438
|
+
}
|
|
4439
|
+
} catch (error) {
|
|
4440
|
+
this.logger?.warn("Failed to load file", {
|
|
4441
|
+
file,
|
|
4442
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4443
|
+
});
|
|
4444
|
+
}
|
|
4281
4445
|
}
|
|
4282
4446
|
return items;
|
|
4283
4447
|
} catch (error) {
|
|
@@ -4346,13 +4510,13 @@ var _FilesystemLoader = class _FilesystemLoader {
|
|
|
4346
4510
|
*/
|
|
4347
4511
|
async list(type) {
|
|
4348
4512
|
const typeDir = path.join(this.rootDir, type);
|
|
4513
|
+
let files;
|
|
4349
4514
|
try {
|
|
4350
|
-
|
|
4515
|
+
files = await (0, import_glob.glob)("**/*", {
|
|
4351
4516
|
cwd: typeDir,
|
|
4352
4517
|
ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*"],
|
|
4353
4518
|
nodir: true
|
|
4354
4519
|
});
|
|
4355
|
-
return files.map((file) => this.resolvableNameForPath(typeDir, path.join(typeDir, file))).filter((name) => name !== null);
|
|
4356
4520
|
} catch (error) {
|
|
4357
4521
|
this.logger?.error("Failed to list", void 0, {
|
|
4358
4522
|
type,
|
|
@@ -4360,6 +4524,7 @@ var _FilesystemLoader = class _FilesystemLoader {
|
|
|
4360
4524
|
});
|
|
4361
4525
|
return [];
|
|
4362
4526
|
}
|
|
4527
|
+
return this.resolvableNames(type, typeDir, files.map((file) => path.join(typeDir, file)));
|
|
4363
4528
|
}
|
|
4364
4529
|
async save(type, name, data, options) {
|
|
4365
4530
|
const startTime = Date.now();
|
|
@@ -4484,6 +4649,49 @@ var _FilesystemLoader = class _FilesystemLoader {
|
|
|
4484
4649
|
}
|
|
4485
4650
|
return _FilesystemLoader.nameFromFilename(rel);
|
|
4486
4651
|
}
|
|
4652
|
+
/**
|
|
4653
|
+
* [#14921] The names this loader reports for `files` — and the ONE place an
|
|
4654
|
+
* ambiguous stem is refused.
|
|
4655
|
+
*
|
|
4656
|
+
* Shared by {@link list} and {@link loadManyEntries} so the two can never
|
|
4657
|
+
* disagree about which trees are admissible: a stem that `list()` refuses
|
|
4658
|
+
* must not still be walked and returned as two bodies by `loadMany()`, which
|
|
4659
|
+
* is exactly the split this card measured.
|
|
4660
|
+
*
|
|
4661
|
+
* Refuses on the FIRST colliding name in sorted order, so a tree holding more
|
|
4662
|
+
* than one collision always names the same one — a refusal that moves
|
|
4663
|
+
* between runs reads as flakiness rather than as the fixed authoring error it
|
|
4664
|
+
* is. Paths are deduplicated because two overlapping `patterns` legitimately
|
|
4665
|
+
* match one file twice, and counting that as a collision would refuse a
|
|
4666
|
+
* perfectly good tree.
|
|
4667
|
+
*
|
|
4668
|
+
* ⛔ Not a precedence resolver. Picking a winner here is what the ruling
|
|
4669
|
+
* declined (option 2, keep the precedence and log): the loser would stay
|
|
4670
|
+
* unreachable and the listed set would stay different from the addressable
|
|
4671
|
+
* one.
|
|
4672
|
+
*/
|
|
4673
|
+
resolvableNames(type, typeDir, files) {
|
|
4674
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4675
|
+
for (const file of files) {
|
|
4676
|
+
const name = this.resolvableNameForPath(typeDir, file);
|
|
4677
|
+
if (name === null) {
|
|
4678
|
+
continue;
|
|
4679
|
+
}
|
|
4680
|
+
let paths = byName.get(name);
|
|
4681
|
+
if (!paths) {
|
|
4682
|
+
paths = /* @__PURE__ */ new Set();
|
|
4683
|
+
byName.set(name, paths);
|
|
4684
|
+
}
|
|
4685
|
+
paths.add(file);
|
|
4686
|
+
}
|
|
4687
|
+
for (const name of [...byName.keys()].sort()) {
|
|
4688
|
+
const paths = byName.get(name);
|
|
4689
|
+
if (paths.size > 1) {
|
|
4690
|
+
throw new AmbiguousMetadataStemError(type, name, [...paths]);
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
return [...byName.keys()];
|
|
4694
|
+
}
|
|
4487
4695
|
/**
|
|
4488
4696
|
* Find file for a given type and name
|
|
4489
4697
|
*/
|
|
@@ -5571,9 +5779,43 @@ var RemoteLoader = class {
|
|
|
5571
5779
|
format: "json"
|
|
5572
5780
|
};
|
|
5573
5781
|
}
|
|
5782
|
+
/**
|
|
5783
|
+
* [#15037] Report only the names that ARE names.
|
|
5784
|
+
*
|
|
5785
|
+
* This read used to be `loadMany<{ name: string }>(type)` mapped straight to
|
|
5786
|
+
* `items.map(i => i.name)`. That type argument is an ASSERTION about bodies
|
|
5787
|
+
* that arrived over HTTP, and nothing checked it: a body with no top-level
|
|
5788
|
+
* `name` yielded `undefined`, which went into an array this signature
|
|
5789
|
+
* declares as `string[]` and reached consumers through
|
|
5790
|
+
* `MetadataManager.listNames()` — a runtime violation of a declared type,
|
|
5791
|
+
* not an untidy entry. A consumer that keys by it, lower-cases it, or feeds
|
|
5792
|
+
* it back to a by-name `load()` gets `undefined` where the type says it
|
|
5793
|
+
* cannot be.
|
|
5794
|
+
*
|
|
5795
|
+
* The guard is `DatabaseLoader.list()`'s, one file away: same cast-then-map
|
|
5796
|
+
* spelling, one `typeof` filter behind it. Silently dropping is the landed
|
|
5797
|
+
* direction, not a preference — `DatabaseLoader` drops rather than throws,
|
|
5798
|
+
* and `FilesystemLoader`'s narrowing carries a maintainer ruling (via the
|
|
5799
|
+
* director seat on #14486, 2026-09-02) that chose narrowing (A) over
|
|
5800
|
+
* refusing loudly (B), because a name in the list that the door answers
|
|
5801
|
+
* `null` for is the silent failure an author reads as their own typo. An
|
|
5802
|
+
* `undefined` here is the extreme form of that name.
|
|
5803
|
+
*
|
|
5804
|
+
* ⛔ NOT copied from the siblings: `MemoryLoader` answers with its store
|
|
5805
|
+
* keys, and #14205 ruled that identity is the key the store holds an item
|
|
5806
|
+
* under rather than `body.name`. This loader reads over HTTP and holds no
|
|
5807
|
+
* store key, so `body.name` is the only identity it has — the list is
|
|
5808
|
+
* narrowed to agree with the door instead. `loadMany()` is deliberately
|
|
5809
|
+
* untouched: it keys nothing, so a nameless body is still served there.
|
|
5810
|
+
*
|
|
5811
|
+
* The predicate is spelled as a type guard, and the mapped element type left
|
|
5812
|
+
* `unknown`, so `tsc` PROVES the declared `string[]` instead of a cast
|
|
5813
|
+
* asserting it — otherwise the compiler reads the filter as always-true and
|
|
5814
|
+
* a later reader deletes it as dead.
|
|
5815
|
+
*/
|
|
5574
5816
|
async list(type) {
|
|
5575
5817
|
const items = await this.loadMany(type);
|
|
5576
|
-
return items.map((
|
|
5818
|
+
return items.map((item) => item.name).filter((name) => typeof name === "string");
|
|
5577
5819
|
}
|
|
5578
5820
|
async save(type, name, data, _options) {
|
|
5579
5821
|
const response = await fetch(`${this.baseUrl}/${type}/${name}`, {
|
|
@@ -5613,9 +5855,9 @@ var HistoryCleanupManager = class {
|
|
|
5613
5855
|
return;
|
|
5614
5856
|
}
|
|
5615
5857
|
const intervalMs = (this.policy.cleanupIntervalHours ?? 24) * 60 * 60 * 1e3;
|
|
5616
|
-
void this
|
|
5858
|
+
void runCleanupAndReport(this);
|
|
5617
5859
|
this.cleanupTimer = setInterval(() => {
|
|
5618
|
-
void this
|
|
5860
|
+
void runCleanupAndReport(this);
|
|
5619
5861
|
}, intervalMs);
|
|
5620
5862
|
}
|
|
5621
5863
|
/**
|
|
@@ -5642,7 +5884,7 @@ var HistoryCleanupManager = class {
|
|
|
5642
5884
|
try {
|
|
5643
5885
|
if (this.policy.maxAgeDays) {
|
|
5644
5886
|
const cutoffDate = /* @__PURE__ */ new Date();
|
|
5645
|
-
cutoffDate.
|
|
5887
|
+
cutoffDate.setUTCDate(cutoffDate.getUTCDate() - this.policy.maxAgeDays);
|
|
5646
5888
|
const cutoffISO = cutoffDate.toISOString();
|
|
5647
5889
|
const filter = {
|
|
5648
5890
|
recorded_at: { $lt: cutoffISO }
|
|
@@ -5762,7 +6004,7 @@ var HistoryCleanupManager = class {
|
|
|
5762
6004
|
if (organizationId) baseWhere.organization_id = organizationId;
|
|
5763
6005
|
if (this.policy.maxAgeDays) {
|
|
5764
6006
|
const cutoffDate = /* @__PURE__ */ new Date();
|
|
5765
|
-
cutoffDate.
|
|
6007
|
+
cutoffDate.setUTCDate(cutoffDate.getUTCDate() - this.policy.maxAgeDays);
|
|
5766
6008
|
const cutoffISO = cutoffDate.toISOString();
|
|
5767
6009
|
const filter = {
|
|
5768
6010
|
recorded_at: { $lt: cutoffISO },
|
|
@@ -5809,6 +6051,23 @@ var HistoryCleanupManager = class {
|
|
|
5809
6051
|
};
|
|
5810
6052
|
}
|
|
5811
6053
|
};
|
|
6054
|
+
async function runCleanupAndReport(manager) {
|
|
6055
|
+
let outcome;
|
|
6056
|
+
try {
|
|
6057
|
+
outcome = await manager.runCleanup();
|
|
6058
|
+
} catch (error) {
|
|
6059
|
+
console.error(
|
|
6060
|
+
"History cleanup: the run did not complete, so no history row past the retention policy was deleted and the table keeps growing while the system reports healthy. Fix: the cause below comes from the configured data driver, not from the retention policy; call `runCleanup()` directly to reproduce it. Cause:",
|
|
6061
|
+
error
|
|
6062
|
+
);
|
|
6063
|
+
return;
|
|
6064
|
+
}
|
|
6065
|
+
if (outcome.errors > 0) {
|
|
6066
|
+
console.error(
|
|
6067
|
+
`History cleanup: ${outcome.errors} delete operation(s) failed and ${outcome.deleted} row(s) were deleted. The history rows those deletes were meant to remove are still in the table, nothing retries them, and the table grows past the retention policy while the system keeps reporting healthy. Fix: check the data driver delete path for the metadata history table. The per-failure causes are not carried out of \`runCleanup()\`, so reproduce them against the driver directly.`
|
|
6068
|
+
);
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
5812
6071
|
|
|
5813
6072
|
// src/migration/index.ts
|
|
5814
6073
|
var migration_exports = {};
|
|
@@ -5867,6 +6126,9 @@ var MigrationExecutor = class {
|
|
|
5867
6126
|
};
|
|
5868
6127
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5869
6128
|
0 && (module.exports = {
|
|
6129
|
+
AMBIGUOUS_METADATA_STEM_CODE,
|
|
6130
|
+
AMBIGUOUS_METADATA_STEM_STATUS,
|
|
6131
|
+
AmbiguousMetadataStemError,
|
|
5870
6132
|
DatabaseLoader,
|
|
5871
6133
|
FilesystemLoader,
|
|
5872
6134
|
HistoryCleanupManager,
|
|
@@ -5884,6 +6146,7 @@ var MigrationExecutor = class {
|
|
|
5884
6146
|
calculateChecksum,
|
|
5885
6147
|
deriveViewContainerObject,
|
|
5886
6148
|
generateDiffSummary,
|
|
5887
|
-
generateSimpleDiff
|
|
6149
|
+
generateSimpleDiff,
|
|
6150
|
+
isAmbiguousMetadataStemError
|
|
5888
6151
|
});
|
|
5889
6152
|
//# sourceMappingURL=node.cjs.map
|