tempest-db-js 0.6.0 → 0.7.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.cjs CHANGED
@@ -611,6 +611,21 @@ function assertWritableValues(model, values, clause) {
611
611
  }
612
612
  if (issues.length > 0) throw new ValidationError(model.tablename, issues);
613
613
  }
614
+ function assertConsistentRows(model, rows) {
615
+ if (rows.length < 2) return;
616
+ const union = /* @__PURE__ */ new Set();
617
+ for (const row of rows) for (const key of Object.keys(row)) union.add(key);
618
+ const inconsistent = [...union].filter((key) => rows.some((row) => !(key in row)));
619
+ if (inconsistent.length === 0) return;
620
+ const columns = columnsOf(model);
621
+ const defaulted = inconsistent.filter((key) => columns[key]?.flags.hasDefault);
622
+ if (defaulted.length === 0) return;
623
+ const named = defaulted.map((c) => `"${c}"`).join(", ");
624
+ const verb = defaulted.length === 1 ? "has" : "have";
625
+ throw new ValidationError(model.tablename, [
626
+ `values: ${named} ${verb} a default but is missing from some rows of this multi-row insert \u2014 every row shares one column list, so the omitting rows would be written as NULL instead of taking the default. Give the column in every row, or insert the rows separately.`
627
+ ]);
628
+ }
614
629
  function describeValue(value) {
615
630
  if (typeof value === "function") return "a function";
616
631
  if (Array.isArray(value)) return "an array";
@@ -633,11 +648,13 @@ var InsertBuilder = class _InsertBuilder {
633
648
  * @param rows One row, or an array of rows.
634
649
  * @returns A builder carrying the rows.
635
650
  * @throws ValidationError When a value is not a column value the dialect can
636
- * bind (see the `sql` helpers for writing an expression instead).
651
+ * bind (see the `sql` helpers for writing an expression instead), or when the
652
+ * rows of a multi-row insert disagree about a column that has a default.
637
653
  */
638
654
  values(rows) {
639
655
  const list = Array.isArray(rows) ? rows : [rows];
640
656
  for (const row of list) assertWritableValues(this.source, row, "values");
657
+ assertConsistentRows(this.source, list);
641
658
  return this.with({ values: list });
642
659
  }
643
660
  /**
@@ -939,6 +956,18 @@ var Params = class {
939
956
  return this.placeholder(this.values.length);
940
957
  }
941
958
  };
959
+ function insertColumns(rows) {
960
+ const columns = [];
961
+ const seen = /* @__PURE__ */ new Set();
962
+ for (const row of rows) {
963
+ for (const key of Object.keys(row)) {
964
+ if (seen.has(key)) continue;
965
+ seen.add(key);
966
+ columns.push(key);
967
+ }
968
+ }
969
+ return columns;
970
+ }
942
971
  function insertHasExpression(node) {
943
972
  for (const row of node.values) {
944
973
  for (const value of Object.values(row)) {
@@ -1168,7 +1197,7 @@ var BaseDialect = class _BaseDialect {
1168
1197
  * SQL order, so placeholder positions stay correct.
1169
1198
  */
1170
1199
  compileInsert(node, params) {
1171
- const columns = node.values.length > 0 ? Object.keys(node.values[0]) : [];
1200
+ const columns = insertColumns(node.values);
1172
1201
  const conflict = node.onConflict;
1173
1202
  const cacheable = conflict?.targetWhere === void 0 && conflict?.updateWhere === void 0 && !insertHasExpression(node);
1174
1203
  if (!cacheable) return this.compileInsertDirect(node, columns, params);
@@ -1977,10 +2006,18 @@ var NodeSqliteDriver = class _NodeSqliteDriver {
1977
2006
  constructor(database) {
1978
2007
  this.db = database;
1979
2008
  }
1980
- /** Open a `node:sqlite` database at the given path (or `:memory:`). */
1981
- static open(path) {
2009
+ /**
2010
+ * Open a `node:sqlite` database at the given path (or `:memory:`).
2011
+ *
2012
+ * @param path The database file, or `":memory:"`.
2013
+ * @param options Passed straight to `DatabaseSync` (`readOnly`, `timeout`, …).
2014
+ * @returns A driver over the open handle.
2015
+ */
2016
+ static open(path, options) {
1982
2017
  const { DatabaseSync } = nodeRequire("node:sqlite");
1983
- return new _NodeSqliteDriver(new DatabaseSync(path));
2018
+ return new _NodeSqliteDriver(
2019
+ options ? new DatabaseSync(path, { ...options }) : new DatabaseSync(path)
2020
+ );
1984
2021
  }
1985
2022
  /** Return the cached prepared statement for `sql`, preparing it on first use. */
1986
2023
  // biome-ignore lint/suspicious/noExplicitAny: statement type is unavailable here.
@@ -2459,6 +2496,13 @@ var AsyncSession = class _AsyncSession {
2459
2496
  await this.close();
2460
2497
  }
2461
2498
  };
2499
+ function emitNotice(logger, notice) {
2500
+ if (!logger) return;
2501
+ try {
2502
+ logger(notice);
2503
+ } catch {
2504
+ }
2505
+ }
2462
2506
  var SyncEngine = class {
2463
2507
  constructor(driver, logger) {
2464
2508
  this.driver = driver;
@@ -2526,8 +2570,8 @@ function asAsync(driver) {
2526
2570
  } : {}
2527
2571
  };
2528
2572
  }
2529
- function openSqliteDriver(path, _options) {
2530
- return NodeSqliteDriver.open(path);
2573
+ function openSqliteDriver(path, options) {
2574
+ return NodeSqliteDriver.open(path, options?.driverOptions);
2531
2575
  }
2532
2576
  function createSyncEngine(url, options) {
2533
2577
  const parsed = parseDatabaseUrl(url);
@@ -2537,7 +2581,7 @@ function createSyncEngine(url, options) {
2537
2581
  );
2538
2582
  }
2539
2583
  return new SyncEngine(
2540
- openSqliteDriver(parsed.database ?? ":memory:"),
2584
+ openSqliteDriver(parsed.database ?? ":memory:", options),
2541
2585
  options?.onQuery
2542
2586
  );
2543
2587
  }
@@ -2545,20 +2589,20 @@ function createEngine(url, options) {
2545
2589
  const parsed = parseDatabaseUrl(url);
2546
2590
  if (parsed.dialect === "sqlite") {
2547
2591
  return new AsyncEngine(
2548
- asAsync(openSqliteDriver(parsed.database ?? ":memory:")),
2592
+ asAsync(openSqliteDriver(parsed.database ?? ":memory:", options)),
2549
2593
  "sqlite",
2550
2594
  options?.onQuery
2551
2595
  );
2552
2596
  }
2553
2597
  if (parsed.dialect === "mysql") {
2554
2598
  return new AsyncEngine(
2555
- createMysqlDriver(parsed.raw, options?.pool),
2599
+ createMysqlDriver(parsed.raw, options),
2556
2600
  "mysql",
2557
2601
  options?.onQuery
2558
2602
  );
2559
2603
  }
2560
2604
  return new AsyncEngine(
2561
- createPostgresDriver(parsed.raw, options?.pool),
2605
+ createPostgresDriver(parsed.raw, options),
2562
2606
  "postgresql",
2563
2607
  options?.onQuery
2564
2608
  );
@@ -2578,7 +2622,8 @@ function toMysqlResult(rows) {
2578
2622
  const header = rows;
2579
2623
  return { rows: [], changes: header.affectedRows ?? 0 };
2580
2624
  }
2581
- function createMysqlDriver(url, pool) {
2625
+ function createMysqlDriver(url, options) {
2626
+ const pool = options?.pool;
2582
2627
  let poolHandle;
2583
2628
  const ensure = async () => {
2584
2629
  if (poolHandle) return;
@@ -2591,6 +2636,7 @@ function createMysqlDriver(url, pool) {
2591
2636
  if (pool?.size !== void 0) opts.connectionLimit = pool.size;
2592
2637
  if (pool?.idleTimeoutMs !== void 0) opts.idleTimeout = pool.idleTimeoutMs;
2593
2638
  if (pool?.connectTimeoutMs !== void 0) opts.connectTimeout = pool.connectTimeoutMs;
2639
+ Object.assign(opts, options?.driverOptions ?? {});
2594
2640
  poolHandle = mod.createPool(opts);
2595
2641
  };
2596
2642
  const runOn = async (queryable, sql2, params) => {
@@ -2624,7 +2670,8 @@ function toPostgresResult(rows) {
2624
2670
  const arr = rows;
2625
2671
  return { rows: Array.from(arr), changes: arr.count ?? arr.length };
2626
2672
  }
2627
- function createPostgresDriver(url, pool) {
2673
+ function createPostgresDriver(url, options) {
2674
+ const pool = options?.pool;
2628
2675
  let client;
2629
2676
  const ensure = async () => {
2630
2677
  if (client) return;
@@ -2640,6 +2687,8 @@ function createPostgresDriver(url, pool) {
2640
2687
  if (pool?.connectTimeoutMs !== void 0) {
2641
2688
  opts.connect_timeout = Math.ceil(pool.connectTimeoutMs / 1e3);
2642
2689
  }
2690
+ opts.onnotice = (notice) => emitNotice(options?.onNotice, notice);
2691
+ Object.assign(opts, options?.driverOptions ?? {});
2643
2692
  client = (mod.default ?? mod)(url, opts);
2644
2693
  };
2645
2694
  return {