@fragno-dev/db 0.1.13 → 0.1.15

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 (178) hide show
  1. package/.turbo/turbo-build.log +179 -132
  2. package/CHANGELOG.md +30 -0
  3. package/dist/adapters/adapters.d.ts +27 -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 +5 -1
  7. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  8. package/dist/adapters/drizzle/drizzle-adapter.js +15 -3
  9. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  10. package/dist/adapters/drizzle/drizzle-query.js +7 -5
  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 +76 -44
  15. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
  16. package/dist/adapters/drizzle/drizzle-uow-decoder.js +23 -16
  17. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
  18. package/dist/adapters/drizzle/drizzle-uow-executor.js +18 -7
  19. package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
  20. package/dist/adapters/drizzle/generate.d.ts +4 -1
  21. package/dist/adapters/drizzle/generate.d.ts.map +1 -1
  22. package/dist/adapters/drizzle/generate.js +11 -18
  23. package/dist/adapters/drizzle/generate.js.map +1 -1
  24. package/dist/adapters/drizzle/shared.d.ts +14 -1
  25. package/dist/adapters/drizzle/shared.d.ts.map +1 -0
  26. package/dist/adapters/kysely/kysely-adapter.d.ts +5 -1
  27. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  28. package/dist/adapters/kysely/kysely-adapter.js +14 -3
  29. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  30. package/dist/adapters/kysely/kysely-query-builder.js +1 -1
  31. package/dist/adapters/kysely/kysely-query-compiler.js +3 -2
  32. package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
  33. package/dist/adapters/kysely/kysely-query.d.ts +1 -0
  34. package/dist/adapters/kysely/kysely-query.d.ts.map +1 -1
  35. package/dist/adapters/kysely/kysely-query.js +28 -19
  36. package/dist/adapters/kysely/kysely-query.js.map +1 -1
  37. package/dist/adapters/kysely/kysely-shared.d.ts +14 -0
  38. package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -0
  39. package/dist/adapters/kysely/kysely-shared.js +16 -1
  40. package/dist/adapters/kysely/kysely-shared.js.map +1 -1
  41. package/dist/adapters/kysely/kysely-uow-compiler.js +68 -16
  42. package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
  43. package/dist/adapters/kysely/kysely-uow-executor.js +8 -4
  44. package/dist/adapters/kysely/kysely-uow-executor.js.map +1 -1
  45. package/dist/adapters/kysely/migration/execute-base.js +1 -1
  46. package/dist/adapters/kysely/migration/execute-base.js.map +1 -1
  47. package/dist/db-fragment-definition-builder.d.ts +152 -0
  48. package/dist/db-fragment-definition-builder.d.ts.map +1 -0
  49. package/dist/db-fragment-definition-builder.js +137 -0
  50. package/dist/db-fragment-definition-builder.js.map +1 -0
  51. package/dist/fragments/internal-fragment.d.ts +19 -0
  52. package/dist/fragments/internal-fragment.d.ts.map +1 -0
  53. package/dist/fragments/internal-fragment.js +39 -0
  54. package/dist/fragments/internal-fragment.js.map +1 -0
  55. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  56. package/dist/migration-engine/generation-engine.js +35 -15
  57. package/dist/migration-engine/generation-engine.js.map +1 -1
  58. package/dist/mod.d.ts +8 -18
  59. package/dist/mod.d.ts.map +1 -1
  60. package/dist/mod.js +7 -34
  61. package/dist/mod.js.map +1 -1
  62. package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js +165 -0
  63. package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js.map +1 -0
  64. package/dist/packages/fragno/dist/api/bind-services.js +20 -0
  65. package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
  66. package/dist/packages/fragno/dist/api/error.js +48 -0
  67. package/dist/packages/fragno/dist/api/error.js.map +1 -0
  68. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
  69. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
  70. package/dist/packages/fragno/dist/api/fragment-instantiator.js +487 -0
  71. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
  72. package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
  73. package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
  74. package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
  75. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
  76. package/dist/packages/fragno/dist/api/internal/route.js +10 -0
  77. package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
  78. package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
  79. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
  80. package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
  81. package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
  82. package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
  83. package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
  84. package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
  85. package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
  86. package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
  87. package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
  88. package/dist/packages/fragno/dist/api/route.js +17 -0
  89. package/dist/packages/fragno/dist/api/route.js.map +1 -0
  90. package/dist/packages/fragno/dist/internal/symbols.js +10 -0
  91. package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
  92. package/dist/query/cursor.d.ts +10 -2
  93. package/dist/query/cursor.d.ts.map +1 -1
  94. package/dist/query/cursor.js +11 -4
  95. package/dist/query/cursor.js.map +1 -1
  96. package/dist/query/execute-unit-of-work.d.ts +123 -0
  97. package/dist/query/execute-unit-of-work.d.ts.map +1 -0
  98. package/dist/query/execute-unit-of-work.js +184 -0
  99. package/dist/query/execute-unit-of-work.js.map +1 -0
  100. package/dist/query/query.d.ts +3 -3
  101. package/dist/query/query.d.ts.map +1 -1
  102. package/dist/query/result-transform.js +4 -2
  103. package/dist/query/result-transform.js.map +1 -1
  104. package/dist/query/retry-policy.d.ts +88 -0
  105. package/dist/query/retry-policy.d.ts.map +1 -0
  106. package/dist/query/retry-policy.js +61 -0
  107. package/dist/query/retry-policy.js.map +1 -0
  108. package/dist/query/unit-of-work.d.ts +171 -32
  109. package/dist/query/unit-of-work.d.ts.map +1 -1
  110. package/dist/query/unit-of-work.js +530 -133
  111. package/dist/query/unit-of-work.js.map +1 -1
  112. package/dist/schema/serialize.js +12 -7
  113. package/dist/schema/serialize.js.map +1 -1
  114. package/dist/with-database.d.ts +28 -0
  115. package/dist/with-database.d.ts.map +1 -0
  116. package/dist/with-database.js +34 -0
  117. package/dist/with-database.js.map +1 -0
  118. package/package.json +10 -3
  119. package/src/adapters/adapters.ts +30 -0
  120. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +86 -17
  121. package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +291 -7
  122. package/src/adapters/drizzle/drizzle-adapter.test.ts +3 -51
  123. package/src/adapters/drizzle/drizzle-adapter.ts +35 -7
  124. package/src/adapters/drizzle/drizzle-query.ts +25 -15
  125. package/src/adapters/drizzle/drizzle-uow-compiler-mysql.test.ts +1442 -0
  126. package/src/adapters/drizzle/drizzle-uow-compiler-sqlite.test.ts +1414 -0
  127. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +78 -61
  128. package/src/adapters/drizzle/drizzle-uow-compiler.ts +123 -42
  129. package/src/adapters/drizzle/drizzle-uow-decoder.ts +34 -27
  130. package/src/adapters/drizzle/drizzle-uow-executor.ts +41 -8
  131. package/src/adapters/drizzle/generate.test.ts +102 -269
  132. package/src/adapters/drizzle/generate.ts +12 -30
  133. package/src/adapters/drizzle/test-utils.ts +36 -5
  134. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +66 -22
  135. package/src/adapters/kysely/kysely-adapter-sqlite.test.ts +156 -0
  136. package/src/adapters/kysely/kysely-adapter.ts +25 -2
  137. package/src/adapters/kysely/kysely-query-compiler.ts +3 -8
  138. package/src/adapters/kysely/kysely-query.ts +57 -37
  139. package/src/adapters/kysely/kysely-shared.ts +34 -0
  140. package/src/adapters/kysely/kysely-uow-compiler.test.ts +62 -74
  141. package/src/adapters/kysely/kysely-uow-compiler.ts +92 -24
  142. package/src/adapters/kysely/kysely-uow-executor.ts +26 -7
  143. package/src/adapters/kysely/kysely-uow-joins.test.ts +33 -50
  144. package/src/adapters/kysely/migration/execute-base.ts +1 -1
  145. package/src/db-fragment-definition-builder.test.ts +887 -0
  146. package/src/db-fragment-definition-builder.ts +506 -0
  147. package/src/db-fragment-instantiator.test.ts +467 -0
  148. package/src/db-fragment-integration.test.ts +408 -0
  149. package/src/fragments/internal-fragment.test.ts +160 -0
  150. package/src/fragments/internal-fragment.ts +85 -0
  151. package/src/migration-engine/generation-engine.test.ts +58 -15
  152. package/src/migration-engine/generation-engine.ts +78 -25
  153. package/src/mod.ts +35 -43
  154. package/src/query/cursor.test.ts +119 -0
  155. package/src/query/cursor.ts +17 -4
  156. package/src/query/execute-unit-of-work.test.ts +1310 -0
  157. package/src/query/execute-unit-of-work.ts +463 -0
  158. package/src/query/query.ts +4 -4
  159. package/src/query/result-transform.test.ts +129 -0
  160. package/src/query/result-transform.ts +4 -1
  161. package/src/query/retry-policy.test.ts +217 -0
  162. package/src/query/retry-policy.ts +141 -0
  163. package/src/query/unit-of-work-coordinator.test.ts +833 -0
  164. package/src/query/unit-of-work-types.test.ts +15 -2
  165. package/src/query/unit-of-work.test.ts +878 -200
  166. package/src/query/unit-of-work.ts +963 -321
  167. package/src/schema/serialize.ts +22 -11
  168. package/src/with-database.ts +140 -0
  169. package/tsdown.config.ts +1 -0
  170. package/dist/fragment.d.ts +0 -54
  171. package/dist/fragment.d.ts.map +0 -1
  172. package/dist/fragment.js +0 -92
  173. package/dist/fragment.js.map +0 -1
  174. package/dist/shared/settings-schema.js +0 -36
  175. package/dist/shared/settings-schema.js.map +0 -1
  176. package/src/fragment.test.ts +0 -341
  177. package/src/fragment.ts +0 -198
  178. package/src/shared/settings-schema.ts +0 -61
