@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 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.4.0] - to be released
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
  [![npm version](https://img.shields.io/npm/v/@photostructure/sqlite.svg)](https://www.npmjs.com/package/@photostructure/sqlite)
4
6
  [![CI](https://github.com/photostructure/node-sqlite/actions/workflows/build.yml/badge.svg)](https://github.com/photostructure/node-sqlite/actions/workflows/build.yml)
5
7
 
6
- Native SQLite for Node.js 20+ without the experimental flag. Drop-in replacement for `node:sqlite`. Synced with Node.js v25.6.1 for the latest features including native `Symbol.dispose` resource management.
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.6.1 built-in `node:sqlite` module\*
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
- const firstRow = rows[0];
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 rows;
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
- return db;
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");