turbine-orm 0.50.0 → 0.51.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 +66 -66
- package/dist/adapters/cockroachdb.d.ts +5 -5
- package/dist/adapters/cockroachdb.js +10 -10
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/yugabytedb.d.ts +7 -7
- package/dist/adapters/yugabytedb.js +10 -10
- package/dist/cjs/adapters/cockroachdb.d.ts +5 -5
- package/dist/cjs/adapters/cockroachdb.js +10 -10
- package/dist/cjs/adapters/index.d.ts +5 -5
- package/dist/cjs/adapters/index.js +7 -7
- package/dist/cjs/adapters/yugabytedb.d.ts +7 -7
- package/dist/cjs/adapters/yugabytedb.js +10 -10
- package/dist/cjs/cli/config.d.ts +13 -2
- package/dist/cjs/cli/config.js +3 -2
- package/dist/cjs/cli/destructive.d.ts +1 -1
- package/dist/cjs/cli/destructive.js +1 -1
- package/dist/cjs/cli/index.d.ts +10 -10
- package/dist/cjs/cli/index.js +49 -45
- package/dist/cjs/cli/loader.d.ts +7 -7
- package/dist/cjs/cli/loader.js +9 -9
- package/dist/cjs/cli/mcp.js +4 -4
- package/dist/cjs/cli/migrate.d.ts +5 -5
- package/dist/cjs/cli/migrate.js +11 -11
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/ui.d.ts +2 -2
- package/dist/cjs/cli/ui.js +2 -2
- package/dist/cjs/client.d.ts +49 -38
- package/dist/cjs/client.js +57 -56
- package/dist/cjs/dialect.d.ts +62 -18
- package/dist/cjs/dialect.js +40 -2
- package/dist/cjs/errors.d.ts +5 -5
- package/dist/cjs/errors.js +11 -11
- package/dist/cjs/generate.d.ts +6 -6
- package/dist/cjs/generate.js +31 -29
- package/dist/cjs/index-advisor.d.ts +5 -5
- package/dist/cjs/index-advisor.js +0 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +7 -7
- package/dist/cjs/introspect.d.ts +35 -9
- package/dist/cjs/introspect.js +83 -32
- package/dist/cjs/mssql.d.ts +11 -11
- package/dist/cjs/mssql.js +64 -29
- package/dist/cjs/mysql.d.ts +8 -8
- package/dist/cjs/mysql.js +61 -23
- package/dist/cjs/nested-write.d.ts +21 -2
- package/dist/cjs/nested-write.js +51 -14
- package/dist/cjs/optional-peer-import.cjs +7 -7
- package/dist/cjs/optional-peer-import.d.cts +7 -7
- package/dist/cjs/pipeline-submittable.d.ts +2 -2
- package/dist/cjs/pipeline-submittable.js +6 -6
- package/dist/cjs/pipeline.d.ts +1 -1
- package/dist/cjs/pipeline.js +4 -4
- package/dist/cjs/powdb-introspect.d.ts +1 -1
- package/dist/cjs/powdb-introspect.js +1 -1
- package/dist/cjs/powdb.d.ts +28 -28
- package/dist/cjs/powdb.js +66 -66
- package/dist/cjs/powql.d.ts +27 -27
- package/dist/cjs/powql.js +73 -52
- package/dist/cjs/query/aggregates.d.ts +1 -1
- package/dist/cjs/query/aggregates.js +5 -5
- package/dist/cjs/query/batched-loader.d.ts +11 -11
- package/dist/cjs/query/batched-loader.js +24 -24
- package/dist/cjs/query/builder.d.ts +39 -21
- package/dist/cjs/query/builder.js +99 -57
- package/dist/cjs/query/compound-unique.d.ts +1 -1
- package/dist/cjs/query/compound-unique.js +0 -0
- package/dist/cjs/query/deferred.d.ts +12 -6
- package/dist/cjs/query/deferred.js +1 -1
- package/dist/cjs/query/filters.d.ts +31 -11
- package/dist/cjs/query/filters.js +67 -14
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/index.js +1 -1
- package/dist/cjs/query/relations.d.ts +9 -9
- package/dist/cjs/query/relations.js +164 -57
- package/dist/cjs/query/types.d.ts +86 -35
- package/dist/cjs/query/types.js +1 -1
- package/dist/cjs/query/utils.d.ts +27 -10
- package/dist/cjs/query/utils.js +86 -14
- package/dist/cjs/query/where.d.ts +47 -28
- package/dist/cjs/query/where.js +130 -31
- package/dist/cjs/query/writes.d.ts +24 -5
- package/dist/cjs/query/writes.js +102 -13
- package/dist/cjs/realtime.d.ts +7 -7
- package/dist/cjs/realtime.js +9 -9
- package/dist/cjs/schema-builder.d.ts +18 -7
- package/dist/cjs/schema-builder.js +17 -10
- package/dist/cjs/schema-metadata.d.ts +3 -3
- package/dist/cjs/schema-metadata.js +9 -9
- package/dist/cjs/schema-sql.d.ts +9 -9
- package/dist/cjs/schema-sql.js +20 -20
- package/dist/cjs/schema.d.ts +19 -9
- package/dist/cjs/schema.js +6 -6
- package/dist/cjs/serverless.d.ts +15 -15
- package/dist/cjs/serverless.js +16 -16
- package/dist/cjs/sqlite.d.ts +8 -8
- package/dist/cjs/sqlite.js +53 -22
- package/dist/cjs/typed-sql.d.ts +4 -4
- package/dist/cjs/typed-sql.js +5 -5
- package/dist/cli/config.d.ts +13 -2
- package/dist/cli/config.js +3 -2
- package/dist/cli/destructive.d.ts +1 -1
- package/dist/cli/destructive.js +1 -1
- package/dist/cli/index.d.ts +10 -10
- package/dist/cli/index.js +49 -45
- package/dist/cli/loader.d.ts +7 -7
- package/dist/cli/loader.js +9 -9
- package/dist/cli/mcp.js +4 -4
- package/dist/cli/migrate.d.ts +5 -5
- package/dist/cli/migrate.js +11 -11
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/ui.d.ts +2 -2
- package/dist/cli/ui.js +2 -2
- package/dist/client.d.ts +49 -38
- package/dist/client.js +57 -56
- package/dist/dialect.d.ts +62 -18
- package/dist/dialect.js +40 -2
- package/dist/errors.d.ts +5 -5
- package/dist/errors.js +11 -11
- package/dist/generate.d.ts +6 -6
- package/dist/generate.js +31 -29
- package/dist/index-advisor.d.ts +5 -5
- package/dist/index-advisor.js +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/introspect.d.ts +35 -9
- package/dist/introspect.js +82 -32
- package/dist/mssql.d.ts +11 -11
- package/dist/mssql.js +64 -29
- package/dist/mysql.d.ts +8 -8
- package/dist/mysql.js +61 -23
- package/dist/nested-write.d.ts +21 -2
- package/dist/nested-write.js +51 -14
- package/dist/optional-peer-import.cjs +7 -7
- package/dist/optional-peer-import.d.cts +7 -7
- package/dist/pipeline-submittable.d.ts +2 -2
- package/dist/pipeline-submittable.js +6 -6
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +4 -4
- package/dist/powdb-introspect.d.ts +1 -1
- package/dist/powdb-introspect.js +1 -1
- package/dist/powdb.d.ts +28 -28
- package/dist/powdb.js +66 -66
- package/dist/powql.d.ts +27 -27
- package/dist/powql.js +73 -52
- package/dist/query/aggregates.d.ts +1 -1
- package/dist/query/aggregates.js +5 -5
- package/dist/query/batched-loader.d.ts +11 -11
- package/dist/query/batched-loader.js +24 -24
- package/dist/query/builder.d.ts +39 -21
- package/dist/query/builder.js +100 -58
- package/dist/query/compound-unique.d.ts +1 -1
- package/dist/query/compound-unique.js +0 -0
- package/dist/query/deferred.d.ts +12 -6
- package/dist/query/deferred.js +1 -1
- package/dist/query/filters.d.ts +31 -11
- package/dist/query/filters.js +66 -13
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/relations.d.ts +9 -9
- package/dist/query/relations.js +165 -58
- package/dist/query/types.d.ts +86 -35
- package/dist/query/types.js +1 -1
- package/dist/query/utils.d.ts +27 -10
- package/dist/query/utils.js +84 -14
- package/dist/query/where.d.ts +47 -28
- package/dist/query/where.js +129 -32
- package/dist/query/writes.d.ts +24 -5
- package/dist/query/writes.js +101 -13
- package/dist/realtime.d.ts +7 -7
- package/dist/realtime.js +9 -9
- package/dist/schema-builder.d.ts +18 -7
- package/dist/schema-builder.js +17 -10
- package/dist/schema-metadata.d.ts +3 -3
- package/dist/schema-metadata.js +9 -9
- package/dist/schema-sql.d.ts +9 -9
- package/dist/schema-sql.js +20 -20
- package/dist/schema.d.ts +19 -9
- package/dist/schema.js +6 -6
- package/dist/serverless.d.ts +15 -15
- package/dist/serverless.js +16 -16
- package/dist/sqlite.d.ts +8 -8
- package/dist/sqlite.js +53 -22
- package/dist/typed-sql.d.ts +4 -4
- package/dist/typed-sql.js +5 -5
- package/package.json +2 -2
package/dist/nested-write.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm
|
|
2
|
+
* turbine-orm, Nested write engine
|
|
3
3
|
*
|
|
4
4
|
* Tree-walking create/update that resolves relation fields in `data` into
|
|
5
5
|
* batched SQL operations within a transaction. Supports create, connect,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* This module is imported by `query/builder.ts` when the `data` argument
|
|
10
10
|
* of `create()` or `update()` contains relation fields. It never imports
|
|
11
|
-
* `client.ts` directly
|
|
11
|
+
* `client.ts` directly, the transaction handle is passed in via
|
|
12
12
|
* `NestedWriteContext`.
|
|
13
13
|
*/
|
|
14
14
|
import { CircularRelationError, describeTargetForMessage, NotFoundError, RelationError, UnsupportedFeatureError, ValidationError, } from './errors.js';
|
|
@@ -242,7 +242,7 @@ const M2M_SUPPORTED_OPS = new Set(['connect', 'disconnect', 'set']);
|
|
|
242
242
|
* On the core client the auto-created property accessors are CAMELCASED
|
|
243
243
|
* (`db.userTags` for `user_tags`, see the `Object.defineProperty` loops in
|
|
244
244
|
* client.ts), so `db["user_tags"]` is `undefined` for every snake_case junction
|
|
245
|
-
*
|
|
245
|
+
* - which is every junction `turbine pull` produces over a hand-written join
|
|
246
246
|
* table. `db.table("user_tags")` takes the raw table name and exists on both
|
|
247
247
|
* TurbineClient and the TransactionClient handed to `$transaction`, so that is
|
|
248
248
|
* the form to recommend.
|
|
@@ -469,9 +469,9 @@ const SKIP_DUPLICATES_FEATURE = 'createMany({ skipDuplicates: true })';
|
|
|
469
469
|
* `createMany({ skipDuplicates })`, so the engines that refuse it pay for the
|
|
470
470
|
* refusal once instead of on every connect.
|
|
471
471
|
*
|
|
472
|
-
* The capability is not reachable from here as a flag
|
|
472
|
+
* The capability is not reachable from here as a flag, the `Dialect` is private
|
|
473
473
|
* to QueryInterface / TurbineClient and `NestedWriteContext` carries only the
|
|
474
|
-
* schema and the transaction
|
|
474
|
+
* schema and the transaction, so it is read from the engine's OWN structured
|
|
475
475
|
* refusal (`UnsupportedFeatureError.feature`) rather than from a hardcoded
|
|
476
476
|
* engine list that would drift. Both refusing engines throw while BUILDING the
|
|
477
477
|
* statement, before anything is written, so the attempt is side-effect-free.
|
|
@@ -509,11 +509,11 @@ async function insertJunctionRowsSkippingDuplicates(ctx, plan, values) {
|
|
|
509
509
|
*
|
|
510
510
|
* 1. Junction constrains the pair AND the engine supports `skipDuplicates`
|
|
511
511
|
* (PostgreSQL, SQLite and MySQL: `ON CONFLICT DO NOTHING` / a no-op
|
|
512
|
-
* `ON DUPLICATE KEY UPDATE`)
|
|
512
|
+
* `ON DUPLICATE KEY UPDATE`), one INSERT, no read. The engine resolves the
|
|
513
513
|
* conflict, so two transactions connecting the same pair at the same time
|
|
514
514
|
* both succeed and one link row exists. This is the introspected path:
|
|
515
515
|
* every junction introspection can detect declares the pair unique.
|
|
516
|
-
* 2. Otherwise
|
|
516
|
+
* 2. Otherwise, read this parent's existing link rows for exactly these targets
|
|
517
517
|
* and insert only the missing ones. Used when the engine refuses the option
|
|
518
518
|
* (SQL Server and PowDB have no single-statement skip-duplicates form and
|
|
519
519
|
* throw E017), and when the junction does NOT constrain the pair, where
|
|
@@ -658,7 +658,7 @@ export async function executeNestedCreate(ctx, tableName, data, depth = 0, path
|
|
|
658
658
|
validateOps(relName, ops, false);
|
|
659
659
|
}
|
|
660
660
|
// belongsTo relations put the foreign key on the PARENT row, so they must be
|
|
661
|
-
// resolved BEFORE the parent is inserted
|
|
661
|
+
// resolved BEFORE the parent is inserted, otherwise a NOT NULL FK column
|
|
662
662
|
// fails on the initial INSERT. We resolve each belongsTo op (create/connect/
|
|
663
663
|
// connectOrCreate) to its referenced row and fold the FK values into the
|
|
664
664
|
// parent's own INSERT.
|
|
@@ -679,7 +679,7 @@ export async function executeNestedCreate(ctx, tableName, data, depth = 0, path
|
|
|
679
679
|
const parentRow = (await ctx.tx.table(tableName).create({
|
|
680
680
|
data: { ...scalars, ...belongsToFks },
|
|
681
681
|
}));
|
|
682
|
-
// Process hasMany / hasOne relations
|
|
682
|
+
// Process hasMany / hasOne relations, their FK lives on the CHILD, so they
|
|
683
683
|
// need the parent row to exist first.
|
|
684
684
|
for (const [relName, ops] of Object.entries(relations)) {
|
|
685
685
|
const rel = tableMeta.relations[relName];
|
|
@@ -746,7 +746,7 @@ export async function executeNestedUpdate(ctx, tableName, where, data, depth = 0
|
|
|
746
746
|
for (const [relName, ops] of Object.entries(relations)) {
|
|
747
747
|
const rel = tableMeta.relations[relName];
|
|
748
748
|
if (rel.type === 'hasMany' || rel.type === 'hasOne') {
|
|
749
|
-
// create, connect, connectOrCreate
|
|
749
|
+
// create, connect, connectOrCreate, same as nested create
|
|
750
750
|
await processHasManyCreate(ctx, rel, ops, parentRow, depth, path, relName);
|
|
751
751
|
// disconnect
|
|
752
752
|
if (ops.disconnect !== undefined) {
|
|
@@ -771,7 +771,7 @@ export async function executeNestedUpdate(ctx, tableName, where, data, depth = 0
|
|
|
771
771
|
}
|
|
772
772
|
else if (rel.type === 'belongsTo') {
|
|
773
773
|
await processBelongsToCreate(ctx, rel, ops, parentRow, tableName, depth, path, relName);
|
|
774
|
-
// update (belongsTo
|
|
774
|
+
// update (belongsTo, derive where from parent FK)
|
|
775
775
|
if (ops.update !== undefined) {
|
|
776
776
|
await processBelongsToUpdate(ctx, rel, ops.update, parentRow, tableName);
|
|
777
777
|
}
|
|
@@ -834,7 +834,7 @@ async function processHasManyCreate(ctx, rel, ops, parentRow, depth, path, relNa
|
|
|
834
834
|
}
|
|
835
835
|
}
|
|
836
836
|
else {
|
|
837
|
-
// Batch via createMany (UNNEST)
|
|
837
|
+
// Batch via createMany (UNNEST), fast path
|
|
838
838
|
const injected = items.map((item) => injectForeignKey(item, rel, parentRow, ctx.schema));
|
|
839
839
|
await ctx.tx.table(rel.to).createMany({ data: injected });
|
|
840
840
|
}
|
|
@@ -914,7 +914,7 @@ async function resolveBelongsToForCreate(ctx, rel, ops, parentTable, depth, path
|
|
|
914
914
|
async function processBelongsToCreate(ctx, rel, ops, parentRow, parentTable, depth, path, relName) {
|
|
915
915
|
const fks = normalizeKeyColumns(rel.foreignKey);
|
|
916
916
|
const refs = normalizeKeyColumns(rel.referenceKey);
|
|
917
|
-
// create
|
|
917
|
+
// create, insert the related row, then update parent's FK
|
|
918
918
|
if (ops.create !== undefined) {
|
|
919
919
|
const items = toArray(ops.create);
|
|
920
920
|
if (items.length > 0) {
|
|
@@ -933,7 +933,7 @@ async function processBelongsToCreate(ctx, rel, ops, parentRow, parentTable, dep
|
|
|
933
933
|
});
|
|
934
934
|
}
|
|
935
935
|
}
|
|
936
|
-
// connect
|
|
936
|
+
// connect, validate existence, update parent's FK
|
|
937
937
|
if (ops.connect !== undefined) {
|
|
938
938
|
const items = toArray(ops.connect);
|
|
939
939
|
if (items.length > 0) {
|
|
@@ -960,6 +960,41 @@ async function processBelongsToCreate(ctx, rel, ops, parentRow, parentTable, dep
|
|
|
960
960
|
// ---------------------------------------------------------------------------
|
|
961
961
|
// connect, connectOrCreate, disconnect, set, delete helpers
|
|
962
962
|
// ---------------------------------------------------------------------------
|
|
963
|
+
/**
|
|
964
|
+
* Refuse a connect that would take a to-many child away from another parent,
|
|
965
|
+
* when {@link NestedWriteContext.scopedConnect} is on. See that field for why.
|
|
966
|
+
*
|
|
967
|
+
* Compares the child's CURRENT foreign key against the parent's reference
|
|
968
|
+
* value. Null (unowned) passes, an exact match passes (idempotent re-connect),
|
|
969
|
+
* anything else is refused. Values are compared after normalizing to a
|
|
970
|
+
* primitive, so a bigint FK read back as a string cannot look like a mismatch
|
|
971
|
+
* against the number the parent write returned.
|
|
972
|
+
*/
|
|
973
|
+
function assertConnectInScope(ctx, rel, child, parentRow, target) {
|
|
974
|
+
if (!ctx.scopedConnect)
|
|
975
|
+
return;
|
|
976
|
+
if (rel.type !== 'hasMany' && rel.type !== 'hasOne')
|
|
977
|
+
return;
|
|
978
|
+
const childTable = ctx.schema.tables[rel.to];
|
|
979
|
+
const parentTable = ctx.schema.tables[rel.from];
|
|
980
|
+
if (!childTable)
|
|
981
|
+
return;
|
|
982
|
+
const fks = normalizeKeyColumns(rel.foreignKey);
|
|
983
|
+
const refs = normalizeKeyColumns(rel.referenceKey);
|
|
984
|
+
const key = (v) => (v === null || v === undefined ? null : String(v));
|
|
985
|
+
for (let i = 0; i < fks.length; i++) {
|
|
986
|
+
const fkField = childTable.reverseColumnMap[fks[i]] ?? fks[i];
|
|
987
|
+
const refField = parentTable?.reverseColumnMap[refs[i]] ?? refs[i];
|
|
988
|
+
const current = key(child[fkField]);
|
|
989
|
+
if (current === null)
|
|
990
|
+
continue;
|
|
991
|
+
if (current === key(parentRow[refField]))
|
|
992
|
+
continue;
|
|
993
|
+
throw new ValidationError(`[turbine] connect refused: ${rel.to} row ${describeTargetForMessage(target)} is already owned by ` +
|
|
994
|
+
`another "${rel.from}" (its ${fks[i]} is ${current}). \`scopedConnect\` only allows connecting a row ` +
|
|
995
|
+
`that is unowned or already owned by this parent; re-parenting must be an explicit update.`);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
963
998
|
async function batchConnect(ctx, rel, items, parentRow) {
|
|
964
999
|
const fks = normalizeKeyColumns(rel.foreignKey);
|
|
965
1000
|
const refs = normalizeKeyColumns(rel.referenceKey);
|
|
@@ -972,6 +1007,7 @@ async function batchConnect(ctx, rel, items, parentRow) {
|
|
|
972
1007
|
if (!existing) {
|
|
973
1008
|
throw new ValidationError(`[turbine] connect: no ${rel.to} row found matching ${describeTargetForMessage(target)}.`);
|
|
974
1009
|
}
|
|
1010
|
+
assertConnectInScope(ctx, rel, existing, parentRow, target);
|
|
975
1011
|
}
|
|
976
1012
|
// Build FK update data to point children at parent
|
|
977
1013
|
const updateData = {};
|
|
@@ -999,6 +1035,7 @@ async function connectOrCreate(ctx, rel, op, parentRow) {
|
|
|
999
1035
|
row = (await ctx.tx.table(rel.to).create({ data: injected }));
|
|
1000
1036
|
}
|
|
1001
1037
|
else {
|
|
1038
|
+
assertConnectInScope(ctx, rel, row, parentRow, op.where);
|
|
1002
1039
|
// Update FK to point to parent
|
|
1003
1040
|
const updateData = {};
|
|
1004
1041
|
for (let i = 0; i < fks.length; i++) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* True dynamic `import()` for the optional peer dependencies (`mysql2`,
|
|
4
|
-
* `mssql`, `@zvndev/powdb-client`, `@zvndev/powdb-embedded`)
|
|
4
|
+
* `mssql`, `@zvndev/powdb-client`, `@zvndev/powdb-embedded`), safe in BOTH
|
|
5
5
|
* build outputs, including for peers that are ESM-only.
|
|
6
6
|
*
|
|
7
7
|
* THE PROBLEM THIS FILE SOLVES (the `@zvndev/powdb-client` ≥ 0.9 CJS break):
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* peers stay out of the static graph. The ESM build (`tsconfig.json`, module
|
|
10
10
|
* NodeNext) emits that `import()` verbatim. The CJS build (`tsconfig.cjs.json`,
|
|
11
11
|
* module CommonJS) however TRANSPILES `import()` into
|
|
12
|
-
* `Promise.resolve().then(() => require(...))
|
|
12
|
+
* `Promise.resolve().then(() => require(...))`, and `require()` of an
|
|
13
13
|
* ESM-only package (no `require` export condition, e.g. powdb-client ≥ 0.9)
|
|
14
14
|
* throws `ERR_PACKAGE_PATH_NOT_EXPORTED`, breaking every CJS consumer.
|
|
15
15
|
*
|
|
@@ -18,18 +18,18 @@
|
|
|
18
18
|
* says `"type": "module"`, so NodeNext would classify every `.ts` source as
|
|
19
19
|
* ESM and emit ESM into dist/cjs). A `.cts` file is the escape hatch: it is
|
|
20
20
|
* CommonJS-format by extension regardless of package `type`, so under the ESM
|
|
21
|
-
* pass (NodeNext) it compiles to `dist/optional-peer-import.cjs
|
|
21
|
+
* pass (NodeNext) it compiles to `dist/optional-peer-import.cjs`, a CommonJS
|
|
22
22
|
* file whose `import()` SURVIVES transpilation (NodeNext preserves dynamic
|
|
23
23
|
* import in CJS files precisely because it is the only way CJS can load ESM).
|
|
24
24
|
*
|
|
25
25
|
* That gives the published package two copies of this module:
|
|
26
|
-
* - `dist/optional-peer-import.cjs` (ESM pass, NodeNext)
|
|
27
|
-
* - `dist/cjs/optional-peer-import.cjs` (CJS pass, CommonJS)
|
|
26
|
+
* - `dist/optional-peer-import.cjs` (ESM pass, NodeNext) , real `import()`
|
|
27
|
+
* - `dist/cjs/optional-peer-import.cjs` (CJS pass, CommonJS) , lowered to `require()`
|
|
28
28
|
*
|
|
29
29
|
* The lowered copy works fine for CJS-loadable peers (`mysql2`, `mssql`, older
|
|
30
30
|
* powdb peers). When it hits an ESM-only peer, the `require()` fails with a
|
|
31
31
|
* recognizable code and this function falls back to delegating the load to the
|
|
32
|
-
* sibling NodeNext copy one directory up (`../optional-peer-import.cjs`)
|
|
32
|
+
* sibling NodeNext copy one directory up (`../optional-peer-import.cjs`) -
|
|
33
33
|
* which is a plain CommonJS file (loadable by `require()` on every supported
|
|
34
34
|
* Node) whose real `import()` then loads the ESM peer. The ESM-pass copy has
|
|
35
35
|
* no such sibling; its lazy `require` throws and the original error surfaces,
|
|
@@ -60,7 +60,7 @@ function isEsmOnlyLoadError(err) {
|
|
|
60
60
|
* transpilation (see the module doc comment).
|
|
61
61
|
*
|
|
62
62
|
* @param specifier bare package specifier (e.g. `'@zvndev/powdb-client'`).
|
|
63
|
-
* @param allowEsmFallback internal recursion guard
|
|
63
|
+
* @param allowEsmFallback internal recursion guard, the delegated call passes
|
|
64
64
|
* `false` so a failure in the sibling copy can never bounce back.
|
|
65
65
|
*/
|
|
66
66
|
async function importOptionalPeer(specifier, allowEsmFallback = true) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* True dynamic `import()` for the optional peer dependencies (`mysql2`,
|
|
3
|
-
* `mssql`, `@zvndev/powdb-client`, `@zvndev/powdb-embedded`)
|
|
3
|
+
* `mssql`, `@zvndev/powdb-client`, `@zvndev/powdb-embedded`), safe in BOTH
|
|
4
4
|
* build outputs, including for peers that are ESM-only.
|
|
5
5
|
*
|
|
6
6
|
* THE PROBLEM THIS FILE SOLVES (the `@zvndev/powdb-client` ≥ 0.9 CJS break):
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* peers stay out of the static graph. The ESM build (`tsconfig.json`, module
|
|
9
9
|
* NodeNext) emits that `import()` verbatim. The CJS build (`tsconfig.cjs.json`,
|
|
10
10
|
* module CommonJS) however TRANSPILES `import()` into
|
|
11
|
-
* `Promise.resolve().then(() => require(...))
|
|
11
|
+
* `Promise.resolve().then(() => require(...))`, and `require()` of an
|
|
12
12
|
* ESM-only package (no `require` export condition, e.g. powdb-client ≥ 0.9)
|
|
13
13
|
* throws `ERR_PACKAGE_PATH_NOT_EXPORTED`, breaking every CJS consumer.
|
|
14
14
|
*
|
|
@@ -17,18 +17,18 @@
|
|
|
17
17
|
* says `"type": "module"`, so NodeNext would classify every `.ts` source as
|
|
18
18
|
* ESM and emit ESM into dist/cjs). A `.cts` file is the escape hatch: it is
|
|
19
19
|
* CommonJS-format by extension regardless of package `type`, so under the ESM
|
|
20
|
-
* pass (NodeNext) it compiles to `dist/optional-peer-import.cjs
|
|
20
|
+
* pass (NodeNext) it compiles to `dist/optional-peer-import.cjs`, a CommonJS
|
|
21
21
|
* file whose `import()` SURVIVES transpilation (NodeNext preserves dynamic
|
|
22
22
|
* import in CJS files precisely because it is the only way CJS can load ESM).
|
|
23
23
|
*
|
|
24
24
|
* That gives the published package two copies of this module:
|
|
25
|
-
* - `dist/optional-peer-import.cjs` (ESM pass, NodeNext)
|
|
26
|
-
* - `dist/cjs/optional-peer-import.cjs` (CJS pass, CommonJS)
|
|
25
|
+
* - `dist/optional-peer-import.cjs` (ESM pass, NodeNext) , real `import()`
|
|
26
|
+
* - `dist/cjs/optional-peer-import.cjs` (CJS pass, CommonJS) , lowered to `require()`
|
|
27
27
|
*
|
|
28
28
|
* The lowered copy works fine for CJS-loadable peers (`mysql2`, `mssql`, older
|
|
29
29
|
* powdb peers). When it hits an ESM-only peer, the `require()` fails with a
|
|
30
30
|
* recognizable code and this function falls back to delegating the load to the
|
|
31
|
-
* sibling NodeNext copy one directory up (`../optional-peer-import.cjs`)
|
|
31
|
+
* sibling NodeNext copy one directory up (`../optional-peer-import.cjs`) -
|
|
32
32
|
* which is a plain CommonJS file (loadable by `require()` on every supported
|
|
33
33
|
* Node) whose real `import()` then loads the ESM peer. The ESM-pass copy has
|
|
34
34
|
* no such sibling; its lazy `require` throws and the original error surfaces,
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
* transpilation (see the module doc comment).
|
|
47
47
|
*
|
|
48
48
|
* @param specifier bare package specifier (e.g. `'@zvndev/powdb-client'`).
|
|
49
|
-
* @param allowEsmFallback internal recursion guard
|
|
49
|
+
* @param allowEsmFallback internal recursion guard, the delegated call passes
|
|
50
50
|
* `false` so a failure in the sibling copy can never bounce back.
|
|
51
51
|
*/
|
|
52
52
|
declare function importOptionalPeer(specifier: string, allowEsmFallback?: boolean): Promise<unknown>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm
|
|
2
|
+
* turbine-orm, Real Postgres pipeline protocol implementation
|
|
3
3
|
*
|
|
4
4
|
* Uses the pg extended-query protocol wire methods (parse/bind/describe/execute/sync)
|
|
5
5
|
* exposed on pg.Client's Connection object to send multiple queries in a single
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import type { EventEmitter } from 'node:events';
|
|
20
20
|
import type { DeferredQuery } from './query/index.js';
|
|
21
|
-
/** The pg Connection object
|
|
21
|
+
/** The pg Connection object, an EventEmitter with wire-protocol methods */
|
|
22
22
|
export interface PgConnection extends EventEmitter {
|
|
23
23
|
stream: {
|
|
24
24
|
cork?: () => void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm
|
|
2
|
+
* turbine-orm, Real Postgres pipeline protocol implementation
|
|
3
3
|
*
|
|
4
4
|
* Uses the pg extended-query protocol wire methods (parse/bind/describe/execute/sync)
|
|
5
5
|
* exposed on pg.Client's Connection object to send multiple queries in a single
|
|
@@ -108,7 +108,7 @@ export async function runPipelined(client, queries, options = {}) {
|
|
|
108
108
|
for (let i = 0; i < queries.length; i++) {
|
|
109
109
|
results.push(new Result(undefined, client._types));
|
|
110
110
|
}
|
|
111
|
-
// commandComplete counter
|
|
111
|
+
// commandComplete counter, tracks position across all commands
|
|
112
112
|
let commandIndex = 0;
|
|
113
113
|
// readyForQuery counter
|
|
114
114
|
let rfqCount = 0;
|
|
@@ -186,7 +186,7 @@ export async function runPipelined(client, queries, options = {}) {
|
|
|
186
186
|
reject(pipelineError);
|
|
187
187
|
return;
|
|
188
188
|
}
|
|
189
|
-
// All succeeded
|
|
189
|
+
// All succeeded, transform results
|
|
190
190
|
try {
|
|
191
191
|
const transformed = [];
|
|
192
192
|
for (let i = 0; i < queries.length; i++) {
|
|
@@ -203,13 +203,13 @@ export async function runPipelined(client, queries, options = {}) {
|
|
|
203
203
|
// Event handlers
|
|
204
204
|
// -----------------------------------------------------------------------
|
|
205
205
|
function onParseComplete() {
|
|
206
|
-
// No action needed
|
|
206
|
+
// No action needed, anonymous prepared statements
|
|
207
207
|
}
|
|
208
208
|
function onBindComplete() {
|
|
209
209
|
// No action needed
|
|
210
210
|
}
|
|
211
211
|
function onNoData() {
|
|
212
|
-
// DML without RETURNING
|
|
212
|
+
// DML without RETURNING, no RowDescription follows. Fine.
|
|
213
213
|
}
|
|
214
214
|
function onRowDescription(msg) {
|
|
215
215
|
const qIdx = currentQueryIndex();
|
|
@@ -306,7 +306,7 @@ export async function runPipelined(client, queries, options = {}) {
|
|
|
306
306
|
}, timeout);
|
|
307
307
|
}
|
|
308
308
|
// -----------------------------------------------------------------------
|
|
309
|
-
// Send protocol messages
|
|
309
|
+
// Send protocol messages, all in one TCP flush
|
|
310
310
|
// -----------------------------------------------------------------------
|
|
311
311
|
try {
|
|
312
312
|
if (connection.stream.cork) {
|
package/dist/pipeline.d.ts
CHANGED
package/dist/pipeline.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm
|
|
2
|
+
* turbine-orm, Pipeline execution
|
|
3
3
|
*
|
|
4
4
|
* Pipelines batch multiple independent queries into a single database round-trip.
|
|
5
5
|
* Instead of N sequential awaits (N round-trips), you get 1 round-trip for all N queries.
|
|
@@ -86,11 +86,11 @@ export async function executePipeline(pool, queries, options) {
|
|
|
86
86
|
if (queries.length === 0) {
|
|
87
87
|
return [];
|
|
88
88
|
}
|
|
89
|
-
// Acquire a single client
|
|
89
|
+
// Acquire a single client, reused for both capability check and execution
|
|
90
90
|
const client = await pool.connect();
|
|
91
91
|
try {
|
|
92
92
|
if (supportsExtendedPipeline(client)) {
|
|
93
|
-
// Real pipeline path
|
|
93
|
+
// Real pipeline path, uses extended-query protocol wire methods
|
|
94
94
|
const pipelineOptions = {
|
|
95
95
|
transactional: options?.transactional ?? true,
|
|
96
96
|
timeout: options?.timeout,
|
|
@@ -98,7 +98,7 @@ export async function executePipeline(pool, queries, options) {
|
|
|
98
98
|
const results = await runPipelined(client, queries, pipelineOptions);
|
|
99
99
|
return results;
|
|
100
100
|
}
|
|
101
|
-
// Sequential fallback
|
|
101
|
+
// Sequential fallback, reuses the same client
|
|
102
102
|
return await runSequential(client, queries, options);
|
|
103
103
|
}
|
|
104
104
|
finally {
|
package/dist/powdb-introspect.js
CHANGED
package/dist/powdb.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm/powdb
|
|
2
|
+
* turbine-orm/powdb, Turbine's PowDB / PowQL backend.
|
|
3
3
|
*
|
|
4
4
|
* PowDB is a single-node embedded database with its own query language, **PowQL**
|
|
5
5
|
* (not SQL), reached over `@zvndev/powdb-client`'s binary TCP protocol. PowDB is a
|
|
@@ -10,26 +10,26 @@
|
|
|
10
10
|
*
|
|
11
11
|
* PowDB realities shape the design (all verified firsthand against a live
|
|
12
12
|
* `powdb-server` / the embedded addon, see `docs/internal/strategy/powdb-parity-matrix.md`):
|
|
13
|
-
* - **`RETURNING` (since 0.7.0)
|
|
13
|
+
* - **`RETURNING` (since 0.7.0)**, `create/createMany/update/delete` append the
|
|
14
14
|
* trailing `returning` keyword (`RETURNING *`, all columns) and read the
|
|
15
15
|
* affected rows back in one round-trip. `upsert` is the lone exception (its
|
|
16
16
|
* statement rejects `returning`) and reselects by primary key.
|
|
17
|
-
* - **No generated IDs
|
|
17
|
+
* - **No generated IDs**, the app must supply every value → Turbine generates a
|
|
18
18
|
* client-side UUID for the primary key when it has a default.
|
|
19
19
|
* - **`uuid`/`datetime`/`bytes` columns can't hold client-supplied values** (no
|
|
20
20
|
* literal, no working cast on the wire) → Turbine maps everything onto the four
|
|
21
21
|
* writable types (`str`/`int`/`float`/`bool`); `Date` → `int` epoch micros;
|
|
22
22
|
* `string` PKs hold UUID strings.
|
|
23
|
-
* - **No JSON aggregation / link navigation
|
|
23
|
+
* - **No JSON aggregation / link navigation**, single-query nested `with` is
|
|
24
24
|
* impossible → it degrades to batched N+1 loaders (Phase B).
|
|
25
|
-
* - **Single global write lock; no savepoints/isolation
|
|
25
|
+
* - **Single global write lock; no savepoints/isolation**, nested
|
|
26
26
|
* transactions / isolation / vector / LISTEN-NOTIFY / RLS throw.
|
|
27
27
|
* Independent concurrent `db.$transaction` calls do NOT throw: they queue
|
|
28
28
|
* FIFO on a pool-level gate and run one at a time (see {@link PowdbTxGate}).
|
|
29
|
-
* Only a *re-entrant* transaction
|
|
29
|
+
* Only a *re-entrant* transaction, a `db.$transaction` opened from inside
|
|
30
30
|
* an active transaction callback's async context, which queueing would
|
|
31
|
-
* deadlock
|
|
32
|
-
* - **The wire protocol pipelines
|
|
31
|
+
* deadlock, fails fast with E017.
|
|
32
|
+
* - **The wire protocol pipelines**, `@zvndev/powdb-client` writes each
|
|
33
33
|
* request frame immediately and matches replies FIFO, so multiple queries
|
|
34
34
|
* may be in flight on one connection. {@link PowdbPool}'s checked-out
|
|
35
35
|
* clients advertise `supportsPipelining`, which lets the batch
|
|
@@ -211,7 +211,7 @@ interface PowdbModule {
|
|
|
211
211
|
message: string;
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
|
-
/** Connection options for {@link turbinePowDB}
|
|
214
|
+
/** Connection options for {@link turbinePowDB}, host/port, not a connection string. */
|
|
215
215
|
export interface PowdbConnOptions {
|
|
216
216
|
host: string;
|
|
217
217
|
port: number;
|
|
@@ -258,7 +258,7 @@ export interface PowdbCapabilities {
|
|
|
258
258
|
/** ≥ 0.13: server-side joins, hash-accelerated and bounded. */
|
|
259
259
|
serverJoins: boolean;
|
|
260
260
|
/**
|
|
261
|
-
* ≥ 0.18: nested projections (shaped results)
|
|
261
|
+
* ≥ 0.18: nested projections (shaped results), a projection field may be a
|
|
262
262
|
* whole correlated child query returning a per-parent JSON array. When set,
|
|
263
263
|
* eligible `with` clauses compile into the parent statement instead of the
|
|
264
264
|
* batched loaders.
|
|
@@ -367,7 +367,7 @@ export declare function powqlColumnType(col: ColumnMetadata): PowqlType;
|
|
|
367
367
|
* synthesizing a client-side value for it.
|
|
368
368
|
*/
|
|
369
369
|
/**
|
|
370
|
-
* PowQL reserved words
|
|
370
|
+
* PowQL reserved words, the v0.10 lexer keyword table from POWQL.md's
|
|
371
371
|
* "Reserved Words and Quoting" section, including the v0.10 additions
|
|
372
372
|
* `schema` and `describe`. Keyword matching is case-sensitive in the lexer,
|
|
373
373
|
* so only the exact lowercase form collides.
|
|
@@ -376,7 +376,7 @@ export declare const POWQL_KEYWORDS: ReadonlySet<string>;
|
|
|
376
376
|
/**
|
|
377
377
|
* Backtick-quote an identifier when PowQL would otherwise lex it as a keyword
|
|
378
378
|
* (or when it contains characters outside the bare-identifier grammar).
|
|
379
|
-
* Applied only in bare-identifier positions
|
|
379
|
+
* Applied only in bare-identifier positions, DDL type/field names, index DDL,
|
|
380
380
|
* and `insert`/`update`/`upsert` assignment targets. Dotted references
|
|
381
381
|
* (`.col` in filters/projections/ordering) bypass keyword lookup on every
|
|
382
382
|
* engine version and deliberately stay bare for ≤0.9 compatibility. Backticks
|
|
@@ -505,7 +505,7 @@ export declare function rowToEntity(raw: Record<string, unknown>, meta: TableMet
|
|
|
505
505
|
* So we always run the unique-constraint and message-shape checks first (they
|
|
506
506
|
* fire for both transports and extract detail like constraint / column names),
|
|
507
507
|
* then classify by the typed wire error class (`.wireErrorClass`, networked
|
|
508
|
-
* server >= 0.17
|
|
508
|
+
* server >= 0.17, accurate even when the server sanitized the message text),
|
|
509
509
|
* then fall through to the networked `.code` switch.
|
|
510
510
|
*/
|
|
511
511
|
export declare function wrapPowdbError(err: unknown): Error;
|
|
@@ -587,7 +587,7 @@ export declare class PowdbPool implements PgCompatPool {
|
|
|
587
587
|
* Pool-level single-writer gate. PowDB holds one global write lock, so at
|
|
588
588
|
* most one transaction may be open across the whole pool. Concurrent
|
|
589
589
|
* `begin`s queue FIFO on the gate (instead of checking out a second
|
|
590
|
-
* connection and blocking on the lock forever
|
|
590
|
+
* connection and blocking on the lock forever, the networked hang);
|
|
591
591
|
* re-entrant `begin`s throw E017 (see {@link PowdbTxGate}).
|
|
592
592
|
*/
|
|
593
593
|
private readonly txGate;
|
|
@@ -624,7 +624,7 @@ export declare class PowdbPool implements PgCompatPool {
|
|
|
624
624
|
/**
|
|
625
625
|
* Typed guard mirroring {@link PowdbEmbeddedPool}: after `end()` the driver
|
|
626
626
|
* pool throws a raw `Error('pool closed')` that {@link wrapPowdbError}
|
|
627
|
-
* cannot classify
|
|
627
|
+
* cannot classify, surface the same ConnectionError on both transports.
|
|
628
628
|
*/
|
|
629
629
|
private assertOpen;
|
|
630
630
|
connect(): Promise<PgCompatPoolClient>;
|
|
@@ -653,7 +653,7 @@ interface EmbeddedDatabase {
|
|
|
653
653
|
querySql(sql: string): EmbeddedQueryResult;
|
|
654
654
|
queryReadonly(powql: string): EmbeddedQueryResult;
|
|
655
655
|
isPoisoned(): boolean;
|
|
656
|
-
/** WAL durability selector
|
|
656
|
+
/** WAL durability selector, `@zvndev/powdb-embedded` ≥ 0.7.1. */
|
|
657
657
|
setSyncMode?(mode: string): void;
|
|
658
658
|
/**
|
|
659
659
|
* Lossless typed native wire (`@zvndev/powdb-embedded` ≥ 0.14). All optional
|
|
@@ -671,7 +671,7 @@ interface EmbeddedDatabase {
|
|
|
671
671
|
interface EmbeddedModule {
|
|
672
672
|
Database: {
|
|
673
673
|
open(dir: string): EmbeddedDatabase;
|
|
674
|
-
/** Open with a per-query memory budget
|
|
674
|
+
/** Open with a per-query memory budget, `@zvndev/powdb-embedded` ≥ 0.7.1. */
|
|
675
675
|
openWithMemoryLimit?(dir: string, limitBytes: number): EmbeddedDatabase;
|
|
676
676
|
/**
|
|
677
677
|
* Open a read-only handle for snapshot serving (`@zvndev/powdb-embedded` ≥
|
|
@@ -684,14 +684,14 @@ interface EmbeddedModule {
|
|
|
684
684
|
}
|
|
685
685
|
/**
|
|
686
686
|
* Encode a JS value as a **PowQL literal** for the embedded driver, which takes
|
|
687
|
-
* no params array
|
|
687
|
+
* no params array, `$N` placeholders must be materialized into the query text.
|
|
688
688
|
*
|
|
689
689
|
* This is the single place Turbine builds PowQL text from a value, so it is the
|
|
690
690
|
* security-critical surface. String encoding matches PowDB's lexer
|
|
691
691
|
* (`crates/query/src/lexer.rs`) exactly: a string literal is `"…"`, and inside
|
|
692
692
|
* it the lexer recognizes only the escapes `\"`, `\\`, `\n`, `\t` (any other
|
|
693
|
-
* `\x` drops the backslash and keeps `x`; every non-`\`/non-`"` char
|
|
694
|
-
* newlines, CR, unicode
|
|
693
|
+
* `\x` drops the backslash and keeps `x`; every non-`\`/non-`"` char, raw
|
|
694
|
+
* newlines, CR, unicode, is taken literally). So we escape `\` → `\\` and
|
|
695
695
|
* `"` → `\"` (the only breakout vectors), render `\n`/`\t` as their recognized
|
|
696
696
|
* escapes, and leave everything else raw. Verified against the real engine:
|
|
697
697
|
* quotes, backslashes, `$N`, `"); drop … --`, raw CR, and emoji all round-trip
|
|
@@ -725,13 +725,13 @@ export declare function encodePowqlLiteral(value: unknown, position?: string): s
|
|
|
725
725
|
* `git diff v0.19.0 v0.19.1 -- crates/query/src/lexer.rs` is empty (the bare-dotted-path
|
|
726
726
|
* hard error is parser-level, not tokenization), so this ceiling stays `'0.19'`. The
|
|
727
727
|
* guard in {@link PowdbEmbeddedPool.exec} compares major.minor only, so `'0.19'`
|
|
728
|
-
* already covers every 0.19.x patch
|
|
728
|
+
* already covers every 0.19.x patch, no bump is needed for 0.19.1.
|
|
729
729
|
*/
|
|
730
730
|
export declare const POWQL_LEXER_TESTED_CEILING = "0.19";
|
|
731
731
|
/**
|
|
732
732
|
* Substitute every `$N` placeholder in a generator-produced PowQL template with
|
|
733
733
|
* the encoded literal of `params[N-1]`. Safe because the template is produced by
|
|
734
|
-
* {@link PowqlInterface} and contains **no** user string literals
|
|
734
|
+
* {@link PowqlInterface} and contains **no** user string literals, the only
|
|
735
735
|
* `$<digits>` tokens are genuine positional placeholders, so a single scan
|
|
736
736
|
* cannot accidentally rewrite a `$N` that is itself part of a value (values are
|
|
737
737
|
* params, never inlined into the template by the generator).
|
|
@@ -754,7 +754,7 @@ export declare class PowdbEmbeddedPool implements PgCompatPool {
|
|
|
754
754
|
private closed;
|
|
755
755
|
/**
|
|
756
756
|
* Single-writer gate. The embedded engine is one handle with one global
|
|
757
|
-
* write lock
|
|
757
|
+
* write lock, only one transaction may be open at a time. A re-entrant
|
|
758
758
|
* `begin` (a fresh top-level `db.$transaction` opened inside an open one's
|
|
759
759
|
* callback) would otherwise hit PowDB's raw "already in a transaction"
|
|
760
760
|
* parse error; the gate surfaces a typed E017 instead, while INDEPENDENT
|
|
@@ -820,7 +820,7 @@ export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'de
|
|
|
820
820
|
* `0` / `Infinity` = wait without limit). Independent concurrent
|
|
821
821
|
* transactions queue and run one at a time; only a re-entrant
|
|
822
822
|
* `db.$transaction` (opened inside an active transaction callback) throws
|
|
823
|
-
* E017
|
|
823
|
+
* E017, queueing that shape would deadlock.
|
|
824
824
|
*
|
|
825
825
|
* Ignored when you inject an already-constructed {@link PowdbPool} (it carries
|
|
826
826
|
* its own {@link PowdbPoolOptions}); set it on that pool's constructor instead.
|
|
@@ -876,7 +876,7 @@ export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'de
|
|
|
876
876
|
powdbEmbeddedModule?: EmbeddedModule;
|
|
877
877
|
}
|
|
878
878
|
/**
|
|
879
|
-
* Selects the **embedded** transport
|
|
879
|
+
* Selects the **embedded** transport, an in-process `@zvndev/powdb-embedded`
|
|
880
880
|
* database at the given data directory (no server, no socket). The value is the
|
|
881
881
|
* data dir path. Preview: Full-durability checkpoint-bound; built binaries ship
|
|
882
882
|
* for macOS (arm64/x64) and Unix-glibc only (Intel-mac/musl/Windows fall back to
|
|
@@ -885,7 +885,7 @@ export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'de
|
|
|
885
885
|
* @example
|
|
886
886
|
* ```ts
|
|
887
887
|
* const db = await turbinePowDB({ embedded: '/var/data/app.powdb' }, SCHEMA);
|
|
888
|
-
* // Faster writes (fsync off the commit path, bounded-loss)
|
|
888
|
+
* // Faster writes (fsync off the commit path, bounded-loss), requires addon >= 0.7.1:
|
|
889
889
|
* const fast = await turbinePowDB({ embedded: '/var/data/app.powdb', syncMode: 'normal' }, SCHEMA);
|
|
890
890
|
* ```
|
|
891
891
|
*/
|
|
@@ -893,7 +893,7 @@ export interface TurbinePowdbEmbeddedTarget {
|
|
|
893
893
|
embedded: string;
|
|
894
894
|
/**
|
|
895
895
|
* WAL durability for the embedded engine (requires `@zvndev/powdb-embedded` ≥ 0.7.1):
|
|
896
|
-
* `'full'` (default
|
|
896
|
+
* `'full'` (default, fsync per commit), `'normal'` (fsync off the commit path,
|
|
897
897
|
* ~15–40× faster writes, bounded loss on OS crash/power loss), `'off'` (bench-only).
|
|
898
898
|
*/
|
|
899
899
|
syncMode?: 'full' | 'normal' | 'off';
|
|
@@ -919,7 +919,7 @@ export interface TurbinePowdbEmbeddedTarget {
|
|
|
919
919
|
* - an `{ embedded: <data-dir> }` object → an in-process
|
|
920
920
|
* `@zvndev/powdb-embedded` database (no server);
|
|
921
921
|
* - an already-constructed `@zvndev/powdb-client` `Pool` or {@link PowdbPool}
|
|
922
|
-
* (injection
|
|
922
|
+
* (injection, you own its lifecycle and `disconnect()` is a no-op).
|
|
923
923
|
*
|
|
924
924
|
* On the networked transport the server version is probed and a clear
|
|
925
925
|
* {@link ConnectionError} is thrown if it is older than {@link MIN_POWDB_VERSION}
|