@@ -1,10 +1,7 @@
1
- import { Kysely, PostgresDialect } from "kysely";
2
- import { assert, beforeAll, describe, expect, it } from "vitest";
1
+ import { assert, describe, expect, it } from "vitest";
3
2
  import { column, FragnoId, idColumn, referenceColumn, schema } from "../../schema/create";
4
3
  import { UnitOfWork, type UOWDecoder } from "../../query/unit-of-work";
5
4
  import { createKyselyUOWCompiler } from "./kysely-uow-compiler";
6
- import type { ConnectionPool } from "../../shared/connection-pool";
7
- import { createKyselyConnectionPool } from "./kysely-connection-pool";
8
5
  import { Cursor } from "../../query/cursor";
9
6
 
10
7
  describe("kysely-uow-compiler", () => {
@@ -86,47 +83,21 @@ describe("kysely-uow-compiler", () => {
86
83
  });
87
84
  });
88
85
 
89
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
- let kysely: Kysely<any>;
91
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
- let pool: ConnectionPool<Kysely<any>>;
93
-
94
- beforeAll(() => {
95
- // Create a mock Kysely instance (we won't execute queries, just compile them)
96
- // We need a minimal pool that won't actually connect
97
- const mockPool = {
98
- connect: () => Promise.reject(new Error("Mock pool - no actual connections")),
99
- end: () => Promise.resolve(),
100
- on: () => {},
101
- };
102
-
103
- kysely = new Kysely({
104
- dialect: new PostgresDialect({
105
- // Safe: we're only compiling queries, not executing them
106
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
- pool: mockPool as any,
108
- }),
109
- });
110
-
111
- // Wrap in connection pool
112
- pool = createKyselyConnectionPool(kysely);
113
- });
114
-
115
86
  // Helper to create UnitOfWork for testing
