@remix-run/data-table 0.1.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.
- package/README.md +306 -55
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/lib/adapter.d.ts +386 -16
- package/dist/lib/adapter.d.ts.map +1 -1
- package/dist/lib/column.d.ts +193 -0
- package/dist/lib/column.d.ts.map +1 -0
- package/dist/lib/column.js +302 -0
- package/dist/lib/database/execution-context.d.ts +10 -0
- package/dist/lib/database/execution-context.d.ts.map +1 -0
- package/dist/lib/database/execution-context.js +1 -0
- package/dist/lib/database/helpers.d.ts +26 -0
- package/dist/lib/database/helpers.d.ts.map +1 -0
- package/dist/lib/database/helpers.js +116 -0
- package/dist/lib/database/query-execution.d.ts +7 -0
- package/dist/lib/database/query-execution.d.ts.map +1 -0
- package/dist/lib/database/query-execution.js +401 -0
- package/dist/lib/database/relations.d.ts +4 -0
- package/dist/lib/database/relations.d.ts.map +1 -0
- package/dist/lib/database/relations.js +207 -0
- package/dist/lib/database/write-lifecycle.d.ts +13 -0
- package/dist/lib/database/write-lifecycle.d.ts.map +1 -0
- package/dist/lib/database/write-lifecycle.js +279 -0
- package/dist/lib/database.d.ts +141 -238
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +73 -1122
- package/dist/lib/errors.d.ts +9 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +9 -0
- package/dist/lib/migrations/filename.d.ts +12 -0
- package/dist/lib/migrations/filename.d.ts.map +1 -0
- package/dist/lib/migrations/filename.js +20 -0
- package/dist/lib/migrations/helpers.d.ts +11 -0
- package/dist/lib/migrations/helpers.d.ts.map +1 -0
- package/dist/lib/migrations/helpers.js +77 -0
- package/dist/lib/migrations/journal-store.d.ts +15 -0
- package/dist/lib/migrations/journal-store.d.ts.map +1 -0
- package/dist/lib/migrations/journal-store.js +83 -0
- package/dist/lib/migrations/registry.d.ts +27 -0
- package/dist/lib/migrations/registry.d.ts.map +1 -0
- package/dist/lib/migrations/registry.js +51 -0
- package/dist/lib/migrations/runner.d.ts +20 -0
- package/dist/lib/migrations/runner.d.ts.map +1 -0
- package/dist/lib/migrations/runner.js +273 -0
- package/dist/lib/migrations/schema-api.d.ts +7 -0
- package/dist/lib/migrations/schema-api.d.ts.map +1 -0
- package/dist/lib/migrations/schema-api.js +326 -0
- package/dist/lib/migrations-node.d.ts +17 -0
- package/dist/lib/migrations-node.d.ts.map +1 -0
- package/dist/lib/migrations-node.js +65 -0
- package/dist/lib/migrations.d.ts +292 -0
- package/dist/lib/migrations.d.ts.map +1 -0
- package/dist/lib/migrations.js +38 -0
- package/dist/lib/operators.d.ts +3 -0
- package/dist/lib/operators.d.ts.map +1 -1
- package/dist/lib/query.d.ts +159 -0
- package/dist/lib/query.d.ts.map +1 -0
- package/dist/lib/query.js +401 -0
- package/dist/lib/references.d.ts +0 -1
- package/dist/lib/references.d.ts.map +1 -1
- package/dist/lib/sql-helpers.d.ts +50 -0
- package/dist/lib/sql-helpers.d.ts.map +1 -0
- package/dist/lib/sql-helpers.js +111 -0
- package/dist/lib/sql.d.ts +23 -6
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/sql.js +19 -5
- package/dist/lib/table.d.ts +355 -40
- package/dist/lib/table.d.ts.map +1 -1
- package/dist/lib/table.js +113 -90
- package/dist/migrations/node.d.ts +2 -0
- package/dist/migrations/node.d.ts.map +1 -0
- package/dist/migrations/node.js +1 -0
- package/dist/migrations.d.ts +8 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +5 -0
- package/dist/operators.d.ts +3 -0
- package/dist/operators.d.ts.map +1 -0
- package/dist/operators.js +1 -0
- package/dist/sql-helpers.d.ts +3 -0
- package/dist/sql-helpers.d.ts.map +1 -0
- package/dist/sql-helpers.js +1 -0
- package/package.json +23 -8
- package/src/index.ts +93 -10
- package/src/lib/adapter.ts +469 -25
- package/src/lib/column.ts +384 -0
- package/src/lib/database/execution-context.ts +15 -0
- package/src/lib/database/helpers.ts +216 -0
- package/src/lib/database/query-execution.ts +638 -0
- package/src/lib/database/relations.ts +332 -0
- package/src/lib/database/write-lifecycle.ts +487 -0
- package/src/lib/database.ts +246 -1848
- package/src/lib/errors.ts +10 -0
- package/src/lib/migrations/filename.ts +25 -0
- package/src/lib/migrations/helpers.ts +108 -0
- package/src/lib/migrations/journal-store.ts +122 -0
- package/src/lib/migrations/registry.ts +62 -0
- package/src/lib/migrations/runner.ts +374 -0
- package/src/lib/migrations/schema-api.ts +417 -0
- package/src/lib/migrations-node.ts +71 -0
- package/src/lib/migrations.ts +328 -0
- package/src/lib/operators.ts +3 -0
- package/src/lib/query.ts +958 -0
- package/src/lib/references.ts +0 -1
- package/src/lib/sql-helpers.ts +146 -0
- package/src/lib/sql.ts +23 -6
- package/src/lib/table.ts +484 -156
- package/src/migrations/node.ts +1 -0
- package/src/migrations.ts +26 -0
- package/src/operators.ts +18 -0
- package/src/sql-helpers.ts +9 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { DataTableQueryError, DataTableValidationError } from "../errors.js";
|
|
2
|
+
import { getTableAfterDelete, getTableAfterRead, getTableAfterWrite, getTableBeforeDelete, getTableBeforeWrite, getTableColumns, getTableName, getTableTimestamps, getTableValidator, } from "../table.js";
|
|
3
|
+
export function prepareInsertValues(table, values, now, touch) {
|
|
4
|
+
let output = validateWriteValues(table, values, 'create');
|
|
5
|
+
let timestamps = getTableTimestamps(table);
|
|
6
|
+
let columns = getTableColumns(table);
|
|
7
|
+
if (touch && timestamps) {
|
|
8
|
+
let createdAt = timestamps.createdAt;
|
|
9
|
+
let updatedAt = timestamps.updatedAt;
|
|
10
|
+
if (Object.prototype.hasOwnProperty.call(columns, createdAt) &&
|
|
11
|
+
output[createdAt] === undefined) {
|
|
12
|
+
output[createdAt] = now;
|
|
13
|
+
}
|
|
14
|
+
if (Object.prototype.hasOwnProperty.call(columns, updatedAt) &&
|
|
15
|
+
output[updatedAt] === undefined) {
|
|
16
|
+
output[updatedAt] = now;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return output;
|
|
20
|
+
}
|
|
21
|
+
export function prepareUpdateValues(table, values, now, touch, operation = 'update') {
|
|
22
|
+
let output = validateWriteValues(table, values, operation);
|
|
23
|
+
let timestamps = getTableTimestamps(table);
|
|
24
|
+
let columns = getTableColumns(table);
|
|
25
|
+
if (touch && timestamps) {
|
|
26
|
+
let updatedAt = timestamps.updatedAt;
|
|
27
|
+
if (Object.prototype.hasOwnProperty.call(columns, updatedAt) &&
|
|
28
|
+
output[updatedAt] === undefined) {
|
|
29
|
+
output[updatedAt] = now;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return output;
|
|
33
|
+
}
|
|
34
|
+
export function applyAfterReadHooksToRows(table, rows) {
|
|
35
|
+
let callback = getTableAfterRead(table);
|
|
36
|
+
if (!callback || rows.length === 0) {
|
|
37
|
+
return rows;
|
|
38
|
+
}
|
|
39
|
+
let tableName = getTableName(table);
|
|
40
|
+
return rows.map((row) => {
|
|
41
|
+
let callbackResult = callback({
|
|
42
|
+
tableName,
|
|
43
|
+
value: row,
|
|
44
|
+
});
|
|
45
|
+
assertSynchronousCallbackResult(tableName, 'read', 'afterRead', callbackResult);
|
|
46
|
+
if (hasIssues(callbackResult)) {
|
|
47
|
+
throwValidationIssues(tableName, callbackResult.issues, 'read', 'afterRead');
|
|
48
|
+
}
|
|
49
|
+
if (!hasValue(callbackResult)) {
|
|
50
|
+
throw new DataTableValidationError('Invalid afterRead callback result for table "' + tableName + '"', [{ message: 'Expected afterRead to return { value } or { issues }' }], {
|
|
51
|
+
metadata: {
|
|
52
|
+
table: tableName,
|
|
53
|
+
operation: 'read',
|
|
54
|
+
source: 'afterRead',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return normalizeReadObject(tableName, callbackResult.value);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function applyAfterReadHooksToLoadedRows(table, rows, relationMap) {
|
|
62
|
+
if (rows.length === 0) {
|
|
63
|
+
return rows;
|
|
64
|
+
}
|
|
65
|
+
let relationNames = Object.keys(relationMap);
|
|
66
|
+
if (relationNames.length > 0) {
|
|
67
|
+
for (let row of rows) {
|
|
68
|
+
for (let relationName of relationNames) {
|
|
69
|
+
let relation = relationMap[relationName];
|
|
70
|
+
let relationValue = row[relationName];
|
|
71
|
+
if (relation.cardinality === 'many') {
|
|
72
|
+
if (!Array.isArray(relationValue)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
row[relationName] = applyAfterReadHooksToLoadedRows(relation.targetTable, relationValue, relation.modifiers.with);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (relationValue === null || relationValue === undefined) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (typeof relationValue !== 'object' || Array.isArray(relationValue)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
let transformed = applyAfterReadHooksToLoadedRows(relation.targetTable, [relationValue], relation.modifiers.with);
|
|
85
|
+
row[relationName] = transformed[0] ?? null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return applyAfterReadHooksToRows(table, rows);
|
|
90
|
+
}
|
|
91
|
+
export function runBeforeDeleteHook(table, context) {
|
|
92
|
+
let callback = getTableBeforeDelete(table);
|
|
93
|
+
if (!callback) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
let callbackResult = callback(context);
|
|
97
|
+
assertSynchronousCallbackResult(context.tableName, 'delete', 'beforeDelete', callbackResult);
|
|
98
|
+
if (callbackResult === undefined) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (hasIssues(callbackResult)) {
|
|
102
|
+
throwValidationIssues(context.tableName, callbackResult.issues, 'delete', 'beforeDelete');
|
|
103
|
+
}
|
|
104
|
+
throw new DataTableValidationError('Invalid beforeDelete callback result for table "' + context.tableName + '"', [{ message: 'Expected beforeDelete to return nothing or { issues }' }], {
|
|
105
|
+
metadata: {
|
|
106
|
+
table: context.tableName,
|
|
107
|
+
operation: 'delete',
|
|
108
|
+
source: 'beforeDelete',
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export function runAfterWriteHook(table, context) {
|
|
113
|
+
let callback = getTableAfterWrite(table);
|
|
114
|
+
if (!callback) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
let callbackResult = callback(context);
|
|
118
|
+
assertSynchronousCallbackResult(context.tableName, context.operation, 'afterWrite', callbackResult);
|
|
119
|
+
}
|
|
120
|
+
export function runAfterDeleteHook(table, context) {
|
|
121
|
+
let callback = getTableAfterDelete(table);
|
|
122
|
+
if (!callback) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
let callbackResult = callback(context);
|
|
126
|
+
assertSynchronousCallbackResult(context.tableName, 'delete', 'afterDelete', callbackResult);
|
|
127
|
+
}
|
|
128
|
+
export function assertReturningCapability(adapter, operation, returning) {
|
|
129
|
+
if (returning && !adapter.capabilities.returning) {
|
|
130
|
+
throw new DataTableQueryError(operation + '() returning is not supported by this adapter');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function normalizeReturningSelection(returning) {
|
|
134
|
+
if (returning === '*') {
|
|
135
|
+
return '*';
|
|
136
|
+
}
|
|
137
|
+
return [...returning];
|
|
138
|
+
}
|
|
139
|
+
function validateWriteValues(table, values, operation) {
|
|
140
|
+
let tableName = getTableName(table);
|
|
141
|
+
let normalizedInput = normalizeWriteObject(table, values, operation);
|
|
142
|
+
let beforeWrite = getTableBeforeWrite(table);
|
|
143
|
+
if (beforeWrite) {
|
|
144
|
+
let beforeWriteResult = beforeWrite({
|
|
145
|
+
operation,
|
|
146
|
+
tableName,
|
|
147
|
+
value: normalizedInput,
|
|
148
|
+
});
|
|
149
|
+
assertSynchronousCallbackResult(tableName, operation, 'beforeWrite', beforeWriteResult);
|
|
150
|
+
if (hasIssues(beforeWriteResult)) {
|
|
151
|
+
throwValidationIssues(tableName, beforeWriteResult.issues, operation, 'beforeWrite');
|
|
152
|
+
}
|
|
153
|
+
if (!hasValue(beforeWriteResult)) {
|
|
154
|
+
throw new DataTableValidationError('Invalid beforeWrite callback result for table "' + tableName + '"', [{ message: 'Expected beforeWrite to return { value } or { issues }' }], {
|
|
155
|
+
metadata: {
|
|
156
|
+
table: tableName,
|
|
157
|
+
operation,
|
|
158
|
+
source: 'beforeWrite',
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
normalizedInput = normalizeWriteObject(table, beforeWriteResult.value, operation, 'beforeWrite');
|
|
163
|
+
}
|
|
164
|
+
let validator = getTableValidator(table);
|
|
165
|
+
if (!validator) {
|
|
166
|
+
return normalizedInput;
|
|
167
|
+
}
|
|
168
|
+
let validationResult = validator({
|
|
169
|
+
operation,
|
|
170
|
+
tableName,
|
|
171
|
+
value: normalizedInput,
|
|
172
|
+
});
|
|
173
|
+
assertSynchronousCallbackResult(tableName, operation, 'validate', validationResult);
|
|
174
|
+
if (hasIssues(validationResult)) {
|
|
175
|
+
throwValidationIssues(tableName, validationResult.issues, operation, 'validate');
|
|
176
|
+
}
|
|
177
|
+
if (!hasValue(validationResult)) {
|
|
178
|
+
throw new DataTableValidationError('Invalid validator result for table "' + tableName + '"', [{ message: 'Expected validator to return { value } or { issues }' }], {
|
|
179
|
+
metadata: {
|
|
180
|
+
table: tableName,
|
|
181
|
+
operation,
|
|
182
|
+
source: 'validate',
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return normalizeWriteObject(table, validationResult.value, operation, 'validate');
|
|
187
|
+
}
|
|
188
|
+
function hasIssues(value) {
|
|
189
|
+
return typeof value === 'object' && value !== null && 'issues' in value;
|
|
190
|
+
}
|
|
191
|
+
function hasValue(value) {
|
|
192
|
+
return typeof value === 'object' && value !== null && 'value' in value;
|
|
193
|
+
}
|
|
194
|
+
function normalizeWriteObject(table, value, operation, source) {
|
|
195
|
+
let tableName = getTableName(table);
|
|
196
|
+
let columns = getTableColumns(table);
|
|
197
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
198
|
+
throw new DataTableValidationError('Invalid value for table "' + tableName + '"', [{ message: 'Expected object' }], {
|
|
199
|
+
metadata: {
|
|
200
|
+
table: tableName,
|
|
201
|
+
operation,
|
|
202
|
+
...(source ? { source } : {}),
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
let output = {};
|
|
207
|
+
for (let key in value) {
|
|
208
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (!Object.prototype.hasOwnProperty.call(columns, key)) {
|
|
212
|
+
throw new DataTableValidationError('Unknown column "' + key + '" for table "' + tableName + '"', [], {
|
|
213
|
+
metadata: {
|
|
214
|
+
table: tableName,
|
|
215
|
+
column: key,
|
|
216
|
+
operation,
|
|
217
|
+
...(source ? { source } : {}),
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
output[key] = value[key];
|
|
222
|
+
}
|
|
223
|
+
return output;
|
|
224
|
+
}
|
|
225
|
+
function throwValidationIssues(tableName, issues, operation, source) {
|
|
226
|
+
let firstIssue = issues[0];
|
|
227
|
+
let issuePath = firstIssue?.path;
|
|
228
|
+
let firstPathSegment = issuePath && issuePath.length > 0 ? issuePath[0] : undefined;
|
|
229
|
+
let column = typeof firstPathSegment === 'string' ? firstPathSegment : undefined;
|
|
230
|
+
if (column) {
|
|
231
|
+
throw new DataTableValidationError('Invalid value for column "' + column + '" in table "' + tableName + '"', issues, {
|
|
232
|
+
metadata: {
|
|
233
|
+
table: tableName,
|
|
234
|
+
column,
|
|
235
|
+
operation,
|
|
236
|
+
...(source ? { source } : {}),
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
throw new DataTableValidationError('Invalid value for table "' + tableName + '"', issues, {
|
|
241
|
+
metadata: {
|
|
242
|
+
table: tableName,
|
|
243
|
+
operation,
|
|
244
|
+
...(source ? { source } : {}),
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
function assertSynchronousCallbackResult(tableName, operation, callbackName, value) {
|
|
249
|
+
if (!isPromiseLike(value)) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
throw new DataTableValidationError('Invalid ' + callbackName + ' callback result for table "' + tableName + '"', [{ message: callbackName + ' callbacks must be synchronous and cannot return a Promise' }], {
|
|
253
|
+
metadata: {
|
|
254
|
+
table: tableName,
|
|
255
|
+
operation,
|
|
256
|
+
source: callbackName,
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function isPromiseLike(value) {
|
|
261
|
+
return ((typeof value === 'object' || typeof value === 'function') &&
|
|
262
|
+
value !== null &&
|
|
263
|
+
'then' in value &&
|
|
264
|
+
typeof value.then === 'function');
|
|
265
|
+
}
|
|
266
|
+
function normalizeReadObject(tableName, value) {
|
|
267
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
268
|
+
throw new DataTableValidationError('Invalid afterRead callback result for table "' + tableName + '"', [{ message: 'Expected afterRead to return an object value' }], {
|
|
269
|
+
metadata: {
|
|
270
|
+
table: tableName,
|
|
271
|
+
operation: 'read',
|
|
272
|
+
source: 'afterRead',
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
...value,
|
|
278
|
+
};
|
|
279
|
+
}
|