@hedystia/db 2.0.14 → 2.1.1

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,"file":"file.mjs","names":[],"sources":["../../src/drivers/file.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { DriverError } from \"../errors\";\nimport type { ColumnMetadata, FileConnectionConfig, TableMetadata } from \"../types\";\nimport { BaseDriver } from \"./driver\";\n\ninterface FileTableData {\n rows: Record<string, unknown>[];\n autoIncrementId: number;\n meta: {\n columns: ColumnMetadata[];\n };\n}\n\n/**\n * File-based database driver using JSON files for storage\n */\nexport class FileDriver extends BaseDriver {\n private config: FileConnectionConfig;\n private data = new Map<string, FileTableData>();\n\n constructor(config: FileConnectionConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Connect to the file database (ensures directory exists and loads data)\n */\n async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n if (!existsSync(this.config.directory)) {\n mkdirSync(this.config.directory, { recursive: true });\n }\n this.loadAll();\n this.connected = true;\n }\n\n /**\n * Disconnect from the file database (flushes data to disk)\n */\n async disconnect(): Promise<void> {\n this.flushAll();\n this.data.clear();\n this.connected = false;\n }\n\n /**\n * Execute a SQL-like statement (parsed internally for file driver)\n * @param {string} sql - SQL statement\n * @param {unknown[]} [params] - Query parameters\n * @returns {Promise<any>} Execution result\n */\n async execute(sql: string, params: unknown[] = []): Promise<any> {\n return this.executeParsed(sql, params);\n }\n\n /**\n * Execute a SQL-like query\n * @param {string} sql - SQL query\n * @param {unknown[]} [params] - Query parameters\n * @returns {Promise<any[]>} Query results\n */\n async query(sql: string, params: unknown[] = []): Promise<any[]> {\n const result = this.executeParsed(sql, params);\n return Array.isArray(result) ? result : [];\n }\n\n /**\n * Check if a table exists\n * @param {string} name - Table name\n * @returns {Promise<boolean>} Whether the table exists\n */\n async tableExists(name: string): Promise<boolean> {\n return this.data.has(name);\n }\n\n /**\n * Get column metadata for a table\n * @param {string} name - Table name\n * @returns {Promise<ColumnMetadata[]>} Column metadata\n */\n async getTableColumns(name: string): Promise<ColumnMetadata[]> {\n const table = this.data.get(name);\n if (!table) {\n return [];\n }\n return Object.values(table.meta.columns);\n }\n\n /**\n * Create a table from metadata\n * @param {TableMetadata} meta - Table metadata\n */\n async createTable(meta: TableMetadata): Promise<void> {\n if (this.data.has(meta.name)) {\n return;\n }\n this.data.set(meta.name, {\n rows: [],\n autoIncrementId: 0,\n meta: { columns: [...meta.columns] },\n });\n this.flush(meta.name);\n }\n\n /**\n * Drop a table\n * @param {string} name - Table name\n */\n async dropTable(name: string): Promise<void> {\n this.data.delete(name);\n const filePath = join(this.config.directory, `${name}.json`);\n try {\n const { unlinkSync } = await import(\"fs\");\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n }\n } catch {}\n }\n\n /**\n * Add a column to a table\n * @param {string} table - Table name\n * @param {ColumnMetadata} column - Column metadata\n */\n async addColumn(table: string, column: ColumnMetadata): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n tableData.meta.columns.push(column);\n for (const row of tableData.rows) {\n row[column.name] = column.defaultValue ?? null;\n }\n this.flush(table);\n }\n\n /**\n * Drop a column from a table\n * @param {string} table - Table name\n * @param {string} name - Column name\n */\n async dropColumn(table: string, name: string): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n tableData.meta.columns = tableData.meta.columns.filter((c) => c.name !== name);\n for (const row of tableData.rows) {\n delete row[name];\n }\n this.flush(table);\n }\n\n /**\n * Rename a column\n * @param {string} table - Table name\n * @param {string} oldName - Current name\n * @param {string} newName - New name\n */\n async renameColumn(table: string, oldName: string, newName: string): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n const col = tableData.meta.columns.find((c) => c.name === oldName);\n if (col) {\n col.name = newName;\n }\n for (const row of tableData.rows) {\n row[newName] = row[oldName];\n delete row[oldName];\n }\n this.flush(table);\n }\n\n /**\n * Execute within a transaction (pseudo-transaction for file driver)\n * @param {() => Promise<T>} fn - Function to execute\n * @returns {Promise<T>} Result\n */\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n const snapshot = new Map<string, string>();\n for (const [name, tableData] of this.data) {\n snapshot.set(name, JSON.stringify(tableData));\n }\n try {\n const result = await fn();\n this.flushAll();\n return result;\n } catch (err) {\n for (const [name, json] of snapshot) {\n this.data.set(name, JSON.parse(json));\n }\n throw err;\n }\n }\n\n /**\n * Batch fetch all table columns (already in memory for file driver)\n */\n async getAllTableColumns(): Promise<Record<string, ColumnMetadata[]>> {\n const result: Record<string, ColumnMetadata[]> = {};\n for (const [name, tableData] of this.data) {\n result[name] = [...tableData.meta.columns];\n }\n return result;\n }\n\n /**\n * Direct access for the repository to perform typed operations\n * @param {string} tableName - Table name\n * @returns {FileTableData | undefined} Table data\n */\n getTableData(tableName: string): FileTableData | undefined {\n return this.data.get(tableName);\n }\n\n /**\n * Insert a row directly into the file store\n * @param {string} tableName - Table name\n * @param {Record<string, unknown>} row - Row data\n * @returns {number} The insert ID\n */\n insertRow(tableName: string, row: Record<string, unknown>): number {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n throw new DriverError(`Table \"${tableName}\" does not exist`);\n }\n let pkCol: string | null = null;\n for (const col of tableData.meta.columns) {\n if (col.autoIncrement) {\n pkCol = col.name;\n break;\n }\n }\n if (pkCol && row[pkCol] === undefined) {\n tableData.autoIncrementId++;\n row[pkCol] = tableData.autoIncrementId;\n }\n tableData.rows.push({ ...row });\n this.flush(tableName);\n return pkCol ? (row[pkCol] as number) : 0;\n }\n\n /**\n * Find rows matching a filter function\n * @param {string} tableName - Table name\n * @param {(row: Record<string, unknown>) => boolean} filter - Filter function\n * @returns {Record<string, unknown>[]} Matching rows\n */\n findRows(\n tableName: string,\n filter?: (row: Record<string, unknown>) => boolean,\n ): Record<string, unknown>[] {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return [];\n }\n if (!filter) {\n return tableData.rows.map((r) => ({ ...r }));\n }\n return tableData.rows.filter(filter).map((r) => ({ ...r }));\n }\n\n /**\n * Update rows matching a filter\n * @param {string} tableName - Table name\n * @param {(row: Record<string, unknown>) => boolean} filter - Filter function\n * @param {Record<string, unknown>} data - Update data\n * @returns {number} Number of affected rows\n */\n updateRows(\n tableName: string,\n filter: (row: Record<string, unknown>) => boolean,\n data: Record<string, unknown>,\n ): number {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return 0;\n }\n let count = 0;\n for (const row of tableData.rows) {\n if (filter(row)) {\n Object.assign(row, data);\n count++;\n }\n }\n if (count > 0) {\n this.flush(tableName);\n }\n return count;\n }\n\n /**\n * Delete rows matching a filter\n * @param {string} tableName - Table name\n * @param {(row: Record<string, unknown>) => boolean} filter - Filter function\n * @returns {number} Number of deleted rows\n */\n deleteRows(tableName: string, filter: (row: Record<string, unknown>) => boolean): number {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return 0;\n }\n const before = tableData.rows.length;\n tableData.rows = tableData.rows.filter((r) => !filter(r));\n const deleted = before - tableData.rows.length;\n if (deleted > 0) {\n this.flush(tableName);\n }\n return deleted;\n }\n\n /**\n * Truncate a table\n * @param {string} tableName - Table name\n */\n truncateTable(tableName: string): void {\n const tableData = this.data.get(tableName);\n if (tableData) {\n tableData.rows = [];\n tableData.autoIncrementId = 0;\n this.flush(tableName);\n }\n }\n\n /**\n * Count rows matching a filter\n * @param {string} tableName - Table name\n * @param {(row: Record<string, unknown>) => boolean} [filter] - Filter function\n * @returns {number} Row count\n */\n countRows(tableName: string, filter?: (row: Record<string, unknown>) => boolean): number {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return 0;\n }\n if (!filter) {\n return tableData.rows.length;\n }\n return tableData.rows.filter(filter).length;\n }\n\n private loadAll(): void {\n if (!existsSync(this.config.directory)) {\n return;\n }\n const { readdirSync } = require(\"fs\");\n const files = readdirSync(this.config.directory) as string[];\n for (const file of files) {\n if (file.endsWith(\".json\")) {\n const name = file.replace(\".json\", \"\");\n try {\n const content = readFileSync(join(this.config.directory, file), \"utf-8\");\n this.data.set(name, JSON.parse(content));\n } catch {}\n }\n }\n }\n\n private flush(tableName: string): void {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return;\n }\n const filePath = join(this.config.directory, `${tableName}.json`);\n writeFileSync(filePath, JSON.stringify(tableData, null, 2), \"utf-8\");\n }\n\n private flushAll(): void {\n for (const [name] of this.data) {\n this.flush(name);\n }\n }\n\n private executeParsed(sql: string, params: unknown[]): any {\n const trimmed = sql.trim().toUpperCase();\n if (trimmed.startsWith(\"CREATE TABLE\")) {\n return { insertId: 0, affectedRows: 0 };\n }\n if (trimmed.startsWith(\"DROP TABLE\")) {\n const match = sql.match(/DROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?`?(\\w+)`?/i);\n if (match?.[1]) {\n this.data.delete(match[1]);\n }\n return { insertId: 0, affectedRows: 0 };\n }\n if (trimmed.startsWith(\"SELECT\")) {\n const tableMatch = sql.match(/FROM\\s+`?(\\w+)`?/i);\n if (!tableMatch) {\n return [];\n }\n const tableName = tableMatch[1];\n if (!tableName) {\n return [];\n }\n return this.findRows(tableName);\n }\n if (trimmed.startsWith(\"INSERT\")) {\n const tableMatch = sql.match(/INTO\\s+`?(\\w+)`?/i);\n if (!tableMatch) {\n return { insertId: 0, affectedRows: 0 };\n }\n const colMatch = sql.match(/\\(([^)]+)\\)\\s+VALUES/i);\n if (!colMatch) {\n return { insertId: 0, affectedRows: 0 };\n }\n const cols = colMatch[1]?.split(\",\").map((c) => c.trim().replace(/`/g, \"\"));\n if (!cols) {\n return { insertId: 0, affectedRows: 0 };\n }\n const row: Record<string, unknown> = {};\n for (let i = 0; i < cols.length; i++) {\n const col = cols[i];\n if (col) {\n row[col] = params[i];\n }\n }\n const id = this.insertRow(tableMatch[1]!, row);\n return { insertId: id, affectedRows: 1 };\n }\n return { insertId: 0, affectedRows: 0 };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"file.mjs","names":[],"sources":["../../src/drivers/file.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { DriverError } from \"../errors\";\nimport type { ColumnMetadata, FileConnectionConfig, TableMetadata } from \"../types\";\nimport { BaseDriver } from \"./driver\";\n\ninterface FileTableData {\n rows: Record<string, unknown>[];\n autoIncrementId: number;\n meta: {\n columns: ColumnMetadata[];\n };\n}\n\n/**\n * File-based database driver using JSON files for storage\n */\nexport class FileDriver extends BaseDriver {\n private config: FileConnectionConfig;\n private data = new Map<string, FileTableData>();\n\n constructor(config: FileConnectionConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Connect to the file database (ensures directory exists and loads data)\n */\n async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n if (!existsSync(this.config.directory)) {\n mkdirSync(this.config.directory, { recursive: true });\n }\n this.loadAll();\n this.connected = true;\n }\n\n /**\n * Disconnect from the file database (flushes data to disk)\n */\n async disconnect(): Promise<void> {\n this.flushAll();\n this.data.clear();\n this.connected = false;\n }\n\n /**\n * Execute a SQL-like statement (parsed internally for file driver)\n * @param {string} sql - SQL statement\n * @param {unknown[]} [params] - Query parameters\n * @returns {Promise<any>} Execution result\n */\n async execute(sql: string, params: unknown[] = []): Promise<any> {\n return this.executeParsed(sql, params);\n }\n\n /**\n * Execute a SQL-like query\n * @param {string} sql - SQL query\n * @param {unknown[]} [params] - Query parameters\n * @returns {Promise<any[]>} Query results\n */\n async query(sql: string, params: unknown[] = []): Promise<any[]> {\n const result = this.executeParsed(sql, params);\n return Array.isArray(result) ? result : [];\n }\n\n /**\n * Check if a table exists\n * @param {string} name - Table name\n * @returns {Promise<boolean>} Whether the table exists\n */\n async tableExists(name: string): Promise<boolean> {\n return this.data.has(name);\n }\n\n /**\n * Get column metadata for a table\n * @param {string} name - Table name\n * @returns {Promise<ColumnMetadata[]>} Column metadata\n */\n async getTableColumns(name: string): Promise<ColumnMetadata[]> {\n const table = this.data.get(name);\n if (!table) {\n return [];\n }\n return Object.values(table.meta.columns);\n }\n\n /**\n * Create a table from metadata\n * @param {TableMetadata} meta - Table metadata\n */\n async createTable(meta: TableMetadata): Promise<void> {\n if (this.data.has(meta.name)) {\n return;\n }\n this.data.set(meta.name, {\n rows: [],\n autoIncrementId: 0,\n meta: { columns: [...meta.columns] },\n });\n this.flush(meta.name);\n }\n\n /**\n * Drop a table\n * @param {string} name - Table name\n */\n async dropTable(name: string): Promise<void> {\n this.data.delete(name);\n const filePath = join(this.config.directory, `${name}.json`);\n try {\n const { unlinkSync } = await import(\"fs\");\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n }\n } catch {}\n }\n\n /**\n * Add a column to a table\n * @param {string} table - Table name\n * @param {ColumnMetadata} column - Column metadata\n */\n async addColumn(table: string, column: ColumnMetadata): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n tableData.meta.columns.push(column);\n for (const row of tableData.rows) {\n row[column.name] = column.defaultValue ?? null;\n }\n this.flush(table);\n }\n\n /**\n * Drop a column from a table\n * @param {string} table - Table name\n * @param {string} name - Column name\n */\n async dropColumn(table: string, name: string): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n tableData.meta.columns = tableData.meta.columns.filter((c) => c.name !== name);\n for (const row of tableData.rows) {\n delete row[name];\n }\n this.flush(table);\n }\n\n /**\n * Rename a column\n * @param {string} table - Table name\n * @param {string} oldName - Current name\n * @param {string} newName - New name\n */\n async renameColumn(table: string, oldName: string, newName: string): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n const col = tableData.meta.columns.find((c) => c.name === oldName);\n if (col) {\n col.name = newName;\n }\n for (const row of tableData.rows) {\n row[newName] = row[oldName];\n delete row[oldName];\n }\n this.flush(table);\n }\n\n /**\n * Execute within a transaction (pseudo-transaction for file driver)\n * @param {() => Promise<T>} fn - Function to execute\n * @returns {Promise<T>} Result\n */\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n const snapshot = new Map<string, string>();\n for (const [name, tableData] of this.data) {\n snapshot.set(name, JSON.stringify(tableData));\n }\n try {\n const result = await fn();\n this.flushAll();\n return result;\n } catch (err) {\n for (const [name, json] of snapshot) {\n this.data.set(name, JSON.parse(json));\n }\n throw err;\n }\n }\n\n /**\n * Batch fetch all table columns (already in memory for file driver)\n */\n async getAllTableColumns(): Promise<Record<string, ColumnMetadata[]>> {\n const result: Record<string, ColumnMetadata[]> = {};\n for (const [name, tableData] of this.data) {\n result[name] = [...tableData.meta.columns];\n }\n return result;\n }\n\n private loadAll(): void {\n if (!existsSync(this.config.directory)) {\n return;\n }\n const { readdirSync } = require(\"fs\");\n const files = readdirSync(this.config.directory) as string[];\n for (const file of files) {\n if (file.endsWith(\".json\")) {\n const name = file.replace(\".json\", \"\");\n try {\n const content = readFileSync(join(this.config.directory, file), \"utf-8\");\n this.data.set(name, JSON.parse(content));\n } catch {}\n }\n }\n }\n\n private flush(tableName: string): void {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return;\n }\n const filePath = join(this.config.directory, `${tableName}.json`);\n writeFileSync(filePath, JSON.stringify(tableData, null, 2), \"utf-8\");\n }\n\n private flushAll(): void {\n for (const [name] of this.data) {\n this.flush(name);\n }\n }\n\n private executeParsed(sql: string, params: unknown[]): any {\n const trimmed = sql.trim().toUpperCase();\n\n if (trimmed.startsWith(\"CREATE TABLE\")) {\n return { insertId: 0, affectedRows: 0 };\n }\n\n if (trimmed.startsWith(\"DROP TABLE\")) {\n const match = sql.match(/DROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?`?(\\w+)`?/i);\n if (match?.[1]) {\n this.data.delete(match[1]);\n }\n return { insertId: 0, affectedRows: 0 };\n }\n\n if (trimmed.startsWith(\"INSERT\")) {\n return this.handleInsert(sql, params);\n }\n\n if (trimmed.startsWith(\"UPDATE\")) {\n return this.handleUpdate(sql, params);\n }\n if (trimmed.startsWith(\"DELETE\")) {\n return this.handleDelete(sql, params);\n }\n\n if (trimmed.startsWith(\"SELECT\")) {\n return this.handleSelect(sql, params);\n }\n\n return { insertId: 0, affectedRows: 0 };\n }\n\n private handleInsert(sql: string, params: unknown[]): any {\n const tableMatch = sql.match(/INTO\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return { insertId: 0, affectedRows: 0 };\n }\n\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return { insertId: 0, affectedRows: 0 };\n }\n\n const colMatch = sql.match(/\\(([^)]+)\\)\\s+VALUES/i);\n if (!colMatch?.[1]) {\n return { insertId: 0, affectedRows: 0 };\n }\n\n const cols = colMatch[1]\n .split(\",\")\n .map((c) => c.trim().replace(/`/g, \"\"))\n .filter(Boolean);\n\n const rowCount = Math.floor(params.length / cols.length);\n\n let insertId = 0;\n let pkCol: string | null = null;\n for (const col of tableData.meta.columns) {\n if (col.autoIncrement) {\n pkCol = col.name;\n break;\n }\n }\n\n for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) {\n const row: Record<string, unknown> = {};\n const rowParamStart = rowIdx * cols.length;\n\n for (let i = 0; i < cols.length; i++) {\n const col = cols[i];\n if (col) {\n row[col] = params[rowParamStart + i];\n }\n }\n\n if (pkCol && row[pkCol] === undefined) {\n tableData.autoIncrementId++;\n row[pkCol] = tableData.autoIncrementId;\n if (rowIdx === 0) {\n insertId = tableData.autoIncrementId;\n }\n } else if (pkCol && row[pkCol]) {\n if (rowIdx === 0) {\n insertId = row[pkCol] as number;\n }\n }\n\n tableData.rows.push({ ...row });\n }\n\n this.flush(tableName);\n\n return { insertId, affectedRows: rowCount };\n }\n\n private handleUpdate(sql: string, params: unknown[]): any {\n const tableMatch = sql.match(/UPDATE\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return { affectedRows: 0 };\n }\n\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return { affectedRows: 0 };\n }\n\n const setMatch = sql.match(/SET\\s+(.+?)\\s+WHERE/i) || sql.match(/SET\\s+(.+)$/i);\n if (!setMatch?.[1]) {\n return { affectedRows: 0 };\n }\n\n const setClauses = setMatch[1].split(\",\");\n const updates: Record<string, unknown> = {};\n let paramIndex = 0;\n\n for (const clause of setClauses) {\n const eqMatch = clause.match(/`?(\\w+)`?\\s*=\\s*\\?/);\n if (eqMatch?.[1]) {\n updates[eqMatch[1]] = params[paramIndex];\n paramIndex++;\n }\n }\n\n const whereMatch = sql.match(/WHERE\\s+(.+)$/i);\n const whereConditions = whereMatch?.[1] || \"\";\n\n let affectedRows = 0;\n for (const row of tableData.rows) {\n if (this.matchesWhere(row, whereConditions, params, paramIndex)) {\n Object.assign(row, updates);\n affectedRows++;\n }\n }\n\n if (affectedRows > 0) {\n this.flush(tableName);\n }\n\n return { affectedRows };\n }\n\n private handleDelete(sql: string, params: unknown[]): any {\n const tableMatch = sql.match(/FROM\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return { affectedRows: 0 };\n }\n\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return { affectedRows: 0 };\n }\n\n const whereMatch = sql.match(/WHERE\\s+(.+)$/i);\n if (!whereMatch?.[1]) {\n const affectedRows = tableData.rows.length;\n tableData.rows = [];\n if (affectedRows > 0) {\n this.flush(tableName);\n }\n return { affectedRows };\n }\n\n const whereConditions = whereMatch[1];\n const before = tableData.rows.length;\n\n tableData.rows = tableData.rows.filter(\n (row) => !this.matchesWhere(row, whereConditions, params, 0),\n );\n\n const affectedRows = before - tableData.rows.length;\n if (affectedRows > 0) {\n this.flush(tableName);\n }\n\n return { affectedRows };\n }\n\n private handleSelect(sql: string, params: unknown[]): any {\n const countMatch = sql.match(/SELECT\\s+COUNT\\s*\\(\\s*\\*\\s*\\)\\s+as\\s+(\\w+)/i);\n if (countMatch) {\n const tableMatch = sql.match(/FROM\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return [];\n }\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return [{ count: 0 }];\n }\n\n const whereMatch = sql.match(/WHERE\\s+(.+?)(?:\\s+ORDER|\\s+LIMIT|\\s*$)/i);\n if (whereMatch?.[1]) {\n const filter = (row: Record<string, unknown>) =>\n this.matchesWhere(row, whereMatch[1]!, params, 0);\n const count = tableData.rows.filter(filter).length;\n return [{ count }];\n }\n\n return [{ count: tableData.rows.length }];\n }\n\n const tableMatch = sql.match(/FROM\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return [];\n }\n\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return [];\n }\n\n let rows = [...tableData.rows];\n\n const whereMatch = sql.match(/WHERE\\s+(.+?)(?:\\s+ORDER\\s+BY|\\s+LIMIT|\\s*$)/i);\n if (whereMatch?.[1]) {\n rows = rows.filter((row) => this.matchesWhere(row, whereMatch[1]!, params, 0));\n }\n\n const orderMatch = sql.match(/ORDER\\s+BY\\s+(.+?)(?:\\s+LIMIT|\\s*$)/i);\n if (orderMatch?.[1]) {\n const orders = orderMatch[1].split(\",\");\n rows.sort((a, b) => {\n for (const orderClause of orders) {\n const [col, dir] = orderClause.trim().split(/\\s+/);\n const colName = col?.replace(/`/g, \"\") ?? \"\";\n const aVal = (a as any)[colName];\n const bVal = (b as any)[colName];\n if (aVal < bVal) {\n return dir?.toUpperCase() === \"DESC\" ? 1 : -1;\n }\n if (aVal > bVal) {\n return dir?.toUpperCase() === \"DESC\" ? -1 : 1;\n }\n }\n return 0;\n });\n }\n\n const limitMatch = sql.match(/LIMIT\\s+(\\d+)(?:\\s+OFFSET\\s+(\\d+)|\\s*,\\s*(\\d+))?/i);\n if (limitMatch) {\n const limit = Number.parseInt(limitMatch[1]!, 10);\n const offset = limitMatch[2]\n ? Number.parseInt(limitMatch[2], 10)\n : limitMatch[3]\n ? Number.parseInt(limitMatch[3], 10)\n : 0;\n rows = rows.slice(offset, offset + limit);\n }\n\n return rows.map((r) => ({ ...r }));\n }\n\n private matchesWhere(\n row: Record<string, unknown>,\n whereStr: string,\n params: unknown[],\n paramOffset: number,\n ): boolean {\n return this.evaluateWhereExpression(row, whereStr, params, paramOffset).matched;\n }\n\n private evaluateWhereExpression(\n row: Record<string, unknown>,\n expr: string,\n params: unknown[],\n startParamIdx: number,\n ): { matched: boolean; paramIdx: number } {\n let paramIdx = startParamIdx;\n expr = expr.trim();\n\n if (expr.startsWith(\"(\") && expr.endsWith(\")\")) {\n const result = this.evaluateWhereExpression(row, expr.slice(1, -1), params, paramIdx);\n return result;\n }\n\n const orClauses = this.splitByTopLevel(expr, \"OR\");\n if (orClauses.length > 1) {\n for (const orClause of orClauses) {\n const result = this.evaluateWhereExpression(row, orClause.trim(), params, paramIdx);\n paramIdx = result.paramIdx;\n if (result.matched) {\n return { matched: true, paramIdx };\n }\n }\n return { matched: false, paramIdx };\n }\n\n const andClauses = this.splitByTopLevel(expr, \"AND\");\n if (andClauses.length > 1) {\n for (const andClause of andClauses) {\n const result = this.evaluateWhereExpression(row, andClause.trim(), params, paramIdx);\n paramIdx = result.paramIdx;\n if (!result.matched) {\n return { matched: false, paramIdx };\n }\n }\n return { matched: true, paramIdx };\n }\n\n return this.evaluateSingleCondition(row, expr, params, paramIdx);\n }\n\n private splitByTopLevel(expr: string, operator: string): string[] {\n const parts: string[] = [];\n let current = \"\";\n let parenDepth = 0;\n\n const opRegex = new RegExp(`\\\\s+${operator}\\\\s+`, \"i\");\n let i = 0;\n\n while (i < expr.length) {\n const char = expr[i];\n\n if (char === \"(\") {\n parenDepth++;\n current += char;\n i++;\n } else if (char === \")\") {\n parenDepth--;\n current += char;\n i++;\n } else if (parenDepth === 0) {\n const remaining = expr.slice(i);\n const match = remaining.match(opRegex);\n\n if (match && match.index === 0) {\n if (current.trim()) {\n parts.push(current.trim());\n }\n i += match[0]!.length;\n current = \"\";\n } else {\n current += char;\n i++;\n }\n } else {\n current += char;\n i++;\n }\n }\n\n if (current.trim()) {\n parts.push(current.trim());\n }\n\n return parts.length > 0 ? parts : [expr];\n }\n\n private evaluateSingleCondition(\n row: Record<string, unknown>,\n condition: string,\n params: unknown[],\n paramIdx: number,\n ): { matched: boolean; paramIdx: number } {\n condition = condition.trim();\n\n const notInMatch = condition.match(/`?(\\w+)`?\\s+NOT\\s+IN\\s+\\(([^)]*)\\)/i);\n if (notInMatch) {\n const col = notInMatch[1]!;\n const placeholders = notInMatch[2]!.split(\",\").map((p) => p.trim());\n const values: unknown[] = [];\n for (const placeholder of placeholders) {\n if (placeholder === \"?\") {\n values.push(params[paramIdx]);\n paramIdx++;\n }\n }\n const matched = !values.includes(row[col]);\n return { matched, paramIdx };\n }\n\n const inMatch = condition.match(/`?(\\w+)`?\\s+IN\\s+\\(([^)]*)\\)/i);\n if (inMatch) {\n const col = inMatch[1]!;\n const placeholders = inMatch[2]!.split(\",\").map((p) => p.trim());\n const values: unknown[] = [];\n for (const placeholder of placeholders) {\n if (placeholder === \"?\") {\n values.push(params[paramIdx]);\n paramIdx++;\n }\n }\n const matched = values.includes(row[col]);\n return { matched, paramIdx };\n }\n\n const eqMatch = condition.match(/`?(\\w+)`?\\s*=\\s*\\?/);\n if (eqMatch) {\n const matched = row[eqMatch[1]!] === params[paramIdx];\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const neqMatch = condition.match(/`?(\\w+)`?\\s*!=\\s*\\?/);\n if (neqMatch) {\n const matched = row[neqMatch[1]!] !== params[paramIdx];\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const gtMatch = condition.match(/`?(\\w+)`?\\s*>\\s*\\?/);\n if (gtMatch) {\n const matched = (row[gtMatch[1]!] as any) > (params[paramIdx] as any);\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const gteMatch = condition.match(/`?(\\w+)`?\\s*>=\\s*\\?/);\n if (gteMatch) {\n const matched = (row[gteMatch[1]!] as any) >= (params[paramIdx] as any);\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const ltMatch = condition.match(/`?(\\w+)`?\\s*<\\s*\\?/);\n if (ltMatch) {\n const matched = (row[ltMatch[1]!] as any) < (params[paramIdx] as any);\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const lteMatch = condition.match(/`?(\\w+)`?\\s*<=\\s*\\?/);\n if (lteMatch) {\n const matched = (row[lteMatch[1]!] as any) <= (params[paramIdx] as any);\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const notLikeMatch = condition.match(/`?(\\w+)`?\\s+NOT\\s+LIKE\\s+\\?/i);\n if (notLikeMatch) {\n const pattern = (params[paramIdx] as string).replace(/%/g, \".*\");\n const matched = !new RegExp(`^${pattern}$`).test(String(row[notLikeMatch[1]!]));\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const likeMatch = condition.match(/`?(\\w+)`?\\s+LIKE\\s+\\?/i);\n if (likeMatch) {\n const pattern = (params[paramIdx] as string).replace(/%/g, \".*\");\n const matched = new RegExp(`^${pattern}$`).test(String(row[likeMatch[1]!]));\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n return { matched: true, paramIdx };\n }\n}\n"],"mappings":"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"}
@@ -126,65 +126,6 @@ var S3Driver = class extends require_driver.BaseDriver {
126
126
  for (const [name, tableData] of this.data) result[name] = [...tableData.meta.columns];
127
127
  return result;
128
128
  }
