@remix-run/data-table 0.0.0 → 0.2.0

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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +549 -2
  3. package/dist/index.d.ts +15 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +7 -0
  6. package/dist/lib/adapter.d.ts +550 -0
  7. package/dist/lib/adapter.d.ts.map +1 -0
  8. package/dist/lib/adapter.js +1 -0
  9. package/dist/lib/column.d.ts +193 -0
  10. package/dist/lib/column.d.ts.map +1 -0
  11. package/dist/lib/column.js +302 -0
  12. package/dist/lib/database/execution-context.d.ts +10 -0
  13. package/dist/lib/database/execution-context.d.ts.map +1 -0
  14. package/dist/lib/database/execution-context.js +1 -0
  15. package/dist/lib/database/helpers.d.ts +26 -0
  16. package/dist/lib/database/helpers.d.ts.map +1 -0
  17. package/dist/lib/database/helpers.js +116 -0
  18. package/dist/lib/database/query-execution.d.ts +7 -0
  19. package/dist/lib/database/query-execution.d.ts.map +1 -0
  20. package/dist/lib/database/query-execution.js +401 -0
  21. package/dist/lib/database/relations.d.ts +4 -0
  22. package/dist/lib/database/relations.d.ts.map +1 -0
  23. package/dist/lib/database/relations.js +207 -0
  24. package/dist/lib/database/write-lifecycle.d.ts +13 -0
  25. package/dist/lib/database/write-lifecycle.d.ts.map +1 -0
  26. package/dist/lib/database/write-lifecycle.js +279 -0
  27. package/dist/lib/database.d.ts +264 -0
  28. package/dist/lib/database.d.ts.map +1 -0
  29. package/dist/lib/database.js +319 -0
  30. package/dist/lib/errors.d.ts +59 -0
  31. package/dist/lib/errors.d.ts.map +1 -0
  32. package/dist/lib/errors.js +76 -0
  33. package/dist/lib/inflection.d.ts +3 -0
  34. package/dist/lib/inflection.d.ts.map +1 -0
  35. package/dist/lib/inflection.js +56 -0
  36. package/dist/lib/migrations/filename.d.ts +12 -0
  37. package/dist/lib/migrations/filename.d.ts.map +1 -0
  38. package/dist/lib/migrations/filename.js +20 -0
  39. package/dist/lib/migrations/helpers.d.ts +11 -0
  40. package/dist/lib/migrations/helpers.d.ts.map +1 -0
  41. package/dist/lib/migrations/helpers.js +77 -0
  42. package/dist/lib/migrations/journal-store.d.ts +15 -0
  43. package/dist/lib/migrations/journal-store.d.ts.map +1 -0
  44. package/dist/lib/migrations/journal-store.js +83 -0
  45. package/dist/lib/migrations/registry.d.ts +27 -0
  46. package/dist/lib/migrations/registry.d.ts.map +1 -0
  47. package/dist/lib/migrations/registry.js +51 -0
  48. package/dist/lib/migrations/runner.d.ts +20 -0
  49. package/dist/lib/migrations/runner.d.ts.map +1 -0
  50. package/dist/lib/migrations/runner.js +273 -0
  51. package/dist/lib/migrations/schema-api.d.ts +7 -0
  52. package/dist/lib/migrations/schema-api.d.ts.map +1 -0
  53. package/dist/lib/migrations/schema-api.js +326 -0
  54. package/dist/lib/migrations-node.d.ts +17 -0
  55. package/dist/lib/migrations-node.d.ts.map +1 -0
  56. package/dist/lib/migrations-node.js +65 -0
  57. package/dist/lib/migrations.d.ts +292 -0
  58. package/dist/lib/migrations.d.ts.map +1 -0
  59. package/dist/lib/migrations.js +38 -0
  60. package/dist/lib/operators.d.ts +154 -0
  61. package/dist/lib/operators.d.ts.map +1 -0
  62. package/dist/lib/operators.js +218 -0
  63. package/dist/lib/query.d.ts +159 -0
  64. package/dist/lib/query.d.ts.map +1 -0
  65. package/dist/lib/query.js +401 -0
  66. package/dist/lib/references.d.ts +41 -0
  67. package/dist/lib/references.d.ts.map +1 -0
  68. package/dist/lib/references.js +33 -0
  69. package/dist/lib/sql-helpers.d.ts +50 -0
  70. package/dist/lib/sql-helpers.d.ts.map +1 -0
  71. package/dist/lib/sql-helpers.js +111 -0
  72. package/dist/lib/sql.d.ts +45 -0
  73. package/dist/lib/sql.d.ts.map +1 -0
  74. package/dist/lib/sql.js +65 -0
  75. package/dist/lib/table.d.ts +569 -0
  76. package/dist/lib/table.d.ts.map +1 -0
  77. package/dist/lib/table.js +519 -0
  78. package/dist/lib/types.d.ts +4 -0
  79. package/dist/lib/types.d.ts.map +1 -0
  80. package/dist/lib/types.js +1 -0
  81. package/dist/migrations/node.d.ts +2 -0
  82. package/dist/migrations/node.d.ts.map +1 -0
  83. package/dist/migrations/node.js +1 -0
  84. package/dist/migrations.d.ts +8 -0
  85. package/dist/migrations.d.ts.map +1 -0
  86. package/dist/migrations.js +5 -0
  87. package/dist/operators.d.ts +3 -0
  88. package/dist/operators.d.ts.map +1 -0
  89. package/dist/operators.js +1 -0
  90. package/dist/sql-helpers.d.ts +3 -0
  91. package/dist/sql-helpers.d.ts.map +1 -0
  92. package/dist/sql-helpers.js +1 -0
  93. package/package.json +56 -7
  94. package/src/index.ts +198 -0
  95. package/src/lib/adapter.ts +653 -0
  96. package/src/lib/column.ts +384 -0
  97. package/src/lib/database/execution-context.ts +15 -0
  98. package/src/lib/database/helpers.ts +216 -0
  99. package/src/lib/database/query-execution.ts +638 -0
  100. package/src/lib/database/relations.ts +332 -0
  101. package/src/lib/database/write-lifecycle.ts +487 -0
  102. package/src/lib/database.ts +856 -0
  103. package/src/lib/errors.ts +119 -0
  104. package/src/lib/inflection.ts +69 -0
  105. package/src/lib/migrations/filename.ts +25 -0
  106. package/src/lib/migrations/helpers.ts +108 -0
  107. package/src/lib/migrations/journal-store.ts +122 -0
  108. package/src/lib/migrations/registry.ts +62 -0
  109. package/src/lib/migrations/runner.ts +374 -0
  110. package/src/lib/migrations/schema-api.ts +417 -0
  111. package/src/lib/migrations-node.ts +71 -0
  112. package/src/lib/migrations.ts +328 -0
  113. package/src/lib/operators.ts +436 -0
  114. package/src/lib/query.ts +958 -0
  115. package/src/lib/references.ts +78 -0
  116. package/src/lib/sql-helpers.ts +146 -0
  117. package/src/lib/sql.ts +84 -0
  118. package/src/lib/table.ts +1309 -0
  119. package/src/lib/types.ts +3 -0
  120. package/src/migrations/node.ts +1 -0
  121. package/src/migrations.ts +26 -0
  122. package/src/operators.ts +18 -0
  123. package/src/sql-helpers.ts +9 -0
