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
|
@@ -608,6 +608,21 @@ function assertWritableValues(model, values, clause) {
|
|
|
608
608
|
}
|
|
609
609
|
if (issues.length > 0) throw new ValidationError(model.tablename, issues);
|
|
610
610
|
}
|
|
611
|
+
function assertConsistentRows(model, rows) {
|
|
612
|
+
if (rows.length < 2) return;
|
|
613
|
+
const union = /* @__PURE__ */ new Set();
|
|
614
|
+
for (const row of rows) for (const key of Object.keys(row)) union.add(key);
|
|
615
|
+
const inconsistent = [...union].filter((key) => rows.some((row) => !(key in row)));
|
|
616
|
+
if (inconsistent.length === 0) return;
|
|
617
|
+
const columns = columnsOf(model);
|
|
618
|
+
const defaulted = inconsistent.filter((key) => columns[key]?.flags.hasDefault);
|
|
619
|
+
if (defaulted.length === 0) return;
|
|
620
|
+
const named = defaulted.map((c) => `"${c}"`).join(", ");
|
|
621
|
+
const verb = defaulted.length === 1 ? "has" : "have";
|
|
622
|
+
throw new ValidationError(model.tablename, [
|
|
623
|
+
`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.`
|
|
624
|
+
]);
|
|
625
|
+
}
|
|
611
626
|
function describeValue(value) {
|
|
612
627
|
if (typeof value === "function") return "a function";
|
|
613
628
|
if (Array.isArray(value)) return "an array";
|
|
@@ -630,11 +645,13 @@ var InsertBuilder = class _InsertBuilder {
|
|
|
630
645
|
* @param rows One row, or an array of rows.
|
|
631
646
|
* @returns A builder carrying the rows.
|
|
632
647
|
* @throws ValidationError When a value is not a column value the dialect can
|
|
633
|
-
* bind (see the `sql` helpers for writing an expression instead)
|
|
648
|
+
* bind (see the `sql` helpers for writing an expression instead), or when the
|
|
649
|
+
* rows of a multi-row insert disagree about a column that has a default.
|
|
634
650
|
*/
|
|
635
651
|
values(rows) {
|
|
636
652
|
const list = Array.isArray(rows) ? rows : [rows];
|
|
637
653
|
for (const row of list) assertWritableValues(this.source, row, "values");
|
|
654
|
+
assertConsistentRows(this.source, list);
|
|
638
655
|
return this.with({ values: list });
|
|
639
656
|
}
|
|
640
657
|
/**
|
|
@@ -936,6 +953,18 @@ var Params = class {
|
|
|
936
953
|
return this.placeholder(this.values.length);
|
|
937
954
|
}
|
|
938
955
|
};
|
|
956
|
+
function insertColumns(rows) {
|
|
957
|
+
const columns = [];
|
|
958
|
+
const seen = /* @__PURE__ */ new Set();
|
|
959
|
+
for (const row of rows) {
|
|
960
|
+
for (const key of Object.keys(row)) {
|
|
961
|
+
if (seen.has(key)) continue;
|
|
962
|
+
seen.add(key);
|
|
963
|
+
columns.push(key);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return columns;
|
|
967
|
+
}
|
|
939
968
|
function insertHasExpression(node) {
|
|
940
969
|
for (const row of node.values) {
|
|
941
970
|
for (const value of Object.values(row)) {
|
|
@@ -1165,7 +1194,7 @@ var BaseDialect = class _BaseDialect {
|
|
|
1165
1194
|
* SQL order, so placeholder positions stay correct.
|
|
1166
1195
|
*/
|
|
1167
1196
|
compileInsert(node, params) {
|
|
1168
|
-
const columns =
|
|
1197
|
+
const columns = insertColumns(node.values);
|
|
1169
1198
|
const conflict = node.onConflict;
|
|
1170
1199
|
const cacheable = conflict?.targetWhere === void 0 && conflict?.updateWhere === void 0 && !insertHasExpression(node);
|
|
1171
1200
|
if (!cacheable) return this.compileInsertDirect(node, columns, params);
|
|
@@ -1974,10 +2003,18 @@ var NodeSqliteDriver = class _NodeSqliteDriver {
|
|
|
1974
2003
|
constructor(database) {
|
|
1975
2004
|
this.db = database;
|
|
1976
2005
|
}
|
|
1977
|
-
/**
|
|
1978
|
-
|
|
2006
|
+
/**
|
|
2007
|
+
* Open a `node:sqlite` database at the given path (or `:memory:`).
|
|
2008
|
+
*
|
|
2009
|
+
* @param path The database file, or `":memory:"`.
|
|
2010
|
+
* @param options Passed straight to `DatabaseSync` (`readOnly`, `timeout`, …).
|
|
2011
|
+
* @returns A driver over the open handle.
|
|
2012
|
+
*/
|
|
2013
|
+
static open(path, options) {
|
|
1979
2014
|
const { DatabaseSync } = nodeRequire("node:sqlite");
|
|
1980
|
-
return new _NodeSqliteDriver(
|
|
2015
|
+
return new _NodeSqliteDriver(
|
|
2016
|
+
options ? new DatabaseSync(path, { ...options }) : new DatabaseSync(path)
|
|
2017
|
+
);
|
|
1981
2018
|
}
|
|
1982
2019
|
/** Return the cached prepared statement for `sql`, preparing it on first use. */
|
|
1983
2020
|
// biome-ignore lint/suspicious/noExplicitAny: statement type is unavailable here.
|
|
@@ -2007,6 +2044,68 @@ var NodeSqliteDriver = class _NodeSqliteDriver {
|
|
|
2007
2044
|
this.db.close();
|
|
2008
2045
|
}
|
|
2009
2046
|
};
|
|
2047
|
+
var BetterSqliteDriver = class _BetterSqliteDriver {
|
|
2048
|
+
// biome-ignore lint/suspicious/noExplicitAny: the peer dep's types are optional here.
|
|
2049
|
+
db;
|
|
2050
|
+
/** Prepared-statement cache keyed by SQL text — see {@link NodeSqliteDriver}. */
|
|
2051
|
+
// biome-ignore lint/suspicious/noExplicitAny: see above.
|
|
2052
|
+
statements = /* @__PURE__ */ new Map();
|
|
2053
|
+
// biome-ignore lint/suspicious/noExplicitAny: accept an already-open Database handle.
|
|
2054
|
+
constructor(database) {
|
|
2055
|
+
this.db = database;
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Open a `better-sqlite3` database at the given path (or `":memory:"`).
|
|
2059
|
+
*
|
|
2060
|
+
* @param path The database file, or `":memory:"`.
|
|
2061
|
+
* @param options Passed straight to `new Database()` (`readonly`, `timeout`, …).
|
|
2062
|
+
* @returns A driver over the open handle.
|
|
2063
|
+
* @throws If `better-sqlite3` is not installed — it is an optional peer
|
|
2064
|
+
* dependency, so the error names the package to install.
|
|
2065
|
+
*/
|
|
2066
|
+
static open(path, options) {
|
|
2067
|
+
let Database;
|
|
2068
|
+
try {
|
|
2069
|
+
const mod = nodeRequire("better-sqlite3");
|
|
2070
|
+
Database = mod.default ?? mod;
|
|
2071
|
+
} catch (cause) {
|
|
2072
|
+
throw new Error(
|
|
2073
|
+
'The "better-sqlite3" driver requires the better-sqlite3 package: npm install better-sqlite3',
|
|
2074
|
+
{ cause }
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
2077
|
+
return new _BetterSqliteDriver(
|
|
2078
|
+
options ? new Database(path, { ...options }) : new Database(path)
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
/** Return the cached prepared statement for `sql`, preparing it on first use. */
|
|
2082
|
+
// biome-ignore lint/suspicious/noExplicitAny: statement type is optional here.
|
|
2083
|
+
prepare(sql2) {
|
|
2084
|
+
const cached = this.statements.get(sql2);
|
|
2085
|
+
if (cached) return cached;
|
|
2086
|
+
const stmt = this.db.prepare(sql2);
|
|
2087
|
+
this.statements.set(sql2, stmt);
|
|
2088
|
+
return stmt;
|
|
2089
|
+
}
|
|
2090
|
+
execute(sql2, params) {
|
|
2091
|
+
const stmt = this.prepare(sql2);
|
|
2092
|
+
const bound = params.map(encodeSqliteParam);
|
|
2093
|
+
if (stmt.reader) {
|
|
2094
|
+
return { rows: stmt.all(...bound), changes: 0 };
|
|
2095
|
+
}
|
|
2096
|
+
const info = stmt.run(...bound);
|
|
2097
|
+
return { rows: [], changes: Number(info.changes ?? 0) };
|
|
2098
|
+
}
|
|
2099
|
+
*iterate(sql2, params) {
|
|
2100
|
+
const stmt = this.prepare(sql2);
|
|
2101
|
+
const bound = params.map(encodeSqliteParam);
|
|
2102
|
+
yield* stmt.iterate(...bound);
|
|
2103
|
+
}
|
|
2104
|
+
close() {
|
|
2105
|
+
this.statements.clear();
|
|
2106
|
+
this.db.close();
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2010
2109
|
function returnsRows(sql2) {
|
|
2011
2110
|
return /^\s*(select|pragma)/i.test(sql2) || /\breturning\b/i.test(sql2);
|
|
2012
2111
|
}
|
|
@@ -2456,6 +2555,13 @@ var AsyncSession = class _AsyncSession {
|
|
|
2456
2555
|
await this.close();
|
|
2457
2556
|
}
|
|
2458
2557
|
};
|
|
2558
|
+
function emitNotice(logger, notice) {
|
|
2559
|
+
if (!logger) return;
|
|
2560
|
+
try {
|
|
2561
|
+
logger(notice);
|
|
2562
|
+
} catch {
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2459
2565
|
var SyncEngine = class {
|
|
2460
2566
|
constructor(driver, logger) {
|
|
2461
2567
|
this.driver = driver;
|
|
@@ -2523,8 +2629,43 @@ function asAsync(driver) {
|
|
|
2523
2629
|
} : {}
|
|
2524
2630
|
};
|
|
2525
2631
|
}
|
|
2526
|
-
|
|
2527
|
-
|
|
2632
|
+
var SQLITE_DRIVER_ALIASES = {
|
|
2633
|
+
"node:sqlite": "node:sqlite",
|
|
2634
|
+
"node-sqlite": "node:sqlite",
|
|
2635
|
+
node: "node:sqlite",
|
|
2636
|
+
"better-sqlite3": "better-sqlite3",
|
|
2637
|
+
better_sqlite3: "better-sqlite3",
|
|
2638
|
+
bettersqlite3: "better-sqlite3"
|
|
2639
|
+
};
|
|
2640
|
+
var SERVER_DRIVER_ALIASES = {
|
|
2641
|
+
postgresql: ["postgres", "postgres.js", "postgresjs", "pg"],
|
|
2642
|
+
mysql: ["mysql", "mysql2"]
|
|
2643
|
+
};
|
|
2644
|
+
function checkServerDriver(dialect, driver) {
|
|
2645
|
+
if (!driver) return;
|
|
2646
|
+
const accepted = SERVER_DRIVER_ALIASES[dialect] ?? [];
|
|
2647
|
+
if (accepted.includes(driver.toLowerCase())) return;
|
|
2648
|
+
throw new Error(
|
|
2649
|
+
`Unknown ${dialect} driver ${JSON.stringify(driver)}; tempest-db-js runs ${dialect} on ${JSON.stringify(accepted[0])}.`
|
|
2650
|
+
);
|
|
2651
|
+
}
|
|
2652
|
+
function resolveSqliteDriver(parsed, options) {
|
|
2653
|
+
const explicit = options?.driver;
|
|
2654
|
+
if (explicit) {
|
|
2655
|
+
const resolved = SQLITE_DRIVER_ALIASES[explicit.toLowerCase()];
|
|
2656
|
+
if (!resolved) {
|
|
2657
|
+
throw new Error(
|
|
2658
|
+
`Unknown SQLite driver ${JSON.stringify(explicit)}; supported: "node:sqlite" (built-in, default) and "better-sqlite3".`
|
|
2659
|
+
);
|
|
2660
|
+
}
|
|
2661
|
+
return resolved;
|
|
2662
|
+
}
|
|
2663
|
+
const fromUrl = parsed.driver ? SQLITE_DRIVER_ALIASES[parsed.driver.toLowerCase()] : void 0;
|
|
2664
|
+
return fromUrl ?? "node:sqlite";
|
|
2665
|
+
}
|
|
2666
|
+
function openSqliteDriver(parsed, options) {
|
|
2667
|
+
const path = parsed.database ?? ":memory:";
|
|
2668
|
+
return resolveSqliteDriver(parsed, options) === "better-sqlite3" ? BetterSqliteDriver.open(path, options?.driverOptions) : NodeSqliteDriver.open(path, options?.driverOptions);
|
|
2528
2669
|
}
|
|
2529
2670
|
function createSyncEngine(url, options) {
|
|
2530
2671
|
const parsed = parseDatabaseUrl(url);
|
|
@@ -2533,29 +2674,28 @@ function createSyncEngine(url, options) {
|
|
|
2533
2674
|
`createSyncEngine supports only SQLite; ${parsed.dialect} is async-only \u2014 use createEngine.`
|
|
2534
2675
|
);
|
|
2535
2676
|
}
|
|
2536
|
-
return new SyncEngine(
|
|
2537
|
-
openSqliteDriver(parsed.database ?? ":memory:"),
|
|
2538
|
-
options?.onQuery
|
|
2539
|
-
);
|
|
2677
|
+
return new SyncEngine(openSqliteDriver(parsed, options), options?.onQuery);
|
|
2540
2678
|
}
|
|
2541
2679
|
function createEngine(url, options) {
|
|
2542
2680
|
const parsed = parseDatabaseUrl(url);
|
|
2543
2681
|
if (parsed.dialect === "sqlite") {
|
|
2544
2682
|
return new AsyncEngine(
|
|
2545
|
-
asAsync(openSqliteDriver(parsed
|
|
2683
|
+
asAsync(openSqliteDriver(parsed, options)),
|
|
2546
2684
|
"sqlite",
|
|
2547
2685
|
options?.onQuery
|
|
2548
2686
|
);
|
|
2549
2687
|
}
|
|
2550
2688
|
if (parsed.dialect === "mysql") {
|
|
2689
|
+
checkServerDriver("mysql", options?.driver);
|
|
2551
2690
|
return new AsyncEngine(
|
|
2552
|
-
createMysqlDriver(parsed.raw, options
|
|
2691
|
+
createMysqlDriver(parsed.raw, options),
|
|
2553
2692
|
"mysql",
|
|
2554
2693
|
options?.onQuery
|
|
2555
2694
|
);
|
|
2556
2695
|
}
|
|
2696
|
+
checkServerDriver("postgresql", options?.driver);
|
|
2557
2697
|
return new AsyncEngine(
|
|
2558
|
-
createPostgresDriver(parsed.raw, options
|
|
2698
|
+
createPostgresDriver(parsed.raw, options),
|
|
2559
2699
|
"postgresql",
|
|
2560
2700
|
options?.onQuery
|
|
2561
2701
|
);
|
|
@@ -2575,7 +2715,8 @@ function toMysqlResult(rows) {
|
|
|
2575
2715
|
const header = rows;
|
|
2576
2716
|
return { rows: [], changes: header.affectedRows ?? 0 };
|
|
2577
2717
|
}
|
|
2578
|
-
function createMysqlDriver(url,
|
|
2718
|
+
function createMysqlDriver(url, options) {
|
|
2719
|
+
const pool = options?.pool;
|
|
2579
2720
|
let poolHandle;
|
|
2580
2721
|
const ensure = async () => {
|
|
2581
2722
|
if (poolHandle) return;
|
|
@@ -2588,6 +2729,7 @@ function createMysqlDriver(url, pool) {
|
|
|
2588
2729
|
if (pool?.size !== void 0) opts.connectionLimit = pool.size;
|
|
2589
2730
|
if (pool?.idleTimeoutMs !== void 0) opts.idleTimeout = pool.idleTimeoutMs;
|
|
2590
2731
|
if (pool?.connectTimeoutMs !== void 0) opts.connectTimeout = pool.connectTimeoutMs;
|
|
2732
|
+
Object.assign(opts, options?.driverOptions ?? {});
|
|
2591
2733
|
poolHandle = mod.createPool(opts);
|
|
2592
2734
|
};
|
|
2593
2735
|
const runOn = async (queryable, sql2, params) => {
|
|
@@ -2621,7 +2763,8 @@ function toPostgresResult(rows) {
|
|
|
2621
2763
|
const arr = rows;
|
|
2622
2764
|
return { rows: Array.from(arr), changes: arr.count ?? arr.length };
|
|
2623
2765
|
}
|
|
2624
|
-
function createPostgresDriver(url,
|
|
2766
|
+
function createPostgresDriver(url, options) {
|
|
2767
|
+
const pool = options?.pool;
|
|
2625
2768
|
let client;
|
|
2626
2769
|
const ensure = async () => {
|
|
2627
2770
|
if (client) return;
|
|
@@ -2637,6 +2780,8 @@ function createPostgresDriver(url, pool) {
|
|
|
2637
2780
|
if (pool?.connectTimeoutMs !== void 0) {
|
|
2638
2781
|
opts.connect_timeout = Math.ceil(pool.connectTimeoutMs / 1e3);
|
|
2639
2782
|
}
|
|
2783
|
+
opts.onnotice = (notice) => emitNotice(options?.onNotice, notice);
|
|
2784
|
+
Object.assign(opts, options?.driverOptions ?? {});
|
|
2640
2785
|
client = (mod.default ?? mod)(url, opts);
|
|
2641
2786
|
};
|
|
2642
2787
|
return {
|
|
@@ -3022,6 +3167,6 @@ function dbColumn(names, prop) {
|
|
|
3022
3167
|
return names?.[prop] ?? prop;
|
|
3023
3168
|
}
|
|
3024
3169
|
|
|
3025
|
-
export { ActiveRecord, Agg, AsyncEngine, AsyncResult, AsyncSession, BaseDialect, BaseRepository, Column, DeleteBuilder, Expression, InsertBuilder, InvalidDatabaseUrl, JoinBuilder, Model, MysqlDialect, NoResultError, NodeSqliteDriver, OPERATORS, Params, PostgresDialect, QueryExecutionError, RecordNotFound, SelectBuilder, SqliteDialect, SyncEngine, SyncResult, SyncSession, UpdateBuilder, ValidationError, activeRecord, and, avg, belongsTo, col, column, columnNamesOf, columnPropsOf, columnsOf, count, createEngine, createSyncEngine, dbColumn, del, detectDialect, fn, foreignKey, fromDict, getDialect, hasMany, insert, isCondition, isExpression, isSqlExpression, isSubquery, join, loadRelations, max, min, not, or, parse, parseDatabaseUrl, renderPortableToken, select, sql, stringify, sum, toAsyncDriver, toCondNode, toDict, toJSON, toSnakeCase, unique, update, val };
|
|
3026
|
-
//# sourceMappingURL=chunk-
|
|
3027
|
-
//# sourceMappingURL=chunk-
|
|
3170
|
+
export { ActiveRecord, Agg, AsyncEngine, AsyncResult, AsyncSession, BaseDialect, BaseRepository, BetterSqliteDriver, Column, DeleteBuilder, Expression, InsertBuilder, InvalidDatabaseUrl, JoinBuilder, Model, MysqlDialect, NoResultError, NodeSqliteDriver, OPERATORS, Params, PostgresDialect, QueryExecutionError, RecordNotFound, SelectBuilder, SqliteDialect, SyncEngine, SyncResult, SyncSession, UpdateBuilder, ValidationError, activeRecord, and, avg, belongsTo, col, column, columnNamesOf, columnPropsOf, columnsOf, count, createEngine, createSyncEngine, dbColumn, del, detectDialect, fn, foreignKey, fromDict, getDialect, hasMany, insert, isCondition, isExpression, isSqlExpression, isSubquery, join, loadRelations, max, min, not, or, parse, parseDatabaseUrl, renderPortableToken, select, sql, stringify, sum, toAsyncDriver, toCondNode, toDict, toJSON, toSnakeCase, unique, update, val };
|
|
3171
|
+
//# sourceMappingURL=chunk-NH6K5LTX.js.map
|
|
3172
|
+
//# sourceMappingURL=chunk-NH6K5LTX.js.map
|