@fragno-dev/db 0.1.13 → 0.1.14

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.
Files changed (75) hide show
  1. package/.turbo/turbo-build.log +48 -41
  2. package/CHANGELOG.md +6 -0
  3. package/dist/adapters/adapters.d.ts +13 -1
  4. package/dist/adapters/adapters.d.ts.map +1 -1
  5. package/dist/adapters/adapters.js.map +1 -1
  6. package/dist/adapters/drizzle/drizzle-adapter.d.ts +2 -0
  7. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  8. package/dist/adapters/drizzle/drizzle-adapter.js +6 -1
  9. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  10. package/dist/adapters/drizzle/drizzle-query.js +6 -4
  11. package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
  12. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -1
  13. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +1 -1
  14. package/dist/adapters/drizzle/drizzle-uow-compiler.js +49 -36
  15. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
  16. package/dist/adapters/drizzle/drizzle-uow-decoder.js +1 -1
  17. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
  18. package/dist/adapters/drizzle/shared.d.ts +14 -1
  19. package/dist/adapters/drizzle/shared.d.ts.map +1 -0
  20. package/dist/adapters/kysely/kysely-adapter.d.ts +2 -0
  21. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  22. package/dist/adapters/kysely/kysely-adapter.js +7 -2
  23. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  24. package/dist/adapters/kysely/kysely-query.js +5 -3
  25. package/dist/adapters/kysely/kysely-query.js.map +1 -1
  26. package/dist/adapters/kysely/kysely-shared.d.ts +11 -0
  27. package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -0
  28. package/dist/adapters/kysely/kysely-uow-compiler.js +38 -9
  29. package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
  30. package/dist/bind-services.d.ts +7 -0
  31. package/dist/bind-services.d.ts.map +1 -0
  32. package/dist/bind-services.js +14 -0
  33. package/dist/bind-services.js.map +1 -0
  34. package/dist/fragment.d.ts +131 -12
  35. package/dist/fragment.d.ts.map +1 -1
  36. package/dist/fragment.js +107 -8
  37. package/dist/fragment.js.map +1 -1
  38. package/dist/mod.d.ts +4 -2
  39. package/dist/mod.d.ts.map +1 -1
  40. package/dist/mod.js +3 -2
  41. package/dist/mod.js.map +1 -1
  42. package/dist/query/query.d.ts +2 -2
  43. package/dist/query/query.d.ts.map +1 -1
  44. package/dist/query/unit-of-work.d.ts +100 -15
  45. package/dist/query/unit-of-work.d.ts.map +1 -1
  46. package/dist/query/unit-of-work.js +214 -7
  47. package/dist/query/unit-of-work.js.map +1 -1
  48. package/package.json +3 -3
  49. package/src/adapters/adapters.ts +14 -0
  50. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +6 -1
  51. package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +133 -5
  52. package/src/adapters/drizzle/drizzle-adapter.ts +16 -1
  53. package/src/adapters/drizzle/drizzle-query.ts +26 -15
  54. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +57 -57
  55. package/src/adapters/drizzle/drizzle-uow-compiler.ts +79 -39
  56. package/src/adapters/drizzle/drizzle-uow-decoder.ts +2 -5
  57. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +2 -2
  58. package/src/adapters/kysely/kysely-adapter.ts +16 -1
  59. package/src/adapters/kysely/kysely-query.ts +26 -15
  60. package/src/adapters/kysely/kysely-uow-compiler.test.ts +43 -43
  61. package/src/adapters/kysely/kysely-uow-compiler.ts +50 -14
  62. package/src/adapters/kysely/kysely-uow-joins.test.ts +30 -30
  63. package/src/bind-services.test.ts +214 -0
  64. package/src/bind-services.ts +37 -0
  65. package/src/db-fragment.test.ts +800 -0
  66. package/src/fragment.ts +557 -28
  67. package/src/mod.ts +19 -0
  68. package/src/query/query.ts +2 -2
  69. package/src/query/unit-of-work-multi-schema.test.ts +64 -0
  70. package/src/query/unit-of-work-types.test.ts +13 -0
  71. package/src/query/unit-of-work.test.ts +5 -9
  72. package/src/query/unit-of-work.ts +511 -62
  73. package/src/uow-context-integration.test.ts +102 -0
  74. package/src/uow-context.test.ts +182 -0
  75. package/src/fragment.test.ts +0 -341
@@ -2,7 +2,7 @@ import { Column } from "../../schema/create.js";
2
2
  import { serialize } from "../../schema/serialize.js";
3
3
  import { decodeCursor, serializeCursorValues } from "../../query/cursor.js";
4
4
  import { buildCondition } from "../../query/condition-builder.js";
5
- import { parseDrizzle } from "./shared.js";
5
+ import { createTableNameMapper, parseDrizzle } from "./shared.js";
6
6
  import { ReferenceSubquery, encodeValues } from "../../query/result-transform.js";
7
7
  import { getOrderedJoinColumns } from "./join-column-utils.js";
8
8
  import * as Drizzle from "drizzle-orm";
@@ -14,20 +14,28 @@ import * as Drizzle from "drizzle-orm";
14
14
  * This compiler translates UOW operations into Drizzle query functions
15
15
  * that can be executed as a batch/transaction.
16
16
  *
17
- * @param schema - The database schema
18
17
  * @param pool - Connection pool for acquiring database connections
19
18
  * @param provider - SQL provider (sqlite, mysql, postgresql)
20
- * @param mapper - Optional table name mapper for namespace prefixing
19
+ * @param mapper - Optional table name mapper for namespace prefixing (fallback for operations without explicit namespace)
21
20
  * @returns A UOWCompiler instance for Drizzle
22
21
  */