129
- getTableData(tableName) {
130
- return this.data.get(tableName);
131
- }
132
- insertRow(tableName, row) {
133
- const tableData = this.data.get(tableName);
134
- if (!tableData) throw new require_errors.DriverError(`Table "${tableName}" does not exist`);
135
- let pkCol = null;
136
- for (const col of tableData.meta.columns) if (col.autoIncrement) {
137
- pkCol = col.name;
138
- break;
139
- }
140
- if (pkCol && row[pkCol] === void 0) {
141
- tableData.autoIncrementId++;
142
- row[pkCol] = tableData.autoIncrementId;
143
- }
144
- tableData.rows.push({ ...row });
145
- this.flush(tableName);
146
- return pkCol ? row[pkCol] : 0;
147
- }
148
- findRows(tableName, filter) {
149
- const tableData = this.data.get(tableName);
150
- if (!tableData) return [];
151
- if (!filter) return tableData.rows.map((r) => ({ ...r }));
152
- return tableData.rows.filter(filter).map((r) => ({ ...r }));
153
- }
154
- updateRows(tableName, filter, data) {
155
- const tableData = this.data.get(tableName);
156
- if (!tableData) return 0;
157
- let count = 0;
158
- for (const row of tableData.rows) if (filter(row)) {
159
- Object.assign(row, data);
160
- count++;
161
- }
162
- if (count > 0) this.flush(tableName);
163
- return count;
164
- }
165
- deleteRows(tableName, filter) {
166
- const tableData = this.data.get(tableName);
167
- if (!tableData) return 0;
168
- const before = tableData.rows.length;
169
- tableData.rows = tableData.rows.filter((r) => !filter(r));
170
- const deleted = before - tableData.rows.length;
171
- if (deleted > 0) this.flush(tableName);
172
- return deleted;
173
- }
174
- truncateTable(tableName) {
175
- const tableData = this.data.get(tableName);
176
- if (tableData) {
177
- tableData.rows = [];
178
- tableData.autoIncrementId = 0;
179
- this.flush(tableName);
180
- }
181
- }
182
- countRows(tableName, filter) {
183
- const tableData = this.data.get(tableName);
184
- if (!tableData) return 0;
185
- if (!filter) return tableData.rows.length;
186
- return tableData.rows.filter(filter).length;
187
- }
188
129
  async loadAll() {
189
130
  try {
190
131
  const { ListObjectsV2Command, GetObjectCommand } = await import("@aws-sdk/client-s3");
@@ -237,42 +178,306 @@ var S3Driver = class extends require_driver.BaseDriver {
237
178
  affectedRows: 0
238
179
  };
239
180
  }
240
- if (trimmed.startsWith("SELECT")) {
181
+ if (trimmed.startsWith("INSERT")) return this.handleInsert(sql, params);
182
+ if (trimmed.startsWith("UPDATE")) return this.handleUpdate(sql, params);
183
+ if (trimmed.startsWith("DELETE")) return this.handleDelete(sql, params);
184
+ if (trimmed.startsWith("SELECT")) return this.handleSelect(sql, params);
185
+ return {
186
+ insertId: 0,
187
+ affectedRows: 0
188
+ };
189
+ }
190
+ handleInsert(sql, params) {
191
+ const tableMatch = sql.match(/INTO\s+`?(\w+)`?/i);
192
+ if (!tableMatch?.[1]) return {
193
+ insertId: 0,
194
+ affectedRows: 0
195
+ };
196
+ const tableName = tableMatch[1];
197
+ const tableData = this.data.get(tableName);
198
+ if (!tableData) return {
199
+ insertId: 0,
200
+ affectedRows: 0
201
+ };
202
+ const colMatch = sql.match(/\(([^)]+)\)\s+VALUES/i);
203
+ if (!colMatch?.[1]) return {
204
+ insertId: 0,
205
+ affectedRows: 0
206
+ };
207
+ const cols = colMatch[1].split(",").map((c) => c.trim().replace(/`/g, "")).filter(Boolean);
208
+ const rowCount = Math.floor(params.length / cols.length);
209
+ let insertId = 0;
210
+ let pkCol = null;
211
+ for (const col of tableData.meta.columns) if (col.autoIncrement) {
212
+ pkCol = col.name;
213
+ break;
214
+ }
215
+ for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) {
216
+ const row = {};
217
+ const rowParamStart = rowIdx * cols.length;
218
+ for (let i = 0; i < cols.length; i++) {
219
+ const col = cols[i];
220
+ if (col) row[col] = params[rowParamStart + i];
221
+ }
222
+ if (pkCol && row[pkCol] === void 0) {
223
+ tableData.autoIncrementId++;
224
+ row[pkCol] = tableData.autoIncrementId;
225
+ if (rowIdx === 0) insertId = tableData.autoIncrementId;
226
+ } else if (pkCol && row[pkCol]) {
227
+ if (rowIdx === 0) insertId = row[pkCol];
228
+ }
229
+ tableData.rows.push({ ...row });
230
+ }
231
+ this.flush(tableName);
232
+ return {
233
+ insertId,
234
+ affectedRows: rowCount
235
+ };
236
+ }
237
+ handleUpdate(sql, params) {
238
+ const tableMatch = sql.match(/UPDATE\s+`?(\w+)`?/i);
239
+ if (!tableMatch?.[1]) return { affectedRows: 0 };
240
+ const tableName = tableMatch[1];
241
+ const tableData = this.data.get(tableName);
242
+ if (!tableData) return { affectedRows: 0 };
243
+ const setMatch = sql.match(/SET\s+(.+?)\s+WHERE/i) || sql.match(/SET\s+(.+)$/i);
244
+ if (!setMatch?.[1]) return { affectedRows: 0 };
245
+ const setClauses = setMatch[1].split(",");
246
+ const updates = {};
247
+ let paramIndex = 0;
248
+ for (const clause of setClauses) {
249
+ const eqMatch = clause.match(/`?(\w+)`?\s*=\s*\?/);
250
+ if (eqMatch?.[1]) {
251
+ updates[eqMatch[1]] = params[paramIndex];
252
+ paramIndex++;
253
+ }
254
+ }
255
+ const whereConditions = sql.match(/WHERE\s+(.+)$/i)?.[1] || "";
256
+ let affectedRows = 0;
257
+ for (const row of tableData.rows) if (this.matchesWhere(row, whereConditions, params, paramIndex)) {
258
+ Object.assign(row, updates);
259
+ affectedRows++;
260
+ }
261
+ if (affectedRows > 0) this.flush(tableName);
262
+ return { affectedRows };
263
+ }
264
+ handleDelete(sql, params) {
265
+ const tableMatch = sql.match(/FROM\s+`?(\w+)`?/i);
266
+ if (!tableMatch?.[1]) return { affectedRows: 0 };
267
+ const tableName = tableMatch[1];
268
+ const tableData = this.data.get(tableName);
269
+ if (!tableData) return { affectedRows: 0 };
270
+ const whereMatch = sql.match(/WHERE\s+(.+)$/i);
271
+ if (!whereMatch?.[1]) {
272
+ const affectedRows = tableData.rows.length;
273
+ tableData.rows = [];
274
+ if (affectedRows > 0) this.flush(tableName);
275
+ return { affectedRows };
276
+ }
277
+ const whereConditions = whereMatch[1];
278
+ const before = tableData.rows.length;
279
+ tableData.rows = tableData.rows.filter((row) => !this.matchesWhere(row, whereConditions, params, 0));
280
+ const affectedRows = before - tableData.rows.length;
281
+ if (affectedRows > 0) this.flush(tableName);
282
+ return { affectedRows };
283
+ }
284
+ handleSelect(sql, params) {
285
+ if (sql.match(/SELECT\s+COUNT\s*\(\s*\*\s*\)\s+as\s+(\w+)/i)) {
241
286
  const tableMatch = sql.match(/FROM\s+`?(\w+)`?/i);
242
- if (!tableMatch) return [];
287
+ if (!tableMatch?.[1]) return [];
243
288
  const tableName = tableMatch[1];
244
- if (!tableName) return [];
245
- return this.findRows(tableName);
289
+ const tableData = this.data.get(tableName);
290
+ if (!tableData) return [{ count: 0 }];
291
+ const whereMatch = sql.match(/WHERE\s+(.+?)(?:\s+ORDER|\s+LIMIT|\s*$)/i);
292
+ if (whereMatch?.[1]) {
293
+ const filter = (row) => this.matchesWhere(row, whereMatch[1], params, 0);
294
+ return [{ count: tableData.rows.filter(filter).length }];
295
+ }
296
+ return [{ count: tableData.rows.length }];
246
297
  }
247
- if (trimmed.startsWith("INSERT")) {
248
- const tableMatch = sql.match(/INTO\s+`?(\w+)`?/i);
249
- if (!tableMatch) return {
250
- insertId: 0,
251
- affectedRows: 0
298
+ const tableMatch = sql.match(/FROM\s+`?(\w+)`?/i);
299
+ if (!tableMatch?.[1]) return [];
300
+ const tableName = tableMatch[1];
301
+ const tableData = this.data.get(tableName);
302
+ if (!tableData) return [];
303
+ let rows = [...tableData.rows];
304
+ const whereMatch = sql.match(/WHERE\s+(.+?)(?:\s+ORDER\s+BY|\s+LIMIT|\s*$)/i);
305
+ if (whereMatch?.[1]) rows = rows.filter((row) => this.matchesWhere(row, whereMatch[1], params, 0));
306
+ const orderMatch = sql.match(/ORDER\s+BY\s+(.+?)(?:\s+LIMIT|\s*$)/i);
307
+ if (orderMatch?.[1]) {
308
+ const orders = orderMatch[1].split(",");
309
+ rows.sort((a, b) => {
310
+ for (const orderClause of orders) {
311
+ const [col, dir] = orderClause.trim().split(/\s+/);
312
+ const colName = col?.replace(/`/g, "") ?? "";
313
+ const aVal = a[colName];
314
+ const bVal = b[colName];
315
+ if (aVal < bVal) return dir?.toUpperCase() === "DESC" ? 1 : -1;
316
+ if (aVal > bVal) return dir?.toUpperCase() === "DESC" ? -1 : 1;
317
+ }
318
+ return 0;
319
+ });
320
+ }
321
+ const limitMatch = sql.match(/LIMIT\s+(\d+)(?:\s+OFFSET\s+(\d+)|\s*,\s*(\d+))?/i);
322
+ if (limitMatch) {
323
+ const limit = Number.parseInt(limitMatch[1], 10);
324
+ const offset = limitMatch[2] ? Number.parseInt(limitMatch[2], 10) : limitMatch[3] ? Number.parseInt(limitMatch[3], 10) : 0;
325
+ rows = rows.slice(offset, offset + limit);
326
+ }
327
+ return rows.map((r) => ({ ...r }));
328
+ }
329
+ matchesWhere(row, whereStr, params, paramOffset) {
330
+ return this.evaluateWhereExpression(row, whereStr, params, paramOffset).matched;
331
+ }
332
+ evaluateWhereExpression(row, expr, params, startParamIdx) {
333
+ let paramIdx = startParamIdx;
334
+ expr = expr.trim();
335
+ if (expr.startsWith("(") && expr.endsWith(")")) return this.evaluateWhereExpression(row, expr.slice(1, -1), params, paramIdx);
336
+ const orClauses = this.splitByTopLevel(expr, "OR");
337
+ if (orClauses.length > 1) {
338
+ for (const orClause of orClauses) {
339
+ const result = this.evaluateWhereExpression(row, orClause.trim(), params, paramIdx);
340
+ paramIdx = result.paramIdx;
341
+ if (result.matched) return {
342
+ matched: true,
343
+ paramIdx
344
+ };
345
+ }
346
+ return {
347
+ matched: false,
348
+ paramIdx
252
349
  };
253
- const colMatch = sql.match(/\(([^)]+)\)\s+VALUES/i);
254
- if (!colMatch) return {
255
- insertId: 0,
256
- affectedRows: 0
350
+ }
351
+ const andClauses = this.splitByTopLevel(expr, "AND");
352
+ if (andClauses.length > 1) {
353
+ for (const andClause of andClauses) {
354
+ const result = this.evaluateWhereExpression(row, andClause.trim(), params, paramIdx);
355
+ paramIdx = result.paramIdx;
356
+ if (!result.matched) return {
357
+ matched: false,
358
+ paramIdx
359
+ };
360
+ }
361
+ return {
362
+ matched: true,
363
+ paramIdx
257
364
  };
258
- const cols = colMatch[1]?.split(",").map((c) => c.trim().replace(/`/g, ""));
259
- if (!cols) return {
260
- insertId: 0,
261
- affectedRows: 0
365
+ }
366
+ return this.evaluateSingleCondition(row, expr, params, paramIdx);
367
+ }
368
+ splitByTopLevel(expr, operator) {
369
+ const parts = [];
370
+ let current = "";
371
+ let parenDepth = 0;
372
+ const opRegex = new RegExp(`\\s+${operator}\\s+`, "i");
373
+ let i = 0;
374
+ while (i < expr.length) {
375
+ const char = expr[i];
376
+ if (char === "(") {
377
+ parenDepth++;
378
+ current += char;
379
+ i++;
380
+ } else if (char === ")") {
381
+ parenDepth--;
382
+ current += char;
383
+ i++;
384
+ } else if (parenDepth === 0) {
385
+ const match = expr.slice(i).match(opRegex);
386
+ if (match && match.index === 0) {
387
+ if (current.trim()) parts.push(current.trim());
388
+ i += match[0].length;
389
+ current = "";
390
+ } else {
391
+ current += char;
392
+ i++;
393
+ }
394
+ } else {
395
+ current += char;
396
+ i++;
397
+ }
398
+ }
399
+ if (current.trim()) parts.push(current.trim());
400
+ return parts.length > 0 ? parts : [expr];
401
+ }
402
+ evaluateSingleCondition(row, condition, params, paramIdx) {
403
+ condition = condition.trim();
404
+ const notInMatch = condition.match(/`?(\w+)`?\s+NOT\s+IN\s+\(([^)]*)\)/i);
405
+ if (notInMatch) {
406
+ const col = notInMatch[1];
407
+ const placeholders = notInMatch[2].split(",").map((p) => p.trim());
408
+ const values = [];
409
+ for (const placeholder of placeholders) if (placeholder === "?") {
410
+ values.push(params[paramIdx]);
411
+ paramIdx++;
412
+ }
413
+ return {
414
+ matched: !values.includes(row[col]),
415
+ paramIdx
262
416
  };
263
- const row = {};
264
- for (let i = 0; i < cols.length; i++) {
265
- const col = cols[i];
266
- if (col) row[col] = params[i];
417
+ }
418
+ const inMatch = condition.match(/`?(\w+)`?\s+IN\s+\(([^)]*)\)/i);
419
+ if (inMatch) {
420
+ const col = inMatch[1];
421
+ const placeholders = inMatch[2].split(",").map((p) => p.trim());
422
+ const values = [];
423
+ for (const placeholder of placeholders) if (placeholder === "?") {
424
+ values.push(params[paramIdx]);
425
+ paramIdx++;
267
426
  }
268
427
  return {
269
- insertId: this.insertRow(tableMatch[1], row),
270
- affectedRows: 1
428
+ matched: values.includes(row[col]),
429
+ paramIdx
430
+ };
431
+ }
432
+ const eqMatch = condition.match(/`?(\w+)`?\s*=\s*\?/);
433
+ if (eqMatch) return {
434
+ matched: row[eqMatch[1]] === params[paramIdx],
435
+ paramIdx: paramIdx + 1
436
+ };
437
+ const neqMatch = condition.match(/`?(\w+)`?\s*!=\s*\?/);
438
+ if (neqMatch) return {
439
+ matched: row[neqMatch[1]] !== params[paramIdx],
440
+ paramIdx: paramIdx + 1
441
+ };
442
+ const gtMatch = condition.match(/`?(\w+)`?\s*>\s*\?/);
443
+ if (gtMatch) return {
444
+ matched: row[gtMatch[1]] > params[paramIdx],
445
+ paramIdx: paramIdx + 1
446
+ };
447
+ const gteMatch = condition.match(/`?(\w+)`?\s*>=\s*\?/);
448
+ if (gteMatch) return {
449
+ matched: row[gteMatch[1]] >= params[paramIdx],
450
+ paramIdx: paramIdx + 1
451
+ };
452
+ const ltMatch = condition.match(/`?(\w+)`?\s*<\s*\?/);
453
+ if (ltMatch) return {
454
+ matched: row[ltMatch[1]] < params[paramIdx],
455
+ paramIdx: paramIdx + 1
456
+ };
457
+ const lteMatch = condition.match(/`?(\w+)`?\s*<=\s*\?/);
458
+ if (lteMatch) return {
459
+ matched: row[lteMatch[1]] <= params[paramIdx],
460
+ paramIdx: paramIdx + 1
461
+ };
462
+ const notLikeMatch = condition.match(/`?(\w+)`?\s+NOT\s+LIKE\s+\?/i);
463
+ if (notLikeMatch) {
464
+ const pattern = params[paramIdx].replace(/%/g, ".*");
465
+ return {
466
+ matched: !new RegExp(`^${pattern}$`).test(String(row[notLikeMatch[1]])),
467
+ paramIdx: paramIdx + 1
468
+ };
469
+ }
470
+ const likeMatch = condition.match(/`?(\w+)`?\s+LIKE\s+\?/i);
471
+ if (likeMatch) {
472
+ const pattern = params[paramIdx].replace(/%/g, ".*");
473
+ return {
474
+ matched: new RegExp(`^${pattern}$`).test(String(row[likeMatch[1]])),
475
+ paramIdx: paramIdx + 1
271
476
  };
272
477
  }
273
478
  return {
274
- insertId: 0,
275
- affectedRows: 0
479
+ matched: true,
480
+ paramIdx
276
481
  };
277
482
  }
278
483
  };
@@ -1 +1 @@
1
- {"version":3,"file":"s3.cjs","names":["BaseDriver","DriverError"],"sources":["../../src/drivers/s3.ts"],"sourcesContent":["import { DriverError } from \"../errors\";\nimport type { ColumnMetadata, S3ConnectionConfig, TableMetadata } from \"../types\";\nimport { BaseDriver } from \"./driver\";\n\ninterface S3TableData {\n rows: Record<string, unknown>[];\n autoIncrementId: number;\n meta: {\n columns: ColumnMetadata[];\n };\n}\n\n/**\n * S3-based database driver using JSON objects for storage\n */\nexport class S3Driver extends BaseDriver {\n private config: S3ConnectionConfig;\n private data = new Map<string, S3TableData>();\n private client: any = null;\n\n constructor(config: S3ConnectionConfig) {\n super();\n this.config = config;\n }\n\n private getKey(tableName: string): string {\n const prefix = this.config.prefix ? `${this.config.prefix}/` : \"\";\n return `${prefix}${tableName}.json`;\n }\n\n async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n try {\n const { S3Client, HeadBucketCommand } = await import(\"@aws-sdk/client-s3\");\n const clientConfig: any = {};\n if (this.config.region) {\n clientConfig.region = this.config.region;\n }\n if (this.config.endpoint) {\n clientConfig.endpoint = this.config.endpoint;\n clientConfig.forcePathStyle = true;\n }\n if (this.config.accessKeyId && this.config.secretAccessKey) {\n clientConfig.credentials = {\n accessKeyId: this.config.accessKeyId,\n secretAccessKey: this.config.secretAccessKey,\n };\n }\n this.client = new S3Client(clientConfig);\n\n try {\n await this.client.send(new HeadBucketCommand({ Bucket: this.config.bucket }));\n } catch (err: any) {\n if (err.name === \"NotFound\" || err.$metadata?.httpStatusCode === 404) {\n const { CreateBucketCommand } = await import(\"@aws-sdk/client-s3\");\n await this.client.send(new CreateBucketCommand({ Bucket: this.config.bucket }));\n } else if (err.$metadata?.httpStatusCode !== 200) {\n throw err;\n }\n }\n\n await this.loadAll();\n this.connected = true;\n } catch (err: any) {\n throw new DriverError(`Failed to connect to S3: ${err.message}`);\n }\n }\n\n async disconnect(): Promise<void> {\n await this.flushAll();\n this.data.clear();\n this.client = null;\n this.connected = false;\n }\n\n async execute(sql: string, params: unknown[] = []): Promise<any> {\n return this.executeParsed(sql, params);\n }\n\n async query(sql: string, params: unknown[] = []): Promise<any[]> {\n const result = this.executeParsed(sql, params);\n return Array.isArray(result) ? result : [];\n }\n\n async tableExists(name: string): Promise<boolean> {\n return this.data.has(name);\n }\n\n async getTableColumns(name: string): Promise<ColumnMetadata[]> {\n const table = this.data.get(name);\n if (!table) {\n return [];\n }\n return Object.values(table.meta.columns);\n }\n\n async createTable(meta: TableMetadata): Promise<void> {\n if (this.data.has(meta.name)) {\n return;\n }\n this.data.set(meta.name, {\n rows: [],\n autoIncrementId: 0,\n meta: { columns: [...meta.columns] },\n });\n await this.flush(meta.name);\n }\n\n async dropTable(name: string): Promise<void> {\n this.data.delete(name);\n try {\n const { DeleteObjectCommand } = await import(\"@aws-sdk/client-s3\");\n await this.client.send(\n new DeleteObjectCommand({\n Bucket: this.config.bucket,\n Key: this.getKey(name),\n }),\n );\n } catch {}\n }\n\n async addColumn(table: string, column: ColumnMetadata): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n tableData.meta.columns.push(column);\n for (const row of tableData.rows) {\n row[column.name] = column.defaultValue ?? null;\n }\n await this.flush(table);\n }\n\n async dropColumn(table: string, name: string): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n tableData.meta.columns = tableData.meta.columns.filter((c) => c.name !== name);\n for (const row of tableData.rows) {\n delete row[name];\n }\n await this.flush(table);\n }\n\n async renameColumn(table: string, oldName: string, newName: string): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n const col = tableData.meta.columns.find((c) => c.name === oldName);\n if (col) {\n col.name = newName;\n }\n for (const row of tableData.rows) {\n row[newName] = row[oldName];\n delete row[oldName];\n }\n await this.flush(table);\n }\n\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n const snapshot = new Map<string, string>();\n for (const [name, tableData] of this.data) {\n snapshot.set(name, JSON.stringify(tableData));\n }\n try {\n const result = await fn();\n await this.flushAll();\n return result;\n } catch (err) {\n for (const [name, json] of snapshot) {\n this.data.set(name, JSON.parse(json));\n }\n throw err;\n }\n }\n\n async getAllTableColumns(): Promise<Record<string, ColumnMetadata[]>> {\n const result: Record<string, ColumnMetadata[]> = {};\n for (const [name, tableData] of this.data) {\n result[name] = [...tableData.meta.columns];\n }\n return result;\n }\n\n getTableData(tableName: string): S3TableData | undefined {\n return this.data.get(tableName);\n }\n\n insertRow(tableName: string, row: Record<string, unknown>): number {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n throw new DriverError(`Table \"${tableName}\" does not exist`);\n }\n let pkCol: string | null = null;\n for (const col of tableData.meta.columns) {\n if (col.autoIncrement) {\n pkCol = col.name;\n break;\n }\n }\n if (pkCol && row[pkCol] === undefined) {\n tableData.autoIncrementId++;\n row[pkCol] = tableData.autoIncrementId;\n }\n tableData.rows.push({ ...row });\n this.flush(tableName);\n return pkCol ? (row[pkCol] as number) : 0;\n }\n\n findRows(\n tableName: string,\n filter?: (row: Record<string, unknown>) => boolean,\n ): Record<string, unknown>[] {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return [];\n }\n if (!filter) {\n return tableData.rows.map((r) => ({ ...r }));\n }\n return tableData.rows.filter(filter).map((r) => ({ ...r }));\n }\n\n updateRows(\n tableName: string,\n filter: (row: Record<string, unknown>) => boolean,\n data: Record<string, unknown>,\n ): number {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return 0;\n }\n let count = 0;\n for (const row of tableData.rows) {\n if (filter(row)) {\n Object.assign(row, data);\n count++;\n }\n }\n if (count > 0) {\n this.flush(tableName);\n }\n return count;\n }\n\n deleteRows(tableName: string, filter: (row: Record<string, unknown>) => boolean): number {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return 0;\n }\n const before = tableData.rows.length;\n tableData.rows = tableData.rows.filter((r) => !filter(r));\n const deleted = before - tableData.rows.length;\n if (deleted > 0) {\n this.flush(tableName);\n }\n return deleted;\n }\n\n truncateTable(tableName: string): void {\n const tableData = this.data.get(tableName);\n if (tableData) {\n tableData.rows = [];\n tableData.autoIncrementId = 0;\n this.flush(tableName);\n }\n }\n\n countRows(tableName: string, filter?: (row: Record<string, unknown>) => boolean): number {\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return 0;\n }\n if (!filter) {\n return tableData.rows.length;\n }\n return tableData.rows.filter(filter).length;\n }\n\n private async loadAll(): Promise<void> {\n try {\n const { ListObjectsV2Command, GetObjectCommand } = await import(\"@aws-sdk/client-s3\");\n const prefix = this.config.prefix ? `${this.config.prefix}/` : \"\";\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.config.bucket,\n Prefix: prefix,\n }),\n );\n\n if (response.Contents) {\n for (const obj of response.Contents) {\n if (obj.Key?.endsWith(\".json\")) {\n const tableName = obj.Key.replace(prefix, \"\").replace(\".json\", \"\");\n try {\n const getResponse = await this.client.send(\n new GetObjectCommand({\n Bucket: this.config.bucket,\n Key: obj.Key,\n }),\n );\n const body = await getResponse.Body?.transformToString();\n if (body) {\n this.data.set(tableName, JSON.parse(body));\n }\n } catch {}\n }\n }\n }\n } catch {}\n }\n\n private async flush(tableName: string): Promise<void> {\n const tableData = this.data.get(tableName);\n if (!tableData || !this.client) {\n return;\n }\n try {\n const { PutObjectCommand } = await import(\"@aws-sdk/client-s3\");\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.config.bucket,\n Key: this.getKey(tableName),\n Body: JSON.stringify(tableData),\n ContentType: \"application/json\",\n }),\n );\n } catch {}\n }\n\n private async flushAll(): Promise<void> {\n for (const [name] of this.data) {\n await this.flush(name);\n }\n }\n\n private executeParsed(sql: string, params: unknown[]): any {\n const trimmed = sql.trim().toUpperCase();\n if (trimmed.startsWith(\"CREATE TABLE\")) {\n return { insertId: 0, affectedRows: 0 };\n }\n if (trimmed.startsWith(\"DROP TABLE\")) {\n const match = sql.match(/DROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?`?(\\w+)`?/i);\n if (match?.[1]) {\n this.data.delete(match[1]);\n }\n return { insertId: 0, affectedRows: 0 };\n }\n if (trimmed.startsWith(\"SELECT\")) {\n const tableMatch = sql.match(/FROM\\s+`?(\\w+)`?/i);\n if (!tableMatch) {\n return [];\n }\n const tableName = tableMatch[1];\n if (!tableName) {\n return [];\n }\n return this.findRows(tableName);\n }\n if (trimmed.startsWith(\"INSERT\")) {\n const tableMatch = sql.match(/INTO\\s+`?(\\w+)`?/i);\n if (!tableMatch) {\n return { insertId: 0, affectedRows: 0 };\n }\n const colMatch = sql.match(/\\(([^)]+)\\)\\s+VALUES/i);\n if (!colMatch) {\n return { insertId: 0, affectedRows: 0 };\n }\n const cols = colMatch[1]?.split(\",\").map((c) => c.trim().replace(/`/g, \"\"));\n if (!cols) {\n return { insertId: 0, affectedRows: 0 };\n }\n const row: Record<string, unknown> = {};\n for (let i = 0; i < cols.length; i++) {\n const col = cols[i];\n if (col) {\n row[col] = params[i];\n }\n }\n const id = this.insertRow(tableMatch[1]!, row);\n return { insertId: id, affectedRows: 1 };\n }\n return { insertId: 0, affectedRows: 0 };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"s3.cjs","names":["BaseDriver","DriverError"],"sources":["../../src/drivers/s3.ts"],"sourcesContent":["import { DriverError } from \"../errors\";\nimport type { ColumnMetadata, S3ConnectionConfig, TableMetadata } from \"../types\";\nimport { BaseDriver } from \"./driver\";\n\ninterface S3TableData {\n rows: Record<string, unknown>[];\n autoIncrementId: number;\n meta: {\n columns: ColumnMetadata[];\n };\n}\n\n/**\n * S3-based database driver using JSON objects for storage\n */\nexport class S3Driver extends BaseDriver {\n private config: S3ConnectionConfig;\n private data = new Map<string, S3TableData>();\n private client: any = null;\n\n constructor(config: S3ConnectionConfig) {\n super();\n this.config = config;\n }\n\n private getKey(tableName: string): string {\n const prefix = this.config.prefix ? `${this.config.prefix}/` : \"\";\n return `${prefix}${tableName}.json`;\n }\n\n async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n try {\n const { S3Client, HeadBucketCommand } = await import(\"@aws-sdk/client-s3\");\n const clientConfig: any = {};\n if (this.config.region) {\n clientConfig.region = this.config.region;\n }\n if (this.config.endpoint) {\n clientConfig.endpoint = this.config.endpoint;\n clientConfig.forcePathStyle = true;\n }\n if (this.config.accessKeyId && this.config.secretAccessKey) {\n clientConfig.credentials = {\n accessKeyId: this.config.accessKeyId,\n secretAccessKey: this.config.secretAccessKey,\n };\n }\n this.client = new S3Client(clientConfig);\n\n try {\n await this.client.send(new HeadBucketCommand({ Bucket: this.config.bucket }));\n } catch (err: any) {\n if (err.name === \"NotFound\" || err.$metadata?.httpStatusCode === 404) {\n const { CreateBucketCommand } = await import(\"@aws-sdk/client-s3\");\n await this.client.send(new CreateBucketCommand({ Bucket: this.config.bucket }));\n } else if (err.$metadata?.httpStatusCode !== 200) {\n throw err;\n }\n }\n\n await this.loadAll();\n this.connected = true;\n } catch (err: any) {\n throw new DriverError(`Failed to connect to S3: ${err.message}`);\n }\n }\n\n async disconnect(): Promise<void> {\n await this.flushAll();\n this.data.clear();\n this.client = null;\n this.connected = false;\n }\n\n async execute(sql: string, params: unknown[] = []): Promise<any> {\n return this.executeParsed(sql, params);\n }\n\n async query(sql: string, params: unknown[] = []): Promise<any[]> {\n const result = this.executeParsed(sql, params);\n return Array.isArray(result) ? result : [];\n }\n\n async tableExists(name: string): Promise<boolean> {\n return this.data.has(name);\n }\n\n async getTableColumns(name: string): Promise<ColumnMetadata[]> {\n const table = this.data.get(name);\n if (!table) {\n return [];\n }\n return Object.values(table.meta.columns);\n }\n\n async createTable(meta: TableMetadata): Promise<void> {\n if (this.data.has(meta.name)) {\n return;\n }\n this.data.set(meta.name, {\n rows: [],\n autoIncrementId: 0,\n meta: { columns: [...meta.columns] },\n });\n await this.flush(meta.name);\n }\n\n async dropTable(name: string): Promise<void> {\n this.data.delete(name);\n try {\n const { DeleteObjectCommand } = await import(\"@aws-sdk/client-s3\");\n await this.client.send(\n new DeleteObjectCommand({\n Bucket: this.config.bucket,\n Key: this.getKey(name),\n }),\n );\n } catch {}\n }\n\n async addColumn(table: string, column: ColumnMetadata): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n tableData.meta.columns.push(column);\n for (const row of tableData.rows) {\n row[column.name] = column.defaultValue ?? null;\n }\n await this.flush(table);\n }\n\n async dropColumn(table: string, name: string): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n tableData.meta.columns = tableData.meta.columns.filter((c) => c.name !== name);\n for (const row of tableData.rows) {\n delete row[name];\n }\n await this.flush(table);\n }\n\n async renameColumn(table: string, oldName: string, newName: string): Promise<void> {\n const tableData = this.data.get(table);\n if (!tableData) {\n throw new DriverError(`Table \"${table}\" does not exist`);\n }\n const col = tableData.meta.columns.find((c) => c.name === oldName);\n if (col) {\n col.name = newName;\n }\n for (const row of tableData.rows) {\n row[newName] = row[oldName];\n delete row[oldName];\n }\n await this.flush(table);\n }\n\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n const snapshot = new Map<string, string>();\n for (const [name, tableData] of this.data) {\n snapshot.set(name, JSON.stringify(tableData));\n }\n try {\n const result = await fn();\n await this.flushAll();\n return result;\n } catch (err) {\n for (const [name, json] of snapshot) {\n this.data.set(name, JSON.parse(json));\n }\n throw err;\n }\n }\n\n async getAllTableColumns(): Promise<Record<string, ColumnMetadata[]>> {\n const result: Record<string, ColumnMetadata[]> = {};\n for (const [name, tableData] of this.data) {\n result[name] = [...tableData.meta.columns];\n }\n return result;\n }\n\n private async loadAll(): Promise<void> {\n try {\n const { ListObjectsV2Command, GetObjectCommand } = await import(\"@aws-sdk/client-s3\");\n const prefix = this.config.prefix ? `${this.config.prefix}/` : \"\";\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.config.bucket,\n Prefix: prefix,\n }),\n );\n\n if (response.Contents) {\n for (const obj of response.Contents) {\n if (obj.Key?.endsWith(\".json\")) {\n const tableName = obj.Key.replace(prefix, \"\").replace(\".json\", \"\");\n try {\n const getResponse = await this.client.send(\n new GetObjectCommand({\n Bucket: this.config.bucket,\n Key: obj.Key,\n }),\n );\n const body = await getResponse.Body?.transformToString();\n if (body) {\n this.data.set(tableName, JSON.parse(body));\n }\n } catch {}\n }\n }\n }\n } catch {}\n }\n\n private async flush(tableName: string): Promise<void> {\n const tableData = this.data.get(tableName);\n if (!tableData || !this.client) {\n return;\n }\n try {\n const { PutObjectCommand } = await import(\"@aws-sdk/client-s3\");\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.config.bucket,\n Key: this.getKey(tableName),\n Body: JSON.stringify(tableData),\n ContentType: \"application/json\",\n }),\n );\n } catch {}\n }\n\n private async flushAll(): Promise<void> {\n for (const [name] of this.data) {\n await this.flush(name);\n }\n }\n\n private executeParsed(sql: string, params: unknown[]): any {\n const trimmed = sql.trim().toUpperCase();\n\n if (trimmed.startsWith(\"CREATE TABLE\")) {\n return { insertId: 0, affectedRows: 0 };\n }\n\n if (trimmed.startsWith(\"DROP TABLE\")) {\n const match = sql.match(/DROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?`?(\\w+)`?/i);\n if (match?.[1]) {\n this.data.delete(match[1]);\n }\n return { insertId: 0, affectedRows: 0 };\n }\n\n if (trimmed.startsWith(\"INSERT\")) {\n return this.handleInsert(sql, params);\n }\n\n if (trimmed.startsWith(\"UPDATE\")) {\n return this.handleUpdate(sql, params);\n }\n\n if (trimmed.startsWith(\"DELETE\")) {\n return this.handleDelete(sql, params);\n }\n\n if (trimmed.startsWith(\"SELECT\")) {\n return this.handleSelect(sql, params);\n }\n\n return { insertId: 0, affectedRows: 0 };\n }\n\n private handleInsert(sql: string, params: unknown[]): any {\n const tableMatch = sql.match(/INTO\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return { insertId: 0, affectedRows: 0 };\n }\n\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return { insertId: 0, affectedRows: 0 };\n }\n\n const colMatch = sql.match(/\\(([^)]+)\\)\\s+VALUES/i);\n if (!colMatch?.[1]) {\n return { insertId: 0, affectedRows: 0 };\n }\n\n const cols = colMatch[1]\n .split(\",\")\n .map((c) => c.trim().replace(/`/g, \"\"))\n .filter(Boolean);\n\n const rowCount = Math.floor(params.length / cols.length);\n\n let insertId = 0;\n let pkCol: string | null = null;\n for (const col of tableData.meta.columns) {\n if (col.autoIncrement) {\n pkCol = col.name;\n break;\n }\n }\n\n for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) {\n const row: Record<string, unknown> = {};\n const rowParamStart = rowIdx * cols.length;\n\n for (let i = 0; i < cols.length; i++) {\n const col = cols[i];\n if (col) {\n row[col] = params[rowParamStart + i];\n }\n }\n\n if (pkCol && row[pkCol] === undefined) {\n tableData.autoIncrementId++;\n row[pkCol] = tableData.autoIncrementId;\n if (rowIdx === 0) {\n insertId = tableData.autoIncrementId;\n }\n } else if (pkCol && row[pkCol]) {\n if (rowIdx === 0) {\n insertId = row[pkCol] as number;\n }\n }\n\n tableData.rows.push({ ...row });\n }\n\n this.flush(tableName);\n\n return { insertId, affectedRows: rowCount };\n }\n\n private handleUpdate(sql: string, params: unknown[]): any {\n const tableMatch = sql.match(/UPDATE\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return { affectedRows: 0 };\n }\n\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return { affectedRows: 0 };\n }\n\n const setMatch = sql.match(/SET\\s+(.+?)\\s+WHERE/i) || sql.match(/SET\\s+(.+)$/i);\n if (!setMatch?.[1]) {\n return { affectedRows: 0 };\n }\n\n const setClauses = setMatch[1].split(\",\");\n const updates: Record<string, unknown> = {};\n let paramIndex = 0;\n\n for (const clause of setClauses) {\n const eqMatch = clause.match(/`?(\\w+)`?\\s*=\\s*\\?/);\n if (eqMatch?.[1]) {\n updates[eqMatch[1]] = params[paramIndex];\n paramIndex++;\n }\n }\n\n const whereMatch = sql.match(/WHERE\\s+(.+)$/i);\n const whereConditions = whereMatch?.[1] || \"\";\n\n let affectedRows = 0;\n for (const row of tableData.rows) {\n if (this.matchesWhere(row, whereConditions, params, paramIndex)) {\n Object.assign(row, updates);\n affectedRows++;\n }\n }\n\n if (affectedRows > 0) {\n this.flush(tableName);\n }\n\n return { affectedRows };\n }\n\n private handleDelete(sql: string, params: unknown[]): any {\n const tableMatch = sql.match(/FROM\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return { affectedRows: 0 };\n }\n\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return { affectedRows: 0 };\n }\n\n const whereMatch = sql.match(/WHERE\\s+(.+)$/i);\n if (!whereMatch?.[1]) {\n const affectedRows = tableData.rows.length;\n tableData.rows = [];\n if (affectedRows > 0) {\n this.flush(tableName);\n }\n return { affectedRows };\n }\n\n const whereConditions = whereMatch[1];\n const before = tableData.rows.length;\n\n tableData.rows = tableData.rows.filter(\n (row) => !this.matchesWhere(row, whereConditions, params, 0),\n );\n\n const affectedRows = before - tableData.rows.length;\n if (affectedRows > 0) {\n this.flush(tableName);\n }\n\n return { affectedRows };\n }\n\n private handleSelect(sql: string, params: unknown[]): any {\n const countMatch = sql.match(/SELECT\\s+COUNT\\s*\\(\\s*\\*\\s*\\)\\s+as\\s+(\\w+)/i);\n if (countMatch) {\n const tableMatch = sql.match(/FROM\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return [];\n }\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return [{ count: 0 }];\n }\n\n const whereMatch = sql.match(/WHERE\\s+(.+?)(?:\\s+ORDER|\\s+LIMIT|\\s*$)/i);\n if (whereMatch?.[1]) {\n const filter = (row: Record<string, unknown>) =>\n this.matchesWhere(row, whereMatch[1]!, params, 0);\n const count = tableData.rows.filter(filter).length;\n return [{ count }];\n }\n\n return [{ count: tableData.rows.length }];\n }\n\n const tableMatch = sql.match(/FROM\\s+`?(\\w+)`?/i);\n if (!tableMatch?.[1]) {\n return [];\n }\n\n const tableName = tableMatch[1];\n const tableData = this.data.get(tableName);\n if (!tableData) {\n return [];\n }\n\n let rows = [...tableData.rows];\n\n const whereMatch = sql.match(/WHERE\\s+(.+?)(?:\\s+ORDER\\s+BY|\\s+LIMIT|\\s*$)/i);\n if (whereMatch?.[1]) {\n rows = rows.filter((row) => this.matchesWhere(row, whereMatch[1]!, params, 0));\n }\n\n const orderMatch = sql.match(/ORDER\\s+BY\\s+(.+?)(?:\\s+LIMIT|\\s*$)/i);\n if (orderMatch?.[1]) {\n const orders = orderMatch[1].split(\",\");\n rows.sort((a, b) => {\n for (const orderClause of orders) {\n const [col, dir] = orderClause.trim().split(/\\s+/);\n const colName = col?.replace(/`/g, \"\") ?? \"\";\n const aVal = (a as any)[colName];\n const bVal = (b as any)[colName];\n if (aVal < bVal) {\n return dir?.toUpperCase() === \"DESC\" ? 1 : -1;\n }\n if (aVal > bVal) {\n return dir?.toUpperCase() === \"DESC\" ? -1 : 1;\n }\n }\n return 0;\n });\n }\n\n const limitMatch = sql.match(/LIMIT\\s+(\\d+)(?:\\s+OFFSET\\s+(\\d+)|\\s*,\\s*(\\d+))?/i);\n if (limitMatch) {\n const limit = Number.parseInt(limitMatch[1]!, 10);\n const offset = limitMatch[2]\n ? Number.parseInt(limitMatch[2], 10)\n : limitMatch[3]\n ? Number.parseInt(limitMatch[3], 10)\n : 0;\n rows = rows.slice(offset, offset + limit);\n }\n\n return rows.map((r) => ({ ...r }));\n }\n\n private matchesWhere(\n row: Record<string, unknown>,\n whereStr: string,\n params: unknown[],\n paramOffset: number,\n ): boolean {\n return this.evaluateWhereExpression(row, whereStr, params, paramOffset).matched;\n }\n\n private evaluateWhereExpression(\n row: Record<string, unknown>,\n expr: string,\n params: unknown[],\n startParamIdx: number,\n ): { matched: boolean; paramIdx: number } {\n let paramIdx = startParamIdx;\n expr = expr.trim();\n\n if (expr.startsWith(\"(\") && expr.endsWith(\")\")) {\n const result = this.evaluateWhereExpression(row, expr.slice(1, -1), params, paramIdx);\n return result;\n }\n const orClauses = this.splitByTopLevel(expr, \"OR\");\n if (orClauses.length > 1) {\n for (const orClause of orClauses) {\n const result = this.evaluateWhereExpression(row, orClause.trim(), params, paramIdx);\n paramIdx = result.paramIdx;\n if (result.matched) {\n return { matched: true, paramIdx };\n }\n }\n return { matched: false, paramIdx };\n }\n\n const andClauses = this.splitByTopLevel(expr, \"AND\");\n if (andClauses.length > 1) {\n for (const andClause of andClauses) {\n const result = this.evaluateWhereExpression(row, andClause.trim(), params, paramIdx);\n paramIdx = result.paramIdx;\n if (!result.matched) {\n return { matched: false, paramIdx };\n }\n }\n return { matched: true, paramIdx };\n }\n\n return this.evaluateSingleCondition(row, expr, params, paramIdx);\n }\n\n private splitByTopLevel(expr: string, operator: string): string[] {\n const parts: string[] = [];\n let current = \"\";\n let parenDepth = 0;\n\n const opRegex = new RegExp(`\\\\s+${operator}\\\\s+`, \"i\");\n let i = 0;\n\n while (i < expr.length) {\n const char = expr[i];\n\n if (char === \"(\") {\n parenDepth++;\n current += char;\n i++;\n } else if (char === \")\") {\n parenDepth--;\n current += char;\n i++;\n } else if (parenDepth === 0) {\n const remaining = expr.slice(i);\n const match = remaining.match(opRegex);\n\n if (match && match.index === 0) {\n if (current.trim()) {\n parts.push(current.trim());\n }\n i += match[0]!.length;\n current = \"\";\n } else {\n current += char;\n i++;\n }\n } else {\n current += char;\n i++;\n }\n }\n\n if (current.trim()) {\n parts.push(current.trim());\n }\n\n return parts.length > 0 ? parts : [expr];\n }\n\n private evaluateSingleCondition(\n row: Record<string, unknown>,\n condition: string,\n params: unknown[],\n paramIdx: number,\n ): { matched: boolean; paramIdx: number } {\n condition = condition.trim();\n\n const notInMatch = condition.match(/`?(\\w+)`?\\s+NOT\\s+IN\\s+\\(([^)]*)\\)/i);\n if (notInMatch) {\n const col = notInMatch[1]!;\n const placeholders = notInMatch[2]!.split(\",\").map((p) => p.trim());\n const values: unknown[] = [];\n for (const placeholder of placeholders) {\n if (placeholder === \"?\") {\n values.push(params[paramIdx]);\n paramIdx++;\n }\n }\n const matched = !values.includes(row[col]);\n return { matched, paramIdx };\n }\n\n const inMatch = condition.match(/`?(\\w+)`?\\s+IN\\s+\\(([^)]*)\\)/i);\n if (inMatch) {\n const col = inMatch[1]!;\n const placeholders = inMatch[2]!.split(\",\").map((p) => p.trim());\n const values: unknown[] = [];\n for (const placeholder of placeholders) {\n if (placeholder === \"?\") {\n values.push(params[paramIdx]);\n paramIdx++;\n }\n }\n const matched = values.includes(row[col]);\n return { matched, paramIdx };\n }\n\n const eqMatch = condition.match(/`?(\\w+)`?\\s*=\\s*\\?/);\n if (eqMatch) {\n const matched = row[eqMatch[1]!] === params[paramIdx];\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const neqMatch = condition.match(/`?(\\w+)`?\\s*!=\\s*\\?/);\n if (neqMatch) {\n const matched = row[neqMatch[1]!] !== params[paramIdx];\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const gtMatch = condition.match(/`?(\\w+)`?\\s*>\\s*\\?/);\n if (gtMatch) {\n const matched = (row[gtMatch[1]!] as any) > (params[paramIdx] as any);\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const gteMatch = condition.match(/`?(\\w+)`?\\s*>=\\s*\\?/);\n if (gteMatch) {\n const matched = (row[gteMatch[1]!] as any) >= (params[paramIdx] as any);\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const ltMatch = condition.match(/`?(\\w+)`?\\s*<\\s*\\?/);\n if (ltMatch) {\n const matched = (row[ltMatch[1]!] as any) < (params[paramIdx] as any);\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const lteMatch = condition.match(/`?(\\w+)`?\\s*<=\\s*\\?/);\n if (lteMatch) {\n const matched = (row[lteMatch[1]!] as any) <= (params[paramIdx] as any);\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const notLikeMatch = condition.match(/`?(\\w+)`?\\s+NOT\\s+LIKE\\s+\\?/i);\n if (notLikeMatch) {\n const pattern = (params[paramIdx] as string).replace(/%/g, \".*\");\n const matched = !new RegExp(`^${pattern}$`).test(String(row[notLikeMatch[1]!]));\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n const likeMatch = condition.match(/`?(\\w+)`?\\s+LIKE\\s+\\?/i);\n if (likeMatch) {\n const pattern = (params[paramIdx] as string).replace(/%/g, \".*\");\n const matched = new RegExp(`^${pattern}$`).test(String(row[likeMatch[1]!]));\n return { matched, paramIdx: paramIdx + 1 };\n }\n\n return { matched: true, paramIdx };\n }\n}\n"],"mappings":"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"}