@crvouga/sqlite-mem 0.2.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/dist/index.js CHANGED
@@ -2,18 +2,21 @@
2
2
  var SqliteError = class extends Error {
3
3
  /** Coarse error class (constraint vs syntax vs missing object, …). */
4
4
  category;
5
- /** Optional SQLite result-code name such as `SQLITE_CONSTRAINT`. */
5
+ /** SQLite result-code name such as `SQLITE_CONSTRAINT_UNIQUE`. */
6
6
  sqliteCode;
7
+ /** Same value as {@link sqliteCode} (Node `err.code` convention). */
8
+ code;
7
9
  /**
8
10
  * @param message - Human-readable error text.
9
11
  * @param category - Coarse class; defaults to `"other"`.
10
- * @param sqliteCode - Optional SQLite result-code name.
12
+ * @param sqliteCode - SQLite result-code name; defaults to `"SQLITE_ERROR"`.
11
13
  */
12
- constructor(message, category = "other", sqliteCode) {
14
+ constructor(message, category = "other", sqliteCode = "SQLITE_ERROR") {
13
15
  super(message);
14
16
  this.name = "SqliteError";
15
17
  this.category = category;
16
18
  this.sqliteCode = sqliteCode;
19
+ this.code = sqliteCode;
17
20
  }
18
21
  };
19
22
  var TriggerRaiseError = class extends SqliteError {
@@ -2396,17 +2399,6 @@ var Prng = class _Prng {
2396
2399
  return copy;
2397
2400
  }
2398
2401
  };
2399
- function deriveSeed(...parts) {
2400
- let hash = 2166136261;
2401
- for (const part of parts) {
2402
- const text2 = String(part);
2403
- for (let i = 0; i < text2.length; i++) {
2404
- hash ^= text2.charCodeAt(i);
2405
- hash = Math.imul(hash, 16777619);
2406
- }
2407
- }
2408
- return hash | 0;
2409
- }
2410
2402
 
2411
2403
  // src/types/value.ts
2412
2404
  var SqlReal = class {
@@ -8733,7 +8725,7 @@ function decodeDatabaseState(snapshot) {
8733
8725
  throw new SqliteError("invalid sqlite-mem snapshot magic", "other");
8734
8726
  const version = reader.u32();
8735
8727
  if (version !== VERSION && version !== VERSION_V1) {
8736
- throw new SqliteError(`unsupported sqlite-mem snapshot version: ${version}`, "unsupported");
8728
+ throw new SqliteError(`unsupported sqlite-mem snapshot version: ${version}`, "snapshot_version", "SQLITE_FORMAT");
8737
8729
  }
8738
8730
  const state = new DatabaseState();
8739
8731
  state.foreignKeysEnabled = reader.u8() !== 0;
@@ -8989,16 +8981,14 @@ var ExecutionEnv = class {
8989
8981
  }
8990
8982
  getBoundParameter(name) {
8991
8983
  if (typeof name === "number") {
8992
- if (name < 1 || name > this.positional.length)
8993
- throw new SqliteError(`binding parameter ${name} is not supplied`, "misuse");
8984
+ if (name < 1 || name > this.positional.length) return null;
8994
8985
  return this.positional[name - 1];
8995
8986
  }
8996
8987
  const key = name.toLowerCase();
8997
- const value = this.named.get(key);
8998
- if (value === void 0 && !this.named.has(key)) {
8999
- throw new SqliteError(`binding parameter :${name} is not supplied`, "misuse");
8988
+ if (!this.named.has(key)) {
8989
+ return null;
9000
8990
  }
9001
- return value ?? null;
8991
+ return this.named.get(key) ?? null;
9002
8992
  }
9003
8993
  setNamed(name, value) {
9004
8994
  this.named.set(name.toLowerCase(), toSqlValue(value));
@@ -9086,7 +9076,7 @@ var ExecutionEnv = class {
9086
9076
  const result = this.selectRunner(select, this, context);
9087
9077
  return {
9088
9078
  columns: result.columns,
9089
- rows: result.values?.map((row2) => [...row2]) ?? result.rows.map((record) => result.columns.map((column) => record[column] ?? null))
9079
+ rows: result.values.length > 0 ? result.values.map((row2) => [...row2]) : result.rows.map((record) => result.columns.map((column) => record[column] ?? null))
9090
9080
  };
9091
9081
  },
9092
9082
  matchFts: (table, column, query) => {
@@ -9114,14 +9104,29 @@ function toSqlValue(value) {
9114
9104
  }
9115
9105
  if (typeof value === "boolean") return value ? 1 : 0;
9116
9106
  if (value instanceof SqlReal || value instanceof SqlJsonText) return value;
9117
- if (value instanceof Uint8Array) return new Uint8Array(value);
9107
+ if (value instanceof Uint8Array) {
9108
+ if (isSharedArrayBufferView(value)) {
9109
+ throw new SqliteError("cannot bind SharedArrayBuffer-backed buffers; copy into a Uint8Array first", "misuse");
9110
+ }
9111
+ return new Uint8Array(value);
9112
+ }
9118
9113
  if (value instanceof ArrayBuffer) return new Uint8Array(value);
9114
+ if (typeof SharedArrayBuffer !== "undefined" && value instanceof SharedArrayBuffer) {
9115
+ throw new SqliteError("cannot bind SharedArrayBuffer; copy into an ArrayBuffer or Uint8Array first", "misuse");
9116
+ }
9117
+ if (ArrayBuffer.isView(value)) {
9118
+ const kind = Object.prototype.toString.call(value).slice(8, -1);
9119
+ throw new SqliteError(`cannot bind ${kind}; only Uint8Array and ArrayBuffer are accepted as BLOB values`, "misuse");
9120
+ }
9119
9121
  throw new SqliteError(`unsupported bind value: ${typeof value}`, "datatype_mismatch");
9120
9122
  }
9123
+ function isSharedArrayBufferView(view) {
9124
+ return typeof SharedArrayBuffer !== "undefined" && view.buffer instanceof SharedArrayBuffer;
9125
+ }
9121
9126
 
9122
9127
  // src/executor/result.ts
9123
9128
  function emptyResult(changes = 0, lastInsertRowid = 0) {
9124
- return { columns: [], rows: [], changes, lastInsertRowid };
9129
+ return { columns: [], rows: [], values: [], changes, lastInsertRowid };
9125
9130
  }
9126
9131
  function exportSqlValue(value) {
9127
9132
  if (isSqlReal(value)) return value.value;
@@ -9133,7 +9138,7 @@ function valuesToResult(columns, values, changes = 0, lastInsertRowid = 0, optio
9133
9138
  const keepValues = options?.keepValues !== false;
9134
9139
  return {
9135
9140
  columns,
9136
- values: keepValues ? values.map((row) => row.map(exportSqlValue)) : void 0,
9141
+ values: keepValues ? values.map((row) => row.map(exportSqlValue)) : [],
9137
9142
  rows: named ? values.map((row) => {
9138
9143
  const object = {};
9139
9144
  for (let index = 0; index < columns.length; index++) {
@@ -9146,7 +9151,10 @@ function valuesToResult(columns, values, changes = 0, lastInsertRowid = 0, optio
9146
9151
  };
9147
9152
  }
9148
9153
  function resultValues(result) {
9149
- return result.values?.map((row) => [...row]) ?? result.rows.map((row) => result.columns.map((column) => row[column] ?? null));
9154
+ if (result.values.length > 0 || result.rows.length === 0) {
9155
+ return result.values.map((row) => [...row]);
9156
+ }
9157
+ return result.rows.map((row) => result.columns.map((column) => row[column] ?? null));
9150
9158
  }
9151
9159
 
9152
9160
  // src/executor/attach.ts
@@ -12637,50 +12645,37 @@ var Statement = class _Statement {
12637
12645
  }
12638
12646
  database;
12639
12647
  sql;
12640
- bound = [];
12641
12648
  namedPlan = null;
12642
12649
  env = null;
12643
12650
  statements;
12644
12651
  schemaVersion;
12645
12652
  /**
12646
- * Construct a {@link Statement} for {@link Database.prepare}.
12653
+ * Construct a {@link Statement} for {@link Database.prepare} / {@link Database.exec}.
12647
12654
  * @internal
12648
12655
  */
12649
12656
  static create(database, sql, statements) {
12650
12657
  return new _Statement(database, sql, statements);
12651
12658
  }
12652
- /**
12653
- * Store parameters for later {@link run} / {@link all} / {@link get} / {@link result}.
12654
- *
12655
- * Supports positional `?` / `?NNN` and named `:name` / `@name` / `$name` placeholders.
12656
- *
12657
- * @param params - Values to bind, in placeholder order.
12658
- * @returns `this` for chaining.
12659
- */
12660
- bind(...params) {
12661
- this.bound = [...params];
12662
- return this;
12663
- }
12664
12659
  /**
12665
12660
  * Execute for side effects (INSERT / UPDATE / DELETE / DDL).
12666
12661
  *
12667
- * @param params - If provided, override the last {@link bind}; otherwise bound values are used.
12662
+ * @param params - Bind values for this call only.
12668
12663
  * @returns Mutation counters for this execution.
12669
12664
  * @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
12670
12665
  */
12671
12666
  run(...params) {
12672
- const result = this.execute(params.length > 0 ? params : this.bound, { named: false });
12667
+ const result = this.execute(params, { named: false });
12673
12668
  return { changes: result.changes, lastInsertRowid: result.lastInsertRowid };
12674
12669
  }
12675
12670
  /**
12676
12671
  * Execute and return every result row as an object keyed by column name.
12677
12672
  *
12678
12673
  * @typeParam T - Row shape. Defaults to {@link QueryRow}.
12679
- * @param params - If provided, override the last {@link bind}; otherwise bound values are used.
12674
+ * @param params - Bind values for this call only.
12680
12675
  * @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
12681
12676
  */
12682
12677
  all(...params) {
12683
- return this.execute(params.length > 0 ? params : this.bound, { named: true }).rows;
12678
+ return this.execute(params, { named: true }).rows;
12684
12679
  }
12685
12680
  /**
12686
12681
  * Execute and return the full {@link ResultSet}, including column names.
@@ -12688,38 +12683,41 @@ var Statement = class _Statement {
12688
12683
  * Use this when you need metadata for an empty result (column names with zero rows).
12689
12684
  * {@link all} only returns row objects.
12690
12685
  *
12691
- * @param params - If provided, override the last {@link bind}; otherwise bound values are used.
12686
+ * @param params - Bind values for this call only.
12692
12687
  * @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
12693
12688
  */
12694
12689
  result(...params) {
12695
- return this.execute(params.length > 0 ? params : this.bound, { named: true });
12690
+ return this.execute(params, { named: true });
12696
12691
  }
12697
12692
  /**
12698
12693
  * Execute and return the first row, or `undefined` if there are no rows.
12699
12694
  *
12700
12695
  * @typeParam T - Row shape. Defaults to {@link QueryRow}.
12701
- * @param params - If provided, override the last {@link bind}; otherwise bound values are used.
12696
+ * @param params - Bind values for this call only.
12702
12697
  * @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
12703
12698
  */
12704
12699
  get(...params) {
12705
- return this.execute(params.length > 0 ? params : this.bound, { named: true, maxRows: 1 }).rows[0];
12700
+ return this.execute(params, { named: true, maxRows: 1 }).rows[0];
12706
12701
  }
12707
12702
  execute(params, options) {
12708
12703
  this.database.assertOpen();
12709
12704
  this.reprepareIfSchemaChanged();
12710
12705
  if (this.statements.length === 0) throw new SqliteError("empty statement", "misuse");
12706
+ this.namedPlan ??= planNamedParameters(this.sql);
12707
+ const expected = this.namedPlan.expectedCount;
12708
+ if (params.length > 0 && params.length !== expected) {
12709
+ throw new SqliteError(`SQLite query expected ${expected} values, received ${params.length}`, "misuse");
12710
+ }
12711
12711
  const env = this.obtainEnv(params);
12712
12712
  env.maxRows = options?.maxRows ?? Number.POSITIVE_INFINITY;
12713
12713
  env.includeNamedRows = options?.named !== false;
12714
12714
  env.includeValues = true;
12715
12715
  this.bindNamed(env, params);
12716
12716
  let result;
12717
- let lastQuery;
12718
12717
  for (const statement of this.statements) {
12719
12718
  result = executeStatement(statement, env);
12720
- if (result.columns.length > 0) lastQuery = result;
12721
12719
  }
12722
- return lastQuery ?? result;
12720
+ return result;
12723
12721
  }
12724
12722
  obtainEnv(params) {
12725
12723
  if (this.env) {
@@ -12769,18 +12767,18 @@ function planNamedParameters(sql) {
12769
12767
  }
12770
12768
  }
12771
12769
  }
12772
- return { named };
12770
+ return { named, expectedCount: nextSlot - 1 };
12773
12771
  }
12774
12772
 
12775
12773
  // src/api/database.ts
12776
12774
  var Database = class {
12777
12775
  /** @internal Engine catalog, tables, and mutation counters. */
12778
12776
  state = new DatabaseState();
12779
- /** Seed used to construct the PRNG when `options.prng` is omitted. */
12777
+ /** Seed used to construct the PRNG. */
12780
12778
  seed;
12781
12779
  /**
12782
12780
  * PRNG backing `random()` / `randomblob()` and related builtins.
12783
- * Prefer passing `seed` or `prng` to the constructor.
12781
+ * Prefer passing `seed` to the constructor.
12784
12782
  * @internal
12785
12783
  */
12786
12784
  prng;
@@ -12794,6 +12792,8 @@ var Database = class {
12794
12792
  transactions;
12795
12793
  closed = false;
12796
12794
  transactionSequence = 0;
12795
+ /** Depth of active {@link transaction} callbacks (not SQL BEGIN). */
12796
+ apiTransactionDepth = 0;
12797
12797
  /**
12798
12798
  * Create an empty in-memory database.
12799
12799
  *
@@ -12801,49 +12801,53 @@ var Database = class {
12801
12801
  */
12802
12802
  constructor(options = {}) {
12803
12803
  this.seed = options.seed ?? DEFAULT_DATABASE_SEED;
12804
- this.prng = options.prng ?? new Prng(this.seed);
12804
+ this.prng = new Prng(this.seed);
12805
12805
  this.now = resolveClock(options.now);
12806
12806
  this.transactions = new TransactionManager(this.state, this.prng);
12807
12807
  }
12808
12808
  /**
12809
12809
  * Execute SQL for its side effects (DDL/DML). Multiple statements are allowed.
12810
12810
  *
12811
+ * Does not accept bind parameters — use {@link prepare} or {@link query}.
12812
+ *
12811
12813
  * @param sql - SQL to run (semicolon-separated statements are ok).
12812
- * @param params - Bound parameters for `?` / `:name` placeholders.
12813
- * @throws {SqliteError} If the database is closed or the SQL fails.
12814
+ * @throws {SqliteError} If the database is closed, extra arguments are passed, or the SQL fails.
12814
12815
  */
12815
- exec(sql, params = []) {
12816
+ exec(sql) {
12816
12817
  this.assertOpen();
12817
- Statement.create(this, sql, parse(sql)).run(...params);
12818
+ if (arguments.length > 1) {
12819
+ throw new SqliteError("exec() does not accept parameters; use prepare() or query()", "misuse");
12820
+ }
12821
+ Statement.create(this, sql, parse(sql)).run();
12818
12822
  }
12819
12823
  /**
12820
- * Execute a query and return all rows as objects keyed by column name.
12824
+ * Execute a single-statement query and return all rows as objects keyed by column name.
12821
12825
  *
12822
12826
  * @typeParam T - Row shape. Defaults to {@link QueryRow}.
12823
- * @param sql - SQL SELECT (or any statement that produces a result set).
12827
+ * @param sql - A single SQL statement (trailing `;` is fine).
12824
12828
  * @param params - Bound parameters for `?` / `:name` placeholders.
12825
12829
  * @returns All result rows.
12826
- * @throws {SqliteError} If the database is closed or the SQL fails.
12830
+ * @throws {SqliteError} If the database is closed, `sql` is not a single statement, or execution fails.
12827
12831
  */
12828
12832
  query(sql, params = []) {
12829
12833
  this.assertOpen();
12830
- return Statement.create(this, sql, parse(sql)).all(...params);
12834
+ return this.prepareSingle(sql).all(...params);
12831
12835
  }
12832
12836
  /**
12833
- * Compile `sql` into a reusable {@link Statement}.
12837
+ * Compile a single SQL statement into a reusable {@link Statement}.
12834
12838
  *
12835
- * @param sql - SQL to prepare.
12836
- * @throws {SqliteError} If the database is closed or `sql` cannot be parsed.
12839
+ * @param sql - A single SQL statement (trailing `;` is fine). Multi-statement scripts are rejected.
12840
+ * @throws {SqliteError} If the database is closed or `sql` cannot be prepared as one statement.
12837
12841
  */
12838
12842
  prepare(sql) {
12839
12843
  this.assertOpen();
12840
- return Statement.create(this, sql, parse(sql));
12844
+ return this.prepareSingle(sql);
12841
12845
  }
12842
12846
  /**
12843
12847
  * Run `fn` inside a transaction. Commits on success; rolls back if `fn` throws.
12844
12848
  *
12845
12849
  * Nested calls use SAVEPOINTs so an inner failure does not abort the outer
12846
- * transaction.
12850
+ * transaction. Calling {@link close} from inside `fn` throws `misuse`.
12847
12851
  *
12848
12852
  * @param fn - Work to run while the transaction is open.
12849
12853
  * @returns The value returned by `fn`.
@@ -12851,27 +12855,32 @@ var Database = class {
12851
12855
  */
12852
12856
  transaction(fn) {
12853
12857
  this.assertOpen();
12854
- if (!this.transactions.inTransaction) {
12855
- this.transactions.begin();
12858
+ this.apiTransactionDepth++;
12859
+ try {
12860
+ if (!this.transactions.inTransaction) {
12861
+ this.transactions.begin();
12862
+ try {
12863
+ const value = fn();
12864
+ this.transactions.commit();
12865
+ return value;
12866
+ } catch (error) {
12867
+ this.transactions.rollback();
12868
+ throw error;
12869
+ }
12870
+ }
12871
+ const name = `__api_transaction_${++this.transactionSequence}`;
12872
+ this.transactions.savepoint(name);
12856
12873
  try {
12857
12874
  const value = fn();
12858
- this.transactions.commit();
12875
+ this.transactions.release(name);
12859
12876
  return value;
12860
12877
  } catch (error) {
12861
- this.transactions.rollback();
12878
+ this.transactions.rollback(name);
12879
+ this.transactions.release(name);
12862
12880
  throw error;
12863
12881
  }
12864
- }
12865
- const name = `__api_transaction_${++this.transactionSequence}`;
12866
- this.transactions.savepoint(name);
12867
- try {
12868
- const value = fn();
12869
- this.transactions.release(name);
12870
- return value;
12871
- } catch (error) {
12872
- this.transactions.rollback(name);
12873
- this.transactions.release(name);
12874
- throw error;
12882
+ } finally {
12883
+ this.apiTransactionDepth--;
12875
12884
  }
12876
12885
  }
12877
12886
  /**
@@ -12892,6 +12901,8 @@ var Database = class {
12892
12901
  * Replace this database's contents with a blob from {@link snapshot}.
12893
12902
  *
12894
12903
  * Restores PRNG state and the clock when the snapshot includes them (v2).
12904
+ * Newer library versions can restore older snapshots; older libraries cannot
12905
+ * restore newer format versions.
12895
12906
  *
12896
12907
  * @param snapshot - Bytes previously returned by {@link snapshot}.
12897
12908
  * @throws {SqliteError} If the database is closed, a transaction is open, or the blob is invalid.
@@ -12910,10 +12921,14 @@ var Database = class {
12910
12921
  /**
12911
12922
  * Close the database. Further SQL throws {@link SqliteError}. Idempotent.
12912
12923
  *
12913
- * Rolls back an open transaction, if any.
12924
+ * Rolls back an open SQL transaction, if any. Throws if called from inside
12925
+ * a {@link transaction} callback.
12914
12926
  */
12915
12927
  close() {
12916
12928
  if (this.closed) return;
12929
+ if (this.apiTransactionDepth > 0) {
12930
+ throw new SqliteError("cannot close database inside transaction()", "misuse");
12931
+ }
12917
12932
  if (this.transactions.inTransaction) this.transactions.rollback();
12918
12933
  this.closed = true;
12919
12934
  }
@@ -12943,42 +12958,30 @@ var Database = class {
12943
12958
  assertOpen() {
12944
12959
  if (this.closed) throw new SqliteError("Database is closed", "misuse");
12945
12960
  }
12961
+ prepareSingle(sql) {
12962
+ const statements = parse(sql);
12963
+ if (statements.length === 0) {
12964
+ throw new SqliteError("empty statement", "misuse");
12965
+ }
12966
+ if (statements.length > 1) {
12967
+ throw new SqliteError("query()/prepare() accept a single statement only; use exec() for scripts", "misuse");
12968
+ }
12969
+ return Statement.create(this, sql, statements);
12970
+ }
12946
12971
  };
12972
+ var disposeKey = Symbol.dispose;
12973
+ if (typeof disposeKey === "symbol") {
12974
+ Object.defineProperty(Database.prototype, disposeKey, {
12975
+ value: function() {
12976
+ this.close();
12977
+ },
12978
+ writable: true,
12979
+ configurable: true
12980
+ });
12981
+ }
12947
12982
  export {
12948
- DEFAULT_DATABASE_SEED,
12949
- DEFAULT_NOW,
12950
12983
  Database,
12951
- Prng,
12952
- SqlJsonText,
12953
- SqlReal,
12954
12984
  SqliteError,
12955
- Statement,
12956
- affinityFromTypeName,
12957
- applyAffinity,
12958
- asSqlJsonText,
12959
- asSqlReal,
12960
- canonicalizeNumber,
12961
- cloneSqlValue,
12962
- coerceToNumber,
12963
- compareSql,
12964
- decodeDatabaseState,
12965
- deriveSeed,
12966
- encodeDatabaseState,
12967
- evalExpr,
12968
- fixedClock,
12969
- globMatch,
12970
- isSqlJsonText,
12971
- isSqlReal,
12972
- isTruthySql,
12973
- likeMatch,
12974
- parse,
12975
- resolveClock,
12976
- sqlValueEquals,
12977
- storageClassOf,
12978
- toInteger,
12979
- tokenize,
12980
- typeofSql,
12981
- utf8Decode,
12982
- utf8Encode
12985
+ Statement
12983
12986
  };
12984
12987
  //# sourceMappingURL=index.js.map