tempest-db-js 0.6.0 → 0.8.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/README.md +4 -3
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +2 -2
- package/dist/{chunk-KOW3LSWP.js → chunk-MF6O56RO.js} +3 -3
- package/dist/{chunk-KOW3LSWP.js.map → chunk-MF6O56RO.js.map} +1 -1
- package/dist/{chunk-G7O5DCCC.js → chunk-NH6K5LTX.js} +164 -19
- package/dist/chunk-NH6K5LTX.js.map +1 -0
- package/dist/index.cjs +162 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -8
- package/dist/index.d.ts +112 -8
- package/dist/index.js +1 -1
- package/dist/migrations/index.cjs.map +1 -1
- package/dist/migrations/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-G7O5DCCC.js.map +0 -1
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 =
|
|
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
|
-
/**
|
|
1981
|
-
|
|
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(
|
|
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.
|
|
@@ -2010,6 +2047,68 @@ var NodeSqliteDriver = class _NodeSqliteDriver {
|
|
|
2010
2047
|
this.db.close();
|
|
2011
2048
|
}
|
|
2012
2049
|
};
|
|
2050
|
+
var BetterSqliteDriver = class _BetterSqliteDriver {
|
|
2051
|
+
// biome-ignore lint/suspicious/noExplicitAny: the peer dep's types are optional here.
|
|
2052
|
+
db;
|
|
2053
|
+
/** Prepared-statement cache keyed by SQL text — see {@link NodeSqliteDriver}. */
|
|
2054
|
+
// biome-ignore lint/suspicious/noExplicitAny: see above.
|
|
2055
|
+
statements = /* @__PURE__ */ new Map();
|
|
2056
|
+
// biome-ignore lint/suspicious/noExplicitAny: accept an already-open Database handle.
|
|
2057
|
+
constructor(database) {
|
|
2058
|
+
this.db = database;
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Open a `better-sqlite3` database at the given path (or `":memory:"`).
|
|
2062
|
+
*
|
|
2063
|
+
* @param path The database file, or `":memory:"`.
|
|
2064
|
+
* @param options Passed straight to `new Database()` (`readonly`, `timeout`, …).
|
|
2065
|
+
* @returns A driver over the open handle.
|
|
2066
|
+
* @throws If `better-sqlite3` is not installed — it is an optional peer
|
|
2067
|
+
* dependency, so the error names the package to install.
|
|
2068
|
+
*/
|
|
2069
|
+
static open(path, options) {
|
|
2070
|
+
let Database;
|
|
2071
|
+
try {
|
|
2072
|
+
const mod = nodeRequire("better-sqlite3");
|
|
2073
|
+
Database = mod.default ?? mod;
|
|
2074
|
+
} catch (cause) {
|
|
2075
|
+
throw new Error(
|
|
2076
|
+
'The "better-sqlite3" driver requires the better-sqlite3 package: npm install better-sqlite3',
|
|
2077
|
+
{ cause }
|
|
2078
|
+
);
|
|
2079
|
+
}
|
|
2080
|
+
return new _BetterSqliteDriver(
|
|
2081
|
+
options ? new Database(path, { ...options }) : new Database(path)
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
2084
|
+
/** Return the cached prepared statement for `sql`, preparing it on first use. */
|
|
2085
|
+
// biome-ignore lint/suspicious/noExplicitAny: statement type is optional here.
|
|
2086
|
+
prepare(sql2) {
|
|
2087
|
+
const cached = this.statements.get(sql2);
|
|
2088
|
+
if (cached) return cached;
|
|
2089
|
+
const stmt = this.db.prepare(sql2);
|
|
2090
|
+
this.statements.set(sql2, stmt);
|
|
2091
|
+
return stmt;
|
|
2092
|
+
}
|
|
2093
|
+
execute(sql2, params) {
|
|
2094
|
+
const stmt = this.prepare(sql2);
|
|
2095
|
+
const bound = params.map(encodeSqliteParam);
|
|
2096
|
+
if (stmt.reader) {
|
|
2097
|
+
return { rows: stmt.all(...bound), changes: 0 };
|
|
2098
|
+
}
|
|
2099
|
+
const info = stmt.run(...bound);
|
|
2100
|
+
return { rows: [], changes: Number(info.changes ?? 0) };
|
|
2101
|
+
}
|
|
2102
|
+
*iterate(sql2, params) {
|
|
2103
|
+
const stmt = this.prepare(sql2);
|
|
2104
|
+
const bound = params.map(encodeSqliteParam);
|
|
2105
|
+
yield* stmt.iterate(...bound);
|
|
2106
|
+
}
|
|
2107
|
+
close() {
|
|
2108
|
+
this.statements.clear();
|
|
2109
|
+
this.db.close();
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2013
2112
|
function returnsRows(sql2) {
|
|
2014
2113
|
return /^\s*(select|pragma)/i.test(sql2) || /\breturning\b/i.test(sql2);
|
|
2015
2114
|
}
|
|
@@ -2459,6 +2558,13 @@ var AsyncSession = class _AsyncSession {
|
|
|
2459
2558
|
await this.close();
|
|
2460
2559
|
}
|
|
2461
2560
|
};
|
|
2561
|
+
function emitNotice(logger, notice) {
|
|
2562
|
+
if (!logger) return;
|
|
2563
|
+
try {
|
|
2564
|
+
logger(notice);
|
|
2565
|
+
} catch {
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2462
2568
|
var SyncEngine = class {
|
|
2463
2569
|
constructor(driver, logger) {
|
|
2464
2570
|
this.driver = driver;
|
|
@@ -2526,8 +2632,43 @@ function asAsync(driver) {
|
|
|
2526
2632
|
} : {}
|
|
2527
2633
|
};
|
|
2528
2634
|
}
|
|
2529
|
-
|
|
2530
|
-
|
|
2635
|
+
var SQLITE_DRIVER_ALIASES = {
|
|
2636
|
+
"node:sqlite": "node:sqlite",
|
|
2637
|
+
"node-sqlite": "node:sqlite",
|
|
2638
|
+
node: "node:sqlite",
|
|
2639
|
+
"better-sqlite3": "better-sqlite3",
|
|
2640
|
+
better_sqlite3: "better-sqlite3",
|
|
2641
|
+
bettersqlite3: "better-sqlite3"
|
|
2642
|
+
};
|
|
2643
|
+
var SERVER_DRIVER_ALIASES = {
|
|
2644
|
+
postgresql: ["postgres", "postgres.js", "postgresjs", "pg"],
|
|
2645
|
+
mysql: ["mysql", "mysql2"]
|
|
2646
|
+
};
|
|
2647
|
+
function checkServerDriver(dialect, driver) {
|
|
2648
|
+
if (!driver) return;
|
|
2649
|
+
const accepted = SERVER_DRIVER_ALIASES[dialect] ?? [];
|
|
2650
|
+
if (accepted.includes(driver.toLowerCase())) return;
|
|
2651
|
+
throw new Error(
|
|
2652
|
+
`Unknown ${dialect} driver ${JSON.stringify(driver)}; tempest-db-js runs ${dialect} on ${JSON.stringify(accepted[0])}.`
|
|
2653
|
+
);
|
|
2654
|
+
}
|
|
2655
|
+
function resolveSqliteDriver(parsed, options) {
|
|
2656
|
+
const explicit = options?.driver;
|
|
2657
|
+
if (explicit) {
|
|
2658
|
+
const resolved = SQLITE_DRIVER_ALIASES[explicit.toLowerCase()];
|
|
2659
|
+
if (!resolved) {
|
|
2660
|
+
throw new Error(
|
|
2661
|
+
`Unknown SQLite driver ${JSON.stringify(explicit)}; supported: "node:sqlite" (built-in, default) and "better-sqlite3".`
|
|
2662
|
+
);
|
|
2663
|
+
}
|
|
2664
|
+
return resolved;
|
|
2665
|
+
}
|
|
2666
|
+
const fromUrl = parsed.driver ? SQLITE_DRIVER_ALIASES[parsed.driver.toLowerCase()] : void 0;
|
|
2667
|
+
return fromUrl ?? "node:sqlite";
|
|
2668
|
+
}
|
|
2669
|
+
function openSqliteDriver(parsed, options) {
|
|
2670
|
+
const path = parsed.database ?? ":memory:";
|
|
2671
|
+
return resolveSqliteDriver(parsed, options) === "better-sqlite3" ? BetterSqliteDriver.open(path, options?.driverOptions) : NodeSqliteDriver.open(path, options?.driverOptions);
|
|
2531
2672
|
}
|
|
2532
2673
|
function createSyncEngine(url, options) {
|
|
2533
2674
|
const parsed = parseDatabaseUrl(url);
|
|
@@ -2536,29 +2677,28 @@ function createSyncEngine(url, options) {
|
|
|
2536
2677
|
`createSyncEngine supports only SQLite; ${parsed.dialect} is async-only \u2014 use createEngine.`
|
|
2537
2678
|
);
|
|
2538
2679
|
}
|
|
2539
|
-
return new SyncEngine(
|
|
2540
|
-
openSqliteDriver(parsed.database ?? ":memory:"),
|
|
2541
|
-
options?.onQuery
|
|
2542
|
-
);
|
|
2680
|
+
return new SyncEngine(openSqliteDriver(parsed, options), options?.onQuery);
|
|
2543
2681
|
}
|
|
2544
2682
|
function createEngine(url, options) {
|
|
2545
2683
|
const parsed = parseDatabaseUrl(url);
|
|
2546
2684
|
if (parsed.dialect === "sqlite") {
|
|
2547
2685
|
return new AsyncEngine(
|
|
2548
|
-
asAsync(openSqliteDriver(parsed
|
|
2686
|
+
asAsync(openSqliteDriver(parsed, options)),
|
|
2549
2687
|
"sqlite",
|
|
2550
2688
|
options?.onQuery
|
|
2551
2689
|
);
|
|
2552
2690
|
}
|
|
2553
2691
|
if (parsed.dialect === "mysql") {
|
|
2692
|
+
checkServerDriver("mysql", options?.driver);
|
|
2554
2693
|
return new AsyncEngine(
|
|
2555
|
-
createMysqlDriver(parsed.raw, options
|
|
2694
|
+
createMysqlDriver(parsed.raw, options),
|
|
2556
2695
|
"mysql",
|
|
2557
2696
|
options?.onQuery
|
|
2558
2697
|
);
|
|
2559
2698
|
}
|
|
2699
|
+
checkServerDriver("postgresql", options?.driver);
|
|
2560
2700
|
return new AsyncEngine(
|
|
2561
|
-
createPostgresDriver(parsed.raw, options
|
|
2701
|
+
createPostgresDriver(parsed.raw, options),
|
|
2562
2702
|
"postgresql",
|
|
2563
2703
|
options?.onQuery
|
|
2564
2704
|
);
|
|
@@ -2578,7 +2718,8 @@ function toMysqlResult(rows) {
|
|
|
2578
2718
|
const header = rows;
|
|
2579
2719
|
return { rows: [], changes: header.affectedRows ?? 0 };
|
|
2580
2720
|
}
|
|
2581
|
-
function createMysqlDriver(url,
|
|
2721
|
+
function createMysqlDriver(url, options) {
|
|
2722
|
+
const pool = options?.pool;
|
|
2582
2723
|
let poolHandle;
|
|
2583
2724
|
const ensure = async () => {
|
|
2584
2725
|
if (poolHandle) return;
|
|
@@ -2591,6 +2732,7 @@ function createMysqlDriver(url, pool) {
|
|
|
2591
2732
|
if (pool?.size !== void 0) opts.connectionLimit = pool.size;
|
|
2592
2733
|
if (pool?.idleTimeoutMs !== void 0) opts.idleTimeout = pool.idleTimeoutMs;
|
|
2593
2734
|
if (pool?.connectTimeoutMs !== void 0) opts.connectTimeout = pool.connectTimeoutMs;
|
|
2735
|
+
Object.assign(opts, options?.driverOptions ?? {});
|
|
2594
2736
|
poolHandle = mod.createPool(opts);
|
|
2595
2737
|
};
|
|
2596
2738
|
const runOn = async (queryable, sql2, params) => {
|
|
@@ -2624,7 +2766,8 @@ function toPostgresResult(rows) {
|
|
|
2624
2766
|
const arr = rows;
|
|
2625
2767
|
return { rows: Array.from(arr), changes: arr.count ?? arr.length };
|
|
2626
2768
|
}
|
|
2627
|
-
function createPostgresDriver(url,
|
|
2769
|
+
function createPostgresDriver(url, options) {
|
|
2770
|
+
const pool = options?.pool;
|
|
2628
2771
|
let client;
|
|
2629
2772
|
const ensure = async () => {
|
|
2630
2773
|
if (client) return;
|
|
@@ -2640,6 +2783,8 @@ function createPostgresDriver(url, pool) {
|
|
|
2640
2783
|
if (pool?.connectTimeoutMs !== void 0) {
|
|
2641
2784
|
opts.connect_timeout = Math.ceil(pool.connectTimeoutMs / 1e3);
|
|
2642
2785
|
}
|
|
2786
|
+
opts.onnotice = (notice) => emitNotice(options?.onNotice, notice);
|
|
2787
|
+
Object.assign(opts, options?.driverOptions ?? {});
|
|
2643
2788
|
client = (mod.default ?? mod)(url, opts);
|
|
2644
2789
|
};
|
|
2645
2790
|
return {
|
|
@@ -3032,6 +3177,7 @@ exports.AsyncResult = AsyncResult;
|
|
|
3032
3177
|
exports.AsyncSession = AsyncSession;
|
|
3033
3178
|
exports.BaseDialect = BaseDialect;
|
|
3034
3179
|
exports.BaseRepository = BaseRepository;
|
|
3180
|
+
exports.BetterSqliteDriver = BetterSqliteDriver;
|
|
3035
3181
|
exports.Column = Column;
|
|
3036
3182
|
exports.DeleteBuilder = DeleteBuilder;
|
|
3037
3183
|
exports.Expression = Expression;
|