@datrix/adapter-json 0.1.0 → 0.1.2

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/adapter.ts","../src/runner.ts","../src/lock.ts","../src/transaction.ts","../src/table-utils.ts","../src/export-import/exporter.ts","../src/export-import/importer.ts","../src/populate.ts","../src/query-handlers.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n\tAlterOperation,\n\tConnectionState,\n\tDatabaseAdapter,\n\tQueryResult,\n\tTransaction,\n} from \"@datrix/core\";\nimport { QueryObject } from \"@datrix/core\";\nimport { DatrixEntry, IndexDefinition, SchemaDefinition } from \"@datrix/core\";\nimport { validateQueryObject } from \"@datrix/core\";\nimport {\n\tCacheEntry,\n\tExecuteQueryOptions,\n\tJsonAdapterConfig,\n\tJsonTableFile,\n\tSchemaOperationOptions,\n} from \"./types\";\nimport { JsonQueryRunner } from \"./runner\";\nimport { SimpleLock } from \"./lock\";\nimport {\n\tDatrixAdapterError,\n\tthrowNotConnected,\n\tthrowConnectionError,\n\tthrowMigrationError,\n\tthrowTransactionError,\n\tthrowQueryError,\n\tthrowMetaFieldAlreadyExists,\n\tthrowMetaFieldNotFound,\n} from \"@datrix/core\";\nimport { JsonTransaction } from \"./transaction\";\nimport { FORJA_META_MODEL, FORJA_META_KEY_PREFIX } from \"@datrix/core\";\nimport { createMetaTable, validateTableName } from \"./table-utils\";\nimport { JsonExporter } from \"./export-import/exporter\";\nimport { JsonImporter } from \"./export-import/importer\";\nimport type { ExportWriter, ImportReader } from \"@datrix/core\";\nimport {\n\thandleCount,\n\thandleDelete,\n\thandleInsert,\n\thandleSelect,\n\thandleUpdate,\n} from \"./query-handlers\";\n\n/**\n * JSON File Adapter\n */\nexport class JsonAdapter implements DatabaseAdapter<JsonAdapterConfig> {\n\treadonly name = \"json\";\n\treadonly config: JsonAdapterConfig;\n\tprivate state: ConnectionState = \"disconnected\";\n\tprivate cache = new Map<string, CacheEntry>();\n\tprivate lock: SimpleLock;\n\tprivate cacheEnabled: boolean;\n\tprivate readLockEnabled: boolean;\n\n\t/**\n\t * Active transaction cache reference\n\t * When a transaction is active, all reads/writes go through this cache first.\n\t * Set by beginTransaction, cleared by commit/rollback.\n\t */\n\tprivate activeTransactionCache: Map<string, CacheEntry> | null = null;\n\n\t/**\n\t * Track modified tables during transaction for commit\n\t */\n\tprivate activeTransactionModifiedTables: Set<string> | null = null;\n\n\t/**\n\t * Tombstone set for tables deleted during transaction.\n\t * Prevents fallback to main cache or disk for dropped tables.\n\t */\n\tprivate activeTransactionDeletedTables: Set<string> | null = null;\n\n\tconstructor(config: JsonAdapterConfig) {\n\t\tthis.config = config;\n\t\tthis.lock = new SimpleLock(\n\t\t\tconfig.root,\n\t\t\tconfig.lockTimeout,\n\t\t\tconfig.staleTimeout,\n\t\t);\n\t\tthis.cacheEnabled = config.cache !== false; // default: true\n\t\tthis.readLockEnabled = config.readLock === true; // default: false\n\t}\n\n\t/**\n\t * Connect involves ensuring the root directory exists\n\t */\n\tasync connect(): Promise<void> {\n\t\tif (this.state === \"connected\") {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.state = \"connecting\";\n\n\t\ttry {\n\t\t\tawait fs.mkdir(this.config.root, { recursive: true });\n\t\t\tthis.state = \"connected\";\n\n\t\t\t// Standalone mode: bootstrap _datrix metadata table automatically\n\t\t\tif (this.config.standalone) {\n\t\t\t\ttry {\n\t\t\t\t\tawait createMetaTable(this);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.state = \"error\";\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.state = \"error\";\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrowConnectionError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Failed to access root directory: ${message}`,\n\t\t\t\tcause: error instanceof Error ? error : new Error(String(error)),\n\t\t\t});\n\t\t}\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tthis.state = \"disconnected\";\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.state === \"connected\";\n\t}\n\n\tgetConnectionState(): ConnectionState {\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Helper to get file path for a table\n\t */\n\tprivate getTablePath(tableName: string): string {\n\t\treturn path.join(this.config.root, `${tableName}.json`);\n\t}\n\n\t/**\n\t * Read table with cache support\n\t *\n\t * Cache lookup order:\n\t * 1. Check tombstone (if transaction active and table was dropped)\n\t * 2. Transaction cache (if active)\n\t * 3. Main cache (with mtime validation)\n\t * 4. Disk\n\t *\n\t * When transaction is active, new reads are cached in transaction cache.\n\t * This ensures isolation - transaction sees its own writes.\n\t */\n\tprivate async readTable(tableName: string): Promise<JsonTableFile> {\n\t\tconst filePath = this.getTablePath(tableName);\n\n\t\t// 1. Check tombstone first - table was dropped in this transaction\n\t\tif (this.activeTransactionDeletedTables?.has(tableName)) {\n\t\t\tthrow new Error(`Table '${tableName}' does not exist`);\n\t\t}\n\n\t\t// 2. Check transaction cache (if transaction active)\n\t\tif (this.activeTransactionCache) {\n\t\t\tconst txCached = this.activeTransactionCache.get(tableName);\n\t\t\tif (txCached) {\n\t\t\t\treturn txCached.data;\n\t\t\t}\n\t\t}\n\n\t\t// 2. Check main cache (with mtime validation)\n\t\tif (this.cacheEnabled) {\n\t\t\tconst stat = await fs.stat(filePath);\n\t\t\tconst mtime = stat.mtimeMs;\n\n\t\t\tconst cached = this.cache.get(tableName);\n\t\t\tif (cached && cached.mtime === mtime) {\n\t\t\t\t// If transaction active, copy to tx cache for isolation\n\t\t\t\tif (this.activeTransactionCache) {\n\t\t\t\t\t// Deep copy to prevent mutation of main cache\n\t\t\t\t\tconst txData = JSON.parse(JSON.stringify(cached.data));\n\t\t\t\t\tthis.activeTransactionCache.set(tableName, { data: txData, mtime });\n\t\t\t\t\treturn txData;\n\t\t\t\t}\n\t\t\t\treturn cached.data;\n\t\t\t}\n\n\t\t\t// Cache miss or stale - read from disk\n\t\t\tconst content = await fs.readFile(filePath, \"utf-8\");\n\t\t\tconst data: JsonTableFile = JSON.parse(content);\n\n\t\t\t// Store in appropriate cache\n\t\t\tif (this.activeTransactionCache) {\n\t\t\t\tthis.activeTransactionCache.set(tableName, { data, mtime });\n\t\t\t} else {\n\t\t\t\tthis.cache.set(tableName, { data, mtime });\n\t\t\t}\n\n\t\t\treturn data;\n\t\t}\n\n\t\t// 3. No cache - read from disk\n\t\tconst content = await fs.readFile(filePath, \"utf-8\");\n\t\treturn JSON.parse(content);\n\t}\n\n\t/**\n\t * Get cached table data (for external use like Populate)\n\t */\n\tasync getCachedTable(tableName: string): Promise<JsonTableFile | null> {\n\t\ttry {\n\t\t\treturn await this.readTable(tableName);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get schema directly from table file (cache-aware)\n\t * This is faster than going through Datrix registry and ensures consistency\n\t *\n\t * @param tableName - Table name (e.g., \"users\")\n\t * @returns Schema definition or null if not found\n\t */\n\tasync getSchemaByTableName(\n\t\ttableName: string,\n\t): Promise<SchemaDefinition | null> {\n\t\ttry {\n\t\t\treturn await this.readTableSchema(tableName);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get schema by model name\n\t * Requires scanning all tables to find matching schema.name\n\t * Prefer getSchemaByTableName when table name is known (faster)\n\t *\n\t * @param modelName - Model name from schema (e.g., \"User\")\n\t * @returns Schema definition or null if not found\n\t */\n\tasync getSchemaByModelName(\n\t\tmodelName: string,\n\t): Promise<SchemaDefinition | null> {\n\t\ttry {\n\t\t\tconst tablesResult = await this.getTables();\n\n\t\t\tfor (const tableName of tablesResult) {\n\t\t\t\tconst schema = await this.getSchemaByTableName(tableName);\n\t\t\t\tif (schema?.name === modelName) {\n\t\t\t\t\treturn schema;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Find table name by schema model name\n\t *\n\t * @param modelName - Model name (e.g., \"User\")\n\t * @returns Table name or null if not found\n\t */\n\tasync findTableNameByModelName(modelName: string): Promise<string | null> {\n\t\tconst schema = await this.getSchemaByModelName(modelName);\n\t\treturn schema?.tableName ?? null;\n\t}\n\n\t/**\n\t * Read schema for a table from _datrix metadata table.\n\t * Transaction-aware: reads from tx cache when inside a transaction.\n\t *\n\t * @param tableName - Physical table name (e.g. \"users\")\n\t */\n\tasync readTableSchema(tableName: string): Promise<SchemaDefinition> {\n\t\tconst metaFile = await this.readTable(FORJA_META_MODEL);\n\t\tconst metaKey = `${FORJA_META_KEY_PREFIX}${tableName}`;\n\t\tconst row = metaFile.data.find(\n\t\t\t(r) => (r as Record<string, unknown>)[\"key\"] === metaKey,\n\t\t);\n\t\tif (!row) {\n\t\t\tthrow new Error(`Schema for '${tableName}' not found in _datrix`);\n\t\t}\n\t\treturn JSON.parse(\n\t\t\t(row as Record<string, unknown>)[\"value\"] as string,\n\t\t) as SchemaDefinition;\n\t}\n\n\t/**\n\t * Upsert schema into _datrix metadata table\n\t */\n\tprivate async upsertSchemaMeta(\n\t\tschema: SchemaDefinition,\n\t\tskipWrite: boolean,\n\t): Promise<void> {\n\t\tconst metaKey = `${FORJA_META_KEY_PREFIX}${schema.tableName ?? schema.name}`;\n\t\tconst metaValue = JSON.stringify(schema);\n\t\tconst metaFile = await this.readTable(FORJA_META_MODEL);\n\n\t\tconst existingIndex = metaFile.data.findIndex(\n\t\t\t(r) => (r as Record<string, unknown>)[\"key\"] === metaKey,\n\t\t);\n\n\t\tif (existingIndex >= 0) {\n\t\t\t(metaFile.data[existingIndex] as Record<string, unknown>)[\"value\"] =\n\t\t\t\tmetaValue;\n\t\t} else {\n\t\t\tconst lastInsertId = (metaFile.meta.lastInsertId ?? 0) + 1;\n\t\t\tmetaFile.meta.lastInsertId = lastInsertId;\n\t\t\tmetaFile.data.push({\n\t\t\t\tid: lastInsertId,\n\t\t\t\tkey: metaKey,\n\t\t\t\tvalue: metaValue,\n\t\t\t} as Record<string, unknown>);\n\t\t}\n\n\t\tmetaFile.meta.updatedAt = new Date().toISOString();\n\n\t\tif (skipWrite) {\n\t\t\tthis.activeTransactionCache!.set(FORJA_META_MODEL, {\n\t\t\t\tdata: metaFile,\n\t\t\t\tmtime: Date.now(),\n\t\t\t});\n\t\t\tthis.activeTransactionModifiedTables!.add(FORJA_META_MODEL);\n\t\t} else {\n\t\t\tconst filePath = this.getTablePath(FORJA_META_MODEL);\n\t\t\tawait fs.writeFile(filePath, JSON.stringify(metaFile, null, 2), \"utf-8\");\n\t\t\tawait this.updateCache(FORJA_META_MODEL, metaFile);\n\t\t}\n\t}\n\n\t/**\n\t * Apply AlterOperations to schema in _datrix and write back\n\t */\n\tprivate async applyOperationsToMetaSchema(\n\t\ttableName: string,\n\t\toperations: readonly AlterOperation[],\n\t\tskipWrite: boolean,\n\t): Promise<void> {\n\t\tconst schema = await this.readTableSchema(tableName);\n\t\tconst fields = { ...schema.fields };\n\n\t\tfor (const op of operations) {\n\t\t\tswitch (op.type) {\n\t\t\t\tcase \"addColumn\":\n\t\t\t\t\tfields[op.column] = op.definition;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"dropColumn\":\n\t\t\t\t\tdelete fields[op.column];\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"modifyColumn\":\n\t\t\t\t\tfields[op.column] = op.newDefinition;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"renameColumn\": {\n\t\t\t\t\tconst fieldDef = fields[op.from];\n\t\t\t\t\tif (fieldDef !== undefined) {\n\t\t\t\t\t\tfields[op.to] = fieldDef;\n\t\t\t\t\t\tdelete fields[op.from];\n\t\t\t\t\t}\n\t\t\t\t\t// Update relation fields that reference the renamed column\n\t\t\t\t\tfor (const [key, def] of Object.entries(fields)) {\n\t\t\t\t\t\tif (def.type === \"relation\" && def.foreignKey === op.from) {\n\t\t\t\t\t\t\tfields[key] = { ...def, foreignKey: op.to };\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"addMetaField\":\n\t\t\t\t\tif (fields[op.field] !== undefined) {\n\t\t\t\t\t\tthrowMetaFieldAlreadyExists({\n\t\t\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\t\t\tfield: op.field,\n\t\t\t\t\t\t\ttable: tableName,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tfields[op.field] = op.definition;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"dropMetaField\":\n\t\t\t\t\tif (fields[op.field] === undefined) {\n\t\t\t\t\t\tthrowMetaFieldNotFound({\n\t\t\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\t\t\tfield: op.field,\n\t\t\t\t\t\t\ttable: tableName,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tdelete fields[op.field];\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"modifyMetaField\":\n\t\t\t\t\tif (fields[op.field] === undefined) {\n\t\t\t\t\t\tthrowMetaFieldNotFound({\n\t\t\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\t\t\tfield: op.field,\n\t\t\t\t\t\t\ttable: tableName,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tfields[op.field] = op.newDefinition;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tconst updatedSchema: SchemaDefinition = { ...schema, fields };\n\t\tawait this.upsertSchemaMeta(updatedSchema, skipWrite);\n\t}\n\n\t/**\n\t * Invalidate cache for a specific table\n\t */\n\tprivate invalidateCache(tableName: string): void {\n\t\tthis.cache.delete(tableName);\n\t}\n\n\t/**\n\t * Update cache after write operation\n\t */\n\tprivate async updateCache(\n\t\ttableName: string,\n\t\tdata: JsonTableFile,\n\t): Promise<void> {\n\t\tif (!this.cacheEnabled) return;\n\n\t\tconst filePath = this.getTablePath(tableName);\n\t\ttry {\n\t\t\tconst stat = await fs.stat(filePath);\n\t\t\tthis.cache.set(tableName, { data, mtime: stat.mtimeMs });\n\t\t} catch {\n\t\t\tthis.invalidateCache(tableName);\n\t\t}\n\t}\n\n\tasync exportData(writer: ExportWriter): Promise<void> {\n\t\tawait new JsonExporter(this.config.root, this).export(writer);\n\t}\n\n\tasync importData(reader: ImportReader): Promise<void> {\n\t\tawait new JsonImporter(this.config.root, this).import(reader);\n\t}\n\n\tasync createTable(\n\t\tschema: SchemaDefinition,\n\t\toptions?: {\n\t\t\t/**\n\t\t\t * Set to true when called from the importer.\n\t\t\t * Skips upsertSchemaMeta so the importer can restore _datrix data as-is.\n\t\t\t */\n\t\t\tisImport?: boolean;\n\t\t},\n\t): Promise<void> {\n\t\treturn this.createTableWithOptions(schema, undefined, options?.isImport);\n\t}\n\n\t/**\n\t * Create table with options (for transaction support)\n\t */\n\tasync createTableWithOptions(\n\t\tschema: SchemaDefinition,\n\t\toptions?: SchemaOperationOptions,\n\t\tisImport?: boolean,\n\t): Promise<void> {\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\n\t\t// Standalone mode: ensure id field exists since registry is not present to add it\n\t\tif (this.config.standalone && !(\"id\" in schema.fields)) {\n\t\t\tschema = {\n\t\t\t\t...schema,\n\t\t\t\tfields: {\n\t\t\t\t\tid: { type: \"number\", autoIncrement: true },\n\t\t\t\t\t...schema.fields,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tconst tableName = schema.tableName!;\n\n\t\tvalidateTableName(tableName);\n\n\t\t// Check if table exists in transaction cache first\n\t\tif (this.activeTransactionCache?.has(tableName)) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${schema.name}' already exists`,\n\t\t\t\ttable: tableName,\n\t\t\t});\n\t\t}\n\n\t\t// Check if table was deleted in this transaction - allow recreation\n\t\tconst wasDeleted = this.activeTransactionDeletedTables?.has(tableName);\n\t\tif (wasDeleted) {\n\t\t\t// Remove from tombstone - we're recreating\n\t\t\tthis.activeTransactionDeletedTables!.delete(tableName);\n\t\t}\n\n\t\t// Check disk only if not in transaction or table wasn't deleted\n\t\tif (!wasDeleted) {\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\ttry {\n\t\t\t\tawait fs.access(filePath);\n\t\t\t\tthrowMigrationError({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tmessage: `Table '${schema.name}' already exists`,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof DatrixAdapterError) throw err;\n\t\t\t\t// File does not exist, proceed\n\t\t\t}\n\t\t}\n\n\t\tconst initialContent: JsonTableFile = {\n\t\t\tmeta: {\n\t\t\t\tversion: 1,\n\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\tname: schema.name,\n\t\t\t},\n\t\t\tdata: [],\n\t\t};\n\n\t\tif (skipWrite) {\n\t\t\t// Transaction mode: write to transaction cache\n\t\t\tthis.activeTransactionCache!.set(tableName, {\n\t\t\t\tdata: initialContent,\n\t\t\t\tmtime: Date.now(),\n\t\t\t});\n\t\t\tthis.activeTransactionModifiedTables!.add(tableName);\n\t\t} else {\n\t\t\t// Normal mode: write to disk and update cache\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\tawait fs.writeFile(\n\t\t\t\tfilePath,\n\t\t\t\tJSON.stringify(initialContent, null, 2),\n\t\t\t\t\"utf-8\",\n\t\t\t);\n\t\t\tawait this.updateCache(tableName, initialContent);\n\t\t}\n\n\t\t// Track schema in _datrix (skip during import — _datrix data will be restored as-is)\n\t\tif (!isImport) {\n\t\t\tif (schema.name !== FORJA_META_MODEL) {\n\t\t\t\tconst metaExists = await this.tableExists(FORJA_META_MODEL);\n\t\t\t\tif (!metaExists) {\n\t\t\t\t\tthrowMigrationError({\n\t\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\t\tmessage: `Cannot create table '${schema.name}': '${FORJA_META_MODEL}' table does not exist yet. Create '${FORJA_META_MODEL}' first.`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait this.upsertSchemaMeta(schema, skipWrite);\n\t\t}\n\t}\n\n\tasync dropTable(tableName: string): Promise<void> {\n\t\treturn this.dropTableWithOptions(tableName);\n\t}\n\n\t/**\n\t * Drop table with options (for transaction support)\n\t */\n\tasync dropTableWithOptions(\n\t\ttableName: string,\n\t\toptions?: SchemaOperationOptions,\n\t): Promise<void> {\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\t\tconst isImport = options?.isImport ?? false;\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\t// Check if table was already deleted in this transaction\n\t\tif (this.activeTransactionDeletedTables?.has(tableName)) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${tableName}' does not exist`,\n\t\t\t});\n\t\t}\n\n\t\t// Check if table exists (in tx cache, main cache, or disk)\n\t\tconst existsInTxCache = this.activeTransactionCache?.has(tableName);\n\t\tconst existsInMainCache = this.cache.has(tableName);\n\t\tlet existsOnDisk = false;\n\n\t\tif (!existsInTxCache && !existsInMainCache) {\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\ttry {\n\t\t\t\tawait fs.access(filePath);\n\t\t\t\texistsOnDisk = true;\n\t\t\t} catch {\n\t\t\t\t// Not on disk\n\t\t\t}\n\t\t}\n\n\t\tif (!existsInTxCache && !existsInMainCache && !existsOnDisk) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${tableName}' does not exist`,\n\t\t\t});\n\t\t}\n\n\t\tif (skipWrite) {\n\t\t\t// Transaction mode: add to tombstone, remove from tx cache\n\t\t\tthis.activeTransactionDeletedTables!.add(tableName);\n\t\t\tthis.activeTransactionCache!.delete(tableName);\n\t\t} else {\n\t\t\t// Normal mode: delete from disk\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\tawait fs.unlink(filePath);\n\t\t\tthis.invalidateCache(tableName);\n\t\t}\n\n\t\t// Remove schema from _datrix (skip during import — _datrix data will be restored as-is)\n\t\tif (!isImport && tableName !== FORJA_META_MODEL) {\n\t\t\t// TODO: _datrix table diger tablelar gibi bir table. burada kod tekrari yapmak yerine executeQuery({delete from _datrix where key = metaKey}) gibi bir sey yapilabilir. eger lock problem cikarmiyorsa\n\t\t\tconst metaKey = `${FORJA_META_KEY_PREFIX}${tableName}`;\n\t\t\tconst metaFile = await this.readTable(FORJA_META_MODEL);\n\t\t\tmetaFile.data = metaFile.data.filter(\n\t\t\t\t(r) => (r as Record<string, unknown>)[\"key\"] !== metaKey,\n\t\t\t);\n\t\t\tmetaFile.meta.updatedAt = new Date().toISOString();\n\n\t\t\tif (skipWrite) {\n\t\t\t\tthis.activeTransactionCache!.set(FORJA_META_MODEL, {\n\t\t\t\t\tdata: metaFile,\n\t\t\t\t\tmtime: Date.now(),\n\t\t\t\t});\n\t\t\t\tthis.activeTransactionModifiedTables!.add(FORJA_META_MODEL);\n\t\t\t} else {\n\t\t\t\tconst filePath = this.getTablePath(FORJA_META_MODEL);\n\t\t\t\tawait fs.writeFile(\n\t\t\t\t\tfilePath,\n\t\t\t\t\tJSON.stringify(metaFile, null, 2),\n\t\t\t\t\t\"utf-8\",\n\t\t\t\t);\n\t\t\t\tawait this.updateCache(FORJA_META_MODEL, metaFile);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Execute query (public interface)\n\t *\n\t * This is the standard DatabaseAdapter interface method.\n\t * Internally calls executeQueryWithOptions with default options.\n\t */\n\tasync executeQuery<TResult extends DatrixEntry>(\n\t\tquery: QueryObject<TResult>,\n\t): Promise<QueryResult<TResult>> {\n\t\treturn this.executeQueryWithOptions(query);\n\t}\n\n\t/**\n\t * Execute query with options (for transaction support)\n\t *\n\t * @param query - Query to execute\n\t * @param options - Execution options\n\t * @param options.skipLock - Skip lock acquisition (transaction already holds lock)\n\t * @param options.skipWrite - Skip writing to disk (transaction will write on commit)\n\t */\n\tasync executeQueryWithOptions<TResult extends DatrixEntry>(\n\t\tquery: QueryObject<TResult>,\n\t\toptions?: ExecuteQueryOptions,\n\t): Promise<QueryResult<TResult>> {\n\t\tconst skipLock = options?.skipLock ?? false;\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\n\t\tvalidateQueryObject(query);\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tconst isWriteOp = [\"insert\", \"update\", \"delete\"].includes(query.type);\n\t\tconst needsLock = !skipLock && (isWriteOp || this.readLockEnabled);\n\t\tlet lockAcquired = false;\n\n\t\tif (needsLock) {\n\t\t\ttry {\n\t\t\t\tawait this.lock.acquire();\n\t\t\t\tlockAcquired = true;\n\t\t\t} catch (err) {\n\t\t\t\tthrowQueryError({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tmessage: `Failed to acquire lock: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t\tquery: query as QueryObject,\n\t\t\t\t\tcause: err instanceof Error ? err : new Error(String(err)),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tlet tableData: JsonTableFile<Record<string, unknown>>;\n\n\t\t\ttry {\n\t\t\t\ttableData = await this.readTable(query.table);\n\t\t\t} catch (err) {\n\t\t\t\tif (lockAcquired) await this.lock.release();\n\t\t\t\tthrowQueryError({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tmessage: `Table '${query.table}' not found`,\n\t\t\t\t\tquery: query as QueryObject,\n\t\t\t\t\tcause: err instanceof Error ? err : new Error(String(err)),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Handle missing data field\n\t\t\tif (!tableData!.data || !Array.isArray(tableData!.data)) {\n\t\t\t\ttableData!.data = [];\n\t\t\t}\n\n\t\t\t// Load schema from _datrix for this table (transaction-aware)\n\t\t\tlet tableSchema: SchemaDefinition | undefined;\n\t\t\ttry {\n\t\t\t\ttableSchema = await this.readTableSchema(query.table);\n\t\t\t} catch {\n\t\t\t\t// Schema not found in _datrix — proceed without it\n\t\t\t}\n\n\t\t\tconst runner = new JsonQueryRunner(tableData!, this, tableSchema);\n\n\t\t\tlet handlerResult: Awaited<ReturnType<typeof handleSelect>>;\n\n\t\t\tswitch (query.type) {\n\t\t\t\tcase \"count\":\n\t\t\t\t\thandlerResult = await handleCount({ runner, query });\n\t\t\t\t\tif (handlerResult.earlyReturn) {\n\t\t\t\t\t\tif (lockAcquired) await this.lock.release();\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\trows: [] as TResult[],\n\t\t\t\t\t\t\tmetadata: handlerResult.metadata,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"select\":\n\t\t\t\t\thandlerResult = await handleSelect({ runner, query, adapter: this });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"insert\":\n\t\t\t\t\thandlerResult = await handleInsert({ runner, query });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"update\":\n\t\t\t\t\thandlerResult = await handleUpdate({ runner, query });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"delete\":\n\t\t\t\t\thandlerResult = await handleDelete({\n\t\t\t\t\t\trunner,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tadapter: this,\n\t\t\t\t\t\tqueryOptions: { skipLock: true, skipWrite },\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst rows = handlerResult!.rows as TResult[];\n\t\t\tconst metadata = handlerResult!.metadata;\n\t\t\tconst shouldWrite = handlerResult!.shouldWrite;\n\n\t\t\t// Handle write\n\t\t\tif (shouldWrite) {\n\t\t\t\tif (skipWrite) {\n\t\t\t\t\t// Transaction mode: track modified table, don't write to disk\n\t\t\t\t\tif (this.activeTransactionModifiedTables) {\n\t\t\t\t\t\tthis.activeTransactionModifiedTables.add(query.table);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Normal mode: write to disk immediately\n\t\t\t\t\ttableData!.meta.updatedAt = new Date().toISOString();\n\t\t\t\t\tconst filePath = this.getTablePath(query.table);\n\t\t\t\t\tawait fs.writeFile(\n\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\tJSON.stringify(tableData, null, 2),\n\t\t\t\t\t\t\"utf-8\",\n\t\t\t\t\t);\n\t\t\t\t\tawait this.updateCache(query.table, tableData!);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmetadata.rowCount = rows.length;\n\n\t\t\tif (lockAcquired) await this.lock.release();\n\n\t\t\treturn {\n\t\t\t\trows: rows as TResult[],\n\t\t\t\tmetadata,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (lockAcquired) await this.lock.release();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync executeRawQuery<TResult extends DatrixEntry>(\n\t\t_sql: string,\n\t\t_params: readonly unknown[],\n\t): Promise<QueryResult<TResult>> {\n\t\tthrowQueryError({\n\t\t\tadapter: \"json\",\n\t\t\tmessage: \"executeRawQuery is not supported by JsonAdapter\",\n\t\t});\n\t}\n\n\t/**\n\t * Begin a new transaction\n\t *\n\t * Acquires lock and creates isolated transaction cache.\n\t * All reads/writes within transaction use txCache.\n\t */\n\tasync beginTransaction(): Promise<Transaction> {\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tif (this.activeTransactionCache) {\n\t\t\tthrowTransactionError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: \"A transaction is already active\",\n\t\t\t});\n\t\t}\n\n\t\ttry {\n\t\t\t// Acquire lock for entire transaction duration\n\t\t\tawait this.lock.acquire();\n\n\t\t\t// Initialize transaction state\n\t\t\tthis.activeTransactionCache = new Map<string, CacheEntry>();\n\t\t\tthis.activeTransactionModifiedTables = new Set<string>();\n\t\t\tthis.activeTransactionDeletedTables = new Set<string>();\n\n\t\t\t// Create transaction with commit/rollback callbacks\n\t\t\tconst transaction = new JsonTransaction(\n\t\t\t\tthis,\n\t\t\t\t// Commit callback\n\t\t\t\tasync () => {\n\t\t\t\t\tawait this.commitTransaction();\n\t\t\t\t},\n\t\t\t\t// Rollback callback\n\t\t\t\tasync () => {\n\t\t\t\t\tawait this.rollbackTransaction();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn transaction;\n\t\t} catch (err) {\n\t\t\tthrowTransactionError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Failed to begin transaction: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\tcause: err instanceof Error ? err : new Error(String(err)),\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Commit transaction - write modified tables to disk\n\t * @internal Called by JsonTransaction.commit()\n\t */\n\tprivate async commitTransaction(): Promise<void> {\n\t\tif (!this.activeTransactionCache || !this.activeTransactionModifiedTables) {\n\t\t\tthrowTransactionError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: \"No active transaction to commit\",\n\t\t\t});\n\t\t}\n\n\t\ttry {\n\t\t\t// 1. Delete dropped tables from disk\n\t\t\tif (this.activeTransactionDeletedTables) {\n\t\t\t\tfor (const tableName of this.activeTransactionDeletedTables) {\n\t\t\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait fs.unlink(filePath);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// File might not exist on disk (created and dropped in same tx)\n\t\t\t\t\t}\n\t\t\t\t\t// Remove from main cache\n\t\t\t\t\tthis.cache.delete(tableName);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 2. Write all modified tables to disk\n\t\t\tfor (const tableName of this.activeTransactionModifiedTables) {\n\t\t\t\t// Skip if table was deleted\n\t\t\t\tif (this.activeTransactionDeletedTables?.has(tableName)) continue;\n\n\t\t\t\tconst entry = this.activeTransactionCache.get(tableName);\n\t\t\t\tif (entry) {\n\t\t\t\t\t// Write to disk\n\t\t\t\t\tentry.data.meta.updatedAt = new Date().toISOString();\n\t\t\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\t\t\tawait fs.writeFile(\n\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\tJSON.stringify(entry.data, null, 2),\n\t\t\t\t\t\t\"utf-8\",\n\t\t\t\t\t);\n\n\t\t\t\t\t// Update mtime and merge to main cache\n\t\t\t\t\tconst stat = await fs.stat(filePath);\n\t\t\t\t\tentry.mtime = stat.mtimeMs;\n\t\t\t\t\tthis.cache.set(tableName, entry);\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\t// Clear transaction state and release lock\n\t\t\tthis.activeTransactionCache = null;\n\t\t\tthis.activeTransactionModifiedTables = null;\n\t\t\tthis.activeTransactionDeletedTables = null;\n\t\t\tawait this.lock.release();\n\t\t}\n\t}\n\n\t/**\n\t * Rollback transaction - discard changes\n\t * @internal Called by JsonTransaction.rollback()\n\t */\n\tprivate async rollbackTransaction(): Promise<void> {\n\t\t// Simply discard transaction cache - main cache unchanged\n\t\tthis.activeTransactionCache = null;\n\t\tthis.activeTransactionModifiedTables = null;\n\t\tthis.activeTransactionDeletedTables = null;\n\t\tawait this.lock.release();\n\t}\n\n\tasync alterTable(\n\t\ttableName: string,\n\t\toperations: readonly AlterOperation[],\n\t): Promise<void> {\n\t\treturn this.alterTableWithOptions(tableName, operations);\n\t}\n\n\t/**\n\t * Alter table with options (for transaction support)\n\t */\n\tasync alterTableWithOptions(\n\t\ttableName: string,\n\t\toperations: readonly AlterOperation[],\n\t\toptions?: SchemaOperationOptions,\n\t): Promise<void> {\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tconst json = await this.readTable(tableName);\n\n\t\t// Apply each operation to table data rows\n\t\tfor (const op of operations) {\n\t\t\tswitch (op.type) {\n\t\t\t\tcase \"addColumn\": {\n\t\t\t\t\tconst defaultValue = (op.definition as { default?: unknown }).default;\n\t\t\t\t\tfor (const row of json.data) {\n\t\t\t\t\t\tif (!(op.column in row)) {\n\t\t\t\t\t\t\t(row as Record<string, unknown>)[op.column] =\n\t\t\t\t\t\t\t\tdefaultValue ?? null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"dropColumn\": {\n\t\t\t\t\tfor (const row of json.data) {\n\t\t\t\t\t\tdelete (row as Record<string, unknown>)[op.column];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"modifyColumn\": {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"renameColumn\": {\n\t\t\t\t\tfor (const row of json.data) {\n\t\t\t\t\t\tconst r = row as Record<string, unknown>;\n\t\t\t\t\t\tif (op.from in r) {\n\t\t\t\t\t\t\tr[op.to] = r[op.from];\n\t\t\t\t\t\t\tdelete r[op.from];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tjson.meta.updatedAt = new Date().toISOString();\n\n\t\tif (skipWrite) {\n\t\t\t// Transaction mode: update transaction cache\n\t\t\tthis.activeTransactionCache!.set(tableName, {\n\t\t\t\tdata: json,\n\t\t\t\tmtime: Date.now(),\n\t\t\t});\n\t\t\tthis.activeTransactionModifiedTables!.add(tableName);\n\t\t} else {\n\t\t\t// Normal mode: write to disk\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\tawait fs.writeFile(filePath, JSON.stringify(json, null, 2), \"utf-8\");\n\t\t\tawait this.updateCache(tableName, json);\n\t\t}\n\n\t\t// Update schema in _datrix\n\t\tif (tableName !== FORJA_META_MODEL) {\n\t\t\tawait this.applyOperationsToMetaSchema(tableName, operations, skipWrite);\n\t\t}\n\t}\n\n\tasync renameTable(from: string, to: string): Promise<void> {\n\t\treturn this.renameTableWithOptions(from, to);\n\t}\n\n\t/**\n\t * Rename table with options (for transaction support)\n\t */\n\tasync renameTableWithOptions(\n\t\tfrom: string,\n\t\tto: string,\n\t\toptions?: SchemaOperationOptions,\n\t): Promise<void> {\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tvalidateTableName(to);\n\n\t\t// Check source table exists\n\t\tif (this.activeTransactionDeletedTables?.has(from)) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${from}' does not exist`,\n\t\t\t});\n\t\t}\n\n\t\t// Check target table doesn't exist\n\t\tconst targetExistsInTxCache = this.activeTransactionCache?.has(to);\n\t\tconst targetExistsInMainCache = this.cache.has(to);\n\t\tlet targetExistsOnDisk = false;\n\n\t\tif (!targetExistsInTxCache && !targetExistsInMainCache) {\n\t\t\tconst toPath = this.getTablePath(to);\n\t\t\ttry {\n\t\t\t\tawait fs.access(toPath);\n\t\t\t\ttargetExistsOnDisk = true;\n\t\t\t} catch {\n\t\t\t\t// Not on disk - good\n\t\t\t}\n\t\t}\n\n\t\t// Target exists and not in tombstone = error\n\t\tconst targetInTombstone = this.activeTransactionDeletedTables?.has(to);\n\t\tif (\n\t\t\t(targetExistsInTxCache ||\n\t\t\t\ttargetExistsInMainCache ||\n\t\t\t\ttargetExistsOnDisk) &&\n\t\t\t!targetInTombstone\n\t\t) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${to}' already exists`,\n\t\t\t});\n\t\t}\n\n\t\t// Read source table (will throw if doesn't exist)\n\t\tconst json = await this.readTable(from);\n\t\tjson.meta.updatedAt = new Date().toISOString();\n\n\t\tif (skipWrite) {\n\t\t\t// Transaction mode: add new table to cache, tombstone old table\n\t\t\tthis.activeTransactionCache!.set(to, {\n\t\t\t\tdata: json,\n\t\t\t\tmtime: Date.now(),\n\t\t\t});\n\t\t\tthis.activeTransactionModifiedTables!.add(to);\n\t\t\tthis.activeTransactionDeletedTables!.add(from);\n\t\t\tthis.activeTransactionCache!.delete(from);\n\t\t\t// Remove target from tombstone if it was there (we're overwriting)\n\t\t\tthis.activeTransactionDeletedTables!.delete(to);\n\t\t} else {\n\t\t\t// Normal mode: rename file on disk\n\t\t\tconst fromPath = this.getTablePath(from);\n\t\t\tconst toPath = this.getTablePath(to);\n\t\t\tawait fs.writeFile(toPath, JSON.stringify(json, null, 2), \"utf-8\");\n\t\t\tawait fs.unlink(fromPath);\n\t\t\tthis.invalidateCache(from);\n\t\t\tawait this.updateCache(to, json);\n\t\t}\n\n\t\t// Update key in _datrix\n\t\tif (from !== FORJA_META_MODEL && to !== FORJA_META_MODEL) {\n\t\t\tconst oldKey = `${FORJA_META_KEY_PREFIX}${from}`;\n\t\t\tconst newKey = `${FORJA_META_KEY_PREFIX}${to}`;\n\t\t\tconst metaFile = await this.readTable(FORJA_META_MODEL);\n\t\t\tconst row = metaFile.data.find(\n\t\t\t\t(r) => (r as Record<string, unknown>)[\"key\"] === oldKey,\n\t\t\t);\n\t\t\tif (row) {\n\t\t\t\t(row as Record<string, unknown>)[\"key\"] = newKey;\n\t\t\t\tmetaFile.meta.updatedAt = new Date().toISOString();\n\n\t\t\t\tif (skipWrite) {\n\t\t\t\t\tthis.activeTransactionCache!.set(FORJA_META_MODEL, {\n\t\t\t\t\t\tdata: metaFile,\n\t\t\t\t\t\tmtime: Date.now(),\n\t\t\t\t\t});\n\t\t\t\t\tthis.activeTransactionModifiedTables!.add(FORJA_META_MODEL);\n\t\t\t\t} else {\n\t\t\t\t\tconst metaPath = this.getTablePath(FORJA_META_MODEL);\n\t\t\t\t\tawait fs.writeFile(\n\t\t\t\t\t\tmetaPath,\n\t\t\t\t\t\tJSON.stringify(metaFile, null, 2),\n\t\t\t\t\t\t\"utf-8\",\n\t\t\t\t\t);\n\t\t\t\t\tawait this.updateCache(FORJA_META_MODEL, metaFile);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tasync addIndex(tableName: string, index: IndexDefinition): Promise<void> {\n\t\treturn this.addIndexWithOptions(tableName, index);\n\t}\n\n\t/**\n\t * Add index with options (for transaction support)\n\t * Note: JSON adapter doesn't actually create indexes, but we track the operation\n\t */\n\tasync addIndexWithOptions(\n\t\t_tableName: string,\n\t\t_index: IndexDefinition,\n\t\t_options?: SchemaOperationOptions,\n\t): Promise<void> { }\n\n\tasync dropIndex(tableName: string, indexName: string): Promise<void> {\n\t\treturn this.dropIndexWithOptions(tableName, indexName);\n\t}\n\n\t/**\n\t * Drop index with options (for transaction support)\n\t * Note: JSON adapter doesn't actually manage indexes, but we track the operation\n\t */\n\tasync dropIndexWithOptions(\n\t\t_tableName: string,\n\t\t_indexName: string,\n\t\t_options?: SchemaOperationOptions,\n\t): Promise<void> { }\n\n\tasync getTables(): Promise<readonly string[]> {\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\t\tconst files = await fs.readdir(this.config.root);\n\t\tconst tables = files\n\t\t\t.filter((f) => f.endsWith(\".json\"))\n\t\t\t.map((f) => f.replace(\".json\", \"\"));\n\t\treturn tables;\n\t}\n\n\tasync getTableSchema(tableName: string): Promise<SchemaDefinition | null> {\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\t\ttry {\n\t\t\tconst schema = await this.readTableSchema(tableName);\n\t\t\treturn schema;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync tableExists(tableName: string): Promise<boolean> {\n\t\tif (!this.isConnected()) return false;\n\t\ttry {\n\t\t\tawait fs.access(this.getTablePath(tableName));\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","import {\n\tWhereClause,\n\tFallbackOrderByItem,\n\tComparisonOperators,\n\tQuerySelectObject,\n\tQuerySelect,\n\tQueryCountObject,\n} from \"@datrix/core\";\nimport {\n\tDatrixEntry,\n\tDatrixRecord,\n\tRelationField,\n\tSchemaDefinition,\n} from \"@datrix/core\";\nimport { JsonTableFile } from \"./types\";\nimport type { JsonAdapter } from \"./adapter\";\nimport {\n\tthrowInvalidRelationWhereSyntax,\n\tthrowInvalidWhereField,\n} from \"@datrix/core\";\n\nexport class JsonQueryRunner {\n\tprivate schema: SchemaDefinition | undefined;\n\n\tconstructor(\n\t\tprivate table: JsonTableFile,\n\t\tprivate adapter: JsonAdapter,\n\t\tschema?: SchemaDefinition,\n\t) {\n\t\tthis.schema = schema;\n\t}\n\n\tget tableData(): JsonTableFile {\n\t\treturn this.table;\n\t}\n\n\tget tableSchema(): SchemaDefinition | undefined {\n\t\treturn this.schema;\n\t}\n\n\tget adapterRef(): JsonAdapter {\n\t\treturn this.adapter;\n\t}\n\n\tasync run<T extends DatrixEntry = DatrixRecord>(\n\t\tquery: QuerySelectObject<T> | QueryCountObject<T>,\n\t): Promise<Partial<T>[]> {\n\t\tlet result = this.table.data as T[];\n\n\t\t// 1. Filter (async for nested relation WHERE support)\n\t\tif (query.where) {\n\t\t\tconst matchResults = await Promise.all(\n\t\t\t\tresult.map((item) => this.match(item, query.where!)),\n\t\t\t);\n\t\t\tresult = result.filter((_, i) => matchResults[i]);\n\t\t} else if (\n\t\t\tquery.type === \"select\" &&\n\t\t\tquery.orderBy &&\n\t\t\tquery.orderBy.length > 0\n\t\t) {\n\t\t\t// No filter but need sort - must copy to avoid mutating original\n\t\t\tresult = [...result];\n\t\t}\n\n\t\tif (query.type === \"count\") {\n\t\t\treturn result;\n\t\t}\n\n\t\t// 3. Project & Distinct\n\t\tif (query.select || query.distinct) {\n\t\t\tresult = this.project(result, query.select, query.distinct) as T[];\n\t\t}\n\n\t\t// 4. Sort (mutates array in-place)\n\t\tif (query.orderBy && query.orderBy.length > 0) {\n\t\t\tresult.sort((a, b) =>\n\t\t\t\tthis.sort(\n\t\t\t\t\ta as Record<string, unknown>,\n\t\t\t\t\tb as Record<string, unknown>,\n\t\t\t\t\tquery.orderBy!,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// 5. Offset/Limit\n\t\tconst offset = query.offset ?? 0;\n\n\t\tif (query.limit !== undefined) {\n\t\t\tresult = result.slice(offset, offset + query.limit);\n\t\t} else if (offset > 0) {\n\t\t\tresult = result.slice(offset);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Run query without projection (for populate workflow)\n\t * Applies WHERE, ORDER BY, OFFSET, LIMIT but keeps all fields\n\t */\n\tasync filterAndSort<T extends DatrixEntry>(\n\t\tquery: QuerySelectObject<T>,\n\t): Promise<T[]> {\n\t\tlet result = this.table.data as T[];\n\n\t\t// 1. Filter (async for nested relation WHERE support)\n\t\tif (query.where) {\n\t\t\tconst matchResults = await Promise.all(\n\t\t\t\tresult.map((item) => this.match(item, query.where!)),\n\t\t\t);\n\t\t\tresult = result.filter((_, i) => matchResults[i]);\n\t\t} else if (query.orderBy && query.orderBy.length > 0) {\n\t\t\t// No filter but need sort - must copy to avoid mutating original\n\t\t\tresult = [...result];\n\t\t}\n\n\t\t// 2. Sort (mutates array in-place)\n\t\tif (query.orderBy && query.orderBy.length > 0) {\n\t\t\tresult.sort((a, b) =>\n\t\t\t\tthis.sort(\n\t\t\t\t\ta as Record<string, unknown>,\n\t\t\t\t\tb as Record<string, unknown>,\n\t\t\t\t\tquery.orderBy!,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// 3. Offset/Limit\n\t\tconst offset = query.offset ?? 0;\n\n\t\tif (query.limit !== undefined) {\n\t\t\tresult = result.slice(offset, offset + query.limit);\n\t\t} else if (offset > 0) {\n\t\t\tresult = result.slice(offset);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t// Exposed for Adapter's RETURNING clause usage\n\tpublic projectData<T extends DatrixEntry>(\n\t\tdata: T[],\n\t\tselect?: QuerySelect<T>,\n\t\tdistinct?: boolean,\n\t): Partial<T>[] {\n\t\treturn this.project(data, select, distinct);\n\t}\n\n\tprivate project<T extends DatrixEntry>(\n\t\tdata: T[],\n\t\tselect?: QuerySelect<T>,\n\t\tdistinct?: boolean,\n\t): Partial<T>[] {\n\t\tlet result: any[] = data;\n\n\t\t// Projection\n\t\tif (select && (select as unknown as string) !== \"*\") {\n\t\t\tresult = data.map((item) => {\n\t\t\t\tconst projected: any = {};\n\t\t\t\tfor (const field of select) {\n\t\t\t\t\tprojected[field] = item[field as keyof T];\n\t\t\t\t\tif (projected[field] === undefined) {\n\t\t\t\t\t\tprojected[field] = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn projected;\n\t\t\t});\n\t\t}\n\n\t\t// Distinct\n\t\tif (distinct) {\n\t\t\tconst seen = new Set<string>();\n\t\t\tresult = result.filter((item) => {\n\t\t\t\tconst key = JSON.stringify(item); // Simple serialization for distinct check\n\t\t\t\tif (seen.has(key)) return false;\n\t\t\t\tseen.add(key);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Match WHERE clause against an item\n\t *\n\t * **NEW:** Now supports nested relation WHERE queries!\n\t *\n\t * @param item - The record to match against\n\t * @param where - WHERE clause (may contain nested relation conditions)\n\t * @param overrideSchema - Optional schema to use instead of this.table.schema (for nested relation matching)\n\t * @returns True if item matches all conditions\n\t *\n\t * @example\n\t * ```ts\n\t * // Simple WHERE\n\t * await match(item, { price: { $gt: 10 } })\n\t *\n\t * // Nested relation WHERE\n\t * await match(item, {\n\t * author: { // Relation field\n\t * verified: { $eq: true }\n\t * }\n\t * })\n\t * ```\n\t */\n\tprivate async match<T extends DatrixEntry>(\n\t\titem: any,\n\t\twhere: WhereClause<T>,\n\t\toverrideSchema?: SchemaDefinition,\n\t): Promise<boolean> {\n\t\tconst schema = overrideSchema ?? this.schema;\n\n\t\tfor (const [key, value] of Object.entries(where)) {\n\t\t\t// Handle logical operators\n\t\t\tif (key === \"$and\") {\n\t\t\t\tconst results = await Promise.all(\n\t\t\t\t\t(value as WhereClause<T>[]).map((cond) =>\n\t\t\t\t\t\tthis.match(item, cond, schema),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (!results.every((r) => r)) return false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (key === \"$or\") {\n\t\t\t\tconst results = await Promise.all(\n\t\t\t\t\t(value as WhereClause<T>[]).map((cond) =>\n\t\t\t\t\t\tthis.match(item, cond, schema),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (!results.some((r) => r)) return false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (key === \"$not\") {\n\t\t\t\tif (await this.match(item, value as WhereClause<T>, schema))\n\t\t\t\t\treturn false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// ✨ NEW: Check if this field is a RELATION\n\t\t\tconst fieldDef = schema?.fields?.[key];\n\t\t\tif (fieldDef?.type === \"relation\") {\n\t\t\t\t// This is a nested relation WHERE!\n\t\t\t\tconst matched = await this.matchRelation(\n\t\t\t\t\titem,\n\t\t\t\t\tkey,\n\t\t\t\t\tvalue as WhereClause<T>,\n\t\t\t\t\tfieldDef,\n\t\t\t\t);\n\t\t\t\tif (!matched) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Regular field matching (existing logic)\n\t\t\t// Validate that field exists in schema (catch typos and invalid fields)\n\t\t\tif (schema && !schema.fields[key]) {\n\t\t\t\tthrowInvalidWhereField({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tfield: key,\n\t\t\t\t\tschemaName: schema.name,\n\t\t\t\t\tavailableFields: Object.keys(schema.fields),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst itemValue = item[key];\n\n\t\t\tif (value === null) {\n\t\t\t\tif (itemValue !== null && itemValue !== undefined) return false;\n\t\t\t} else if (\n\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\t!Array.isArray(value) &&\n\t\t\t\t!(value instanceof Date)\n\t\t\t) {\n\t\t\t\t// Check if this is a ComparisonOperators object or nested WHERE\n\t\t\t\t// If it has operator keys ($eq, $gt, etc.), it's operators\n\t\t\t\tconst isOperators = Object.keys(value).some((k) => k.startsWith(\"$\"));\n\t\t\t\tif (isOperators) {\n\t\t\t\t\t// Operators\n\t\t\t\t\tif (\n\t\t\t\t\t\t!this.matchOperators(itemValue, value as ComparisonOperators, key)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\t// Not operators and not a relation - treat as direct equality\n\t\t\t\t\tif (!this.compareValues(itemValue, value, key)) return false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Direct equality - type-aware comparison\n\t\t\t\tif (!this.compareValues(itemValue, value, key)) return false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Match nested relation WHERE\n\t *\n\t * Loads the related record(s) and recursively matches the nested WHERE clause.\n\t *\n\t * @param item - Current record\n\t * @param relationName - Name of the relation field\n\t * @param relationWhere - Nested WHERE clause for the relation\n\t * @param relationField - Relation field definition\n\t * @returns True if relation matches\n\t */\n\tprivate async matchRelation<T extends DatrixEntry>(\n\t\titem: any,\n\t\trelationName: string,\n\t\trelationWhere: WhereClause<T>,\n\t\trelationField: RelationField,\n\t): Promise<boolean> {\n\t\tconst foreignKey = relationField.foreignKey!;\n\t\tconst targetModelName = relationField.model;\n\t\tconst kind = relationField.kind;\n\n\t\t// Validate: Ensure relationWhere is not using comparison operators directly\n\t\t// Valid: { user: { id: { $eq: 1 } } }\n\t\t// Valid: { user: { $and: [{ id: { $eq: 1 } }] } } (logical operator)\n\t\t// Invalid: { user: { $eq: 1 } } (comparison operator)\n\t\tif (typeof relationWhere === \"object\" && relationWhere !== null) {\n\t\t\tconst keys = Object.keys(relationWhere);\n\t\t\tconst logicalOps = new Set([\"$and\", \"$or\", \"$not\"]);\n\t\t\tconst hasOnlyComparisonOperators =\n\t\t\t\tkeys.length > 0 &&\n\t\t\t\tkeys.every((k) => k.startsWith(\"$\")) &&\n\t\t\t\t!keys.some((k) => logicalOps.has(k));\n\n\t\t\tif (hasOnlyComparisonOperators) {\n\t\t\t\tthrowInvalidRelationWhereSyntax({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\trelationName,\n\t\t\t\t\tschemaName: this.schema?.name ?? \"unknown\",\n\t\t\t\t\tforeignKey,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Get related ID(s) from current record\n\t\tif (kind === \"belongsTo\" || kind === \"hasOne\") {\n\t\t\t// Single relation - check FK value\n\t\t\tconst relatedId = item[foreignKey];\n\t\t\tif (relatedId === null || relatedId === undefined) {\n\t\t\t\t// No relation - doesn't match\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Get target schema for validation\n\t\t\tconst targetSchema =\n\t\t\t\tawait this.adapter.getSchemaByModelName(targetModelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Load related record\n\t\t\tconst relatedRecord = await this.loadRelatedRecord(\n\t\t\t\ttargetModelName,\n\t\t\t\trelatedId,\n\t\t\t);\n\t\t\tif (!relatedRecord) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Recursively match nested WHERE on related record with target schema\n\t\t\treturn await this.match(relatedRecord, relationWhere, targetSchema);\n\t\t}\n\n\t\tif (kind === \"hasMany\") {\n\t\t\t// Target table holds the FK pointing back to this record\n\t\t\tconst sourceId = item[\"id\"] as number | string | undefined;\n\t\t\tif (sourceId === null || sourceId === undefined) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst targetSchema =\n\t\t\t\tawait this.adapter.getSchemaByModelName(targetModelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst targetTable =\n\t\t\t\ttargetSchema.tableName ?? targetModelName.toLowerCase();\n\t\t\tconst targetTableData = await this.adapter.getCachedTable(targetTable);\n\t\t\tif (!targetTableData) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Use the foreignKey directly from the relation definition.\n\t\t\t// Schema authors must specify explicit foreignKey when the inverse\n\t\t\t// belongsTo uses a different name (e.g. foreignKey: \"authorId\").\n\t\t\tconst relatedRecords = (\n\t\t\t\ttargetTableData.data as Record<string, unknown>[]\n\t\t\t).filter(\n\t\t\t\t(r) => r[foreignKey] === sourceId || r[foreignKey] === Number(sourceId),\n\t\t\t);\n\n\t\t\tfor (const related of relatedRecords) {\n\t\t\t\tconst matches = await this.match(related, relationWhere, targetSchema);\n\t\t\t\tif (matches) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tif (kind === \"manyToMany\") {\n\t\t\t// Junction table bridges this record and target records\n\t\t\tconst junctionTableName = relationField.through;\n\t\t\tif (!junctionTableName) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst sourceId = item[\"id\"] as number | string | undefined;\n\t\t\tif (sourceId === null || sourceId === undefined) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst junctionTableData =\n\t\t\t\tawait this.adapter.getCachedTable(junctionTableName);\n\t\t\tif (!junctionTableData) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Determine FK column names in junction table (e.g. userId, roleId)\n\t\t\tconst currentModelName = this.schema?.name ?? \"\";\n\t\t\tconst sourceFK = `${currentModelName}Id`;\n\t\t\tconst targetFK = `${targetModelName}Id`;\n\n\t\t\t// Collect target IDs from junction rows matching this source\n\t\t\tconst targetIds = (junctionTableData.data as Record<string, unknown>[])\n\t\t\t\t.filter((row) => {\n\t\t\t\t\tconst rowSourceId = row[sourceFK];\n\t\t\t\t\treturn rowSourceId === sourceId || rowSourceId === Number(sourceId);\n\t\t\t\t})\n\t\t\t\t.map((row) => {\n\t\t\t\t\tconst rawId = row[targetFK];\n\t\t\t\t\treturn typeof rawId === \"string\" ? Number(rawId) : (rawId as number);\n\t\t\t\t})\n\t\t\t\t.filter((id): id is number => id !== null && id !== undefined);\n\n\t\t\tif (targetIds.length === 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst targetSchema =\n\t\t\t\tawait this.adapter.getSchemaByModelName(targetModelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst targetTable =\n\t\t\t\ttargetSchema.tableName ?? targetModelName.toLowerCase();\n\t\t\tconst targetTableData = await this.adapter.getCachedTable(targetTable);\n\t\t\tif (!targetTableData) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Check if any target record matches the nested WHERE\n\t\t\tconst targetRecords = (\n\t\t\t\ttargetTableData.data as Record<string, unknown>[]\n\t\t\t).filter((r) => targetIds.includes(r[\"id\"] as number));\n\n\t\t\tfor (const target of targetRecords) {\n\t\t\t\tconst matches = await this.match(target, relationWhere, targetSchema);\n\t\t\t\tif (matches) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Load a related record from adapter's cache\n\t *\n\t * Uses getCachedTable which reads from cache or disk if needed.\n\t *\n\t * @param modelName - Target model name\n\t * @param id - Record ID to load\n\t * @returns Related record or null\n\t */\n\tprivate async loadRelatedRecord(\n\t\tmodelName: string,\n\t\tid: string | number,\n\t): Promise<Record<string, unknown> | null> {\n\t\ttry {\n\t\t\t// Get target schema from adapter (cache-aware, no Datrix dependency)\n\t\t\tconst targetSchema = await this.adapter.getSchemaByModelName(modelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst targetTable = targetSchema.tableName ?? modelName.toLowerCase();\n\n\t\t\t// Get table data from adapter's cache (async - reads from disk if cache stale)\n\t\t\tconst tableData = await this.adapter.getCachedTable(targetTable);\n\t\t\tif (!tableData) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Find record by ID\n\t\t\tconst relatedData = tableData.data as Record<string, unknown>[];\n\t\t\tconst record = relatedData.find((r) => r[\"id\"] === id);\n\n\t\t\treturn record ?? null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate compareValues(\n\t\titemValue: any,\n\t\tqueryValue: any,\n\t\tfieldName: string,\n\t): boolean {\n\t\tconst schema = this.schema as any;\n\t\tconst fieldDef = schema?.fields?.[fieldName];\n\n\t\t// No schema or field definition - use strict equality\n\t\tif (!fieldDef) {\n\t\t\treturn itemValue === queryValue;\n\t\t}\n\n\t\t// Type coercion based on field type\n\t\tconst fieldType = fieldDef.type;\n\n\t\tif (fieldType === \"number\") {\n\t\t\tconst itemNum = Number(itemValue);\n\t\t\tconst queryNum = Number(queryValue);\n\t\t\treturn !isNaN(itemNum) && !isNaN(queryNum) && itemNum === queryNum;\n\t\t}\n\n\t\tif (fieldType === \"string\") {\n\t\t\treturn String(itemValue) === String(queryValue);\n\t\t}\n\n\t\tif (fieldType === \"boolean\") {\n\t\t\treturn Boolean(itemValue) === Boolean(queryValue);\n\t\t}\n\n\t\t// Default: strict equality\n\t\treturn itemValue === queryValue;\n\t}\n\n\tprivate matchOperators(\n\t\tvalue: any,\n\t\toperators: ComparisonOperators,\n\t\tfieldName: string,\n\t): boolean {\n\t\tfor (const [op, opValue] of Object.entries(operators)) {\n\t\t\tswitch (op) {\n\t\t\t\tcase \"$eq\":\n\t\t\t\t\tif (!this.compareValues(value, opValue, fieldName)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$ne\":\n\t\t\t\t\tif (this.compareValues(value, opValue, fieldName)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$gt\": {\n\t\t\t\t\t// NULL comparisons always return false (SQL behavior)\n\t\t\t\t\tif (value === null || value === undefined) return false;\n\t\t\t\t\tconst coercedVal = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedOp = this.coerceForComparison(opValue, fieldName);\n\t\t\t\t\tif (!((coercedVal as number) > (coercedOp as number))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$gte\": {\n\t\t\t\t\tif (value === null || value === undefined) return false;\n\t\t\t\t\tconst coercedVal = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedOp = this.coerceForComparison(opValue, fieldName);\n\t\t\t\t\tif (!((coercedVal as number) >= (coercedOp as number))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$lt\": {\n\t\t\t\t\tif (value === null || value === undefined) return false;\n\t\t\t\t\tconst coercedVal = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedOp = this.coerceForComparison(opValue, fieldName);\n\t\t\t\t\tif (!((coercedVal as number) < (coercedOp as number))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$lte\": {\n\t\t\t\t\tif (value === null || value === undefined) return false;\n\t\t\t\t\tconst coercedVal = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedOp = this.coerceForComparison(opValue, fieldName);\n\t\t\t\t\tif (!((coercedVal as number) <= (coercedOp as number))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$in\": {\n\t\t\t\t\tconst coercedValue = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedArray = (opValue as unknown[]).map((v) =>\n\t\t\t\t\t\tthis.coerceForComparison(v, fieldName),\n\t\t\t\t\t);\n\t\t\t\t\tif (!coercedArray.includes(coercedValue)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$nin\": {\n\t\t\t\t\tconst coercedValue = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedArray = (opValue as unknown[]).map((v) =>\n\t\t\t\t\t\tthis.coerceForComparison(v, fieldName),\n\t\t\t\t\t);\n\t\t\t\t\tif (coercedArray.includes(coercedValue)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$exists\":\n\t\t\t\t\tif (opValue && (value === undefined || value === null)) return false;\n\t\t\t\t\tif (!opValue && value !== undefined && value !== null) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$null\":\n\t\t\t\t\t// Checks if value is null or undefined (no value)\n\t\t\t\t\tif (opValue && value !== null && value !== undefined) return false;\n\t\t\t\t\tif (!opValue && (value === null || value === undefined)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$like\":\n\t\t\t\tcase \"$ilike\": {\n\t\t\t\t\tconst pattern = (opValue as string)\n\t\t\t\t\t\t.replace(/%/g, \".*\")\n\t\t\t\t\t\t.replace(/_/g, \".\");\n\t\t\t\t\tconst flags = op === \"$ilike\" ? \"i\" : \"\";\n\t\t\t\t\tconst regex = new RegExp(`^${pattern}$`, flags);\n\t\t\t\t\tif (!regex.test(String(value ?? \"\"))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$contains\":\n\t\t\t\t\tif (!String(value ?? \"\").includes(String(opValue))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$notContains\":\n\t\t\t\t\tif (String(value ?? \"\").includes(String(opValue))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$startsWith\":\n\t\t\t\t\tif (!String(value ?? \"\").startsWith(String(opValue))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$endsWith\":\n\t\t\t\t\tif (!String(value ?? \"\").endsWith(String(opValue))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$notNull\":\n\t\t\t\t\t// Checks if value is NOT null/undefined (has value)\n\t\t\t\t\tif (opValue && (value === null || value === undefined)) return false;\n\t\t\t\t\tif (!opValue && value !== null && value !== undefined) return false;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate coerceForComparison(value: unknown, fieldName: string): unknown {\n\t\t// Preserve null/undefined as-is\n\t\tif (value === null || value === undefined) {\n\t\t\treturn value;\n\t\t}\n\n\t\tconst schema = this.schema as {\n\t\t\tfields?: Record<string, { type?: string }>;\n\t\t};\n\t\tconst fieldDef = schema?.fields?.[fieldName];\n\n\t\tif (!fieldDef) return value;\n\n\t\tconst fieldType = fieldDef.type;\n\n\t\tif (fieldType === \"number\") {\n\t\t\tconst num = Number(value);\n\t\t\treturn isNaN(num) ? value : num;\n\t\t}\n\n\t\tif (fieldType === \"string\") {\n\t\t\treturn String(value);\n\t\t}\n\n\t\treturn value;\n\t}\n\n\tprivate sort(\n\t\ta: Record<string, unknown>,\n\t\tb: Record<string, unknown>,\n\t\torderBy: readonly FallbackOrderByItem[],\n\t): number {\n\t\tfor (const order of orderBy) {\n\t\t\tconst fieldName = order.field;\n\t\t\tconst valA = this.coerceForComparison(a[fieldName], fieldName);\n\t\t\tconst valB = this.coerceForComparison(b[fieldName], fieldName);\n\n\t\t\tif (valA === valB) continue;\n\n\t\t\tconst direction = order.direction === \"asc\" ? 1 : -1;\n\n\t\t\tif (valA === null || valA === undefined)\n\t\t\t\treturn order.nulls === \"first\" ? -1 : 1;\n\t\t\tif (valB === null || valB === undefined)\n\t\t\t\treturn order.nulls === \"first\" ? 1 : -1;\n\n\t\t\tif (valA < valB) return -1 * direction;\n\t\t\tif (valA > valB) return 1 * direction;\n\t\t}\n\t\treturn 0;\n\t}\n}\n","import { throwLockTimeout } from \"@datrix/core\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport class SimpleLock {\n\tprivate lockPath: string;\n\tprivate lockTimeout: number; // How long to wait to acquire lock\n\tprivate staleTimeout: number; // How long a lock is valid\n\n\tconstructor(\n\t\troot: string,\n\t\tlockTimeout: number = 5000,\n\t\tstaleTimeout: number = 30000,\n\t) {\n\t\tthis.lockPath = path.join(root, \"db.lock\");\n\t\tthis.lockTimeout = lockTimeout;\n\t\tthis.staleTimeout = staleTimeout;\n\t}\n\n\tasync acquire(): Promise<void> {\n\t\tconst start = Date.now();\n\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\t// Try to create the lock file (fails if exists)\n\t\t\t\t// \"wx\" flag: Open file for writing. The file is created (if it does not exist) or fails (if it exists).\n\t\t\t\tawait fs.writeFile(this.lockPath, Date.now().toString(), {\n\t\t\t\t\tflag: \"wx\",\n\t\t\t\t});\n\t\t\t\treturn; // Acquired!\n\t\t\t} catch (error: any) {\n\t\t\t\tif (error.code !== \"EEXIST\") {\n\t\t\t\t\tthrow error; // Unexpected error\n\t\t\t\t}\n\n\t\t\t\t// Lock exists. Check if stale.\n\t\t\t\tconst isStale = await this.checkStale();\n\t\t\t\tif (isStale) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait fs.unlink(this.lockPath);\n\t\t\t\t\t\tcontinue; // Retry immediately\n\t\t\t\t\t} catch (unlinkError) {\n\t\t\t\t\t\t// Could happen if another process released it just now.\n\t\t\t\t\t\t// Just continue loop to try acquiring again.\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check timeout\n\t\t\t\tif (Date.now() - start > this.lockTimeout) {\n\t\t\t\t\tthrowLockTimeout({ adapter: \"json\", lockTimeout: this.lockTimeout });\n\t\t\t\t}\n\n\t\t\t\t// Wait a bit before retrying\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10)); // 10ms poll\n\t\t\t}\n\t\t}\n\t}\n\n\tasync release(): Promise<void> {\n\t\ttry {\n\t\t\tawait fs.unlink(this.lockPath);\n\t\t} catch (error: any) {\n\t\t\t// Ignore if file doesn't exist (maybe already released or stolen?)\n\t\t\tif (error.code !== \"ENOENT\") {\n\t\t\t\t// warning?\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async checkStale(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst content = await fs.readFile(this.lockPath, \"utf-8\");\n\t\t\tconst timestamp = parseInt(content, 10);\n\t\t\tif (isNaN(timestamp)) return true; // Corrupt lock\n\n\t\t\tconst age = Date.now() - timestamp;\n\t\t\treturn age > this.staleTimeout;\n\t\t} catch {\n\t\t\t// If can't read (e.g. deleted while checking), assume free/stale logic handles it by retry loop\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","/**\n * JSON Adapter Transaction Implementation\n *\n * Provides ACID-like transaction support for JSON file adapter.\n *\n * Strategy:\n * - BEGIN: Adapter acquires lock, creates isolated txCache\n * - QUERY: Adapter's readTable/writeTable automatically use txCache\n * - COMMIT: Adapter writes modified tables to disk, merges to main cache\n * - ROLLBACK: Adapter discards txCache, releases lock\n *\n * The transaction object is a thin wrapper that delegates to adapter.\n */\n\nimport { Transaction, QueryResult, AlterOperation } from \"@datrix/core\";\nimport {\n\tthrowTransactionAlreadyCommitted,\n\tthrowTransactionAlreadyRolledBack,\n\tthrowTransactionSavepointNotSupported,\n\tthrowRawQueryNotSupported,\n} from \"@datrix/core\";\nimport { QueryObject } from \"@datrix/core\";\nimport { DatrixEntry, IndexDefinition, SchemaDefinition } from \"@datrix/core\";\nimport type { JsonAdapter } from \"./adapter\";\n\n/**\n * JSON Transaction\n *\n * Thin wrapper around adapter that executes queries with transaction options.\n * Actual transaction state is managed by the adapter.\n */\nexport class JsonTransaction implements Transaction {\n\treadonly id: string;\n\n\tprivate committed = false;\n\tprivate rolledBack = false;\n\n\tconstructor(\n\t\tprivate adapter: JsonAdapter,\n\t\tprivate commitCallback: () => Promise<void>,\n\t\tprivate rollbackCallback: () => Promise<void>,\n\t) {\n\t\tthis.id = `tx_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;\n\t}\n\n\t/**\n\t * Check if transaction is still active\n\t */\n\tprivate assertActive(): void {\n\t\tif (this.committed) {\n\t\t\tthrowTransactionAlreadyCommitted({ adapter: \"json\" });\n\t\t}\n\t\tif (this.rolledBack) {\n\t\t\tthrowTransactionAlreadyRolledBack({ adapter: \"json\" });\n\t\t}\n\t}\n\n\t/**\n\t * Execute query within transaction\n\t *\n\t * Uses adapter's executeQueryWithOptions with skipLock and skipWrite.\n\t * Adapter automatically uses transaction cache.\n\t */\n\tasync executeQuery<TResult extends DatrixEntry>(\n\t\tquery: QueryObject<TResult>,\n\t): Promise<QueryResult<TResult>> {\n\t\tthis.assertActive();\n\n\t\treturn this.adapter.executeQueryWithOptions<TResult>(query, {\n\t\t\tskipLock: true,\n\t\t\tskipWrite: true,\n\t\t});\n\t}\n\n\t/**\n\t * Execute raw SQL query (not supported for JSON adapter)\n\t */\n\tasync executeRawQuery<TResult extends DatrixEntry>(\n\t\t_sql: string,\n\t\t_params: readonly unknown[],\n\t): Promise<QueryResult<TResult>> {\n\t\tthrowRawQueryNotSupported({ adapter: \"json\" });\n\t}\n\n\t// ========================================\n\t// SchemaOperations implementation\n\t// ========================================\n\n\t/**\n\t * Create table within transaction\n\t */\n\tasync createTable(schema: SchemaDefinition): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.createTableWithOptions(schema, { skipWrite: true });\n\t}\n\n\t/**\n\t * Drop table within transaction\n\t */\n\tasync dropTable(tableName: string): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.dropTableWithOptions(tableName, { skipWrite: true });\n\t}\n\n\t/**\n\t * Rename table within transaction\n\t */\n\tasync renameTable(from: string, to: string): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.renameTableWithOptions(from, to, { skipWrite: true });\n\t}\n\n\t/**\n\t * Alter table within transaction\n\t */\n\tasync alterTable(\n\t\ttableName: string,\n\t\toperations: readonly AlterOperation[],\n\t): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.alterTableWithOptions(tableName, operations, {\n\t\t\tskipWrite: true,\n\t\t});\n\t}\n\n\t/**\n\t * Add index within transaction\n\t */\n\tasync addIndex(tableName: string, index: IndexDefinition): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.addIndexWithOptions(tableName, index, {\n\t\t\tskipWrite: true,\n\t\t});\n\t}\n\n\t/**\n\t * Drop index within transaction\n\t */\n\tasync dropIndex(tableName: string, indexName: string): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.dropIndexWithOptions(tableName, indexName, {\n\t\t\tskipWrite: true,\n\t\t});\n\t}\n\n\t/**\n\t * Commit transaction\n\t *\n\t * Delegates to adapter's commitTransaction which writes to disk.\n\t */\n\tasync commit(): Promise<void> {\n\t\tthis.assertActive();\n\t\tawait this.commitCallback();\n\t\tthis.committed = true;\n\t}\n\n\t/**\n\t * Rollback transaction\n\t *\n\t * Delegates to adapter's rollbackTransaction which discards changes.\n\t */\n\tasync rollback(): Promise<void> {\n\t\tif (this.committed) {\n\t\t\tthrowTransactionAlreadyCommitted({ adapter: \"json\" });\n\t\t}\n\n\t\tif (this.rolledBack) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.rollbackCallback();\n\t\tthis.rolledBack = true;\n\t}\n\n\t/**\n\t * Create savepoint (not yet implemented for JSON adapter)\n\t */\n\tasync savepoint(_name: string): Promise<void> {\n\t\tthrowTransactionSavepointNotSupported({ adapter: \"json\" });\n\t}\n\n\t/**\n\t * Rollback to savepoint (not yet implemented for JSON adapter)\n\t */\n\tasync rollbackTo(_name: string): Promise<void> {\n\t\tthrowTransactionSavepointNotSupported({ adapter: \"json\" });\n\t}\n\n\t/**\n\t * Release savepoint (not yet implemented for JSON adapter)\n\t */\n\tasync release(_name: string): Promise<void> {\n\t\tthrowTransactionSavepointNotSupported({ adapter: \"json\" });\n\t}\n}\n","import { DatrixEntry, ForeignKeyReference, SchemaDefinition } from \"@datrix/core\";\nimport { QuerySelectObject } from \"@datrix/core\";\nimport { ExecuteQueryOptions, JsonTableFile } from \"./types\";\nimport type { JsonAdapter } from \"./adapter\";\nimport {\n\tDatrixAdapterError,\n\tthrowForeignKeyConstraint,\n\tthrowMigrationError,\n\tthrowUniqueConstraintField,\n\tthrowUniqueConstraintIndex,\n} from \"@datrix/core\";\nimport { FORJA_META_MODEL } from \"@datrix/core\";\n\n/**\n * Validate table name for security (no null bytes, path separators, or parent refs)\n */\nexport function validateTableName(tableName: string): void {\n\tif (tableName.includes(\"\\x00\")) {\n\t\tthrowMigrationError({\n\t\t\tadapter: \"json\",\n\t\t\tmessage: \"Invalid table name: contains null byte\",\n\t\t\ttable: tableName,\n\t\t});\n\t}\n\n\tif (tableName.includes(\"/\") || tableName.includes(\"\\\\\")) {\n\t\tthrowMigrationError({\n\t\t\tadapter: \"json\",\n\t\t\tmessage: \"Invalid table name: contains path separators\",\n\t\t\ttable: tableName,\n\t\t});\n\t}\n\n\tif (tableName.includes(\"..\")) {\n\t\tthrowMigrationError({\n\t\t\tadapter: \"json\",\n\t\t\tmessage: \"Invalid table name: contains parent directory reference\",\n\t\t\ttable: tableName,\n\t\t});\n\t}\n}\n\n/**\n * Bootstrap _datrix metadata table for standalone mode.\n * Called during connect() when standalone: true is set in config.\n */\nexport async function createMetaTable(adapter: JsonAdapter): Promise<void> {\n\tconst metaExists = await adapter.tableExists(FORJA_META_MODEL);\n\tif (metaExists) {\n\t\treturn;\n\t}\n\n\tconst metaSchema: SchemaDefinition = {\n\t\tname: FORJA_META_MODEL,\n\t\ttableName: FORJA_META_MODEL,\n\t\tfields: {\n\t\t\tid: { type: \"number\", autoIncrement: true },\n\t\t\tkey: { type: \"string\", required: true, unique: true, maxLength: 255 },\n\t\t\tvalue: { type: \"string\", required: true },\n\t\t\tcreatedAt: { type: \"date\" },\n\t\t\tupdatedAt: { type: \"date\" },\n\t\t},\n\t};\n\n\tawait adapter.createTable(metaSchema);\n}\n\n/**\n * Apply default values from schema for fields not provided.\n * Mimics SQL DEFAULT behavior.\n */\nexport function applyDefaultValues(\n\tschema: SchemaDefinition | undefined,\n\tdata: Record<string, unknown>,\n): void {\n\tif (!schema?.fields) return;\n\n\tfor (const [fieldName, fieldDef] of Object.entries(schema.fields)) {\n\t\tif (fieldName in data) continue;\n\n\t\tconst defaultValue = (fieldDef as { default?: unknown }).default;\n\t\tif (defaultValue !== undefined) {\n\t\t\tdata[fieldName] = defaultValue;\n\t\t}\n\t}\n}\n\n/**\n * Check unique constraints before insert/update.\n * Validates field-level unique and composite unique indexes.\n */\nexport function checkUniqueConstraints(\n\ttableData: JsonTableFile,\n\tschema: SchemaDefinition | undefined,\n\tnewData: Record<string, unknown>,\n\texcludeId?: number | string,\n): void {\n\tif (!schema?.fields) return;\n\n\tconst existingData = tableData.data;\n\n\t// 1. Check unique fields (field.unique === true)\n\tfor (const [fieldName, fieldDef] of Object.entries(schema.fields)) {\n\t\tif (!(fieldDef as { unique: boolean }).unique) continue;\n\n\t\tconst value = newData[fieldName];\n\t\tif (value === undefined || value === null) continue;\n\n\t\tconst duplicate = existingData.find(\n\t\t\t(row) => row[fieldName] === value && row[\"id\"] !== excludeId,\n\t\t);\n\n\t\tif (duplicate) {\n\t\t\tthrowUniqueConstraintField({\n\t\t\t\tfield: fieldName,\n\t\t\t\tvalue,\n\t\t\t\tadapter: \"json\",\n\t\t\t\ttable: schema.tableName ?? \"unknown\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// 2. Check unique indexes\n\tif (!schema.indexes) return;\n\n\tfor (const index of schema.indexes) {\n\t\tif (!index.unique) continue;\n\n\t\tconst indexValues = index.fields.map((f) => newData[f]);\n\n\t\tif (indexValues.some((v) => v === undefined || v === null)) continue;\n\n\t\tconst duplicate = existingData.find(\n\t\t\t(row) =>\n\t\t\t\tindex.fields.every((f) => row[f] === newData[f]) &&\n\t\t\t\trow[\"id\"] !== excludeId,\n\t\t);\n\n\t\tif (duplicate) {\n\t\t\tthrowUniqueConstraintIndex({\n\t\t\t\tfields: index.fields,\n\t\t\t\ttable: schema.tableName ?? \"unknown\",\n\t\t\t\tadapter: \"json\",\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Check foreign key constraints before insert/update.\n * Validates that FK values reference existing records in target tables.\n */\nexport async function checkForeignKeyConstraints(\n\tschema: SchemaDefinition | undefined,\n\tdata: Record<string, unknown>,\n\tadapter: JsonAdapter,\n): Promise<void> {\n\tif (!schema?.fields) return;\n\n\tfor (const [fieldName, fieldDef] of Object.entries(schema.fields)) {\n\t\tif (fieldDef.type !== \"relation\") continue;\n\n\t\tconst relationField = fieldDef as {\n\t\t\ttype: \"relation\";\n\t\t\tmodel: string;\n\t\t\tforeignKey?: string;\n\t\t\tkind?: string;\n\t\t};\n\n\t\tif (relationField.kind !== \"belongsTo\" && relationField.kind !== \"hasOne\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst foreignKey = relationField.foreignKey ?? `${fieldName}Id`;\n\t\tconst fkValue = data[foreignKey];\n\n\t\tif (fkValue === undefined || fkValue === null) continue;\n\n\t\tconst targetSchema = await adapter.getSchemaByModelName(\n\t\t\trelationField.model,\n\t\t);\n\t\tif (!targetSchema) continue;\n\n\t\tconst targetTable =\n\t\t\ttargetSchema.tableName ?? relationField.model.toLowerCase();\n\t\tconst targetData = await adapter.getCachedTable(targetTable);\n\n\t\tif (!targetData) continue;\n\n\t\tconst exists = targetData.data.some((row) => row[\"id\"] === fkValue);\n\n\t\tif (!exists) {\n\t\t\tthrowForeignKeyConstraint({\n\t\t\t\tforeignKey,\n\t\t\t\tvalue: fkValue,\n\t\t\t\ttargetModel: relationField.model,\n\t\t\t\ttable: schema.tableName ?? \"unknown\",\n\t\t\t\tadapter: \"json\",\n\t\t\t});\n\t\t}\n\t}\n}\n\ntype FkDependency = {\n\ttableName: string;\n\tfieldName: string;\n\tonDelete: NonNullable<ForeignKeyReference[\"onDelete\"]>;\n};\n\n/**\n * Find all FK fields across all tables that reference the given table.\n */\nasync function findFkDependencies(\n\ttargetTable: string,\n\tadapter: JsonAdapter,\n): Promise<FkDependency[]> {\n\tconst allTables = await adapter.getTables();\n\tconst deps: FkDependency[] = [];\n\n\tfor (const tableName of allTables) {\n\t\tconst schema = await adapter.getSchemaByTableName(tableName);\n\t\tif (!schema?.fields) continue;\n\n\t\tfor (const [fieldName, fieldDef] of Object.entries(schema.fields)) {\n\t\t\tif (fieldDef.type !== \"number\") continue;\n\n\t\t\tconst numField = fieldDef as { references?: ForeignKeyReference };\n\t\t\tconst ref = numField.references;\n\t\t\tif (!ref || ref.table !== targetTable) continue;\n\n\t\t\tconst onDelete = ref.onDelete ?? \"setNull\";\n\t\t\tdeps.push({ tableName, fieldName, onDelete });\n\t\t}\n\t}\n\n\treturn deps;\n}\n\n/**\n * Apply ON DELETE actions for the JSON adapter before deleting rows.\n * Mimics SQL FK ON DELETE behavior: restrict, setNull, cascade.\n *\n * Must be called BEFORE the actual delete.\n * Uses adapter.executeQuery for transaction safety.\n */\nexport async function applyOnDeleteActions(\n\ttargetTable: string,\n\tidsToDelete: ReadonlyArray<number>,\n\tadapter: JsonAdapter,\n\tqueryOptions?: ExecuteQueryOptions,\n): Promise<void> {\n\tif (idsToDelete.length === 0) return;\n\n\tconst deps = await findFkDependencies(targetTable, adapter);\n\tif (deps.length === 0) return;\n\n\t// Pass 1: Check restrict constraints\n\tfor (const dep of deps) {\n\t\tif (dep.onDelete !== \"restrict\") continue;\n\n\t\tconst tableData = await adapter.getCachedTable(dep.tableName);\n\t\tif (!tableData) continue;\n\n\t\tconst hasReference = tableData.data.some((row) =>\n\t\t\tidsToDelete.includes(row[dep.fieldName] as number),\n\t\t);\n\n\t\tif (hasReference) {\n\t\t\tthrow new DatrixAdapterError(\n\t\t\t\t`Cannot delete from '${targetTable}': referenced by '${dep.tableName}.${dep.fieldName}' with ON DELETE RESTRICT`,\n\t\t\t\t{\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tcode: \"ADAPTER_FOREIGN_KEY_CONSTRAINT\",\n\t\t\t\t\toperation: \"query\",\n\t\t\t\t\tcontext: {\n\t\t\t\t\t\ttable: targetTable,\n\t\t\t\t\t\treferencedBy: `${dep.tableName}.${dep.fieldName}`,\n\t\t\t\t\t},\n\t\t\t\t\tsuggestion: `Remove or update referencing rows in '${dep.tableName}' before deleting from '${targetTable}'`,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\t// Pass 2: Apply setNull\n\tfor (const dep of deps) {\n\t\tif (dep.onDelete !== \"setNull\") continue;\n\n\t\tawait adapter.executeQueryWithOptions(\n\t\t\t{\n\t\t\t\ttype: \"update\",\n\t\t\t\ttable: dep.tableName,\n\t\t\t\twhere: { [dep.fieldName]: { $in: idsToDelete } },\n\t\t\t\tdata: { [dep.fieldName]: null },\n\t\t\t},\n\t\t\tqueryOptions,\n\t\t);\n\t}\n\n\t// Pass 3: Apply cascade (recursive - child deletes trigger their own onDelete)\n\tfor (const dep of deps) {\n\t\tif (dep.onDelete !== \"cascade\") continue;\n\n\t\tconst tableData = await adapter.getCachedTable(dep.tableName);\n\t\tif (!tableData) continue;\n\n\t\tconst childIds = tableData.data\n\t\t\t.filter((row) => idsToDelete.includes(row[dep.fieldName] as number))\n\t\t\t.map((row) => row[\"id\"] as number);\n\n\t\tif (childIds.length === 0) continue;\n\n\t\t// Recursive: apply onDelete for children before deleting them\n\t\tawait applyOnDeleteActions(dep.tableName, childIds, adapter, queryOptions);\n\n\t\tawait adapter.executeQueryWithOptions(\n\t\t\t{\n\t\t\t\ttype: \"delete\",\n\t\t\t\ttable: dep.tableName,\n\t\t\t\twhere: { id: { $in: childIds } },\n\t\t\t},\n\t\t\tqueryOptions,\n\t\t);\n\t}\n}\n\n/**\n * Apply SELECT recursively (preserves populated fields).\n * Ensures nested populate selects are applied to related data.\n */\nexport function applySelectRecursive<T extends DatrixEntry>(\n\trows: T[],\n\tselect?: QuerySelectObject<T>[\"select\"],\n\tpopulate?: QuerySelectObject<T>[\"populate\"],\n): Partial<T>[] {\n\tif (!rows || rows.length === 0) {\n\t\treturn rows;\n\t}\n\n\tlet result = rows as Partial<T>[];\n\n\tif (select && (select as unknown as string) !== \"*\") {\n\t\tconst fieldsToKeep = new Set(select as unknown as (keyof T)[]);\n\n\t\tif (populate) {\n\t\t\tfor (const relationName of Object.keys(populate)) {\n\t\t\t\tfieldsToKeep.add(relationName as keyof T);\n\t\t\t}\n\t\t}\n\n\t\tresult = rows.map((row) => {\n\t\t\tconst projected: Partial<T> = {};\n\t\t\tfor (const field of fieldsToKeep) {\n\t\t\t\tif (field in row) {\n\t\t\t\t\tprojected[field] = row[field];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn projected;\n\t\t});\n\t}\n\n\tif (populate) {\n\t\tfor (const [relationName, options] of Object.entries(populate)) {\n\t\t\tif (typeof options === \"boolean\") continue;\n\n\t\t\tconst nestedSelect = options === \"*\" ? \"*\" : options.select;\n\t\t\tconst nestedPopulate = options === \"*\" ? undefined : options.populate;\n\n\t\t\tfor (const row of result) {\n\t\t\t\tconst relationValue = row[relationName as keyof T] as T;\n\t\t\t\tif (!relationValue) continue;\n\n\t\t\t\tif (Array.isArray(relationValue)) {\n\t\t\t\t\trow[relationName as keyof T] = applySelectRecursive<T>(\n\t\t\t\t\t\trelationValue,\n\t\t\t\t\t\tnestedSelect,\n\t\t\t\t\t\tnestedPopulate,\n\t\t\t\t\t) as T[keyof T];\n\t\t\t\t} else {\n\t\t\t\t\trow[relationName as keyof T] = applySelectRecursive<T>(\n\t\t\t\t\t\t[relationValue],\n\t\t\t\t\t\tnestedSelect,\n\t\t\t\t\t\tnestedPopulate,\n\t\t\t\t\t)[0] as T[keyof T];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n","import type { ExportWriter } from \"@datrix/core\";\nimport type { JsonAdapter } from \"../adapter\";\n\nconst CHUNK_SIZE = 1000;\n\nexport class JsonExporter {\n\tconstructor(\n\t\tprivate root: string,\n\t\tprivate adapter: JsonAdapter,\n\t) { }\n\n\tasync export(writer: ExportWriter): Promise<void> {\n\t\tawait writer.writeMeta({\n\t\t\tversion: 1,\n\t\t\texportedAt: new Date().toISOString(),\n\t\t});\n\n\t\tconst tables = await this.adapter.getTables();\n\n\t\tfor (const tableName of tables) {\n\t\t\tconst schema = await this.adapter.getTableSchema(tableName);\n\t\t\tif (schema) {\n\t\t\t\tawait writer.writeSchema(schema);\n\t\t\t}\n\t\t}\n\n\t\tfor (const tableName of tables) {\n\t\t\tawait this.exportTable(tableName, writer);\n\t\t}\n\n\t\tawait writer.finalize();\n\t}\n\n\tprivate async exportTable(\n\t\ttableName: string,\n\t\twriter: ExportWriter,\n\t): Promise<void> {\n\t\tconst fs = await import(\"node:fs/promises\");\n\t\tconst path = await import(\"node:path\");\n\t\tconst filePath = path.join(this.root, `${tableName}.json`);\n\n\t\tconst content = await fs.readFile(filePath, \"utf-8\");\n\t\tconst tableFile = JSON.parse(content) as {\n\t\t\tdata: Record<string, unknown>[];\n\t\t};\n\t\tconst rows = tableFile.data;\n\n\t\tfor (let i = 0; i < rows.length; i += CHUNK_SIZE) {\n\t\t\tawait writer.writeChunk(tableName, rows.slice(i, i + CHUNK_SIZE));\n\t\t}\n\n\t\tif (rows.length === 0) {\n\t\t\tawait writer.writeChunk(tableName, []);\n\t\t}\n\t}\n}\n","import type { ImportReader } from \"@datrix/core\";\nimport type { SchemaDefinition } from \"@datrix/core\";\nimport type { JsonAdapter } from \"../adapter\";\nimport type { JsonTableFile } from \"../types\";\n\nexport class JsonImporter {\n\tconstructor(\n\t\tprivate root: string,\n\t\tprivate adapter: JsonAdapter,\n\t) { }\n\n\tasync import(reader: ImportReader): Promise<void> {\n\t\tconst schemas = await this.collectSchemas(reader);\n\n\t\t// 1. Drop all existing tables\n\t\tconst existingTables = await this.adapter.getTables();\n\t\tfor (const tableName of existingTables) {\n\t\t\tawait this.adapter.dropTableWithOptions(tableName, { isImport: true });\n\t\t}\n\n\t\t// 2. Create tables — isImport skips upsertSchemaMeta so the importer\n\t\t// can restore _datrix data as-is.\n\t\tfor (const schema of schemas.values()) {\n\t\t\tawait this.adapter.createTable(schema, { isImport: true });\n\t\t}\n\n\t\t// 3. Collect all rows per table then write directly to file (with correct lastInsertId)\n\t\tconst tables = await reader.getTables();\n\t\tfor (const tableName of tables) {\n\t\t\tconst rows: Record<string, unknown>[] = [];\n\n\t\t\tfor await (const chunk of reader.readChunks(tableName)) {\n\t\t\t\trows.push(...chunk);\n\t\t\t}\n\n\t\t\tawait this.writeTableFile(tableName, rows);\n\t\t}\n\t}\n\n\tprivate async collectSchemas(\n\t\treader: ImportReader,\n\t): Promise<Map<string, SchemaDefinition>> {\n\t\tconst schemas = new Map<string, SchemaDefinition>();\n\t\tfor await (const schema of reader.readSchemas()) {\n\t\t\tschemas.set(schema.tableName!, schema);\n\t\t}\n\t\treturn schemas;\n\t}\n\n\tprivate async writeTableFile(\n\t\ttableName: string,\n\t\trows: Record<string, unknown>[],\n\t): Promise<void> {\n\t\tconst fs = await import(\"node:fs/promises\");\n\t\tconst path = await import(\"node:path\");\n\t\tconst filePath = path.join(this.root, `${tableName}.json`);\n\n\t\tconst maxId = rows.reduce((max, row) => {\n\t\t\tconst id = typeof row[\"id\"] === \"number\" ? row[\"id\"] : 0;\n\t\t\treturn id > max ? id : max;\n\t\t}, 0);\n\n\t\tconst tableFile: JsonTableFile = {\n\t\t\tmeta: {\n\t\t\t\tversion: 1,\n\t\t\t\tlastInsertId: maxId,\n\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\tname: tableName,\n\t\t\t},\n\t\t\tdata: rows,\n\t\t};\n\n\t\tawait fs.writeFile(filePath, JSON.stringify(tableFile, null, 2), \"utf-8\");\n\t}\n}\n","import {\n\tQuerySelect,\n\tQuerySelectObject,\n\tQueryPopulateOptions,\n} from \"@datrix/core\";\nimport type { JsonAdapter } from \"./adapter\";\nimport type { DatrixEntry, RelationField } from \"@datrix/core\";\nimport {\n\tthrowSchemaNotFound,\n\tthrowRelationNotFound,\n\tthrowInvalidRelationType,\n\tthrowTargetModelNotFound,\n} from \"@datrix/core\";\nimport { JsonQueryRunner } from \"./runner\";\n\nexport class JsonPopulator {\n\tconstructor(private adapter: JsonAdapter) { }\n\n\tasync populate<T extends DatrixEntry>(\n\t\trows: T[],\n\t\tquery: QuerySelectObject<T>,\n\t): Promise<T[]> {\n\t\tif (!query.populate || rows.length === 0) {\n\t\t\treturn rows;\n\t\t}\n\n\t\t// Get current schema directly from table file (cache-aware, O(1) lookup)\n\t\tconst _currentSchema = await this.adapter.getSchemaByTableName(query.table);\n\t\tif (!_currentSchema) {\n\t\t\tthrowSchemaNotFound({ adapter: \"json\", modelName: query.table });\n\t\t}\n\t\tconst currentSchema = _currentSchema!;\n\t\tconst currentModelName = currentSchema.name;\n\n\t\tconst result = [...rows];\n\n\t\tfor (const [relationName, _options] of Object.entries(query.populate)) {\n\t\t\t// Get relation field from current schema\n\t\t\tconst relationField = currentSchema.fields[relationName]!;\n\t\t\tif (!relationField) {\n\t\t\t\tthrowRelationNotFound({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\trelationName,\n\t\t\t\t\tschemaName: currentSchema.name,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (relationField.type !== \"relation\") {\n\t\t\t\tthrowInvalidRelationType({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\trelationName,\n\t\t\t\t\tfieldType: relationField.type,\n\t\t\t\t\tschemaName: currentSchema.name,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst relField = relationField as RelationField;\n\t\t\tconst targetModelName = relField.model;\n\t\t\tconst foreignKey = relField.foreignKey!;\n\t\t\tconst kind = relField.kind;\n\n\t\t\t// Get target schema from adapter (cache-aware)\n\t\t\tconst targetSchema =\n\t\t\t\tawait this.adapter.getSchemaByModelName(targetModelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\tthrowTargetModelNotFound({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\ttargetModel: targetModelName,\n\t\t\t\t\trelationName,\n\t\t\t\t\tschemaName: currentSchema.name,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst targetTable =\n\t\t\t\ttargetSchema!.tableName ?? targetModelName.toLowerCase();\n\n\t\t\t// Load target table using adapter's cache\n\t\t\tconst tableData = await this.adapter.getCachedTable(targetTable);\n\t\t\tif (!tableData) continue;\n\n\t\t\tconst relatedData = tableData.data as Record<string, unknown>[];\n\n\t\t\t// NOTE: We no longer apply select here - it's handled by adapter's applySelectRecursive\n\t\t\t// This ensures proper handling of nested populate + select combinations\n\n\t\t\tconst options =\n\t\t\t\ttypeof _options === \"object\" &&\n\t\t\t\t\t_options !== null &&\n\t\t\t\t\t!Array.isArray(_options)\n\t\t\t\t\t? (_options as QueryPopulateOptions<DatrixEntry>)\n\t\t\t\t\t: undefined;\n\n\t\t\t// Map data based on relation type\n\t\t\tif (kind === \"belongsTo\") {\n\t\t\t\t// Source has FK (e.g. Post.authorId -> User.id)\n\t\t\t\tconst ids = new Set(\n\t\t\t\t\tresult\n\t\t\t\t\t\t.map((r) => r[foreignKey as keyof T] as number)\n\t\t\t\t\t\t.filter((id): id is number => id !== null && id !== undefined),\n\t\t\t\t);\n\n\t\t\t\tconst relatedMap = new Map<number, Record<string, unknown>>();\n\t\t\t\tif (ids.size > 0) {\n\t\t\t\t\tfor (const item of relatedData) {\n\t\t\t\t\t\tconst itemId = item[\"id\"] as number;\n\t\t\t\t\t\tif (ids.has(itemId)) {\n\t\t\t\t\t\t\trelatedMap.set(itemId, item);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If where is specified, pre-filter the map using runner's match logic\n\t\t\t\tlet filteredMap = relatedMap;\n\t\t\t\tif (options?.where) {\n\t\t\t\t\tfilteredMap = new Map();\n\t\t\t\t\tconst filterRunner = new JsonQueryRunner(\n\t\t\t\t\t\ttableData,\n\t\t\t\t\t\tthis.adapter,\n\t\t\t\t\t\ttargetSchema!,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const [id, item] of relatedMap) {\n\t\t\t\t\t\tconst matched = await filterRunner.filterAndSort({\n\t\t\t\t\t\t\ttype: \"select\",\n\t\t\t\t\t\t\ttable: targetTable,\n\t\t\t\t\t\t\twhere: options.where,\n\t\t\t\t\t\t\tselect: \"*\" as unknown as QuerySelect,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (matched.some((r) => r[\"id\"] === id)) {\n\t\t\t\t\t\t\tfilteredMap.set(id, item);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (const row of result) {\n\t\t\t\t\tconst fkValue = row[foreignKey as keyof T] as\n\t\t\t\t\t\t| number\n\t\t\t\t\t\t| null\n\t\t\t\t\t\t| undefined;\n\t\t\t\t\tif (fkValue !== null && fkValue !== undefined) {\n\t\t\t\t\t\trow[relationName as keyof T] = (filteredMap.get(fkValue) ??\n\t\t\t\t\t\t\tnull) as T[keyof T];\n\t\t\t\t\t} else {\n\t\t\t\t\t\trow[relationName as keyof T] = null as T[keyof T];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (kind === \"hasMany\" || kind === \"hasOne\") {\n\t\t\t\t// Target has FK (e.g. User.id <- Post.authorId)\n\t\t\t\tconst sourceIds = new Set(\n\t\t\t\t\tresult\n\t\t\t\t\t\t.map((r) => r[\"id\"])\n\t\t\t\t\t\t.filter((id): id is number => id !== null && id !== undefined),\n\t\t\t\t);\n\n\t\t\t\t// Group related items by FK\n\t\t\t\tconst grouped = new Map<number, Record<string, unknown>[]>();\n\t\t\t\tfor (const item of relatedData) {\n\t\t\t\t\tconst fkValue = item[foreignKey] as number | null | undefined;\n\t\t\t\t\tif (\n\t\t\t\t\t\tfkValue !== null &&\n\t\t\t\t\t\tfkValue !== undefined &&\n\t\t\t\t\t\tsourceIds.has(fkValue)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst group = grouped.get(fkValue) ?? [];\n\t\t\t\t\t\tgroup.push(item);\n\t\t\t\t\t\tgrouped.set(fkValue, group);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst hasSortOrFilter =\n\t\t\t\t\toptions?.where ||\n\t\t\t\t\toptions?.orderBy ||\n\t\t\t\t\toptions?.limit !== undefined ||\n\t\t\t\t\toptions?.offset !== undefined;\n\n\t\t\t\tfor (const row of result) {\n\t\t\t\t\tconst rowId = row[\"id\"] as number;\n\t\t\t\t\tlet group = grouped.get(rowId) ?? [];\n\n\t\t\t\t\tif (kind === \"hasOne\") {\n\t\t\t\t\t\trow[relationName as keyof T] = (group[0] ?? null) as T[keyof T];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (hasSortOrFilter && group.length > 0) {\n\t\t\t\t\t\t\tconst groupTable = { ...tableData, data: group };\n\t\t\t\t\t\t\tconst groupRunner = new JsonQueryRunner(\n\t\t\t\t\t\t\t\tgroupTable,\n\t\t\t\t\t\t\t\tthis.adapter,\n\t\t\t\t\t\t\t\ttargetSchema!,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tgroup = (await groupRunner.filterAndSort({\n\t\t\t\t\t\t\t\ttype: \"select\",\n\t\t\t\t\t\t\t\ttable: targetTable,\n\t\t\t\t\t\t\t\twhere: options?.where!,\n\t\t\t\t\t\t\t\torderBy: options?.orderBy,\n\t\t\t\t\t\t\t\tlimit: options?.limit,\n\t\t\t\t\t\t\t\toffset: options?.offset,\n\t\t\t\t\t\t\t\tselect: \"*\" as unknown as QuerySelect,\n\t\t\t\t\t\t\t})) as unknown as Record<string, unknown>[];\n\t\t\t\t\t\t}\n\t\t\t\t\t\trow[relationName as keyof T] = group as T[keyof T];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (kind === \"manyToMany\") {\n\t\t\t\t// ManyToMany uses junction table (e.g. Post <-> Tag via post_tag)\n\t\t\t\tconst junctionTableName = relField.through!;\n\t\t\t\tconst sourceFK = `${currentModelName}Id`;\n\t\t\t\tconst targetFK = `${targetModelName}Id`;\n\n\t\t\t\t// Load junction table using adapter cache\n\t\t\t\tconst junctionData =\n\t\t\t\t\tawait this.adapter.getCachedTable(junctionTableName);\n\t\t\t\tif (!junctionData) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Junction table '${junctionTableName}' not found for manyToMany relation '${relationName}' in schema '${currentSchema.name}'`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Collect source IDs\n\t\t\t\tconst sourceIds = result\n\t\t\t\t\t.map((r) => r[\"id\"])\n\t\t\t\t\t.filter((id): id is number => id !== null && id !== undefined);\n\n\t\t\t\tif (sourceIds.length === 0) continue;\n\n\t\t\t\t// Use Runner for schema-aware filtering (handles string/number coercion)\n\t\t\t\tconst junctionRunner = new JsonQueryRunner(junctionData, this.adapter);\n\t\t\t\tconst relevantJunctions = await junctionRunner.run({\n\t\t\t\t\ttype: \"select\",\n\t\t\t\t\ttable: junctionTableName,\n\t\t\t\t\twhere: { [sourceFK]: { $in: sourceIds } },\n\t\t\t\t\tselect: \"*\" as unknown as QuerySelect,\n\t\t\t\t});\n\n\t\t\t\t// Build mapping: sourceId -> targetIds[]\n\t\t\t\t// Normalize all IDs to number for consistent comparison\n\t\t\t\tconst mapping = new Map<number, number[]>();\n\t\t\t\tfor (const junction of relevantJunctions) {\n\t\t\t\t\tconst srcId = junction[sourceFK as keyof typeof junction];\n\t\t\t\t\tconst tgtId = junction[targetFK as keyof typeof junction];\n\n\t\t\t\t\t// Normalize to number\n\t\t\t\t\tconst normalizedSrcId =\n\t\t\t\t\t\ttypeof srcId === \"string\" ? Number(srcId) : (srcId as number);\n\t\t\t\t\tconst normalizedTgtId =\n\t\t\t\t\t\ttypeof tgtId === \"string\" ? Number(tgtId) : (tgtId as number);\n\n\t\t\t\t\tconst existing = mapping.get(normalizedSrcId) ?? [];\n\t\t\t\t\texisting.push(normalizedTgtId);\n\t\t\t\t\tmapping.set(normalizedSrcId, existing);\n\t\t\t\t}\n\n\t\t\t\t// Collect all unique target IDs\n\t\t\t\tconst allTargetIds = new Set<number>();\n\t\t\t\tfor (const ids of mapping.values()) {\n\t\t\t\t\tids.forEach((id) => allTargetIds.add(id));\n\t\t\t\t}\n\n\t\t\t\t// Filter target records with id filter merged with user's where\n\t\t\t\tconst targetDataForRunner =\n\t\t\t\t\tawait this.adapter.getCachedTable(targetTable);\n\t\t\t\tif (!targetDataForRunner) continue;\n\n\t\t\t\tconst idFilter = { id: { $in: Array.from(allTargetIds) } };\n\t\t\t\tconst userWhere = options?.where;\n\t\t\t\tconst mergedWhere = userWhere\n\t\t\t\t\t? { $and: [idFilter, userWhere] }\n\t\t\t\t\t: idFilter;\n\n\t\t\t\tconst targetRunner = new JsonQueryRunner(\n\t\t\t\t\ttargetDataForRunner,\n\t\t\t\t\tthis.adapter,\n\t\t\t\t\ttargetSchema!,\n\t\t\t\t);\n\t\t\t\tconst targetRecords = await targetRunner.run({\n\t\t\t\t\ttype: \"select\",\n\t\t\t\t\ttable: targetTable,\n\t\t\t\t\twhere: mergedWhere,\n\t\t\t\t\torderBy: options?.orderBy,\n\t\t\t\t\tselect: \"*\" as unknown as QuerySelect,\n\t\t\t\t});\n\n\t\t\t\t// Map to result rows, applying limit/offset per-row\n\t\t\t\tfor (const row of result) {\n\t\t\t\t\tconst rowId = row[\"id\"];\n\t\t\t\t\tconst normalizedRowId =\n\t\t\t\t\t\ttypeof rowId === \"string\" ? Number(rowId) : (rowId as number);\n\t\t\t\t\tconst targetIds = mapping.get(normalizedRowId) ?? [];\n\n\t\t\t\t\t// Filter to this row's related records (already where/order filtered above)\n\t\t\t\t\tlet relatedRecords = targetRecords.filter((r) => {\n\t\t\t\t\t\tconst rID = r[\"id\"];\n\t\t\t\t\t\tconst normalizedRID =\n\t\t\t\t\t\t\ttypeof rID === \"string\" ? Number(rID) : (rID as number);\n\t\t\t\t\t\treturn targetIds.includes(normalizedRID);\n\t\t\t\t\t});\n\n\t\t\t\t\t// Apply limit/offset per-row\n\t\t\t\t\tconst offset = options?.offset ?? 0;\n\t\t\t\t\tif (options?.limit !== undefined) {\n\t\t\t\t\t\trelatedRecords = relatedRecords.slice(\n\t\t\t\t\t\t\toffset,\n\t\t\t\t\t\t\toffset + options.limit,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (offset > 0) {\n\t\t\t\t\t\trelatedRecords = relatedRecords.slice(offset);\n\t\t\t\t\t}\n\n\t\t\t\t\trow[relationName as keyof T] = relatedRecords as T[keyof T];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Nested populate (recursion)\n\t\t\tif (\n\t\t\t\ttypeof _options === \"object\" &&\n\t\t\t\t_options !== null &&\n\t\t\t\t_options.populate\n\t\t\t) {\n\t\t\t\tconst nextRows: T[] = [];\n\t\t\t\tfor (const row of result) {\n\t\t\t\t\tconst val = row[relationName as keyof T] as T;\n\t\t\t\t\tif (!val) continue;\n\t\t\t\t\tif (Array.isArray(val)) {\n\t\t\t\t\t\tnextRows.push(...val);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextRows.push(val);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (nextRows.length > 0) {\n\t\t\t\t\tawait this.populate(nextRows, {\n\t\t\t\t\t\ttype: \"select\",\n\t\t\t\t\t\ttable: targetTable,\n\t\t\t\t\t\tpopulate: _options.populate,\n\t\t\t\t\t\tselect: \"*\" as unknown as QuerySelect<T>,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n}\n","import { DatrixEntry } from \"@datrix/core\";\nimport {\n\tQueryCountObject,\n\tQueryInsertObject,\n\tQueryObject,\n\tQuerySelectObject,\n\tQueryUpdateObject,\n} from \"@datrix/core\";\nimport { JsonQueryRunner } from \"./runner\";\nimport { JsonPopulator } from \"./populate\";\nimport {\n\tapplyDefaultValues,\n\tapplyOnDeleteActions,\n\tapplySelectRecursive,\n\tcheckForeignKeyConstraints,\n\tcheckUniqueConstraints,\n} from \"./table-utils\";\nimport type { JsonAdapter } from \"./adapter\";\nimport type { ExecuteQueryOptions } from \"./types\";\nimport { throwQueryMissingData } from \"@datrix/core\";\n\nexport type QueryHandlerResult<T extends DatrixEntry> = {\n\trows: T[];\n\tmetadata: {\n\t\trowCount: number;\n\t\taffectedRows: number;\n\t\tinsertIds?: number[];\n\t\tcount?: number;\n\t};\n\tshouldWrite: boolean;\n\tearlyReturn?: boolean;\n};\n\nexport async function handleSelect<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QuerySelectObject<T>;\n\tadapter: JsonAdapter;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query, adapter } = ctx;\n\n\tlet rows: T[];\n\n\tif (query.populate) {\n\t\trows = await runner.filterAndSort(query);\n\t\tconst populator = new JsonPopulator(adapter);\n\t\trows = await populator.populate(rows, query);\n\t\trows = applySelectRecursive<T>(rows, query.select, query.populate) as T[];\n\t} else {\n\t\trows = (await runner.run(query)) as T[];\n\t}\n\n\treturn {\n\t\trows,\n\t\tmetadata: { rowCount: rows.length, affectedRows: 0 },\n\t\tshouldWrite: false,\n\t};\n}\n\nexport async function handleCount<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QueryCountObject<T>;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query } = ctx;\n\tconst rows = (await runner.run(query)) as T[];\n\n\treturn {\n\t\trows: [] as T[],\n\t\tmetadata: { rowCount: 0, affectedRows: 0, count: rows.length },\n\t\tshouldWrite: false,\n\t\tearlyReturn: true,\n\t};\n}\n\nexport async function handleInsert<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QueryInsertObject<T>;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query } = ctx;\n\tconst tableData = runner.tableData;\n\tconst tableSchema = runner.tableSchema;\n\tconst adapter = runner.adapterRef;\n\n\tif (!query.data || !Array.isArray(query.data)) {\n\t\tthrowQueryMissingData({\n\t\t\tqueryType: \"insert\",\n\t\t\ttable: query.table,\n\t\t\tadapter: \"json\",\n\t\t});\n\t}\n\n\tconst insertedIds: number[] = [];\n\tconst isJunctionTable =\n\t\t(tableSchema as unknown as { _isJunctionTable?: boolean })\n\t\t\t?._isJunctionTable === true;\n\n\tfor (const item of query.data) {\n\t\tconst newItem = { ...item };\n\n\t\tif (isJunctionTable) {\n\t\t\tconst alreadyExists = tableData.data.some((row) =>\n\t\t\t\tObject.keys(newItem).every(\n\t\t\t\t\t(key) => key === \"id\" || row[key] === newItem[key],\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (alreadyExists) continue;\n\t\t}\n\n\t\tif (!newItem[\"id\"]) {\n\t\t\ttableData.meta.lastInsertId = (tableData.meta.lastInsertId ?? 0) + 1;\n\t\t\tnewItem[\"id\"] = tableData.meta.lastInsertId;\n\t\t} else {\n\t\t\tconst manualId = Number(newItem[\"id\"]);\n\t\t\tif (!isNaN(manualId) && manualId > (tableData.meta.lastInsertId ?? 0)) {\n\t\t\t\ttableData.meta.lastInsertId = manualId;\n\t\t\t}\n\t\t}\n\n\t\tapplyDefaultValues(tableSchema, newItem);\n\t\tawait checkForeignKeyConstraints(tableSchema, newItem, adapter);\n\t\tcheckUniqueConstraints(tableData, tableSchema, newItem);\n\t\ttableData.data.push(newItem);\n\t\tinsertedIds.push(newItem[\"id\"] as number);\n\t}\n\n\tconst rows = insertedIds.map((id) => ({ id })) as T[];\n\n\treturn {\n\t\trows,\n\t\tmetadata: {\n\t\t\trowCount: insertedIds.length,\n\t\t\taffectedRows: insertedIds.length,\n\t\t\tinsertIds: insertedIds,\n\t\t},\n\t\tshouldWrite: true,\n\t};\n}\n\nexport async function handleUpdate<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QueryUpdateObject<T>;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query } = ctx;\n\tconst tableData = runner.tableData;\n\tconst tableSchema = runner.tableSchema;\n\tconst adapter = runner.adapterRef;\n\n\tif (!query.data) {\n\t\tthrowQueryMissingData({\n\t\t\tqueryType: \"update\",\n\t\t\ttable: query.table,\n\t\t\tadapter: \"json\",\n\t\t});\n\t}\n\n\tconst updateQuery: QuerySelectObject<T> = {\n\t\t...(query as unknown as QuerySelectObject<T>),\n\t\tlimit: undefined,\n\t\toffset: undefined,\n\t\torderBy: undefined,\n\t};\n\tconst rowsToUpdate = await runner.filterAndSort(updateQuery);\n\n\tfor (const row of rowsToUpdate) {\n\t\tconst updatedData = { ...row, ...query.data };\n\t\tawait checkForeignKeyConstraints(tableSchema, updatedData, adapter);\n\t\tcheckUniqueConstraints(\n\t\t\ttableData,\n\t\t\ttableSchema,\n\t\t\tupdatedData,\n\t\t\trow[\"id\"] as number,\n\t\t);\n\t}\n\n\tfor (const row of rowsToUpdate) {\n\t\tObject.assign(row, query.data);\n\t}\n\n\tconst updatedIds = rowsToUpdate.map((r) => r[\"id\"] as number);\n\tconst rows = updatedIds.map((id) => ({ id })) as T[];\n\n\treturn {\n\t\trows,\n\t\tmetadata: { rowCount: updatedIds.length, affectedRows: updatedIds.length },\n\t\tshouldWrite: true,\n\t};\n}\n\nexport async function handleDelete<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QueryObject<T>;\n\tadapter: JsonAdapter;\n\tqueryOptions?: ExecuteQueryOptions;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query, adapter, queryOptions } = ctx;\n\tconst tableData = runner.tableData;\n\n\tconst deleteQuery: QuerySelectObject<T> = {\n\t\t...(query as unknown as QuerySelectObject<T>),\n\t\tlimit: undefined,\n\t\toffset: undefined,\n\t\torderBy: undefined,\n\t};\n\tconst rowsToDelete = await runner.filterAndSort(deleteQuery);\n\tconst idsToDelete = rowsToDelete.map((r) => r.id as number);\n\n\t// Apply ON DELETE actions (restrict/setNull/cascade) before deleting\n\t// Pass queryOptions to avoid re-acquiring the already-held lock\n\tawait applyOnDeleteActions(query.table, idsToDelete, adapter, queryOptions);\n\n\tconst idsSet = new Set(idsToDelete);\n\tconst originalLength = tableData.data.length;\n\ttableData.data = tableData.data.filter((d) => !idsSet.has(d[\"id\"] as number));\n\n\tconst deletedIds = rowsToDelete.map((r) => r[\"id\"] as number);\n\tconst rows = deletedIds.map((id) => ({ id })) as T[];\n\n\treturn {\n\t\trows,\n\t\tmetadata: {\n\t\t\trowCount: deletedIds.length,\n\t\t\taffectedRows: originalLength - tableData.data.length,\n\t\t},\n\t\tshouldWrite: true,\n\t};\n}\n"],"mappings":"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","names":["fs","path","DatrixAdapterError","throwMigrationError","FORJA_META_MODEL","fs","path","fs","path","fs","path","content","FORJA_META_MODEL","throwMigrationError","DatrixAdapterError"]}
1
+ {"version":3,"sources":["../src/adapter.ts","../src/runner.ts","../src/lock.ts","../src/transaction.ts","../src/table-utils.ts","../src/export-import/exporter.ts","../src/export-import/importer.ts","../src/populate.ts","../src/query-handlers.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n\tAlterOperation,\n\tConnectionState,\n\tDatabaseAdapter,\n\tQueryResult,\n\tTransaction,\n} from \"@datrix/core\";\nimport { QueryObject } from \"@datrix/core\";\nimport { DatrixEntry, IndexDefinition, SchemaDefinition } from \"@datrix/core\";\nimport { validateQueryObject } from \"@datrix/core\";\nimport {\n\tCacheEntry,\n\tExecuteQueryOptions,\n\tJsonAdapterConfig,\n\tJsonTableFile,\n\tSchemaOperationOptions,\n} from \"./types\";\nimport { JsonQueryRunner } from \"./runner\";\nimport { SimpleLock } from \"./lock\";\nimport {\n\tDatrixAdapterError,\n\tthrowNotConnected,\n\tthrowConnectionError,\n\tthrowMigrationError,\n\tthrowTransactionError,\n\tthrowQueryError,\n\tthrowMetaFieldAlreadyExists,\n\tthrowMetaFieldNotFound,\n} from \"@datrix/core\";\nimport { JsonTransaction } from \"./transaction\";\nimport { DATRIX_META_MODEL, DATRIX_META_KEY_PREFIX } from \"@datrix/core\";\nimport { createMetaTable, validateTableName } from \"./table-utils\";\nimport { JsonExporter } from \"./export-import/exporter\";\nimport { JsonImporter } from \"./export-import/importer\";\nimport type { ExportWriter, ImportReader } from \"@datrix/core\";\nimport {\n\thandleCount,\n\thandleDelete,\n\thandleInsert,\n\thandleSelect,\n\thandleUpdate,\n} from \"./query-handlers\";\n\n/**\n * JSON File Adapter\n */\nexport class JsonAdapter implements DatabaseAdapter<JsonAdapterConfig> {\n\treadonly name = \"json\";\n\treadonly config: JsonAdapterConfig;\n\tprivate state: ConnectionState = \"disconnected\";\n\tprivate cache = new Map<string, CacheEntry>();\n\tprivate lock: SimpleLock;\n\tprivate cacheEnabled: boolean;\n\tprivate readLockEnabled: boolean;\n\n\t/**\n\t * Active transaction cache reference\n\t * When a transaction is active, all reads/writes go through this cache first.\n\t * Set by beginTransaction, cleared by commit/rollback.\n\t */\n\tprivate activeTransactionCache: Map<string, CacheEntry> | null = null;\n\n\t/**\n\t * Track modified tables during transaction for commit\n\t */\n\tprivate activeTransactionModifiedTables: Set<string> | null = null;\n\n\t/**\n\t * Tombstone set for tables deleted during transaction.\n\t * Prevents fallback to main cache or disk for dropped tables.\n\t */\n\tprivate activeTransactionDeletedTables: Set<string> | null = null;\n\n\tconstructor(config: JsonAdapterConfig) {\n\t\tthis.config = config;\n\t\tthis.lock = new SimpleLock(\n\t\t\tconfig.root,\n\t\t\tconfig.lockTimeout,\n\t\t\tconfig.staleTimeout,\n\t\t);\n\t\tthis.cacheEnabled = config.cache !== false; // default: true\n\t\tthis.readLockEnabled = config.readLock === true; // default: false\n\t}\n\n\t/**\n\t * Connect involves ensuring the root directory exists\n\t */\n\tasync connect(): Promise<void> {\n\t\tif (this.state === \"connected\") {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.state = \"connecting\";\n\n\t\ttry {\n\t\t\tawait fs.mkdir(this.config.root, { recursive: true });\n\t\t\tthis.state = \"connected\";\n\n\t\t\t// Standalone mode: bootstrap _datrix metadata table automatically\n\t\t\tif (this.config.standalone) {\n\t\t\t\ttry {\n\t\t\t\t\tawait createMetaTable(this);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.state = \"error\";\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.state = \"error\";\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrowConnectionError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Failed to access root directory: ${message}`,\n\t\t\t\tcause: error instanceof Error ? error : new Error(String(error)),\n\t\t\t});\n\t\t}\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tthis.state = \"disconnected\";\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.state === \"connected\";\n\t}\n\n\tgetConnectionState(): ConnectionState {\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Helper to get file path for a table\n\t */\n\tprivate getTablePath(tableName: string): string {\n\t\treturn path.join(this.config.root, `${tableName}.json`);\n\t}\n\n\t/**\n\t * Read table with cache support\n\t *\n\t * Cache lookup order:\n\t * 1. Check tombstone (if transaction active and table was dropped)\n\t * 2. Transaction cache (if active)\n\t * 3. Main cache (with mtime validation)\n\t * 4. Disk\n\t *\n\t * When transaction is active, new reads are cached in transaction cache.\n\t * This ensures isolation - transaction sees its own writes.\n\t */\n\tprivate async readTable(tableName: string): Promise<JsonTableFile> {\n\t\tconst filePath = this.getTablePath(tableName);\n\n\t\t// 1. Check tombstone first - table was dropped in this transaction\n\t\tif (this.activeTransactionDeletedTables?.has(tableName)) {\n\t\t\tthrow new Error(`Table '${tableName}' does not exist`);\n\t\t}\n\n\t\t// 2. Check transaction cache (if transaction active)\n\t\tif (this.activeTransactionCache) {\n\t\t\tconst txCached = this.activeTransactionCache.get(tableName);\n\t\t\tif (txCached) {\n\t\t\t\treturn txCached.data;\n\t\t\t}\n\t\t}\n\n\t\t// 2. Check main cache (with mtime validation)\n\t\tif (this.cacheEnabled) {\n\t\t\tconst stat = await fs.stat(filePath);\n\t\t\tconst mtime = stat.mtimeMs;\n\n\t\t\tconst cached = this.cache.get(tableName);\n\t\t\tif (cached && cached.mtime === mtime) {\n\t\t\t\t// If transaction active, copy to tx cache for isolation\n\t\t\t\tif (this.activeTransactionCache) {\n\t\t\t\t\t// Deep copy to prevent mutation of main cache\n\t\t\t\t\tconst txData = JSON.parse(JSON.stringify(cached.data));\n\t\t\t\t\tthis.activeTransactionCache.set(tableName, { data: txData, mtime });\n\t\t\t\t\treturn txData;\n\t\t\t\t}\n\t\t\t\treturn cached.data;\n\t\t\t}\n\n\t\t\t// Cache miss or stale - read from disk\n\t\t\tconst content = await fs.readFile(filePath, \"utf-8\");\n\t\t\tconst data: JsonTableFile = JSON.parse(content);\n\n\t\t\t// Store in appropriate cache\n\t\t\tif (this.activeTransactionCache) {\n\t\t\t\tthis.activeTransactionCache.set(tableName, { data, mtime });\n\t\t\t} else {\n\t\t\t\tthis.cache.set(tableName, { data, mtime });\n\t\t\t}\n\n\t\t\treturn data;\n\t\t}\n\n\t\t// 3. No cache - read from disk\n\t\tconst content = await fs.readFile(filePath, \"utf-8\");\n\t\treturn JSON.parse(content);\n\t}\n\n\t/**\n\t * Get cached table data (for external use like Populate)\n\t */\n\tasync getCachedTable(tableName: string): Promise<JsonTableFile | null> {\n\t\ttry {\n\t\t\treturn await this.readTable(tableName);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get schema directly from table file (cache-aware)\n\t * This is faster than going through Datrix registry and ensures consistency\n\t *\n\t * @param tableName - Table name (e.g., \"users\")\n\t * @returns Schema definition or null if not found\n\t */\n\tasync getSchemaByTableName(\n\t\ttableName: string,\n\t): Promise<SchemaDefinition | null> {\n\t\ttry {\n\t\t\treturn await this.readTableSchema(tableName);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get schema by model name\n\t * Requires scanning all tables to find matching schema.name\n\t * Prefer getSchemaByTableName when table name is known (faster)\n\t *\n\t * @param modelName - Model name from schema (e.g., \"User\")\n\t * @returns Schema definition or null if not found\n\t */\n\tasync getSchemaByModelName(\n\t\tmodelName: string,\n\t): Promise<SchemaDefinition | null> {\n\t\ttry {\n\t\t\tconst tablesResult = await this.getTables();\n\n\t\t\tfor (const tableName of tablesResult) {\n\t\t\t\tconst schema = await this.getSchemaByTableName(tableName);\n\t\t\t\tif (schema?.name === modelName) {\n\t\t\t\t\treturn schema;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Find table name by schema model name\n\t *\n\t * @param modelName - Model name (e.g., \"User\")\n\t * @returns Table name or null if not found\n\t */\n\tasync findTableNameByModelName(modelName: string): Promise<string | null> {\n\t\tconst schema = await this.getSchemaByModelName(modelName);\n\t\treturn schema?.tableName ?? null;\n\t}\n\n\t/**\n\t * Read schema for a table from _datrix metadata table.\n\t * Transaction-aware: reads from tx cache when inside a transaction.\n\t *\n\t * @param tableName - Physical table name (e.g. \"users\")\n\t */\n\tasync readTableSchema(tableName: string): Promise<SchemaDefinition> {\n\t\tconst metaFile = await this.readTable(DATRIX_META_MODEL);\n\t\tconst metaKey = `${DATRIX_META_KEY_PREFIX}${tableName}`;\n\t\tconst row = metaFile.data.find(\n\t\t\t(r) => (r as Record<string, unknown>)[\"key\"] === metaKey,\n\t\t);\n\t\tif (!row) {\n\t\t\tthrow new Error(`Schema for '${tableName}' not found in _datrix`);\n\t\t}\n\t\treturn JSON.parse(\n\t\t\t(row as Record<string, unknown>)[\"value\"] as string,\n\t\t) as SchemaDefinition;\n\t}\n\n\t/**\n\t * Upsert schema into _datrix metadata table\n\t */\n\tprivate async upsertSchemaMeta(\n\t\tschema: SchemaDefinition,\n\t\tskipWrite: boolean,\n\t): Promise<void> {\n\t\tconst metaKey = `${DATRIX_META_KEY_PREFIX}${schema.tableName ?? schema.name}`;\n\t\tconst metaValue = JSON.stringify(schema);\n\t\tconst metaFile = await this.readTable(DATRIX_META_MODEL);\n\n\t\tconst existingIndex = metaFile.data.findIndex(\n\t\t\t(r) => (r as Record<string, unknown>)[\"key\"] === metaKey,\n\t\t);\n\n\t\tif (existingIndex >= 0) {\n\t\t\t(metaFile.data[existingIndex] as Record<string, unknown>)[\"value\"] =\n\t\t\t\tmetaValue;\n\t\t} else {\n\t\t\tconst lastInsertId = (metaFile.meta.lastInsertId ?? 0) + 1;\n\t\t\tmetaFile.meta.lastInsertId = lastInsertId;\n\t\t\tmetaFile.data.push({\n\t\t\t\tid: lastInsertId,\n\t\t\t\tkey: metaKey,\n\t\t\t\tvalue: metaValue,\n\t\t\t} as Record<string, unknown>);\n\t\t}\n\n\t\tmetaFile.meta.updatedAt = new Date().toISOString();\n\n\t\tif (skipWrite) {\n\t\t\tthis.activeTransactionCache!.set(DATRIX_META_MODEL, {\n\t\t\t\tdata: metaFile,\n\t\t\t\tmtime: Date.now(),\n\t\t\t});\n\t\t\tthis.activeTransactionModifiedTables!.add(DATRIX_META_MODEL);\n\t\t} else {\n\t\t\tconst filePath = this.getTablePath(DATRIX_META_MODEL);\n\t\t\tawait fs.writeFile(filePath, JSON.stringify(metaFile, null, 2), \"utf-8\");\n\t\t\tawait this.updateCache(DATRIX_META_MODEL, metaFile);\n\t\t}\n\t}\n\n\t/**\n\t * Apply AlterOperations to schema in _datrix and write back\n\t */\n\tprivate async applyOperationsToMetaSchema(\n\t\ttableName: string,\n\t\toperations: readonly AlterOperation[],\n\t\tskipWrite: boolean,\n\t): Promise<void> {\n\t\tconst schema = await this.readTableSchema(tableName);\n\t\tconst fields = { ...schema.fields };\n\n\t\tfor (const op of operations) {\n\t\t\tswitch (op.type) {\n\t\t\t\tcase \"addColumn\":\n\t\t\t\t\tfields[op.column] = op.definition;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"dropColumn\":\n\t\t\t\t\tdelete fields[op.column];\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"modifyColumn\":\n\t\t\t\t\tfields[op.column] = op.newDefinition;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"renameColumn\": {\n\t\t\t\t\tconst fieldDef = fields[op.from];\n\t\t\t\t\tif (fieldDef !== undefined) {\n\t\t\t\t\t\tfields[op.to] = fieldDef;\n\t\t\t\t\t\tdelete fields[op.from];\n\t\t\t\t\t}\n\t\t\t\t\t// Update relation fields that reference the renamed column\n\t\t\t\t\tfor (const [key, def] of Object.entries(fields)) {\n\t\t\t\t\t\tif (def.type === \"relation\" && def.foreignKey === op.from) {\n\t\t\t\t\t\t\tfields[key] = { ...def, foreignKey: op.to };\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"addMetaField\":\n\t\t\t\t\tif (fields[op.field] !== undefined) {\n\t\t\t\t\t\tthrowMetaFieldAlreadyExists({\n\t\t\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\t\t\tfield: op.field,\n\t\t\t\t\t\t\ttable: tableName,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tfields[op.field] = op.definition;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"dropMetaField\":\n\t\t\t\t\tif (fields[op.field] === undefined) {\n\t\t\t\t\t\tthrowMetaFieldNotFound({\n\t\t\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\t\t\tfield: op.field,\n\t\t\t\t\t\t\ttable: tableName,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tdelete fields[op.field];\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"modifyMetaField\":\n\t\t\t\t\tif (fields[op.field] === undefined) {\n\t\t\t\t\t\tthrowMetaFieldNotFound({\n\t\t\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\t\t\tfield: op.field,\n\t\t\t\t\t\t\ttable: tableName,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tfields[op.field] = op.newDefinition;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tconst updatedSchema: SchemaDefinition = { ...schema, fields };\n\t\tawait this.upsertSchemaMeta(updatedSchema, skipWrite);\n\t}\n\n\t/**\n\t * Invalidate cache for a specific table\n\t */\n\tprivate invalidateCache(tableName: string): void {\n\t\tthis.cache.delete(tableName);\n\t}\n\n\t/**\n\t * Update cache after write operation\n\t */\n\tprivate async updateCache(\n\t\ttableName: string,\n\t\tdata: JsonTableFile,\n\t): Promise<void> {\n\t\tif (!this.cacheEnabled) return;\n\n\t\tconst filePath = this.getTablePath(tableName);\n\t\ttry {\n\t\t\tconst stat = await fs.stat(filePath);\n\t\t\tthis.cache.set(tableName, { data, mtime: stat.mtimeMs });\n\t\t} catch {\n\t\t\tthis.invalidateCache(tableName);\n\t\t}\n\t}\n\n\tasync exportData(writer: ExportWriter): Promise<void> {\n\t\tawait new JsonExporter(this.config.root, this).export(writer);\n\t}\n\n\tasync importData(reader: ImportReader): Promise<void> {\n\t\tawait new JsonImporter(this.config.root, this).import(reader);\n\t}\n\n\tasync createTable(\n\t\tschema: SchemaDefinition,\n\t\toptions?: {\n\t\t\t/**\n\t\t\t * Set to true when called from the importer.\n\t\t\t * Skips upsertSchemaMeta so the importer can restore _datrix data as-is.\n\t\t\t */\n\t\t\tisImport?: boolean;\n\t\t},\n\t): Promise<void> {\n\t\treturn this.createTableWithOptions(schema, undefined, options?.isImport);\n\t}\n\n\t/**\n\t * Create table with options (for transaction support)\n\t */\n\tasync createTableWithOptions(\n\t\tschema: SchemaDefinition,\n\t\toptions?: SchemaOperationOptions,\n\t\tisImport?: boolean,\n\t): Promise<void> {\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\n\t\t// Standalone mode: ensure id field exists since registry is not present to add it\n\t\tif (this.config.standalone && !(\"id\" in schema.fields)) {\n\t\t\tschema = {\n\t\t\t\t...schema,\n\t\t\t\tfields: {\n\t\t\t\t\tid: { type: \"number\", autoIncrement: true },\n\t\t\t\t\t...schema.fields,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tconst tableName = schema.tableName!;\n\n\t\tvalidateTableName(tableName);\n\n\t\t// Check if table exists in transaction cache first\n\t\tif (this.activeTransactionCache?.has(tableName)) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${schema.name}' already exists`,\n\t\t\t\ttable: tableName,\n\t\t\t});\n\t\t}\n\n\t\t// Check if table was deleted in this transaction - allow recreation\n\t\tconst wasDeleted = this.activeTransactionDeletedTables?.has(tableName);\n\t\tif (wasDeleted) {\n\t\t\t// Remove from tombstone - we're recreating\n\t\t\tthis.activeTransactionDeletedTables!.delete(tableName);\n\t\t}\n\n\t\t// Check disk only if not in transaction or table wasn't deleted\n\t\tif (!wasDeleted) {\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\ttry {\n\t\t\t\tawait fs.access(filePath);\n\t\t\t\tthrowMigrationError({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tmessage: `Table '${schema.name}' already exists`,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof DatrixAdapterError) throw err;\n\t\t\t\t// File does not exist, proceed\n\t\t\t}\n\t\t}\n\n\t\tconst initialContent: JsonTableFile = {\n\t\t\tmeta: {\n\t\t\t\tversion: 1,\n\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\tname: schema.name,\n\t\t\t},\n\t\t\tdata: [],\n\t\t};\n\n\t\tif (skipWrite) {\n\t\t\t// Transaction mode: write to transaction cache\n\t\t\tthis.activeTransactionCache!.set(tableName, {\n\t\t\t\tdata: initialContent,\n\t\t\t\tmtime: Date.now(),\n\t\t\t});\n\t\t\tthis.activeTransactionModifiedTables!.add(tableName);\n\t\t} else {\n\t\t\t// Normal mode: write to disk and update cache\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\tawait fs.writeFile(\n\t\t\t\tfilePath,\n\t\t\t\tJSON.stringify(initialContent, null, 2),\n\t\t\t\t\"utf-8\",\n\t\t\t);\n\t\t\tawait this.updateCache(tableName, initialContent);\n\t\t}\n\n\t\t// Track schema in _datrix (skip during import — _datrix data will be restored as-is)\n\t\tif (!isImport) {\n\t\t\tif (schema.name !== DATRIX_META_MODEL) {\n\t\t\t\tconst metaExists = await this.tableExists(DATRIX_META_MODEL);\n\t\t\t\tif (!metaExists) {\n\t\t\t\t\tthrowMigrationError({\n\t\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\t\tmessage: `Cannot create table '${schema.name}': '${DATRIX_META_MODEL}' table does not exist yet. Create '${DATRIX_META_MODEL}' first.`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait this.upsertSchemaMeta(schema, skipWrite);\n\t\t}\n\t}\n\n\tasync dropTable(tableName: string): Promise<void> {\n\t\treturn this.dropTableWithOptions(tableName);\n\t}\n\n\t/**\n\t * Drop table with options (for transaction support)\n\t */\n\tasync dropTableWithOptions(\n\t\ttableName: string,\n\t\toptions?: SchemaOperationOptions,\n\t): Promise<void> {\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\t\tconst isImport = options?.isImport ?? false;\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\t// Check if table was already deleted in this transaction\n\t\tif (this.activeTransactionDeletedTables?.has(tableName)) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${tableName}' does not exist`,\n\t\t\t});\n\t\t}\n\n\t\t// Check if table exists (in tx cache, main cache, or disk)\n\t\tconst existsInTxCache = this.activeTransactionCache?.has(tableName);\n\t\tconst existsInMainCache = this.cache.has(tableName);\n\t\tlet existsOnDisk = false;\n\n\t\tif (!existsInTxCache && !existsInMainCache) {\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\ttry {\n\t\t\t\tawait fs.access(filePath);\n\t\t\t\texistsOnDisk = true;\n\t\t\t} catch {\n\t\t\t\t// Not on disk\n\t\t\t}\n\t\t}\n\n\t\tif (!existsInTxCache && !existsInMainCache && !existsOnDisk) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${tableName}' does not exist`,\n\t\t\t});\n\t\t}\n\n\t\tif (skipWrite) {\n\t\t\t// Transaction mode: add to tombstone, remove from tx cache\n\t\t\tthis.activeTransactionDeletedTables!.add(tableName);\n\t\t\tthis.activeTransactionCache!.delete(tableName);\n\t\t} else {\n\t\t\t// Normal mode: delete from disk\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\tawait fs.unlink(filePath);\n\t\t\tthis.invalidateCache(tableName);\n\t\t}\n\n\t\t// Remove schema from _datrix (skip during import — _datrix data will be restored as-is)\n\t\tif (!isImport && tableName !== DATRIX_META_MODEL) {\n\t\t\t// TODO: _datrix table diger tablelar gibi bir table. burada kod tekrari yapmak yerine executeQuery({delete from _datrix where key = metaKey}) gibi bir sey yapilabilir. eger lock problem cikarmiyorsa\n\t\t\tconst metaKey = `${DATRIX_META_KEY_PREFIX}${tableName}`;\n\t\t\tconst metaFile = await this.readTable(DATRIX_META_MODEL);\n\t\t\tmetaFile.data = metaFile.data.filter(\n\t\t\t\t(r) => (r as Record<string, unknown>)[\"key\"] !== metaKey,\n\t\t\t);\n\t\t\tmetaFile.meta.updatedAt = new Date().toISOString();\n\n\t\t\tif (skipWrite) {\n\t\t\t\tthis.activeTransactionCache!.set(DATRIX_META_MODEL, {\n\t\t\t\t\tdata: metaFile,\n\t\t\t\t\tmtime: Date.now(),\n\t\t\t\t});\n\t\t\t\tthis.activeTransactionModifiedTables!.add(DATRIX_META_MODEL);\n\t\t\t} else {\n\t\t\t\tconst filePath = this.getTablePath(DATRIX_META_MODEL);\n\t\t\t\tawait fs.writeFile(\n\t\t\t\t\tfilePath,\n\t\t\t\t\tJSON.stringify(metaFile, null, 2),\n\t\t\t\t\t\"utf-8\",\n\t\t\t\t);\n\t\t\t\tawait this.updateCache(DATRIX_META_MODEL, metaFile);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Execute query (public interface)\n\t *\n\t * This is the standard DatabaseAdapter interface method.\n\t * Internally calls executeQueryWithOptions with default options.\n\t */\n\tasync executeQuery<TResult extends DatrixEntry>(\n\t\tquery: QueryObject<TResult>,\n\t): Promise<QueryResult<TResult>> {\n\t\treturn this.executeQueryWithOptions(query);\n\t}\n\n\t/**\n\t * Execute query with options (for transaction support)\n\t *\n\t * @param query - Query to execute\n\t * @param options - Execution options\n\t * @param options.skipLock - Skip lock acquisition (transaction already holds lock)\n\t * @param options.skipWrite - Skip writing to disk (transaction will write on commit)\n\t */\n\tasync executeQueryWithOptions<TResult extends DatrixEntry>(\n\t\tquery: QueryObject<TResult>,\n\t\toptions?: ExecuteQueryOptions,\n\t): Promise<QueryResult<TResult>> {\n\t\tconst skipLock = options?.skipLock ?? false;\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\n\t\tvalidateQueryObject(query);\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tconst isWriteOp = [\"insert\", \"update\", \"delete\"].includes(query.type);\n\t\tconst needsLock = !skipLock && (isWriteOp || this.readLockEnabled);\n\t\tlet lockAcquired = false;\n\n\t\tif (needsLock) {\n\t\t\ttry {\n\t\t\t\tawait this.lock.acquire();\n\t\t\t\tlockAcquired = true;\n\t\t\t} catch (err) {\n\t\t\t\tthrowQueryError({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tmessage: `Failed to acquire lock: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t\tquery: query as QueryObject,\n\t\t\t\t\tcause: err instanceof Error ? err : new Error(String(err)),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tlet tableData: JsonTableFile<Record<string, unknown>>;\n\n\t\t\ttry {\n\t\t\t\ttableData = await this.readTable(query.table);\n\t\t\t} catch (err) {\n\t\t\t\tif (lockAcquired) await this.lock.release();\n\t\t\t\tthrowQueryError({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tmessage: `Table '${query.table}' not found`,\n\t\t\t\t\tquery: query as QueryObject,\n\t\t\t\t\tcause: err instanceof Error ? err : new Error(String(err)),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Handle missing data field\n\t\t\tif (!tableData!.data || !Array.isArray(tableData!.data)) {\n\t\t\t\ttableData!.data = [];\n\t\t\t}\n\n\t\t\t// Load schema from _datrix for this table (transaction-aware)\n\t\t\tlet tableSchema: SchemaDefinition | undefined;\n\t\t\ttry {\n\t\t\t\ttableSchema = await this.readTableSchema(query.table);\n\t\t\t} catch {\n\t\t\t\t// Schema not found in _datrix — proceed without it\n\t\t\t}\n\n\t\t\tconst runner = new JsonQueryRunner(tableData!, this, tableSchema);\n\n\t\t\tlet handlerResult: Awaited<ReturnType<typeof handleSelect>>;\n\n\t\t\tswitch (query.type) {\n\t\t\t\tcase \"count\":\n\t\t\t\t\thandlerResult = await handleCount({ runner, query });\n\t\t\t\t\tif (handlerResult.earlyReturn) {\n\t\t\t\t\t\tif (lockAcquired) await this.lock.release();\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\trows: [] as TResult[],\n\t\t\t\t\t\t\tmetadata: handlerResult.metadata,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"select\":\n\t\t\t\t\thandlerResult = await handleSelect({ runner, query, adapter: this });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"insert\":\n\t\t\t\t\thandlerResult = await handleInsert({ runner, query });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"update\":\n\t\t\t\t\thandlerResult = await handleUpdate({ runner, query });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"delete\":\n\t\t\t\t\thandlerResult = await handleDelete({\n\t\t\t\t\t\trunner,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tadapter: this,\n\t\t\t\t\t\tqueryOptions: { skipLock: true, skipWrite },\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst rows = handlerResult!.rows as TResult[];\n\t\t\tconst metadata = handlerResult!.metadata;\n\t\t\tconst shouldWrite = handlerResult!.shouldWrite;\n\n\t\t\t// Handle write\n\t\t\tif (shouldWrite) {\n\t\t\t\tif (skipWrite) {\n\t\t\t\t\t// Transaction mode: track modified table, don't write to disk\n\t\t\t\t\tif (this.activeTransactionModifiedTables) {\n\t\t\t\t\t\tthis.activeTransactionModifiedTables.add(query.table);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Normal mode: write to disk immediately\n\t\t\t\t\ttableData!.meta.updatedAt = new Date().toISOString();\n\t\t\t\t\tconst filePath = this.getTablePath(query.table);\n\t\t\t\t\tawait fs.writeFile(\n\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\tJSON.stringify(tableData, null, 2),\n\t\t\t\t\t\t\"utf-8\",\n\t\t\t\t\t);\n\t\t\t\t\tawait this.updateCache(query.table, tableData!);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmetadata.rowCount = rows.length;\n\n\t\t\tif (lockAcquired) await this.lock.release();\n\n\t\t\treturn {\n\t\t\t\trows: rows as TResult[],\n\t\t\t\tmetadata,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (lockAcquired) await this.lock.release();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync executeRawQuery<TResult extends DatrixEntry>(\n\t\t_sql: string,\n\t\t_params: readonly unknown[],\n\t): Promise<QueryResult<TResult>> {\n\t\tthrowQueryError({\n\t\t\tadapter: \"json\",\n\t\t\tmessage: \"executeRawQuery is not supported by JsonAdapter\",\n\t\t});\n\t}\n\n\t/**\n\t * Begin a new transaction\n\t *\n\t * Acquires lock and creates isolated transaction cache.\n\t * All reads/writes within transaction use txCache.\n\t */\n\tasync beginTransaction(): Promise<Transaction> {\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tif (this.activeTransactionCache) {\n\t\t\tthrowTransactionError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: \"A transaction is already active\",\n\t\t\t});\n\t\t}\n\n\t\ttry {\n\t\t\t// Acquire lock for entire transaction duration\n\t\t\tawait this.lock.acquire();\n\n\t\t\t// Initialize transaction state\n\t\t\tthis.activeTransactionCache = new Map<string, CacheEntry>();\n\t\t\tthis.activeTransactionModifiedTables = new Set<string>();\n\t\t\tthis.activeTransactionDeletedTables = new Set<string>();\n\n\t\t\t// Create transaction with commit/rollback callbacks\n\t\t\tconst transaction = new JsonTransaction(\n\t\t\t\tthis,\n\t\t\t\t// Commit callback\n\t\t\t\tasync () => {\n\t\t\t\t\tawait this.commitTransaction();\n\t\t\t\t},\n\t\t\t\t// Rollback callback\n\t\t\t\tasync () => {\n\t\t\t\t\tawait this.rollbackTransaction();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn transaction;\n\t\t} catch (err) {\n\t\t\tthrowTransactionError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Failed to begin transaction: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\tcause: err instanceof Error ? err : new Error(String(err)),\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Commit transaction - write modified tables to disk\n\t * @internal Called by JsonTransaction.commit()\n\t */\n\tprivate async commitTransaction(): Promise<void> {\n\t\tif (!this.activeTransactionCache || !this.activeTransactionModifiedTables) {\n\t\t\tthrowTransactionError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: \"No active transaction to commit\",\n\t\t\t});\n\t\t}\n\n\t\ttry {\n\t\t\t// 1. Delete dropped tables from disk\n\t\t\tif (this.activeTransactionDeletedTables) {\n\t\t\t\tfor (const tableName of this.activeTransactionDeletedTables) {\n\t\t\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait fs.unlink(filePath);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// File might not exist on disk (created and dropped in same tx)\n\t\t\t\t\t}\n\t\t\t\t\t// Remove from main cache\n\t\t\t\t\tthis.cache.delete(tableName);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 2. Write all modified tables to disk\n\t\t\tfor (const tableName of this.activeTransactionModifiedTables) {\n\t\t\t\t// Skip if table was deleted\n\t\t\t\tif (this.activeTransactionDeletedTables?.has(tableName)) continue;\n\n\t\t\t\tconst entry = this.activeTransactionCache.get(tableName);\n\t\t\t\tif (entry) {\n\t\t\t\t\t// Write to disk\n\t\t\t\t\tentry.data.meta.updatedAt = new Date().toISOString();\n\t\t\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\t\t\tawait fs.writeFile(\n\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\tJSON.stringify(entry.data, null, 2),\n\t\t\t\t\t\t\"utf-8\",\n\t\t\t\t\t);\n\n\t\t\t\t\t// Update mtime and merge to main cache\n\t\t\t\t\tconst stat = await fs.stat(filePath);\n\t\t\t\t\tentry.mtime = stat.mtimeMs;\n\t\t\t\t\tthis.cache.set(tableName, entry);\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\t// Clear transaction state and release lock\n\t\t\tthis.activeTransactionCache = null;\n\t\t\tthis.activeTransactionModifiedTables = null;\n\t\t\tthis.activeTransactionDeletedTables = null;\n\t\t\tawait this.lock.release();\n\t\t}\n\t}\n\n\t/**\n\t * Rollback transaction - discard changes\n\t * @internal Called by JsonTransaction.rollback()\n\t */\n\tprivate async rollbackTransaction(): Promise<void> {\n\t\t// Simply discard transaction cache - main cache unchanged\n\t\tthis.activeTransactionCache = null;\n\t\tthis.activeTransactionModifiedTables = null;\n\t\tthis.activeTransactionDeletedTables = null;\n\t\tawait this.lock.release();\n\t}\n\n\tasync alterTable(\n\t\ttableName: string,\n\t\toperations: readonly AlterOperation[],\n\t): Promise<void> {\n\t\treturn this.alterTableWithOptions(tableName, operations);\n\t}\n\n\t/**\n\t * Alter table with options (for transaction support)\n\t */\n\tasync alterTableWithOptions(\n\t\ttableName: string,\n\t\toperations: readonly AlterOperation[],\n\t\toptions?: SchemaOperationOptions,\n\t): Promise<void> {\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tconst json = await this.readTable(tableName);\n\n\t\t// Apply each operation to table data rows\n\t\tfor (const op of operations) {\n\t\t\tswitch (op.type) {\n\t\t\t\tcase \"addColumn\": {\n\t\t\t\t\tconst defaultValue = (op.definition as { default?: unknown }).default;\n\t\t\t\t\tfor (const row of json.data) {\n\t\t\t\t\t\tif (!(op.column in row)) {\n\t\t\t\t\t\t\t(row as Record<string, unknown>)[op.column] =\n\t\t\t\t\t\t\t\tdefaultValue ?? null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"dropColumn\": {\n\t\t\t\t\tfor (const row of json.data) {\n\t\t\t\t\t\tdelete (row as Record<string, unknown>)[op.column];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"modifyColumn\": {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"renameColumn\": {\n\t\t\t\t\tfor (const row of json.data) {\n\t\t\t\t\t\tconst r = row as Record<string, unknown>;\n\t\t\t\t\t\tif (op.from in r) {\n\t\t\t\t\t\t\tr[op.to] = r[op.from];\n\t\t\t\t\t\t\tdelete r[op.from];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tjson.meta.updatedAt = new Date().toISOString();\n\n\t\tif (skipWrite) {\n\t\t\t// Transaction mode: update transaction cache\n\t\t\tthis.activeTransactionCache!.set(tableName, {\n\t\t\t\tdata: json,\n\t\t\t\tmtime: Date.now(),\n\t\t\t});\n\t\t\tthis.activeTransactionModifiedTables!.add(tableName);\n\t\t} else {\n\t\t\t// Normal mode: write to disk\n\t\t\tconst filePath = this.getTablePath(tableName);\n\t\t\tawait fs.writeFile(filePath, JSON.stringify(json, null, 2), \"utf-8\");\n\t\t\tawait this.updateCache(tableName, json);\n\t\t}\n\n\t\t// Update schema in _datrix\n\t\tif (tableName !== DATRIX_META_MODEL) {\n\t\t\tawait this.applyOperationsToMetaSchema(tableName, operations, skipWrite);\n\t\t}\n\t}\n\n\tasync renameTable(from: string, to: string): Promise<void> {\n\t\treturn this.renameTableWithOptions(from, to);\n\t}\n\n\t/**\n\t * Rename table with options (for transaction support)\n\t */\n\tasync renameTableWithOptions(\n\t\tfrom: string,\n\t\tto: string,\n\t\toptions?: SchemaOperationOptions,\n\t): Promise<void> {\n\t\tconst skipWrite = options?.skipWrite ?? false;\n\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\n\t\tvalidateTableName(to);\n\n\t\t// Check source table exists\n\t\tif (this.activeTransactionDeletedTables?.has(from)) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${from}' does not exist`,\n\t\t\t});\n\t\t}\n\n\t\t// Check target table doesn't exist\n\t\tconst targetExistsInTxCache = this.activeTransactionCache?.has(to);\n\t\tconst targetExistsInMainCache = this.cache.has(to);\n\t\tlet targetExistsOnDisk = false;\n\n\t\tif (!targetExistsInTxCache && !targetExistsInMainCache) {\n\t\t\tconst toPath = this.getTablePath(to);\n\t\t\ttry {\n\t\t\t\tawait fs.access(toPath);\n\t\t\t\ttargetExistsOnDisk = true;\n\t\t\t} catch {\n\t\t\t\t// Not on disk - good\n\t\t\t}\n\t\t}\n\n\t\t// Target exists and not in tombstone = error\n\t\tconst targetInTombstone = this.activeTransactionDeletedTables?.has(to);\n\t\tif (\n\t\t\t(targetExistsInTxCache ||\n\t\t\t\ttargetExistsInMainCache ||\n\t\t\t\ttargetExistsOnDisk) &&\n\t\t\t!targetInTombstone\n\t\t) {\n\t\t\tthrowMigrationError({\n\t\t\t\tadapter: \"json\",\n\t\t\t\tmessage: `Table '${to}' already exists`,\n\t\t\t});\n\t\t}\n\n\t\t// Read source table (will throw if doesn't exist)\n\t\tconst json = await this.readTable(from);\n\t\tjson.meta.updatedAt = new Date().toISOString();\n\n\t\tif (skipWrite) {\n\t\t\t// Transaction mode: add new table to cache, tombstone old table\n\t\t\tthis.activeTransactionCache!.set(to, {\n\t\t\t\tdata: json,\n\t\t\t\tmtime: Date.now(),\n\t\t\t});\n\t\t\tthis.activeTransactionModifiedTables!.add(to);\n\t\t\tthis.activeTransactionDeletedTables!.add(from);\n\t\t\tthis.activeTransactionCache!.delete(from);\n\t\t\t// Remove target from tombstone if it was there (we're overwriting)\n\t\t\tthis.activeTransactionDeletedTables!.delete(to);\n\t\t} else {\n\t\t\t// Normal mode: rename file on disk\n\t\t\tconst fromPath = this.getTablePath(from);\n\t\t\tconst toPath = this.getTablePath(to);\n\t\t\tawait fs.writeFile(toPath, JSON.stringify(json, null, 2), \"utf-8\");\n\t\t\tawait fs.unlink(fromPath);\n\t\t\tthis.invalidateCache(from);\n\t\t\tawait this.updateCache(to, json);\n\t\t}\n\n\t\t// Update key in _datrix\n\t\tif (from !== DATRIX_META_MODEL && to !== DATRIX_META_MODEL) {\n\t\t\tconst oldKey = `${DATRIX_META_KEY_PREFIX}${from}`;\n\t\t\tconst newKey = `${DATRIX_META_KEY_PREFIX}${to}`;\n\t\t\tconst metaFile = await this.readTable(DATRIX_META_MODEL);\n\t\t\tconst row = metaFile.data.find(\n\t\t\t\t(r) => (r as Record<string, unknown>)[\"key\"] === oldKey,\n\t\t\t);\n\t\t\tif (row) {\n\t\t\t\t(row as Record<string, unknown>)[\"key\"] = newKey;\n\t\t\t\tmetaFile.meta.updatedAt = new Date().toISOString();\n\n\t\t\t\tif (skipWrite) {\n\t\t\t\t\tthis.activeTransactionCache!.set(DATRIX_META_MODEL, {\n\t\t\t\t\t\tdata: metaFile,\n\t\t\t\t\t\tmtime: Date.now(),\n\t\t\t\t\t});\n\t\t\t\t\tthis.activeTransactionModifiedTables!.add(DATRIX_META_MODEL);\n\t\t\t\t} else {\n\t\t\t\t\tconst metaPath = this.getTablePath(DATRIX_META_MODEL);\n\t\t\t\t\tawait fs.writeFile(\n\t\t\t\t\t\tmetaPath,\n\t\t\t\t\t\tJSON.stringify(metaFile, null, 2),\n\t\t\t\t\t\t\"utf-8\",\n\t\t\t\t\t);\n\t\t\t\t\tawait this.updateCache(DATRIX_META_MODEL, metaFile);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tasync addIndex(tableName: string, index: IndexDefinition): Promise<void> {\n\t\treturn this.addIndexWithOptions(tableName, index);\n\t}\n\n\t/**\n\t * Add index with options (for transaction support)\n\t * Note: JSON adapter doesn't actually create indexes, but we track the operation\n\t */\n\tasync addIndexWithOptions(\n\t\t_tableName: string,\n\t\t_index: IndexDefinition,\n\t\t_options?: SchemaOperationOptions,\n\t): Promise<void> {}\n\n\tasync dropIndex(tableName: string, indexName: string): Promise<void> {\n\t\treturn this.dropIndexWithOptions(tableName, indexName);\n\t}\n\n\t/**\n\t * Drop index with options (for transaction support)\n\t * Note: JSON adapter doesn't actually manage indexes, but we track the operation\n\t */\n\tasync dropIndexWithOptions(\n\t\t_tableName: string,\n\t\t_indexName: string,\n\t\t_options?: SchemaOperationOptions,\n\t): Promise<void> {}\n\n\tasync getTables(): Promise<readonly string[]> {\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\t\tconst files = await fs.readdir(this.config.root);\n\t\tconst tables = files\n\t\t\t.filter((f) => f.endsWith(\".json\"))\n\t\t\t.map((f) => f.replace(\".json\", \"\"));\n\t\treturn tables;\n\t}\n\n\tasync getTableSchema(tableName: string): Promise<SchemaDefinition | null> {\n\t\tif (!this.isConnected()) {\n\t\t\tthrowNotConnected({ adapter: \"json\" });\n\t\t}\n\t\ttry {\n\t\t\tconst schema = await this.readTableSchema(tableName);\n\t\t\treturn schema;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync tableExists(tableName: string): Promise<boolean> {\n\t\tif (!this.isConnected()) return false;\n\t\ttry {\n\t\t\tawait fs.access(this.getTablePath(tableName));\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","import {\n\tWhereClause,\n\tFallbackOrderByItem,\n\tComparisonOperators,\n\tQuerySelectObject,\n\tQuerySelect,\n\tQueryCountObject,\n} from \"@datrix/core\";\nimport {\n\tDatrixEntry,\n\tDatrixRecord,\n\tRelationField,\n\tSchemaDefinition,\n} from \"@datrix/core\";\nimport { JsonTableFile } from \"./types\";\nimport type { JsonAdapter } from \"./adapter\";\nimport {\n\tthrowInvalidRelationWhereSyntax,\n\tthrowInvalidWhereField,\n} from \"@datrix/core\";\n\nexport class JsonQueryRunner {\n\tprivate schema: SchemaDefinition | undefined;\n\n\tconstructor(\n\t\tprivate table: JsonTableFile,\n\t\tprivate adapter: JsonAdapter,\n\t\tschema?: SchemaDefinition,\n\t) {\n\t\tthis.schema = schema;\n\t}\n\n\tget tableData(): JsonTableFile {\n\t\treturn this.table;\n\t}\n\n\tget tableSchema(): SchemaDefinition | undefined {\n\t\treturn this.schema;\n\t}\n\n\tget adapterRef(): JsonAdapter {\n\t\treturn this.adapter;\n\t}\n\n\tasync run<T extends DatrixEntry = DatrixRecord>(\n\t\tquery: QuerySelectObject<T> | QueryCountObject<T>,\n\t): Promise<Partial<T>[]> {\n\t\tlet result = this.table.data as T[];\n\n\t\t// 1. Filter (async for nested relation WHERE support)\n\t\tif (query.where) {\n\t\t\tconst matchResults = await Promise.all(\n\t\t\t\tresult.map((item) => this.match(item, query.where!)),\n\t\t\t);\n\t\t\tresult = result.filter((_, i) => matchResults[i]);\n\t\t} else if (\n\t\t\tquery.type === \"select\" &&\n\t\t\tquery.orderBy &&\n\t\t\tquery.orderBy.length > 0\n\t\t) {\n\t\t\t// No filter but need sort - must copy to avoid mutating original\n\t\t\tresult = [...result];\n\t\t}\n\n\t\tif (query.type === \"count\") {\n\t\t\treturn result;\n\t\t}\n\n\t\t// 3. Project & Distinct\n\t\tif (query.select || query.distinct) {\n\t\t\tresult = this.project(result, query.select, query.distinct) as T[];\n\t\t}\n\n\t\t// 4. Sort (mutates array in-place)\n\t\tif (query.orderBy && query.orderBy.length > 0) {\n\t\t\tresult.sort((a, b) =>\n\t\t\t\tthis.sort(\n\t\t\t\t\ta as Record<string, unknown>,\n\t\t\t\t\tb as Record<string, unknown>,\n\t\t\t\t\tquery.orderBy!,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// 5. Offset/Limit\n\t\tconst offset = query.offset ?? 0;\n\n\t\tif (query.limit !== undefined) {\n\t\t\tresult = result.slice(offset, offset + query.limit);\n\t\t} else if (offset > 0) {\n\t\t\tresult = result.slice(offset);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Run query without projection (for populate workflow)\n\t * Applies WHERE, ORDER BY, OFFSET, LIMIT but keeps all fields\n\t */\n\tasync filterAndSort<T extends DatrixEntry>(\n\t\tquery: QuerySelectObject<T>,\n\t): Promise<T[]> {\n\t\tlet result = this.table.data as T[];\n\n\t\t// 1. Filter (async for nested relation WHERE support)\n\t\tif (query.where) {\n\t\t\tconst matchResults = await Promise.all(\n\t\t\t\tresult.map((item) => this.match(item, query.where!)),\n\t\t\t);\n\t\t\tresult = result.filter((_, i) => matchResults[i]);\n\t\t} else if (query.orderBy && query.orderBy.length > 0) {\n\t\t\t// No filter but need sort - must copy to avoid mutating original\n\t\t\tresult = [...result];\n\t\t}\n\n\t\t// 2. Sort (mutates array in-place)\n\t\tif (query.orderBy && query.orderBy.length > 0) {\n\t\t\tresult.sort((a, b) =>\n\t\t\t\tthis.sort(\n\t\t\t\t\ta as Record<string, unknown>,\n\t\t\t\t\tb as Record<string, unknown>,\n\t\t\t\t\tquery.orderBy!,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// 3. Offset/Limit\n\t\tconst offset = query.offset ?? 0;\n\n\t\tif (query.limit !== undefined) {\n\t\t\tresult = result.slice(offset, offset + query.limit);\n\t\t} else if (offset > 0) {\n\t\t\tresult = result.slice(offset);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t// Exposed for Adapter's RETURNING clause usage\n\tpublic projectData<T extends DatrixEntry>(\n\t\tdata: T[],\n\t\tselect?: QuerySelect<T>,\n\t\tdistinct?: boolean,\n\t): Partial<T>[] {\n\t\treturn this.project(data, select, distinct);\n\t}\n\n\tprivate project<T extends DatrixEntry>(\n\t\tdata: T[],\n\t\tselect?: QuerySelect<T>,\n\t\tdistinct?: boolean,\n\t): Partial<T>[] {\n\t\tlet result: any[] = data;\n\n\t\t// Projection\n\t\tif (select && (select as unknown as string) !== \"*\") {\n\t\t\tresult = data.map((item) => {\n\t\t\t\tconst projected: any = {};\n\t\t\t\tfor (const field of select) {\n\t\t\t\t\tprojected[field] = item[field as keyof T];\n\t\t\t\t\tif (projected[field] === undefined) {\n\t\t\t\t\t\tprojected[field] = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn projected;\n\t\t\t});\n\t\t}\n\n\t\t// Distinct\n\t\tif (distinct) {\n\t\t\tconst seen = new Set<string>();\n\t\t\tresult = result.filter((item) => {\n\t\t\t\tconst key = JSON.stringify(item); // Simple serialization for distinct check\n\t\t\t\tif (seen.has(key)) return false;\n\t\t\t\tseen.add(key);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Match WHERE clause against an item\n\t *\n\t * **NEW:** Now supports nested relation WHERE queries!\n\t *\n\t * @param item - The record to match against\n\t * @param where - WHERE clause (may contain nested relation conditions)\n\t * @param overrideSchema - Optional schema to use instead of this.table.schema (for nested relation matching)\n\t * @returns True if item matches all conditions\n\t *\n\t * @example\n\t * ```ts\n\t * // Simple WHERE\n\t * await match(item, { price: { $gt: 10 } })\n\t *\n\t * // Nested relation WHERE\n\t * await match(item, {\n\t * author: { // Relation field\n\t * verified: { $eq: true }\n\t * }\n\t * })\n\t * ```\n\t */\n\tprivate async match<T extends DatrixEntry>(\n\t\titem: any,\n\t\twhere: WhereClause<T>,\n\t\toverrideSchema?: SchemaDefinition,\n\t): Promise<boolean> {\n\t\tconst schema = overrideSchema ?? this.schema;\n\n\t\tfor (const [key, value] of Object.entries(where)) {\n\t\t\t// Handle logical operators\n\t\t\tif (key === \"$and\") {\n\t\t\t\tconst results = await Promise.all(\n\t\t\t\t\t(value as WhereClause<T>[]).map((cond) =>\n\t\t\t\t\t\tthis.match(item, cond, schema),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (!results.every((r) => r)) return false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (key === \"$or\") {\n\t\t\t\tconst results = await Promise.all(\n\t\t\t\t\t(value as WhereClause<T>[]).map((cond) =>\n\t\t\t\t\t\tthis.match(item, cond, schema),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (!results.some((r) => r)) return false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (key === \"$not\") {\n\t\t\t\tif (await this.match(item, value as WhereClause<T>, schema))\n\t\t\t\t\treturn false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// ✨ NEW: Check if this field is a RELATION\n\t\t\tconst fieldDef = schema?.fields?.[key];\n\t\t\tif (fieldDef?.type === \"relation\") {\n\t\t\t\t// This is a nested relation WHERE!\n\t\t\t\tconst matched = await this.matchRelation(\n\t\t\t\t\titem,\n\t\t\t\t\tkey,\n\t\t\t\t\tvalue as WhereClause<T>,\n\t\t\t\t\tfieldDef,\n\t\t\t\t);\n\t\t\t\tif (!matched) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Regular field matching (existing logic)\n\t\t\t// Validate that field exists in schema (catch typos and invalid fields)\n\t\t\tif (schema && !schema.fields[key]) {\n\t\t\t\tthrowInvalidWhereField({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tfield: key,\n\t\t\t\t\tschemaName: schema.name,\n\t\t\t\t\tavailableFields: Object.keys(schema.fields),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst itemValue = item[key];\n\n\t\t\tif (value === null) {\n\t\t\t\tif (itemValue !== null && itemValue !== undefined) return false;\n\t\t\t} else if (\n\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\t!Array.isArray(value) &&\n\t\t\t\t!(value instanceof Date)\n\t\t\t) {\n\t\t\t\t// Check if this is a ComparisonOperators object or nested WHERE\n\t\t\t\t// If it has operator keys ($eq, $gt, etc.), it's operators\n\t\t\t\tconst isOperators = Object.keys(value).some((k) => k.startsWith(\"$\"));\n\t\t\t\tif (isOperators) {\n\t\t\t\t\t// Operators\n\t\t\t\t\tif (\n\t\t\t\t\t\t!this.matchOperators(itemValue, value as ComparisonOperators, key)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\t// Not operators and not a relation - treat as direct equality\n\t\t\t\t\tif (!this.compareValues(itemValue, value, key)) return false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Direct equality - type-aware comparison\n\t\t\t\tif (!this.compareValues(itemValue, value, key)) return false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Match nested relation WHERE\n\t *\n\t * Loads the related record(s) and recursively matches the nested WHERE clause.\n\t *\n\t * @param item - Current record\n\t * @param relationName - Name of the relation field\n\t * @param relationWhere - Nested WHERE clause for the relation\n\t * @param relationField - Relation field definition\n\t * @returns True if relation matches\n\t */\n\tprivate async matchRelation<T extends DatrixEntry>(\n\t\titem: any,\n\t\trelationName: string,\n\t\trelationWhere: WhereClause<T>,\n\t\trelationField: RelationField,\n\t): Promise<boolean> {\n\t\tconst foreignKey = relationField.foreignKey!;\n\t\tconst targetModelName = relationField.model;\n\t\tconst kind = relationField.kind;\n\n\t\t// Validate: Ensure relationWhere is not using comparison operators directly\n\t\t// Valid: { user: { id: { $eq: 1 } } }\n\t\t// Valid: { user: { $and: [{ id: { $eq: 1 } }] } } (logical operator)\n\t\t// Invalid: { user: { $eq: 1 } } (comparison operator)\n\t\tif (typeof relationWhere === \"object\" && relationWhere !== null) {\n\t\t\tconst keys = Object.keys(relationWhere);\n\t\t\tconst logicalOps = new Set([\"$and\", \"$or\", \"$not\"]);\n\t\t\tconst hasOnlyComparisonOperators =\n\t\t\t\tkeys.length > 0 &&\n\t\t\t\tkeys.every((k) => k.startsWith(\"$\")) &&\n\t\t\t\t!keys.some((k) => logicalOps.has(k));\n\n\t\t\tif (hasOnlyComparisonOperators) {\n\t\t\t\tthrowInvalidRelationWhereSyntax({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\trelationName,\n\t\t\t\t\tschemaName: this.schema?.name ?? \"unknown\",\n\t\t\t\t\tforeignKey,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Get related ID(s) from current record\n\t\tif (kind === \"belongsTo\" || kind === \"hasOne\") {\n\t\t\t// Single relation - check FK value\n\t\t\tconst relatedId = item[foreignKey];\n\t\t\tif (relatedId === null || relatedId === undefined) {\n\t\t\t\t// No relation - doesn't match\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Get target schema for validation\n\t\t\tconst targetSchema =\n\t\t\t\tawait this.adapter.getSchemaByModelName(targetModelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Load related record\n\t\t\tconst relatedRecord = await this.loadRelatedRecord(\n\t\t\t\ttargetModelName,\n\t\t\t\trelatedId,\n\t\t\t);\n\t\t\tif (!relatedRecord) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Recursively match nested WHERE on related record with target schema\n\t\t\treturn await this.match(relatedRecord, relationWhere, targetSchema);\n\t\t}\n\n\t\tif (kind === \"hasMany\") {\n\t\t\t// Target table holds the FK pointing back to this record\n\t\t\tconst sourceId = item[\"id\"] as number | string | undefined;\n\t\t\tif (sourceId === null || sourceId === undefined) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst targetSchema =\n\t\t\t\tawait this.adapter.getSchemaByModelName(targetModelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst targetTable =\n\t\t\t\ttargetSchema.tableName ?? targetModelName.toLowerCase();\n\t\t\tconst targetTableData = await this.adapter.getCachedTable(targetTable);\n\t\t\tif (!targetTableData) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Use the foreignKey directly from the relation definition.\n\t\t\t// Schema authors must specify explicit foreignKey when the inverse\n\t\t\t// belongsTo uses a different name (e.g. foreignKey: \"authorId\").\n\t\t\tconst relatedRecords = (\n\t\t\t\ttargetTableData.data as Record<string, unknown>[]\n\t\t\t).filter(\n\t\t\t\t(r) => r[foreignKey] === sourceId || r[foreignKey] === Number(sourceId),\n\t\t\t);\n\n\t\t\tfor (const related of relatedRecords) {\n\t\t\t\tconst matches = await this.match(related, relationWhere, targetSchema);\n\t\t\t\tif (matches) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tif (kind === \"manyToMany\") {\n\t\t\t// Junction table bridges this record and target records\n\t\t\tconst junctionTableName = relationField.through;\n\t\t\tif (!junctionTableName) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst sourceId = item[\"id\"] as number | string | undefined;\n\t\t\tif (sourceId === null || sourceId === undefined) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst junctionTableData =\n\t\t\t\tawait this.adapter.getCachedTable(junctionTableName);\n\t\t\tif (!junctionTableData) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Determine FK column names in junction table (e.g. userId, roleId)\n\t\t\tconst currentModelName = this.schema?.name ?? \"\";\n\t\t\tconst sourceFK = `${currentModelName}Id`;\n\t\t\tconst targetFK = `${targetModelName}Id`;\n\n\t\t\t// Collect target IDs from junction rows matching this source\n\t\t\tconst targetIds = (junctionTableData.data as Record<string, unknown>[])\n\t\t\t\t.filter((row) => {\n\t\t\t\t\tconst rowSourceId = row[sourceFK];\n\t\t\t\t\treturn rowSourceId === sourceId || rowSourceId === Number(sourceId);\n\t\t\t\t})\n\t\t\t\t.map((row) => {\n\t\t\t\t\tconst rawId = row[targetFK];\n\t\t\t\t\treturn typeof rawId === \"string\" ? Number(rawId) : (rawId as number);\n\t\t\t\t})\n\t\t\t\t.filter((id): id is number => id !== null && id !== undefined);\n\n\t\t\tif (targetIds.length === 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst targetSchema =\n\t\t\t\tawait this.adapter.getSchemaByModelName(targetModelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst targetTable =\n\t\t\t\ttargetSchema.tableName ?? targetModelName.toLowerCase();\n\t\t\tconst targetTableData = await this.adapter.getCachedTable(targetTable);\n\t\t\tif (!targetTableData) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Check if any target record matches the nested WHERE\n\t\t\tconst targetRecords = (\n\t\t\t\ttargetTableData.data as Record<string, unknown>[]\n\t\t\t).filter((r) => targetIds.includes(r[\"id\"] as number));\n\n\t\t\tfor (const target of targetRecords) {\n\t\t\t\tconst matches = await this.match(target, relationWhere, targetSchema);\n\t\t\t\tif (matches) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Load a related record from adapter's cache\n\t *\n\t * Uses getCachedTable which reads from cache or disk if needed.\n\t *\n\t * @param modelName - Target model name\n\t * @param id - Record ID to load\n\t * @returns Related record or null\n\t */\n\tprivate async loadRelatedRecord(\n\t\tmodelName: string,\n\t\tid: string | number,\n\t): Promise<Record<string, unknown> | null> {\n\t\ttry {\n\t\t\t// Get target schema from adapter (cache-aware, no Datrix dependency)\n\t\t\tconst targetSchema = await this.adapter.getSchemaByModelName(modelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst targetTable = targetSchema.tableName ?? modelName.toLowerCase();\n\n\t\t\t// Get table data from adapter's cache (async - reads from disk if cache stale)\n\t\t\tconst tableData = await this.adapter.getCachedTable(targetTable);\n\t\t\tif (!tableData) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Find record by ID\n\t\t\tconst relatedData = tableData.data as Record<string, unknown>[];\n\t\t\tconst record = relatedData.find((r) => r[\"id\"] === id);\n\n\t\t\treturn record ?? null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate compareValues(\n\t\titemValue: any,\n\t\tqueryValue: any,\n\t\tfieldName: string,\n\t): boolean {\n\t\tconst schema = this.schema as any;\n\t\tconst fieldDef = schema?.fields?.[fieldName];\n\n\t\t// No schema or field definition - use strict equality\n\t\tif (!fieldDef) {\n\t\t\treturn itemValue === queryValue;\n\t\t}\n\n\t\t// Type coercion based on field type\n\t\tconst fieldType = fieldDef.type;\n\n\t\tif (fieldType === \"number\") {\n\t\t\tconst itemNum = Number(itemValue);\n\t\t\tconst queryNum = Number(queryValue);\n\t\t\treturn !isNaN(itemNum) && !isNaN(queryNum) && itemNum === queryNum;\n\t\t}\n\n\t\tif (fieldType === \"string\") {\n\t\t\treturn String(itemValue) === String(queryValue);\n\t\t}\n\n\t\tif (fieldType === \"boolean\") {\n\t\t\treturn Boolean(itemValue) === Boolean(queryValue);\n\t\t}\n\n\t\t// Default: strict equality\n\t\treturn itemValue === queryValue;\n\t}\n\n\tprivate matchOperators(\n\t\tvalue: any,\n\t\toperators: ComparisonOperators,\n\t\tfieldName: string,\n\t): boolean {\n\t\tfor (const [op, opValue] of Object.entries(operators)) {\n\t\t\tswitch (op) {\n\t\t\t\tcase \"$eq\":\n\t\t\t\t\tif (!this.compareValues(value, opValue, fieldName)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$ne\":\n\t\t\t\t\tif (this.compareValues(value, opValue, fieldName)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$gt\": {\n\t\t\t\t\t// NULL comparisons always return false (SQL behavior)\n\t\t\t\t\tif (value === null || value === undefined) return false;\n\t\t\t\t\tconst coercedVal = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedOp = this.coerceForComparison(opValue, fieldName);\n\t\t\t\t\tif (!((coercedVal as number) > (coercedOp as number))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$gte\": {\n\t\t\t\t\tif (value === null || value === undefined) return false;\n\t\t\t\t\tconst coercedVal = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedOp = this.coerceForComparison(opValue, fieldName);\n\t\t\t\t\tif (!((coercedVal as number) >= (coercedOp as number))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$lt\": {\n\t\t\t\t\tif (value === null || value === undefined) return false;\n\t\t\t\t\tconst coercedVal = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedOp = this.coerceForComparison(opValue, fieldName);\n\t\t\t\t\tif (!((coercedVal as number) < (coercedOp as number))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$lte\": {\n\t\t\t\t\tif (value === null || value === undefined) return false;\n\t\t\t\t\tconst coercedVal = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedOp = this.coerceForComparison(opValue, fieldName);\n\t\t\t\t\tif (!((coercedVal as number) <= (coercedOp as number))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$in\": {\n\t\t\t\t\tconst coercedValue = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedArray = (opValue as unknown[]).map((v) =>\n\t\t\t\t\t\tthis.coerceForComparison(v, fieldName),\n\t\t\t\t\t);\n\t\t\t\t\tif (!coercedArray.includes(coercedValue)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$nin\": {\n\t\t\t\t\tconst coercedValue = this.coerceForComparison(value, fieldName);\n\t\t\t\t\tconst coercedArray = (opValue as unknown[]).map((v) =>\n\t\t\t\t\t\tthis.coerceForComparison(v, fieldName),\n\t\t\t\t\t);\n\t\t\t\t\tif (coercedArray.includes(coercedValue)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$exists\":\n\t\t\t\t\tif (opValue && (value === undefined || value === null)) return false;\n\t\t\t\t\tif (!opValue && value !== undefined && value !== null) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$null\":\n\t\t\t\t\t// Checks if value is null or undefined (no value)\n\t\t\t\t\tif (opValue && value !== null && value !== undefined) return false;\n\t\t\t\t\tif (!opValue && (value === null || value === undefined)) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$like\":\n\t\t\t\tcase \"$ilike\": {\n\t\t\t\t\tconst pattern = (opValue as string)\n\t\t\t\t\t\t.replace(/%/g, \".*\")\n\t\t\t\t\t\t.replace(/_/g, \".\");\n\t\t\t\t\tconst flags = op === \"$ilike\" ? \"i\" : \"\";\n\t\t\t\t\tconst regex = new RegExp(`^${pattern}$`, flags);\n\t\t\t\t\tif (!regex.test(String(value ?? \"\"))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"$contains\":\n\t\t\t\t\tif (!String(value ?? \"\").includes(String(opValue))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$notContains\":\n\t\t\t\t\tif (String(value ?? \"\").includes(String(opValue))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$startsWith\":\n\t\t\t\t\tif (!String(value ?? \"\").startsWith(String(opValue))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$endsWith\":\n\t\t\t\t\tif (!String(value ?? \"\").endsWith(String(opValue))) return false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"$notNull\":\n\t\t\t\t\t// Checks if value is NOT null/undefined (has value)\n\t\t\t\t\tif (opValue && (value === null || value === undefined)) return false;\n\t\t\t\t\tif (!opValue && value !== null && value !== undefined) return false;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate coerceForComparison(value: unknown, fieldName: string): unknown {\n\t\t// Preserve null/undefined as-is\n\t\tif (value === null || value === undefined) {\n\t\t\treturn value;\n\t\t}\n\n\t\tconst schema = this.schema as {\n\t\t\tfields?: Record<string, { type?: string }>;\n\t\t};\n\t\tconst fieldDef = schema?.fields?.[fieldName];\n\n\t\tif (!fieldDef) return value;\n\n\t\tconst fieldType = fieldDef.type;\n\n\t\tif (fieldType === \"number\") {\n\t\t\tconst num = Number(value);\n\t\t\treturn isNaN(num) ? value : num;\n\t\t}\n\n\t\tif (fieldType === \"string\") {\n\t\t\treturn String(value);\n\t\t}\n\n\t\treturn value;\n\t}\n\n\tprivate sort(\n\t\ta: Record<string, unknown>,\n\t\tb: Record<string, unknown>,\n\t\torderBy: readonly FallbackOrderByItem[],\n\t): number {\n\t\tfor (const order of orderBy) {\n\t\t\tconst fieldName = order.field;\n\t\t\tconst valA = this.coerceForComparison(a[fieldName], fieldName);\n\t\t\tconst valB = this.coerceForComparison(b[fieldName], fieldName);\n\n\t\t\tif (valA === valB) continue;\n\n\t\t\tconst direction = order.direction === \"asc\" ? 1 : -1;\n\n\t\t\tif (valA === null || valA === undefined)\n\t\t\t\treturn order.nulls === \"first\" ? -1 : 1;\n\t\t\tif (valB === null || valB === undefined)\n\t\t\t\treturn order.nulls === \"first\" ? 1 : -1;\n\n\t\t\tif (valA < valB) return -1 * direction;\n\t\t\tif (valA > valB) return 1 * direction;\n\t\t}\n\t\treturn 0;\n\t}\n}\n","import { throwLockTimeout } from \"@datrix/core\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport class SimpleLock {\n\tprivate lockPath: string;\n\tprivate lockTimeout: number; // How long to wait to acquire lock\n\tprivate staleTimeout: number; // How long a lock is valid\n\n\tconstructor(\n\t\troot: string,\n\t\tlockTimeout: number = 5000,\n\t\tstaleTimeout: number = 30000,\n\t) {\n\t\tthis.lockPath = path.join(root, \"db.lock\");\n\t\tthis.lockTimeout = lockTimeout;\n\t\tthis.staleTimeout = staleTimeout;\n\t}\n\n\tasync acquire(): Promise<void> {\n\t\tconst start = Date.now();\n\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\t// Try to create the lock file (fails if exists)\n\t\t\t\t// \"wx\" flag: Open file for writing. The file is created (if it does not exist) or fails (if it exists).\n\t\t\t\tawait fs.writeFile(this.lockPath, Date.now().toString(), {\n\t\t\t\t\tflag: \"wx\",\n\t\t\t\t});\n\t\t\t\treturn; // Acquired!\n\t\t\t} catch (error: any) {\n\t\t\t\tif (error.code !== \"EEXIST\") {\n\t\t\t\t\tthrow error; // Unexpected error\n\t\t\t\t}\n\n\t\t\t\t// Lock exists. Check if stale.\n\t\t\t\tconst isStale = await this.checkStale();\n\t\t\t\tif (isStale) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait fs.unlink(this.lockPath);\n\t\t\t\t\t\tcontinue; // Retry immediately\n\t\t\t\t\t} catch (unlinkError) {\n\t\t\t\t\t\t// Could happen if another process released it just now.\n\t\t\t\t\t\t// Just continue loop to try acquiring again.\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check timeout\n\t\t\t\tif (Date.now() - start > this.lockTimeout) {\n\t\t\t\t\tthrowLockTimeout({ adapter: \"json\", lockTimeout: this.lockTimeout });\n\t\t\t\t}\n\n\t\t\t\t// Wait a bit before retrying\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10)); // 10ms poll\n\t\t\t}\n\t\t}\n\t}\n\n\tasync release(): Promise<void> {\n\t\ttry {\n\t\t\tawait fs.unlink(this.lockPath);\n\t\t} catch (error: any) {\n\t\t\t// Ignore if file doesn't exist (maybe already released or stolen?)\n\t\t\tif (error.code !== \"ENOENT\") {\n\t\t\t\t// warning?\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async checkStale(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst content = await fs.readFile(this.lockPath, \"utf-8\");\n\t\t\tconst timestamp = parseInt(content, 10);\n\t\t\tif (isNaN(timestamp)) return true; // Corrupt lock\n\n\t\t\tconst age = Date.now() - timestamp;\n\t\t\treturn age > this.staleTimeout;\n\t\t} catch {\n\t\t\t// If can't read (e.g. deleted while checking), assume free/stale logic handles it by retry loop\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","/**\n * JSON Adapter Transaction Implementation\n *\n * Provides ACID-like transaction support for JSON file adapter.\n *\n * Strategy:\n * - BEGIN: Adapter acquires lock, creates isolated txCache\n * - QUERY: Adapter's readTable/writeTable automatically use txCache\n * - COMMIT: Adapter writes modified tables to disk, merges to main cache\n * - ROLLBACK: Adapter discards txCache, releases lock\n *\n * The transaction object is a thin wrapper that delegates to adapter.\n */\n\nimport { Transaction, QueryResult, AlterOperation } from \"@datrix/core\";\nimport {\n\tthrowTransactionAlreadyCommitted,\n\tthrowTransactionAlreadyRolledBack,\n\tthrowTransactionSavepointNotSupported,\n\tthrowRawQueryNotSupported,\n} from \"@datrix/core\";\nimport { QueryObject } from \"@datrix/core\";\nimport { DatrixEntry, IndexDefinition, SchemaDefinition } from \"@datrix/core\";\nimport type { JsonAdapter } from \"./adapter\";\n\n/**\n * JSON Transaction\n *\n * Thin wrapper around adapter that executes queries with transaction options.\n * Actual transaction state is managed by the adapter.\n */\nexport class JsonTransaction implements Transaction {\n\treadonly id: string;\n\n\tprivate committed = false;\n\tprivate rolledBack = false;\n\n\tconstructor(\n\t\tprivate adapter: JsonAdapter,\n\t\tprivate commitCallback: () => Promise<void>,\n\t\tprivate rollbackCallback: () => Promise<void>,\n\t) {\n\t\tthis.id = `tx_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;\n\t}\n\n\t/**\n\t * Check if transaction is still active\n\t */\n\tprivate assertActive(): void {\n\t\tif (this.committed) {\n\t\t\tthrowTransactionAlreadyCommitted({ adapter: \"json\" });\n\t\t}\n\t\tif (this.rolledBack) {\n\t\t\tthrowTransactionAlreadyRolledBack({ adapter: \"json\" });\n\t\t}\n\t}\n\n\t/**\n\t * Execute query within transaction\n\t *\n\t * Uses adapter's executeQueryWithOptions with skipLock and skipWrite.\n\t * Adapter automatically uses transaction cache.\n\t */\n\tasync executeQuery<TResult extends DatrixEntry>(\n\t\tquery: QueryObject<TResult>,\n\t): Promise<QueryResult<TResult>> {\n\t\tthis.assertActive();\n\n\t\treturn this.adapter.executeQueryWithOptions<TResult>(query, {\n\t\t\tskipLock: true,\n\t\t\tskipWrite: true,\n\t\t});\n\t}\n\n\t/**\n\t * Execute raw SQL query (not supported for JSON adapter)\n\t */\n\tasync executeRawQuery<TResult extends DatrixEntry>(\n\t\t_sql: string,\n\t\t_params: readonly unknown[],\n\t): Promise<QueryResult<TResult>> {\n\t\tthrowRawQueryNotSupported({ adapter: \"json\" });\n\t}\n\n\t// ========================================\n\t// SchemaOperations implementation\n\t// ========================================\n\n\t/**\n\t * Create table within transaction\n\t */\n\tasync createTable(schema: SchemaDefinition): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.createTableWithOptions(schema, { skipWrite: true });\n\t}\n\n\t/**\n\t * Drop table within transaction\n\t */\n\tasync dropTable(tableName: string): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.dropTableWithOptions(tableName, { skipWrite: true });\n\t}\n\n\t/**\n\t * Rename table within transaction\n\t */\n\tasync renameTable(from: string, to: string): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.renameTableWithOptions(from, to, { skipWrite: true });\n\t}\n\n\t/**\n\t * Alter table within transaction\n\t */\n\tasync alterTable(\n\t\ttableName: string,\n\t\toperations: readonly AlterOperation[],\n\t): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.alterTableWithOptions(tableName, operations, {\n\t\t\tskipWrite: true,\n\t\t});\n\t}\n\n\t/**\n\t * Add index within transaction\n\t */\n\tasync addIndex(tableName: string, index: IndexDefinition): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.addIndexWithOptions(tableName, index, {\n\t\t\tskipWrite: true,\n\t\t});\n\t}\n\n\t/**\n\t * Drop index within transaction\n\t */\n\tasync dropIndex(tableName: string, indexName: string): Promise<void> {\n\t\tthis.assertActive();\n\t\treturn this.adapter.dropIndexWithOptions(tableName, indexName, {\n\t\t\tskipWrite: true,\n\t\t});\n\t}\n\n\t/**\n\t * Commit transaction\n\t *\n\t * Delegates to adapter's commitTransaction which writes to disk.\n\t */\n\tasync commit(): Promise<void> {\n\t\tthis.assertActive();\n\t\tawait this.commitCallback();\n\t\tthis.committed = true;\n\t}\n\n\t/**\n\t * Rollback transaction\n\t *\n\t * Delegates to adapter's rollbackTransaction which discards changes.\n\t */\n\tasync rollback(): Promise<void> {\n\t\tif (this.committed) {\n\t\t\tthrowTransactionAlreadyCommitted({ adapter: \"json\" });\n\t\t}\n\n\t\tif (this.rolledBack) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.rollbackCallback();\n\t\tthis.rolledBack = true;\n\t}\n\n\t/**\n\t * Create savepoint (not yet implemented for JSON adapter)\n\t */\n\tasync savepoint(_name: string): Promise<void> {\n\t\tthrowTransactionSavepointNotSupported({ adapter: \"json\" });\n\t}\n\n\t/**\n\t * Rollback to savepoint (not yet implemented for JSON adapter)\n\t */\n\tasync rollbackTo(_name: string): Promise<void> {\n\t\tthrowTransactionSavepointNotSupported({ adapter: \"json\" });\n\t}\n\n\t/**\n\t * Release savepoint (not yet implemented for JSON adapter)\n\t */\n\tasync release(_name: string): Promise<void> {\n\t\tthrowTransactionSavepointNotSupported({ adapter: \"json\" });\n\t}\n}\n","import {\n\tDatrixEntry,\n\tForeignKeyReference,\n\tSchemaDefinition,\n} from \"@datrix/core\";\nimport { QuerySelectObject } from \"@datrix/core\";\nimport { ExecuteQueryOptions, JsonTableFile } from \"./types\";\nimport type { JsonAdapter } from \"./adapter\";\nimport {\n\tDatrixAdapterError,\n\tthrowForeignKeyConstraint,\n\tthrowMigrationError,\n\tthrowUniqueConstraintField,\n\tthrowUniqueConstraintIndex,\n} from \"@datrix/core\";\nimport { DATRIX_META_MODEL } from \"@datrix/core\";\n\n/**\n * Validate table name for security (no null bytes, path separators, or parent refs)\n */\nexport function validateTableName(tableName: string): void {\n\tif (tableName.includes(\"\\x00\")) {\n\t\tthrowMigrationError({\n\t\t\tadapter: \"json\",\n\t\t\tmessage: \"Invalid table name: contains null byte\",\n\t\t\ttable: tableName,\n\t\t});\n\t}\n\n\tif (tableName.includes(\"/\") || tableName.includes(\"\\\\\")) {\n\t\tthrowMigrationError({\n\t\t\tadapter: \"json\",\n\t\t\tmessage: \"Invalid table name: contains path separators\",\n\t\t\ttable: tableName,\n\t\t});\n\t}\n\n\tif (tableName.includes(\"..\")) {\n\t\tthrowMigrationError({\n\t\t\tadapter: \"json\",\n\t\t\tmessage: \"Invalid table name: contains parent directory reference\",\n\t\t\ttable: tableName,\n\t\t});\n\t}\n}\n\n/**\n * Bootstrap _datrix metadata table for standalone mode.\n * Called during connect() when standalone: true is set in config.\n */\nexport async function createMetaTable(adapter: JsonAdapter): Promise<void> {\n\tconst metaExists = await adapter.tableExists(DATRIX_META_MODEL);\n\tif (metaExists) {\n\t\treturn;\n\t}\n\n\tconst metaSchema: SchemaDefinition = {\n\t\tname: DATRIX_META_MODEL,\n\t\ttableName: DATRIX_META_MODEL,\n\t\tfields: {\n\t\t\tid: { type: \"number\", autoIncrement: true },\n\t\t\tkey: { type: \"string\", required: true, unique: true, maxLength: 255 },\n\t\t\tvalue: { type: \"string\", required: true },\n\t\t\tcreatedAt: { type: \"date\" },\n\t\t\tupdatedAt: { type: \"date\" },\n\t\t},\n\t};\n\n\tawait adapter.createTable(metaSchema);\n}\n\n/**\n * Apply default values from schema for fields not provided.\n * Mimics SQL DEFAULT behavior.\n */\nexport function applyDefaultValues(\n\tschema: SchemaDefinition | undefined,\n\tdata: Record<string, unknown>,\n): void {\n\tif (!schema?.fields) return;\n\n\tfor (const [fieldName, fieldDef] of Object.entries(schema.fields)) {\n\t\tif (fieldName in data) continue;\n\n\t\tconst defaultValue = (fieldDef as { default?: unknown }).default;\n\t\tif (defaultValue !== undefined) {\n\t\t\tdata[fieldName] = defaultValue;\n\t\t}\n\t}\n}\n\n/**\n * Check unique constraints before insert/update.\n * Validates field-level unique and composite unique indexes.\n */\nexport function checkUniqueConstraints(\n\ttableData: JsonTableFile,\n\tschema: SchemaDefinition | undefined,\n\tnewData: Record<string, unknown>,\n\texcludeId?: number | string,\n): void {\n\tif (!schema?.fields) return;\n\n\tconst existingData = tableData.data;\n\n\t// 1. Check unique fields (field.unique === true)\n\tfor (const [fieldName, fieldDef] of Object.entries(schema.fields)) {\n\t\tif (!(fieldDef as { unique: boolean }).unique) continue;\n\n\t\tconst value = newData[fieldName];\n\t\tif (value === undefined || value === null) continue;\n\n\t\tconst duplicate = existingData.find(\n\t\t\t(row) => row[fieldName] === value && row[\"id\"] !== excludeId,\n\t\t);\n\n\t\tif (duplicate) {\n\t\t\tthrowUniqueConstraintField({\n\t\t\t\tfield: fieldName,\n\t\t\t\tvalue,\n\t\t\t\tadapter: \"json\",\n\t\t\t\ttable: schema.tableName ?? \"unknown\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// 2. Check unique indexes\n\tif (!schema.indexes) return;\n\n\tfor (const index of schema.indexes) {\n\t\tif (!index.unique) continue;\n\n\t\tconst indexValues = index.fields.map((f) => newData[f]);\n\n\t\tif (indexValues.some((v) => v === undefined || v === null)) continue;\n\n\t\tconst duplicate = existingData.find(\n\t\t\t(row) =>\n\t\t\t\tindex.fields.every((f) => row[f] === newData[f]) &&\n\t\t\t\trow[\"id\"] !== excludeId,\n\t\t);\n\n\t\tif (duplicate) {\n\t\t\tthrowUniqueConstraintIndex({\n\t\t\t\tfields: index.fields,\n\t\t\t\ttable: schema.tableName ?? \"unknown\",\n\t\t\t\tadapter: \"json\",\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Check foreign key constraints before insert/update.\n * Validates that FK values reference existing records in target tables.\n */\nexport async function checkForeignKeyConstraints(\n\tschema: SchemaDefinition | undefined,\n\tdata: Record<string, unknown>,\n\tadapter: JsonAdapter,\n): Promise<void> {\n\tif (!schema?.fields) return;\n\n\tfor (const [fieldName, fieldDef] of Object.entries(schema.fields)) {\n\t\tif (fieldDef.type !== \"relation\") continue;\n\n\t\tconst relationField = fieldDef as {\n\t\t\ttype: \"relation\";\n\t\t\tmodel: string;\n\t\t\tforeignKey?: string;\n\t\t\tkind?: string;\n\t\t};\n\n\t\tif (relationField.kind !== \"belongsTo\" && relationField.kind !== \"hasOne\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst foreignKey = relationField.foreignKey ?? `${fieldName}Id`;\n\t\tconst fkValue = data[foreignKey];\n\n\t\tif (fkValue === undefined || fkValue === null) continue;\n\n\t\tconst targetSchema = await adapter.getSchemaByModelName(\n\t\t\trelationField.model,\n\t\t);\n\t\tif (!targetSchema) continue;\n\n\t\tconst targetTable =\n\t\t\ttargetSchema.tableName ?? relationField.model.toLowerCase();\n\t\tconst targetData = await adapter.getCachedTable(targetTable);\n\n\t\tif (!targetData) continue;\n\n\t\tconst exists = targetData.data.some((row) => row[\"id\"] === fkValue);\n\n\t\tif (!exists) {\n\t\t\tthrowForeignKeyConstraint({\n\t\t\t\tforeignKey,\n\t\t\t\tvalue: fkValue,\n\t\t\t\ttargetModel: relationField.model,\n\t\t\t\ttable: schema.tableName ?? \"unknown\",\n\t\t\t\tadapter: \"json\",\n\t\t\t});\n\t\t}\n\t}\n}\n\ntype FkDependency = {\n\ttableName: string;\n\tfieldName: string;\n\tonDelete: NonNullable<ForeignKeyReference[\"onDelete\"]>;\n};\n\n/**\n * Find all FK fields across all tables that reference the given table.\n */\nasync function findFkDependencies(\n\ttargetTable: string,\n\tadapter: JsonAdapter,\n): Promise<FkDependency[]> {\n\tconst allTables = await adapter.getTables();\n\tconst deps: FkDependency[] = [];\n\n\tfor (const tableName of allTables) {\n\t\tconst schema = await adapter.getSchemaByTableName(tableName);\n\t\tif (!schema?.fields) continue;\n\n\t\tfor (const [fieldName, fieldDef] of Object.entries(schema.fields)) {\n\t\t\tif (fieldDef.type !== \"number\") continue;\n\n\t\t\tconst numField = fieldDef as { references?: ForeignKeyReference };\n\t\t\tconst ref = numField.references;\n\t\t\tif (!ref || ref.table !== targetTable) continue;\n\n\t\t\tconst onDelete = ref.onDelete ?? \"setNull\";\n\t\t\tdeps.push({ tableName, fieldName, onDelete });\n\t\t}\n\t}\n\n\treturn deps;\n}\n\n/**\n * Apply ON DELETE actions for the JSON adapter before deleting rows.\n * Mimics SQL FK ON DELETE behavior: restrict, setNull, cascade.\n *\n * Must be called BEFORE the actual delete.\n * Uses adapter.executeQuery for transaction safety.\n */\nexport async function applyOnDeleteActions(\n\ttargetTable: string,\n\tidsToDelete: ReadonlyArray<number>,\n\tadapter: JsonAdapter,\n\tqueryOptions?: ExecuteQueryOptions,\n): Promise<void> {\n\tif (idsToDelete.length === 0) return;\n\n\tconst deps = await findFkDependencies(targetTable, adapter);\n\tif (deps.length === 0) return;\n\n\t// Pass 1: Check restrict constraints\n\tfor (const dep of deps) {\n\t\tif (dep.onDelete !== \"restrict\") continue;\n\n\t\tconst tableData = await adapter.getCachedTable(dep.tableName);\n\t\tif (!tableData) continue;\n\n\t\tconst hasReference = tableData.data.some((row) =>\n\t\t\tidsToDelete.includes(row[dep.fieldName] as number),\n\t\t);\n\n\t\tif (hasReference) {\n\t\t\tthrow new DatrixAdapterError(\n\t\t\t\t`Cannot delete from '${targetTable}': referenced by '${dep.tableName}.${dep.fieldName}' with ON DELETE RESTRICT`,\n\t\t\t\t{\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\tcode: \"ADAPTER_FOREIGN_KEY_CONSTRAINT\",\n\t\t\t\t\toperation: \"query\",\n\t\t\t\t\tcontext: {\n\t\t\t\t\t\ttable: targetTable,\n\t\t\t\t\t\treferencedBy: `${dep.tableName}.${dep.fieldName}`,\n\t\t\t\t\t},\n\t\t\t\t\tsuggestion: `Remove or update referencing rows in '${dep.tableName}' before deleting from '${targetTable}'`,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\t// Pass 2: Apply setNull\n\tfor (const dep of deps) {\n\t\tif (dep.onDelete !== \"setNull\") continue;\n\n\t\tawait adapter.executeQueryWithOptions(\n\t\t\t{\n\t\t\t\ttype: \"update\",\n\t\t\t\ttable: dep.tableName,\n\t\t\t\twhere: { [dep.fieldName]: { $in: idsToDelete } },\n\t\t\t\tdata: { [dep.fieldName]: null },\n\t\t\t},\n\t\t\tqueryOptions,\n\t\t);\n\t}\n\n\t// Pass 3: Apply cascade (recursive - child deletes trigger their own onDelete)\n\tfor (const dep of deps) {\n\t\tif (dep.onDelete !== \"cascade\") continue;\n\n\t\tconst tableData = await adapter.getCachedTable(dep.tableName);\n\t\tif (!tableData) continue;\n\n\t\tconst childIds = tableData.data\n\t\t\t.filter((row) => idsToDelete.includes(row[dep.fieldName] as number))\n\t\t\t.map((row) => row[\"id\"] as number);\n\n\t\tif (childIds.length === 0) continue;\n\n\t\t// Recursive: apply onDelete for children before deleting them\n\t\tawait applyOnDeleteActions(dep.tableName, childIds, adapter, queryOptions);\n\n\t\tawait adapter.executeQueryWithOptions(\n\t\t\t{\n\t\t\t\ttype: \"delete\",\n\t\t\t\ttable: dep.tableName,\n\t\t\t\twhere: { id: { $in: childIds } },\n\t\t\t},\n\t\t\tqueryOptions,\n\t\t);\n\t}\n}\n\n/**\n * Apply SELECT recursively (preserves populated fields).\n * Ensures nested populate selects are applied to related data.\n */\nexport function applySelectRecursive<T extends DatrixEntry>(\n\trows: T[],\n\tselect?: QuerySelectObject<T>[\"select\"],\n\tpopulate?: QuerySelectObject<T>[\"populate\"],\n): Partial<T>[] {\n\tif (!rows || rows.length === 0) {\n\t\treturn rows;\n\t}\n\n\tlet result = rows as Partial<T>[];\n\n\tif (select && (select as unknown as string) !== \"*\") {\n\t\tconst fieldsToKeep = new Set(select as unknown as (keyof T)[]);\n\n\t\tif (populate) {\n\t\t\tfor (const relationName of Object.keys(populate)) {\n\t\t\t\tfieldsToKeep.add(relationName as keyof T);\n\t\t\t}\n\t\t}\n\n\t\tresult = rows.map((row) => {\n\t\t\tconst projected: Partial<T> = {};\n\t\t\tfor (const field of fieldsToKeep) {\n\t\t\t\tif (field in row) {\n\t\t\t\t\tprojected[field] = row[field];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn projected;\n\t\t});\n\t}\n\n\tif (populate) {\n\t\tfor (const [relationName, options] of Object.entries(populate)) {\n\t\t\tif (typeof options === \"boolean\") continue;\n\n\t\t\tconst nestedSelect = options === \"*\" ? \"*\" : options.select;\n\t\t\tconst nestedPopulate = options === \"*\" ? undefined : options.populate;\n\n\t\t\tfor (const row of result) {\n\t\t\t\tconst relationValue = row[relationName as keyof T] as T;\n\t\t\t\tif (!relationValue) continue;\n\n\t\t\t\tif (Array.isArray(relationValue)) {\n\t\t\t\t\trow[relationName as keyof T] = applySelectRecursive<T>(\n\t\t\t\t\t\trelationValue,\n\t\t\t\t\t\tnestedSelect,\n\t\t\t\t\t\tnestedPopulate,\n\t\t\t\t\t) as T[keyof T];\n\t\t\t\t} else {\n\t\t\t\t\trow[relationName as keyof T] = applySelectRecursive<T>(\n\t\t\t\t\t\t[relationValue],\n\t\t\t\t\t\tnestedSelect,\n\t\t\t\t\t\tnestedPopulate,\n\t\t\t\t\t)[0] as T[keyof T];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n","import type { ExportWriter } from \"@datrix/core\";\nimport type { JsonAdapter } from \"../adapter\";\n\nconst CHUNK_SIZE = 1000;\n\nexport class JsonExporter {\n\tconstructor(\n\t\tprivate root: string,\n\t\tprivate adapter: JsonAdapter,\n\t) {}\n\n\tasync export(writer: ExportWriter): Promise<void> {\n\t\tawait writer.writeMeta({\n\t\t\tversion: 1,\n\t\t\texportedAt: new Date().toISOString(),\n\t\t});\n\n\t\tconst tables = await this.adapter.getTables();\n\n\t\tfor (const tableName of tables) {\n\t\t\tconst schema = await this.adapter.getTableSchema(tableName);\n\t\t\tif (schema) {\n\t\t\t\tawait writer.writeSchema(schema);\n\t\t\t}\n\t\t}\n\n\t\tfor (const tableName of tables) {\n\t\t\tawait this.exportTable(tableName, writer);\n\t\t}\n\n\t\tawait writer.finalize();\n\t}\n\n\tprivate async exportTable(\n\t\ttableName: string,\n\t\twriter: ExportWriter,\n\t): Promise<void> {\n\t\tconst fs = await import(\"node:fs/promises\");\n\t\tconst path = await import(\"node:path\");\n\t\tconst filePath = path.join(this.root, `${tableName}.json`);\n\n\t\tconst content = await fs.readFile(filePath, \"utf-8\");\n\t\tconst tableFile = JSON.parse(content) as {\n\t\t\tdata: Record<string, unknown>[];\n\t\t};\n\t\tconst rows = tableFile.data;\n\n\t\tfor (let i = 0; i < rows.length; i += CHUNK_SIZE) {\n\t\t\tawait writer.writeChunk(tableName, rows.slice(i, i + CHUNK_SIZE));\n\t\t}\n\n\t\tif (rows.length === 0) {\n\t\t\tawait writer.writeChunk(tableName, []);\n\t\t}\n\t}\n}\n","import type { ImportReader } from \"@datrix/core\";\nimport type { SchemaDefinition } from \"@datrix/core\";\nimport type { JsonAdapter } from \"../adapter\";\nimport type { JsonTableFile } from \"../types\";\n\nexport class JsonImporter {\n\tconstructor(\n\t\tprivate root: string,\n\t\tprivate adapter: JsonAdapter,\n\t) {}\n\n\tasync import(reader: ImportReader): Promise<void> {\n\t\tconst schemas = await this.collectSchemas(reader);\n\n\t\t// 1. Drop all existing tables\n\t\tconst existingTables = await this.adapter.getTables();\n\t\tfor (const tableName of existingTables) {\n\t\t\tawait this.adapter.dropTableWithOptions(tableName, { isImport: true });\n\t\t}\n\n\t\t// 2. Create tables — isImport skips upsertSchemaMeta so the importer\n\t\t// can restore _datrix data as-is.\n\t\tfor (const schema of schemas.values()) {\n\t\t\tawait this.adapter.createTable(schema, { isImport: true });\n\t\t}\n\n\t\t// 3. Collect all rows per table then write directly to file (with correct lastInsertId)\n\t\tconst tables = await reader.getTables();\n\t\tfor (const tableName of tables) {\n\t\t\tconst rows: Record<string, unknown>[] = [];\n\n\t\t\tfor await (const chunk of reader.readChunks(tableName)) {\n\t\t\t\trows.push(...chunk);\n\t\t\t}\n\n\t\t\tawait this.writeTableFile(tableName, rows);\n\t\t}\n\t}\n\n\tprivate async collectSchemas(\n\t\treader: ImportReader,\n\t): Promise<Map<string, SchemaDefinition>> {\n\t\tconst schemas = new Map<string, SchemaDefinition>();\n\t\tfor await (const schema of reader.readSchemas()) {\n\t\t\tschemas.set(schema.tableName!, schema);\n\t\t}\n\t\treturn schemas;\n\t}\n\n\tprivate async writeTableFile(\n\t\ttableName: string,\n\t\trows: Record<string, unknown>[],\n\t): Promise<void> {\n\t\tconst fs = await import(\"node:fs/promises\");\n\t\tconst path = await import(\"node:path\");\n\t\tconst filePath = path.join(this.root, `${tableName}.json`);\n\n\t\tconst maxId = rows.reduce((max, row) => {\n\t\t\tconst id = typeof row[\"id\"] === \"number\" ? row[\"id\"] : 0;\n\t\t\treturn id > max ? id : max;\n\t\t}, 0);\n\n\t\tconst tableFile: JsonTableFile = {\n\t\t\tmeta: {\n\t\t\t\tversion: 1,\n\t\t\t\tlastInsertId: maxId,\n\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\tname: tableName,\n\t\t\t},\n\t\t\tdata: rows,\n\t\t};\n\n\t\tawait fs.writeFile(filePath, JSON.stringify(tableFile, null, 2), \"utf-8\");\n\t}\n}\n","import {\n\tQuerySelect,\n\tQuerySelectObject,\n\tQueryPopulateOptions,\n} from \"@datrix/core\";\nimport type { JsonAdapter } from \"./adapter\";\nimport type { DatrixEntry, RelationField } from \"@datrix/core\";\nimport {\n\tthrowSchemaNotFound,\n\tthrowRelationNotFound,\n\tthrowInvalidRelationType,\n\tthrowTargetModelNotFound,\n} from \"@datrix/core\";\nimport { JsonQueryRunner } from \"./runner\";\n\nexport class JsonPopulator {\n\tconstructor(private adapter: JsonAdapter) {}\n\n\tasync populate<T extends DatrixEntry>(\n\t\trows: T[],\n\t\tquery: QuerySelectObject<T>,\n\t): Promise<T[]> {\n\t\tif (!query.populate || rows.length === 0) {\n\t\t\treturn rows;\n\t\t}\n\n\t\t// Get current schema directly from table file (cache-aware, O(1) lookup)\n\t\tconst _currentSchema = await this.adapter.getSchemaByTableName(query.table);\n\t\tif (!_currentSchema) {\n\t\t\tthrowSchemaNotFound({ adapter: \"json\", modelName: query.table });\n\t\t}\n\t\tconst currentSchema = _currentSchema!;\n\t\tconst currentModelName = currentSchema.name;\n\n\t\tconst result = [...rows];\n\n\t\tfor (const [relationName, _options] of Object.entries(query.populate)) {\n\t\t\t// Get relation field from current schema\n\t\t\tconst relationField = currentSchema.fields[relationName]!;\n\t\t\tif (!relationField) {\n\t\t\t\tthrowRelationNotFound({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\trelationName,\n\t\t\t\t\tschemaName: currentSchema.name,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (relationField.type !== \"relation\") {\n\t\t\t\tthrowInvalidRelationType({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\trelationName,\n\t\t\t\t\tfieldType: relationField.type,\n\t\t\t\t\tschemaName: currentSchema.name,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst relField = relationField as RelationField;\n\t\t\tconst targetModelName = relField.model;\n\t\t\tconst foreignKey = relField.foreignKey!;\n\t\t\tconst kind = relField.kind;\n\n\t\t\t// Get target schema from adapter (cache-aware)\n\t\t\tconst targetSchema =\n\t\t\t\tawait this.adapter.getSchemaByModelName(targetModelName);\n\t\t\tif (!targetSchema) {\n\t\t\t\tthrowTargetModelNotFound({\n\t\t\t\t\tadapter: \"json\",\n\t\t\t\t\ttargetModel: targetModelName,\n\t\t\t\t\trelationName,\n\t\t\t\t\tschemaName: currentSchema.name,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst targetTable =\n\t\t\t\ttargetSchema!.tableName ?? targetModelName.toLowerCase();\n\n\t\t\t// Load target table using adapter's cache\n\t\t\tconst tableData = await this.adapter.getCachedTable(targetTable);\n\t\t\tif (!tableData) continue;\n\n\t\t\tconst relatedData = tableData.data as Record<string, unknown>[];\n\n\t\t\t// NOTE: We no longer apply select here - it's handled by adapter's applySelectRecursive\n\t\t\t// This ensures proper handling of nested populate + select combinations\n\n\t\t\tconst options =\n\t\t\t\ttypeof _options === \"object\" &&\n\t\t\t\t_options !== null &&\n\t\t\t\t!Array.isArray(_options)\n\t\t\t\t\t? (_options as QueryPopulateOptions<DatrixEntry>)\n\t\t\t\t\t: undefined;\n\n\t\t\t// Map data based on relation type\n\t\t\tif (kind === \"belongsTo\") {\n\t\t\t\t// Source has FK (e.g. Post.authorId -> User.id)\n\t\t\t\tconst ids = new Set(\n\t\t\t\t\tresult\n\t\t\t\t\t\t.map((r) => r[foreignKey as keyof T] as number)\n\t\t\t\t\t\t.filter((id): id is number => id !== null && id !== undefined),\n\t\t\t\t);\n\n\t\t\t\tconst relatedMap = new Map<number, Record<string, unknown>>();\n\t\t\t\tif (ids.size > 0) {\n\t\t\t\t\tfor (const item of relatedData) {\n\t\t\t\t\t\tconst itemId = item[\"id\"] as number;\n\t\t\t\t\t\tif (ids.has(itemId)) {\n\t\t\t\t\t\t\trelatedMap.set(itemId, item);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If where is specified, pre-filter the map using runner's match logic\n\t\t\t\tlet filteredMap = relatedMap;\n\t\t\t\tif (options?.where) {\n\t\t\t\t\tfilteredMap = new Map();\n\t\t\t\t\tconst filterRunner = new JsonQueryRunner(\n\t\t\t\t\t\ttableData,\n\t\t\t\t\t\tthis.adapter,\n\t\t\t\t\t\ttargetSchema!,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const [id, item] of relatedMap) {\n\t\t\t\t\t\tconst matched = await filterRunner.filterAndSort({\n\t\t\t\t\t\t\ttype: \"select\",\n\t\t\t\t\t\t\ttable: targetTable,\n\t\t\t\t\t\t\twhere: options.where,\n\t\t\t\t\t\t\tselect: \"*\" as unknown as QuerySelect,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (matched.some((r) => r[\"id\"] === id)) {\n\t\t\t\t\t\t\tfilteredMap.set(id, item);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (const row of result) {\n\t\t\t\t\tconst fkValue = row[foreignKey as keyof T] as\n\t\t\t\t\t\t| number\n\t\t\t\t\t\t| null\n\t\t\t\t\t\t| undefined;\n\t\t\t\t\tif (fkValue !== null && fkValue !== undefined) {\n\t\t\t\t\t\trow[relationName as keyof T] = (filteredMap.get(fkValue) ??\n\t\t\t\t\t\t\tnull) as T[keyof T];\n\t\t\t\t\t} else {\n\t\t\t\t\t\trow[relationName as keyof T] = null as T[keyof T];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (kind === \"hasMany\" || kind === \"hasOne\") {\n\t\t\t\t// Target has FK (e.g. User.id <- Post.authorId)\n\t\t\t\tconst sourceIds = new Set(\n\t\t\t\t\tresult\n\t\t\t\t\t\t.map((r) => r[\"id\"])\n\t\t\t\t\t\t.filter((id): id is number => id !== null && id !== undefined),\n\t\t\t\t);\n\n\t\t\t\t// Group related items by FK\n\t\t\t\tconst grouped = new Map<number, Record<string, unknown>[]>();\n\t\t\t\tfor (const item of relatedData) {\n\t\t\t\t\tconst fkValue = item[foreignKey] as number | null | undefined;\n\t\t\t\t\tif (\n\t\t\t\t\t\tfkValue !== null &&\n\t\t\t\t\t\tfkValue !== undefined &&\n\t\t\t\t\t\tsourceIds.has(fkValue)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst group = grouped.get(fkValue) ?? [];\n\t\t\t\t\t\tgroup.push(item);\n\t\t\t\t\t\tgrouped.set(fkValue, group);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst hasSortOrFilter =\n\t\t\t\t\toptions?.where ||\n\t\t\t\t\toptions?.orderBy ||\n\t\t\t\t\toptions?.limit !== undefined ||\n\t\t\t\t\toptions?.offset !== undefined;\n\n\t\t\t\tfor (const row of result) {\n\t\t\t\t\tconst rowId = row[\"id\"] as number;\n\t\t\t\t\tlet group = grouped.get(rowId) ?? [];\n\n\t\t\t\t\tif (kind === \"hasOne\") {\n\t\t\t\t\t\trow[relationName as keyof T] = (group[0] ?? null) as T[keyof T];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (hasSortOrFilter && group.length > 0) {\n\t\t\t\t\t\t\tconst groupTable = { ...tableData, data: group };\n\t\t\t\t\t\t\tconst groupRunner = new JsonQueryRunner(\n\t\t\t\t\t\t\t\tgroupTable,\n\t\t\t\t\t\t\t\tthis.adapter,\n\t\t\t\t\t\t\t\ttargetSchema!,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tgroup = (await groupRunner.filterAndSort({\n\t\t\t\t\t\t\t\ttype: \"select\",\n\t\t\t\t\t\t\t\ttable: targetTable,\n\t\t\t\t\t\t\t\twhere: options?.where!,\n\t\t\t\t\t\t\t\torderBy: options?.orderBy,\n\t\t\t\t\t\t\t\tlimit: options?.limit,\n\t\t\t\t\t\t\t\toffset: options?.offset,\n\t\t\t\t\t\t\t\tselect: \"*\" as unknown as QuerySelect,\n\t\t\t\t\t\t\t})) as unknown as Record<string, unknown>[];\n\t\t\t\t\t\t}\n\t\t\t\t\t\trow[relationName as keyof T] = group as T[keyof T];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (kind === \"manyToMany\") {\n\t\t\t\t// ManyToMany uses junction table (e.g. Post <-> Tag via post_tag)\n\t\t\t\tconst junctionTableName = relField.through!;\n\t\t\t\tconst sourceFK = `${currentModelName}Id`;\n\t\t\t\tconst targetFK = `${targetModelName}Id`;\n\n\t\t\t\t// Load junction table using adapter cache\n\t\t\t\tconst junctionData =\n\t\t\t\t\tawait this.adapter.getCachedTable(junctionTableName);\n\t\t\t\tif (!junctionData) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Junction table '${junctionTableName}' not found for manyToMany relation '${relationName}' in schema '${currentSchema.name}'`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Collect source IDs\n\t\t\t\tconst sourceIds = result\n\t\t\t\t\t.map((r) => r[\"id\"])\n\t\t\t\t\t.filter((id): id is number => id !== null && id !== undefined);\n\n\t\t\t\tif (sourceIds.length === 0) continue;\n\n\t\t\t\t// Use Runner for schema-aware filtering (handles string/number coercion)\n\t\t\t\tconst junctionRunner = new JsonQueryRunner(junctionData, this.adapter);\n\t\t\t\tconst relevantJunctions = await junctionRunner.run({\n\t\t\t\t\ttype: \"select\",\n\t\t\t\t\ttable: junctionTableName,\n\t\t\t\t\twhere: { [sourceFK]: { $in: sourceIds } },\n\t\t\t\t\tselect: \"*\" as unknown as QuerySelect,\n\t\t\t\t});\n\n\t\t\t\t// Build mapping: sourceId -> targetIds[]\n\t\t\t\t// Normalize all IDs to number for consistent comparison\n\t\t\t\tconst mapping = new Map<number, number[]>();\n\t\t\t\tfor (const junction of relevantJunctions) {\n\t\t\t\t\tconst srcId = junction[sourceFK as keyof typeof junction];\n\t\t\t\t\tconst tgtId = junction[targetFK as keyof typeof junction];\n\n\t\t\t\t\t// Normalize to number\n\t\t\t\t\tconst normalizedSrcId =\n\t\t\t\t\t\ttypeof srcId === \"string\" ? Number(srcId) : (srcId as number);\n\t\t\t\t\tconst normalizedTgtId =\n\t\t\t\t\t\ttypeof tgtId === \"string\" ? Number(tgtId) : (tgtId as number);\n\n\t\t\t\t\tconst existing = mapping.get(normalizedSrcId) ?? [];\n\t\t\t\t\texisting.push(normalizedTgtId);\n\t\t\t\t\tmapping.set(normalizedSrcId, existing);\n\t\t\t\t}\n\n\t\t\t\t// Collect all unique target IDs\n\t\t\t\tconst allTargetIds = new Set<number>();\n\t\t\t\tfor (const ids of mapping.values()) {\n\t\t\t\t\tids.forEach((id) => allTargetIds.add(id));\n\t\t\t\t}\n\n\t\t\t\t// Filter target records with id filter merged with user's where\n\t\t\t\tconst targetDataForRunner =\n\t\t\t\t\tawait this.adapter.getCachedTable(targetTable);\n\t\t\t\tif (!targetDataForRunner) continue;\n\n\t\t\t\tconst idFilter = { id: { $in: Array.from(allTargetIds) } };\n\t\t\t\tconst userWhere = options?.where;\n\t\t\t\tconst mergedWhere = userWhere\n\t\t\t\t\t? { $and: [idFilter, userWhere] }\n\t\t\t\t\t: idFilter;\n\n\t\t\t\tconst targetRunner = new JsonQueryRunner(\n\t\t\t\t\ttargetDataForRunner,\n\t\t\t\t\tthis.adapter,\n\t\t\t\t\ttargetSchema!,\n\t\t\t\t);\n\t\t\t\tconst targetRecords = await targetRunner.run({\n\t\t\t\t\ttype: \"select\",\n\t\t\t\t\ttable: targetTable,\n\t\t\t\t\twhere: mergedWhere,\n\t\t\t\t\torderBy: options?.orderBy,\n\t\t\t\t\tselect: \"*\" as unknown as QuerySelect,\n\t\t\t\t});\n\n\t\t\t\t// Map to result rows, applying limit/offset per-row\n\t\t\t\tfor (const row of result) {\n\t\t\t\t\tconst rowId = row[\"id\"];\n\t\t\t\t\tconst normalizedRowId =\n\t\t\t\t\t\ttypeof rowId === \"string\" ? Number(rowId) : (rowId as number);\n\t\t\t\t\tconst targetIds = mapping.get(normalizedRowId) ?? [];\n\n\t\t\t\t\t// Filter to this row's related records (already where/order filtered above)\n\t\t\t\t\tlet relatedRecords = targetRecords.filter((r) => {\n\t\t\t\t\t\tconst rID = r[\"id\"];\n\t\t\t\t\t\tconst normalizedRID =\n\t\t\t\t\t\t\ttypeof rID === \"string\" ? Number(rID) : (rID as number);\n\t\t\t\t\t\treturn targetIds.includes(normalizedRID);\n\t\t\t\t\t});\n\n\t\t\t\t\t// Apply limit/offset per-row\n\t\t\t\t\tconst offset = options?.offset ?? 0;\n\t\t\t\t\tif (options?.limit !== undefined) {\n\t\t\t\t\t\trelatedRecords = relatedRecords.slice(\n\t\t\t\t\t\t\toffset,\n\t\t\t\t\t\t\toffset + options.limit,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (offset > 0) {\n\t\t\t\t\t\trelatedRecords = relatedRecords.slice(offset);\n\t\t\t\t\t}\n\n\t\t\t\t\trow[relationName as keyof T] = relatedRecords as T[keyof T];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Nested populate (recursion)\n\t\t\tif (\n\t\t\t\ttypeof _options === \"object\" &&\n\t\t\t\t_options !== null &&\n\t\t\t\t_options.populate\n\t\t\t) {\n\t\t\t\tconst nextRows: T[] = [];\n\t\t\t\tfor (const row of result) {\n\t\t\t\t\tconst val = row[relationName as keyof T] as T;\n\t\t\t\t\tif (!val) continue;\n\t\t\t\t\tif (Array.isArray(val)) {\n\t\t\t\t\t\tnextRows.push(...val);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextRows.push(val);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (nextRows.length > 0) {\n\t\t\t\t\tawait this.populate(nextRows, {\n\t\t\t\t\t\ttype: \"select\",\n\t\t\t\t\t\ttable: targetTable,\n\t\t\t\t\t\tpopulate: _options.populate,\n\t\t\t\t\t\tselect: \"*\" as unknown as QuerySelect<T>,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n}\n","import { DatrixEntry } from \"@datrix/core\";\nimport {\n\tQueryCountObject,\n\tQueryInsertObject,\n\tQueryObject,\n\tQuerySelectObject,\n\tQueryUpdateObject,\n} from \"@datrix/core\";\nimport { JsonQueryRunner } from \"./runner\";\nimport { JsonPopulator } from \"./populate\";\nimport {\n\tapplyDefaultValues,\n\tapplyOnDeleteActions,\n\tapplySelectRecursive,\n\tcheckForeignKeyConstraints,\n\tcheckUniqueConstraints,\n} from \"./table-utils\";\nimport type { JsonAdapter } from \"./adapter\";\nimport type { ExecuteQueryOptions } from \"./types\";\nimport { throwQueryMissingData } from \"@datrix/core\";\n\nexport type QueryHandlerResult<T extends DatrixEntry> = {\n\trows: T[];\n\tmetadata: {\n\t\trowCount: number;\n\t\taffectedRows: number;\n\t\tinsertIds?: number[];\n\t\tcount?: number;\n\t};\n\tshouldWrite: boolean;\n\tearlyReturn?: boolean;\n};\n\nexport async function handleSelect<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QuerySelectObject<T>;\n\tadapter: JsonAdapter;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query, adapter } = ctx;\n\n\tlet rows: T[];\n\n\tif (query.populate) {\n\t\trows = await runner.filterAndSort(query);\n\t\tconst populator = new JsonPopulator(adapter);\n\t\trows = await populator.populate(rows, query);\n\t\trows = applySelectRecursive<T>(rows, query.select, query.populate) as T[];\n\t} else {\n\t\trows = (await runner.run(query)) as T[];\n\t}\n\n\treturn {\n\t\trows,\n\t\tmetadata: { rowCount: rows.length, affectedRows: 0 },\n\t\tshouldWrite: false,\n\t};\n}\n\nexport async function handleCount<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QueryCountObject<T>;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query } = ctx;\n\tconst rows = (await runner.run(query)) as T[];\n\n\treturn {\n\t\trows: [] as T[],\n\t\tmetadata: { rowCount: 0, affectedRows: 0, count: rows.length },\n\t\tshouldWrite: false,\n\t\tearlyReturn: true,\n\t};\n}\n\nexport async function handleInsert<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QueryInsertObject<T>;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query } = ctx;\n\tconst tableData = runner.tableData;\n\tconst tableSchema = runner.tableSchema;\n\tconst adapter = runner.adapterRef;\n\n\tif (!query.data || !Array.isArray(query.data)) {\n\t\tthrowQueryMissingData({\n\t\t\tqueryType: \"insert\",\n\t\t\ttable: query.table,\n\t\t\tadapter: \"json\",\n\t\t});\n\t}\n\n\tconst insertedIds: number[] = [];\n\tconst isJunctionTable =\n\t\t(tableSchema as unknown as { _isJunctionTable?: boolean })\n\t\t\t?._isJunctionTable === true;\n\n\tfor (const item of query.data) {\n\t\tconst newItem = { ...item };\n\n\t\tif (isJunctionTable) {\n\t\t\tconst alreadyExists = tableData.data.some((row) =>\n\t\t\t\tObject.keys(newItem).every(\n\t\t\t\t\t(key) => key === \"id\" || row[key] === newItem[key],\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (alreadyExists) continue;\n\t\t}\n\n\t\tif (!newItem[\"id\"]) {\n\t\t\ttableData.meta.lastInsertId = (tableData.meta.lastInsertId ?? 0) + 1;\n\t\t\tnewItem[\"id\"] = tableData.meta.lastInsertId;\n\t\t} else {\n\t\t\tconst manualId = Number(newItem[\"id\"]);\n\t\t\tif (!isNaN(manualId) && manualId > (tableData.meta.lastInsertId ?? 0)) {\n\t\t\t\ttableData.meta.lastInsertId = manualId;\n\t\t\t}\n\t\t}\n\n\t\tapplyDefaultValues(tableSchema, newItem);\n\t\tawait checkForeignKeyConstraints(tableSchema, newItem, adapter);\n\t\tcheckUniqueConstraints(tableData, tableSchema, newItem);\n\t\ttableData.data.push(newItem);\n\t\tinsertedIds.push(newItem[\"id\"] as number);\n\t}\n\n\tconst rows = insertedIds.map((id) => ({ id })) as T[];\n\n\treturn {\n\t\trows,\n\t\tmetadata: {\n\t\t\trowCount: insertedIds.length,\n\t\t\taffectedRows: insertedIds.length,\n\t\t\tinsertIds: insertedIds,\n\t\t},\n\t\tshouldWrite: true,\n\t};\n}\n\nexport async function handleUpdate<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QueryUpdateObject<T>;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query } = ctx;\n\tconst tableData = runner.tableData;\n\tconst tableSchema = runner.tableSchema;\n\tconst adapter = runner.adapterRef;\n\n\tif (!query.data) {\n\t\tthrowQueryMissingData({\n\t\t\tqueryType: \"update\",\n\t\t\ttable: query.table,\n\t\t\tadapter: \"json\",\n\t\t});\n\t}\n\n\tconst updateQuery: QuerySelectObject<T> = {\n\t\t...(query as unknown as QuerySelectObject<T>),\n\t\tlimit: undefined,\n\t\toffset: undefined,\n\t\torderBy: undefined,\n\t};\n\tconst rowsToUpdate = await runner.filterAndSort(updateQuery);\n\n\tfor (const row of rowsToUpdate) {\n\t\tconst updatedData = { ...row, ...query.data };\n\t\tawait checkForeignKeyConstraints(tableSchema, updatedData, adapter);\n\t\tcheckUniqueConstraints(\n\t\t\ttableData,\n\t\t\ttableSchema,\n\t\t\tupdatedData,\n\t\t\trow[\"id\"] as number,\n\t\t);\n\t}\n\n\tfor (const row of rowsToUpdate) {\n\t\tObject.assign(row, query.data);\n\t}\n\n\tconst updatedIds = rowsToUpdate.map((r) => r[\"id\"] as number);\n\tconst rows = updatedIds.map((id) => ({ id })) as T[];\n\n\treturn {\n\t\trows,\n\t\tmetadata: { rowCount: updatedIds.length, affectedRows: updatedIds.length },\n\t\tshouldWrite: true,\n\t};\n}\n\nexport async function handleDelete<T extends DatrixEntry>(ctx: {\n\trunner: JsonQueryRunner;\n\tquery: QueryObject<T>;\n\tadapter: JsonAdapter;\n\tqueryOptions?: ExecuteQueryOptions;\n}): Promise<QueryHandlerResult<T>> {\n\tconst { runner, query, adapter, queryOptions } = ctx;\n\tconst tableData = runner.tableData;\n\n\tconst deleteQuery: QuerySelectObject<T> = {\n\t\t...(query as unknown as QuerySelectObject<T>),\n\t\tlimit: undefined,\n\t\toffset: undefined,\n\t\torderBy: undefined,\n\t};\n\tconst rowsToDelete = await runner.filterAndSort(deleteQuery);\n\tconst idsToDelete = rowsToDelete.map((r) => r.id as number);\n\n\t// Apply ON DELETE actions (restrict/setNull/cascade) before deleting\n\t// Pass queryOptions to avoid re-acquiring the already-held lock\n\tawait applyOnDeleteActions(query.table, idsToDelete, adapter, queryOptions);\n\n\tconst idsSet = new Set(idsToDelete);\n\tconst originalLength = tableData.data.length;\n\ttableData.data = tableData.data.filter((d) => !idsSet.has(d[\"id\"] as number));\n\n\tconst deletedIds = rowsToDelete.map((r) => r[\"id\"] as number);\n\tconst rows = deletedIds.map((id) => ({ id })) as T[];\n\n\treturn {\n\t\trows,\n\t\tmetadata: {\n\t\t\trowCount: deletedIds.length,\n\t\t\taffectedRows: originalLength - tableData.data.length,\n\t\t},\n\t\tshouldWrite: true,\n\t};\n}\n"],"mappings":"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","names":["fs","path","DatrixAdapterError","throwMigrationError","DATRIX_META_MODEL","fs","path","fs","path","fs","path","content","DATRIX_META_MODEL","throwMigrationError","DatrixAdapterError"]}