@danceroutine/tango-orm 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/InternalDialect-ClSaUNso.js +10 -0
- package/dist/InternalDialect-ClSaUNso.js.map +1 -0
- package/dist/PostgresAdapter-CXKdKBG-.js +4 -0
- package/dist/PostgresAdapter-DySFW6vy.js +128 -0
- package/dist/PostgresAdapter-DySFW6vy.js.map +1 -0
- package/dist/{SqliteClient-CjOK9-ki.js → SqliteAdapter-CDdOjRmW.js} +57 -3
- package/dist/SqliteAdapter-CDdOjRmW.js.map +1 -0
- package/dist/SqliteAdapter-mjtXuVTg.js +4 -0
- package/dist/connection/adapters/Adapter.d.ts +32 -1
- package/dist/connection/adapters/dialects/PostgresAdapter.d.ts +5 -6
- package/dist/connection/adapters/dialects/SqliteAdapter.d.ts +4 -6
- package/dist/connection/adapters/index.d.ts +1 -1
- package/dist/connection/index.d.ts +1 -1
- package/dist/connection/index.js +4 -5
- package/dist/{connection-B_K2ZAf7.js → connection-Dmhgx31M.js} +5 -7
- package/dist/{connection-B_K2ZAf7.js.map → connection-Dmhgx31M.js.map} +1 -1
- package/dist/{defaultRuntime-BPK9kWEW.js → defaultRuntime-DzqBQ9Hb.js} +63 -16
- package/dist/defaultRuntime-DzqBQ9Hb.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +11 -12
- package/dist/manager/ManagerLike.d.ts +19 -0
- package/dist/manager/ModelManager.d.ts +45 -2
- package/dist/manager/index.d.ts +6 -0
- package/dist/manager/index.js +8 -7
- package/dist/manager/internal/MutationCompiler.d.ts +14 -6
- package/dist/manager/relations/ManyToManyRelatedManager.d.ts +147 -0
- package/dist/manager/relations/ManyToManyRelatedQuerySet.d.ts +62 -0
- package/dist/manager/relations/MaterializedModelRecord.d.ts +28 -0
- package/dist/manager/relations/index.d.ts +9 -0
- package/dist/manager/relations/internal/ThroughTableManager.d.ts +79 -0
- package/dist/manager-DrDTiCAz.js +24 -0
- package/dist/manager-DrDTiCAz.js.map +1 -0
- package/dist/query/ModelQuerySet.d.ts +20 -0
- package/dist/query/QBuilder.d.ts +3 -3
- package/dist/query/QuerySet.d.ts +58 -18
- package/dist/query/compiler/QueryCompiler.d.ts +13 -4
- package/dist/query/domain/CompiledQuery.d.ts +169 -2
- package/dist/query/domain/FilterInput.d.ts +1 -1
- package/dist/query/domain/FilterKey.d.ts +4 -2
- package/dist/query/domain/QNode.d.ts +4 -4
- package/dist/query/domain/QuerySetState.d.ts +3 -3
- package/dist/query/domain/RelationMeta.d.ts +9 -0
- package/dist/query/domain/RelationTyping.d.ts +47 -0
- package/dist/query/domain/TableMetaFactory.d.ts +1 -14
- package/dist/query/domain/index.d.ts +1 -1
- package/dist/query/domain/internal/InternalPrefetchQueryKind.d.ts +20 -0
- package/dist/query/index.d.ts +1 -0
- package/dist/query/index.js +3 -2
- package/dist/query/internal/isQNodeLike.d.ts +3 -0
- package/dist/query/planning/QueryPlanner.d.ts +1 -1
- package/dist/{query-C6So1r6H.js → query-DUZnBFhf.js} +474 -156
- package/dist/query-DUZnBFhf.js.map +1 -0
- package/dist/registerModelObjects-DxlBfuUN.js +797 -0
- package/dist/registerModelObjects-DxlBfuUN.js.map +1 -0
- package/dist/runtime/TangoRuntime.d.ts +9 -0
- package/dist/runtime/index.d.ts +3 -2
- package/dist/runtime/index.js +7 -6
- package/dist/runtime/internal/SqliteDBClientProvider.d.ts +3 -0
- package/dist/{runtime-ByXbpVBS.js → runtime-1H88J3nN.js} +3 -3
- package/dist/runtime-1H88J3nN.js.map +1 -0
- package/dist/transaction/index.js +5 -4
- package/dist/{transaction-Cs0Z9tbW.js → transaction-ZhfDf-f8.js} +2 -2
- package/dist/{transaction-Cs0Z9tbW.js.map → transaction-ZhfDf-f8.js.map} +1 -1
- package/dist/validation/SQLValidationEngine.d.ts +22 -5
- package/dist/validation/SqlValidationPlan.d.ts +5 -4
- package/dist/validation/internal/InternalSqlValidationPlanKind.d.ts +25 -0
- package/dist/validation/internal/InternalValidatedFilterDescriptorKind.d.ts +4 -0
- package/package.json +6 -6
- package/dist/PostgresAdapter-BFdo_nIt.js +0 -4
- package/dist/PostgresAdapter-CMiEpHya.js +0 -49
- package/dist/PostgresAdapter-CMiEpHya.js.map +0 -1
- package/dist/PostgresClient-BQJZfEOT.js +0 -68
- package/dist/PostgresClient-BQJZfEOT.js.map +0 -1
- package/dist/SqliteAdapter-A-P9zUhP.js +0 -4
- package/dist/SqliteAdapter-CeqhyrPC.js +0 -44
- package/dist/SqliteAdapter-CeqhyrPC.js.map +0 -1
- package/dist/SqliteClient-CjOK9-ki.js.map +0 -1
- package/dist/defaultRuntime-BPK9kWEW.js.map +0 -1
- package/dist/manager-C6oJ2tAF.js +0 -13
- package/dist/manager-C6oJ2tAF.js.map +0 -1
- package/dist/query-C6So1r6H.js.map +0 -1
- package/dist/registerModelObjects-BKMpfc4Z.js +0 -263
- package/dist/registerModelObjects-BKMpfc4Z.js.map +0 -1
- package/dist/runtime-ByXbpVBS.js.map +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { __export } from "./chunk-DLY2FNSh.js";
|
|
2
|
+
import { ManyToManyRelatedManager, ManyToManyRelatedQuerySet, ModelManager, registerModelObjects } from "./registerModelObjects-DxlBfuUN.js";
|
|
3
|
+
|
|
4
|
+
//#region src/manager/relations/index.ts
|
|
5
|
+
var relations_exports = {};
|
|
6
|
+
__export(relations_exports, {
|
|
7
|
+
ManyToManyRelatedManager: () => ManyToManyRelatedManager,
|
|
8
|
+
ManyToManyRelatedQuerySet: () => ManyToManyRelatedQuerySet
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/manager/index.ts
|
|
13
|
+
var manager_exports = {};
|
|
14
|
+
__export(manager_exports, {
|
|
15
|
+
ManyToManyRelatedManager: () => ManyToManyRelatedManager,
|
|
16
|
+
ManyToManyRelatedQuerySet: () => ManyToManyRelatedQuerySet,
|
|
17
|
+
ModelManager: () => ModelManager,
|
|
18
|
+
registerModelObjects: () => registerModelObjects,
|
|
19
|
+
relations: () => relations_exports
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { manager_exports, relations_exports };
|
|
24
|
+
//# sourceMappingURL=manager-DrDTiCAz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager-DrDTiCAz.js","names":[],"sources":["../src/manager/relations/index.ts","../src/manager/index.ts"],"sourcesContent":["/**\n * Domain boundary barrel: centralizes ORM relation managers attached to\n * materialized model records.\n */\n\nexport { ManyToManyRelatedQuerySet } from './ManyToManyRelatedQuerySet';\nexport type { ManyToManyRelatedQuerySetBridge } from './ManyToManyRelatedQuerySet';\nexport { ManyToManyRelatedManager } from './ManyToManyRelatedManager';\nexport type { ManyToManyRelatedManagerCreateInputs, ManyToManyTargetRef } from './ManyToManyRelatedManager';\nexport type { MaterializedModelRecord } from './MaterializedModelRecord';\n","/**\n * Domain boundary barrel: centralizes manager-first ORM APIs.\n */\nexport * as relations from './relations/index';\n\nexport type { ManagerLike } from './ManagerLike';\nexport { ModelManager } from './ModelManager';\nexport { registerModelObjects } from './registerModelObjects';\nexport { ManyToManyRelatedQuerySet } from './relations/ManyToManyRelatedQuerySet';\nexport type { ManyToManyRelatedQuerySetBridge } from './relations/ManyToManyRelatedQuerySet';\nexport { ManyToManyRelatedManager } from './relations/ManyToManyRelatedManager';\nexport type { ManyToManyRelatedManagerCreateInputs, ManyToManyTargetRef } from './relations/ManyToManyRelatedManager';\nexport type { MaterializedModelRecord } from './relations/MaterializedModelRecord';\n"],"mappings":""}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maintainer note: model-backed and specialized relation-backed querysets
|
|
3
|
+
* share the fluent API through `QuerySet.spawn(...)`. This concrete class
|
|
4
|
+
* owns the direct "compile the accumulated state and execute it" path, while
|
|
5
|
+
* subclasses preserve specialized execution behavior by returning their own
|
|
6
|
+
* queryset family from `spawn(...)`.
|
|
7
|
+
*/
|
|
8
|
+
import type { QuerySetState } from './domain/QuerySetState';
|
|
9
|
+
import type { QueryExecutor } from './QuerySet';
|
|
10
|
+
import { QuerySet } from './QuerySet';
|
|
11
|
+
/**
|
|
12
|
+
* Concrete `QuerySet` implementation returned by `Model.objects.query()`.
|
|
13
|
+
*
|
|
14
|
+
* It executes the accumulated queryset state directly against the bound
|
|
15
|
+
* model executor.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ModelQuerySet<TModel extends Record<string, unknown>, TBaseResult extends Record<string, unknown> = TModel, TSourceModel = unknown, THydrated extends Record<string, unknown> = Record<never, never>> extends QuerySet<TModel, TBaseResult, TSourceModel, THydrated> {
|
|
18
|
+
constructor(executor: QueryExecutor<TModel>, state?: QuerySetState<TModel, TSourceModel>);
|
|
19
|
+
protected spawn<TNextBaseResult extends Record<string, unknown> = TBaseResult, TNextHydrated extends Record<string, unknown> = THydrated>(state: QuerySetState<TModel, TSourceModel>): ModelQuerySet<TModel, TNextBaseResult, TSourceModel, TNextHydrated>;
|
|
20
|
+
}
|
package/dist/query/QBuilder.d.ts
CHANGED
|
@@ -16,14 +16,14 @@ export declare class QBuilder {
|
|
|
16
16
|
/**
|
|
17
17
|
* Combine multiple filter fragments using logical `AND`.
|
|
18
18
|
*/
|
|
19
|
-
static and<T>(...nodes: Array<FilterInput<T> | QNode<T>>): QNode<T>;
|
|
19
|
+
static and<T, TSourceModel = unknown>(...nodes: Array<FilterInput<T, TSourceModel> | QNode<T, TSourceModel>>): QNode<T, TSourceModel>;
|
|
20
20
|
/**
|
|
21
21
|
* Combine multiple filter fragments using logical `OR`.
|
|
22
22
|
*/
|
|
23
|
-
static or<T>(...nodes: Array<FilterInput<T> | QNode<T>>): QNode<T>;
|
|
23
|
+
static or<T, TSourceModel = unknown>(...nodes: Array<FilterInput<T, TSourceModel> | QNode<T, TSourceModel>>): QNode<T, TSourceModel>;
|
|
24
24
|
/**
|
|
25
25
|
* Negate a filter fragment using logical `NOT`.
|
|
26
26
|
*/
|
|
27
|
-
static not<T>(node: FilterInput<T> | QNode<T>): QNode<T>;
|
|
27
|
+
static not<T, TSourceModel = unknown>(node: FilterInput<T, TSourceModel> | QNode<T, TSourceModel>): QNode<T, TSourceModel>;
|
|
28
28
|
private static wrapNode;
|
|
29
29
|
}
|
package/dist/query/QuerySet.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { DBClient } from '../connection/clients/DBClient';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Adapter } from '../connection/adapters/Adapter';
|
|
3
3
|
import type { QuerySetState } from './domain/QuerySetState';
|
|
4
4
|
import type { TableMeta } from './domain/TableMeta';
|
|
5
5
|
import type { QNode } from './domain/QNode';
|
|
6
|
+
import { QueryResult } from './domain/QueryResult';
|
|
6
7
|
import type { OrderToken } from './domain/OrderToken';
|
|
8
|
+
import type { OrderSpec } from './domain/OrderSpec';
|
|
7
9
|
import type { FilterInput } from './domain/FilterInput';
|
|
8
10
|
import type { CompiledQuery } from './domain/CompiledQuery';
|
|
9
|
-
import { QueryResult } from './domain/QueryResult';
|
|
10
11
|
import type { GeneratedHydratedRelationMap, GeneratedPrefetchRelatedPathKeys, GeneratedSelectRelatedPathKeys, HydratedQueryResult, ManyRelationHydrationCardinality, MaybeHydratedRelationMap, PrefetchRelatedRelations, RelationKeys, SelectRelatedRelations, SingleRelationHydrationCardinality } from './domain/RelationTyping';
|
|
11
12
|
/**
|
|
12
13
|
* Query execution seam consumed by `QuerySet`.
|
|
@@ -19,8 +20,20 @@ import type { GeneratedHydratedRelationMap, GeneratedPrefetchRelatedPathKeys, Ge
|
|
|
19
20
|
export interface QueryExecutor<TModel> {
|
|
20
21
|
meta: TableMeta;
|
|
21
22
|
client: DBClient;
|
|
22
|
-
|
|
23
|
+
adapter: Adapter;
|
|
23
24
|
run(compiled: CompiledQuery): Promise<TModel[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Optional hook invoked by `QuerySet` after a record has been
|
|
27
|
+
* materialized so executors can attach related-manager accessors
|
|
28
|
+
* (such as the many-to-many related manager) onto the record.
|
|
29
|
+
*
|
|
30
|
+
* The optional `modelKey` argument lets the executor route the attach
|
|
31
|
+
* call to the correct model when the record belongs to a related
|
|
32
|
+
* model rather than the executor's own source model. Implementations
|
|
33
|
+
* must not overwrite existing properties on the record so prior
|
|
34
|
+
* hydration assignments survive the attach pass.
|
|
35
|
+
*/
|
|
36
|
+
attachPersistedRecordAccessors?(record: Record<string, unknown>, modelKey?: string): void;
|
|
24
37
|
}
|
|
25
38
|
type QueryShapeFunction<TInput, TOutput> = (row: TInput) => TOutput;
|
|
26
39
|
type QueryShapeParser<TInput, TOutput> = {
|
|
@@ -34,12 +47,6 @@ type ProjectedResult<TModel extends Record<string, unknown>, TKeys extends reado
|
|
|
34
47
|
* Provides a fluent API for filtering, ordering, pagination, projection, and
|
|
35
48
|
* nested relation hydration.
|
|
36
49
|
*
|
|
37
|
-
* Refinements such as `filter`, `orderBy`, `select`, and relation loaders build
|
|
38
|
-
* query state only. SQL runs when you call an evaluation method (`fetch`,
|
|
39
|
-
* `fetchOne`, `count`, `exists`, or `for await` over this queryset). After the
|
|
40
|
-
* first row-returning evaluation, this queryset instance reuses its cached
|
|
41
|
-
* materialized result on later `fetch()` or async-iteration calls.
|
|
42
|
-
*
|
|
43
50
|
* @template TModel - The full model row type used for query composition
|
|
44
51
|
* @template TBaseResult - The selected base-row shape returned by execution methods
|
|
45
52
|
* @template TSourceModel - The source Tango model used for typed relation metadata
|
|
@@ -56,30 +63,47 @@ type ProjectedResult<TModel extends Record<string, unknown>, TKeys extends reado
|
|
|
56
63
|
* .fetch();
|
|
57
64
|
* ```
|
|
58
65
|
*/
|
|
59
|
-
export declare class QuerySet<TModel extends Record<string, unknown>, TBaseResult extends Record<string, unknown> = TModel, TSourceModel = unknown, THydrated extends Record<string, unknown> = Record<never, never>> implements AsyncIterable<HydratedQueryResult<TBaseResult, THydrated>> {
|
|
66
|
+
export declare abstract class QuerySet<TModel extends Record<string, unknown>, TBaseResult extends Record<string, unknown> = TModel, TSourceModel = unknown, THydrated extends Record<string, unknown> = Record<never, never>> implements AsyncIterable<HydratedQueryResult<TBaseResult, THydrated>> {
|
|
60
67
|
[Symbol.asyncIterator]: () => AsyncIterator<HydratedQueryResult<TBaseResult, THydrated>>;
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
protected executor: QueryExecutor<TModel>;
|
|
69
|
+
protected state: QuerySetState<TModel, TSourceModel>;
|
|
63
70
|
static readonly BRAND: "tango.orm.query_set";
|
|
64
71
|
readonly __tangoBrand: typeof QuerySet.BRAND;
|
|
65
72
|
private evaluationCache?;
|
|
66
|
-
constructor(executor: QueryExecutor<TModel>, state?: QuerySetState<TModel>);
|
|
73
|
+
constructor(executor: QueryExecutor<TModel>, state?: QuerySetState<TModel, TSourceModel>);
|
|
74
|
+
/**
|
|
75
|
+
* Create another queryset of the same runtime family with the supplied
|
|
76
|
+
* query state. Concrete subclasses implement this so fluent calls keep
|
|
77
|
+
* their subclass-specific execution behavior instead of falling back to
|
|
78
|
+
* the standard queryset implementation.
|
|
79
|
+
*/
|
|
80
|
+
protected abstract spawn<TNextBaseResult extends Record<string, unknown> = TBaseResult, TNextHydrated extends Record<string, unknown> = THydrated>(state: QuerySetState<TModel, TSourceModel>): QuerySet<TModel, TNextBaseResult, TSourceModel, TNextHydrated>;
|
|
67
81
|
/**
|
|
68
82
|
* Narrow an unknown value to `QuerySet`.
|
|
69
83
|
*/
|
|
70
84
|
static isQuerySet<TModel extends Record<string, unknown>, TResult extends Record<string, unknown> = TModel>(value: unknown): value is QuerySet<TModel, TResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Translate user-facing order tokens like `'name'` or `'-createdAt'` into
|
|
87
|
+
* the internal `OrderSpec` array used by `QuerySetState`.
|
|
88
|
+
*
|
|
89
|
+
* Exposed as `protected` so subclasses can compose the same parse logic
|
|
90
|
+
* when they need to return their own concrete type from `orderBy` without
|
|
91
|
+
* reaching into a base-class instance's protected state.
|
|
92
|
+
*/
|
|
93
|
+
protected static buildOrderSpecs<T extends Record<string, unknown>>(tokens: readonly OrderToken<T>[]): OrderSpec<T>[];
|
|
94
|
+
private static invertOrderSpec;
|
|
71
95
|
/**
|
|
72
96
|
* Add a filter expression to the query.
|
|
73
97
|
*
|
|
74
98
|
* Multiple `filter()` calls are composed with `AND`.
|
|
75
99
|
*/
|
|
76
|
-
filter(q: FilterInput<TModel> | QNode<TModel>): QuerySet<TModel, TBaseResult, TSourceModel, THydrated>;
|
|
100
|
+
filter(q: FilterInput<TModel, TSourceModel> | QNode<TModel, TSourceModel>): QuerySet<TModel, TBaseResult, TSourceModel, THydrated>;
|
|
77
101
|
/**
|
|
78
102
|
* Add an exclusion expression to the query.
|
|
79
103
|
*
|
|
80
104
|
* Exclusions are translated to `NOT (...)` predicates.
|
|
81
105
|
*/
|
|
82
|
-
exclude(q: FilterInput<TModel> | QNode<TModel>): QuerySet<TModel, TBaseResult, TSourceModel, THydrated>;
|
|
106
|
+
exclude(q: FilterInput<TModel, TSourceModel> | QNode<TModel, TSourceModel>): QuerySet<TModel, TBaseResult, TSourceModel, THydrated>;
|
|
83
107
|
/**
|
|
84
108
|
* Apply ordering tokens such as `'name'` or `'-createdAt'`.
|
|
85
109
|
*/
|
|
@@ -121,6 +145,7 @@ export declare class QuerySet<TModel extends Record<string, unknown>, TBaseResul
|
|
|
121
145
|
* app-local registry current.
|
|
122
146
|
*/
|
|
123
147
|
prefetchRelated<TTargetModel = undefined, const TRelationName extends RelationKeys<PrefetchRelatedRelations<TSourceModel, NoInfer<TTargetModel>>> | GeneratedPrefetchRelatedPathKeys<TSourceModel> = RelationKeys<PrefetchRelatedRelations<TSourceModel, NoInfer<TTargetModel>>> | GeneratedPrefetchRelatedPathKeys<TSourceModel>>(...rels: readonly TRelationName[]): QuerySet<TModel, TBaseResult, TSourceModel, THydrated & MaybeHydratedRelationMap<TSourceModel, PrefetchRelatedRelations<TSourceModel, NoInfer<TTargetModel>>, Extract<TRelationName, RelationKeys<PrefetchRelatedRelations<TSourceModel, NoInfer<TTargetModel>>>>, ManyRelationHydrationCardinality> & GeneratedHydratedRelationMap<TSourceModel, Extract<TRelationName, GeneratedPrefetchRelatedPathKeys<TSourceModel>>>>;
|
|
148
|
+
all(): QuerySet<TModel, TBaseResult, TSourceModel, THydrated>;
|
|
124
149
|
/**
|
|
125
150
|
* Execute the query and optionally shape each row.
|
|
126
151
|
*
|
|
@@ -134,9 +159,10 @@ export declare class QuerySet<TModel extends Record<string, unknown>, TBaseResul
|
|
|
134
159
|
/**
|
|
135
160
|
* Async iterable surface for `for await (... of queryset)`.
|
|
136
161
|
*
|
|
137
|
-
* Evaluates this queryset on first use by awaiting
|
|
138
|
-
* yields each element from that
|
|
139
|
-
*
|
|
162
|
+
* Evaluates this queryset on first use by awaiting `fetch()` without
|
|
163
|
+
* arguments, then yields each element from that materialized result.
|
|
164
|
+
* Later async iterations over the same queryset instance reuse the cached
|
|
165
|
+
* materialized result instead of issuing another database round-trip.
|
|
140
166
|
*/
|
|
141
167
|
[Symbol.asyncIterator](): AsyncIterator<HydratedQueryResult<TBaseResult, THydrated>>;
|
|
142
168
|
/**
|
|
@@ -149,6 +175,15 @@ export declare class QuerySet<TModel extends Record<string, unknown>, TBaseResul
|
|
|
149
175
|
fetchOne<Out>(shape: QueryShapeFunction<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
|
|
150
176
|
fetchOne<Out>(shape: QueryShapeParser<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
|
|
151
177
|
fetchOne<TShape extends QueryShape<HydratedQueryResult<TBaseResult, THydrated>> | undefined>(shape: TShape): Promise<HydratedQueryResult<TBaseResult, THydrated> | QueryShapeOutput<HydratedQueryResult<TBaseResult, THydrated>, NonNullable<TShape>> | null>;
|
|
178
|
+
first(): Promise<HydratedQueryResult<TBaseResult, THydrated> | null>;
|
|
179
|
+
first<Out>(shape: QueryShapeFunction<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
|
|
180
|
+
first<Out>(shape: QueryShapeParser<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
|
|
181
|
+
last(): Promise<HydratedQueryResult<TBaseResult, THydrated> | null>;
|
|
182
|
+
last<Out>(shape: QueryShapeFunction<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
|
|
183
|
+
last<Out>(shape: QueryShapeParser<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out | null>;
|
|
184
|
+
get(q: FilterInput<TModel, TSourceModel> | QNode<TModel, TSourceModel>): Promise<HydratedQueryResult<TBaseResult, THydrated>>;
|
|
185
|
+
get<Out>(q: FilterInput<TModel, TSourceModel> | QNode<TModel, TSourceModel>, shape: QueryShapeFunction<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out>;
|
|
186
|
+
get<Out>(q: FilterInput<TModel, TSourceModel> | QNode<TModel, TSourceModel>, shape: QueryShapeParser<HydratedQueryResult<TBaseResult, THydrated>, Out>): Promise<Out>;
|
|
152
187
|
/**
|
|
153
188
|
* Execute a `COUNT(*)` query for the current filtered state.
|
|
154
189
|
*/
|
|
@@ -157,12 +192,17 @@ export declare class QuerySet<TModel extends Record<string, unknown>, TBaseResul
|
|
|
157
192
|
* Return whether at least one row matches the current query state.
|
|
158
193
|
*/
|
|
159
194
|
exists(): Promise<boolean>;
|
|
195
|
+
private shapeFetchedRow;
|
|
160
196
|
private getOrCreateEvaluationCache;
|
|
161
197
|
private evaluateRows;
|
|
198
|
+
private normalizeRowsForSchemaParsing;
|
|
162
199
|
private normalizeHydratedRowsForParserShape;
|
|
163
200
|
private hydrateRows;
|
|
201
|
+
private primeManyToManyOwnerCache;
|
|
202
|
+
private attachRootRecordAccessors;
|
|
164
203
|
private hydrateJoinNodesForOwner;
|
|
165
204
|
private hydratePrefetchNode;
|
|
205
|
+
private chunkValues;
|
|
166
206
|
private groupOwnersByAccessor;
|
|
167
207
|
private canonicalizeEntity;
|
|
168
208
|
private normalizeTargetRow;
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import type { QuerySetState } from '../domain/QuerySetState';
|
|
2
2
|
import type { TableMeta } from '../domain/TableMeta';
|
|
3
3
|
import type { CompiledHydrationNode, CompiledPrefetchQuery, CompiledQuery } from '../domain/CompiledQuery';
|
|
4
|
-
import type {
|
|
4
|
+
import type { Adapter } from '../../connection/adapters/Adapter';
|
|
5
5
|
/**
|
|
6
6
|
* Compiles immutable `QuerySet` state into parameterized SQL and recursive
|
|
7
7
|
* hydration execution artifacts.
|
|
8
8
|
*/
|
|
9
9
|
export declare class QueryCompiler {
|
|
10
10
|
private meta;
|
|
11
|
-
private
|
|
11
|
+
private adapter;
|
|
12
12
|
static readonly BRAND: "tango.orm.query_compiler";
|
|
13
13
|
readonly __tangoBrand: typeof QueryCompiler.BRAND;
|
|
14
|
-
|
|
14
|
+
private readonly placeholders;
|
|
15
|
+
constructor(meta: TableMeta, adapter: Adapter);
|
|
15
16
|
static isQueryCompiler(value: unknown): value is QueryCompiler;
|
|
16
|
-
compile<T>(state: QuerySetState<T>): CompiledQuery;
|
|
17
|
+
compile<T, TSourceModel = unknown>(state: QuerySetState<T, TSourceModel>): CompiledQuery;
|
|
17
18
|
compilePrefetch(node: CompiledHydrationNode, sourceValues: readonly (string | number)[]): CompiledPrefetchQuery;
|
|
19
|
+
compileManyToManyTargets(node: CompiledHydrationNode, targetIds: readonly (string | number)[]): {
|
|
20
|
+
sql: string;
|
|
21
|
+
params: readonly unknown[];
|
|
22
|
+
};
|
|
23
|
+
private compileManyToManyPrefetch;
|
|
18
24
|
private compileHydrationNode;
|
|
19
25
|
private validateHydrationRelation;
|
|
20
26
|
private buildRootHiddenSelects;
|
|
@@ -26,6 +32,7 @@ export declare class QueryCompiler {
|
|
|
26
32
|
private buildPrefetchBaseAlias;
|
|
27
33
|
private buildHydrationColumnAlias;
|
|
28
34
|
private buildPrefetchSourceAlias;
|
|
35
|
+
private buildFilterAlias;
|
|
29
36
|
private sanitizeRelationPath;
|
|
30
37
|
private assertInternalAliasDoesNotCollide;
|
|
31
38
|
private compileQNode;
|
|
@@ -33,6 +40,8 @@ export declare class QueryCompiler {
|
|
|
33
40
|
private compileAnd;
|
|
34
41
|
private compileOr;
|
|
35
42
|
private compileNot;
|
|
43
|
+
private compileRelationFilter;
|
|
44
|
+
private buildRelationFilterExists;
|
|
36
45
|
private lookupToSQL;
|
|
37
46
|
private normalizeParam;
|
|
38
47
|
private collectStateFilterKeys;
|
|
@@ -1,42 +1,209 @@
|
|
|
1
1
|
import type { RelationHydrationLoadMode } from './RelationMeta';
|
|
2
2
|
import type { RelationHydrationCardinality } from './RelationTyping';
|
|
3
|
+
import { InternalPrefetchQueryKind } from './internal/InternalPrefetchQueryKind';
|
|
4
|
+
/**
|
|
5
|
+
* Result of compiling a {@link QuerySet} into an executable SQL statement plus
|
|
6
|
+
* the hydration plan the executor needs to reshape flat rows into nested
|
|
7
|
+
* relation graphs.
|
|
8
|
+
*/
|
|
3
9
|
export interface CompiledQuery {
|
|
10
|
+
/**
|
|
11
|
+
* Parameterized SQL string ready for `client.query(...)`. Identifiers and
|
|
12
|
+
* relation names are pre-validated; values never appear inline.
|
|
13
|
+
*/
|
|
4
14
|
sql: string;
|
|
15
|
+
/**
|
|
16
|
+
* Parameter values bound to the SQL statement in the same order the
|
|
17
|
+
* placeholders appear.
|
|
18
|
+
*/
|
|
5
19
|
params: readonly unknown[];
|
|
20
|
+
/**
|
|
21
|
+
* Optional hydration plan produced when the query declared selected or
|
|
22
|
+
* prefetched relations. Absent for plain row reads that do no
|
|
23
|
+
* relation-shaping work.
|
|
24
|
+
*/
|
|
6
25
|
hydrationPlan?: CompiledHydrationPlanRoot;
|
|
7
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Top-level hydration plan for a compiled query. Groups the join-time
|
|
29
|
+
* hydration nodes (emitted inline with the root query) with the prefetch
|
|
30
|
+
* hydration nodes (executed as follow-up queries after the root rows land).
|
|
31
|
+
*/
|
|
8
32
|
export interface CompiledHydrationPlanRoot {
|
|
33
|
+
/**
|
|
34
|
+
* The relation paths the caller originally requested via
|
|
35
|
+
* `selectRelated(...)` / `prefetchRelated(...)`. Retained for diagnostic
|
|
36
|
+
* messages and snapshot stability.
|
|
37
|
+
*/
|
|
9
38
|
requestedPaths: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* Column aliases on the root query that exist solely to support hydration
|
|
41
|
+
* (for example, join-mirrored primary keys). The hydrator strips these
|
|
42
|
+
* before handing rows back to application code.
|
|
43
|
+
*/
|
|
10
44
|
hiddenRootAliases: readonly string[];
|
|
45
|
+
/**
|
|
46
|
+
* Hydration nodes whose rows arrive joined into the root query's result
|
|
47
|
+
* set. Each node describes how to fold those joined columns back into a
|
|
48
|
+
* nested relation attribute.
|
|
49
|
+
*/
|
|
11
50
|
joinNodes: readonly CompiledHydrationNode[];
|
|
51
|
+
/**
|
|
52
|
+
* Hydration nodes that require a follow-up prefetch query after the root
|
|
53
|
+
* rows land. Each node drives one or more `compilePrefetch(...)` calls.
|
|
54
|
+
*/
|
|
12
55
|
prefetchNodes: readonly CompiledHydrationNode[];
|
|
13
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Recursive description of how to hydrate one relation edge and its
|
|
59
|
+
* descendants. The same node shape is used for join-loaded and
|
|
60
|
+
* prefetch-loaded relations; {@link loadMode} distinguishes them.
|
|
61
|
+
*/
|
|
14
62
|
export interface CompiledHydrationNode {
|
|
63
|
+
/** Stable identifier for this node in the plan. */
|
|
15
64
|
nodeId: string;
|
|
65
|
+
/**
|
|
66
|
+
* Public relation name as declared in the schema, mirrored onto the
|
|
67
|
+
* hydrated record (for example, `author` or `tags`).
|
|
68
|
+
*/
|
|
16
69
|
relationName: string;
|
|
70
|
+
/**
|
|
71
|
+
* Dot-less path expression that reaches this node from the root, used in
|
|
72
|
+
* error messages and plan diagnostics.
|
|
73
|
+
*/
|
|
17
74
|
relationPath: string;
|
|
75
|
+
/** Model key of the owner side of this edge. */
|
|
18
76
|
ownerModelKey: string;
|
|
77
|
+
/** Model key of the target side of this edge. */
|
|
19
78
|
targetModelKey: string;
|
|
79
|
+
/** Join-inline vs follow-up prefetch load strategy. */
|
|
20
80
|
loadMode: RelationHydrationLoadMode;
|
|
81
|
+
/** Whether this edge yields one target or many. */
|
|
21
82
|
cardinality: RelationHydrationCardinality;
|
|
83
|
+
/**
|
|
84
|
+
* Owner-side column whose value scopes the hydration read. For
|
|
85
|
+
* foreign-key relations this is the local column; for many-to-many this
|
|
86
|
+
* is the owner primary key.
|
|
87
|
+
*/
|
|
22
88
|
sourceKey: string;
|
|
89
|
+
/**
|
|
90
|
+
* Column alias on the root SQL row that surfaces `sourceKey` to the
|
|
91
|
+
* hydrator after projection.
|
|
92
|
+
*/
|
|
23
93
|
ownerSourceAccessor: string;
|
|
94
|
+
/**
|
|
95
|
+
* Target-side column that matches `sourceKey` during resolution.
|
|
96
|
+
*/
|
|
24
97
|
targetKey: string;
|
|
98
|
+
/** Target table name for the related rows. */
|
|
25
99
|
targetTable: string;
|
|
100
|
+
/** Primary-key column name on the target table. */
|
|
26
101
|
targetPrimaryKey: string;
|
|
102
|
+
/** Join table name for many-to-many edges. */
|
|
103
|
+
throughTable?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Join-table column storing the owner-side primary key for many-to-many
|
|
106
|
+
* edges.
|
|
107
|
+
*/
|
|
108
|
+
throughSourceKey?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Join-table column storing the target-side primary key for many-to-many
|
|
111
|
+
* edges.
|
|
112
|
+
*/
|
|
113
|
+
throughTargetKey?: string;
|
|
114
|
+
/**
|
|
115
|
+
* SQL type of the owner-side join column, used to validate compiled
|
|
116
|
+
* placeholder values.
|
|
117
|
+
*/
|
|
118
|
+
throughSourceColumnType?: string;
|
|
119
|
+
/**
|
|
120
|
+
* SQL type of the target-side join column, used to validate compiled
|
|
121
|
+
* placeholder values.
|
|
122
|
+
*/
|
|
123
|
+
throughTargetColumnType?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Column map for the target table keyed by column name. Used by the
|
|
126
|
+
* hydrator to materialize target rows and by the compiler to emit a
|
|
127
|
+
* stable projection.
|
|
128
|
+
*/
|
|
27
129
|
targetColumns: Record<string, string>;
|
|
130
|
+
/**
|
|
131
|
+
* Ordered trail of relation names that led to this node. Used to produce
|
|
132
|
+
* precise error messages when a hydration plan fails to compile.
|
|
133
|
+
*/
|
|
28
134
|
provenance: readonly string[];
|
|
135
|
+
/** Child nodes whose rows arrive joined with this node's rows. */
|
|
29
136
|
joinChildren: readonly CompiledHydrationNode[];
|
|
137
|
+
/** Child nodes that require their own follow-up prefetch. */
|
|
30
138
|
prefetchChildren: readonly CompiledHydrationNode[];
|
|
139
|
+
/** SQL join descriptor attached when this node is loaded inline. */
|
|
31
140
|
join?: CompiledJoinHydrationDescriptor;
|
|
32
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Join-specific details for a hydration node loaded inline with its parent
|
|
144
|
+
* query. Captures the alias the compiler emitted for the joined table and
|
|
145
|
+
* the column aliases under which each projected column appears in the result
|
|
146
|
+
* row.
|
|
147
|
+
*/
|
|
33
148
|
export interface CompiledJoinHydrationDescriptor {
|
|
149
|
+
/** SQL alias emitted for the joined table. */
|
|
34
150
|
alias: string;
|
|
151
|
+
/**
|
|
152
|
+
* Aliases for each projected target column, keyed by column name.
|
|
153
|
+
* Ensures the hydrator can pull the column out of the flat SQL row.
|
|
154
|
+
*/
|
|
35
155
|
columns: Record<string, string>;
|
|
36
156
|
}
|
|
37
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Discriminated union describing a prefetch query that must run after the
|
|
159
|
+
* root rows load. `direct` is a one-shot select against the target table;
|
|
160
|
+
* `manyToMany` is a two-phase plan that first reads join rows, then loads
|
|
161
|
+
* the target rows by primary key.
|
|
162
|
+
*/
|
|
163
|
+
export type CompiledPrefetchQuery = {
|
|
164
|
+
/** Marks a single-query prefetch against the target table. */
|
|
165
|
+
kind: typeof InternalPrefetchQueryKind.DIRECT;
|
|
166
|
+
/** Parameterized select statement to execute. */
|
|
38
167
|
sql: string;
|
|
168
|
+
/** Parameter values bound to the statement. */
|
|
39
169
|
params: readonly unknown[];
|
|
170
|
+
/**
|
|
171
|
+
* Target column whose value matches the owner-side `sourceKey`. The
|
|
172
|
+
* hydrator buckets the returned rows by this column.
|
|
173
|
+
*/
|
|
40
174
|
targetKey: string;
|
|
175
|
+
/** Column map for the target rows the hydrator will materialize. */
|
|
41
176
|
targetColumns: Record<string, string>;
|
|
42
|
-
}
|
|
177
|
+
} | {
|
|
178
|
+
/**
|
|
179
|
+
* Marks a two-phase many-to-many prefetch: the join-row query runs
|
|
180
|
+
* first, then a follow-up target query resolves the actual rows by
|
|
181
|
+
* primary key.
|
|
182
|
+
*/
|
|
183
|
+
kind: typeof InternalPrefetchQueryKind.MANY_TO_MANY;
|
|
184
|
+
/**
|
|
185
|
+
* SQL for the first phase: select owner/target id pairs from the
|
|
186
|
+
* join table.
|
|
187
|
+
*/
|
|
188
|
+
throughSql: string;
|
|
189
|
+
/** Parameter values bound to the join-row statement. */
|
|
190
|
+
throughParams: readonly unknown[];
|
|
191
|
+
/**
|
|
192
|
+
* Alias the compiler assigned to the owner-side join column in the
|
|
193
|
+
* join-row result set. The hydrator uses it to group targets by
|
|
194
|
+
* owner.
|
|
195
|
+
*/
|
|
196
|
+
ownerAlias: string;
|
|
197
|
+
/**
|
|
198
|
+
* Alias the compiler assigned to the target-side join column in
|
|
199
|
+
* the join-row result set. The hydrator uses it to assemble the
|
|
200
|
+
* target primary-key list for phase two.
|
|
201
|
+
*/
|
|
202
|
+
targetAlias: string;
|
|
203
|
+
/** Target table name for the phase-two read. */
|
|
204
|
+
targetTable: string;
|
|
205
|
+
/** Primary-key column on the target table for the phase-two read. */
|
|
206
|
+
targetPrimaryKey: string;
|
|
207
|
+
/** Column map for the target rows the hydrator will materialize. */
|
|
208
|
+
targetColumns: Record<string, string>;
|
|
209
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { FilterKey } from './FilterKey';
|
|
2
2
|
import type { FilterValue } from './FilterValue';
|
|
3
|
-
export type FilterInput<T> = Partial<Record<FilterKey<T>, FilterValue>>;
|
|
3
|
+
export type FilterInput<T, TSourceModel = unknown> = Partial<Record<FilterKey<T, TSourceModel>, FilterValue>>;
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import type { LookupType } from '
|
|
2
|
-
|
|
1
|
+
import type { LookupType } from './LookupType';
|
|
2
|
+
type ScalarFilterKey<T> = Extract<keyof T, string> | `${Extract<keyof T, string>}__${LookupType}`;
|
|
3
|
+
export type FilterKey<T, _TSourceModel = unknown> = ScalarFilterKey<T> | string;
|
|
4
|
+
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { FilterInput } from './FilterInput';
|
|
2
2
|
import type { InternalQNodeType } from './internal/InternalQNodeType';
|
|
3
3
|
export type QNodeType = (typeof InternalQNodeType)[keyof typeof InternalQNodeType];
|
|
4
|
-
export interface QNode<T> {
|
|
4
|
+
export interface QNode<T, TSourceModel = unknown> {
|
|
5
5
|
kind: QNodeType;
|
|
6
|
-
where?: FilterInput<T>;
|
|
7
|
-
nodes?: QNode<T>[];
|
|
8
|
-
node?: QNode<T>;
|
|
6
|
+
where?: FilterInput<T, TSourceModel>;
|
|
7
|
+
nodes?: QNode<T, TSourceModel>[];
|
|
8
|
+
node?: QNode<T, TSourceModel>;
|
|
9
9
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { OrderSpec } from './OrderSpec';
|
|
2
2
|
import type { QNode } from './QNode';
|
|
3
|
-
export interface QuerySetState<T> {
|
|
4
|
-
q?: QNode<T>;
|
|
5
|
-
excludes?: QNode<T>[];
|
|
3
|
+
export interface QuerySetState<T, TSourceModel = unknown> {
|
|
4
|
+
q?: QNode<T, TSourceModel>;
|
|
5
|
+
excludes?: QNode<T, TSourceModel>[];
|
|
6
6
|
order?: OrderSpec<T>[];
|
|
7
7
|
limit?: number;
|
|
8
8
|
offset?: number;
|
|
@@ -36,6 +36,15 @@ export interface RelationMeta {
|
|
|
36
36
|
sourceKey: string;
|
|
37
37
|
/** Target-side column matched against the source key. */
|
|
38
38
|
targetKey: string;
|
|
39
|
+
/** Many-to-many through table name when applicable. */
|
|
40
|
+
throughTable?: string;
|
|
41
|
+
throughModelKey?: string;
|
|
42
|
+
/** Many-to-many through column that matches the owner source key. */
|
|
43
|
+
throughSourceKey?: string;
|
|
44
|
+
/** Many-to-many through column that matches the target primary key. */
|
|
45
|
+
throughTargetKey?: string;
|
|
46
|
+
throughSourceColumnType?: string;
|
|
47
|
+
throughTargetColumnType?: string;
|
|
39
48
|
/** Primary key column for the target model. */
|
|
40
49
|
targetPrimaryKey: string;
|
|
41
50
|
/** Target model columns and their storage types. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
2
|
import type { Model, PersistedModelOutput } from '@danceroutine/tango-schema/domain';
|
|
3
3
|
import type { DecoratedFieldKind, InternalDecoratedFieldKind, RelationDecoratedSchema } from '@danceroutine/tango-schema/model';
|
|
4
|
+
import type { ManyToManyRelatedManager } from '../../manager/relations/ManyToManyRelatedManager';
|
|
4
5
|
export declare const InternalRelationHydrationCardinality: {
|
|
5
6
|
readonly SINGLE: "single";
|
|
6
7
|
readonly MANY: "many";
|
|
@@ -37,22 +38,68 @@ type NextSeenModels<TSeen extends readonly string[], TTargetModel> = ModelKey<TT
|
|
|
37
38
|
type CanTraverseGeneratedTarget<TTargetModel, TSeen extends readonly string[], TCycleBudget extends readonly unknown[]> = ModelKey<TTargetModel> extends TSeen[number] ? (TCycleBudget extends [] ? false : true) : true;
|
|
38
39
|
type NextGeneratedCycleBudget<TTargetModel, TSeen extends readonly string[], TCycleBudget extends readonly unknown[]> = ModelKey<TTargetModel> extends TSeen[number] ? TailTuple<TCycleBudget> : TCycleBudget;
|
|
39
40
|
type GeneratedCardinalityIncludesMany<TCardinality extends RelationHydrationCardinality> = TCardinality extends typeof InternalRelationHydrationCardinality.MANY ? true : false;
|
|
41
|
+
/**
|
|
42
|
+
* Generated path keys accepted by `selectRelated(...)`.
|
|
43
|
+
*
|
|
44
|
+
* Walks the ambient relation registry one hop at a time. Each hop contributes:
|
|
45
|
+
* (a) the bare relation key (e.g. `author`) as a valid path terminus, and
|
|
46
|
+
* (b) a dunder-joined extension (`author__profile`, ...) that recurses into
|
|
47
|
+
* the target model.
|
|
48
|
+
*
|
|
49
|
+
* Because `selectRelated` only composes single-valued hops, the mapped type
|
|
50
|
+
* filters relations whose `cardinality` is not `SINGLE`. The `TSeen` tuple
|
|
51
|
+
* tracks which model keys the recursion has visited so cyclic schemas
|
|
52
|
+
* (`manager -> manager`) are allowed a bounded number of revisits before
|
|
53
|
+
* typing falls back to `never`. `TCycleBudget` is that revisit budget; see
|
|
54
|
+
* `CanTraverseGeneratedTarget` / `NextGeneratedCycleBudget` for how it
|
|
55
|
+
* shrinks only when the next hop actually re-enters a seen model.
|
|
56
|
+
*/
|
|
40
57
|
export type GeneratedSelectRelatedPathKeys<TSourceModel, TSeen extends readonly string[] = [ModelKey<TSourceModel>], TCycleBudget extends readonly unknown[] = DefaultGeneratedCycleBudget> = {
|
|
41
58
|
[TKey in GeneratedRelationKeys<TSourceModel>]: GeneratedRelations<TSourceModel>[TKey] extends {
|
|
42
59
|
target: infer TTarget extends AnyModel;
|
|
43
60
|
cardinality: typeof InternalRelationHydrationCardinality.SINGLE;
|
|
44
61
|
} ? TKey | (CanTraverseGeneratedTarget<TTarget, TSeen, TCycleBudget> extends true ? `${TKey}__${GeneratedSelectRelatedPathKeys<TTarget, NextSeenModels<TSeen, TTarget>, NextGeneratedCycleBudget<TTarget, TSeen, TCycleBudget>>}` : never) : never;
|
|
45
62
|
}[GeneratedRelationKeys<TSourceModel>];
|
|
63
|
+
/**
|
|
64
|
+
* Generated path keys accepted by `prefetchRelated(...)`.
|
|
65
|
+
*
|
|
66
|
+
* Similar in shape to {@link GeneratedSelectRelatedPathKeys}, but relaxes two
|
|
67
|
+
* constraints because prefetch can cross and continue past collection edges:
|
|
68
|
+
* 1. Any hop whose cardinality is `MANY` is a valid terminus. The
|
|
69
|
+
* `THasMany` flag threads through recursion so once a collection edge
|
|
70
|
+
* has been crossed, every subsequent hop is also a valid terminus
|
|
71
|
+
* (matches Django's `prefetch_related` semantics).
|
|
72
|
+
* 2. Single-valued hops are still accepted as terminators so paths like
|
|
73
|
+
* `posts__author` survive the join.
|
|
74
|
+
*
|
|
75
|
+
* Cycle handling reuses the same `TSeen` / `TCycleBudget` machinery described
|
|
76
|
+
* in {@link GeneratedSelectRelatedPathKeys}: cyclic schemas are typed up to
|
|
77
|
+
* the bound, then fall back to weaker typing rather than failing.
|
|
78
|
+
*/
|
|
46
79
|
export type GeneratedPrefetchRelatedPathKeys<TSourceModel, THasMany extends boolean = false, TSeen extends readonly string[] = [ModelKey<TSourceModel>], TCycleBudget extends readonly unknown[] = DefaultGeneratedCycleBudget> = {
|
|
47
80
|
[TKey in GeneratedRelationKeys<TSourceModel>]: GeneratedRelations<TSourceModel>[TKey] extends {
|
|
48
81
|
target: infer TTarget extends AnyModel;
|
|
49
82
|
cardinality: infer TCardinality extends RelationHydrationCardinality;
|
|
50
83
|
} ? (THasMany extends true ? TKey : GeneratedCardinalityIncludesMany<TCardinality> extends true ? TKey : never) | (CanTraverseGeneratedTarget<TTarget, TSeen, TCycleBudget> extends true ? `${TKey}__${GeneratedPrefetchRelatedPathKeys<TTarget, THasMany extends true ? true : GeneratedCardinalityIncludesMany<TCardinality>, NextSeenModels<TSeen, TTarget>, NextGeneratedCycleBudget<TTarget, TSeen, TCycleBudget>>}` : never) : never;
|
|
51
84
|
}[GeneratedRelationKeys<TSourceModel>];
|
|
85
|
+
/**
|
|
86
|
+
* Generated relation-path filter keys accepted by `filter(...)`, `exclude(...)`,
|
|
87
|
+
* and `Q(...)`.
|
|
88
|
+
*
|
|
89
|
+
* This stays intentionally lighter than the eager-loading path typing:
|
|
90
|
+
* applications with generated relation registries get completion for the
|
|
91
|
+
* relation path prefix, while the terminal field and lookup suffix remain a
|
|
92
|
+
* string tail so the compiler does not explode on deep recursive field
|
|
93
|
+
* extraction.
|
|
94
|
+
*/
|
|
95
|
+
export type GeneratedRelationFilterKeys<TSourceModel> = `${GeneratedSelectRelatedPathKeys<TSourceModel>}__${string}` | `${GeneratedPrefetchRelatedPathKeys<TSourceModel>}__${string}`;
|
|
52
96
|
type GeneratedHydratedTarget<TDescriptor, TPath extends string | never> = TDescriptor extends {
|
|
53
97
|
target: infer TTarget extends AnyModel;
|
|
54
98
|
} ? [TPath] extends [never] ? ModelRow<TTarget> : HydratedQueryResult<ModelRow<TTarget>, GeneratedHydratedRelationMap<TTarget, TPath>> : never;
|
|
55
99
|
type GeneratedHydratedValue<TDescriptor, TPath extends string | never> = TDescriptor extends {
|
|
100
|
+
target: infer TTarget extends AnyModel;
|
|
101
|
+
kind: 'manyToMany';
|
|
102
|
+
} ? ManyToManyRelatedManager<ModelRow<TTarget>> : TDescriptor extends {
|
|
56
103
|
cardinality: infer TCardinality extends RelationHydrationCardinality;
|
|
57
104
|
} ? TCardinality extends typeof InternalRelationHydrationCardinality.SINGLE ? GeneratedHydratedTarget<TDescriptor, TPath> | null : GeneratedHydratedTarget<TDescriptor, TPath>[] : never;
|
|
58
105
|
type GeneratedHydrationForPath<TSourceModel, TPath extends string> = SplitPath<TPath> extends [infer THead extends string, ...infer TRest extends string[]] ? THead extends GeneratedRelationKeys<TSourceModel> ? {
|