turbine-orm 0.49.0 → 0.50.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.
Files changed (157) hide show
  1. package/README.md +122 -39
  2. package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
  3. package/dist/cjs/adapters/index.d.ts +110 -0
  4. package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
  5. package/dist/cjs/cli/config.d.ts +181 -0
  6. package/dist/cjs/cli/config.js +32 -6
  7. package/dist/cjs/cli/destructive.d.ts +38 -0
  8. package/dist/cjs/cli/index.d.ts +359 -0
  9. package/dist/cjs/cli/index.js +228 -56
  10. package/dist/cjs/cli/loader.d.ts +61 -0
  11. package/dist/cjs/cli/mcp.d.ts +42 -0
  12. package/dist/cjs/cli/migrate.d.ts +356 -0
  13. package/dist/cjs/cli/migrate.js +131 -40
  14. package/dist/cjs/cli/observe-ui.d.ts +1 -0
  15. package/dist/cjs/cli/observe-ui.js +14 -5
  16. package/dist/cjs/cli/observe.d.ts +25 -0
  17. package/dist/cjs/cli/observe.js +49 -12
  18. package/dist/cjs/cli/pii-tags.d.ts +53 -0
  19. package/dist/cjs/cli/prisma-report.d.ts +33 -0
  20. package/dist/cjs/cli/prisma-report.js +73 -0
  21. package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
  22. package/dist/cjs/cli/prisma-resolve.js +1 -0
  23. package/dist/cjs/cli/prisma-schema.d.ts +176 -0
  24. package/dist/cjs/cli/prisma-schema.js +82 -4
  25. package/dist/cjs/cli/rate-limit.d.ts +32 -0
  26. package/dist/cjs/cli/rate-limit.js +45 -0
  27. package/dist/cjs/cli/studio-demo.d.ts +43 -0
  28. package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
  29. package/dist/cjs/cli/studio.d.ts +207 -0
  30. package/dist/cjs/cli/studio.js +136 -71
  31. package/dist/cjs/cli/ui.d.ts +73 -0
  32. package/dist/cjs/cli/ui.js +51 -9
  33. package/dist/cjs/client.d.ts +837 -0
  34. package/dist/cjs/client.js +3 -0
  35. package/dist/cjs/dialect.d.ts +516 -0
  36. package/dist/cjs/dialect.js +37 -12
  37. package/dist/cjs/errors.d.ts +370 -0
  38. package/dist/cjs/generate.d.ts +137 -0
  39. package/dist/cjs/generate.js +39 -6
  40. package/dist/cjs/index-advisor.d.ts +153 -0
  41. package/dist/cjs/index-stats.d.ts +384 -0
  42. package/dist/cjs/index.d.ts +55 -0
  43. package/dist/cjs/index.js +7 -2
  44. package/dist/cjs/introspect.d.ts +269 -0
  45. package/dist/cjs/mssql.d.ts +232 -0
  46. package/dist/cjs/mssql.js +6 -0
  47. package/dist/cjs/mysql.d.ts +173 -0
  48. package/dist/cjs/mysql.js +16 -0
  49. package/dist/cjs/nested-write.d.ts +96 -0
  50. package/dist/cjs/nested-write.js +414 -24
  51. package/dist/cjs/observe.d.ts +115 -0
  52. package/dist/cjs/optional-peer-import.d.cts +72 -0
  53. package/dist/cjs/pipeline-submittable.d.ts +93 -0
  54. package/dist/cjs/pipeline.d.ts +71 -0
  55. package/dist/cjs/powdb-introspect.d.ts +84 -0
  56. package/dist/cjs/powdb.d.ts +931 -0
  57. package/dist/cjs/powdb.js +106 -21
  58. package/dist/cjs/powql.d.ts +592 -0
  59. package/dist/cjs/powql.js +42 -6
  60. package/dist/cjs/prisma-compat.d.ts +283 -0
  61. package/dist/cjs/prisma-compat.js +167 -9
  62. package/dist/cjs/query/aggregates.d.ts +92 -0
  63. package/dist/cjs/query/aggregates.js +7 -3
  64. package/dist/cjs/query/batched-loader.d.ts +193 -0
  65. package/dist/cjs/query/builder.d.ts +849 -0
  66. package/dist/cjs/query/builder.js +571 -65
  67. package/dist/cjs/query/compound-unique.d.ts +51 -0
  68. package/dist/cjs/query/deferred.d.ts +223 -0
  69. package/dist/cjs/query/filters.d.ts +201 -0
  70. package/dist/cjs/query/index.d.ts +14 -0
  71. package/dist/cjs/query/index.js +6 -1
  72. package/dist/cjs/query/relations.d.ts +609 -0
  73. package/dist/cjs/query/relations.js +693 -46
  74. package/dist/cjs/query/types.d.ts +1300 -0
  75. package/dist/cjs/query/utils.d.ts +209 -0
  76. package/dist/cjs/query/utils.js +208 -1
  77. package/dist/cjs/query/warn-registry.d.ts +68 -0
  78. package/dist/cjs/query/warn-registry.js +9 -0
  79. package/dist/cjs/query/where-compile.d.ts +139 -0
  80. package/dist/cjs/query/where.d.ts +548 -0
  81. package/dist/cjs/query/where.js +58 -22
  82. package/dist/cjs/query/writes.d.ts +172 -0
  83. package/dist/cjs/query/writes.js +105 -12
  84. package/dist/cjs/realtime.d.ts +70 -0
  85. package/dist/cjs/schema-builder.d.ts +354 -0
  86. package/dist/cjs/schema-metadata.d.ts +83 -0
  87. package/dist/cjs/schema-sql.d.ts +217 -0
  88. package/dist/cjs/schema-sql.js +23 -5
  89. package/dist/cjs/schema.d.ts +356 -0
  90. package/dist/cjs/schema.js +125 -0
  91. package/dist/cjs/seed.d.ts +15 -0
  92. package/dist/cjs/serverless.d.ts +142 -0
  93. package/dist/cjs/sqlite.d.ts +143 -0
  94. package/dist/cjs/sqlite.js +4 -0
  95. package/dist/cjs/typed-sql.d.ts +102 -0
  96. package/dist/cli/config.d.ts +18 -4
  97. package/dist/cli/config.js +31 -6
  98. package/dist/cli/index.d.ts +123 -0
  99. package/dist/cli/index.js +223 -58
  100. package/dist/cli/migrate.d.ts +59 -10
  101. package/dist/cli/migrate.js +128 -41
  102. package/dist/cli/observe-ui.d.ts +1 -1
  103. package/dist/cli/observe-ui.js +14 -5
  104. package/dist/cli/observe.d.ts +7 -1
  105. package/dist/cli/observe.js +48 -12
  106. package/dist/cli/prisma-report.d.ts +14 -0
  107. package/dist/cli/prisma-report.js +72 -0
  108. package/dist/cli/prisma-resolve.d.ts +6 -0
  109. package/dist/cli/prisma-resolve.js +1 -0
  110. package/dist/cli/prisma-schema.d.ts +62 -2
  111. package/dist/cli/prisma-schema.js +81 -4
  112. package/dist/cli/rate-limit.d.ts +32 -0
  113. package/dist/cli/rate-limit.js +40 -0
  114. package/dist/cli/studio.d.ts +5 -5
  115. package/dist/cli/studio.js +135 -70
  116. package/dist/cli/ui.d.ts +1 -1
  117. package/dist/cli/ui.js +51 -9
  118. package/dist/client.d.ts +40 -0
  119. package/dist/client.js +3 -0
  120. package/dist/dialect.d.ts +17 -1
  121. package/dist/dialect.js +37 -12
  122. package/dist/generate.js +40 -7
  123. package/dist/index.d.ts +1 -1
  124. package/dist/index.js +1 -1
  125. package/dist/mssql.js +6 -0
  126. package/dist/mysql.js +16 -0
  127. package/dist/nested-write.d.ts +2 -0
  128. package/dist/nested-write.js +415 -25
  129. package/dist/powdb.d.ts +4 -2
  130. package/dist/powdb.js +106 -21
  131. package/dist/powql.d.ts +5 -0
  132. package/dist/powql.js +42 -6
  133. package/dist/prisma-compat.d.ts +2 -0
  134. package/dist/prisma-compat.js +166 -8
  135. package/dist/query/aggregates.js +7 -3
  136. package/dist/query/builder.d.ts +292 -21
  137. package/dist/query/builder.js +570 -64
  138. package/dist/query/deferred.d.ts +39 -0
  139. package/dist/query/index.d.ts +1 -1
  140. package/dist/query/index.js +1 -1
  141. package/dist/query/relations.d.ts +173 -5
  142. package/dist/query/relations.js +688 -47
  143. package/dist/query/types.d.ts +123 -39
  144. package/dist/query/utils.d.ts +116 -0
  145. package/dist/query/utils.js +198 -0
  146. package/dist/query/warn-registry.d.ts +9 -0
  147. package/dist/query/warn-registry.js +9 -0
  148. package/dist/query/where.d.ts +38 -1
  149. package/dist/query/where.js +58 -23
  150. package/dist/query/writes.d.ts +42 -1
  151. package/dist/query/writes.js +104 -13
  152. package/dist/schema-sql.d.ts +14 -0
  153. package/dist/schema-sql.js +23 -5
  154. package/dist/schema.d.ts +38 -0
  155. package/dist/schema.js +123 -0
  156. package/dist/sqlite.js +4 -0
  157. package/package.json +77 -28
