@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 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 `better-sqlite3`.
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 `better-sqlite3` Integration**: Works well for local and embedded deployments
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 better-sqlite3
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 'better-sqlite3'
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 `better-sqlite3`.
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 Database from 'better-sqlite3'
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 Database(':memory:')
71
+ let sqlite = new DatabaseSync(':memory:')
59
72
  let db = createDatabase(createSqliteDatabaseAdapter(sqlite))
60
73
  ```
61
74
 
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { createSqliteDatabaseAdapter, SqliteDatabaseAdapter } from './lib/adapter.ts';
2
+ export type { SqliteDatabase, SqliteRunResult, SqliteStatement } from './lib/adapter.ts';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -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"}
@@ -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
- * `DatabaseAdapter` implementation for Better SQLite3.
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: BetterSqliteDatabase);
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 Better SQLite3 database instance.
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 BetterSqlite3 from 'better-sqlite3'
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 BetterSqlite3('./data/app.db')
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: BetterSqliteDatabase): SqliteDatabaseAdapter;
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;AAO9B,OAAO,KAAK,EAAE,QAAQ,IAAI,oBAAoB,EAAa,MAAM,gBAAgB,CAAA;AAIjF;;GAEG;AACH,qBAAa,qBAAsB,YAAW,eAAe;;IAC3D;;OAEG;IACH,OAAO,SAAW;IAElB;;OAEG;IACH,YAAY;;;;;;MAAA;IAMZ,YAAY,QAAQ,EAAE,oBAAoB,EASzC;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;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,oBAAoB,GAAG,qBAAqB,CAEjG"}
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"}
@@ -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 Better SQLite3.
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.reader) {
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.pragma('read_uncommitted = true');
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 Better SQLite3 database instance.
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 BetterSqlite3 from 'better-sqlite3'
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 BetterSqlite3('./data/app.db')
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.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
- "better-sqlite3": "^12.4.1",
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": "node --test",
58
- "test:coverage": "node --experimental-test-coverage --test-coverage-include='src/**/*.ts' --test-coverage-lines=90 --test-coverage-branches=90 --test-coverage-functions=90 --test ./src/lib/adapter.test.ts ./src/lib/sql-compiler.test.ts",
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
@@ -1 +1,2 @@
1
1
  export { createSqliteDatabaseAdapter, SqliteDatabaseAdapter } from './lib/adapter.ts'
2
+ export type { SqliteDatabase, SqliteRunResult, SqliteStatement } from './lib/adapter.ts'
@@ -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
- * `DatabaseAdapter` implementation for Better SQLite3.
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: BetterSqliteDatabase
69
+ #database: SqliteDatabase
40
70
  #transactions = new Set<string>()
41
71
  #transactionCounter = 0
42
72
 
43
- constructor(database: BetterSqliteDatabase) {
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.reader) {
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.pragma('read_uncommitted = true')
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 Better SQLite3 database instance.
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 BetterSqlite3 from 'better-sqlite3'
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 BetterSqlite3('./data/app.db')
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: BetterSqliteDatabase): SqliteDatabaseAdapter {
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: RunResult,
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: RunResult,
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
  }