@danceroutine/tango-orm 1.7.0 → 1.8.1
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/ModelManager.d.ts +25 -5
- 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 +49 -21
- 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/planning/QueryPlanner.d.ts +1 -1
- package/dist/{query-FZJoSCg4.js → query-DUZnBFhf.js} +425 -166
- 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-FZJoSCg4.js.map +0 -1
- package/dist/registerModelObjects-C-1RbUHS.js +0 -385
- package/dist/registerModelObjects-C-1RbUHS.js.map +0 -1
- package/dist/runtime-ByXbpVBS.js.map +0 -1
|
@@ -1,84 +1,8 @@
|
|
|
1
1
|
import { __export } from "./chunk-DLY2FNSh.js";
|
|
2
|
+
import { InternalDialect } from "./InternalDialect-ClSaUNso.js";
|
|
2
3
|
import { MultipleObjectsReturned, NotFoundError, SqlSafetyEngine, getLogger, isError } from "@danceroutine/tango-core";
|
|
3
4
|
import { ModelRegistry } from "@danceroutine/tango-schema";
|
|
4
5
|
|
|
5
|
-
//#region src/query/domain/internal/InternalQNodeType.ts
|
|
6
|
-
const InternalQNodeType = {
|
|
7
|
-
ATOM: "atom",
|
|
8
|
-
AND: "and",
|
|
9
|
-
OR: "or",
|
|
10
|
-
NOT: "not"
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/query/domain/internal/InternalRelationKind.ts
|
|
15
|
-
const InternalRelationKind = {
|
|
16
|
-
HAS_MANY: "hasMany",
|
|
17
|
-
BELONGS_TO: "belongsTo",
|
|
18
|
-
HAS_ONE: "hasOne",
|
|
19
|
-
MANY_TO_MANY: "manyToMany"
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
//#endregion
|
|
23
|
-
//#region src/query/domain/TableMetaFactory.ts
|
|
24
|
-
var TableMetaFactory = class TableMetaFactory {
|
|
25
|
-
static create(model) {
|
|
26
|
-
const owner = model.metadata.key ? ModelRegistry.getOwner(model) : undefined;
|
|
27
|
-
const cache = new Map();
|
|
28
|
-
return TableMetaFactory.createWithCache(model, owner, cache);
|
|
29
|
-
}
|
|
30
|
-
static createWithCache(model, owner, cache) {
|
|
31
|
-
if (model.metadata.key) {
|
|
32
|
-
const cached = cache.get(model.metadata.key);
|
|
33
|
-
if (cached) return cached;
|
|
34
|
-
}
|
|
35
|
-
const pkField = model.metadata.fields.find((field) => field.primaryKey);
|
|
36
|
-
if (!pkField) throw new Error(`Model '${model.metadata.name}' cannot attach a manager without a primary key field.`);
|
|
37
|
-
const tableMeta = {
|
|
38
|
-
modelKey: model.metadata.key,
|
|
39
|
-
table: model.metadata.table,
|
|
40
|
-
pk: pkField.name,
|
|
41
|
-
columns: Object.fromEntries(model.metadata.fields.map((field) => [field.name, field.type]))
|
|
42
|
-
};
|
|
43
|
-
if (model.metadata.key) cache.set(model.metadata.key, tableMeta);
|
|
44
|
-
if (!model.metadata.key || !owner) return tableMeta;
|
|
45
|
-
const relations = owner.getResolvedRelationGraph().byModel.get(model.metadata.key);
|
|
46
|
-
if (!relations || relations.size === 0) return tableMeta;
|
|
47
|
-
tableMeta.relations = Object.fromEntries(Array.from(relations.entries()).filter(([, relation]) => relation.capabilities.queryable && relation.capabilities.hydratable).map(([name, relation]) => {
|
|
48
|
-
const targetModel = owner.getByKey(relation.targetModelKey);
|
|
49
|
-
const targetMeta = TableMetaFactory.createWithCache(targetModel, owner, cache);
|
|
50
|
-
const { queryable, hydratable } = relation.capabilities;
|
|
51
|
-
const isSingleRelation = relation.kind === InternalRelationKind.BELONGS_TO || relation.kind === InternalRelationKind.HAS_ONE;
|
|
52
|
-
const sourceKey = relation.kind === InternalRelationKind.BELONGS_TO ? relation.localFieldName : relation.targetFieldName;
|
|
53
|
-
const targetKey = relation.kind === InternalRelationKind.BELONGS_TO ? relation.targetFieldName : relation.localFieldName;
|
|
54
|
-
const targetColumns = Object.fromEntries(targetModel.metadata.fields.map((field) => [field.name, field.type]));
|
|
55
|
-
const capabilities = {
|
|
56
|
-
queryable,
|
|
57
|
-
hydratable,
|
|
58
|
-
joinable: isSingleRelation && queryable && hydratable,
|
|
59
|
-
prefetchable: queryable && hydratable
|
|
60
|
-
};
|
|
61
|
-
return [name, {
|
|
62
|
-
edgeId: relation.edgeId,
|
|
63
|
-
sourceModelKey: relation.sourceModelKey,
|
|
64
|
-
targetModelKey: relation.targetModelKey,
|
|
65
|
-
kind: relation.kind,
|
|
66
|
-
cardinality: isSingleRelation ? "single" : "many",
|
|
67
|
-
capabilities,
|
|
68
|
-
table: targetModel.metadata.table,
|
|
69
|
-
sourceKey,
|
|
70
|
-
targetKey,
|
|
71
|
-
targetPrimaryKey: targetMeta.pk,
|
|
72
|
-
targetColumns,
|
|
73
|
-
alias: relation.alias,
|
|
74
|
-
targetMeta
|
|
75
|
-
}];
|
|
76
|
-
}));
|
|
77
|
-
return tableMeta;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
//#endregion
|
|
82
6
|
//#region src/query/domain/RelationMeta.ts
|
|
83
7
|
const InternalRelationHydrationLoadMode = {
|
|
84
8
|
JOIN: "join",
|
|
@@ -86,10 +10,19 @@ const InternalRelationHydrationLoadMode = {
|
|
|
86
10
|
};
|
|
87
11
|
|
|
88
12
|
//#endregion
|
|
89
|
-
//#region src/query/domain/internal/
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
13
|
+
//#region src/query/domain/internal/InternalPrefetchQueryKind.ts
|
|
14
|
+
const InternalPrefetchQueryKind = {
|
|
15
|
+
DIRECT: "direct",
|
|
16
|
+
MANY_TO_MANY: "manyToMany"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/query/domain/internal/InternalQNodeType.ts
|
|
21
|
+
const InternalQNodeType = {
|
|
22
|
+
ATOM: "atom",
|
|
23
|
+
AND: "and",
|
|
24
|
+
OR: "or",
|
|
25
|
+
NOT: "not"
|
|
93
26
|
};
|
|
94
27
|
|
|
95
28
|
//#endregion
|
|
@@ -110,6 +43,22 @@ const InternalLookupType = {
|
|
|
110
43
|
IENDSWITH: "iendswith"
|
|
111
44
|
};
|
|
112
45
|
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/validation/internal/InternalSqlValidationPlanKind.ts
|
|
48
|
+
const InternalSqlValidationPlanKind = {
|
|
49
|
+
SELECT: "select",
|
|
50
|
+
INSERT: "insert",
|
|
51
|
+
UPDATE: "update",
|
|
52
|
+
DELETE: "delete"
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/validation/internal/InternalValidatedFilterDescriptorKind.ts
|
|
57
|
+
const InternalValidatedFilterDescriptorKind = {
|
|
58
|
+
COLUMN: "column",
|
|
59
|
+
RELATION: "relation"
|
|
60
|
+
};
|
|
61
|
+
|
|
113
62
|
//#endregion
|
|
114
63
|
//#region src/validation/OrmSqlSafetyAdapter.ts
|
|
115
64
|
const ALLOWED_LOOKUPS = Object.values(InternalLookupType);
|
|
@@ -121,35 +70,35 @@ var OrmSqlSafetyAdapter = class OrmSqlSafetyAdapter {
|
|
|
121
70
|
}
|
|
122
71
|
validate(plan) {
|
|
123
72
|
switch (plan.kind) {
|
|
124
|
-
case
|
|
73
|
+
case InternalSqlValidationPlanKind.SELECT: {
|
|
125
74
|
const meta = this.validateTableMeta(plan.meta, plan.relationNames ?? []);
|
|
126
75
|
return {
|
|
127
|
-
kind:
|
|
76
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
128
77
|
meta,
|
|
129
78
|
selectFields: Object.fromEntries((plan.selectFields ?? []).map((field) => [field, `${meta.table}.${this.resolveColumn(meta, field)}`])),
|
|
130
|
-
filterKeys: Object.fromEntries((plan.filterKeys ?? []).map((rawKey) => [rawKey, this.validateFilterKey(meta, rawKey)])),
|
|
79
|
+
filterKeys: Object.fromEntries((plan.filterKeys ?? []).map((rawKey) => [rawKey, this.validateFilterKey(meta, plan.meta, rawKey)])),
|
|
131
80
|
orderFields: Object.fromEntries((plan.orderFields ?? []).map((field) => [field, `${meta.table}.${this.resolveColumn(meta, field)}`])),
|
|
132
81
|
relations: Object.fromEntries((plan.relationNames ?? []).map((relationName) => [relationName, this.resolveRelation(meta, relationName)]))
|
|
133
82
|
};
|
|
134
83
|
}
|
|
135
|
-
case
|
|
84
|
+
case InternalSqlValidationPlanKind.INSERT: {
|
|
136
85
|
const meta = this.validateTableMeta(plan.meta);
|
|
137
86
|
return {
|
|
138
|
-
kind:
|
|
87
|
+
kind: InternalSqlValidationPlanKind.INSERT,
|
|
139
88
|
meta,
|
|
140
89
|
writeKeys: plan.writeKeys.map((key) => this.resolveColumn(meta, key))
|
|
141
90
|
};
|
|
142
91
|
}
|
|
143
|
-
case
|
|
92
|
+
case InternalSqlValidationPlanKind.UPDATE: {
|
|
144
93
|
const meta = this.validateTableMeta(plan.meta);
|
|
145
94
|
return {
|
|
146
|
-
kind:
|
|
95
|
+
kind: InternalSqlValidationPlanKind.UPDATE,
|
|
147
96
|
meta,
|
|
148
97
|
writeKeys: plan.writeKeys.map((key) => this.resolveColumn(meta, key))
|
|
149
98
|
};
|
|
150
99
|
}
|
|
151
|
-
case
|
|
152
|
-
kind:
|
|
100
|
+
case InternalSqlValidationPlanKind.DELETE: return {
|
|
101
|
+
kind: InternalSqlValidationPlanKind.DELETE,
|
|
153
102
|
meta: this.validateTableMeta(plan.meta)
|
|
154
103
|
};
|
|
155
104
|
}
|
|
@@ -203,6 +152,26 @@ var OrmSqlSafetyAdapter = class OrmSqlSafetyAdapter {
|
|
|
203
152
|
role: "relationTargetPrimaryKey",
|
|
204
153
|
value: relation.targetKey
|
|
205
154
|
},
|
|
155
|
+
{
|
|
156
|
+
key: "targetPrimaryKey",
|
|
157
|
+
role: "relationTargetPrimaryKey",
|
|
158
|
+
value: relation.targetPrimaryKey
|
|
159
|
+
},
|
|
160
|
+
...relation.throughTable ? [{
|
|
161
|
+
key: "throughTable",
|
|
162
|
+
role: "table",
|
|
163
|
+
value: relation.throughTable
|
|
164
|
+
}] : [],
|
|
165
|
+
...relation.throughSourceKey ? [{
|
|
166
|
+
key: "throughSourceKey",
|
|
167
|
+
role: "column",
|
|
168
|
+
value: relation.throughSourceKey
|
|
169
|
+
}] : [],
|
|
170
|
+
...relation.throughTargetKey ? [{
|
|
171
|
+
key: "throughTargetKey",
|
|
172
|
+
role: "column",
|
|
173
|
+
value: relation.throughTargetKey
|
|
174
|
+
}] : [],
|
|
206
175
|
...Object.keys(relation.targetColumns).map((column) => ({
|
|
207
176
|
key: `targetColumn:${column}`,
|
|
208
177
|
role: "column",
|
|
@@ -215,24 +184,62 @@ var OrmSqlSafetyAdapter = class OrmSqlSafetyAdapter {
|
|
|
215
184
|
alias: validated.identifiers.alias.value,
|
|
216
185
|
sourceKey: this.resolveColumn(meta, relation.sourceKey),
|
|
217
186
|
targetKey: validated.identifiers.targetKey.value,
|
|
218
|
-
|
|
187
|
+
targetPrimaryKey: validated.identifiers.targetPrimaryKey.value,
|
|
188
|
+
targetColumns: Object.fromEntries(Object.keys(relation.targetColumns).map((column) => [validated.identifiers[`targetColumn:${column}`].value, relation.targetColumns[column]])),
|
|
189
|
+
throughTable: validated.identifiers.throughTable?.value,
|
|
190
|
+
throughSourceKey: validated.identifiers.throughSourceKey?.value,
|
|
191
|
+
throughTargetKey: validated.identifiers.throughTargetKey?.value
|
|
219
192
|
};
|
|
220
193
|
}
|
|
221
|
-
validateFilterKey(meta, rawKey) {
|
|
194
|
+
validateFilterKey(meta, rawMeta, rawKey) {
|
|
222
195
|
const segments = rawKey.split("__");
|
|
223
|
-
if (segments.length
|
|
224
|
-
const
|
|
225
|
-
const
|
|
196
|
+
if (segments.length === 0 || segments.some((segment) => segment.length === 0)) throw new Error(`Invalid SQL lookup key: '${rawKey}'.`);
|
|
197
|
+
const lookupToken = segments.at(-1);
|
|
198
|
+
const hasExplicitLookup = ALLOWED_LOOKUPS.includes(lookupToken);
|
|
199
|
+
const lookup = hasExplicitLookup ? lookupToken : InternalLookupType.EXACT;
|
|
200
|
+
const pathSegments = hasExplicitLookup ? segments.slice(0, -1) : segments;
|
|
201
|
+
if (pathSegments.length === 0) throw new Error(`Invalid SQL lookup key: '${rawKey}'.`);
|
|
226
202
|
const validated = this.engine.validate({ lookupTokens: [{
|
|
227
203
|
key: rawKey,
|
|
228
204
|
lookup,
|
|
229
205
|
allowed: ALLOWED_LOOKUPS
|
|
230
206
|
}] });
|
|
207
|
+
if (pathSegments.length === 1) {
|
|
208
|
+
const field$1 = pathSegments[0];
|
|
209
|
+
return {
|
|
210
|
+
kind: InternalValidatedFilterDescriptorKind.COLUMN,
|
|
211
|
+
rawKey,
|
|
212
|
+
field: field$1,
|
|
213
|
+
lookup: validated.lookupTokens[rawKey].lookup,
|
|
214
|
+
qualifiedColumn: `${meta.table}.${this.resolveColumn(meta, field$1)}`
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const rootSegment = pathSegments[0];
|
|
218
|
+
const hasRootColumn = rootSegment in rawMeta.columns;
|
|
219
|
+
const hasRootRelation = rootSegment in (rawMeta.relations ?? {});
|
|
220
|
+
if (!hasExplicitLookup && hasRootColumn && !hasRootRelation) throw new Error(`Invalid SQL lookup key: '${rawKey}'.`);
|
|
221
|
+
const field = pathSegments.at(-1);
|
|
222
|
+
const relationSegments = pathSegments.slice(0, -1);
|
|
223
|
+
const relationChain = [];
|
|
224
|
+
let currentValidatedMeta = meta;
|
|
225
|
+
let currentRawMeta = rawMeta;
|
|
226
|
+
for (const relationName of relationSegments) {
|
|
227
|
+
const relation = currentRawMeta.relations?.[relationName];
|
|
228
|
+
if (!relation) throw new Error(`Unknown relation '${relationName}' for table '${currentValidatedMeta.table}'.`);
|
|
229
|
+
if (!relation.targetMeta) throw new Error(`Relation '${relationName}' for table '${currentValidatedMeta.table}' is missing target metadata.`);
|
|
230
|
+
const validatedRelation = this.validateRelationMeta(currentValidatedMeta, relationName, currentRawMeta.relations);
|
|
231
|
+
relationChain.push(validatedRelation);
|
|
232
|
+
currentRawMeta = relation.targetMeta;
|
|
233
|
+
currentValidatedMeta = this.validateTableMeta(currentRawMeta);
|
|
234
|
+
}
|
|
231
235
|
return {
|
|
236
|
+
kind: InternalValidatedFilterDescriptorKind.RELATION,
|
|
232
237
|
rawKey,
|
|
233
238
|
field,
|
|
234
239
|
lookup: validated.lookupTokens[rawKey].lookup,
|
|
235
|
-
|
|
240
|
+
relationPath: relationSegments.join("__"),
|
|
241
|
+
relationChain,
|
|
242
|
+
terminalColumn: this.resolveColumn(currentValidatedMeta, field)
|
|
236
243
|
};
|
|
237
244
|
}
|
|
238
245
|
resolveColumn(meta, field) {
|
|
@@ -287,18 +294,15 @@ var QueryPlanner = class QueryPlanner {
|
|
|
287
294
|
let currentMeta = this.meta;
|
|
288
295
|
let currentChildren = rootChildren;
|
|
289
296
|
let builtPath = "";
|
|
290
|
-
let containsCollection = false;
|
|
291
297
|
for (const segment of segments) {
|
|
292
298
|
const relation = currentMeta.relations?.[segment];
|
|
293
299
|
if (!relation) throw new Error(`Unknown relation path '${relationPath}' for table '${currentMeta.table}'.`);
|
|
294
300
|
if (segment in currentMeta.columns && relation.sourceKey !== segment) throw new Error(`Relation path '${relationPath}' collides with an existing field on table '${currentMeta.table}'.`);
|
|
295
|
-
if (relation.kind === InternalRelationKind.MANY_TO_MANY) throw new Error(`Relation path '${relationPath}' uses unsupported many-to-many hydration.`);
|
|
296
301
|
if (!relation.capabilities.queryable || !relation.capabilities.hydratable) throw new Error(`Relation path '${relationPath}' cannot be hydrated.`);
|
|
297
302
|
if (mode === "select") {
|
|
298
303
|
if (relation.cardinality !== InternalRelationHydrationCardinality.SINGLE || !relation.capabilities.joinable) throw new Error(`Relation path '${relationPath}' cannot be loaded with selectRelated(...).`);
|
|
299
304
|
} else if (relation.cardinality === InternalRelationHydrationCardinality.MANY) {
|
|
300
305
|
if (!relation.capabilities.prefetchable) throw new Error(`Relation path '${relationPath}' cannot be loaded with prefetchRelated(...).`);
|
|
301
|
-
containsCollection = true;
|
|
302
306
|
} else if (!relation.capabilities.joinable) throw new Error(`Relation path '${relationPath}' cannot be loaded with prefetchRelated(...).`);
|
|
303
307
|
const targetMeta = relation.targetMeta;
|
|
304
308
|
if (!targetMeta) throw new Error(`Relation path '${relationPath}' is missing target metadata.`);
|
|
@@ -317,7 +321,6 @@ var QueryPlanner = class QueryPlanner {
|
|
|
317
321
|
currentChildren = nextNode.children;
|
|
318
322
|
currentMeta = targetMeta;
|
|
319
323
|
}
|
|
320
|
-
if (mode === "prefetch" && !containsCollection) throw new Error(`Relation path '${relationPath}' cannot be loaded with prefetchRelated(...).`);
|
|
321
324
|
}
|
|
322
325
|
buildPlannedChildren(children) {
|
|
323
326
|
const joinNodes = [];
|
|
@@ -353,9 +356,11 @@ const sqlSafetyAdapter = new OrmSqlSafetyAdapter();
|
|
|
353
356
|
var QueryCompiler = class QueryCompiler {
|
|
354
357
|
static BRAND = "tango.orm.query_compiler";
|
|
355
358
|
__tangoBrand = QueryCompiler.BRAND;
|
|
356
|
-
|
|
359
|
+
placeholders;
|
|
360
|
+
constructor(meta, adapter) {
|
|
357
361
|
this.meta = meta;
|
|
358
|
-
this.
|
|
362
|
+
this.adapter = adapter;
|
|
363
|
+
this.placeholders = adapter.placeholders;
|
|
359
364
|
}
|
|
360
365
|
static isQueryCompiler(value) {
|
|
361
366
|
return typeof value === "object" && value !== null && value.__tangoBrand === QueryCompiler.BRAND;
|
|
@@ -363,7 +368,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
363
368
|
compile(state) {
|
|
364
369
|
const hydrationPlan = new QueryPlanner(this.meta).plan(state);
|
|
365
370
|
const validatedPlan = sqlSafetyAdapter.validate({
|
|
366
|
-
kind:
|
|
371
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
367
372
|
meta: this.meta,
|
|
368
373
|
selectFields: state.select?.map(String),
|
|
369
374
|
filterKeys: this.collectStateFilterKeys(state),
|
|
@@ -437,7 +442,8 @@ var QueryCompiler = class QueryCompiler {
|
|
|
437
442
|
};
|
|
438
443
|
}
|
|
439
444
|
compilePrefetch(node, sourceValues) {
|
|
440
|
-
|
|
445
|
+
if (node.throughTable && node.throughSourceKey && node.throughTargetKey) return this.compileManyToManyPrefetch(node, sourceValues);
|
|
446
|
+
const placeholders = this.placeholders.list(sourceValues.length);
|
|
441
447
|
const validatedTarget = this.validatePrefetchTarget(node);
|
|
442
448
|
const baseAlias = this.buildPrefetchBaseAlias(node.relationPath);
|
|
443
449
|
const joinCollection = {
|
|
@@ -447,12 +453,58 @@ var QueryCompiler = class QueryCompiler {
|
|
|
447
453
|
for (const joinChild of node.joinChildren) this.collectNestedJoinSql(joinChild, baseAlias, validatedTarget.columns, joinCollection);
|
|
448
454
|
const baseSelects = Object.keys(validatedTarget.columns).map((column) => `${baseAlias}.${column} AS ${column}`);
|
|
449
455
|
return {
|
|
456
|
+
kind: InternalPrefetchQueryKind.DIRECT,
|
|
450
457
|
sql: `SELECT ${[...baseSelects, ...joinCollection.selects].join(", ")} FROM ${validatedTarget.table} ${baseAlias}${joinCollection.joins.length ? ` ${joinCollection.joins.join(" ")}` : ""} WHERE ${baseAlias}.${validatedTarget.targetKey} IN (${placeholders}) ORDER BY ${baseAlias}.${validatedTarget.targetKey} ASC, ${baseAlias}.${validatedTarget.primaryKey} ASC`,
|
|
451
458
|
params: sourceValues,
|
|
452
459
|
targetKey: validatedTarget.targetKey,
|
|
453
460
|
targetColumns: validatedTarget.columns
|
|
454
461
|
};
|
|
455
462
|
}
|
|
463
|
+
compileManyToManyTargets(node, targetIds) {
|
|
464
|
+
const placeholders = this.placeholders.list(targetIds.length);
|
|
465
|
+
const validatedTarget = this.validatePrefetchTarget(node);
|
|
466
|
+
const baseAlias = this.buildPrefetchBaseAlias(node.relationPath);
|
|
467
|
+
const joinCollection = {
|
|
468
|
+
selects: [],
|
|
469
|
+
joins: []
|
|
470
|
+
};
|
|
471
|
+
for (const joinChild of node.joinChildren) this.collectNestedJoinSql(joinChild, baseAlias, validatedTarget.columns, joinCollection);
|
|
472
|
+
const baseSelects = Object.keys(validatedTarget.columns).map((column) => `${baseAlias}.${column} AS ${column}`);
|
|
473
|
+
return {
|
|
474
|
+
sql: `SELECT ${[...baseSelects, ...joinCollection.selects].join(", ")} FROM ${validatedTarget.table} ${baseAlias}${joinCollection.joins.length ? ` ${joinCollection.joins.join(" ")}` : ""} WHERE ${baseAlias}.${validatedTarget.primaryKey} IN (${placeholders}) ORDER BY ${baseAlias}.${validatedTarget.primaryKey} ASC`,
|
|
475
|
+
params: targetIds
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
compileManyToManyPrefetch(node, sourceValues) {
|
|
479
|
+
const placeholders = this.placeholders.list(sourceValues.length);
|
|
480
|
+
const throughValidated = sqlSafetyAdapter.validate({
|
|
481
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
482
|
+
meta: {
|
|
483
|
+
table: node.throughTable,
|
|
484
|
+
pk: node.throughSourceKey,
|
|
485
|
+
columns: {
|
|
486
|
+
[node.throughSourceKey]: node.throughSourceColumnType ?? "int",
|
|
487
|
+
[node.throughTargetKey]: node.throughTargetColumnType ?? "int"
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
filterKeys: [node.throughSourceKey, node.throughTargetKey],
|
|
491
|
+
relationNames: []
|
|
492
|
+
});
|
|
493
|
+
const ownerAlias = this.validateInternalAlias("__tango_m2m_owner");
|
|
494
|
+
const targetAlias = this.validateInternalAlias("__tango_m2m_target");
|
|
495
|
+
const throughSourceColumn = throughValidated.filterKeys[node.throughSourceKey].field;
|
|
496
|
+
const throughTargetColumn = throughValidated.filterKeys[node.throughTargetKey].field;
|
|
497
|
+
return {
|
|
498
|
+
kind: InternalPrefetchQueryKind.MANY_TO_MANY,
|
|
499
|
+
throughSql: `SELECT ${throughValidated.meta.table}.${throughSourceColumn} AS ${ownerAlias}, ${throughValidated.meta.table}.${throughTargetColumn} AS ${targetAlias} FROM ${throughValidated.meta.table} WHERE ${throughValidated.meta.table}.${throughSourceColumn} IN (${placeholders}) ORDER BY ${throughValidated.meta.table}.${throughSourceColumn} ASC, ${throughValidated.meta.table}.${throughTargetColumn} ASC`,
|
|
500
|
+
throughParams: sourceValues,
|
|
501
|
+
ownerAlias,
|
|
502
|
+
targetAlias,
|
|
503
|
+
targetTable: node.targetTable,
|
|
504
|
+
targetPrimaryKey: node.targetPrimaryKey,
|
|
505
|
+
targetColumns: node.targetColumns
|
|
506
|
+
};
|
|
507
|
+
}
|
|
456
508
|
compileHydrationNode(node, context) {
|
|
457
509
|
const validatedRelation = this.validateHydrationRelation(context.ownerMeta, node.relationName);
|
|
458
510
|
const targetColumns = validatedRelation.targetColumns;
|
|
@@ -496,6 +548,11 @@ var QueryCompiler = class QueryCompiler {
|
|
|
496
548
|
targetKey: validatedRelation.targetKey,
|
|
497
549
|
targetTable: validatedRelation.table,
|
|
498
550
|
targetPrimaryKey: node.relationEdge.targetPrimaryKey,
|
|
551
|
+
throughTable: node.relationEdge.throughTable,
|
|
552
|
+
throughSourceKey: node.relationEdge.throughSourceKey,
|
|
553
|
+
throughTargetKey: node.relationEdge.throughTargetKey,
|
|
554
|
+
throughSourceColumnType: node.relationEdge.throughSourceColumnType,
|
|
555
|
+
throughTargetColumnType: node.relationEdge.throughTargetColumnType,
|
|
499
556
|
targetColumns,
|
|
500
557
|
provenance: node.provenance,
|
|
501
558
|
joinChildren: compiledJoinChildren,
|
|
@@ -505,7 +562,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
505
562
|
}
|
|
506
563
|
validateHydrationRelation(ownerMeta, relationName) {
|
|
507
564
|
return sqlSafetyAdapter.validate({
|
|
508
|
-
kind:
|
|
565
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
509
566
|
meta: ownerMeta,
|
|
510
567
|
relationNames: [relationName]
|
|
511
568
|
}).relations[relationName];
|
|
@@ -519,7 +576,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
519
576
|
validatePrefetchTarget(node) {
|
|
520
577
|
try {
|
|
521
578
|
const validated = sqlSafetyAdapter.validate({
|
|
522
|
-
kind:
|
|
579
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
523
580
|
meta: {
|
|
524
581
|
table: node.targetTable,
|
|
525
582
|
pk: node.targetPrimaryKey,
|
|
@@ -570,6 +627,9 @@ var QueryCompiler = class QueryCompiler {
|
|
|
570
627
|
buildPrefetchSourceAlias(relationPath, sourceKey) {
|
|
571
628
|
return this.assertInternalAliasDoesNotCollide(`__tango_prefetch_${this.sanitizeRelationPath(relationPath)}_${sourceKey}`);
|
|
572
629
|
}
|
|
630
|
+
buildFilterAlias(relationPath, suffix) {
|
|
631
|
+
return this.assertInternalAliasDoesNotCollide(`__tango_filter_${this.sanitizeRelationPath(relationPath)}_${suffix}`);
|
|
632
|
+
}
|
|
573
633
|
sanitizeRelationPath(relationPath) {
|
|
574
634
|
return relationPath.replace(/[^a-zA-Z0-9]+/g, "_");
|
|
575
635
|
}
|
|
@@ -594,7 +654,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
594
654
|
const { parts, params } = entries.reduce((accumulator, [key, value]) => {
|
|
595
655
|
const descriptor = filterKeys[String(key)];
|
|
596
656
|
const idx = paramIndex + accumulator.params.length;
|
|
597
|
-
const clause = this.lookupToSQL(descriptor.qualifiedColumn, descriptor.lookup, value, idx);
|
|
657
|
+
const clause = descriptor.kind === InternalValidatedFilterDescriptorKind.COLUMN ? this.lookupToSQL(descriptor.qualifiedColumn, descriptor.lookup, value, idx) : this.compileRelationFilter(descriptor, value, idx);
|
|
598
658
|
accumulator.parts.push(clause.sql);
|
|
599
659
|
accumulator.params.push(...clause.params);
|
|
600
660
|
return accumulator;
|
|
@@ -652,8 +712,28 @@ var QueryCompiler = class QueryCompiler {
|
|
|
652
712
|
params: result.params
|
|
653
713
|
};
|
|
654
714
|
}
|
|
715
|
+
compileRelationFilter(descriptor, value, paramIndex) {
|
|
716
|
+
return this.buildRelationFilterExists(this.meta.table, descriptor.relationChain, descriptor.terminalColumn, descriptor.lookup, value, paramIndex, descriptor.relationPath);
|
|
717
|
+
}
|
|
718
|
+
buildRelationFilterExists(ownerAlias, relationChain, terminalColumn, lookup, value, paramIndex, relationPath) {
|
|
719
|
+
const [relation, ...rest] = relationChain;
|
|
720
|
+
if (!relation) throw new Error(`Cannot compile empty relation filter path '${relationPath}'.`);
|
|
721
|
+
const targetAlias = this.buildFilterAlias(relationPath, `target_${relation.alias}_${rest.length}`);
|
|
722
|
+
const targetPredicate = rest.length === 0 ? this.lookupToSQL(`${targetAlias}.${terminalColumn}`, lookup, value, paramIndex) : this.buildRelationFilterExists(targetAlias, rest, terminalColumn, lookup, value, paramIndex, relationPath);
|
|
723
|
+
if (relation.throughTable && relation.throughSourceKey && relation.throughTargetKey) {
|
|
724
|
+
const throughAlias = this.buildFilterAlias(relationPath, `through_${relation.alias}_${rest.length}`);
|
|
725
|
+
return {
|
|
726
|
+
sql: `EXISTS (SELECT 1 FROM ${relation.throughTable} ${throughAlias} INNER JOIN ${relation.table} ${targetAlias} ON ${targetAlias}.${relation.targetKey} = ${throughAlias}.${relation.throughTargetKey} WHERE ${throughAlias}.${relation.throughSourceKey} = ${ownerAlias}.${relation.sourceKey} AND ${targetPredicate.sql})`,
|
|
727
|
+
params: targetPredicate.params
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
return {
|
|
731
|
+
sql: `EXISTS (SELECT 1 FROM ${relation.table} ${targetAlias} WHERE ${targetAlias}.${relation.targetKey} = ${ownerAlias}.${relation.sourceKey} AND ${targetPredicate.sql})`,
|
|
732
|
+
params: targetPredicate.params
|
|
733
|
+
};
|
|
734
|
+
}
|
|
655
735
|
lookupToSQL(col, lookup, value, paramIndex) {
|
|
656
|
-
const placeholder = this.
|
|
736
|
+
const placeholder = this.placeholders.at(paramIndex);
|
|
657
737
|
const normalized = this.normalizeParam(value);
|
|
658
738
|
switch (lookup) {
|
|
659
739
|
case InternalLookupType.EXACT:
|
|
@@ -687,7 +767,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
687
767
|
sql: "1=0",
|
|
688
768
|
params: []
|
|
689
769
|
};
|
|
690
|
-
const placeholders = this.
|
|
770
|
+
const placeholders = this.placeholders.listFromOffset(entries.length, paramIndex - 1);
|
|
691
771
|
return {
|
|
692
772
|
sql: `${col} IN (${placeholders})`,
|
|
693
773
|
params: entries
|
|
@@ -702,7 +782,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
702
782
|
params: [`%${value}%`]
|
|
703
783
|
};
|
|
704
784
|
case InternalLookupType.ICONTAINS: {
|
|
705
|
-
const lowerCol = this.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
785
|
+
const lowerCol = this.adapter.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
706
786
|
return {
|
|
707
787
|
sql: `${lowerCol} LIKE ${placeholder}`,
|
|
708
788
|
params: [`%${String(value).toLowerCase()}%`]
|
|
@@ -713,7 +793,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
713
793
|
params: [`${value}%`]
|
|
714
794
|
};
|
|
715
795
|
case InternalLookupType.ISTARTSWITH: {
|
|
716
|
-
const lowerCol = this.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
796
|
+
const lowerCol = this.adapter.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
717
797
|
return {
|
|
718
798
|
sql: `${lowerCol} LIKE ${placeholder}`,
|
|
719
799
|
params: [`${String(value).toLowerCase()}%`]
|
|
@@ -724,7 +804,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
724
804
|
params: [`%${value}`]
|
|
725
805
|
};
|
|
726
806
|
case InternalLookupType.IENDSWITH: {
|
|
727
|
-
const lowerCol = this.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
807
|
+
const lowerCol = this.adapter.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
728
808
|
return {
|
|
729
809
|
sql: `${lowerCol} LIKE ${placeholder}`,
|
|
730
810
|
params: [`%${String(value).toLowerCase()}`]
|
|
@@ -734,7 +814,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
734
814
|
}
|
|
735
815
|
}
|
|
736
816
|
normalizeParam(value) {
|
|
737
|
-
if (this.dialect === InternalDialect.SQLITE && typeof value === "boolean") return value ? 1 : 0;
|
|
817
|
+
if (this.adapter.dialect === InternalDialect.SQLITE && typeof value === "boolean") return value ? 1 : 0;
|
|
738
818
|
return value;
|
|
739
819
|
}
|
|
740
820
|
collectStateFilterKeys(state) {
|
|
@@ -805,6 +885,87 @@ var QueryResult = class QueryResult {
|
|
|
805
885
|
}
|
|
806
886
|
};
|
|
807
887
|
|
|
888
|
+
//#endregion
|
|
889
|
+
//#region src/query/domain/internal/InternalRelationKind.ts
|
|
890
|
+
const InternalRelationKind = {
|
|
891
|
+
HAS_MANY: "hasMany",
|
|
892
|
+
BELONGS_TO: "belongsTo",
|
|
893
|
+
HAS_ONE: "hasOne",
|
|
894
|
+
MANY_TO_MANY: "manyToMany"
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
//#endregion
|
|
898
|
+
//#region src/query/domain/TableMetaFactory.ts
|
|
899
|
+
var TableMetaFactory = class TableMetaFactory {
|
|
900
|
+
static create(model) {
|
|
901
|
+
const owner = model.metadata.key ? ModelRegistry.getOwner(model) : undefined;
|
|
902
|
+
const cache = new Map();
|
|
903
|
+
return TableMetaFactory.createWithCache(model, owner, cache);
|
|
904
|
+
}
|
|
905
|
+
static createWithCache(model, owner, cache) {
|
|
906
|
+
if (model.metadata.key) {
|
|
907
|
+
const cached = cache.get(model.metadata.key);
|
|
908
|
+
if (cached) return cached;
|
|
909
|
+
}
|
|
910
|
+
const pkField = model.metadata.fields.find((field) => field.primaryKey);
|
|
911
|
+
if (!pkField) throw new Error(`Model '${model.metadata.name}' cannot attach a manager without a primary key field.`);
|
|
912
|
+
const tableMeta = {
|
|
913
|
+
modelKey: model.metadata.key,
|
|
914
|
+
table: model.metadata.table,
|
|
915
|
+
pk: pkField.name,
|
|
916
|
+
columns: Object.fromEntries(model.metadata.fields.map((field) => [field.name, field.type]))
|
|
917
|
+
};
|
|
918
|
+
if (model.metadata.key) cache.set(model.metadata.key, tableMeta);
|
|
919
|
+
if (!model.metadata.key || !owner) return tableMeta;
|
|
920
|
+
const relations = owner.getResolvedRelationGraph().byModel.get(model.metadata.key);
|
|
921
|
+
if (!relations || relations.size === 0) return tableMeta;
|
|
922
|
+
tableMeta.relations = Object.fromEntries(Array.from(relations.entries()).filter(([, relation]) => relation.capabilities.queryable && relation.capabilities.hydratable).map(([name, relation]) => {
|
|
923
|
+
const targetModel = owner.getByKey(relation.targetModelKey);
|
|
924
|
+
const targetMeta = TableMetaFactory.createWithCache(targetModel, owner, cache);
|
|
925
|
+
const { queryable, hydratable } = relation.capabilities;
|
|
926
|
+
const isSingleRelation = relation.kind === InternalRelationKind.BELONGS_TO || relation.kind === InternalRelationKind.HAS_ONE;
|
|
927
|
+
const targetColumns = Object.fromEntries(targetModel.metadata.fields.map((field) => [field.name, field.type]));
|
|
928
|
+
let throughSourceColumnType;
|
|
929
|
+
let throughTargetColumnType;
|
|
930
|
+
if (relation.kind === InternalRelationKind.MANY_TO_MANY && relation.throughModelKey && relation.throughSourceKey && relation.throughTargetKey) {
|
|
931
|
+
const throughModel = owner.getByKey(relation.throughModelKey);
|
|
932
|
+
throughSourceColumnType = throughModel?.metadata.fields.find((field) => field.name === relation.throughSourceKey)?.type;
|
|
933
|
+
throughTargetColumnType = throughModel?.metadata.fields.find((field) => field.name === relation.throughTargetKey)?.type;
|
|
934
|
+
}
|
|
935
|
+
const capabilities = {
|
|
936
|
+
queryable,
|
|
937
|
+
hydratable,
|
|
938
|
+
joinable: isSingleRelation && queryable && hydratable,
|
|
939
|
+
prefetchable: queryable && hydratable
|
|
940
|
+
};
|
|
941
|
+
const sourceKey = relation.kind === InternalRelationKind.MANY_TO_MANY ? tableMeta.pk : relation.kind === InternalRelationKind.BELONGS_TO ? relation.localFieldName : relation.targetFieldName;
|
|
942
|
+
const targetKey = relation.kind === InternalRelationKind.MANY_TO_MANY ? targetMeta.pk : relation.kind === InternalRelationKind.BELONGS_TO ? relation.targetFieldName : relation.localFieldName;
|
|
943
|
+
return [name, {
|
|
944
|
+
edgeId: relation.edgeId,
|
|
945
|
+
sourceModelKey: relation.sourceModelKey,
|
|
946
|
+
targetModelKey: relation.targetModelKey,
|
|
947
|
+
kind: relation.kind,
|
|
948
|
+
cardinality: isSingleRelation ? "single" : "many",
|
|
949
|
+
capabilities,
|
|
950
|
+
table: targetModel.metadata.table,
|
|
951
|
+
sourceKey,
|
|
952
|
+
targetKey,
|
|
953
|
+
throughTable: relation.throughTable,
|
|
954
|
+
throughModelKey: relation.throughModelKey,
|
|
955
|
+
throughSourceKey: relation.throughSourceKey,
|
|
956
|
+
throughTargetKey: relation.throughTargetKey,
|
|
957
|
+
throughSourceColumnType,
|
|
958
|
+
throughTargetColumnType,
|
|
959
|
+
targetPrimaryKey: targetMeta.pk,
|
|
960
|
+
targetColumns,
|
|
961
|
+
alias: relation.alias,
|
|
962
|
+
targetMeta
|
|
963
|
+
}];
|
|
964
|
+
}));
|
|
965
|
+
return tableMeta;
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
|
|
808
969
|
//#endregion
|
|
809
970
|
//#region src/query/domain/index.ts
|
|
810
971
|
var domain_exports = {};
|
|
@@ -897,6 +1058,27 @@ var QuerySet = class QuerySet {
|
|
|
897
1058
|
static isQuerySet(value) {
|
|
898
1059
|
return typeof value === "object" && value !== null && value.__tangoBrand === QuerySet.BRAND;
|
|
899
1060
|
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Translate user-facing order tokens like `'name'` or `'-createdAt'` into
|
|
1063
|
+
* the internal `OrderSpec` array used by `QuerySetState`.
|
|
1064
|
+
*
|
|
1065
|
+
* Exposed as `protected` so subclasses can compose the same parse logic
|
|
1066
|
+
* when they need to return their own concrete type from `orderBy` without
|
|
1067
|
+
* reaching into a base-class instance's protected state.
|
|
1068
|
+
*/
|
|
1069
|
+
static buildOrderSpecs(tokens) {
|
|
1070
|
+
return tokens.map((t) => {
|
|
1071
|
+
const str = String(t);
|
|
1072
|
+
if (str.startsWith("-")) return {
|
|
1073
|
+
by: str.slice(1),
|
|
1074
|
+
dir: InternalDirection.DESC
|
|
1075
|
+
};
|
|
1076
|
+
return {
|
|
1077
|
+
by: t,
|
|
1078
|
+
dir: InternalDirection.ASC
|
|
1079
|
+
};
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
900
1082
|
static invertOrderSpec(order) {
|
|
901
1083
|
if (!order?.length) return [];
|
|
902
1084
|
return order.map((spec) => ({
|
|
@@ -915,7 +1097,7 @@ var QuerySet = class QuerySet {
|
|
|
915
1097
|
where: q
|
|
916
1098
|
};
|
|
917
1099
|
const merged = this.state.q ? QBuilder.and(this.state.q, wrapped) : wrapped;
|
|
918
|
-
return
|
|
1100
|
+
return this.spawn({
|
|
919
1101
|
...this.state,
|
|
920
1102
|
q: merged
|
|
921
1103
|
});
|
|
@@ -931,7 +1113,7 @@ var QuerySet = class QuerySet {
|
|
|
931
1113
|
where: q
|
|
932
1114
|
};
|
|
933
1115
|
const excludes = [...this.state.excludes ?? [], wrapped];
|
|
934
|
-
return
|
|
1116
|
+
return this.spawn({
|
|
935
1117
|
...this.state,
|
|
936
1118
|
excludes
|
|
937
1119
|
});
|
|
@@ -940,27 +1122,16 @@ var QuerySet = class QuerySet {
|
|
|
940
1122
|
* Apply ordering tokens such as `'name'` or `'-createdAt'`.
|
|
941
1123
|
*/
|
|
942
1124
|
orderBy(...tokens) {
|
|
943
|
-
|
|
944
|
-
const str = String(t);
|
|
945
|
-
if (str.startsWith("-")) return {
|
|
946
|
-
by: str.slice(1),
|
|
947
|
-
dir: InternalDirection.DESC
|
|
948
|
-
};
|
|
949
|
-
return {
|
|
950
|
-
by: t,
|
|
951
|
-
dir: InternalDirection.ASC
|
|
952
|
-
};
|
|
953
|
-
});
|
|
954
|
-
return new QuerySet(this.executor, {
|
|
1125
|
+
return this.spawn({
|
|
955
1126
|
...this.state,
|
|
956
|
-
order
|
|
1127
|
+
order: QuerySet.buildOrderSpecs(tokens)
|
|
957
1128
|
});
|
|
958
1129
|
}
|
|
959
1130
|
/**
|
|
960
1131
|
* Limit the maximum number of rows returned.
|
|
961
1132
|
*/
|
|
962
1133
|
limit(n) {
|
|
963
|
-
return
|
|
1134
|
+
return this.spawn({
|
|
964
1135
|
...this.state,
|
|
965
1136
|
limit: n
|
|
966
1137
|
});
|
|
@@ -969,13 +1140,13 @@ var QuerySet = class QuerySet {
|
|
|
969
1140
|
* Skip the first `n` rows.
|
|
970
1141
|
*/
|
|
971
1142
|
offset(n) {
|
|
972
|
-
return
|
|
1143
|
+
return this.spawn({
|
|
973
1144
|
...this.state,
|
|
974
1145
|
offset: n
|
|
975
1146
|
});
|
|
976
1147
|
}
|
|
977
1148
|
select(fields) {
|
|
978
|
-
return
|
|
1149
|
+
return this.spawn({
|
|
979
1150
|
...this.state,
|
|
980
1151
|
select: [...fields]
|
|
981
1152
|
});
|
|
@@ -990,7 +1161,7 @@ var QuerySet = class QuerySet {
|
|
|
990
1161
|
* path keys for applications that keep the app-local registry current.
|
|
991
1162
|
*/
|
|
992
1163
|
selectRelated(...rels) {
|
|
993
|
-
return
|
|
1164
|
+
return this.spawn({
|
|
994
1165
|
...this.state,
|
|
995
1166
|
selectRelated: [...rels]
|
|
996
1167
|
});
|
|
@@ -1004,13 +1175,13 @@ var QuerySet = class QuerySet {
|
|
|
1004
1175
|
* app-local registry current.
|
|
1005
1176
|
*/
|
|
1006
1177
|
prefetchRelated(...rels) {
|
|
1007
|
-
return
|
|
1178
|
+
return this.spawn({
|
|
1008
1179
|
...this.state,
|
|
1009
1180
|
prefetchRelated: [...rels]
|
|
1010
1181
|
});
|
|
1011
1182
|
}
|
|
1012
1183
|
all() {
|
|
1013
|
-
return
|
|
1184
|
+
return this.spawn({ ...this.state });
|
|
1014
1185
|
}
|
|
1015
1186
|
async fetch(shape) {
|
|
1016
1187
|
const baseResult = await this.getOrCreateEvaluationCache();
|
|
@@ -1021,9 +1192,10 @@ var QuerySet = class QuerySet {
|
|
|
1021
1192
|
/**
|
|
1022
1193
|
* Async iterable surface for `for await (... of queryset)`.
|
|
1023
1194
|
*
|
|
1024
|
-
* Evaluates this queryset on first use by awaiting
|
|
1025
|
-
* yields each element from that
|
|
1026
|
-
*
|
|
1195
|
+
* Evaluates this queryset on first use by awaiting `fetch()` without
|
|
1196
|
+
* arguments, then yields each element from that materialized result.
|
|
1197
|
+
* Later async iterations over the same queryset instance reuse the cached
|
|
1198
|
+
* materialized result instead of issuing another database round-trip.
|
|
1027
1199
|
*/
|
|
1028
1200
|
async *[Symbol.asyncIterator]() {
|
|
1029
1201
|
const result = await this.fetch();
|
|
@@ -1050,7 +1222,7 @@ var QuerySet = class QuerySet {
|
|
|
1050
1222
|
by: this.executor.meta.pk,
|
|
1051
1223
|
dir: InternalDirection.DESC
|
|
1052
1224
|
}];
|
|
1053
|
-
const qs =
|
|
1225
|
+
const qs = this.spawn({
|
|
1054
1226
|
...this.state,
|
|
1055
1227
|
order: effectiveOrder
|
|
1056
1228
|
});
|
|
@@ -1060,16 +1232,15 @@ var QuerySet = class QuerySet {
|
|
|
1060
1232
|
const limited = this.filter(q).limit(2);
|
|
1061
1233
|
const page = await limited.fetch();
|
|
1062
1234
|
const rows = page.items;
|
|
1063
|
-
|
|
1064
|
-
if (
|
|
1065
|
-
if (count > 1) throw new MultipleObjectsReturned(`${this.executor.meta.table}: more than one matching record`);
|
|
1235
|
+
if (rows.length === 0) throw new NotFoundError(`${this.executor.meta.table}: no matching record`);
|
|
1236
|
+
if (rows.length > 1) throw new MultipleObjectsReturned(`${this.executor.meta.table}: more than one matching record`);
|
|
1066
1237
|
return this.shapeFetchedRow(rows[0], shape);
|
|
1067
1238
|
}
|
|
1068
1239
|
/**
|
|
1069
1240
|
* Execute a `COUNT(*)` query for the current filtered state.
|
|
1070
1241
|
*/
|
|
1071
1242
|
async count() {
|
|
1072
|
-
const compiler = new QueryCompiler(this.executor.meta, this.executor.
|
|
1243
|
+
const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
|
|
1073
1244
|
const compiled = compiler.compile(this.withoutHydrationState());
|
|
1074
1245
|
const countQuery = `SELECT COUNT(*) as count FROM (${compiled.sql}) AS tango_count_subquery`;
|
|
1075
1246
|
const rows = await this.executor.client.query(countQuery, compiled.params);
|
|
@@ -1096,15 +1267,23 @@ var QuerySet = class QuerySet {
|
|
|
1096
1267
|
return this.evaluationCache;
|
|
1097
1268
|
}
|
|
1098
1269
|
async evaluateRows() {
|
|
1099
|
-
const compiler = new QueryCompiler(this.executor.meta, this.executor.
|
|
1270
|
+
const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
|
|
1100
1271
|
const compiled = compiler.compile(this.state);
|
|
1101
1272
|
const rows = await this.executor.run(compiled);
|
|
1102
|
-
const
|
|
1273
|
+
const normalizedRows = this.normalizeRowsForSchemaParsing(rows);
|
|
1274
|
+
const hydratedRows = await this.hydrateRows(normalizedRows, compiled);
|
|
1275
|
+
this.attachRootRecordAccessors(hydratedRows);
|
|
1103
1276
|
const projectedRows = hydratedRows;
|
|
1104
1277
|
return new QueryResult(projectedRows);
|
|
1105
1278
|
}
|
|
1279
|
+
normalizeRowsForSchemaParsing(rows) {
|
|
1280
|
+
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return [...rows];
|
|
1281
|
+
const booleanColumns = Object.entries(this.executor.meta.columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
|
|
1282
|
+
if (booleanColumns.length === 0) return [...rows];
|
|
1283
|
+
return rows.map((row) => this.normalizeBooleanColumns(row, booleanColumns));
|
|
1284
|
+
}
|
|
1106
1285
|
normalizeHydratedRowsForParserShape(rows) {
|
|
1107
|
-
if (this.executor.dialect !== InternalDialect.SQLITE) return [...rows];
|
|
1286
|
+
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return [...rows];
|
|
1108
1287
|
const booleanColumns = Object.entries(this.executor.meta.columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
|
|
1109
1288
|
if (booleanColumns.length === 0) return [...rows];
|
|
1110
1289
|
return rows.map((row) => this.normalizeBooleanColumns(row, booleanColumns));
|
|
@@ -1112,15 +1291,29 @@ var QuerySet = class QuerySet {
|
|
|
1112
1291
|
async hydrateRows(rows, compiled) {
|
|
1113
1292
|
if (!compiled.hydrationPlan) return rows;
|
|
1114
1293
|
const hydratedRows = rows.map((row) => ({ ...row }));
|
|
1294
|
+
this.attachRootRecordAccessors(hydratedRows);
|
|
1115
1295
|
const canonicalEntities = new Map();
|
|
1116
1296
|
const queuedJoinPrefetchOwners = new Map();
|
|
1117
|
-
const compiler = new QueryCompiler(this.executor.meta, this.executor.
|
|
1297
|
+
const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
|
|
1118
1298
|
for (const row of hydratedRows) this.hydrateJoinNodesForOwner(row, row, compiled.hydrationPlan.joinNodes, canonicalEntities, queuedJoinPrefetchOwners);
|
|
1119
1299
|
for (const node of compiled.hydrationPlan.prefetchNodes) await this.hydratePrefetchNode(node, hydratedRows, canonicalEntities, compiler);
|
|
1120
1300
|
for (const [node, owners] of queuedJoinPrefetchOwners.entries()) await this.hydratePrefetchNode(node, [...owners], canonicalEntities, compiler);
|
|
1121
1301
|
for (const row of hydratedRows) for (const alias of compiled.hydrationPlan.hiddenRootAliases) delete row[alias];
|
|
1122
1302
|
return hydratedRows;
|
|
1123
1303
|
}
|
|
1304
|
+
primeManyToManyOwnerCache(owner, relationName, bucket) {
|
|
1305
|
+
const existing = owner[relationName];
|
|
1306
|
+
if (existing && typeof existing.primePrefetchCache === "function") {
|
|
1307
|
+
existing.primePrefetchCache(bucket);
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
owner[relationName] = bucket.slice();
|
|
1311
|
+
}
|
|
1312
|
+
attachRootRecordAccessors(rows) {
|
|
1313
|
+
if (!this.executor.attachPersistedRecordAccessors) return;
|
|
1314
|
+
const sourceModelKey = this.executor.meta.modelKey;
|
|
1315
|
+
for (const row of rows) this.executor.attachPersistedRecordAccessors(row, sourceModelKey);
|
|
1316
|
+
}
|
|
1124
1317
|
hydrateJoinNodesForOwner(owner, rawRow, nodes, canonicalEntities, queuedJoinPrefetchOwners) {
|
|
1125
1318
|
for (const node of nodes) {
|
|
1126
1319
|
if (!node.join) continue;
|
|
@@ -1153,28 +1346,81 @@ var QuerySet = class QuerySet {
|
|
|
1153
1346
|
if (owners.length === 0) return;
|
|
1154
1347
|
const groupedOwners = this.groupOwnersByAccessor(owners, node.ownerSourceAccessor);
|
|
1155
1348
|
const sourceValues = [...groupedOwners.keys()];
|
|
1156
|
-
|
|
1349
|
+
const isManyToMany = !!node.throughTable;
|
|
1350
|
+
if (!isManyToMany) for (const owner of owners) owner[node.relationName] = node.cardinality === InternalRelationHydrationCardinality.MANY ? [] : null;
|
|
1157
1351
|
if (sourceValues.length === 0) return;
|
|
1158
|
-
const
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1352
|
+
const sourceChunks = this.chunkValues(sourceValues, 500);
|
|
1353
|
+
const compiledPrefetch = compiler.compilePrefetch(node, sourceChunks[0]);
|
|
1354
|
+
if (compiledPrefetch.kind === InternalPrefetchQueryKind.MANY_TO_MANY) {
|
|
1355
|
+
const idsByOwner = new Map();
|
|
1356
|
+
const uniqueTargetIds = new Set();
|
|
1357
|
+
for (const chunk of sourceChunks) {
|
|
1358
|
+
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1359
|
+
const throughResult = await this.executor.client.query(chunkCompiled.throughSql, chunkCompiled.throughParams);
|
|
1360
|
+
for (const row of throughResult.rows) {
|
|
1361
|
+
const ownerId = row[chunkCompiled.ownerAlias];
|
|
1362
|
+
const targetId = row[chunkCompiled.targetAlias];
|
|
1363
|
+
if (typeof ownerId !== "string" && typeof ownerId !== "number" || typeof targetId !== "string" && typeof targetId !== "number") continue;
|
|
1364
|
+
const bucket = idsByOwner.get(ownerId) ?? [];
|
|
1365
|
+
bucket.push(targetId);
|
|
1366
|
+
idsByOwner.set(ownerId, bucket);
|
|
1367
|
+
uniqueTargetIds.add(targetId);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
const targets = {};
|
|
1371
|
+
const targetIds = [...uniqueTargetIds.values()];
|
|
1372
|
+
if (targetIds.length > 0) for (const targetChunk of this.chunkValues(targetIds, 500)) {
|
|
1373
|
+
const targetQuery = compiler.compileManyToManyTargets(node, targetChunk);
|
|
1374
|
+
const targetResult = await this.executor.client.query(targetQuery.sql, targetQuery.params);
|
|
1375
|
+
for (const rawTargetRow of targetResult.rows) {
|
|
1376
|
+
const normalized = this.normalizeTargetRow({ targetColumns: compiledPrefetch.targetColumns }, rawTargetRow);
|
|
1377
|
+
const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
|
|
1378
|
+
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
|
|
1379
|
+
const primaryKey = canonical[node.targetPrimaryKey];
|
|
1380
|
+
if (typeof primaryKey === "string" || typeof primaryKey === "number") targets[primaryKey] = canonical;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
const canonicalChildren$1 = new Map();
|
|
1384
|
+
const handledOwners = new Set();
|
|
1385
|
+
for (const [ownerId, ids] of idsByOwner.entries()) {
|
|
1386
|
+
const bucket = ids.map((id) => targets[id]).filter((value) => !!value);
|
|
1387
|
+
for (const owner of groupedOwners.get(ownerId) ?? []) {
|
|
1388
|
+
this.primeManyToManyOwnerCache(owner, node.relationName, bucket);
|
|
1389
|
+
handledOwners.add(owner);
|
|
1390
|
+
}
|
|
1391
|
+
for (const child of bucket) canonicalChildren$1.set(child[node.targetPrimaryKey], child);
|
|
1392
|
+
}
|
|
1393
|
+
for (const owner of owners) if (!handledOwners.has(owner)) this.primeManyToManyOwnerCache(owner, node.relationName, []);
|
|
1394
|
+
const childOwners$1 = [...canonicalChildren$1.values()];
|
|
1395
|
+
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners$1, canonicalEntities, compiler);
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1161
1398
|
const canonicalChildren = new Map();
|
|
1162
|
-
for (const
|
|
1163
|
-
const
|
|
1164
|
-
const
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1399
|
+
for (const chunk of sourceChunks) {
|
|
1400
|
+
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1401
|
+
const result = await this.executor.client.query(chunkCompiled.sql, chunkCompiled.params);
|
|
1402
|
+
for (const rawResultRow of result.rows) {
|
|
1403
|
+
const normalized = this.normalizeTargetRow(chunkCompiled, rawResultRow);
|
|
1404
|
+
const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
|
|
1405
|
+
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
|
|
1406
|
+
const key = normalized[chunkCompiled.targetKey];
|
|
1407
|
+
if (typeof key !== "string" && typeof key !== "number") continue;
|
|
1408
|
+
for (const owner of groupedOwners.get(key) ?? []) if (node.cardinality === InternalRelationHydrationCardinality.MANY) owner[node.relationName].push(canonical);
|
|
1409
|
+
else if (owner[node.relationName] === null) owner[node.relationName] = canonical;
|
|
1410
|
+
const childPrimaryKey = canonical[node.targetPrimaryKey];
|
|
1411
|
+
if (typeof childPrimaryKey === "string" || typeof childPrimaryKey === "number") canonicalChildren.set(childPrimaryKey, canonical);
|
|
1412
|
+
}
|
|
1173
1413
|
}
|
|
1174
|
-
for (const [sourceValue, grouped] of groupedTargets.entries()) for (const owner of groupedOwners.get(sourceValue) ?? []) owner[node.relationName] = node.cardinality === InternalRelationHydrationCardinality.MANY ? grouped : grouped[0];
|
|
1175
1414
|
const childOwners = [...canonicalChildren.values()];
|
|
1176
1415
|
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, canonicalEntities, compiler);
|
|
1177
1416
|
}
|
|
1417
|
+
chunkValues(values, size) {
|
|
1418
|
+
if (values.length === 0) return [];
|
|
1419
|
+
if (values.length <= size) return [Array.from(values)];
|
|
1420
|
+
const chunks = [];
|
|
1421
|
+
for (let i = 0; i < values.length; i += size) chunks.push(values.slice(i, i + size));
|
|
1422
|
+
return chunks;
|
|
1423
|
+
}
|
|
1178
1424
|
groupOwnersByAccessor(owners, accessor) {
|
|
1179
1425
|
const grouped = new Map();
|
|
1180
1426
|
for (const owner of owners) {
|
|
@@ -1197,10 +1443,11 @@ var QuerySet = class QuerySet {
|
|
|
1197
1443
|
}
|
|
1198
1444
|
byModel.set(primaryKeyValue, row);
|
|
1199
1445
|
canonicalEntities.set(node.targetModelKey, byModel);
|
|
1446
|
+
this.executor.attachPersistedRecordAccessors?.(row, node.targetModelKey);
|
|
1200
1447
|
return row;
|
|
1201
1448
|
}
|
|
1202
1449
|
normalizeTargetRow(prefetch, row) {
|
|
1203
|
-
if (this.executor.dialect !== InternalDialect.SQLITE) return row;
|
|
1450
|
+
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return row;
|
|
1204
1451
|
let normalized = null;
|
|
1205
1452
|
for (const [column, type] of Object.entries(prefetch.targetColumns)) {
|
|
1206
1453
|
if (!this.isBooleanColumnType(type)) continue;
|
|
@@ -1212,7 +1459,7 @@ var QuerySet = class QuerySet {
|
|
|
1212
1459
|
return normalized ?? row;
|
|
1213
1460
|
}
|
|
1214
1461
|
normalizeColumnValue(columnType, value) {
|
|
1215
|
-
return this.executor.dialect === InternalDialect.SQLITE && this.isBooleanColumnType(columnType) ? this.normalizeSqliteBoolean(value) : value;
|
|
1462
|
+
return this.executor.adapter.dialect === InternalDialect.SQLITE && this.isBooleanColumnType(columnType) ? this.normalizeSqliteBoolean(value) : value;
|
|
1216
1463
|
}
|
|
1217
1464
|
isBooleanColumnType(value) {
|
|
1218
1465
|
return typeof value === "string" && ["bool", "boolean"].includes(value.trim().toLowerCase());
|
|
@@ -1239,10 +1486,22 @@ var QuerySet = class QuerySet {
|
|
|
1239
1486
|
}
|
|
1240
1487
|
};
|
|
1241
1488
|
|
|
1489
|
+
//#endregion
|
|
1490
|
+
//#region src/query/ModelQuerySet.ts
|
|
1491
|
+
var ModelQuerySet = class ModelQuerySet extends QuerySet {
|
|
1492
|
+
constructor(executor, state = {}) {
|
|
1493
|
+
super(executor, state);
|
|
1494
|
+
}
|
|
1495
|
+
spawn(state) {
|
|
1496
|
+
return new ModelQuerySet(this.executor, state);
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
|
|
1242
1500
|
//#endregion
|
|
1243
1501
|
//#region src/query/index.ts
|
|
1244
1502
|
var query_exports = {};
|
|
1245
1503
|
__export(query_exports, {
|
|
1504
|
+
ModelQuerySet: () => ModelQuerySet,
|
|
1246
1505
|
Q: () => QBuilder,
|
|
1247
1506
|
QBuilder: () => QBuilder,
|
|
1248
1507
|
QueryCompiler: () => QueryCompiler,
|
|
@@ -1253,5 +1512,5 @@ __export(query_exports, {
|
|
|
1253
1512
|
});
|
|
1254
1513
|
|
|
1255
1514
|
//#endregion
|
|
1256
|
-
export { InternalQNodeType, OrmSqlSafetyAdapter, QBuilder, QueryCompiler, QueryResult, QuerySet, TableMetaFactory, compiler_exports, domain_exports, isQNodeLike, query_exports };
|
|
1257
|
-
//# sourceMappingURL=query-
|
|
1515
|
+
export { InternalQNodeType, InternalRelationKind, InternalSqlValidationPlanKind, ModelQuerySet, OrmSqlSafetyAdapter, QBuilder, QueryCompiler, QueryResult, QuerySet, TableMetaFactory, compiler_exports, domain_exports, isQNodeLike, query_exports };
|
|
1516
|
+
//# sourceMappingURL=query-DUZnBFhf.js.map
|