@@ -0,0 +1,92 @@
1
+ /**
2
+ * turbine-orm: aggregate / groupBy compilation (extracted from builder.ts)
3
+ *
4
+ * buildAggregate + buildGroupBy and their helpers (HAVING clauses, groupBy
5
+ * ordering, DISTINCT-ON sources, JSON-path aggregate targets). All functions
6
+ * take a {@link BuilderCtx} first argument; WHERE compilation is reused from
7
+ * where.ts (via `whereMod`), and the shared orderBy / row-parse primitives
8
+ * stay class-resident, reached through the ctx. See builder.ts for the thin
9
+ * delegating methods (buildGroupBy / buildAggregate).
10
+ */
11
+ import type { TableMetadata } from '../schema.js';
12
+ import type { DeferredQuery } from './deferred.js';
13
+ import type { AggregateArgs, AggregateResult, GroupByArgs, GroupByOrderBy, HavingClause, HavingFilter } from './types.js';
14
+ import type { BuilderCtx } from './where.js';
15
+ /**
16
+ * Enforce the PII contract on the aggregate surface. A PII-tagged
17
+ * (`defineSchema` `pii: true`) column is excluded from every default
18
+ * projection, and a value-returning aggregate is a projection by another name:
19
+ * `groupBy({ by: ['email'] })` emits one row per distinct plaintext email, and
20
+ * `_min`/`_max` return a stored cell verbatim. Both therefore REQUIRE the same
21
+ * `includePii: true` opt-in reads use.
22
+ *
23
+ * Deliberately NOT gated: `_count` (a count, never a value), `_sum` / `_avg`
24
+ * (a computed total across many rows, not a stored cell), and `where` /
25
+ * `orderBy` / `having` on PII columns (they return no values at all). Untagged
26
+ * schemas short-circuit on the `pii` lookup, so their SQL is byte-identical.
27
+ *
28
+ * Shared with the PowQL aggregate paths (src/powql.ts) so every engine applies
29
+ * one policy.
30
+ */
31
+ export declare function assertAggregatePiiOptIn(table: string, meta: TableMetadata | undefined, field: string, column: string, usage: string, includePii: boolean | undefined): void;
32
+ export declare function buildGroupBy<T extends object>(qi: BuilderCtx, args: GroupByArgs<T>): DeferredQuery<Record<string, unknown>[]>;
33
+ /**
34
+ * Compile a groupBy `orderBy` into an ORDER BY body. Unlike findMany ORDER BY
35
+ * ({@link buildOrderBy}, which validates keys against the table's physical
36
+ * columns), groupBy ordering targets the columns the RESULT actually
37
+ * contains: plain by-fields, JSON group-key aliases, and requested aggregates
38
+ * (`_count` / `_sum` / `_avg` / `_min` / `_max`). Each key re-emits the exact
39
+ * SELECT expression that produced it (`byOrderExprs` / `aggOrderExprs`),
40
+ * mirroring how HAVING re-emits aggregate expressions, so no dialect ever has
41
+ * to accept a SELECT-alias reference in ORDER BY, and any already-bound
42
+ * JSON-path placeholder is reused verbatim (ORDER BY is the last clause, so
43
+ * no `$n` renumbering). An aggregate key that was not requested, or an unknown
44
+ * by-key, throws {@link ValidationError} E003 listing the valid keys.
45
+ */
46
+ export declare function buildGroupByOrderBy(qi: BuilderCtx, orderBy: GroupByOrderBy | GroupByOrderBy[], byOrderExprs: Map<string, string>, aggOrderExprs: Map<string, string>): string;
47
+ /**
48
+ * Validate a JSON-path target (group key or aggregate target) in groupBy:
49
+ * the field must resolve to a real json/jsonb column and the path must be a
50
+ * non-empty array of keys/indexes. Returns the resolved snake_case column.
51
+ */
52
+ export declare function resolveJsonPathTarget(qi: BuilderCtx, context: string, field: string, path: (string | number)[]): string;
53
+ /**
54
+ * Build the `distinctOn` row source for groupBy (PostgreSQL only: other
55
+ * engines throw {@link UnsupportedFeatureError} E017):
56
+ *
57
+ * ```sql
58
+ * (SELECT DISTINCT ON ("c1") * FROM "table"<WHERE> ORDER BY "c1", <orderBy>) AS "table"
59
+ * ```
60
+ *
61
+ * The wrapper is aliased as the table name so every outer expression (group
62
+ * keys, aggregates, HAVING, ORDER BY) is byte-identical to the plain path.
63
+ * `distinctOn.orderBy` is required (it decides which row survives) and
64
+ * supports plain columns, {@link OrderBySpec} nulls, and JSON-path specs;
65
+ * JSON paths push their text[] param here, after the WHERE params.
66
+ */
67
+ export declare function buildDistinctOnSource<T extends object>(qi: BuilderCtx, distinctOn: NonNullable<GroupByArgs<T>['distinctOn']>, whereSql: string, params: unknown[]): string;
68
+ /**
69
+ * Build the SQL fragments for a {@link HavingClause}.
70
+ *
71
+ * Each aggregate expression (`COUNT(*)`, `SUM("col")`, etc.) is constructed
72
+ * from a **schema-validated, quoted** column identifier: `qi.toColumn()`
73
+ * throws {@link ValidationError} for unknown fields and `qi.q()` quotes via
74
+ * the dialect, so no unvalidated identifier ever reaches the SQL string. Every
75
+ * comparison value is pushed onto the shared `params` array and referenced by
76
+ * a `$N` placeholder via {@link buildHavingNumericClauses} — there is no string
77
+ * interpolation of user values.
78
+ *
79
+ * `jsonAggExprs` (from {@link buildGroupBy}) maps `alias:aggKey` to the
80
+ * exact aggregate expression a JSON-path aggregate emitted in SELECT
81
+ * (including its already-bound path placeholder), so HAVING on a JSON-path
82
+ * aggregate alias reuses the same expression instead of resolving the alias
83
+ * as a column.
84
+ */
85
+ export declare function buildHavingClauses<T extends object>(qi: BuilderCtx, having: HavingClause<T>, params: unknown[], jsonAggExprs?: Map<string, string>): string[];
86
+ /**
87
+ * Convert a single having filter into one or more parameterized SQL
88
+ * comparisons against the given aggregate expression. A bare number is
89
+ * shorthand for equality. Unknown operator keys throw {@link ValidationError}.
90
+ */
91
+ export declare function buildHavingNumericClauses(qi: BuilderCtx, expr: string, filter: HavingFilter, params: unknown[]): string[];
92
+ export declare function buildAggregate<T extends object>(qi: BuilderCtx, args: AggregateArgs<T>): DeferredQuery<AggregateResult<T>>;
@@ -54,6 +54,7 @@ exports.buildAggregate = buildAggregate;
54
54
  const errors_js_1 = require("../errors.js");
