@prisma/adapter-better-sqlite3 6.7.0-dev.10

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/dist/index.mjs ADDED
@@ -0,0 +1,582 @@
1
+ // src/better-sqlite3.ts
2
+ import { Debug as Debug2, DriverAdapterError } from "@prisma/driver-adapter-utils";
3
+
4
+ // ../../node_modules/.pnpm/async-mutex@0.5.0/node_modules/async-mutex/index.mjs
5
+ var E_TIMEOUT = new Error("timeout while waiting for mutex to become available");
6
+ var E_ALREADY_LOCKED = new Error("mutex already locked");
7
+ var E_CANCELED = new Error("request for lock canceled");
8
+ var __awaiter$2 = function(thisArg, _arguments, P, generator) {
9
+ function adopt(value) {
10
+ return value instanceof P ? value : new P(function(resolve) {
11
+ resolve(value);
12
+ });
13
+ }
14
+ return new (P || (P = Promise))(function(resolve, reject) {
15
+ function fulfilled(value) {
16
+ try {
17
+ step(generator.next(value));
18
+ } catch (e) {
19
+ reject(e);
20
+ }
21
+ }
22
+ function rejected(value) {
23
+ try {
24
+ step(generator["throw"](value));
25
+ } catch (e) {
26
+ reject(e);
27
+ }
28
+ }
29
+ function step(result) {
30
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
31
+ }
32
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
33
+ });
34
+ };
35
+ var Semaphore = class {
36
+ constructor(_value, _cancelError = E_CANCELED) {
37
+ this._value = _value;
38
+ this._cancelError = _cancelError;
39
+ this._queue = [];
40
+ this._weightedWaiters = [];
41
+ }
42
+ acquire(weight = 1, priority = 0) {
43
+ if (weight <= 0)
44
+ throw new Error(`invalid weight ${weight}: must be positive`);
45
+ return new Promise((resolve, reject) => {
46
+ const task = { resolve, reject, weight, priority };
47
+ const i = findIndexFromEnd(this._queue, (other) => priority <= other.priority);
48
+ if (i === -1 && weight <= this._value) {
49
+ this._dispatchItem(task);
50
+ } else {
51
+ this._queue.splice(i + 1, 0, task);
52
+ }
53
+ });
54
+ }
55
+ runExclusive(callback_1) {
56
+ return __awaiter$2(this, arguments, void 0, function* (callback, weight = 1, priority = 0) {
57
+ const [value, release] = yield this.acquire(weight, priority);
58
+ try {
59
+ return yield callback(value);
60
+ } finally {
61
+ release();
62
+ }
63
+ });
64
+ }
65
+ waitForUnlock(weight = 1, priority = 0) {
66
+ if (weight <= 0)
67
+ throw new Error(`invalid weight ${weight}: must be positive`);
68
+ if (this._couldLockImmediately(weight, priority)) {
69
+ return Promise.resolve();
70
+ } else {
71
+ return new Promise((resolve) => {
72
+ if (!this._weightedWaiters[weight - 1])
73
+ this._weightedWaiters[weight - 1] = [];
74
+ insertSorted(this._weightedWaiters[weight - 1], { resolve, priority });
75
+ });
76
+ }
77
+ }
78
+ isLocked() {
79
+ return this._value <= 0;
80
+ }
81
+ getValue() {
82
+ return this._value;
83
+ }
84
+ setValue(value) {
85
+ this._value = value;
86
+ this._dispatchQueue();
87
+ }
88
+ release(weight = 1) {
89
+ if (weight <= 0)
90
+ throw new Error(`invalid weight ${weight}: must be positive`);
91
+ this._value += weight;
92
+ this._dispatchQueue();
93
+ }
94
+ cancel() {
95
+ this._queue.forEach((entry) => entry.reject(this._cancelError));
96
+ this._queue = [];
97
+ }
98
+ _dispatchQueue() {
99
+ this._drainUnlockWaiters();
100
+ while (this._queue.length > 0 && this._queue[0].weight <= this._value) {
101
+ this._dispatchItem(this._queue.shift());
102
+ this._drainUnlockWaiters();
103
+ }
104
+ }
105
+ _dispatchItem(item) {
106
+ const previousValue = this._value;
107
+ this._value -= item.weight;
108
+ item.resolve([previousValue, this._newReleaser(item.weight)]);
109
+ }
110
+ _newReleaser(weight) {
111
+ let called = false;
112
+ return () => {
113
+ if (called)
114
+ return;
115
+ called = true;
116
+ this.release(weight);
117
+ };
118
+ }
119
+ _drainUnlockWaiters() {
120
+ if (this._queue.length === 0) {
121
+ for (let weight = this._value; weight > 0; weight--) {
122
+ const waiters = this._weightedWaiters[weight - 1];
123
+ if (!waiters)
124
+ continue;
125
+ waiters.forEach((waiter) => waiter.resolve());
126
+ this._weightedWaiters[weight - 1] = [];
127
+ }
128
+ } else {
129
+ const queuedPriority = this._queue[0].priority;
130
+ for (let weight = this._value; weight > 0; weight--) {
131
+ const waiters = this._weightedWaiters[weight - 1];
132
+ if (!waiters)
133
+ continue;
134
+ const i = waiters.findIndex((waiter) => waiter.priority <= queuedPriority);
135
+ (i === -1 ? waiters : waiters.splice(0, i)).forEach((waiter) => waiter.resolve());
136
+ }
137
+ }
138
+ }
139
+ _couldLockImmediately(weight, priority) {
140
+ return (this._queue.length === 0 || this._queue[0].priority < priority) && weight <= this._value;
141
+ }
142
+ };
143
+ function insertSorted(a, v) {
144
+ const i = findIndexFromEnd(a, (other) => v.priority <= other.priority);
145
+ a.splice(i + 1, 0, v);
146
+ }
147
+ function findIndexFromEnd(a, predicate) {
148
+ for (let i = a.length - 1; i >= 0; i--) {
149
+ if (predicate(a[i])) {
150
+ return i;
151
+ }
152
+ }
153
+ return -1;
154
+ }
155
+ var __awaiter$1 = function(thisArg, _arguments, P, generator) {
156
+ function adopt(value) {
157
+ return value instanceof P ? value : new P(function(resolve) {
158
+ resolve(value);
159
+ });
160
+ }
161
+ return new (P || (P = Promise))(function(resolve, reject) {
162
+ function fulfilled(value) {
163
+ try {
164
+ step(generator.next(value));
165
+ } catch (e) {
166
+ reject(e);
167
+ }
168
+ }
169
+ function rejected(value) {
170
+ try {
171
+ step(generator["throw"](value));
172
+ } catch (e) {
173
+ reject(e);
174
+ }
175
+ }
176
+ function step(result) {
177
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
178
+ }
179
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
180
+ });
181
+ };
182
+ var Mutex = class {
183
+ constructor(cancelError) {
184
+ this._semaphore = new Semaphore(1, cancelError);
185
+ }
186
+ acquire() {
187
+ return __awaiter$1(this, arguments, void 0, function* (priority = 0) {
188
+ const [, releaser] = yield this._semaphore.acquire(1, priority);
189
+ return releaser;
190
+ });
191
+ }
192
+ runExclusive(callback, priority = 0) {
193
+ return this._semaphore.runExclusive(() => callback(), 1, priority);
194
+ }
195
+ isLocked() {
196
+ return this._semaphore.isLocked();
197
+ }
198
+ waitForUnlock(priority = 0) {
199
+ return this._semaphore.waitForUnlock(1, priority);
200
+ }
201
+ release() {
202
+ if (this._semaphore.isLocked())
203
+ this._semaphore.release();
204
+ }
205
+ cancel() {
206
+ return this._semaphore.cancel();
207
+ }
208
+ };
209
+
210
+ // src/better-sqlite3.ts
211
+ import Database from "better-sqlite3";
212
+
213
+ // package.json
214
+ var name = "@prisma/adapter-better-sqlite3";
215
+
216
+ // src/conversion.ts
217
+ import { ColumnTypeEnum, Debug } from "@prisma/driver-adapter-utils";
218
+ var debug = Debug("prisma:driver-adapter:better-sqlite3:conversion");
219
+ function mapDeclType(declType) {
220
+ if (declType === null) {
221
+ return null;
222
+ }
223
+ switch (declType.toUpperCase()) {
224
+ case "":
225
+ return null;
226
+ case "DECIMAL":
227
+ return ColumnTypeEnum.Numeric;
228
+ case "FLOAT":
229
+ return ColumnTypeEnum.Float;
230
+ case "DOUBLE":
231
+ case "DOUBLE PRECISION":
232
+ case "NUMERIC":
233
+ case "REAL":
234
+ return ColumnTypeEnum.Double;
235
+ case "TINYINT":
236
+ case "SMALLINT":
237
+ case "MEDIUMINT":
238
+ case "INT":
239
+ case "INTEGER":
240
+ case "SERIAL":
241
+ case "INT2":
242
+ return ColumnTypeEnum.Int32;
243
+ case "BIGINT":
244
+ case "UNSIGNED BIG INT":
245
+ case "INT8":
246
+ return ColumnTypeEnum.Int64;
247
+ case "DATETIME":
248
+ case "TIMESTAMP":
249
+ return ColumnTypeEnum.DateTime;
250
+ case "TIME":
251
+ return ColumnTypeEnum.Time;
252
+ case "DATE":
253
+ return ColumnTypeEnum.Date;
254
+ case "TEXT":
255
+ case "CLOB":
256
+ case "CHARACTER":
257
+ case "VARCHAR":
258
+ case "VARYING CHARACTER":
259
+ case "NCHAR":
260
+ case "NATIVE CHARACTER":
261
+ case "NVARCHAR":
262
+ return ColumnTypeEnum.Text;
263
+ case "BLOB":
264
+ return ColumnTypeEnum.Bytes;
265
+ case "BOOLEAN":
266
+ return ColumnTypeEnum.Boolean;
267
+ case "JSONB":
268
+ return ColumnTypeEnum.Json;
269
+ default:
270
+ debug("unknown decltype:", declType);
271
+ return null;
272
+ }
273
+ }
274
+ function mapDeclaredColumnTypes(columnTypes) {
275
+ const emptyIndices = /* @__PURE__ */ new Set();
276
+ const result = columnTypes.map((typeName, index) => {
277
+ const mappedType = mapDeclType(typeName);
278
+ if (mappedType === null) {
279
+ emptyIndices.add(index);
280
+ }
281
+ return mappedType;
282
+ });
283
+ return [result, emptyIndices];
284
+ }
285
+ function getColumnTypes(declaredTypes, rows) {
286
+ const [columnTypes, emptyIndices] = mapDeclaredColumnTypes(declaredTypes);
287
+ if (emptyIndices.size === 0) {
288
+ return columnTypes;
289
+ }
290
+ columnLoop: for (const columnIndex of emptyIndices) {
291
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
292
+ const candidateValue = rows[rowIndex][columnIndex];
293
+ if (candidateValue !== null) {
294
+ columnTypes[columnIndex] = inferColumnType(candidateValue);
295
+ continue columnLoop;
296
+ }
297
+ }
298
+ columnTypes[columnIndex] = ColumnTypeEnum.Int32;
299
+ }
300
+ return columnTypes;
301
+ }
302
+ function inferColumnType(value) {
303
+ switch (typeof value) {
304
+ case "string":
305
+ return ColumnTypeEnum.Text;
306
+ case "bigint":
307
+ return ColumnTypeEnum.Int64;
308
+ case "boolean":
309
+ return ColumnTypeEnum.Boolean;
310
+ case "number":
311
+ return ColumnTypeEnum.UnknownNumber;
312
+ case "object":
313
+ return inferObjectType(value);
314
+ default:
315
+ throw new UnexpectedTypeError(value);
316
+ }
317
+ }
318
+ function inferObjectType(value) {
319
+ if (value instanceof ArrayBuffer) {
320
+ return ColumnTypeEnum.Bytes;
321
+ }
322
+ throw new UnexpectedTypeError(value);
323
+ }
324
+ var UnexpectedTypeError = class extends Error {
325
+ name = "UnexpectedTypeError";
326
+ constructor(value) {
327
+ const type = typeof value;
328
+ const repr = type === "object" ? JSON.stringify(value) : String(value);
329
+ super(`unexpected value of type ${type}: ${repr}`);
330
+ }
331
+ };
332
+ function mapRow(row, columnTypes) {
333
+ const result = Array.from(row);
334
+ for (let i = 0; i < result.length; i++) {
335
+ const value = result[i];
336
+ if (value instanceof ArrayBuffer || value instanceof Buffer) {
337
+ result[i] = Array.from(new Uint8Array(value));
338
+ continue;
339
+ }
340
+ if (typeof value === "number" && (columnTypes[i] === ColumnTypeEnum.Int32 || columnTypes[i] === ColumnTypeEnum.Int64) && !Number.isInteger(value)) {
341
+ result[i] = Math.trunc(value);
342
+ continue;
343
+ }
344
+ if (["number", "bigint"].includes(typeof value) && columnTypes[i] === ColumnTypeEnum.DateTime) {
345
+ result[i] = new Date(Number(value)).toISOString();
346
+ continue;
347
+ }
348
+ if (typeof value === "bigint") {
349
+ result[i] = value.toString();
350
+ continue;
351
+ }
352
+ }
353
+ return result;
354
+ }
355
+ function mapQueryArgs(args, argTypes) {
356
+ return args.map((arg, i) => {
357
+ const argType = argTypes[i];
358
+ if (argType === "Int32") {
359
+ return Number.parseInt(arg);
360
+ }
361
+ if (argType === "Float" || argType === "Double") {
362
+ return Number.parseFloat(arg);
363
+ }
364
+ if (typeof arg === "boolean") {
365
+ return arg ? 1 : 0;
366
+ }
367
+ if (arg instanceof Date) {
368
+ return arg.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
369
+ }
370
+ if (arg instanceof Uint8Array) {
371
+ return Buffer.from(arg);
372
+ }
373
+ return arg;
374
+ });
375
+ }
376
+
377
+ // src/errors.ts
378
+ function convertDriverError(error) {
379
+ if (typeof error.code !== "string" || typeof error.message !== "string") {
380
+ throw error;
381
+ }
382
+ switch (error.code) {
383
+ case "SQLITE_BUSY":
384
+ return {
385
+ kind: "SocketTimeout"
386
+ };
387
+ case "SQLITE_CONSTRAINT_UNIQUE":
388
+ case "SQLITE_CONSTRAINT_PRIMARYKEY":
389
+ return {
390
+ kind: "UniqueConstraintViolation",
391
+ fields: error.message.split("constraint failed: ").at(1)?.split(", ").map((field) => field.split(".").pop()) ?? []
392
+ };
393
+ case "SQLITE_CONSTRAINT_NOTNULL":
394
+ return {
395
+ kind: "NullConstraintViolation",
396
+ fields: error.message.split("constraint failed: ").at(1)?.split(", ").map((field) => field.split(".").pop()) ?? []
397
+ };
398
+ case "SQLITE_CONSTRAINT_FOREIGNKEY":
399
+ case "SQLITE_CONSTRAINT_TRIGGER":
400
+ return {
401
+ kind: "ForeignKeyConstraintViolation",
402
+ constraint: { foreignKey: {} }
403
+ };
404
+ default:
405
+ if (error.message.startsWith("no such table")) {
406
+ return {
407
+ kind: "TableDoesNotExist",
408
+ table: error.message.split(": ").pop()
409
+ };
410
+ } else if (error.message.startsWith("no such column")) {
411
+ return {
412
+ kind: "ColumnNotFound",
413
+ column: error.message.split(": ").pop()
414
+ };
415
+ } else if (error.message.includes("has no column named ")) {
416
+ return {
417
+ kind: "ColumnNotFound",
418
+ column: error.message.split("has no column named ").pop()
419
+ };
420
+ }
421
+ throw error;
422
+ }
423
+ }
424
+
425
+ // src/better-sqlite3.ts
426
+ var debug2 = Debug2("prisma:driver-adapter:better-sqlite3");
427
+ var LOCK_TAG = Symbol();
428
+ var BetterSQLite3Queryable = class {
429
+ constructor(client) {
430
+ this.client = client;
431
+ }
432
+ provider = "sqlite";
433
+ adapterName = name;
434
+ /**
435
+ * Execute a query given as SQL, interpolating the given parameters.
436
+ */
437
+ async queryRaw(query) {
438
+ const tag = "[js::queryRaw]";
439
+ debug2(`${tag} %O`, query);
440
+ const { columnNames, declaredTypes, values } = await this.performIO(query);
441
+ const rows = values;
442
+ const columnTypes = getColumnTypes(declaredTypes, rows);
443
+ return {
444
+ columnNames,
445
+ columnTypes,
446
+ rows: rows.map((row) => mapRow(row, columnTypes))
447
+ };
448
+ }
449
+ /**
450
+ * Execute a query given as SQL, interpolating the given parameters and
451
+ * returning the number of affected rows.
452
+ * Note: Queryable expects a u64, but napi.rs only supports u32.
453
+ */
454
+ async executeRaw(query) {
455
+ const tag = "[js::executeRaw]";
456
+ debug2(`${tag} %O`, query);
457
+ return (await this.executeIO(query)).changes;
458
+ }
459
+ /**
460
+ * Run a query against the database, returning the result set.
461
+ * Should the query fail due to a connection error, the connection is
462
+ * marked as unhealthy.
463
+ */
464
+ executeIO(query) {
465
+ try {
466
+ const stmt = this.client.prepare(query.sql).bind(mapQueryArgs(query.args, query.argTypes));
467
+ const result = stmt.run();
468
+ return Promise.resolve(result);
469
+ } catch (e) {
470
+ this.onError(e);
471
+ }
472
+ }
473
+ /**
474
+ * Run a query against the database, returning the result set.
475
+ * Should the query fail due to a connection error, the connection is
476
+ * marked as unhealthy.
477
+ */
478
+ performIO(query) {
479
+ try {
480
+ const stmt = this.client.prepare(query.sql).bind(mapQueryArgs(query.args, query.argTypes));
481
+ if (!stmt.reader) {
482
+ stmt.run();
483
+ return Promise.resolve({
484
+ columnNames: [],
485
+ declaredTypes: [],
486
+ values: []
487
+ });
488
+ }
489
+ const columns = stmt.columns();
490
+ const resultSet = {
491
+ declaredTypes: columns.map((column) => column.type),
492
+ columnNames: columns.map((column) => column.name),
493
+ values: stmt.raw().all()
494
+ };
495
+ return Promise.resolve(resultSet);
496
+ } catch (e) {
497
+ this.onError(e);
498
+ }
499
+ }
500
+ onError(error) {
501
+ debug2("Error in performIO: %O", error);
502
+ throw new DriverAdapterError(convertDriverError(error));
503
+ }
504
+ };
505
+ var BetterSQLite3Transaction = class extends BetterSQLite3Queryable {
506
+ constructor(client, options, unlockParent) {
507
+ super(client);
508
+ this.options = options;
509
+ this.unlockParent = unlockParent;
510
+ }
511
+ commit() {
512
+ debug2(`[js::commit]`);
513
+ this.unlockParent();
514
+ return Promise.resolve();
515
+ }
516
+ rollback() {
517
+ debug2(`[js::rollback]`);
518
+ this.unlockParent();
519
+ return Promise.resolve();
520
+ }
521
+ };
522
+ var PrismaBetterSQLite3Adapter = class extends BetterSQLite3Queryable {
523
+ [LOCK_TAG] = new Mutex();
524
+ constructor(client) {
525
+ super(client);
526
+ }
527
+ executeScript(script) {
528
+ try {
529
+ this.client.exec(script);
530
+ } catch (e) {
531
+ this.onError(e);
532
+ }
533
+ return Promise.resolve();
534
+ }
535
+ async startTransaction(isolationLevel) {
536
+ if (isolationLevel && isolationLevel !== "SERIALIZABLE") {
537
+ throw new DriverAdapterError({
538
+ kind: "InvalidIsolationLevel",
539
+ level: isolationLevel
540
+ });
541
+ }
542
+ const options = {
543
+ usePhantomQuery: false
544
+ };
545
+ const tag = "[js::startTransaction]";
546
+ debug2("%s options: %O", tag, options);
547
+ try {
548
+ const release = await this[LOCK_TAG].acquire();
549
+ this.client.prepare("BEGIN").run();
550
+ return new BetterSQLite3Transaction(this.client, options, release);
551
+ } catch (e) {
552
+ this.onError(e);
553
+ }
554
+ }
555
+ dispose() {
556
+ this.client.close();
557
+ return Promise.resolve();
558
+ }
559
+ };
560
+ var PrismaBetterSQLite3AdapterFactory = class {
561
+ constructor(config) {
562
+ this.config = config;
563
+ }
564
+ provider = "sqlite";
565
+ adapterName = name;
566
+ connect() {
567
+ return Promise.resolve(new PrismaBetterSQLite3Adapter(createBetterSQLite3Client(this.config)));
568
+ }
569
+ connectToShadowDb() {
570
+ const url = this.config.shadowDatabaseURL ?? ":memory:";
571
+ return Promise.resolve(new PrismaBetterSQLite3Adapter(createBetterSQLite3Client({ ...this.config, url })));
572
+ }
573
+ };
574
+ function createBetterSQLite3Client(input) {
575
+ const { url, ...config } = input;
576
+ const db = new Database(url, config);
577
+ db.defaultSafeIntegers(true);
578
+ return db;
579
+ }
580
+ export {
581
+ PrismaBetterSQLite3AdapterFactory as PrismaBetterSQLite3
582
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@prisma/adapter-better-sqlite3",
3
+ "version": "6.7.0-dev.10",
4
+ "description": "Prisma's driver adapter for better-sqlite3, a fast SQLite3 driver for JavaScript runtimes",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "require": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "import": {
15
+ "types": "./dist/index.d.mts",
16
+ "default": "./dist/index.mjs"
17
+ }
18
+ }
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/prisma/prisma.git",
23
+ "directory": "packages/adapter-better-sqlite3"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md"
28
+ ],
29
+ "keywords": [],
30
+ "author": "Alberto Schiabel <schiabel@prisma.io>",
31
+ "license": "Apache-2.0",
32
+ "sideEffects": false,
33
+ "dependencies": {
34
+ "@prisma/driver-adapter-utils": "6.7.0-dev.10"
35
+ },
36
+ "devDependencies": {
37
+ "@types/better-sqlite3": "7.6.12",
38
+ "better-sqlite3": "11.9.1",
39
+ "async-mutex": "0.5.0"
40
+ },
41
+ "peerDependencies": {
42
+ "better-sqlite3": ">= ^11.9.0 < 12"
43
+ },
44
+ "scripts": {
45
+ "dev": "DEV=true tsx helpers/build.ts",
46
+ "build": "tsx helpers/build.ts"
47
+ }
48
+ }