@remix-run/data-table 0.3.0 → 0.5.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 +217 -78
- package/dist/cli.d.ts +76 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +78 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -7
- package/dist/lib/column.d.ts +1 -1
- package/dist/lib/column.d.ts.map +1 -1
- package/dist/lib/database/execution-context.d.ts +5 -5
- package/dist/lib/database/execution-context.d.ts.map +1 -1
- package/dist/lib/database/execution-context.js +1 -0
- package/dist/lib/database/helpers.js +6 -6
- package/dist/lib/database/query-execution.d.ts.map +1 -1
- package/dist/lib/database/query-execution.js +17 -17
- package/dist/lib/database/relations.js +5 -5
- package/dist/lib/database/write-lifecycle.d.ts +2 -2
- package/dist/lib/database/write-lifecycle.d.ts.map +1 -1
- package/dist/lib/database/write-lifecycle.js +5 -5
- package/dist/lib/database.d.ts +74 -59
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +176 -83
- package/dist/lib/{adapter.d.ts → driver.d.ts} +46 -48
- package/dist/lib/driver.d.ts.map +1 -0
- package/dist/lib/errors.d.ts +2 -2
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +4 -4
- package/dist/lib/migrations/journal-store.d.ts +6 -6
- package/dist/lib/migrations/journal-store.d.ts.map +1 -1
- package/dist/lib/migrations/journal-store.js +20 -11
- package/dist/lib/migrations/runner.d.ts +12 -19
- package/dist/lib/migrations/runner.d.ts.map +1 -1
- package/dist/lib/migrations/runner.js +152 -130
- package/dist/lib/migrations-node.d.ts +19 -1
- package/dist/lib/migrations-node.d.ts.map +1 -1
- package/dist/lib/migrations-node.js +22 -1
- package/dist/lib/migrations.d.ts +55 -20
- package/dist/lib/migrations.d.ts.map +1 -1
- package/dist/lib/operators.d.ts +8 -7
- package/dist/lib/operators.d.ts.map +1 -1
- package/dist/lib/operators.js +15 -11
- package/dist/lib/query.d.ts +1 -1
- package/dist/lib/query.d.ts.map +1 -1
- package/dist/lib/query.js +4 -4
- package/dist/lib/sql-helpers.d.ts +1 -1
- package/dist/lib/sql-helpers.d.ts.map +1 -1
- package/dist/lib/sql.d.ts +1 -1
- package/dist/lib/sql.js +1 -1
- package/dist/lib/table.d.ts +1 -1
- package/dist/lib/table.d.ts.map +1 -1
- package/dist/lib/table.js +5 -5
- package/dist/migrations/node.d.ts +1 -1
- package/dist/migrations/node.d.ts.map +1 -1
- package/dist/migrations/node.js +1 -1
- package/dist/migrations.d.ts +1 -2
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -3
- package/dist/operators.js +1 -1
- package/dist/sql-helpers.js +1 -1
- package/package.json +13 -9
- package/src/cli.ts +179 -0
- package/src/index.ts +19 -20
- package/src/lib/column.ts +1 -1
- package/src/lib/database/execution-context.ts +10 -6
- package/src/lib/database/helpers.ts +1 -1
- package/src/lib/database/query-execution.ts +15 -11
- package/src/lib/database/write-lifecycle.ts +4 -4
- package/src/lib/database.ts +223 -96
- package/src/lib/{adapter.ts → driver.ts} +48 -48
- package/src/lib/errors.ts +4 -4
- package/src/lib/migrations/journal-store.ts +21 -11
- package/src/lib/migrations/runner.ts +201 -148
- package/src/lib/migrations-node.ts +23 -1
- package/src/lib/migrations.ts +58 -19
- package/src/lib/operators.ts +24 -11
- package/src/lib/query.ts +1 -1
- package/src/lib/sql-helpers.ts +1 -1
- package/src/lib/sql.ts +1 -1
- package/src/lib/table.ts +1 -1
- package/src/migrations/node.ts +1 -1
- package/src/migrations.ts +3 -4
- package/dist/lib/adapter.d.ts.map +0 -1
- /package/dist/lib/{adapter.js → driver.js} +0 -0
package/src/lib/database.ts
CHANGED
|
@@ -2,13 +2,19 @@ import type {
|
|
|
2
2
|
ColumnDefinition,
|
|
3
3
|
DataManipulationOperation,
|
|
4
4
|
DataManipulationResult,
|
|
5
|
-
|
|
5
|
+
DatabaseCapabilities,
|
|
6
|
+
DatabaseDriver,
|
|
7
|
+
TableRef,
|
|
6
8
|
TransactionOptions,
|
|
7
9
|
TransactionToken,
|
|
8
|
-
} from './
|
|
10
|
+
} from './driver.ts'
|
|
9
11
|
import type { ColumnBuilder } from './column.ts'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
+
import { DataTableDatabaseError, DataTableQueryError } from './errors.ts'
|
|
13
|
+
import {
|
|
14
|
+
executeOperation,
|
|
15
|
+
runInTransaction,
|
|
16
|
+
type QueryExecutionContext,
|
|
17
|
+
} from './database/execution-context.ts'
|
|
12
18
|
import {
|
|
13
19
|
asQueryTableInput,
|
|
14
20
|
getPrimaryKeyWhere,
|
|
@@ -26,6 +32,15 @@ import type {
|
|
|
26
32
|
} from './query.ts'
|
|
27
33
|
import { bindQueryRuntime, query as createQuery } from './query.ts'
|
|
28
34
|
import type { ColumnInput, NormalizeColumnInput, TableMetadataLike } from './references.ts'
|
|
35
|
+
import type {
|
|
36
|
+
DatabaseMigrateOptions,
|
|
37
|
+
DatabaseMigrationStatusOptions,
|
|
38
|
+
DatabaseResetOptions,
|
|
39
|
+
MigrateResult,
|
|
40
|
+
Migrations,
|
|
41
|
+
MigrationStatusEntry,
|
|
42
|
+
} from './migrations.ts'
|
|
43
|
+
import { createMigrationRunner } from './migrations/runner.ts'
|
|
29
44
|
import type { SqlStatement } from './sql.ts'
|
|
30
45
|
import { isSqlStatement, rawSql } from './sql.ts'
|
|
31
46
|
import type {
|
|
@@ -327,7 +342,9 @@ type SavepointCounter = {
|
|
|
327
342
|
value: number
|
|
328
343
|
}
|
|
329
344
|
|
|
330
|
-
|
|
345
|
+
/** Options shared by database instances. */
|
|
346
|
+
export interface DatabaseOptions {
|
|
347
|
+
/** Clock function used for auto-managed timestamps. */
|
|
331
348
|
now?: () => unknown
|
|
332
349
|
}
|
|
333
350
|
|
|
@@ -336,39 +353,168 @@ type DatabaseInternalState = {
|
|
|
336
353
|
savepointCounter: SavepointCounter
|
|
337
354
|
}
|
|
338
355
|
|
|
339
|
-
const createInternalDatabase = Symbol('createInternalDatabase')
|
|
340
|
-
|
|
341
356
|
/**
|
|
342
|
-
* High-level database runtime used to
|
|
357
|
+
* High-level database runtime used to query and manage a database.
|
|
343
358
|
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
359
|
+
* Database dialects extend this class and provide a {@link DatabaseDriver} to the constructor.
|
|
360
|
+
* The driver owns SQL execution, transactions, and connection lifecycle while this class provides
|
|
361
|
+
* the shared query, persistence, and migration APIs.
|
|
346
362
|
*/
|
|
347
|
-
export class Database
|
|
348
|
-
#
|
|
363
|
+
export class Database<dialect extends string = string> {
|
|
364
|
+
#driver: DatabaseDriver<dialect>
|
|
365
|
+
#executionContext: QueryExecutionContext<dialect>
|
|
349
366
|
#token?: TransactionToken
|
|
350
367
|
#now: () => unknown
|
|
351
368
|
#savepointCounter: SavepointCounter
|
|
352
369
|
|
|
353
|
-
|
|
354
|
-
|
|
370
|
+
/**
|
|
371
|
+
* Creates a database backed by a driver.
|
|
372
|
+
* @param driver Low-level database engine integration.
|
|
373
|
+
* @param options Database runtime options.
|
|
374
|
+
*/
|
|
375
|
+
constructor(driver: DatabaseDriver<dialect>, options?: DatabaseOptions) {
|
|
376
|
+
this.#driver = driver
|
|
355
377
|
this.#now = options?.now ?? defaultNow
|
|
356
378
|
this.#savepointCounter = { value: 0 }
|
|
379
|
+
|
|
380
|
+
let database = this
|
|
381
|
+
this.#executionContext = {
|
|
382
|
+
get capabilities() {
|
|
383
|
+
return database.capabilities
|
|
384
|
+
},
|
|
385
|
+
now() {
|
|
386
|
+
return database.now()
|
|
387
|
+
},
|
|
388
|
+
[executeOperation](operation) {
|
|
389
|
+
return database.#executeOperation(operation)
|
|
390
|
+
},
|
|
391
|
+
[runInTransaction](callback, transactionOptions) {
|
|
392
|
+
return database.#runInTransaction(
|
|
393
|
+
(transactionDatabase) => callback(transactionDatabase.#executionContext),
|
|
394
|
+
transactionOptions,
|
|
395
|
+
)
|
|
396
|
+
},
|
|
397
|
+
}
|
|
357
398
|
}
|
|
358
399
|
|
|
359
|
-
static
|
|
360
|
-
|
|
400
|
+
static #createInternalDatabase<dialect extends string>(
|
|
401
|
+
driver: DatabaseDriver<dialect>,
|
|
361
402
|
options: DatabaseOptions | undefined,
|
|
362
403
|
internal: DatabaseInternalState,
|
|
363
|
-
): Database {
|
|
364
|
-
let database = new Database(
|
|
404
|
+
): Database<dialect> {
|
|
405
|
+
let database = new Database(driver, options)
|
|
365
406
|
database.#token = internal.token
|
|
366
407
|
database.#savepointCounter = internal.savepointCounter
|
|
367
408
|
return database
|
|
368
409
|
}
|
|
369
410
|
|
|
370
|
-
|
|
371
|
-
|
|
411
|
+
/** Stable identifier for the SQL dialect. */
|
|
412
|
+
get dialect(): dialect {
|
|
413
|
+
return this.#driver.dialect
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** Immutable feature flags used by shared query and migration behavior. */
|
|
417
|
+
get capabilities(): DatabaseCapabilities {
|
|
418
|
+
return this.#driver.capabilities
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Executes a migration or raw multi-statement SQL script.
|
|
423
|
+
* @param sql SQL script to execute.
|
|
424
|
+
* @returns A promise that resolves when execution completes.
|
|
425
|
+
*/
|
|
426
|
+
executeScript(sql: string): Promise<void> {
|
|
427
|
+
return this.#driver.executeScript(sql, this.#token)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Reports whether a table exists.
|
|
432
|
+
* @param table Table to inspect.
|
|
433
|
+
* @returns A promise that resolves to `true` when the table exists.
|
|
434
|
+
*/
|
|
435
|
+
hasTable(table: TableRef): Promise<boolean> {
|
|
436
|
+
return this.#driver.hasTable(table, this.#token)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Reports whether a column exists on a table.
|
|
441
|
+
* @param table Table to inspect.
|
|
442
|
+
* @param column Column name to inspect.
|
|
443
|
+
* @returns A promise that resolves to `true` when the column exists.
|
|
444
|
+
*/
|
|
445
|
+
hasColumn(table: TableRef, column: string): Promise<boolean> {
|
|
446
|
+
return this.#driver.hasColumn(table, column, this.#token)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Closes resources owned by this database.
|
|
451
|
+
* @returns A promise that resolves when owned resources have been released.
|
|
452
|
+
*/
|
|
453
|
+
async close(): Promise<void> {
|
|
454
|
+
this.#assertLifecycleOperationAllowed('close')
|
|
455
|
+
await this.#driver.close()
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Destructively recreates the configured database.
|
|
460
|
+
* @returns A promise that resolves when the database is ready for use.
|
|
461
|
+
*/
|
|
462
|
+
async wipe(): Promise<void> {
|
|
463
|
+
this.#assertLifecycleOperationAllowed('wipe')
|
|
464
|
+
await this.#driver.wipe()
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Applies or reverts migrations in order.
|
|
469
|
+
*
|
|
470
|
+
* @param migrations Migration descriptors or registry to apply.
|
|
471
|
+
* @param options Migration direction, bound, dry-run, and journal configuration.
|
|
472
|
+
* @returns The migrations applied or reverted by this run and their SQL scripts.
|
|
473
|
+
*/
|
|
474
|
+
async migrate(migrations: Migrations, options?: DatabaseMigrateOptions): Promise<MigrateResult> {
|
|
475
|
+
this.#assertLifecycleOperationAllowed('migrate')
|
|
476
|
+
let { direction = 'up', journalTable, ...migrateOptions } = options ?? {}
|
|
477
|
+
let runner = createMigrationRunner(this.#driver, migrations, { journalTable })
|
|
478
|
+
return direction === 'up' ? runner.up(migrateOptions) : runner.down(migrateOptions)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Reports the current state of the provided migrations.
|
|
483
|
+
*
|
|
484
|
+
* @param migrations Migration descriptors or registry to inspect.
|
|
485
|
+
* @param options Migration journal configuration.
|
|
486
|
+
* @returns Status entries for the provided migrations.
|
|
487
|
+
*/
|
|
488
|
+
async migrationStatus(
|
|
489
|
+
migrations: Migrations,
|
|
490
|
+
options: DatabaseMigrationStatusOptions = {},
|
|
491
|
+
): Promise<MigrationStatusEntry[]> {
|
|
492
|
+
this.#assertLifecycleOperationAllowed('migrationStatus')
|
|
493
|
+
let runner = createMigrationRunner(this.#driver, migrations, options)
|
|
494
|
+
return runner.status()
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Wipes the database, applies migrations, and optionally seeds data.
|
|
499
|
+
*
|
|
500
|
+
* @param options Migrations and optional seed function used to rebuild the database.
|
|
501
|
+
* @returns A promise that resolves when the database has been rebuilt.
|
|
502
|
+
*/
|
|
503
|
+
async reset(options: DatabaseResetOptions): Promise<void> {
|
|
504
|
+
this.#assertLifecycleOperationAllowed('reset')
|
|
505
|
+
await this.wipe()
|
|
506
|
+
await this.migrate(options.migrations, { journalTable: options.journalTable })
|
|
507
|
+
await options.seed?.(this)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
#assertLifecycleOperationAllowed(
|
|
511
|
+
method: 'close' | 'migrate' | 'migrationStatus' | 'reset' | 'wipe',
|
|
512
|
+
): void {
|
|
513
|
+
if (this.#token) {
|
|
514
|
+
throw new DataTableQueryError(
|
|
515
|
+
'Cannot call ' + method + '() from a transaction-scoped database',
|
|
516
|
+
)
|
|
517
|
+
}
|
|
372
518
|
}
|
|
373
519
|
|
|
374
520
|
now(): unknown {
|
|
@@ -423,7 +569,7 @@ export class Database implements QueryExecutionContext {
|
|
|
423
569
|
return toWriteResult(result)
|
|
424
570
|
}
|
|
425
571
|
|
|
426
|
-
if (this
|
|
572
|
+
if (this.capabilities.returning) {
|
|
427
573
|
let result = (await query.insert(values, {
|
|
428
574
|
returning: '*',
|
|
429
575
|
touch,
|
|
@@ -485,9 +631,9 @@ export class Database implements QueryExecutionContext {
|
|
|
485
631
|
let query: QueryForTable<table> = this.query(asQueryTableInput(table))
|
|
486
632
|
|
|
487
633
|
if (options?.returnRows === true) {
|
|
488
|
-
if (!this
|
|
634
|
+
if (!this.capabilities.returning) {
|
|
489
635
|
throw new DataTableQueryError(
|
|
490
|
-
'createMany({ returnRows: true }) is not supported by this
|
|
636
|
+
'createMany({ returnRows: true }) is not supported by this database',
|
|
491
637
|
)
|
|
492
638
|
}
|
|
493
639
|
|
|
@@ -616,7 +762,7 @@ export class Database implements QueryExecutionContext {
|
|
|
616
762
|
): Promise<TableRowWith<table, LoadedRelationMap<relations>>> {
|
|
617
763
|
let where = getPrimaryKeyWhere(table, value)
|
|
618
764
|
|
|
619
|
-
if (this
|
|
765
|
+
if (this.capabilities.returning) {
|
|
620
766
|
let updateResult = (await this.query(asQueryTableInput(table)).where(where).update(changes, {
|
|
621
767
|
touch: options?.touch,
|
|
622
768
|
returning: '*',
|
|
@@ -729,23 +875,30 @@ export class Database implements QueryExecutionContext {
|
|
|
729
875
|
? statementOrInput
|
|
730
876
|
: rawSql(statementOrInput, values)
|
|
731
877
|
|
|
732
|
-
return this
|
|
878
|
+
return this.#executeOperation({
|
|
733
879
|
kind: 'raw',
|
|
734
880
|
sql: sqlStatement,
|
|
735
881
|
})
|
|
736
882
|
}
|
|
737
883
|
|
|
738
|
-
return executeQuery(this, statementOrInput)
|
|
884
|
+
return executeQuery(this.#executionContext, statementOrInput)
|
|
739
885
|
}
|
|
740
886
|
|
|
741
887
|
async transaction<result>(
|
|
742
|
-
callback: (database: Database) => Promise<result>,
|
|
888
|
+
callback: (database: Database<dialect>) => Promise<result>,
|
|
889
|
+
options?: TransactionOptions,
|
|
890
|
+
): Promise<result> {
|
|
891
|
+
return this.#runInTransaction(callback, options)
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
async #runInTransaction<result>(
|
|
895
|
+
callback: (database: Database<dialect>) => Promise<result>,
|
|
743
896
|
options?: TransactionOptions,
|
|
744
897
|
): Promise<result> {
|
|
745
898
|
if (!this.#token) {
|
|
746
|
-
let token = await this.#
|
|
747
|
-
let tx = Database
|
|
748
|
-
this.#
|
|
899
|
+
let token = await this.#driver.beginTransaction(options)
|
|
900
|
+
let tx = Database.#createInternalDatabase(
|
|
901
|
+
this.#driver,
|
|
749
902
|
{ now: this.#now },
|
|
750
903
|
{
|
|
751
904
|
token,
|
|
@@ -753,47 +906,71 @@ export class Database implements QueryExecutionContext {
|
|
|
753
906
|
},
|
|
754
907
|
)
|
|
755
908
|
|
|
909
|
+
let result: result
|
|
756
910
|
try {
|
|
757
|
-
|
|
758
|
-
await this.#adapter.commitTransaction(token)
|
|
759
|
-
return result
|
|
911
|
+
result = await callback(tx)
|
|
760
912
|
} catch (error) {
|
|
761
|
-
|
|
913
|
+
try {
|
|
914
|
+
await this.#driver.rollbackTransaction(token)
|
|
915
|
+
} catch (rollbackError) {
|
|
916
|
+
throw new AggregateError(
|
|
917
|
+
[error, rollbackError],
|
|
918
|
+
'Database transaction and rollback both failed',
|
|
919
|
+
{ cause: error },
|
|
920
|
+
)
|
|
921
|
+
}
|
|
762
922
|
throw error
|
|
763
923
|
}
|
|
924
|
+
|
|
925
|
+
await this.#driver.commitTransaction(token)
|
|
926
|
+
return result
|
|
764
927
|
}
|
|
765
928
|
|
|
766
|
-
if (!this
|
|
767
|
-
throw new DataTableQueryError('Nested transactions require
|
|
929
|
+
if (!this.capabilities.savepoints) {
|
|
930
|
+
throw new DataTableQueryError('Nested transactions require database savepoint support')
|
|
768
931
|
}
|
|
769
932
|
|
|
770
933
|
let savepointName = 'sp_' + String(this.#savepointCounter.value)
|
|
771
934
|
this.#savepointCounter.value += 1
|
|
772
935
|
|
|
773
|
-
await this.#
|
|
936
|
+
await this.#driver.createSavepoint(this.#token, savepointName)
|
|
774
937
|
|
|
938
|
+
let result: result
|
|
775
939
|
try {
|
|
776
|
-
|
|
777
|
-
await this.#adapter.releaseSavepoint(this.#token, savepointName)
|
|
778
|
-
return result
|
|
940
|
+
result = await callback(this)
|
|
779
941
|
} catch (error) {
|
|
780
|
-
|
|
781
|
-
|
|
942
|
+
let failures: unknown[] = [error]
|
|
943
|
+
try {
|
|
944
|
+
await this.#driver.rollbackToSavepoint(this.#token, savepointName)
|
|
945
|
+
} catch (rollbackError) {
|
|
946
|
+
failures.push(rollbackError)
|
|
947
|
+
}
|
|
948
|
+
try {
|
|
949
|
+
await this.#driver.releaseSavepoint(this.#token, savepointName)
|
|
950
|
+
} catch (releaseError) {
|
|
951
|
+
failures.push(releaseError)
|
|
952
|
+
}
|
|
953
|
+
if (failures.length > 1) {
|
|
954
|
+
throw new AggregateError(failures, 'Nested transaction cleanup failed', { cause: error })
|
|
955
|
+
}
|
|
782
956
|
throw error
|
|
783
957
|
}
|
|
958
|
+
|
|
959
|
+
await this.#driver.releaseSavepoint(this.#token, savepointName)
|
|
960
|
+
return result
|
|
784
961
|
}
|
|
785
962
|
|
|
786
|
-
async
|
|
963
|
+
async #executeOperation(operation: DataManipulationOperation): Promise<DataManipulationResult> {
|
|
787
964
|
try {
|
|
788
|
-
return await this.#
|
|
965
|
+
return await this.#driver.execute({
|
|
789
966
|
operation,
|
|
790
967
|
transaction: this.#token,
|
|
791
968
|
})
|
|
792
969
|
} catch (error) {
|
|
793
|
-
throw new
|
|
970
|
+
throw new DataTableDatabaseError('Database execution failed', {
|
|
794
971
|
cause: error,
|
|
795
972
|
metadata: {
|
|
796
|
-
dialect: this
|
|
973
|
+
dialect: this.dialect,
|
|
797
974
|
operationKind: operation.kind,
|
|
798
975
|
},
|
|
799
976
|
})
|
|
@@ -801,56 +978,6 @@ export class Database implements QueryExecutionContext {
|
|
|
801
978
|
}
|
|
802
979
|
}
|
|
803
980
|
|
|
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
981
|
function defaultNow(): Date {
|
|
855
982
|
return new Date()
|
|
856
983
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { AnyTable, OrderByClause } from './table.ts'
|
|
2
2
|
import type { Predicate } from './operators.ts'
|
|
3
3
|
import type { SqlStatement } from './sql.ts'
|
|
4
|
-
import type { Pretty } from './types.ts'
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
6
|
+
* SQL join kinds supported by database drivers.
|
|
8
7
|
*/
|
|
9
8
|
export type JoinType = 'inner' | 'left' | 'right'
|
|
10
9
|
|
|
@@ -31,7 +30,7 @@ export type SelectColumn = {
|
|
|
31
30
|
export type ReturningSelection = '*' | string[]
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
|
-
* Canonical select statement shape consumed by
|
|
33
|
+
* Canonical select statement shape consumed by drivers.
|
|
35
34
|
*/
|
|
36
35
|
export type SelectOperation<table extends AnyTable = AnyTable> = {
|
|
37
36
|
kind: 'select'
|
|
@@ -48,7 +47,7 @@ export type SelectOperation<table extends AnyTable = AnyTable> = {
|
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
/**
|
|
51
|
-
* Canonical count statement shape consumed by
|
|
50
|
+
* Canonical count statement shape consumed by drivers.
|
|
52
51
|
*/
|
|
53
52
|
export type CountOperation<table extends AnyTable = AnyTable> = {
|
|
54
53
|
kind: 'count'
|
|
@@ -60,7 +59,7 @@ export type CountOperation<table extends AnyTable = AnyTable> = {
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
/**
|
|
63
|
-
* Canonical exists statement shape consumed by
|
|
62
|
+
* Canonical exists statement shape consumed by drivers.
|
|
64
63
|
*/
|
|
65
64
|
export type ExistsOperation<table extends AnyTable = AnyTable> = {
|
|
66
65
|
kind: 'exists'
|
|
@@ -72,7 +71,7 @@ export type ExistsOperation<table extends AnyTable = AnyTable> = {
|
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
/**
|
|
75
|
-
* Canonical insert statement shape consumed by
|
|
74
|
+
* Canonical insert statement shape consumed by drivers.
|
|
76
75
|
*/
|
|
77
76
|
export type InsertOperation<table extends AnyTable = AnyTable> = {
|
|
78
77
|
kind: 'insert'
|
|
@@ -82,7 +81,7 @@ export type InsertOperation<table extends AnyTable = AnyTable> = {
|
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
/**
|
|
85
|
-
* Canonical bulk-insert statement shape consumed by
|
|
84
|
+
* Canonical bulk-insert statement shape consumed by drivers.
|
|
86
85
|
*/
|
|
87
86
|
export type InsertManyOperation<table extends AnyTable = AnyTable> = {
|
|
88
87
|
kind: 'insertMany'
|
|
@@ -92,7 +91,7 @@ export type InsertManyOperation<table extends AnyTable = AnyTable> = {
|
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
/**
|
|
95
|
-
* Canonical update statement shape consumed by
|
|
94
|
+
* Canonical update statement shape consumed by drivers.
|
|
96
95
|
*/
|
|
97
96
|
export type UpdateOperation<table extends AnyTable = AnyTable> = {
|
|
98
97
|
kind: 'update'
|
|
@@ -103,7 +102,7 @@ export type UpdateOperation<table extends AnyTable = AnyTable> = {
|
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
/**
|
|
106
|
-
* Canonical delete statement shape consumed by
|
|
105
|
+
* Canonical delete statement shape consumed by drivers.
|
|
107
106
|
*/
|
|
108
107
|
export type DeleteOperation<table extends AnyTable = AnyTable> = {
|
|
109
108
|
kind: 'delete'
|
|
@@ -113,7 +112,7 @@ export type DeleteOperation<table extends AnyTable = AnyTable> = {
|
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
/**
|
|
116
|
-
* Canonical upsert statement shape consumed by
|
|
115
|
+
* Canonical upsert statement shape consumed by drivers.
|
|
117
116
|
*/
|
|
118
117
|
export type UpsertOperation<table extends AnyTable = AnyTable> = {
|
|
119
118
|
kind: 'upsert'
|
|
@@ -243,7 +242,7 @@ export type ColumnDefinition = {
|
|
|
243
242
|
}
|
|
244
243
|
|
|
245
244
|
/**
|
|
246
|
-
* Opaque transaction handle supplied by
|
|
245
|
+
* Opaque transaction handle supplied by database drivers.
|
|
247
246
|
*/
|
|
248
247
|
export type TransactionToken = {
|
|
249
248
|
id: string
|
|
@@ -251,7 +250,7 @@ export type TransactionToken = {
|
|
|
251
250
|
}
|
|
252
251
|
|
|
253
252
|
/**
|
|
254
|
-
* Transaction hints that
|
|
253
|
+
* Transaction hints that database drivers may apply when supported by the dialect.
|
|
255
254
|
*/
|
|
256
255
|
export type TransactionOptions = {
|
|
257
256
|
isolationLevel?: 'read uncommitted' | 'read committed' | 'repeatable read' | 'serializable'
|
|
@@ -259,7 +258,7 @@ export type TransactionOptions = {
|
|
|
259
258
|
}
|
|
260
259
|
|
|
261
260
|
/**
|
|
262
|
-
*
|
|
261
|
+
* Database driver execution request payload.
|
|
263
262
|
*/
|
|
264
263
|
export type DataManipulationRequest = {
|
|
265
264
|
operation: DataManipulationOperation
|
|
@@ -267,7 +266,7 @@ export type DataManipulationRequest = {
|
|
|
267
266
|
}
|
|
268
267
|
|
|
269
268
|
/**
|
|
270
|
-
*
|
|
269
|
+
* Database data-manipulation result payload.
|
|
271
270
|
*/
|
|
272
271
|
export type DataManipulationResult = {
|
|
273
272
|
rows?: Record<string, unknown>[]
|
|
@@ -276,58 +275,59 @@ export type DataManipulationResult = {
|
|
|
276
275
|
}
|
|
277
276
|
|
|
278
277
|
/**
|
|
279
|
-
* Declares
|
|
278
|
+
* Declares database feature support.
|
|
280
279
|
*/
|
|
281
|
-
export type
|
|
282
|
-
returning: boolean
|
|
283
|
-
savepoints: boolean
|
|
284
|
-
upsert: boolean
|
|
285
|
-
transactionalDdl: boolean
|
|
286
|
-
migrationLock: boolean
|
|
280
|
+
export type DatabaseCapabilities = {
|
|
281
|
+
readonly returning: boolean
|
|
282
|
+
readonly savepoints: boolean
|
|
283
|
+
readonly upsert: boolean
|
|
284
|
+
readonly transactionalDdl: boolean
|
|
285
|
+
readonly migrationLock: boolean
|
|
287
286
|
}
|
|
288
287
|
|
|
289
288
|
/**
|
|
290
|
-
*
|
|
289
|
+
* Low-level contract that connects a `Database` to a database engine.
|
|
291
290
|
*/
|
|
292
|
-
export
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
export interface DatabaseAdapter {
|
|
298
|
-
/** Database dialect name exposed by the adapter. */
|
|
299
|
-
dialect: string
|
|
300
|
-
/** Feature flags describing the adapter's supported behaviors. */
|
|
301
|
-
capabilities: AdapterCapabilities
|
|
302
|
-
/** Compiles a data-manipulation operation into executable SQL statements. */
|
|
303
|
-
compileSql(operation: DataManipulationOperation): SqlStatement[]
|
|
291
|
+
export interface DatabaseDriver<dialect extends string = string> {
|
|
292
|
+
/** Stable identifier for the SQL dialect. */
|
|
293
|
+
readonly dialect: dialect
|
|
294
|
+
/** Immutable feature flags used by shared query and migration behavior. */
|
|
295
|
+
readonly capabilities: DatabaseCapabilities
|
|
304
296
|
/** Executes a data-manipulation request. */
|
|
305
297
|
execute(request: DataManipulationRequest): Promise<DataManipulationResult>
|
|
306
|
-
/**
|
|
307
|
-
* Executes a raw SQL script that may contain multiple statements.
|
|
308
|
-
*
|
|
309
|
-
* Drivers must be configured to accept multi-statement scripts where required
|
|
310
|
-
* (for example, mysql2 needs `multipleStatements: true`).
|
|
311
|
-
*/
|
|
298
|
+
/** Executes a raw SQL script that may contain multiple statements. */
|
|
312
299
|
executeScript(sql: string, transaction?: TransactionToken): Promise<void>
|
|
313
|
-
/** Checks whether a table exists. */
|
|
314
|
-
hasTable(table: TableRef, transaction?: TransactionToken): Promise<boolean>
|
|
315
|
-
/** Checks whether a column exists on a table. */
|
|
316
|
-
hasColumn(table: TableRef, column: string, transaction?: TransactionToken): Promise<boolean>
|
|
317
300
|
/** Starts a new database transaction. */
|
|
318
301
|
beginTransaction(options?: TransactionOptions): Promise<TransactionToken>
|
|
319
302
|
/** Commits an open transaction. */
|
|
320
303
|
commitTransaction(token: TransactionToken): Promise<void>
|
|
321
304
|
/** Rolls back an open transaction. */
|
|
322
305
|
rollbackTransaction(token: TransactionToken): Promise<void>
|
|
306
|
+
/** Checks whether a table exists. */
|
|
307
|
+
hasTable(table: TableRef, transaction?: TransactionToken): Promise<boolean>
|
|
308
|
+
/** Checks whether a column exists on a table. */
|
|
309
|
+
hasColumn(table: TableRef, column: string, transaction?: TransactionToken): Promise<boolean>
|
|
323
310
|
/** Creates a savepoint inside an open transaction. */
|
|
324
311
|
createSavepoint(token: TransactionToken, name: string): Promise<void>
|
|
325
312
|
/** Rolls back to a previously created savepoint. */
|
|
326
313
|
rollbackToSavepoint(token: TransactionToken, name: string): Promise<void>
|
|
327
314
|
/** Releases a previously created savepoint. */
|
|
328
315
|
releaseSavepoint(token: TransactionToken, name: string): Promise<void>
|
|
329
|
-
/**
|
|
330
|
-
|
|
331
|
-
/** Releases the
|
|
332
|
-
|
|
316
|
+
/** Destructively recreates the configured database. */
|
|
317
|
+
wipe(): Promise<void>
|
|
318
|
+
/** Releases connection handles owned by the driver. Must be safe to call repeatedly. */
|
|
319
|
+
close(): void | Promise<void>
|
|
320
|
+
/**
|
|
321
|
+
* Runs migration work while holding a driver-specific lock.
|
|
322
|
+
*
|
|
323
|
+
* The callback receives a driver bound to the connection that owns the lock. Drivers must
|
|
324
|
+
* release the lock when the callback rejects as well as when it resolves.
|
|
325
|
+
* @param name Logical migration lock name.
|
|
326
|
+
* @param run Migration work to run with the connection-bound driver.
|
|
327
|
+
* @returns The callback result.
|
|
328
|
+
*/
|
|
329
|
+
withMigrationLock?<result>(
|
|
330
|
+
name: string,
|
|
331
|
+
run: (driver: DatabaseDriver<dialect>) => Promise<result>,
|
|
332
|
+
): Promise<result>
|
|
333
333
|
}
|
package/src/lib/errors.ts
CHANGED
|
@@ -77,9 +77,9 @@ export class DataTableQueryError extends DataTableError {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* Thrown when
|
|
80
|
+
* Thrown when database execution fails.
|
|
81
81
|
*/
|
|
82
|
-
export class
|
|
82
|
+
export class DataTableDatabaseError extends DataTableError {
|
|
83
83
|
constructor(
|
|
84
84
|
message: string,
|
|
85
85
|
options?: {
|
|
@@ -88,12 +88,12 @@ export class DataTableAdapterError extends DataTableError {
|
|
|
88
88
|
},
|
|
89
89
|
) {
|
|
90
90
|
super(message, {
|
|
91
|
-
code: '
|
|
91
|
+
code: 'DATA_TABLE_DATABASE_ERROR',
|
|
92
92
|
cause: options?.cause,
|
|
93
93
|
metadata: options?.metadata,
|
|
94
94
|
})
|
|
95
95
|
|
|
96
|
-
this.name = '
|
|
96
|
+
this.name = 'DataTableDatabaseError'
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|