@remix-run/data-table-sqlite 0.3.0 → 0.4.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 +20 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/adapter.d.ts +34 -8
- package/dist/lib/adapter.d.ts.map +1 -1
- package/dist/lib/adapter.js +26 -7
- package/package.json +7 -14
- package/src/index.ts +1 -0
- package/src/lib/adapter.ts +69 -13
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# data-table-sqlite
|
|
2
2
|
|
|
3
3
|
SQLite adapter for [`remix/data-table`](https://github.com/remix-run/remix/tree/main/packages/data-table).
|
|
4
|
-
Use this package when you want `data-table` APIs backed by
|
|
4
|
+
Use this package when you want `data-table` APIs backed by a synchronous SQLite client.
|
|
5
5
|
|
|
6
6
|
## Features
|
|
7
7
|
|
|
8
|
-
- **Native
|
|
8
|
+
- **Native Runtime SQLite Support**: Works with Node's `node:sqlite` `DatabaseSync`, Bun's `bun:sqlite` `Database`, and compatible synchronous SQLite clients
|
|
9
9
|
- **Full `data-table` API Support**: Queries, relations, writes, and transactions
|
|
10
10
|
- **Adapter-Owned Compiler**: SQL compilation lives in this adapter, with optional shared pure helpers from `data-table`
|
|
11
11
|
- **Migration DDL Support**: Compiles and executes `DataMigrationOperation` operations for `remix/data-table/migrations`
|
|
@@ -19,13 +19,26 @@ Use this package when you want `data-table` APIs backed by `better-sqlite3`.
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
21
21
|
```sh
|
|
22
|
-
npm i remix
|
|
22
|
+
npm i remix
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Usage
|
|
26
26
|
|
|
27
|
+
### Node
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { DatabaseSync } from 'node:sqlite'
|
|
31
|
+
import { createDatabase } from 'remix/data-table'
|
|
32
|
+
import { createSqliteDatabaseAdapter } from 'remix/data-table-sqlite'
|
|
33
|
+
|
|
34
|
+
let sqlite = new DatabaseSync('app.db')
|
|
35
|
+
let db = createDatabase(createSqliteDatabaseAdapter(sqlite))
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Bun
|
|
39
|
+
|
|
27
40
|
```ts
|
|
28
|
-
import Database from '
|
|
41
|
+
import { Database } from 'bun:sqlite'
|
|
29
42
|
import { createDatabase } from 'remix/data-table'
|
|
30
43
|
import { createSqliteDatabaseAdapter } from 'remix/data-table-sqlite'
|
|
31
44
|
|
|
@@ -34,7 +47,7 @@ let db = createDatabase(createSqliteDatabaseAdapter(sqlite))
|
|
|
34
47
|
```
|
|
35
48
|
|
|
36
49
|
This is a good fit for local development, embedded deployments, and single-node services.
|
|
37
|
-
Import any driver-specific types you need directly from
|
|
50
|
+
Import any driver-specific types you need directly from your runtime's SQLite module.
|
|
38
51
|
|
|
39
52
|
## Adapter Capabilities
|
|
40
53
|
|
|
@@ -51,11 +64,11 @@ Import any driver-specific types you need directly from `better-sqlite3`.
|
|
|
51
64
|
### In-Memory Database For Tests
|
|
52
65
|
|
|
53
66
|
```ts
|
|
54
|
-
import
|
|
67
|
+
import { DatabaseSync } from 'node:sqlite'
|
|
55
68
|
import { createDatabase } from 'remix/data-table'
|
|
56
69
|
import { createSqliteDatabaseAdapter } from 'remix/data-table-sqlite'
|
|
57
70
|
|
|
58
|
-
let sqlite = new
|
|
71
|
+
let sqlite = new DatabaseSync(':memory:')
|
|
59
72
|
let db = createDatabase(createSqliteDatabaseAdapter(sqlite))
|
|
60
73
|
```
|
|
61
74
|
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AACrF,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/lib/adapter.d.ts
CHANGED
|
@@ -1,7 +1,34 @@
|
|
|
1
1
|
import type { DataManipulationRequest, DataMigrationRequest, DataMigrationResult, DataMigrationOperation, DataManipulationResult, DataManipulationOperation, DatabaseAdapter, SqlStatement, TableRef, TransactionOptions, TransactionToken } from '@remix-run/data-table';
|
|
2
|
-
import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
3
|
+
* Synchronous SQLite database client accepted by the sqlite adapter.
|
|
4
|
+
*
|
|
5
|
+
* This matches the shared surface of Node's `node:sqlite` `DatabaseSync`, Bun's `bun:sqlite`
|
|
6
|
+
* `Database`, and `better-sqlite3` database instances.
|
|
7
|
+
*/
|
|
8
|
+
export interface SqliteDatabase {
|
|
9
|
+
prepare(sql: string): SqliteStatement;
|
|
10
|
+
exec(sql: string): unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Prepared statement shape used by {@link SqliteDatabase}.
|
|
14
|
+
*/
|
|
15
|
+
export interface SqliteStatement {
|
|
16
|
+
all(...values: unknown[]): unknown[];
|
|
17
|
+
get(...values: unknown[]): unknown;
|
|
18
|
+
run(...values: unknown[]): SqliteRunResult;
|
|
19
|
+
reader?: boolean;
|
|
20
|
+
columns?: () => unknown[];
|
|
21
|
+
columnNames?: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* SQLite write execution metadata.
|
|
25
|
+
*/
|
|
26
|
+
export interface SqliteRunResult {
|
|
27
|
+
changes: number;
|
|
28
|
+
lastInsertRowid: unknown;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `DatabaseAdapter` implementation for synchronous SQLite clients.
|
|
5
32
|
*/
|
|
6
33
|
export declare class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
7
34
|
#private;
|
|
@@ -19,7 +46,7 @@ export declare class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
19
46
|
transactionalDdl: boolean;
|
|
20
47
|
migrationLock: boolean;
|
|
21
48
|
};
|
|
22
|
-
constructor(database:
|
|
49
|
+
constructor(database: SqliteDatabase);
|
|
23
50
|
/**
|
|
24
51
|
* Compiles a data or migration operation to sqlite SQL statements.
|
|
25
52
|
* @param operation Operation to compile.
|
|
@@ -95,19 +122,18 @@ export declare class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
95
122
|
}
|
|
96
123
|
/**
|
|
97
124
|
* Creates a sqlite `DatabaseAdapter`.
|
|
98
|
-
* @param database
|
|
99
|
-
* @param options Optional adapter capability overrides.
|
|
125
|
+
* @param database Synchronous SQLite database client.
|
|
100
126
|
* @returns A configured sqlite adapter.
|
|
101
127
|
* @example
|
|
102
128
|
* ```ts
|
|
103
|
-
* import
|
|
129
|
+
* import { DatabaseSync } from 'node:sqlite'
|
|
104
130
|
* import { createDatabase } from 'remix/data-table'
|
|
105
131
|
* import { createSqliteDatabaseAdapter } from 'remix/data-table-sqlite'
|
|
106
132
|
*
|
|
107
|
-
* let sqlite = new
|
|
133
|
+
* let sqlite = new DatabaseSync('./data/app.db')
|
|
108
134
|
* let adapter = createSqliteDatabaseAdapter(sqlite)
|
|
109
135
|
* let db = createDatabase(adapter)
|
|
110
136
|
* ```
|
|
111
137
|
*/
|
|
112
|
-
export declare function createSqliteDatabaseAdapter(database:
|
|
138
|
+
export declare function createSqliteDatabaseAdapter(database: SqliteDatabase): SqliteDatabaseAdapter;
|
|
113
139
|
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/lib/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EAEf,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/lib/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EAEf,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,uBAAuB,CAAA;AAU9B;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAA;IACrC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAA;IACpC,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAClC,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,eAAe,CAAA;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,OAAO,EAAE,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,OAAO,CAAA;CACzB;AAED;;GAEG;AACH,qBAAa,qBAAsB,YAAW,eAAe;;IAC3D;;OAEG;IACH,OAAO,SAAW;IAElB;;OAEG;IACH,YAAY;;;;;;MAAA;IAMZ,YAAY,QAAQ,EAAE,cAAc,EASnC;IAED;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,yBAAyB,GAAG,sBAAsB,GAAG,YAAY,EAAE,CAOxF;IAED;;;;OAIG;IACG,OAAO,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAgC/E;IAED;;;;OAIG;IACG,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAWzE;IAED;;;;;OAKG;IACG,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAahF;IAED;;;;;;OAMG;IACG,SAAS,CACb,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,gBAAgB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAYlB;IAED;;;;OAIG;IACG,gBAAgB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAY9E;IAED;;;;OAIG;IACG,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAI9D;IAED;;;;OAIG;IACG,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE;IAED;;;;;OAKG;IACG,eAAe,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1E;IAED;;;;;OAKG;IACG,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9E;IAED;;;;;OAKG;IACG,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3E;CAOF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,cAAc,GAAG,qBAAqB,CAE3F"}
|
package/dist/lib/adapter.js
CHANGED
|
@@ -2,7 +2,7 @@ import { getTablePrimaryKey } from '@remix-run/data-table';
|
|
|
2
2
|
import { isDataManipulationOperation as isDataManipulationOperationHelper, quoteLiteral as quoteLiteralHelper, quoteTableRef as quoteTableRefHelper, } from '@remix-run/data-table/sql-helpers';
|
|
3
3
|
import { compileSqliteOperation } from "./sql-compiler.js";
|
|
4
4
|
/**
|
|
5
|
-
* `DatabaseAdapter` implementation for
|
|
5
|
+
* `DatabaseAdapter` implementation for synchronous SQLite clients.
|
|
6
6
|
*/
|
|
7
7
|
export class SqliteDatabaseAdapter {
|
|
8
8
|
/**
|
|
@@ -53,7 +53,7 @@ export class SqliteDatabaseAdapter {
|
|
|
53
53
|
}
|
|
54
54
|
let statement = this.compileSql(request.operation)[0];
|
|
55
55
|
let prepared = this.#database.prepare(statement.text);
|
|
56
|
-
if (prepared
|
|
56
|
+
if (shouldReadStatement(request.operation, prepared)) {
|
|
57
57
|
let rows = normalizeRows(prepared.all(...statement.values));
|
|
58
58
|
if (request.operation.kind === 'count' || request.operation.kind === 'exists') {
|
|
59
59
|
rows = normalizeCountRows(rows);
|
|
@@ -125,7 +125,7 @@ export class SqliteDatabaseAdapter {
|
|
|
125
125
|
*/
|
|
126
126
|
async beginTransaction(options) {
|
|
127
127
|
if (options?.isolationLevel === 'read uncommitted') {
|
|
128
|
-
this.#database.
|
|
128
|
+
this.#database.exec('pragma read_uncommitted = true');
|
|
129
129
|
}
|
|
130
130
|
this.#database.exec('begin');
|
|
131
131
|
this.#transactionCounter += 1;
|
|
@@ -191,16 +191,15 @@ export class SqliteDatabaseAdapter {
|
|
|
191
191
|
}
|
|
192
192
|
/**
|
|
193
193
|
* Creates a sqlite `DatabaseAdapter`.
|
|
194
|
-
* @param database
|
|
195
|
-
* @param options Optional adapter capability overrides.
|
|
194
|
+
* @param database Synchronous SQLite database client.
|
|
196
195
|
* @returns A configured sqlite adapter.
|
|
197
196
|
* @example
|
|
198
197
|
* ```ts
|
|
199
|
-
* import
|
|
198
|
+
* import { DatabaseSync } from 'node:sqlite'
|
|
200
199
|
* import { createDatabase } from 'remix/data-table'
|
|
201
200
|
* import { createSqliteDatabaseAdapter } from 'remix/data-table-sqlite'
|
|
202
201
|
*
|
|
203
|
-
* let sqlite = new
|
|
202
|
+
* let sqlite = new DatabaseSync('./data/app.db')
|
|
204
203
|
* let adapter = createSqliteDatabaseAdapter(sqlite)
|
|
205
204
|
* let db = createDatabase(adapter)
|
|
206
205
|
* ```
|
|
@@ -270,6 +269,26 @@ function normalizeInsertIdForRun(kind, operation, result) {
|
|
|
270
269
|
}
|
|
271
270
|
return result.lastInsertRowid;
|
|
272
271
|
}
|
|
272
|
+
function shouldReadStatement(operation, statement) {
|
|
273
|
+
if (operation.kind === 'select' || operation.kind === 'count' || operation.kind === 'exists') {
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
if (operation.kind !== 'raw') {
|
|
277
|
+
return operation.returning !== undefined;
|
|
278
|
+
}
|
|
279
|
+
if (typeof statement.reader === 'boolean') {
|
|
280
|
+
return statement.reader;
|
|
281
|
+
}
|
|
282
|
+
if (statement.columns) {
|
|
283
|
+
return statement.columns().length > 0;
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
return statement.columnNames !== undefined && statement.columnNames.length > 0;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
273
292
|
function quoteIdentifier(value) {
|
|
274
293
|
return '"' + value.replace(/"/g, '""') + '"';
|
|
275
294
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/data-table-sqlite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "SQLite adapter for remix/data-table",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,23 +27,15 @@
|
|
|
27
27
|
"./package.json": "./package.json"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
31
30
|
"@types/node": "^24.6.0",
|
|
32
31
|
"@typescript/native-preview": "7.0.0-dev.20251125.1",
|
|
33
|
-
"
|
|
34
|
-
"@remix-run/data-table": "0.2.0"
|
|
32
|
+
"@remix-run/assert": "0.1.0",
|
|
33
|
+
"@remix-run/data-table": "0.2.0",
|
|
34
|
+
"@remix-run/test": "0.2.0"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@remix-run/data-table": "^0.2.0"
|
|
38
38
|
},
|
|
39
|
-
"peerDependencies": {
|
|
40
|
-
"better-sqlite3": "^12.4.1"
|
|
41
|
-
},
|
|
42
|
-
"peerDependenciesMeta": {
|
|
43
|
-
"better-sqlite3": {
|
|
44
|
-
"optional": true
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
39
|
"keywords": [
|
|
48
40
|
"remix",
|
|
49
41
|
"orm",
|
|
@@ -54,8 +46,9 @@
|
|
|
54
46
|
"scripts": {
|
|
55
47
|
"build": "tsgo -p tsconfig.build.json",
|
|
56
48
|
"clean": "git clean -fdX",
|
|
57
|
-
"test": "
|
|
58
|
-
"test:
|
|
49
|
+
"test": "remix-test",
|
|
50
|
+
"test:bun": "bun x --bun remix-test",
|
|
51
|
+
"test:coverage": "remix-test --coverage",
|
|
59
52
|
"typecheck": "tsgo --noEmit"
|
|
60
53
|
}
|
|
61
54
|
}
|
package/src/index.ts
CHANGED
package/src/lib/adapter.ts
CHANGED
|
@@ -18,12 +18,42 @@ import {
|
|
|
18
18
|
quoteLiteral as quoteLiteralHelper,
|
|
19
19
|
quoteTableRef as quoteTableRefHelper,
|
|
20
20
|
} from '@remix-run/data-table/sql-helpers'
|
|
21
|
-
import type { Database as BetterSqliteDatabase, RunResult } from 'better-sqlite3'
|
|
22
21
|
|
|
23
22
|
import { compileSqliteOperation } from './sql-compiler.ts'
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
|
-
*
|
|
25
|
+
* Synchronous SQLite database client accepted by the sqlite adapter.
|
|
26
|
+
*
|
|
27
|
+
* This matches the shared surface of Node's `node:sqlite` `DatabaseSync`, Bun's `bun:sqlite`
|
|
28
|
+
* `Database`, and `better-sqlite3` database instances.
|
|
29
|
+
*/
|
|
30
|
+
export interface SqliteDatabase {
|
|
31
|
+
prepare(sql: string): SqliteStatement
|
|
32
|
+
exec(sql: string): unknown
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Prepared statement shape used by {@link SqliteDatabase}.
|
|
37
|
+
*/
|
|
38
|
+
export interface SqliteStatement {
|
|
39
|
+
all(...values: unknown[]): unknown[]
|
|
40
|
+
get(...values: unknown[]): unknown
|
|
41
|
+
run(...values: unknown[]): SqliteRunResult
|
|
42
|
+
reader?: boolean
|
|
43
|
+
columns?: () => unknown[]
|
|
44
|
+
columnNames?: string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* SQLite write execution metadata.
|
|
49
|
+
*/
|
|
50
|
+
export interface SqliteRunResult {
|
|
51
|
+
changes: number
|
|
52
|
+
lastInsertRowid: unknown
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* `DatabaseAdapter` implementation for synchronous SQLite clients.
|
|
27
57
|
*/
|
|
28
58
|
export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
29
59
|
/**
|
|
@@ -36,11 +66,11 @@ export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
36
66
|
*/
|
|
37
67
|
capabilities
|
|
38
68
|
|
|
39
|
-
#database:
|
|
69
|
+
#database: SqliteDatabase
|
|
40
70
|
#transactions = new Set<string>()
|
|
41
71
|
#transactionCounter = 0
|
|
42
72
|
|
|
43
|
-
constructor(database:
|
|
73
|
+
constructor(database: SqliteDatabase) {
|
|
44
74
|
this.#database = database
|
|
45
75
|
this.capabilities = {
|
|
46
76
|
returning: true,
|
|
@@ -82,7 +112,7 @@ export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
82
112
|
let statement = this.compileSql(request.operation)[0]
|
|
83
113
|
let prepared = this.#database.prepare(statement.text)
|
|
84
114
|
|
|
85
|
-
if (prepared
|
|
115
|
+
if (shouldReadStatement(request.operation, prepared)) {
|
|
86
116
|
let rows = normalizeRows(prepared.all(...statement.values))
|
|
87
117
|
|
|
88
118
|
if (request.operation.kind === 'count' || request.operation.kind === 'exists') {
|
|
@@ -175,7 +205,7 @@ export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
175
205
|
*/
|
|
176
206
|
async beginTransaction(options?: TransactionOptions): Promise<TransactionToken> {
|
|
177
207
|
if (options?.isolationLevel === 'read uncommitted') {
|
|
178
|
-
this.#database.
|
|
208
|
+
this.#database.exec('pragma read_uncommitted = true')
|
|
179
209
|
}
|
|
180
210
|
|
|
181
211
|
this.#database.exec('begin')
|
|
@@ -251,21 +281,20 @@ export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
251
281
|
|
|
252
282
|
/**
|
|
253
283
|
* Creates a sqlite `DatabaseAdapter`.
|
|
254
|
-
* @param database
|
|
255
|
-
* @param options Optional adapter capability overrides.
|
|
284
|
+
* @param database Synchronous SQLite database client.
|
|
256
285
|
* @returns A configured sqlite adapter.
|
|
257
286
|
* @example
|
|
258
287
|
* ```ts
|
|
259
|
-
* import
|
|
288
|
+
* import { DatabaseSync } from 'node:sqlite'
|
|
260
289
|
* import { createDatabase } from 'remix/data-table'
|
|
261
290
|
* import { createSqliteDatabaseAdapter } from 'remix/data-table-sqlite'
|
|
262
291
|
*
|
|
263
|
-
* let sqlite = new
|
|
292
|
+
* let sqlite = new DatabaseSync('./data/app.db')
|
|
264
293
|
* let adapter = createSqliteDatabaseAdapter(sqlite)
|
|
265
294
|
* let db = createDatabase(adapter)
|
|
266
295
|
* ```
|
|
267
296
|
*/
|
|
268
|
-
export function createSqliteDatabaseAdapter(database:
|
|
297
|
+
export function createSqliteDatabaseAdapter(database: SqliteDatabase): SqliteDatabaseAdapter {
|
|
269
298
|
return new SqliteDatabaseAdapter(database)
|
|
270
299
|
}
|
|
271
300
|
|
|
@@ -339,7 +368,7 @@ function normalizeInsertIdForReader(
|
|
|
339
368
|
|
|
340
369
|
function normalizeAffectedRowsForRun(
|
|
341
370
|
kind: DataManipulationRequest['operation']['kind'],
|
|
342
|
-
result:
|
|
371
|
+
result: SqliteRunResult,
|
|
343
372
|
): number | undefined {
|
|
344
373
|
if (kind === 'select' || kind === 'count' || kind === 'exists') {
|
|
345
374
|
return undefined
|
|
@@ -351,7 +380,7 @@ function normalizeAffectedRowsForRun(
|
|
|
351
380
|
function normalizeInsertIdForRun(
|
|
352
381
|
kind: DataManipulationRequest['operation']['kind'],
|
|
353
382
|
operation: DataManipulationRequest['operation'],
|
|
354
|
-
result:
|
|
383
|
+
result: SqliteRunResult,
|
|
355
384
|
): unknown {
|
|
356
385
|
if (!isInsertOperationKind(kind) || !isInsertOperation(operation)) {
|
|
357
386
|
return undefined
|
|
@@ -364,6 +393,33 @@ function normalizeInsertIdForRun(
|
|
|
364
393
|
return result.lastInsertRowid
|
|
365
394
|
}
|
|
366
395
|
|
|
396
|
+
function shouldReadStatement(
|
|
397
|
+
operation: DataManipulationRequest['operation'],
|
|
398
|
+
statement: SqliteStatement,
|
|
399
|
+
): boolean {
|
|
400
|
+
if (operation.kind === 'select' || operation.kind === 'count' || operation.kind === 'exists') {
|
|
401
|
+
return true
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (operation.kind !== 'raw') {
|
|
405
|
+
return operation.returning !== undefined
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (typeof statement.reader === 'boolean') {
|
|
409
|
+
return statement.reader
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (statement.columns) {
|
|
413
|
+
return statement.columns().length > 0
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
return statement.columnNames !== undefined && statement.columnNames.length > 0
|
|
418
|
+
} catch {
|
|
419
|
+
return false
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
367
423
|
function quoteIdentifier(value: string): string {
|
|
368
424
|
return '"' + value.replace(/"/g, '""') + '"'
|
|
369
425
|
}
|