23
- function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
22
+ function createDrizzleUOWCompiler(pool, provider, mapper) {
24
23
  const [db, drizzleTables] = parseDrizzle(pool.getDatabaseSync());
25
24
  /**
25
+ * Get the mapper for a specific operation
26
+ * Uses operation's namespace if provided, otherwise falls back to the default mapper
27
+ */
28
+ function getMapperForOperation(namespace) {
29
+ if (namespace) return createTableNameMapper(namespace);
30
+ return mapper;
31
+ }
32
+ /**
26
33
  * Convert a Fragno table to a Drizzle table
27
34
  * @throws Error if table is not found in Drizzle schema
28
35
  */
29
- function toDrizzleTable(table) {
30
- const physicalTableName = mapper ? mapper.toPhysical(table.ormName) : table.ormName;
36
+ function toDrizzleTable(table, namespace) {
37
+ const opMapper = getMapperForOperation(namespace);
38
+ const physicalTableName = opMapper ? opMapper.toPhysical(table.ormName) : table.ormName;
31
39
  const out = drizzleTables[physicalTableName];
32
40
  if (out) return out;
33
41
  throw new Error(`[Drizzle] Unknown table name ${physicalTableName} (logical: ${table.ormName}), is it included in your Drizzle schema?`);
@@ -36,22 +44,22 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
36
44
  * Convert a Fragno column to a Drizzle column
37
45
  * @throws Error if column is not found in Drizzle table
38
46
  */
39
- function toDrizzleColumn(col) {
47
+ function toDrizzleColumn(schema, namespace, col) {
40
48
  const fragnoTable = schema.tables[col.tableName];
41
49
  if (!fragnoTable) throw new Error(`[Drizzle] Unknown table ${col.tableName} for column ${col.ormName}.`);
42
- const out = toDrizzleTable(fragnoTable)[col.ormName];
50
+ const out = toDrizzleTable(fragnoTable, namespace)[col.ormName];
43
51
  if (out) return out;
44
52
  throw new Error(`[Drizzle] Unknown column name ${col.ormName} in ${fragnoTable.ormName}.`);
45
53
  }
46
54
  /**
47
55
  * Build a WHERE clause from a condition using Drizzle's query builder
48
56
  */
49
- function buildWhere(condition) {
57
+ function buildWhere(schema, namespace, condition) {
50
58
  if (condition.type === "compare") {
51
- const left = toDrizzleColumn(condition.a);
59
+ const left = toDrizzleColumn(schema, namespace, condition.a);
52
60
  const op = condition.operator;
53
61
  let right = condition.b;
54
- if (right instanceof Column) right = toDrizzleColumn(right);
62
+ if (right instanceof Column) right = toDrizzleColumn(schema, namespace, right);
55
63
  else if (condition.a.role === "reference" && typeof right === "string") {
56
64
  const table = Object.values(schema.tables).find((t) => Object.values(t.columns).includes(condition.a));
57
65
  if (table) {
@@ -97,13 +105,13 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
97
105
  default: throw new Error(`Unsupported operator: ${op}`);
98
106
  }
99
107
  }
100
- if (condition.type === "and") return Drizzle.and(...condition.items.map((item) => buildWhere(item)));
108
+ if (condition.type === "and") return Drizzle.and(...condition.items.map((item) => buildWhere(schema, namespace, item)));
101
109
  if (condition.type === "not") {
102
- const result = buildWhere(condition.item);
110
+ const result = buildWhere(schema, namespace, condition.item);
103
111
  if (!result) return;
104
112
  return Drizzle.not(result);
105
113
  }
106
- return Drizzle.or(...condition.items.map((item) => buildWhere(item)));
114
+ return Drizzle.or(...condition.items.map((item) => buildWhere(schema, namespace, item)));
107
115
  }
108
116
  /**
109
117
  * Process reference subqueries in encoded values, converting them to Drizzle SQL subqueries
@@ -124,7 +132,7 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
124
132
  * Get table from schema by name
125
133
  * @throws Error if table is not found in schema
126
134
  */
127
- function getTable(name) {
135
+ function getTable(schema, name) {
128
136
  const table = schema.tables[name];
129
137
  if (!table) throw new Error(`Invalid table name ${name}.`);
130
138
  return table;
@@ -142,7 +150,7 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
142
150
  /**
143
151
  * Process joins recursively to support nested joins with orderBy and limit
144
152
  */
145
- function processJoins(joins) {
153
+ function processJoins(schema, namespace, joins) {
146
154
  const result = {};
147
155
  for (const join of joins) {
148
156
  const { options, relation } = join;
@@ -154,33 +162,34 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
154
162
  for (const colName of orderedColumns) joinColumns[colName] = true;
155
163
  let joinOrderBy;
156
164
  if (options.orderBy && options.orderBy.length > 0) joinOrderBy = options.orderBy.map(([col, direction]) => {
157
- const drizzleCol = toDrizzleColumn(col);
165
+ const drizzleCol = toDrizzleColumn(schema, namespace, col);
158
166
  return direction === "asc" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);
159
167
  });
160
168
  let joinWhere;
161
- if (options.where) joinWhere = buildWhere(options.where);
169
+ if (options.where) joinWhere = buildWhere(schema, namespace, options.where);
162
170
  const joinConfig = {
163
171
  columns: joinColumns,
164
172
  orderBy: joinOrderBy,
165
173
  limit: options.limit,
166
174
  where: joinWhere
167
175
  };
168
- if (options.join && options.join.length > 0) joinConfig.with = processJoins(options.join);
176
+ if (options.join && options.join.length > 0) joinConfig.with = processJoins(schema, namespace, options.join);
169
177
  result[joinName] = joinConfig;
170
178
  }
171
179
  return result;
172
180
  }
173
181
  return {
174
182
  compileRetrievalOperation(op) {
183
+ const schema = op.schema;
175
184
  switch (op.type) {
176
185
  case "count": {
177
186
  let whereClause;
178
187
  if (op.options.where) {
179
188
  const condition = buildCondition(op.table.columns, op.options.where);
180
189
  if (condition === false) return null;
181
- if (condition !== true) whereClause = buildWhere(condition);
190
+ if (condition !== true) whereClause = buildWhere(schema, op.namespace, condition);
182
191
  }
183
- const drizzleTable = toDrizzleTable(op.table);
192
+ const drizzleTable = toDrizzleTable(op.table, op.namespace);
184
193
  const query = db.select({ count: Drizzle.count() }).from(drizzleTable);
185
194
  return whereClause ? query.where(whereClause).toSQL() : query.toSQL();
186
195
  }
@@ -197,7 +206,7 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
197
206
  }
198
207
  let orderBy;
199
208
  if (indexColumns.length > 0) orderBy = indexColumns.map((col) => {
200
- const drizzleCol = toDrizzleColumn(col);
209
+ const drizzleCol = toDrizzleColumn(schema, op.namespace, col);
201
210
  return orderDirection === "asc" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);
202
211
  });
203
212
  const columns = {};
@@ -212,7 +221,7 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
212
221
  const condition = buildCondition(op.table.columns, findOptions.where);
213
222
  if (condition === false) return null;
214
223
  if (condition !== true) {
215
- const clause = buildWhere(condition);
224
+ const clause = buildWhere(schema, op.namespace, condition);
216
225
  if (clause) whereClauses.push(clause);
217
226
  }
218
227
  }
@@ -222,11 +231,11 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
222
231
  const isAfter = !!after;
223
232
  const useGreaterThan = isAfter && orderDirection === "asc" || !isAfter && orderDirection === "desc";
224
233
  if (indexColumns.length === 1) {
225
- const col = toDrizzleColumn(indexColumns[0]);
234
+ const col = toDrizzleColumn(schema, op.namespace, indexColumns[0]);
226
235
  const val = serializedValues[indexColumns[0].ormName];
227
236
  whereClauses.push(useGreaterThan ? Drizzle.gt(col, val) : Drizzle.lt(col, val));
228
237
  } else {
229
- const drizzleCols = indexColumns.map((c) => toDrizzleColumn(c));
238
+ const drizzleCols = indexColumns.map((c) => toDrizzleColumn(schema, op.namespace, c));
230
239
  const vals = indexColumns.map((c) => serializedValues[c.ormName]);
231
240
  const operator = useGreaterThan ? ">" : "<";
232
241
  const colsSQL = Drizzle.sql.join(drizzleCols, Drizzle.sql.raw(", "));
@@ -241,17 +250,21 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
241
250
  orderBy,
242
251
  with: {}
243
252
  };
244
- if (joins) queryConfig.with = processJoins(joins);
245
- const physicalTableName = mapper ? mapper.toPhysical(op.table.ormName) : op.table.ormName;
246
- return db.query[physicalTableName].findMany(queryConfig).toSQL();
253
+ if (joins) queryConfig.with = processJoins(schema, op.namespace, joins);
254
+ const opMapper = getMapperForOperation(op.namespace);
255
+ const physicalTableName = opMapper ? opMapper.toPhysical(op.table.ormName) : op.table.ormName;
256
+ const tableQuery = db.query[physicalTableName];
257
+ if (!tableQuery) throw new Error(`[Drizzle] Table ${op.table.ormName} (physical: ${physicalTableName}) not found in db.query. Available tables: ${Object.keys(db.query).join(", ")}`);
258
+ return tableQuery.findMany(queryConfig).toSQL();
247
259
  }
248
260
  }
249
261
  },
250
262
  compileMutationOperation(op) {
263
+ const schema = op.schema;
251
264
  switch (op.type) {
252
265
  case "create": {
253
- const table = getTable(op.table);
254
- const drizzleTable = toDrizzleTable(table);
266
+ const table = getTable(schema, op.table);
267
+ const drizzleTable = toDrizzleTable(table, op.namespace);
255
268
  const values = processReferenceSubqueries(encodeValues(op.values, table, true, provider));
256
269
  return {
257
270
  query: db.insert(drizzleTable).values(values).toSQL(),
@@ -259,15 +272,15 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
259
272
  };
260
273
  }
261
274
  case "update": {
262
- const table = getTable(op.table);
275
+ const table = getTable(schema, op.table);
263
276
  const idColumn = table.getIdColumn();
264
277
  const versionColumn = table.getVersionColumn();
265
- const drizzleTable = toDrizzleTable(table);
278
+ const drizzleTable = toDrizzleTable(table, op.namespace);
266
279
  const externalId = typeof op.id === "string" ? op.id : op.id.externalId;
267
280
  const versionToCheck = getVersionToCheck(op.id, op.checkVersion);
268
281
  const condition = versionToCheck !== void 0 ? buildCondition(table.columns, (eb) => eb.and(eb(idColumn.ormName, "=", externalId), eb(versionColumn.ormName, "=", versionToCheck))) : buildCondition(table.columns, (eb) => eb(idColumn.ormName, "=", externalId));
269
282
  if (condition === false) return null;
270
- const whereClause = condition === true ? void 0 : buildWhere(condition);
283
+ const whereClause = condition === true ? void 0 : buildWhere(schema, op.namespace, condition);
271
284
  const setValues = processReferenceSubqueries(encodeValues(op.set, table, false, provider));
272
285
  setValues[versionColumn.ormName] = Drizzle.sql.raw(`COALESCE(${versionColumn.ormName}, 0) + 1`);
273
286
  return {
@@ -276,17 +289,17 @@ function createDrizzleUOWCompiler(schema, pool, provider, mapper) {
276
289
  };
277
290
  }
278
291
  case "delete": {
279
- const table = getTable(op.table);
292
+ const table = getTable(schema, op.table);
280
293
  const idColumn = table.getIdColumn();
281
294
  const versionColumn = table.getVersionColumn();
282
- const drizzleTable = toDrizzleTable(table);
295
+ const drizzleTable = toDrizzleTable(table, op.namespace);
283
296
  if (!op.id) throw new Error(`[Drizzle] Delete operation on table "${op.table}" has undefined id. Make sure you're passing a valid FragnoId or string ID.`);
284
297
  const externalId = typeof op.id === "string" ? op.id : op.id.externalId;
285
298
  if (!externalId) throw new Error(`[Drizzle] Delete operation on table "${op.table}" has invalid id. The FragnoId object exists but has no externalId. Received: ${JSON.stringify(op.id)}. Make sure the record was properly loaded from the database.`);
286
299
  const versionToCheck = getVersionToCheck(op.id, op.checkVersion);
287
300
  const condition = versionToCheck !== void 0 ? buildCondition(table.columns, (eb) => eb.and(eb(idColumn.ormName, "=", externalId), eb(versionColumn.ormName, "=", versionToCheck))) : buildCondition(table.columns, (eb) => eb(idColumn.ormName, "=", externalId));
288
301
  if (condition === false) return null;
289
- const whereClause = condition === true ? void 0 : buildWhere(condition);
302
+ const whereClause = condition === true ? void 0 : buildWhere(schema, op.namespace, condition);
290
303
  return {
291
304
  query: db.delete(drizzleTable).where(whereClause).toSQL(),
292
305
  expectedAffectedRows: op.checkVersion ? 1 : null
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle-uow-compiler.js","names":["processed: Record<string, unknown>","result: Record<string, Drizzle.DBQueryConfig<\"many\", boolean>>","joinColumns: Record<string, boolean>","joinOrderBy: Drizzle.SQL[] | undefined","joinWhere: Drizzle.SQL | undefined","joinConfig: Drizzle.DBQueryConfig<\"many\", boolean>","whereClause: Drizzle.SQL | undefined","indexColumns: AnyColumn[]","orderDirection: \"asc\" | \"desc\"","orderBy: Drizzle.SQL[] | undefined","columns: Record<string, boolean>","whereClauses: Drizzle.SQL[]","queryConfig: Drizzle.DBQueryConfig<\"many\", boolean>"],"sources":["../../../src/adapters/drizzle/drizzle-uow-compiler.ts"],"sourcesContent":["import * as Drizzle from \"drizzle-orm\";\nimport type { AnyColumn, AnySchema, AnyTable, FragnoId } from \"../../schema/create\";\nimport { Column } from \"../../schema/create\";\nimport type {\n CompiledMutation,\n MutationOperation,\n RetrievalOperation,\n UOWCompiler,\n} from \"../../query/unit-of-work\";\nimport { buildCondition, type Condition } from \"../../query/condition-builder\";\nimport {\n type ColumnType,\n type TableType,\n type TableNameMapper,\n parseDrizzle,\n type DBType,\n} from \"./shared\";\nimport { encodeValues, ReferenceSubquery } from \"../../query/result-transform\";\nimport { serialize } from \"../../schema/serialize\";\nimport { decodeCursor, serializeCursorValues } from \"../../query/cursor\";\nimport type { CompiledJoin } from \"../../query/orm/orm\";\nimport { getOrderedJoinColumns } from \"./join-column-utils\";\nimport type { ConnectionPool } from \"../../shared/connection-pool\";\n\nexport type DrizzleCompiledQuery = {\n sql: string;\n params: unknown[];\n};\n\n/**\n * Create a Drizzle-specific Unit of Work compiler\n *\n * This compiler translates UOW operations into Drizzle query functions\n * that can be executed as a batch/transaction.\n *\n * @param schema - The database schema\n * @param pool - Connection pool for acquiring database connections\n * @param provider - SQL provider (sqlite, mysql, postgresql)\n * @param mapper - Optional table name mapper for namespace prefixing\n * @returns A UOWCompiler instance for Drizzle\n */\nexport function createDrizzleUOWCompiler<TSchema extends AnySchema>(\n schema: TSchema,\n pool: ConnectionPool<DBType>,\n provider: \"sqlite\" | \"mysql\" | \"postgresql\",\n mapper?: TableNameMapper,\n): UOWCompiler<TSchema, DrizzleCompiledQuery> {\n // Get db synchronously for compilation (doesn't execute, just builds SQL)\n // TODO: We don't even need a Drizzle instance with a db client attached here. `drizzle({ schema })` is enough.\n const dbRaw = pool.getDatabaseSync();\n const [db, drizzleTables] = parseDrizzle(dbRaw);\n\n /**\n * Convert a Fragno table to a Drizzle table\n * @throws Error if table is not found in Drizzle schema\n */\n function toDrizzleTable(table: AnyTable): TableType {\n // Map logical table name to physical table name using the mapper\n const physicalTableName = mapper ? mapper.toPhysical(table.ormName) : table.ormName;\n const out = drizzleTables[physicalTableName];\n if (out) {\n return out;\n }\n\n throw new Error(\n `[Drizzle] Unknown table name ${physicalTableName} (logical: ${table.ormName}), is it included in your Drizzle schema?`,\n );\n }\n\n /**\n * Convert a Fragno column to a Drizzle column\n * @throws Error if column is not found in Drizzle table\n */\n function toDrizzleColumn(col: AnyColumn): ColumnType {\n const fragnoTable = schema.tables[col.tableName];\n if (!fragnoTable) {\n throw new Error(`[Drizzle] Unknown table ${col.tableName} for column ${col.ormName}.`);\n }\n\n const table = toDrizzleTable(fragnoTable);\n const out = table[col.ormName];\n if (out) {\n return out;\n }\n\n throw new Error(`[Drizzle] Unknown column name ${col.ormName} in ${fragnoTable.ormName}.`);\n }\n\n /**\n * Build a WHERE clause from a condition using Drizzle's query builder\n */\n function buildWhere(condition: Condition): Drizzle.SQL | undefined {\n if (condition.type === \"compare\") {\n const left = toDrizzleColumn(condition.a);\n const op = condition.operator;\n let right = condition.b;\n if (right instanceof Column) {\n right = toDrizzleColumn(right);\n } else {\n // Handle string references - convert external ID to internal ID via subquery\n if (condition.a.role === \"reference\" && typeof right === \"string\") {\n // Find the table that contains this column\n const table = Object.values(schema.tables).find((t) =>\n Object.values(t.columns).includes(condition.a),\n );\n if (table) {\n // Find relation that uses this column\n const relation = Object.values(table.relations).find((rel) =>\n rel.on.some(([localCol]) => localCol === condition.a.ormName),\n );\n if (relation) {\n const refTable = relation.table;\n const internalIdCol = refTable.getInternalIdColumn();\n const idCol = refTable.getIdColumn();\n const physicalTableName = mapper\n ? mapper.toPhysical(refTable.ormName)\n : refTable.ormName;\n\n // Build a SQL subquery using Drizzle's sql template\n right = Drizzle.sql`(select ${Drizzle.sql.identifier(internalIdCol.name)} from ${Drizzle.sql.identifier(physicalTableName)} where ${Drizzle.sql.identifier(idCol.name)} = ${right} limit 1)`;\n }\n }\n } else {\n // Serialize non-Column values (e.g., FragnoId -> string, Date -> number for SQLite)\n right = serialize(right, condition.a, provider);\n }\n }\n\n switch (op) {\n case \"=\":\n return Drizzle.eq(left, right);\n case \"!=\":\n return Drizzle.ne(left, right);\n case \">\":\n return Drizzle.gt(left, right);\n case \">=\":\n return Drizzle.gte(left, right);\n case \"<\":\n return Drizzle.lt(left, right);\n case \"<=\":\n return Drizzle.lte(left, right);\n case \"in\": {\n return Drizzle.inArray(left, right as never[]);\n }\n case \"not in\":\n return Drizzle.notInArray(left, right as never[]);\n case \"is\":\n return right === null ? Drizzle.isNull(left) : Drizzle.eq(left, right);\n case \"is not\":\n return right === null ? Drizzle.isNotNull(left) : Drizzle.ne(left, right);\n case \"contains\": {\n right =\n typeof right === \"string\" ? `%${right}%` : Drizzle.sql`concat('%', ${right}, '%')`;\n return Drizzle.like(left, right as string);\n }\n case \"not contains\": {\n right =\n typeof right === \"string\" ? `%${right}%` : Drizzle.sql`concat('%', ${right}, '%')`;\n return Drizzle.notLike(left, right as string);\n }\n case \"ends with\": {\n right = typeof right === \"string\" ? `%${right}` : Drizzle.sql`concat('%', ${right})`;\n return Drizzle.like(left, right as string);\n }\n case \"not ends with\": {\n right = typeof right === \"string\" ? `%${right}` : Drizzle.sql`concat('%', ${right})`;\n return Drizzle.notLike(left, right as string);\n }\n case \"starts with\": {\n right = typeof right === \"string\" ? `${right}%` : Drizzle.sql`concat(${right}, '%')`;\n return Drizzle.like(left, right as string);\n }\n case \"not starts with\": {\n right = typeof right === \"string\" ? `${right}%` : Drizzle.sql`concat(${right}, '%')`;\n return Drizzle.notLike(left, right as string);\n }\n\n default:\n throw new Error(`Unsupported operator: ${op}`);\n }\n }\n\n if (condition.type === \"and\") {\n return Drizzle.and(...condition.items.map((item) => buildWhere(item)));\n }\n\n if (condition.type === \"not\") {\n const result = buildWhere(condition.item);\n if (!result) {\n return;\n }\n\n return Drizzle.not(result);\n }\n\n return Drizzle.or(...condition.items.map((item) => buildWhere(item)));\n }\n\n /**\n * Process reference subqueries in encoded values, converting them to Drizzle SQL subqueries\n */\n function processReferenceSubqueries(values: Record<string, unknown>): Record<string, unknown> {\n const processed: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(values)) {\n if (value instanceof ReferenceSubquery) {\n const refTable = value.referencedTable;\n const externalId = value.externalIdValue;\n const internalIdCol = refTable.getInternalIdColumn();\n const idCol = refTable.getIdColumn();\n\n // Map logical table name to physical table name using the mapper\n const physicalTableName = mapper ? mapper.toPhysical(refTable.ormName) : refTable.ormName;\n\n // Build a SQL subquery using Drizzle's sql template\n // This creates a subquery: (SELECT _internalId FROM table WHERE id = ? LIMIT 1)\n // Safe cast: we're building a SQL subquery that returns a single bigint value\n processed[key] =\n Drizzle.sql`(select ${Drizzle.sql.identifier(internalIdCol.name)} from ${Drizzle.sql.identifier(physicalTableName)} where ${Drizzle.sql.identifier(idCol.name)} = ${externalId} limit 1)`;\n } else {\n processed[key] = value;\n }\n }\n\n return processed;\n }\n\n /**\n * Get table from schema by name\n * @throws Error if table is not found in schema\n */\n function getTable(name: unknown): AnyTable {\n const table = schema.tables[name as string];\n if (!table) {\n throw new Error(`Invalid table name ${name}.`);\n }\n return table;\n }\n\n /**\n * Get the version to check for a given ID and checkVersion flag.\n * @returns The version to check or undefined if no check is required.\n * @throws Error if the ID is a string and checkVersion is true.\n */\n function getVersionToCheck(id: FragnoId | string, checkVersion: boolean): number | undefined {\n if (!checkVersion) {\n return undefined;\n }\n\n if (typeof id === \"string\") {\n throw new Error(\n `Cannot use checkVersion with a string ID. Version checking requires a FragnoId with version information.`,\n );\n }\n\n return id.version;\n }\n\n /**\n * Process joins recursively to support nested joins with orderBy and limit\n */\n function processJoins(\n joins: CompiledJoin[],\n ): Record<string, Drizzle.DBQueryConfig<\"many\", boolean>> {\n const result: Record<string, Drizzle.DBQueryConfig<\"many\", boolean>> = {};\n\n for (const join of joins) {\n const { options, relation } = join;\n\n if (!options) {\n continue;\n }\n\n const targetTable = relation.table;\n const joinName = relation.name;\n\n // Build columns for this join using shared utility\n const selectOption = options.select === undefined ? true : options.select;\n const orderedColumns = getOrderedJoinColumns(targetTable, selectOption);\n const joinColumns: Record<string, boolean> = {};\n for (const colName of orderedColumns) {\n joinColumns[colName] = true;\n }\n\n // Build orderBy for this join\n let joinOrderBy: Drizzle.SQL[] | undefined;\n if (options.orderBy && options.orderBy.length > 0) {\n joinOrderBy = options.orderBy.map(([col, direction]) => {\n const drizzleCol = toDrizzleColumn(col);\n return direction === \"asc\" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);\n });\n }\n\n // Build WHERE clause for this join if provided\n let joinWhere: Drizzle.SQL | undefined;\n if (options.where) {\n joinWhere = buildWhere(options.where);\n }\n\n // Build the join config\n const joinConfig: Drizzle.DBQueryConfig<\"many\", boolean> = {\n columns: joinColumns,\n orderBy: joinOrderBy,\n limit: options.limit,\n where: joinWhere,\n };\n\n // Recursively process nested joins\n if (options.join && options.join.length > 0) {\n joinConfig.with = processJoins(options.join);\n }\n\n result[joinName] = joinConfig;\n }\n\n return result;\n }\n\n return {\n compileRetrievalOperation(op: RetrievalOperation<TSchema>): DrizzleCompiledQuery | null {\n switch (op.type) {\n case \"count\": {\n // Build WHERE clause\n let whereClause: Drizzle.SQL | undefined;\n if (op.options.where) {\n const condition = buildCondition(op.table.columns, op.options.where);\n if (condition === false) {\n // Never matches - return null\n return null;\n }\n if (condition !== true) {\n whereClause = buildWhere(condition);\n }\n }\n\n const drizzleTable = toDrizzleTable(op.table);\n const query = db.select({ count: Drizzle.count() }).from(drizzleTable);\n\n const compiledQuery = whereClause ? query.where(whereClause).toSQL() : query.toSQL();\n return compiledQuery;\n }\n\n case \"find\": {\n const {\n useIndex: _useIndex,\n orderByIndex,\n joins,\n after,\n before,\n pageSize,\n ...findOptions\n } = op.options;\n\n // Get index columns for ordering and cursor pagination\n let indexColumns: AnyColumn[] = [];\n let orderDirection: \"asc\" | \"desc\" = \"asc\";\n\n if (orderByIndex) {\n const index = op.table.indexes[orderByIndex.indexName];\n orderDirection = orderByIndex.direction;\n\n if (!index) {\n // If _primary index doesn't exist, fall back to ID column\n if (orderByIndex.indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n throw new Error(\n `Index \"${orderByIndex.indexName}\" not found on table \"${op.table.name}\"`,\n );\n }\n } else {\n indexColumns = index.columns;\n }\n }\n\n // Convert orderByIndex to orderBy format\n let orderBy: Drizzle.SQL[] | undefined;\n if (indexColumns.length > 0) {\n orderBy = indexColumns.map((col) => {\n const drizzleCol = toDrizzleColumn(col);\n return orderDirection === \"asc\" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);\n });\n }\n\n // Build query configuration\n const columns: Record<string, boolean> = {};\n const select = findOptions.select;\n\n if (select === true || select === undefined) {\n for (const col of Object.values(op.table.columns)) {\n columns[col.ormName] = true;\n }\n } else {\n for (const k of select) {\n columns[op.table.columns[k].ormName] = true;\n }\n // Always include hidden columns (for FragnoId construction with internal ID and version)\n for (const col of Object.values(op.table.columns)) {\n if (col.isHidden && !columns[col.ormName]) {\n columns[col.ormName] = true;\n }\n }\n }\n\n // Build WHERE clause with cursor conditions\n const whereClauses: Drizzle.SQL[] = [];\n\n // Add user-defined where clause\n if (findOptions.where) {\n const condition = buildCondition(op.table.columns, findOptions.where);\n if (condition === false) {\n // Never matches - return null to indicate this query should be skipped\n return null;\n }\n if (condition !== true) {\n const clause = buildWhere(condition);\n if (clause) {\n whereClauses.push(clause);\n }\n }\n }\n\n // Add cursor-based pagination conditions\n if ((after || before) && indexColumns.length > 0) {\n const cursor = after || before;\n // Decode cursor if it's a string, otherwise use it as-is\n const cursorObj = typeof cursor === \"string\" ? decodeCursor(cursor!) : cursor!;\n const serializedValues = serializeCursorValues(cursorObj, indexColumns, provider);\n\n // Build tuple comparison for cursor pagination\n // For \"after\" with \"asc\": (col1, col2, ...) > (val1, val2, ...)\n // For \"before\" with \"desc\": reverse the comparison\n const isAfter = !!after;\n const useGreaterThan =\n (isAfter && orderDirection === \"asc\") || (!isAfter && orderDirection === \"desc\");\n\n if (indexColumns.length === 1) {\n // Simple single-column case\n const col = toDrizzleColumn(indexColumns[0]!);\n const val = serializedValues[indexColumns[0]!.ormName];\n whereClauses.push(useGreaterThan ? Drizzle.gt(col, val) : Drizzle.lt(col, val));\n } else {\n // Multi-column tuple comparison using SQL\n const drizzleCols = indexColumns.map((c) => toDrizzleColumn(c));\n const vals = indexColumns.map((c) => serializedValues[c.ormName]);\n const operator = useGreaterThan ? \">\" : \"<\";\n // Safe cast: building a SQL comparison expression for cursor pagination\n // Build the tuple comparison: (col1, col2) > (val1, val2)\n const colsSQL = Drizzle.sql.join(drizzleCols, Drizzle.sql.raw(\", \"));\n const valsSQL = Drizzle.sql.join(\n vals.map((v) => Drizzle.sql`${v}`),\n Drizzle.sql.raw(\", \"),\n );\n whereClauses.push(\n Drizzle.sql`(${colsSQL}) ${Drizzle.sql.raw(operator)} (${valsSQL})`,\n );\n }\n }\n\n const whereClause = whereClauses.length > 0 ? Drizzle.and(...whereClauses) : undefined;\n\n const queryConfig: Drizzle.DBQueryConfig<\"many\", boolean> = {\n columns,\n limit: pageSize,\n where: whereClause,\n orderBy,\n with: {},\n };\n\n // Process joins recursively to support nested joins\n if (joins) {\n queryConfig.with = processJoins(joins);\n }\n\n const physicalTableName = mapper ? mapper.toPhysical(op.table.ormName) : op.table.ormName;\n const compiledQuery = db.query[physicalTableName].findMany(queryConfig).toSQL();\n return compiledQuery;\n }\n }\n },\n\n compileMutationOperation(\n op: MutationOperation<TSchema>,\n ): CompiledMutation<DrizzleCompiledQuery> | null {\n switch (op.type) {\n case \"create\": {\n const table = getTable(op.table);\n const drizzleTable = toDrizzleTable(table);\n // encodeValues now handles runtime defaults automatically\n const encodedValues = encodeValues(op.values, table, true, provider);\n const values = processReferenceSubqueries(encodedValues);\n\n const compiledQuery = db.insert(drizzleTable).values(values).toSQL();\n return {\n query: compiledQuery,\n expectedAffectedRows: null, // creates don't need affected row checks\n };\n }\n\n case \"update\": {\n const table = getTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n const drizzleTable = toDrizzleTable(table);\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const condition =\n versionToCheck !== undefined\n ? buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n // Handle boolean cases\n if (condition === false) {\n // Never matches - skip this operation\n return null;\n }\n\n const whereClause = condition === true ? undefined : buildWhere(condition);\n const encodedSetValues = encodeValues(op.set, table, false, provider);\n const setValues = processReferenceSubqueries(encodedSetValues);\n\n // Automatically increment _version for optimistic concurrency control\n // Safe cast: we're building a SQL expression for incrementing the version\n setValues[versionColumn.ormName] = Drizzle.sql.raw(\n `COALESCE(${versionColumn.ormName}, 0) + 1`,\n ) as unknown;\n\n const compiledQuery = db.update(drizzleTable).set(setValues).where(whereClause).toSQL();\n return {\n query: compiledQuery,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n };\n }\n\n case \"delete\": {\n const table = getTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n const drizzleTable = toDrizzleTable(table);\n\n if (!op.id) {\n throw new Error(\n `[Drizzle] Delete operation on table \"${op.table}\" has undefined id. ` +\n `Make sure you're passing a valid FragnoId or string ID.`,\n );\n }\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n\n if (!externalId) {\n throw new Error(\n `[Drizzle] Delete operation on table \"${op.table}\" has invalid id. ` +\n `The FragnoId object exists but has no externalId. ` +\n `Received: ${JSON.stringify(op.id)}. ` +\n `Make sure the record was properly loaded from the database.`,\n );\n }\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const condition =\n versionToCheck !== undefined\n ? buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n // Handle boolean cases\n if (condition === false) {\n // Never matches - skip this operation\n return null;\n }\n\n const whereClause = condition === true ? undefined : buildWhere(condition);\n\n const compiledQuery = db.delete(drizzleTable).where(whereClause).toSQL();\n return {\n query: compiledQuery,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n };\n }\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"drizzle-uow-compiler.js","names":["processed: Record<string, unknown>","result: Record<string, Drizzle.DBQueryConfig<\"many\", boolean>>","joinColumns: Record<string, boolean>","joinOrderBy: Drizzle.SQL[] | undefined","joinWhere: Drizzle.SQL | undefined","joinConfig: Drizzle.DBQueryConfig<\"many\", boolean>","whereClause: Drizzle.SQL | undefined","indexColumns: AnyColumn[]","orderDirection: \"asc\" | \"desc\"","orderBy: Drizzle.SQL[] | undefined","columns: Record<string, boolean>","whereClauses: Drizzle.SQL[]","queryConfig: Drizzle.DBQueryConfig<\"many\", boolean>"],"sources":["../../../src/adapters/drizzle/drizzle-uow-compiler.ts"],"sourcesContent":["import * as Drizzle from \"drizzle-orm\";\nimport type { AnyColumn, AnySchema, AnyTable, FragnoId } from \"../../schema/create\";\nimport { Column } from \"../../schema/create\";\nimport type {\n CompiledMutation,\n MutationOperation,\n RetrievalOperation,\n UOWCompiler,\n} from \"../../query/unit-of-work\";\nimport { buildCondition, type Condition } from \"../../query/condition-builder\";\nimport {\n type ColumnType,\n type TableType,\n type TableNameMapper,\n parseDrizzle,\n type DBType,\n createTableNameMapper,\n} from \"./shared\";\nimport { encodeValues, ReferenceSubquery } from \"../../query/result-transform\";\nimport { serialize } from \"../../schema/serialize\";\nimport { decodeCursor, serializeCursorValues } from \"../../query/cursor\";\nimport type { CompiledJoin } from \"../../query/orm/orm\";\nimport { getOrderedJoinColumns } from \"./join-column-utils\";\nimport type { ConnectionPool } from \"../../shared/connection-pool\";\n\nexport type DrizzleCompiledQuery = {\n sql: string;\n params: unknown[];\n};\n\n/**\n * Create a Drizzle-specific Unit of Work compiler\n *\n * This compiler translates UOW operations into Drizzle query functions\n * that can be executed as a batch/transaction.\n *\n * @param pool - Connection pool for acquiring database connections\n * @param provider - SQL provider (sqlite, mysql, postgresql)\n * @param mapper - Optional table name mapper for namespace prefixing (fallback for operations without explicit namespace)\n * @returns A UOWCompiler instance for Drizzle\n */\nexport function createDrizzleUOWCompiler(\n pool: ConnectionPool<DBType>,\n provider: \"sqlite\" | \"mysql\" | \"postgresql\",\n mapper?: TableNameMapper,\n): UOWCompiler<DrizzleCompiledQuery> {\n // Get db synchronously for compilation (doesn't execute, just builds SQL)\n // TODO: We don't even need a Drizzle instance with a db client attached here. `drizzle({ schema })` is enough.\n const dbRaw = pool.getDatabaseSync();\n const [db, drizzleTables] = parseDrizzle(dbRaw);\n\n /**\n * Get the mapper for a specific operation\n * Uses operation's namespace if provided, otherwise falls back to the default mapper\n */\n function getMapperForOperation(namespace: string | undefined): TableNameMapper | undefined {\n if (namespace) {\n return createTableNameMapper(namespace);\n }\n return mapper;\n }\n\n /**\n * Convert a Fragno table to a Drizzle table\n * @throws Error if table is not found in Drizzle schema\n */\n function toDrizzleTable(table: AnyTable, namespace: string | undefined): TableType {\n // Get the mapper for this operation's namespace\n const opMapper = getMapperForOperation(namespace);\n\n // Map logical table name to physical table name using the operation-specific mapper\n const physicalTableName = opMapper ? opMapper.toPhysical(table.ormName) : table.ormName;\n const out = drizzleTables[physicalTableName];\n if (out) {\n return out;\n }\n\n throw new Error(\n `[Drizzle] Unknown table name ${physicalTableName} (logical: ${table.ormName}), is it included in your Drizzle schema?`,\n );\n }\n\n /**\n * Convert a Fragno column to a Drizzle column\n * @throws Error if column is not found in Drizzle table\n */\n function toDrizzleColumn(\n schema: AnySchema,\n namespace: string | undefined,\n col: AnyColumn,\n ): ColumnType {\n const fragnoTable = schema.tables[col.tableName];\n if (!fragnoTable) {\n throw new Error(`[Drizzle] Unknown table ${col.tableName} for column ${col.ormName}.`);\n }\n\n const table = toDrizzleTable(fragnoTable, namespace);\n const out = table[col.ormName];\n if (out) {\n return out;\n }\n\n throw new Error(`[Drizzle] Unknown column name ${col.ormName} in ${fragnoTable.ormName}.`);\n }\n\n /**\n * Build a WHERE clause from a condition using Drizzle's query builder\n */\n function buildWhere(\n schema: AnySchema,\n namespace: string | undefined,\n condition: Condition,\n ): Drizzle.SQL | undefined {\n if (condition.type === \"compare\") {\n const left = toDrizzleColumn(schema, namespace, condition.a);\n const op = condition.operator;\n let right = condition.b;\n if (right instanceof Column) {\n right = toDrizzleColumn(schema, namespace, right);\n } else {\n // Handle string references - convert external ID to internal ID via subquery\n if (condition.a.role === \"reference\" && typeof right === \"string\") {\n // Find the table that contains this column\n const table = Object.values(schema.tables).find((t) =>\n Object.values(t.columns).includes(condition.a),\n );\n if (table) {\n // Find relation that uses this column\n const relation = Object.values(table.relations).find((rel) =>\n rel.on.some(([localCol]) => localCol === condition.a.ormName),\n );\n if (relation) {\n const refTable = relation.table;\n const internalIdCol = refTable.getInternalIdColumn();\n const idCol = refTable.getIdColumn();\n const physicalTableName = mapper\n ? mapper.toPhysical(refTable.ormName)\n : refTable.ormName;\n\n // Build a SQL subquery using Drizzle's sql template\n right = Drizzle.sql`(select ${Drizzle.sql.identifier(internalIdCol.name)} from ${Drizzle.sql.identifier(physicalTableName)} where ${Drizzle.sql.identifier(idCol.name)} = ${right} limit 1)`;\n }\n }\n } else {\n // Serialize non-Column values (e.g., FragnoId -> string, Date -> number for SQLite)\n right = serialize(right, condition.a, provider);\n }\n }\n\n switch (op) {\n case \"=\":\n return Drizzle.eq(left, right);\n case \"!=\":\n return Drizzle.ne(left, right);\n case \">\":\n return Drizzle.gt(left, right);\n case \">=\":\n return Drizzle.gte(left, right);\n case \"<\":\n return Drizzle.lt(left, right);\n case \"<=\":\n return Drizzle.lte(left, right);\n case \"in\": {\n return Drizzle.inArray(left, right as never[]);\n }\n case \"not in\":\n return Drizzle.notInArray(left, right as never[]);\n case \"is\":\n return right === null ? Drizzle.isNull(left) : Drizzle.eq(left, right);\n case \"is not\":\n return right === null ? Drizzle.isNotNull(left) : Drizzle.ne(left, right);\n case \"contains\": {\n right =\n typeof right === \"string\" ? `%${right}%` : Drizzle.sql`concat('%', ${right}, '%')`;\n return Drizzle.like(left, right as string);\n }\n case \"not contains\": {\n right =\n typeof right === \"string\" ? `%${right}%` : Drizzle.sql`concat('%', ${right}, '%')`;\n return Drizzle.notLike(left, right as string);\n }\n case \"ends with\": {\n right = typeof right === \"string\" ? `%${right}` : Drizzle.sql`concat('%', ${right})`;\n return Drizzle.like(left, right as string);\n }\n case \"not ends with\": {\n right = typeof right === \"string\" ? `%${right}` : Drizzle.sql`concat('%', ${right})`;\n return Drizzle.notLike(left, right as string);\n }\n case \"starts with\": {\n right = typeof right === \"string\" ? `${right}%` : Drizzle.sql`concat(${right}, '%')`;\n return Drizzle.like(left, right as string);\n }\n case \"not starts with\": {\n right = typeof right === \"string\" ? `${right}%` : Drizzle.sql`concat(${right}, '%')`;\n return Drizzle.notLike(left, right as string);\n }\n\n default:\n throw new Error(`Unsupported operator: ${op}`);\n }\n }\n\n if (condition.type === \"and\") {\n return Drizzle.and(...condition.items.map((item) => buildWhere(schema, namespace, item)));\n }\n\n if (condition.type === \"not\") {\n const result = buildWhere(schema, namespace, condition.item);\n if (!result) {\n return;\n }\n\n return Drizzle.not(result);\n }\n\n return Drizzle.or(...condition.items.map((item) => buildWhere(schema, namespace, item)));\n }\n\n /**\n * Process reference subqueries in encoded values, converting them to Drizzle SQL subqueries\n */\n function processReferenceSubqueries(values: Record<string, unknown>): Record<string, unknown> {\n const processed: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(values)) {\n if (value instanceof ReferenceSubquery) {\n const refTable = value.referencedTable;\n const externalId = value.externalIdValue;\n const internalIdCol = refTable.getInternalIdColumn();\n const idCol = refTable.getIdColumn();\n\n // Map logical table name to physical table name using the mapper\n const physicalTableName = mapper ? mapper.toPhysical(refTable.ormName) : refTable.ormName;\n\n // Build a SQL subquery using Drizzle's sql template\n // This creates a subquery: (SELECT _internalId FROM table WHERE id = ? LIMIT 1)\n // Safe cast: we're building a SQL subquery that returns a single bigint value\n processed[key] =\n Drizzle.sql`(select ${Drizzle.sql.identifier(internalIdCol.name)} from ${Drizzle.sql.identifier(physicalTableName)} where ${Drizzle.sql.identifier(idCol.name)} = ${externalId} limit 1)`;\n } else {\n processed[key] = value;\n }\n }\n\n return processed;\n }\n\n /**\n * Get table from schema by name\n * @throws Error if table is not found in schema\n */\n function getTable(schema: AnySchema, name: unknown): AnyTable {\n const table = schema.tables[name as string];\n if (!table) {\n throw new Error(`Invalid table name ${name}.`);\n }\n return table;\n }\n\n /**\n * Get the version to check for a given ID and checkVersion flag.\n * @returns The version to check or undefined if no check is required.\n * @throws Error if the ID is a string and checkVersion is true.\n */\n function getVersionToCheck(id: FragnoId | string, checkVersion: boolean): number | undefined {\n if (!checkVersion) {\n return undefined;\n }\n\n if (typeof id === \"string\") {\n throw new Error(\n `Cannot use checkVersion with a string ID. Version checking requires a FragnoId with version information.`,\n );\n }\n\n return id.version;\n }\n\n /**\n * Process joins recursively to support nested joins with orderBy and limit\n */\n function processJoins(\n schema: AnySchema,\n namespace: string | undefined,\n joins: CompiledJoin[],\n ): Record<string, Drizzle.DBQueryConfig<\"many\", boolean>> {\n const result: Record<string, Drizzle.DBQueryConfig<\"many\", boolean>> = {};\n\n for (const join of joins) {\n const { options, relation } = join;\n\n if (!options) {\n continue;\n }\n\n const targetTable = relation.table;\n const joinName = relation.name;\n\n // Build columns for this join using shared utility\n const selectOption = options.select === undefined ? true : options.select;\n const orderedColumns = getOrderedJoinColumns(targetTable, selectOption);\n const joinColumns: Record<string, boolean> = {};\n for (const colName of orderedColumns) {\n joinColumns[colName] = true;\n }\n\n // Build orderBy for this join\n let joinOrderBy: Drizzle.SQL[] | undefined;\n if (options.orderBy && options.orderBy.length > 0) {\n joinOrderBy = options.orderBy.map(([col, direction]) => {\n const drizzleCol = toDrizzleColumn(schema, namespace, col);\n return direction === \"asc\" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);\n });\n }\n\n // Build WHERE clause for this join if provided\n let joinWhere: Drizzle.SQL | undefined;\n if (options.where) {\n joinWhere = buildWhere(schema, namespace, options.where);\n }\n\n // Build the join config\n const joinConfig: Drizzle.DBQueryConfig<\"many\", boolean> = {\n columns: joinColumns,\n orderBy: joinOrderBy,\n limit: options.limit,\n where: joinWhere,\n };\n\n // Recursively process nested joins\n if (options.join && options.join.length > 0) {\n joinConfig.with = processJoins(schema, namespace, options.join);\n }\n\n result[joinName] = joinConfig;\n }\n\n return result;\n }\n\n return {\n compileRetrievalOperation(op: RetrievalOperation<AnySchema>): DrizzleCompiledQuery | null {\n const schema = op.schema;\n switch (op.type) {\n case \"count\": {\n // Build WHERE clause\n let whereClause: Drizzle.SQL | undefined;\n if (op.options.where) {\n const condition = buildCondition(op.table.columns, op.options.where);\n if (condition === false) {\n // Never matches - return null\n return null;\n }\n if (condition !== true) {\n whereClause = buildWhere(schema, op.namespace, condition);\n }\n }\n\n const drizzleTable = toDrizzleTable(op.table, op.namespace);\n const query = db.select({ count: Drizzle.count() }).from(drizzleTable);\n\n const compiledQuery = whereClause ? query.where(whereClause).toSQL() : query.toSQL();\n return compiledQuery;\n }\n\n case \"find\": {\n const {\n useIndex: _useIndex,\n orderByIndex,\n joins,\n after,\n before,\n pageSize,\n ...findOptions\n } = op.options;\n\n // Get index columns for ordering and cursor pagination\n let indexColumns: AnyColumn[] = [];\n let orderDirection: \"asc\" | \"desc\" = \"asc\";\n\n if (orderByIndex) {\n const index = op.table.indexes[orderByIndex.indexName];\n orderDirection = orderByIndex.direction;\n\n if (!index) {\n // If _primary index doesn't exist, fall back to ID column\n if (orderByIndex.indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n throw new Error(\n `Index \"${orderByIndex.indexName}\" not found on table \"${op.table.name}\"`,\n );\n }\n } else {\n indexColumns = index.columns;\n }\n }\n\n // Convert orderByIndex to orderBy format\n let orderBy: Drizzle.SQL[] | undefined;\n if (indexColumns.length > 0) {\n orderBy = indexColumns.map((col) => {\n const drizzleCol = toDrizzleColumn(schema, op.namespace, col);\n return orderDirection === \"asc\" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);\n });\n }\n\n // Build query configuration\n const columns: Record<string, boolean> = {};\n const select = findOptions.select;\n\n if (select === true || select === undefined) {\n for (const col of Object.values(op.table.columns)) {\n columns[col.ormName] = true;\n }\n } else {\n for (const k of select) {\n columns[op.table.columns[k].ormName] = true;\n }\n // Always include hidden columns (for FragnoId construction with internal ID and version)\n for (const col of Object.values(op.table.columns)) {\n if (col.isHidden && !columns[col.ormName]) {\n columns[col.ormName] = true;\n }\n }\n }\n\n // Build WHERE clause with cursor conditions\n const whereClauses: Drizzle.SQL[] = [];\n\n // Add user-defined where clause\n if (findOptions.where) {\n const condition = buildCondition(op.table.columns, findOptions.where);\n if (condition === false) {\n // Never matches - return null to indicate this query should be skipped\n return null;\n }\n if (condition !== true) {\n const clause = buildWhere(schema, op.namespace, condition);\n if (clause) {\n whereClauses.push(clause);\n }\n }\n }\n\n // Add cursor-based pagination conditions\n if ((after || before) && indexColumns.length > 0) {\n const cursor = after || before;\n // Decode cursor if it's a string, otherwise use it as-is\n const cursorObj = typeof cursor === \"string\" ? decodeCursor(cursor!) : cursor!;\n const serializedValues = serializeCursorValues(cursorObj, indexColumns, provider);\n\n // Build tuple comparison for cursor pagination\n // For \"after\" with \"asc\": (col1, col2, ...) > (val1, val2, ...)\n // For \"before\" with \"desc\": reverse the comparison\n const isAfter = !!after;\n const useGreaterThan =\n (isAfter && orderDirection === \"asc\") || (!isAfter && orderDirection === \"desc\");\n\n if (indexColumns.length === 1) {\n // Simple single-column case\n const col = toDrizzleColumn(schema, op.namespace, indexColumns[0]!);\n const val = serializedValues[indexColumns[0]!.ormName];\n whereClauses.push(useGreaterThan ? Drizzle.gt(col, val) : Drizzle.lt(col, val));\n } else {\n // Multi-column tuple comparison using SQL\n const drizzleCols = indexColumns.map((c) => toDrizzleColumn(schema, op.namespace, c));\n const vals = indexColumns.map((c) => serializedValues[c.ormName]);\n const operator = useGreaterThan ? \">\" : \"<\";\n // Safe cast: building a SQL comparison expression for cursor pagination\n // Build the tuple comparison: (col1, col2) > (val1, val2)\n const colsSQL = Drizzle.sql.join(drizzleCols, Drizzle.sql.raw(\", \"));\n const valsSQL = Drizzle.sql.join(\n vals.map((v) => Drizzle.sql`${v}`),\n Drizzle.sql.raw(\", \"),\n );\n whereClauses.push(\n Drizzle.sql`(${colsSQL}) ${Drizzle.sql.raw(operator)} (${valsSQL})`,\n );\n }\n }\n\n const whereClause = whereClauses.length > 0 ? Drizzle.and(...whereClauses) : undefined;\n\n const queryConfig: Drizzle.DBQueryConfig<\"many\", boolean> = {\n columns,\n limit: pageSize,\n where: whereClause,\n orderBy,\n with: {},\n };\n\n // Process joins recursively to support nested joins\n if (joins) {\n queryConfig.with = processJoins(schema, op.namespace, joins);\n }\n\n // For multi-schema support: get the mapper for the operation's namespace\n const opMapper = getMapperForOperation(op.namespace);\n const physicalTableName = opMapper\n ? opMapper.toPhysical(op.table.ormName)\n : op.table.ormName;\n const tableQuery = db.query[physicalTableName];\n\n if (!tableQuery) {\n throw new Error(\n `[Drizzle] Table ${op.table.ormName} (physical: ${physicalTableName}) not found in db.query. ` +\n `Available tables: ${Object.keys(db.query).join(\", \")}`,\n );\n }\n\n const compiledQuery = tableQuery.findMany(queryConfig).toSQL();\n return compiledQuery;\n }\n }\n },\n\n compileMutationOperation(\n op: MutationOperation<AnySchema>,\n ): CompiledMutation<DrizzleCompiledQuery> | null {\n const schema = op.schema;\n switch (op.type) {\n case \"create\": {\n const table = getTable(schema, op.table);\n const drizzleTable = toDrizzleTable(table, op.namespace);\n // encodeValues now handles runtime defaults automatically\n const encodedValues = encodeValues(op.values, table, true, provider);\n const values = processReferenceSubqueries(encodedValues);\n\n const compiledQuery = db.insert(drizzleTable).values(values).toSQL();\n return {\n query: compiledQuery,\n expectedAffectedRows: null, // creates don't need affected row checks\n };\n }\n\n case \"update\": {\n const table = getTable(schema, op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n const drizzleTable = toDrizzleTable(table, op.namespace);\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const condition =\n versionToCheck !== undefined\n ? buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n // Handle boolean cases\n if (condition === false) {\n // Never matches - skip this operation\n return null;\n }\n\n const whereClause =\n condition === true ? undefined : buildWhere(schema, op.namespace, condition);\n const encodedSetValues = encodeValues(op.set, table, false, provider);\n const setValues = processReferenceSubqueries(encodedSetValues);\n\n // Automatically increment _version for optimistic concurrency control\n // Safe cast: we're building a SQL expression for incrementing the version\n setValues[versionColumn.ormName] = Drizzle.sql.raw(\n `COALESCE(${versionColumn.ormName}, 0) + 1`,\n ) as unknown;\n\n const compiledQuery = db.update(drizzleTable).set(setValues).where(whereClause).toSQL();\n return {\n query: compiledQuery,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n };\n }\n\n case \"delete\": {\n const table = getTable(schema, op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n const drizzleTable = toDrizzleTable(table, op.namespace);\n\n if (!op.id) {\n throw new Error(\n `[Drizzle] Delete operation on table \"${op.table}\" has undefined id. ` +\n `Make sure you're passing a valid FragnoId or string ID.`,\n );\n }\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n\n if (!externalId) {\n throw new Error(\n `[Drizzle] Delete operation on table \"${op.table}\" has invalid id. ` +\n `The FragnoId object exists but has no externalId. ` +\n `Received: ${JSON.stringify(op.id)}. ` +\n `Make sure the record was properly loaded from the database.`,\n );\n }\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const condition =\n versionToCheck !== undefined\n ? buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n // Handle boolean cases\n if (condition === false) {\n // Never matches - skip this operation\n return null;\n }\n\n const whereClause =\n condition === true ? undefined : buildWhere(schema, op.namespace, condition);\n\n const compiledQuery = db.delete(drizzleTable).where(whereClause).toSQL();\n return {\n query: compiledQuery,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n };\n }\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyCA,SAAgB,yBACd,MACA,UACA,QACmC;CAInC,MAAM,CAAC,IAAI,iBAAiB,aADd,KAAK,iBAAiB,CACW;;;;;CAM/C,SAAS,sBAAsB,WAA4D;AACzF,MAAI,UACF,QAAO,sBAAsB,UAAU;AAEzC,SAAO;;;;;;CAOT,SAAS,eAAe,OAAiB,WAA0C;EAEjF,MAAM,WAAW,sBAAsB,UAAU;EAGjD,MAAM,oBAAoB,WAAW,SAAS,WAAW,MAAM,QAAQ,GAAG,MAAM;EAChF,MAAM,MAAM,cAAc;AAC1B,MAAI,IACF,QAAO;AAGT,QAAM,IAAI,MACR,gCAAgC,kBAAkB,aAAa,MAAM,QAAQ,2CAC9E;;;;;;CAOH,SAAS,gBACP,QACA,WACA,KACY;EACZ,MAAM,cAAc,OAAO,OAAO,IAAI;AACtC,MAAI,CAAC,YACH,OAAM,IAAI,MAAM,2BAA2B,IAAI,UAAU,cAAc,IAAI,QAAQ,GAAG;EAIxF,MAAM,MADQ,eAAe,aAAa,UAAU,CAClC,IAAI;AACtB,MAAI,IACF,QAAO;AAGT,QAAM,IAAI,MAAM,iCAAiC,IAAI,QAAQ,MAAM,YAAY,QAAQ,GAAG;;;;;CAM5F,SAAS,WACP,QACA,WACA,WACyB;AACzB,MAAI,UAAU,SAAS,WAAW;GAChC,MAAM,OAAO,gBAAgB,QAAQ,WAAW,UAAU,EAAE;GAC5D,MAAM,KAAK,UAAU;GACrB,IAAI,QAAQ,UAAU;AACtB,OAAI,iBAAiB,OACnB,SAAQ,gBAAgB,QAAQ,WAAW,MAAM;YAG7C,UAAU,EAAE,SAAS,eAAe,OAAO,UAAU,UAAU;IAEjE,MAAM,QAAQ,OAAO,OAAO,OAAO,OAAO,CAAC,MAAM,MAC/C,OAAO,OAAO,EAAE,QAAQ,CAAC,SAAS,UAAU,EAAE,CAC/C;AACD,QAAI,OAAO;KAET,MAAM,WAAW,OAAO,OAAO,MAAM,UAAU,CAAC,MAAM,QACpD,IAAI,GAAG,MAAM,CAAC,cAAc,aAAa,UAAU,EAAE,QAAQ,CAC9D;AACD,SAAI,UAAU;MACZ,MAAM,WAAW,SAAS;MAC1B,MAAM,gBAAgB,SAAS,qBAAqB;MACpD,MAAM,QAAQ,SAAS,aAAa;MACpC,MAAM,oBAAoB,SACtB,OAAO,WAAW,SAAS,QAAQ,GACnC,SAAS;AAGb,cAAQ,QAAQ,GAAG,WAAW,QAAQ,IAAI,WAAW,cAAc,KAAK,CAAC,QAAQ,QAAQ,IAAI,WAAW,kBAAkB,CAAC,SAAS,QAAQ,IAAI,WAAW,MAAM,KAAK,CAAC,KAAK,MAAM;;;SAKtL,SAAQ,UAAU,OAAO,UAAU,GAAG,SAAS;AAInD,WAAQ,IAAR;IACE,KAAK,IACH,QAAO,QAAQ,GAAG,MAAM,MAAM;IAChC,KAAK,KACH,QAAO,QAAQ,GAAG,MAAM,MAAM;IAChC,KAAK,IACH,QAAO,QAAQ,GAAG,MAAM,MAAM;IAChC,KAAK,KACH,QAAO,QAAQ,IAAI,MAAM,MAAM;IACjC,KAAK,IACH,QAAO,QAAQ,GAAG,MAAM,MAAM;IAChC,KAAK,KACH,QAAO,QAAQ,IAAI,MAAM,MAAM;IACjC,KAAK,KACH,QAAO,QAAQ,QAAQ,MAAM,MAAiB;IAEhD,KAAK,SACH,QAAO,QAAQ,WAAW,MAAM,MAAiB;IACnD,KAAK,KACH,QAAO,UAAU,OAAO,QAAQ,OAAO,KAAK,GAAG,QAAQ,GAAG,MAAM,MAAM;IACxE,KAAK,SACH,QAAO,UAAU,OAAO,QAAQ,UAAU,KAAK,GAAG,QAAQ,GAAG,MAAM,MAAM;IAC3E,KAAK;AACH,aACE,OAAO,UAAU,WAAW,IAAI,MAAM,KAAK,QAAQ,GAAG,eAAe,MAAM;AAC7E,YAAO,QAAQ,KAAK,MAAM,MAAgB;IAE5C,KAAK;AACH,aACE,OAAO,UAAU,WAAW,IAAI,MAAM,KAAK,QAAQ,GAAG,eAAe,MAAM;AAC7E,YAAO,QAAQ,QAAQ,MAAM,MAAgB;IAE/C,KAAK;AACH,aAAQ,OAAO,UAAU,WAAW,IAAI,UAAU,QAAQ,GAAG,eAAe,MAAM;AAClF,YAAO,QAAQ,KAAK,MAAM,MAAgB;IAE5C,KAAK;AACH,aAAQ,OAAO,UAAU,WAAW,IAAI,UAAU,QAAQ,GAAG,eAAe,MAAM;AAClF,YAAO,QAAQ,QAAQ,MAAM,MAAgB;IAE/C,KAAK;AACH,aAAQ,OAAO,UAAU,WAAW,GAAG,MAAM,KAAK,QAAQ,GAAG,UAAU,MAAM;AAC7E,YAAO,QAAQ,KAAK,MAAM,MAAgB;IAE5C,KAAK;AACH,aAAQ,OAAO,UAAU,WAAW,GAAG,MAAM,KAAK,QAAQ,GAAG,UAAU,MAAM;AAC7E,YAAO,QAAQ,QAAQ,MAAM,MAAgB;IAG/C,QACE,OAAM,IAAI,MAAM,yBAAyB,KAAK;;;AAIpD,MAAI,UAAU,SAAS,MACrB,QAAO,QAAQ,IAAI,GAAG,UAAU,MAAM,KAAK,SAAS,WAAW,QAAQ,WAAW,KAAK,CAAC,CAAC;AAG3F,MAAI,UAAU,SAAS,OAAO;GAC5B,MAAM,SAAS,WAAW,QAAQ,WAAW,UAAU,KAAK;AAC5D,OAAI,CAAC,OACH;AAGF,UAAO,QAAQ,IAAI,OAAO;;AAG5B,SAAO,QAAQ,GAAG,GAAG,UAAU,MAAM,KAAK,SAAS,WAAW,QAAQ,WAAW,KAAK,CAAC,CAAC;;;;;CAM1F,SAAS,2BAA2B,QAA0D;EAC5F,MAAMA,YAAqC,EAAE;AAE7C,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,CAC/C,KAAI,iBAAiB,mBAAmB;GACtC,MAAM,WAAW,MAAM;GACvB,MAAM,aAAa,MAAM;GACzB,MAAM,gBAAgB,SAAS,qBAAqB;GACpD,MAAM,QAAQ,SAAS,aAAa;GAGpC,MAAM,oBAAoB,SAAS,OAAO,WAAW,SAAS,QAAQ,GAAG,SAAS;AAKlF,aAAU,OACR,QAAQ,GAAG,WAAW,QAAQ,IAAI,WAAW,cAAc,KAAK,CAAC,QAAQ,QAAQ,IAAI,WAAW,kBAAkB,CAAC,SAAS,QAAQ,IAAI,WAAW,MAAM,KAAK,CAAC,KAAK,WAAW;QAEjL,WAAU,OAAO;AAIrB,SAAO;;;;;;CAOT,SAAS,SAAS,QAAmB,MAAyB;EAC5D,MAAM,QAAQ,OAAO,OAAO;AAC5B,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,sBAAsB,KAAK,GAAG;AAEhD,SAAO;;;;;;;CAQT,SAAS,kBAAkB,IAAuB,cAA2C;AAC3F,MAAI,CAAC,aACH;AAGF,MAAI,OAAO,OAAO,SAChB,OAAM,IAAI,MACR,2GACD;AAGH,SAAO,GAAG;;;;;CAMZ,SAAS,aACP,QACA,WACA,OACwD;EACxD,MAAMC,SAAiE,EAAE;AAEzE,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,EAAE,SAAS,aAAa;AAE9B,OAAI,CAAC,QACH;GAGF,MAAM,cAAc,SAAS;GAC7B,MAAM,WAAW,SAAS;GAI1B,MAAM,iBAAiB,sBAAsB,aADxB,QAAQ,WAAW,SAAY,OAAO,QAAQ,OACI;GACvE,MAAMC,cAAuC,EAAE;AAC/C,QAAK,MAAM,WAAW,eACpB,aAAY,WAAW;GAIzB,IAAIC;AACJ,OAAI,QAAQ,WAAW,QAAQ,QAAQ,SAAS,EAC9C,eAAc,QAAQ,QAAQ,KAAK,CAAC,KAAK,eAAe;IACtD,MAAM,aAAa,gBAAgB,QAAQ,WAAW,IAAI;AAC1D,WAAO,cAAc,QAAQ,QAAQ,IAAI,WAAW,GAAG,QAAQ,KAAK,WAAW;KAC/E;GAIJ,IAAIC;AACJ,OAAI,QAAQ,MACV,aAAY,WAAW,QAAQ,WAAW,QAAQ,MAAM;GAI1D,MAAMC,aAAqD;IACzD,SAAS;IACT,SAAS;IACT,OAAO,QAAQ;IACf,OAAO;IACR;AAGD,OAAI,QAAQ,QAAQ,QAAQ,KAAK,SAAS,EACxC,YAAW,OAAO,aAAa,QAAQ,WAAW,QAAQ,KAAK;AAGjE,UAAO,YAAY;;AAGrB,SAAO;;AAGT,QAAO;EACL,0BAA0B,IAAgE;GACxF,MAAM,SAAS,GAAG;AAClB,WAAQ,GAAG,MAAX;IACE,KAAK,SAAS;KAEZ,IAAIC;AACJ,SAAI,GAAG,QAAQ,OAAO;MACpB,MAAM,YAAY,eAAe,GAAG,MAAM,SAAS,GAAG,QAAQ,MAAM;AACpE,UAAI,cAAc,MAEhB,QAAO;AAET,UAAI,cAAc,KAChB,eAAc,WAAW,QAAQ,GAAG,WAAW,UAAU;;KAI7D,MAAM,eAAe,eAAe,GAAG,OAAO,GAAG,UAAU;KAC3D,MAAM,QAAQ,GAAG,OAAO,EAAE,OAAO,QAAQ,OAAO,EAAE,CAAC,CAAC,KAAK,aAAa;AAGtE,YADsB,cAAc,MAAM,MAAM,YAAY,CAAC,OAAO,GAAG,MAAM,OAAO;;IAItF,KAAK,QAAQ;KACX,MAAM,EACJ,UAAU,WACV,cACA,OACA,OACA,QACA,SACA,GAAG,gBACD,GAAG;KAGP,IAAIC,eAA4B,EAAE;KAClC,IAAIC,iBAAiC;AAErC,SAAI,cAAc;MAChB,MAAM,QAAQ,GAAG,MAAM,QAAQ,aAAa;AAC5C,uBAAiB,aAAa;AAE9B,UAAI,CAAC,MAEH,KAAI,aAAa,cAAc,WAC7B,gBAAe,CAAC,GAAG,MAAM,aAAa,CAAC;UAEvC,OAAM,IAAI,MACR,UAAU,aAAa,UAAU,wBAAwB,GAAG,MAAM,KAAK,GACxE;UAGH,gBAAe,MAAM;;KAKzB,IAAIC;AACJ,SAAI,aAAa,SAAS,EACxB,WAAU,aAAa,KAAK,QAAQ;MAClC,MAAM,aAAa,gBAAgB,QAAQ,GAAG,WAAW,IAAI;AAC7D,aAAO,mBAAmB,QAAQ,QAAQ,IAAI,WAAW,GAAG,QAAQ,KAAK,WAAW;OACpF;KAIJ,MAAMC,UAAmC,EAAE;KAC3C,MAAM,SAAS,YAAY;AAE3B,SAAI,WAAW,QAAQ,WAAW,OAChC,MAAK,MAAM,OAAO,OAAO,OAAO,GAAG,MAAM,QAAQ,CAC/C,SAAQ,IAAI,WAAW;UAEpB;AACL,WAAK,MAAM,KAAK,OACd,SAAQ,GAAG,MAAM,QAAQ,GAAG,WAAW;AAGzC,WAAK,MAAM,OAAO,OAAO,OAAO,GAAG,MAAM,QAAQ,CAC/C,KAAI,IAAI,YAAY,CAAC,QAAQ,IAAI,SAC/B,SAAQ,IAAI,WAAW;;KAM7B,MAAMC,eAA8B,EAAE;AAGtC,SAAI,YAAY,OAAO;MACrB,MAAM,YAAY,eAAe,GAAG,MAAM,SAAS,YAAY,MAAM;AACrE,UAAI,cAAc,MAEhB,QAAO;AAET,UAAI,cAAc,MAAM;OACtB,MAAM,SAAS,WAAW,QAAQ,GAAG,WAAW,UAAU;AAC1D,WAAI,OACF,cAAa,KAAK,OAAO;;;AAM/B,UAAK,SAAS,WAAW,aAAa,SAAS,GAAG;MAChD,MAAM,SAAS,SAAS;MAGxB,MAAM,mBAAmB,sBADP,OAAO,WAAW,WAAW,aAAa,OAAQ,GAAG,QACb,cAAc,SAAS;MAKjF,MAAM,UAAU,CAAC,CAAC;MAClB,MAAM,iBACH,WAAW,mBAAmB,SAAW,CAAC,WAAW,mBAAmB;AAE3E,UAAI,aAAa,WAAW,GAAG;OAE7B,MAAM,MAAM,gBAAgB,QAAQ,GAAG,WAAW,aAAa,GAAI;OACnE,MAAM,MAAM,iBAAiB,aAAa,GAAI;AAC9C,oBAAa,KAAK,iBAAiB,QAAQ,GAAG,KAAK,IAAI,GAAG,QAAQ,GAAG,KAAK,IAAI,CAAC;aAC1E;OAEL,MAAM,cAAc,aAAa,KAAK,MAAM,gBAAgB,QAAQ,GAAG,WAAW,EAAE,CAAC;OACrF,MAAM,OAAO,aAAa,KAAK,MAAM,iBAAiB,EAAE,SAAS;OACjE,MAAM,WAAW,iBAAiB,MAAM;OAGxC,MAAM,UAAU,QAAQ,IAAI,KAAK,aAAa,QAAQ,IAAI,IAAI,KAAK,CAAC;OACpE,MAAM,UAAU,QAAQ,IAAI,KAC1B,KAAK,KAAK,MAAM,QAAQ,GAAG,GAAG,IAAI,EAClC,QAAQ,IAAI,IAAI,KAAK,CACtB;AACD,oBAAa,KACX,QAAQ,GAAG,IAAI,QAAQ,IAAI,QAAQ,IAAI,IAAI,SAAS,CAAC,IAAI,QAAQ,GAClE;;;KAML,MAAMC,cAAsD;MAC1D;MACA,OAAO;MACP,OALkB,aAAa,SAAS,IAAI,QAAQ,IAAI,GAAG,aAAa,GAAG;MAM3E;MACA,MAAM,EAAE;MACT;AAGD,SAAI,MACF,aAAY,OAAO,aAAa,QAAQ,GAAG,WAAW,MAAM;KAI9D,MAAM,WAAW,sBAAsB,GAAG,UAAU;KACpD,MAAM,oBAAoB,WACtB,SAAS,WAAW,GAAG,MAAM,QAAQ,GACrC,GAAG,MAAM;KACb,MAAM,aAAa,GAAG,MAAM;AAE5B,SAAI,CAAC,WACH,OAAM,IAAI,MACR,mBAAmB,GAAG,MAAM,QAAQ,cAAc,kBAAkB,6CAC7C,OAAO,KAAK,GAAG,MAAM,CAAC,KAAK,KAAK,GACxD;AAIH,YADsB,WAAW,SAAS,YAAY,CAAC,OAAO;;;;EAMpE,yBACE,IAC+C;GAC/C,MAAM,SAAS,GAAG;AAClB,WAAQ,GAAG,MAAX;IACE,KAAK,UAAU;KACb,MAAM,QAAQ,SAAS,QAAQ,GAAG,MAAM;KACxC,MAAM,eAAe,eAAe,OAAO,GAAG,UAAU;KAGxD,MAAM,SAAS,2BADO,aAAa,GAAG,QAAQ,OAAO,MAAM,SAAS,CACZ;AAGxD,YAAO;MACL,OAFoB,GAAG,OAAO,aAAa,CAAC,OAAO,OAAO,CAAC,OAAO;MAGlE,sBAAsB;MACvB;;IAGH,KAAK,UAAU;KACb,MAAM,QAAQ,SAAS,QAAQ,GAAG,MAAM;KACxC,MAAM,WAAW,MAAM,aAAa;KACpC,MAAM,gBAAgB,MAAM,kBAAkB;KAC9C,MAAM,eAAe,eAAe,OAAO,GAAG,UAAU;KAExD,MAAM,aAAa,OAAO,GAAG,OAAO,WAAW,GAAG,KAAK,GAAG,GAAG;KAC7D,MAAM,iBAAiB,kBAAkB,GAAG,IAAI,GAAG,aAAa;KAGhE,MAAM,YACJ,mBAAmB,SACf,eAAe,MAAM,UAAU,OAC7B,GAAG,IACD,GAAG,SAAS,SAAS,KAAK,WAAW,EACrC,GAAG,cAAc,SAAS,KAAK,eAAe,CAC/C,CACF,GACD,eAAe,MAAM,UAAU,OAAO,GAAG,SAAS,SAAS,KAAK,WAAW,CAAC;AAGlF,SAAI,cAAc,MAEhB,QAAO;KAGT,MAAM,cACJ,cAAc,OAAO,SAAY,WAAW,QAAQ,GAAG,WAAW,UAAU;KAE9E,MAAM,YAAY,2BADO,aAAa,GAAG,KAAK,OAAO,OAAO,SAAS,CACP;AAI9D,eAAU,cAAc,WAAW,QAAQ,IAAI,IAC7C,YAAY,cAAc,QAAQ,UACnC;AAGD,YAAO;MACL,OAFoB,GAAG,OAAO,aAAa,CAAC,IAAI,UAAU,CAAC,MAAM,YAAY,CAAC,OAAO;MAGrF,sBAAsB,GAAG,eAAe,IAAI;MAC7C;;IAGH,KAAK,UAAU;KACb,MAAM,QAAQ,SAAS,QAAQ,GAAG,MAAM;KACxC,MAAM,WAAW,MAAM,aAAa;KACpC,MAAM,gBAAgB,MAAM,kBAAkB;KAC9C,MAAM,eAAe,eAAe,OAAO,GAAG,UAAU;AAExD,SAAI,CAAC,GAAG,GACN,OAAM,IAAI,MACR,wCAAwC,GAAG,MAAM,6EAElD;KAGH,MAAM,aAAa,OAAO,GAAG,OAAO,WAAW,GAAG,KAAK,GAAG,GAAG;AAE7D,SAAI,CAAC,WACH,OAAM,IAAI,MACR,wCAAwC,GAAG,MAAM,gFAElC,KAAK,UAAU,GAAG,GAAG,CAAC,+DAEtC;KAEH,MAAM,iBAAiB,kBAAkB,GAAG,IAAI,GAAG,aAAa;KAGhE,MAAM,YACJ,mBAAmB,SACf,eAAe,MAAM,UAAU,OAC7B,GAAG,IACD,GAAG,SAAS,SAAS,KAAK,WAAW,EACrC,GAAG,cAAc,SAAS,KAAK,eAAe,CAC/C,CACF,GACD,eAAe,MAAM,UAAU,OAAO,GAAG,SAAS,SAAS,KAAK,WAAW,CAAC;AAGlF,SAAI,cAAc,MAEhB,QAAO;KAGT,MAAM,cACJ,cAAc,OAAO,SAAY,WAAW,QAAQ,GAAG,WAAW,UAAU;AAG9E,YAAO;MACL,OAFoB,GAAG,OAAO,aAAa,CAAC,MAAM,YAAY,CAAC,OAAO;MAGtE,sBAAsB,GAAG,eAAe,IAAI;MAC7C;;;;EAIR"}
@@ -66,7 +66,7 @@ function transformJoinArraysToObjects(row, op, provider) {
66
66
  }
67
67
  return transformedRow;
68
68
  }
69
- function createDrizzleUOWDecoder(_schema, provider) {
69
+ function createDrizzleUOWDecoder(provider) {
70
70
  return (rawResults, ops) => {
71
71
  if (rawResults.length !== ops.length) throw new Error("rawResults and ops must have the same length");
72
72
  return rawResults.map((result, index) => {
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle-uow-decoder.js","names":["result: Record<string, unknown>","cursor: Cursor | undefined","index"],"sources":["../../../src/adapters/drizzle/drizzle-uow-decoder.ts"],"sourcesContent":["import type { AnySchema, AnyTable } from \"../../schema/create\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport type { RetrievalOperation, UOWDecoder } from \"../../query/unit-of-work\";\nimport { decodeResult } from \"../../query/result-transform\";\nimport { getOrderedJoinColumns } from \"./join-column-utils\";\nimport type { DrizzleResult } from \"./shared\";\nimport { createCursorFromRecord, Cursor, type CursorResult } from \"../../query/cursor\";\n\n/**\n * Join information with nested join support\n */\ninterface JoinInfo {\n relation: { name: string; table: AnyTable };\n options:\n | {\n select: true | string[];\n join?: JoinInfo[];\n }\n | false;\n}\n\n/**\n * Recursively transform join arrays to objects, handling nested joins.\n *\n * Drizzle joins use `json_build_array` where nested join data is appended after the parent's columns.\n * For example, if post has columns [id, title, content, _internalId, _version] and a nested author join,\n * the array will be: [id, title, content, _internalId, _version, authorArray]\n *\n * @param value - The join array from Drizzle\n * @param joinInfo - Join metadata including nested joins\n * @param relationName - Name of the current relation (for prefixing column names)\n * @returns Object with flattened keys (relationName:columnName) for all levels\n */\nfunction transformJoinArray(\n value: unknown[],\n joinInfo: JoinInfo,\n relationName: string,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n if (joinInfo.options === false) {\n return result;\n }\n\n const targetTable = joinInfo.relation.table;\n\n // Get ordered columns using shared utility (must match compiler's column order)\n const orderedSelectedColumns = getOrderedJoinColumns(targetTable, joinInfo.options.select);\n\n // Map column values to flattened format: relationName:columnName\n for (let i = 0; i < orderedSelectedColumns.length && i < value.length; i++) {\n const columnName = orderedSelectedColumns[i];\n if (columnName) {\n result[`${relationName}:${columnName}`] = value[i];\n }\n }\n\n // Handle nested joins - they appear after all columns in the array\n if (joinInfo.options.join && joinInfo.options.join.length > 0) {\n let nestedArrayIndex = orderedSelectedColumns.length;\n\n for (const nestedJoin of joinInfo.options.join) {\n const nestedRelationName = `${relationName}:${nestedJoin.relation.name}`;\n const nestedValue = value[nestedArrayIndex];\n\n if (Array.isArray(nestedValue)) {\n // Recursively transform nested join\n const nestedResult = transformJoinArray(nestedValue, nestedJoin, nestedRelationName);\n Object.assign(result, nestedResult);\n }\n\n nestedArrayIndex++;\n }\n }\n\n return result;\n}\n\n/**\n * Drizzle joins using `json_build_array` so the result is a tuple of values that we need to map to\n * the correct columns. This function handles nested joins recursively.\n *\n * @param row - Raw database result row that may contain join arrays\n * @param op - The retrieval operation containing join information\n * @returns Transformed row with join arrays converted to objects\n */\nfunction transformJoinArraysToObjects(\n row: Record<string, unknown>,\n op: {\n type: string;\n table: AnyTable;\n options?: {\n joins?: JoinInfo[];\n };\n },\n provider: SQLProvider,\n): Record<string, unknown> {\n // Only process find operations with joins\n if (op.type !== \"find\" || !op.options?.joins) {\n return row;\n }\n\n const transformedRow = { ...row };\n\n for (const join of op.options.joins) {\n const relationName = join.relation.name;\n let value = row[relationName];\n\n // For SQLite, json_array returns a JSON string that needs to be parsed\n if (provider === \"sqlite\" && typeof value === \"string\") {\n try {\n value = JSON.parse(value) as unknown;\n } catch {\n // If parsing fails, skip this join\n continue;\n }\n }\n\n // Skip if not an array (join didn't return data)\n if (!Array.isArray(value)) {\n continue;\n }\n\n // Skip if join options are false (join was disabled)\n if (join.options === false) {\n continue;\n }\n\n // Get the target table for this relation\n const relation = op.table.relations[relationName];\n if (!relation) {\n continue;\n }\n\n // Recursively transform this join and its nested joins\n const joinResult = transformJoinArray(value, join, relationName);\n Object.assign(transformedRow, joinResult);\n\n // Remove the original array property\n delete transformedRow[relationName];\n }\n\n return transformedRow;\n}\n\nexport function createDrizzleUOWDecoder<TSchema extends AnySchema>(\n _schema: TSchema,\n provider: SQLProvider,\n): UOWDecoder<TSchema, DrizzleResult> {\n return (rawResults, ops) => {\n if (rawResults.length !== ops.length) {\n throw new Error(\"rawResults and ops must have the same length\");\n }\n\n return rawResults.map((result, index) => {\n const op = ops[index] as RetrievalOperation<TSchema>;\n if (!op) {\n throw new Error(\"op must be defined\");\n }\n\n // Handle count operations - return the count value directly\n if (op.type === \"count\") {\n if (result.rows.length > 0 && result.rows[0]) {\n const row = result.rows[0] as Record<string, unknown>;\n const countValue = row[\"count\"] ?? row[\"count(*)\"];\n\n if (typeof countValue !== \"number\") {\n throw new Error(`Unexpected result for count, received: ${countValue}`);\n }\n\n return countValue;\n }\n return 0;\n }\n\n // Handle find operations - decode each row\n const decodedRows = result.rows.map((row) => {\n const transformedRow = transformJoinArraysToObjects(row, op, provider);\n return decodeResult(transformedRow, op.table, provider);\n });\n\n // If cursor generation is requested, wrap in CursorResult\n if (op.withCursor) {\n let cursor: Cursor | undefined;\n\n // Generate cursor from last item if results exist\n if (decodedRows.length > 0 && op.options.orderByIndex && op.options.pageSize) {\n const lastItem = decodedRows[decodedRows.length - 1];\n const indexName = op.options.orderByIndex.indexName;\n\n // Get index columns\n let indexColumns;\n if (indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n const index = op.table.indexes[indexName];\n if (index) {\n indexColumns = index.columns;\n }\n }\n\n if (indexColumns && lastItem) {\n cursor = createCursorFromRecord(lastItem as Record<string, unknown>, indexColumns, {\n indexName: op.options.orderByIndex.indexName,\n orderDirection: op.options.orderByIndex.direction,\n pageSize: op.options.pageSize,\n });\n }\n }\n\n const cursorResult: CursorResult<unknown> = {\n items: decodedRows,\n cursor,\n };\n return cursorResult;\n }\n\n return decodedRows;\n });\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"drizzle-uow-decoder.js","names":["result: Record<string, unknown>","cursor: Cursor | undefined","index"],"sources":["../../../src/adapters/drizzle/drizzle-uow-decoder.ts"],"sourcesContent":["import type { AnySchema, AnyTable } from \"../../schema/create\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport type { RetrievalOperation, UOWDecoder } from \"../../query/unit-of-work\";\nimport { decodeResult } from \"../../query/result-transform\";\nimport { getOrderedJoinColumns } from \"./join-column-utils\";\nimport type { DrizzleResult } from \"./shared\";\nimport { createCursorFromRecord, Cursor, type CursorResult } from \"../../query/cursor\";\n\n/**\n * Join information with nested join support\n */\ninterface JoinInfo {\n relation: { name: string; table: AnyTable };\n options:\n | {\n select: true | string[];\n join?: JoinInfo[];\n }\n | false;\n}\n\n/**\n * Recursively transform join arrays to objects, handling nested joins.\n *\n * Drizzle joins use `json_build_array` where nested join data is appended after the parent's columns.\n * For example, if post has columns [id, title, content, _internalId, _version] and a nested author join,\n * the array will be: [id, title, content, _internalId, _version, authorArray]\n *\n * @param value - The join array from Drizzle\n * @param joinInfo - Join metadata including nested joins\n * @param relationName - Name of the current relation (for prefixing column names)\n * @returns Object with flattened keys (relationName:columnName) for all levels\n */\nfunction transformJoinArray(\n value: unknown[],\n joinInfo: JoinInfo,\n relationName: string,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n if (joinInfo.options === false) {\n return result;\n }\n\n const targetTable = joinInfo.relation.table;\n\n // Get ordered columns using shared utility (must match compiler's column order)\n const orderedSelectedColumns = getOrderedJoinColumns(targetTable, joinInfo.options.select);\n\n // Map column values to flattened format: relationName:columnName\n for (let i = 0; i < orderedSelectedColumns.length && i < value.length; i++) {\n const columnName = orderedSelectedColumns[i];\n if (columnName) {\n result[`${relationName}:${columnName}`] = value[i];\n }\n }\n\n // Handle nested joins - they appear after all columns in the array\n if (joinInfo.options.join && joinInfo.options.join.length > 0) {\n let nestedArrayIndex = orderedSelectedColumns.length;\n\n for (const nestedJoin of joinInfo.options.join) {\n const nestedRelationName = `${relationName}:${nestedJoin.relation.name}`;\n const nestedValue = value[nestedArrayIndex];\n\n if (Array.isArray(nestedValue)) {\n // Recursively transform nested join\n const nestedResult = transformJoinArray(nestedValue, nestedJoin, nestedRelationName);\n Object.assign(result, nestedResult);\n }\n\n nestedArrayIndex++;\n }\n }\n\n return result;\n}\n\n/**\n * Drizzle joins using `json_build_array` so the result is a tuple of values that we need to map to\n * the correct columns. This function handles nested joins recursively.\n *\n * @param row - Raw database result row that may contain join arrays\n * @param op - The retrieval operation containing join information\n * @returns Transformed row with join arrays converted to objects\n */\nfunction transformJoinArraysToObjects(\n row: Record<string, unknown>,\n op: {\n type: string;\n table: AnyTable;\n options?: {\n joins?: JoinInfo[];\n };\n },\n provider: SQLProvider,\n): Record<string, unknown> {\n // Only process find operations with joins\n if (op.type !== \"find\" || !op.options?.joins) {\n return row;\n }\n\n const transformedRow = { ...row };\n\n for (const join of op.options.joins) {\n const relationName = join.relation.name;\n let value = row[relationName];\n\n // For SQLite, json_array returns a JSON string that needs to be parsed\n if (provider === \"sqlite\" && typeof value === \"string\") {\n try {\n value = JSON.parse(value) as unknown;\n } catch {\n // If parsing fails, skip this join\n continue;\n }\n }\n\n // Skip if not an array (join didn't return data)\n if (!Array.isArray(value)) {\n continue;\n }\n\n // Skip if join options are false (join was disabled)\n if (join.options === false) {\n continue;\n }\n\n // Get the target table for this relation\n const relation = op.table.relations[relationName];\n if (!relation) {\n continue;\n }\n\n // Recursively transform this join and its nested joins\n const joinResult = transformJoinArray(value, join, relationName);\n Object.assign(transformedRow, joinResult);\n\n // Remove the original array property\n delete transformedRow[relationName];\n }\n\n return transformedRow;\n}\n\nexport function createDrizzleUOWDecoder(provider: SQLProvider): UOWDecoder<DrizzleResult> {\n return (rawResults, ops) => {\n if (rawResults.length !== ops.length) {\n throw new Error(\"rawResults and ops must have the same length\");\n }\n\n return rawResults.map((result, index) => {\n const op = ops[index] as RetrievalOperation<AnySchema>;\n if (!op) {\n throw new Error(\"op must be defined\");\n }\n\n // Handle count operations - return the count value directly\n if (op.type === \"count\") {\n if (result.rows.length > 0 && result.rows[0]) {\n const row = result.rows[0] as Record<string, unknown>;\n const countValue = row[\"count\"] ?? row[\"count(*)\"];\n\n if (typeof countValue !== \"number\") {\n throw new Error(`Unexpected result for count, received: ${countValue}`);\n }\n\n return countValue;\n }\n return 0;\n }\n\n // Handle find operations - decode each row\n const decodedRows = result.rows.map((row) => {\n const transformedRow = transformJoinArraysToObjects(row, op, provider);\n return decodeResult(transformedRow, op.table, provider);\n });\n\n // If cursor generation is requested, wrap in CursorResult\n if (op.withCursor) {\n let cursor: Cursor | undefined;\n\n // Generate cursor from last item if results exist\n if (decodedRows.length > 0 && op.options.orderByIndex && op.options.pageSize) {\n const lastItem = decodedRows[decodedRows.length - 1];\n const indexName = op.options.orderByIndex.indexName;\n\n // Get index columns\n let indexColumns;\n if (indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n const index = op.table.indexes[indexName];\n if (index) {\n indexColumns = index.columns;\n }\n }\n\n if (indexColumns && lastItem) {\n cursor = createCursorFromRecord(lastItem as Record<string, unknown>, indexColumns, {\n indexName: op.options.orderByIndex.indexName,\n orderDirection: op.options.orderByIndex.direction,\n pageSize: op.options.pageSize,\n });\n }\n }\n\n const cursorResult: CursorResult<unknown> = {\n items: decodedRows,\n cursor,\n };\n return cursorResult;\n }\n\n return decodedRows;\n });\n };\n}\n"],"mappings":"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"}
@@ -1 +1,14 @@
1
- import "drizzle-orm";
1
+ import "drizzle-orm";
2
+
3
+ //#region src/adapters/drizzle/shared.d.ts
4
+
5
+ /**
6
+ * Maps logical table names (used by fragment authors) to physical table names (with namespace suffix)
7
+ */
8
+ interface TableNameMapper {
9
+ toPhysical(logicalName: string): string;
10
+ toLogical(physicalName: string): string;
11
+ }
12
+ //#endregion
13
+ export { TableNameMapper };
14
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","names":[],"sources":["../../../src/adapters/drizzle/shared.ts"],"sourcesContent":[],"mappings":";;;;;;;UA2BiB,eAAA"}
@@ -3,6 +3,7 @@ import { Migrator } from "../../migration-engine/create.js";
3
3
  import { SQLProvider } from "../../shared/providers.js";
4
4
  import { AbstractQuery } from "../../query/query.js";
5
5
  import { DatabaseAdapter, fragnoDatabaseAdapterNameFakeSymbol, fragnoDatabaseAdapterVersionFakeSymbol } from "../adapters.js";
6
+ import { TableNameMapper } from "./kysely-shared.js";
6
7
  import { KyselyUOWConfig } from "./kysely-query.js";
7
8
  import { Kysely } from "kysely";
8
9
 
@@ -18,6 +19,7 @@ declare class KyselyAdapter implements DatabaseAdapter<KyselyUOWConfig> {
18
19
  get [fragnoDatabaseAdapterNameFakeSymbol](): string;
19
20
  get [fragnoDatabaseAdapterVersionFakeSymbol](): number;
20
21
  close(): Promise<void>;
22
+ createTableNameMapper(namespace: string): TableNameMapper;
21
23
  createQueryEngine<T extends AnySchema>(schema: T, namespace: string): AbstractQuery<T, KyselyUOWConfig>;
22
24
  isConnectionHealthy(): Promise<boolean>;
23
25
  createMigrationEngine(schema: AnySchema, namespace: string): Migrator;
@@ -1 +1 @@
1
- {"version":3,"file":"kysely-adapter.d.ts","names":[],"sources":["../../../src/adapters/kysely/kysely-adapter.ts"],"sourcesContent":[],"mappings":";;;;;;;;;KAoBK,SAAA,GAAY;UAEA,YAAA;EAFZ,EAAA,EAGC,SAHQ,GAAA,CAAA,GAAA,GAGW,SAHF,GAGc,OAHd,CAGsB,SAHtB,CAAA,CAAA;EAEN,QAAA,EAEL,WAFiB;;AACJ,cAIZ,aAAA,YAAyB,eAJb,CAI6B,eAJ7B,CAAA,CAAA;EAAoB,CAAA,OAAA;EAAR,WAAA,CAAA,MAAA,EAQf,YARe;EACzB,KAYL,mCAAA,GAZK,EAAA,MAAA;EAAW,KAgBhB,sCAAA,GAhBgB,EAAA,MAAA;EAGV,KAAA,CAAA,CAAA,EAiBI,OAjBU,CAAA,IAAA,CAAA;EAA2B,iBAAA,CAAA,UAqBxB,SArBwB,CAAA,CAAA,MAAA,EAsB1C,CAtB0C,EAAA,SAAA,EAAA,MAAA,CAAA,EAwBjD,aAxBiD,CAwBnC,CAxBmC,EAwBhC,eAxBgC,CAAA;EAIhC,mBAAA,CAAA,CAAA,EA0BS,OA1BT,CAAA,OAAA,CAAA;EAKf,qBAAA,CAAA,MAAA,EAiCyB,SAjCzB,EAAA,SAAA,EAAA,MAAA,CAAA,EAiCwD,QAjCxD;EAIA,gBAAA,CAAA,SAAA,EAAA,MAAA,CAAA,EAuJsC,OAvJtC,CAAA,MAAA,GAAA,SAAA,CAAA"}
1
+ {"version":3,"file":"kysely-adapter.d.ts","names":[],"sources":["../../../src/adapters/kysely/kysely-adapter.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;KAoBK,SAAA,GAAY;UAEA,YAAA;MACX,mBAAmB,YAAY,QAAQ;EAHxC,QAAA,EAIO,WAJE;AAEd;AACM,cAIO,aAAA,YAAyB,eAJhC,CAIgD,eAJhD,CAAA,CAAA;EAAmB,CAAA,OAAA;EAAoB,WAAA,CAAA,MAAA,EASvB,YATuB;EAAR,KAc9B,mCAAA,GAd8B,EAAA,MAAA;EACzB,KAiBL,sCAAA,GAjBK,EAAA,MAAA;EAAW,KAAA,CAAA,CAAA,EAqBN,OArBM,CAAA,IAAA,CAAA;EAGV,qBAAc,CAAA,SAAA,EAAA,MAAA,CAAA,EAkBH,eAlBG;EAA2B,iBAAA,CAAA,UA0BxB,SA1BwB,CAAA,CAAA,MAAA,EA2B1C,CA3B0C,EAAA,SAAA,EAAA,MAAA,CAAA,EA6BjD,aA7BiD,CA6BnC,CA7BmC,EA6BhC,eA7BgC,CAAA;EAKhC,mBAAA,CAAA,CAAA,EAwCS,OAxCT,CAAA,OAAA,CAAA;EAKf,qBAAA,CAAA,MAAA,EA+CyB,SA/CzB,EAAA,SAAA,EAAA,MAAA,CAAA,EA+CwD,QA/CxD;EAIA,gBAAA,CAAA,SAAA,EAAA,MAAA,CAAA,EAqKsC,OArKtC,CAAA,MAAA,GAAA,SAAA,CAAA"}
@@ -2,8 +2,8 @@ import { fragnoDatabaseAdapterNameFakeSymbol, fragnoDatabaseAdapterVersionFakeSy
2
2
  import { SETTINGS_TABLE_NAME } from "../../shared/settings-schema.js";
3
3
  import { createMigrator } from "../../migration-engine/create.js";
4
4
  import { execute, preprocessOperations } from "./migration/execute.js";
5
- import { fromKysely } from "./kysely-query.js";
6
5
  import { createTableNameMapper } from "./kysely-shared.js";
6
+ import { fromKysely } from "./kysely-query.js";
7
7
  import { createKyselyConnectionPool } from "./kysely-connection-pool.js";
8
8
  import { sql } from "kysely";
9
9
  import { createHash } from "node:crypto";
@@ -12,6 +12,7 @@ import { createHash } from "node:crypto";
12
12
  var KyselyAdapter = class {
13
13
  #connectionPool;
14
14
  #provider;
15
+ #schemaNamespaceMap = /* @__PURE__ */ new WeakMap();
15
16
  constructor(config) {
16
17
  this.#connectionPool = createKyselyConnectionPool(config.db);
17
18
  this.#provider = config.provider;
@@ -25,9 +26,13 @@ var KyselyAdapter = class {
25
26
  async close() {
26
27
  await this.#connectionPool.close();
27
28
  }
29
+ createTableNameMapper(namespace) {
30
+ return createTableNameMapper(namespace);
31
+ }
28
32
  createQueryEngine(schema, namespace) {
33
+ this.#schemaNamespaceMap.set(schema, namespace);
29
34
  const mapper = namespace ? createTableNameMapper(namespace) : void 0;
30
- return fromKysely(schema, this.#connectionPool, this.#provider, mapper);
35
+ return fromKysely(schema, this.#connectionPool, this.#provider, mapper, void 0, this.#schemaNamespaceMap);
31
36
  }
32
37
  async isConnectionHealthy() {
33
38
  const conn = await this.#connectionPool.connect();
@@ -1 +1 @@
1
- {"version":3,"file":"kysely-adapter.js","names":["#connectionPool","#provider","db","config: KyselyConfig","parts: string[]"],"sources":["../../../src/adapters/kysely/kysely-adapter.ts"],"sourcesContent":["import { sql, type Kysely } from \"kysely\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport {\n fragnoDatabaseAdapterNameFakeSymbol,\n fragnoDatabaseAdapterVersionFakeSymbol,\n type DatabaseAdapter,\n} from \"../adapters\";\nimport { createMigrator, type Migrator } from \"../../migration-engine/create\";\nimport type { AnySchema } from \"../../schema/create\";\nimport type { CustomOperation, MigrationOperation } from \"../../migration-engine/shared\";\nimport { execute, preprocessOperations } from \"./migration/execute\";\nimport type { AbstractQuery } from \"../../query/query\";\nimport { fromKysely, type KyselyUOWConfig } from \"./kysely-query\";\nimport { createTableNameMapper } from \"./kysely-shared\";\nimport { createHash } from \"node:crypto\";\nimport { SETTINGS_TABLE_NAME } from \"../../shared/settings-schema\";\nimport type { ConnectionPool } from \"../../shared/connection-pool\";\nimport { createKyselyConnectionPool } from \"./kysely-connection-pool\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KyselyAny = Kysely<any>;\n\nexport interface KyselyConfig {\n db: KyselyAny | (() => KyselyAny | Promise<KyselyAny>);\n provider: SQLProvider;\n}\n\nexport class KyselyAdapter implements DatabaseAdapter<KyselyUOWConfig> {\n #connectionPool: ConnectionPool<KyselyAny>;\n #provider: SQLProvider;\n\n constructor(config: KyselyConfig) {\n this.#connectionPool = createKyselyConnectionPool(config.db);\n this.#provider = config.provider;\n }\n\n get [fragnoDatabaseAdapterNameFakeSymbol](): string {\n return \"kysely\";\n }\n\n get [fragnoDatabaseAdapterVersionFakeSymbol](): number {\n return 0;\n }\n\n async close(): Promise<void> {\n await this.#connectionPool.close();\n }\n\n createQueryEngine<T extends AnySchema>(\n schema: T,\n namespace: string,\n ): AbstractQuery<T, KyselyUOWConfig> {\n // Only create mapper if namespace is non-empty\n const mapper = namespace ? createTableNameMapper(namespace) : undefined;\n return fromKysely(schema, this.#connectionPool, this.#provider, mapper);\n }\n\n async isConnectionHealthy(): Promise<boolean> {\n const conn = await this.#connectionPool.connect();\n try {\n const result = await conn.db.executeQuery(sql`SELECT 1 as healthy`.compile(conn.db));\n return (result.rows[0] as Record<string, unknown>)[\"healthy\"] === 1;\n } catch {\n return false;\n } finally {\n await conn.release();\n }\n }\n\n createMigrationEngine(schema: AnySchema, namespace: string): Migrator {\n const mapper = namespace ? createTableNameMapper(namespace) : undefined;\n\n const preprocessMigrationOperations = (operations: MigrationOperation[], db: KyselyAny) => {\n // Preprocess operations using the provided db instance\n const config: KyselyConfig = {\n db,\n provider: this.#provider,\n };\n let preprocessed = preprocessOperations(operations, config);\n\n if (this.#provider === \"mysql\") {\n preprocessed.unshift({ type: \"custom\", sql: \"SET FOREIGN_KEY_CHECKS = 0\" });\n preprocessed.push({ type: \"custom\", sql: \"SET FOREIGN_KEY_CHECKS = 1\" });\n }\n\n return preprocessed;\n };\n\n // Convert operations to executable nodes bound to specific db instance\n const toExecutableNodes = (operations: MigrationOperation[], db: KyselyAny) => {\n const onCustomNode = (node: CustomOperation, db: KyselyAny) => {\n const statement = sql.raw(node[\"sql\"] as string);\n\n return {\n compile() {\n return statement.compile(db);\n },\n execute() {\n return statement.execute(db);\n },\n };\n };\n\n const config: KyselyConfig = { db, provider: this.#provider };\n return operations.flatMap((op) =>\n execute(op, config, (node) => onCustomNode(node, db), mapper),\n );\n };\n\n const migrator = createMigrator({\n schema,\n executor: async (operations) => {\n const conn = await this.#connectionPool.connect();\n try {\n // For SQLite, execute PRAGMA defer_foreign_keys BEFORE transaction\n if (this.#provider === \"sqlite\") {\n await sql.raw(\"PRAGMA defer_foreign_keys = ON\").execute(conn.db);\n }\n\n await conn.db.transaction().execute(async (tx) => {\n // Use the transaction instance for both preprocessing and execution\n const preprocessed = preprocessMigrationOperations(operations, tx);\n const nodes = toExecutableNodes(preprocessed, tx);\n for (const node of nodes) {\n try {\n await node.execute();\n } catch (e) {\n console.error(\"failed at\", node.compile(), e);\n throw e;\n }\n }\n });\n } finally {\n await conn.release();\n }\n },\n sql: {\n toSql: (operations) => {\n const parts: string[] = [];\n\n // Add SQLite PRAGMA at the beginning\n if (this.#provider === \"sqlite\") {\n parts.push(\"PRAGMA defer_foreign_keys = ON;\");\n }\n\n // Use getDatabaseSync for SQL generation (doesn't execute, just builds SQL strings)\n const db = this.#connectionPool.getDatabaseSync();\n const preprocessed = preprocessMigrationOperations(operations, db);\n const nodes = toExecutableNodes(preprocessed, db);\n const compiled = nodes.map((node) => `${node.compile().sql};`);\n\n parts.push(...compiled);\n\n return parts.join(\"\\n\\n\");\n },\n },\n\n settings: {\n getVersion: async () => {\n const conn = await this.#connectionPool.connect();\n try {\n const manager = createSettingsManager(conn.db, namespace);\n const v = await manager.get(`schema_version`);\n return v ? parseInt(v) : 0;\n } finally {\n await conn.release();\n }\n },\n updateSettingsInMigration: async (fromVersion, toVersion) => {\n const conn = await this.#connectionPool.connect();\n try {\n const manager = createSettingsManager(conn.db, namespace);\n return [\n {\n type: \"custom\",\n sql:\n fromVersion === 0\n ? manager.insert(`schema_version`, toVersion.toString())\n : manager.update(`schema_version`, toVersion.toString()),\n },\n ];\n } finally {\n await conn.release();\n }\n },\n },\n });\n\n return migrator;\n }\n\n async getSchemaVersion(namespace: string): Promise<string | undefined> {\n const conn = await this.#connectionPool.connect();\n try {\n const manager = createSettingsManager(conn.db, namespace);\n return await manager.get(`schema_version`);\n } finally {\n await conn.release();\n }\n }\n}\n\nfunction createSettingsManager(db: KyselyAny, namespace: string) {\n // Settings table is never namespaced, but keys include namespace prefix\n const tableName = SETTINGS_TABLE_NAME;\n\n return {\n async get(key: string): Promise<string | undefined> {\n try {\n const result = await db\n .selectFrom(tableName)\n .where(\"key\", \"=\", sql.lit(`${namespace}.${key}`))\n .select([\"value\"])\n .executeTakeFirstOrThrow();\n return result.value as string;\n } catch {\n return;\n }\n },\n\n insert(key: string, value: string) {\n return db\n .insertInto(tableName)\n .values({\n id: sql.lit(\n createHash(\"md5\").update(`${namespace}.${key}`).digest(\"base64url\").replace(/=/g, \"\"),\n ),\n key: sql.lit(`${namespace}.${key}`),\n value: sql.lit(value),\n })\n .compile().sql;\n },\n\n update(key: string, value: string) {\n return db\n .updateTable(tableName)\n .set({\n value: sql.lit(value),\n })\n .where(\"key\", \"=\", sql.lit(`${namespace}.${key}`))\n .compile().sql;\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"kysely-adapter.js","names":["#connectionPool","#provider","#schemaNamespaceMap","db","config: KyselyConfig","parts: string[]"],"sources":["../../../src/adapters/kysely/kysely-adapter.ts"],"sourcesContent":["import { sql, type Kysely } from \"kysely\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport {\n fragnoDatabaseAdapterNameFakeSymbol,\n fragnoDatabaseAdapterVersionFakeSymbol,\n type DatabaseAdapter,\n} from \"../adapters\";\nimport { createMigrator, type Migrator } from \"../../migration-engine/create\";\nimport type { AnySchema } from \"../../schema/create\";\nimport type { CustomOperation, MigrationOperation } from \"../../migration-engine/shared\";\nimport { execute, preprocessOperations } from \"./migration/execute\";\nimport type { AbstractQuery } from \"../../query/query\";\nimport { fromKysely, type KyselyUOWConfig } from \"./kysely-query\";\nimport { createTableNameMapper } from \"./kysely-shared\";\nimport { createHash } from \"node:crypto\";\nimport { SETTINGS_TABLE_NAME } from \"../../shared/settings-schema\";\nimport type { ConnectionPool } from \"../../shared/connection-pool\";\nimport { createKyselyConnectionPool } from \"./kysely-connection-pool\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KyselyAny = Kysely<any>;\n\nexport interface KyselyConfig {\n db: KyselyAny | (() => KyselyAny | Promise<KyselyAny>);\n provider: SQLProvider;\n}\n\nexport class KyselyAdapter implements DatabaseAdapter<KyselyUOWConfig> {\n #connectionPool: ConnectionPool<KyselyAny>;\n #provider: SQLProvider;\n #schemaNamespaceMap = new WeakMap<AnySchema, string>();\n\n constructor(config: KyselyConfig) {\n this.#connectionPool = createKyselyConnectionPool(config.db);\n this.#provider = config.provider;\n }\n\n get [fragnoDatabaseAdapterNameFakeSymbol](): string {\n return \"kysely\";\n }\n\n get [fragnoDatabaseAdapterVersionFakeSymbol](): number {\n return 0;\n }\n\n async close(): Promise<void> {\n await this.#connectionPool.close();\n }\n\n createTableNameMapper(namespace: string) {\n return createTableNameMapper(namespace);\n }\n\n createQueryEngine<T extends AnySchema>(\n schema: T,\n namespace: string,\n ): AbstractQuery<T, KyselyUOWConfig> {\n // Register schema-namespace mapping\n this.#schemaNamespaceMap.set(schema, namespace);\n\n // Only create mapper if namespace is non-empty\n const mapper = namespace ? createTableNameMapper(namespace) : undefined;\n return fromKysely(\n schema,\n this.#connectionPool,\n this.#provider,\n mapper,\n undefined,\n this.#schemaNamespaceMap,\n );\n }\n\n async isConnectionHealthy(): Promise<boolean> {\n const conn = await this.#connectionPool.connect();\n try {\n const result = await conn.db.executeQuery(sql`SELECT 1 as healthy`.compile(conn.db));\n return (result.rows[0] as Record<string, unknown>)[\"healthy\"] === 1;\n } catch {\n return false;\n } finally {\n await conn.release();\n }\n }\n\n createMigrationEngine(schema: AnySchema, namespace: string): Migrator {\n const mapper = namespace ? createTableNameMapper(namespace) : undefined;\n\n const preprocessMigrationOperations = (operations: MigrationOperation[], db: KyselyAny) => {\n // Preprocess operations using the provided db instance\n const config: KyselyConfig = {\n db,\n provider: this.#provider,\n };\n let preprocessed = preprocessOperations(operations, config);\n\n if (this.#provider === \"mysql\") {\n preprocessed.unshift({ type: \"custom\", sql: \"SET FOREIGN_KEY_CHECKS = 0\" });\n preprocessed.push({ type: \"custom\", sql: \"SET FOREIGN_KEY_CHECKS = 1\" });\n }\n\n return preprocessed;\n };\n\n // Convert operations to executable nodes bound to specific db instance\n const toExecutableNodes = (operations: MigrationOperation[], db: KyselyAny) => {\n const onCustomNode = (node: CustomOperation, db: KyselyAny) => {\n const statement = sql.raw(node[\"sql\"] as string);\n\n return {\n compile() {\n return statement.compile(db);\n },\n execute() {\n return statement.execute(db);\n },\n };\n };\n\n const config: KyselyConfig = { db, provider: this.#provider };\n return operations.flatMap((op) =>\n execute(op, config, (node) => onCustomNode(node, db), mapper),\n );\n };\n\n const migrator = createMigrator({\n schema,\n executor: async (operations) => {\n const conn = await this.#connectionPool.connect();\n try {\n // For SQLite, execute PRAGMA defer_foreign_keys BEFORE transaction\n if (this.#provider === \"sqlite\") {\n await sql.raw(\"PRAGMA defer_foreign_keys = ON\").execute(conn.db);\n }\n\n await conn.db.transaction().execute(async (tx) => {\n // Use the transaction instance for both preprocessing and execution\n const preprocessed = preprocessMigrationOperations(operations, tx);\n const nodes = toExecutableNodes(preprocessed, tx);\n for (const node of nodes) {\n try {\n await node.execute();\n } catch (e) {\n console.error(\"failed at\", node.compile(), e);\n throw e;\n }\n }\n });\n } finally {\n await conn.release();\n }\n },\n sql: {\n toSql: (operations) => {\n const parts: string[] = [];\n\n // Add SQLite PRAGMA at the beginning\n if (this.#provider === \"sqlite\") {\n parts.push(\"PRAGMA defer_foreign_keys = ON;\");\n }\n\n // Use getDatabaseSync for SQL generation (doesn't execute, just builds SQL strings)\n const db = this.#connectionPool.getDatabaseSync();\n const preprocessed = preprocessMigrationOperations(operations, db);\n const nodes = toExecutableNodes(preprocessed, db);\n const compiled = nodes.map((node) => `${node.compile().sql};`);\n\n parts.push(...compiled);\n\n return parts.join(\"\\n\\n\");\n },\n },\n\n settings: {\n getVersion: async () => {\n const conn = await this.#connectionPool.connect();\n try {\n const manager = createSettingsManager(conn.db, namespace);\n const v = await manager.get(`schema_version`);\n return v ? parseInt(v) : 0;\n } finally {\n await conn.release();\n }\n },\n updateSettingsInMigration: async (fromVersion, toVersion) => {\n const conn = await this.#connectionPool.connect();\n try {\n const manager = createSettingsManager(conn.db, namespace);\n return [\n {\n type: \"custom\",\n sql:\n fromVersion === 0\n ? manager.insert(`schema_version`, toVersion.toString())\n : manager.update(`schema_version`, toVersion.toString()),\n },\n ];\n } finally {\n await conn.release();\n }\n },\n },\n });\n\n return migrator;\n }\n\n async getSchemaVersion(namespace: string): Promise<string | undefined> {\n const conn = await this.#connectionPool.connect();\n try {\n const manager = createSettingsManager(conn.db, namespace);\n return await manager.get(`schema_version`);\n } finally {\n await conn.release();\n }\n }\n}\n\nfunction createSettingsManager(db: KyselyAny, namespace: string) {\n // Settings table is never namespaced, but keys include namespace prefix\n const tableName = SETTINGS_TABLE_NAME;\n\n return {\n async get(key: string): Promise<string | undefined> {\n try {\n const result = await db\n .selectFrom(tableName)\n .where(\"key\", \"=\", sql.lit(`${namespace}.${key}`))\n .select([\"value\"])\n .executeTakeFirstOrThrow();\n return result.value as string;\n } catch {\n return;\n }\n },\n\n insert(key: string, value: string) {\n return db\n .insertInto(tableName)\n .values({\n id: sql.lit(\n createHash(\"md5\").update(`${namespace}.${key}`).digest(\"base64url\").replace(/=/g, \"\"),\n ),\n key: sql.lit(`${namespace}.${key}`),\n value: sql.lit(value),\n })\n .compile().sql;\n },\n\n update(key: string, value: string) {\n return db\n .updateTable(tableName)\n .set({\n value: sql.lit(value),\n })\n .where(\"key\", \"=\", sql.lit(`${namespace}.${key}`))\n .compile().sql;\n },\n };\n}\n"],"mappings":"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"}