@atscript/db 0.1.38
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/LICENSE +21 -0
- package/README.md +343 -0
- package/dist/agg-BJFJ3dFQ.mjs +8 -0
- package/dist/agg-DnUWAOK8.cjs +14 -0
- package/dist/agg.cjs +3 -0
- package/dist/agg.d.ts +13 -0
- package/dist/agg.mjs +3 -0
- package/dist/chunk-CrpGerW8.cjs +31 -0
- package/dist/control_as-BFPERAF_.cjs +28 -0
- package/dist/control_as-bjmwe24C.mjs +26 -0
- package/dist/index.cjs +2887 -0
- package/dist/index.d.ts +1706 -0
- package/dist/index.mjs +2846 -0
- package/dist/logger-B7oxCfLQ.mjs +12 -0
- package/dist/logger-Dt2v_-wb.cjs +18 -0
- package/dist/nested-writer-BkqL7cp3.cjs +667 -0
- package/dist/nested-writer-NEN51mnR.mjs +576 -0
- package/dist/plugin.cjs +993 -0
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.mjs +989 -0
- package/dist/rel.cjs +20 -0
- package/dist/rel.d.ts +1305 -0
- package/dist/rel.mjs +5 -0
- package/dist/relation-helpers-DyBIlQnB.mjs +29 -0
- package/dist/relation-helpers-guFL_oRf.cjs +47 -0
- package/dist/relation-loader-CpnDRf9k.cjs +415 -0
- package/dist/relation-loader-D4mTw6yH.cjs +4 -0
- package/dist/relation-loader-Dv7qXYq7.mjs +409 -0
- package/dist/relation-loader-Ggy1ujwR.mjs +4 -0
- package/dist/shared.cjs +13 -0
- package/dist/shared.d.ts +70 -0
- package/dist/shared.mjs +3 -0
- package/dist/sync.cjs +1205 -0
- package/dist/sync.d.ts +1878 -0
- package/dist/sync.mjs +1186 -0
- package/dist/validation-utils-DEoCMmEb.cjs +304 -0
- package/dist/validation-utils-DhR_mtKa.mjs +237 -0
- package/package.json +81 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { findFKForRelation, findRemoteFK, resolveRelationTargetTable } from "./relation-helpers-DyBIlQnB.mjs";
|
|
2
|
+
|
|
3
|
+
//#region packages/db/src/rel/relation-loader.ts
|
|
4
|
+
async function loadRelationsImpl(rows, withRelations, host) {
|
|
5
|
+
if (rows.length === 0 || withRelations.length === 0) return;
|
|
6
|
+
if (host.adapter.supportsNativeRelations()) return host.adapter.loadRelations(rows, withRelations, host._meta.relations, host._meta.foreignKeys, host._tableResolver);
|
|
7
|
+
if (!host._tableResolver) return;
|
|
8
|
+
const tasks = [];
|
|
9
|
+
for (const withRel of withRelations) {
|
|
10
|
+
const relName = withRel.name;
|
|
11
|
+
if (relName.includes(".")) continue;
|
|
12
|
+
const relation = host._meta.relations.get(relName);
|
|
13
|
+
if (!relation) throw new Error(`Unknown relation "${relName}" in $with. Available relations: ${[...host._meta.relations.keys()].join(", ") || "(none)"}`);
|
|
14
|
+
const targetType = relation.targetType();
|
|
15
|
+
if (!targetType) continue;
|
|
16
|
+
const targetTable = host._tableResolver(targetType);
|
|
17
|
+
if (!targetTable) {
|
|
18
|
+
host.logger.warn(`Could not resolve table for relation "${relName}" — skipping`);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const filter = withRel.filter && Object.keys(withRel.filter).length > 0 ? withRel.filter : undefined;
|
|
22
|
+
const flatRel = withRel;
|
|
23
|
+
const nested = withRel.controls || {};
|
|
24
|
+
const controls = { ...nested };
|
|
25
|
+
if (flatRel.$sort && !controls.$sort) controls.$sort = flatRel.$sort;
|
|
26
|
+
if (flatRel.$limit !== null && flatRel.$limit !== undefined && (controls.$limit === null || controls.$limit === undefined)) controls.$limit = flatRel.$limit;
|
|
27
|
+
if (flatRel.$skip !== null && flatRel.$skip !== undefined && (controls.$skip === null || controls.$skip === undefined)) controls.$skip = flatRel.$skip;
|
|
28
|
+
if (flatRel.$select && !controls.$select) controls.$select = flatRel.$select;
|
|
29
|
+
if (flatRel.$with && !controls.$with) controls.$with = flatRel.$with;
|
|
30
|
+
const relQuery = {
|
|
31
|
+
filter,
|
|
32
|
+
controls
|
|
33
|
+
};
|
|
34
|
+
if (relation.direction === "to") tasks.push(loadToRelation(rows, {
|
|
35
|
+
relName,
|
|
36
|
+
relation,
|
|
37
|
+
targetTable,
|
|
38
|
+
relQuery
|
|
39
|
+
}, host));
|
|
40
|
+
else if (relation.direction === "via") tasks.push(loadViaRelation(rows, {
|
|
41
|
+
relName,
|
|
42
|
+
relation,
|
|
43
|
+
targetTable,
|
|
44
|
+
relQuery
|
|
45
|
+
}, host));
|
|
46
|
+
else tasks.push(loadFromRelation(rows, {
|
|
47
|
+
relName,
|
|
48
|
+
relation,
|
|
49
|
+
targetTable,
|
|
50
|
+
relQuery
|
|
51
|
+
}, host));
|
|
52
|
+
}
|
|
53
|
+
await Promise.all(tasks);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Loads a `@db.rel.to` relation (FK is on this table).
|
|
57
|
+
*/ async function loadToRelation(rows, opts, host) {
|
|
58
|
+
const { relName, relation, targetTable, relQuery } = opts;
|
|
59
|
+
const fkEntry = findFKForRelation(relation, host._meta.foreignKeys);
|
|
60
|
+
if (!fkEntry) return;
|
|
61
|
+
const { localFields, targetFields } = fkEntry;
|
|
62
|
+
if (localFields.length === 1) {
|
|
63
|
+
const localField = localFields[0];
|
|
64
|
+
const targetField = targetFields[0];
|
|
65
|
+
const fkValues = collectUniqueValues(rows, localField);
|
|
66
|
+
if (fkValues.length === 0) {
|
|
67
|
+
for (const row of rows) row[relName] = null;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const inFilter = { [targetField]: { $in: fkValues } };
|
|
71
|
+
const targetFilter = relQuery.filter ? { $and: [inFilter, relQuery.filter] } : inFilter;
|
|
72
|
+
const controls = ensureSelectIncludesFields(relQuery.controls, targetFields);
|
|
73
|
+
const related = await targetTable.findMany({
|
|
74
|
+
filter: targetFilter,
|
|
75
|
+
controls
|
|
76
|
+
});
|
|
77
|
+
assignSingle({
|
|
78
|
+
rows,
|
|
79
|
+
related,
|
|
80
|
+
localField,
|
|
81
|
+
remoteField: targetField,
|
|
82
|
+
relName
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
const related = await queryCompositeFK(rows, {
|
|
86
|
+
localFields,
|
|
87
|
+
targetFields,
|
|
88
|
+
targetTable,
|
|
89
|
+
relQuery
|
|
90
|
+
});
|
|
91
|
+
const index = new Map();
|
|
92
|
+
for (const item of related) index.set(compositeKey(targetFields, item), item);
|
|
93
|
+
for (const row of rows) row[relName] = index.get(compositeKey(localFields, row)) ?? null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Loads a `@db.rel.from` relation (FK is on the target table).
|
|
98
|
+
*/ async function loadFromRelation(rows, opts, host) {
|
|
99
|
+
const { relName, relation, targetTable, relQuery } = opts;
|
|
100
|
+
const remoteFK = findRemoteFK(targetTable, host.tableName, relation.alias);
|
|
101
|
+
if (!remoteFK) {
|
|
102
|
+
host.logger.warn(`Could not find FK on target table for relation "${relName}"`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const localFields = remoteFK.targetFields;
|
|
106
|
+
const remoteFields = remoteFK.fields;
|
|
107
|
+
if (localFields.length === 1) {
|
|
108
|
+
const localField = localFields[0];
|
|
109
|
+
const remoteField = remoteFields[0];
|
|
110
|
+
const pkValues = collectUniqueValues(rows, localField);
|
|
111
|
+
if (pkValues.length === 0) return;
|
|
112
|
+
const inFilter = { [remoteField]: { $in: pkValues } };
|
|
113
|
+
const targetFilter = relQuery.filter ? { $and: [inFilter, relQuery.filter] } : inFilter;
|
|
114
|
+
const controls = ensureSelectIncludesFields(relQuery.controls, remoteFields);
|
|
115
|
+
const related = await targetTable.findMany({
|
|
116
|
+
filter: targetFilter,
|
|
117
|
+
controls
|
|
118
|
+
});
|
|
119
|
+
if (relation.isArray) assignGrouped({
|
|
120
|
+
rows,
|
|
121
|
+
related,
|
|
122
|
+
localField,
|
|
123
|
+
remoteField,
|
|
124
|
+
relName
|
|
125
|
+
});
|
|
126
|
+
else assignSingle({
|
|
127
|
+
rows,
|
|
128
|
+
related,
|
|
129
|
+
localField,
|
|
130
|
+
remoteField,
|
|
131
|
+
relName
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
const related = await queryCompositeFK(rows, {
|
|
135
|
+
localFields,
|
|
136
|
+
targetFields: remoteFields,
|
|
137
|
+
targetTable,
|
|
138
|
+
relQuery
|
|
139
|
+
});
|
|
140
|
+
if (relation.isArray) {
|
|
141
|
+
const groups = new Map();
|
|
142
|
+
for (const item of related) {
|
|
143
|
+
const key = compositeKey(remoteFields, item);
|
|
144
|
+
let group = groups.get(key);
|
|
145
|
+
if (!group) {
|
|
146
|
+
group = [];
|
|
147
|
+
groups.set(key, group);
|
|
148
|
+
}
|
|
149
|
+
group.push(item);
|
|
150
|
+
}
|
|
151
|
+
for (const row of rows) row[relName] = groups.get(compositeKey(localFields, row)) ?? [];
|
|
152
|
+
} else {
|
|
153
|
+
const index = new Map();
|
|
154
|
+
for (const item of related) {
|
|
155
|
+
const key = compositeKey(remoteFields, item);
|
|
156
|
+
if (!index.has(key)) index.set(key, item);
|
|
157
|
+
}
|
|
158
|
+
for (const row of rows) row[relName] = index.get(compositeKey(localFields, row)) ?? null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Loads a `@db.rel.via` relation (M:N through a junction table).
|
|
164
|
+
*/ async function loadViaRelation(rows, opts, host) {
|
|
165
|
+
const { relName, relation, targetTable, relQuery } = opts;
|
|
166
|
+
if (!relation.viaType || !host._tableResolver) return;
|
|
167
|
+
const junctionType = relation.viaType();
|
|
168
|
+
if (!junctionType) return;
|
|
169
|
+
const junctionTable = host._tableResolver(junctionType);
|
|
170
|
+
if (!junctionTable) {
|
|
171
|
+
host.logger.warn(`Could not resolve junction table for via relation "${relName}"`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const fkToThis = findRemoteFK(junctionTable, host.tableName);
|
|
175
|
+
if (!fkToThis) {
|
|
176
|
+
host.logger.warn(`Could not find FK on junction table pointing to "${host.tableName}" for via relation "${relName}"`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const targetTableName = resolveRelationTargetTable(relation);
|
|
180
|
+
const fkToTarget = findRemoteFK(junctionTable, targetTableName);
|
|
181
|
+
if (!fkToTarget) {
|
|
182
|
+
host.logger.warn(`Could not find FK on junction table pointing to target "${targetTableName}" for via relation "${relName}"`);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const localPKFields = fkToThis.targetFields;
|
|
186
|
+
const junctionLocalFields = fkToThis.fields;
|
|
187
|
+
const targetPKFields = fkToTarget.targetFields;
|
|
188
|
+
const junctionTargetFields = fkToTarget.fields;
|
|
189
|
+
if (localPKFields.length === 1) await loadViaSingleKey(rows, {
|
|
190
|
+
relName,
|
|
191
|
+
relation,
|
|
192
|
+
targetTable,
|
|
193
|
+
relQuery,
|
|
194
|
+
localPKFields,
|
|
195
|
+
junctionLocalFields,
|
|
196
|
+
targetPKFields,
|
|
197
|
+
junctionTargetFields,
|
|
198
|
+
junctionTable
|
|
199
|
+
});
|
|
200
|
+
else await loadViaCompositeKey(rows, {
|
|
201
|
+
relName,
|
|
202
|
+
relation,
|
|
203
|
+
targetTable,
|
|
204
|
+
relQuery,
|
|
205
|
+
localPKFields,
|
|
206
|
+
junctionLocalFields,
|
|
207
|
+
targetPKFields,
|
|
208
|
+
junctionTargetFields,
|
|
209
|
+
junctionTable
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async function loadViaSingleKey(rows, opts) {
|
|
213
|
+
const { relName, relation, targetTable, relQuery, localPKFields, junctionLocalFields, targetPKFields, junctionTargetFields, junctionTable } = opts;
|
|
214
|
+
const localField = localPKFields[0];
|
|
215
|
+
const junctionLocalField = junctionLocalFields[0];
|
|
216
|
+
const junctionTargetField = junctionTargetFields[0];
|
|
217
|
+
const targetPKField = targetPKFields[0];
|
|
218
|
+
const pkValues = collectUniqueValues(rows, localField);
|
|
219
|
+
if (pkValues.length === 0) {
|
|
220
|
+
for (const row of rows) row[relName] = [];
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const junctionFilter = { [junctionLocalField]: { $in: pkValues } };
|
|
224
|
+
const junctionRows = await junctionTable.findMany({
|
|
225
|
+
filter: junctionFilter,
|
|
226
|
+
controls: { $select: [junctionLocalField, junctionTargetField] }
|
|
227
|
+
});
|
|
228
|
+
if (junctionRows.length === 0) {
|
|
229
|
+
for (const row of rows) row[relName] = relation.isArray ? [] : null;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const targetFKValues = collectUniqueValues(junctionRows, junctionTargetField);
|
|
233
|
+
const inFilter = { [targetPKField]: { $in: targetFKValues } };
|
|
234
|
+
const targetFilter = relQuery.filter ? { $and: [inFilter, relQuery.filter] } : inFilter;
|
|
235
|
+
const controls = ensureSelectIncludesFields(relQuery.controls, targetPKFields);
|
|
236
|
+
const targetRows = await targetTable.findMany({
|
|
237
|
+
filter: targetFilter,
|
|
238
|
+
controls
|
|
239
|
+
});
|
|
240
|
+
const targetIndex = new Map();
|
|
241
|
+
for (const item of targetRows) targetIndex.set(String(item[targetPKField]), item);
|
|
242
|
+
const groups = new Map();
|
|
243
|
+
for (const jRow of junctionRows) {
|
|
244
|
+
const localKey = String(jRow[junctionLocalField]);
|
|
245
|
+
const targetKey = String(jRow[junctionTargetField]);
|
|
246
|
+
const target = targetIndex.get(targetKey);
|
|
247
|
+
if (!target) continue;
|
|
248
|
+
let group = groups.get(localKey);
|
|
249
|
+
if (!group) {
|
|
250
|
+
group = [];
|
|
251
|
+
groups.set(localKey, group);
|
|
252
|
+
}
|
|
253
|
+
group.push(target);
|
|
254
|
+
}
|
|
255
|
+
for (const row of rows) {
|
|
256
|
+
const key = String(row[localField]);
|
|
257
|
+
row[relName] = relation.isArray ? groups.get(key) ?? [] : groups.get(key)?.[0] ?? null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async function loadViaCompositeKey(rows, opts) {
|
|
261
|
+
const { relName, relation, targetTable, relQuery, localPKFields, junctionLocalFields, targetPKFields, junctionTargetFields, junctionTable } = opts;
|
|
262
|
+
const orFilters = [];
|
|
263
|
+
for (const row of rows) {
|
|
264
|
+
const condition = {};
|
|
265
|
+
let valid = true;
|
|
266
|
+
for (let i = 0; i < localPKFields.length; i++) {
|
|
267
|
+
const val = row[localPKFields[i]];
|
|
268
|
+
if (val === null || val === undefined) {
|
|
269
|
+
valid = false;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
condition[junctionLocalFields[i]] = val;
|
|
273
|
+
}
|
|
274
|
+
if (valid) orFilters.push(condition);
|
|
275
|
+
}
|
|
276
|
+
if (orFilters.length === 0) {
|
|
277
|
+
for (const row of rows) row[relName] = relation.isArray ? [] : null;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const junctionFilter = orFilters.length === 1 ? orFilters[0] : { $or: orFilters };
|
|
281
|
+
const junctionRows = await junctionTable.findMany({
|
|
282
|
+
filter: junctionFilter,
|
|
283
|
+
controls: { $select: [...junctionLocalFields, ...junctionTargetFields] }
|
|
284
|
+
});
|
|
285
|
+
if (junctionRows.length === 0) {
|
|
286
|
+
for (const row of rows) row[relName] = relation.isArray ? [] : null;
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const targetOrFilters = [];
|
|
290
|
+
const seenTargets = new Set();
|
|
291
|
+
for (const jRow of junctionRows) {
|
|
292
|
+
const key = compositeKey(junctionTargetFields, jRow);
|
|
293
|
+
if (seenTargets.has(key)) continue;
|
|
294
|
+
seenTargets.add(key);
|
|
295
|
+
const condition = {};
|
|
296
|
+
for (let i = 0; i < junctionTargetFields.length; i++) condition[targetPKFields[i]] = jRow[junctionTargetFields[i]];
|
|
297
|
+
targetOrFilters.push(condition);
|
|
298
|
+
}
|
|
299
|
+
const targetBaseFilter = targetOrFilters.length === 1 ? targetOrFilters[0] : { $or: targetOrFilters };
|
|
300
|
+
const finalFilter = relQuery.filter ? { $and: [targetBaseFilter, relQuery.filter] } : targetBaseFilter;
|
|
301
|
+
const targetRows = await targetTable.findMany({
|
|
302
|
+
filter: finalFilter,
|
|
303
|
+
controls: relQuery.controls
|
|
304
|
+
});
|
|
305
|
+
const targetIndex = new Map();
|
|
306
|
+
for (const item of targetRows) targetIndex.set(compositeKey(targetPKFields, item), item);
|
|
307
|
+
const groups = new Map();
|
|
308
|
+
for (const jRow of junctionRows) {
|
|
309
|
+
const localKey = compositeKey(junctionLocalFields, jRow);
|
|
310
|
+
const targetKey = compositeKey(junctionTargetFields, jRow);
|
|
311
|
+
const target = targetIndex.get(targetKey);
|
|
312
|
+
if (!target) continue;
|
|
313
|
+
let group = groups.get(localKey);
|
|
314
|
+
if (!group) {
|
|
315
|
+
group = [];
|
|
316
|
+
groups.set(localKey, group);
|
|
317
|
+
}
|
|
318
|
+
group.push(target);
|
|
319
|
+
}
|
|
320
|
+
for (const row of rows) {
|
|
321
|
+
const key = compositeKey(localPKFields, row);
|
|
322
|
+
row[relName] = relation.isArray ? groups.get(key) ?? [] : groups.get(key)?.[0] ?? null;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* If controls include an array-style $select, ensure the given join fields
|
|
327
|
+
* are present so that FK matching works after the query returns.
|
|
328
|
+
*/ function ensureSelectIncludesFields(controls, fields) {
|
|
329
|
+
if (!controls) return controls;
|
|
330
|
+
const sel = controls.$select;
|
|
331
|
+
if (!Array.isArray(sel)) return controls;
|
|
332
|
+
const augmented = [...sel];
|
|
333
|
+
for (const f of fields) if (!augmented.includes(f)) augmented.push(f);
|
|
334
|
+
return {
|
|
335
|
+
...controls,
|
|
336
|
+
$select: augmented
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function compositeKey(fields, obj) {
|
|
340
|
+
let key = "";
|
|
341
|
+
for (let i = 0; i < fields.length; i++) {
|
|
342
|
+
if (i > 0) key += "\0\0";
|
|
343
|
+
const v = obj[fields[i]];
|
|
344
|
+
key += v === null || v === undefined ? "\0" : String(v);
|
|
345
|
+
}
|
|
346
|
+
return key;
|
|
347
|
+
}
|
|
348
|
+
/** Collects unique non-null values for a field across rows. */ function collectUniqueValues(rows, field) {
|
|
349
|
+
const set = new Set();
|
|
350
|
+
for (const row of rows) {
|
|
351
|
+
const v = row[field];
|
|
352
|
+
if (v !== null && v !== undefined) set.add(v);
|
|
353
|
+
}
|
|
354
|
+
return [...set];
|
|
355
|
+
}
|
|
356
|
+
/** Assigns related items grouped by FK value (one-to-many). */ function assignGrouped(opts) {
|
|
357
|
+
const { rows, related, localField, remoteField, relName } = opts;
|
|
358
|
+
const groups = new Map();
|
|
359
|
+
for (const item of related) {
|
|
360
|
+
const key = item[remoteField];
|
|
361
|
+
let group = groups.get(key);
|
|
362
|
+
if (!group) {
|
|
363
|
+
group = [];
|
|
364
|
+
groups.set(key, group);
|
|
365
|
+
}
|
|
366
|
+
group.push(item);
|
|
367
|
+
}
|
|
368
|
+
for (const row of rows) row[relName] = groups.get(row[localField]) ?? [];
|
|
369
|
+
}
|
|
370
|
+
/** Assigns related items by FK value (many-to-one / one-to-one). */ function assignSingle(opts) {
|
|
371
|
+
const { rows, related, localField, remoteField, relName } = opts;
|
|
372
|
+
const index = new Map();
|
|
373
|
+
for (const item of related) {
|
|
374
|
+
const key = item[remoteField];
|
|
375
|
+
if (!index.has(key)) index.set(key, item);
|
|
376
|
+
}
|
|
377
|
+
for (const row of rows) row[relName] = index.get(row[localField]) ?? null;
|
|
378
|
+
}
|
|
379
|
+
/** Batch query for composite FK. */ function queryCompositeFK(rows, opts) {
|
|
380
|
+
const { localFields, targetFields, targetTable, relQuery } = opts;
|
|
381
|
+
const seen = new Set();
|
|
382
|
+
const orFilters = [];
|
|
383
|
+
for (const row of rows) {
|
|
384
|
+
const key = compositeKey(localFields, row);
|
|
385
|
+
if (seen.has(key)) continue;
|
|
386
|
+
seen.add(key);
|
|
387
|
+
const condition = {};
|
|
388
|
+
let valid = true;
|
|
389
|
+
for (let i = 0; i < localFields.length; i++) {
|
|
390
|
+
const val = row[localFields[i]];
|
|
391
|
+
if (val === null || val === undefined) {
|
|
392
|
+
valid = false;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
condition[targetFields[i]] = val;
|
|
396
|
+
}
|
|
397
|
+
if (valid) orFilters.push(condition);
|
|
398
|
+
}
|
|
399
|
+
if (orFilters.length === 0) return Promise.resolve([]);
|
|
400
|
+
const baseFilter = orFilters.length === 1 ? orFilters[0] : { $or: orFilters };
|
|
401
|
+
const targetFilter = relQuery.filter ? { $and: [baseFilter, relQuery.filter] } : baseFilter;
|
|
402
|
+
return targetTable.findMany({
|
|
403
|
+
filter: targetFilter,
|
|
404
|
+
controls: relQuery.controls
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
//#endregion
|
|
409
|
+
export { loadRelationsImpl };
|
package/dist/shared.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const require_validation_utils = require('./validation-utils-DEoCMmEb.cjs');
|
|
2
|
+
|
|
3
|
+
exports.findFKFieldsPointingTo = require_validation_utils.findFKFieldsPointingTo
|
|
4
|
+
exports.getAnnotationAlias = require_validation_utils.getAnnotationAlias
|
|
5
|
+
exports.getDbTableOwner = require_validation_utils.getDbTableOwner
|
|
6
|
+
exports.getNavTargetTypeName = require_validation_utils.getNavTargetTypeName
|
|
7
|
+
exports.getParentStruct = require_validation_utils.getParentStruct
|
|
8
|
+
exports.getParentTypeName = require_validation_utils.getParentTypeName
|
|
9
|
+
exports.hasAnyViewAnnotation = require_validation_utils.hasAnyViewAnnotation
|
|
10
|
+
exports.refActionAnnotation = require_validation_utils.refActionAnnotation
|
|
11
|
+
exports.validateFieldBaseType = require_validation_utils.validateFieldBaseType
|
|
12
|
+
exports.validateQueryScope = require_validation_utils.validateQueryScope
|
|
13
|
+
exports.validateRefArgument = require_validation_utils.validateRefArgument
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { SemanticNode, Token, SemanticStructureNode, AnnotationSpec, AtscriptDoc, TMessages, SemanticPropNode, SemanticInterfaceNode } from '@atscript/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Traverse from annotation token → prop → structure → interface
|
|
5
|
+
* to check if the parent interface has @db.table.
|
|
6
|
+
*/
|
|
7
|
+
declare function getDbTableOwner(token: Token): SemanticNode | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Get the parent structure node from an annotation token.
|
|
10
|
+
*/
|
|
11
|
+
declare function getParentStruct(token: Token): SemanticStructureNode | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Get the parent interface name (for error messages and cross-type resolution).
|
|
14
|
+
*/
|
|
15
|
+
declare function getParentTypeName(token: Token): string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Validate that an annotation is on a field with the expected base type.
|
|
18
|
+
*/
|
|
19
|
+
declare function validateFieldBaseType(token: Token, doc: AtscriptDoc, annotationName: string, expectedType: string | string[]): TMessages;
|
|
20
|
+
/**
|
|
21
|
+
* Extract target type name from a navigational field definition.
|
|
22
|
+
* Unwraps arrays (e.g., `Post[]` → `Post`).
|
|
23
|
+
*/
|
|
24
|
+
declare function getNavTargetTypeName(field: SemanticNode): string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Get the alias argument from an annotation on a field.
|
|
27
|
+
*/
|
|
28
|
+
declare function getAnnotationAlias(prop: SemanticNode, annotationName: string): string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Factory for @db.rel.onDelete / @db.rel.onUpdate — identical validation logic,
|
|
31
|
+
* only the annotation name and description verb differ.
|
|
32
|
+
*/
|
|
33
|
+
declare function refActionAnnotation(name: 'onDelete' | 'onUpdate'): AnnotationSpec;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate a ref annotation argument against the document's type registry.
|
|
37
|
+
* Returns diagnostic messages for unknown types or fields.
|
|
38
|
+
*/
|
|
39
|
+
declare function validateRefArgument(token: Token, doc: AtscriptDoc, options?: {
|
|
40
|
+
requireDbTable?: boolean;
|
|
41
|
+
}): TMessages;
|
|
42
|
+
interface TFKFieldMatch {
|
|
43
|
+
name: string;
|
|
44
|
+
prop: SemanticPropNode;
|
|
45
|
+
chainRef: {
|
|
46
|
+
type: string;
|
|
47
|
+
field: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Find all `@db.rel.FK` fields on a type that reference `targetTypeName`.
|
|
52
|
+
* Resolves `extends` to include inherited fields.
|
|
53
|
+
*/
|
|
54
|
+
declare function findFKFieldsPointingTo(doc: AtscriptDoc, iface: SemanticInterfaceNode | SemanticStructureNode, targetTypeName: string, alias?: string): TFKFieldMatch[];
|
|
55
|
+
/**
|
|
56
|
+
* Check if a node has any @db.view.* annotation.
|
|
57
|
+
*/
|
|
58
|
+
declare function hasAnyViewAnnotation(node: SemanticNode): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Validate that all type refs in a query expression are within the allowed scope.
|
|
61
|
+
*
|
|
62
|
+
* @param queryToken - The query arg token (must have .queryNode)
|
|
63
|
+
* @param allowedTypes - Type names allowed as qualified refs
|
|
64
|
+
* @param unqualifiedTarget - Type name for resolving unqualified refs, or null to disallow them
|
|
65
|
+
* @param doc - The document for type lookups
|
|
66
|
+
*/
|
|
67
|
+
declare function validateQueryScope(queryToken: Token, allowedTypes: string[], unqualifiedTarget: string | null, doc: AtscriptDoc): TMessages;
|
|
68
|
+
|
|
69
|
+
export { findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateFieldBaseType, validateQueryScope, validateRefArgument };
|
|
70
|
+
export type { TFKFieldMatch };
|
package/dist/shared.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateFieldBaseType, validateQueryScope, validateRefArgument } from "./validation-utils-DhR_mtKa.mjs";
|
|
2
|
+
|
|
3
|
+
export { findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateFieldBaseType, validateQueryScope, validateRefArgument };
|