@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.
- package/LICENSE +21 -0
- package/README.md +549 -2
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/lib/adapter.d.ts +550 -0
- package/dist/lib/adapter.d.ts.map +1 -0
- package/dist/lib/adapter.js +1 -0
- 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 +264 -0
- package/dist/lib/database.d.ts.map +1 -0
- package/dist/lib/database.js +319 -0
- package/dist/lib/errors.d.ts +59 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +76 -0
- package/dist/lib/inflection.d.ts +3 -0
- package/dist/lib/inflection.d.ts.map +1 -0
- package/dist/lib/inflection.js +56 -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 +154 -0
- package/dist/lib/operators.d.ts.map +1 -0
- package/dist/lib/operators.js +218 -0
- 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 +41 -0
- package/dist/lib/references.d.ts.map +1 -0
- package/dist/lib/references.js +33 -0
- 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 +45 -0
- package/dist/lib/sql.d.ts.map +1 -0
- package/dist/lib/sql.js +65 -0
- package/dist/lib/table.d.ts +569 -0
- package/dist/lib/table.d.ts.map +1 -0
- package/dist/lib/table.js +519 -0
- package/dist/lib/types.d.ts +4 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +1 -0
- 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 +56 -7
- package/src/index.ts +198 -0
- package/src/lib/adapter.ts +653 -0
- 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 +856 -0
- package/src/lib/errors.ts +119 -0
- package/src/lib/inflection.ts +69 -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 +436 -0
- package/src/lib/query.ts +958 -0
- package/src/lib/references.ts +78 -0
- package/src/lib/sql-helpers.ts +146 -0
- package/src/lib/sql.ts +84 -0
- package/src/lib/table.ts +1309 -0
- package/src/lib/types.ts +3 -0
- 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,519 @@
|
|
|
1
|
+
import { ColumnBuilder } from "./column.js";
|
|
2
|
+
import { inferForeignKey } from "./inflection.js";
|
|
3
|
+
import { normalizeWhereInput } from "./operators.js";
|
|
4
|
+
import { columnMetadataKey, normalizeColumnInput, tableMetadataKey } from "./references.js";
|
|
5
|
+
/**
|
|
6
|
+
* Symbol key used to store non-enumerable table metadata.
|
|
7
|
+
*/
|
|
8
|
+
export { columnMetadataKey, tableMetadataKey } from "./references.js";
|
|
9
|
+
/**
|
|
10
|
+
* Creates a plain table reference snapshot from a table instance.
|
|
11
|
+
* @param table Source table instance.
|
|
12
|
+
* @returns Table metadata snapshot.
|
|
13
|
+
*/
|
|
14
|
+
export function getTableReference(table) {
|
|
15
|
+
let metadata = table[tableMetadataKey];
|
|
16
|
+
return {
|
|
17
|
+
kind: 'table',
|
|
18
|
+
name: metadata.name,
|
|
19
|
+
columns: metadata.columns,
|
|
20
|
+
primaryKey: metadata.primaryKey,
|
|
21
|
+
timestamps: metadata.timestamps,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Returns a table's SQL name.
|
|
26
|
+
* @param table Source table instance.
|
|
27
|
+
* @returns Table SQL name.
|
|
28
|
+
*/
|
|
29
|
+
export function getTableName(table) {
|
|
30
|
+
return table[tableMetadataKey].name;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Returns a table's column builder map.
|
|
34
|
+
* @param table Source table instance.
|
|
35
|
+
* @returns Table column builder map.
|
|
36
|
+
*/
|
|
37
|
+
export function getTableColumns(table) {
|
|
38
|
+
return table[tableMetadataKey].columns;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns a table's resolved physical column definitions.
|
|
42
|
+
* @param table Source table instance.
|
|
43
|
+
* @returns Column definition map.
|
|
44
|
+
*/
|
|
45
|
+
export function getTableColumnDefinitions(table) {
|
|
46
|
+
return table[tableMetadataKey].columnDefinitions;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns a table's optional write validator.
|
|
50
|
+
* @param table Source table instance.
|
|
51
|
+
* @returns Validation function or `undefined`.
|
|
52
|
+
*/
|
|
53
|
+
export function getTableValidator(table) {
|
|
54
|
+
return table[tableMetadataKey].validate;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Returns a table's optional before-write lifecycle callback.
|
|
58
|
+
* @param table Source table instance.
|
|
59
|
+
* @returns Before-write callback or `undefined`.
|
|
60
|
+
*/
|
|
61
|
+
export function getTableBeforeWrite(table) {
|
|
62
|
+
return table[tableMetadataKey].beforeWrite;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Returns a table's optional after-write lifecycle callback.
|
|
66
|
+
* @param table Source table instance.
|
|
67
|
+
* @returns After-write callback or `undefined`.
|
|
68
|
+
*/
|
|
69
|
+
export function getTableAfterWrite(table) {
|
|
70
|
+
return table[tableMetadataKey].afterWrite;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns a table's optional before-delete lifecycle callback.
|
|
74
|
+
* @param table Source table instance.
|
|
75
|
+
* @returns Before-delete callback or `undefined`.
|
|
76
|
+
*/
|
|
77
|
+
export function getTableBeforeDelete(table) {
|
|
78
|
+
return table[tableMetadataKey].beforeDelete;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns a table's optional after-delete lifecycle callback.
|
|
82
|
+
* @param table Source table instance.
|
|
83
|
+
* @returns After-delete callback or `undefined`.
|
|
84
|
+
*/
|
|
85
|
+
export function getTableAfterDelete(table) {
|
|
86
|
+
return table[tableMetadataKey].afterDelete;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Returns a table's optional after-read lifecycle callback.
|
|
90
|
+
* The callback receives the current read shape, which may be a projected partial row.
|
|
91
|
+
* @param table Source table instance.
|
|
92
|
+
* @returns After-read callback or `undefined`.
|
|
93
|
+
*/
|
|
94
|
+
export function getTableAfterRead(table) {
|
|
95
|
+
return table[tableMetadataKey].afterRead;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Returns a table's primary key columns.
|
|
99
|
+
* @param table Source table instance.
|
|
100
|
+
* @returns Primary key columns.
|
|
101
|
+
*/
|
|
102
|
+
export function getTablePrimaryKey(table) {
|
|
103
|
+
return table[tableMetadataKey].primaryKey;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Returns a table's resolved timestamp configuration.
|
|
107
|
+
* @param table Source table instance.
|
|
108
|
+
* @returns Timestamp configuration or `null`.
|
|
109
|
+
*/
|
|
110
|
+
export function getTableTimestamps(table) {
|
|
111
|
+
return table[tableMetadataKey].timestamps;
|
|
112
|
+
}
|
|
113
|
+
const defaultTimestampConfig = {
|
|
114
|
+
createdAt: 'created_at',
|
|
115
|
+
updatedAt: 'updated_at',
|
|
116
|
+
};
|
|
117
|
+
export function fail(messageOrIssues, path) {
|
|
118
|
+
if (typeof messageOrIssues === 'string') {
|
|
119
|
+
return {
|
|
120
|
+
issues: [{ message: messageOrIssues, path }],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
issues: [...messageOrIssues],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Creates a table object with symbol-backed metadata and direct column references.
|
|
129
|
+
* @param options Table declaration options.
|
|
130
|
+
* @returns A frozen table object.
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* import { column as c, table } from 'remix/data-table'
|
|
134
|
+
*
|
|
135
|
+
* let users = table({
|
|
136
|
+
* name: 'users',
|
|
137
|
+
* columns: {
|
|
138
|
+
* id: c.integer(),
|
|
139
|
+
* email: c.varchar(255),
|
|
140
|
+
* },
|
|
141
|
+
* primaryKey: 'id',
|
|
142
|
+
* })
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export function table(options) {
|
|
146
|
+
let tableName = options.name;
|
|
147
|
+
let columns = options.columns;
|
|
148
|
+
let resolvedPrimaryKey = normalizePrimaryKey(tableName, columns, options.primaryKey);
|
|
149
|
+
let timestampConfig = normalizeTimestampConfig(options.timestamps);
|
|
150
|
+
let columnDefinitions = resolveTableColumns(tableName, columns);
|
|
151
|
+
let table = Object.create(null);
|
|
152
|
+
Object.defineProperty(table, tableMetadataKey, {
|
|
153
|
+
value: Object.freeze({
|
|
154
|
+
name: tableName,
|
|
155
|
+
columns,
|
|
156
|
+
primaryKey: resolvedPrimaryKey,
|
|
157
|
+
timestamps: timestampConfig,
|
|
158
|
+
columnDefinitions,
|
|
159
|
+
beforeWrite: options.beforeWrite,
|
|
160
|
+
afterWrite: options.afterWrite,
|
|
161
|
+
beforeDelete: options.beforeDelete,
|
|
162
|
+
afterDelete: options.afterDelete,
|
|
163
|
+
afterRead: options.afterRead,
|
|
164
|
+
validate: options.validate,
|
|
165
|
+
}),
|
|
166
|
+
enumerable: false,
|
|
167
|
+
writable: false,
|
|
168
|
+
configurable: false,
|
|
169
|
+
});
|
|
170
|
+
for (let columnName in columns) {
|
|
171
|
+
if (!Object.prototype.hasOwnProperty.call(columns, columnName)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
let column = createColumnReference(tableName, columnName);
|
|
175
|
+
Object.defineProperty(table, columnName, {
|
|
176
|
+
value: column,
|
|
177
|
+
enumerable: true,
|
|
178
|
+
writable: false,
|
|
179
|
+
configurable: false,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return Object.freeze(table);
|
|
183
|
+
}
|
|
184
|
+
function createColumnReference(tableName, columnName) {
|
|
185
|
+
return Object.freeze({
|
|
186
|
+
kind: 'column',
|
|
187
|
+
[columnMetadataKey]: Object.freeze({
|
|
188
|
+
tableName,
|
|
189
|
+
columnName,
|
|
190
|
+
qualifiedName: tableName + '.' + columnName,
|
|
191
|
+
}),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function resolveTableColumns(tableName, columns) {
|
|
195
|
+
let columnDefinitions = {};
|
|
196
|
+
for (let columnName in columns) {
|
|
197
|
+
if (!Object.prototype.hasOwnProperty.call(columns, columnName)) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
let column = columns[columnName];
|
|
201
|
+
if (!(column instanceof ColumnBuilder)) {
|
|
202
|
+
throw new Error('Invalid column "' +
|
|
203
|
+
columnName +
|
|
204
|
+
'" for table "' +
|
|
205
|
+
tableName +
|
|
206
|
+
'". Expected a column(...) builder');
|
|
207
|
+
}
|
|
208
|
+
columnDefinitions[columnName] = column.build();
|
|
209
|
+
}
|
|
210
|
+
return Object.freeze(columnDefinitions);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Defines a one-to-many relation from `source` to `target`.
|
|
214
|
+
* @param source Source table.
|
|
215
|
+
* @param target Target table.
|
|
216
|
+
* @param relationOptions Relation key configuration.
|
|
217
|
+
* @returns A relation descriptor.
|
|
218
|
+
*/
|
|
219
|
+
export function hasMany(source, target, relationOptions) {
|
|
220
|
+
let sourceKey = normalizeKeySelector(source, relationOptions?.targetKey, 'targetKey', getTablePrimaryKey(source));
|
|
221
|
+
let targetKey = normalizeKeySelector(target, relationOptions?.foreignKey, 'foreignKey', [
|
|
222
|
+
inferForeignKey(getTableName(source)),
|
|
223
|
+
]);
|
|
224
|
+
assertKeyLengths(getTableName(source), getTableName(target), sourceKey, targetKey);
|
|
225
|
+
return createRelation({
|
|
226
|
+
relationKind: 'hasMany',
|
|
227
|
+
cardinality: 'many',
|
|
228
|
+
sourceTable: source,
|
|
229
|
+
targetTable: target,
|
|
230
|
+
sourceKey,
|
|
231
|
+
targetKey,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Defines a one-to-one relation from `source` to `target` where the foreign key lives on `target`.
|
|
236
|
+
* @param source Source table.
|
|
237
|
+
* @param target Target table.
|
|
238
|
+
* @param relationOptions Relation key configuration.
|
|
239
|
+
* @returns A relation descriptor.
|
|
240
|
+
*/
|
|
241
|
+
export function hasOne(source, target, relationOptions) {
|
|
242
|
+
let sourceKey = normalizeKeySelector(source, relationOptions?.targetKey, 'targetKey', getTablePrimaryKey(source));
|
|
243
|
+
let targetKey = normalizeKeySelector(target, relationOptions?.foreignKey, 'foreignKey', [
|
|
244
|
+
inferForeignKey(getTableName(source)),
|
|
245
|
+
]);
|
|
246
|
+
assertKeyLengths(getTableName(source), getTableName(target), sourceKey, targetKey);
|
|
247
|
+
return createRelation({
|
|
248
|
+
relationKind: 'hasOne',
|
|
249
|
+
cardinality: 'one',
|
|
250
|
+
sourceTable: source,
|
|
251
|
+
targetTable: target,
|
|
252
|
+
sourceKey,
|
|
253
|
+
targetKey,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Defines a one-to-one relation from `source` to `target`.
|
|
258
|
+
* @param source Source table.
|
|
259
|
+
* @param target Target table.
|
|
260
|
+
* @param relationOptions Relation key configuration.
|
|
261
|
+
* @returns A relation descriptor.
|
|
262
|
+
*/
|
|
263
|
+
export function belongsTo(source, target, relationOptions) {
|
|
264
|
+
let sourceKey = normalizeKeySelector(source, relationOptions?.foreignKey, 'foreignKey', [
|
|
265
|
+
inferForeignKey(getTableName(target)),
|
|
266
|
+
]);
|
|
267
|
+
let targetKey = normalizeKeySelector(target, relationOptions?.targetKey, 'targetKey', getTablePrimaryKey(target));
|
|
268
|
+
assertKeyLengths(getTableName(source), getTableName(target), sourceKey, targetKey);
|
|
269
|
+
return createRelation({
|
|
270
|
+
relationKind: 'belongsTo',
|
|
271
|
+
cardinality: 'one',
|
|
272
|
+
sourceTable: source,
|
|
273
|
+
targetTable: target,
|
|
274
|
+
sourceKey,
|
|
275
|
+
targetKey,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Defines a one-to-many relation from `source` to `target` through an intermediate relation.
|
|
280
|
+
* @param source Source table.
|
|
281
|
+
* @param target Target table.
|
|
282
|
+
* @param relationOptions Through relation configuration.
|
|
283
|
+
* @returns A relation descriptor.
|
|
284
|
+
*/
|
|
285
|
+
export function hasManyThrough(source, target, relationOptions) {
|
|
286
|
+
let throughRelation = relationOptions.through;
|
|
287
|
+
if (throughRelation.sourceTable !== source) {
|
|
288
|
+
throw new Error('hasManyThrough expects a through relation whose source table matches ' +
|
|
289
|
+
getTableName(source));
|
|
290
|
+
}
|
|
291
|
+
let throughTargetKey = normalizeKeysForTable(throughRelation.targetTable, relationOptions.throughTargetKey, 'throughTargetKey', getTablePrimaryKey(throughRelation.targetTable));
|
|
292
|
+
let throughForeignKey = normalizeKeySelector(target, relationOptions.throughForeignKey, 'throughForeignKey', [inferForeignKey(getTableName(throughRelation.targetTable))]);
|
|
293
|
+
assertKeyLengths(getTableName(throughRelation.targetTable), getTableName(target), throughTargetKey, throughForeignKey);
|
|
294
|
+
return createRelation({
|
|
295
|
+
relationKind: 'hasManyThrough',
|
|
296
|
+
cardinality: 'many',
|
|
297
|
+
sourceTable: source,
|
|
298
|
+
targetTable: target,
|
|
299
|
+
sourceKey: [...throughRelation.sourceKey],
|
|
300
|
+
targetKey: [...throughRelation.targetKey],
|
|
301
|
+
through: {
|
|
302
|
+
relation: throughRelation,
|
|
303
|
+
throughSourceKey: throughTargetKey,
|
|
304
|
+
throughTargetKey: throughForeignKey,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Convenience helper for standard snake_case timestamp columns.
|
|
310
|
+
* @returns Column-builder map for `created_at`/`updated_at`.
|
|
311
|
+
*/
|
|
312
|
+
export function timestamps() {
|
|
313
|
+
let timestampColumn = () => new ColumnBuilder({ type: 'timestamp' });
|
|
314
|
+
return {
|
|
315
|
+
created_at: timestampColumn(),
|
|
316
|
+
updated_at: timestampColumn(),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Normalizes a primary-key input into an object keyed by primary-key columns.
|
|
321
|
+
* @param table Source table.
|
|
322
|
+
* @param value Primary-key input value.
|
|
323
|
+
* @returns Primary-key object.
|
|
324
|
+
*/
|
|
325
|
+
export function getPrimaryKeyObject(table, value) {
|
|
326
|
+
let keys = getTablePrimaryKey(table);
|
|
327
|
+
if (keys.length === 1 && (typeof value !== 'object' || value === null || Array.isArray(value))) {
|
|
328
|
+
let key = keys[0];
|
|
329
|
+
return { [key]: value };
|
|
330
|
+
}
|
|
331
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
332
|
+
throw new Error('Composite primary keys require an object value');
|
|
333
|
+
}
|
|
334
|
+
let objectValue = value;
|
|
335
|
+
let output = {};
|
|
336
|
+
for (let key of keys) {
|
|
337
|
+
if (!(key in objectValue)) {
|
|
338
|
+
throw new Error('Missing key "' + key + '" for primary key lookup on "' + getTableName(table) + '"');
|
|
339
|
+
}
|
|
340
|
+
;
|
|
341
|
+
output[key] = objectValue[key];
|
|
342
|
+
}
|
|
343
|
+
return output;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Builds a stable key for a row tuple.
|
|
347
|
+
* @param row Source row.
|
|
348
|
+
* @param columns Columns included in the tuple.
|
|
349
|
+
* @returns Stable tuple key.
|
|
350
|
+
*/
|
|
351
|
+
export function getCompositeKey(row, columns) {
|
|
352
|
+
let values = columns.map((column) => stableSerialize(row[column]));
|
|
353
|
+
return values.join('::');
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Serializes values into stable string representations for key generation.
|
|
357
|
+
* @param value Value to serialize.
|
|
358
|
+
* @returns Stable serialized value.
|
|
359
|
+
*/
|
|
360
|
+
export function stableSerialize(value) {
|
|
361
|
+
if (value === null) {
|
|
362
|
+
return 'null';
|
|
363
|
+
}
|
|
364
|
+
if (value === undefined) {
|
|
365
|
+
return 'undefined';
|
|
366
|
+
}
|
|
367
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
368
|
+
return String(value);
|
|
369
|
+
}
|
|
370
|
+
if (typeof value === 'string') {
|
|
371
|
+
return JSON.stringify(value);
|
|
372
|
+
}
|
|
373
|
+
if (value instanceof Date) {
|
|
374
|
+
return 'date:' + value.toISOString();
|
|
375
|
+
}
|
|
376
|
+
return JSON.stringify(value);
|
|
377
|
+
}
|
|
378
|
+
function normalizePrimaryKey(tableName, columns, primaryKey) {
|
|
379
|
+
if (primaryKey === undefined) {
|
|
380
|
+
if (!Object.prototype.hasOwnProperty.call(columns, 'id')) {
|
|
381
|
+
throw new Error('Table "' + tableName + '" must include an "id" column or an explicit primaryKey');
|
|
382
|
+
}
|
|
383
|
+
return ['id'];
|
|
384
|
+
}
|
|
385
|
+
let keys = Array.isArray(primaryKey) ? [...primaryKey] : [primaryKey];
|
|
386
|
+
if (keys.length === 0) {
|
|
387
|
+
throw new Error('Table "' + tableName + '" primaryKey must contain at least one column');
|
|
388
|
+
}
|
|
389
|
+
for (let key of keys) {
|
|
390
|
+
if (!Object.prototype.hasOwnProperty.call(columns, key)) {
|
|
391
|
+
throw new Error('Table "' + tableName + '" primaryKey column "' + key + '" does not exist');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return keys;
|
|
395
|
+
}
|
|
396
|
+
function normalizeKeySelector(table, selector, optionName, defaultValue) {
|
|
397
|
+
return normalizeKeysForTable(table, selector, optionName, defaultValue);
|
|
398
|
+
}
|
|
399
|
+
function normalizeKeysForTable(table, selector, optionName, defaultValue) {
|
|
400
|
+
if (selector === undefined) {
|
|
401
|
+
return [...defaultValue];
|
|
402
|
+
}
|
|
403
|
+
let keys = Array.isArray(selector) ? [...selector] : [selector];
|
|
404
|
+
if (keys.length === 0) {
|
|
405
|
+
throw new Error('Option "' + optionName + '" for table "' + getTableName(table) + '" must not be empty');
|
|
406
|
+
}
|
|
407
|
+
let columns = getTableColumns(table);
|
|
408
|
+
for (let key of keys) {
|
|
409
|
+
if (!Object.prototype.hasOwnProperty.call(columns, key)) {
|
|
410
|
+
throw new Error('Unknown column "' +
|
|
411
|
+
key +
|
|
412
|
+
'" in option "' +
|
|
413
|
+
optionName +
|
|
414
|
+
'" for table "' +
|
|
415
|
+
getTableName(table) +
|
|
416
|
+
'"');
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return keys;
|
|
420
|
+
}
|
|
421
|
+
function normalizeTimestampConfig(options) {
|
|
422
|
+
if (!options) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
if (options === true) {
|
|
426
|
+
return { ...defaultTimestampConfig };
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
createdAt: options.createdAt ?? defaultTimestampConfig.createdAt,
|
|
430
|
+
updatedAt: options.updatedAt ?? defaultTimestampConfig.updatedAt,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function assertKeyLengths(sourceTableName, targetTableName, sourceKey, targetKey) {
|
|
434
|
+
if (sourceKey.length !== targetKey.length) {
|
|
435
|
+
throw new Error('Relation key mismatch between "' +
|
|
436
|
+
sourceTableName +
|
|
437
|
+
'" (' +
|
|
438
|
+
sourceKey.join(', ') +
|
|
439
|
+
') and "' +
|
|
440
|
+
targetTableName +
|
|
441
|
+
'" (' +
|
|
442
|
+
targetKey.join(', ') +
|
|
443
|
+
')');
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function createRelation(options) {
|
|
447
|
+
let baseModifiers = {
|
|
448
|
+
where: options.modifiers?.where ? [...options.modifiers.where] : [],
|
|
449
|
+
orderBy: options.modifiers?.orderBy ? [...options.modifiers.orderBy] : [],
|
|
450
|
+
limit: options.modifiers?.limit,
|
|
451
|
+
offset: options.modifiers?.offset,
|
|
452
|
+
with: options.modifiers?.with ? { ...options.modifiers.with } : {},
|
|
453
|
+
};
|
|
454
|
+
let relation = {
|
|
455
|
+
kind: 'relation',
|
|
456
|
+
relationKind: options.relationKind,
|
|
457
|
+
sourceTable: options.sourceTable,
|
|
458
|
+
targetTable: options.targetTable,
|
|
459
|
+
cardinality: options.cardinality,
|
|
460
|
+
sourceKey: [...options.sourceKey],
|
|
461
|
+
targetKey: [...options.targetKey],
|
|
462
|
+
through: options.through,
|
|
463
|
+
modifiers: baseModifiers,
|
|
464
|
+
where(input) {
|
|
465
|
+
let predicate = normalizeWhereInput(input);
|
|
466
|
+
return cloneRelation(relation, {
|
|
467
|
+
where: [...relation.modifiers.where, predicate],
|
|
468
|
+
});
|
|
469
|
+
},
|
|
470
|
+
orderBy(column, direction = 'asc') {
|
|
471
|
+
return cloneRelation(relation, {
|
|
472
|
+
orderBy: [
|
|
473
|
+
...relation.modifiers.orderBy,
|
|
474
|
+
{
|
|
475
|
+
column: normalizeColumnInput(column),
|
|
476
|
+
direction,
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
});
|
|
480
|
+
},
|
|
481
|
+
limit(value) {
|
|
482
|
+
return cloneRelation(relation, {
|
|
483
|
+
limit: value,
|
|
484
|
+
});
|
|
485
|
+
},
|
|
486
|
+
offset(value) {
|
|
487
|
+
return cloneRelation(relation, {
|
|
488
|
+
offset: value,
|
|
489
|
+
});
|
|
490
|
+
},
|
|
491
|
+
with(relations) {
|
|
492
|
+
return cloneRelation(relation, {
|
|
493
|
+
with: {
|
|
494
|
+
...relation.modifiers.with,
|
|
495
|
+
...relations,
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
return relation;
|
|
501
|
+
}
|
|
502
|
+
function cloneRelation(relation, patch) {
|
|
503
|
+
return createRelation({
|
|
504
|
+
relationKind: relation.relationKind,
|
|
505
|
+
cardinality: relation.cardinality,
|
|
506
|
+
sourceTable: relation.sourceTable,
|
|
507
|
+
targetTable: relation.targetTable,
|
|
508
|
+
sourceKey: relation.sourceKey,
|
|
509
|
+
targetKey: relation.targetKey,
|
|
510
|
+
through: relation.through,
|
|
511
|
+
modifiers: {
|
|
512
|
+
where: patch.where ?? relation.modifiers.where,
|
|
513
|
+
orderBy: patch.orderBy ?? relation.modifiers.orderBy,
|
|
514
|
+
limit: patch.limit ?? relation.modifiers.limit,
|
|
515
|
+
offset: patch.offset ?? relation.modifiers.offset,
|
|
516
|
+
with: patch.with ?? relation.modifiers.with,
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,CAAC,KAAK,IAAI;KACzB,GAAG,IAAI,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC;CACjC,GAAG,EAAE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/migrations/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { loadMigrations } from "../lib/migrations-node.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { AlterTableBuilder, CreateMigrationInput, Migration, MigrationContext, MigrationDescriptor, MigrationDirection, MigrationJournalRow, MigrationSchema, MigrationRegistry, MigrationRunner, MigrationRunnerOptions, MigrationStatus, MigrationStatusEntry, MigrationTransactionMode, MigrateOptions, MigrateResult, KeyColumns, TableInput, } from './lib/migrations.ts';
|
|
2
|
+
export { createMigration } from './lib/migrations.ts';
|
|
3
|
+
export type { ColumnNamespace } from './lib/column.ts';
|
|
4
|
+
export { ColumnBuilder, column } from './lib/column.ts';
|
|
5
|
+
export { createMigrationRegistry } from './lib/migrations/registry.ts';
|
|
6
|
+
export { createMigrationRunner } from './lib/migrations/runner.ts';
|
|
7
|
+
export { parseMigrationFilename } from './lib/migrations/filename.ts';
|
|
8
|
+
//# sourceMappingURL=migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,SAAS,EACT,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACpB,wBAAwB,EACxB,cAAc,EACd,aAAa,EACb,UAAU,EACV,UAAU,GACX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAA"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createMigration } from "./lib/migrations.js";
|
|
2
|
+
export { ColumnBuilder, column } from "./lib/column.js";
|
|
3
|
+
export { createMigrationRegistry } from "./lib/migrations/registry.js";
|
|
4
|
+
export { createMigrationRunner } from "./lib/migrations/runner.js";
|
|
5
|
+
export { parseMigrationFilename } from "./lib/migrations/filename.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operators.d.ts","sourceRoot":"","sources":["../src/operators.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EACL,GAAG,EACH,OAAO,EACP,EAAE,EACF,EAAE,EACF,GAAG,EACH,KAAK,EACL,MAAM,EACN,MAAM,EACN,IAAI,EACJ,EAAE,EACF,GAAG,EACH,EAAE,EACF,SAAS,EACT,OAAO,EACP,EAAE,GACH,MAAM,oBAAoB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { and, between, eq, gt, gte, ilike, inList, isNull, like, lt, lte, ne, notInList, notNull, or, } from "./lib/operators.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-helpers.d.ts","sourceRoot":"","sources":["../src/sql-helpers.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,EACL,cAAc,EACd,2BAA2B,EAC3B,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,aAAa,GACd,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { collectColumns, isDataManipulationOperation, normalizeJoinType, quoteLiteral, quotePath, quoteTableRef, } from "./lib/sql-helpers.js";
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/data-table",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A typed, relational query toolkit for Remix",
|
|
5
|
+
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -10,11 +10,60 @@
|
|
|
10
10
|
"directory": "packages/data-table"
|
|
11
11
|
},
|
|
12
12
|
"homepage": "https://github.com/remix-run/remix/tree/main/packages/data-table#readme",
|
|
13
|
+
"files": [
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"README.md",
|
|
16
|
+
"dist",
|
|
17
|
+
"src",
|
|
18
|
+
"!src/**/*.test.ts"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./migrations": {
|
|
28
|
+
"types": "./dist/migrations.d.ts",
|
|
29
|
+
"default": "./dist/migrations.js"
|
|
30
|
+
},
|
|
31
|
+
"./migrations/node": {
|
|
32
|
+
"types": "./dist/migrations/node.d.ts",
|
|
33
|
+
"default": "./dist/migrations/node.js"
|
|
34
|
+
},
|
|
35
|
+
"./operators": {
|
|
36
|
+
"types": "./dist/operators.d.ts",
|
|
37
|
+
"default": "./dist/operators.js"
|
|
38
|
+
},
|
|
39
|
+
"./sql-helpers": {
|
|
40
|
+
"types": "./dist/sql-helpers.d.ts",
|
|
41
|
+
"default": "./dist/sql-helpers.js"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
47
|
+
"@types/node": "^24.6.0",
|
|
48
|
+
"@typescript/native-preview": "7.0.0-dev.20251125.1",
|
|
49
|
+
"better-sqlite3": "^12.4.1"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {},
|
|
13
52
|
"keywords": [
|
|
14
53
|
"remix",
|
|
15
|
-
"
|
|
54
|
+
"orm",
|
|
55
|
+
"sql",
|
|
56
|
+
"database",
|
|
57
|
+
"query-builder",
|
|
58
|
+
"relational"
|
|
16
59
|
],
|
|
17
|
-
"
|
|
18
|
-
"
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsgo -p tsconfig.build.json",
|
|
62
|
+
"clean": "git clean -fdX",
|
|
63
|
+
"test": "node --test",
|
|
64
|
+
"test:coverage": "pnpm run test:coverage:core && pnpm run test:coverage:migrations",
|
|
65
|
+
"test:coverage:core": "node --experimental-test-coverage --test-coverage-include='src/lib/operators.ts' --test-coverage-include='src/lib/references.ts' --test-coverage-include='src/lib/inflection.ts' --test-coverage-include='src/lib/sql.ts' --test-coverage-include='src/lib/errors.ts' --test-coverage-lines=90 --test-coverage-branches=90 --test-coverage-functions=90 --test ./src/lib/operators.test.ts ./src/lib/references.test.ts ./src/lib/inflection.test.ts ./src/lib/sql.test.ts ./src/lib/errors.test.ts",
|
|
66
|
+
"test:coverage:migrations": "node --experimental-test-coverage --test-coverage-include='src/lib/migrations.ts' --test-coverage-lines=90 --test-coverage-branches=90 --test-coverage-functions=90 --test ./src/lib/migrations.test.ts",
|
|
67
|
+
"typecheck": "tsgo --noEmit"
|
|
19
68
|
}
|
|
20
|
-
}
|
|
69
|
+
}
|