@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,856 @@
1
+ import type {
2
+ ColumnDefinition,
3
+ DataManipulationOperation,
4
+ DataManipulationResult,
5
+ DatabaseAdapter,
6
+ TransactionOptions,
7
+ TransactionToken,
8
+ } from './adapter.ts'
9
+ import type { ColumnBuilder } from './column.ts'
10
+ import { DataTableAdapterError, DataTableQueryError } from './errors.ts'
11
+ import { executeOperation, type QueryExecutionContext } from './database/execution-context.ts'
12
+ import {
13
+ asQueryTableInput,
14
+ getPrimaryKeyWhere,
15
+ getPrimaryKeyWhereFromRow,
16
+ normalizeOrderByInput,
17
+ resolveCreateRowWhere,
18
+ toWriteResult,
19
+ } from './database/helpers.ts'
20
+ import { executeQuery } from './database/query-execution.ts'
21
+ import type {
22
+ AnyQuery,
23
+ BoundQueryPhase,
24
+ Query as QueryObject,
25
+ QueryExecutionResult,
26
+ } from './query.ts'
27
+ import { bindQueryRuntime, query as createQuery } from './query.ts'
28
+ import type { ColumnInput, NormalizeColumnInput, TableMetadataLike } from './references.ts'
29
+ import type { SqlStatement } from './sql.ts'
30
+ import { isSqlStatement, rawSql } from './sql.ts'
31
+ import type {
32
+ AnyRelation,
33
+ AnyTable,
34
+ LoadedRelationMap,
35
+ OrderDirection,
36
+ PrimaryKeyInput,
37
+ TableName,
38
+ TablePrimaryKey,
39
+ TableRow,
40
+ TableRowWith,
41
+ TableValidate,
42
+ tableMetadataKey,
43
+ TimestampConfig,
44
+ } from './table.ts'
45
+ import { getTableName } from './table.ts'
46
+ import type { Pretty } from './types.ts'
47
+ import type { WhereInput } from './operators.ts'
48
+
49
+ export type TableColumnName<table extends AnyTable> = keyof TableRow<table> & string
50
+ export type QualifiedTableColumnName<table extends AnyTable> =
51
+ `${TableName<table>}.${TableColumnName<table>}`
52
+ export type QueryColumnName<table extends AnyTable> =
53
+ | TableColumnName<table>
54
+ | QualifiedTableColumnName<table>
55
+
56
+ type RowColumnName<row extends Record<string, unknown>> = keyof row & string
57
+ type QualifiedRowColumnName<
58
+ tableName extends string,
59
+ row extends Record<string, unknown>,
60
+ > = `${tableName}.${RowColumnName<row>}`
61
+
62
+ export type QueryColumnTypeMapFromRow<
63
+ tableName extends string,
64
+ row extends Record<string, unknown>,
65
+ > = {
66
+ [column in
67
+ | RowColumnName<row>
68
+ | QualifiedRowColumnName<tableName, row>]: column extends RowColumnName<row>
69
+ ? row[column]
70
+ : column extends `${tableName}.${infer name extends RowColumnName<row>}`
71
+ ? row[name]
72
+ : never
73
+ }
74
+
75
+ export type QueryColumnTypeMap<table extends AnyTable> = Pretty<
76
+ QueryColumnTypeMapFromRow<TableName<table>, TableRow<table>>
77
+ >
78
+
79
+ export type MergeColumnTypeMaps<
80
+ left extends Record<string, unknown>,
81
+ right extends Record<string, unknown>,
82
+ > = Pretty<{
83
+ [column in Extract<keyof left | keyof right, string>]: column extends keyof right
84
+ ? column extends keyof left
85
+ ? left[column] | right[column]
86
+ : right[column]
87
+ : column extends keyof left
88
+ ? left[column]
89
+ : never
90
+ }>
91
+
92
+ export type QueryColumns<columnTypes extends Record<string, unknown>> = Extract<
93
+ keyof columnTypes,
94
+ string
95
+ >
96
+
97
+ export type QueryColumnInput<columnTypes extends Record<string, unknown>> = ColumnInput<
98
+ QueryColumns<columnTypes>
99
+ >
100
+
101
+ export type SelectedAliasRow<
102
+ columnTypes extends Record<string, unknown>,
103
+ selection extends Record<string, QueryColumnInput<columnTypes>>,
104
+ > = Pretty<{
105
+ [alias in keyof selection]: NormalizeColumnInput<selection[alias]> extends keyof columnTypes
106
+ ? columnTypes[NormalizeColumnInput<selection[alias]>]
107
+ : never
108
+ }>
109
+
110
+ export type RelationMapForSourceName<tableName extends string> = Record<
111
+ string,
112
+ AnyRelation & {
113
+ sourceTable: {
114
+ [tableMetadataKey]: {
115
+ name: tableName
116
+ }
117
+ }
118
+ }
119
+ >
120
+
121
+ export type PrimaryKeyInputForRow<
122
+ row extends Record<string, unknown>,
123
+ primaryKey extends readonly string[],
124
+ > = primaryKey extends readonly [infer column extends keyof row & string]
125
+ ? row[column]
126
+ : {
127
+ [column in primaryKey[number] & keyof row]: row[column]
128
+ }
129
+
130
+ export type ReturningInput<row extends Record<string, unknown>> = '*' | (keyof row & string)[]
131
+
132
+ /**
133
+ * Table-like metadata accepted by `database.query()`.
134
+ */
135
+ export type QueryTableInput<
136
+ tableName extends string,
137
+ row extends Record<string, unknown>,
138
+ primaryKey extends readonly (keyof row & string)[],
139
+ > = TableMetadataLike<
140
+ tableName,
141
+ {
142
+ [column in keyof row & string]: ColumnBuilder<row[column]>
143
+ },
144
+ primaryKey,
145
+ TimestampConfig | null
146
+ > & {
147
+ [tableMetadataKey]: {
148
+ name: tableName
149
+ columns: {
150
+ [column in keyof row & string]: ColumnBuilder<row[column]>
151
+ }
152
+ primaryKey: primaryKey
153
+ timestamps: TimestampConfig | null
154
+ columnDefinitions: Record<string, ColumnDefinition>
155
+ validate?: TableValidate<Record<string, unknown>>
156
+ }
157
+ } & Record<string, unknown>
158
+
159
+ /**
160
+ * Result metadata for write operations that do not return rows.
161
+ */
162
+ export type WriteResult = {
163
+ affectedRows: number
164
+ insertId?: unknown
165
+ }
166
+
167
+ /**
168
+ * Result metadata for write operations that return multiple rows.
169
+ */
170
+ export type WriteRowsResult<row> = {
171
+ affectedRows: number
172
+ insertId?: unknown
173
+ rows: row[]
174
+ }
175
+
176
+ /**
177
+ * Result metadata for write operations that return a single row.
178
+ */
179
+ export type WriteRowResult<row> = {
180
+ affectedRows: number
181
+ insertId?: unknown
182
+ row: row | null
183
+ }
184
+
185
+ /**
186
+ * Queryable column type map for a concrete table.
187
+ */
188
+ export type QueryColumnTypesForTable<table extends AnyTable> = QueryColumnTypeMap<table>
189
+
190
+ /**
191
+ * Query type produced for a concrete table.
192
+ */
193
+ export type QueryForTable<
194
+ table extends AnyTable,
195
+ loaded extends Record<string, unknown> = {},
196
+ > = QueryObject<
197
+ QueryTableInput<TableName<table>, TableRow<table>, TablePrimaryKey<table>>,
198
+ QueryColumnTypesForTable<table>,
199
+ TableRow<table>,
200
+ loaded,
201
+ BoundQueryPhase<'all'>
202
+ >
203
+
204
+ /**
205
+ * Column names accepted in single-table queries.
206
+ */
207
+ export type SingleTableColumn<table extends AnyTable> = QueryColumns<QueryColumnTypeMap<table>>
208
+
209
+ /**
210
+ * `where` input accepted in single-table queries.
211
+ */
212
+ export type SingleTableWhere<table extends AnyTable> = WhereInput<SingleTableColumn<table>>
213
+
214
+ /**
215
+ * Tuple form accepted by `orderBy` for a single table.
216
+ */
217
+ export type OrderByTuple<table extends AnyTable> = [
218
+ column: SingleTableColumn<table>,
219
+ direction?: OrderDirection,
220
+ ]
221
+
222
+ /**
223
+ * `orderBy` input accepted in single-table queries.
224
+ */
225
+ export type OrderByInput<table extends AnyTable> = OrderByTuple<table> | OrderByTuple<table>[]
226
+
227
+ /**
228
+ * Options for loading many rows from a table.
229
+ */
230
+ export type FindManyOptions<
231
+ table extends AnyTable,
232
+ relations extends RelationMapForSourceName<TableName<table>> = {},
233
+ > = {
234
+ where?: SingleTableWhere<table>
235
+ orderBy?: OrderByInput<table>
236
+ limit?: number
237
+ offset?: number
238
+ with?: relations
239
+ }
240
+
241
+ /**
242
+ * Options for loading a single row from a table.
243
+ */
244
+ export type FindOneOptions<
245
+ table extends AnyTable,
246
+ relations extends RelationMapForSourceName<TableName<table>> = {},
247
+ > = Omit<FindManyOptions<table, relations>, 'limit' | 'offset'> & {
248
+ where: SingleTableWhere<table>
249
+ }
250
+
251
+ /**
252
+ * Options for updating a single row.
253
+ */
254
+ export type UpdateOptions<
255
+ table extends AnyTable,
256
+ relations extends RelationMapForSourceName<TableName<table>> = {},
257
+ > = {
258
+ touch?: boolean
259
+ with?: relations
260
+ }
261
+
262
+ /**
263
+ * Options for updating many rows.
264
+ */
265
+ export type UpdateManyOptions<table extends AnyTable> = {
266
+ where: SingleTableWhere<table>
267
+ orderBy?: OrderByInput<table>
268
+ limit?: number
269
+ offset?: number
270
+ touch?: boolean
271
+ }
272
+
273
+ /**
274
+ * Options for deleting many rows.
275
+ */
276
+ export type DeleteManyOptions<table extends AnyTable> = {
277
+ where: SingleTableWhere<table>
278
+ orderBy?: OrderByInput<table>
279
+ limit?: number
280
+ offset?: number
281
+ }
282
+
283
+ /**
284
+ * Options for counting rows.
285
+ */
286
+ export type CountOptions<table extends AnyTable> = {
287
+ where?: SingleTableWhere<table>
288
+ }
289
+
290
+ /**
291
+ * Options for create operations that return only write metadata.
292
+ */
293
+ export type CreateResultOptions = {
294
+ touch?: boolean
295
+ returnRow?: false
296
+ }
297
+
298
+ /**
299
+ * Options for create operations that return the inserted row.
300
+ */
301
+ export type CreateRowOptions<
302
+ table extends AnyTable,
303
+ relations extends RelationMapForSourceName<TableName<table>> = {},
304
+ > = {
305
+ touch?: boolean
306
+ with?: relations
307
+ returnRow: true
308
+ }
309
+
310
+ /**
311
+ * Options for bulk-create operations that return only write metadata.
312
+ */
313
+ export type CreateManyResultOptions = {
314
+ touch?: boolean
315
+ returnRows?: false
316
+ }
317
+
318
+ /**
319
+ * Options for bulk-create operations that return inserted rows.
320
+ */
321
+ export type CreateManyRowsOptions = {
322
+ touch?: boolean
323
+ returnRows: true
324
+ }
325
+
326
+ type SavepointCounter = {
327
+ value: number
328
+ }
329
+
330
+ type DatabaseOptions = {
331
+ now?: () => unknown
332
+ }
333
+
334
+ type DatabaseInternalState = {
335
+ token?: TransactionToken
336
+ savepointCounter: SavepointCounter
337
+ }
338
+
339
+ const createInternalDatabase = Symbol('createInternalDatabase')
340
+
341
+ /**
342
+ * High-level database runtime used to build and execute data manipulation operations.
343
+ *
344
+ * Create instances directly with `new Database(adapter, options)` or use
345
+ * `createDatabase(adapter, options)` as a thin wrapper.
346
+ */
347
+ export class Database implements QueryExecutionContext {
348
+ #adapter: DatabaseAdapter
349
+ #token?: TransactionToken
350
+ #now: () => unknown
351
+ #savepointCounter: SavepointCounter
352
+
353
+ constructor(adapter: DatabaseAdapter, options?: DatabaseOptions) {
354
+ this.#adapter = adapter
355
+ this.#now = options?.now ?? defaultNow
356
+ this.#savepointCounter = { value: 0 }
357
+ }
358
+
359
+ static [createInternalDatabase](
360
+ adapter: DatabaseAdapter,
361
+ options: DatabaseOptions | undefined,
362
+ internal: DatabaseInternalState,
363
+ ): Database {
364
+ let database = new Database(adapter, options)
365
+ database.#token = internal.token
366
+ database.#savepointCounter = internal.savepointCounter
367
+ return database
368
+ }
369
+
370
+ get adapter(): DatabaseAdapter {
371
+ return this.#adapter
372
+ }
373
+
374
+ now(): unknown {
375
+ return this.#now()
376
+ }
377
+
378
+ query<
379
+ tableName extends string,
380
+ row extends Record<string, unknown>,
381
+ primaryKey extends readonly (keyof row & string)[],
382
+ >(
383
+ table: QueryTableInput<tableName, row, primaryKey>,
384
+ ): QueryObject<
385
+ QueryTableInput<tableName, row, primaryKey>,
386
+ Pretty<QueryColumnTypeMapFromRow<tableName, row>>,
387
+ row,
388
+ {},
389
+ BoundQueryPhase<'all'>
390
+ > {
391
+ return createQuery(table)[bindQueryRuntime](this) as QueryObject<
392
+ QueryTableInput<tableName, row, primaryKey>,
393
+ Pretty<QueryColumnTypeMapFromRow<tableName, row>>,
394
+ row,
395
+ {},
396
+ BoundQueryPhase<'all'>
397
+ >
398
+ }
399
+
400
+ create<table extends AnyTable>(
401
+ table: table,
402
+ values: Partial<TableRow<table>>,
403
+ options?: CreateResultOptions,
404
+ ): Promise<WriteResult>
405
+ create<table extends AnyTable, relations extends RelationMapForSourceName<TableName<table>> = {}>(
406
+ table: table,
407
+ values: Partial<TableRow<table>>,
408
+ options: CreateRowOptions<table, relations>,
409
+ ): Promise<TableRowWith<table, LoadedRelationMap<relations>>>
410
+ async create<
411
+ table extends AnyTable,
412
+ relations extends RelationMapForSourceName<TableName<table>> = {},
413
+ >(
414
+ table: table,
415
+ values: Partial<TableRow<table>>,
416
+ options?: CreateResultOptions | CreateRowOptions<table, relations>,
417
+ ): Promise<WriteResult | TableRowWith<table, LoadedRelationMap<relations>>> {
418
+ let touch = options?.touch
419
+ let query: QueryForTable<table> = this.query(asQueryTableInput(table))
420
+
421
+ if (options?.returnRow !== true) {
422
+ let result = await query.insert(values, { touch })
423
+ return toWriteResult(result)
424
+ }
425
+
426
+ if (this.#adapter.capabilities.returning) {
427
+ let result = (await query.insert(values, {
428
+ returning: '*',
429
+ touch,
430
+ })) as { row: TableRow<table> | null }
431
+ let row = result.row
432
+
433
+ if (!row) {
434
+ throw new DataTableQueryError(
435
+ 'create({ returnRow: true }) failed to return an inserted row',
436
+ )
437
+ }
438
+
439
+ if (!options.with) {
440
+ return row as TableRowWith<table, LoadedRelationMap<relations>>
441
+ }
442
+
443
+ let where = getPrimaryKeyWhereFromRow(table, row)
444
+ let loaded = await this.findOne(table, {
445
+ where,
446
+ with: options.with,
447
+ })
448
+
449
+ if (!loaded) {
450
+ throw new DataTableQueryError('create({ returnRow: true }) failed to load inserted row')
451
+ }
452
+
453
+ return loaded
454
+ }
455
+
456
+ let insertResult = await query.insert(values, { touch })
457
+ let where = resolveCreateRowWhere(table, values, toWriteResult(insertResult).insertId)
458
+ let loaded = await this.findOne(table, {
459
+ where,
460
+ with: options.with,
461
+ })
462
+
463
+ if (!loaded) {
464
+ throw new DataTableQueryError('create({ returnRow: true }) failed to load inserted row')
465
+ }
466
+
467
+ return loaded
468
+ }
469
+
470
+ createMany<table extends AnyTable>(
471
+ table: table,
472
+ values: Array<Partial<TableRow<table>>>,
473
+ options?: CreateManyResultOptions,
474
+ ): Promise<WriteResult>
475
+ createMany<table extends AnyTable>(
476
+ table: table,
477
+ values: Array<Partial<TableRow<table>>>,
478
+ options: CreateManyRowsOptions,
479
+ ): Promise<TableRow<table>[]>
480
+ async createMany<table extends AnyTable>(
481
+ table: table,
482
+ values: Array<Partial<TableRow<table>>>,
483
+ options?: CreateManyResultOptions | CreateManyRowsOptions,
484
+ ): Promise<WriteResult | TableRow<table>[]> {
485
+ let query: QueryForTable<table> = this.query(asQueryTableInput(table))
486
+
487
+ if (options?.returnRows === true) {
488
+ if (!this.#adapter.capabilities.returning) {
489
+ throw new DataTableQueryError(
490
+ 'createMany({ returnRows: true }) is not supported by this adapter',
491
+ )
492
+ }
493
+
494
+ let result = (await query.insertMany(values, {
495
+ returning: '*',
496
+ touch: options.touch,
497
+ })) as { rows: TableRow<table>[] }
498
+
499
+ return result.rows
500
+ }
501
+
502
+ let result = await query.insertMany(values, {
503
+ touch: options?.touch,
504
+ })
505
+
506
+ return toWriteResult(result)
507
+ }
508
+
509
+ async find<
510
+ table extends AnyTable,
511
+ relations extends RelationMapForSourceName<TableName<table>> = {},
512
+ >(
513
+ table: table,
514
+ value: PrimaryKeyInput<table>,
515
+ options?: { with?: relations },
516
+ ): Promise<TableRowWith<table, LoadedRelationMap<relations>> | null> {
517
+ if (value == null) {
518
+ return null
519
+ }
520
+
521
+ let query: QueryForTable<table> = this.query(asQueryTableInput(table))
522
+
523
+ if (options?.with) {
524
+ return query.with(options.with).find(value as any) as Promise<TableRowWith<
525
+ table,
526
+ LoadedRelationMap<relations>
527
+ > | null>
528
+ }
529
+
530
+ return query.find(value as any) as Promise<TableRowWith<
531
+ table,
532
+ LoadedRelationMap<relations>
533
+ > | null>
534
+ }
535
+
536
+ async findOne<
537
+ table extends AnyTable,
538
+ relations extends RelationMapForSourceName<TableName<table>> = {},
539
+ >(
540
+ table: table,
541
+ options: FindOneOptions<table, relations>,
542
+ ): Promise<TableRowWith<table, LoadedRelationMap<relations>> | null> {
543
+ let query: QueryForTable<table> = this.query(asQueryTableInput(table)).where(options.where)
544
+ let orderBy = normalizeOrderByInput(options.orderBy)
545
+
546
+ for (let [column, direction] of orderBy) {
547
+ query = query.orderBy(column, direction)
548
+ }
549
+
550
+ if (options.with) {
551
+ return query.with(options.with).first() as Promise<TableRowWith<
552
+ table,
553
+ LoadedRelationMap<relations>
554
+ > | null>
555
+ }
556
+
557
+ return query.first() as Promise<TableRowWith<table, LoadedRelationMap<relations>> | null>
558
+ }
559
+
560
+ async findMany<
561
+ table extends AnyTable,
562
+ relations extends RelationMapForSourceName<TableName<table>> = {},
563
+ >(
564
+ table: table,
565
+ options?: FindManyOptions<table, relations>,
566
+ ): Promise<Array<TableRowWith<table, LoadedRelationMap<relations>>>> {
567
+ let query: QueryForTable<table> = this.query(asQueryTableInput(table))
568
+
569
+ if (options?.where) {
570
+ query = query.where(options.where)
571
+ }
572
+
573
+ let orderBy = normalizeOrderByInput(options?.orderBy)
574
+ for (let [column, direction] of orderBy) {
575
+ query = query.orderBy(column, direction)
576
+ }
577
+
578
+ if (options?.limit !== undefined) {
579
+ query = query.limit(options.limit)
580
+ }
581
+
582
+ if (options?.offset !== undefined) {
583
+ query = query.offset(options.offset)
584
+ }
585
+
586
+ if (options?.with) {
587
+ return query.with(options.with).all() as Promise<
588
+ Array<TableRowWith<table, LoadedRelationMap<relations>>>
589
+ >
590
+ }
591
+
592
+ return query.all() as Promise<Array<TableRowWith<table, LoadedRelationMap<relations>>>>
593
+ }
594
+
595
+ async count<table extends AnyTable>(
596
+ table: table,
597
+ options?: CountOptions<table>,
598
+ ): Promise<number> {
599
+ let query: QueryForTable<table> = this.query(asQueryTableInput(table))
600
+
601
+ if (options?.where) {
602
+ query = query.where(options.where)
603
+ }
604
+
605
+ return query.count()
606
+ }
607
+
608
+ async update<
609
+ table extends AnyTable,
610
+ relations extends RelationMapForSourceName<TableName<table>> = {},
611
+ >(
612
+ table: table,
613
+ value: PrimaryKeyInput<table>,
614
+ changes: Partial<TableRow<table>>,
615
+ options?: UpdateOptions<table, relations>,
616
+ ): Promise<TableRowWith<table, LoadedRelationMap<relations>>> {
617
+ let where = getPrimaryKeyWhere(table, value)
618
+
619
+ if (this.#adapter.capabilities.returning) {
620
+ let updateResult = (await this.query(asQueryTableInput(table)).where(where).update(changes, {
621
+ touch: options?.touch,
622
+ returning: '*',
623
+ })) as { rows: TableRow<table>[] }
624
+ let updatedRow = updateResult.rows[0]
625
+
626
+ if (!updatedRow) {
627
+ throw new DataTableQueryError(
628
+ 'update() failed to find row for table "' + getTableName(table) + '"',
629
+ )
630
+ }
631
+
632
+ if (!options?.with) {
633
+ return updatedRow as TableRowWith<table, LoadedRelationMap<relations>>
634
+ }
635
+
636
+ let loaded = await this.findOne(table, {
637
+ where: getPrimaryKeyWhereFromRow(table, updatedRow),
638
+ with: options.with,
639
+ })
640
+
641
+ if (!loaded) {
642
+ throw new DataTableQueryError(
643
+ 'update() failed to find row for table "' + getTableName(table) + '"',
644
+ )
645
+ }
646
+
647
+ return loaded
648
+ }
649
+
650
+ await this.query(asQueryTableInput(table)).where(where).update(changes, {
651
+ touch: options?.touch,
652
+ })
653
+
654
+ let loaded = await this.find(table, value, { with: options?.with })
655
+
656
+ if (!loaded) {
657
+ throw new DataTableQueryError(
658
+ 'update() failed to find row for table "' + getTableName(table) + '"',
659
+ )
660
+ }
661
+
662
+ return loaded
663
+ }
664
+
665
+ async updateMany<table extends AnyTable>(
666
+ table: table,
667
+ changes: Partial<TableRow<table>>,
668
+ options: UpdateManyOptions<table>,
669
+ ): Promise<WriteResult> {
670
+ let query: QueryForTable<table> = this.query(asQueryTableInput(table)).where(options.where)
671
+ let orderBy = normalizeOrderByInput(options.orderBy)
672
+
673
+ for (let [column, direction] of orderBy) {
674
+ query = query.orderBy(column, direction)
675
+ }
676
+
677
+ if (options.limit !== undefined) {
678
+ query = query.limit(options.limit)
679
+ }
680
+
681
+ if (options.offset !== undefined) {
682
+ query = query.offset(options.offset)
683
+ }
684
+
685
+ let result = await query.update(changes, { touch: options.touch })
686
+ return toWriteResult(result)
687
+ }
688
+
689
+ async delete<table extends AnyTable>(
690
+ table: table,
691
+ value: PrimaryKeyInput<table>,
692
+ ): Promise<boolean> {
693
+ let where = getPrimaryKeyWhere(table, value)
694
+ let result = await this.query(asQueryTableInput(table)).where(where).delete()
695
+ return toWriteResult(result).affectedRows > 0
696
+ }
697
+
698
+ async deleteMany<table extends AnyTable>(
699
+ table: table,
700
+ options: DeleteManyOptions<table>,
701
+ ): Promise<WriteResult> {
702
+ let query: QueryForTable<table> = this.query(asQueryTableInput(table)).where(options.where)
703
+ let orderBy = normalizeOrderByInput(options.orderBy)
704
+
705
+ for (let [column, direction] of orderBy) {
706
+ query = query.orderBy(column, direction)
707
+ }
708
+
709
+ if (options.limit !== undefined) {
710
+ query = query.limit(options.limit)
711
+ }
712
+
713
+ if (options.offset !== undefined) {
714
+ query = query.offset(options.offset)
715
+ }
716
+
717
+ let result = await query.delete()
718
+ return toWriteResult(result)
719
+ }
720
+
721
+ async exec(statement: string | SqlStatement, values?: unknown[]): Promise<DataManipulationResult>
722
+ async exec<input extends AnyQuery>(input: input): Promise<QueryExecutionResult<input>>
723
+ async exec<input extends AnyQuery>(
724
+ statementOrInput: string | SqlStatement | input,
725
+ values: unknown[] = [],
726
+ ): Promise<DataManipulationResult | QueryExecutionResult<input>> {
727
+ if (typeof statementOrInput === 'string' || isSqlStatement(statementOrInput)) {
728
+ let sqlStatement = isSqlStatement(statementOrInput)
729
+ ? statementOrInput
730
+ : rawSql(statementOrInput, values)
731
+
732
+ return this[executeOperation]({
733
+ kind: 'raw',
734
+ sql: sqlStatement,
735
+ })
736
+ }
737
+
738
+ return executeQuery(this, statementOrInput)
739
+ }
740
+
741
+ async transaction<result>(
742
+ callback: (database: Database) => Promise<result>,
743
+ options?: TransactionOptions,
744
+ ): Promise<result> {
745
+ if (!this.#token) {
746
+ let token = await this.#adapter.beginTransaction(options)
747
+ let tx = Database[createInternalDatabase](
748
+ this.#adapter,
749
+ { now: this.#now },
750
+ {
751
+ token,
752
+ savepointCounter: this.#savepointCounter,
753
+ },
754
+ )
755
+
756
+ try {
757
+ let result = await callback(tx)
758
+ await this.#adapter.commitTransaction(token)
759
+ return result
760
+ } catch (error) {
761
+ await this.#adapter.rollbackTransaction(token)
762
+ throw error
763
+ }
764
+ }
765
+
766
+ if (!this.#adapter.capabilities.savepoints) {
767
+ throw new DataTableQueryError('Nested transactions require adapter savepoint support')
768
+ }
769
+
770
+ let savepointName = 'sp_' + String(this.#savepointCounter.value)
771
+ this.#savepointCounter.value += 1
772
+
773
+ await this.#adapter.createSavepoint(this.#token, savepointName)
774
+
775
+ try {
776
+ let result = await callback(this)
777
+ await this.#adapter.releaseSavepoint(this.#token, savepointName)
778
+ return result
779
+ } catch (error) {
780
+ await this.#adapter.rollbackToSavepoint(this.#token, savepointName)
781
+ await this.#adapter.releaseSavepoint(this.#token, savepointName)
782
+ throw error
783
+ }
784
+ }
785
+
786
+ async [executeOperation](operation: DataManipulationOperation): Promise<DataManipulationResult> {
787
+ try {
788
+ return await this.#adapter.execute({
789
+ operation,
790
+ transaction: this.#token,
791
+ })
792
+ } catch (error) {
793
+ throw new DataTableAdapterError('Adapter execution failed', {
794
+ cause: error,
795
+ metadata: {
796
+ dialect: this.#adapter.dialect,
797
+ operationKind: operation.kind,
798
+ },
799
+ })
800
+ }
801
+ }
802
+ }
803
+
804
+ /**
805
+ * Creates a database runtime from an adapter.
806
+ * Thin wrapper around `new Database(adapter, options)`.
807
+ * @param adapter Adapter implementation responsible for SQL execution.
808
+ * @param options Optional runtime options.
809
+ * @param options.now Clock function used for auto-managed timestamps.
810
+ * @returns A {@link Database} API instance.
811
+ * @example
812
+ * ```ts
813
+ * import { column as c, createDatabase, table } from 'remix/data-table'
814
+ *
815
+ * let users = table({
816
+ * name: 'users',
817
+ * columns: {
818
+ * id: c.integer(),
819
+ * email: c.varchar(255),
820
+ * },
821
+ * })
822
+ *
823
+ * let db = createDatabase(adapter)
824
+ * let rows = await db.query(users).where({ id: 1 }).all()
825
+ * ```
826
+ */
827
+ export function createDatabase(
828
+ adapter: DatabaseAdapter,
829
+ options?: { now?: () => unknown },
830
+ ): Database {
831
+ return new Database(adapter, options)
832
+ }
833
+
834
+ /**
835
+ * Creates a database runtime bound to an existing adapter transaction token.
836
+ * This is an internal helper used by the migration runner.
837
+ * @param adapter Adapter implementation responsible for SQL execution.
838
+ * @param token Active adapter transaction token.
839
+ * @param options Optional runtime options.
840
+ * @param options.now Clock function used for auto-managed timestamps.
841
+ * @returns A {@link Database} API instance bound to the provided transaction.
842
+ */
843
+ export function createDatabaseWithTransaction(
844
+ adapter: DatabaseAdapter,
845
+ token: TransactionToken,
846
+ options?: { now?: () => unknown },
847
+ ): Database {
848
+ return Database[createInternalDatabase](adapter, options, {
849
+ token,
850
+ savepointCounter: { value: 0 },
851
+ })
852
+ }
853
+
854
+ function defaultNow(): Date {
855
+ return new Date()
856
+ }