55
55
  const schema_js_1 = require("../schema.js");
56
56
  const filters_js_1 = require("./filters.js");
57
+ const utils_js_1 = require("./utils.js");
57
58
  const whereMod = __importStar(require("./where.js"));
58
59
  /**
59
60
  * Enforce the PII contract on the aggregate surface. A PII-tagged
@@ -275,8 +276,8 @@ function buildGroupBy(qi, args) {
275
276
  // no `$n` renumbering. `offset` without a deterministic `orderBy` yields an
276
277
  // arbitrary window (same caveat as findMany).
277
278
  if (args.limit !== undefined || args.offset !== undefined) {
278
- const limitPh = args.limit !== undefined ? qi.paginationRef(args.limit, params) : undefined;
279
- const offsetPh = args.offset !== undefined ? qi.paginationRef(args.offset, params) : undefined;
279
+ const limitPh = args.limit !== undefined ? qi.paginationRef(args.limit, params, 'limit') : undefined;
280
+ const offsetPh = args.offset !== undefined ? qi.paginationRef(args.offset, params, 'skip/offset') : undefined;
280
281
  sql += qi.buildPagination(limitPh, offsetPh, args.orderBy !== undefined);
281
282
  }
282
283
  return {
@@ -556,7 +557,10 @@ function buildHavingClauses(qi, having, params, jsonAggExprs) {
556
557
  for (const [aggKey, filter] of Object.entries(value)) {
557
558
  if (filter === undefined)
558
559
  continue;
559
- const fn = aggFnByKey[aggKey];
560
+ // ownLookup, not a bare index: an inherited Object.prototype member
561
+ // ("constructor", "toString", …) would otherwise resolve to a truthy
562
+ // builtin and be spliced into the HAVING clause as its source text.
563
+ const fn = (0, utils_js_1.ownLookup)(aggFnByKey, aggKey);
560
564
  if (!fn) {
561
565
  throw new errors_js_1.ValidationError(`[turbine] Unknown aggregate "${aggKey}" in having for field "${key}" on table "${qi.table}". ` +
562
566
  `Supported: ${Object.keys(aggFnByKey).join(', ')}.`);
@@ -0,0 +1,193 @@
1
+ /**
2
+ * turbine-orm — Batched relation loader (the `relationLoadStrategy: 'batched'` path)
3
+ *
4
+ * ## Why this exists
5
+ *
6
+ * Turbine's default `with`-clause strategy resolves nested relations in ONE SQL
7
+ * statement using correlated `json_agg(json_build_object(...))` subqueries — one
8
+ * probe per parent row (see `buildRelationSubquery` in builder.ts). That is the
9
+ * right default: a single round-trip, and when the child FK columns are indexed
10
+ * each probe is an index seek. But it degrades in two situations:
11
+ *
12
+ * 1. **Missing FK index** — a correlated probe per parent row becomes
13
+ * N-parents × full-table-scan. A batched-loader ORM pays that missing index
14
+ * only ONCE (a single `WHERE fk = ANY($1)` seq-scan), which is why schemas
15
+ * migrated from those ORMs often lack the index the json_agg path needs.
16
+ * 2. **Huge unpaginated result sets** — the JSON wire format
17
+ * (`json_build_object` per row, re-serialized inside `json_agg`) is heavy to
18
+ * encode/decode compared with flat rows.
19
+ *
20
+ * This module implements the alternative, opt-in strategy: run the base query
21
+ * WITHOUT relation subqueries, collect the parent keys, then issue ONE flat
22
+ * follow-up query per relation (`SELECT ... FROM child WHERE fk = ANY($1)`),
23
+ * and stitch the children onto the parents in memory. D relation levels cost D
24
+ * extra round-trips instead of one, but each is a single indexed lookup over a
25
+ * key set, and rows come back flat.
26
+ *
27
+ * ## Design constraints (see CLAUDE.md)
28
+ *
29
+ * - **Same executor / connection path.** Every follow-up query runs through the
30
+ * caller's own executor ({@link RelationLoadContext.exec}) and child query
31
+ * interfaces built on the caller's pool. Inside a `$transaction` that pool is
32
+ * the pinned-connection `txPool`, so batched loads join the transaction — no
33
+ * separate pool checkout per query.
34
+ * - **Identical output shape.** The stitched result is byte-for-byte the same
35
+ * shape the join strategy produces: relation arrays for hasMany/manyToMany
36
+ * (`[]` when empty), single-or-null for hasOne/belongsTo, with the same
37
+ * camelCase keys and Date coercion — because the child rows are parsed by the
38
+ * very same `parseRow`/`buildFindMany` machinery via a child QueryInterface.
39
+ * - **Stitch keys never leak.** To stitch, the follow-up query must select the
40
+ * FK/PK it joins on even when the caller's `select`/`omit` excluded it; the
41
+ * loader adds those columns for the query and strips them from the returned
42
+ * entities afterwards ({@link includeKeysForBatching}).
43
+ *
44
+ * PowDB (powql.ts) has its own batched loaders for the same reasons — this is the
45
+ * clean Postgres/SQL implementation, deliberately NOT shared with PowQL.
46
+ *
47
+ * @module
48
+ */
49
+ import type pg from 'pg';
50
+ import { type RelationDef, type SchemaMetadata, type TableMetadata } from '../schema.js';
51
+ import type { ReselectExecutor } from './builder.js';
52
+ import type { SkipGlobalFilters, WithClause, WithCount } from './types.js';
53
+ /**
54
+ * A DeferredQuery, minimally typed for what the loader consumes. Kept local to
55
+ * avoid a value import of builder.ts (which imports this module).
56
+ */
57
+ interface Deferred {
58
+ sql: string;
59
+ params: unknown[];
60
+ preparedName?: string;
61
+ transform: (result: pg.QueryResult) => unknown;
62
+ }
63
+ /**
64
+ * The read surface the loader needs from a child QueryInterface: build (but do
65
+ * not execute) a flat findMany. The loader runs the built SQL through
66
+ * {@link RelationLoadContext.exec}, so execution stays on the caller's connection.
67
+ */
68
+ export interface BatchedChildReader {
69
+ buildFindMany(args: Record<string, unknown>): Deferred;
70
+ }
71
+ /**
72
+ * Everything the loader needs from the owning QueryInterface, passed as closures
73
+ * so this module never imports builder.ts at runtime (it is imported BY it).
74
+ */
75
+ export interface RelationLoadContext {
76
+ /** Metadata of the table whose rows are the current `parents`. */
77
+ parentMeta: TableMetadata;
78
+ schema: SchemaMetadata;
79
+ /** Build a child reader for `table`, bound to the caller's pool (tx-safe). */
80
+ makeChild: (table: string) => BatchedChildReader;
81
+ /** Run raw SQL through the caller's executor (same timeout/instrumentation path). */
82
+ exec: ReselectExecutor;
83
+ /** Quote an identifier via the active dialect. */
84
+ quote: (name: string) => string;
85
+ /** Build an `IN`/`ANY` predicate via the active dialect (PG: `expr = ANY($n)`). */
86
+ buildInClause: (expr: string, paramRef: string, negated: boolean) => string;
87
+ /** The single bound value for an `IN` list (PG: the array as-is). */
88
+ inClauseParam: (values: unknown[]) => unknown;
89
+ /** Placeholder for a 1-indexed parameter position (PG: `$n`). */
90
+ paramPlaceholder: (index: number) => string;
91
+ /**
92
+ * The query's `skipGlobalFilters` opt-out, threaded onto every child
93
+ * `buildFindMany` so relation row loads honor (or skip) the target table's
94
+ * global filter exactly as the join strategy would.
95
+ */
96
+ skipGlobalFilters?: SkipGlobalFilters;
97
+ /**
98
+ * The query's `includePii` opt-in, threaded onto every child `buildFindMany`
99
+ * so a batched relation load excludes (or includes) PII-tagged columns exactly
100
+ * as the join strategy does at every nested level. Default `false`.
101
+ */
102
+ includePii?: boolean;
103
+ /**
104
+ * Render `table`'s global filter against `alias` for a raw follow-up query
105
+ * (the batched `_count`), numbering its `$n` placeholders AFTER
106
+ * `precedingParams` already-bound params. Returns `null` when no filter
107
+ * applies. Provided by the owning QueryInterface so this module needs no
108
+ * filter machinery of its own.
109
+ */
110
+ tableGlobalFilter?: (table: string, alias: string, precedingParams: number) => {
111
+ clause: string;
112
+ params: unknown[];
113
+ } | null;
114
+ }
115
+ /**
116
+ * The default projection of `meta` expressed in FIELD names: which fields the
117
+ * default (no `select`/`omit`) projection hides, and which it returns. Today the
118
+ * only hidden class is PII-tagged columns, and only when `includePii` is off.
119
+ *
120
+ * Returns `undefined` for the overwhelmingly common untagged case, so callers
121
+ * keep the `select: undefined, omit: undefined` fast path and the emitted SQL
122
+ * stays byte-identical.
123
+ */
124
+ export declare function defaultProjectionFields(meta: TableMetadata, includePii: boolean | undefined): {
125
+ hidden: ReadonlySet<string>;
126
+ visible: string[];
127
+ } | undefined;
128
+ /**
129
+ * Adjust a `select`/`omit` pair so that `fields` are guaranteed present in the
130
+ * query result, returning the adjusted projection plus the list of fields that
131
+ * were added ONLY for stitching and must be stripped from the final entities.
132
+ *
133
+ * Used both for the base query (parent keys) and each follow-up query (child
134
+ * keys) so a caller's `select: { title: true }` on a relation still stitches even
135
+ * though the FK was not requested — and the FK never appears in the output.
136
+ */
137
+ export declare function includeKeysForBatching(select: Record<string, boolean> | undefined, omit: Record<string, boolean> | undefined, fields: string[],
138
+ /**
139
+ * The default projection for this table when it is NOT `select`/`omit`-driven:
140
+ * `hidden` are fields the default projection leaves out (today: PII-tagged
141
+ * columns without `includePii`), `visible` is everything it does return.
142
+ *
143
+ * Without this, a correlation key that is itself PII-tagged is absent from
144
+ * every row, the loader sees no keys, and it silently hands back empty
145
+ * relation arrays. Passing it turns that case into an explicit select that
146
+ * re-adds only the key, which is then stripped like any other stitch-only
147
+ * field, so no PII value ever reaches the caller.
148
+ */
149
+ defaultProjection?: {
150
+ hidden: ReadonlySet<string>;
151
+ visible: string[];
152
+ }): {
153
+ select?: Record<string, boolean>;
154
+ omit?: Record<string, boolean>;
155
+ strip: string[];
156
+ };
157
+ /** Delete stitch-only key fields from each row (no-op when `fields` is empty). */
158
+ export declare function stripFields(rows: Record<string, unknown>[], fields: string[]): void;
159
+ /**
160
+ * The set of parent FIELD names a batched load of `withClause` needs present on
161
+ * each parent row in order to stitch (the local key of every requested relation).
162
+ * The caller adds these to the base query and strips the added ones afterwards.
163
+ */
164
+ export declare function neededParentKeyFields(parentMeta: TableMetadata, withClause: WithClause): string[];
165
+ /**
166
+ * Resolve the set of to-many relations a `_count` spec selects. `true` counts
167
+ * every to-many relation (hasMany + manyToMany) of the table; the record form
168
+ * counts only the enabled names. Shared by the join builder and the batched
169
+ * loader so both count the exact same relations.
170
+ *
171
+ * Errors: E005 ({@link RelationError}) for an unknown relation name, E003
172
+ * ({@link ValidationError}) when a named relation is to-one.
173
+ */
174
+ export declare function resolveCountRelations(parentMeta: TableMetadata, countSpec: WithCount): RelationDef[];
175
+ /**
176
+ * Reject pick-row relation ordering anywhere inside a `with` tree's orderBy —
177
+ * strategy parity with the join path, which throws this exact E003 at SQL
178
+ * build time (`pickOrderNestedError` in builder.ts). Without this guard the
179
+ * loaders would forward `options.orderBy` as the child reader's TOP-LEVEL
180
+ * findMany orderBy, where the pick shape compiles fine — so the same query
181
+ * would execute on 'batched' but throw on 'join'. Walks the whole tree up
182
+ * front so acceptance never depends on which levels have rows — the batched
183
+ * runners in builder.ts call this BEFORE the base query (a zero-row base
184
+ * result must still reject, exactly like the join strategy's build-time throw).
185
+ */
186
+ export declare function rejectNestedPickOrder(withClause: WithClause): void;
187
+ /**
188
+ * Load every relation in `withClause` for `parents` and attach it onto each row
189
+ * in place. Mirrors the join strategy's output shape exactly. Recurses for nested
190
+ * `with` by re-running itself against the freshly-loaded child rows.
191
+ */
192
+ export declare function loadRelationsBatched(ctx: RelationLoadContext, parents: Record<string, unknown>[], withClause: WithClause, timeout?: number, depth?: number, path?: string[]): Promise<void>;
193
+ export {};