turbine-orm 0.91.0 → 0.92.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 +23 -24
- package/dist/cjs/cli/config.d.ts +5 -0
- package/dist/cjs/cli/config.js +1 -0
- package/dist/cjs/cli/index.d.ts +17 -17
- package/dist/cjs/cli/index.js +34 -27
- package/dist/cjs/cli/observe-ui.d.ts +1 -1
- package/dist/cjs/cli/observe-ui.js +1 -1
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/powdb.d.ts +10 -6
- package/dist/cjs/powdb.js +4 -2
- package/dist/cjs/query/builder.d.ts +8 -0
- package/dist/cjs/query/builder.js +31 -23
- package/dist/cjs/query/relations.d.ts +21 -3
- package/dist/cjs/query/relations.js +331 -68
- package/dist/cjs/query/utils.d.ts +13 -0
- package/dist/cjs/query/utils.js +44 -1
- package/dist/cjs/query/where.d.ts +15 -0
- package/dist/cjs/schema-builder.d.ts +8 -5
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.js +1 -0
- package/dist/cli/index.d.ts +17 -17
- package/dist/cli/index.js +35 -28
- package/dist/cli/observe-ui.d.ts +1 -1
- package/dist/cli/observe-ui.js +1 -1
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/powdb.d.ts +10 -6
- package/dist/powdb.js +4 -2
- package/dist/query/builder.d.ts +8 -0
- package/dist/query/builder.js +18 -10
- package/dist/query/relations.d.ts +21 -3
- package/dist/query/relations.js +330 -69
- package/dist/query/utils.d.ts +13 -0
- package/dist/query/utils.js +43 -0
- package/dist/query/where.d.ts +15 -0
- package/dist/schema-builder.d.ts +8 -5
- package/package.json +1 -1
package/dist/cjs/powdb.d.ts
CHANGED
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
* writable types (`str`/`int`/`float`/`bool`); `Date` → `int` epoch micros;
|
|
22
22
|
* `string` PKs hold UUID strings.
|
|
23
23
|
* - **No JSON aggregation / link navigation**, single-query nested `with` is
|
|
24
|
-
*
|
|
24
|
+
* not available → on PowDB 0.18+ an eligible `with` compiles to nested
|
|
25
|
+
* projections inside the parent statement; older engines and ineligible
|
|
26
|
+
* relations use batched loaders.
|
|
25
27
|
* - **Single global write lock; no savepoints/isolation**, nested
|
|
26
28
|
* transactions / isolation / vector / LISTEN-NOTIFY / RLS throw.
|
|
27
29
|
* Independent concurrent `db.$transaction` calls do NOT throw: they queue
|
|
@@ -667,8 +669,10 @@ export { PowqlInterface } from './powql.js';
|
|
|
667
669
|
*/
|
|
668
670
|
export interface TurbinePowdbOptions extends EngineClientConfig {
|
|
669
671
|
/**
|
|
670
|
-
* Client-level default `with`-relation load strategy. On PowDB the default
|
|
671
|
-
*
|
|
672
|
+
* Client-level default `with`-relation load strategy. On PowDB 0.18+ the default
|
|
673
|
+
* compiles eligible relations to nested projections in one statement (older
|
|
674
|
+
* engines and ineligible relations use the batched loaders); `'batched'` forces
|
|
675
|
+
* the loaders, and setting `'join'` opts INTO native PowQL server-side
|
|
672
676
|
* joins for eligible top-level relations (ineligible ones, e.g. a paged parent
|
|
673
677
|
* or a nested `with`, fall back to the loaders per-relation and silently). A
|
|
674
678
|
* per-query `relationLoadStrategy` arg still overrides this. Requires an engine
|
|
@@ -743,9 +747,9 @@ export interface TurbinePowdbOptions extends EngineClientConfig {
|
|
|
743
747
|
/**
|
|
744
748
|
* Selects the **embedded** transport, an in-process `@zvndev/powdb-embedded`
|
|
745
749
|
* database at the given data directory (no server, no socket). The value is the
|
|
746
|
-
* data dir path. Preview: Full-durability checkpoint-bound;
|
|
747
|
-
* for macOS
|
|
748
|
-
* a from-source `npm run build`).
|
|
750
|
+
* data dir path. Preview: Full-durability checkpoint-bound; prebuilt binaries ship
|
|
751
|
+
* for macOS arm64 and Linux glibc x64/arm64 only (Intel macOS, musl and Windows
|
|
752
|
+
* fall back to a from-source `npm run build`).
|
|
749
753
|
*
|
|
750
754
|
* @example
|
|
751
755
|
* ```ts
|
package/dist/cjs/powdb.js
CHANGED
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
* writable types (`str`/`int`/`float`/`bool`); `Date` → `int` epoch micros;
|
|
23
23
|
* `string` PKs hold UUID strings.
|
|
24
24
|
* - **No JSON aggregation / link navigation**, single-query nested `with` is
|
|
25
|
-
*
|
|
25
|
+
* not available → on PowDB 0.18+ an eligible `with` compiles to nested
|
|
26
|
+
* projections inside the parent statement; older engines and ineligible
|
|
27
|
+
* relations use batched loaders.
|
|
26
28
|
* - **Single global write lock; no savepoints/isolation**, nested
|
|
27
29
|
* transactions / isolation / vector / LISTEN-NOTIFY / RLS throw.
|
|
28
30
|
* Independent concurrent `db.$transaction` calls do NOT throw: they queue
|
|
@@ -1867,7 +1869,7 @@ async function loadPowdbEmbedded() {
|
|
|
1867
1869
|
catch (err) {
|
|
1868
1870
|
throw new errors_js_1.ConnectionError("turbine-orm/powdb embedded mode requires the optional peer '@zvndev/powdb-embedded'. " +
|
|
1869
1871
|
'Install it: npm i @zvndev/powdb-embedded. If install succeeded but loading failed, your platform has no ' +
|
|
1870
|
-
'prebuilt binary (prebuilts ship for macOS arm64
|
|
1872
|
+
'prebuilt binary (prebuilts ship for macOS arm64 and Linux glibc x64/arm64; Intel macOS, musl and Windows ' +
|
|
1871
1873
|
'build from source), build it with `npm run build` in the addon, then retry. You can also construct the ' +
|
|
1872
1874
|
'pool yourself and inject it: turbinePowDB(pool, schema). ' +
|
|
1873
1875
|
`(${err.message})`);
|
|
@@ -1702,6 +1702,14 @@ export declare class QueryInterface<T extends object, R extends object = {}, M e
|
|
|
1702
1702
|
* search down the wrong path.
|
|
1703
1703
|
*/
|
|
1704
1704
|
private buildRowDecodePlan;
|
|
1705
|
+
/**
|
|
1706
|
+
* The cached decode plan for one exact column list, without touching the
|
|
1707
|
+
* per-table most-recent slot. {@link buildRowDecodePlan} is the per-row entry;
|
|
1708
|
+
* this is also what the positional nested-row plan in relations.ts reads
|
|
1709
|
+
* (through the ctx) when it is built, so a nested row decodes by exactly the
|
|
1710
|
+
* field names and cell kinds {@link parseRow} would give the same keys.
|
|
1711
|
+
*/
|
|
1712
|
+
private rowDecodePlanFor;
|
|
1705
1713
|
/**
|
|
1706
1714
|
* Whether a cached plan describes exactly this row's column list, in order.
|
|
1707
1715
|
* A pointer compare per column: both sides are the driver's own interned
|
|
@@ -277,14 +277,6 @@ const ROW_PLAN_CACHE_SIZE = 128;
|
|
|
277
277
|
* probe's own limit.
|
|
278
278
|
*/
|
|
279
279
|
const STREAM_SPECULATION_MAX_BATCH = 1000;
|
|
280
|
-
/** Per-cell decode work in a {@link RowDecodePlan}. */
|
|
281
|
-
const CELL_PLAIN = 0;
|
|
282
|
-
const CELL_TEMPORAL = 1;
|
|
283
|
-
const CELL_FLOAT = 2;
|
|
284
|
-
const CELL_FLOAT_ARRAY = 3;
|
|
285
|
-
const CELL_TEXT_ARRAY = 4;
|
|
286
|
-
/** The dialect's own decode for a type whose storage differs (see `Dialect.decodeColumnValue`). */
|
|
287
|
-
const CELL_DIALECT = 5;
|
|
288
280
|
const FLOAT_TYPES = new Set(['float4', 'float8', 'real', 'double precision']);
|
|
289
281
|
const FLOAT_ARRAY_TYPES = new Set(['_float4', '_float8', 'float4[]', 'float8[]', 'real[]', 'double precision[]']);
|
|
290
282
|
/**
|
|
@@ -294,11 +286,11 @@ const FLOAT_ARRAY_TYPES = new Set(['_float4', '_float8', 'float4[]', 'float8[]',
|
|
|
294
286
|
*/
|
|
295
287
|
function cellKindOf(pgType, enums) {
|
|
296
288
|
if (FLOAT_TYPES.has(pgType))
|
|
297
|
-
return CELL_FLOAT;
|
|
289
|
+
return utils_js_1.CELL_FLOAT;
|
|
298
290
|
if (FLOAT_ARRAY_TYPES.has(pgType))
|
|
299
|
-
return CELL_FLOAT_ARRAY;
|
|
291
|
+
return utils_js_1.CELL_FLOAT_ARRAY;
|
|
300
292
|
const element = pgType.startsWith('_') ? pgType.slice(1) : pgType.endsWith('[]') ? pgType.slice(0, -2) : undefined;
|
|
301
|
-
return element !== undefined && enums !== undefined && Object.hasOwn(enums, element) ? CELL_TEXT_ARRAY : CELL_PLAIN;
|
|
293
|
+
return element !== undefined && enums !== undefined && Object.hasOwn(enums, element) ? utils_js_1.CELL_TEXT_ARRAY : utils_js_1.CELL_PLAIN;
|
|
302
294
|
}
|
|
303
295
|
/**
|
|
304
296
|
* Strict structural equality for a single SQL parameter value. Handles the
|
|
@@ -1020,6 +1012,11 @@ class QueryInterface {
|
|
|
1020
1012
|
toColumn: (field) => this.toColumn(field),
|
|
1021
1013
|
castAgg: (expr, target) => this.castAgg(expr, target),
|
|
1022
1014
|
parseRow: (row, table) => this.parseRow(row, table),
|
|
1015
|
+
rowDecodePlan: (table, keys) => {
|
|
1016
|
+
const meta = this.schema.tables[table];
|
|
1017
|
+
return meta ? this.rowDecodePlanFor(table, meta, keys) : undefined;
|
|
1018
|
+
},
|
|
1019
|
+
decodeCell: (kind, value, table, field) => this.decodeCell(kind, value, table, field),
|
|
1023
1020
|
nullsSuffix: (nulls) => this.nullsSuffix(nulls),
|
|
1024
1021
|
isRelationOrderByValue: (value) => this.isRelationOrderByValue(value),
|
|
1025
1022
|
resolveOrderByColumn: (table, meta, key) => this.resolveOrderByColumn(table, meta, key),
|
|
@@ -5266,9 +5263,9 @@ class QueryInterface {
|
|
|
5266
5263
|
if (typeof pgType !== 'string')
|
|
5267
5264
|
continue;
|
|
5268
5265
|
let kind = cellKindOf(pgType, this.schema.enums);
|
|
5269
|
-
const decode = kind === CELL_PLAIN ? this.dialect.decodeColumnValue?.(pgType) : undefined;
|
|
5266
|
+
const decode = kind === utils_js_1.CELL_PLAIN ? this.dialect.decodeColumnValue?.(pgType) : undefined;
|
|
5270
5267
|
if (decode !== undefined) {
|
|
5271
|
-
kind = CELL_DIALECT;
|
|
5268
|
+
kind = utils_js_1.CELL_DIALECT;
|
|
5272
5269
|
let decoders = this.dialectDecodeCache.get(table);
|
|
5273
5270
|
if (!decoders) {
|
|
5274
5271
|
decoders = new Map();
|
|
@@ -5277,7 +5274,7 @@ class QueryInterface {
|
|
|
5277
5274
|
decoders.set(column.name, decode);
|
|
5278
5275
|
decoders.set(column.field, decode);
|
|
5279
5276
|
}
|
|
5280
|
-
if (kind === CELL_PLAIN)
|
|
5277
|
+
if (kind === utils_js_1.CELL_PLAIN)
|
|
5281
5278
|
continue;
|
|
5282
5279
|
kinds.set(column.name, kind);
|
|
5283
5280
|
kinds.set(column.field, kind);
|
|
@@ -5289,15 +5286,15 @@ class QueryInterface {
|
|
|
5289
5286
|
/** One cell's decode, for every kind but {@link CELL_PLAIN}. */
|
|
5290
5287
|
decodeCell(kind, value, table, field) {
|
|
5291
5288
|
switch (kind) {
|
|
5292
|
-
case CELL_TEMPORAL:
|
|
5289
|
+
case utils_js_1.CELL_TEMPORAL:
|
|
5293
5290
|
return this.decodeTemporalCell(value, table, field);
|
|
5294
|
-
case CELL_FLOAT:
|
|
5291
|
+
case utils_js_1.CELL_FLOAT:
|
|
5295
5292
|
return (0, utils_js_1.readJsonFloat)(value);
|
|
5296
|
-
case CELL_FLOAT_ARRAY:
|
|
5293
|
+
case utils_js_1.CELL_FLOAT_ARRAY:
|
|
5297
5294
|
return (0, utils_js_1.readJsonFloatArray)(value);
|
|
5298
|
-
case CELL_TEXT_ARRAY:
|
|
5295
|
+
case utils_js_1.CELL_TEXT_ARRAY:
|
|
5299
5296
|
return (0, utils_js_1.readTextArray)(value);
|
|
5300
|
-
case CELL_DIALECT:
|
|
5297
|
+
case utils_js_1.CELL_DIALECT:
|
|
5301
5298
|
return this.decodeDialectCell(value, table, field);
|
|
5302
5299
|
default:
|
|
5303
5300
|
return value;
|
|
@@ -5328,6 +5325,18 @@ class QueryInterface {
|
|
|
5328
5325
|
* search down the wrong path.
|
|
5329
5326
|
*/
|
|
5330
5327
|
buildRowDecodePlan(table, meta, keys) {
|
|
5328
|
+
const plan = this.rowDecodePlanFor(table, meta, keys);
|
|
5329
|
+
this.rowPlanLast.set(table, plan);
|
|
5330
|
+
return plan;
|
|
5331
|
+
}
|
|
5332
|
+
/**
|
|
5333
|
+
* The cached decode plan for one exact column list, without touching the
|
|
5334
|
+
* per-table most-recent slot. {@link buildRowDecodePlan} is the per-row entry;
|
|
5335
|
+
* this is also what the positional nested-row plan in relations.ts reads
|
|
5336
|
+
* (through the ctx) when it is built, so a nested row decodes by exactly the
|
|
5337
|
+
* field names and cell kinds {@link parseRow} would give the same keys.
|
|
5338
|
+
*/
|
|
5339
|
+
rowDecodePlanFor(table, meta, keys) {
|
|
5331
5340
|
const shapeKey = `${table}\u0000${keys.join('\u0000')}`;
|
|
5332
5341
|
let plan = this.rowPlanCache.get(shapeKey);
|
|
5333
5342
|
if (plan === undefined) {
|
|
@@ -5345,13 +5354,12 @@ class QueryInterface {
|
|
|
5345
5354
|
fields.push(field);
|
|
5346
5355
|
// Top-level rows are snake_case (dateCols); nested rows are camelCase.
|
|
5347
5356
|
kinds.push(dateCols.has(col) || camelDateFields.has(field)
|
|
5348
|
-
? CELL_TEMPORAL
|
|
5349
|
-
: (cellKinds.get(col) ?? cellKinds.get(field) ?? CELL_PLAIN));
|
|
5357
|
+
? utils_js_1.CELL_TEMPORAL
|
|
5358
|
+
: (cellKinds.get(col) ?? cellKinds.get(field) ?? utils_js_1.CELL_PLAIN));
|
|
5350
5359
|
}
|
|
5351
5360
|
plan = { cols, fields, kinds };
|
|
5352
5361
|
this.rowPlanCache.set(shapeKey, plan);
|
|
5353
5362
|
}
|
|
5354
|
-
this.rowPlanLast.set(table, plan);
|
|
5355
5363
|
return plan;
|
|
5356
5364
|
}
|
|
5357
5365
|
/**
|
|
@@ -5399,7 +5407,7 @@ class QueryInterface {
|
|
|
5399
5407
|
const kind = kinds[i];
|
|
5400
5408
|
// A float from the driver is already a number; only JSON's strings need reading.
|
|
5401
5409
|
parsed[field] =
|
|
5402
|
-
kind === CELL_PLAIN || (kind === CELL_FLOAT && typeof value !== 'string')
|
|
5410
|
+
kind === utils_js_1.CELL_PLAIN || (kind === utils_js_1.CELL_FLOAT && typeof value !== 'string')
|
|
5403
5411
|
? value
|
|
5404
5412
|
: this.decodeCell(kind, value, table, field);
|
|
5405
5413
|
}
|
|
@@ -353,11 +353,22 @@ export declare function buildRelationShapes(qi: BuilderCtx, table: string, withC
|
|
|
353
353
|
export declare function buildRelationShape(qi: BuilderCtx, relDef: RelationDef, spec: true | WithOptions, parentMeta: TableMetadata, includePii?: boolean): RelationShape;
|
|
354
354
|
/**
|
|
355
355
|
* Build the row parser for a `with` clause. In object mode this is just
|
|
356
|
-
* {@link parseNestedRow}. In positional mode
|
|
357
|
-
*
|
|
358
|
-
*
|
|
356
|
+
* {@link parseNestedRow}. In positional mode the relation shapes are built
|
|
357
|
+
* once (not per row) and compiled into a decode plan
|
|
358
|
+
* ({@link makePositionalRowParser}) that turns each positional array straight
|
|
359
|
+
* into its parsed row. A query whose shapes the plan declines takes the
|
|
360
|
+
* reference path instead, which decodes each positional array into the object
|
|
361
|
+
* form and hands it to parseNestedRow.
|
|
359
362
|
*/
|
|
360
363
|
export declare function makeNestedParser(qi: BuilderCtx, withClause: WithClause, includePii?: boolean, flattenPlan?: FlattenPlan | null): (row: Record<string, unknown>) => Record<string, unknown>;
|
|
364
|
+
/**
|
|
365
|
+
* The positional parser the plan in {@link makePositionalRowParser} is held to:
|
|
366
|
+
* decode each relation's arrays into the object representation, then parse
|
|
367
|
+
* that exactly as an object-encoded row is parsed. It is the path for every
|
|
368
|
+
* query the plan declines, and is exported so the differential tests can
|
|
369
|
+
* compare the two.
|
|
370
|
+
*/
|
|
371
|
+
export declare function makeReferencePositionalParser(qi: BuilderCtx, shapes: Record<string, RelationShape>): (row: Record<string, unknown>) => Record<string, unknown>;
|
|
361
372
|
/**
|
|
362
373
|
* Return a shallow copy of a top-level row with each relation column decoded
|
|
363
374
|
* from its positional array(s) into the object representation. Only relation
|
|
@@ -374,6 +385,13 @@ export declare function decodePositionalRelations(qi: BuilderCtx, row: Record<st
|
|
|
374
385
|
export declare function decodePositionalValue(qi: BuilderCtx, raw: unknown, shape: RelationShape): unknown;
|
|
375
386
|
/** Map one positional array back to a keyed object using the shape's key order. */
|
|
376
387
|
export declare function decodePositionalObject(qi: BuilderCtx, arr: unknown, shape: RelationShape): unknown;
|
|
388
|
+
/**
|
|
389
|
+
* Compile a top-level positional parser for `shapes`, or `undefined` when any
|
|
390
|
+
* part of it is a shape the plan does not take (the caller then uses
|
|
391
|
+
* {@link makeReferencePositionalParser}). Rows are identical to the
|
|
392
|
+
* reference's on every shape it does take.
|
|
393
|
+
*/
|
|
394
|
+
export declare function makePositionalRowParser(qi: BuilderCtx, table: string, shapes: Record<string, RelationShape>): ((row: Record<string, unknown>) => Record<string, unknown>) | undefined;
|
|
377
395
|
/** One relation `'flatten'` declined, with the reason, for the dev warning. */
|
|
378
396
|
export interface FlattenReject {
|
|
379
397
|
/** Dotted path from the root table's `with`, e.g. `order.customer`. */
|