turbine-orm 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/powdb.js +23 -1
- package/dist/cjs/powql.js +13 -3
- package/dist/powdb.d.ts +19 -2
- package/dist/powdb.js +23 -1
- package/dist/powql.js +13 -3
- package/package.json +3 -3
package/dist/cjs/powdb.js
CHANGED
|
@@ -254,6 +254,7 @@ const POWDB_FEATURE_MIN_VERSION = {
|
|
|
254
254
|
docFieldIndexes: '0.13',
|
|
255
255
|
serverJoins: '0.13',
|
|
256
256
|
nestedProjections: '0.18',
|
|
257
|
+
entityLinks: '0.19',
|
|
257
258
|
};
|
|
258
259
|
/**
|
|
259
260
|
* Trusted-caller default: every FEATURE gate on, engine version unknown. Used
|
|
@@ -264,6 +265,9 @@ const POWDB_FEATURE_MIN_VERSION = {
|
|
|
264
265
|
* never inferred from a bare construction. `nestedProjections` stays OFF for
|
|
265
266
|
* the same reason: it changes the generated PowQL for every `with` query, and
|
|
266
267
|
* an unprobed engine below 0.18 would reject the syntax outright.
|
|
268
|
+
* `entityLinks` stays OFF for a stronger reason still: declaring a link
|
|
269
|
+
* one-way-upgrades the on-disk catalog to v7 and locks out pre-0.19 binaries,
|
|
270
|
+
* so it must only ever light up behind a real version probe.
|
|
267
271
|
*/
|
|
268
272
|
exports.ALL_POWDB_CAPABILITIES = {
|
|
269
273
|
engineVersion: null,
|
|
@@ -272,6 +276,7 @@ exports.ALL_POWDB_CAPABILITIES = {
|
|
|
272
276
|
introspection: true,
|
|
273
277
|
serverJoins: true,
|
|
274
278
|
nestedProjections: false,
|
|
279
|
+
entityLinks: false,
|
|
275
280
|
nativeRaw: false,
|
|
276
281
|
};
|
|
277
282
|
/** Parse a PowDB semver prefix (`0.13.0`, `0.13`, `1.2.3-rc`) into components, or `null`. */
|
|
@@ -301,6 +306,7 @@ function capabilitiesFromVersion(version, opts = {}) {
|
|
|
301
306
|
introspection: false,
|
|
302
307
|
serverJoins: false,
|
|
303
308
|
nestedProjections: false,
|
|
309
|
+
entityLinks: false,
|
|
304
310
|
nativeRaw: false,
|
|
305
311
|
};
|
|
306
312
|
}
|
|
@@ -311,6 +317,7 @@ function capabilitiesFromVersion(version, opts = {}) {
|
|
|
311
317
|
docFieldIndexes: atLeastVersion(sem, 0, 13),
|
|
312
318
|
serverJoins: atLeastVersion(sem, 0, 13),
|
|
313
319
|
nestedProjections: atLeastVersion(sem, 0, 18),
|
|
320
|
+
entityLinks: atLeastVersion(sem, 0, 19),
|
|
314
321
|
nativeRaw: Boolean(opts.hasNativeRaw) && atLeastVersion(sem, 0, 13),
|
|
315
322
|
};
|
|
316
323
|
}
|
|
@@ -810,6 +817,15 @@ function wrapPowdbError(err) {
|
|
|
810
817
|
return new errors_js_1.ConnectionError(`[turbine] PowDB could not open the directory read-only: ${msg}. Open it once with a writable handle to ` +
|
|
811
818
|
'flush the WAL (recover the directory), then reopen it read-only for snapshot serving.', { cause: err });
|
|
812
819
|
}
|
|
820
|
+
// Catalog-version mismatch → ConnectionError (E004). A pre-0.19 binary/addon
|
|
821
|
+
// cannot open a data directory whose catalog was upgraded to v7 by a `link`
|
|
822
|
+
// declaration (the upgrade is one-way). It is an open-time connection failure,
|
|
823
|
+
// not a query defect, so it runs among the connection families BEFORE the
|
|
824
|
+
// generic validation regexes below.
|
|
825
|
+
if (/unsupported catalog version/i.test(msg)) {
|
|
826
|
+
return new errors_js_1.ConnectionError(`[turbine] PowDB could not open the data directory: ${msg}. This directory uses a newer PowDB catalog ` +
|
|
827
|
+
'format (a `link` declaration upgrades it to v7); upgrade the PowDB addon/server to a version that can read it.', { cause: err });
|
|
828
|
+
}
|
|
813
829
|
// Per-query deadline → TimeoutError (E002). Message-path so it fires on the
|
|
814
830
|
// embedded transport too (code is always 'GenericFailure' there); retryable.
|
|
815
831
|
// Pass the engine prose through the message override (same pattern as the
|
|
@@ -1497,8 +1513,14 @@ function encodePowqlLiteral(value) {
|
|
|
1497
1513
|
* {@link PowdbEmbeddedPool.exec}): an embedded addon whose engine line exceeds
|
|
1498
1514
|
* the ceiling yet still routes through the string wire is refused rather than
|
|
1499
1515
|
* materialized.
|
|
1516
|
+
*
|
|
1517
|
+
* Verification for the 0.19 line: `crates/query/src/lexer.rs` is byte-identical
|
|
1518
|
+
* between v0.18.0 and v0.19.0 (empty git diff), and `link` was already a lexer
|
|
1519
|
+
* keyword at 0.18.0. The 0.19 entity-links surface adds new STATEMENTS built from
|
|
1520
|
+
* pre-existing tokens, so the tokenization / escape surface this ceiling guards is
|
|
1521
|
+
* unmoved.
|
|
1500
1522
|
*/
|
|
1501
|
-
exports.POWQL_LEXER_TESTED_CEILING = '0.
|
|
1523
|
+
exports.POWQL_LEXER_TESTED_CEILING = '0.19';
|
|
1502
1524
|
/** Escape a string into a PowQL `"…"` literal, matching the engine lexer's escape rules. */
|
|
1503
1525
|
function encodePowqlString(s) {
|
|
1504
1526
|
let out = '"';
|
package/dist/cjs/powql.js
CHANGED
|
@@ -360,7 +360,11 @@ class PowqlInterface {
|
|
|
360
360
|
conds.push(opVal === null ? `${ref} is null` : `${lhs} = ${this.bind(opVal, params, insensitive)}`);
|
|
361
361
|
break;
|
|
362
362
|
case 'not':
|
|
363
|
-
|
|
363
|
+
// Spell as `lhs != $N` (not `not (lhs = $N)`) so a missing-value row is
|
|
364
|
+
// EXCLUDED, matching SQL null semantics on PowDB >= 0.18.2. Behavior-neutral
|
|
365
|
+
// on older engines (pre-0.18.2 `!=` MATCHED missing rows exactly like the
|
|
366
|
+
// old `not (...)` spelling), so no capability gate is needed.
|
|
367
|
+
conds.push(opVal === null ? `${ref} is not null` : `${lhs} != ${this.bind(opVal, params, insensitive)}`);
|
|
364
368
|
break;
|
|
365
369
|
case 'gt':
|
|
366
370
|
conds.push(`${lhs} > ${this.bind(opVal, params, insensitive)}`);
|
|
@@ -502,11 +506,17 @@ class PowqlInterface {
|
|
|
502
506
|
/** `lhs [not] in ($1, $2, …)` — empty list collapses to a constant. */
|
|
503
507
|
buildInList(lhs, values, params, insensitive, negate) {
|
|
504
508
|
if (!Array.isArray(values) || values.length === 0) {
|
|
505
|
-
// `in []` matches nothing; `not in []` matches everything
|
|
509
|
+
// `in []` matches nothing; `not in []` matches everything (SQL parity requires a
|
|
510
|
+
// missing-value row to match `notIn []`, so NO presence guard is appended here).
|
|
506
511
|
return negate ? '(1 = 1)' : this.alwaysFalse();
|
|
507
512
|
}
|
|
508
513
|
const items = values.map((v) => this.bind(v, params, insensitive)).join(', ');
|
|
509
|
-
|
|
514
|
+
if (negate) {
|
|
515
|
+
// PowQL `not in` matches missing-value rows, so append `and lhs is not null`
|
|
516
|
+
// for SQL null-semantics parity (non-empty list only, per the empty-list note above).
|
|
517
|
+
return `(${lhs} not in (${items}) and ${lhs} is not null)`;
|
|
518
|
+
}
|
|
519
|
+
return `${lhs} in (${items})`;
|
|
510
520
|
}
|
|
511
521
|
/**
|
|
512
522
|
* Pre-resolve every relation filter (`some`/`none`/`every`) in a where clause
|
package/dist/powdb.d.ts
CHANGED
|
@@ -261,11 +261,19 @@ export interface PowdbCapabilities {
|
|
|
261
261
|
* batched loaders.
|
|
262
262
|
*/
|
|
263
263
|
nestedProjections: boolean;
|
|
264
|
+
/**
|
|
265
|
+
* ≥ 0.19: entity links (`link` DDL, scalar/block traversal). Capability is
|
|
266
|
+
* recognized (probe-only), but query generation deliberately does NOT consume
|
|
267
|
+
* links yet: turbine keeps composing its own nested projections (see the
|
|
268
|
+
* PowDB engine page for the rationale). Declaring a link permanently upgrades
|
|
269
|
+
* the on-disk catalog to v7, so this stays FALSE in ALL_POWDB_CAPABILITIES.
|
|
270
|
+
*/
|
|
271
|
+
entityLinks: boolean;
|
|
264
272
|
/** Networked only: server ≥ 0.13 AND the client exposes `queryNativeRaw`. */
|
|
265
273
|
nativeRaw: boolean;
|
|
266
274
|
}
|
|
267
275
|
/** The feature-gate capability keys (everything except the version/nativeRaw metadata). */
|
|
268
|
-
type PowdbFeatureKey = 'jsonDocs' | 'docFieldIndexes' | 'introspection' | 'serverJoins' | 'nestedProjections';
|
|
276
|
+
type PowdbFeatureKey = 'jsonDocs' | 'docFieldIndexes' | 'introspection' | 'serverJoins' | 'nestedProjections' | 'entityLinks';
|
|
269
277
|
/**
|
|
270
278
|
* Trusted-caller default: every FEATURE gate on, engine version unknown. Used
|
|
271
279
|
* for a directly-constructed {@link PowdbPool} / {@link PowdbEmbeddedPool} that
|
|
@@ -275,6 +283,9 @@ type PowdbFeatureKey = 'jsonDocs' | 'docFieldIndexes' | 'introspection' | 'serve
|
|
|
275
283
|
* never inferred from a bare construction. `nestedProjections` stays OFF for
|
|
276
284
|
* the same reason: it changes the generated PowQL for every `with` query, and
|
|
277
285
|
* an unprobed engine below 0.18 would reject the syntax outright.
|
|
286
|
+
* `entityLinks` stays OFF for a stronger reason still: declaring a link
|
|
287
|
+
* one-way-upgrades the on-disk catalog to v7 and locks out pre-0.19 binaries,
|
|
288
|
+
* so it must only ever light up behind a real version probe.
|
|
278
289
|
*/
|
|
279
290
|
export declare const ALL_POWDB_CAPABILITIES: PowdbCapabilities;
|
|
280
291
|
/**
|
|
@@ -610,8 +621,14 @@ export declare function encodePowqlLiteral(value: unknown): string;
|
|
|
610
621
|
* {@link PowdbEmbeddedPool.exec}): an embedded addon whose engine line exceeds
|
|
611
622
|
* the ceiling yet still routes through the string wire is refused rather than
|
|
612
623
|
* materialized.
|
|
624
|
+
*
|
|
625
|
+
* Verification for the 0.19 line: `crates/query/src/lexer.rs` is byte-identical
|
|
626
|
+
* between v0.18.0 and v0.19.0 (empty git diff), and `link` was already a lexer
|
|
627
|
+
* keyword at 0.18.0. The 0.19 entity-links surface adds new STATEMENTS built from
|
|
628
|
+
* pre-existing tokens, so the tokenization / escape surface this ceiling guards is
|
|
629
|
+
* unmoved.
|
|
613
630
|
*/
|
|
614
|
-
export declare const POWQL_LEXER_TESTED_CEILING = "0.
|
|
631
|
+
export declare const POWQL_LEXER_TESTED_CEILING = "0.19";
|
|
615
632
|
/**
|
|
616
633
|
* Substitute every `$N` placeholder in a generator-produced PowQL template with
|
|
617
634
|
* the encoded literal of `params[N-1]`. Safe because the template is produced by
|
package/dist/powdb.js
CHANGED
|
@@ -197,6 +197,7 @@ const POWDB_FEATURE_MIN_VERSION = {
|
|
|
197
197
|
docFieldIndexes: '0.13',
|
|
198
198
|
serverJoins: '0.13',
|
|
199
199
|
nestedProjections: '0.18',
|
|
200
|
+
entityLinks: '0.19',
|
|
200
201
|
};
|
|
201
202
|
/**
|
|
202
203
|
* Trusted-caller default: every FEATURE gate on, engine version unknown. Used
|
|
@@ -207,6 +208,9 @@ const POWDB_FEATURE_MIN_VERSION = {
|
|
|
207
208
|
* never inferred from a bare construction. `nestedProjections` stays OFF for
|
|
208
209
|
* the same reason: it changes the generated PowQL for every `with` query, and
|
|
209
210
|
* an unprobed engine below 0.18 would reject the syntax outright.
|
|
211
|
+
* `entityLinks` stays OFF for a stronger reason still: declaring a link
|
|
212
|
+
* one-way-upgrades the on-disk catalog to v7 and locks out pre-0.19 binaries,
|
|
213
|
+
* so it must only ever light up behind a real version probe.
|
|
210
214
|
*/
|
|
211
215
|
export const ALL_POWDB_CAPABILITIES = {
|
|
212
216
|
engineVersion: null,
|
|
@@ -215,6 +219,7 @@ export const ALL_POWDB_CAPABILITIES = {
|
|
|
215
219
|
introspection: true,
|
|
216
220
|
serverJoins: true,
|
|
217
221
|
nestedProjections: false,
|
|
222
|
+
entityLinks: false,
|
|
218
223
|
nativeRaw: false,
|
|
219
224
|
};
|
|
220
225
|
/** Parse a PowDB semver prefix (`0.13.0`, `0.13`, `1.2.3-rc`) into components, or `null`. */
|
|
@@ -244,6 +249,7 @@ export function capabilitiesFromVersion(version, opts = {}) {
|
|
|
244
249
|
introspection: false,
|
|
245
250
|
serverJoins: false,
|
|
246
251
|
nestedProjections: false,
|
|
252
|
+
entityLinks: false,
|
|
247
253
|
nativeRaw: false,
|
|
248
254
|
};
|
|
249
255
|
}
|
|
@@ -254,6 +260,7 @@ export function capabilitiesFromVersion(version, opts = {}) {
|
|
|
254
260
|
docFieldIndexes: atLeastVersion(sem, 0, 13),
|
|
255
261
|
serverJoins: atLeastVersion(sem, 0, 13),
|
|
256
262
|
nestedProjections: atLeastVersion(sem, 0, 18),
|
|
263
|
+
entityLinks: atLeastVersion(sem, 0, 19),
|
|
257
264
|
nativeRaw: Boolean(opts.hasNativeRaw) && atLeastVersion(sem, 0, 13),
|
|
258
265
|
};
|
|
259
266
|
}
|
|
@@ -753,6 +760,15 @@ export function wrapPowdbError(err) {
|
|
|
753
760
|
return new ConnectionError(`[turbine] PowDB could not open the directory read-only: ${msg}. Open it once with a writable handle to ` +
|
|
754
761
|
'flush the WAL (recover the directory), then reopen it read-only for snapshot serving.', { cause: err });
|
|
755
762
|
}
|
|
763
|
+
// Catalog-version mismatch → ConnectionError (E004). A pre-0.19 binary/addon
|
|
764
|
+
// cannot open a data directory whose catalog was upgraded to v7 by a `link`
|
|
765
|
+
// declaration (the upgrade is one-way). It is an open-time connection failure,
|
|
766
|
+
// not a query defect, so it runs among the connection families BEFORE the
|
|
767
|
+
// generic validation regexes below.
|
|
768
|
+
if (/unsupported catalog version/i.test(msg)) {
|
|
769
|
+
return new ConnectionError(`[turbine] PowDB could not open the data directory: ${msg}. This directory uses a newer PowDB catalog ` +
|
|
770
|
+
'format (a `link` declaration upgrades it to v7); upgrade the PowDB addon/server to a version that can read it.', { cause: err });
|
|
771
|
+
}
|
|
756
772
|
// Per-query deadline → TimeoutError (E002). Message-path so it fires on the
|
|
757
773
|
// embedded transport too (code is always 'GenericFailure' there); retryable.
|
|
758
774
|
// Pass the engine prose through the message override (same pattern as the
|
|
@@ -1439,8 +1455,14 @@ export function encodePowqlLiteral(value) {
|
|
|
1439
1455
|
* {@link PowdbEmbeddedPool.exec}): an embedded addon whose engine line exceeds
|
|
1440
1456
|
* the ceiling yet still routes through the string wire is refused rather than
|
|
1441
1457
|
* materialized.
|
|
1458
|
+
*
|
|
1459
|
+
* Verification for the 0.19 line: `crates/query/src/lexer.rs` is byte-identical
|
|
1460
|
+
* between v0.18.0 and v0.19.0 (empty git diff), and `link` was already a lexer
|
|
1461
|
+
* keyword at 0.18.0. The 0.19 entity-links surface adds new STATEMENTS built from
|
|
1462
|
+
* pre-existing tokens, so the tokenization / escape surface this ceiling guards is
|
|
1463
|
+
* unmoved.
|
|
1442
1464
|
*/
|
|
1443
|
-
export const POWQL_LEXER_TESTED_CEILING = '0.
|
|
1465
|
+
export const POWQL_LEXER_TESTED_CEILING = '0.19';
|
|
1444
1466
|
/** Escape a string into a PowQL `"…"` literal, matching the engine lexer's escape rules. */
|
|
1445
1467
|
function encodePowqlString(s) {
|
|
1446
1468
|
let out = '"';
|
package/dist/powql.js
CHANGED
|
@@ -324,7 +324,11 @@ export class PowqlInterface {
|
|
|
324
324
|
conds.push(opVal === null ? `${ref} is null` : `${lhs} = ${this.bind(opVal, params, insensitive)}`);
|
|
325
325
|
break;
|
|
326
326
|
case 'not':
|
|
327
|
-
|
|
327
|
+
// Spell as `lhs != $N` (not `not (lhs = $N)`) so a missing-value row is
|
|
328
|
+
// EXCLUDED, matching SQL null semantics on PowDB >= 0.18.2. Behavior-neutral
|
|
329
|
+
// on older engines (pre-0.18.2 `!=` MATCHED missing rows exactly like the
|
|
330
|
+
// old `not (...)` spelling), so no capability gate is needed.
|
|
331
|
+
conds.push(opVal === null ? `${ref} is not null` : `${lhs} != ${this.bind(opVal, params, insensitive)}`);
|
|
328
332
|
break;
|
|
329
333
|
case 'gt':
|
|
330
334
|
conds.push(`${lhs} > ${this.bind(opVal, params, insensitive)}`);
|
|
@@ -466,11 +470,17 @@ export class PowqlInterface {
|
|
|
466
470
|
/** `lhs [not] in ($1, $2, …)` — empty list collapses to a constant. */
|
|
467
471
|
buildInList(lhs, values, params, insensitive, negate) {
|
|
468
472
|
if (!Array.isArray(values) || values.length === 0) {
|
|
469
|
-
// `in []` matches nothing; `not in []` matches everything
|
|
473
|
+
// `in []` matches nothing; `not in []` matches everything (SQL parity requires a
|
|
474
|
+
// missing-value row to match `notIn []`, so NO presence guard is appended here).
|
|
470
475
|
return negate ? '(1 = 1)' : this.alwaysFalse();
|
|
471
476
|
}
|
|
472
477
|
const items = values.map((v) => this.bind(v, params, insensitive)).join(', ');
|
|
473
|
-
|
|
478
|
+
if (negate) {
|
|
479
|
+
// PowQL `not in` matches missing-value rows, so append `and lhs is not null`
|
|
480
|
+
// for SQL null-semantics parity (non-empty list only, per the empty-list note above).
|
|
481
|
+
return `(${lhs} not in (${items}) and ${lhs} is not null)`;
|
|
482
|
+
}
|
|
483
|
+
return `${lhs} in (${items})`;
|
|
474
484
|
}
|
|
475
485
|
/**
|
|
476
486
|
* Pre-resolve every relation filter (`some`/`none`/`every`) in a where clause
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbine-orm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
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": {
|
|
@@ -108,8 +108,8 @@
|
|
|
108
108
|
"@size-limit/esbuild": "^12.1.0",
|
|
109
109
|
"@size-limit/file": "^12.1.0",
|
|
110
110
|
"@types/node": "^26.1.0",
|
|
111
|
-
"@zvndev/powdb-client": "^0.
|
|
112
|
-
"@zvndev/powdb-embedded": "^0.
|
|
111
|
+
"@zvndev/powdb-client": "^0.19.0",
|
|
112
|
+
"@zvndev/powdb-embedded": "^0.19.0",
|
|
113
113
|
"c8": "^11.0.0",
|
|
114
114
|
"husky": "^9.1.7",
|
|
115
115
|
"lint-staged": "^17.0.8",
|