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
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* turbine-orm
|
|
3
|
+
* turbine-orm, Query builder
|
|
4
4
|
*
|
|
5
5
|
* Each table accessor (db.users, db.posts, etc.) returns a QueryInterface<T>
|
|
6
6
|
* that builds parameterized SQL and executes it through the connection pool.
|
|
7
7
|
*
|
|
8
8
|
* Nested relations use json_build_object + json_agg subqueries for single-query
|
|
9
|
-
* resolution
|
|
9
|
+
* resolution, a PostgreSQL-native approach that eliminates N+1 query patterns.
|
|
10
10
|
*
|
|
11
11
|
* Schema-driven: all column names, types, and relations come from introspected
|
|
12
|
-
* metadata
|
|
12
|
+
* metadata, nothing is hardcoded.
|
|
13
13
|
*/
|
|
14
14
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
15
|
if (k2 === undefined) k2 = k;
|
|
@@ -275,7 +275,7 @@ function isEmptyOrderBy(orderBy) {
|
|
|
275
275
|
}
|
|
276
276
|
return orderBy === undefined || orderBy === null;
|
|
277
277
|
}
|
|
278
|
-
// biome-ignore lint/complexity/noBannedTypes: {} means "no relations known"
|
|
278
|
+
// biome-ignore lint/complexity/noBannedTypes: {} means "no relations known", intentional for untyped table access
|
|
279
279
|
class QueryInterface {
|
|
280
280
|
pool;
|
|
281
281
|
table;
|
|
@@ -299,6 +299,7 @@ class QueryInterface {
|
|
|
299
299
|
middlewares;
|
|
300
300
|
defaultLimit;
|
|
301
301
|
warnOnUnlimited;
|
|
302
|
+
scopedConnect;
|
|
302
303
|
utcTimestamps;
|
|
303
304
|
preparedStatementsEnabled;
|
|
304
305
|
/**
|
|
@@ -346,7 +347,7 @@ class QueryInterface {
|
|
|
346
347
|
globalFilters;
|
|
347
348
|
/**
|
|
348
349
|
* Tracks tables that have already triggered an unlimited-query warning so
|
|
349
|
-
* the user is not spammed once per row. Per-instance state
|
|
350
|
+
* the user is not spammed once per row. Per-instance state, each
|
|
350
351
|
* QueryInterface is bound to a single table, so this set will only ever
|
|
351
352
|
* contain at most one entry, but using a Set keeps the API consistent with
|
|
352
353
|
* the audit's "Set<string>" guidance and leaves room for future
|
|
@@ -361,7 +362,7 @@ class QueryInterface {
|
|
|
361
362
|
columnArrayTypeMap;
|
|
362
363
|
/**
|
|
363
364
|
* Columns whose type lives in a DIFFERENT schema than the introspected one
|
|
364
|
-
* (ColumnMetadata.pgTypeSchema is recorded only in that case)
|
|
365
|
+
* (ColumnMetadata.pgTypeSchema is recorded only in that case), such columns
|
|
365
366
|
* must never receive this schema's `::"enum"` cast (see enumTypeForColumn).
|
|
366
367
|
*/
|
|
367
368
|
crossSchemaTypeColumns;
|
|
@@ -375,7 +376,7 @@ class QueryInterface {
|
|
|
375
376
|
camelDateFieldCache = new Map();
|
|
376
377
|
/** True when this QI runs inside an active transaction (set via _txScoped option). */
|
|
377
378
|
txScoped;
|
|
378
|
-
/** Original options reference
|
|
379
|
+
/** Original options reference, forwarded to child QIs in nested writes. */
|
|
379
380
|
options;
|
|
380
381
|
/** Set by executeWithMiddleware so queryWithTimeout can include it in events. */
|
|
381
382
|
currentAction = 'raw';
|
|
@@ -391,7 +392,7 @@ class QueryInterface {
|
|
|
391
392
|
/**
|
|
392
393
|
* The active query's `skipGlobalFilters` opt-out, set at the top of each
|
|
393
394
|
* `build*` method and read deep in the (synchronous) SQL-build + param-collect
|
|
394
|
-
* tree
|
|
395
|
+
* tree, so relation subqueries, relation filters, `_count`, and relation
|
|
395
396
|
* `orderBy` all see it without threading it through dozens of signatures.
|
|
396
397
|
* Only load-bearing when {@link globalFilters} is configured; build+collect are
|
|
397
398
|
* synchronous per call, so this transient is never observed across an await.
|
|
@@ -435,8 +436,8 @@ class QueryInterface {
|
|
|
435
436
|
// `warnOnUnlimited: false`, per table with `warnOnUnlimited: { users:
|
|
436
437
|
// false }` (unlisted tables keep the default), or per call via
|
|
437
438
|
// `findMany({ warnOnUnlimited: false })`.
|
|
438
|
-
// Per-table maps accept BOTH key forms
|
|
439
|
-
// (`user_profiles`) and the camelCase accessor (`userProfiles`)
|
|
439
|
+
// Per-table maps accept BOTH key forms, the snake_case table name
|
|
440
|
+
// (`user_profiles`) and the camelCase accessor (`userProfiles`), since
|
|
440
441
|
// users naturally key by the accessor they type everywhere else. The
|
|
441
442
|
// snake_case entry wins when both are present.
|
|
442
443
|
const warnOpt = options?.warnOnUnlimited;
|
|
@@ -444,6 +445,7 @@ class QueryInterface {
|
|
|
444
445
|
typeof warnOpt === 'object' && warnOpt !== null
|
|
445
446
|
? (warnOpt[table] ?? warnOpt[(0, schema_js_1.snakeToCamel)(table)]) !== false
|
|
446
447
|
: warnOpt !== false;
|
|
448
|
+
this.scopedConnect = options?.scopedConnect === true;
|
|
447
449
|
this.utcTimestamps = options?.utcTimestamps !== false;
|
|
448
450
|
this.preparedStatementsEnabled = options?.preparedStatements ?? true;
|
|
449
451
|
// SQL template cache capacity. `sqlCacheSize: 0` disables caching entirely
|
|
@@ -554,7 +556,7 @@ class QueryInterface {
|
|
|
554
556
|
* SQLite use ` LIMIT <ph>` and/or ` OFFSET <ph>`. SQL Server has no `LIMIT`, so
|
|
555
557
|
* its dialect implements {@link Dialect.buildLimitOffset} to emit
|
|
556
558
|
* `[ORDER BY (SELECT NULL)] OFFSET <off> ROWS [FETCH NEXT <lim> ROWS ONLY]`.
|
|
557
|
-
* Param-push order (limit before offset) is owned by the caller and unchanged
|
|
559
|
+
* Param-push order (limit before offset) is owned by the caller and unchanged -
|
|
558
560
|
* this only varies the SQL text, so PG output stays byte-identical.
|
|
559
561
|
*/
|
|
560
562
|
buildPagination(limitPh, offsetPh, hasOrderBy) {
|
|
@@ -984,7 +986,7 @@ class QueryInterface {
|
|
|
984
986
|
* join for a to-one relation.
|
|
985
987
|
*
|
|
986
988
|
* Resolution order:
|
|
987
|
-
* 1. an explicit `autoToOneJoinMaxRows
|
|
989
|
+
* 1. an explicit `autoToOneJoinMaxRows`, an instruction, used verbatim
|
|
988
990
|
* (no clamping: the caller has measured their own workload);
|
|
989
991
|
* 2. the configured `autoRoundTripMs` divided by
|
|
990
992
|
* {@link AUTO_JOIN_PENALTY_MS_PER_ROW}, clamped to
|
|
@@ -998,7 +1000,7 @@ class QueryInterface {
|
|
|
998
1000
|
* put, so any single constant is wrong for someone by more than the margin it
|
|
999
1001
|
* is trying to save. Placing the switch AT the break-even is also what removes
|
|
1000
1002
|
* the old cliff: two plans that cost the same at the boundary make the regret
|
|
1001
|
-
* there ~1.0x, rising only as the true row count moves away from it
|
|
1003
|
+
* there ~1.0x, rising only as the true row count moves away from it, where
|
|
1002
1004
|
* the previous fixed 1000 put its WORST case (1.44x measured) immediately
|
|
1003
1005
|
* below its own switch point.
|
|
1004
1006
|
*
|
|
@@ -1162,7 +1164,7 @@ class QueryInterface {
|
|
|
1162
1164
|
* Build the {@link RelationLoadContext} the batched loader needs, closing over
|
|
1163
1165
|
* this interface's pool/dialect/executor. Child readers are constructed on the
|
|
1164
1166
|
* SAME pool (so they join an active transaction) with `defaultLimit` cleared
|
|
1165
|
-
* and unlimited-warnings silenced
|
|
1167
|
+
* and unlimited-warnings silenced, a relation load must fetch every matching
|
|
1166
1168
|
* child, and the per-relation `limit` is applied client-side by the loader.
|
|
1167
1169
|
*/
|
|
1168
1170
|
batchedContext(timeout, skip, includePii) {
|
|
@@ -1394,7 +1396,7 @@ class QueryInterface {
|
|
|
1394
1396
|
async queryWithTimeout(sql, params, timeout, preparedName) {
|
|
1395
1397
|
const start = performance.now();
|
|
1396
1398
|
const action = this.currentAction;
|
|
1397
|
-
// Build the query argument
|
|
1399
|
+
// Build the query argument, use object form with `name` for prepared
|
|
1398
1400
|
// statements, or the plain (text, values) form otherwise.
|
|
1399
1401
|
const usePrepared = preparedName && this.preparedStatementsEnabled;
|
|
1400
1402
|
const exec = usePrepared
|
|
@@ -1437,7 +1439,7 @@ class QueryInterface {
|
|
|
1437
1439
|
*
|
|
1438
1440
|
* - `'returning'` / `'output'`: the statement returns its own affected rows
|
|
1439
1441
|
* (`RETURNING *` / `OUTPUT INSERTED.*`). Byte-identical to the historical
|
|
1440
|
-
* single `queryWithTimeout` + `transform(result)` path
|
|
1442
|
+
* single `queryWithTimeout` + `transform(result)` path, the PostgreSQL
|
|
1441
1443
|
* route is unchanged.
|
|
1442
1444
|
* - `'reselect'`: the engine cannot return rows from a write, so the build
|
|
1443
1445
|
* method attached a {@link DeferredQuery.reselect} plan that runs the
|
|
@@ -1491,7 +1493,7 @@ class QueryInterface {
|
|
|
1491
1493
|
*
|
|
1492
1494
|
* Middleware can inspect and log query parameters, measure timing, and
|
|
1493
1495
|
* transform the result returned by `next()`. Note: query SQL is generated
|
|
1494
|
-
* BEFORE middleware runs
|
|
1496
|
+
* BEFORE middleware runs, `params.args` is a read-only snapshot, and
|
|
1495
1497
|
* mutating it does NOT change the executed SQL. Cross-cutting filters
|
|
1496
1498
|
* (e.g. soft deletes) belong in the query itself: pass an explicit
|
|
1497
1499
|
* `where: { deletedAt: null }` or wrap the table accessor in a small helper.
|
|
@@ -1509,7 +1511,7 @@ class QueryInterface {
|
|
|
1509
1511
|
const mw = this.middlewares[index++];
|
|
1510
1512
|
return mw(p, next);
|
|
1511
1513
|
}
|
|
1512
|
-
// End of chain
|
|
1514
|
+
// End of chain, execute the actual query
|
|
1513
1515
|
return executor();
|
|
1514
1516
|
};
|
|
1515
1517
|
return next(params);
|
|
@@ -1561,7 +1563,7 @@ class QueryInterface {
|
|
|
1561
1563
|
(0, batched_loader_js_1.stripFields)([entity], proj.strip);
|
|
1562
1564
|
return entity;
|
|
1563
1565
|
}
|
|
1564
|
-
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause"
|
|
1566
|
+
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause", matches TypedWithClause default
|
|
1565
1567
|
buildFindUnique(args) {
|
|
1566
1568
|
this.currentSkip = args.skipGlobalFilters;
|
|
1567
1569
|
// Prisma compound-unique selector expansion (before global-filter merge and
|
|
@@ -1595,7 +1597,7 @@ class QueryInterface {
|
|
|
1595
1597
|
const ck = `fu:${whereFingerprint}|c=${colKey}|w=${withFp}|pii=${includePii ? 1 : 0}${this.globalFilterCacheSegment()}`;
|
|
1596
1598
|
const params = [];
|
|
1597
1599
|
// Check if all where values are simple (plain equality, no operators/null/OR).
|
|
1598
|
-
// Keys are sorted to match fingerprintWhere
|
|
1600
|
+
// Keys are sorted to match fingerprintWhere, insertion order here would let
|
|
1599
1601
|
// permuted where literals share a cache entry with misaligned params.
|
|
1600
1602
|
const whereKeys = Object.keys(whereObj)
|
|
1601
1603
|
.filter((k) => whereObj[k] !== undefined)
|
|
@@ -1699,7 +1701,7 @@ class QueryInterface {
|
|
|
1699
1701
|
if (args?.with) {
|
|
1700
1702
|
const depth = this.measureWithDepth(args.with);
|
|
1701
1703
|
if (depth > 5 && (0, warn_registry_js_1.shouldWarnOnce)(warn_registry_js_1.WARN_NS.deepWith, this.table)) {
|
|
1702
|
-
console.warn(`[turbine] Deep with clause (depth ${depth}) on "${this.tableMeta.name}"
|
|
1704
|
+
console.warn(`[turbine] Deep with clause (depth ${depth}) on "${this.tableMeta.name}", ` +
|
|
1703
1705
|
'consider splitting into separate queries for better performance.');
|
|
1704
1706
|
}
|
|
1705
1707
|
}
|
|
@@ -1799,12 +1801,53 @@ class QueryInterface {
|
|
|
1799
1801
|
const hasExplicitLimit = args?.limit !== undefined || args?.take !== undefined || args?.cursor !== undefined;
|
|
1800
1802
|
if (hasExplicitLimit)
|
|
1801
1803
|
return;
|
|
1804
|
+
if (this.whereMatchesAtMostOneRow(args?.where))
|
|
1805
|
+
return;
|
|
1802
1806
|
if (this.warnedTables.has(this.table))
|
|
1803
1807
|
return;
|
|
1804
1808
|
this.warnedTables.add(this.table);
|
|
1805
1809
|
console.warn(`[turbine] warning: findMany on "${this.table}" has no limit: this will fetch every row. ` +
|
|
1806
1810
|
'Pass `limit`, or silence with `warnOnUnlimited: false` (per call, per table, or in config).');
|
|
1807
1811
|
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Whether `where` can match at most one row, because it pins every column of
|
|
1814
|
+
* the primary key or of some unique column set to a literal value.
|
|
1815
|
+
*
|
|
1816
|
+
* The unlimited-read warning is about accidentally fetching a whole table,
|
|
1817
|
+
* so firing it on `findMany({ where: { id: 1 } })` is noise: that query is
|
|
1818
|
+
* bounded by a uniqueness constraint just as firmly as by a `limit`, and a
|
|
1819
|
+
* warning that cries wolf on correct code trains people to disable it.
|
|
1820
|
+
*
|
|
1821
|
+
* Deliberately conservative. Only DIRECT equality on a literal counts: an
|
|
1822
|
+
* operator object (`{ id: { in: [...] } }`, `{ id: { gt: 1 } }`) can match
|
|
1823
|
+
* many rows, and any `OR` / `NOT` / relation filter can widen the result, so
|
|
1824
|
+
* anything that is not a plain scalar equality leaves the warning in place.
|
|
1825
|
+
* A compound-unique SELECTOR (`{ orgId_userId: {...} }`) is expanded first,
|
|
1826
|
+
* so both spellings are recognized.
|
|
1827
|
+
*/
|
|
1828
|
+
whereMatchesAtMostOneRow(where) {
|
|
1829
|
+
if (where === null || typeof where !== 'object' || Array.isArray(where))
|
|
1830
|
+
return false;
|
|
1831
|
+
const expanded = (0, compound_unique_js_1.expandCompoundUniqueWhere)(this.tableMeta, where);
|
|
1832
|
+
const pinned = new Set();
|
|
1833
|
+
for (const [field, value] of Object.entries(expanded)) {
|
|
1834
|
+
if (value === undefined)
|
|
1835
|
+
continue;
|
|
1836
|
+
// A plain scalar (or a Date) is an equality. `null` is NOT: `col IS NULL`
|
|
1837
|
+
// is not a uniqueness match, since SQL uniqueness permits many nulls.
|
|
1838
|
+
const isScalarEquality = value !== null && (typeof value !== 'object' || value instanceof Date) && typeof value !== 'function';
|
|
1839
|
+
if (!isScalarEquality)
|
|
1840
|
+
return false;
|
|
1841
|
+
const column = (0, utils_js_1.ownLookup)(this.tableMeta.columnMap, field);
|
|
1842
|
+
if (!column)
|
|
1843
|
+
return false;
|
|
1844
|
+
pinned.add(column);
|
|
1845
|
+
}
|
|
1846
|
+
if (pinned.size === 0)
|
|
1847
|
+
return false;
|
|
1848
|
+
const covers = (columns) => !!columns && columns.length > 0 && columns.every((c) => pinned.has(c));
|
|
1849
|
+
return covers(this.tableMeta.primaryKey) || (this.tableMeta.uniqueColumns ?? []).some(covers);
|
|
1850
|
+
}
|
|
1808
1851
|
/**
|
|
1809
1852
|
* Recursively measure the maximum depth of a `with` clause tree.
|
|
1810
1853
|
* Used by the dev-only deep-with warning guard.
|
|
@@ -1819,7 +1862,7 @@ class QueryInterface {
|
|
|
1819
1862
|
}
|
|
1820
1863
|
return maxDepth;
|
|
1821
1864
|
}
|
|
1822
|
-
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause"
|
|
1865
|
+
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause", matches TypedWithClause default
|
|
1823
1866
|
buildFindMany(args) {
|
|
1824
1867
|
this.currentSkip = args?.skipGlobalFilters;
|
|
1825
1868
|
// Stable relation order (opt-in): fill PK-asc orderBy into unordered to-many
|
|
@@ -1851,7 +1894,7 @@ class QueryInterface {
|
|
|
1851
1894
|
// path re-orders in an outer wrapper (`... AS "<table>_distinct" ORDER BY
|
|
1852
1895
|
// <userOrder>`) where a correlated relation subquery (pick-row, `_count`,
|
|
1853
1896
|
// to-one relation ordering) would reference the parent table name out of
|
|
1854
|
-
// scope
|
|
1897
|
+
// scope, a guaranteed "missing FROM-clause entry" crash on Postgres.
|
|
1855
1898
|
// Checked BEFORE the SQL cache so build and warm-cache paths throw
|
|
1856
1899
|
// identically (same rule as the vector guard inside the distinct branch).
|
|
1857
1900
|
if (args?.distinct && args.distinct.length > 0 && args.orderBy) {
|
|
@@ -1910,7 +1953,7 @@ class QueryInterface {
|
|
|
1910
1953
|
// own cache-key segment: a cached no-PII statement must never serve an
|
|
1911
1954
|
// `includePii` call, nor vice versa.
|
|
1912
1955
|
// `relationLoadStrategy: 'flatten'` compiles eligible to-one relations to
|
|
1913
|
-
// LEFT JOINs instead of correlated subqueries
|
|
1956
|
+
// LEFT JOINs instead of correlated subqueries, a completely different
|
|
1914
1957
|
// statement for the same `with` shape, which `withFp` (strategy-blind)
|
|
1915
1958
|
// does not distinguish. So the plan gets its own cache-key segment, exactly
|
|
1916
1959
|
// like `pii=`: a join-planned template must never serve a flatten-planned
|
|
@@ -1961,7 +2004,7 @@ class QueryInterface {
|
|
|
1961
2004
|
// where → cursor order (the collect path mirrors this exactly).
|
|
1962
2005
|
let tail = freshWhereSql;
|
|
1963
2006
|
if (args?.cursor) {
|
|
1964
|
-
// Sorted (canonical) order
|
|
2007
|
+
// Sorted (canonical) order, MUST match cursorFp and the cache-hit collect below.
|
|
1965
2008
|
const cursorEntries = (0, filters_js_1.sortedEntries)(args.cursor).filter(([, v]) => v !== undefined);
|
|
1966
2009
|
if (cursorEntries.length > 0) {
|
|
1967
2010
|
// Resolve the seek direction per cursor field from the flattened
|
|
@@ -2011,7 +2054,7 @@ class QueryInterface {
|
|
|
2011
2054
|
: '';
|
|
2012
2055
|
sql = `SELECT ${distinctPrefix}${selectClause} FROM ${qt}${relationJoins.join('')}${lateralJoins.join('')}${tail}${orderBySql}`;
|
|
2013
2056
|
}
|
|
2014
|
-
// Pagination
|
|
2057
|
+
// Pagination, push params in the same order the collect path mirrors
|
|
2015
2058
|
// (limit before offset); the SQL TEXT shape is dialect-owned via
|
|
2016
2059
|
// buildPagination (PG: ` LIMIT $n`/` OFFSET $n`; SQL Server: OFFSET/FETCH).
|
|
2017
2060
|
let limitPh;
|
|
@@ -2035,7 +2078,7 @@ class QueryInterface {
|
|
|
2035
2078
|
if (args?.with) {
|
|
2036
2079
|
this.collectWithParams(args.with, params, undefined, flattenPlan);
|
|
2037
2080
|
}
|
|
2038
|
-
// 3. Cursor params
|
|
2081
|
+
// 3. Cursor params, sorted (canonical) order, matching cursorFp and the build path.
|
|
2039
2082
|
if (args?.cursor) {
|
|
2040
2083
|
const cursorEntries = (0, filters_js_1.sortedEntries)(args.cursor).filter(([, v]) => v !== undefined);
|
|
2041
2084
|
for (const [, v] of cursorEntries) {
|
|
@@ -2043,11 +2086,11 @@ class QueryInterface {
|
|
|
2043
2086
|
}
|
|
2044
2087
|
}
|
|
2045
2088
|
// 4. ORDER BY params (vector KNN ordering binds a `$n::vector` query vector).
|
|
2046
|
-
// Mirrors buildOrderBy's push order
|
|
2089
|
+
// Mirrors buildOrderBy's push order, between cursor and LIMIT.
|
|
2047
2090
|
if (args?.orderBy) {
|
|
2048
2091
|
this.collectOrderByParams(args.orderBy, params);
|
|
2049
2092
|
}
|
|
2050
|
-
// 5. LIMIT param
|
|
2093
|
+
// 5. LIMIT param, skipped when the dialect inlines pagination (build path
|
|
2051
2094
|
// mirrors via paginationRef → no placeholder, no param).
|
|
2052
2095
|
if (effectiveLimit !== undefined && !this.dialect.inlineLimitOffset) {
|
|
2053
2096
|
// Validate here too: on a cache HIT the build path never runs, and a
|
|
@@ -2055,7 +2098,7 @@ class QueryInterface {
|
|
|
2055
2098
|
// i.e. no limit at all).
|
|
2056
2099
|
params.push(this.paginationValue(effectiveLimit, 'limit'));
|
|
2057
2100
|
}
|
|
2058
|
-
// 6. OFFSET param
|
|
2101
|
+
// 6. OFFSET param, same inline gate as LIMIT above.
|
|
2059
2102
|
if (args?.offset !== undefined && !this.dialect.inlineLimitOffset) {
|
|
2060
2103
|
params.push(this.paginationValue(args.offset, 'skip/offset'));
|
|
2061
2104
|
}
|
|
@@ -2071,7 +2114,7 @@ class QueryInterface {
|
|
|
2071
2114
|
};
|
|
2072
2115
|
}
|
|
2073
2116
|
// -------------------------------------------------------------------------
|
|
2074
|
-
// findManyStream
|
|
2117
|
+
// findManyStream, async iterable using PostgreSQL cursors
|
|
2075
2118
|
// -------------------------------------------------------------------------
|
|
2076
2119
|
/**
|
|
2077
2120
|
* Stream rows from a findMany query using PostgreSQL cursors.
|
|
@@ -2110,8 +2153,8 @@ class QueryInterface {
|
|
|
2110
2153
|
const hasRelations = !!args?.with;
|
|
2111
2154
|
// Build the positional-aware relation parser once for the whole stream.
|
|
2112
2155
|
// Same flatten plan buildFindMany compiles below. The plan is a pure
|
|
2113
|
-
// function of the schema, the `with` shape and `includePii
|
|
2114
|
-
// `limit
|
|
2156
|
+
// function of the schema, the `with` shape and `includePii`, never of
|
|
2157
|
+
// `limit`, so the batch-size override the speculative fetch applies cannot
|
|
2115
2158
|
// change it, and the stream's parser matches the emitted SQL.
|
|
2116
2159
|
const streamFlattenPlan = hasRelations ? this.planFlatten(args, args?.includePii === true) : null;
|
|
2117
2160
|
const parseWith = hasRelations
|
|
@@ -2125,7 +2168,7 @@ class QueryInterface {
|
|
|
2125
2168
|
this.currentAction = 'findManyStream';
|
|
2126
2169
|
const speculativeResult = await this.queryWithTimeout(speculativeDeferred.sql, speculativeDeferred.params, args?.timeout);
|
|
2127
2170
|
if (speculativeResult.rows.length <= batchSize) {
|
|
2128
|
-
// Small drain
|
|
2171
|
+
// Small drain, yield all rows and return, no cursor needed
|
|
2129
2172
|
for (const row of speculativeResult.rows) {
|
|
2130
2173
|
yield (parseWith ? parseWith(row) : this.parseRow(row, this.table));
|
|
2131
2174
|
}
|
|
@@ -2165,7 +2208,7 @@ class QueryInterface {
|
|
|
2165
2208
|
}
|
|
2166
2209
|
}
|
|
2167
2210
|
// -------------------------------------------------------------------------
|
|
2168
|
-
// findFirst
|
|
2211
|
+
// findFirst, like findMany but returns a single row or null
|
|
2169
2212
|
// -------------------------------------------------------------------------
|
|
2170
2213
|
async findFirst(args) {
|
|
2171
2214
|
return this.executeWithMiddleware('findFirst', (args ?? {}), async () => {
|
|
@@ -2190,7 +2233,7 @@ class QueryInterface {
|
|
|
2190
2233
|
return deferred.transform(result);
|
|
2191
2234
|
});
|
|
2192
2235
|
}
|
|
2193
|
-
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause"
|
|
2236
|
+
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause", matches TypedWithClause default
|
|
2194
2237
|
buildFindFirst(args) {
|
|
2195
2238
|
// Reuse findMany's SQL builder but force LIMIT 1
|
|
2196
2239
|
const findManyArgs = { ...args, limit: 1 };
|
|
@@ -2206,7 +2249,7 @@ class QueryInterface {
|
|
|
2206
2249
|
};
|
|
2207
2250
|
}
|
|
2208
2251
|
// -------------------------------------------------------------------------
|
|
2209
|
-
// findFirstOrThrow
|
|
2252
|
+
// findFirstOrThrow, like findFirst but throws if no record found
|
|
2210
2253
|
// -------------------------------------------------------------------------
|
|
2211
2254
|
async findFirstOrThrow(args) {
|
|
2212
2255
|
return this.executeWithMiddleware('findFirstOrThrow', (args ?? {}), async () => {
|
|
@@ -2215,7 +2258,7 @@ class QueryInterface {
|
|
|
2215
2258
|
return deferred.transform(result);
|
|
2216
2259
|
});
|
|
2217
2260
|
}
|
|
2218
|
-
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause"
|
|
2261
|
+
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause", matches TypedWithClause default
|
|
2219
2262
|
buildFindFirstOrThrow(args) {
|
|
2220
2263
|
const inner = this.buildFindFirst(args);
|
|
2221
2264
|
return {
|
|
@@ -2236,7 +2279,7 @@ class QueryInterface {
|
|
|
2236
2279
|
};
|
|
2237
2280
|
}
|
|
2238
2281
|
// -------------------------------------------------------------------------
|
|
2239
|
-
// findUniqueOrThrow
|
|
2282
|
+
// findUniqueOrThrow, like findUnique but throws if no record found
|
|
2240
2283
|
// -------------------------------------------------------------------------
|
|
2241
2284
|
async findUniqueOrThrow(args) {
|
|
2242
2285
|
return this.executeWithMiddleware('findUniqueOrThrow', args, async () => {
|
|
@@ -2245,7 +2288,7 @@ class QueryInterface {
|
|
|
2245
2288
|
return deferred.transform(result);
|
|
2246
2289
|
});
|
|
2247
2290
|
}
|
|
2248
|
-
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause"
|
|
2291
|
+
// biome-ignore lint/complexity/noBannedTypes: {} means "no with clause", matches TypedWithClause default
|
|
2249
2292
|
buildFindUniqueOrThrow(args) {
|
|
2250
2293
|
const inner = this.buildFindUnique(args);
|
|
2251
2294
|
return {
|
|
@@ -2278,7 +2321,7 @@ class QueryInterface {
|
|
|
2278
2321
|
});
|
|
2279
2322
|
}
|
|
2280
2323
|
// -------------------------------------------------------------------------
|
|
2281
|
-
// createMany
|
|
2324
|
+
// createMany, uses UNNEST for performance
|
|
2282
2325
|
// -------------------------------------------------------------------------
|
|
2283
2326
|
async createMany(args) {
|
|
2284
2327
|
return this.executeWithMiddleware('createMany', args, async () => {
|
|
@@ -2344,7 +2387,7 @@ class QueryInterface {
|
|
|
2344
2387
|
// implicit transaction.
|
|
2345
2388
|
this.pool);
|
|
2346
2389
|
// biome-ignore lint/suspicious/noExplicitAny: TransactionClient satisfies NestedWriteContext['tx'] at runtime
|
|
2347
|
-
const ctx = { schema: this.schema, tx: tx };
|
|
2390
|
+
const ctx = { schema: this.schema, tx: tx, scopedConnect: this.scopedConnect };
|
|
2348
2391
|
const result = await fn(ctx);
|
|
2349
2392
|
await client.query(this.dialect.commitStatement());
|
|
2350
2393
|
return result;
|
|
@@ -2392,7 +2435,7 @@ class QueryInterface {
|
|
|
2392
2435
|
});
|
|
2393
2436
|
}
|
|
2394
2437
|
// -------------------------------------------------------------------------
|
|
2395
|
-
// upsert
|
|
2438
|
+
// upsert, INSERT ... ON CONFLICT ... DO UPDATE
|
|
2396
2439
|
// -------------------------------------------------------------------------
|
|
2397
2440
|
async upsert(args) {
|
|
2398
2441
|
return this.executeWithMiddleware('upsert', args, async () => {
|
|
@@ -2401,7 +2444,7 @@ class QueryInterface {
|
|
|
2401
2444
|
});
|
|
2402
2445
|
}
|
|
2403
2446
|
// -------------------------------------------------------------------------
|
|
2404
|
-
// updateMany
|
|
2447
|
+
// updateMany, UPDATE ... WHERE ... returning count
|
|
2405
2448
|
// -------------------------------------------------------------------------
|
|
2406
2449
|
async updateMany(args) {
|
|
2407
2450
|
return this.executeWithMiddleware('updateMany', args, async () => {
|
|
@@ -2411,7 +2454,7 @@ class QueryInterface {
|
|
|
2411
2454
|
});
|
|
2412
2455
|
}
|
|
2413
2456
|
// -------------------------------------------------------------------------
|
|
2414
|
-
// deleteMany
|
|
2457
|
+
// deleteMany, DELETE ... WHERE ... returning count
|
|
2415
2458
|
// -------------------------------------------------------------------------
|
|
2416
2459
|
async deleteMany(args) {
|
|
2417
2460
|
return this.executeWithMiddleware('deleteMany', args, async () => {
|
|
@@ -2484,7 +2527,7 @@ class QueryInterface {
|
|
|
2484
2527
|
return aggMod.buildAggregate(this.ctx, args);
|
|
2485
2528
|
}
|
|
2486
2529
|
// -------------------------------------------------------------------------
|
|
2487
|
-
// aggregate
|
|
2530
|
+
// aggregate, standalone aggregation without groupBy
|
|
2488
2531
|
// -------------------------------------------------------------------------
|
|
2489
2532
|
async aggregate(args) {
|
|
2490
2533
|
return this.executeWithMiddleware('aggregate', args, async () => {
|
|
@@ -2620,7 +2663,7 @@ class QueryInterface {
|
|
|
2620
2663
|
// Fall back to camelToSnake ONLY if that snake_cased name also exists as a
|
|
2621
2664
|
// real column on the table. This preserves the convenience of writing
|
|
2622
2665
|
// `userId` when the schema exposes `user_id` under an unusual field name,
|
|
2623
|
-
// but rejects arbitrary strings
|
|
2666
|
+
// but rejects arbitrary strings, closing the defense-in-depth gap for
|
|
2624
2667
|
// SQL injection and catching typos like `where: { emial: 'x' }` with a
|
|
2625
2668
|
// clear error instead of a cryptic Postgres "column does not exist".
|
|
2626
2669
|
const snake = (0, schema_js_1.camelToSnake)(field);
|
|
@@ -2630,15 +2673,14 @@ class QueryInterface {
|
|
|
2630
2673
|
if (this.tableMeta.allColumns?.includes(snake)) {
|
|
2631
2674
|
return snake;
|
|
2632
2675
|
}
|
|
2633
|
-
throw new errors_js_1.ValidationError(
|
|
2634
|
-
`Known fields: ${Object.keys(this.tableMeta.columnMap).join(', ') || '(none)'}.`);
|
|
2676
|
+
throw new errors_js_1.ValidationError((0, utils_js_1.unknownFieldMessage)(this.table, field, this.tableMeta));
|
|
2635
2677
|
}
|
|
2636
2678
|
/** Convert camelCase field name to a double-quoted SQL identifier */
|
|
2637
2679
|
toSqlColumn(field) {
|
|
2638
2680
|
return this.q(this.toColumn(field));
|
|
2639
2681
|
}
|
|
2640
2682
|
// =========================================================================
|
|
2641
|
-
// Fingerprinting
|
|
2683
|
+
// Fingerprinting, value-invariant shape keys for SQL cache lookup
|
|
2642
2684
|
// =========================================================================
|
|
2643
2685
|
// ---------------------------------------------------------------------------
|
|
2644
2686
|
// WHERE-clause compilation (extracted to where.ts).
|
|
@@ -2735,7 +2777,7 @@ class QueryInterface {
|
|
|
2735
2777
|
}
|
|
2736
2778
|
}
|
|
2737
2779
|
// -------------------------------------------------------------------------
|
|
2738
|
-
// Global filters (soft-delete / multi-tenancy
|
|
2780
|
+
// Global filters (soft-delete / multi-tenancy, WS-G)
|
|
2739
2781
|
//
|
|
2740
2782
|
// A configured global filter for a table is AND-merged into the compiled WHERE
|
|
2741
2783
|
// of every query on that table (via {@link mergeGlobalFilter}, so the merge is
|
|
@@ -2743,12 +2785,12 @@ class QueryInterface {
|
|
|
2743
2785
|
// subquery targeting it (rendered at build time against the subquery's alias/
|
|
2744
2786
|
// table by the `*GlobalFilterAlias`/`*GlobalFilterExists` helpers, with the
|
|
2745
2787
|
// shape folded into the SQL-cache key via {@link globalFilterCacheSegment}).
|
|
2746
|
-
// Function filters are evaluated per resolve
|
|
2788
|
+
// Function filters are evaluated per resolve, at query-build time, enabling
|
|
2747
2789
|
// per-request tenancy via a closure. They must return a STABLE shape (same
|
|
2748
2790
|
// keys/operators); only values may vary between calls.
|
|
2749
2791
|
// -------------------------------------------------------------------------
|
|
2750
2792
|
// -------------------------------------------------------------------------
|
|
2751
|
-
// Scoped (non-top-level) WHERE compilation
|
|
2793
|
+
// Scoped (non-top-level) WHERE compilation, relation EXISTS sub-wheres and
|
|
2752
2794
|
// relation `with`-clause `where`s. All three consumers below drive the SAME
|
|
2753
2795
|
// canonical `walkWhere` the top level uses (bound to the SCOPE's target
|
|
2754
2796
|
// table), so their key order + combinator structure + relation detection can
|
|
@@ -2764,12 +2806,12 @@ class QueryInterface {
|
|
|
2764
2806
|
/**
|
|
2765
2807
|
* Build ORDER BY clause from an object.
|
|
2766
2808
|
*
|
|
2767
|
-
* Each value is either a plain direction (`'asc'`/`'desc'`) or
|
|
2768
|
-
* columns
|
|
2809
|
+
* Each value is either a plain direction (`'asc'`/`'desc'`) or, for pgvector
|
|
2810
|
+
* columns, a `{ distance: { to, metric, direction? } }` KNN ordering object.
|
|
2769
2811
|
* Vector ordering binds the query vector as a `$n::vector` param, so a `params`
|
|
2770
2812
|
* array MUST be supplied when a vector ordering may be present (top-level
|
|
2771
2813
|
* findMany path). When `params` is omitted (groupBy / relation path) a vector
|
|
2772
|
-
* ordering throws
|
|
2814
|
+
* ordering throws, KNN ordering is only supported at the top level.
|
|
2773
2815
|
*/
|
|
2774
2816
|
// -------------------------------------------------------------------------
|
|
2775
2817
|
// pgvector helpers (similarity search)
|
|
@@ -2802,7 +2844,7 @@ class QueryInterface {
|
|
|
2802
2844
|
// columns (`date[]`, `timestamp[]`, `timestamptz[]`), for which the
|
|
2803
2845
|
// driver already hands back a `Date[]`. Coercing it ran
|
|
2804
2846
|
// `new Date(String(theArray))` and replaced the whole array with a
|
|
2805
|
-
// single Invalid Date
|
|
2847
|
+
// single Invalid Date, the column was unreadable on every strategy.
|
|
2806
2848
|
// The join strategy's string arrays are handled upstream instead, by
|
|
2807
2849
|
// the JSON-wire decode in relations.ts.
|
|
2808
2850
|
if ((dateCols.has(col) || camelDateFields.has(field)) &&
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm
|
|
2
|
+
* turbine-orm, Prisma-style compound-unique `where` selectors.
|
|
3
3
|
*
|
|
4
4
|
* Prisma lets a `findUnique`-family `where` address a multi-column unique
|
|
5
5
|
* constraint through a single synthetic key holding the member columns:
|
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm
|
|
2
|
+
* turbine-orm, Deferred query + QueryInterface option types
|
|
3
3
|
*
|
|
4
4
|
* Split from builder.ts so the class file focuses on SQL assembly / execution.
|
|
5
5
|
*/
|
|
@@ -27,7 +27,7 @@ export interface DeferredQuery<T> {
|
|
|
27
27
|
preparedName?: string;
|
|
28
28
|
/**
|
|
29
29
|
* Execution plan for dialects whose {@link Dialect.resultStrategy} is
|
|
30
|
-
* `'reselect'` (no RETURNING
|
|
30
|
+
* `'reselect'` (no RETURNING, e.g. MySQL). Owns the statement ordering: it
|
|
31
31
|
* runs the write and the follow-up row-fetching SELECT(s) via `exec`, and
|
|
32
32
|
* resolves the result whose rows {@link DeferredQuery.transform} consumes.
|
|
33
33
|
* Absent for `'returning'`/`'output'` dialects (the statement returns its own
|
|
@@ -35,7 +35,7 @@ export interface DeferredQuery<T> {
|
|
|
35
35
|
*/
|
|
36
36
|
reselect?: (exec: ReselectExecutor) => Promise<pg.QueryResult>;
|
|
37
37
|
}
|
|
38
|
-
/** Middleware function type
|
|
38
|
+
/** Middleware function type, imported from client to avoid circular deps */
|
|
39
39
|
export type MiddlewareFn = (params: {
|
|
40
40
|
model: string;
|
|
41
41
|
action: string;
|
|
@@ -75,13 +75,19 @@ export interface QueryInterfaceOptions {
|
|
|
75
75
|
* queries are surfaced loudly during development. Pass `false` to silence
|
|
76
76
|
* the warning entirely (e.g. for CLI tooling that intentionally streams
|
|
77
77
|
* full tables), or a per-table map (`{ userProfiles: false }`) to silence
|
|
78
|
-
* only the tables that intentionally read full sets
|
|
78
|
+
* only the tables that intentionally read full sets, unlisted tables keep
|
|
79
79
|
* the default. Map keys accept BOTH the camelCase accessor name
|
|
80
80
|
* (`userProfiles`) and the snake_case table name (`user_profiles`); the
|
|
81
81
|
* snake_case entry wins if both are present. Individual calls can also
|
|
82
82
|
* override via `findMany({ warnOnUnlimited: false })`.
|
|
83
83
|
*/
|
|
84
84
|
warnOnUnlimited?: boolean | Record<string, boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* Refuse a nested `connect` / `connectOrCreate` that would re-parent a
|
|
87
|
+
* to-many child already owned by a different parent. Off by default; see
|
|
88
|
+
* {@link import('../nested-write.js').NestedWriteContext.scopedConnect}.
|
|
89
|
+
*/
|
|
90
|
+
scopedConnect?: boolean;
|
|
85
91
|
/**
|
|
86
92
|
* Enable prepared statements. When true, queries are submitted with a
|
|
87
93
|
* `{ name, text, values }` object to the pg driver, which caches the
|
|
@@ -196,7 +202,7 @@ export interface QueryInterfaceOptions {
|
|
|
196
202
|
autoRoundTripMs?: number;
|
|
197
203
|
/**
|
|
198
204
|
* How nested-relation subqueries encode each row's JSON: `'object'` (default,
|
|
199
|
-
* `json_build_object`) or `'positional'` (`json_build_array`, key-less
|
|
205
|
+
* `json_build_object`) or `'positional'` (`json_build_array`, key-less, see
|
|
200
206
|
* {@link Dialect.buildJsonArray}). Positional is Postgres-only in v1; a
|
|
201
207
|
* `with` clause on any other dialect throws `UnsupportedFeatureError` (E017).
|
|
202
208
|
*/
|
|
@@ -208,7 +214,7 @@ export interface QueryInterfaceOptions {
|
|
|
208
214
|
* {@link GlobalFilters}.
|
|
209
215
|
*/
|
|
210
216
|
globalFilters?: GlobalFilters;
|
|
211
|
-
/** @internal Set by TransactionClient
|
|
217
|
+
/** @internal Set by TransactionClient, signals that this QI runs inside an active transaction. */
|
|
212
218
|
_txScoped?: boolean;
|
|
213
219
|
/** @internal Callback from TurbineClient for query event emission. */
|
|
214
220
|
_onQuery?: (event: QueryEvent) => void;
|