@photostructure/sqlite 0.2.0 → 0.2.1

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,28 +2,30 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [0.2.0] (unreleased)
5
+ ## [0.2.1] (2025-12-01)
6
6
 
7
7
  ### Added
8
8
 
9
- - **Node.js v25 API sync**: SQLite 3.51.1, native `Symbol.dispose` in C++, Session class exposed in public API
9
+ - Windows ARM64 prebuilt binaries
10
10
 
11
- - **New database open options**: `readBigInts`, `returnArrays`, `allowBareNamedParameters`, `allowUnknownNamedParameters`, `defensive`, `open`
11
+ ### Fixed
12
12
 
13
- - **Defensive mode**: `enableDefensive()` method to prevent SQL from deliberately corrupting the database
13
+ - Error message handling on Windows ARM64 (ABI compatibility)
14
+ - Error handling consistency across platforms
14
15
 
15
- - **Statement enhancements**: `setAllowUnknownNamedParameters()` method, `finalized` property
16
+ ## [0.2.0] (2025-12-01)
16
17
 
17
- - **Type identification**: `sqlite-type` symbol property on DatabaseSync (Node.js PR #59405)
18
+ ### Added
18
19
 
20
+ - **Node.js v25 API sync**: SQLite 3.51.1, native `Symbol.dispose` in C++, Session class exposed in public API
21
+ - **New database open options**: `readBigInts`, `returnArrays`, `allowBareNamedParameters`, `allowUnknownNamedParameters`, `defensive`, `open`
22
+ - **Defensive mode**: `enableDefensive()` method to prevent SQL from deliberately corrupting the database
23
+ - **Statement enhancements**: `setAllowUnknownNamedParameters()` method, `finalized` property
24
+ - **Type identification**: `sqlite-type` symbol property on DatabaseSync (Node.js PR #59405)
19
25
  - **Enhanced SQLite errors**: New properties `sqliteCode`, `sqliteExtendedCode`, `code`, `sqliteErrorString`, `systemErrno`
20
-
21
26
  - **ARM64 prebuilds**: macOS Apple Silicon and Windows ARM64 binaries
22
-
23
27
  - **Tagged template literals**: `db.createTagStore()` for cached prepared statements (Node.js PR #58748)
24
-
25
28
  - **Authorization API**: `db.setAuthorizer()` for security callbacks (Node.js PR #59928)
26
-
27
29
  - **Standalone backup**: `backup(srcDb, destFile, options?)` for one-liner database backups with progress callbacks
28
30
 
29
31
  ### Fixed
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # @photostructure/sqlite
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@photostructure/sqlite.svg)](https://www.npmjs.com/package/@photostructure/sqlite)
4
+ [![CI](https://github.com/photostructure/node-sqlite/actions/workflows/build.yml/badge.svg)](https://github.com/photostructure/node-sqlite/actions/workflows/build.yml)
5
+
3
6
  Native SQLite for Node.js 20+ without the experimental flag. Drop-in replacement for `node:sqlite`. Updated to Node.js v25 for latest features and native Symbol.dispose resource management.
4
7
 
5
8
  ## Installation
package/SECURITY.md CHANGED
@@ -9,6 +9,7 @@ Security updates are provided for the latest released version only.
9
9
  **Do not report security vulnerabilities through public GitHub issues.**
10
10
 
11
11
  Report via:
12
+
12
13
  - Email: security@photostructure.com
13
14
  - GitHub's private vulnerability reporting
14
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photostructure/sqlite",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Drop-in replacement for node:sqlite",
5
5
  "homepage": "https://photostructure.github.io/node-sqlite/",
6
6
  "types": "./dist/index.d.ts",
Binary file
@@ -34,7 +34,9 @@ inline void THROW_ERR_INVALID_ARG_VALUE(Napi::Env env,
34
34
 
35
35
  inline void THROW_ERR_SQLITE_ERROR(Napi::Env env,
36
36
  const char *message = nullptr) {
37
- const char *msg = message ? message : "SQLite error";
37
+ // Check for both null and empty string - on Windows (MSVC),
38
+ // std::exception::what() can sometimes return an empty string
39
+ const char *msg = (message && message[0] != '\0') ? message : "SQLite error";
38
40
  Napi::Error::New(env, msg).ThrowAsJavaScriptException();
39
41
  }
40
42
 
@@ -84,7 +84,10 @@ inline const char *GetSqliteErrorCodeName(int code) {
84
84
  inline void ThrowEnhancedSqliteError(Napi::Env env, sqlite3 *db,
85
85
  int sqlite_code,
86
86
  const std::string &message) {
87
- Napi::Error error = Napi::Error::New(env, message);
87
+ // Use c_str() explicitly to avoid potential ABI issues on Windows ARM
88
+ // where passing std::string directly to Napi::Error::New can result in
89
+ // truncated or corrupted error messages
90
+ Napi::Error error = Napi::Error::New(env, message.c_str());
88
91
 
89
92
  // Add SQLite error code information
90
93
  error.Set("sqliteCode", Napi::Number::New(env, sqlite_code));
@@ -127,7 +130,8 @@ inline void ThrowSqliteError(Napi::Env env, sqlite3 *db,
127
130
  ThrowEnhancedSqliteError(env, db, errcode, message);
128
131
  } else {
129
132
  // Fallback to simple error when no db handle available
130
- Napi::Error::New(env, message).ThrowAsJavaScriptException();
133
+ // Use c_str() explicitly to avoid potential ABI issues on Windows ARM
134
+ Napi::Error::New(env, message.c_str()).ThrowAsJavaScriptException();
131
135
  }
132
136
  }
133
137
 
@@ -150,8 +154,10 @@ ThrowFromSqliteException(Napi::Env env,
150
154
  error.Set("code", Napi::String::New(env, code_name));
151
155
 
152
156
  // Also set the human-readable error string
157
+ // Use c_str() explicitly to avoid potential ABI issues on Windows ARM
153
158
  if (!ex.error_string().empty()) {
154
- error.Set("sqliteErrorString", Napi::String::New(env, ex.error_string()));
159
+ error.Set("sqliteErrorString",
160
+ Napi::String::New(env, ex.error_string().c_str()));
155
161
  }
156
162
 
157
163
  error.ThrowAsJavaScriptException();
@@ -22,7 +22,8 @@ inline void ThrowErrSqliteErrorWithDb(Napi::Env env,
22
22
  if (db->HasDeferredAuthorizerException()) {
23
23
  std::string deferred_msg = db->GetDeferredAuthorizerException();
24
24
  db->ClearDeferredAuthorizerException();
25
- Napi::Error::New(env, deferred_msg).ThrowAsJavaScriptException();
25
+ // Use c_str() explicitly to avoid potential ABI issues on Windows ARM
26
+ Napi::Error::New(env, deferred_msg.c_str()).ThrowAsJavaScriptException();
26
27
  }
27
28
  return; // Don't throw SQLite error, JavaScript exception takes precedence
28
29
  }
@@ -42,7 +43,8 @@ inline void ThrowEnhancedSqliteErrorWithDB(
42
43
  if (db_sync->HasDeferredAuthorizerException()) {
43
44
  std::string deferred_msg = db_sync->GetDeferredAuthorizerException();
44
45
  db_sync->ClearDeferredAuthorizerException();
45
- Napi::Error::New(env, deferred_msg).ThrowAsJavaScriptException();
46
+ // Use c_str() explicitly to avoid potential ABI issues on Windows ARM
47
+ Napi::Error::New(env, deferred_msg.c_str()).ThrowAsJavaScriptException();
46
48
  }
47
49
  return; // Don't throw SQLite error, JavaScript exception takes precedence
48
50
  }
@@ -576,6 +578,19 @@ Napi::Value DatabaseSync::Prepare(const Napi::CallbackInfo &info) {
576
578
  stmt->InitStatement(this, sql);
577
579
 
578
580
  return stmt_obj;
581
+ } catch (const SqliteException &e) {
582
+ // SqliteException stores message in std::string, avoiding Windows ARM ABI
583
+ // issues where std::exception::what() can return corrupted strings
584
+ if (HasDeferredAuthorizerException()) {
585
+ std::string deferred_msg = GetDeferredAuthorizerException();
586
+ ClearDeferredAuthorizerException();
587
+ SetIgnoreNextSQLiteError(false);
588
+ // Use c_str() explicitly to avoid potential ABI issues on Windows ARM
589
+ Napi::Error::New(env, deferred_msg.c_str()).ThrowAsJavaScriptException();
590
+ return env.Undefined();
591
+ }
592
+ node::ThrowFromSqliteException(env, e);
593
+ return env.Undefined();
579
594
  } catch (const std::exception &e) {
580
595
  // Handle deferred authorizer exceptions:
581
596
  //
@@ -586,14 +601,17 @@ Napi::Value DatabaseSync::Prepare(const Napi::CallbackInfo &info) {
586
601
  // empty string, causing message loss.
587
602
  //
588
603
  // 2. By storing the message in the DatabaseSync instance, we can retrieve
589
- // it here and throw a proper JavaScript exception with the original text.
604
+ // it here and throw a proper JavaScript exception with the original
605
+ // text.
590
606
  //
591
- // See also: StatementSync::InitStatement for the other half of this pattern.
607
+ // See also: StatementSync::InitStatement for the other half of this
608
+ // pattern.
592
609
  if (HasDeferredAuthorizerException()) {
593
610
  std::string deferred_msg = GetDeferredAuthorizerException();
594
611
  ClearDeferredAuthorizerException();
595
612
  SetIgnoreNextSQLiteError(false);
596
- Napi::Error::New(env, deferred_msg).ThrowAsJavaScriptException();
613
+ // Use c_str() explicitly to avoid potential ABI issues on Windows ARM
614
+ Napi::Error::New(env, deferred_msg.c_str()).ThrowAsJavaScriptException();
597
615
  return env.Undefined();
598
616
  }
599
617
  node::THROW_ERR_SQLITE_ERROR(env, e.what());
@@ -636,7 +654,8 @@ Napi::Value DatabaseSync::Exec(const Napi::CallbackInfo &info) {
636
654
  std::string deferred_msg = GetDeferredAuthorizerException();
637
655
  ClearDeferredAuthorizerException();
638
656
  SetIgnoreNextSQLiteError(false);
639
- Napi::Error::New(env, deferred_msg).ThrowAsJavaScriptException();
657
+ // Use c_str() explicitly to avoid potential ABI issues on Windows ARM
658
+ Napi::Error::New(env, deferred_msg.c_str()).ThrowAsJavaScriptException();
640
659
  return env.Undefined();
641
660
  }
642
661
  std::string error = error_msg ? error_msg : "Unknown SQLite error";
@@ -1495,7 +1514,8 @@ void StatementSync::InitStatement(DatabaseSync *database,
1495
1514
  // empty string, causing message loss.
1496
1515
  //
1497
1516
  // 2. By storing the message in the DatabaseSync instance, the caller can
1498
- // retrieve it and throw a proper JavaScript exception with the original text.
1517
+ // retrieve it and throw a proper JavaScript exception with the original
1518
+ // text.
1499
1519
  //
1500
1520
  // 3. This matches Node.js's behavior where JavaScript exceptions from
1501
1521
  // authorizer callbacks propagate correctly to the caller.
@@ -1504,8 +1524,11 @@ void StatementSync::InitStatement(DatabaseSync *database,
1504
1524
  // object and will be retrieved by the caller.
1505
1525
  throw std::runtime_error("");
1506
1526
  }
1507
- std::string error = sqlite3_errmsg(database->connection());
1508
- throw std::runtime_error("Failed to prepare statement: " + error);
1527
+ std::string error = "Failed to prepare statement: ";
1528
+ error += sqlite3_errmsg(database->connection());
1529
+ // Use SqliteException to capture error info - avoids Windows ARM ABI issues
1530
+ // with std::runtime_error::what() returning corrupted strings
1531
+ throw SqliteException(database->connection(), result, error);
1509
1532
  }
1510
1533
  }
1511
1534