@photostructure/sqlite 0.4.0 → 1.0.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/CHANGELOG.md +30 -1
- package/README.md +4 -8
- package/binding.gyp +2 -0
- package/dist/index.cjs +247 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +145 -24
- package/dist/index.d.mts +145 -24
- package/dist/index.d.ts +145 -24
- package/dist/index.mjs +247 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -11
- package/prebuilds/darwin-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/darwin-x64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+sqlite.musl.node +0 -0
- package/prebuilds/linux-x64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-x64/@photostructure+sqlite.musl.node +0 -0
- package/prebuilds/test_extension.so +0 -0
- package/prebuilds/win32-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/win32-x64/@photostructure+sqlite.glibc.node +0 -0
- package/src/enhance.ts +382 -55
- package/src/index.ts +85 -2
- package/src/sqlite_impl.cpp +133 -10
- package/src/sqlite_impl.h +19 -0
- package/src/types/database-sync-instance.ts +43 -0
- package/src/types/database-sync-options.ts +19 -0
- package/src/upstream/node_sqlite.cc +312 -17
- package/src/upstream/node_sqlite.h +80 -0
- package/src/upstream/sqlite.js +0 -3
- package/src/upstream/sqlite3.c +5027 -3518
- package/src/upstream/sqlite3.h +195 -58
- package/src/upstream/sqlite3ext.h +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [0.
|
|
5
|
+
## [1.0.0] (2026-03-07)
|
|
6
|
+
|
|
7
|
+
Promotion to v1.0.0 following API stabilization and 0.5.0 release.
|
|
8
|
+
|
|
9
|
+
API compatible with `node:sqlite` from Node.js v25.8.1.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **`db.limits` property**: Get and set SQLite limits (length, sqlLength, column, exprDepth, compoundSelect, vdbeOp, functionArg, attach, likePatternLength, variableNumber, triggerDepth) at runtime. Supports `Infinity` to reset to compile-time maximum. Also accepts `limits` option in `DatabaseSync` constructor.
|
|
14
|
+
- **Statement iterator invalidation**: Calling `stmt.run()`, `stmt.get()`, `stmt.all()`, or `stmt.iterate()` now invalidates any active iterator on the same statement, throwing `ERR_INVALID_STATE`
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **SQLite 3.52.0**: Updated from 3.51.2
|
|
19
|
+
|
|
20
|
+
## [0.5.0] (2026-02-06)
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **Statement modes via `enhance()`**: `stmt.pluck()`, `stmt.raw()`, `stmt.expand()` for better-sqlite3 compatibility
|
|
25
|
+
- `.pluck()` returns only the first column value from queries
|
|
26
|
+
- `.raw()` returns rows as arrays instead of objects
|
|
27
|
+
- `.expand()` returns rows namespaced by table, correctly handling duplicate column names across JOINs
|
|
28
|
+
- All three modes are mutually exclusive, matching better-sqlite3's toggle semantics
|
|
29
|
+
- **`stmt.database`**: Back-reference from prepared statements to their parent database instance
|
|
30
|
+
- **`EnhancedStatementMethods` type**: TypeScript interface for `pluck()`, `raw()`, `expand()`, and `database`
|
|
31
|
+
|
|
32
|
+
## [0.4.0] (2026-02-04)
|
|
6
33
|
|
|
7
34
|
API compatible with `node:sqlite` from Node.js v25.6.1.
|
|
8
35
|
|
|
@@ -118,6 +145,8 @@ API compatible with `node:sqlite` from Node.js v25.6.1.
|
|
|
118
145
|
- macOS (x64, ARM64)
|
|
119
146
|
- Linux (x64, ARM64), (glibc 2.28+, musl)
|
|
120
147
|
|
|
148
|
+
[1.0.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v1.0.0
|
|
149
|
+
[0.5.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.5.0
|
|
121
150
|
[0.4.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.4.0
|
|
122
151
|
[0.3.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.3.0
|
|
123
152
|
[0.2.1]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.2.1
|
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
<img src="https://raw.githubusercontent.com/photostructure/node-sqlite/main/doc/logo.svg" alt="PhotoStructure SQLite logo" width="200">
|
|
2
|
+
|
|
1
3
|
# @photostructure/sqlite
|
|
2
4
|
|
|
3
5
|
[](https://www.npmjs.com/package/@photostructure/sqlite)
|
|
4
6
|
[](https://github.com/photostructure/node-sqlite/actions/workflows/build.yml)
|
|
5
7
|
|
|
6
|
-
Native SQLite for Node.js 20
|
|
8
|
+
Native SQLite for Node.js 20+. Drop-in replacement for `node:sqlite`. Synced with Node.js v25.8.1 for the latest features including native `Symbol.dispose` resource management.
|
|
7
9
|
|
|
8
10
|
## Installation
|
|
9
11
|
|
|
@@ -27,7 +29,7 @@ db.close();
|
|
|
27
29
|
|
|
28
30
|
## Features
|
|
29
31
|
|
|
30
|
-
- 100% compatible with Node.js v25.
|
|
32
|
+
- 100% compatible with Node.js v25.8.1 built-in `node:sqlite` module\*
|
|
31
33
|
- Zero dependencies - native SQLite implementation
|
|
32
34
|
- Synchronous API - no async overhead
|
|
33
35
|
- Performance comparable to better-sqlite3
|
|
@@ -70,12 +72,6 @@ db.close();
|
|
|
70
72
|
- [Build Flags & Configuration](./doc/build-flags.md)
|
|
71
73
|
- [Library Comparison](./doc/library-comparison.md)
|
|
72
74
|
|
|
73
|
-
## Support
|
|
74
|
-
|
|
75
|
-
- 🐛 [Issues](https://github.com/photostructure/node-sqlite/issues)
|
|
76
|
-
- 💬 [Discussions](https://github.com/photostructure/node-sqlite/discussions)
|
|
77
|
-
- 📧 [Security](./SECURITY.md)
|
|
78
|
-
|
|
79
75
|
## License
|
|
80
76
|
|
|
81
77
|
MIT - see [LICENSE](./LICENSE) for details.
|
package/binding.gyp
CHANGED
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
],
|
|
88
88
|
"msvs_settings": {
|
|
89
89
|
"VCCLCompilerTool": {
|
|
90
|
+
"WarningLevel": 4,
|
|
90
91
|
"AdditionalOptions": [
|
|
91
92
|
"/Qspectre",
|
|
92
93
|
"/guard:cf",
|
|
@@ -115,6 +116,7 @@
|
|
|
115
116
|
],
|
|
116
117
|
"msvs_settings": {
|
|
117
118
|
"VCCLCompilerTool": {
|
|
119
|
+
"WarningLevel": 4,
|
|
118
120
|
"AdditionalOptions": [
|
|
119
121
|
"/guard:cf",
|
|
120
122
|
"/ZH:SHA_256",
|
package/dist/index.cjs
CHANGED
|
@@ -352,6 +352,148 @@ function getBeginStatement(mode) {
|
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
// src/enhance.ts
|
|
355
|
+
var ENHANCED_PREPARE = /* @__PURE__ */ Symbol.for("@photostructure/sqlite:enhancedPrepare");
|
|
356
|
+
function extractFirstColumn(row) {
|
|
357
|
+
if (row == null) return row;
|
|
358
|
+
if (Array.isArray(row)) return row[0];
|
|
359
|
+
const keys = Object.keys(row);
|
|
360
|
+
return keys.length > 0 ? row[keys[0]] : void 0;
|
|
361
|
+
}
|
|
362
|
+
function buildColumnTableMap(stmt) {
|
|
363
|
+
if (typeof stmt.columns !== "function") return void 0;
|
|
364
|
+
const cols = stmt.columns();
|
|
365
|
+
return cols.map((c) => ({
|
|
366
|
+
table: c.table ?? "$",
|
|
367
|
+
column: c.name
|
|
368
|
+
}));
|
|
369
|
+
}
|
|
370
|
+
function expandRowFromArray(row, columnMap) {
|
|
371
|
+
const result = {};
|
|
372
|
+
for (let i = 0; i < columnMap.length && i < row.length; i++) {
|
|
373
|
+
const { table, column } = columnMap[i];
|
|
374
|
+
result[table] ??= {};
|
|
375
|
+
result[table][column] = row[i];
|
|
376
|
+
}
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
function expandRowFromObject(row, columnMap) {
|
|
380
|
+
const result = {};
|
|
381
|
+
const keys = Object.keys(row);
|
|
382
|
+
for (let i = 0; i < keys.length && i < columnMap.length; i++) {
|
|
383
|
+
const { table, column } = columnMap[i];
|
|
384
|
+
result[table] ??= {};
|
|
385
|
+
result[table][column] = row[keys[i]];
|
|
386
|
+
}
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
function validateToggle(value) {
|
|
390
|
+
const use = value === void 0 ? true : value;
|
|
391
|
+
if (typeof use !== "boolean") {
|
|
392
|
+
throw new TypeError("Expected first argument to be a boolean");
|
|
393
|
+
}
|
|
394
|
+
return use;
|
|
395
|
+
}
|
|
396
|
+
function transformRow(row, mode, columnMap) {
|
|
397
|
+
if (row == null || mode === "flat") return row;
|
|
398
|
+
if (mode === "pluck") return extractFirstColumn(row);
|
|
399
|
+
if (mode === "expand") {
|
|
400
|
+
if (Array.isArray(row)) {
|
|
401
|
+
return expandRowFromArray(row, columnMap);
|
|
402
|
+
}
|
|
403
|
+
return expandRowFromObject(row, columnMap);
|
|
404
|
+
}
|
|
405
|
+
return row;
|
|
406
|
+
}
|
|
407
|
+
function enhanceStatement(stmt) {
|
|
408
|
+
if (typeof stmt.pluck === "function") {
|
|
409
|
+
return stmt;
|
|
410
|
+
}
|
|
411
|
+
let mode = "flat";
|
|
412
|
+
let columnMap;
|
|
413
|
+
const originalGet = typeof stmt.get === "function" ? stmt.get.bind(stmt) : void 0;
|
|
414
|
+
const originalAll = stmt.all.bind(stmt);
|
|
415
|
+
const originalIterate = typeof stmt.iterate === "function" ? stmt.iterate.bind(stmt) : void 0;
|
|
416
|
+
function setMode(target, use) {
|
|
417
|
+
if (use) {
|
|
418
|
+
mode = target;
|
|
419
|
+
if (target === "expand" && columnMap == null) {
|
|
420
|
+
columnMap = buildColumnTableMap(stmt);
|
|
421
|
+
if (columnMap == null) {
|
|
422
|
+
throw new TypeError(
|
|
423
|
+
"expand() requires the statement to have a columns() method"
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} else if (mode === target) {
|
|
428
|
+
mode = "flat";
|
|
429
|
+
}
|
|
430
|
+
if (typeof stmt.setReturnArrays === "function") {
|
|
431
|
+
stmt.setReturnArrays(mode === "raw" || mode === "expand");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
Object.defineProperty(stmt, "pluck", {
|
|
435
|
+
value: function pluck(toggle) {
|
|
436
|
+
setMode("pluck", validateToggle(toggle));
|
|
437
|
+
return stmt;
|
|
438
|
+
},
|
|
439
|
+
writable: true,
|
|
440
|
+
configurable: true,
|
|
441
|
+
enumerable: false
|
|
442
|
+
});
|
|
443
|
+
Object.defineProperty(stmt, "raw", {
|
|
444
|
+
value: function raw(toggle) {
|
|
445
|
+
setMode("raw", validateToggle(toggle));
|
|
446
|
+
return stmt;
|
|
447
|
+
},
|
|
448
|
+
writable: true,
|
|
449
|
+
configurable: true,
|
|
450
|
+
enumerable: false
|
|
451
|
+
});
|
|
452
|
+
Object.defineProperty(stmt, "expand", {
|
|
453
|
+
value: function expand(toggle) {
|
|
454
|
+
setMode("expand", validateToggle(toggle));
|
|
455
|
+
return stmt;
|
|
456
|
+
},
|
|
457
|
+
writable: true,
|
|
458
|
+
configurable: true,
|
|
459
|
+
enumerable: false
|
|
460
|
+
});
|
|
461
|
+
if (originalGet != null) {
|
|
462
|
+
Object.defineProperty(stmt, "get", {
|
|
463
|
+
value: (...params) => {
|
|
464
|
+
const row = originalGet(...params);
|
|
465
|
+
return transformRow(row, mode, columnMap);
|
|
466
|
+
},
|
|
467
|
+
writable: true,
|
|
468
|
+
configurable: true,
|
|
469
|
+
enumerable: false
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
Object.defineProperty(stmt, "all", {
|
|
473
|
+
value: (...params) => {
|
|
474
|
+
const rows = originalAll(...params);
|
|
475
|
+
if (mode === "flat" || mode === "raw") return rows;
|
|
476
|
+
return rows.map((row) => transformRow(row, mode, columnMap));
|
|
477
|
+
},
|
|
478
|
+
writable: true,
|
|
479
|
+
configurable: true,
|
|
480
|
+
enumerable: false
|
|
481
|
+
});
|
|
482
|
+
if (originalIterate != null) {
|
|
483
|
+
Object.defineProperty(stmt, "iterate", {
|
|
484
|
+
value: function* (...params) {
|
|
485
|
+
const iter = originalIterate(...params);
|
|
486
|
+
for (const row of iter) {
|
|
487
|
+
yield transformRow(row, mode, columnMap);
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
writable: true,
|
|
491
|
+
configurable: true,
|
|
492
|
+
enumerable: false
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
return stmt;
|
|
496
|
+
}
|
|
355
497
|
function pragmaImpl(source, options) {
|
|
356
498
|
if (typeof source !== "string") {
|
|
357
499
|
throw new TypeError("Expected first argument to be a string");
|
|
@@ -364,20 +506,10 @@ function pragmaImpl(source, options) {
|
|
|
364
506
|
throw new TypeError('Expected the "simple" option to be a boolean');
|
|
365
507
|
}
|
|
366
508
|
const stmt = this.prepare(`PRAGMA ${source}`);
|
|
367
|
-
const rows = stmt.all();
|
|
368
509
|
if (simple) {
|
|
369
|
-
|
|
370
|
-
if (firstRow == null) {
|
|
371
|
-
return void 0;
|
|
372
|
-
}
|
|
373
|
-
const keys = Object.keys(firstRow);
|
|
374
|
-
const firstKey = keys[0];
|
|
375
|
-
if (firstKey == null) {
|
|
376
|
-
return void 0;
|
|
377
|
-
}
|
|
378
|
-
return firstRow[firstKey];
|
|
510
|
+
return extractFirstColumn(stmt.all()[0]);
|
|
379
511
|
}
|
|
380
|
-
return
|
|
512
|
+
return stmt.all();
|
|
381
513
|
}
|
|
382
514
|
function transactionImpl(fn) {
|
|
383
515
|
return createTransaction(this, fn);
|
|
@@ -386,21 +518,45 @@ function hasEnhancedMethods(db) {
|
|
|
386
518
|
return typeof db.pragma === "function" && typeof db.transaction === "function";
|
|
387
519
|
}
|
|
388
520
|
function enhance(db) {
|
|
389
|
-
if (hasEnhancedMethods(db)) {
|
|
390
|
-
|
|
521
|
+
if (!hasEnhancedMethods(db)) {
|
|
522
|
+
Object.defineProperty(db, "pragma", {
|
|
523
|
+
value: pragmaImpl,
|
|
524
|
+
writable: true,
|
|
525
|
+
configurable: true,
|
|
526
|
+
enumerable: false
|
|
527
|
+
});
|
|
528
|
+
Object.defineProperty(db, "transaction", {
|
|
529
|
+
value: transactionImpl,
|
|
530
|
+
writable: true,
|
|
531
|
+
configurable: true,
|
|
532
|
+
enumerable: false
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
if (!db[ENHANCED_PREPARE]) {
|
|
536
|
+
const originalPrepare = db.prepare.bind(db);
|
|
537
|
+
Object.defineProperty(db, "prepare", {
|
|
538
|
+
value: (...args) => {
|
|
539
|
+
const stmt = originalPrepare(...args);
|
|
540
|
+
enhanceStatement(stmt);
|
|
541
|
+
Object.defineProperty(stmt, "database", {
|
|
542
|
+
value: db,
|
|
543
|
+
writable: false,
|
|
544
|
+
configurable: true,
|
|
545
|
+
enumerable: false
|
|
546
|
+
});
|
|
547
|
+
return stmt;
|
|
548
|
+
},
|
|
549
|
+
writable: true,
|
|
550
|
+
configurable: true,
|
|
551
|
+
enumerable: false
|
|
552
|
+
});
|
|
553
|
+
Object.defineProperty(db, ENHANCED_PREPARE, {
|
|
554
|
+
value: true,
|
|
555
|
+
writable: false,
|
|
556
|
+
configurable: false,
|
|
557
|
+
enumerable: false
|
|
558
|
+
});
|
|
391
559
|
}
|
|
392
|
-
Object.defineProperty(db, "pragma", {
|
|
393
|
-
value: pragmaImpl,
|
|
394
|
-
writable: true,
|
|
395
|
-
configurable: true,
|
|
396
|
-
enumerable: false
|
|
397
|
-
});
|
|
398
|
-
Object.defineProperty(db, "transaction", {
|
|
399
|
-
value: transactionImpl,
|
|
400
|
-
writable: true,
|
|
401
|
-
configurable: true,
|
|
402
|
-
enumerable: false
|
|
403
|
-
});
|
|
404
560
|
return db;
|
|
405
561
|
}
|
|
406
562
|
function isEnhanced(db) {
|
|
@@ -423,6 +579,71 @@ DatabaseSync.prototype = _DatabaseSync.prototype;
|
|
|
423
579
|
DatabaseSync.prototype.createTagStore = function(capacity) {
|
|
424
580
|
return new SQLTagStore(this, capacity);
|
|
425
581
|
};
|
|
582
|
+
var LIMIT_MAPPING = [
|
|
583
|
+
{ name: "length", id: 0 },
|
|
584
|
+
{ name: "sqlLength", id: 1 },
|
|
585
|
+
{ name: "column", id: 2 },
|
|
586
|
+
{ name: "exprDepth", id: 3 },
|
|
587
|
+
{ name: "compoundSelect", id: 4 },
|
|
588
|
+
{ name: "vdbeOp", id: 5 },
|
|
589
|
+
{ name: "functionArg", id: 6 },
|
|
590
|
+
{ name: "attach", id: 7 },
|
|
591
|
+
{ name: "likePatternLength", id: 8 },
|
|
592
|
+
{ name: "variableNumber", id: 9 },
|
|
593
|
+
{ name: "triggerDepth", id: 10 }
|
|
594
|
+
];
|
|
595
|
+
var INT_MAX = 2147483647;
|
|
596
|
+
var limitsCache = /* @__PURE__ */ new WeakMap();
|
|
597
|
+
function validateLimitValue(value) {
|
|
598
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
599
|
+
throw new TypeError(
|
|
600
|
+
"Limit value must be a non-negative integer or Infinity."
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
if (value === Infinity) {
|
|
604
|
+
return INT_MAX;
|
|
605
|
+
}
|
|
606
|
+
if (!Number.isFinite(value) || value !== Math.trunc(value)) {
|
|
607
|
+
throw new TypeError(
|
|
608
|
+
"Limit value must be a non-negative integer or Infinity."
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
if (value < 0) {
|
|
612
|
+
throw new RangeError("Limit value must be non-negative.");
|
|
613
|
+
}
|
|
614
|
+
return value;
|
|
615
|
+
}
|
|
616
|
+
function createLimitsObject(db) {
|
|
617
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
618
|
+
for (const { name, id } of LIMIT_MAPPING) {
|
|
619
|
+
Object.defineProperty(obj, name, {
|
|
620
|
+
get() {
|
|
621
|
+
return db.getLimit(id);
|
|
622
|
+
},
|
|
623
|
+
set(value) {
|
|
624
|
+
const validated = validateLimitValue(value);
|
|
625
|
+
db.setLimit(id, validated);
|
|
626
|
+
},
|
|
627
|
+
enumerable: true,
|
|
628
|
+
configurable: false
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return obj;
|
|
632
|
+
}
|
|
633
|
+
if (!Object.getOwnPropertyDescriptor(DatabaseSync.prototype, "limits")) {
|
|
634
|
+
Object.defineProperty(DatabaseSync.prototype, "limits", {
|
|
635
|
+
get() {
|
|
636
|
+
let obj = limitsCache.get(this);
|
|
637
|
+
if (obj == null) {
|
|
638
|
+
obj = createLimitsObject(this);
|
|
639
|
+
limitsCache.set(this, obj);
|
|
640
|
+
}
|
|
641
|
+
return obj;
|
|
642
|
+
},
|
|
643
|
+
enumerable: true,
|
|
644
|
+
configurable: true
|
|
645
|
+
});
|
|
646
|
+
}
|
|
426
647
|
var _StatementSync = binding.StatementSync;
|
|
427
648
|
var StatementSync = function StatementSync2() {
|
|
428
649
|
const err = new TypeError("Illegal constructor");
|