@objectstack/driver-sql 7.2.1 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sql-driver.ts","../src/index.ts"],"sourcesContent":["// 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 } from '@objectstack/spec/data';\nimport type { IDataDriver } from '@objectstack/spec/contracts';\nimport { StorageNameMapping } from '@objectstack/spec/system';\nimport knex, { Knex } from 'knex';\nimport { nanoid } from 'nanoid';\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// ── 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 */\nexport type SqlDriverConfig = Knex.Config;\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\n // Schema Management\n schemaSync: true,\n batchSchemaSync: false,\n migrations: false,\n indexes: false,\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 dateFields: Record<string, Set<string>> = {};\n protected datetimeFields: Record<string, Set<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; prefix: string; padWidth: number; tenantField: string | null }>\n > = {};\n\n /** Whether the sequences table has been ensured this process. */\n protected sequencesTableReady = 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 constructor(config: SqlDriverConfig) {\n this.config = config;\n this.knex = knex(config);\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\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 const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\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 // WHERE\n if (query.where) {\n this.applyFilters(builder, 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 builder.orderBy(this.mapSortField(item.field), item.order || 'asc');\n }\n }\n }\n\n // PAGINATION\n if (query.offset !== undefined) builder.offset(query.offset);\n if (query.limit !== undefined) builder.limit(query.limit);\n\n let results: any[];\n try {\n results = await builder;\n } catch (error: any) {\n if (\n error.message &&\n (error.message.includes('no such column') ||\n (error.message.includes('column') && error.message.includes('does not exist')))\n ) {\n return [];\n }\n throw error;\n }\n\n if (!Array.isArray(results)) {\n return [];\n }\n\n if (this.isSqlite) {\n for (const row of results) {\n this.formatOutput(object, row);\n }\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.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 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.knex.schema.createTable(SEQUENCES_TABLE, (t) => {\n t.string('object').notNullable();\n t.string('tenant_id').notNullable();\n t.string('field').notNullable();\n t.bigInteger('last_value').notNullable().defaultTo(0);\n t.timestamp('updated_at').defaultTo(this.knex.fn.now());\n t.primary(['object', 'tenant_id', 'field']);\n });\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 }\n }\n this.sequencesTableReady = true;\n })();\n try {\n await this.sequencesTableEnsurePromise;\n } finally {\n this.sequencesTableEnsurePromise = null;\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 ): Promise<number> {\n await this.ensureSequencesTable();\n const resolvedTenantId = tenantField && tenantId ? String(tenantId) : GLOBAL_TENANT;\n const key = { object: tableName, tenant_id: resolvedTenantId, field };\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({ ...key, 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 on the object that the caller did not\n * provide a value for, reserve the next sequence value scoped to the\n * record's tenant (or globally if the object has no tenant field) and\n * render `prefix + zero-padded(value)`.\n */\n protected async fillAutoNumberFields(\n object: string,\n row: Record<string, any>,\n options?: DriverOptions,\n ): Promise<void> {\n const tableName = StorageNameMapping.resolveTableName({ name: object } as any);\n const cfgs = this.autoNumberFields[tableName] || this.autoNumberFields[object];\n if (!cfgs || cfgs.length === 0) return;\n const parentTrx = options?.transaction as Knex.Transaction | undefined;\n for (const cfg of cfgs) {\n if (row[cfg.name] !== undefined && row[cfg.name] !== null && row[cfg.name] !== '') continue;\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 const next = await this.getNextSequenceValue(\n object,\n tableName,\n cfg.name,\n cfg.prefix,\n cfg.tenantField,\n tenantId,\n parentTrx,\n );\n row[cfg.name] = `${cfg.prefix}${String(next).padStart(cfg.padWidth, '0')}`;\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.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.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') this.injectTenantOnInsert(object, row, options);\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 await this.knex.schema.dropTableIfExists(object);\n }\n\n /**\n * Batch-initialise tables from an array of object definitions.\n */\n async initObjects(objects: Array<{ name: string; fields?: Record<string, any> }>): Promise<void> {\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 autoNumberCols: Array<{ name: string; format: string; prefix: string; padWidth: number; 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 if (type === 'boolean') {\n booleanCols.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 const fmt = typeof field.format === 'string' && field.format\n ? field.format\n : '{0000}';\n const m = fmt.match(/\\{(0+)\\}/);\n const padWidth = m ? m[1].length : 4;\n const prefix = m ? fmt.slice(0, m.index ?? 0) : fmt;\n autoNumberCols.push({ name, format: fmt, prefix, padWidth, tenantField });\n }\n }\n }\n this.jsonFields[tableName] = jsonCols;\n this.booleanFields[tableName] = booleanCols;\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 }\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 let builder = this.knex(object);\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 * 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 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 * 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 const ms = toMs(value);\n return ms == null ? value : ms;\n }\n\n // Field.date — normalise to YYYY-MM-DD.\n if (value instanceof Date) {\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 protected applyFilters(builder: Knex.QueryBuilder, filters: any) {\n if (!filters) return;\n const table = this.tableNameForBuilder(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(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 field = this.mapSortField(fieldRaw);\n const coerced = this.coerceFilterValue(table, field, 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 b[method](field, 'like', `%${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 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.tableNameForBuilder(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 field = this.mapSortField(key);\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, field, 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 (builder as any)[method](field, 'like', `%${opValue}%`);\n break;\n default:\n (builder as any)[method](field, coerced);\n }\n }\n } else {\n const field = this.mapSortField(key);\n const method = logicalOp === 'or' ? 'orWhere' : 'where';\n (builder as any)[method](field, this.coerceFilterValue(table, field, 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 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 col = table.float(name);\n break;\n case 'boolean':\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 'json':\n case 'object':\n case 'array':\n case 'image':\n case 'file':\n case 'avatar':\n case 'location':\n col = table.json(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 col = 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', 'object', 'array', 'image', 'file', 'avatar', 'location'].includes(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 if (!this.isSqlite) return copy;\n\n const fields = this.jsonFields[object];\n if (!fields || fields.length === 0) return copy;\n\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 return copy;\n }\n\n protected formatOutput(object: string, data: any): any {\n if (!data) return data;\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\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\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"],"mappings":"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","names":["n"]}
1
+ {"version":3,"sources":["../src/sql-driver.ts","../src/index.ts"],"sourcesContent":["// 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 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// ── 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\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 dateFields: Record<string, Set<string>> = {};\n protected datetimeFields: Record<string, Set<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; prefix: string; padWidth: number; tenantField: string | null }>\n > = {};\n\n /** Whether the sequences table has been ensured this process. */\n protected sequencesTableReady = 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\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 const builder = this.getBuilder(object, options);\n this.applyTenantScope(builder, object, options);\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 // WHERE\n if (query.where) {\n this.applyFilters(builder, 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 builder.orderBy(this.mapSortField(item.field), item.order || 'asc');\n }\n }\n }\n\n // PAGINATION\n if (query.offset !== undefined) builder.offset(query.offset);\n if (query.limit !== undefined) builder.limit(query.limit);\n\n let results: any[];\n try {\n results = await builder;\n } catch (error: any) {\n if (\n error.message &&\n (error.message.includes('no such column') ||\n (error.message.includes('column') && error.message.includes('does not exist')))\n ) {\n return [];\n }\n throw error;\n }\n\n if (!Array.isArray(results)) {\n return [];\n }\n\n if (this.isSqlite) {\n for (const row of results) {\n this.formatOutput(object, row);\n }\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.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 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.knex.schema.createTable(SEQUENCES_TABLE, (t) => {\n t.string('object').notNullable();\n t.string('tenant_id').notNullable();\n t.string('field').notNullable();\n t.bigInteger('last_value').notNullable().defaultTo(0);\n t.timestamp('updated_at').defaultTo(this.knex.fn.now());\n t.primary(['object', 'tenant_id', 'field']);\n });\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 }\n }\n this.sequencesTableReady = true;\n })();\n try {\n await this.sequencesTableEnsurePromise;\n } finally {\n this.sequencesTableEnsurePromise = null;\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 ): Promise<number> {\n await this.ensureSequencesTable();\n const resolvedTenantId = tenantField && tenantId ? String(tenantId) : GLOBAL_TENANT;\n const key = { object: tableName, tenant_id: resolvedTenantId, field };\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({ ...key, 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 on the object that the caller did not\n * provide a value for, reserve the next sequence value scoped to the\n * record's tenant (or globally if the object has no tenant field) and\n * render `prefix + zero-padded(value)`.\n */\n protected async fillAutoNumberFields(\n object: string,\n row: Record<string, any>,\n options?: DriverOptions,\n ): Promise<void> {\n const tableName = StorageNameMapping.resolveTableName({ name: object } as any);\n const cfgs = this.autoNumberFields[tableName] || this.autoNumberFields[object];\n if (!cfgs || cfgs.length === 0) return;\n const parentTrx = options?.transaction as Knex.Transaction | undefined;\n for (const cfg of cfgs) {\n if (row[cfg.name] !== undefined && row[cfg.name] !== null && row[cfg.name] !== '') continue;\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 const next = await this.getNextSequenceValue(\n object,\n tableName,\n cfg.name,\n cfg.prefix,\n cfg.tenantField,\n tenantId,\n parentTrx,\n );\n row[cfg.name] = `${cfg.prefix}${String(next).padStart(cfg.padWidth, '0')}`;\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.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.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') this.injectTenantOnInsert(object, row, options);\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 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 autoNumberCols: Array<{ name: string; format: string; prefix: string; padWidth: number; 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 if (type === 'boolean') {\n booleanCols.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 const fmt = typeof field.format === 'string' && field.format\n ? field.format\n : '{0000}';\n const m = fmt.match(/\\{(0+)\\}/);\n const padWidth = m ? m[1].length : 4;\n const prefix = m ? fmt.slice(0, m.index ?? 0) : fmt;\n autoNumberCols.push({ name, format: fmt, prefix, padWidth, tenantField });\n }\n }\n }\n this.jsonFields[tableName] = jsonCols;\n this.booleanFields[tableName] = booleanCols;\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 let builder = this.knex(object);\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 * 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 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 * 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 const ms = toMs(value);\n return ms == null ? value : ms;\n }\n\n // Field.date — normalise to YYYY-MM-DD.\n if (value instanceof Date) {\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 protected applyFilters(builder: Knex.QueryBuilder, filters: any) {\n if (!filters) return;\n const table = this.tableNameForBuilder(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(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 field = this.mapSortField(fieldRaw);\n const coerced = this.coerceFilterValue(table, field, 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 b[method](field, 'like', `%${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 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.tableNameForBuilder(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 field = this.mapSortField(key);\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, field, 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 (builder as any)[method](field, 'like', `%${opValue}%`);\n break;\n default:\n (builder as any)[method](field, coerced);\n }\n }\n } else {\n const field = this.mapSortField(key);\n const method = logicalOp === 'or' ? 'orWhere' : 'where';\n (builder as any)[method](field, this.coerceFilterValue(table, field, 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 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 col = table.float(name);\n break;\n case 'boolean':\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 'json':\n case 'object':\n case 'array':\n case 'image':\n case 'file':\n case 'avatar':\n case 'location':\n col = table.json(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 col = 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', 'object', 'array', 'image', 'file', 'avatar', 'location'].includes(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 if (!this.isSqlite) return copy;\n\n const fields = this.jsonFields[object];\n if (!fields || fields.length === 0) return copy;\n\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 return copy;\n }\n\n protected formatOutput(object: string, data: any): any {\n if (!data) return data;\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\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\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"],"mappings":"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","names":["n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/driver-sql",
3
- "version": "7.2.1",
3
+ "version": "7.4.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "SQL Driver for ObjectStack - Supports PostgreSQL, MySQL, SQLite via Knex",
6
6
  "main": "dist/index.js",
@@ -15,8 +15,8 @@
15
15
  "dependencies": {
16
16
  "knex": "^3.2.10",
17
17
  "nanoid": "^5.1.11",
18
- "@objectstack/core": "7.2.1",
19
- "@objectstack/spec": "7.2.1"
18
+ "@objectstack/core": "7.4.0",
19
+ "@objectstack/spec": "7.4.0"
20
20
  },
21
21
  "optionalDependencies": {
22
22
  "better-sqlite3": "^12.10.0"