appflare 0.2.24 → 0.2.26

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 (138) hide show
  1. package/Documentation.md +758 -758
  2. package/cli/commands/index.ts +238 -238
  3. package/cli/generate.ts +178 -178
  4. package/cli/index.ts +120 -120
  5. package/cli/load-config.ts +184 -184
  6. package/cli/schema-compiler.ts +1183 -1183
  7. package/cli/templates/auth/README.md +156 -156
  8. package/cli/templates/auth/config.ts +61 -61
  9. package/cli/templates/auth/route-config.ts +1 -1
  10. package/cli/templates/auth/route-handler.ts +1 -1
  11. package/cli/templates/auth/route-request-utils.ts +5 -5
  12. package/cli/templates/auth/route.config.ts +18 -18
  13. package/cli/templates/auth/route.handler.ts +18 -18
  14. package/cli/templates/auth/route.request-utils.ts +55 -55
  15. package/cli/templates/auth/route.ts +14 -14
  16. package/cli/templates/core/README.md +266 -266
  17. package/cli/templates/core/app-creation.ts +19 -19
  18. package/cli/templates/core/client/appflare.ts +112 -112
  19. package/cli/templates/core/client/handlers/index.ts +748 -749
  20. package/cli/templates/core/client/handlers.ts +1 -1
  21. package/cli/templates/core/client/index.ts +7 -7
  22. package/cli/templates/core/client/storage.ts +180 -180
  23. package/cli/templates/core/client/types.ts +184 -184
  24. package/cli/templates/core/client-modules/appflare.ts +1 -1
  25. package/cli/templates/core/client-modules/handlers.ts +1 -1
  26. package/cli/templates/core/client-modules/index.ts +1 -1
  27. package/cli/templates/core/client-modules/storage.ts +1 -1
  28. package/cli/templates/core/client-modules/types.ts +1 -1
  29. package/cli/templates/core/client.artifacts.ts +39 -39
  30. package/cli/templates/core/client.ts +4 -4
  31. package/cli/templates/core/drizzle.ts +15 -15
  32. package/cli/templates/core/export.ts +14 -14
  33. package/cli/templates/core/handlers.route.ts +24 -24
  34. package/cli/templates/core/handlers.ts +1 -1
  35. package/cli/templates/core/imports.ts +9 -9
  36. package/cli/templates/core/server.ts +38 -38
  37. package/cli/templates/core/types.ts +6 -6
  38. package/cli/templates/core/wrangler.ts +109 -109
  39. package/cli/templates/dashboard/builders/functions/index.ts +17 -17
  40. package/cli/templates/dashboard/builders/functions/render-page/header.ts +20 -20
  41. package/cli/templates/dashboard/builders/functions/render-page/index.ts +33 -33
  42. package/cli/templates/dashboard/builders/functions/render-page/request-panel.ts +171 -171
  43. package/cli/templates/dashboard/builders/functions/render-page/result-panel.ts +85 -85
  44. package/cli/templates/dashboard/builders/functions/render-page/scripts.ts +554 -554
  45. package/cli/templates/dashboard/builders/navigation.ts +122 -122
  46. package/cli/templates/dashboard/builders/storage/index.ts +13 -13
  47. package/cli/templates/dashboard/builders/storage/routes/create-directory-route.ts +29 -29
  48. package/cli/templates/dashboard/builders/storage/routes/delete-route.ts +18 -18
  49. package/cli/templates/dashboard/builders/storage/routes/download-route.ts +23 -23
  50. package/cli/templates/dashboard/builders/storage/routes/index.ts +22 -22
  51. package/cli/templates/dashboard/builders/storage/routes/list-route.ts +25 -25
  52. package/cli/templates/dashboard/builders/storage/routes/preview-route.ts +21 -21
  53. package/cli/templates/dashboard/builders/storage/routes/upload-route.ts +21 -21
  54. package/cli/templates/dashboard/builders/storage/runtime/helpers.ts +72 -72
  55. package/cli/templates/dashboard/builders/storage/runtime/storage-page.ts +130 -130
  56. package/cli/templates/dashboard/builders/table-routes/common/drawer-panel.ts +27 -27
  57. package/cli/templates/dashboard/builders/table-routes/common/pagination.ts +30 -30
  58. package/cli/templates/dashboard/builders/table-routes/common/search-bar.ts +23 -23
  59. package/cli/templates/dashboard/builders/table-routes/fragments.ts +217 -217
  60. package/cli/templates/dashboard/builders/table-routes/helpers.ts +45 -45
  61. package/cli/templates/dashboard/builders/table-routes/index.ts +8 -8
  62. package/cli/templates/dashboard/builders/table-routes/table/actions-cell.ts +71 -71
  63. package/cli/templates/dashboard/builders/table-routes/table/get-route.ts +291 -291
  64. package/cli/templates/dashboard/builders/table-routes/table/index.ts +80 -80
  65. package/cli/templates/dashboard/builders/table-routes/table/post-routes.ts +163 -163
  66. package/cli/templates/dashboard/builders/table-routes/table-route.ts +7 -7
  67. package/cli/templates/dashboard/builders/table-routes/users/get-route.ts +69 -69
  68. package/cli/templates/dashboard/builders/table-routes/users/html/modals.ts +57 -57
  69. package/cli/templates/dashboard/builders/table-routes/users/html/page.ts +27 -27
  70. package/cli/templates/dashboard/builders/table-routes/users/html/table.ts +128 -128
  71. package/cli/templates/dashboard/builders/table-routes/users/index.ts +32 -32
  72. package/cli/templates/dashboard/builders/table-routes/users/post-routes.ts +150 -150
  73. package/cli/templates/dashboard/builders/table-routes/users/redirect.ts +14 -14
  74. package/cli/templates/dashboard/builders/table-routes/users-route.ts +10 -10
  75. package/cli/templates/dashboard/components/dashboard-home.ts +23 -23
  76. package/cli/templates/dashboard/components/layout.ts +388 -388
  77. package/cli/templates/dashboard/components/login-page.ts +65 -65
  78. package/cli/templates/dashboard/index.ts +61 -61
  79. package/cli/templates/dashboard/types.ts +9 -9
  80. package/cli/templates/handlers/README.md +353 -353
  81. package/cli/templates/handlers/auth.ts +37 -37
  82. package/cli/templates/handlers/execution.ts +42 -42
  83. package/cli/templates/handlers/generators/context/context-creation.ts +101 -101
  84. package/cli/templates/handlers/generators/context/error-helpers.ts +11 -11
  85. package/cli/templates/handlers/generators/context/scheduler.ts +24 -24
  86. package/cli/templates/handlers/generators/context/storage-api.ts +134 -112
  87. package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -59
  88. package/cli/templates/handlers/generators/context/types.ts +18 -18
  89. package/cli/templates/handlers/generators/context.ts +43 -43
  90. package/cli/templates/handlers/generators/execution.ts +15 -15
  91. package/cli/templates/handlers/generators/handlers.ts +13 -13
  92. package/cli/templates/handlers/generators/registration/modules/cron.ts +26 -26
  93. package/cli/templates/handlers/generators/registration/modules/realtime/auth.ts +75 -75
  94. package/cli/templates/handlers/generators/registration/modules/realtime/durable-object.ts +144 -144
  95. package/cli/templates/handlers/generators/registration/modules/realtime/index.ts +14 -14
  96. package/cli/templates/handlers/generators/registration/modules/realtime/publisher.ts +102 -102
  97. package/cli/templates/handlers/generators/registration/modules/realtime/routes.ts +164 -164
  98. package/cli/templates/handlers/generators/registration/modules/realtime/types.ts +30 -30
  99. package/cli/templates/handlers/generators/registration/modules/realtime/utils.ts +516 -516
  100. package/cli/templates/handlers/generators/registration/modules/scheduler.ts +56 -56
  101. package/cli/templates/handlers/generators/registration/modules/storage.ts +196 -194
  102. package/cli/templates/handlers/generators/registration/sections.ts +210 -210
  103. package/cli/templates/handlers/generators/types/context.ts +68 -66
  104. package/cli/templates/handlers/generators/types/core.ts +106 -106
  105. package/cli/templates/handlers/generators/types/operations.ts +135 -135
  106. package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +259 -259
  107. package/cli/templates/handlers/generators/types/query-definitions/query-api-types.ts +135 -135
  108. package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +1031 -1031
  109. package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +246 -246
  110. package/cli/templates/handlers/generators/types/query-definitions.ts +13 -13
  111. package/cli/templates/handlers/generators/types/query-runtime/handled-error.ts +13 -13
  112. package/cli/templates/handlers/generators/types/query-runtime/runtime-aggregate-and-footer.ts +174 -174
  113. package/cli/templates/handlers/generators/types/query-runtime/runtime-read.ts +121 -121
  114. package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -45
  115. package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +676 -676
  116. package/cli/templates/handlers/generators/types/query-runtime.ts +15 -15
  117. package/cli/templates/handlers/index.ts +43 -43
  118. package/cli/templates/handlers/operations.ts +116 -116
  119. package/cli/templates/handlers/registration.ts +91 -83
  120. package/cli/templates/handlers/types.ts +15 -15
  121. package/cli/templates/handlers/utils.ts +48 -48
  122. package/cli/types.ts +110 -110
  123. package/cli/utils/handler-discovery.ts +466 -466
  124. package/cli/utils/json-utils.ts +24 -24
  125. package/cli/utils/path-utils.ts +19 -19
  126. package/cli/utils/schema-discovery.ts +399 -399
  127. package/dist/cli/index.js +61 -28
  128. package/dist/cli/index.mjs +61 -28
  129. package/index.ts +18 -18
  130. package/package.json +58 -58
  131. package/react/index.ts +5 -5
  132. package/react/use-infinite-query.ts +252 -252
  133. package/react/use-mutation.ts +89 -89
  134. package/react/use-query.ts +207 -207
  135. package/schema.ts +415 -415
  136. package/test-better-auth-hash.ts +2 -2
  137. package/tsconfig.json +6 -6
  138. package/tsup.config.ts +82 -82
