@objectstack/driver-sql 10.0.0 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/sql-driver.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { SqlDriver } from './sql-driver.js';\n\nexport { SqlDriver };\nexport type {\n SqlDriverConfig,\n IntrospectedSchema,\n IntrospectedTable,\n IntrospectedColumn,\n IntrospectedForeignKey,\n} from './sql-driver.js';\n\nexport default {\n id: 'com.objectstack.driver.sql',\n version: '1.0.0',\n\n onEnable: async (context: any) => {\n const { logger, config, drivers } = context;\n logger.info('[SQL Driver] Initializing...');\n\n if (drivers) {\n const driver = new SqlDriver(config);\n drivers.register(driver);\n logger.info(`[SQL Driver] Registered driver: ${driver.name}`);\n } else {\n logger.warn('[SQL Driver] No driver registry found in context.');\n }\n },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * SQL Driver for ObjectStack\n *\n * Implements the standard IDataDriver from @objectstack/spec via Knex.js.\n * Supports PostgreSQL, MySQL, SQLite, and other SQL databases.\n */\n\nimport type { QueryAST, DriverOptions, SchemaMode } from '@objectstack/spec/data';\nimport { parseAutonumberFormat, renderAutonumber, missingFieldValues, type AutonumberToken } from '@objectstack/spec/data';\nimport type { IDataDriver } from '@objectstack/spec/contracts';\nimport { StorageNameMapping } from '@objectstack/spec/system';\nimport { ExternalSchemaModeViolationError } from '@objectstack/spec/shared';\nimport knex, { Knex } from 'knex';\nimport { nanoid } from 'nanoid';\nimport { createHash } from 'node:crypto';\n\n/**\n * Default ID length for auto-generated IDs.\n */\nconst DEFAULT_ID_LENGTH = 16;\n\n/**\n * Internal table that persists per-(object, tenant, field) auto-number\n * counters so sequences are monotonic, tenant-isolated, and resilient to\n * concurrent writers. Lazily created on first autonumber-bearing insert.\n */\nconst SEQUENCES_TABLE = '_objectstack_sequences';\n\n/**\n * Sentinel tenant_id used when an object has no tenant field (org-less\n * objects like Setup-side singletons). Keeps the (object, tenant, field)\n * primary key non-null.\n */\nconst GLOBAL_TENANT = '__global__';\n\n/**\n * Field types whose value is an array or object and must be stored as a JSON\n * column (and JSON-(de)serialized at the driver boundary). SINGLE SOURCE for\n * both the DDL column-type switch and `isJsonField` so the two can't drift —\n * the drift between them is exactly what let array-valued fields (multiselect/\n * checkboxes/tags/repeater/vector) reach the SQLite binder un-serialized and\n * crash with \"SQLite3 can only bind numbers, strings, bigints, buffers, and\n * null\" (#field-zoo). `image`/`file`/`avatar`/`video`/`audio` hold structured\n * upload metadata; `composite`/`address`/`location`/`record` are objects; the\n * rest are arrays.\n */\nconst JSON_COLUMN_TYPES = new Set<string>([\n 'json', 'object', 'array', 'record',\n 'image', 'file', 'avatar', 'video', 'audio',\n 'location', 'address', 'composite',\n 'multiselect', 'checkboxes', 'tags', 'repeater', 'vector',\n]);\n\n/**\n * Field types whose value is a numeric scalar. SINGLE SOURCE for the DDL\n * column-type switch (these map to INTEGER/REAL columns) and the read-side\n * coercion registry (`numericFields`).\n *\n * The read coercion exists so the fix is robust on SQLite even when the column\n * predates it: a `rating`/`slider`/`progress` column created before #2025 has\n * TEXT affinity and returns '4' not 4, and SQLite never alters a column's type\n * in-place (the reconciler only ADDS columns). Coercing numeric-looking strings\n * back to numbers on read transparently repairs those legacy rows — mirroring\n * how `dateFields` repairs legacy timestamp-typed `Field.date` rows — so the\n * type fidelity no longer depends on column affinity alone. `toggle`/`record`\n * already self-heal this way via `booleanFields`/`jsonFields`; this closes the\n * gap for the numeric scalars.\n */\nconst NUMERIC_SCALAR_TYPES = new Set<string>([\n 'integer', 'int',\n 'float', 'number', 'currency', 'percent', 'summary',\n 'rating', 'slider', 'progress',\n]);\n\n// ── Introspection Types ──────────────────────────────────────────────────────\n\nexport interface IntrospectedColumn {\n name: string;\n type: string;\n nullable: boolean;\n defaultValue?: unknown;\n isPrimary?: boolean;\n isUnique?: boolean;\n maxLength?: number;\n}\n\nexport interface IntrospectedForeignKey {\n columnName: string;\n referencedTable: string;\n referencedColumn: string;\n constraintName?: string;\n}\n\nexport interface IntrospectedTable {\n name: string;\n columns: IntrospectedColumn[];\n foreignKeys: IntrospectedForeignKey[];\n primaryKeys: string[];\n}\n\nexport interface IntrospectedSchema {\n tables: Record<string, IntrospectedTable>;\n}\n\n// ── Configuration Types ──────────────────────────────────────────────────────\n\n/**\n * SqlDriver configuration — passed directly to Knex.\n * See https://knexjs.org/guide/#configuration-options\n *\n * `schemaMode` (ADR-0015) is an ObjectStack-level concern, not a Knex\n * option: it is stripped before constructing the Knex instance and gates\n * all schema-mutating DDL. Defaults to `'managed'` when omitted, preserving\n * legacy behaviour.\n */\nexport type SqlDriverConfig = Knex.Config & { schemaMode?: SchemaMode };\n\n// ── SQL Driver ───────────────────────────────────────────────────────────────\n\n/**\n * SQL Driver for ObjectStack.\n *\n * Implements the IDataDriver contract via Knex.js for optimal SQL\n * generation against PostgreSQL, MySQL, SQLite and other SQL databases.\n */\nexport class SqlDriver implements IDataDriver {\n // IDataDriver metadata\n public readonly name: string = 'com.objectstack.driver.sql';\n public readonly version: string = '1.0.0';\n public get supports() {\n return {\n // Basic CRUD Operations\n create: true,\n read: true,\n update: true,\n delete: true,\n\n // Bulk Operations\n bulkCreate: true,\n bulkUpdate: true,\n bulkDelete: true,\n\n // Transaction & Connection Management\n transactions: true,\n savepoints: false,\n\n // Query Operations\n queryFilters: true,\n queryAggregations: true,\n /**\n * Per-granularity native date bucket support. Granularities marked\n * `false` (or absent) fall back to in-memory `bucketDateValue()` via\n * `engine.findData` — see `buildDateBucketExpr()` for the SQL emitted.\n */\n queryDateGranularity: this.dateGranularityCapabilities,\n querySorting: true,\n queryPagination: true,\n queryWindowFunctions: true,\n querySubqueries: true,\n queryCTE: false,\n joins: true,\n\n // Advanced Features\n fullTextSearch: false,\n jsonQuery: false,\n geospatialQuery: false,\n streaming: false,\n jsonFields: true,\n arrayFields: true,\n vectorSearch: false,\n // Persistent, atomic autonumber sequences via `_objectstack_sequences`\n // (see fillAutoNumberFields / getNextSequenceValue). The engine defers\n // autonumber generation to this driver — it is the single source of truth.\n autonumber: true,\n\n // Schema Management\n schemaSync: true,\n batchSchemaSync: false,\n migrations: false,\n // Object-level declared `indexes` (incl. multi-column UNIQUE) are\n // materialized during `initObjects` — see `syncDeclaredIndexes`.\n indexes: true,\n\n // Performance & Optimization\n connectionPooling: true,\n preparedStatements: true,\n queryCache: false,\n };\n }\n\n protected knex: Knex;\n protected config: Knex.Config;\n protected jsonFields: Record<string, string[]> = {};\n protected booleanFields: Record<string, string[]> = {};\n protected numericFields: Record<string, string[]> = {};\n protected dateFields: Record<string, Set<string>> = {};\n protected datetimeFields: Record<string, Set<string>> = {};\n /**\n * Federation read path (ADR-0015). For external objects whose physical\n * remote table differs from the object name, these map between the two so\n * {@link getBuilder} targets the remote table while the coercion maps above\n * stay keyed by OBJECT name (matching formatInput/formatOutput). Empty for\n * managed objects, so the managed query path is unchanged.\n */\n protected physicalTableByObject: Record<string, string> = {};\n protected physicalSchemaByObject: Record<string, string> = {};\n protected objectByPhysicalTable: Record<string, string> = {};\n /** External columnMap (ADR-0015): logical field -> physical remote column (for WHERE/ORDER BY/writes). */\n protected fieldColumnByObject: Record<string, Record<string, string>> = {};\n /** External columnMap inverse: physical remote column -> logical field (for read output remap). */\n protected columnFieldByObject: Record<string, Record<string, string>> = {};\n protected tablesWithTimestamps: Set<string> = new Set();\n /**\n * Autonumber field configs per table, captured during initObjects.\n *\n * Each entry records:\n * - `prefix` + `padWidth`: how to render the next value (`CTR-0007`)\n * - `tenantField`: the column to scope the sequence by (defaults to\n * `organization_id` if the object has that field, otherwise null →\n * sequence is shared globally for that field)\n *\n * Numbering is backed by the `_objectstack_sequences` row keyed by\n * `(object, tenant_id, field)`, not by scanning the data table on each\n * insert. The sequence row is bootstrapped from the existing MAX on\n * first use so legacy data is respected.\n */\n protected autoNumberFields: Record<\n string,\n Array<{ name: string; format: string; tokens: AutonumberToken[]; tenantField: string | null }>\n > = {};\n\n /** Whether the sequences table has been ensured this process. */\n protected sequencesTableReady = false;\n /**\n * Whether `_objectstack_sequences` is the current `key_hash`-keyed shape.\n * Set on a fresh create or a successful in-place migration. If a legacy table\n * could NOT be migrated, this stays false: fixed-prefix sequences (empty\n * scope) keep working via the legacy `(object, tenant_id, field)` key, while a\n * per-scope write raises an actionable error rather than corrupting counters.\n */\n protected sequencesHasKeyHash = false;\n /** In-flight ensure promise; deduplicates concurrent first calls. */\n protected sequencesTableEnsurePromise: Promise<void> | null = null;\n\n /**\n * Per-table tenant-isolation column. Populated during `initObjects` by\n * detecting an `organization_id` field. When set and the caller passes\n * `DriverOptions.tenantId`, the driver automatically:\n *\n * - scopes reads/updates/deletes/aggregates to that tenant\n * - injects `organization_id` on inserts that omit it\n *\n * If `tenantId` is absent (admin / seed / system path) no scope is\n * applied — preserves backward compatibility for tools that legitimately\n * need cross-tenant access. Tenant enforcement is therefore opt-in by\n * the caller, not by the driver.\n */\n protected tenantFieldByTable: Record<string, string | null> = {};\n\n /** Throttle table for missing-tenantId warnings ({object}:{op}). */\n protected tenantAuditWarned: Set<string> = new Set();\n\n /**\n * Optional logger sink for security-audit warnings. Tests inject a spy;\n * production callers wire in their preferred logger. Defaults to\n * `console.warn` so warnings surface even without setup.\n */\n protected logger: { warn: (msg: string, meta?: any) => void } = {\n warn: (msg, meta) => console.warn(msg, meta ?? ''),\n };\n\n /** Whether the underlying database is a SQLite variant (sqlite3 or better-sqlite3). */\n protected get isSqlite(): boolean {\n const c = (this.config as any).client;\n return c === 'sqlite3' || c === 'better-sqlite3';\n }\n\n /** Whether the underlying database is PostgreSQL. */\n protected get isPostgres(): boolean {\n const c = (this.config as any).client;\n return c === 'pg' || c === 'postgresql';\n }\n\n /** Whether the underlying database is MySQL. */\n protected get isMysql(): boolean {\n const c = (this.config as any).client;\n return c === 'mysql' || c === 'mysql2';\n }\n\n /**\n * Per-granularity native SQL bucket support, computed from dialect.\n *\n * Must match `bucketDateValue()` in @objectstack/objectql exactly:\n * year → 'YYYY'\n * month → 'YYYY-MM'\n * day → 'YYYY-MM-DD'\n * quarter → 'YYYY-Q[1-4]'\n * week → 'YYYY-W[01-53]' (ISO-8601)\n *\n * Granularities not listed (or set to false) fall back to in-memory bucketing\n * via engine.findData → applyInMemoryAggregation.\n */\n protected get dateGranularityCapabilities(): Record<string, boolean> {\n if (this.isPostgres) {\n return { day: true, month: true, quarter: true, year: true, week: true };\n }\n if (this.isMysql) {\n return { day: true, month: true, quarter: true, year: true, week: true };\n }\n if (this.isSqlite) {\n // SQLite's strftime gained ISO week (%V) in 3.46 (2024-05-23); play it safe\n // and bucket week in-memory. Day/month/year/quarter are universally available.\n return { day: true, month: true, quarter: true, year: true, week: false };\n }\n return {};\n }\n\n /**\n * Build SQL fragment + bindings for a date bucket expression.\n * Returns `null` when the current dialect does not support the requested\n * granularity — callers must fall back to in-memory bucketing.\n *\n * Exposed as `{sql, bindings}` (not `Knex.Raw`) so callers can both\n * `groupByRaw()` and embed the same expression inside a `select() as alias`\n * with correctly forwarded identifier bindings.\n */\n protected buildDateBucketExpr(\n field: string,\n granularity: 'day' | 'week' | 'month' | 'quarter' | 'year',\n ): { sql: string; bindings: any[] } | null {\n if (!this.dateGranularityCapabilities[granularity]) return null;\n\n if (this.isPostgres) {\n switch (granularity) {\n case 'year': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY')`, bindings: [field] };\n case 'month': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY-MM')`, bindings: [field] };\n case 'day': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY-MM-DD')`, bindings: [field] };\n case 'quarter': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY\"-Q\"Q')`, bindings: [field] };\n case 'week': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'IYYY\"-W\"IW')`, bindings: [field] };\n }\n }\n\n if (this.isMysql) {\n switch (granularity) {\n case 'year': return { sql: `date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%Y')`, bindings: [field] };\n case 'month': return { sql: `date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%Y-%m')`, bindings: [field] };\n case 'day': return { sql: `date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%Y-%m-%d')`, bindings: [field] };\n case 'quarter': return { sql: `concat(date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%Y'), '-Q', quarter(convert_tz(??, @@session.time_zone, '+00:00')))`, bindings: [field, field] };\n case 'week': return { sql: `date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%x-W%v')`, bindings: [field] };\n }\n }\n\n if (this.isSqlite) {\n switch (granularity) {\n case 'year': return { sql: `strftime('%Y', ??)`, bindings: [field] };\n case 'month': return { sql: `strftime('%Y-%m', ??)`, bindings: [field] };\n case 'day': return { sql: `strftime('%Y-%m-%d', ??)`, bindings: [field] };\n case 'quarter': return { sql: `(strftime('%Y', ??) || '-Q' || ((cast(strftime('%m', ??) as integer) - 1) / 3 + 1))`, bindings: [field, field] };\n case 'week': return null; // see capabilities note\n }\n }\n\n return null;\n }\n\n /**\n * Schema ownership mode (ADR-0015). When not `'managed'`, all\n * schema-mutating DDL is rejected by {@link assertSchemaMutable}. The\n * runtime injects this from `Datasource.schemaMode`; defaults to\n * `'managed'` so existing callers are unaffected.\n */\n protected readonly schemaMode: SchemaMode;\n\n constructor(config: SqlDriverConfig) {\n // `schemaMode` is an ObjectStack concern, not a Knex option — strip it\n // before handing the config to Knex.\n const { schemaMode, ...knexConfig } = config;\n this.schemaMode = schemaMode ?? 'managed';\n this.config = knexConfig;\n this.knex = knex(knexConfig);\n }\n\n /**\n * DDL gate (ADR-0015 §5.1). Single choke-point asserting that\n * schema-mutating DDL is only performed on a `managed` datasource.\n * Federated datasources (`external` / `validate-only`) are guests in a\n * database ObjectStack does not own and must never run DDL against.\n */\n protected assertSchemaMutable(operation: string): void {\n if (this.schemaMode !== 'managed') {\n throw new ExternalSchemaModeViolationError(\n `DDL operation '${operation}' is forbidden: datasource schemaMode='${this.schemaMode}'. ` +\n `ObjectStack never mutates the schema of an external database.`,\n );\n }\n }\n\n // ===================================\n // Lifecycle\n // ===================================\n\n async connect(): Promise<void> {\n // Ensure the database directory exists before any query can trigger\n // better-sqlite3 to open the file (e.g. loadMetaFromDb on startup).\n await this.ensureDatabaseExists();\n\n // SQLite space hygiene (ADR-0057). With the default `auto_vacuum=NONE`,\n // freed pages are never returned to the OS — a database that briefly grew\n // (e.g. high-frequency telemetry before retention sweeps run) stays at its\n // high-water mark forever. INCREMENTAL lets a later `PRAGMA incremental_vacuum`\n // (run by the lifecycle Reaper, or manually) reclaim that space without a\n // full blocking VACUUM. NOTE: auto_vacuum only changes layout on a *fresh*\n // database or after a one-time full VACUUM, so this benefits new dev DBs;\n // existing files need a single `VACUUM` to adopt it. Harmless / no-op on\n // :memory: and on already-incremental databases.\n if (this.isSqlite) {\n try {\n await this.knex.raw('PRAGMA auto_vacuum = INCREMENTAL');\n } catch (e) {\n this.logger.warn('Failed to set PRAGMA auto_vacuum=INCREMENTAL', e);\n }\n }\n }\n\n async checkHealth(): Promise<boolean> {\n try {\n await this.knex.raw('SELECT 1');\n return true;\n } catch {\n return false;\n }\n }\n\n async disconnect(): Promise<void> {\n await this.knex.destroy();\n }\n\n // ===================================\n // CRUD — DriverInterface core\n // ===================================\n\n async find(object: string, query: QueryAST, options?: DriverOptions): Promise<any[]> {\n // Build everything EXCEPT the SELECT list, so the unknown-column retry\n // below can rebuild without re-deriving where/order/pagination.\n const buildBase = () => {\n const b = this.getBuilder(object, options);\n this.applyTenantScope(b, object, options);\n\n // WHERE\n if (query.where) {\n this.applyFilters(b, query.where);\n }\n\n // ORDER BY\n if (query.orderBy && Array.isArray(query.orderBy)) {\n for (const item of query.orderBy) {\n if (item.field) {\n b.orderBy(this.remoteColumn(object, item.field, this.mapSortField(item.field)), item.order || 'asc');\n }\n }\n }\n\n // PAGINATION\n if (query.offset !== undefined) b.offset(query.offset);\n if (query.limit !== undefined) b.limit(query.limit);\n\n return b;\n };\n\n const builder = buildBase();\n\n // SELECT\n if (query.fields) {\n builder.select((query.fields as string[]).map((f: string) => this.mapSortField(f)));\n } else {\n builder.select('*');\n }\n\n let results: any[];\n try {\n results = await builder;\n } catch (error: any) {\n const isUnknownColumn =\n error.message &&\n (error.message.includes('no such column') ||\n (error.message.includes('column') && error.message.includes('does not exist')));\n if (isUnknownColumn) {\n // A `$select` projection naming a column the table lacks (e.g. a\n // generic list view auto-requesting `status`/`due_date`/`image` on an\n // object without them) makes the WHOLE query fail. Swallowing that\n // into an empty result — the old behavior — reads to the UI as \"no\n // records exist\" even though rows are there: a silent data-loss\n // footgun. When the failure came from the projection, retry once\n // selecting all columns so the real rows still come back; the unknown\n // field is simply absent from each row (it never existed). The\n // engine's unknown-field filter is the first line of defense, but it\n // only fires when the object's schema is populated in the registry —\n // this driver backstop holds even when it isn't (notably the cloud\n // multi-tenant runtime, where the projection otherwise zeroes the list).\n if (query.fields) {\n try {\n results = await buildBase().select('*');\n } catch {\n return [];\n }\n } else {\n return [];\n }\n } else {\n throw error;\n }\n }\n\n if (!Array.isArray(results)) {\n return [];\n }\n\n // formatOutput is dialect-agnostic for `Field.date` (ADR-0053 Phase 1);\n // its json/boolean deserialisation stays SQLite-gated internally. Run it\n // for every dialect so reads match `findOne` and date columns come back\n // as `YYYY-MM-DD`.\n for (const row of results) {\n this.formatOutput(object, row);\n }\n return results;\n }\n\n async findOne(object: string, query: QueryAST, options?: DriverOptions): Promise<any> {\n // When called with a string/number id fall back gracefully\n if (typeof query === 'string' || typeof query === 'number') {\n const builder = this.getBuilder(object, options).where('id', query);\n this.applyTenantScope(builder, object, options);\n const res = await builder.first();\n return this.formatOutput(object, res) || null;\n }\n\n if (query && typeof query === 'object') {\n const results = await this.find(object, { ...query, limit: 1 }, options);\n return results[0] || null;\n }\n\n return null;\n }\n\n /**\n * Stream records matching a structured query.\n * NOTE: Current implementation fetches all results then yields them.\n * TODO: Use Knex .stream() for true cursor-based streaming on large datasets.\n */\n async *findStream(object: string, query: QueryAST, options?: DriverOptions): AsyncGenerator<Record<string, any>> {\n const results = await this.find(object, query, options);\n for (const row of results) {\n yield row;\n }\n }\n\n async create(object: string, data: Record<string, any>, options?: DriverOptions): Promise<any> {\n const { _id, ...rest } = data;\n const toInsert = { ...rest };\n\n if (_id !== undefined && toInsert.id === undefined) {\n toInsert.id = _id;\n } else if (toInsert.id === undefined) {\n toInsert.id = nanoid(DEFAULT_ID_LENGTH);\n }\n\n this.auditMissingTenant(object, 'create', options);\n this.injectTenantOnInsert(object, toInsert, options);\n await this.fillAutoNumberFields(object, toInsert, options);\n\n const builder = this.getBuilder(object, options);\n const formatted = this.applyWriteColumnMap(object, this.formatInput(object, toInsert));\n\n const result = await builder.insert(formatted).returning('*');\n return this.formatOutput(object, result[0]);\n }\n\n /**\n * Ensure the sequence-counter table exists. Idempotent and cheap after\n * the first call (cached via `sequencesTableReady`).\n *\n * The row key is `key_hash` — a SHA-256 of `(object, tenant_id, field, scope)`\n * where `scope` is the rendered autonumber prefix (date/field tokens before\n * the `{0000}` slot), so a new day/group/parent starts a fresh counter. A\n * single 64-char hashed primary key (rather than the four raw columns, which\n * blow past MySQL's 3072-byte index limit under utf8mb4 and bound how long a\n * `{field}` scope may be) keys every dialect uniformly and lets `scope` be a\n * generous non-indexed column. Fixed-prefix formats use the empty scope and\n * keep their single global counter (backward compatible).\n */\n protected async ensureSequencesTable(): Promise<void> {\n if (this.sequencesTableReady) return;\n if (this.sequencesTableEnsurePromise) {\n await this.sequencesTableEnsurePromise;\n return;\n }\n this.sequencesTableEnsurePromise = (async () => {\n const exists = await this.knex.schema.hasTable(SEQUENCES_TABLE);\n if (!exists) {\n try {\n await this.createSequencesTable(SEQUENCES_TABLE);\n this.sequencesHasKeyHash = true;\n } catch (err: any) {\n // Race or cross-process create — re-check existence; ignore\n // \"already exists\" errors from any dialect.\n const stillMissing = !(await this.knex.schema.hasTable(SEQUENCES_TABLE));\n if (stillMissing) throw err;\n // A racing creator may have used an older schema. Migrate in place.\n await this.ensureSequencesKeyHashShape();\n }\n } else {\n // Pre-existing table may predate the `key_hash`/`scope` shape. Migrate.\n await this.ensureSequencesKeyHashShape();\n }\n this.sequencesTableReady = true;\n })();\n try {\n await this.sequencesTableEnsurePromise;\n } finally {\n this.sequencesTableEnsurePromise = null;\n }\n }\n\n /** SHA-256 of the composite counter key — the table's single-column PK. */\n protected sequenceKeyHash(object: string, tenantId: string, field: string, scope: string): string {\n return createHash('sha256')\n .update(`${object}\\u001f${tenantId}\\u001f${field}\\u001f${scope}`)\n .digest('hex');\n }\n\n /** Create the current `key_hash`-keyed sequences table shape. */\n protected async createSequencesTable(table: string): Promise<void> {\n await this.knex.schema.createTable(table, (t) => {\n t.string('key_hash', 64).notNullable().primary();\n t.string('object').notNullable();\n t.string('tenant_id').notNullable();\n t.string('field').notNullable();\n // Non-indexed, so it is free of the PK length limit — a long `{plan_no}`\n // composite scope fits. 1024 is far above any realistic rendered prefix.\n t.string('scope', 1024).notNullable().defaultTo('');\n t.bigInteger('last_value').notNullable().defaultTo(0);\n t.timestamp('updated_at').defaultTo(this.knex.fn.now());\n });\n }\n\n /**\n * Migrate a pre-existing `_objectstack_sequences` table to the current\n * `key_hash`-keyed shape. Handles both the original 3-column table (no\n * `scope`) and an interim 4-column `(object, tenant_id, field, scope)` table:\n * every legacy row is read, its `key_hash` computed in app code (no portable\n * SQL hash exists), and re-inserted into a freshly built table that then\n * replaces the original. Idempotent — a no-op once `key_hash` is present.\n *\n * If the rebuild fails, `sequencesHasKeyHash` stays false: fixed-prefix\n * sequences keep working via the legacy key and per-scope writes error\n * actionably (see getNextSequenceValue), rather than corrupting data.\n */\n protected async ensureSequencesKeyHashShape(): Promise<void> {\n if (await this.knex.schema.hasColumn(SEQUENCES_TABLE, 'key_hash')) {\n this.sequencesHasKeyHash = true;\n return;\n }\n const hasScope = await this.knex.schema.hasColumn(SEQUENCES_TABLE, 'scope');\n const TMP = `${SEQUENCES_TABLE}__rebuild`;\n try {\n const rows: any[] = await this.knex(SEQUENCES_TABLE).select('*');\n await this.knex.schema.dropTableIfExists(TMP);\n await this.createSequencesTable(TMP);\n const migrated = rows.map((r) => {\n const scope = hasScope && r.scope != null ? String(r.scope) : '';\n return {\n key_hash: this.sequenceKeyHash(String(r.object), String(r.tenant_id), String(r.field), scope),\n object: r.object,\n tenant_id: r.tenant_id,\n field: r.field,\n scope,\n last_value: r.last_value ?? 0,\n updated_at: r.updated_at ?? this.knex.fn.now(),\n };\n });\n if (migrated.length > 0) await this.knex(TMP).insert(migrated);\n await this.knex.schema.dropTable(SEQUENCES_TABLE);\n await this.knex.schema.renameTable(TMP, SEQUENCES_TABLE);\n this.sequencesHasKeyHash = true;\n } catch (err) {\n // Leave the original table intact; fall back to legacy keying for\n // fixed-prefix sequences and refuse per-scope writes until migrated.\n this.sequencesHasKeyHash = false;\n await this.knex.schema.dropTableIfExists(TMP).catch(() => {});\n this.logger.warn(\n `[autonumber] Failed to migrate ${SEQUENCES_TABLE} to the key_hash shape. ` +\n `Fixed-prefix autonumbers keep working; date/{field}/per-parent formats will ` +\n `error until the table is migrated.`,\n { error: String(err) },\n );\n }\n }\n\n /**\n * Bootstrap helper: scan the data table for the highest numeric suffix\n * matching `prefix` (optionally scoped to a tenant). Used the first time\n * a sequence row is created so legacy/seeded data continues monotonically.\n */\n protected async scanMaxNumericTail(\n queryRunner: Knex | Knex.Transaction,\n tableName: string,\n field: string,\n prefix: string,\n tenantField: string | null,\n tenantId: string | null,\n ): Promise<number> {\n const escapedPrefix = prefix.replace(/([\\\\%_])/g, '\\\\$1');\n let builder = queryRunner(tableName).select(field).where(field, 'like', `${escapedPrefix}%`).whereNotNull(field);\n if (tenantField && tenantId !== null) {\n builder = builder.where(tenantField, tenantId);\n }\n const rows = await builder;\n let maxN = 0;\n for (const r of rows as any[]) {\n const v: string = (r as any)[field];\n if (typeof v !== 'string') continue;\n const tail = v.slice(prefix.length);\n const n = parseInt(tail.replace(/[^0-9]/g, ''), 10);\n if (Number.isFinite(n) && n > maxN) maxN = n;\n }\n return maxN;\n }\n\n /**\n * Atomically reserve and return the next sequence value for\n * `(object, tenantId, field)`. Bootstraps from the data-table MAX on\n * first call so existing seeded records continue monotonically.\n *\n * Concurrency:\n * - SQLite: a write transaction (`BEGIN IMMEDIATE` via knex) serializes\n * all writers; safe in-process. Cross-process SQLite is out of scope.\n * - Postgres/MySQL: `SELECT … FOR UPDATE` row lock ensures only one\n * transaction reads-modifies-writes at a time. A PK-violation race on\n * first insert is retried as an UPDATE.\n *\n * Gaps are tolerated by design — a rolled-back insert \"burns\" a number,\n * matching standard sequence semantics.\n */\n protected async getNextSequenceValue(\n object: string,\n tableName: string,\n field: string,\n prefix: string,\n tenantField: string | null,\n tenantId: string | null,\n parentTrx?: Knex.Transaction,\n scope = '',\n ): Promise<number> {\n await this.ensureSequencesTable();\n const resolvedTenantId = tenantField && tenantId ? String(tenantId) : GLOBAL_TENANT;\n if (scope !== '' && !this.sequencesHasKeyHash) {\n // The legacy sequences table could not be migrated to the key_hash shape,\n // so it cannot represent per-scope counters. Fail with a clear, actionable\n // message instead of corrupting the single legacy counter.\n throw new Error(\n `Cannot generate a per-scope autonumber for \"${object}.${field}\": the ` +\n `${SEQUENCES_TABLE} table is still the legacy shape. ` +\n `Migrate it to the key_hash shape before using date/{field}/per-parent formats.`,\n );\n }\n // `scope` (rendered date/field prefix, boundary-delimited) gives each\n // period/group its own counter; '' keeps the single global counter for\n // fixed-prefix formats. `prefix` is the full rendered prefix used to\n // bootstrap from existing data. The row is keyed by a hash of the composite;\n // on an un-migrated legacy table only fixed-prefix (scope '') reaches here,\n // so fall back to the original `(object, tenant_id, field)` key for it.\n const key = this.sequencesHasKeyHash\n ? { key_hash: this.sequenceKeyHash(tableName, resolvedTenantId, field, scope) }\n : { object: tableName, tenant_id: resolvedTenantId, field };\n const insertRow = this.sequencesHasKeyHash\n ? { ...key, object: tableName, tenant_id: resolvedTenantId, field, scope }\n : { ...key };\n\n const runner: Knex | Knex.Transaction = parentTrx ?? this.knex;\n\n return runner.transaction(async (trx) => {\n // Lock the row (no-op on SQLite, real lock on Postgres/MySQL).\n let existing: any;\n try {\n existing = await trx(SEQUENCES_TABLE).where(key).forUpdate().first();\n } catch {\n // Some dialects/versions reject .forUpdate() on a missing row in\n // weird ways; fall back to plain SELECT then rely on transaction\n // isolation. Postgres/MySQL behave normally here.\n existing = await trx(SEQUENCES_TABLE).where(key).first();\n }\n\n if (!existing) {\n const seedMax = await this.scanMaxNumericTail(\n trx,\n tableName,\n field,\n prefix,\n tenantField,\n resolvedTenantId === GLOBAL_TENANT ? null : resolvedTenantId,\n );\n const initial = seedMax + 1;\n try {\n await trx(SEQUENCES_TABLE).insert({ ...insertRow, last_value: initial });\n return initial;\n } catch (err) {\n // Another writer raced us to the first INSERT. Fall through to\n // the UPDATE path with the now-present row.\n existing = await trx(SEQUENCES_TABLE).where(key).forUpdate().first();\n if (!existing) throw err;\n }\n }\n\n const next = Number(existing.last_value) + 1;\n await trx(SEQUENCES_TABLE).where(key).update({ last_value: next, updated_at: this.knex.fn.now() });\n return next;\n });\n }\n\n /**\n * For each `auto_number` field the caller left empty, render the format and\n * reserve the next counter value. The counter is scoped to the rendered\n * prefix (date tokens like `{YYYYMMDD}` in the request's business timezone,\n * plus `{field}` interpolation from the row), so it resets per period/group;\n * the full rendered prefix bootstraps the counter from existing data, and the\n * tenant scopes it for isolation.\n */\n protected async fillAutoNumberFields(\n object: string,\n row: Record<string, any>,\n options?: DriverOptions,\n ): Promise<void> {\n // Scan/seed the physical (remote) table for an external object; managed\n // objects fall through to the storage-mapped name. Config lookup stays\n // keyed by object name (matching initObjects/registerExternalObject).\n const tableName = this.physicalTableByObject[object] ?? StorageNameMapping.resolveTableName({ name: object } as any);\n const cfgs = this.autoNumberFields[object] || this.autoNumberFields[tableName];\n if (!cfgs || cfgs.length === 0) return;\n const parentTrx = options?.transaction as Knex.Transaction | undefined;\n const timezone = (options as any)?.timezone as string | undefined;\n const now = new Date();\n for (const cfg of cfgs) {\n if (row[cfg.name] !== undefined && row[cfg.name] !== null && row[cfg.name] !== '') continue;\n // A `{field}` token with no value would render to an empty prefix and\n // silently merge this record into the wrong counter scope, so refuse to\n // generate rather than emit a wrong record number (the referenced field\n // must be populated before the autonumber — see field.zod docs).\n const missing = missingFieldValues(cfg.tokens, row);\n if (missing.length > 0) {\n throw new Error(\n `Cannot generate autonumber \"${object}.${cfg.name}\" (format \"${cfg.format}\"): ` +\n `referenced field(s) [${missing.join(', ')}] are empty on the record. ` +\n `Fields interpolated into an autonumber format must be set before the record is created.`,\n );\n }\n // Resolve tenant for this row: explicit field on the record wins,\n // then driver options, else null → global sequence.\n const rowTenant = cfg.tenantField ? row[cfg.tenantField] : undefined;\n const optTenant = (options as any)?.tenantId;\n const tenantId = rowTenant != null && rowTenant !== ''\n ? String(rowTenant)\n : optTenant != null && optTenant !== ''\n ? String(optTenant)\n : null;\n // Resolve the scope/prefix for this row (counter-value-independent),\n // reserve the next value under that scope, then render the final string.\n const probe = renderAutonumber({ tokens: cfg.tokens, seq: 0, record: row, now, timezone });\n const next = await this.getNextSequenceValue(\n object,\n tableName,\n cfg.name,\n probe.prefix,\n cfg.tenantField,\n tenantId,\n parentTrx,\n probe.scope,\n );\n row[cfg.name] = renderAutonumber({ tokens: cfg.tokens, seq: next, record: row, now, timezone }).value;\n }\n }\n\n async update(object: string, id: string | number, data: Record<string, any>, options?: DriverOptions): Promise<any> {\n this.auditMissingTenant(object, 'update', options);\n const builder = this.getBuilder(object, options).where('id', id);\n this.applyTenantScope(builder, object, options);\n const formatted = this.applyWriteColumnMap(object, this.formatInput(object, data));\n\n if (this.tablesWithTimestamps.has(object)) {\n if (this.isSqlite) {\n const now = new Date();\n formatted.updated_at = now.toISOString().replace('T', ' ').replace('Z', '');\n } else {\n formatted.updated_at = this.knex.fn.now();\n }\n }\n\n await builder.update(formatted);\n\n const readback = this.getBuilder(object, options).where('id', id);\n this.applyTenantScope(readback, object, options);\n const updated = await readback.first();\n return this.formatOutput(object, updated) || null;\n }\n\n async upsert(object: string, data: Record<string, any>, conflictKeys?: string[], options?: DriverOptions): Promise<Record<string, any>> {\n const { _id, ...rest } = data;\n const toUpsert = { ...rest };\n\n if (_id !== undefined && toUpsert.id === undefined) {\n toUpsert.id = _id;\n } else if (toUpsert.id === undefined) {\n toUpsert.id = nanoid(DEFAULT_ID_LENGTH);\n }\n\n this.auditMissingTenant(object, 'upsert', options);\n this.injectTenantOnInsert(object, toUpsert, options);\n await this.fillAutoNumberFields(object, toUpsert, options);\n\n const formatted = this.applyWriteColumnMap(object, this.formatInput(object, toUpsert));\n const mergeKeys = conflictKeys && conflictKeys.length > 0 ? conflictKeys : ['id'];\n\n const builder = this.getBuilder(object, options);\n await builder.insert(formatted).onConflict(mergeKeys).merge();\n\n const readback = this.getBuilder(object, options).where('id', toUpsert.id);\n this.applyTenantScope(readback, object, options);\n const result = await readback.first();\n return this.formatOutput(object, result) || toUpsert;\n }\n\n async delete(object: string, id: string | number, options?: DriverOptions): Promise<boolean> {\n this.auditMissingTenant(object, 'delete', options);\n const builder = this.getBuilder(object, options).where('id', id);\n this.applyTenantScope(builder, object, options);\n const count = await builder.delete();\n return count > 0;\n }\n\n // ===================================\n // Bulk & Batch Operations\n // ===================================\n\n async bulkCreate(object: string, data: any[], options?: DriverOptions): Promise<any> {\n this.auditMissingTenant(object, 'bulkCreate', options);\n for (const row of data) {\n if (row && typeof row === 'object') {\n this.injectTenantOnInsert(object, row, options);\n // Reserve a persistent sequence value for each row's autonumber\n // field(s) — the engine no longer pre-fills these (see #1603).\n await this.fillAutoNumberFields(object, row, options);\n }\n }\n const builder = this.getBuilder(object, options);\n return await builder.insert(data).returning('*');\n }\n\n /**\n * Batch-update multiple records by ID.\n * NOTE: Current implementation performs sequential updates for correctness.\n * TODO: Optimize with SQL CASE statements or batched transactions for performance.\n */\n async bulkUpdate(object: string, updates: Array<{ id: string | number; data: Record<string, any> }>, options?: DriverOptions): Promise<Record<string, any>[]> {\n const results: Record<string, any>[] = [];\n for (const { id, data } of updates) {\n const updated = await this.update(object, id, data, options);\n if (updated) results.push(updated);\n }\n return results;\n }\n\n async bulkDelete(object: string, ids: Array<string | number>, options?: DriverOptions): Promise<void> {\n this.auditMissingTenant(object, 'bulkDelete', options);\n const builder = this.getBuilder(object, options).whereIn('id', ids);\n this.applyTenantScope(builder, object, options);\n await builder.delete();\n }\n\n async updateMany(object: string, query: QueryAST, data: any, options?: DriverOptions): Promise<number> {\n this.auditMissingTenant(object, 'updateMany', options);\n const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\n if (query.where) this.applyFilters(builder, query.where);\n const count = await builder.update(data);\n return count || 0;\n }\n\n async deleteMany(object: string, query: QueryAST, options?: DriverOptions): Promise<number> {\n this.auditMissingTenant(object, 'deleteMany', options);\n const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\n if (query.where) this.applyFilters(builder, query.where);\n const count = await builder.delete();\n return count || 0;\n }\n\n async count(object: string, query?: QueryAST, options?: DriverOptions): Promise<number> {\n const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\n\n if (query?.where) {\n this.applyFilters(builder, query.where);\n }\n\n const result = await builder.count<{ count: number }[]>('* as count');\n if (result && result.length > 0) {\n const row: any = result[0];\n return Number(row.count ?? row['count(*)'] ?? 0);\n }\n return 0;\n }\n\n // ===================================\n // Raw Execution\n // ===================================\n\n /**\n * Run a raw SQL string or knex builder through the underlying knex\n * connection.\n *\n * ⚠️ **Tenant isolation bypass.** Unlike `find`/`update`/`delete` etc.,\n * raw `execute()` does NOT inject the `organization_id` predicate. The\n * caller is responsible for either:\n * - inlining the tenant filter into the SQL (`WHERE organization_id = ?`),\n * - or restricting `execute()` to genuinely global queries\n * (schema introspection, sys_* tables that opt out of tenancy).\n *\n * Prefer the typed CRUD APIs whenever the operation can be expressed\n * through them — they handle tenancy, soft-delete, and audit warnings\n * automatically. See `README.md > Tenant Isolation` for the full bypass\n * matrix.\n */\n async execute(command: any, params?: any[], options?: DriverOptions): Promise<any> {\n if (typeof command !== 'string') {\n return command;\n }\n\n const builder =\n options?.transaction\n ? this.knex.raw(command, params || []).transacting(options.transaction as Knex.Transaction)\n : this.knex.raw(command, params || []);\n\n return await builder;\n }\n\n // ===================================\n // Transactions\n // ===================================\n\n async beginTransaction(): Promise<Knex.Transaction> {\n return await this.knex.transaction();\n }\n\n /** IDataDriver standard */\n async commit(transaction: unknown): Promise<void> {\n await (transaction as Knex.Transaction).commit();\n }\n\n /** IDataDriver standard */\n async rollback(transaction: unknown): Promise<void> {\n await (transaction as Knex.Transaction).rollback();\n }\n\n /** @deprecated Use commit() instead */\n async commitTransaction(trx: Knex.Transaction): Promise<void> {\n await this.commit(trx);\n }\n\n /** @deprecated Use rollback() instead */\n async rollbackTransaction(trx: Knex.Transaction): Promise<void> {\n await this.rollback(trx);\n }\n\n // ===================================\n // Aggregation\n // ===================================\n\n async aggregate(object: string, query: any, options?: DriverOptions): Promise<any> {\n const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\n\n if (query.where) {\n this.applyFilters(builder, query.where);\n }\n\n if (query.groupBy) {\n // groupBy items may be plain strings ('region') or structured objects\n // ({ field: 'closed_at', dateGranularity: 'quarter' }). For structured\n // items we emit a dialect-specific bucket expression aliased as the\n // field name so the resulting row keys match in-memory bucketDateValue.\n for (const g of query.groupBy as Array<string | { field: string; dateGranularity?: string }>) {\n if (typeof g === 'string') {\n builder.groupBy(g);\n builder.select(g);\n } else if (g && typeof g === 'object' && g.field) {\n if (g.dateGranularity) {\n const bucket = this.buildDateBucketExpr(g.field, g.dateGranularity as any);\n if (!bucket) {\n throw new Error(\n `SqlDriver: dateGranularity '${g.dateGranularity}' not supported on dialect ` +\n `'${(this.config as any).client}'. Engine must fall back to in-memory bucketing.`,\n );\n }\n builder.groupByRaw(bucket.sql, bucket.bindings);\n builder.select(this.knex.raw(`${bucket.sql} as ??`, [...bucket.bindings, g.field]));\n } else {\n builder.groupBy(g.field);\n builder.select(g.field);\n }\n }\n }\n }\n\n const aggregates = query.aggregations || query.aggregate;\n if (aggregates) {\n for (const agg of aggregates) {\n const funcName = agg.function || agg.func;\n const rawFunc = this.mapAggregateFunc(funcName);\n // Spec: `field` is optional for COUNT (means COUNT(*)).\n const fieldExpr = agg.field ?? '*';\n if (agg.alias) {\n if (fieldExpr === '*') {\n builder.select(this.knex.raw(`${rawFunc}(*) as ??`, [agg.alias]));\n } else {\n builder.select(this.knex.raw(`${rawFunc}(??) as ??`, [fieldExpr, agg.alias]));\n }\n } else {\n if (fieldExpr === '*') {\n builder.select(this.knex.raw(`${rawFunc}(*)`));\n } else {\n builder.select(this.knex.raw(`${rawFunc}(??)`, [fieldExpr]));\n }\n }\n }\n }\n\n return await builder;\n }\n\n // ===================================\n // Distinct\n // ===================================\n\n async distinct(object: string, field: string, filters?: any, options?: DriverOptions): Promise<any[]> {\n const builder = this.getBuilder(object, options);\n\n if (filters) {\n this.applyFilters(builder, filters);\n }\n\n builder.distinct(field);\n const results = await builder;\n return results.map((row: any) => row[field]);\n }\n\n // ===================================\n // Window Functions\n // ===================================\n\n async findWithWindowFunctions(object: string, query: any, options?: DriverOptions): Promise<any[]> {\n const builder = this.getBuilder(object, options);\n\n builder.select('*');\n\n if (query.where) {\n this.applyFilters(builder, query.where);\n }\n\n if (query.windowFunctions && Array.isArray(query.windowFunctions)) {\n for (const wf of query.windowFunctions) {\n const windowFunc = this.buildWindowFunction(wf);\n builder.select(this.knex.raw(`${windowFunc} as ??`, [wf.alias]));\n }\n }\n\n if (query.orderBy && Array.isArray(query.orderBy)) {\n for (const sort of query.orderBy) {\n builder.orderBy(this.mapSortField(sort.field), sort.order || 'asc');\n }\n }\n\n if (query.limit) builder.limit(query.limit);\n if (query.offset) builder.offset(query.offset);\n\n return await builder;\n }\n\n // ===================================\n // Query Plan Analysis\n // ===================================\n\n /** IDataDriver standard: analyze query performance */\n async explain(object: string, query: any, options?: DriverOptions): Promise<any> {\n return this.analyzeQuery(object, query, options);\n }\n\n async analyzeQuery(object: string, query: any, options?: DriverOptions): Promise<any> {\n const builder = this.getBuilder(object, options);\n\n if (query.fields) {\n builder.select(query.fields);\n } else {\n builder.select('*');\n }\n\n if (query.where) {\n this.applyFilters(builder, query.where);\n }\n\n if (query.orderBy && Array.isArray(query.orderBy)) {\n for (const sort of query.orderBy) {\n builder.orderBy(this.mapSortField(sort.field), sort.order || 'asc');\n }\n }\n\n if (query.limit) builder.limit(query.limit);\n if (query.offset) builder.offset(query.offset);\n\n const sql = builder.toSQL();\n const client = (this.config as any).client;\n let explainResults: any;\n\n try {\n if (this.isPostgres) {\n explainResults = await this.knex.raw(`EXPLAIN (FORMAT JSON, ANALYZE) ${sql.sql}`, sql.bindings);\n } else if (this.isMysql) {\n explainResults = await this.knex.raw(`EXPLAIN FORMAT=JSON ${sql.sql}`, sql.bindings);\n } else if (this.isSqlite) {\n explainResults = await this.knex.raw(`EXPLAIN QUERY PLAN ${sql.sql}`, sql.bindings);\n } else {\n return {\n sql: sql.sql,\n bindings: sql.bindings,\n client,\n note: 'EXPLAIN not supported for this database client',\n };\n }\n\n return { sql: sql.sql, bindings: sql.bindings, client, plan: explainResults };\n } catch (error: any) {\n return {\n sql: sql.sql,\n bindings: sql.bindings,\n client,\n error: error.message,\n note: 'Failed to execute EXPLAIN.',\n };\n }\n }\n\n // ===================================\n // Schema Sync (syncSchema / init)\n // ===================================\n\n async syncSchema(object: string, schema: unknown, _options?: DriverOptions): Promise<void> {\n const objectDef = schema as { name: string; fields?: Record<string, any> };\n // The caller passes the resolved physical table name as `object`. Override\n // the def's `name` to ensure DDL targets the physical table even if the\n // schema's `name` is the canonical object name (e.g. 'account').\n await this.initObjects([{ ...objectDef, name: object }]);\n }\n\n async dropTable(object: string, _options?: DriverOptions): Promise<void> {\n this.assertSchemaMutable('dropTable');\n await this.knex.schema.dropTableIfExists(object);\n }\n\n /**\n * Batch-initialise tables from an array of object definitions.\n */\n /**\n * DDL-free metadata registration for a federated (external) object — the\n * read-path counterpart to {@link initObjects} (ADR-0015 federation).\n *\n * `initObjects` is gated by `assertSchemaMutable` and therefore throws for\n * any non-`managed` driver, which left external objects with NO read-coercion\n * metadata and the query path resolving to a table named after the object\n * instead of its remote table. This populates the same coercion maps (keyed\n * by OBJECT name, matching formatInput/formatOutput/coerceFilterValue) and\n * records the physical remote table (`external.remoteName`, optionally\n * `external.remoteSchema`) so {@link getBuilder} targets it — WITHOUT running\n * any DDL (createTable/alterTable/columnInfo). Keep the field-classification\n * below in sync with initObjects() if the field-type -> storage mapping changes.\n */\n registerExternalObject(schema: {\n name: string;\n fields?: Record<string, any>;\n tenancy?: any;\n external?: { remoteName?: string; remoteSchema?: string; columnMap?: Record<string, string> };\n }): void {\n const key = schema.name;\n const remoteName = schema.external?.remoteName || schema.name;\n const remoteSchema = schema.external?.remoteSchema;\n this.physicalTableByObject[key] = remoteName;\n this.objectByPhysicalTable[remoteName] = key;\n if (remoteSchema) {\n if (this.isSqlite) {\n this.logger.warn(\n `[sql-driver] external object \"${key}\" declares remoteSchema=\"${remoteSchema}\" but SQLite has no schema namespace; ignoring (treating \"${remoteName}\" as a bare table).`,\n );\n } else {\n this.physicalSchemaByObject[key] = remoteSchema;\n }\n }\n\n // External columnMap (ADR-0015) is declared as { remoteColumn -> localField }.\n // Keep it for read-output remap, and invert to { localField -> remoteColumn }\n // for WHERE/ORDER BY/write translation. Absent => managed-identical behavior.\n const columnMap = schema.external?.columnMap;\n if (columnMap && typeof columnMap === 'object' && Object.keys(columnMap).length > 0) {\n const fieldToCol: Record<string, string> = {};\n const colToField: Record<string, string> = {};\n for (const [remoteCol, localField] of Object.entries(columnMap)) {\n if (typeof localField === 'string' && localField) {\n fieldToCol[localField] = remoteCol;\n colToField[remoteCol] = localField;\n }\n }\n this.fieldColumnByObject[key] = fieldToCol;\n this.columnFieldByObject[key] = colToField;\n }\n\n const jsonCols: string[] = [];\n const booleanCols: string[] = [];\n const numericCols: string[] = [];\n const dateCols: string[] = [];\n const datetimeCols: string[] = [];\n const autoNumberCols: Array<{ name: string; format: string; tokens: AutonumberToken[]; tenantField: string | null }> = [];\n\n const tenancyDecl = (schema as any)?.tenancy;\n let tenantField: string | null = null;\n if (tenancyDecl && tenancyDecl.enabled !== false && tenancyDecl.tenantField) {\n const declared = String(tenancyDecl.tenantField);\n if (schema.fields && Object.prototype.hasOwnProperty.call(schema.fields, declared)) {\n tenantField = declared;\n }\n }\n if (!tenantField) {\n const hasOrgField = !!(schema.fields && Object.prototype.hasOwnProperty.call(schema.fields, 'organization_id'));\n tenantField = hasOrgField ? 'organization_id' : null;\n }\n if (schema.fields) {\n for (const [name, field] of Object.entries<any>(schema.fields)) {\n const type = field.type || 'string';\n if (this.isJsonField(type, field)) jsonCols.push(name);\n if (type === 'boolean' || type === 'toggle') booleanCols.push(name);\n if (NUMERIC_SCALAR_TYPES.has(type) && !field.multiple) numericCols.push(name);\n if (type === 'date') dateCols.push(name);\n if (type === 'datetime') datetimeCols.push(name);\n if (type === 'auto_number' || type === 'autonumber') {\n const rawFmt = (typeof field.autonumberFormat === 'string' && field.autonumberFormat)\n ? field.autonumberFormat\n : (typeof field.format === 'string' && field.format ? field.format : '');\n const fmt = rawFmt || '{0000}';\n autoNumberCols.push({ name, format: fmt, tokens: parseAutonumberFormat(fmt), tenantField });\n }\n }\n }\n this.jsonFields[key] = jsonCols;\n this.booleanFields[key] = booleanCols;\n this.numericFields[key] = numericCols;\n this.autoNumberFields[key] = autoNumberCols;\n this.tenantFieldByTable[key] = tenantField;\n if (dateCols.length) this.dateFields[key] = new Set(dateCols);\n if (datetimeCols.length) this.datetimeFields[key] = new Set(datetimeCols);\n }\n\n async initObjects(objects: Array<{ name: string; fields?: Record<string, any> }>): Promise<void> {\n // DDL gate (ADR-0015 §5.1): createTable/alterTable below mutate schema.\n // Also covers `syncSchema`, which delegates here.\n this.assertSchemaMutable('initObjects');\n await this.ensureDatabaseExists();\n\n for (const obj of objects) {\n const tableName = StorageNameMapping.resolveTableName(obj);\n\n const jsonCols: string[] = [];\n const booleanCols: string[] = [];\n const numericCols: string[] = [];\n const autoNumberCols: Array<{ name: string; format: string; tokens: AutonumberToken[]; tenantField: string | null }> = [];\n // Auto-detect tenant field. Convention: the field named\n // `organization_id` (matching tenantPolicy default) scopes the\n // Resolve tenant scope declaratively first (obj.tenancy.{enabled,\n // tenantField}) — that's the user's explicit intent. Fall back to the\n // implicit \"has an organization_id field\" detection so legacy objects\n // (whose multi-tenant column was injected by the kernel implicitly)\n // keep working without a spec migration.\n const tenancyDecl = (obj as any)?.tenancy;\n let tenantField: string | null = null;\n if (tenancyDecl && tenancyDecl.enabled !== false && tenancyDecl.tenantField) {\n const declared = String(tenancyDecl.tenantField);\n if (obj.fields && Object.prototype.hasOwnProperty.call(obj.fields, declared)) {\n tenantField = declared;\n }\n }\n if (!tenantField) {\n const hasOrgField = !!(obj.fields && Object.prototype.hasOwnProperty.call(obj.fields, 'organization_id'));\n tenantField = hasOrgField ? 'organization_id' : null;\n }\n if (obj.fields) {\n for (const [name, field] of Object.entries<any>(obj.fields)) {\n const type = field.type || 'string';\n if (this.isJsonField(type, field)) {\n jsonCols.push(name);\n }\n // `toggle` shares boolean storage/affinity, so it needs the same\n // read coercion (stored 1/0 → JS true/false) or it leaks back as a\n // number/string instead of a boolean (#field-zoo).\n if (type === 'boolean' || type === 'toggle') {\n booleanCols.push(name);\n }\n // Numeric scalars are coerced back to JS numbers on read so legacy\n // TEXT-affinity columns (created before they were mapped to a numeric\n // column) still return numbers, not strings — see NUMERIC_SCALAR_TYPES.\n if (NUMERIC_SCALAR_TYPES.has(type) && !field.multiple) {\n numericCols.push(name);\n }\n if (type === 'date') {\n (this.dateFields[tableName] ??= new Set()).add(name);\n }\n if (type === 'datetime') {\n (this.datetimeFields[tableName] ??= new Set()).add(name);\n }\n if (type === 'auto_number' || type === 'autonumber') {\n // Honor either the spec-canonical `autonumberFormat` or the\n // shorthand `format` (both appear in metadata) — see #1603.\n const rawFmt = (typeof field.autonumberFormat === 'string' && field.autonumberFormat)\n ? field.autonumberFormat\n : (typeof field.format === 'string' && field.format ? field.format : '');\n const fmt = rawFmt || '{0000}';\n // Tokenize once: the renderer resolves date tokens (`{YYYYMMDD}`),\n // field interpolation (`{island_zone}`) and the sequence slot at\n // fill time. The counter scopes to whatever renders before the slot.\n const tokens = parseAutonumberFormat(fmt);\n autoNumberCols.push({ name, format: fmt, tokens, tenantField });\n }\n }\n }\n this.jsonFields[tableName] = jsonCols;\n this.booleanFields[tableName] = booleanCols;\n this.numericFields[tableName] = numericCols;\n this.autoNumberFields[tableName] = autoNumberCols;\n this.tenantFieldByTable[tableName] = tenantField;\n\n let exists = await this.knex.schema.hasTable(tableName);\n\n if (exists) {\n const columnInfo = await this.knex(tableName).columnInfo();\n const existingColumns = Object.keys(columnInfo);\n\n if (existingColumns.includes('_id') && !existingColumns.includes('id')) {\n await this.knex.schema.dropTable(tableName);\n exists = false;\n }\n }\n\n // Columns created unconditionally by initObjects — skip them when\n // iterating obj.fields to avoid duplicate-column errors (e.g. SQLite\n // rejects CREATE TABLE with two columns of the same name).\n const builtinColumns = new Set(['id', 'created_at', 'updated_at']);\n\n if (!exists) {\n await this.knex.schema.createTable(tableName, (table) => {\n table.string('id').primary();\n table.timestamp('created_at').defaultTo(this.knex.fn.now());\n table.timestamp('updated_at').defaultTo(this.knex.fn.now());\n if (obj.fields) {\n for (const [name, field] of Object.entries(obj.fields)) {\n if (builtinColumns.has(name)) continue;\n this.createColumn(table, name, field);\n }\n }\n });\n this.tablesWithTimestamps.add(tableName);\n } else {\n const columnInfo = await this.knex(tableName).columnInfo();\n const existingColumns = Object.keys(columnInfo);\n\n if (existingColumns.includes('updated_at')) {\n this.tablesWithTimestamps.add(tableName);\n }\n\n await this.knex.schema.alterTable(tableName, (table) => {\n if (obj.fields) {\n for (const [name, field] of Object.entries(obj.fields)) {\n if (!existingColumns.includes(name)) {\n this.createColumn(table, name, field);\n }\n }\n }\n });\n }\n\n // Materialize object-level declared indexes (`indexes: [{ fields,\n // unique }]`). These are distinct from field-level `unique` (handled\n // in `createColumn`) and carry the multi-column UNIQUE guarantees that\n // dedup/convergence paths rely on (ADR-0030). Done after the table is\n // created/altered so every referenced column physically exists.\n const declaredIndexes = (obj as any).indexes;\n if (Array.isArray(declaredIndexes) && declaredIndexes.length > 0) {\n const colInfo = await this.knex(tableName).columnInfo();\n const physicalColumns = new Set(Object.keys(colInfo));\n await this.syncDeclaredIndexes(tableName, declaredIndexes, physicalColumns);\n }\n }\n }\n\n /**\n * Build a deterministic index name for a declared index so repeated\n * `initObjects` runs converge on the same identifier (and can detect an\n * already-materialized index by name). Long names are hash-suffixed to\n * stay within the 63/64-char identifier limits of Postgres/MySQL.\n */\n protected buildIndexName(tableName: string, fields: string[], unique: boolean): string {\n const prefix = unique ? 'uniq' : 'idx';\n const base = `${prefix}_${tableName}_${fields.join('_')}`;\n const MAX = 60;\n if (base.length <= MAX) return base;\n const hash = createHash('sha1').update(base).digest('hex').slice(0, 8);\n return `${`${prefix}_${tableName}`.slice(0, MAX - 9)}_${hash}`;\n }\n\n /**\n * Read the names of indexes that already exist on a table, per dialect.\n * Used to make declared-index sync idempotent across repeated runs.\n * Failures are swallowed — at worst we attempt a create and absorb the\n * \"already exists\" error in `syncDeclaredIndexes`.\n */\n protected async getExistingIndexNames(tableName: string): Promise<Set<string>> {\n const names = new Set<string>();\n try {\n if (this.isSqlite) {\n const safe = tableName.replace(/[^a-zA-Z0-9_]/g, '');\n const rows: any = await this.knex.raw(`PRAGMA index_list(${safe})`);\n for (const r of rows) names.add(r.name);\n } else if (this.isPostgres) {\n const res: any = await this.knex.raw(\n `SELECT indexname FROM pg_indexes WHERE schemaname = 'public' AND tablename = ?`,\n [tableName],\n );\n for (const r of res.rows) names.add(r.indexname);\n } else if (this.isMysql) {\n const res: any = await this.knex.raw(\n `SELECT INDEX_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`,\n [tableName],\n );\n for (const r of res[0]) names.add(r.INDEX_NAME);\n }\n } catch {\n // Best-effort — fall through and let creation handle conflicts.\n }\n return names;\n }\n\n /**\n * Materialize declared object-level indexes.\n *\n * - Multi-column and single-column indexes are both supported.\n * - `unique: true` emits a UNIQUE index. NULL-distinct semantics are the\n * default across SQLite/Postgres/MySQL, so multiple NULL rows remain\n * allowed while non-NULL duplicates are rejected — matching the\n * convergence-on-conflict pattern the messaging pipeline relies on.\n * - Idempotent: indexes already present (by deterministic name) are\n * skipped, and an \"already exists\" race is absorbed.\n * - Indexes referencing a column that wasn't materialized (e.g. a virtual\n * `formula` field) are skipped with a warning rather than failing sync.\n */\n protected async syncDeclaredIndexes(\n tableName: string,\n indexes: Array<{ name?: string; fields?: string[]; unique?: boolean }>,\n physicalColumns: Set<string>,\n ): Promise<void> {\n const existing = await this.getExistingIndexNames(tableName);\n\n for (const idx of indexes) {\n const fields = Array.isArray(idx?.fields)\n ? idx.fields.filter((f): f is string => typeof f === 'string' && f.length > 0)\n : [];\n if (fields.length === 0) continue;\n\n const missing = fields.filter((f) => !physicalColumns.has(f));\n if (missing.length > 0) {\n this.logger.warn(\n `[sql-driver] skipping declared index on \"${tableName}\" — column(s) not materialized: ${missing.join(', ')}`,\n { tableName, fields },\n );\n continue;\n }\n\n const unique = idx.unique === true;\n const name =\n typeof idx.name === 'string' && idx.name.trim()\n ? idx.name.trim()\n : this.buildIndexName(tableName, fields, unique);\n\n if (existing.has(name)) continue;\n\n try {\n await this.knex.schema.alterTable(tableName, (table) => {\n if (unique) {\n table.unique(fields, { indexName: name });\n } else {\n table.index(fields, name);\n }\n });\n existing.add(name);\n } catch (e: any) {\n const msg = String(e?.message ?? e);\n // A concurrent creator or a pre-existing equivalent index under a\n // different name can race us here — both are benign for our intent\n // (the index exists). Anything else is a real failure.\n if (/already exists|duplicate key name|exists/i.test(msg)) continue;\n throw e;\n }\n }\n }\n\n // ===================================\n // Schema Introspection\n // ===================================\n\n async introspectSchema(): Promise<IntrospectedSchema> {\n const tables: Record<string, IntrospectedTable> = {};\n let tableNames: string[] = [];\n\n if (this.isPostgres) {\n const result = await this.knex.raw(`\n SELECT table_name\n FROM information_schema.tables\n WHERE table_schema = 'public'\n AND table_type = 'BASE TABLE'\n `);\n tableNames = result.rows.map((row: any) => row.table_name);\n } else if (this.isMysql) {\n const result = await this.knex.raw(`\n SELECT table_name\n FROM information_schema.tables\n WHERE table_schema = DATABASE()\n AND table_type = 'BASE TABLE'\n `);\n tableNames = result[0].map((row: any) => row.TABLE_NAME);\n } else if (this.isSqlite) {\n const result = await this.knex.raw(`\n SELECT name as table_name\n FROM sqlite_master\n WHERE type='table'\n AND name NOT LIKE 'sqlite_%'\n `);\n tableNames = result.map((row: any) => row.table_name);\n }\n\n for (const tableName of tableNames) {\n const columns = await this.introspectColumns(tableName);\n const foreignKeys = await this.introspectForeignKeys(tableName);\n const primaryKeys = await this.introspectPrimaryKeys(tableName);\n const uniqueConstraints = await this.introspectUniqueConstraints(tableName);\n\n for (const col of columns) {\n if (primaryKeys.includes(col.name)) col.isPrimary = true;\n if (uniqueConstraints.includes(col.name)) col.isUnique = true;\n }\n\n tables[tableName] = { name: tableName, columns, foreignKeys, primaryKeys };\n }\n\n return { tables };\n }\n\n // ===================================\n // Internal helpers\n // ===================================\n\n /** Expose the underlying Knex instance for advanced usage. */\n getKnex(): Knex {\n return this.knex;\n }\n\n protected getBuilder(object: string, options?: DriverOptions) {\n // Federation (ADR-0015): an external object resolves to its remote table\n // (`external.remoteName`, optionally schema-qualified). Managed objects miss\n // both maps, so this is `this.knex(object)` — unchanged. `.withSchema()` is\n // applied on the builder (not via `knex.withSchema().from()`) so the builder\n // type is identical to the managed path for every downstream caller.\n const physical = this.physicalTableByObject[object] ?? object;\n let builder = this.knex(physical);\n const remoteSchema = this.physicalSchemaByObject[object];\n if (remoteSchema) {\n builder = builder.withSchema(remoteSchema);\n }\n if (options?.transaction) {\n builder = builder.transacting(options.transaction as Knex.Transaction);\n }\n return builder;\n }\n\n /**\n * Resolve the tenant column for the given object, if any.\n *\n * Lookup falls back to both the storage-mapped table name and the raw\n * object name so callers that pass either form get the same answer.\n * Returns `null` when the object has no tenant-isolation field.\n */\n protected resolveTenantField(object: string): string | null {\n const tableName = StorageNameMapping.resolveTableName({ name: object } as any);\n const cached =\n this.tenantFieldByTable[tableName] ?? this.tenantFieldByTable[object];\n return cached ?? null;\n }\n\n /**\n * Whether the host kernel runs in multi-tenant mode — read once from\n * `OS_MULTI_ORG_ENABLED` (or the deprecated `OS_MULTI_TENANT`), matching how\n * the SchemaRegistry / SecurityPlugin pick the mode. Used to gate the\n * tenant-audit warning: it's only meaningful where tenant isolation is\n * actually enforced (org-scoping installed).\n */\n private _multiTenantMode?: boolean;\n protected isMultiTenantMode(): boolean {\n if (this._multiTenantMode === undefined) {\n const raw =\n process.env.OS_MULTI_ORG_ENABLED ?? process.env.OS_MULTI_TENANT ?? 'false';\n this._multiTenantMode = String(raw).toLowerCase() !== 'false';\n }\n return this._multiTenantMode;\n }\n\n /**\n * Apply a `WHERE tenant_field = ?` clause to the given query builder\n * when:\n * 1. `options.tenantId` is provided by the caller, AND\n * 2. the object actually has a tenant-isolation field\n * (`organization_id` by convention).\n *\n * Without a tenantId the call is treated as an unscoped/admin path —\n * keeps legacy callers, seed scripts, and cross-org tooling working.\n * This is the single chokepoint for read-side tenant isolation in the\n * SQL driver; every CRUD method routes through it.\n */\n protected applyTenantScope(\n builder: Knex.QueryBuilder,\n object: string,\n options?: DriverOptions,\n ): Knex.QueryBuilder {\n const tenantId = (options as any)?.tenantId;\n if (tenantId === undefined || tenantId === null || tenantId === '') return builder;\n const field = this.resolveTenantField(object);\n if (!field) return builder;\n return builder.where(field, String(tenantId));\n }\n\n /**\n * Auto-inject the tenant column on insert rows when:\n * 1. `options.tenantId` is provided, AND\n * 2. the object has a tenant-isolation field, AND\n * 3. the row does not already set that field.\n *\n * Explicit values are never overwritten — admins writing to a specific\n * tenant via raw row data keep that authority.\n */\n protected injectTenantOnInsert(\n object: string,\n row: Record<string, any>,\n options?: DriverOptions,\n ): void {\n const tenantId = (options as any)?.tenantId;\n if (tenantId === undefined || tenantId === null || tenantId === '') return;\n const field = this.resolveTenantField(object);\n if (!field) return;\n if (row[field] === undefined || row[field] === null || row[field] === '') {\n row[field] = String(tenantId);\n }\n }\n\n /**\n * Surface writes that target a tenant-scoped object but don't carry a\n * `tenantId`. These are almost always system / seed / admin paths that\n * forgot to thread the active session context — easy to miss in code\n * review and impossible to find after a breach.\n *\n * Throttled to one warning per `${object}:${op}` so background workers\n * don't spam the log. Set `options.bypassTenantAudit = true` (or env\n * `OS_TENANT_AUDIT=0`) to silence intentionally.\n */\n protected auditMissingTenant(\n object: string,\n op: 'create' | 'update' | 'delete' | 'bulkCreate' | 'bulkDelete' | 'updateMany' | 'deleteMany' | 'upsert',\n options?: DriverOptions,\n ): void {\n if (process.env.OS_TENANT_AUDIT === '0') return;\n if ((options as any)?.bypassTenantAudit === true) return;\n // Only meaningful in multi-tenant deployments. Single-tenant stacks have no\n // tenant isolation, yet the kernel now ALWAYS provisions an `organization_id`\n // column (its existence is decoupled from the tenant flag). Column presence\n // alone therefore no longer implies \"tenant-scoped\" — without this gate every\n // system/sudo write (e.g. the notification/http delivery dispatchers' claim\n // updates) would spam a meaningless warning on single-tenant boots.\n if (!this.isMultiTenantMode()) return;\n const tenantId = (options as any)?.tenantId;\n if (tenantId !== undefined && tenantId !== null && tenantId !== '') return;\n const field = this.resolveTenantField(object);\n if (!field) return;\n const key = `${object}:${op}`;\n if (this.tenantAuditWarned.has(key)) return;\n this.tenantAuditWarned.add(key);\n this.logger.warn(\n `[tenant-audit] ${op} on tenant-scoped object \"${object}\" without options.tenantId — writes will not be tenant-isolated. Pass tenantId via ExecutionContext or set bypassTenantAudit:true to silence.`,\n { object, op, tenantField: field },\n );\n }\n\n // ── Filter helpers ──────────────────────────────────────────────────────────\n\n /**\n * Resolve the underlying table name for a Knex query builder so we can\n * look up column type metadata (date/datetime maps populated during\n * `initObjects`). Returns null when the builder is not table-scoped yet.\n */\n protected tableNameForBuilder(builder: any): string | null {\n const t = builder?._single?.table;\n if (typeof t === 'string') return t;\n return null;\n }\n\n /**\n * Coercion-map key for a builder. Coercion maps (date/datetime) are keyed by\n * OBJECT name, but after the federation change {@link getBuilder} targets the\n * physical remote table, so a builder reports the remote name. Map it back to\n * the object name for external objects; identity for managed ones (no reverse\n * entry). Note datetime coercion is a SQLite-only concern (see\n * coerceFilterValue), and SQLite external tables are bare-named, so this is\n * exact where it matters.\n */\n protected coercionKey(builder: any): string | null {\n const physical = this.tableNameForBuilder(builder);\n if (physical == null) return null;\n return this.objectByPhysicalTable[physical] ?? physical;\n }\n\n /**\n * Collapse a `Field.date` value to a timezone-naive `YYYY-MM-DD`\n * calendar-day string (ADR-0053 Phase 1). A `Date` collapses to its UTC\n * calendar day; a string keeps its leading date and drops any time\n * component. Anything else (and `null`/`undefined`) passes through\n * unchanged. This is the single source of truth for date-only truncation,\n * shared by the filter (`coerceFilterValue`), write (`formatInput`) and\n * read (`formatOutput`) paths so all three agree on what a date *is*.\n */\n protected toDateOnly(value: any): any {\n if (value == null) return value;\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return value;\n const y = value.getUTCFullYear();\n const m = String(value.getUTCMonth() + 1).padStart(2, '0');\n const d = String(value.getUTCDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n }\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(trimmed)) return trimmed.slice(0, 10);\n }\n return value;\n }\n\n /**\n * Normalise a filter value for a single column so the comparison the\n * driver sends to SQLite matches the on-disk representation.\n *\n * The platform stores `Field.datetime()` values as INTEGER milliseconds\n * (the result of passing a JS `Date` through better-sqlite3) but date\n * macros like `{last_quarter_start}` expand to an ISO `YYYY-MM-DD` string\n * client-side. Without coercion the SQL becomes `published_at >= '2026-…'`\n * which collapses to a TEXT-vs-INTEGER affinity compare and never\n * matches. We translate the ISO/Date/numeric inputs into the storage\n * type so the comparison works.\n *\n * For `Field.date()` we keep ISO TEXT but normalise Date objects to\n * `YYYY-MM-DD` for the same reason.\n */\n protected coerceFilterValue(table: string | null, field: string, value: any): any {\n if (value == null || !table) return value;\n if (Array.isArray(value)) return value.map((v) => this.coerceFilterValue(table, field, v));\n\n const isDatetime = this.datetimeFields[table]?.has(field);\n const isDate = this.dateFields[table]?.has(field);\n if (!isDatetime && !isDate) return value;\n\n const toMs = (v: any): number | null => {\n if (v instanceof Date) return v.getTime();\n if (typeof v === 'number' && Number.isFinite(v)) return v;\n if (typeof v === 'string') {\n const trimmed = v.trim();\n if (trimmed === '') return null;\n if (/^-?\\d+$/.test(trimmed)) {\n const n = Number(trimmed);\n if (Number.isFinite(n)) return n;\n }\n // Treat bare YYYY-MM-DD as start-of-day UTC; full ISO is parsed\n // as-is so timezones round-trip correctly.\n const iso = /^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed) ? `${trimmed}T00:00:00.000Z` : trimmed;\n const n = Date.parse(iso);\n return Number.isFinite(n) ? n : null;\n }\n return null;\n };\n\n if (isDatetime) {\n // Only SQLite stores `Field.datetime` as an INTEGER epoch (better-sqlite3\n // binds a JS `Date` as `.getTime()`); there the ISO/text comparand MUST be\n // coerced to epoch ms or it collapses to a TEXT-vs-INTEGER affinity compare\n // that never matches. Postgres/MySQL map datetime to a native TIMESTAMP\n // (see `defineColumn` → `table.timestamp`), where Knex binds an ISO string\n // or `Date` correctly — coercing to an epoch integer there would compare an\n // INTEGER against a TIMESTAMP and break the query. So gate on dialect.\n if (!this.isSqlite) return value;\n const ms = toMs(value);\n return ms == null ? value : ms;\n }\n\n // Field.date — normalise the comparand to YYYY-MM-DD (ADR-0053 Phase 1).\n return this.toDateOnly(value);\n }\n\n /**\n * Public, dialect-correct temporal filter-value coercion for callers that\n * build SQL *outside* the normal `find()`/`applyFilters()` path — chiefly the\n * analytics native-SQL strategy, which compiles a raw `SELECT … WHERE col >= $N`\n * and binds the value directly, bypassing `coerceFilterValue`.\n *\n * Given a logical object (table) name, a field name and a filter value\n * (typically an ISO date/datetime string from a dashboard relative-date\n * token like `{12_months_ago}`), this returns the value in the column's\n * on-disk storage form:\n * - SQLite `Field.datetime` → epoch milliseconds (INTEGER), so the\n * comparison matches the stored integer rather than failing a\n * TEXT-vs-INTEGER affinity compare.\n * - `Field.date` (any dialect) → `YYYY-MM-DD` text.\n * - Native-timestamp dialects / non-temporal fields → value unchanged.\n *\n * This is a thin, intentionally narrow wrapper over the same `coerceFilterValue`\n * the driver already uses, so there is exactly one source of truth for the\n * storage convention and the analytics path can never drift from CRUD.\n */\n public temporalFilterValue(objectName: string, field: string, value: any): any {\n return this.coerceFilterValue(objectName, field, value);\n }\n\n protected applyFilters(builder: Knex.QueryBuilder, filters: any) {\n if (!filters) return;\n const table = this.coercionKey(builder);\n\n if (!Array.isArray(filters) && typeof filters === 'object') {\n const hasMongoOperators = Object.keys(filters).some(\n (k) =>\n k.startsWith('$') ||\n (typeof filters[k] === 'object' &&\n filters[k] !== null &&\n Object.keys(filters[k]).some((op) => op.startsWith('$'))),\n );\n\n if (hasMongoOperators) {\n this.applyFilterCondition(builder, filters, 'and', table);\n return;\n }\n\n for (const [key, value] of Object.entries(filters)) {\n if (['limit', 'offset', 'fields', 'orderBy'].includes(key)) continue;\n builder.where(this.remoteColumn(table, key, key), this.coerceFilterValue(table, key, value) as any);\n }\n return;\n }\n\n if (!Array.isArray(filters) || filters.length === 0) return;\n\n let nextJoin: 'and' | 'or' = 'and';\n\n for (const item of filters) {\n if (typeof item === 'string') {\n if (item.toLowerCase() === 'or') nextJoin = 'or';\n else if (item.toLowerCase() === 'and') nextJoin = 'and';\n continue;\n }\n\n if (Array.isArray(item)) {\n const [fieldRaw, op, value] = item;\n const isCriterion = typeof fieldRaw === 'string' && typeof op === 'string';\n\n if (isCriterion) {\n const localField = this.mapSortField(fieldRaw);\n const field = this.remoteColumn(table, fieldRaw, localField);\n const coerced = this.coerceFilterValue(table, localField, value);\n const apply = (b: any) => {\n const method = nextJoin === 'or' ? 'orWhere' : 'where';\n const methodIn = nextJoin === 'or' ? 'orWhereIn' : 'whereIn';\n const methodNotIn = nextJoin === 'or' ? 'orWhereNotIn' : 'whereNotIn';\n\n if (op === 'contains') {\n this.applyContainsLike(b, method, field, value);\n return;\n }\n\n switch (op) {\n case '=':\n b[method](field, coerced);\n break;\n case '!=':\n b[method](field, '<>', coerced);\n break;\n case 'in':\n b[methodIn](field, coerced);\n break;\n case 'nin':\n b[methodNotIn](field, coerced);\n break;\n default:\n b[method](field, op, coerced);\n }\n };\n apply(builder);\n } else {\n const method = nextJoin === 'or' ? 'orWhere' : 'where';\n (builder as any)[method]((qb: any) => {\n this.applyFilters(qb, item);\n });\n }\n\n nextJoin = 'and';\n }\n }\n }\n\n /**\n * Apply a `contains` substring match as a parameterized `LIKE '%…%'`, escaping\n * the LIKE metacharacters `%` / `_` (and the escape char `\\`) in the user value\n * so they match literally instead of acting as wildcards — otherwise a value of\n * `%` matches every row (a filter-bypass, P0). Binds an explicit `ESCAPE '\\'`\n * because SQLite does not honour a default escape character (MySQL/Postgres do,\n * but the explicit clause is correct for all three).\n */\n private applyContainsLike(builder: any, method: string, field: string, value: unknown): void {\n const escaped = String(value).replace(/[\\\\%_]/g, '\\\\$&');\n const rawMethod = method.startsWith('or') ? 'orWhereRaw' : 'whereRaw';\n builder[rawMethod]('?? LIKE ? ESCAPE ?', [field, `%${escaped}%`, '\\\\']);\n }\n\n protected applyFilterCondition(builder: Knex.QueryBuilder, condition: any, logicalOp: 'and' | 'or' = 'and', tableHint?: string | null) {\n if (!condition || typeof condition !== 'object') return;\n const table = tableHint ?? this.coercionKey(builder);\n\n for (const [key, value] of Object.entries(condition)) {\n if (key === '$and' && Array.isArray(value)) {\n builder.where((qb) => {\n for (const sub of value) {\n qb.where((subQb) => {\n this.applyFilterCondition(subQb, sub, 'and', table);\n });\n }\n });\n } else if (key === '$or' && Array.isArray(value)) {\n const method = logicalOp === 'or' ? 'orWhere' : 'where';\n (builder as any)[method]((qb: any) => {\n for (const sub of value) {\n qb.orWhere((subQb: any) => {\n this.applyFilterCondition(subQb, sub, 'or', table);\n });\n }\n });\n } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const localField = this.mapSortField(key);\n const field = this.remoteColumn(table, key, localField);\n for (const [op, opValue] of Object.entries(value as Record<string, any>)) {\n const method = logicalOp === 'or' ? 'orWhere' : 'where';\n const coerced = this.coerceFilterValue(table, localField, opValue);\n switch (op) {\n case '$eq':\n (builder as any)[method](field, coerced);\n break;\n case '$ne':\n (builder as any)[method](field, '<>', coerced);\n break;\n case '$gt':\n (builder as any)[method](field, '>', coerced);\n break;\n case '$gte':\n (builder as any)[method](field, '>=', coerced);\n break;\n case '$lt':\n (builder as any)[method](field, '<', coerced);\n break;\n case '$lte':\n (builder as any)[method](field, '<=', coerced);\n break;\n case '$in': {\n const mIn = logicalOp === 'or' ? 'orWhereIn' : 'whereIn';\n (builder as any)[mIn](field, coerced as any[]);\n break;\n }\n case '$nin': {\n const mNotIn = logicalOp === 'or' ? 'orWhereNotIn' : 'whereNotIn';\n (builder as any)[mNotIn](field, coerced as any[]);\n break;\n }\n case '$contains':\n this.applyContainsLike(builder, method, field, opValue);\n break;\n default:\n (builder as any)[method](field, coerced);\n }\n }\n } else {\n const localField = this.mapSortField(key);\n const field = this.remoteColumn(table, key, localField);\n const method = logicalOp === 'or' ? 'orWhere' : 'where';\n (builder as any)[method](field, this.coerceFilterValue(table, localField, value) as any);\n }\n }\n }\n\n // ── Field mapping ───────────────────────────────────────────────────────────\n\n protected mapSortField(field: string): string {\n if (field === 'createdAt') return 'created_at';\n if (field === 'updatedAt') return 'updated_at';\n return field;\n }\n\n /**\n * Physical column for a logical field on an external object that declares an\n * `external.columnMap` (ADR-0015). Returns `fallback` (the caller's existing\n * per-site resolution) when the object has no columnMap, so managed objects\n * and external objects without a columnMap are byte-for-byte unchanged.\n */\n protected remoteColumn(object: string | null | undefined, field: string, fallback: string): string {\n const m = object ? this.fieldColumnByObject[object] : undefined;\n return (m && m[field]) || fallback;\n }\n\n /**\n * Remap a write payload's logical field keys to physical remote columns for an\n * external object with a columnMap. No-op otherwise. Applied AFTER formatInput\n * (whose value coercion is keyed by logical field name).\n */\n protected applyWriteColumnMap(object: string, data: any): any {\n const m = this.fieldColumnByObject[object];\n if (!m || !data || typeof data !== 'object') return data;\n const out: any = {};\n for (const [k, v] of Object.entries(data)) out[m[k] ?? k] = v;\n return out;\n }\n\n protected mapAggregateFunc(func: string): string {\n switch (func) {\n case 'count':\n return 'count';\n case 'sum':\n return 'sum';\n case 'avg':\n return 'avg';\n case 'min':\n return 'min';\n case 'max':\n return 'max';\n default:\n throw new Error(`Unsupported aggregate function: ${func}`);\n }\n }\n\n // ── Window function builder ─────────────────────────────────────────────────\n\n protected buildWindowFunction(spec: any): string {\n const func = spec.function.toUpperCase();\n let sql = `${func}()`;\n\n const overParts: string[] = [];\n\n if (spec.partitionBy && Array.isArray(spec.partitionBy) && spec.partitionBy.length > 0) {\n const partitionFields = spec.partitionBy.map((f: string) => this.mapSortField(f)).join(', ');\n overParts.push(`PARTITION BY ${partitionFields}`);\n }\n\n if (spec.orderBy && Array.isArray(spec.orderBy) && spec.orderBy.length > 0) {\n const orderFields = spec.orderBy\n .map((s: any) => {\n const field = this.mapSortField(s.field);\n const order = (s.order || 'asc').toUpperCase();\n return `${field} ${order}`;\n })\n .join(', ');\n overParts.push(`ORDER BY ${orderFields}`);\n }\n\n sql += overParts.length > 0 ? ` OVER (${overParts.join(' ')})` : ` OVER ()`;\n return sql;\n }\n\n // ── Column creation helper ──────────────────────────────────────────────────\n\n protected createColumn(table: Knex.CreateTableBuilder, name: string, field: any) {\n if (field.multiple) {\n table.json(name);\n return;\n }\n\n const type = field.type || 'string';\n let col: any;\n switch (type) {\n case 'string':\n case 'email':\n case 'url':\n case 'phone':\n case 'password':\n col = table.string(name);\n break;\n case 'text':\n case 'textarea':\n case 'html':\n case 'markdown':\n col = table.text(name);\n break;\n case 'integer':\n case 'int':\n col = table.integer(name);\n break;\n case 'float':\n case 'number':\n case 'currency':\n case 'percent':\n // `rating`/`slider`/`progress` are authored as numeric scalars (a star\n // count, a slider position, a percent-of-completion). Without an explicit\n // case they fell to `default → table.string`, giving the column TEXT\n // affinity so SQLite coerced the written number to a string ('4' not 4) —\n // a silent type-fidelity leak the value-loss tests didn't catch. REAL\n // affinity round-trips them as JS numbers (#field-zoo).\n case 'rating':\n case 'slider':\n case 'progress':\n col = table.float(name);\n break;\n // `toggle` is a boolean rendered as a switch. Same leak as above (TEXT\n // affinity stored '1'); a boolean column gives NUMERIC affinity and the\n // `booleanFields` read-coercion below converts the stored 1/0 back to a\n // real JS boolean.\n case 'boolean':\n case 'toggle':\n col = table.boolean(name);\n break;\n case 'date':\n col = table.date(name);\n break;\n case 'datetime':\n col = table.timestamp(name);\n break;\n case 'time':\n col = table.time(name);\n break;\n case 'lookup':\n col = table.string(name);\n if (field.reference_to) {\n table.foreign(name).references('id').inTable(field.reference_to);\n }\n break;\n case 'summary':\n col = table.float(name);\n break;\n case 'auto_number':\n case 'autonumber':\n col = table.string(name);\n break;\n case 'formula':\n return; // Virtual — no column\n default:\n // Array/object-valued types are stored as a JSON column. Driven by the\n // single `JSON_COLUMN_TYPES` source so this DDL switch and `isJsonField`\n // (the read-side deserializer) can never drift — the drift between them\n // is exactly what let array-valued fields reach the binder un-serialized\n // (#field-zoo). Everything else is a plain string.\n col = JSON_COLUMN_TYPES.has(type) ? table.json(name) : table.string(name);\n }\n\n if (col) {\n if (field.unique) col.unique();\n if (field.required) col.notNullable();\n // `defaultValue: 'NOW()'` is a framework convention for \"use the\n // database clock at insert time\". Translate it to the driver-native\n // CURRENT_TIMESTAMP equivalent so the column gets a real default\n // instead of leaving the literal string 'NOW()' for whatever\n // upstream code happens to write.\n if (\n (type === 'datetime' || type === 'date' || type === 'time') &&\n typeof field.defaultValue === 'string' &&\n /^now\\(\\)$/i.test(field.defaultValue.trim())\n ) {\n col.defaultTo(this.knex.fn.now());\n } else if (field.defaultValue !== undefined && field.defaultValue !== null) {\n const dv = field.defaultValue;\n if (typeof dv === 'string' && /^now\\(\\)$/i.test(dv.trim())) {\n col.defaultTo(this.knex.fn.now());\n } else if (typeof dv !== 'object') {\n col.defaultTo(dv as any);\n }\n }\n }\n }\n\n // ── Database helpers ────────────────────────────────────────────────────────\n\n protected async ensureDatabaseExists() {\n // SQLite auto-creates database files but NOT parent directories.\n // Ensure the directory exists so better-sqlite3 can create the file.\n if (this.isSqlite) {\n const conn = (this.config as any).connection;\n const filename = typeof conn === 'string' ? conn : conn?.filename;\n if (filename && filename !== ':memory:' && !filename.startsWith(':')) {\n const { dirname } = await import('node:path');\n const { mkdir } = await import('node:fs/promises');\n const dir = dirname(filename);\n if (dir && dir !== '.') {\n await mkdir(dir, { recursive: true });\n }\n }\n return;\n }\n\n // Only PostgreSQL and MySQL support programmatic database creation\n if (!this.isPostgres && !this.isMysql) return;\n\n try {\n await this.knex.raw('SELECT 1');\n } catch (e: any) {\n // PostgreSQL: '3D000' = database does not exist\n // MySQL: 'ER_BAD_DB_ERROR' (errno 1049) = unknown database\n if (\n e.code === '3D000' ||\n e.code === 'ER_BAD_DB_ERROR' ||\n e.errno === 1049\n ) {\n await this.createDatabase();\n } else {\n throw e;\n }\n }\n }\n\n protected async createDatabase() {\n const config = this.config as any;\n const connection = config.connection;\n let dbName = '';\n const adminConfig = { ...config };\n\n if (this.isPostgres) {\n // PostgreSQL: connect to the 'postgres' maintenance database\n if (typeof connection === 'string') {\n const url = new URL(connection);\n dbName = url.pathname.slice(1);\n url.pathname = '/postgres';\n adminConfig.connection = url.toString();\n } else {\n dbName = connection.database;\n adminConfig.connection = { ...connection, database: 'postgres' };\n }\n } else if (this.isMysql) {\n // MySQL: connect without specifying a database\n if (typeof connection === 'string') {\n const url = new URL(connection);\n dbName = url.pathname.slice(1);\n url.pathname = '/';\n adminConfig.connection = url.toString();\n } else {\n dbName = connection.database;\n const { database: _db, ...rest } = connection;\n adminConfig.connection = rest;\n }\n } else {\n return; // Unsupported dialect for auto-creation\n }\n\n const adminKnex = knex(adminConfig);\n try {\n if (this.isPostgres) {\n await adminKnex.raw(`CREATE DATABASE \"${dbName}\"`);\n } else if (this.isMysql) {\n await adminKnex.raw(`CREATE DATABASE IF NOT EXISTS \\`${dbName}\\``);\n }\n } finally {\n await adminKnex.destroy();\n }\n }\n\n protected isJsonField(type: string, field: any): boolean {\n return JSON_COLUMN_TYPES.has(type) || !!field.multiple;\n }\n\n // ── SQLite serialisation ────────────────────────────────────────────────────\n\n protected formatInput(object: string, data: any): any {\n let copy: any = data;\n let copied = false;\n\n // Insert/update-time safety net: any caller that passes the literal\n // string 'NOW()' (often because a field defaultValue leaked unresolved)\n // gets it replaced with a real ISO timestamp here, before it hits the\n // wire. Applies to every driver, not just SQLite.\n if (data && typeof data === 'object') {\n const now = new Date().toISOString();\n for (const key of Object.keys(data)) {\n const v = (data as any)[key];\n if (typeof v === 'string' && /^now\\(\\)$/i.test(v.trim())) {\n if (!copied) { copy = { ...data }; copied = true; }\n copy[key] = now;\n }\n }\n }\n\n // ADR-0053 Phase 1: a `Field.date` is a timezone-naive calendar day, not\n // an instant. Collapse any `Date` or full-ISO value to `YYYY-MM-DD` before\n // it hits the wire so storage matches the date-only contract the filter\n // layer (`coerceFilterValue`) already enforces — the write/filter\n // asymmetry was the root cause of the silent date-equality miss.\n // `Field.datetime` is untouched (it keeps full-instant semantics).\n const dateFields = this.dateFields[object];\n if (dateFields && dateFields.size > 0 && copy && typeof copy === 'object') {\n for (const field of dateFields) {\n const v = copy[field];\n if (v == null) continue;\n const normalized = this.toDateOnly(v);\n if (normalized !== v) {\n if (!copied) { copy = { ...copy }; copied = true; }\n copy[field] = normalized;\n }\n }\n }\n\n if (!this.isSqlite) return copy;\n\n const fields = this.jsonFields[object];\n if (fields && fields.length > 0) {\n if (!copied) { copy = { ...copy }; copied = true; }\n for (const field of fields) {\n if (copy[field] !== undefined && typeof copy[field] === 'object' && copy[field] !== null) {\n copy[field] = JSON.stringify(copy[field]);\n }\n }\n }\n\n // Safety net: better-sqlite3 can only bind numbers/strings/bigints/buffers/\n // null. Any value still an array or plain object here (a field type not\n // classified as JSON, a `Field.multiple` we didn't catch, or an ad-hoc\n // payload) would otherwise throw a raw TypeError mid-insert. Serialize it\n // to JSON so the write degrades to a stored string instead of a 500.\n for (const key of Object.keys(copy)) {\n const v = copy[key];\n if (v !== null && typeof v === 'object' && !(v instanceof Date) && !Buffer.isBuffer(v)) {\n if (!copied) { copy = { ...copy }; copied = true; }\n copy[key] = JSON.stringify(v);\n }\n }\n return copy;\n }\n\n protected formatOutput(object: string, data: any): any {\n if (!data) return data;\n\n // External columnMap (ADR-0015): rename physical remote-column keys to local\n // field names BEFORE coercion (which is keyed by local field). No-op for\n // managed objects and external objects without a columnMap.\n const colToField = this.columnFieldByObject[object];\n if (colToField && typeof data === 'object') {\n for (const [remoteCol, localField] of Object.entries(colToField)) {\n if (remoteCol !== localField && Object.prototype.hasOwnProperty.call(data, remoteCol)) {\n // Explicit columnMap wins: the remote column is the source of truth for\n // this local field, even if a same-named native column also exists.\n data[localField] = data[remoteCol];\n delete data[remoteCol];\n }\n }\n }\n\n if (this.isSqlite) {\n const jsonFields = this.jsonFields[object];\n if (jsonFields && jsonFields.length > 0) {\n for (const field of jsonFields) {\n if (data[field] !== undefined && typeof data[field] === 'string') {\n try {\n data[field] = JSON.parse(data[field]);\n } catch {\n // keep as string\n }\n }\n }\n }\n\n const booleanFields = this.booleanFields[object];\n if (booleanFields && booleanFields.length > 0) {\n for (const field of booleanFields) {\n if (data[field] !== undefined && data[field] !== null) {\n data[field] = Boolean(data[field]);\n }\n }\n }\n\n // Numeric scalars stored on a legacy TEXT-affinity column come back as\n // strings ('4'); coerce numeric-looking strings back to numbers so the\n // declared type wins regardless of when the column was created. Only\n // touch strings — a fresh REAL/INTEGER column already yields a number,\n // and a genuinely non-numeric value (junk legacy data) is left intact\n // rather than turned into NaN. See NUMERIC_SCALAR_TYPES.\n const numericFields = this.numericFields[object];\n if (numericFields && numericFields.length > 0) {\n for (const field of numericFields) {\n const v = data[field];\n if (typeof v === 'string' && v.trim() !== '') {\n const n = Number(v);\n if (!Number.isNaN(n)) data[field] = n;\n }\n }\n }\n }\n\n // ADR-0053 Phase 1: present `Field.date` as a timezone-naive `YYYY-MM-DD`\n // string, slicing any stored time component. This transparently repairs\n // legacy rows written as a full timestamp before this normalization, so\n // date-equality works without a data migration. Runs for every dialect.\n const dateFields = this.dateFields[object];\n if (dateFields && dateFields.size > 0) {\n for (const field of dateFields) {\n const v = data[field];\n if (v == null) continue;\n const normalized = this.toDateOnly(v);\n if (normalized !== v) data[field] = normalized;\n }\n }\n\n return data;\n }\n\n // ── Introspection internals ─────────────────────────────────────────────────\n\n protected async introspectColumns(tableName: string): Promise<IntrospectedColumn[]> {\n const columnInfo = await this.knex(tableName).columnInfo();\n const columns: IntrospectedColumn[] = [];\n\n for (const [colName, info] of Object.entries<any>(columnInfo)) {\n let type = 'string';\n let maxLength: number | undefined;\n\n if (this.isSqlite) {\n type = info.type?.toLowerCase() || 'string';\n } else {\n type = info.type || 'string';\n }\n\n if (info.maxLength) {\n maxLength = info.maxLength;\n }\n\n columns.push({\n name: colName,\n type,\n nullable: info.nullable !== false,\n defaultValue: info.defaultValue,\n isPrimary: false,\n isUnique: false,\n maxLength,\n });\n }\n\n return columns;\n }\n\n protected async introspectForeignKeys(tableName: string): Promise<IntrospectedForeignKey[]> {\n const foreignKeys: IntrospectedForeignKey[] = [];\n\n try {\n if (this.isPostgres) {\n const result = await this.knex.raw(\n `\n SELECT\n kcu.column_name,\n ccu.table_name AS referenced_table,\n ccu.column_name AS referenced_column,\n tc.constraint_name\n FROM information_schema.table_constraints AS tc\n JOIN information_schema.key_column_usage AS kcu\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n JOIN information_schema.constraint_column_usage AS ccu\n ON ccu.constraint_name = tc.constraint_name\n AND ccu.table_schema = tc.table_schema\n WHERE tc.constraint_type = 'FOREIGN KEY'\n AND tc.table_name = ?\n `,\n [tableName],\n );\n\n for (const row of result.rows) {\n foreignKeys.push({\n columnName: row.column_name,\n referencedTable: row.referenced_table,\n referencedColumn: row.referenced_column,\n constraintName: row.constraint_name,\n });\n }\n } else if (this.isMysql) {\n const result = await this.knex.raw(\n `\n SELECT\n COLUMN_NAME as column_name,\n REFERENCED_TABLE_NAME as referenced_table,\n REFERENCED_COLUMN_NAME as referenced_column,\n CONSTRAINT_NAME as constraint_name\n FROM information_schema.KEY_COLUMN_USAGE\n WHERE TABLE_SCHEMA = DATABASE()\n AND TABLE_NAME = ?\n AND REFERENCED_TABLE_NAME IS NOT NULL\n `,\n [tableName],\n );\n\n for (const row of result[0]) {\n foreignKeys.push({\n columnName: row.column_name,\n referencedTable: row.referenced_table,\n referencedColumn: row.referenced_column,\n constraintName: row.constraint_name,\n });\n }\n } else if (this.isSqlite) {\n const tableExistsResult = await this.knex.raw(\n \"SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?\",\n [tableName],\n );\n\n if (!Array.isArray(tableExistsResult) || tableExistsResult.length === 0) {\n return foreignKeys;\n }\n\n const safeTableName = tableName.replace(/[^a-zA-Z0-9_]/g, '');\n const result = await this.knex.raw(`PRAGMA foreign_key_list(${safeTableName})`);\n\n for (const row of result) {\n foreignKeys.push({\n columnName: row.from,\n referencedTable: row.table,\n referencedColumn: row.to,\n constraintName: `fk_${tableName}_${row.from}`,\n });\n }\n }\n } catch {\n // silently ignore introspection errors\n }\n\n return foreignKeys;\n }\n\n protected async introspectPrimaryKeys(tableName: string): Promise<string[]> {\n const primaryKeys: string[] = [];\n\n try {\n if (this.isPostgres) {\n const result = await this.knex.raw(\n `\n SELECT a.attname as column_name\n FROM pg_index i\n JOIN pg_attribute a ON a.attrelid = i.indrelid\n AND a.attnum = ANY(i.indkey)\n WHERE i.indrelid = ?::regclass\n AND i.indisprimary\n `,\n [tableName],\n );\n\n for (const row of result.rows) {\n primaryKeys.push(row.column_name);\n }\n } else if (this.isMysql) {\n const result = await this.knex.raw(\n `\n SELECT COLUMN_NAME as column_name\n FROM information_schema.KEY_COLUMN_USAGE\n WHERE TABLE_SCHEMA = DATABASE()\n AND TABLE_NAME = ?\n AND CONSTRAINT_NAME = 'PRIMARY'\n `,\n [tableName],\n );\n\n for (const row of result[0]) {\n primaryKeys.push(row.column_name);\n }\n } else if (this.isSqlite) {\n const safeTableName = tableName.replace(/[^a-zA-Z0-9_]/g, '');\n\n const tablesResult = await this.knex.raw(\"SELECT name FROM sqlite_master WHERE type = 'table'\");\n const tableNames = Array.isArray(tablesResult) ? tablesResult.map((row: any) => row.name) : [];\n\n if (!tableNames.includes(safeTableName)) {\n return primaryKeys;\n }\n\n const result = await this.knex.raw(`PRAGMA table_info(${safeTableName})`);\n\n for (const row of result) {\n if (row.pk === 1) {\n primaryKeys.push(row.name);\n }\n }\n }\n } catch {\n // silently ignore\n }\n\n return primaryKeys;\n }\n\n protected async introspectUniqueConstraints(tableName: string): Promise<string[]> {\n const uniqueColumns: string[] = [];\n\n try {\n if (this.isPostgres) {\n const result = await this.knex.raw(\n `\n SELECT c.column_name\n FROM information_schema.table_constraints tc\n JOIN information_schema.constraint_column_usage AS ccu\n ON tc.constraint_schema = ccu.constraint_schema\n AND tc.constraint_name = ccu.constraint_name\n WHERE tc.constraint_type = 'UNIQUE'\n AND tc.table_name = ?\n `,\n [tableName],\n );\n\n for (const row of result.rows) {\n uniqueColumns.push(row.column_name);\n }\n } else if (this.isMysql) {\n const result = await this.knex.raw(\n `\n SELECT COLUMN_NAME\n FROM information_schema.TABLE_CONSTRAINTS tc\n JOIN information_schema.KEY_COLUMN_USAGE kcu\n USING (CONSTRAINT_NAME, TABLE_SCHEMA, TABLE_NAME)\n WHERE CONSTRAINT_TYPE = 'UNIQUE'\n AND TABLE_SCHEMA = DATABASE()\n AND TABLE_NAME = ?\n `,\n [tableName],\n );\n\n for (const row of result[0]) {\n uniqueColumns.push(row.COLUMN_NAME);\n }\n } else if (this.isSqlite) {\n const safeTableName = tableName.replace(/[^a-zA-Z0-9_]/g, '');\n\n const tablesResult = await this.knex.raw(\"SELECT name FROM sqlite_master WHERE type = 'table'\");\n const tableNames = Array.isArray(tablesResult) ? tablesResult.map((row: any) => row.name) : [];\n\n if (!tableNames.includes(safeTableName)) {\n return uniqueColumns;\n }\n\n const indexes = await this.knex.raw(`PRAGMA index_list(${safeTableName})`);\n\n for (const idx of indexes) {\n if (idx.unique === 1) {\n const info = await this.knex.raw(`PRAGMA index_info(${idx.name})`);\n if (info.length === 1) {\n uniqueColumns.push(info[0].name);\n }\n }\n }\n }\n } catch {\n // silently ignore\n }\n\n return uniqueColumns;\n }\n}\n"],"mappings":"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","names":["knex","n"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/sql-driver.ts","../src/schema-drift.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { SqlDriver } from './sql-driver.js';\n\nexport { SqlDriver };\nexport type {\n SqlDriverConfig,\n IntrospectedSchema,\n IntrospectedTable,\n IntrospectedColumn,\n IntrospectedForeignKey,\n} from './sql-driver.js';\n\n// Managed-schema drift / reconcile (#2186)\nexport {\n diffManagedTable,\n driftKey,\n fieldHasColumn,\n BUILTIN_COLUMNS,\n} from './schema-drift.js';\nexport type {\n ManagedDriftEntry,\n DriftOp,\n DriftCategory,\n SqlDialectName,\n PhysicalColumn,\n FieldDef as DriftFieldDef,\n} from './schema-drift.js';\n\nexport default {\n id: 'com.objectstack.driver.sql',\n version: '1.0.0',\n\n onEnable: async (context: any) => {\n const { logger, config, drivers } = context;\n logger.info('[SQL Driver] Initializing...');\n\n if (drivers) {\n const driver = new SqlDriver(config);\n drivers.register(driver);\n logger.info(`[SQL Driver] Registered driver: ${driver.name}`);\n } else {\n logger.warn('[SQL Driver] No driver registry found in context.');\n }\n },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * SQL Driver for ObjectStack\n *\n * Implements the standard IDataDriver from @objectstack/spec via Knex.js.\n * Supports PostgreSQL, MySQL, SQLite, and other SQL databases.\n */\n\nimport type { QueryAST, DriverOptions, SchemaMode } from '@objectstack/spec/data';\nimport { parseAutonumberFormat, renderAutonumber, missingFieldValues, type AutonumberToken } from '@objectstack/spec/data';\nimport type { IDataDriver } from '@objectstack/spec/contracts';\nimport { StorageNameMapping } from '@objectstack/spec/system';\nimport { ExternalSchemaModeViolationError } from '@objectstack/spec/shared';\nimport {\n diffManagedTable,\n driftKey,\n type ManagedDriftEntry,\n type DriftOp,\n type SqlDialectName,\n type PhysicalColumn,\n} from './schema-drift.js';\nimport knex, { Knex } from 'knex';\nimport { nanoid } from 'nanoid';\nimport { createHash } from 'node:crypto';\n\n/**\n * Default ID length for auto-generated IDs.\n */\nconst DEFAULT_ID_LENGTH = 16;\n\n/**\n * Internal table that persists per-(object, tenant, field) auto-number\n * counters so sequences are monotonic, tenant-isolated, and resilient to\n * concurrent writers. Lazily created on first autonumber-bearing insert.\n */\nconst SEQUENCES_TABLE = '_objectstack_sequences';\n\n/**\n * Sentinel tenant_id used when an object has no tenant field (org-less\n * objects like Setup-side singletons). Keeps the (object, tenant, field)\n * primary key non-null.\n */\nconst GLOBAL_TENANT = '__global__';\n\n/**\n * Field types whose value is an array or object and must be stored as a JSON\n * column (and JSON-(de)serialized at the driver boundary). SINGLE SOURCE for\n * both the DDL column-type switch and `isJsonField` so the two can't drift —\n * the drift between them is exactly what let array-valued fields (multiselect/\n * checkboxes/tags/repeater/vector) reach the SQLite binder un-serialized and\n * crash with \"SQLite3 can only bind numbers, strings, bigints, buffers, and\n * null\" (#field-zoo). `image`/`file`/`avatar`/`video`/`audio` hold structured\n * upload metadata; `composite`/`address`/`location`/`record` are objects; the\n * rest are arrays.\n */\nconst JSON_COLUMN_TYPES = new Set<string>([\n 'json', 'object', 'array', 'record',\n 'image', 'file', 'avatar', 'video', 'audio',\n 'location', 'address', 'composite',\n 'multiselect', 'checkboxes', 'tags', 'repeater', 'vector',\n]);\n\n/**\n * Field types whose value is a numeric scalar. SINGLE SOURCE for the DDL\n * column-type switch (these map to INTEGER/REAL columns) and the read-side\n * coercion registry (`numericFields`).\n *\n * The read coercion exists so the fix is robust on SQLite even when the column\n * predates it: a `rating`/`slider`/`progress` column created before #2025 has\n * TEXT affinity and returns '4' not 4, and SQLite never alters a column's type\n * in-place (the reconciler only ADDS columns). Coercing numeric-looking strings\n * back to numbers on read transparently repairs those legacy rows — mirroring\n * how `dateFields` repairs legacy timestamp-typed `Field.date` rows — so the\n * type fidelity no longer depends on column affinity alone. `toggle`/`record`\n * already self-heal this way via `booleanFields`/`jsonFields`; this closes the\n * gap for the numeric scalars.\n */\nconst NUMERIC_SCALAR_TYPES = new Set<string>([\n 'integer', 'int',\n 'float', 'number', 'currency', 'percent', 'summary',\n 'rating', 'slider', 'progress',\n]);\n\n// ── Introspection Types ──────────────────────────────────────────────────────\n\nexport interface IntrospectedColumn {\n name: string;\n type: string;\n nullable: boolean;\n defaultValue?: unknown;\n isPrimary?: boolean;\n isUnique?: boolean;\n maxLength?: number;\n}\n\nexport interface IntrospectedForeignKey {\n columnName: string;\n referencedTable: string;\n referencedColumn: string;\n constraintName?: string;\n}\n\nexport interface IntrospectedTable {\n name: string;\n columns: IntrospectedColumn[];\n foreignKeys: IntrospectedForeignKey[];\n primaryKeys: string[];\n}\n\nexport interface IntrospectedSchema {\n tables: Record<string, IntrospectedTable>;\n}\n\n// ── Configuration Types ──────────────────────────────────────────────────────\n\n/**\n * SqlDriver configuration — passed directly to Knex.\n * See https://knexjs.org/guide/#configuration-options\n *\n * `schemaMode` (ADR-0015) is an ObjectStack-level concern, not a Knex\n * option: it is stripped before constructing the Knex instance and gates\n * all schema-mutating DDL. Defaults to `'managed'` when omitted, preserving\n * legacy behaviour.\n */\nexport type SqlDriverConfig = Knex.Config & {\n schemaMode?: SchemaMode;\n /**\n * Dev-only schema auto-reconcile (issue #2186). When `'safe'`, `initObjects`\n * automatically applies *non-destructive* alters (relax NOT NULL, widen\n * varchar) so an existing database self-heals after a metadata change\n * loosens a constraint. `'off'` (default) only warns. Never applies\n * destructive DDL, and is force-disabled when `NODE_ENV==='production'`.\n */\n autoMigrate?: 'off' | 'safe';\n};\n\n// ── SQL Driver ───────────────────────────────────────────────────────────────\n\n/**\n * SQL Driver for ObjectStack.\n *\n * Implements the IDataDriver contract via Knex.js for optimal SQL\n * generation against PostgreSQL, MySQL, SQLite and other SQL databases.\n */\nexport class SqlDriver implements IDataDriver {\n // IDataDriver metadata\n public readonly name: string = 'com.objectstack.driver.sql';\n public readonly version: string = '1.0.0';\n public get supports() {\n return {\n // Basic CRUD Operations\n create: true,\n read: true,\n update: true,\n delete: true,\n\n // Bulk Operations\n bulkCreate: true,\n bulkUpdate: true,\n bulkDelete: true,\n\n // Transaction & Connection Management\n transactions: true,\n savepoints: false,\n\n // Query Operations\n queryFilters: true,\n queryAggregations: true,\n /**\n * Per-granularity native date bucket support. Granularities marked\n * `false` (or absent) fall back to in-memory `bucketDateValue()` via\n * `engine.findData` — see `buildDateBucketExpr()` for the SQL emitted.\n */\n queryDateGranularity: this.dateGranularityCapabilities,\n querySorting: true,\n queryPagination: true,\n queryWindowFunctions: true,\n querySubqueries: true,\n queryCTE: false,\n joins: true,\n\n // Advanced Features\n fullTextSearch: false,\n jsonQuery: false,\n geospatialQuery: false,\n streaming: false,\n jsonFields: true,\n arrayFields: true,\n vectorSearch: false,\n // Persistent, atomic autonumber sequences via `_objectstack_sequences`\n // (see fillAutoNumberFields / getNextSequenceValue). The engine defers\n // autonumber generation to this driver — it is the single source of truth.\n autonumber: true,\n\n // Schema Management\n schemaSync: true,\n batchSchemaSync: false,\n migrations: false,\n // Object-level declared `indexes` (incl. multi-column UNIQUE) are\n // materialized during `initObjects` — see `syncDeclaredIndexes`.\n indexes: true,\n\n // Performance & Optimization\n connectionPooling: true,\n preparedStatements: true,\n queryCache: false,\n };\n }\n\n protected knex: Knex;\n protected config: Knex.Config;\n protected jsonFields: Record<string, string[]> = {};\n protected booleanFields: Record<string, string[]> = {};\n protected numericFields: Record<string, string[]> = {};\n protected dateFields: Record<string, Set<string>> = {};\n protected datetimeFields: Record<string, Set<string>> = {};\n /**\n * Federation read path (ADR-0015). For external objects whose physical\n * remote table differs from the object name, these map between the two so\n * {@link getBuilder} targets the remote table while the coercion maps above\n * stay keyed by OBJECT name (matching formatInput/formatOutput). Empty for\n * managed objects, so the managed query path is unchanged.\n */\n protected physicalTableByObject: Record<string, string> = {};\n protected physicalSchemaByObject: Record<string, string> = {};\n protected objectByPhysicalTable: Record<string, string> = {};\n /** External columnMap (ADR-0015): logical field -> physical remote column (for WHERE/ORDER BY/writes). */\n protected fieldColumnByObject: Record<string, Record<string, string>> = {};\n /** External columnMap inverse: physical remote column -> logical field (for read output remap). */\n protected columnFieldByObject: Record<string, Record<string, string>> = {};\n protected tablesWithTimestamps: Set<string> = new Set();\n /**\n * Autonumber field configs per table, captured during initObjects.\n *\n * Each entry records:\n * - `prefix` + `padWidth`: how to render the next value (`CTR-0007`)\n * - `tenantField`: the column to scope the sequence by (defaults to\n * `organization_id` if the object has that field, otherwise null →\n * sequence is shared globally for that field)\n *\n * Numbering is backed by the `_objectstack_sequences` row keyed by\n * `(object, tenant_id, field)`, not by scanning the data table on each\n * insert. The sequence row is bootstrapped from the existing MAX on\n * first use so legacy data is respected.\n */\n protected autoNumberFields: Record<\n string,\n Array<{ name: string; format: string; tokens: AutonumberToken[]; tenantField: string | null }>\n > = {};\n\n /** Whether the sequences table has been ensured this process. */\n protected sequencesTableReady = false;\n /**\n * Whether `_objectstack_sequences` is the current `key_hash`-keyed shape.\n * Set on a fresh create or a successful in-place migration. If a legacy table\n * could NOT be migrated, this stays false: fixed-prefix sequences (empty\n * scope) keep working via the legacy `(object, tenant_id, field)` key, while a\n * per-scope write raises an actionable error rather than corrupting counters.\n */\n protected sequencesHasKeyHash = false;\n /** In-flight ensure promise; deduplicates concurrent first calls. */\n protected sequencesTableEnsurePromise: Promise<void> | null = null;\n\n /**\n * Per-table tenant-isolation column. Populated during `initObjects` by\n * detecting an `organization_id` field. When set and the caller passes\n * `DriverOptions.tenantId`, the driver automatically:\n *\n * - scopes reads/updates/deletes/aggregates to that tenant\n * - injects `organization_id` on inserts that omit it\n *\n * If `tenantId` is absent (admin / seed / system path) no scope is\n * applied — preserves backward compatibility for tools that legitimately\n * need cross-tenant access. Tenant enforcement is therefore opt-in by\n * the caller, not by the driver.\n */\n protected tenantFieldByTable: Record<string, string | null> = {};\n\n /** Throttle table for missing-tenantId warnings ({object}:{op}). */\n protected tenantAuditWarned: Set<string> = new Set();\n\n /**\n * Optional logger sink for security-audit warnings. Tests inject a spy;\n * production callers wire in their preferred logger. Defaults to\n * `console.warn` so warnings surface even without setup.\n */\n protected logger: {\n warn: (msg: string, meta?: any) => void;\n info?: (msg: string, meta?: any) => void;\n } = {\n warn: (msg, meta) => console.warn(msg, meta ?? ''),\n };\n\n /** Whether the underlying database is a SQLite variant (sqlite3 or better-sqlite3). */\n protected get isSqlite(): boolean {\n const c = (this.config as any).client;\n return c === 'sqlite3' || c === 'better-sqlite3';\n }\n\n /** Whether the underlying database is PostgreSQL. */\n protected get isPostgres(): boolean {\n const c = (this.config as any).client;\n return c === 'pg' || c === 'postgresql';\n }\n\n /** Whether the underlying database is MySQL. */\n protected get isMysql(): boolean {\n const c = (this.config as any).client;\n return c === 'mysql' || c === 'mysql2';\n }\n\n /**\n * Per-granularity native SQL bucket support, computed from dialect.\n *\n * Must match `bucketDateValue()` in @objectstack/objectql exactly:\n * year → 'YYYY'\n * month → 'YYYY-MM'\n * day → 'YYYY-MM-DD'\n * quarter → 'YYYY-Q[1-4]'\n * week → 'YYYY-W[01-53]' (ISO-8601)\n *\n * Granularities not listed (or set to false) fall back to in-memory bucketing\n * via engine.findData → applyInMemoryAggregation.\n */\n protected get dateGranularityCapabilities(): Record<string, boolean> {\n if (this.isPostgres) {\n return { day: true, month: true, quarter: true, year: true, week: true };\n }\n if (this.isMysql) {\n return { day: true, month: true, quarter: true, year: true, week: true };\n }\n if (this.isSqlite) {\n // SQLite's strftime gained ISO week (%V) in 3.46 (2024-05-23); play it safe\n // and bucket week in-memory. Day/month/year/quarter are universally available.\n return { day: true, month: true, quarter: true, year: true, week: false };\n }\n return {};\n }\n\n /**\n * Build SQL fragment + bindings for a date bucket expression.\n * Returns `null` when the current dialect does not support the requested\n * granularity — callers must fall back to in-memory bucketing.\n *\n * Exposed as `{sql, bindings}` (not `Knex.Raw`) so callers can both\n * `groupByRaw()` and embed the same expression inside a `select() as alias`\n * with correctly forwarded identifier bindings.\n */\n protected buildDateBucketExpr(\n field: string,\n granularity: 'day' | 'week' | 'month' | 'quarter' | 'year',\n ): { sql: string; bindings: any[] } | null {\n if (!this.dateGranularityCapabilities[granularity]) return null;\n\n if (this.isPostgres) {\n switch (granularity) {\n case 'year': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY')`, bindings: [field] };\n case 'month': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY-MM')`, bindings: [field] };\n case 'day': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY-MM-DD')`, bindings: [field] };\n case 'quarter': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'YYYY\"-Q\"Q')`, bindings: [field] };\n case 'week': return { sql: `to_char((??)::timestamptz AT TIME ZONE 'UTC', 'IYYY\"-W\"IW')`, bindings: [field] };\n }\n }\n\n if (this.isMysql) {\n switch (granularity) {\n case 'year': return { sql: `date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%Y')`, bindings: [field] };\n case 'month': return { sql: `date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%Y-%m')`, bindings: [field] };\n case 'day': return { sql: `date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%Y-%m-%d')`, bindings: [field] };\n case 'quarter': return { sql: `concat(date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%Y'), '-Q', quarter(convert_tz(??, @@session.time_zone, '+00:00')))`, bindings: [field, field] };\n case 'week': return { sql: `date_format(convert_tz(??, @@session.time_zone, '+00:00'), '%x-W%v')`, bindings: [field] };\n }\n }\n\n if (this.isSqlite) {\n switch (granularity) {\n case 'year': return { sql: `strftime('%Y', ??)`, bindings: [field] };\n case 'month': return { sql: `strftime('%Y-%m', ??)`, bindings: [field] };\n case 'day': return { sql: `strftime('%Y-%m-%d', ??)`, bindings: [field] };\n case 'quarter': return { sql: `(strftime('%Y', ??) || '-Q' || ((cast(strftime('%m', ??) as integer) - 1) / 3 + 1))`, bindings: [field, field] };\n case 'week': return null; // see capabilities note\n }\n }\n\n return null;\n }\n\n /**\n * Schema ownership mode (ADR-0015). When not `'managed'`, all\n * schema-mutating DDL is rejected by {@link assertSchemaMutable}. The\n * runtime injects this from `Datasource.schemaMode`; defaults to\n * `'managed'` so existing callers are unaffected.\n */\n protected readonly schemaMode: SchemaMode;\n\n /**\n * Dev-only auto-reconcile policy (issue #2186). See {@link SqlDriverConfig.autoMigrate}.\n */\n protected readonly autoMigrate: 'off' | 'safe';\n\n /**\n * Metadata field defs for every table this driver manages, captured during\n * `initObjects` (tableName → fields). The source of truth that\n * {@link detectManagedDrift} diffs the physical schema against.\n */\n protected managedObjectFields = new Map<string, Record<string, any>>();\n\n /** Declared indexes per managed table (tableName → indexes[]), captured in `initObjects`. Used to recreate indexes after a SQLite table rebuild. */\n protected managedObjectIndexes = new Map<string, any[]>();\n\n /** De-dup set for boot-time drift warnings (keyed by {@link driftKey}). */\n protected driftWarned = new Set<string>();\n\n constructor(config: SqlDriverConfig) {\n // `schemaMode` / `autoMigrate` are ObjectStack concerns, not Knex options —\n // strip them before handing the config to Knex.\n const { schemaMode, autoMigrate, ...knexConfig } = config;\n this.schemaMode = schemaMode ?? 'managed';\n this.autoMigrate = autoMigrate ?? 'off';\n this.config = knexConfig;\n this.knex = knex(knexConfig);\n }\n\n /**\n * DDL gate (ADR-0015 §5.1). Single choke-point asserting that\n * schema-mutating DDL is only performed on a `managed` datasource.\n * Federated datasources (`external` / `validate-only`) are guests in a\n * database ObjectStack does not own and must never run DDL against.\n */\n protected assertSchemaMutable(operation: string): void {\n if (this.schemaMode !== 'managed') {\n throw new ExternalSchemaModeViolationError(\n `DDL operation '${operation}' is forbidden: datasource schemaMode='${this.schemaMode}'. ` +\n `ObjectStack never mutates the schema of an external database.`,\n );\n }\n }\n\n // ===================================\n // Lifecycle\n // ===================================\n\n async connect(): Promise<void> {\n // Ensure the database directory exists before any query can trigger\n // better-sqlite3 to open the file (e.g. loadMetaFromDb on startup).\n await this.ensureDatabaseExists();\n\n // SQLite space hygiene (ADR-0057). With the default `auto_vacuum=NONE`,\n // freed pages are never returned to the OS — a database that briefly grew\n // (e.g. high-frequency telemetry before retention sweeps run) stays at its\n // high-water mark forever. INCREMENTAL lets a later `PRAGMA incremental_vacuum`\n // (run by the lifecycle Reaper, or manually) reclaim that space without a\n // full blocking VACUUM. NOTE: auto_vacuum only changes layout on a *fresh*\n // database or after a one-time full VACUUM, so this benefits new dev DBs;\n // existing files need a single `VACUUM` to adopt it. Harmless / no-op on\n // :memory: and on already-incremental databases.\n if (this.isSqlite) {\n try {\n await this.knex.raw('PRAGMA auto_vacuum = INCREMENTAL');\n } catch (e) {\n this.logger.warn('Failed to set PRAGMA auto_vacuum=INCREMENTAL', e);\n }\n }\n }\n\n async checkHealth(): Promise<boolean> {\n try {\n await this.knex.raw('SELECT 1');\n return true;\n } catch {\n return false;\n }\n }\n\n async disconnect(): Promise<void> {\n await this.knex.destroy();\n }\n\n // ===================================\n // CRUD — DriverInterface core\n // ===================================\n\n async find(object: string, query: QueryAST, options?: DriverOptions): Promise<any[]> {\n // Build everything EXCEPT the SELECT list, so the unknown-column retry\n // below can rebuild without re-deriving where/order/pagination.\n const buildBase = () => {\n const b = this.getBuilder(object, options);\n this.applyTenantScope(b, object, options);\n\n // WHERE\n if (query.where) {\n this.applyFilters(b, query.where);\n }\n\n // ORDER BY\n if (query.orderBy && Array.isArray(query.orderBy)) {\n for (const item of query.orderBy) {\n if (item.field) {\n b.orderBy(this.remoteColumn(object, item.field, this.mapSortField(item.field)), item.order || 'asc');\n }\n }\n }\n\n // PAGINATION\n if (query.offset !== undefined) b.offset(query.offset);\n if (query.limit !== undefined) b.limit(query.limit);\n\n return b;\n };\n\n const builder = buildBase();\n\n // SELECT\n if (query.fields) {\n builder.select((query.fields as string[]).map((f: string) => this.mapSortField(f)));\n } else {\n builder.select('*');\n }\n\n let results: any[];\n try {\n results = await builder;\n } catch (error: any) {\n const isUnknownColumn =\n error.message &&\n (error.message.includes('no such column') ||\n (error.message.includes('column') && error.message.includes('does not exist')));\n if (isUnknownColumn) {\n // A `$select` projection naming a column the table lacks (e.g. a\n // generic list view auto-requesting `status`/`due_date`/`image` on an\n // object without them) makes the WHOLE query fail. Swallowing that\n // into an empty result — the old behavior — reads to the UI as \"no\n // records exist\" even though rows are there: a silent data-loss\n // footgun. When the failure came from the projection, retry once\n // selecting all columns so the real rows still come back; the unknown\n // field is simply absent from each row (it never existed). The\n // engine's unknown-field filter is the first line of defense, but it\n // only fires when the object's schema is populated in the registry —\n // this driver backstop holds even when it isn't (notably the cloud\n // multi-tenant runtime, where the projection otherwise zeroes the list).\n if (query.fields) {\n try {\n results = await buildBase().select('*');\n } catch {\n return [];\n }\n } else {\n return [];\n }\n } else {\n throw error;\n }\n }\n\n if (!Array.isArray(results)) {\n return [];\n }\n\n // formatOutput is dialect-agnostic for `Field.date` (ADR-0053 Phase 1);\n // its json/boolean deserialisation stays SQLite-gated internally. Run it\n // for every dialect so reads match `findOne` and date columns come back\n // as `YYYY-MM-DD`.\n for (const row of results) {\n this.formatOutput(object, row);\n }\n return results;\n }\n\n async findOne(object: string, query: QueryAST, options?: DriverOptions): Promise<any> {\n // When called with a string/number id fall back gracefully\n if (typeof query === 'string' || typeof query === 'number') {\n const builder = this.getBuilder(object, options).where('id', query);\n this.applyTenantScope(builder, object, options);\n const res = await builder.first();\n return this.formatOutput(object, res) || null;\n }\n\n if (query && typeof query === 'object') {\n const results = await this.find(object, { ...query, limit: 1 }, options);\n return results[0] || null;\n }\n\n return null;\n }\n\n /**\n * Stream records matching a structured query.\n * NOTE: Current implementation fetches all results then yields them.\n * TODO: Use Knex .stream() for true cursor-based streaming on large datasets.\n */\n async *findStream(object: string, query: QueryAST, options?: DriverOptions): AsyncGenerator<Record<string, any>> {\n const results = await this.find(object, query, options);\n for (const row of results) {\n yield row;\n }\n }\n\n async create(object: string, data: Record<string, any>, options?: DriverOptions): Promise<any> {\n const { _id, ...rest } = data;\n const toInsert = { ...rest };\n\n if (_id !== undefined && toInsert.id === undefined) {\n toInsert.id = _id;\n } else if (toInsert.id === undefined) {\n toInsert.id = nanoid(DEFAULT_ID_LENGTH);\n }\n\n this.auditMissingTenant(object, 'create', options);\n this.injectTenantOnInsert(object, toInsert, options);\n await this.fillAutoNumberFields(object, toInsert, options);\n\n const builder = this.getBuilder(object, options);\n const formatted = this.applyWriteColumnMap(object, this.formatInput(object, toInsert));\n\n const result = await builder.insert(formatted).returning('*');\n return this.formatOutput(object, result[0]);\n }\n\n /**\n * Ensure the sequence-counter table exists. Idempotent and cheap after\n * the first call (cached via `sequencesTableReady`).\n *\n * The row key is `key_hash` — a SHA-256 of `(object, tenant_id, field, scope)`\n * where `scope` is the rendered autonumber prefix (date/field tokens before\n * the `{0000}` slot), so a new day/group/parent starts a fresh counter. A\n * single 64-char hashed primary key (rather than the four raw columns, which\n * blow past MySQL's 3072-byte index limit under utf8mb4 and bound how long a\n * `{field}` scope may be) keys every dialect uniformly and lets `scope` be a\n * generous non-indexed column. Fixed-prefix formats use the empty scope and\n * keep their single global counter (backward compatible).\n */\n protected async ensureSequencesTable(): Promise<void> {\n if (this.sequencesTableReady) return;\n if (this.sequencesTableEnsurePromise) {\n await this.sequencesTableEnsurePromise;\n return;\n }\n this.sequencesTableEnsurePromise = (async () => {\n const exists = await this.knex.schema.hasTable(SEQUENCES_TABLE);\n if (!exists) {\n try {\n await this.createSequencesTable(SEQUENCES_TABLE);\n this.sequencesHasKeyHash = true;\n } catch (err: any) {\n // Race or cross-process create — re-check existence; ignore\n // \"already exists\" errors from any dialect.\n const stillMissing = !(await this.knex.schema.hasTable(SEQUENCES_TABLE));\n if (stillMissing) throw err;\n // A racing creator may have used an older schema. Migrate in place.\n await this.ensureSequencesKeyHashShape();\n }\n } else {\n // Pre-existing table may predate the `key_hash`/`scope` shape. Migrate.\n await this.ensureSequencesKeyHashShape();\n }\n this.sequencesTableReady = true;\n })();\n try {\n await this.sequencesTableEnsurePromise;\n } finally {\n this.sequencesTableEnsurePromise = null;\n }\n }\n\n /** SHA-256 of the composite counter key — the table's single-column PK. */\n protected sequenceKeyHash(object: string, tenantId: string, field: string, scope: string): string {\n return createHash('sha256')\n .update(`${object}\\u001f${tenantId}\\u001f${field}\\u001f${scope}`)\n .digest('hex');\n }\n\n /** Create the current `key_hash`-keyed sequences table shape. */\n protected async createSequencesTable(table: string): Promise<void> {\n await this.knex.schema.createTable(table, (t) => {\n t.string('key_hash', 64).notNullable().primary();\n t.string('object').notNullable();\n t.string('tenant_id').notNullable();\n t.string('field').notNullable();\n // Non-indexed, so it is free of the PK length limit — a long `{plan_no}`\n // composite scope fits. 1024 is far above any realistic rendered prefix.\n t.string('scope', 1024).notNullable().defaultTo('');\n t.bigInteger('last_value').notNullable().defaultTo(0);\n t.timestamp('updated_at').defaultTo(this.knex.fn.now());\n });\n }\n\n /**\n * Migrate a pre-existing `_objectstack_sequences` table to the current\n * `key_hash`-keyed shape. Handles both the original 3-column table (no\n * `scope`) and an interim 4-column `(object, tenant_id, field, scope)` table:\n * every legacy row is read, its `key_hash` computed in app code (no portable\n * SQL hash exists), and re-inserted into a freshly built table that then\n * replaces the original. Idempotent — a no-op once `key_hash` is present.\n *\n * If the rebuild fails, `sequencesHasKeyHash` stays false: fixed-prefix\n * sequences keep working via the legacy key and per-scope writes error\n * actionably (see getNextSequenceValue), rather than corrupting data.\n */\n protected async ensureSequencesKeyHashShape(): Promise<void> {\n if (await this.knex.schema.hasColumn(SEQUENCES_TABLE, 'key_hash')) {\n this.sequencesHasKeyHash = true;\n return;\n }\n const hasScope = await this.knex.schema.hasColumn(SEQUENCES_TABLE, 'scope');\n const TMP = `${SEQUENCES_TABLE}__rebuild`;\n try {\n const rows: any[] = await this.knex(SEQUENCES_TABLE).select('*');\n await this.knex.schema.dropTableIfExists(TMP);\n await this.createSequencesTable(TMP);\n const migrated = rows.map((r) => {\n const scope = hasScope && r.scope != null ? String(r.scope) : '';\n return {\n key_hash: this.sequenceKeyHash(String(r.object), String(r.tenant_id), String(r.field), scope),\n object: r.object,\n tenant_id: r.tenant_id,\n field: r.field,\n scope,\n last_value: r.last_value ?? 0,\n updated_at: r.updated_at ?? this.knex.fn.now(),\n };\n });\n if (migrated.length > 0) await this.knex(TMP).insert(migrated);\n await this.knex.schema.dropTable(SEQUENCES_TABLE);\n await this.knex.schema.renameTable(TMP, SEQUENCES_TABLE);\n this.sequencesHasKeyHash = true;\n } catch (err) {\n // Leave the original table intact; fall back to legacy keying for\n // fixed-prefix sequences and refuse per-scope writes until migrated.\n this.sequencesHasKeyHash = false;\n await this.knex.schema.dropTableIfExists(TMP).catch(() => {});\n this.logger.warn(\n `[autonumber] Failed to migrate ${SEQUENCES_TABLE} to the key_hash shape. ` +\n `Fixed-prefix autonumbers keep working; date/{field}/per-parent formats will ` +\n `error until the table is migrated.`,\n { error: String(err) },\n );\n }\n }\n\n /**\n * Bootstrap helper: scan the data table for the highest numeric suffix\n * matching `prefix` (optionally scoped to a tenant). Used the first time\n * a sequence row is created so legacy/seeded data continues monotonically.\n */\n protected async scanMaxNumericTail(\n queryRunner: Knex | Knex.Transaction,\n tableName: string,\n field: string,\n prefix: string,\n tenantField: string | null,\n tenantId: string | null,\n ): Promise<number> {\n const escapedPrefix = prefix.replace(/([\\\\%_])/g, '\\\\$1');\n let builder = queryRunner(tableName).select(field).where(field, 'like', `${escapedPrefix}%`).whereNotNull(field);\n if (tenantField && tenantId !== null) {\n builder = builder.where(tenantField, tenantId);\n }\n const rows = await builder;\n let maxN = 0;\n for (const r of rows as any[]) {\n const v: string = (r as any)[field];\n if (typeof v !== 'string') continue;\n const tail = v.slice(prefix.length);\n const n = parseInt(tail.replace(/[^0-9]/g, ''), 10);\n if (Number.isFinite(n) && n > maxN) maxN = n;\n }\n return maxN;\n }\n\n /**\n * Atomically reserve and return the next sequence value for\n * `(object, tenantId, field)`. Bootstraps from the data-table MAX on\n * first call so existing seeded records continue monotonically.\n *\n * Concurrency:\n * - SQLite: a write transaction (`BEGIN IMMEDIATE` via knex) serializes\n * all writers; safe in-process. Cross-process SQLite is out of scope.\n * - Postgres/MySQL: `SELECT … FOR UPDATE` row lock ensures only one\n * transaction reads-modifies-writes at a time. A PK-violation race on\n * first insert is retried as an UPDATE.\n *\n * Gaps are tolerated by design — a rolled-back insert \"burns\" a number,\n * matching standard sequence semantics.\n */\n protected async getNextSequenceValue(\n object: string,\n tableName: string,\n field: string,\n prefix: string,\n tenantField: string | null,\n tenantId: string | null,\n parentTrx?: Knex.Transaction,\n scope = '',\n ): Promise<number> {\n await this.ensureSequencesTable();\n const resolvedTenantId = tenantField && tenantId ? String(tenantId) : GLOBAL_TENANT;\n if (scope !== '' && !this.sequencesHasKeyHash) {\n // The legacy sequences table could not be migrated to the key_hash shape,\n // so it cannot represent per-scope counters. Fail with a clear, actionable\n // message instead of corrupting the single legacy counter.\n throw new Error(\n `Cannot generate a per-scope autonumber for \"${object}.${field}\": the ` +\n `${SEQUENCES_TABLE} table is still the legacy shape. ` +\n `Migrate it to the key_hash shape before using date/{field}/per-parent formats.`,\n );\n }\n // `scope` (rendered date/field prefix, boundary-delimited) gives each\n // period/group its own counter; '' keeps the single global counter for\n // fixed-prefix formats. `prefix` is the full rendered prefix used to\n // bootstrap from existing data. The row is keyed by a hash of the composite;\n // on an un-migrated legacy table only fixed-prefix (scope '') reaches here,\n // so fall back to the original `(object, tenant_id, field)` key for it.\n const key = this.sequencesHasKeyHash\n ? { key_hash: this.sequenceKeyHash(tableName, resolvedTenantId, field, scope) }\n : { object: tableName, tenant_id: resolvedTenantId, field };\n const insertRow = this.sequencesHasKeyHash\n ? { ...key, object: tableName, tenant_id: resolvedTenantId, field, scope }\n : { ...key };\n\n const runner: Knex | Knex.Transaction = parentTrx ?? this.knex;\n\n return runner.transaction(async (trx) => {\n // Lock the row (no-op on SQLite, real lock on Postgres/MySQL).\n let existing: any;\n try {\n existing = await trx(SEQUENCES_TABLE).where(key).forUpdate().first();\n } catch {\n // Some dialects/versions reject .forUpdate() on a missing row in\n // weird ways; fall back to plain SELECT then rely on transaction\n // isolation. Postgres/MySQL behave normally here.\n existing = await trx(SEQUENCES_TABLE).where(key).first();\n }\n\n if (!existing) {\n const seedMax = await this.scanMaxNumericTail(\n trx,\n tableName,\n field,\n prefix,\n tenantField,\n resolvedTenantId === GLOBAL_TENANT ? null : resolvedTenantId,\n );\n const initial = seedMax + 1;\n try {\n await trx(SEQUENCES_TABLE).insert({ ...insertRow, last_value: initial });\n return initial;\n } catch (err) {\n // Another writer raced us to the first INSERT. Fall through to\n // the UPDATE path with the now-present row.\n existing = await trx(SEQUENCES_TABLE).where(key).forUpdate().first();\n if (!existing) throw err;\n }\n }\n\n const next = Number(existing.last_value) + 1;\n await trx(SEQUENCES_TABLE).where(key).update({ last_value: next, updated_at: this.knex.fn.now() });\n return next;\n });\n }\n\n /**\n * For each `auto_number` field the caller left empty, render the format and\n * reserve the next counter value. The counter is scoped to the rendered\n * prefix (date tokens like `{YYYYMMDD}` in the request's business timezone,\n * plus `{field}` interpolation from the row), so it resets per period/group;\n * the full rendered prefix bootstraps the counter from existing data, and the\n * tenant scopes it for isolation.\n */\n protected async fillAutoNumberFields(\n object: string,\n row: Record<string, any>,\n options?: DriverOptions,\n ): Promise<void> {\n // Scan/seed the physical (remote) table for an external object; managed\n // objects fall through to the storage-mapped name. Config lookup stays\n // keyed by object name (matching initObjects/registerExternalObject).\n const tableName = this.physicalTableByObject[object] ?? StorageNameMapping.resolveTableName({ name: object } as any);\n const cfgs = this.autoNumberFields[object] || this.autoNumberFields[tableName];\n if (!cfgs || cfgs.length === 0) return;\n const parentTrx = options?.transaction as Knex.Transaction | undefined;\n const timezone = (options as any)?.timezone as string | undefined;\n const now = new Date();\n for (const cfg of cfgs) {\n if (row[cfg.name] !== undefined && row[cfg.name] !== null && row[cfg.name] !== '') continue;\n // A `{field}` token with no value would render to an empty prefix and\n // silently merge this record into the wrong counter scope, so refuse to\n // generate rather than emit a wrong record number (the referenced field\n // must be populated before the autonumber — see field.zod docs).\n const missing = missingFieldValues(cfg.tokens, row);\n if (missing.length > 0) {\n throw new Error(\n `Cannot generate autonumber \"${object}.${cfg.name}\" (format \"${cfg.format}\"): ` +\n `referenced field(s) [${missing.join(', ')}] are empty on the record. ` +\n `Fields interpolated into an autonumber format must be set before the record is created.`,\n );\n }\n // Resolve tenant for this row: explicit field on the record wins,\n // then driver options, else null → global sequence.\n const rowTenant = cfg.tenantField ? row[cfg.tenantField] : undefined;\n const optTenant = (options as any)?.tenantId;\n const tenantId = rowTenant != null && rowTenant !== ''\n ? String(rowTenant)\n : optTenant != null && optTenant !== ''\n ? String(optTenant)\n : null;\n // Resolve the scope/prefix for this row (counter-value-independent),\n // reserve the next value under that scope, then render the final string.\n const probe = renderAutonumber({ tokens: cfg.tokens, seq: 0, record: row, now, timezone });\n const next = await this.getNextSequenceValue(\n object,\n tableName,\n cfg.name,\n probe.prefix,\n cfg.tenantField,\n tenantId,\n parentTrx,\n probe.scope,\n );\n row[cfg.name] = renderAutonumber({ tokens: cfg.tokens, seq: next, record: row, now, timezone }).value;\n }\n }\n\n async update(object: string, id: string | number, data: Record<string, any>, options?: DriverOptions): Promise<any> {\n this.auditMissingTenant(object, 'update', options);\n const builder = this.getBuilder(object, options).where('id', id);\n this.applyTenantScope(builder, object, options);\n const formatted = this.applyWriteColumnMap(object, this.formatInput(object, data));\n\n if (this.tablesWithTimestamps.has(object)) {\n if (this.isSqlite) {\n const now = new Date();\n formatted.updated_at = now.toISOString().replace('T', ' ').replace('Z', '');\n } else {\n formatted.updated_at = this.knex.fn.now();\n }\n }\n\n await builder.update(formatted);\n\n const readback = this.getBuilder(object, options).where('id', id);\n this.applyTenantScope(readback, object, options);\n const updated = await readback.first();\n return this.formatOutput(object, updated) || null;\n }\n\n async upsert(object: string, data: Record<string, any>, conflictKeys?: string[], options?: DriverOptions): Promise<Record<string, any>> {\n const { _id, ...rest } = data;\n const toUpsert = { ...rest };\n\n if (_id !== undefined && toUpsert.id === undefined) {\n toUpsert.id = _id;\n } else if (toUpsert.id === undefined) {\n toUpsert.id = nanoid(DEFAULT_ID_LENGTH);\n }\n\n this.auditMissingTenant(object, 'upsert', options);\n this.injectTenantOnInsert(object, toUpsert, options);\n await this.fillAutoNumberFields(object, toUpsert, options);\n\n const formatted = this.applyWriteColumnMap(object, this.formatInput(object, toUpsert));\n const mergeKeys = conflictKeys && conflictKeys.length > 0 ? conflictKeys : ['id'];\n\n const builder = this.getBuilder(object, options);\n await builder.insert(formatted).onConflict(mergeKeys).merge();\n\n const readback = this.getBuilder(object, options).where('id', toUpsert.id);\n this.applyTenantScope(readback, object, options);\n const result = await readback.first();\n return this.formatOutput(object, result) || toUpsert;\n }\n\n async delete(object: string, id: string | number, options?: DriverOptions): Promise<boolean> {\n this.auditMissingTenant(object, 'delete', options);\n const builder = this.getBuilder(object, options).where('id', id);\n this.applyTenantScope(builder, object, options);\n const count = await builder.delete();\n return count > 0;\n }\n\n // ===================================\n // Bulk & Batch Operations\n // ===================================\n\n async bulkCreate(object: string, data: any[], options?: DriverOptions): Promise<any> {\n this.auditMissingTenant(object, 'bulkCreate', options);\n for (const row of data) {\n if (row && typeof row === 'object') {\n this.injectTenantOnInsert(object, row, options);\n // Reserve a persistent sequence value for each row's autonumber\n // field(s) — the engine no longer pre-fills these (see #1603).\n await this.fillAutoNumberFields(object, row, options);\n }\n }\n const builder = this.getBuilder(object, options);\n return await builder.insert(data).returning('*');\n }\n\n /**\n * Batch-update multiple records by ID.\n * NOTE: Current implementation performs sequential updates for correctness.\n * TODO: Optimize with SQL CASE statements or batched transactions for performance.\n */\n async bulkUpdate(object: string, updates: Array<{ id: string | number; data: Record<string, any> }>, options?: DriverOptions): Promise<Record<string, any>[]> {\n const results: Record<string, any>[] = [];\n for (const { id, data } of updates) {\n const updated = await this.update(object, id, data, options);\n if (updated) results.push(updated);\n }\n return results;\n }\n\n async bulkDelete(object: string, ids: Array<string | number>, options?: DriverOptions): Promise<void> {\n this.auditMissingTenant(object, 'bulkDelete', options);\n const builder = this.getBuilder(object, options).whereIn('id', ids);\n this.applyTenantScope(builder, object, options);\n await builder.delete();\n }\n\n async updateMany(object: string, query: QueryAST, data: any, options?: DriverOptions): Promise<number> {\n this.auditMissingTenant(object, 'updateMany', options);\n const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\n if (query.where) this.applyFilters(builder, query.where);\n const count = await builder.update(data);\n return count || 0;\n }\n\n async deleteMany(object: string, query: QueryAST, options?: DriverOptions): Promise<number> {\n this.auditMissingTenant(object, 'deleteMany', options);\n const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\n if (query.where) this.applyFilters(builder, query.where);\n const count = await builder.delete();\n return count || 0;\n }\n\n async count(object: string, query?: QueryAST, options?: DriverOptions): Promise<number> {\n const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\n\n if (query?.where) {\n this.applyFilters(builder, query.where);\n }\n\n const result = await builder.count<{ count: number }[]>('* as count');\n if (result && result.length > 0) {\n const row: any = result[0];\n return Number(row.count ?? row['count(*)'] ?? 0);\n }\n return 0;\n }\n\n // ===================================\n // Raw Execution\n // ===================================\n\n /**\n * Run a raw SQL string or knex builder through the underlying knex\n * connection.\n *\n * ⚠️ **Tenant isolation bypass.** Unlike `find`/`update`/`delete` etc.,\n * raw `execute()` does NOT inject the `organization_id` predicate. The\n * caller is responsible for either:\n * - inlining the tenant filter into the SQL (`WHERE organization_id = ?`),\n * - or restricting `execute()` to genuinely global queries\n * (schema introspection, sys_* tables that opt out of tenancy).\n *\n * Prefer the typed CRUD APIs whenever the operation can be expressed\n * through them — they handle tenancy, soft-delete, and audit warnings\n * automatically. See `README.md > Tenant Isolation` for the full bypass\n * matrix.\n */\n async execute(command: any, params?: any[], options?: DriverOptions): Promise<any> {\n if (typeof command !== 'string') {\n return command;\n }\n\n const builder =\n options?.transaction\n ? this.knex.raw(command, params || []).transacting(options.transaction as Knex.Transaction)\n : this.knex.raw(command, params || []);\n\n return await builder;\n }\n\n // ===================================\n // Transactions\n // ===================================\n\n async beginTransaction(): Promise<Knex.Transaction> {\n return await this.knex.transaction();\n }\n\n /** IDataDriver standard */\n async commit(transaction: unknown): Promise<void> {\n await (transaction as Knex.Transaction).commit();\n }\n\n /** IDataDriver standard */\n async rollback(transaction: unknown): Promise<void> {\n await (transaction as Knex.Transaction).rollback();\n }\n\n /** @deprecated Use commit() instead */\n async commitTransaction(trx: Knex.Transaction): Promise<void> {\n await this.commit(trx);\n }\n\n /** @deprecated Use rollback() instead */\n async rollbackTransaction(trx: Knex.Transaction): Promise<void> {\n await this.rollback(trx);\n }\n\n // ===================================\n // Aggregation\n // ===================================\n\n async aggregate(object: string, query: any, options?: DriverOptions): Promise<any> {\n const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\n\n if (query.where) {\n this.applyFilters(builder, query.where);\n }\n\n if (query.groupBy) {\n // groupBy items may be plain strings ('region') or structured objects\n // ({ field: 'closed_at', dateGranularity: 'quarter' }). For structured\n // items we emit a dialect-specific bucket expression aliased as the\n // field name so the resulting row keys match in-memory bucketDateValue.\n for (const g of query.groupBy as Array<string | { field: string; dateGranularity?: string }>) {\n if (typeof g === 'string') {\n builder.groupBy(g);\n builder.select(g);\n } else if (g && typeof g === 'object' && g.field) {\n if (g.dateGranularity) {\n const bucket = this.buildDateBucketExpr(g.field, g.dateGranularity as any);\n if (!bucket) {\n throw new Error(\n `SqlDriver: dateGranularity '${g.dateGranularity}' not supported on dialect ` +\n `'${(this.config as any).client}'. Engine must fall back to in-memory bucketing.`,\n );\n }\n builder.groupByRaw(bucket.sql, bucket.bindings);\n builder.select(this.knex.raw(`${bucket.sql} as ??`, [...bucket.bindings, g.field]));\n } else {\n builder.groupBy(g.field);\n builder.select(g.field);\n }\n }\n }\n }\n\n const aggregates = query.aggregations || query.aggregate;\n if (aggregates) {\n for (const agg of aggregates) {\n const funcName = agg.function || agg.func;\n const rawFunc = this.mapAggregateFunc(funcName);\n // Spec: `field` is optional for COUNT (means COUNT(*)).\n const fieldExpr = agg.field ?? '*';\n if (agg.alias) {\n if (fieldExpr === '*') {\n builder.select(this.knex.raw(`${rawFunc}(*) as ??`, [agg.alias]));\n } else {\n builder.select(this.knex.raw(`${rawFunc}(??) as ??`, [fieldExpr, agg.alias]));\n }\n } else {\n if (fieldExpr === '*') {\n builder.select(this.knex.raw(`${rawFunc}(*)`));\n } else {\n builder.select(this.knex.raw(`${rawFunc}(??)`, [fieldExpr]));\n }\n }\n }\n }\n\n return await builder;\n }\n\n // ===================================\n // Distinct\n // ===================================\n\n async distinct(object: string, field: string, filters?: any, options?: DriverOptions): Promise<any[]> {\n const builder = this.getBuilder(object, options);\n\n if (filters) {\n this.applyFilters(builder, filters);\n }\n\n builder.distinct(field);\n const results = await builder;\n return results.map((row: any) => row[field]);\n }\n\n // ===================================\n // Window Functions\n // ===================================\n\n async findWithWindowFunctions(object: string, query: any, options?: DriverOptions): Promise<any[]> {\n const builder = this.getBuilder(object, options);\n\n builder.select('*');\n\n if (query.where) {\n this.applyFilters(builder, query.where);\n }\n\n if (query.windowFunctions && Array.isArray(query.windowFunctions)) {\n for (const wf of query.windowFunctions) {\n const windowFunc = this.buildWindowFunction(wf);\n builder.select(this.knex.raw(`${windowFunc} as ??`, [wf.alias]));\n }\n }\n\n if (query.orderBy && Array.isArray(query.orderBy)) {\n for (const sort of query.orderBy) {\n builder.orderBy(this.mapSortField(sort.field), sort.order || 'asc');\n }\n }\n\n if (query.limit) builder.limit(query.limit);\n if (query.offset) builder.offset(query.offset);\n\n return await builder;\n }\n\n // ===================================\n // Query Plan Analysis\n // ===================================\n\n /** IDataDriver standard: analyze query performance */\n async explain(object: string, query: any, options?: DriverOptions): Promise<any> {\n return this.analyzeQuery(object, query, options);\n }\n\n async analyzeQuery(object: string, query: any, options?: DriverOptions): Promise<any> {\n const builder = this.getBuilder(object, options);\n\n if (query.fields) {\n builder.select(query.fields);\n } else {\n builder.select('*');\n }\n\n if (query.where) {\n this.applyFilters(builder, query.where);\n }\n\n if (query.orderBy && Array.isArray(query.orderBy)) {\n for (const sort of query.orderBy) {\n builder.orderBy(this.mapSortField(sort.field), sort.order || 'asc');\n }\n }\n\n if (query.limit) builder.limit(query.limit);\n if (query.offset) builder.offset(query.offset);\n\n const sql = builder.toSQL();\n const client = (this.config as any).client;\n let explainResults: any;\n\n try {\n if (this.isPostgres) {\n explainResults = await this.knex.raw(`EXPLAIN (FORMAT JSON, ANALYZE) ${sql.sql}`, sql.bindings);\n } else if (this.isMysql) {\n explainResults = await this.knex.raw(`EXPLAIN FORMAT=JSON ${sql.sql}`, sql.bindings);\n } else if (this.isSqlite) {\n explainResults = await this.knex.raw(`EXPLAIN QUERY PLAN ${sql.sql}`, sql.bindings);\n } else {\n return {\n sql: sql.sql,\n bindings: sql.bindings,\n client,\n note: 'EXPLAIN not supported for this database client',\n };\n }\n\n return { sql: sql.sql, bindings: sql.bindings, client, plan: explainResults };\n } catch (error: any) {\n return {\n sql: sql.sql,\n bindings: sql.bindings,\n client,\n error: error.message,\n note: 'Failed to execute EXPLAIN.',\n };\n }\n }\n\n // ===================================\n // Schema Sync (syncSchema / init)\n // ===================================\n\n async syncSchema(object: string, schema: unknown, _options?: DriverOptions): Promise<void> {\n const objectDef = schema as { name: string; fields?: Record<string, any> };\n // The caller passes the resolved physical table name as `object`. Override\n // the def's `name` to ensure DDL targets the physical table even if the\n // schema's `name` is the canonical object name (e.g. 'account').\n await this.initObjects([{ ...objectDef, name: object }]);\n }\n\n async dropTable(object: string, _options?: DriverOptions): Promise<void> {\n this.assertSchemaMutable('dropTable');\n await this.knex.schema.dropTableIfExists(object);\n }\n\n /**\n * Batch-initialise tables from an array of object definitions.\n */\n /**\n * DDL-free metadata registration for a federated (external) object — the\n * read-path counterpart to {@link initObjects} (ADR-0015 federation).\n *\n * `initObjects` is gated by `assertSchemaMutable` and therefore throws for\n * any non-`managed` driver, which left external objects with NO read-coercion\n * metadata and the query path resolving to a table named after the object\n * instead of its remote table. This populates the same coercion maps (keyed\n * by OBJECT name, matching formatInput/formatOutput/coerceFilterValue) and\n * records the physical remote table (`external.remoteName`, optionally\n * `external.remoteSchema`) so {@link getBuilder} targets it — WITHOUT running\n * any DDL (createTable/alterTable/columnInfo). Keep the field-classification\n * below in sync with initObjects() if the field-type -> storage mapping changes.\n */\n registerExternalObject(schema: {\n name: string;\n fields?: Record<string, any>;\n tenancy?: any;\n external?: { remoteName?: string; remoteSchema?: string; columnMap?: Record<string, string> };\n }): void {\n const key = schema.name;\n const remoteName = schema.external?.remoteName || schema.name;\n const remoteSchema = schema.external?.remoteSchema;\n this.physicalTableByObject[key] = remoteName;\n this.objectByPhysicalTable[remoteName] = key;\n if (remoteSchema) {\n if (this.isSqlite) {\n this.logger.warn(\n `[sql-driver] external object \"${key}\" declares remoteSchema=\"${remoteSchema}\" but SQLite has no schema namespace; ignoring (treating \"${remoteName}\" as a bare table).`,\n );\n } else {\n this.physicalSchemaByObject[key] = remoteSchema;\n }\n }\n\n // External columnMap (ADR-0015) is declared as { remoteColumn -> localField }.\n // Keep it for read-output remap, and invert to { localField -> remoteColumn }\n // for WHERE/ORDER BY/write translation. Absent => managed-identical behavior.\n const columnMap = schema.external?.columnMap;\n if (columnMap && typeof columnMap === 'object' && Object.keys(columnMap).length > 0) {\n const fieldToCol: Record<string, string> = {};\n const colToField: Record<string, string> = {};\n for (const [remoteCol, localField] of Object.entries(columnMap)) {\n if (typeof localField === 'string' && localField) {\n fieldToCol[localField] = remoteCol;\n colToField[remoteCol] = localField;\n }\n }\n this.fieldColumnByObject[key] = fieldToCol;\n this.columnFieldByObject[key] = colToField;\n }\n\n const jsonCols: string[] = [];\n const booleanCols: string[] = [];\n const numericCols: string[] = [];\n const dateCols: string[] = [];\n const datetimeCols: string[] = [];\n const autoNumberCols: Array<{ name: string; format: string; tokens: AutonumberToken[]; tenantField: string | null }> = [];\n\n const tenancyDecl = (schema as any)?.tenancy;\n let tenantField: string | null = null;\n if (tenancyDecl && tenancyDecl.enabled !== false && tenancyDecl.tenantField) {\n const declared = String(tenancyDecl.tenantField);\n if (schema.fields && Object.prototype.hasOwnProperty.call(schema.fields, declared)) {\n tenantField = declared;\n }\n }\n if (!tenantField) {\n const hasOrgField = !!(schema.fields && Object.prototype.hasOwnProperty.call(schema.fields, 'organization_id'));\n tenantField = hasOrgField ? 'organization_id' : null;\n }\n if (schema.fields) {\n for (const [name, field] of Object.entries<any>(schema.fields)) {\n const type = field.type || 'string';\n if (this.isJsonField(type, field)) jsonCols.push(name);\n if (type === 'boolean' || type === 'toggle') booleanCols.push(name);\n if (NUMERIC_SCALAR_TYPES.has(type) && !field.multiple) numericCols.push(name);\n if (type === 'date') dateCols.push(name);\n if (type === 'datetime') datetimeCols.push(name);\n if (type === 'auto_number' || type === 'autonumber') {\n const rawFmt = (typeof field.autonumberFormat === 'string' && field.autonumberFormat)\n ? field.autonumberFormat\n : (typeof field.format === 'string' && field.format ? field.format : '');\n const fmt = rawFmt || '{0000}';\n autoNumberCols.push({ name, format: fmt, tokens: parseAutonumberFormat(fmt), tenantField });\n }\n }\n }\n this.jsonFields[key] = jsonCols;\n this.booleanFields[key] = booleanCols;\n this.numericFields[key] = numericCols;\n this.autoNumberFields[key] = autoNumberCols;\n this.tenantFieldByTable[key] = tenantField;\n if (dateCols.length) this.dateFields[key] = new Set(dateCols);\n if (datetimeCols.length) this.datetimeFields[key] = new Set(datetimeCols);\n }\n\n async initObjects(objects: Array<{ name: string; fields?: Record<string, any> }>): Promise<void> {\n // DDL gate (ADR-0015 §5.1): createTable/alterTable below mutate schema.\n // Also covers `syncSchema`, which delegates here.\n this.assertSchemaMutable('initObjects');\n await this.ensureDatabaseExists();\n\n for (const obj of objects) {\n const tableName = StorageNameMapping.resolveTableName(obj);\n // #2186: remember the authoritative metadata field set for this table so\n // drift detection / `os migrate` can diff the physical schema against it.\n this.managedObjectFields.set(tableName, obj.fields ?? {});\n if (Array.isArray((obj as any).indexes)) {\n this.managedObjectIndexes.set(tableName, (obj as any).indexes);\n }\n\n const jsonCols: string[] = [];\n const booleanCols: string[] = [];\n const numericCols: string[] = [];\n const autoNumberCols: Array<{ name: string; format: string; tokens: AutonumberToken[]; tenantField: string | null }> = [];\n // Auto-detect tenant field. Convention: the field named\n // `organization_id` (matching tenantPolicy default) scopes the\n // Resolve tenant scope declaratively first (obj.tenancy.{enabled,\n // tenantField}) — that's the user's explicit intent. Fall back to the\n // implicit \"has an organization_id field\" detection so legacy objects\n // (whose multi-tenant column was injected by the kernel implicitly)\n // keep working without a spec migration.\n const tenancyDecl = (obj as any)?.tenancy;\n let tenantField: string | null = null;\n if (tenancyDecl && tenancyDecl.enabled !== false && tenancyDecl.tenantField) {\n const declared = String(tenancyDecl.tenantField);\n if (obj.fields && Object.prototype.hasOwnProperty.call(obj.fields, declared)) {\n tenantField = declared;\n }\n }\n if (!tenantField) {\n const hasOrgField = !!(obj.fields && Object.prototype.hasOwnProperty.call(obj.fields, 'organization_id'));\n tenantField = hasOrgField ? 'organization_id' : null;\n }\n if (obj.fields) {\n for (const [name, field] of Object.entries<any>(obj.fields)) {\n const type = field.type || 'string';\n if (this.isJsonField(type, field)) {\n jsonCols.push(name);\n }\n // `toggle` shares boolean storage/affinity, so it needs the same\n // read coercion (stored 1/0 → JS true/false) or it leaks back as a\n // number/string instead of a boolean (#field-zoo).\n if (type === 'boolean' || type === 'toggle') {\n booleanCols.push(name);\n }\n // Numeric scalars are coerced back to JS numbers on read so legacy\n // TEXT-affinity columns (created before they were mapped to a numeric\n // column) still return numbers, not strings — see NUMERIC_SCALAR_TYPES.\n if (NUMERIC_SCALAR_TYPES.has(type) && !field.multiple) {\n numericCols.push(name);\n }\n if (type === 'date') {\n (this.dateFields[tableName] ??= new Set()).add(name);\n }\n if (type === 'datetime') {\n (this.datetimeFields[tableName] ??= new Set()).add(name);\n }\n if (type === 'auto_number' || type === 'autonumber') {\n // Honor either the spec-canonical `autonumberFormat` or the\n // shorthand `format` (both appear in metadata) — see #1603.\n const rawFmt = (typeof field.autonumberFormat === 'string' && field.autonumberFormat)\n ? field.autonumberFormat\n : (typeof field.format === 'string' && field.format ? field.format : '');\n const fmt = rawFmt || '{0000}';\n // Tokenize once: the renderer resolves date tokens (`{YYYYMMDD}`),\n // field interpolation (`{island_zone}`) and the sequence slot at\n // fill time. The counter scopes to whatever renders before the slot.\n const tokens = parseAutonumberFormat(fmt);\n autoNumberCols.push({ name, format: fmt, tokens, tenantField });\n }\n }\n }\n this.jsonFields[tableName] = jsonCols;\n this.booleanFields[tableName] = booleanCols;\n this.numericFields[tableName] = numericCols;\n this.autoNumberFields[tableName] = autoNumberCols;\n this.tenantFieldByTable[tableName] = tenantField;\n\n let exists = await this.knex.schema.hasTable(tableName);\n\n if (exists) {\n const columnInfo = await this.knex(tableName).columnInfo();\n const existingColumns = Object.keys(columnInfo);\n\n if (existingColumns.includes('_id') && !existingColumns.includes('id')) {\n await this.knex.schema.dropTable(tableName);\n exists = false;\n }\n }\n\n // Columns created unconditionally by initObjects — skip them when\n // iterating obj.fields to avoid duplicate-column errors (e.g. SQLite\n // rejects CREATE TABLE with two columns of the same name).\n const builtinColumns = new Set(['id', 'created_at', 'updated_at']);\n\n if (!exists) {\n await this.knex.schema.createTable(tableName, (table) => {\n table.string('id').primary();\n table.timestamp('created_at').defaultTo(this.knex.fn.now());\n table.timestamp('updated_at').defaultTo(this.knex.fn.now());\n if (obj.fields) {\n for (const [name, field] of Object.entries(obj.fields)) {\n if (builtinColumns.has(name)) continue;\n this.createColumn(table, name, field);\n }\n }\n });\n this.tablesWithTimestamps.add(tableName);\n } else {\n const columnInfo = await this.knex(tableName).columnInfo();\n const existingColumns = Object.keys(columnInfo);\n\n if (existingColumns.includes('updated_at')) {\n this.tablesWithTimestamps.add(tableName);\n }\n\n await this.knex.schema.alterTable(tableName, (table) => {\n if (obj.fields) {\n for (const [name, field] of Object.entries(obj.fields)) {\n if (!existingColumns.includes(name)) {\n this.createColumn(table, name, field);\n }\n }\n }\n });\n }\n\n // Materialize object-level declared indexes (`indexes: [{ fields,\n // unique }]`). These are distinct from field-level `unique` (handled\n // in `createColumn`) and carry the multi-column UNIQUE guarantees that\n // dedup/convergence paths rely on (ADR-0030). Done after the table is\n // created/altered so every referenced column physically exists.\n const declaredIndexes = (obj as any).indexes;\n if (Array.isArray(declaredIndexes) && declaredIndexes.length > 0) {\n const colInfo = await this.knex(tableName).columnInfo();\n const physicalColumns = new Set(Object.keys(colInfo));\n await this.syncDeclaredIndexes(tableName, declaredIndexes, physicalColumns);\n }\n\n // #2186: the additive sync above only ever ADDs tables/columns. For a\n // table that already existed, detect (and in dev, auto-reconcile) any\n // non-additive divergence (relaxed NOT NULL, widened varchar, orphaned\n // column) between metadata and the physical schema.\n if (exists) {\n await this.reconcileAndWarnDrift(tableName, obj.fields ?? {});\n }\n }\n }\n\n // ── Managed-schema drift & reconcile (#2186) ───────────────────────────────\n\n /** Canonical dialect name for the drift differ. */\n protected get dialectName(): SqlDialectName {\n if (this.isSqlite) return 'sqlite';\n if (this.isPostgres) return 'postgres';\n if (this.isMysql) return 'mysql';\n return 'unknown';\n }\n\n /** True only when running under `NODE_ENV=production` — auto-DDL is force-disabled there. */\n protected isProductionEnv(): boolean {\n try {\n return (process.env.NODE_ENV ?? '').toLowerCase() === 'production';\n } catch {\n return false;\n }\n }\n\n /** Diff one table's metadata fields against its physical columns. */\n protected async detectTableDrift(\n tableName: string,\n fields: Record<string, any>,\n ): Promise<ManagedDriftEntry[]> {\n const cols = await this.introspectColumns(tableName);\n const physical: PhysicalColumn[] = cols.map((c) => ({\n name: c.name,\n type: c.type,\n nullable: c.nullable,\n maxLength: c.maxLength,\n }));\n return diffManagedTable({ table: tableName, fields, columns: physical, dialect: this.dialectName });\n }\n\n /**\n * Detect every managed-schema divergence between metadata and the physical\n * database. Metadata is the source of truth. Returns one entry per drift,\n * sorted by table then column. Used by `os migrate` (P3) and tests.\n *\n * @param objects optional explicit object list; defaults to whatever\n * `initObjects` last synced (captured in {@link managedObjectFields}).\n */\n async detectManagedDrift(\n objects?: Array<{ name: string; fields?: Record<string, any> }>,\n ): Promise<ManagedDriftEntry[]> {\n const tables = new Map<string, Record<string, any>>();\n if (objects) {\n for (const o of objects) tables.set(StorageNameMapping.resolveTableName(o), o.fields ?? {});\n } else {\n for (const [t, f] of this.managedObjectFields) tables.set(t, f);\n }\n\n const out: ManagedDriftEntry[] = [];\n for (const [tableName, fields] of tables) {\n if (!(await this.knex.schema.hasTable(tableName))) continue;\n out.push(...(await this.detectTableDrift(tableName, fields)));\n }\n out.sort((a, b) => (a.table === b.table ? (a.column ?? '').localeCompare(b.column ?? '') : a.table.localeCompare(b.table)));\n return out;\n }\n\n /**\n * Boot-time per-table drift handling (P1 + P2): detect divergence, in dev\n * auto-reconcile the *safe* (loosening) subset when `autoMigrate==='safe'`,\n * then WARN once per remaining divergence with an actionable hint.\n */\n protected async reconcileAndWarnDrift(tableName: string, fields: Record<string, any>): Promise<void> {\n let drift: ManagedDriftEntry[];\n try {\n drift = await this.detectTableDrift(tableName, fields);\n } catch (e: any) {\n this.logger.warn(`[schema-drift] could not introspect '${tableName}' for drift detection`, e?.message ?? e);\n return;\n }\n if (drift.length === 0) return;\n\n const autoOn = this.autoMigrate === 'safe' && this.schemaMode === 'managed';\n if (autoOn && this.isProductionEnv()) {\n this.logger.warn(\n `[schema-drift] autoMigrate='safe' is ignored under NODE_ENV=production — schema is never auto-altered in production. Run 'os migrate' deliberately.`,\n );\n } else if (autoOn) {\n const safe = drift.filter((d) => d.category === 'safe');\n if (safe.length > 0) {\n try {\n const { applied } = await this.applyMigrationEntries(safe, { allowDestructive: false });\n for (const d of applied) {\n (this.logger.info ?? this.logger.warn)(`[schema-drift] auto-reconciled ${d.op.type} on ${d.table}.${d.column}`);\n }\n // Re-detect so the warnings below reflect the post-reconcile state.\n drift = await this.detectTableDrift(tableName, fields);\n } catch (e: any) {\n this.logger.warn(`[schema-drift] dev auto-reconcile failed for '${tableName}' — falling back to warning`, e?.message ?? e);\n }\n }\n }\n\n for (const d of drift) {\n const k = driftKey(d);\n if (this.driftWarned.has(k)) continue;\n this.driftWarned.add(k);\n this.logger.warn(`[schema-drift] ${d.message}`);\n }\n }\n\n /**\n * Apply a set of drift entries to the physical schema. Destructive entries\n * are skipped unless `allowDestructive` is set. Postgres/MySQL alter columns\n * in place; SQLite (which cannot alter constraints in place) rebuilds each\n * affected table (copy → swap) applying only the requested edits.\n *\n * @returns the entries actually applied and those skipped (e.g. destructive\n * without `allowDestructive`, or unsupported on the dialect).\n */\n async applyMigrationEntries(\n entries: ManagedDriftEntry[],\n opts: { allowDestructive?: boolean } = {},\n ): Promise<{ applied: ManagedDriftEntry[]; skipped: ManagedDriftEntry[] }> {\n this.assertSchemaMutable('reconcileManagedSchema');\n const allowDestructive = opts.allowDestructive === true;\n\n const applied: ManagedDriftEntry[] = [];\n const skipped: ManagedDriftEntry[] = [];\n\n const candidates = entries.filter((d) => {\n if (d.category === 'destructive' && !allowDestructive) {\n skipped.push(d);\n return false;\n }\n return true;\n });\n if (candidates.length === 0) return { applied, skipped };\n\n // Group by table — SQLite reconciles a whole table in one rebuild.\n const byTable = new Map<string, ManagedDriftEntry[]>();\n for (const d of candidates) {\n (byTable.get(d.table) ?? byTable.set(d.table, []).get(d.table)!).push(d);\n }\n\n for (const [table, ents] of byTable) {\n try {\n if (this.isSqlite) {\n await this.rebuildSqliteTablePatched(table, ents);\n applied.push(...ents);\n } else {\n for (const d of ents) {\n const ok = await this.applyDriftOpInPlace(d.op);\n (ok ? applied : skipped).push(d);\n }\n }\n } catch (e: any) {\n this.logger.warn(`[schema-drift] failed to reconcile '${table}'`, e?.message ?? e);\n for (const d of ents) if (!applied.includes(d)) skipped.push(d);\n }\n }\n return { applied, skipped };\n }\n\n /** Apply a single drift op in place (Postgres / MySQL). Returns false if unsupported. */\n protected async applyDriftOpInPlace(op: DriftOp): Promise<boolean> {\n const { table, column } = op;\n if (this.isPostgres) {\n switch (op.type) {\n case 'relax_not_null':\n await this.knex.raw('ALTER TABLE ?? ALTER COLUMN ?? DROP NOT NULL', [table, column]);\n return true;\n case 'tighten_not_null':\n await this.knex.raw('ALTER TABLE ?? ALTER COLUMN ?? SET NOT NULL', [table, column]);\n return true;\n case 'widen_varchar':\n case 'narrow_varchar':\n await this.knex.raw(`ALTER TABLE ?? ALTER COLUMN ?? TYPE varchar(${op.to})`, [table, column]);\n return true;\n case 'drop_column':\n await this.knex.raw('ALTER TABLE ?? DROP COLUMN ??', [table, column]);\n return true;\n }\n }\n if (this.isMysql) {\n // MySQL MODIFY restates the FULL column definition — reconstruct the\n // type (with length for char types, so a nullability change never\n // silently drops a varchar's declared length) from columnInfo.\n const info: any = await this.knex(table).columnInfo();\n const ci: any = info?.[column];\n const colType: string | undefined = ci?.type\n ? (/char/i.test(ci.type) && ci.maxLength ? `${ci.type}(${ci.maxLength})` : ci.type)\n : undefined;\n switch (op.type) {\n case 'relax_not_null':\n if (!colType) return false;\n await this.knex.raw(`ALTER TABLE ?? MODIFY ?? ${colType} NULL`, [table, column]);\n return true;\n case 'tighten_not_null':\n if (!colType) return false;\n await this.knex.raw(`ALTER TABLE ?? MODIFY ?? ${colType} NOT NULL`, [table, column]);\n return true;\n case 'widen_varchar':\n case 'narrow_varchar':\n await this.knex.raw(`ALTER TABLE ?? MODIFY ?? varchar(${op.to})`, [table, column]);\n return true;\n case 'drop_column':\n await this.knex.raw('ALTER TABLE ?? DROP COLUMN ??', [table, column]);\n return true;\n }\n }\n this.logger.warn(`[schema-drift] ${op.type} on ${table}.${column} is unsupported on dialect '${this.dialectName}' — skipped`);\n return false;\n }\n\n /**\n * Rebuild a SQLite table applying a set of column edits (relax/tighten NOT\n * NULL, drop column), preserving all other columns and their data. Follows\n * the official SQLite procedure: create patched table → copy → drop → rename.\n * varchar widen/narrow are no-ops on SQLite (dynamic typing) and ignored.\n *\n * Unique field-level constraints and declared indexes are recreated from\n * metadata afterwards (the source of truth). DB-level foreign keys declared\n * by `lookup` fields are not re-added (ObjectStack enforces relationships at\n * the application layer, not via SQLite FK constraints).\n */\n protected async rebuildSqliteTablePatched(table: string, ents: ManagedDriftEntry[]): Promise<void> {\n const relax = new Set<string>();\n const tighten = new Set<string>();\n const drop = new Set<string>();\n for (const e of ents) {\n if (e.op.type === 'relax_not_null') relax.add(e.op.column);\n else if (e.op.type === 'tighten_not_null') tighten.add(e.op.column);\n else if (e.op.type === 'drop_column') drop.add(e.op.column);\n // widen/narrow varchar: SQLite ignores declared length — nothing to do.\n }\n\n const physical = await this.introspectColumns(table);\n const kept = physical.filter((c) => !drop.has(c.name));\n const keptNames = kept.map((c) => c.name);\n const fields = this.managedObjectFields.get(table) ?? {};\n const tmp = `__os_mig_${table}`;\n\n // FK enforcement must be toggled OUTSIDE the transaction (SQLite ignores\n // the PRAGMA inside one). Off during the swap so the rename doesn't trip\n // any dangling references mid-flight.\n await this.knex.raw('PRAGMA foreign_keys = OFF');\n try {\n await this.knex.transaction(async (trx) => {\n await trx.schema.dropTableIfExists(tmp);\n await trx.schema.createTable(tmp, (t) => {\n for (const c of kept) {\n const col = this.buildRebuiltColumn(t, c);\n if (!col) continue;\n const nullable = relax.has(c.name) ? true : tighten.has(c.name) ? false : c.nullable;\n if (!nullable && c.name !== 'id') col.notNullable();\n if (c.name === 'created_at' || c.name === 'updated_at') col.defaultTo(this.knex.fn.now());\n }\n });\n const colList = keptNames.map((n) => `\"${n}\"`).join(', ');\n await trx.raw(`INSERT INTO \"${tmp}\" (${colList}) SELECT ${colList} FROM \"${table}\"`);\n await trx.schema.dropTable(table);\n await trx.schema.renameTable(tmp, table);\n });\n } finally {\n await this.knex.raw('PRAGMA foreign_keys = ON');\n }\n\n // Recreate unique constraints + declared indexes from metadata.\n try {\n const keptSet = new Set(keptNames);\n for (const [name, field] of Object.entries<any>(fields)) {\n if (field?.unique && keptSet.has(name)) {\n const idx = `uniq_${table}_${name}`;\n await this.knex.raw('CREATE UNIQUE INDEX IF NOT EXISTS ?? ON ?? (??)', [idx, table, name]);\n }\n }\n const declared = this.managedObjectIndexes.get(table);\n if (Array.isArray(declared) && declared.length > 0) {\n await this.syncDeclaredIndexes(table, declared, keptSet);\n }\n } catch (e: any) {\n this.logger.warn(`[schema-drift] could not fully recreate indexes for '${table}' after rebuild`, e?.message ?? e);\n }\n }\n\n /** Map an introspected SQLite column to a knex builder for the rebuilt table. */\n protected buildRebuiltColumn(t: Knex.CreateTableBuilder, c: IntrospectedColumn): any {\n if (c.name === 'id') return t.string('id').primary();\n const ty = (c.type || 'text').toLowerCase();\n if (ty.includes('int')) return t.integer(c.name);\n if (/(real|floa|doub|num|dec)/.test(ty)) return t.float(c.name);\n if (ty.includes('bool')) return t.boolean(c.name);\n if (ty.includes('datetime') || ty.includes('timestamp')) return t.timestamp(c.name);\n if (ty === 'date') return t.date(c.name);\n if (ty === 'time') return t.time(c.name);\n if (ty.includes('json')) return t.json(c.name);\n if (ty.includes('blob') || ty.includes('binary')) return t.binary(c.name);\n if (ty.includes('text')) return t.text(c.name);\n return t.string(c.name);\n }\n\n /**\n * Build a deterministic index name for a declared index so repeated\n * `initObjects` runs converge on the same identifier (and can detect an\n * already-materialized index by name). Long names are hash-suffixed to\n * stay within the 63/64-char identifier limits of Postgres/MySQL.\n */\n protected buildIndexName(tableName: string, fields: string[], unique: boolean): string {\n const prefix = unique ? 'uniq' : 'idx';\n const base = `${prefix}_${tableName}_${fields.join('_')}`;\n const MAX = 60;\n if (base.length <= MAX) return base;\n const hash = createHash('sha1').update(base).digest('hex').slice(0, 8);\n return `${`${prefix}_${tableName}`.slice(0, MAX - 9)}_${hash}`;\n }\n\n /**\n * Read the names of indexes that already exist on a table, per dialect.\n * Used to make declared-index sync idempotent across repeated runs.\n * Failures are swallowed — at worst we attempt a create and absorb the\n * \"already exists\" error in `syncDeclaredIndexes`.\n */\n protected async getExistingIndexNames(tableName: string): Promise<Set<string>> {\n const names = new Set<string>();\n try {\n if (this.isSqlite) {\n const safe = tableName.replace(/[^a-zA-Z0-9_]/g, '');\n const rows: any = await this.knex.raw(`PRAGMA index_list(${safe})`);\n for (const r of rows) names.add(r.name);\n } else if (this.isPostgres) {\n const res: any = await this.knex.raw(\n `SELECT indexname FROM pg_indexes WHERE schemaname = 'public' AND tablename = ?`,\n [tableName],\n );\n for (const r of res.rows) names.add(r.indexname);\n } else if (this.isMysql) {\n const res: any = await this.knex.raw(\n `SELECT INDEX_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`,\n [tableName],\n );\n for (const r of res[0]) names.add(r.INDEX_NAME);\n }\n } catch {\n // Best-effort — fall through and let creation handle conflicts.\n }\n return names;\n }\n\n /**\n * Materialize declared object-level indexes.\n *\n * - Multi-column and single-column indexes are both supported.\n * - `unique: true` emits a UNIQUE index. NULL-distinct semantics are the\n * default across SQLite/Postgres/MySQL, so multiple NULL rows remain\n * allowed while non-NULL duplicates are rejected — matching the\n * convergence-on-conflict pattern the messaging pipeline relies on.\n * - Idempotent: indexes already present (by deterministic name) are\n * skipped, and an \"already exists\" race is absorbed.\n * - Indexes referencing a column that wasn't materialized (e.g. a virtual\n * `formula` field) are skipped with a warning rather than failing sync.\n */\n protected async syncDeclaredIndexes(\n tableName: string,\n indexes: Array<{ name?: string; fields?: string[]; unique?: boolean }>,\n physicalColumns: Set<string>,\n ): Promise<void> {\n const existing = await this.getExistingIndexNames(tableName);\n\n for (const idx of indexes) {\n const fields = Array.isArray(idx?.fields)\n ? idx.fields.filter((f): f is string => typeof f === 'string' && f.length > 0)\n : [];\n if (fields.length === 0) continue;\n\n const missing = fields.filter((f) => !physicalColumns.has(f));\n if (missing.length > 0) {\n this.logger.warn(\n `[sql-driver] skipping declared index on \"${tableName}\" — column(s) not materialized: ${missing.join(', ')}`,\n { tableName, fields },\n );\n continue;\n }\n\n const unique = idx.unique === true;\n const name =\n typeof idx.name === 'string' && idx.name.trim()\n ? idx.name.trim()\n : this.buildIndexName(tableName, fields, unique);\n\n if (existing.has(name)) continue;\n\n try {\n await this.knex.schema.alterTable(tableName, (table) => {\n if (unique) {\n table.unique(fields, { indexName: name });\n } else {\n table.index(fields, name);\n }\n });\n existing.add(name);\n } catch (e: any) {\n const msg = String(e?.message ?? e);\n // A concurrent creator or a pre-existing equivalent index under a\n // different name can race us here — both are benign for our intent\n // (the index exists). Anything else is a real failure.\n if (/already exists|duplicate key name|exists/i.test(msg)) continue;\n throw e;\n }\n }\n }\n\n // ===================================\n // Schema Introspection\n // ===================================\n\n async introspectSchema(): Promise<IntrospectedSchema> {\n const tables: Record<string, IntrospectedTable> = {};\n let tableNames: string[] = [];\n\n if (this.isPostgres) {\n const result = await this.knex.raw(`\n SELECT table_name\n FROM information_schema.tables\n WHERE table_schema = 'public'\n AND table_type = 'BASE TABLE'\n `);\n tableNames = result.rows.map((row: any) => row.table_name);\n } else if (this.isMysql) {\n const result = await this.knex.raw(`\n SELECT table_name\n FROM information_schema.tables\n WHERE table_schema = DATABASE()\n AND table_type = 'BASE TABLE'\n `);\n tableNames = result[0].map((row: any) => row.TABLE_NAME);\n } else if (this.isSqlite) {\n const result = await this.knex.raw(`\n SELECT name as table_name\n FROM sqlite_master\n WHERE type='table'\n AND name NOT LIKE 'sqlite_%'\n `);\n tableNames = result.map((row: any) => row.table_name);\n }\n\n for (const tableName of tableNames) {\n const columns = await this.introspectColumns(tableName);\n const foreignKeys = await this.introspectForeignKeys(tableName);\n const primaryKeys = await this.introspectPrimaryKeys(tableName);\n const uniqueConstraints = await this.introspectUniqueConstraints(tableName);\n\n for (const col of columns) {\n if (primaryKeys.includes(col.name)) col.isPrimary = true;\n if (uniqueConstraints.includes(col.name)) col.isUnique = true;\n }\n\n tables[tableName] = { name: tableName, columns, foreignKeys, primaryKeys };\n }\n\n return { tables };\n }\n\n // ===================================\n // Internal helpers\n // ===================================\n\n /** Expose the underlying Knex instance for advanced usage. */\n getKnex(): Knex {\n return this.knex;\n }\n\n protected getBuilder(object: string, options?: DriverOptions) {\n // Federation (ADR-0015): an external object resolves to its remote table\n // (`external.remoteName`, optionally schema-qualified). Managed objects miss\n // both maps, so this is `this.knex(object)` — unchanged. `.withSchema()` is\n // applied on the builder (not via `knex.withSchema().from()`) so the builder\n // type is identical to the managed path for every downstream caller.\n const physical = this.physicalTableByObject[object] ?? object;\n let builder = this.knex(physical);\n const remoteSchema = this.physicalSchemaByObject[object];\n if (remoteSchema) {\n builder = builder.withSchema(remoteSchema);\n }\n if (options?.transaction) {\n builder = builder.transacting(options.transaction as Knex.Transaction);\n }\n return builder;\n }\n\n /**\n * Resolve the tenant column for the given object, if any.\n *\n * Lookup falls back to both the storage-mapped table name and the raw\n * object name so callers that pass either form get the same answer.\n * Returns `null` when the object has no tenant-isolation field.\n */\n protected resolveTenantField(object: string): string | null {\n const tableName = StorageNameMapping.resolveTableName({ name: object } as any);\n const cached =\n this.tenantFieldByTable[tableName] ?? this.tenantFieldByTable[object];\n return cached ?? null;\n }\n\n /**\n * Whether the host kernel runs in multi-tenant mode — read once from\n * `OS_MULTI_ORG_ENABLED` (or the deprecated `OS_MULTI_TENANT`), matching how\n * the SchemaRegistry / SecurityPlugin pick the mode. Used to gate the\n * tenant-audit warning: it's only meaningful where tenant isolation is\n * actually enforced (org-scoping installed).\n */\n private _multiTenantMode?: boolean;\n protected isMultiTenantMode(): boolean {\n if (this._multiTenantMode === undefined) {\n const raw =\n process.env.OS_MULTI_ORG_ENABLED ?? process.env.OS_MULTI_TENANT ?? 'false';\n this._multiTenantMode = String(raw).toLowerCase() !== 'false';\n }\n return this._multiTenantMode;\n }\n\n /**\n * Apply a `WHERE tenant_field = ?` clause to the given query builder\n * when:\n * 1. `options.tenantId` is provided by the caller, AND\n * 2. the object actually has a tenant-isolation field\n * (`organization_id` by convention).\n *\n * Without a tenantId the call is treated as an unscoped/admin path —\n * keeps legacy callers, seed scripts, and cross-org tooling working.\n * This is the single chokepoint for read-side tenant isolation in the\n * SQL driver; every CRUD method routes through it.\n */\n protected applyTenantScope(\n builder: Knex.QueryBuilder,\n object: string,\n options?: DriverOptions,\n ): Knex.QueryBuilder {\n const tenantId = (options as any)?.tenantId;\n if (tenantId === undefined || tenantId === null || tenantId === '') return builder;\n const field = this.resolveTenantField(object);\n if (!field) return builder;\n return builder.where(field, String(tenantId));\n }\n\n /**\n * Auto-inject the tenant column on insert rows when:\n * 1. `options.tenantId` is provided, AND\n * 2. the object has a tenant-isolation field, AND\n * 3. the row does not already set that field.\n *\n * Explicit values are never overwritten — admins writing to a specific\n * tenant via raw row data keep that authority.\n */\n protected injectTenantOnInsert(\n object: string,\n row: Record<string, any>,\n options?: DriverOptions,\n ): void {\n const tenantId = (options as any)?.tenantId;\n if (tenantId === undefined || tenantId === null || tenantId === '') return;\n const field = this.resolveTenantField(object);\n if (!field) return;\n if (row[field] === undefined || row[field] === null || row[field] === '') {\n row[field] = String(tenantId);\n }\n }\n\n /**\n * Surface writes that target a tenant-scoped object but don't carry a\n * `tenantId`. These are almost always system / seed / admin paths that\n * forgot to thread the active session context — easy to miss in code\n * review and impossible to find after a breach.\n *\n * Throttled to one warning per `${object}:${op}` so background workers\n * don't spam the log. Set `options.bypassTenantAudit = true` (or env\n * `OS_TENANT_AUDIT=0`) to silence intentionally.\n */\n protected auditMissingTenant(\n object: string,\n op: 'create' | 'update' | 'delete' | 'bulkCreate' | 'bulkDelete' | 'updateMany' | 'deleteMany' | 'upsert',\n options?: DriverOptions,\n ): void {\n if (process.env.OS_TENANT_AUDIT === '0') return;\n if ((options as any)?.bypassTenantAudit === true) return;\n // Only meaningful in multi-tenant deployments. Single-tenant stacks have no\n // tenant isolation, yet the kernel now ALWAYS provisions an `organization_id`\n // column (its existence is decoupled from the tenant flag). Column presence\n // alone therefore no longer implies \"tenant-scoped\" — without this gate every\n // system/sudo write (e.g. the notification/http delivery dispatchers' claim\n // updates) would spam a meaningless warning on single-tenant boots.\n if (!this.isMultiTenantMode()) return;\n const tenantId = (options as any)?.tenantId;\n if (tenantId !== undefined && tenantId !== null && tenantId !== '') return;\n const field = this.resolveTenantField(object);\n if (!field) return;\n const key = `${object}:${op}`;\n if (this.tenantAuditWarned.has(key)) return;\n this.tenantAuditWarned.add(key);\n this.logger.warn(\n `[tenant-audit] ${op} on tenant-scoped object \"${object}\" without options.tenantId — writes will not be tenant-isolated. Pass tenantId via ExecutionContext or set bypassTenantAudit:true to silence.`,\n { object, op, tenantField: field },\n );\n }\n\n // ── Filter helpers ──────────────────────────────────────────────────────────\n\n /**\n * Resolve the underlying table name for a Knex query builder so we can\n * look up column type metadata (date/datetime maps populated during\n * `initObjects`). Returns null when the builder is not table-scoped yet.\n */\n protected tableNameForBuilder(builder: any): string | null {\n const t = builder?._single?.table;\n if (typeof t === 'string') return t;\n return null;\n }\n\n /**\n * Coercion-map key for a builder. Coercion maps (date/datetime) are keyed by\n * OBJECT name, but after the federation change {@link getBuilder} targets the\n * physical remote table, so a builder reports the remote name. Map it back to\n * the object name for external objects; identity for managed ones (no reverse\n * entry). Note datetime coercion is a SQLite-only concern (see\n * coerceFilterValue), and SQLite external tables are bare-named, so this is\n * exact where it matters.\n */\n protected coercionKey(builder: any): string | null {\n const physical = this.tableNameForBuilder(builder);\n if (physical == null) return null;\n return this.objectByPhysicalTable[physical] ?? physical;\n }\n\n /**\n * Collapse a `Field.date` value to a timezone-naive `YYYY-MM-DD`\n * calendar-day string (ADR-0053 Phase 1). A `Date` collapses to its UTC\n * calendar day; a string keeps its leading date and drops any time\n * component. Anything else (and `null`/`undefined`) passes through\n * unchanged. This is the single source of truth for date-only truncation,\n * shared by the filter (`coerceFilterValue`), write (`formatInput`) and\n * read (`formatOutput`) paths so all three agree on what a date *is*.\n */\n protected toDateOnly(value: any): any {\n if (value == null) return value;\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return value;\n const y = value.getUTCFullYear();\n const m = String(value.getUTCMonth() + 1).padStart(2, '0');\n const d = String(value.getUTCDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n }\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(trimmed)) return trimmed.slice(0, 10);\n }\n return value;\n }\n\n /**\n * Normalise a filter value for a single column so the comparison the\n * driver sends to SQLite matches the on-disk representation.\n *\n * The platform stores `Field.datetime()` values as INTEGER milliseconds\n * (the result of passing a JS `Date` through better-sqlite3) but date\n * macros like `{last_quarter_start}` expand to an ISO `YYYY-MM-DD` string\n * client-side. Without coercion the SQL becomes `published_at >= '2026-…'`\n * which collapses to a TEXT-vs-INTEGER affinity compare and never\n * matches. We translate the ISO/Date/numeric inputs into the storage\n * type so the comparison works.\n *\n * For `Field.date()` we keep ISO TEXT but normalise Date objects to\n * `YYYY-MM-DD` for the same reason.\n */\n protected coerceFilterValue(table: string | null, field: string, value: any): any {\n if (value == null || !table) return value;\n if (Array.isArray(value)) return value.map((v) => this.coerceFilterValue(table, field, v));\n\n const isDatetime = this.datetimeFields[table]?.has(field);\n const isDate = this.dateFields[table]?.has(field);\n if (!isDatetime && !isDate) return value;\n\n const toMs = (v: any): number | null => {\n if (v instanceof Date) return v.getTime();\n if (typeof v === 'number' && Number.isFinite(v)) return v;\n if (typeof v === 'string') {\n const trimmed = v.trim();\n if (trimmed === '') return null;\n if (/^-?\\d+$/.test(trimmed)) {\n const n = Number(trimmed);\n if (Number.isFinite(n)) return n;\n }\n // Treat bare YYYY-MM-DD as start-of-day UTC; full ISO is parsed\n // as-is so timezones round-trip correctly.\n const iso = /^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed) ? `${trimmed}T00:00:00.000Z` : trimmed;\n const n = Date.parse(iso);\n return Number.isFinite(n) ? n : null;\n }\n return null;\n };\n\n if (isDatetime) {\n // Only SQLite stores `Field.datetime` as an INTEGER epoch (better-sqlite3\n // binds a JS `Date` as `.getTime()`); there the ISO/text comparand MUST be\n // coerced to epoch ms or it collapses to a TEXT-vs-INTEGER affinity compare\n // that never matches. Postgres/MySQL map datetime to a native TIMESTAMP\n // (see `defineColumn` → `table.timestamp`), where Knex binds an ISO string\n // or `Date` correctly — coercing to an epoch integer there would compare an\n // INTEGER against a TIMESTAMP and break the query. So gate on dialect.\n if (!this.isSqlite) return value;\n const ms = toMs(value);\n return ms == null ? value : ms;\n }\n\n // Field.date — normalise the comparand to YYYY-MM-DD (ADR-0053 Phase 1).\n return this.toDateOnly(value);\n }\n\n /**\n * Public, dialect-correct temporal filter-value coercion for callers that\n * build SQL *outside* the normal `find()`/`applyFilters()` path — chiefly the\n * analytics native-SQL strategy, which compiles a raw `SELECT … WHERE col >= $N`\n * and binds the value directly, bypassing `coerceFilterValue`.\n *\n * Given a logical object (table) name, a field name and a filter value\n * (typically an ISO date/datetime string from a dashboard relative-date\n * token like `{12_months_ago}`), this returns the value in the column's\n * on-disk storage form:\n * - SQLite `Field.datetime` → epoch milliseconds (INTEGER), so the\n * comparison matches the stored integer rather than failing a\n * TEXT-vs-INTEGER affinity compare.\n * - `Field.date` (any dialect) → `YYYY-MM-DD` text.\n * - Native-timestamp dialects / non-temporal fields → value unchanged.\n *\n * This is a thin, intentionally narrow wrapper over the same `coerceFilterValue`\n * the driver already uses, so there is exactly one source of truth for the\n * storage convention and the analytics path can never drift from CRUD.\n */\n public temporalFilterValue(objectName: string, field: string, value: any): any {\n return this.coerceFilterValue(objectName, field, value);\n }\n\n protected applyFilters(builder: Knex.QueryBuilder, filters: any) {\n if (!filters) return;\n const table = this.coercionKey(builder);\n\n if (!Array.isArray(filters) && typeof filters === 'object') {\n const hasMongoOperators = Object.keys(filters).some(\n (k) =>\n k.startsWith('$') ||\n (typeof filters[k] === 'object' &&\n filters[k] !== null &&\n Object.keys(filters[k]).some((op) => op.startsWith('$'))),\n );\n\n if (hasMongoOperators) {\n this.applyFilterCondition(builder, filters, 'and', table);\n return;\n }\n\n for (const [key, value] of Object.entries(filters)) {\n if (['limit', 'offset', 'fields', 'orderBy'].includes(key)) continue;\n builder.where(this.remoteColumn(table, key, key), this.coerceFilterValue(table, key, value) as any);\n }\n return;\n }\n\n if (!Array.isArray(filters) || filters.length === 0) return;\n\n let nextJoin: 'and' | 'or' = 'and';\n\n for (const item of filters) {\n if (typeof item === 'string') {\n if (item.toLowerCase() === 'or') nextJoin = 'or';\n else if (item.toLowerCase() === 'and') nextJoin = 'and';\n continue;\n }\n\n if (Array.isArray(item)) {\n const [fieldRaw, op, value] = item;\n const isCriterion = typeof fieldRaw === 'string' && typeof op === 'string';\n\n if (isCriterion) {\n const localField = this.mapSortField(fieldRaw);\n const field = this.remoteColumn(table, fieldRaw, localField);\n const coerced = this.coerceFilterValue(table, localField, value);\n const apply = (b: any) => {\n const method = nextJoin === 'or' ? 'orWhere' : 'where';\n const methodIn = nextJoin === 'or' ? 'orWhereIn' : 'whereIn';\n const methodNotIn = nextJoin === 'or' ? 'orWhereNotIn' : 'whereNotIn';\n\n if (op === 'contains') {\n this.applyContainsLike(b, method, field, value);\n return;\n }\n\n switch (op) {\n case '=':\n b[method](field, coerced);\n break;\n case '!=':\n b[method](field, '<>', coerced);\n break;\n case 'in':\n b[methodIn](field, coerced);\n break;\n case 'nin':\n b[methodNotIn](field, coerced);\n break;\n default:\n b[method](field, op, coerced);\n }\n };\n apply(builder);\n } else {\n const method = nextJoin === 'or' ? 'orWhere' : 'where';\n (builder as any)[method]((qb: any) => {\n this.applyFilters(qb, item);\n });\n }\n\n nextJoin = 'and';\n }\n }\n }\n\n /**\n * Apply a `contains` substring match as a parameterized `LIKE '%…%'`, escaping\n * the LIKE metacharacters `%` / `_` (and the escape char `\\`) in the user value\n * so they match literally instead of acting as wildcards — otherwise a value of\n * `%` matches every row (a filter-bypass, P0). Binds an explicit `ESCAPE '\\'`\n * because SQLite does not honour a default escape character (MySQL/Postgres do,\n * but the explicit clause is correct for all three).\n */\n private applyContainsLike(builder: any, method: string, field: string, value: unknown): void {\n const escaped = String(value).replace(/[\\\\%_]/g, '\\\\$&');\n const rawMethod = method.startsWith('or') ? 'orWhereRaw' : 'whereRaw';\n builder[rawMethod]('?? LIKE ? ESCAPE ?', [field, `%${escaped}%`, '\\\\']);\n }\n\n protected applyFilterCondition(builder: Knex.QueryBuilder, condition: any, logicalOp: 'and' | 'or' = 'and', tableHint?: string | null) {\n if (!condition || typeof condition !== 'object') return;\n const table = tableHint ?? this.coercionKey(builder);\n\n for (const [key, value] of Object.entries(condition)) {\n if (key === '$and' && Array.isArray(value)) {\n builder.where((qb) => {\n for (const sub of value) {\n qb.where((subQb) => {\n this.applyFilterCondition(subQb, sub, 'and', table);\n });\n }\n });\n } else if (key === '$or' && Array.isArray(value)) {\n const method = logicalOp === 'or' ? 'orWhere' : 'where';\n (builder as any)[method]((qb: any) => {\n for (const sub of value) {\n qb.orWhere((subQb: any) => {\n this.applyFilterCondition(subQb, sub, 'or', table);\n });\n }\n });\n } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const localField = this.mapSortField(key);\n const field = this.remoteColumn(table, key, localField);\n for (const [op, opValue] of Object.entries(value as Record<string, any>)) {\n const method = logicalOp === 'or' ? 'orWhere' : 'where';\n const coerced = this.coerceFilterValue(table, localField, opValue);\n switch (op) {\n case '$eq':\n (builder as any)[method](field, coerced);\n break;\n case '$ne':\n (builder as any)[method](field, '<>', coerced);\n break;\n case '$gt':\n (builder as any)[method](field, '>', coerced);\n break;\n case '$gte':\n (builder as any)[method](field, '>=', coerced);\n break;\n case '$lt':\n (builder as any)[method](field, '<', coerced);\n break;\n case '$lte':\n (builder as any)[method](field, '<=', coerced);\n break;\n case '$in': {\n const mIn = logicalOp === 'or' ? 'orWhereIn' : 'whereIn';\n (builder as any)[mIn](field, coerced as any[]);\n break;\n }\n case '$nin': {\n const mNotIn = logicalOp === 'or' ? 'orWhereNotIn' : 'whereNotIn';\n (builder as any)[mNotIn](field, coerced as any[]);\n break;\n }\n case '$contains':\n this.applyContainsLike(builder, method, field, opValue);\n break;\n default:\n (builder as any)[method](field, coerced);\n }\n }\n } else {\n const localField = this.mapSortField(key);\n const field = this.remoteColumn(table, key, localField);\n const method = logicalOp === 'or' ? 'orWhere' : 'where';\n (builder as any)[method](field, this.coerceFilterValue(table, localField, value) as any);\n }\n }\n }\n\n // ── Field mapping ───────────────────────────────────────────────────────────\n\n protected mapSortField(field: string): string {\n if (field === 'createdAt') return 'created_at';\n if (field === 'updatedAt') return 'updated_at';\n return field;\n }\n\n /**\n * Physical column for a logical field on an external object that declares an\n * `external.columnMap` (ADR-0015). Returns `fallback` (the caller's existing\n * per-site resolution) when the object has no columnMap, so managed objects\n * and external objects without a columnMap are byte-for-byte unchanged.\n */\n protected remoteColumn(object: string | null | undefined, field: string, fallback: string): string {\n const m = object ? this.fieldColumnByObject[object] : undefined;\n return (m && m[field]) || fallback;\n }\n\n /**\n * Remap a write payload's logical field keys to physical remote columns for an\n * external object with a columnMap. No-op otherwise. Applied AFTER formatInput\n * (whose value coercion is keyed by logical field name).\n */\n protected applyWriteColumnMap(object: string, data: any): any {\n const m = this.fieldColumnByObject[object];\n if (!m || !data || typeof data !== 'object') return data;\n const out: any = {};\n for (const [k, v] of Object.entries(data)) out[m[k] ?? k] = v;\n return out;\n }\n\n protected mapAggregateFunc(func: string): string {\n switch (func) {\n case 'count':\n return 'count';\n case 'sum':\n return 'sum';\n case 'avg':\n return 'avg';\n case 'min':\n return 'min';\n case 'max':\n return 'max';\n default:\n throw new Error(`Unsupported aggregate function: ${func}`);\n }\n }\n\n // ── Window function builder ─────────────────────────────────────────────────\n\n protected buildWindowFunction(spec: any): string {\n const func = spec.function.toUpperCase();\n let sql = `${func}()`;\n\n const overParts: string[] = [];\n\n if (spec.partitionBy && Array.isArray(spec.partitionBy) && spec.partitionBy.length > 0) {\n const partitionFields = spec.partitionBy.map((f: string) => this.mapSortField(f)).join(', ');\n overParts.push(`PARTITION BY ${partitionFields}`);\n }\n\n if (spec.orderBy && Array.isArray(spec.orderBy) && spec.orderBy.length > 0) {\n const orderFields = spec.orderBy\n .map((s: any) => {\n const field = this.mapSortField(s.field);\n const order = (s.order || 'asc').toUpperCase();\n return `${field} ${order}`;\n })\n .join(', ');\n overParts.push(`ORDER BY ${orderFields}`);\n }\n\n sql += overParts.length > 0 ? ` OVER (${overParts.join(' ')})` : ` OVER ()`;\n return sql;\n }\n\n // ── Column creation helper ──────────────────────────────────────────────────\n\n protected createColumn(table: Knex.CreateTableBuilder, name: string, field: any) {\n if (field.multiple) {\n table.json(name);\n return;\n }\n\n const type = field.type || 'string';\n let col: any;\n switch (type) {\n case 'string':\n case 'email':\n case 'url':\n case 'phone':\n case 'password':\n col = table.string(name);\n break;\n case 'text':\n case 'textarea':\n case 'html':\n case 'markdown':\n col = table.text(name);\n break;\n case 'integer':\n case 'int':\n col = table.integer(name);\n break;\n case 'float':\n case 'number':\n case 'currency':\n case 'percent':\n // `rating`/`slider`/`progress` are authored as numeric scalars (a star\n // count, a slider position, a percent-of-completion). Without an explicit\n // case they fell to `default → table.string`, giving the column TEXT\n // affinity so SQLite coerced the written number to a string ('4' not 4) —\n // a silent type-fidelity leak the value-loss tests didn't catch. REAL\n // affinity round-trips them as JS numbers (#field-zoo).\n case 'rating':\n case 'slider':\n case 'progress':\n col = table.float(name);\n break;\n // `toggle` is a boolean rendered as a switch. Same leak as above (TEXT\n // affinity stored '1'); a boolean column gives NUMERIC affinity and the\n // `booleanFields` read-coercion below converts the stored 1/0 back to a\n // real JS boolean.\n case 'boolean':\n case 'toggle':\n col = table.boolean(name);\n break;\n case 'date':\n col = table.date(name);\n break;\n case 'datetime':\n col = table.timestamp(name);\n break;\n case 'time':\n col = table.time(name);\n break;\n case 'lookup':\n col = table.string(name);\n if (field.reference_to) {\n table.foreign(name).references('id').inTable(field.reference_to);\n }\n break;\n case 'summary':\n col = table.float(name);\n break;\n case 'auto_number':\n case 'autonumber':\n col = table.string(name);\n break;\n case 'formula':\n return; // Virtual — no column\n default:\n // Array/object-valued types are stored as a JSON column. Driven by the\n // single `JSON_COLUMN_TYPES` source so this DDL switch and `isJsonField`\n // (the read-side deserializer) can never drift — the drift between them\n // is exactly what let array-valued fields reach the binder un-serialized\n // (#field-zoo). Everything else is a plain string.\n col = JSON_COLUMN_TYPES.has(type) ? table.json(name) : table.string(name);\n }\n\n if (col) {\n if (field.unique) col.unique();\n if (field.required) col.notNullable();\n // `defaultValue: 'NOW()'` is a framework convention for \"use the\n // database clock at insert time\". Translate it to the driver-native\n // CURRENT_TIMESTAMP equivalent so the column gets a real default\n // instead of leaving the literal string 'NOW()' for whatever\n // upstream code happens to write.\n if (\n (type === 'datetime' || type === 'date' || type === 'time') &&\n typeof field.defaultValue === 'string' &&\n /^now\\(\\)$/i.test(field.defaultValue.trim())\n ) {\n col.defaultTo(this.knex.fn.now());\n } else if (field.defaultValue !== undefined && field.defaultValue !== null) {\n const dv = field.defaultValue;\n if (typeof dv === 'string' && /^now\\(\\)$/i.test(dv.trim())) {\n col.defaultTo(this.knex.fn.now());\n } else if (typeof dv !== 'object') {\n col.defaultTo(dv as any);\n }\n }\n }\n }\n\n // ── Database helpers ────────────────────────────────────────────────────────\n\n protected async ensureDatabaseExists() {\n // SQLite auto-creates database files but NOT parent directories.\n // Ensure the directory exists so better-sqlite3 can create the file.\n if (this.isSqlite) {\n const conn = (this.config as any).connection;\n const filename = typeof conn === 'string' ? conn : conn?.filename;\n if (filename && filename !== ':memory:' && !filename.startsWith(':')) {\n const { dirname } = await import('node:path');\n const { mkdir } = await import('node:fs/promises');\n const dir = dirname(filename);\n if (dir && dir !== '.') {\n await mkdir(dir, { recursive: true });\n }\n }\n return;\n }\n\n // Only PostgreSQL and MySQL support programmatic database creation\n if (!this.isPostgres && !this.isMysql) return;\n\n try {\n await this.knex.raw('SELECT 1');\n } catch (e: any) {\n // PostgreSQL: '3D000' = database does not exist\n // MySQL: 'ER_BAD_DB_ERROR' (errno 1049) = unknown database\n if (\n e.code === '3D000' ||\n e.code === 'ER_BAD_DB_ERROR' ||\n e.errno === 1049\n ) {\n await this.createDatabase();\n } else {\n throw e;\n }\n }\n }\n\n protected async createDatabase() {\n const config = this.config as any;\n const connection = config.connection;\n let dbName = '';\n const adminConfig = { ...config };\n\n if (this.isPostgres) {\n // PostgreSQL: connect to the 'postgres' maintenance database\n if (typeof connection === 'string') {\n const url = new URL(connection);\n dbName = url.pathname.slice(1);\n url.pathname = '/postgres';\n adminConfig.connection = url.toString();\n } else {\n dbName = connection.database;\n adminConfig.connection = { ...connection, database: 'postgres' };\n }\n } else if (this.isMysql) {\n // MySQL: connect without specifying a database\n if (typeof connection === 'string') {\n const url = new URL(connection);\n dbName = url.pathname.slice(1);\n url.pathname = '/';\n adminConfig.connection = url.toString();\n } else {\n dbName = connection.database;\n const { database: _db, ...rest } = connection;\n adminConfig.connection = rest;\n }\n } else {\n return; // Unsupported dialect for auto-creation\n }\n\n const adminKnex = knex(adminConfig);\n try {\n if (this.isPostgres) {\n await adminKnex.raw(`CREATE DATABASE \"${dbName}\"`);\n } else if (this.isMysql) {\n await adminKnex.raw(`CREATE DATABASE IF NOT EXISTS \\`${dbName}\\``);\n }\n } finally {\n await adminKnex.destroy();\n }\n }\n\n protected isJsonField(type: string, field: any): boolean {\n return JSON_COLUMN_TYPES.has(type) || !!field.multiple;\n }\n\n // ── SQLite serialisation ────────────────────────────────────────────────────\n\n protected formatInput(object: string, data: any): any {\n let copy: any = data;\n let copied = false;\n\n // Insert/update-time safety net: any caller that passes the literal\n // string 'NOW()' (often because a field defaultValue leaked unresolved)\n // gets it replaced with a real ISO timestamp here, before it hits the\n // wire. Applies to every driver, not just SQLite.\n if (data && typeof data === 'object') {\n const now = new Date().toISOString();\n for (const key of Object.keys(data)) {\n const v = (data as any)[key];\n if (typeof v === 'string' && /^now\\(\\)$/i.test(v.trim())) {\n if (!copied) { copy = { ...data }; copied = true; }\n copy[key] = now;\n }\n }\n }\n\n // ADR-0053 Phase 1: a `Field.date` is a timezone-naive calendar day, not\n // an instant. Collapse any `Date` or full-ISO value to `YYYY-MM-DD` before\n // it hits the wire so storage matches the date-only contract the filter\n // layer (`coerceFilterValue`) already enforces — the write/filter\n // asymmetry was the root cause of the silent date-equality miss.\n // `Field.datetime` is untouched (it keeps full-instant semantics).\n const dateFields = this.dateFields[object];\n if (dateFields && dateFields.size > 0 && copy && typeof copy === 'object') {\n for (const field of dateFields) {\n const v = copy[field];\n if (v == null) continue;\n const normalized = this.toDateOnly(v);\n if (normalized !== v) {\n if (!copied) { copy = { ...copy }; copied = true; }\n copy[field] = normalized;\n }\n }\n }\n\n if (!this.isSqlite) return copy;\n\n const fields = this.jsonFields[object];\n if (fields && fields.length > 0) {\n if (!copied) { copy = { ...copy }; copied = true; }\n for (const field of fields) {\n if (copy[field] !== undefined && typeof copy[field] === 'object' && copy[field] !== null) {\n copy[field] = JSON.stringify(copy[field]);\n }\n }\n }\n\n // Safety net: better-sqlite3 can only bind numbers/strings/bigints/buffers/\n // null. Any value still an array or plain object here (a field type not\n // classified as JSON, a `Field.multiple` we didn't catch, or an ad-hoc\n // payload) would otherwise throw a raw TypeError mid-insert. Serialize it\n // to JSON so the write degrades to a stored string instead of a 500.\n for (const key of Object.keys(copy)) {\n const v = copy[key];\n if (v !== null && typeof v === 'object' && !(v instanceof Date) && !Buffer.isBuffer(v)) {\n if (!copied) { copy = { ...copy }; copied = true; }\n copy[key] = JSON.stringify(v);\n }\n }\n return copy;\n }\n\n protected formatOutput(object: string, data: any): any {\n if (!data) return data;\n\n // External columnMap (ADR-0015): rename physical remote-column keys to local\n // field names BEFORE coercion (which is keyed by local field). No-op for\n // managed objects and external objects without a columnMap.\n const colToField = this.columnFieldByObject[object];\n if (colToField && typeof data === 'object') {\n for (const [remoteCol, localField] of Object.entries(colToField)) {\n if (remoteCol !== localField && Object.prototype.hasOwnProperty.call(data, remoteCol)) {\n // Explicit columnMap wins: the remote column is the source of truth for\n // this local field, even if a same-named native column also exists.\n data[localField] = data[remoteCol];\n delete data[remoteCol];\n }\n }\n }\n\n if (this.isSqlite) {\n const jsonFields = this.jsonFields[object];\n if (jsonFields && jsonFields.length > 0) {\n for (const field of jsonFields) {\n if (data[field] !== undefined && typeof data[field] === 'string') {\n try {\n data[field] = JSON.parse(data[field]);\n } catch {\n // keep as string\n }\n }\n }\n }\n\n const booleanFields = this.booleanFields[object];\n if (booleanFields && booleanFields.length > 0) {\n for (const field of booleanFields) {\n if (data[field] !== undefined && data[field] !== null) {\n data[field] = Boolean(data[field]);\n }\n }\n }\n\n // Numeric scalars stored on a legacy TEXT-affinity column come back as\n // strings ('4'); coerce numeric-looking strings back to numbers so the\n // declared type wins regardless of when the column was created. Only\n // touch strings — a fresh REAL/INTEGER column already yields a number,\n // and a genuinely non-numeric value (junk legacy data) is left intact\n // rather than turned into NaN. See NUMERIC_SCALAR_TYPES.\n const numericFields = this.numericFields[object];\n if (numericFields && numericFields.length > 0) {\n for (const field of numericFields) {\n const v = data[field];\n if (typeof v === 'string' && v.trim() !== '') {\n const n = Number(v);\n if (!Number.isNaN(n)) data[field] = n;\n }\n }\n }\n }\n\n // ADR-0053 Phase 1: present `Field.date` as a timezone-naive `YYYY-MM-DD`\n // string, slicing any stored time component. This transparently repairs\n // legacy rows written as a full timestamp before this normalization, so\n // date-equality works without a data migration. Runs for every dialect.\n const dateFields = this.dateFields[object];\n if (dateFields && dateFields.size > 0) {\n for (const field of dateFields) {\n const v = data[field];\n if (v == null) continue;\n const normalized = this.toDateOnly(v);\n if (normalized !== v) data[field] = normalized;\n }\n }\n\n return data;\n }\n\n // ── Introspection internals ─────────────────────────────────────────────────\n\n protected async introspectColumns(tableName: string): Promise<IntrospectedColumn[]> {\n const columnInfo = await this.knex(tableName).columnInfo();\n const columns: IntrospectedColumn[] = [];\n\n for (const [colName, info] of Object.entries<any>(columnInfo)) {\n let type = 'string';\n let maxLength: number | undefined;\n\n if (this.isSqlite) {\n type = info.type?.toLowerCase() || 'string';\n } else {\n type = info.type || 'string';\n }\n\n if (info.maxLength) {\n maxLength = info.maxLength;\n }\n\n columns.push({\n name: colName,\n type,\n nullable: info.nullable !== false,\n defaultValue: info.defaultValue,\n isPrimary: false,\n isUnique: false,\n maxLength,\n });\n }\n\n return columns;\n }\n\n protected async introspectForeignKeys(tableName: string): Promise<IntrospectedForeignKey[]> {\n const foreignKeys: IntrospectedForeignKey[] = [];\n\n try {\n if (this.isPostgres) {\n const result = await this.knex.raw(\n `\n SELECT\n kcu.column_name,\n ccu.table_name AS referenced_table,\n ccu.column_name AS referenced_column,\n tc.constraint_name\n FROM information_schema.table_constraints AS tc\n JOIN information_schema.key_column_usage AS kcu\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n JOIN information_schema.constraint_column_usage AS ccu\n ON ccu.constraint_name = tc.constraint_name\n AND ccu.table_schema = tc.table_schema\n WHERE tc.constraint_type = 'FOREIGN KEY'\n AND tc.table_name = ?\n `,\n [tableName],\n );\n\n for (const row of result.rows) {\n foreignKeys.push({\n columnName: row.column_name,\n referencedTable: row.referenced_table,\n referencedColumn: row.referenced_column,\n constraintName: row.constraint_name,\n });\n }\n } else if (this.isMysql) {\n const result = await this.knex.raw(\n `\n SELECT\n COLUMN_NAME as column_name,\n REFERENCED_TABLE_NAME as referenced_table,\n REFERENCED_COLUMN_NAME as referenced_column,\n CONSTRAINT_NAME as constraint_name\n FROM information_schema.KEY_COLUMN_USAGE\n WHERE TABLE_SCHEMA = DATABASE()\n AND TABLE_NAME = ?\n AND REFERENCED_TABLE_NAME IS NOT NULL\n `,\n [tableName],\n );\n\n for (const row of result[0]) {\n foreignKeys.push({\n columnName: row.column_name,\n referencedTable: row.referenced_table,\n referencedColumn: row.referenced_column,\n constraintName: row.constraint_name,\n });\n }\n } else if (this.isSqlite) {\n const tableExistsResult = await this.knex.raw(\n \"SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?\",\n [tableName],\n );\n\n if (!Array.isArray(tableExistsResult) || tableExistsResult.length === 0) {\n return foreignKeys;\n }\n\n const safeTableName = tableName.replace(/[^a-zA-Z0-9_]/g, '');\n const result = await this.knex.raw(`PRAGMA foreign_key_list(${safeTableName})`);\n\n for (const row of result) {\n foreignKeys.push({\n columnName: row.from,\n referencedTable: row.table,\n referencedColumn: row.to,\n constraintName: `fk_${tableName}_${row.from}`,\n });\n }\n }\n } catch {\n // silently ignore introspection errors\n }\n\n return foreignKeys;\n }\n\n protected async introspectPrimaryKeys(tableName: string): Promise<string[]> {\n const primaryKeys: string[] = [];\n\n try {\n if (this.isPostgres) {\n const result = await this.knex.raw(\n `\n SELECT a.attname as column_name\n FROM pg_index i\n JOIN pg_attribute a ON a.attrelid = i.indrelid\n AND a.attnum = ANY(i.indkey)\n WHERE i.indrelid = ?::regclass\n AND i.indisprimary\n `,\n [tableName],\n );\n\n for (const row of result.rows) {\n primaryKeys.push(row.column_name);\n }\n } else if (this.isMysql) {\n const result = await this.knex.raw(\n `\n SELECT COLUMN_NAME as column_name\n FROM information_schema.KEY_COLUMN_USAGE\n WHERE TABLE_SCHEMA = DATABASE()\n AND TABLE_NAME = ?\n AND CONSTRAINT_NAME = 'PRIMARY'\n `,\n [tableName],\n );\n\n for (const row of result[0]) {\n primaryKeys.push(row.column_name);\n }\n } else if (this.isSqlite) {\n const safeTableName = tableName.replace(/[^a-zA-Z0-9_]/g, '');\n\n const tablesResult = await this.knex.raw(\"SELECT name FROM sqlite_master WHERE type = 'table'\");\n const tableNames = Array.isArray(tablesResult) ? tablesResult.map((row: any) => row.name) : [];\n\n if (!tableNames.includes(safeTableName)) {\n return primaryKeys;\n }\n\n const result = await this.knex.raw(`PRAGMA table_info(${safeTableName})`);\n\n for (const row of result) {\n if (row.pk === 1) {\n primaryKeys.push(row.name);\n }\n }\n }\n } catch {\n // silently ignore\n }\n\n return primaryKeys;\n }\n\n protected async introspectUniqueConstraints(tableName: string): Promise<string[]> {\n const uniqueColumns: string[] = [];\n\n try {\n if (this.isPostgres) {\n const result = await this.knex.raw(\n `\n SELECT c.column_name\n FROM information_schema.table_constraints tc\n JOIN information_schema.constraint_column_usage AS ccu\n ON tc.constraint_schema = ccu.constraint_schema\n AND tc.constraint_name = ccu.constraint_name\n WHERE tc.constraint_type = 'UNIQUE'\n AND tc.table_name = ?\n `,\n [tableName],\n );\n\n for (const row of result.rows) {\n uniqueColumns.push(row.column_name);\n }\n } else if (this.isMysql) {\n const result = await this.knex.raw(\n `\n SELECT COLUMN_NAME\n FROM information_schema.TABLE_CONSTRAINTS tc\n JOIN information_schema.KEY_COLUMN_USAGE kcu\n USING (CONSTRAINT_NAME, TABLE_SCHEMA, TABLE_NAME)\n WHERE CONSTRAINT_TYPE = 'UNIQUE'\n AND TABLE_SCHEMA = DATABASE()\n AND TABLE_NAME = ?\n `,\n [tableName],\n );\n\n for (const row of result[0]) {\n uniqueColumns.push(row.COLUMN_NAME);\n }\n } else if (this.isSqlite) {\n const safeTableName = tableName.replace(/[^a-zA-Z0-9_]/g, '');\n\n const tablesResult = await this.knex.raw(\"SELECT name FROM sqlite_master WHERE type = 'table'\");\n const tableNames = Array.isArray(tablesResult) ? tablesResult.map((row: any) => row.name) : [];\n\n if (!tableNames.includes(safeTableName)) {\n return uniqueColumns;\n }\n\n const indexes = await this.knex.raw(`PRAGMA index_list(${safeTableName})`);\n\n for (const idx of indexes) {\n if (idx.unique === 1) {\n const info = await this.knex.raw(`PRAGMA index_info(${idx.name})`);\n if (info.length === 1) {\n uniqueColumns.push(info[0].name);\n }\n }\n }\n }\n } catch {\n // silently ignore\n }\n\n return uniqueColumns;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Managed-datasource schema drift (issue #2186).\n *\n * The driver's `initObjects` sync is *additive-only*: it creates missing\n * tables and adds missing columns, but never alters or drops existing ones.\n * So a non-additive metadata change (relax `required`, change a type/length,\n * drop or rename a field) silently diverges from an existing database — the\n * served metadata says one thing and the physical column enforces another.\n *\n * This module is the single source of truth for *detecting* that divergence\n * (metadata is authoritative on a `managed` datasource) and for *categorising*\n * each divergence by how dangerous it is to reconcile:\n *\n * - `safe` — loosening that cannot lose data and cannot fail:\n * relax NOT NULL → NULL, widen a varchar. Applied\n * automatically by dev auto-reconcile (P2).\n * - `needs_confirm`— a change a human should eyeball but that does not\n * destroy data (e.g. a non-narrowing type change).\n * - `destructive` — drops or tightenings that can lose data or fail:\n * drop an orphaned column, narrow a varchar, add a\n * NOT NULL constraint over possibly-null data. Only\n * applied by `os migrate apply --allow-destructive`.\n *\n * The detector reuses {@link SchemaDiffEntry} (the same shape the external /\n * federated validator emits, ADR-0015 §5.2) so CLI / Studio / audit can render\n * managed and external drift uniformly.\n */\n\nimport type { SchemaDiffEntry } from '@objectstack/spec/shared';\n\nexport type SqlDialectName = 'sqlite' | 'postgres' | 'mysql' | 'unknown';\n\nexport type DriftCategory = 'safe' | 'needs_confirm' | 'destructive';\n\n/** A reconcilable schema operation, machine-readable for the reconciler. */\nexport type DriftOp =\n | { type: 'relax_not_null'; table: string; column: string }\n | { type: 'tighten_not_null'; table: string; column: string }\n | { type: 'widen_varchar'; table: string; column: string; to: number; from?: number }\n | { type: 'narrow_varchar'; table: string; column: string; to: number; from?: number }\n | { type: 'drop_column'; table: string; column: string };\n\n/**\n * A managed-schema drift finding: a {@link SchemaDiffEntry} enriched with the\n * owning table, a reconcile {@link DriftOp}, and a {@link DriftCategory}.\n */\nexport interface ManagedDriftEntry extends SchemaDiffEntry {\n table: string;\n category: DriftCategory;\n op: DriftOp;\n /** Human one-liner with an actionable hint. */\n message: string;\n}\n\n/** Columns the driver creates unconditionally — never metadata fields. */\nexport const BUILTIN_COLUMNS = new Set(['id', 'created_at', 'updated_at']);\n\n/** Minimal shape of an introspected physical column (see SqlDriver.introspectColumns). */\nexport interface PhysicalColumn {\n name: string;\n type: string;\n nullable: boolean;\n maxLength?: number;\n}\n\n/** Minimal shape of a metadata field definition. */\nexport interface FieldDef {\n type?: string;\n required?: boolean;\n multiple?: boolean;\n maxLength?: number;\n}\n\n/**\n * Does this metadata field materialise a physical column? Mirrors\n * `SqlDriver.createColumn` exactly: `formula` is virtual (computed, no column);\n * everything else — including `multiple` (a JSON column) — gets one.\n */\nexport function fieldHasColumn(field: FieldDef): boolean {\n if (field?.multiple) return true;\n return (field?.type ?? 'string') !== 'formula';\n}\n\n/** Whether the dialect physically enforces varchar length (SQLite does not). */\nfunction enforcesVarcharLength(dialect: SqlDialectName): boolean {\n return dialect === 'postgres' || dialect === 'mysql';\n}\n\n/**\n * Diff one table's metadata fields against its physical columns and return the\n * set of *drift* findings. Metadata is authoritative.\n *\n * Note: a metadata field with no physical column is NOT reported — the\n * additive sync (`ALTER TABLE ADD COLUMN`) already covers added fields, so by\n * the time this runs every expected column exists. We only surface the\n * non-additive divergences the additive sync can never fix.\n */\nexport function diffManagedTable(args: {\n table: string;\n fields: Record<string, FieldDef>;\n columns: PhysicalColumn[];\n dialect: SqlDialectName;\n}): ManagedDriftEntry[] {\n const { table, fields, columns, dialect } = args;\n const out: ManagedDriftEntry[] = [];\n\n const columnsByName = new Map(columns.map((c) => [c.name, c]));\n // Field name → physical column it should produce. Built only for fields that\n // materialise a column, so orphan detection below treats virtual fields as\n // \"no column expected\".\n const expectedColumns = new Set<string>();\n\n for (const [fieldName, field] of Object.entries(fields ?? {})) {\n if (BUILTIN_COLUMNS.has(fieldName)) continue;\n if (!fieldHasColumn(field)) continue;\n expectedColumns.add(fieldName);\n\n const col = columnsByName.get(fieldName);\n if (!col) continue; // additive sync adds it; not drift\n\n // ── nullability ──────────────────────────────────────────────────\n const expectNullable = field.required !== true;\n if (expectNullable && !col.nullable) {\n out.push({\n kind: 'nullability_mismatch',\n remoteName: table,\n table,\n column: fieldName,\n expected: 'NULL',\n actual: 'NOT NULL',\n severity: 'warning',\n category: 'safe',\n op: { type: 'relax_not_null', table, column: fieldName },\n message:\n `${table}.${fieldName}: metadata is optional but the column is NOT NULL ` +\n `— writes that omit it fail. Run \"os migrate\" to relax it.`,\n });\n } else if (!expectNullable && col.nullable) {\n out.push({\n kind: 'nullability_mismatch',\n remoteName: table,\n table,\n column: fieldName,\n expected: 'NOT NULL',\n actual: 'NULL',\n severity: 'error',\n category: 'destructive',\n op: { type: 'tighten_not_null', table, column: fieldName },\n message:\n `${table}.${fieldName}: metadata is required but the column is nullable ` +\n `— existing nulls must be backfilled. Run \"os migrate apply --allow-destructive\".`,\n });\n }\n\n // ── varchar length (only where the dialect enforces it) ──────────\n if (\n enforcesVarcharLength(dialect) &&\n typeof field.maxLength === 'number' &&\n typeof col.maxLength === 'number' &&\n field.maxLength !== col.maxLength\n ) {\n if (field.maxLength > col.maxLength) {\n out.push({\n kind: 'type_mismatch',\n remoteName: table,\n table,\n column: fieldName,\n expected: `varchar(${field.maxLength})`,\n actual: `varchar(${col.maxLength})`,\n severity: 'warning',\n category: 'safe',\n op: { type: 'widen_varchar', table, column: fieldName, to: field.maxLength, from: col.maxLength },\n message: `${table}.${fieldName}: metadata allows ${field.maxLength} chars but the column caps at ${col.maxLength} — widen via \"os migrate\".`,\n });\n } else {\n out.push({\n kind: 'type_mismatch',\n remoteName: table,\n table,\n column: fieldName,\n expected: `varchar(${field.maxLength})`,\n actual: `varchar(${col.maxLength})`,\n severity: 'error',\n category: 'destructive',\n op: { type: 'narrow_varchar', table, column: fieldName, to: field.maxLength, from: col.maxLength },\n message: `${table}.${fieldName}: metadata caps at ${field.maxLength} chars but the column allows ${col.maxLength} — narrowing may truncate. \"os migrate apply --allow-destructive\".`,\n });\n }\n }\n }\n\n // ── orphaned columns (physical column, no metadata field) ──────────\n for (const col of columns) {\n if (BUILTIN_COLUMNS.has(col.name)) continue;\n if (expectedColumns.has(col.name)) continue;\n out.push({\n kind: 'unmapped_column',\n remoteName: table,\n table,\n column: col.name,\n expected: '(absent)',\n actual: col.type,\n severity: 'warning',\n category: 'destructive',\n op: { type: 'drop_column', table, column: col.name },\n message:\n `${table}.${col.name}: column exists in the database but not in metadata (orphaned) ` +\n `— \"os migrate apply --allow-destructive\" to drop it.`,\n });\n }\n\n return out;\n}\n\n/** Stable de-dup / sort key for a drift entry. */\nexport function driftKey(d: ManagedDriftEntry): string {\n return `${d.table}.${d.column ?? ''}:${d.kind}`;\n}\n"],"mappings":"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","names":["knex","n"]}