turbine-orm 0.90.0 → 0.90.1-next.708c40c
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 -4
- package/dist/cjs/client.js +6 -14
- package/dist/cjs/dialect.d.ts +15 -4
- package/dist/cjs/dialect.js +18 -0
- package/dist/cjs/introspect.js +1 -0
- package/dist/cjs/powdb.js +4 -38
- package/dist/cjs/powql.d.ts +3 -1
- package/dist/cjs/powql.js +21 -46
- package/dist/cjs/query/builder.js +7 -1
- package/dist/cjs/schema-sql.js +1 -0
- package/dist/cjs/transaction-client-slot.d.ts +56 -0
- package/dist/cjs/transaction-client-slot.js +27 -0
- package/dist/client.js +6 -14
- package/dist/dialect.d.ts +15 -4
- package/dist/dialect.js +17 -0
- package/dist/introspect.js +2 -1
- package/dist/powdb.js +1 -2
- package/dist/powql.d.ts +3 -1
- package/dist/powql.js +22 -14
- package/dist/query/builder.js +8 -2
- package/dist/schema-sql.js +2 -1
- package/dist/transaction-client-slot.d.ts +56 -0
- package/dist/transaction-client-slot.js +22 -0
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -272,7 +272,7 @@ Going deep on one database means the parts other ORMs push to raw SQL are typed
|
|
|
272
272
|
|
|
273
273
|
## Serverless and edge
|
|
274
274
|
|
|
275
|
-
The core is driver-agnostic: hand any pg-compatible pool to `turbineHttp()` and Turbine runs on Vercel Edge, Cloudflare Workers, Deno Deploy, or anywhere else without TCP. The main entry's import graph is held under **
|
|
275
|
+
The core is driver-agnostic: hand any pg-compatible pool to `turbineHttp()` and Turbine runs on Vercel Edge, Cloudflare Workers, Deno Deploy, or anywhere else without TCP. The main entry's import graph is held under **146 kB brotli** (edge entry under **114 kB**) with `pg` external, enforced by `size-limit` in CI at those exact numbers; run `npm run size` for the current figure.
|
|
276
276
|
|
|
277
277
|
```typescript
|
|
278
278
|
import { Pool } from '@neondatabase/serverless';
|
|
@@ -287,7 +287,7 @@ HTTP drivers cannot hold a cursor or a LISTEN connection, so `findManyStream` an
|
|
|
287
287
|
|
|
288
288
|
## Database engines
|
|
289
289
|
|
|
290
|
-
Postgres is the default and primary target. The same typed API also runs on **SQLite** (Node's built-in `node:sqlite`, zero extra installs, Node ≥ 22.5), **MySQL 8** (`mysql2`), **SQL Server 2016+** (`mssql`), and **PowDB** (embedded or networked), each behind a subpath export with its driver as an optional peer. All four are **Experimental** in the [stability policy](STABILITY.md): the query API they expose is the shared one, but each engine's capability boundaries and factory options may still change in a minor release.
|
|
290
|
+
Postgres is the default and primary target. The same typed API also runs on **SQLite** (Node's built-in `node:sqlite`, zero extra installs, Node ≥ 22.5), **MySQL 8** (`mysql2`), **SQL Server 2016+** (`mssql`), and **PowDB** (embedded or networked), each behind a subpath export with its driver as an optional peer. All four are **Experimental** in the [stability policy](STABILITY.md): the query API they expose is the shared one, but each engine's capability boundaries and factory options may still change in a minor release. PowDB is in maintenance mode: bug and data-safety fixes, no new features.
|
|
291
291
|
|
|
292
292
|
```bash
|
|
293
293
|
npm install turbine-orm # SQLite needs nothing else
|
|
@@ -359,11 +359,11 @@ It is also built to be extended rather than wrapped. All SQL generation routes t
|
|
|
359
359
|
|---|---|---|---|---|---|
|
|
360
360
|
| **Engine / runtime** | No engine binary (`pg` only) | Client + TS/WASM query compiler | No engine | No engine | Kysely as query runner; SQL built by MikroORM (v7) |
|
|
361
361
|
| **Runtime deps** | 1 (`pg`) | `@prisma/client` + required driver adapter | 0 | 0 | `@mikro-orm/core` (0 deps) + a driver package |
|
|
362
|
-
| **Main bundle (brotli)** | under
|
|
362
|
+
| **Main bundle (brotli)** | under 146 kB import graph (CI-enforced), `pg` external | ~1.6 MB client (TS/WASM compiler) | ~7 KB core | small | |
|
|
363
363
|
| **Studio** | Read-only by default | Full CRUD, cloud-hosted | Full CRUD; [Gateway](https://gateway.drizzle.team/) self-hosted, free | None | AdminJS integration guide (CRUD panel) |
|
|
364
364
|
| **Error PII safety** | Keys only by default | Values in messages | Raw pg errors | Raw pg errors | |
|
|
365
365
|
| **Migrations** | SQL-first, SHA-256 checksums | DSL-generated, shadow DB | SQL or Drizzle Kit | None | Generated by diffing entities against a snapshot or the database; TS/JS classes |
|
|
366
|
-
| **Edge runtime** | One import swap, under
|
|
366
|
+
| **Edge runtime** | One import swap, under 114 kB brotli (CI-enforced) | Driver adapter + WASM compiler | Native | Native | Cloudflare Workers and Deno (v7) |
|
|
367
367
|
| **Pipeline batching** | Parse/Bind/Execute protocol | Sequential in txn | Sequential | Manual | `flush()` runs queued changes in one transaction |
|
|
368
368
|
| **Typed errors** | `isRetryable` discriminant | Error codes only | None | None | `DriverException` subclasses (`UniqueConstraintViolationException`, ...) |
|
|
369
369
|
| **Nested relations** | 1 query, deep type inference | 1 query per relation by default; single-query `relationJoins` is Preview | 1 query, `relations()` re-declaration | Manual (`jsonArrayFrom`) | `populate`; default `balanced`: joins to-one, one query per to-many relation |
|
package/dist/cjs/client.js
CHANGED
|
@@ -50,6 +50,7 @@ const warn_registry_js_1 = require("./query/warn-registry.js");
|
|
|
50
50
|
const query_events_js_1 = require("./query-events.js");
|
|
51
51
|
const realtime_js_1 = require("./realtime.js");
|
|
52
52
|
const supplied_pool_types_js_1 = require("./supplied-pool-types.js");
|
|
53
|
+
const transaction_client_slot_js_1 = require("./transaction-client-slot.js");
|
|
53
54
|
const transaction_state_js_1 = require("./transaction-state.js");
|
|
54
55
|
const typed_sql_js_1 = require("./typed-sql.js");
|
|
55
56
|
async function withRetry(fn, options) {
|
|
@@ -563,19 +564,6 @@ function resolveNonNegativeInteger(value, name, unit) {
|
|
|
563
564
|
}
|
|
564
565
|
return value;
|
|
565
566
|
}
|
|
566
|
-
/**
|
|
567
|
-
* Refuse a dialect with no `capabilities` object (E003): every code path asks
|
|
568
|
-
* it what the engine can do, so a hand-built dialect without one would fail on
|
|
569
|
-
* its first query with a TypeError instead of here.
|
|
570
|
-
*/
|
|
571
|
-
function assertDialectCapabilities(dialect) {
|
|
572
|
-
const capabilities = dialect.capabilities;
|
|
573
|
-
if (typeof capabilities === 'object' && capabilities !== null)
|
|
574
|
-
return;
|
|
575
|
-
throw new errors_js_1.ValidationError(`The dialect "${String(dialect.name)}" has no \`capabilities\` object, which says what the engine can do. ` +
|
|
576
|
-
'Build it from an engine dialect (`{ ...postgresDialect, name, capabilities: { ... } }`) and state every ' +
|
|
577
|
-
'EngineCapabilities key.');
|
|
578
|
-
}
|
|
579
567
|
/**
|
|
580
568
|
* The statement that opens a transaction, read-only when asked. PostgreSQL and
|
|
581
569
|
* MySQL both accept `READ ONLY` after their own BEGIN form (`BEGIN [ISOLATION
|
|
@@ -1027,6 +1015,10 @@ class TransactionClient {
|
|
|
1027
1015
|
}
|
|
1028
1016
|
}
|
|
1029
1017
|
exports.TransactionClient = TransactionClient;
|
|
1018
|
+
// query/builder.ts opens an implicit transaction with this class but sits
|
|
1019
|
+
// below this module in the import graph, so the class is handed over here,
|
|
1020
|
+
// once, when this module is evaluated (see transaction-client-slot.ts).
|
|
1021
|
+
(0, transaction_client_slot_js_1.registerTransactionClient)(TransactionClient);
|
|
1030
1022
|
/**
|
|
1031
1023
|
* Give each multi-statement write on a transaction-scoped accessor its own
|
|
1032
1024
|
* savepoint: `create` / `update` carrying nested relation writes, and an
|
|
@@ -1251,7 +1243,7 @@ class TurbineClient {
|
|
|
1251
1243
|
// settling the process-global parser mode and then rejecting the config
|
|
1252
1244
|
// would poison the next, valid, TurbineClient with a phantom conflict.
|
|
1253
1245
|
const dialect = config.dialect ?? dialect_js_1.postgresDialect;
|
|
1254
|
-
assertDialectCapabilities(dialect);
|
|
1246
|
+
(0, dialect_js_1.assertDialectCapabilities)(dialect);
|
|
1255
1247
|
const planCacheMode = TurbineClient.resolvePlanCacheMode(config.planCacheMode, dialect);
|
|
1256
1248
|
const statementTimeoutMs = TurbineClient.resolveStatementTimeout(config.statementTimeoutMs, dialect);
|
|
1257
1249
|
const maxLifetimeSeconds = resolveNonNegativeInteger(config.maxLifetimeSeconds, 'maxLifetimeSeconds', 'seconds');
|
package/dist/cjs/dialect.d.ts
CHANGED
|
@@ -389,10 +389,11 @@ export interface RelationSubqueryContext {
|
|
|
389
389
|
* refuses an engine whose `capabilities` is inherited or shared.
|
|
390
390
|
*
|
|
391
391
|
* Code asks these questions instead of testing `dialect.name`; a source scan
|
|
392
|
-
* (src/test/capabilities-guard.test.ts) refuses a new engine-name
|
|
393
|
-
* outside a short, commented allowlist
|
|
394
|
-
* capabilities: they belong in the dialect's
|
|
395
|
-
* name, allowlisted, until engines stop
|
|
392
|
+
* (src/test/capabilities-guard.test.ts) refuses a new engine-name test, of any
|
|
393
|
+
* shape, outside a short, commented allowlist that pins each site it exempts.
|
|
394
|
+
* Rendering differences are not capabilities: they belong in the dialect's
|
|
395
|
+
* hooks (a few still switch on the name, allowlisted, until engines stop
|
|
396
|
+
* spreading `postgresDialect`).
|
|
396
397
|
*/
|
|
397
398
|
export interface EngineCapabilities {
|
|
398
399
|
/** `SELECT DISTINCT ON (...)`: findMany `distinct` and groupBy `distinctOn`. Else E017. */
|
|
@@ -586,6 +587,16 @@ export interface EngineCapabilities {
|
|
|
586
587
|
*/
|
|
587
588
|
readonly unboundedTextKeys: boolean;
|
|
588
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Refuse a dialect with no `capabilities` object (E003). Every path that builds
|
|
592
|
+
* SQL asks it what the engine can do, so a hand-built dialect written before
|
|
593
|
+
* the object existed would otherwise fail on first use with a TypeError
|
|
594
|
+
* ("cannot read properties of undefined"). Every public entry point that takes
|
|
595
|
+
* a dialect calls this (`TurbineClient`, `QueryInterface`, `schemaToSQL`,
|
|
596
|
+
* `introspect`), so the refusal and its words are the same whichever one a
|
|
597
|
+
* caller reaches first. One `typeof`: `QueryInterface` is built per table.
|
|
598
|
+
*/
|
|
599
|
+
export declare function assertDialectCapabilities(dialect: Dialect): void;
|
|
589
600
|
export interface Dialect {
|
|
590
601
|
/** Dialect identifier. */
|
|
591
602
|
readonly name: DialectName;
|
package/dist/cjs/dialect.js
CHANGED
|
@@ -8,11 +8,29 @@
|
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.postgresDialect = void 0;
|
|
11
|
+
exports.assertDialectCapabilities = assertDialectCapabilities;
|
|
11
12
|
exports.maxBindParameters = maxBindParameters;
|
|
12
13
|
exports.onConflictAction = onConflictAction;
|
|
13
14
|
const errors_js_1 = require("./errors.js");
|
|
14
15
|
const utils_js_1 = require("./query/utils.js");
|
|
15
16
|
const schema_js_1 = require("./schema.js");
|
|
17
|
+
/**
|
|
18
|
+
* Refuse a dialect with no `capabilities` object (E003). Every path that builds
|
|
19
|
+
* SQL asks it what the engine can do, so a hand-built dialect written before
|
|
20
|
+
* the object existed would otherwise fail on first use with a TypeError
|
|
21
|
+
* ("cannot read properties of undefined"). Every public entry point that takes
|
|
22
|
+
* a dialect calls this (`TurbineClient`, `QueryInterface`, `schemaToSQL`,
|
|
23
|
+
* `introspect`), so the refusal and its words are the same whichever one a
|
|
24
|
+
* caller reaches first. One `typeof`: `QueryInterface` is built per table.
|
|
25
|
+
*/
|
|
26
|
+
function assertDialectCapabilities(dialect) {
|
|
27
|
+
const capabilities = dialect.capabilities;
|
|
28
|
+
if (typeof capabilities === 'object' && capabilities !== null)
|
|
29
|
+
return;
|
|
30
|
+
throw new errors_js_1.ValidationError(`The dialect "${String(dialect.name)}" has no \`capabilities\` object, which says what the engine can do. ` +
|
|
31
|
+
'Build it from an engine dialect (`{ ...postgresDialect, name, capabilities: { ... } }`) and state every ' +
|
|
32
|
+
'EngineCapabilities key.');
|
|
33
|
+
}
|
|
16
34
|
/** The bind-parameter limit for one statement on this dialect, or `undefined` when it has none. */
|
|
17
35
|
function maxBindParameters(dialect) {
|
|
18
36
|
const limit = dialect.capabilities.maxBindParameters;
|
package/dist/cjs/introspect.js
CHANGED
|
@@ -485,6 +485,7 @@ function assertSafeCatalogSchema(schema) {
|
|
|
485
485
|
*/
|
|
486
486
|
async function introspect(options) {
|
|
487
487
|
const dialect = options.dialect ?? dialect_js_1.postgresDialect;
|
|
488
|
+
(0, dialect_js_1.assertDialectCapabilities)(dialect);
|
|
488
489
|
const introspector = dialect.introspector;
|
|
489
490
|
const schema = introspector
|
|
490
491
|
? await introspector.introspect(options)
|
package/dist/cjs/powdb.js
CHANGED
|
@@ -53,39 +53,6 @@
|
|
|
53
53
|
*
|
|
54
54
|
* @module
|
|
55
55
|
*/
|
|
56
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
57
|
-
if (k2 === undefined) k2 = k;
|
|
58
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
59
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
60
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
61
|
-
}
|
|
62
|
-
Object.defineProperty(o, k2, desc);
|
|
63
|
-
}) : (function(o, m, k, k2) {
|
|
64
|
-
if (k2 === undefined) k2 = k;
|
|
65
|
-
o[k2] = m[k];
|
|
66
|
-
}));
|
|
67
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
68
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
69
|
-
}) : function(o, v) {
|
|
70
|
-
o["default"] = v;
|
|
71
|
-
});
|
|
72
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
73
|
-
var ownKeys = function(o) {
|
|
74
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
75
|
-
var ar = [];
|
|
76
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
77
|
-
return ar;
|
|
78
|
-
};
|
|
79
|
-
return ownKeys(o);
|
|
80
|
-
};
|
|
81
|
-
return function (mod) {
|
|
82
|
-
if (mod && mod.__esModule) return mod;
|
|
83
|
-
var result = {};
|
|
84
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
85
|
-
__setModuleDefault(result, mod);
|
|
86
|
-
return result;
|
|
87
|
-
};
|
|
88
|
-
})();
|
|
89
56
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
90
57
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
91
58
|
};
|
|
@@ -109,6 +76,7 @@ const errors_js_1 = require("./errors.js");
|
|
|
109
76
|
const logger_js_1 = require("./logger.js");
|
|
110
77
|
const optional_peer_import_cjs_1 = __importDefault(require("./optional-peer-import.cjs"));
|
|
111
78
|
const powdb_shared_js_1 = require("./powdb-shared.js");
|
|
79
|
+
const powql_js_1 = require("./powql.js");
|
|
112
80
|
const warn_registry_js_1 = require("./query/warn-registry.js");
|
|
113
81
|
const schema_js_1 = require("./schema.js");
|
|
114
82
|
// The shared PowDB primitives live in a leaf module (see powdb-shared.ts): this
|
|
@@ -1861,8 +1829,8 @@ exports.PowdbEmbeddedPool = PowdbEmbeddedPool;
|
|
|
1861
1829
|
// `describe`-based introspection (programmatic API; see powdb-introspect.ts).
|
|
1862
1830
|
var powdb_introspect_js_1 = require("./powdb-introspect.js");
|
|
1863
1831
|
Object.defineProperty(exports, "introspectPowdbDatabase", { enumerable: true, get: function () { return powdb_introspect_js_1.introspectPowdbDatabase; } });
|
|
1864
|
-
var
|
|
1865
|
-
Object.defineProperty(exports, "PowqlInterface", { enumerable: true, get: function () { return
|
|
1832
|
+
var powql_js_2 = require("./powql.js");
|
|
1833
|
+
Object.defineProperty(exports, "PowqlInterface", { enumerable: true, get: function () { return powql_js_2.PowqlInterface; } });
|
|
1866
1834
|
/**
|
|
1867
1835
|
* Dynamically load `@zvndev/powdb-client`. Kept out of the static import graph so
|
|
1868
1836
|
* `import 'turbine-orm/powdb'` never throws when the optional peer is absent.
|
|
@@ -2064,9 +2032,7 @@ async function turbinePowDB(target, schema, options = {}) {
|
|
|
2064
2032
|
}
|
|
2065
2033
|
let client;
|
|
2066
2034
|
try {
|
|
2067
|
-
|
|
2068
|
-
const { PowqlInterface } = await Promise.resolve().then(() => __importStar(require('./powql.js')));
|
|
2069
|
-
const queryInterfaceFactory = (p, table, sch, middlewares, opts) => new PowqlInterface(p, table, sch, middlewares, opts);
|
|
2035
|
+
const queryInterfaceFactory = (p, table, sch, middlewares, opts) => new powql_js_1.PowqlInterface(p, table, sch, middlewares, opts);
|
|
2070
2036
|
// Forward every client-level option EXCEPT the ones this factory owns (they
|
|
2071
2037
|
// configure the pool / transport above, and TurbineClient warns on a config
|
|
2072
2038
|
// key it does not recognize). Destructured rather than hand-listed on the way
|
package/dist/cjs/powql.d.ts
CHANGED
|
@@ -486,7 +486,9 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
486
486
|
* `gt` / `gte` / `lt` / `lte` compile to `in` over the labels on that side of
|
|
487
487
|
* the operand in DECLARATION order, which is what PostgreSQL compares; the
|
|
488
488
|
* text comparison PowDB would run is alphabetical, a different answer. A null
|
|
489
|
-
* operand
|
|
489
|
+
* operand is refused before this (assertOperatorOperands, as on the SQL
|
|
490
|
+
* engines) and again by {@link assertEnumLabel}: compiled to a constant false,
|
|
491
|
+
* `NOT` would turn it into every row. Case-insensitive matching and
|
|
490
492
|
* `contains` / `startsWith` / `endsWith` are E003: PostgreSQL has no `lower`
|
|
491
493
|
* or `LIKE` on an enum (42883).
|
|
492
494
|
*/
|
package/dist/cjs/powql.js
CHANGED
|
@@ -35,39 +35,6 @@
|
|
|
35
35
|
*
|
|
36
36
|
* @module
|
|
37
37
|
*/
|
|
38
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
39
|
-
if (k2 === undefined) k2 = k;
|
|
40
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
41
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
42
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
43
|
-
}
|
|
44
|
-
Object.defineProperty(o, k2, desc);
|
|
45
|
-
}) : (function(o, m, k, k2) {
|
|
46
|
-
if (k2 === undefined) k2 = k;
|
|
47
|
-
o[k2] = m[k];
|
|
48
|
-
}));
|
|
49
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
50
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
51
|
-
}) : function(o, v) {
|
|
52
|
-
o["default"] = v;
|
|
53
|
-
});
|
|
54
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
55
|
-
var ownKeys = function(o) {
|
|
56
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
57
|
-
var ar = [];
|
|
58
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
59
|
-
return ar;
|
|
60
|
-
};
|
|
61
|
-
return ownKeys(o);
|
|
62
|
-
};
|
|
63
|
-
return function (mod) {
|
|
64
|
-
if (mod && mod.__esModule) return mod;
|
|
65
|
-
var result = {};
|
|
66
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
67
|
-
__setModuleDefault(result, mod);
|
|
68
|
-
return result;
|
|
69
|
-
};
|
|
70
|
-
})();
|
|
71
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
72
39
|
exports.PowqlInterface = exports.MAX_RELATION_FILTER_KEYS = exports.MAX_POWQL_DATETIME_TERMS = void 0;
|
|
73
40
|
const node_crypto_1 = require("node:crypto");
|
|
@@ -99,6 +66,7 @@ const where_js_1 = require("./query/where.js");
|
|
|
99
66
|
const where_compile_js_1 = require("./query/where-compile.js");
|
|
100
67
|
const writes_js_1 = require("./query/writes.js");
|
|
101
68
|
const schema_js_1 = require("./schema.js");
|
|
69
|
+
const transaction_client_slot_js_1 = require("./transaction-client-slot.js");
|
|
102
70
|
/**
|
|
103
71
|
* Max parent keys per relation-loader `in (…)` query. A `with` over a large
|
|
104
72
|
* parent set is split into batches of this size so a single query never exceeds
|
|
@@ -1083,17 +1051,15 @@ class PowqlInterface {
|
|
|
1083
1051
|
// The SQL engines' operand rules and messages, so the engines agree on
|
|
1084
1052
|
// which operator objects are valid; one naming no comparison
|
|
1085
1053
|
// (`{ mode: 'insensitive' }`) is refused instead of compiling to nothing.
|
|
1086
|
-
|
|
1054
|
+
// JSON columns included, as on the SQL engines: skipping them let a
|
|
1055
|
+
// mode-only filter fall through to the constant-false fallback below, which
|
|
1056
|
+
// `NOT` turned into every non-null row (a deleteMany wrote all of them).
|
|
1057
|
+
// The same call refuses a JSON `not` object made only of filter operators.
|
|
1058
|
+
(0, filters_js_1.assertOperatorOperands)(op, {
|
|
1087
1059
|
column: colMeta.name,
|
|
1088
1060
|
table: this.table,
|
|
1089
1061
|
kind: (0, filters_js_1.operandColumnKind)(this.meta.pgTypes[colMeta.name]),
|
|
1090
|
-
};
|
|
1091
|
-
if (!(0, powdb_shared_js_1.isJsonColumn)(colMeta, this.schema.enums))
|
|
1092
|
-
(0, filters_js_1.assertOperatorOperands)(op, operand);
|
|
1093
|
-
// On a JSON column a `not` object made only of filter operators is refused,
|
|
1094
|
-
// as on the SQL engines (one spelling, one meaning on every engine).
|
|
1095
|
-
else
|
|
1096
|
-
(0, filters_js_1.assertJsonNotOperand)(op.not, operand);
|
|
1062
|
+
});
|
|
1097
1063
|
const insensitive = op.mode === 'insensitive';
|
|
1098
1064
|
const lhs = insensitive ? `lower(${ref})` : ref;
|
|
1099
1065
|
const labels = this.enumLabels(colMeta);
|
|
@@ -1179,7 +1145,14 @@ class PowqlInterface {
|
|
|
1179
1145
|
throw new errors_js_1.ValidationError(`Unsupported operator "${opName}" on PowDB.`);
|
|
1180
1146
|
}
|
|
1181
1147
|
}
|
|
1182
|
-
|
|
1148
|
+
// assertOperatorOperands refuses an operator object that names no
|
|
1149
|
+
// comparison, and every comparison above pushes a condition or throws, so
|
|
1150
|
+
// an empty list is a generator bug. It must not become a constant: under
|
|
1151
|
+
// `NOT` a constant false is every row, and a mutation would write them.
|
|
1152
|
+
if (!conds.length) {
|
|
1153
|
+
throw new errors_js_1.InvariantError('powql-filter-empty', `the filter on "${field}" compiled to no condition.`);
|
|
1154
|
+
}
|
|
1155
|
+
return conds.length > 1 ? `(${conds.join(' and ')})` : conds[0];
|
|
1183
1156
|
}
|
|
1184
1157
|
/**
|
|
1185
1158
|
* PowQL JSON path expression `.col->$a->$b…`, binding EVERY path segment as a
|
|
@@ -1354,7 +1327,9 @@ class PowqlInterface {
|
|
|
1354
1327
|
* `gt` / `gte` / `lt` / `lte` compile to `in` over the labels on that side of
|
|
1355
1328
|
* the operand in DECLARATION order, which is what PostgreSQL compares; the
|
|
1356
1329
|
* text comparison PowDB would run is alphabetical, a different answer. A null
|
|
1357
|
-
* operand
|
|
1330
|
+
* operand is refused before this (assertOperatorOperands, as on the SQL
|
|
1331
|
+
* engines) and again by {@link assertEnumLabel}: compiled to a constant false,
|
|
1332
|
+
* `NOT` would turn it into every row. Case-insensitive matching and
|
|
1358
1333
|
* `contains` / `startsWith` / `endsWith` are E003: PostgreSQL has no `lower`
|
|
1359
1334
|
* or `LIKE` on an enum (42883).
|
|
1360
1335
|
*/
|
|
@@ -1365,8 +1340,6 @@ class PowqlInterface {
|
|
|
1365
1340
|
}
|
|
1366
1341
|
if (opName !== 'gt' && opName !== 'gte' && opName !== 'lt' && opName !== 'lte')
|
|
1367
1342
|
return undefined;
|
|
1368
|
-
if (opVal === null)
|
|
1369
|
-
return this.alwaysFalse();
|
|
1370
1343
|
this.assertEnumLabel(col, labels, opVal);
|
|
1371
1344
|
const at = labels.indexOf(opVal);
|
|
1372
1345
|
const side = labels.filter((_, i) => opName === 'gt' ? i > at : opName === 'gte' ? i >= at : opName === 'lt' ? i < at : i <= at);
|
|
@@ -4102,6 +4075,9 @@ class PowqlInterface {
|
|
|
4102
4075
|
// Route tx keywords through the dialect (like the SQL path) so this never
|
|
4103
4076
|
// drifts from `powdbDialect`; falls back to the literal lowercase keywords.
|
|
4104
4077
|
const d = this.options.dialect;
|
|
4078
|
+
// Read before the checkout, so an empty slot (client.ts never loaded in this
|
|
4079
|
+
// copy of the package) refuses with no connection taken and no `begin` sent.
|
|
4080
|
+
const TransactionClient = (0, transaction_client_slot_js_1.transactionClientFor)(`A nested write or lookup-first upsert on "${this.table}"`);
|
|
4105
4081
|
const client = await this.pool.connect();
|
|
4106
4082
|
// A no-op today (PowDB checkouts have no event surface), kept so this path
|
|
4107
4083
|
// cannot become the one unguarded checkout if a transport ever emits 'error'.
|
|
@@ -4110,7 +4086,6 @@ class PowqlInterface {
|
|
|
4110
4086
|
try {
|
|
4111
4087
|
await client.query(d?.beginStatement?.() ?? 'begin');
|
|
4112
4088
|
began = true;
|
|
4113
|
-
const { TransactionClient } = await Promise.resolve().then(() => __importStar(require('./client.js')));
|
|
4114
4089
|
const tx = new TransactionClient(client, this.schema, this.middlewares, this.options,
|
|
4115
4090
|
// Pass the PowDB pool so its read-only guard + capabilities carry into
|
|
4116
4091
|
// the transaction-scoped proxy pool (see createTxPool).
|
|
@@ -56,6 +56,7 @@ const index_advisor_js_1 = require("../index-advisor.js");
|
|
|
56
56
|
const logger_js_1 = require("../logger.js");
|
|
57
57
|
const nested_write_js_1 = require("../nested-write.js");
|
|
58
58
|
const schema_js_1 = require("../schema.js");
|
|
59
|
+
const transaction_client_slot_js_1 = require("../transaction-client-slot.js");
|
|
59
60
|
const transaction_state_js_1 = require("../transaction-state.js");
|
|
60
61
|
const aggMod = __importStar(require("./aggregates.js"));
|
|
61
62
|
const batched_loader_js_1 = require("./batched-loader.js");
|
|
@@ -833,6 +834,7 @@ class QueryInterface {
|
|
|
833
834
|
this.sqlCacheEnabled = options?.sqlCache !== false && sqlCacheSize !== 0;
|
|
834
835
|
this.sqlTemplateCache = new utils_js_1.LRUCache(sqlCacheSize !== undefined && sqlCacheSize > 0 ? Math.floor(sqlCacheSize) : 1000);
|
|
835
836
|
this.dialect = options?.dialect ?? dialect_js_1.postgresDialect;
|
|
837
|
+
(0, dialect_js_1.assertDialectCapabilities)(this.dialect);
|
|
836
838
|
this.sqlCommenter =
|
|
837
839
|
options?._sqlCommenter ??
|
|
838
840
|
(0, sql_comment_js_1.resolveSqlComment)(options?.sqlComment, this.logger, {
|
|
@@ -4447,6 +4449,11 @@ class QueryInterface {
|
|
|
4447
4449
|
return (0, checkout_js_1.acquireConnection)(this.pool);
|
|
4448
4450
|
}
|
|
4449
4451
|
async runInImplicitTx(fn) {
|
|
4452
|
+
// client.ts registers TransactionClient when it is evaluated (it cannot be
|
|
4453
|
+
// imported from here, see transaction-client-slot.ts). Read before the
|
|
4454
|
+
// checkout, so a program that never loaded client.ts gets a typed error
|
|
4455
|
+
// without a connection having been touched.
|
|
4456
|
+
const TransactionClient = (0, transaction_client_slot_js_1.transactionClientFor)(`A nested write or lookup-first upsert on "${this.table}"`);
|
|
4450
4457
|
// BEGIN runs inside openCheckout, which sends it once more on a fresh
|
|
4451
4458
|
// connection when the first one turns out to be dead and releases the
|
|
4452
4459
|
// connection itself when BEGIN fails for good. The catch below therefore
|
|
@@ -4454,7 +4461,6 @@ class QueryInterface {
|
|
|
4454
4461
|
// a connection that opened none.
|
|
4455
4462
|
const { client, checkout } = await (0, checkout_js_1.openCheckout)(this.pool, (c) => c.query(this.dialect.beginStatement()));
|
|
4456
4463
|
try {
|
|
4457
|
-
const { TransactionClient } = await Promise.resolve().then(() => __importStar(require('../client.js')));
|
|
4458
4464
|
const tx = new TransactionClient(
|
|
4459
4465
|
// biome-ignore lint/suspicious/noExplicitAny: MiddlewareFn and Middleware are structurally identical
|
|
4460
4466
|
client, this.schema,
|
package/dist/cjs/schema-sql.js
CHANGED
|
@@ -113,6 +113,7 @@ function resolveDdlType(config, dialect, columnName) {
|
|
|
113
113
|
*/
|
|
114
114
|
function schemaToSQL(schema, options) {
|
|
115
115
|
const dialect = options?.dialect ?? dialect_js_1.postgresDialect;
|
|
116
|
+
(0, dialect_js_1.assertDialectCapabilities)(dialect);
|
|
116
117
|
const extensions = options?.extensions ?? 'auto';
|
|
117
118
|
const statements = [];
|
|
118
119
|
// Topologically sort tables by their foreign key references
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the code that opens an implicit transaction finds `TransactionClient`.
|
|
3
|
+
*
|
|
4
|
+
* A nested write (or an upsert that looks its row up first) issued outside a
|
|
5
|
+
* transaction opens one of its own and runs the nested-write engine against a
|
|
6
|
+
* `TransactionClient` (query/builder.ts; powql.ts, the PowDB generator, does
|
|
7
|
+
* the same and can read this slot too). Those modules sit BELOW client.ts in
|
|
8
|
+
* the import graph: client.ts imports query/, so a static value import back
|
|
9
|
+
* would be a cycle, and scripts/check-import-cycles.mjs refuses one.
|
|
10
|
+
*
|
|
11
|
+
* builder.ts used to reach the class with a dynamic import of client.ts.
|
|
12
|
+
* That works, but a bundler that does not split code (esbuild, which is also
|
|
13
|
+
* what the size budget in .size-limit.js measures, and Wrangler) inlines the
|
|
14
|
+
* dynamic target and, to keep its evaluation lazy, wraps client.ts and every
|
|
15
|
+
* module it reaches in lazy-init closures: about 1 kB brotli on every entry,
|
|
16
|
+
* for a module that is always already loaded by the time a query runs.
|
|
17
|
+
* client.ts instead registers the class here once, when it is evaluated, and
|
|
18
|
+
* every public entry point evaluates client.ts.
|
|
19
|
+
*
|
|
20
|
+
* ## One slot per package copy, deliberately not `Symbol.for`
|
|
21
|
+
*
|
|
22
|
+
* The ESM and CJS builds can both be loaded in one process. Shared state that
|
|
23
|
+
* must agree across them (the warn-once registry, the prepared-statement
|
|
24
|
+
* budget) lives under a `Symbol.for` key; this slot must NOT. A builder has to
|
|
25
|
+
* construct the `TransactionClient` of its OWN copy, exactly as the dynamic
|
|
26
|
+
* import did: the other copy's class builds the other copy's accessors and
|
|
27
|
+
* transaction state and throws the other copy's error classes, so `instanceof`
|
|
28
|
+
* checks in the caller's copy would stop matching. Each copy's entry points
|
|
29
|
+
* load that copy's client.ts, so each copy's slot is filled.
|
|
30
|
+
*
|
|
31
|
+
* ## An empty slot
|
|
32
|
+
*
|
|
33
|
+
* Reachable only when a `QueryInterface` is built directly and client.ts was
|
|
34
|
+
* never loaded in that copy: a test or tool that deep-imports query/, or a
|
|
35
|
+
* bundled application that uses the exported `QueryInterface` without ever
|
|
36
|
+
* importing `TurbineClient` or an engine factory (with `sideEffects: false` a
|
|
37
|
+
* bundler may then drop client.ts). Only the implicit-transaction path reads
|
|
38
|
+
* the slot, so everything else keeps working; that path throws a
|
|
39
|
+
* `ValidationError` (E003) naming the fix, before any connection is checked out.
|
|
40
|
+
*
|
|
41
|
+
* The import of client.ts below is type-only, so this file adds no edge to the
|
|
42
|
+
* runtime graph. errors.ts is a leaf.
|
|
43
|
+
*/
|
|
44
|
+
import type { TransactionClient } from './client.js';
|
|
45
|
+
/** The `TransactionClient` constructor, as client.ts registers it. */
|
|
46
|
+
export type TransactionClientClass = typeof TransactionClient;
|
|
47
|
+
/** @internal Called once, by client.ts, when it is evaluated. */
|
|
48
|
+
export declare function registerTransactionClient(ctor: TransactionClientClass): void;
|
|
49
|
+
/** @internal The registered class, or `undefined` when client.ts was never loaded in this copy. */
|
|
50
|
+
export declare function registeredTransactionClient(): TransactionClientClass | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* @internal The registered class for an implicit transaction. Throws a
|
|
53
|
+
* `ValidationError` when client.ts was never loaded in this copy of the package.
|
|
54
|
+
* `operation` starts the message, for example `A nested write on "users"`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function transactionClientFor(operation: string): TransactionClientClass;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerTransactionClient = registerTransactionClient;
|
|
4
|
+
exports.registeredTransactionClient = registeredTransactionClient;
|
|
5
|
+
exports.transactionClientFor = transactionClientFor;
|
|
6
|
+
const errors_js_1 = require("./errors.js");
|
|
7
|
+
let registered;
|
|
8
|
+
/** @internal Called once, by client.ts, when it is evaluated. */
|
|
9
|
+
function registerTransactionClient(ctor) {
|
|
10
|
+
registered = ctor;
|
|
11
|
+
}
|
|
12
|
+
/** @internal The registered class, or `undefined` when client.ts was never loaded in this copy. */
|
|
13
|
+
function registeredTransactionClient() {
|
|
14
|
+
return registered;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* @internal The registered class for an implicit transaction. Throws a
|
|
18
|
+
* `ValidationError` when client.ts was never loaded in this copy of the package.
|
|
19
|
+
* `operation` starts the message, for example `A nested write on "users"`.
|
|
20
|
+
*/
|
|
21
|
+
function transactionClientFor(operation) {
|
|
22
|
+
if (registered !== undefined)
|
|
23
|
+
return registered;
|
|
24
|
+
throw new errors_js_1.ValidationError(`${operation} outside a transaction opens one of its own through TurbineClient, ` +
|
|
25
|
+
'and TurbineClient was never loaded in this program. Import TurbineClient (or an engine factory such as ' +
|
|
26
|
+
"turbineHttp) from 'turbine-orm' before running it, or run the write inside db.$transaction().");
|
|
27
|
+
}
|
package/dist/client.js
CHANGED
|
@@ -26,7 +26,7 @@ import { openCheckout, typeCheckoutTimeouts } from './checkout.js';
|
|
|
26
26
|
import { absorbCheckedOutErrors, settleLongIdleCheckouts, watchCheckouts, } from './connection-guard.js';
|
|
27
27
|
import { detectPoolerTarget, fillTargetFromEnvironment, parseConnectionTarget, statementTimeoutSetting, withStartupSetting, } from './connection-url.js';
|
|
28
28
|
import { resolveDiagnostics } from './diagnostics.js';
|
|
29
|
-
import { postgresDialect } from './dialect.js';
|
|
29
|
+
import { assertDialectCapabilities, postgresDialect } from './dialect.js';
|
|
30
30
|
import { ConnectionError, errorMessageModesDiverged, explainConnectionLoss, PipelineError, registerClientErrorMessageMode, runWithErrorMessageMode, setErrorMessageMode, TimeoutError, UnsupportedFeatureError, ValidationError, wrapPgError, } from './errors.js';
|
|
31
31
|
import { normalizeGlobalFilters, tableAccessorName } from './global-filters-config.js';
|
|
32
32
|
import { CLIENT_LOG_CONTEXT, logTo, resolveLogger } from './logger.js';
|
|
@@ -43,6 +43,7 @@ import { shouldWarnOnce, WARN_NS } from './query/warn-registry.js';
|
|
|
43
43
|
import { currentQueryTag, deferredIdentity, emitStatementEvent, rawIdentity, resultRowCount, runReportedStatement, runWithQueryTag, } from './query-events.js';
|
|
44
44
|
import { createSubscription, validateChannel, } from './realtime.js';
|
|
45
45
|
import { parseInt8, rawClientOf, typedQueryPool } from './supplied-pool-types.js';
|
|
46
|
+
import { registerTransactionClient } from './transaction-client-slot.js';
|
|
46
47
|
import { commitTransaction, rolledBackError, TRANSACTION_STATE, TransactionState, unawaitedWorkError, } from './transaction-state.js';
|
|
47
48
|
import { buildTypedSql, TypedSqlQuery, utcDateParams } from './typed-sql.js';
|
|
48
49
|
export async function withRetry(fn, options) {
|
|
@@ -556,19 +557,6 @@ function resolveNonNegativeInteger(value, name, unit) {
|
|
|
556
557
|
}
|
|
557
558
|
return value;
|
|
558
559
|
}
|
|
559
|
-
/**
|
|
560
|
-
* Refuse a dialect with no `capabilities` object (E003): every code path asks
|
|
561
|
-
* it what the engine can do, so a hand-built dialect without one would fail on
|
|
562
|
-
* its first query with a TypeError instead of here.
|
|
563
|
-
*/
|
|
564
|
-
function assertDialectCapabilities(dialect) {
|
|
565
|
-
const capabilities = dialect.capabilities;
|
|
566
|
-
if (typeof capabilities === 'object' && capabilities !== null)
|
|
567
|
-
return;
|
|
568
|
-
throw new ValidationError(`The dialect "${String(dialect.name)}" has no \`capabilities\` object, which says what the engine can do. ` +
|
|
569
|
-
'Build it from an engine dialect (`{ ...postgresDialect, name, capabilities: { ... } }`) and state every ' +
|
|
570
|
-
'EngineCapabilities key.');
|
|
571
|
-
}
|
|
572
560
|
/**
|
|
573
561
|
* The statement that opens a transaction, read-only when asked. PostgreSQL and
|
|
574
562
|
* MySQL both accept `READ ONLY` after their own BEGIN form (`BEGIN [ISOLATION
|
|
@@ -1019,6 +1007,10 @@ export class TransactionClient {
|
|
|
1019
1007
|
return txPool;
|
|
1020
1008
|
}
|
|
1021
1009
|
}
|
|
1010
|
+
// query/builder.ts opens an implicit transaction with this class but sits
|
|
1011
|
+
// below this module in the import graph, so the class is handed over here,
|
|
1012
|
+
// once, when this module is evaluated (see transaction-client-slot.ts).
|
|
1013
|
+
registerTransactionClient(TransactionClient);
|
|
1022
1014
|
/**
|
|
1023
1015
|
* Give each multi-statement write on a transaction-scoped accessor its own
|
|
1024
1016
|
* savepoint: `create` / `update` carrying nested relation writes, and an
|
package/dist/dialect.d.ts
CHANGED
|
@@ -389,10 +389,11 @@ export interface RelationSubqueryContext {
|
|
|
389
389
|
* refuses an engine whose `capabilities` is inherited or shared.
|
|
390
390
|
*
|
|
391
391
|
* Code asks these questions instead of testing `dialect.name`; a source scan
|
|
392
|
-
* (src/test/capabilities-guard.test.ts) refuses a new engine-name
|
|
393
|
-
* outside a short, commented allowlist
|
|
394
|
-
* capabilities: they belong in the dialect's
|
|
395
|
-
* name, allowlisted, until engines stop
|
|
392
|
+
* (src/test/capabilities-guard.test.ts) refuses a new engine-name test, of any
|
|
393
|
+
* shape, outside a short, commented allowlist that pins each site it exempts.
|
|
394
|
+
* Rendering differences are not capabilities: they belong in the dialect's
|
|
395
|
+
* hooks (a few still switch on the name, allowlisted, until engines stop
|
|
396
|
+
* spreading `postgresDialect`).
|
|
396
397
|
*/
|
|
397
398
|
export interface EngineCapabilities {
|
|
398
399
|
/** `SELECT DISTINCT ON (...)`: findMany `distinct` and groupBy `distinctOn`. Else E017. */
|
|
@@ -586,6 +587,16 @@ export interface EngineCapabilities {
|
|
|
586
587
|
*/
|
|
587
588
|
readonly unboundedTextKeys: boolean;
|
|
588
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Refuse a dialect with no `capabilities` object (E003). Every path that builds
|
|
592
|
+
* SQL asks it what the engine can do, so a hand-built dialect written before
|
|
593
|
+
* the object existed would otherwise fail on first use with a TypeError
|
|
594
|
+
* ("cannot read properties of undefined"). Every public entry point that takes
|
|
595
|
+
* a dialect calls this (`TurbineClient`, `QueryInterface`, `schemaToSQL`,
|
|
596
|
+
* `introspect`), so the refusal and its words are the same whichever one a
|
|
597
|
+
* caller reaches first. One `typeof`: `QueryInterface` is built per table.
|
|
598
|
+
*/
|
|
599
|
+
export declare function assertDialectCapabilities(dialect: Dialect): void;
|
|
589
600
|
export interface Dialect {
|
|
590
601
|
/** Dialect identifier. */
|
|
591
602
|
readonly name: DialectName;
|
package/dist/dialect.js
CHANGED
|
@@ -8,6 +8,23 @@
|
|
|
8
8
|
import { ValidationError } from './errors.js';
|
|
9
9
|
import { coerceJsonWireValue, jsonWireCoercionOid } from './query/utils.js';
|
|
10
10
|
import { pgArrayType, pgTypeToTs } from './schema.js';
|
|
11
|
+
/**
|
|
12
|
+
* Refuse a dialect with no `capabilities` object (E003). Every path that builds
|
|
13
|
+
* SQL asks it what the engine can do, so a hand-built dialect written before
|
|
14
|
+
* the object existed would otherwise fail on first use with a TypeError
|
|
15
|
+
* ("cannot read properties of undefined"). Every public entry point that takes
|
|
16
|
+
* a dialect calls this (`TurbineClient`, `QueryInterface`, `schemaToSQL`,
|
|
17
|
+
* `introspect`), so the refusal and its words are the same whichever one a
|
|
18
|
+
* caller reaches first. One `typeof`: `QueryInterface` is built per table.
|
|
19
|
+
*/
|
|
20
|
+
export function assertDialectCapabilities(dialect) {
|
|
21
|
+
const capabilities = dialect.capabilities;
|
|
22
|
+
if (typeof capabilities === 'object' && capabilities !== null)
|
|
23
|
+
return;
|
|
24
|
+
throw new ValidationError(`The dialect "${String(dialect.name)}" has no \`capabilities\` object, which says what the engine can do. ` +
|
|
25
|
+
'Build it from an engine dialect (`{ ...postgresDialect, name, capabilities: { ... } }`) and state every ' +
|
|
26
|
+
'EngineCapabilities key.');
|
|
27
|
+
}
|
|
11
28
|
/** The bind-parameter limit for one statement on this dialect, or `undefined` when it has none. */
|
|
12
29
|
export function maxBindParameters(dialect) {
|
|
13
30
|
const limit = dialect.capabilities.maxBindParameters;
|
package/dist/introspect.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* This is the foundation of `npx turbine generate`.
|
|
9
9
|
*/
|
|
10
10
|
import pg from '#pg';
|
|
11
|
-
import { postgresDialect } from './dialect.js';
|
|
11
|
+
import { assertDialectCapabilities, postgresDialect } from './dialect.js';
|
|
12
12
|
import { ValidationError } from './errors.js';
|
|
13
13
|
import { consoleLogger, logTo } from './logger.js';
|
|
14
14
|
import { isDateType, pgTypeToTs, singularize, snakeToCamel, } from './schema.js';
|
|
@@ -455,6 +455,7 @@ export function assertSafeCatalogSchema(schema) {
|
|
|
455
455
|
*/
|
|
456
456
|
export async function introspect(options) {
|
|
457
457
|
const dialect = options.dialect ?? postgresDialect;
|
|
458
|
+
assertDialectCapabilities(dialect);
|
|
458
459
|
const introspector = dialect.introspector;
|
|
459
460
|
const schema = introspector
|
|
460
461
|
? await introspector.introspect(options)
|
package/dist/powdb.js
CHANGED
|
@@ -59,6 +59,7 @@ import { ConnectionError, malformedConnectionStringMessage, NotNullViolationErro
|
|
|
59
59
|
import { consoleLogger, logTo } from './logger.js';
|
|
60
60
|
import importOptionalPeer from './optional-peer-import.cjs';
|
|
61
61
|
import { ALL_POWDB_CAPABILITIES, atLeastVersion, isDateColumn, PowdbFloatParam, PowdbJsonParam, parsePowdbSemver, powqlColumnType, quotePowqlIdent, requireCapability, } from './powdb-shared.js';
|
|
62
|
+
import { PowqlInterface } from './powql.js';
|
|
62
63
|
import { shouldWarnOnce, WARN_NS } from './query/warn-registry.js';
|
|
63
64
|
import { normalizeKeyColumns } from './schema.js';
|
|
64
65
|
// The shared PowDB primitives live in a leaf module (see powdb-shared.ts): this
|
|
@@ -1996,8 +1997,6 @@ export async function turbinePowDB(target, schema, options = {}) {
|
|
|
1996
1997
|
}
|
|
1997
1998
|
let client;
|
|
1998
1999
|
try {
|
|
1999
|
-
// The PowQL generator is loaded here to keep client.ts free of any PowDB import.
|
|
2000
|
-
const { PowqlInterface } = await import('./powql.js');
|
|
2001
2000
|
const queryInterfaceFactory = (p, table, sch, middlewares, opts) => new PowqlInterface(p, table, sch, middlewares, opts);
|
|
2002
2001
|
// Forward every client-level option EXCEPT the ones this factory owns (they
|
|
2003
2002
|
// configure the pool / transport above, and TurbineClient warns on a config
|
package/dist/powql.d.ts
CHANGED
|
@@ -486,7 +486,9 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
486
486
|
* `gt` / `gte` / `lt` / `lte` compile to `in` over the labels on that side of
|
|
487
487
|
* the operand in DECLARATION order, which is what PostgreSQL compares; the
|
|
488
488
|
* text comparison PowDB would run is alphabetical, a different answer. A null
|
|
489
|
-
* operand
|
|
489
|
+
* operand is refused before this (assertOperatorOperands, as on the SQL
|
|
490
|
+
* engines) and again by {@link assertEnumLabel}: compiled to a constant false,
|
|
491
|
+
* `NOT` would turn it into every row. Case-insensitive matching and
|
|
490
492
|
* `contains` / `startsWith` / `endsWith` are E003: PostgreSQL has no `lower`
|
|
491
493
|
* or `LIKE` on an enum (42883).
|
|
492
494
|
*/
|
package/dist/powql.js
CHANGED
|
@@ -47,7 +47,7 @@ import { unlockNestedWriteTx } from './query/builder.js';
|
|
|
47
47
|
import { isColumnRef } from './query/column-ref.js';
|
|
48
48
|
import { assertMutationWhereIdentifiesOneRow, assertWhereIdentifiesOneRow, expandCompoundUniqueWhere, uniqueKeyNames, upsertWhereMatchesCreate, whereIdentifiesOneRow, } from './query/compound-unique.js';
|
|
49
49
|
import { exprNotAllowedMessage, isSqlExpr } from './query/expr.js';
|
|
50
|
-
import { ARRAY_OPERATOR_KEYS,
|
|
50
|
+
import { ARRAY_OPERATOR_KEYS, assertOperatorOperands, cursorFieldValue, dedupeColumnList, isJsonFilter, isJsonPathOrderBy, isOrderBySpec, isRelationPickOrderBy, isUnmatchedPlainObject, isVectorOrderBy, normalizeOrderBy, operandColumnKind, orderByEntries, UPDATE_OPERATOR_KEYS, } from './query/filters.js';
|
|
51
51
|
import { isJsonValue } from './query/json-value.js';
|
|
52
52
|
import { assertCursorNotEmptied, assertCursorWritten, assertWhereNotEmptiedByPruning, hasCursor, pruneUndefinedArgs, } from './query/normalize-args.js';
|
|
53
53
|
import { warnUnknownQueryOptions } from './query/option-surface.js';
|
|
@@ -63,6 +63,7 @@ import { assertJsonFilterKeys, emptyMutationWhereMessage, jsonStringEntries, nor
|
|
|
63
63
|
import { combinatorBranches, foldWhereFor, relationCardinalityProblem, relationFilterValueProblem, } from './query/where-compile.js';
|
|
64
64
|
import { assertNoBulkWritePaging, assertNoOptimisticLock } from './query/writes.js';
|
|
65
65
|
import { normalizeKeyColumns, snakeToCamel, } from './schema.js';
|
|
66
|
+
import { transactionClientFor } from './transaction-client-slot.js';
|
|
66
67
|
/**
|
|
67
68
|
* Max parent keys per relation-loader `in (…)` query. A `with` over a large
|
|
68
69
|
* parent set is split into batches of this size so a single query never exceeds
|
|
@@ -1047,17 +1048,15 @@ export class PowqlInterface {
|
|
|
1047
1048
|
// The SQL engines' operand rules and messages, so the engines agree on
|
|
1048
1049
|
// which operator objects are valid; one naming no comparison
|
|
1049
1050
|
// (`{ mode: 'insensitive' }`) is refused instead of compiling to nothing.
|
|
1050
|
-
|
|
1051
|
+
// JSON columns included, as on the SQL engines: skipping them let a
|
|
1052
|
+
// mode-only filter fall through to the constant-false fallback below, which
|
|
1053
|
+
// `NOT` turned into every non-null row (a deleteMany wrote all of them).
|
|
1054
|
+
// The same call refuses a JSON `not` object made only of filter operators.
|
|
1055
|
+
assertOperatorOperands(op, {
|
|
1051
1056
|
column: colMeta.name,
|
|
1052
1057
|
table: this.table,
|
|
1053
1058
|
kind: operandColumnKind(this.meta.pgTypes[colMeta.name]),
|
|
1054
|
-
};
|
|
1055
|
-
if (!isJsonColumn(colMeta, this.schema.enums))
|
|
1056
|
-
assertOperatorOperands(op, operand);
|
|
1057
|
-
// On a JSON column a `not` object made only of filter operators is refused,
|
|
1058
|
-
// as on the SQL engines (one spelling, one meaning on every engine).
|
|
1059
|
-
else
|
|
1060
|
-
assertJsonNotOperand(op.not, operand);
|
|
1059
|
+
});
|
|
1061
1060
|
const insensitive = op.mode === 'insensitive';
|
|
1062
1061
|
const lhs = insensitive ? `lower(${ref})` : ref;
|
|
1063
1062
|
const labels = this.enumLabels(colMeta);
|
|
@@ -1143,7 +1142,14 @@ export class PowqlInterface {
|
|
|
1143
1142
|
throw new ValidationError(`Unsupported operator "${opName}" on PowDB.`);
|
|
1144
1143
|
}
|
|
1145
1144
|
}
|
|
1146
|
-
|
|
1145
|
+
// assertOperatorOperands refuses an operator object that names no
|
|
1146
|
+
// comparison, and every comparison above pushes a condition or throws, so
|
|
1147
|
+
// an empty list is a generator bug. It must not become a constant: under
|
|
1148
|
+
// `NOT` a constant false is every row, and a mutation would write them.
|
|
1149
|
+
if (!conds.length) {
|
|
1150
|
+
throw new InvariantError('powql-filter-empty', `the filter on "${field}" compiled to no condition.`);
|
|
1151
|
+
}
|
|
1152
|
+
return conds.length > 1 ? `(${conds.join(' and ')})` : conds[0];
|
|
1147
1153
|
}
|
|
1148
1154
|
/**
|
|
1149
1155
|
* PowQL JSON path expression `.col->$a->$b…`, binding EVERY path segment as a
|
|
@@ -1318,7 +1324,9 @@ export class PowqlInterface {
|
|
|
1318
1324
|
* `gt` / `gte` / `lt` / `lte` compile to `in` over the labels on that side of
|
|
1319
1325
|
* the operand in DECLARATION order, which is what PostgreSQL compares; the
|
|
1320
1326
|
* text comparison PowDB would run is alphabetical, a different answer. A null
|
|
1321
|
-
* operand
|
|
1327
|
+
* operand is refused before this (assertOperatorOperands, as on the SQL
|
|
1328
|
+
* engines) and again by {@link assertEnumLabel}: compiled to a constant false,
|
|
1329
|
+
* `NOT` would turn it into every row. Case-insensitive matching and
|
|
1322
1330
|
* `contains` / `startsWith` / `endsWith` are E003: PostgreSQL has no `lower`
|
|
1323
1331
|
* or `LIKE` on an enum (42883).
|
|
1324
1332
|
*/
|
|
@@ -1329,8 +1337,6 @@ export class PowqlInterface {
|
|
|
1329
1337
|
}
|
|
1330
1338
|
if (opName !== 'gt' && opName !== 'gte' && opName !== 'lt' && opName !== 'lte')
|
|
1331
1339
|
return undefined;
|
|
1332
|
-
if (opVal === null)
|
|
1333
|
-
return this.alwaysFalse();
|
|
1334
1340
|
this.assertEnumLabel(col, labels, opVal);
|
|
1335
1341
|
const at = labels.indexOf(opVal);
|
|
1336
1342
|
const side = labels.filter((_, i) => opName === 'gt' ? i > at : opName === 'gte' ? i >= at : opName === 'lt' ? i < at : i <= at);
|
|
@@ -4066,6 +4072,9 @@ export class PowqlInterface {
|
|
|
4066
4072
|
// Route tx keywords through the dialect (like the SQL path) so this never
|
|
4067
4073
|
// drifts from `powdbDialect`; falls back to the literal lowercase keywords.
|
|
4068
4074
|
const d = this.options.dialect;
|
|
4075
|
+
// Read before the checkout, so an empty slot (client.ts never loaded in this
|
|
4076
|
+
// copy of the package) refuses with no connection taken and no `begin` sent.
|
|
4077
|
+
const TransactionClient = transactionClientFor(`A nested write or lookup-first upsert on "${this.table}"`);
|
|
4069
4078
|
const client = await this.pool.connect();
|
|
4070
4079
|
// A no-op today (PowDB checkouts have no event surface), kept so this path
|
|
4071
4080
|
// cannot become the one unguarded checkout if a transport ever emits 'error'.
|
|
@@ -4074,7 +4083,6 @@ export class PowqlInterface {
|
|
|
4074
4083
|
try {
|
|
4075
4084
|
await client.query(d?.beginStatement?.() ?? 'begin');
|
|
4076
4085
|
began = true;
|
|
4077
|
-
const { TransactionClient } = await import('./client.js');
|
|
4078
4086
|
const tx = new TransactionClient(client, this.schema, this.middlewares, this.options,
|
|
4079
4087
|
// Pass the PowDB pool so its read-only guard + capabilities carry into
|
|
4080
4088
|
// the transaction-scoped proxy pool (see createTxPool).
|
package/dist/query/builder.js
CHANGED
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
import { acquireConnection, openCheckout } from '../checkout.js';
|
|
14
14
|
import { settleEventLoop } from '../connection-guard.js';
|
|
15
15
|
import { resolveDiagnostics } from '../diagnostics.js';
|
|
16
|
-
import { postgresDialect } from '../dialect.js';
|
|
16
|
+
import { assertDialectCapabilities, postgresDialect } from '../dialect.js';
|
|
17
17
|
import { explainConnectionLoss, INVARIANT_REPORT_HINT, InvariantError, isStaleConnectionError, NotFoundError, TimeoutError, UnsupportedFeatureError, ValidationError, wrapPgError, } from '../errors.js';
|
|
18
18
|
import { missingIndexForRelation, schemaHasIndexInfo } from '../index-advisor.js';
|
|
19
19
|
import { consoleLogger, logTo } from '../logger.js';
|
|
20
20
|
import { executeNestedCreate, executeNestedUpdate, extractRelationFields, hasRelationFields, } from '../nested-write.js';
|
|
21
21
|
import { normalizeKeyColumns, snakeToCamel } from '../schema.js';
|
|
22
|
+
import { transactionClientFor } from '../transaction-client-slot.js';
|
|
22
23
|
import { transactionStateOf, withSavepoint } from '../transaction-state.js';
|
|
23
24
|
import * as aggMod from './aggregates.js';
|
|
24
25
|
import { assertProjectionShape, defaultProjectionFields, includeKeysForBatching, loadRelationsBatched, neededParentKeyFields, rejectNestedPickOrder, resolveCountRelations, stripFields, } from './batched-loader.js';
|
|
@@ -796,6 +797,7 @@ export class QueryInterface {
|
|
|
796
797
|
this.sqlCacheEnabled = options?.sqlCache !== false && sqlCacheSize !== 0;
|
|
797
798
|
this.sqlTemplateCache = new LRUCache(sqlCacheSize !== undefined && sqlCacheSize > 0 ? Math.floor(sqlCacheSize) : 1000);
|
|
798
799
|
this.dialect = options?.dialect ?? postgresDialect;
|
|
800
|
+
assertDialectCapabilities(this.dialect);
|
|
799
801
|
this.sqlCommenter =
|
|
800
802
|
options?._sqlCommenter ??
|
|
801
803
|
resolveSqlComment(options?.sqlComment, this.logger, {
|
|
@@ -4410,6 +4412,11 @@ export class QueryInterface {
|
|
|
4410
4412
|
return acquireConnection(this.pool);
|
|
4411
4413
|
}
|
|
4412
4414
|
async runInImplicitTx(fn) {
|
|
4415
|
+
// client.ts registers TransactionClient when it is evaluated (it cannot be
|
|
4416
|
+
// imported from here, see transaction-client-slot.ts). Read before the
|
|
4417
|
+
// checkout, so a program that never loaded client.ts gets a typed error
|
|
4418
|
+
// without a connection having been touched.
|
|
4419
|
+
const TransactionClient = transactionClientFor(`A nested write or lookup-first upsert on "${this.table}"`);
|
|
4413
4420
|
// BEGIN runs inside openCheckout, which sends it once more on a fresh
|
|
4414
4421
|
// connection when the first one turns out to be dead and releases the
|
|
4415
4422
|
// connection itself when BEGIN fails for good. The catch below therefore
|
|
@@ -4417,7 +4424,6 @@ export class QueryInterface {
|
|
|
4417
4424
|
// a connection that opened none.
|
|
4418
4425
|
const { client, checkout } = await openCheckout(this.pool, (c) => c.query(this.dialect.beginStatement()));
|
|
4419
4426
|
try {
|
|
4420
|
-
const { TransactionClient } = await import('../client.js');
|
|
4421
4427
|
const tx = new TransactionClient(
|
|
4422
4428
|
// biome-ignore lint/suspicious/noExplicitAny: MiddlewareFn and Middleware are structurally identical
|
|
4423
4429
|
client, this.schema,
|
package/dist/schema-sql.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import pg from 'pg';
|
|
8
8
|
import { DESTRUCTIVE_KIND_LABEL, scanDestructiveSql } from './cli/destructive.js';
|
|
9
9
|
import { guardConnection } from './connection-guard.js';
|
|
10
|
-
import { postgresDialect } from './dialect.js';
|
|
10
|
+
import { assertDialectCapabilities, postgresDialect } from './dialect.js';
|
|
11
11
|
import { UnsupportedFeatureError, ValidationError } from './errors.js';
|
|
12
12
|
import { pgConfActionToReferential, stripCheckWrapper } from './introspect.js';
|
|
13
13
|
import { camelToSnake } from './schema.js';
|
|
@@ -92,6 +92,7 @@ function resolveDdlType(config, dialect, columnName) {
|
|
|
92
92
|
*/
|
|
93
93
|
export function schemaToSQL(schema, options) {
|
|
94
94
|
const dialect = options?.dialect ?? postgresDialect;
|
|
95
|
+
assertDialectCapabilities(dialect);
|
|
95
96
|
const extensions = options?.extensions ?? 'auto';
|
|
96
97
|
const statements = [];
|
|
97
98
|
// Topologically sort tables by their foreign key references
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the code that opens an implicit transaction finds `TransactionClient`.
|
|
3
|
+
*
|
|
4
|
+
* A nested write (or an upsert that looks its row up first) issued outside a
|
|
5
|
+
* transaction opens one of its own and runs the nested-write engine against a
|
|
6
|
+
* `TransactionClient` (query/builder.ts; powql.ts, the PowDB generator, does
|
|
7
|
+
* the same and can read this slot too). Those modules sit BELOW client.ts in
|
|
8
|
+
* the import graph: client.ts imports query/, so a static value import back
|
|
9
|
+
* would be a cycle, and scripts/check-import-cycles.mjs refuses one.
|
|
10
|
+
*
|
|
11
|
+
* builder.ts used to reach the class with a dynamic import of client.ts.
|
|
12
|
+
* That works, but a bundler that does not split code (esbuild, which is also
|
|
13
|
+
* what the size budget in .size-limit.js measures, and Wrangler) inlines the
|
|
14
|
+
* dynamic target and, to keep its evaluation lazy, wraps client.ts and every
|
|
15
|
+
* module it reaches in lazy-init closures: about 1 kB brotli on every entry,
|
|
16
|
+
* for a module that is always already loaded by the time a query runs.
|
|
17
|
+
* client.ts instead registers the class here once, when it is evaluated, and
|
|
18
|
+
* every public entry point evaluates client.ts.
|
|
19
|
+
*
|
|
20
|
+
* ## One slot per package copy, deliberately not `Symbol.for`
|
|
21
|
+
*
|
|
22
|
+
* The ESM and CJS builds can both be loaded in one process. Shared state that
|
|
23
|
+
* must agree across them (the warn-once registry, the prepared-statement
|
|
24
|
+
* budget) lives under a `Symbol.for` key; this slot must NOT. A builder has to
|
|
25
|
+
* construct the `TransactionClient` of its OWN copy, exactly as the dynamic
|
|
26
|
+
* import did: the other copy's class builds the other copy's accessors and
|
|
27
|
+
* transaction state and throws the other copy's error classes, so `instanceof`
|
|
28
|
+
* checks in the caller's copy would stop matching. Each copy's entry points
|
|
29
|
+
* load that copy's client.ts, so each copy's slot is filled.
|
|
30
|
+
*
|
|
31
|
+
* ## An empty slot
|
|
32
|
+
*
|
|
33
|
+
* Reachable only when a `QueryInterface` is built directly and client.ts was
|
|
34
|
+
* never loaded in that copy: a test or tool that deep-imports query/, or a
|
|
35
|
+
* bundled application that uses the exported `QueryInterface` without ever
|
|
36
|
+
* importing `TurbineClient` or an engine factory (with `sideEffects: false` a
|
|
37
|
+
* bundler may then drop client.ts). Only the implicit-transaction path reads
|
|
38
|
+
* the slot, so everything else keeps working; that path throws a
|
|
39
|
+
* `ValidationError` (E003) naming the fix, before any connection is checked out.
|
|
40
|
+
*
|
|
41
|
+
* The import of client.ts below is type-only, so this file adds no edge to the
|
|
42
|
+
* runtime graph. errors.ts is a leaf.
|
|
43
|
+
*/
|
|
44
|
+
import type { TransactionClient } from './client.js';
|
|
45
|
+
/** The `TransactionClient` constructor, as client.ts registers it. */
|
|
46
|
+
export type TransactionClientClass = typeof TransactionClient;
|
|
47
|
+
/** @internal Called once, by client.ts, when it is evaluated. */
|
|
48
|
+
export declare function registerTransactionClient(ctor: TransactionClientClass): void;
|
|
49
|
+
/** @internal The registered class, or `undefined` when client.ts was never loaded in this copy. */
|
|
50
|
+
export declare function registeredTransactionClient(): TransactionClientClass | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* @internal The registered class for an implicit transaction. Throws a
|
|
53
|
+
* `ValidationError` when client.ts was never loaded in this copy of the package.
|
|
54
|
+
* `operation` starts the message, for example `A nested write on "users"`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function transactionClientFor(operation: string): TransactionClientClass;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ValidationError } from './errors.js';
|
|
2
|
+
let registered;
|
|
3
|
+
/** @internal Called once, by client.ts, when it is evaluated. */
|
|
4
|
+
export function registerTransactionClient(ctor) {
|
|
5
|
+
registered = ctor;
|
|
6
|
+
}
|
|
7
|
+
/** @internal The registered class, or `undefined` when client.ts was never loaded in this copy. */
|
|
8
|
+
export function registeredTransactionClient() {
|
|
9
|
+
return registered;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @internal The registered class for an implicit transaction. Throws a
|
|
13
|
+
* `ValidationError` when client.ts was never loaded in this copy of the package.
|
|
14
|
+
* `operation` starts the message, for example `A nested write on "users"`.
|
|
15
|
+
*/
|
|
16
|
+
export function transactionClientFor(operation) {
|
|
17
|
+
if (registered !== undefined)
|
|
18
|
+
return registered;
|
|
19
|
+
throw new ValidationError(`${operation} outside a transaction opens one of its own through TurbineClient, ` +
|
|
20
|
+
'and TurbineClient was never loaded in this program. Import TurbineClient (or an engine factory such as ' +
|
|
21
|
+
"turbineHttp) from 'turbine-orm' before running it, or run the write inside db.$transaction().");
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbine-orm",
|
|
3
|
-
"version": "0.90.
|
|
3
|
+
"version": "0.90.1-next.708c40c",
|
|
4
4
|
"description": "Postgres-native TypeScript ORM, runs on Neon, Vercel Postgres, Cloudflare, Supabase. Streaming cursors, typed errors, single-query nested relations. One dependency, no WASM engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"//exports": "Each subpath declares its types PER CONDITION. A single shared top-level \"types\" resolves to the ESM declarations for `require` too, which is TS1479 (\"is an ES module ... cannot be require()d\") for any CJS consumer on moduleResolution node16/nodenext. The require condition points at dist/cjs, which ships its own {\"type\":\"commonjs\"} package.json, so those declarations are CJS declarations. Gated in CI by publint + @arethetypeswrong/cli + a real .cts consumer typecheck (see the package-types job in ci.yml).",
|
|
@@ -208,23 +208,23 @@
|
|
|
208
208
|
},
|
|
209
209
|
"devDependencies": {
|
|
210
210
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
211
|
-
"@biomejs/biome": "^2.
|
|
211
|
+
"@biomejs/biome": "^2.5.14",
|
|
212
212
|
"@size-limit/esbuild": "^12.1.0",
|
|
213
213
|
"@size-limit/file": "^12.1.0",
|
|
214
214
|
"@swc/core": "1.16.2",
|
|
215
215
|
"@types/node": "^22.20.4",
|
|
216
|
-
"@types/pg": "^8.
|
|
216
|
+
"@types/pg": "^8.23.1",
|
|
217
217
|
"@zvndev/powdb-client": "^0.20.0",
|
|
218
218
|
"@zvndev/powdb-embedded": "^0.20.0",
|
|
219
219
|
"c8": "^12.0.0",
|
|
220
220
|
"esbuild": "^0.28.2",
|
|
221
221
|
"husky": "^9.1.7",
|
|
222
|
-
"lint-staged": "^17.0
|
|
223
|
-
"mssql": "^12.7.
|
|
224
|
-
"mysql2": "^3.
|
|
225
|
-
"publint": "^0.3.
|
|
222
|
+
"lint-staged": "^17.6.0",
|
|
223
|
+
"mssql": "^12.7.2",
|
|
224
|
+
"mysql2": "^3.24.5",
|
|
225
|
+
"publint": "^0.3.24",
|
|
226
226
|
"size-limit": "^12.1.0",
|
|
227
|
-
"tsx": "^4.
|
|
227
|
+
"tsx": "^4.23.15",
|
|
228
228
|
"typescript": "^6.0.3"
|
|
229
229
|
},
|
|
230
230
|
"peerDependencies": {
|