116
87
  function createTestUOW(name?: string) {
117
- const mockCompiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
88
+ const mockCompiler = createKyselyUOWCompiler("postgresql");
118
89
  const mockExecutor = {
119
90
  executeRetrievalPhase: async () => [],
120
91
  executeMutationPhase: async () => ({ success: true, createdInternalIds: [] }),
121
92
  };
122
- const mockDecoder: UOWDecoder<typeof testSchema> = (rawResults, operations) => {
93
+ const mockDecoder: UOWDecoder = (rawResults, operations) => {
123
94
  if (rawResults.length !== operations.length) {
124
95
  throw new Error("rawResults and ops must have the same length");
125
96
  }
126
97
  return rawResults;
127
98
  };
128
99
  // Pass undefined for decoder since we're only testing compilation, not execution
129
- return new UnitOfWork(testSchema, mockCompiler, mockExecutor, mockDecoder, name);
100
+ return new UnitOfWork(mockCompiler, mockExecutor, mockDecoder, name).forSchema(testSchema);
130
101
  }
131
102
 
132
103
  describe("compileRetrievalOperation", () => {
@@ -136,7 +107,7 @@ describe("kysely-uow-compiler", () => {
136
107
  b.whereIndex("idx_email", (eb) => eb("email", "=", "test@example.com")),
137
108
  );
138
109
 
139
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
110
+ const compiler = createKyselyUOWCompiler("postgresql");
140
111
  const compiled = uow.compile(compiler);
141
112
 
142
113
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -152,7 +123,7 @@ describe("kysely-uow-compiler", () => {
152
123
  b.whereIndex("idx_name", (eb) => eb("name", "=", "Alice")).select(["id", "name"]),
153
124
  );
154
125
 
155
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
126
+ const compiler = createKyselyUOWCompiler("postgresql");
156
127
  const compiled = uow.compile(compiler);
157
128
 
158
129
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -166,7 +137,7 @@ describe("kysely-uow-compiler", () => {
166
137
  const uow = createTestUOW();
167
138
  uow.find("users", (b) => b.whereIndex("primary").pageSize(10));
168
139
 
169
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
140
+ const compiler = createKyselyUOWCompiler("postgresql");
170
141
  const compiled = uow.compile(compiler);
171
142
 
172
143
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -180,7 +151,7 @@ describe("kysely-uow-compiler", () => {
180
151
  const uow = createTestUOW();
181
152
  uow.find("users", (b) => b.whereIndex("primary").orderByIndex("primary", "desc"));
182
153
 
183
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
154
+ const compiler = createKyselyUOWCompiler("postgresql");
184
155
  const compiled = uow.compile(compiler);
185
156
 
186
157
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -193,7 +164,7 @@ describe("kysely-uow-compiler", () => {
193
164
  const uow = createTestUOW();
194
165
  uow.find("users", (b) => b.whereIndex("idx_name").orderByIndex("idx_name", "desc"));
195
166
 
196
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
167
+ const compiler = createKyselyUOWCompiler("postgresql");
197
168
  const compiled = uow.compile(compiler);
198
169
 
199
170
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -209,7 +180,7 @@ describe("kysely-uow-compiler", () => {
209
180
  );
210
181
  uow.find("posts", (b) => b.whereIndex("idx_title", (eb) => eb("title", "contains", "test")));
211
182
 
212
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
183
+ const compiler = createKyselyUOWCompiler("postgresql");
213
184
  const compiled = uow.compile(compiler);
214
185
 
215
186
  expect(compiled.retrievalBatch).toHaveLength(2);
@@ -228,7 +199,7 @@ describe("kysely-uow-compiler", () => {
228
199
  return b;
229
200
  });
230
201
 
231
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
202
+ const compiler = createKyselyUOWCompiler("postgresql");
232
203
  const compiled = uow.compile(compiler);
233
204
 
234
205
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -241,7 +212,7 @@ describe("kysely-uow-compiler", () => {
241
212
  const uow = createTestUOW();
242
213
  uow.find("users", (b) => b.whereIndex("idx_age", (eb) => eb("age", ">", 25)).selectCount());
243
214
 
244
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
215
+ const compiler = createKyselyUOWCompiler("postgresql");
245
216
  const compiled = uow.compile(compiler);
246
217
 
247
218
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -263,7 +234,7 @@ describe("kysely-uow-compiler", () => {
263
234
  b.whereIndex("idx_name").orderByIndex("idx_name", "asc").after(cursor).pageSize(10),
264
235
  );
265
236
 
266
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
237
+ const compiler = createKyselyUOWCompiler("postgresql");
267
238
  const compiled = uow.compile(compiler);
268
239
 
269
240
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -285,7 +256,7 @@ describe("kysely-uow-compiler", () => {
285
256
  b.whereIndex("idx_name").orderByIndex("idx_name", "desc").before(cursor).pageSize(10),
286
257
  );
287
258
 
288
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
259
+ const compiler = createKyselyUOWCompiler("postgresql");
289
260
  const compiled = uow.compile(compiler);
290
261
 
291
262
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -311,7 +282,7 @@ describe("kysely-uow-compiler", () => {
311
282
  .pageSize(5),
312
283
  );
313
284
 
314
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
285
+ const compiler = createKyselyUOWCompiler("postgresql");
315
286
  const compiled = uow.compile(compiler);
316
287
 
317
288
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -331,7 +302,7 @@ describe("kysely-uow-compiler", () => {
331
302
  age: 30,
332
303
  });
333
304
 
334
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
305
+ const compiler = createKyselyUOWCompiler("postgresql");
335
306
  const compiled = uow.compile(compiler);
336
307
  const [batch] = compiled.mutationBatch;
337
308
  assert(batch);
@@ -357,7 +328,7 @@ describe("kysely-uow-compiler", () => {
357
328
  }),
358
329
  );
359
330
 
360
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
331
+ const compiler = createKyselyUOWCompiler("postgresql");
361
332
  const compiled = uow.compile(compiler);
362
333
  const [batch] = compiled.mutationBatch;
363
334
  assert(batch);
@@ -373,7 +344,7 @@ describe("kysely-uow-compiler", () => {
373
344
  const userId = FragnoId.fromExternal("user123", 5);
374
345
  uow.update("users", userId, (b) => b.set({ age: 18 }).check());
375
346
 
376
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
347
+ const compiler = createKyselyUOWCompiler("postgresql");
377
348
  const compiled = uow.compile(compiler);
378
349
  const [batch] = compiled.mutationBatch;
379
350
  assert(batch);
@@ -389,7 +360,7 @@ describe("kysely-uow-compiler", () => {
389
360
  const userId = FragnoId.fromExternal("user123", 0);
390
361
  uow.delete("users", userId);
391
362
 
392
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
363
+ const compiler = createKyselyUOWCompiler("postgresql");
393
364
  const compiled = uow.compile(compiler);
394
365
  const [batch] = compiled.mutationBatch;
395
366
  assert(batch);
@@ -405,7 +376,7 @@ describe("kysely-uow-compiler", () => {
405
376
  const userId = FragnoId.fromExternal("user123", 3);
406
377
  uow.delete("users", userId, (b) => b.check());
407
378
 
408
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
379
+ const compiler = createKyselyUOWCompiler("postgresql");
409
380
  const compiled = uow.compile(compiler);
410
381
  const [batch] = compiled.mutationBatch;
411
382
  assert(batch);
@@ -425,7 +396,7 @@ describe("kysely-uow-compiler", () => {
425
396
  }),
426
397
  );
427
398
 
428
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
399
+ const compiler = createKyselyUOWCompiler("postgresql");
429
400
  const compiled = uow.compile(compiler);
430
401
  const [batch] = compiled.mutationBatch;
431
402
  assert(batch);
@@ -440,7 +411,7 @@ describe("kysely-uow-compiler", () => {
440
411
  const uow = createTestUOW();
441
412
  uow.delete("users", "user123");
442
413
 
443
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
414
+ const compiler = createKyselyUOWCompiler("postgresql");
444
415
  const compiled = uow.compile(compiler);
445
416
  const [batch] = compiled.mutationBatch;
446
417
  assert(batch);
@@ -480,7 +451,7 @@ describe("kysely-uow-compiler", () => {
480
451
  const userId = FragnoId.fromExternal("user456", 0);
481
452
  uow.delete("posts", userId);
482
453
 
483
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
454
+ const compiler = createKyselyUOWCompiler("postgresql");
484
455
  const compiled = uow.compile(compiler);
485
456
  const [createBatch, updateBatch, deleteBatch] = compiled.mutationBatch;
486
457
 
@@ -519,7 +490,7 @@ describe("kysely-uow-compiler", () => {
519
490
  const userId = FragnoId.fromExternal("user123", 3);
520
491
  uow.update("users", userId, (b) => b.set({ age: 31 }).check());
521
492
 
522
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
493
+ const compiler = createKyselyUOWCompiler("postgresql");
523
494
  const compiled = uow.compile(compiler);
524
495
 
525
496
  expect(compiled.name).toBe("update-user-balance");
@@ -551,7 +522,7 @@ describe("kysely-uow-compiler", () => {
551
522
  ),
552
523
  );
553
524
 
554
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
525
+ const compiler = createKyselyUOWCompiler("postgresql");
555
526
  const compiled = uow.compile(compiler);
556
527
 
557
528
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -565,7 +536,7 @@ describe("kysely-uow-compiler", () => {
565
536
  const uow = createTestUOW();
566
537
  uow.find("users", (b) => b.whereIndex("primary", () => false));
567
538
 
568
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
539
+ const compiler = createKyselyUOWCompiler("postgresql");
569
540
  const compiled = uow.compile(compiler);
570
541
 
571
542
  // When condition is false, the operation should return null and not be added to batch
@@ -576,7 +547,7 @@ describe("kysely-uow-compiler", () => {
576
547
  const uow = createTestUOW();
577
548
  uow.find("users", (b) => b.whereIndex("primary", () => true));
578
549
 
579
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
550
+ const compiler = createKyselyUOWCompiler("postgresql");
580
551
  const compiled = uow.compile(compiler);
581
552
 
582
553
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -593,7 +564,7 @@ describe("kysely-uow-compiler", () => {
593
564
  const userId = FragnoId.fromExternal("user123", 5);
594
565
  uow.update("users", userId, (b) => b.set({ age: 31 }).check());
595
566
 
596
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
567
+ const compiler = createKyselyUOWCompiler("postgresql");
597
568
  const compiled = uow.compile(compiler);
598
569
  const [batch] = compiled.mutationBatch;
599
570
  assert(batch);
@@ -610,7 +581,7 @@ describe("kysely-uow-compiler", () => {
610
581
  const userId = FragnoId.fromExternal("user456", 3);
611
582
  uow.delete("users", userId, (b) => b.check());
612
583
 
613
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
584
+ const compiler = createKyselyUOWCompiler("postgresql");
614
585
  const compiled = uow.compile(compiler);
615
586
  const [batch] = compiled.mutationBatch;
616
587
  assert(batch);
@@ -630,7 +601,7 @@ describe("kysely-uow-compiler", () => {
630
601
  uow.update("users", userId, (b) => b.set({ age: 30 }).check());
631
602
  uow.update("posts", postId, (b) => b.set({ viewCount: 100 }).check());
632
603
 
633
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
604
+ const compiler = createKyselyUOWCompiler("postgresql");
634
605
  const compiled = uow.compile(compiler);
635
606
  const [userBatch, postBatch] = compiled.mutationBatch;
636
607
 
@@ -657,7 +628,7 @@ describe("kysely-uow-compiler", () => {
657
628
  const userId = FragnoId.fromExternal("user1", 0);
658
629
  uow.update("users", userId, (b) => b.set({ age: 25 }));
659
630
 
660
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
631
+ const compiler = createKyselyUOWCompiler("postgresql");
661
632
  const compiled = uow.compile(compiler);
662
633
  const [batch] = compiled.mutationBatch;
663
634
  assert(batch);
@@ -676,7 +647,7 @@ describe("kysely-uow-compiler", () => {
676
647
  b.whereIndex("primary").join((jb) => jb.author((ab) => ab.select(["name", "email"]))),
677
648
  );
678
649
 
679
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
650
+ const compiler = createKyselyUOWCompiler("postgresql");
680
651
  const compiled = uow.compile(compiler);
681
652
 
682
653
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -700,7 +671,7 @@ describe("kysely-uow-compiler", () => {
700
671
  ),
701
672
  );
702
673
 
703
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
674
+ const compiler = createKyselyUOWCompiler("postgresql");
704
675
  const compiled = uow.compile(compiler);
705
676
 
706
677
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -719,7 +690,7 @@ describe("kysely-uow-compiler", () => {
719
690
  .join((jb) => jb.author((ab) => ab.select(["name"]).orderByIndex("idx_name", "desc"))),
720
691
  );
721
692
 
722
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
693
+ const compiler = createKyselyUOWCompiler("postgresql");
723
694
  const compiled = uow.compile(compiler);
724
695
 
725
696
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -735,7 +706,7 @@ describe("kysely-uow-compiler", () => {
735
706
  b.whereIndex("primary").join((jb) => jb.author((ab) => ab.select(["name"]).pageSize(5))),
736
707
  );
737
708
 
738
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
709
+ const compiler = createKyselyUOWCompiler("postgresql");
739
710
  const compiled = uow.compile(compiler);
740
711
 
741
712
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -766,7 +737,7 @@ describe("kysely-uow-compiler", () => {
766
737
  ),
767
738
  );
768
739
 
769
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
740
+ const compiler = createKyselyUOWCompiler("postgresql");
770
741
  const compiled = uow.compile(compiler);
771
742
 
772
743
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -785,7 +756,7 @@ describe("kysely-uow-compiler", () => {
785
756
  .join((jb) => jb.post((pb) => pb.select(["title"])).author((ab) => ab.select(["name"]))),
786
757
  );
787
758
 
788
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
759
+ const compiler = createKyselyUOWCompiler("postgresql");
789
760
  const compiled = uow.compile(compiler);
790
761
 
791
762
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -803,7 +774,7 @@ describe("kysely-uow-compiler", () => {
803
774
  b.whereIndex("primary").join((jb) => jb.inviter((ib) => ib.select(["name", "email"]))),
804
775
  );
805
776
 
806
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
777
+ const compiler = createKyselyUOWCompiler("postgresql");
807
778
  const compiled = uow.compile(compiler);
808
779
 
809
780
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -833,7 +804,7 @@ describe("kysely-uow-compiler", () => {
833
804
  ),
834
805
  );
835
806
 
836
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
807
+ const compiler = createKyselyUOWCompiler("postgresql");
837
808
  const compiled = uow.compile(compiler);
838
809
 
839
810
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -856,7 +827,7 @@ describe("kysely-uow-compiler", () => {
856
827
  .join((jb) => jb.post((pb) => pb.select(["title"])).tag((tb) => tb.select(["name"]))),
857
828
  );
858
829
 
859
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
830
+ const compiler = createKyselyUOWCompiler("postgresql");
860
831
  const compiled = uow.compile(compiler);
861
832
 
862
833
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -881,7 +852,7 @@ describe("kysely-uow-compiler", () => {
881
852
  ),
882
853
  );
883
854
 
884
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
855
+ const compiler = createKyselyUOWCompiler("postgresql");
885
856
  const compiled = uow.compile(compiler);
886
857
 
887
858
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -897,7 +868,7 @@ describe("kysely-uow-compiler", () => {
897
868
  it("should handle UOW with no operations", () => {
898
869
  const uow = createTestUOW();
899
870
 
900
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
871
+ const compiler = createKyselyUOWCompiler("postgresql");
901
872
  const compiled = uow.compile(compiler);
902
873
 
903
874
  expect(compiled.retrievalBatch).toHaveLength(0);
@@ -908,7 +879,7 @@ describe("kysely-uow-compiler", () => {
908
879
  const uow = createTestUOW();
909
880
  uow.find("users");
910
881
 
911
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
882
+ const compiler = createKyselyUOWCompiler("postgresql");
912
883
  const compiled = uow.compile(compiler);
913
884
 
914
885
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -922,7 +893,7 @@ describe("kysely-uow-compiler", () => {
922
893
  email: "test@example.com",
923
894
  });
924
895
 
925
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
896
+ const compiler = createKyselyUOWCompiler("postgresql");
926
897
  const compiled = uow.compile(compiler);
927
898
 
928
899
  expect(compiled.retrievalBatch).toHaveLength(0);
@@ -949,7 +920,7 @@ describe("kysely-uow-compiler", () => {
949
920
  content: "This is my first post",
950
921
  });
951
922
 
952
- const compiler = createKyselyUOWCompiler(testSchema, pool, "postgresql");
923
+ const compiler = createKyselyUOWCompiler("postgresql");
953
924
  const compiled = uow.compile(compiler);
954
925
 
955
926
  // Should have no retrieval operations
@@ -994,5 +965,22 @@ describe("kysely-uow-compiler", () => {
994
965
  internalId: undefined,
995
966
  });
996
967
  });
968
+
969
+ it("should compile check operation", () => {
970
+ const uow = createTestUOW();
971
+ const userId = FragnoId.fromExternal("user123", 5);
972
+ uow.check("users", userId);
973
+
974
+ const compiler = createKyselyUOWCompiler("postgresql");
975
+ const compiled = uow.compile(compiler);
976
+ const [batch] = compiled.mutationBatch;
977
+ assert(batch);
978
+ expect(batch.expectedAffectedRows).toBe(null);
979
+ expect(batch.expectedReturnedRows).toBe(1);
980
+ expect(batch.query.sql).toMatchInlineSnapshot(
981
+ `"select 1 as "exists" from "users" where ("users"."id" = $1 and "users"."_version" = $2) limit $3"`,
982
+ );
983
+ expect(batch.query.parameters).toMatchObject(["user123", 5, 1]);
984
+ });
997
985
  });
998
986
  });
@@ -1,4 +1,4 @@
1
- import type { CompiledQuery, Kysely } from "kysely";
1
+ import { type CompiledQuery, sql } from "kysely";
2
2
  import type { AnyColumn, AnySchema, FragnoId } from "../../schema/create";
3
3
  import type {
4
4
  CompiledMutation,
@@ -7,41 +7,69 @@ import type {
7
7
  UOWCompiler,
8
8
  } from "../../query/unit-of-work";
9
9
  import { createKyselyQueryCompiler } from "./kysely-query-compiler";
10
- import { createKyselyQueryBuilder } from "./kysely-query-builder";
10
+ import { createKyselyQueryBuilder, buildWhere } from "./kysely-query-builder";
11
11
  import { buildCondition, type Condition } from "../../query/condition-builder";
12
12
  import { decodeCursor, serializeCursorValues } from "../../query/cursor";
13
13
  import type { AnySelectClause } from "../../query/query";
14
- import type { TableNameMapper } from "./kysely-shared";
15
- import type { ConnectionPool } from "../../shared/connection-pool";
14
+ import { type TableNameMapper, createKysely, createTableNameMapper } from "./kysely-shared";
16
15
  import type { SQLProvider } from "../../shared/providers";
17
16
 
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- type KyselyAny = Kysely<any>;
20
-
21
17
  /**
22
18
  * Create a Kysely-specific Unit of Work compiler
23
19
  *
24
20
  * This compiler translates UOW operations into Kysely CompiledQuery objects
25
21
  * that can be executed as a batch/transaction.
26
22
  *
27
- * @param schema - The database schema
28
23
  * @param pool - Connection pool for acquiring database connections
29
24
  * @param provider - SQL provider (postgresql, mysql, sqlite, etc.)
30
- * @param mapper - Optional table name mapper for namespace prefixing
25
+ * @param mapper - Optional table name mapper for namespace prefixing (fallback for operations without explicit namespace)
31
26
  * @returns A UOWCompiler instance for Kysely
32
27
  */
33
- export function createKyselyUOWCompiler<TSchema extends AnySchema>(
34
- schema: TSchema,
35
- pool: ConnectionPool<KyselyAny>,
28
+ export function createKyselyUOWCompiler(
36
29
  provider: SQLProvider,
37
30
  mapper?: TableNameMapper,
38
- ): UOWCompiler<TSchema, CompiledQuery> {
39
- const queryCompiler = createKyselyQueryCompiler(schema, pool, provider, mapper);
31
+ ): UOWCompiler<CompiledQuery> {
40
32
  // Get kysely instance for query building (compilation doesn't execute, just builds SQL)
41
- const kysely = pool.getDatabaseSync();
42
- const queryBuilder = createKyselyQueryBuilder(kysely, provider, mapper);
33
+ const kysely = createKysely(provider);
34
+
35
+ /**
36
+ * Get the mapper for a specific operation
37
+ * Uses operation's namespace if provided, otherwise falls back to the default mapper
38
+ */
39
+ function getMapperForOperation(namespace: string | undefined): TableNameMapper | undefined {
40
+ if (namespace) {
41
+ return createTableNameMapper(namespace);
42
+ }
43
+ return mapper;
44
+ }
43
45
 
44
- function toTable(name: unknown) {
46
+ // Cache query compilers and builders by namespace for performance
47
+ const compilerCache = new Map<string | undefined, ReturnType<typeof createKyselyQueryCompiler>>();
48
+ const builderCache = new Map<string | undefined, ReturnType<typeof createKyselyQueryBuilder>>();
49
+
50
+ function getQueryCompiler(schema: AnySchema, namespace: string | undefined) {
51
+ const cacheKey = namespace;
52
+ let compiler = compilerCache.get(cacheKey);
53
+ if (!compiler) {
54
+ const opMapper = getMapperForOperation(namespace);
55
+ compiler = createKyselyQueryCompiler(schema, provider, opMapper);
56
+ compilerCache.set(cacheKey, compiler);
57
+ }
58
+ return compiler;
59
+ }
60
+
61
+ function getQueryBuilder(namespace: string | undefined) {
62
+ const cacheKey = namespace;
63
+ let builder = builderCache.get(cacheKey);
64
+ if (!builder) {
65
+ const opMapper = getMapperForOperation(namespace);
66
+ builder = createKyselyQueryBuilder(kysely, provider, opMapper);
67
+ builderCache.set(cacheKey, builder);
68
+ }
69
+ return builder;
70
+ }
71
+
72
+ function toTable(schema: AnySchema, name: unknown) {
45
73
  const table = schema.tables[name as string];
46
74
  if (!table) {
47
75
  throw new Error(`Invalid table name ${name}.`);
@@ -50,7 +78,8 @@ export function createKyselyUOWCompiler<TSchema extends AnySchema>(
50
78
  }
51
79
 
52
80
  return {
53
- compileRetrievalOperation(op: RetrievalOperation<TSchema>): CompiledQuery | null {
81
+ compileRetrievalOperation(op: RetrievalOperation<AnySchema>): CompiledQuery | null {
82
+ const queryCompiler = getQueryCompiler(op.schema, op.namespace);
54
83
  switch (op.type) {
55
84
  case "count": {
56
85
  return queryCompiler.count(op.table.name, {
@@ -159,14 +188,19 @@ export function createKyselyUOWCompiler<TSchema extends AnySchema>(
159
188
  }
160
189
  }
161
190
 
162
- // When we have joins or need to bypass buildFindOptions, use queryBuilder directly
191
+ // For cursor pagination, fetch one extra item to determine if there's a next page
192
+ // Only apply this when using the high-level findWithCursor() API (op.withCursor === true)
193
+ const effectiveLimit = pageSize && op.withCursor ? pageSize + 1 : pageSize;
194
+
195
+ // When we have joins or need to bypass buildFindOptions, use operation-specific queryBuilder
163
196
  if (join && join.length > 0) {
197
+ const queryBuilder = getQueryBuilder(op.namespace);
164
198
  return queryBuilder.findMany(op.table, {
165
199
  // Safe cast: select from UOW matches SimplifyFindOptions requirement
166
200
  select: (findManyOptions.select ?? true) as AnySelectClause,
167
201
  where: combinedWhere,
168
202
  orderBy,
169
- limit: pageSize,
203
+ limit: effectiveLimit,
170
204
  join,
171
205
  });
172
206
  }
@@ -175,25 +209,27 @@ export function createKyselyUOWCompiler<TSchema extends AnySchema>(
175
209
  ...findManyOptions,
176
210
  where: combinedWhere ? () => combinedWhere! : undefined,
177
211
  orderBy: orderBy?.map(([col, dir]) => [col.ormName, dir]),
178
- limit: pageSize,
212
+ limit: effectiveLimit,
179
213
  });
180
214
  }
181
215
  }
182
216
  },
183
217
 
184
218
  compileMutationOperation(
185
- op: MutationOperation<TSchema>,
219
+ op: MutationOperation<AnySchema>,
186
220
  ): CompiledMutation<CompiledQuery> | null {
221
+ const queryCompiler = getQueryCompiler(op.schema, op.namespace);
187
222
  switch (op.type) {
188
223
  case "create":
189
224
  // queryCompiler.create() calls encodeValues() which handles runtime defaults
190
225
  return {
191
226
  query: queryCompiler.create(op.table, op.values),
192
227
  expectedAffectedRows: null, // creates don't need affected row checks
228
+ expectedReturnedRows: null,
193
229
  };
194
230
 
195
231
  case "update": {
196
- const table = toTable(op.table);
232
+ const table = toTable(op.schema, op.table);
197
233
  const idColumn = table.getIdColumn();
198
234
  const versionColumn = table.getVersionColumn();
199
235
 
@@ -221,12 +257,13 @@ export function createKyselyUOWCompiler<TSchema extends AnySchema>(
221
257
  ? {
222
258
  query,
223
259
  expectedAffectedRows: op.checkVersion ? 1 : null,
260
+ expectedReturnedRows: null,
224
261
  }
225
262
  : null;
226
263
  }
227
264
 
228
265
  case "delete": {
229
- const table = toTable(op.table);
266
+ const table = toTable(op.schema, op.table);
230
267
  const idColumn = table.getIdColumn();
231
268
  const versionColumn = table.getVersionColumn();
232
269
 
@@ -254,9 +291,40 @@ export function createKyselyUOWCompiler<TSchema extends AnySchema>(
254
291
  ? {
255
292
  query,
256
293
  expectedAffectedRows: op.checkVersion ? 1 : null,
294
+ expectedReturnedRows: null,
257
295
  }
258
296
  : null;
259
297
  }
298
+
299
+ case "check": {
300
+ const table = toTable(op.schema, op.table);
301
+ const idColumn = table.getIdColumn();
302
+ const versionColumn = table.getVersionColumn();
303
+ const mapper = getMapperForOperation(op.namespace);
304
+ const tableName = mapper ? mapper.toPhysical(op.table) : op.table;
305
+
306
+ const externalId = op.id.externalId;
307
+ const version = op.id.version;
308
+
309
+ // Build a SELECT 1 query to check if the row exists with the correct version
310
+ const condition = buildCondition(table.columns, (eb) =>
311
+ eb.and(eb(idColumn.ormName, "=", externalId), eb(versionColumn.ormName, "=", version)),
312
+ );
313
+
314
+ let query = kysely.selectFrom(tableName).select(sql<number>`1`.as("exists"));
315
+
316
+ if (typeof condition === "boolean") {
317
+ throw new Error("Condition is a boolean, but should be a condition object.");
318
+ }
319
+
320
+ query = query.where((eb) => buildWhere(condition, eb, provider, mapper, table)).limit(1);
321
+
322
+ return {
323
+ query: query.compile(),
324
+ expectedAffectedRows: null,
325
+ expectedReturnedRows: 1, // Check that exactly 1 row was returned
326
+ };
327
+ }
260
328
  }
261
329
  },
262
330
  };