@@ -0,0 +1,401 @@
1
+ import { DataTableQueryError } from "../errors.js";
2
+ import { normalizeWhereInput } from "../operators.js";
3
+ import { cloneQueryState, querySnapshot } from "../query.js";
4
+ import { getPrimaryKeyObject, getTableName } from "../table.js";
5
+ import { executeOperation } from "./execution-context.js";
6
+ import { buildPrimaryKeyPredicate, hasScopedWriteModifiers, loadPrimaryKeyRowsForScope, } from "./helpers.js";
7
+ import { loadRelationsForRows } from "./relations.js";
8
+ import { applyAfterReadHooksToLoadedRows, applyAfterReadHooksToRows, assertReturningCapability, normalizeReturningSelection, prepareInsertValues, prepareUpdateValues, runAfterDeleteHook, runAfterWriteHook, runBeforeDeleteHook, } from "./write-lifecycle.js";
9
+ export async function executeQuery(database, input) {
10
+ let snapshot = input[querySnapshot]();
11
+ switch (snapshot.plan.kind) {
12
+ case 'all':
13
+ return (await executeAll(database, snapshot.table, snapshot.state));
14
+ case 'first':
15
+ return (await executeFirst(database, snapshot.table, snapshot.state));
16
+ case 'find':
17
+ return (await executeFind(database, snapshot.table, snapshot.state, snapshot.plan.value));
18
+ case 'count':
19
+ return (await executeCount(database, snapshot.table, snapshot.state));
20
+ case 'exists':
21
+ return (await executeExists(database, snapshot.table, snapshot.state));
22
+ case 'insert':
23
+ return (await executeInsert(database, snapshot.table, snapshot.plan.values, snapshot.plan.options));
24
+ case 'insertMany':
25
+ return (await executeInsertMany(database, snapshot.table, snapshot.plan.values, snapshot.plan.options));
26
+ case 'update':
27
+ return (await executeUpdate(database, snapshot.table, snapshot.state, snapshot.plan.changes, snapshot.plan.options));
28
+ case 'delete':
29
+ return (await executeDelete(database, snapshot.table, snapshot.state, snapshot.plan.options));
30
+ case 'upsert':
31
+ return (await executeUpsert(database, snapshot.table, snapshot.plan.values, snapshot.plan.options));
32
+ default:
33
+ throw new DataTableQueryError('Unknown query execution mode');
34
+ }
35
+ }
36
+ export async function loadRowsWithRelationsForQuery(database, input) {
37
+ let snapshot = input[querySnapshot]();
38
+ return loadRowsWithRelationsForState(database, snapshot.table, snapshot.state);
39
+ }
40
+ export async function loadRowsWithRelationsForState(database, table, state) {
41
+ let operation = createSelectOperation(table, state);
42
+ let result = await database[executeOperation](operation);
43
+ let rows = normalizeRows(result.rows);
44
+ if (Object.keys(state.with).length === 0) {
45
+ return rows;
46
+ }
47
+ return loadRelationsForRows(database, table, rows, state.with);
48
+ }
49
+ async function executeAll(database, table, state) {
50
+ let rows = await loadRowsWithRelationsForState(database, table, state);
51
+ return applyAfterReadHooksToLoadedRows(table, rows, state.with);
52
+ }
53
+ async function executeFirst(database, table, state) {
54
+ let rows = await executeAll(database, table, {
55
+ ...cloneQueryState(state),
56
+ limit: 1,
57
+ });
58
+ return rows[0] ?? null;
59
+ }
60
+ async function executeFind(database, table, state, value) {
61
+ let scopedState = cloneQueryState(state);
62
+ scopedState.where.push(normalizeWhereInput(getPrimaryKeyObject(table, value)));
63
+ return executeFirst(database, table, scopedState);
64
+ }
65
+ async function executeCount(database, table, state) {
66
+ let operation = {
67
+ kind: 'count',
68
+ table,
69
+ joins: [...state.joins],
70
+ where: [...state.where],
71
+ groupBy: [...state.groupBy],
72
+ having: [...state.having],
73
+ };
74
+ let result = await database[executeOperation](operation);
75
+ if (result.rows && result.rows[0] && typeof result.rows[0].count === 'number') {
76
+ return result.rows[0].count;
77
+ }
78
+ if (result.rows) {
79
+ return result.rows.length;
80
+ }
81
+ return 0;
82
+ }
83
+ async function executeExists(database, table, state) {
84
+ let operation = {
85
+ kind: 'exists',
86
+ table,
87
+ joins: [...state.joins],
88
+ where: [...state.where],
89
+ groupBy: [...state.groupBy],
90
+ having: [...state.having],
91
+ };
92
+ let result = await database[executeOperation](operation);
93
+ if (result.rows && result.rows[0] && typeof result.rows[0].exists === 'boolean') {
94
+ return result.rows[0].exists;
95
+ }
96
+ if (result.rows && result.rows[0] && typeof result.rows[0].count === 'number') {
97
+ return Number(result.rows[0].count) > 0;
98
+ }
99
+ return Boolean(result.rows && result.rows.length > 0);
100
+ }
101
+ async function executeInsert(database, table, values, options) {
102
+ let preparedValues = prepareInsertValues(table, values, database.now(), options?.touch ?? true);
103
+ let returning = options?.returning;
104
+ assertReturningCapability(database.adapter, 'insert', returning);
105
+ if (returning) {
106
+ let operation = {
107
+ kind: 'insert',
108
+ table,
109
+ values: preparedValues,
110
+ returning: normalizeReturningSelection(returning),
111
+ };
112
+ let result = await database[executeOperation](operation);
113
+ let row = applyAfterReadHooksToRows(table, normalizeRows(result.rows))[0] ?? null;
114
+ let affectedRows = result.affectedRows ?? 0;
115
+ runAfterWriteHook(table, {
116
+ operation: 'create',
117
+ tableName: getTableName(table),
118
+ values: [preparedValues],
119
+ affectedRows,
120
+ insertId: result.insertId,
121
+ });
122
+ return {
123
+ affectedRows,
124
+ insertId: result.insertId,
125
+ row,
126
+ };
127
+ }
128
+ let operation = {
129
+ kind: 'insert',
130
+ table,
131
+ values: preparedValues,
132
+ };
133
+ let result = await database[executeOperation](operation);
134
+ let affectedRows = result.affectedRows ?? 0;
135
+ runAfterWriteHook(table, {
136
+ operation: 'create',
137
+ tableName: getTableName(table),
138
+ values: [preparedValues],
139
+ affectedRows,
140
+ insertId: result.insertId,
141
+ });
142
+ return {
143
+ affectedRows,
144
+ insertId: result.insertId,
145
+ };
146
+ }
147
+ async function executeInsertMany(database, table, values, options) {
148
+ let preparedValues = values.map((value) => prepareInsertValues(table, value, database.now(), options?.touch ?? true));
149
+ if (preparedValues.length > 0 &&
150
+ preparedValues.every((preparedValue) => Object.keys(preparedValue).length === 0)) {
151
+ throw new DataTableQueryError('insertMany() requires at least one explicit value across the batch');
152
+ }
153
+ let returning = options?.returning;
154
+ assertReturningCapability(database.adapter, 'insertMany', returning);
155
+ if (returning) {
156
+ let operation = {
157
+ kind: 'insertMany',
158
+ table,
159
+ values: preparedValues,
160
+ returning: normalizeReturningSelection(returning),
161
+ };
162
+ let result = await database[executeOperation](operation);
163
+ let affectedRows = result.affectedRows ?? 0;
164
+ runAfterWriteHook(table, {
165
+ operation: 'create',
166
+ tableName: getTableName(table),
167
+ values: preparedValues,
168
+ affectedRows,
169
+ insertId: result.insertId,
170
+ });
171
+ return {
172
+ affectedRows,
173
+ insertId: result.insertId,
174
+ rows: applyAfterReadHooksToRows(table, normalizeRows(result.rows)),
175
+ };
176
+ }
177
+ let operation = {
178
+ kind: 'insertMany',
179
+ table,
180
+ values: preparedValues,
181
+ };
182
+ let result = await database[executeOperation](operation);
183
+ let affectedRows = result.affectedRows ?? 0;
184
+ runAfterWriteHook(table, {
185
+ operation: 'create',
186
+ tableName: getTableName(table),
187
+ values: preparedValues,
188
+ affectedRows,
189
+ insertId: result.insertId,
190
+ });
191
+ return {
192
+ affectedRows,
193
+ insertId: result.insertId,
194
+ };
195
+ }
196
+ async function executeUpdate(database, table, state, changes, options) {
197
+ let returning = options?.returning;
198
+ assertReturningCapability(database.adapter, 'update', returning);
199
+ let preparedChanges = prepareUpdateValues(table, changes, database.now(), options?.touch ?? true);
200
+ if (Object.keys(preparedChanges).length === 0) {
201
+ throw new DataTableQueryError('update() requires at least one change');
202
+ }
203
+ let result;
204
+ if (hasScopedWriteModifiers(state)) {
205
+ result = await database.transaction(async (tx) => {
206
+ let primaryKeys = await loadPrimaryKeyRowsForScope(tx, table, state);
207
+ let primaryKeyPredicate = buildPrimaryKeyPredicate(table, primaryKeys);
208
+ if (!primaryKeyPredicate) {
209
+ return {
210
+ affectedRows: 0,
211
+ insertId: undefined,
212
+ rows: returning ? [] : undefined,
213
+ };
214
+ }
215
+ return tx[executeOperation]({
216
+ kind: 'update',
217
+ table,
218
+ changes: preparedChanges,
219
+ where: [primaryKeyPredicate],
220
+ returning: returning ? normalizeReturningSelection(returning) : undefined,
221
+ });
222
+ });
223
+ }
224
+ else {
225
+ let operation = {
226
+ kind: 'update',
227
+ table,
228
+ changes: preparedChanges,
229
+ where: [...state.where],
230
+ returning: returning ? normalizeReturningSelection(returning) : undefined,
231
+ };
232
+ result = await database[executeOperation](operation);
233
+ }
234
+ let affectedRows = result.affectedRows ?? 0;
235
+ runAfterWriteHook(table, {
236
+ operation: 'update',
237
+ tableName: getTableName(table),
238
+ values: [preparedChanges],
239
+ affectedRows,
240
+ insertId: result.insertId,
241
+ });
242
+ if (!returning) {
243
+ return {
244
+ affectedRows,
245
+ insertId: result.insertId,
246
+ };
247
+ }
248
+ return {
249
+ affectedRows,
250
+ insertId: result.insertId,
251
+ rows: applyAfterReadHooksToRows(table, normalizeRows(result.rows)),
252
+ };
253
+ }
254
+ async function executeDelete(database, table, state, options) {
255
+ let returning = options?.returning;
256
+ assertReturningCapability(database.adapter, 'delete', returning);
257
+ let tableName = getTableName(table);
258
+ let deleteContext = {
259
+ tableName,
260
+ where: [...state.where],
261
+ orderBy: [...state.orderBy],
262
+ limit: state.limit,
263
+ offset: state.offset,
264
+ };
265
+ runBeforeDeleteHook(table, deleteContext);
266
+ let result;
267
+ if (hasScopedWriteModifiers(state)) {
268
+ result = await database.transaction(async (tx) => {
269
+ let primaryKeys = await loadPrimaryKeyRowsForScope(tx, table, state);
270
+ let primaryKeyPredicate = buildPrimaryKeyPredicate(table, primaryKeys);
271
+ if (!primaryKeyPredicate) {
272
+ return {
273
+ affectedRows: 0,
274
+ insertId: undefined,
275
+ rows: returning ? [] : undefined,
276
+ };
277
+ }
278
+ return tx[executeOperation]({
279
+ kind: 'delete',
280
+ table,
281
+ where: [primaryKeyPredicate],
282
+ returning: returning ? normalizeReturningSelection(returning) : undefined,
283
+ });
284
+ });
285
+ }
286
+ else {
287
+ let operation = {
288
+ kind: 'delete',
289
+ table,
290
+ where: [...state.where],
291
+ returning: returning ? normalizeReturningSelection(returning) : undefined,
292
+ };
293
+ result = await database[executeOperation](operation);
294
+ }
295
+ let affectedRows = result.affectedRows ?? 0;
296
+ runAfterDeleteHook(table, {
297
+ tableName,
298
+ where: deleteContext.where,
299
+ orderBy: deleteContext.orderBy,
300
+ limit: deleteContext.limit,
301
+ offset: deleteContext.offset,
302
+ affectedRows,
303
+ });
304
+ if (!returning) {
305
+ return {
306
+ affectedRows,
307
+ insertId: result.insertId,
308
+ };
309
+ }
310
+ return {
311
+ affectedRows,
312
+ insertId: result.insertId,
313
+ rows: applyAfterReadHooksToRows(table, normalizeRows(result.rows)),
314
+ };
315
+ }
316
+ async function executeUpsert(database, table, values, options) {
317
+ if (!database.adapter.capabilities.upsert) {
318
+ throw new DataTableQueryError('Adapter does not support upsert');
319
+ }
320
+ let preparedValues = prepareInsertValues(table, values, database.now(), options?.touch ?? true);
321
+ let updateChanges = options?.update
322
+ ? prepareUpdateValues(table, options.update, database.now(), options?.touch ?? true, 'create')
323
+ : undefined;
324
+ let returning = options?.returning;
325
+ assertReturningCapability(database.adapter, 'upsert', returning);
326
+ if (returning) {
327
+ let operation = {
328
+ kind: 'upsert',
329
+ table,
330
+ values: preparedValues,
331
+ conflictTarget: options?.conflictTarget ? [...options.conflictTarget] : undefined,
332
+ update: updateChanges,
333
+ returning: normalizeReturningSelection(returning),
334
+ };
335
+ let result = await database[executeOperation](operation);
336
+ let row = applyAfterReadHooksToRows(table, normalizeRows(result.rows))[0] ?? null;
337
+ let affectedRows = result.affectedRows ?? 0;
338
+ let preparedWriteValues = updateChanges ? [preparedValues, updateChanges] : [preparedValues];
339
+ runAfterWriteHook(table, {
340
+ operation: 'create',
341
+ tableName: getTableName(table),
342
+ values: preparedWriteValues,
343
+ affectedRows,
344
+ insertId: result.insertId,
345
+ });
346
+ return {
347
+ affectedRows,
348
+ insertId: result.insertId,
349
+ row,
350
+ };
351
+ }
352
+ let operation = {
353
+ kind: 'upsert',
354
+ table,
355
+ values: preparedValues,
356
+ conflictTarget: options?.conflictTarget ? [...options.conflictTarget] : undefined,
357
+ update: updateChanges,
358
+ };
359
+ let result = await database[executeOperation](operation);
360
+ let affectedRows = result.affectedRows ?? 0;
361
+ let preparedWriteValues = updateChanges ? [preparedValues, updateChanges] : [preparedValues];
362
+ runAfterWriteHook(table, {
363
+ operation: 'create',
364
+ tableName: getTableName(table),
365
+ values: preparedWriteValues,
366
+ affectedRows,
367
+ insertId: result.insertId,
368
+ });
369
+ return {
370
+ affectedRows,
371
+ insertId: result.insertId,
372
+ };
373
+ }
374
+ function createSelectOperation(table, state) {
375
+ let clonedState = cloneQueryState(state);
376
+ return {
377
+ kind: 'select',
378
+ table,
379
+ select: cloneSelection(clonedState.select),
380
+ distinct: clonedState.distinct,
381
+ joins: clonedState.joins,
382
+ where: clonedState.where,
383
+ groupBy: clonedState.groupBy,
384
+ having: clonedState.having,
385
+ orderBy: clonedState.orderBy,
386
+ limit: clonedState.limit,
387
+ offset: clonedState.offset,
388
+ };
389
+ }
390
+ function cloneSelection(selection) {
391
+ if (selection === '*') {
392
+ return '*';
393
+ }
394
+ return selection.map((column) => ({ ...column }));
395
+ }
396
+ function normalizeRows(rows) {
397
+ if (!rows) {
398
+ return [];
399
+ }
400
+ return rows.map((row) => ({ ...row }));
401
+ }
@@ -0,0 +1,4 @@
1
+ import type { AnyRelation, AnyTable } from '../table.ts';
2
+ import type { QueryExecutionContext } from './execution-context.ts';
3
+ export declare function loadRelationsForRows(database: QueryExecutionContext, sourceTable: AnyTable, rows: Record<string, unknown>[], relationMap: Record<string, AnyRelation>): Promise<Record<string, unknown>[]>;
4
+ //# sourceMappingURL=relations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../../../src/lib/database/relations.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAY,MAAM,aAAa,CAAA;AAGlE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAWnE,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,qBAAqB,EAC/B,WAAW,EAAE,QAAQ,EACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CA4BpC"}
@@ -0,0 +1,207 @@
1
+ import { DataTableQueryError } from "../errors.js";
2
+ import { and, eq, inList, or } from "../operators.js";
3
+ import { query as createQuery } from "../query.js";
4
+ import { getCompositeKey, getTableName, getTablePrimaryKey } from "../table.js";
5
+ import { loadRowsWithRelationsForQuery } from "./query-execution.js";
6
+ export async function loadRelationsForRows(database, sourceTable, rows, relationMap) {
7
+ let output = rows.map((row) => ({ ...row }));
8
+ let relationNames = Object.keys(relationMap);
9
+ for (let relationName of relationNames) {
10
+ let relation = relationMap[relationName];
11
+ if (relation.sourceTable !== sourceTable) {
12
+ throw new DataTableQueryError('Relation "' +
13
+ relationName +
14
+ '" is not defined for source table "' +
15
+ getTableName(sourceTable) +
16
+ '"');
17
+ }
18
+ let values = await resolveRelationValues(database, output, relation);
19
+ let index = 0;
20
+ while (index < output.length) {
21
+ output[index][relationName] = values[index];
22
+ index += 1;
23
+ }
24
+ }
25
+ return output;
26
+ }
27
+ async function resolveRelationValues(database, sourceRows, relation) {
28
+ if (relation.relationKind === 'hasManyThrough') {
29
+ return loadHasManyThroughValues(database, sourceRows, relation);
30
+ }
31
+ return loadDirectRelationValues(database, sourceRows, relation);
32
+ }
33
+ async function loadDirectRelationValues(database, sourceRows, relation) {
34
+ if (sourceRows.length === 0) {
35
+ return [];
36
+ }
37
+ let sourceTuples = uniqueTuples(sourceRows, relation.sourceKey);
38
+ if (sourceTuples.length === 0) {
39
+ return sourceRows.map(() => (relation.cardinality === 'many' ? [] : null));
40
+ }
41
+ let query = createQuery(relation.targetTable);
42
+ let linkPredicate = buildLinkPredicate(relation.targetKey, sourceTuples);
43
+ if (linkPredicate) {
44
+ query = query.where(linkPredicate);
45
+ }
46
+ query = applyRelationModifiers(query, relation, {
47
+ includePagination: false,
48
+ });
49
+ let relatedRows = await loadRowsWithRelationsForQuery(database, query);
50
+ let grouped = groupRowsByTuple(relatedRows, relation.targetKey);
51
+ return sourceRows.map((sourceRow) => {
52
+ let key = getCompositeKey(sourceRow, relation.sourceKey);
53
+ let matches = grouped.get(key) ?? [];
54
+ let pagedMatches = applyPagination(matches, relation.modifiers.limit, relation.modifiers.offset);
55
+ if (relation.cardinality === 'many') {
56
+ return pagedMatches;
57
+ }
58
+ return pagedMatches[0] ?? null;
59
+ });
60
+ }
61
+ async function loadHasManyThroughValues(database, sourceRows, relation) {
62
+ if (!relation.through) {
63
+ throw new DataTableQueryError('hasManyThrough relation is missing through metadata');
64
+ }
65
+ if (sourceRows.length === 0) {
66
+ return [];
67
+ }
68
+ let throughRelation = relation.through.relation;
69
+ let sourceTuples = uniqueTuples(sourceRows, throughRelation.sourceKey);
70
+ if (sourceTuples.length === 0) {
71
+ return sourceRows.map(() => []);
72
+ }
73
+ let throughQuery = createQuery(throughRelation.targetTable);
74
+ let throughPredicate = buildLinkPredicate(throughRelation.targetKey, sourceTuples);
75
+ if (throughPredicate) {
76
+ throughQuery = throughQuery.where(throughPredicate);
77
+ }
78
+ throughQuery = applyRelationModifiers(throughQuery, throughRelation, {
79
+ includePagination: false,
80
+ });
81
+ let throughRows = await loadRowsWithRelationsForQuery(database, throughQuery);
82
+ if (throughRows.length === 0) {
83
+ return sourceRows.map(() => []);
84
+ }
85
+ let throughRowsBySource = groupRowsByTuple(throughRows, throughRelation.targetKey);
86
+ let pagedThroughRowsBySource = new Map();
87
+ let pagedThroughRows = [];
88
+ for (let sourceRow of sourceRows) {
89
+ let sourceKey = getCompositeKey(sourceRow, throughRelation.sourceKey);
90
+ let matchedThroughRows = throughRowsBySource.get(sourceKey) ?? [];
91
+ let pagedMatchedRows = applyPagination(matchedThroughRows, throughRelation.modifiers.limit, throughRelation.modifiers.offset);
92
+ pagedThroughRowsBySource.set(sourceKey, pagedMatchedRows);
93
+ pagedThroughRows.push(...pagedMatchedRows);
94
+ }
95
+ let throughTuples = uniqueTuples(pagedThroughRows, relation.through.throughSourceKey);
96
+ if (throughTuples.length === 0) {
97
+ return sourceRows.map(() => []);
98
+ }
99
+ let targetQuery = createQuery(relation.targetTable);
100
+ let targetPredicate = buildLinkPredicate(relation.through.throughTargetKey, throughTuples);
101
+ if (targetPredicate) {
102
+ targetQuery = targetQuery.where(targetPredicate);
103
+ }
104
+ targetQuery = applyRelationModifiers(targetQuery, relation, {
105
+ includePagination: false,
106
+ });
107
+ let relatedRows = await loadRowsWithRelationsForQuery(database, targetQuery);
108
+ let targetRowsByThrough = groupRowsByTuple(relatedRows, relation.through.throughTargetKey);
109
+ return sourceRows.map((sourceRow) => {
110
+ let sourceKey = getCompositeKey(sourceRow, throughRelation.sourceKey);
111
+ let matchedThroughRows = pagedThroughRowsBySource.get(sourceKey) ?? [];
112
+ let outputRows = [];
113
+ let seen = new Set();
114
+ for (let throughRow of matchedThroughRows) {
115
+ let throughKey = getCompositeKey(throughRow, relation.through.throughSourceKey);
116
+ let rowsForThrough = targetRowsByThrough.get(throughKey) ?? [];
117
+ for (let row of rowsForThrough) {
118
+ let rowIdentity = getCompositeKey(row, getTablePrimaryKey(relation.targetTable));
119
+ if (!seen.has(rowIdentity)) {
120
+ seen.add(rowIdentity);
121
+ outputRows.push(row);
122
+ }
123
+ }
124
+ }
125
+ return applyPagination(outputRows, relation.modifiers.limit, relation.modifiers.offset);
126
+ });
127
+ }
128
+ function applyRelationModifiers(query, relation, options) {
129
+ let next = query;
130
+ for (let predicate of relation.modifiers.where) {
131
+ next = next.where(predicate);
132
+ }
133
+ for (let clause of relation.modifiers.orderBy) {
134
+ next = next.orderBy(clause.column, clause.direction);
135
+ }
136
+ if (options.includePagination && relation.modifiers.limit !== undefined) {
137
+ next = next.limit(relation.modifiers.limit);
138
+ }
139
+ if (options.includePagination && relation.modifiers.offset !== undefined) {
140
+ next = next.offset(relation.modifiers.offset);
141
+ }
142
+ if (Object.keys(relation.modifiers.with).length > 0) {
143
+ next = next.with(relation.modifiers.with);
144
+ }
145
+ return next;
146
+ }
147
+ function applyPagination(rows, limit, offset) {
148
+ let offsetRows = offset === undefined ? rows : rows.slice(offset);
149
+ return limit === undefined ? offsetRows : offsetRows.slice(0, limit);
150
+ }
151
+ function uniqueTuples(rows, columns) {
152
+ let output = [];
153
+ let seen = new Set();
154
+ for (let row of rows) {
155
+ let tuple = columns.map((column) => row[column]);
156
+ let key = tuple.map(stringifyForKey).join('::');
157
+ if (!seen.has(key)) {
158
+ seen.add(key);
159
+ output.push(tuple);
160
+ }
161
+ }
162
+ return output;
163
+ }
164
+ function buildLinkPredicate(targetColumns, tuples) {
165
+ if (tuples.length === 0) {
166
+ return undefined;
167
+ }
168
+ if (targetColumns.length === 1) {
169
+ return inList(targetColumns[0], tuples.map((tuple) => tuple[0]));
170
+ }
171
+ let tuplePredicates = tuples.map((tuple) => {
172
+ let comparisons = targetColumns.map((column, index) => eq(column, tuple[index]));
173
+ return and(...comparisons);
174
+ });
175
+ return or(...tuplePredicates);
176
+ }
177
+ function groupRowsByTuple(rows, columns) {
178
+ let output = new Map();
179
+ for (let row of rows) {
180
+ let key = getCompositeKey(row, columns);
181
+ let group = output.get(key);
182
+ if (group) {
183
+ group.push(row);
184
+ continue;
185
+ }
186
+ output.set(key, [row]);
187
+ }
188
+ return output;
189
+ }
190
+ function stringifyForKey(value) {
191
+ if (value === null) {
192
+ return 'null';
193
+ }
194
+ if (value === undefined) {
195
+ return 'undefined';
196
+ }
197
+ if (value instanceof Date) {
198
+ return 'date:' + value.toISOString();
199
+ }
200
+ if (typeof value === 'string') {
201
+ return JSON.stringify(value);
202
+ }
203
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
204
+ return String(value);
205
+ }
206
+ return JSON.stringify(value);
207
+ }
@@ -0,0 +1,13 @@
1
+ import type { DatabaseAdapter, ReturningSelection } from '../adapter.ts';
2
+ import type { ReturningInput } from '../database.ts';
3
+ import type { AnyRelation, AnyTable, TableAfterDeleteContext, TableAfterWriteContext, TableBeforeDeleteContext, TableRow, TableWriteOperation } from '../table.ts';
4
+ export declare function prepareInsertValues<table extends AnyTable>(table: table, values: Partial<TableRow<table>>, now: unknown, touch: boolean): Record<string, unknown>;
5
+ export declare function prepareUpdateValues<table extends AnyTable>(table: table, values: Partial<TableRow<table>>, now: unknown, touch: boolean, operation?: TableWriteOperation): Record<string, unknown>;
6
+ export declare function applyAfterReadHooksToRows<table extends AnyTable>(table: table, rows: Record<string, unknown>[]): Record<string, unknown>[];
7
+ export declare function applyAfterReadHooksToLoadedRows(table: AnyTable, rows: Record<string, unknown>[], relationMap: Record<string, AnyRelation>): Record<string, unknown>[];
8
+ export declare function runBeforeDeleteHook<table extends AnyTable>(table: table, context: TableBeforeDeleteContext): void;
9
+ export declare function runAfterWriteHook<table extends AnyTable>(table: table, context: TableAfterWriteContext<TableRow<table>>): void;
10
+ export declare function runAfterDeleteHook<table extends AnyTable>(table: table, context: TableAfterDeleteContext): void;
11
+ export declare function assertReturningCapability<row extends Record<string, unknown>>(adapter: DatabaseAdapter, operation: 'insert' | 'insertMany' | 'update' | 'delete' | 'upsert', returning: ReturningInput<row> | undefined): void;
12
+ export declare function normalizeReturningSelection<row extends Record<string, unknown>>(returning: ReturningInput<row>): ReturningSelection;
13
+ //# sourceMappingURL=write-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/lib/database/write-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAExE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EAExB,QAAQ,EACR,mBAAmB,EAEpB,MAAM,aAAa,CAAA;AAqBpB,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,QAAQ,EACxD,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAChC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,OAAO,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyBzB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,QAAQ,EACxD,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAChC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,OAAO,EACd,SAAS,GAAE,mBAA8B,GACxC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiBzB;AAED,wBAAgB,yBAAyB,CAAC,KAAK,SAAS,QAAQ,EAC9D,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAoC3B;AAED,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,QAAQ,EACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GACvC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CA6C3B;AAED,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,QAAQ,EACxD,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,wBAAwB,GAChC,IAAI,CA6BN;AAED,wBAAgB,iBAAiB,CAAC,KAAK,SAAS,QAAQ,EACtD,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,GAC/C,IAAI,CAcN;AAED,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,QAAQ,EACvD,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,uBAAuB,GAC/B,IAAI,CASN;AAED,wBAAgB,yBAAyB,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3E,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,QAAQ,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACnE,SAAS,EAAE,cAAc,CAAC,GAAG,CAAC,GAAG,SAAS,GACzC,IAAI,CAIN;AAED,wBAAgB,2BAA2B,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7E,SAAS,EAAE,cAAc,CAAC,GAAG,CAAC,GAC7B,kBAAkB,CAMpB"}