@@ -1,676 +1,676 @@
1
- export function generateQueryRuntimeWriteSection(): string {
2
- return ` insert: async <TArgs extends QueryInsertArgs<TableName>>(args: TArgs) => {
3
- const transaction = ($db as any).transaction;
4
-
5
- const valuesArray = Array.isArray(args.values)
6
- ? args.values
7
- : [args.values];
8
- const baseValuesArray: Array<Record<string, unknown>> = [];
9
- const postInsertRelations: Array<
10
- Array<{
11
- relationName: string;
12
- relation: RuntimeRelation;
13
- value: unknown;
14
- }>
15
- > = [];
16
-
17
- const toRelationItems = (
18
- relation: RuntimeRelation,
19
- relationName: string,
20
- value: unknown,
21
- ): unknown[] => {
22
- if (value === undefined || value === null) {
23
- return [];
24
- }
25
-
26
- if (relation.kind === "one") {
27
- if (Array.isArray(value)) {
28
- throw new Error(
29
- "Relation '" +
30
- tableName +
31
- "." +
32
- relationName +
33
- "' expects a single value.",
34
- );
35
- }
36
- return [value];
37
- }
38
-
39
- return Array.isArray(value) ? value : [value];
40
- };
41
-
42
- const resolveRelationIdentifier = async (
43
- tx: any,
44
- relation: RuntimeRelation,
45
- relationName: string,
46
- input: unknown,
47
- ): Promise<unknown> => {
48
- if (
49
- typeof input === "string" ||
50
- typeof input === "number" ||
51
- typeof input === "bigint"
52
- ) {
53
- return input;
54
- }
55
-
56
- if (!isRecord(input)) {
57
- throw new Error(
58
- "Relation '" +
59
- tableName +
60
- "." +
61
- relationName +
62
- "' expects an id or object payload.",
63
- );
64
- }
65
-
66
- const referenceField =
67
- relation.kind === "manyToMany"
68
- ? (relation.targetReferenceField ?? "id")
69
- : (relation.referenceField ?? "id");
70
- const existingId = input[referenceField];
71
- if (existingId !== undefined && existingId !== null) {
72
- return existingId;
73
- }
74
-
75
- const targetTable = (mergedSchema as Record<string, unknown>)[
76
- relation.targetTable
77
- ];
78
- if (!targetTable) {
79
- throw new Error(
80
- "Unknown target table '" +
81
- relation.targetTable +
82
- "' for relation '" +
83
- tableName +
84
- "." +
85
- relationName +
86
- "'.",
87
- );
88
- }
89
-
90
- let createQuery: any = tx.insert(targetTable as any).values(input as any);
91
- if (typeof createQuery.returning === "function") {
92
- createQuery = createQuery.returning();
93
- }
94
- const createdRows = (await createQuery) as Array<Record<string, unknown>>;
95
- const created = createdRows[0];
96
- if (!created) {
97
- throw new Error(
98
- "Failed to create relation target for '" +
99
- tableName +
100
- "." +
101
- relationName +
102
- "'.",
103
- );
104
- }
105
-
106
- const createdId = created[referenceField];
107
- if (createdId === undefined || createdId === null) {
108
- throw new Error(
109
- "Created relation target for '" +
110
- tableName +
111
- "." +
112
- relationName +
113
- "' is missing '" +
114
- referenceField +
115
- "'.",
116
- );
117
- }
118
-
119
- return createdId;
120
- };
121
-
122
- const getRelationTargetTable = (
123
- relation: RuntimeRelation,
124
- relationName: string,
125
- ): any => {
126
- const targetTable = (mergedSchema as Record<string, unknown>)[
127
- relation.targetTable
128
- ];
129
- if (!targetTable) {
130
- throw new Error(
131
- "Unknown target table '" +
132
- relation.targetTable +
133
- "' for relation '" +
134
- tableName +
135
- "." +
136
- relationName +
137
- "'.",
138
- );
139
- }
140
- return targetTable;
141
- };
142
-
143
- const getRelationReferenceField = (relation: RuntimeRelation): string => {
144
- if (relation.kind === "manyToMany") {
145
- return relation.targetReferenceField ?? "id";
146
- }
147
- return relation.referenceField ?? "id";
148
- };
149
-
150
- const fetchRelatedRowByIdentifier = async (
151
- tx: any,
152
- relation: RuntimeRelation,
153
- relationName: string,
154
- identifier: unknown,
155
- ): Promise<Record<string, unknown> | null> => {
156
- if (identifier === undefined || identifier === null) {
157
- return null;
158
- }
159
-
160
- const targetTable = getRelationTargetTable(relation, relationName);
161
- const referenceField = getRelationReferenceField(relation);
162
- const referenceColumn = (targetTable as Record<string, unknown>)[
163
- referenceField
164
- ];
165
- if (!referenceColumn) {
166
- throw new Error(
167
- "Target table '" +
168
- relation.targetTable +
169
- "' is missing column '" +
170
- referenceField +
171
- "' required by relation '" +
172
- tableName +
173
- "." +
174
- relationName +
175
- "'.",
176
- );
177
- }
178
-
179
- let query: any = tx
180
- .select()
181
- .from(targetTable as any)
182
- .where(eq(referenceColumn as any, identifier as any));
183
- if (typeof query.limit === "function") {
184
- query = query.limit(1);
185
- }
186
- const rows = (await query) as Array<Record<string, unknown>>;
187
- return rows[0] ?? null;
188
- };
189
-
190
- for (const inputValue of valuesArray) {
191
- const scalarValues: Record<string, unknown> = {};
192
- const relationInputs: Array<{
193
- relationName: string;
194
- relation: RuntimeRelation;
195
- value: unknown;
196
- }> = [];
197
-
198
- const valueRecord = (inputValue ?? {}) as Record<string, unknown>;
199
- for (const [fieldName, fieldValue] of Object.entries(valueRecord)) {
200
- const runtimeRelation = getRuntimeRelation(tableName, fieldName);
201
- if (runtimeRelation) {
202
- relationInputs.push({
203
- relationName: fieldName,
204
- relation: runtimeRelation,
205
- value: fieldValue,
206
- });
207
- continue;
208
- }
209
- scalarValues[fieldName] = fieldValue;
210
- }
211
-
212
- baseValuesArray.push(scalarValues);
213
- postInsertRelations.push(relationInputs);
214
- }
215
-
216
- const executeInsertGraph = async (
217
- tx: any,
218
- ): Promise<Array<QueryInsertResultRow<TableName, TArgs>>> => {
219
- for (let index = 0; index < baseValuesArray.length; index += 1) {
220
- const relationInputs = postInsertRelations[index] ?? [];
221
- for (const relationInput of relationInputs) {
222
- if (relationInput.relation.kind !== "one") {
223
- continue;
224
- }
225
-
226
- const sourceField = relationInput.relation.sourceField;
227
- if (!sourceField) {
228
- throw new Error(
229
- "Relation '" +
230
- tableName +
231
- "." +
232
- relationInput.relationName +
233
- "' is missing sourceField metadata.",
234
- );
235
- }
236
-
237
- const oneItems = toRelationItems(
238
- relationInput.relation,
239
- relationInput.relationName,
240
- relationInput.value,
241
- );
242
- if (oneItems.length === 0) {
243
- continue;
244
- }
245
-
246
- const relationId = await resolveRelationIdentifier(
247
- tx,
248
- relationInput.relation,
249
- relationInput.relationName,
250
- oneItems[0],
251
- );
252
- const existingValue = baseValuesArray[index][sourceField];
253
- if (
254
- existingValue !== undefined &&
255
- existingValue !== relationId
256
- ) {
257
- throw new Error(
258
- "Insert payload for '" +
259
- tableName +
260
- "' has conflicting values for '" +
261
- sourceField +
262
- "'.",
263
- );
264
- }
265
- baseValuesArray[index][sourceField] = relationId;
266
- }
267
- }
268
-
269
- let insertQuery: any = tx.insert(table as any).values(baseValuesArray as any);
270
- if (typeof insertQuery.returning === "function") {
271
- insertQuery = insertQuery.returning();
272
- }
273
- const insertedRows = (await insertQuery) as Array<TableModel<TableName>>;
274
- const hydratedRows: Array<Record<string, unknown>> = [];
275
-
276
- for (let index = 0; index < insertedRows.length; index += 1) {
277
- const parentRow = insertedRows[index] as unknown as Record<string, unknown>;
278
- const hydratedRow: Record<string, unknown> = {
279
- ...parentRow,
280
- };
281
- const relationInputs = postInsertRelations[index] ?? [];
282
-
283
- for (const relationInput of relationInputs) {
284
- if (relationInput.relation.kind === "one") {
285
- const sourceField = relationInput.relation.sourceField;
286
- if (!sourceField) {
287
- throw new Error(
288
- "Relation '" +
289
- tableName +
290
- "." +
291
- relationInput.relationName +
292
- "' is missing sourceField metadata.",
293
- );
294
- }
295
-
296
- const relationItems = toRelationItems(
297
- relationInput.relation,
298
- relationInput.relationName,
299
- relationInput.value,
300
- );
301
- if (relationItems.length === 0) {
302
- hydratedRow[relationInput.relationName] = null;
303
- continue;
304
- }
305
-
306
- const sourceIdentifier = parentRow[sourceField];
307
- hydratedRow[relationInput.relationName] =
308
- (await fetchRelatedRowByIdentifier(
309
- tx,
310
- relationInput.relation,
311
- relationInput.relationName,
312
- sourceIdentifier,
313
- )) ?? null;
314
- continue;
315
- }
316
-
317
- const sourceReferenceField = relationInput.relation.referenceField ?? "id";
318
- const parentReferenceValue = parentRow[sourceReferenceField];
319
- if (
320
- parentReferenceValue === undefined ||
321
- parentReferenceValue === null
322
- ) {
323
- throw new Error(
324
- "Inserted row for '" +
325
- tableName +
326
- "' is missing '" +
327
- sourceReferenceField +
328
- "' required by relation '" +
329
- relationInput.relationName +
330
- "'.",
331
- );
332
- }
333
-
334
- const relatedRows: Array<Record<string, unknown>> = [];
335
- hydratedRow[relationInput.relationName] = relatedRows;
336
-
337
- const relationItems = toRelationItems(
338
- relationInput.relation,
339
- relationInput.relationName,
340
- relationInput.value,
341
- );
342
- if (relationItems.length === 0) {
343
- continue;
344
- }
345
-
346
- if (relationInput.relation.kind === "many") {
347
- const targetTable = getRelationTargetTable(
348
- relationInput.relation,
349
- relationInput.relationName,
350
- );
351
-
352
- const sourceField = relationInput.relation.sourceField;
353
- const targetReferenceField =
354
- relationInput.relation.referenceField ?? "id";
355
- const targetReferenceColumn =
356
- (targetTable as Record<string, unknown>)[targetReferenceField];
357
- if (!sourceField) {
358
- throw new Error(
359
- "Relation '" +
360
- tableName +
361
- "." +
362
- relationInput.relationName +
363
- "' is missing sourceField metadata.",
364
- );
365
- }
366
- if (!targetReferenceColumn) {
367
- throw new Error(
368
- "Target table '" +
369
- relationInput.relation.targetTable +
370
- "' is missing column '" +
371
- targetReferenceField +
372
- "' required by relation '" +
373
- tableName +
374
- "." +
375
- relationInput.relationName +
376
- "'.",
377
- );
378
- }
379
-
380
- for (const relationItem of relationItems) {
381
- if (
382
- typeof relationItem === "string" ||
383
- typeof relationItem === "number" ||
384
- typeof relationItem === "bigint"
385
- ) {
386
- await tx
387
- .update(targetTable as any)
388
- .set({ [sourceField]: parentReferenceValue } as any)
389
- .where(
390
- eq(targetReferenceColumn as any, relationItem as any),
391
- );
392
- const linkedRow = await fetchRelatedRowByIdentifier(
393
- tx,
394
- relationInput.relation,
395
- relationInput.relationName,
396
- relationItem,
397
- );
398
- if (linkedRow) {
399
- relatedRows.push(linkedRow);
400
- }
401
- continue;
402
- }
403
-
404
- if (!isRecord(relationItem)) {
405
- throw new Error(
406
- "Relation '" +
407
- tableName +
408
- "." +
409
- relationInput.relationName +
410
- "' expects id or object payloads.",
411
- );
412
- }
413
-
414
- const existingId = relationItem[targetReferenceField];
415
- const payload = {
416
- ...relationItem,
417
- [sourceField]: parentReferenceValue,
418
- };
419
-
420
- if (existingId !== undefined && existingId !== null) {
421
- const setPayload: Record<string, unknown> = {
422
- ...payload,
423
- };
424
- delete setPayload[targetReferenceField];
425
- await tx
426
- .update(targetTable as any)
427
- .set(setPayload as any)
428
- .where(
429
- eq(targetReferenceColumn as any, existingId as any),
430
- );
431
- const linkedRow = await fetchRelatedRowByIdentifier(
432
- tx,
433
- relationInput.relation,
434
- relationInput.relationName,
435
- existingId,
436
- );
437
- if (linkedRow) {
438
- relatedRows.push(linkedRow);
439
- }
440
- continue;
441
- }
442
-
443
- let createQuery: any = tx
444
- .insert(targetTable as any)
445
- .values(payload as any);
446
- if (typeof createQuery.returning === "function") {
447
- createQuery = createQuery.returning();
448
- }
449
- const createdRows = (await createQuery) as Array<
450
- Record<string, unknown>
451
- >;
452
- const createdRow = createdRows[0];
453
- if (createdRow) {
454
- relatedRows.push(createdRow);
455
- }
456
- }
457
- continue;
458
- }
459
-
460
- const junctionTable = (mergedSchema as Record<string, unknown>)[
461
- relationInput.relation.junctionTable
462
- ];
463
- if (!junctionTable) {
464
- throw new Error(
465
- "Unknown junction table '" +
466
- relationInput.relation.junctionTable +
467
- "' for relation '" +
468
- tableName +
469
- "." +
470
- relationInput.relationName +
471
- "'.",
472
- );
473
- }
474
-
475
- const sourceField = relationInput.relation.sourceField;
476
- const targetField = relationInput.relation.targetField;
477
- if (!sourceField || !targetField) {
478
- throw new Error(
479
- "Relation '" +
480
- tableName +
481
- "." +
482
- relationInput.relationName +
483
- "' is missing junction metadata fields.",
484
- );
485
- }
486
-
487
- const linkValues: Array<Record<string, unknown>> = [];
488
- const targetIdentifiers: unknown[] = [];
489
- for (const relationItem of relationItems) {
490
- const targetId = await resolveRelationIdentifier(
491
- tx,
492
- relationInput.relation,
493
- relationInput.relationName,
494
- relationItem,
495
- );
496
- targetIdentifiers.push(targetId);
497
-
498
- linkValues.push({
499
- [sourceField]: parentReferenceValue,
500
- [targetField]: targetId,
501
- });
502
- }
503
-
504
- if (linkValues.length > 0) {
505
- await tx.insert(junctionTable as any).values(linkValues as any);
506
- }
507
-
508
- for (const targetId of targetIdentifiers) {
509
- const linkedRow = await fetchRelatedRowByIdentifier(
510
- tx,
511
- relationInput.relation,
512
- relationInput.relationName,
513
- targetId,
514
- );
515
- if (linkedRow) {
516
- relatedRows.push(linkedRow);
517
- }
518
- }
519
- }
520
-
521
- hydratedRows.push(hydratedRow);
522
- }
523
-
524
- return hydratedRows as Array<QueryInsertResultRow<TableName, TArgs>>;
525
- };
526
-
527
- let rows: Array<QueryInsertResultRow<TableName, TArgs>>;
528
- if (typeof transaction === "function") {
529
- try {
530
- rows = await transaction.call($db, (tx: any) =>
531
- executeInsertGraph(tx),
532
- );
533
- } catch (error) {
534
- const message =
535
- error instanceof Error ? error.message : String(error);
536
- const lowered = message.toLowerCase();
537
- if (
538
- lowered.includes("failed query: begin") ||
539
- lowered.includes('near "begin"') ||
540
- lowered.includes("cannot start a transaction")
541
- ) {
542
- rows = await executeInsertGraph($db as any);
543
- } else {
544
- throw error;
545
- }
546
- }
547
- } else {
548
- rows = await executeInsertGraph($db as any);
549
- }
550
-
551
- emitMutation(
552
- "insert",
553
- { values: args.values as unknown as Record<string, unknown> },
554
- rows,
555
- );
556
- return rows;
557
- },
558
- update: async (args: QueryUpdateArgs<TableName>) => {
559
- const whereFilter = buildWhereFilter(
560
- table,
561
- args.where as Record<string, unknown> | undefined,
562
- tableName,
563
- );
564
- let updateQuery: any = ($db as any)
565
- .update(table as any)
566
- .set(args.set as any);
567
-
568
- if (whereFilter) {
569
- updateQuery = updateQuery.where(whereFilter);
570
- }
571
- if (typeof args.limit === "number" && typeof updateQuery.limit === "function") {
572
- updateQuery = updateQuery.limit(args.limit);
573
- }
574
- if (typeof updateQuery.returning === "function") {
575
- updateQuery = updateQuery.returning();
576
- }
577
-
578
- const rows = (await updateQuery) as Array<TableModel<TableName>>;
579
- emitMutation(
580
- "update",
581
- {
582
- set: args.set as unknown as Record<string, unknown>,
583
- where: (args.where ?? undefined) as unknown as Record<string, unknown>,
584
- limit: args.limit,
585
- },
586
- rows,
587
- );
588
- return rows;
589
- },
590
- upsert: async (args: QueryUpsertArgs<TableName>) => {
591
- const valuesArray = Array.isArray(args.values)
592
- ? args.values
593
- : [args.values];
594
- const values = Array.isArray(args.values)
595
- ? args.values
596
- : args.values;
597
-
598
- const targets = args.target
599
- ? Array.isArray(args.target)
600
- ? args.target
601
- : [args.target]
602
- : inferConflictTarget(table);
603
-
604
- if (targets.length === 0) {
605
- throw new Error(
606
- "Unable to infer conflict target for table " + tableName + ". Provide target explicitly.",
607
- );
608
- }
609
-
610
- const tableColumns = getTableColumns(table as never) as Record<string, unknown>;
611
- const targetColumns = targets
612
- .map((target) => tableColumns[target])
613
- .filter(Boolean);
614
-
615
- if (targetColumns.length === 0) {
616
- throw new Error(
617
- "Invalid conflict target for table " + tableName + ".",
618
- );
619
- }
620
-
621
- const setPayload = args.set ?? valuesArray[0] ?? {};
622
- let upsertQuery: any = ($db as any)
623
- .insert(table as any)
624
- .values(values as any)
625
- .onConflictDoUpdate({
626
- target: targetColumns as any,
627
- set: setPayload as any,
628
- });
629
-
630
- if (typeof upsertQuery.returning === "function") {
631
- upsertQuery = upsertQuery.returning();
632
- }
633
-
634
- const rows = (await upsertQuery) as Array<TableModel<TableName>>;
635
- emitMutation(
636
- "upsert",
637
- {
638
- values: args.values as unknown as Record<string, unknown>,
639
- target: targets as unknown as Record<string, unknown>,
640
- set: (args.set ?? undefined) as unknown as Record<string, unknown>,
641
- },
642
- rows,
643
- );
644
- return rows;
645
- },
646
- delete: async (args?: QueryDeleteArgs<TableName>) => {
647
- const whereFilter = buildWhereFilter(
648
- table,
649
- args?.where as Record<string, unknown> | undefined,
650
- tableName,
651
- );
652
- let deleteQuery: any = ($db as any).delete(table as any);
653
-
654
- if (whereFilter) {
655
- deleteQuery = deleteQuery.where(whereFilter);
656
- }
657
- if (typeof args?.limit === "number" && typeof deleteQuery.limit === "function") {
658
- deleteQuery = deleteQuery.limit(args.limit);
659
- }
660
- if (typeof deleteQuery.returning === "function") {
661
- deleteQuery = deleteQuery.returning();
662
- }
663
-
664
- const rows = (await deleteQuery) as Array<TableModel<TableName>>;
665
- emitMutation(
666
- "delete",
667
- {
668
- where: (args?.where ?? undefined) as unknown as Record<string, unknown>,
669
- limit: args?.limit,
670
- },
671
- rows,
672
- );
673
- return rows;
674
- },
675
- `;
676
- }
1
+ export function generateQueryRuntimeWriteSection(): string {
2
+ return ` insert: async <TArgs extends QueryInsertArgs<TableName>>(args: TArgs) => {
3
+ const transaction = ($db as any).transaction;
4
+
5
+ const valuesArray = Array.isArray(args.values)
6
+ ? args.values
7
+ : [args.values];
8
+ const baseValuesArray: Array<Record<string, unknown>> = [];
9
+ const postInsertRelations: Array<
10
+ Array<{
11
+ relationName: string;
12
+ relation: RuntimeRelation;
13
+ value: unknown;
14
+ }>
15
+ > = [];
16
+
17
+ const toRelationItems = (
18
+ relation: RuntimeRelation,
19
+ relationName: string,
20
+ value: unknown,
21
+ ): unknown[] => {
22
+ if (value === undefined || value === null) {
23
+ return [];
24
+ }
25
+
26
+ if (relation.kind === "one") {
27
+ if (Array.isArray(value)) {
28
+ throw new Error(
29
+ "Relation '" +
30
+ tableName +
31
+ "." +
32
+ relationName +
33
+ "' expects a single value.",
34
+ );
35
+ }
36
+ return [value];
37
+ }
38
+
39
+ return Array.isArray(value) ? value : [value];
40
+ };
41
+
42
+ const resolveRelationIdentifier = async (
43
+ tx: any,
44
+ relation: RuntimeRelation,
45
+ relationName: string,
46
+ input: unknown,
47
+ ): Promise<unknown> => {
48
+ if (
49
+ typeof input === "string" ||
50
+ typeof input === "number" ||
51
+ typeof input === "bigint"
52
+ ) {
53
+ return input;
54
+ }
55
+
56
+ if (!isRecord(input)) {
57
+ throw new Error(
58
+ "Relation '" +
59
+ tableName +
60
+ "." +
61
+ relationName +
62
+ "' expects an id or object payload.",
63
+ );
64
+ }
65
+
66
+ const referenceField =
67
+ relation.kind === "manyToMany"
68
+ ? (relation.targetReferenceField ?? "id")
69
+ : (relation.referenceField ?? "id");
70
+ const existingId = input[referenceField];
71
+ if (existingId !== undefined && existingId !== null) {
72
+ return existingId;
73
+ }
74
+
75
+ const targetTable = (mergedSchema as Record<string, unknown>)[
76
+ relation.targetTable
77
+ ];
78
+ if (!targetTable) {
79
+ throw new Error(
80
+ "Unknown target table '" +
81
+ relation.targetTable +
82
+ "' for relation '" +
83
+ tableName +
84
+ "." +
85
+ relationName +
86
+ "'.",
87
+ );
88
+ }
89
+
90
+ let createQuery: any = tx.insert(targetTable as any).values(input as any);
91
+ if (typeof createQuery.returning === "function") {
92
+ createQuery = createQuery.returning();
93
+ }
94
+ const createdRows = (await createQuery) as Array<Record<string, unknown>>;
95
+ const created = createdRows[0];
96
+ if (!created) {
97
+ throw new Error(
98
+ "Failed to create relation target for '" +
99
+ tableName +
100
+ "." +
101
+ relationName +
102
+ "'.",
103
+ );
104
+ }
105
+
106
+ const createdId = created[referenceField];
107
+ if (createdId === undefined || createdId === null) {
108
+ throw new Error(
109
+ "Created relation target for '" +
110
+ tableName +
111
+ "." +
112
+ relationName +
113
+ "' is missing '" +
114
+ referenceField +
115
+ "'.",
116
+ );
117
+ }
118
+
119
+ return createdId;
120
+ };
121
+
122
+ const getRelationTargetTable = (
123
+ relation: RuntimeRelation,
124
+ relationName: string,
125
+ ): any => {
126
+ const targetTable = (mergedSchema as Record<string, unknown>)[
127
+ relation.targetTable
128
+ ];
129
+ if (!targetTable) {
130
+ throw new Error(
131
+ "Unknown target table '" +
132
+ relation.targetTable +
133
+ "' for relation '" +
134
+ tableName +
135
+ "." +
136
+ relationName +
137
+ "'.",
138
+ );
139
+ }
140
+ return targetTable;
141
+ };
142
+
143
+ const getRelationReferenceField = (relation: RuntimeRelation): string => {
144
+ if (relation.kind === "manyToMany") {
145
+ return relation.targetReferenceField ?? "id";
146
+ }
147
+ return relation.referenceField ?? "id";
148
+ };
149
+
150
+ const fetchRelatedRowByIdentifier = async (
151
+ tx: any,
152
+ relation: RuntimeRelation,
153
+ relationName: string,
154
+ identifier: unknown,
155
+ ): Promise<Record<string, unknown> | null> => {
156
+ if (identifier === undefined || identifier === null) {
157
+ return null;
158
+ }
159
+
160
+ const targetTable = getRelationTargetTable(relation, relationName);
161
+ const referenceField = getRelationReferenceField(relation);
162
+ const referenceColumn = (targetTable as Record<string, unknown>)[
163
+ referenceField
164
+ ];
165
+ if (!referenceColumn) {
166
+ throw new Error(
167
+ "Target table '" +
168
+ relation.targetTable +
169
+ "' is missing column '" +
170
+ referenceField +
171
+ "' required by relation '" +
172
+ tableName +
173
+ "." +
174
+ relationName +
175
+ "'.",
176
+ );
177
+ }
178
+
179
+ let query: any = tx
180
+ .select()
181
+ .from(targetTable as any)
182
+ .where(eq(referenceColumn as any, identifier as any));
183
+ if (typeof query.limit === "function") {
184
+ query = query.limit(1);
185
+ }
186
+ const rows = (await query) as Array<Record<string, unknown>>;
187
+ return rows[0] ?? null;
188
+ };
189
+
190
+ for (const inputValue of valuesArray) {
191
+ const scalarValues: Record<string, unknown> = {};
192
+ const relationInputs: Array<{
193
+ relationName: string;
194
+ relation: RuntimeRelation;
195
+ value: unknown;
196
+ }> = [];
197
+
198
+ const valueRecord = (inputValue ?? {}) as Record<string, unknown>;
199
+ for (const [fieldName, fieldValue] of Object.entries(valueRecord)) {
200
+ const runtimeRelation = getRuntimeRelation(tableName, fieldName);
201
+ if (runtimeRelation) {
202
+ relationInputs.push({
203
+ relationName: fieldName,
204
+ relation: runtimeRelation,
205
+ value: fieldValue,
206
+ });
207
+ continue;
208
+ }
209
+ scalarValues[fieldName] = fieldValue;
210
+ }
211
+
212
+ baseValuesArray.push(scalarValues);
213
+ postInsertRelations.push(relationInputs);
214
+ }
215
+
216
+ const executeInsertGraph = async (
217
+ tx: any,
218
+ ): Promise<Array<QueryInsertResultRow<TableName, TArgs>>> => {
219
+ for (let index = 0; index < baseValuesArray.length; index += 1) {
220
+ const relationInputs = postInsertRelations[index] ?? [];
221
+ for (const relationInput of relationInputs) {
222
+ if (relationInput.relation.kind !== "one") {
223
+ continue;
224
+ }
225
+
226
+ const sourceField = relationInput.relation.sourceField;
227
+ if (!sourceField) {
228
+ throw new Error(
229
+ "Relation '" +
230
+ tableName +
231
+ "." +
232
+ relationInput.relationName +
233
+ "' is missing sourceField metadata.",
234
+ );
235
+ }
236
+
237
+ const oneItems = toRelationItems(
238
+ relationInput.relation,
239
+ relationInput.relationName,
240
+ relationInput.value,
241
+ );
242
+ if (oneItems.length === 0) {
243
+ continue;
244
+ }
245
+
246
+ const relationId = await resolveRelationIdentifier(
247
+ tx,
248
+ relationInput.relation,
249
+ relationInput.relationName,
250
+ oneItems[0],
251
+ );
252
+ const existingValue = baseValuesArray[index][sourceField];
253
+ if (
254
+ existingValue !== undefined &&
255
+ existingValue !== relationId
256
+ ) {
257
+ throw new Error(
258
+ "Insert payload for '" +
259
+ tableName +
260
+ "' has conflicting values for '" +
261
+ sourceField +
262
+ "'.",
263
+ );
264
+ }
265
+ baseValuesArray[index][sourceField] = relationId;
266
+ }
267
+ }
268
+
269
+ let insertQuery: any = tx.insert(table as any).values(baseValuesArray as any);
270
+ if (typeof insertQuery.returning === "function") {
271
+ insertQuery = insertQuery.returning();
272
+ }
273
+ const insertedRows = (await insertQuery) as Array<TableModel<TableName>>;
274
+ const hydratedRows: Array<Record<string, unknown>> = [];
275
+
276
+ for (let index = 0; index < insertedRows.length; index += 1) {
277
+ const parentRow = insertedRows[index] as unknown as Record<string, unknown>;
278
+ const hydratedRow: Record<string, unknown> = {
279
+ ...parentRow,
280
+ };
281
+ const relationInputs = postInsertRelations[index] ?? [];
282
+
283
+ for (const relationInput of relationInputs) {
284
+ if (relationInput.relation.kind === "one") {
285
+ const sourceField = relationInput.relation.sourceField;
286
+ if (!sourceField) {
287
+ throw new Error(
288
+ "Relation '" +
289
+ tableName +
290
+ "." +
291
+ relationInput.relationName +
292
+ "' is missing sourceField metadata.",
293
+ );
294
+ }
295
+
296
+ const relationItems = toRelationItems(
297
+ relationInput.relation,
298
+ relationInput.relationName,
299
+ relationInput.value,
300
+ );
301
+ if (relationItems.length === 0) {
302
+ hydratedRow[relationInput.relationName] = null;
303
+ continue;
304
+ }
305
+
306
+ const sourceIdentifier = parentRow[sourceField];
307
+ hydratedRow[relationInput.relationName] =
308
+ (await fetchRelatedRowByIdentifier(
309
+ tx,
310
+ relationInput.relation,
311
+ relationInput.relationName,
312
+ sourceIdentifier,
313
+ )) ?? null;
314
+ continue;
315
+ }
316
+
317
+ const sourceReferenceField = relationInput.relation.referenceField ?? "id";
318
+ const parentReferenceValue = parentRow[sourceReferenceField];
319
+ if (
320
+ parentReferenceValue === undefined ||
321
+ parentReferenceValue === null
322
+ ) {
323
+ throw new Error(
324
+ "Inserted row for '" +
325
+ tableName +
326
+ "' is missing '" +
327
+ sourceReferenceField +
328
+ "' required by relation '" +
329
+ relationInput.relationName +
330
+ "'.",
331
+ );
332
+ }
333
+
334
+ const relatedRows: Array<Record<string, unknown>> = [];
335
+ hydratedRow[relationInput.relationName] = relatedRows;
336
+
337
+ const relationItems = toRelationItems(
338
+ relationInput.relation,
339
+ relationInput.relationName,
340
+ relationInput.value,
341
+ );
342
+ if (relationItems.length === 0) {
343
+ continue;
344
+ }
345
+
346
+ if (relationInput.relation.kind === "many") {
347
+ const targetTable = getRelationTargetTable(
348
+ relationInput.relation,
349
+ relationInput.relationName,
350
+ );
351
+
352
+ const sourceField = relationInput.relation.sourceField;
353
+ const targetReferenceField =
354
+ relationInput.relation.referenceField ?? "id";
355
+ const targetReferenceColumn =
356
+ (targetTable as Record<string, unknown>)[targetReferenceField];
357
+ if (!sourceField) {
358
+ throw new Error(
359
+ "Relation '" +
360
+ tableName +
361
+ "." +
362
+ relationInput.relationName +
363
+ "' is missing sourceField metadata.",
364
+ );
365
+ }
366
+ if (!targetReferenceColumn) {
367
+ throw new Error(
368
+ "Target table '" +
369
+ relationInput.relation.targetTable +
370
+ "' is missing column '" +
371
+ targetReferenceField +
372
+ "' required by relation '" +
373
+ tableName +
374
+ "." +
375
+ relationInput.relationName +
376
+ "'.",
377
+ );
378
+ }
379
+
380
+ for (const relationItem of relationItems) {
381
+ if (
382
+ typeof relationItem === "string" ||
383
+ typeof relationItem === "number" ||
384
+ typeof relationItem === "bigint"
385
+ ) {
386
+ await tx
387
+ .update(targetTable as any)
388
+ .set({ [sourceField]: parentReferenceValue } as any)
389
+ .where(
390
+ eq(targetReferenceColumn as any, relationItem as any),
391
+ );
392
+ const linkedRow = await fetchRelatedRowByIdentifier(
393
+ tx,
394
+ relationInput.relation,
395
+ relationInput.relationName,
396
+ relationItem,
397
+ );
398
+ if (linkedRow) {
399
+ relatedRows.push(linkedRow);
400
+ }
401
+ continue;
402
+ }
403
+
404
+ if (!isRecord(relationItem)) {
405
+ throw new Error(
406
+ "Relation '" +
407
+ tableName +
408
+ "." +
409
+ relationInput.relationName +
410
+ "' expects id or object payloads.",
411
+ );
412
+ }
413
+
414
+ const existingId = relationItem[targetReferenceField];
415
+ const payload = {
416
+ ...relationItem,
417
+ [sourceField]: parentReferenceValue,
418
+ };
419
+
420
+ if (existingId !== undefined && existingId !== null) {
421
+ const setPayload: Record<string, unknown> = {
422
+ ...payload,
423
+ };
424
+ delete setPayload[targetReferenceField];
425
+ await tx
426
+ .update(targetTable as any)
427
+ .set(setPayload as any)
428
+ .where(
429
+ eq(targetReferenceColumn as any, existingId as any),
430
+ );
431
+ const linkedRow = await fetchRelatedRowByIdentifier(
432
+ tx,
433
+ relationInput.relation,
434
+ relationInput.relationName,
435
+ existingId,
436
+ );
437
+ if (linkedRow) {
438
+ relatedRows.push(linkedRow);
439
+ }
440
+ continue;
441
+ }
442
+
443
+ let createQuery: any = tx
444
+ .insert(targetTable as any)
445
+ .values(payload as any);
446
+ if (typeof createQuery.returning === "function") {
447
+ createQuery = createQuery.returning();
448
+ }
449
+ const createdRows = (await createQuery) as Array<
450
+ Record<string, unknown>
451
+ >;
452
+ const createdRow = createdRows[0];
453
+ if (createdRow) {
454
+ relatedRows.push(createdRow);
455
+ }
456
+ }
457
+ continue;
458
+ }
459
+
460
+ const junctionTable = (mergedSchema as Record<string, unknown>)[
461
+ relationInput.relation.junctionTable
462
+ ];
463
+ if (!junctionTable) {
464
+ throw new Error(
465
+ "Unknown junction table '" +
466
+ relationInput.relation.junctionTable +
467
+ "' for relation '" +
468
+ tableName +
469
+ "." +
470
+ relationInput.relationName +
471
+ "'.",
472
+ );
473
+ }
474
+
475
+ const sourceField = relationInput.relation.sourceField;
476
+ const targetField = relationInput.relation.targetField;
477
+ if (!sourceField || !targetField) {
478
+ throw new Error(
479
+ "Relation '" +
480
+ tableName +
481
+ "." +
482
+ relationInput.relationName +
483
+ "' is missing junction metadata fields.",
484
+ );
485
+ }
486
+
487
+ const linkValues: Array<Record<string, unknown>> = [];
488
+ const targetIdentifiers: unknown[] = [];
489
+ for (const relationItem of relationItems) {
490
+ const targetId = await resolveRelationIdentifier(
491
+ tx,
492
+ relationInput.relation,
493
+ relationInput.relationName,
494
+ relationItem,
495
+ );
496
+ targetIdentifiers.push(targetId);
497
+
498
+ linkValues.push({
499
+ [sourceField]: parentReferenceValue,
500
+ [targetField]: targetId,
501
+ });
502
+ }
503
+
504
+ if (linkValues.length > 0) {
505
+ await tx.insert(junctionTable as any).values(linkValues as any);
506
+ }
507
+
508
+ for (const targetId of targetIdentifiers) {
509
+ const linkedRow = await fetchRelatedRowByIdentifier(
510
+ tx,
511
+ relationInput.relation,
512
+ relationInput.relationName,
513
+ targetId,
514
+ );
515
+ if (linkedRow) {
516
+ relatedRows.push(linkedRow);
517
+ }
518
+ }
519
+ }
520
+
521
+ hydratedRows.push(hydratedRow);
522
+ }
523
+
524
+ return hydratedRows as Array<QueryInsertResultRow<TableName, TArgs>>;
525
+ };
526
+
527
+ let rows: Array<QueryInsertResultRow<TableName, TArgs>>;
528
+ if (typeof transaction === "function") {
529
+ try {
530
+ rows = await transaction.call($db, (tx: any) =>
531
+ executeInsertGraph(tx),
532
+ );
533
+ } catch (error) {
534
+ const message =
535
+ error instanceof Error ? error.message : String(error);
536
+ const lowered = message.toLowerCase();
537
+ if (
538
+ lowered.includes("failed query: begin") ||
539
+ lowered.includes('near "begin"') ||
540
+ lowered.includes("cannot start a transaction")
541
+ ) {
542
+ rows = await executeInsertGraph($db as any);
543
+ } else {
544
+ throw error;
545
+ }
546
+ }
547
+ } else {
548
+ rows = await executeInsertGraph($db as any);
549
+ }
550
+
551
+ emitMutation(
552
+ "insert",
553
+ { values: args.values as unknown as Record<string, unknown> },
554
+ rows,
555
+ );
556
+ return rows;
557
+ },
558
+ update: async (args: QueryUpdateArgs<TableName>) => {
559
+ const whereFilter = buildWhereFilter(
560
+ table,
561
+ args.where as Record<string, unknown> | undefined,
562
+ tableName,
563
+ );
564
+ let updateQuery: any = ($db as any)
565
+ .update(table as any)
566
+ .set(args.set as any);
567
+
568
+ if (whereFilter) {
569
+ updateQuery = updateQuery.where(whereFilter);
570
+ }
571
+ if (typeof args.limit === "number" && typeof updateQuery.limit === "function") {
572
+ updateQuery = updateQuery.limit(args.limit);
573
+ }
574
+ if (typeof updateQuery.returning === "function") {
575
+ updateQuery = updateQuery.returning();
576
+ }
577
+
578
+ const rows = (await updateQuery) as Array<TableModel<TableName>>;
579
+ emitMutation(
580
+ "update",
581
+ {
582
+ set: args.set as unknown as Record<string, unknown>,
583
+ where: (args.where ?? undefined) as unknown as Record<string, unknown>,
584
+ limit: args.limit,
585
+ },
586
+ rows,
587
+ );
588
+ return rows;
589
+ },
590
+ upsert: async (args: QueryUpsertArgs<TableName>) => {
591
+ const valuesArray = Array.isArray(args.values)
592
+ ? args.values
593
+ : [args.values];
594
+ const values = Array.isArray(args.values)
595
+ ? args.values
596
+ : args.values;
597
+
598
+ const targets = args.target
599
+ ? Array.isArray(args.target)
600
+ ? args.target
601
+ : [args.target]
602
+ : inferConflictTarget(table);
603
+
604
+ if (targets.length === 0) {
605
+ throw new Error(
606
+ "Unable to infer conflict target for table " + tableName + ". Provide target explicitly.",
607
+ );
608
+ }
609
+
610
+ const tableColumns = getTableColumns(table as never) as Record<string, unknown>;
611
+ const targetColumns = targets
612
+ .map((target) => tableColumns[target])
613
+ .filter(Boolean);
614
+
615
+ if (targetColumns.length === 0) {
616
+ throw new Error(
617
+ "Invalid conflict target for table " + tableName + ".",
618
+ );
619
+ }
620
+
621
+ const setPayload = args.set ?? valuesArray[0] ?? {};
622
+ let upsertQuery: any = ($db as any)
623
+ .insert(table as any)
624
+ .values(values as any)
625
+ .onConflictDoUpdate({
626
+ target: targetColumns as any,
627
+ set: setPayload as any,
628
+ });
629
+
630
+ if (typeof upsertQuery.returning === "function") {
631
+ upsertQuery = upsertQuery.returning();
632
+ }
633
+
634
+ const rows = (await upsertQuery) as Array<TableModel<TableName>>;
635
+ emitMutation(
636
+ "upsert",
637
+ {
638
+ values: args.values as unknown as Record<string, unknown>,
639
+ target: targets as unknown as Record<string, unknown>,
640
+ set: (args.set ?? undefined) as unknown as Record<string, unknown>,
641
+ },
642
+ rows,
643
+ );
644
+ return rows;
645
+ },
646
+ delete: async (args?: QueryDeleteArgs<TableName>) => {
647
+ const whereFilter = buildWhereFilter(
648
+ table,
649
+ args?.where as Record<string, unknown> | undefined,
650
+ tableName,
651
+ );
652
+ let deleteQuery: any = ($db as any).delete(table as any);
653
+
654
+ if (whereFilter) {
655
+ deleteQuery = deleteQuery.where(whereFilter);
656
+ }
657
+ if (typeof args?.limit === "number" && typeof deleteQuery.limit === "function") {
658
+ deleteQuery = deleteQuery.limit(args.limit);
659
+ }
660
+ if (typeof deleteQuery.returning === "function") {
661
+ deleteQuery = deleteQuery.returning();
662
+ }
663
+
664
+ const rows = (await deleteQuery) as Array<TableModel<TableName>>;
665
+ emitMutation(
666
+ "delete",
667
+ {
668
+ where: (args?.where ?? undefined) as unknown as Record<string, unknown>,
669
+ limit: args?.limit,
670
+ },
671
+ rows,
672
+ );
673
+ return rows;
674
+ },
675
+ `;
676
+ }