js-bao 0.2.11 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -0
- package/dist/BaseModel-5YQCROYE.js +17 -0
- package/dist/BaseModel-5YQCROYE.js.map +1 -0
- package/dist/BaseModel-FCNWDJBH.js +17 -0
- package/dist/BaseModel-FCNWDJBH.js.map +1 -0
- package/dist/BrowserDatabaseFactory-PXOTK2DQ.js +119 -0
- package/dist/BrowserDatabaseFactory-PXOTK2DQ.js.map +1 -0
- package/dist/BrowserDatabaseFactory-WD4VX2VZ.js +119 -0
- package/dist/BrowserDatabaseFactory-WD4VX2VZ.js.map +1 -0
- package/dist/IncludeResolver-RCKQGNPZ.js +385 -0
- package/dist/IncludeResolver-RCKQGNPZ.js.map +1 -0
- package/dist/IncludeResolver-WGSQDMS7.js +385 -0
- package/dist/IncludeResolver-WGSQDMS7.js.map +1 -0
- package/dist/NodeDatabaseFactory-J4Z36UF3.js +165 -0
- package/dist/NodeDatabaseFactory-J4Z36UF3.js.map +1 -0
- package/dist/NodeDatabaseFactory-QIEKAXBM.js +10 -0
- package/dist/NodeDatabaseFactory-QIEKAXBM.js.map +1 -0
- package/dist/NodeSqliteEngine-HJSAYE4E.js +383 -0
- package/dist/NodeSqliteEngine-HJSAYE4E.js.map +1 -0
- package/dist/NodeSqliteEngine-I5SLWLME.js +383 -0
- package/dist/NodeSqliteEngine-I5SLWLME.js.map +1 -0
- package/dist/browser.cjs +3779 -3370
- package/dist/browser.d.cts +18 -1
- package/dist/browser.d.ts +18 -1
- package/dist/browser.js +3750 -3341
- package/dist/chunk-3PZWHUZO.js +4153 -0
- package/dist/chunk-3PZWHUZO.js.map +1 -0
- package/dist/chunk-53MS4MN7.js +373 -0
- package/dist/chunk-53MS4MN7.js.map +1 -0
- package/dist/chunk-65G2P4GL.js +709 -0
- package/dist/chunk-65G2P4GL.js.map +1 -0
- package/dist/chunk-6UX3YSCW.js +4151 -0
- package/dist/chunk-6UX3YSCW.js.map +1 -0
- package/dist/chunk-DANSD6BE.js +709 -0
- package/dist/chunk-DANSD6BE.js.map +1 -0
- package/dist/chunk-DF3JEQXA.js +373 -0
- package/dist/chunk-DF3JEQXA.js.map +1 -0
- package/dist/chunk-GO3APTPX.js +61 -0
- package/dist/chunk-GO3APTPX.js.map +1 -0
- package/dist/chunk-ID4U6IQC.js +53 -0
- package/dist/chunk-ID4U6IQC.js.map +1 -0
- package/dist/chunk-RQVS3LVL.js +165 -0
- package/dist/chunk-RQVS3LVL.js.map +1 -0
- package/dist/client.cjs +837 -0
- package/dist/client.d.cts +1101 -0
- package/dist/client.d.ts +1101 -0
- package/dist/client.js +806 -0
- package/dist/cloudflare-do.cjs +3637 -0
- package/dist/cloudflare-do.d.cts +1366 -0
- package/dist/cloudflare-do.d.ts +1366 -0
- package/dist/cloudflare-do.js +3614 -0
- package/dist/cloudflare.cjs +1048 -0
- package/dist/cloudflare.d.cts +1381 -0
- package/dist/cloudflare.d.ts +1381 -0
- package/dist/cloudflare.js +1017 -0
- package/dist/codegen.cjs +260 -19
- package/dist/environment-TOTQICSE.js +17 -0
- package/dist/environment-TOTQICSE.js.map +1 -0
- package/dist/index.cjs +1905 -1492
- package/dist/index.d.cts +19 -2
- package/dist/index.d.ts +19 -2
- package/dist/index.js +1870 -1457
- package/dist/node.cjs +4779 -4366
- package/dist/node.d.cts +18 -1
- package/dist/node.d.ts +18 -1
- package/dist/node.js +4758 -4345
- package/package.json +42 -13
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ModelRegistry
|
|
3
|
+
} from "./chunk-65G2P4GL.js";
|
|
4
|
+
import {
|
|
5
|
+
DocumentQueryTranslator,
|
|
6
|
+
assertValidIdentifier,
|
|
7
|
+
quoteIdentifier
|
|
8
|
+
} from "./chunk-3PZWHUZO.js";
|
|
9
|
+
|
|
10
|
+
// src/query/IncludeResolver.ts
|
|
11
|
+
var IncludeResolver = class {
|
|
12
|
+
db;
|
|
13
|
+
constructor(db) {
|
|
14
|
+
this.db = db;
|
|
15
|
+
}
|
|
16
|
+
/** Main entry — resolve all includes for a set of records. */
|
|
17
|
+
async resolve(records, includes, depth, parentModelName) {
|
|
18
|
+
if (records.length === 0) return;
|
|
19
|
+
if (depth >= 3) return;
|
|
20
|
+
for (const spec of includes) {
|
|
21
|
+
const error = this._validateIncludeSpec(spec);
|
|
22
|
+
if (error) {
|
|
23
|
+
console.warn(`[include] Skipping invalid spec: ${error}`);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const resultKey = spec.as || spec.model;
|
|
27
|
+
for (const rec of records) {
|
|
28
|
+
if (!rec._related) rec._related = {};
|
|
29
|
+
}
|
|
30
|
+
if (spec.type === "refersTo") {
|
|
31
|
+
await this._resolveRefersTo(records, spec, resultKey);
|
|
32
|
+
} else if (spec.type === "refersToMany") {
|
|
33
|
+
await this._resolveRefersToMany(records, spec, resultKey, parentModelName);
|
|
34
|
+
} else {
|
|
35
|
+
await this._resolveHasMany(records, spec, resultKey);
|
|
36
|
+
}
|
|
37
|
+
if (spec.include && spec.include.length > 0) {
|
|
38
|
+
const allRelated = [];
|
|
39
|
+
for (const rec of records) {
|
|
40
|
+
const val = rec._related[resultKey];
|
|
41
|
+
if (Array.isArray(val)) {
|
|
42
|
+
allRelated.push(...val);
|
|
43
|
+
} else if (val) {
|
|
44
|
+
allRelated.push(val);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (allRelated.length > 0) {
|
|
48
|
+
await this.resolve(allRelated, spec.include, depth + 1, spec.model);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Resolve a refersTo include (parent FK → single related record). */
|
|
54
|
+
async _resolveRefersTo(records, spec, resultKey) {
|
|
55
|
+
const sourceField = spec.sourceField;
|
|
56
|
+
const uniqueValues = [
|
|
57
|
+
...new Set(
|
|
58
|
+
records.map((r) => r[sourceField]).filter((v) => v != null && v !== "")
|
|
59
|
+
)
|
|
60
|
+
];
|
|
61
|
+
if (uniqueValues.length === 0) {
|
|
62
|
+
for (const rec of records) {
|
|
63
|
+
rec._related[resultKey] = null;
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const related = await this._chunkedIn(
|
|
68
|
+
spec.model,
|
|
69
|
+
"id",
|
|
70
|
+
uniqueValues,
|
|
71
|
+
spec.filter,
|
|
72
|
+
{ projection: spec.projection }
|
|
73
|
+
);
|
|
74
|
+
const lookupMap = /* @__PURE__ */ new Map();
|
|
75
|
+
for (const r of related) {
|
|
76
|
+
lookupMap.set(r.id, r);
|
|
77
|
+
}
|
|
78
|
+
for (const rec of records) {
|
|
79
|
+
const fk = rec[sourceField];
|
|
80
|
+
rec._related[resultKey] = fk && lookupMap.get(fk) || null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Resolve a refersToMany include (parent StringSet field → array of related records). */
|
|
84
|
+
async _resolveRefersToMany(records, spec, resultKey, parentModelName) {
|
|
85
|
+
const sourceField = spec.sourceField;
|
|
86
|
+
if (!parentModelName) {
|
|
87
|
+
console.warn(
|
|
88
|
+
`[include] refersToMany requires parentModelName, skipping`
|
|
89
|
+
);
|
|
90
|
+
for (const rec of records) {
|
|
91
|
+
rec._related[resultKey] = [];
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const junctionTable = `model_${parentModelName.toLowerCase()}_${sourceField.toLowerCase()}`;
|
|
96
|
+
const fkColumn = `${parentModelName.toLowerCase()}_id`;
|
|
97
|
+
const parentIds = [
|
|
98
|
+
...new Set(
|
|
99
|
+
records.map((r) => r.id).filter((v) => v != null && v !== "")
|
|
100
|
+
)
|
|
101
|
+
];
|
|
102
|
+
if (parentIds.length === 0) {
|
|
103
|
+
for (const rec of records) {
|
|
104
|
+
rec._related[resultKey] = [];
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const junctionMap = /* @__PURE__ */ new Map();
|
|
109
|
+
const chunkSize = 99;
|
|
110
|
+
for (let i = 0; i < parentIds.length; i += chunkSize) {
|
|
111
|
+
const chunk = parentIds.slice(i, i + chunkSize);
|
|
112
|
+
const inPlaceholders = chunk.map(() => "?").join(", ");
|
|
113
|
+
const sql = `SELECT ${quoteIdentifier(fkColumn)}, "value" FROM ${quoteIdentifier(junctionTable)} WHERE ${quoteIdentifier(fkColumn)} IN (${inPlaceholders})`;
|
|
114
|
+
const rows = await this.db.query(sql, chunk);
|
|
115
|
+
for (const row of rows) {
|
|
116
|
+
const parentId = row[fkColumn];
|
|
117
|
+
const value = row.value;
|
|
118
|
+
if (!junctionMap.has(parentId)) junctionMap.set(parentId, []);
|
|
119
|
+
junctionMap.get(parentId).push(value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const allTargetIds = [...new Set(
|
|
123
|
+
Array.from(junctionMap.values()).flat()
|
|
124
|
+
)];
|
|
125
|
+
if (allTargetIds.length === 0) {
|
|
126
|
+
for (const rec of records) {
|
|
127
|
+
rec._related[resultKey] = [];
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const targets = await this._chunkedIn(
|
|
132
|
+
spec.model,
|
|
133
|
+
"id",
|
|
134
|
+
allTargetIds,
|
|
135
|
+
spec.filter,
|
|
136
|
+
{ projection: spec.projection }
|
|
137
|
+
);
|
|
138
|
+
const targetLookup = /* @__PURE__ */ new Map();
|
|
139
|
+
for (const t of targets) {
|
|
140
|
+
targetLookup.set(t.id, t);
|
|
141
|
+
}
|
|
142
|
+
for (const rec of records) {
|
|
143
|
+
const targetIds = junctionMap.get(rec.id) || [];
|
|
144
|
+
rec._related[resultKey] = targetIds.map((id) => targetLookup.get(id)).filter(Boolean);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Resolve a hasMany include (target FK → array of related records). */
|
|
148
|
+
async _resolveHasMany(records, spec, resultKey) {
|
|
149
|
+
const localField = spec.localField || "id";
|
|
150
|
+
const uniqueValues = [
|
|
151
|
+
...new Set(
|
|
152
|
+
records.map((r) => r[localField]).filter((v) => v != null && v !== "")
|
|
153
|
+
)
|
|
154
|
+
];
|
|
155
|
+
if (uniqueValues.length === 0) {
|
|
156
|
+
for (const rec of records) {
|
|
157
|
+
rec._related[resultKey] = [];
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
let related;
|
|
162
|
+
if (spec.limit) {
|
|
163
|
+
related = await this._resolveHasManyWithLimit(spec, uniqueValues);
|
|
164
|
+
} else {
|
|
165
|
+
related = await this._resolveHasManySimple(spec, uniqueValues);
|
|
166
|
+
}
|
|
167
|
+
const foreignKey = spec.foreignKey;
|
|
168
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
169
|
+
for (const r of related) {
|
|
170
|
+
const fk = r[foreignKey];
|
|
171
|
+
if (!grouped.has(fk)) grouped.set(fk, []);
|
|
172
|
+
grouped.get(fk).push(r);
|
|
173
|
+
}
|
|
174
|
+
if (spec.projection) {
|
|
175
|
+
for (const [key, list] of grouped) {
|
|
176
|
+
grouped.set(
|
|
177
|
+
key,
|
|
178
|
+
list.map((r) => this._applyProjection(r, spec.projection))
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const rec of records) {
|
|
183
|
+
rec._related[resultKey] = grouped.get(rec[localField]) || [];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** Simple hasMany without per-parent limit. */
|
|
187
|
+
async _resolveHasManySimple(spec, parentValues) {
|
|
188
|
+
return this._chunkedIn(spec.model, spec.foreignKey, parentValues, spec.filter, {
|
|
189
|
+
sort: spec.sort
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/** hasMany with per-parent limit using ROW_NUMBER(). */
|
|
193
|
+
async _resolveHasManyWithLimit(spec, parentValues) {
|
|
194
|
+
const foreignKey = spec.foreignKey;
|
|
195
|
+
assertValidIdentifier(foreignKey, "include foreignKey");
|
|
196
|
+
const modelInfo = ModelRegistry.getInstance().getModelInfo(spec.model);
|
|
197
|
+
if (!modelInfo) {
|
|
198
|
+
console.warn(
|
|
199
|
+
`[include] Target model "${spec.model}" not found in registry, skipping`
|
|
200
|
+
);
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
const tableName = `model_${spec.model.toLowerCase()}`;
|
|
204
|
+
const quotedTable = quoteIdentifier(tableName);
|
|
205
|
+
let sortClause = `"id" ASC`;
|
|
206
|
+
if (spec.sort) {
|
|
207
|
+
const parts = [];
|
|
208
|
+
for (const [field, dir] of Object.entries(spec.sort)) {
|
|
209
|
+
assertValidIdentifier(field, "include sort field");
|
|
210
|
+
parts.push(`${quoteIdentifier(field)} ${dir === -1 ? "DESC" : "ASC"}`);
|
|
211
|
+
}
|
|
212
|
+
sortClause = parts.join(", ");
|
|
213
|
+
}
|
|
214
|
+
const translator = new DocumentQueryTranslator(
|
|
215
|
+
spec.model,
|
|
216
|
+
modelInfo.fields
|
|
217
|
+
);
|
|
218
|
+
let filterClause = "";
|
|
219
|
+
let filterParams = [];
|
|
220
|
+
if (spec.filter) {
|
|
221
|
+
const translated = translator.translateFilter(spec.filter);
|
|
222
|
+
if (translated.sql) {
|
|
223
|
+
filterClause = ` AND ${translated.sql}`;
|
|
224
|
+
filterParams = translated.params;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const reservedParams = filterParams.length + 1;
|
|
228
|
+
const chunkSize = Math.max(1, 100 - reservedParams);
|
|
229
|
+
const allResults = [];
|
|
230
|
+
for (let i = 0; i < parentValues.length; i += chunkSize) {
|
|
231
|
+
const chunk = parentValues.slice(i, i + chunkSize);
|
|
232
|
+
const inPlaceholders = chunk.map(() => "?").join(", ");
|
|
233
|
+
const sql = `
|
|
234
|
+
SELECT * FROM (
|
|
235
|
+
SELECT *,
|
|
236
|
+
ROW_NUMBER() OVER (
|
|
237
|
+
PARTITION BY ${quoteIdentifier(foreignKey)}
|
|
238
|
+
ORDER BY ${sortClause}
|
|
239
|
+
) as _rn
|
|
240
|
+
FROM ${quotedTable}
|
|
241
|
+
WHERE ${quoteIdentifier(foreignKey)} IN (${inPlaceholders})
|
|
242
|
+
${filterClause}
|
|
243
|
+
) WHERE _rn <= ?
|
|
244
|
+
`;
|
|
245
|
+
const params = [...chunk, ...filterParams, spec.limit];
|
|
246
|
+
const rows = await this.db.query(sql, params);
|
|
247
|
+
for (const row of rows) {
|
|
248
|
+
delete row._rn;
|
|
249
|
+
allResults.push(row);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return allResults;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Execute IN queries in chunks to stay under SQLite's parameter limit.
|
|
256
|
+
* Uses 100-param chunks for consistency with the DO backend.
|
|
257
|
+
*/
|
|
258
|
+
async _chunkedIn(model, field, values, extraFilter, extraOptions) {
|
|
259
|
+
const modelInfo = ModelRegistry.getInstance().getModelInfo(model);
|
|
260
|
+
if (!modelInfo) {
|
|
261
|
+
console.warn(
|
|
262
|
+
`[include] Target model "${model}" not found in registry, skipping`
|
|
263
|
+
);
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
const translator = new DocumentQueryTranslator(model, modelInfo.fields);
|
|
267
|
+
let filterParamCount = 0;
|
|
268
|
+
if (extraFilter) {
|
|
269
|
+
const translated = translator.translateFilter(extraFilter);
|
|
270
|
+
filterParamCount = translated.params.length;
|
|
271
|
+
}
|
|
272
|
+
const chunkSize = Math.max(1, 100 - filterParamCount);
|
|
273
|
+
const allResults = [];
|
|
274
|
+
for (let i = 0; i < values.length; i += chunkSize) {
|
|
275
|
+
const chunk = values.slice(i, i + chunkSize);
|
|
276
|
+
const filter = {
|
|
277
|
+
[field]: { $in: chunk },
|
|
278
|
+
...extraFilter || {}
|
|
279
|
+
};
|
|
280
|
+
const options = {};
|
|
281
|
+
if (extraOptions?.sort) options.sort = extraOptions.sort;
|
|
282
|
+
if (extraOptions?.projection) options.projection = extraOptions.projection;
|
|
283
|
+
const { sql, params } = translator.translateFind(filter, options);
|
|
284
|
+
const rows = await this.db.query(sql, params);
|
|
285
|
+
allResults.push(...rows);
|
|
286
|
+
}
|
|
287
|
+
return allResults;
|
|
288
|
+
}
|
|
289
|
+
/** Validate an include spec, returning an error string or null if valid. */
|
|
290
|
+
_validateIncludeSpec(spec) {
|
|
291
|
+
if (!spec.model || typeof spec.model !== "string") {
|
|
292
|
+
return "include: 'model' is required";
|
|
293
|
+
}
|
|
294
|
+
try {
|
|
295
|
+
assertValidIdentifier(spec.model, "include model");
|
|
296
|
+
} catch {
|
|
297
|
+
return `include: invalid model name '${spec.model}'`;
|
|
298
|
+
}
|
|
299
|
+
if (spec.type !== "refersTo" && spec.type !== "hasMany" && spec.type !== "refersToMany") {
|
|
300
|
+
return `include: 'type' must be 'refersTo', 'hasMany', or 'refersToMany', got '${spec.type}'`;
|
|
301
|
+
}
|
|
302
|
+
if (spec.type === "refersTo") {
|
|
303
|
+
if (!spec.sourceField) {
|
|
304
|
+
return "include refersTo: 'sourceField' is required";
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
assertValidIdentifier(spec.sourceField, "include sourceField");
|
|
308
|
+
} catch {
|
|
309
|
+
return `include refersTo: invalid sourceField '${spec.sourceField}'`;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (spec.type === "refersToMany") {
|
|
313
|
+
if (!spec.sourceField) {
|
|
314
|
+
return "include refersToMany: 'sourceField' is required";
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
assertValidIdentifier(spec.sourceField, "include sourceField");
|
|
318
|
+
} catch {
|
|
319
|
+
return `include refersToMany: invalid sourceField '${spec.sourceField}'`;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (spec.type === "hasMany") {
|
|
323
|
+
if (!spec.foreignKey) {
|
|
324
|
+
return "include hasMany: 'foreignKey' is required";
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
assertValidIdentifier(spec.foreignKey, "include foreignKey");
|
|
328
|
+
} catch {
|
|
329
|
+
return `include hasMany: invalid foreignKey '${spec.foreignKey}'`;
|
|
330
|
+
}
|
|
331
|
+
if (spec.localField) {
|
|
332
|
+
try {
|
|
333
|
+
assertValidIdentifier(spec.localField, "include localField");
|
|
334
|
+
} catch {
|
|
335
|
+
return `include hasMany: invalid localField '${spec.localField}'`;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (spec.as) {
|
|
340
|
+
try {
|
|
341
|
+
assertValidIdentifier(spec.as, "include as");
|
|
342
|
+
} catch {
|
|
343
|
+
return `include: invalid alias '${spec.as}'`;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (spec.sort) {
|
|
347
|
+
for (const field of Object.keys(spec.sort)) {
|
|
348
|
+
try {
|
|
349
|
+
assertValidIdentifier(field, "include sort field");
|
|
350
|
+
} catch {
|
|
351
|
+
return `include: invalid sort field '${field}'`;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
/** Apply projection to a single record. */
|
|
358
|
+
_applyProjection(record, projection) {
|
|
359
|
+
const fields = Object.entries(projection);
|
|
360
|
+
if (fields.length === 0) return record;
|
|
361
|
+
const isInclusion = fields.some(([, v]) => v === 1);
|
|
362
|
+
if (isInclusion) {
|
|
363
|
+
const result = {};
|
|
364
|
+
if (record.id !== void 0) result.id = record.id;
|
|
365
|
+
if (record.type !== void 0) result.type = record.type;
|
|
366
|
+
if (record._related !== void 0) result._related = record._related;
|
|
367
|
+
for (const [f, v] of fields) {
|
|
368
|
+
if (v === 1 && record[f] !== void 0) {
|
|
369
|
+
result[f] = record[f];
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
} else {
|
|
374
|
+
const result = { ...record };
|
|
375
|
+
for (const [f, v] of fields) {
|
|
376
|
+
if (v === 0) delete result[f];
|
|
377
|
+
}
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
export {
|
|
383
|
+
IncludeResolver
|
|
384
|
+
};
|
|
385
|
+
//# sourceMappingURL=IncludeResolver-RCKQGNPZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/query/IncludeResolver.ts"],"sourcesContent":["import {\n IncludeSpec,\n DocumentFilter,\n ProjectionSpec,\n QueryOptions,\n SortSpec,\n} from \"../types/queryTypes\";\nimport { DatabaseEngine } from \"../engines/DatabaseEngine\";\nimport { ModelRegistry } from \"../models/ModelRegistry\";\nimport { DocumentQueryTranslator } from \"./DocumentQueryTranslator\";\nimport { assertValidIdentifier, quoteIdentifier } from \"../utils/sql\";\n\n/**\n * Resolves `include` specs for the BaseModel/SQL.js query path.\n *\n * This mirrors the include resolution logic in `createDocumentDO.ts` but\n * adapted for the column-per-field schema used by `DocumentQueryTranslator`\n * and the async `DatabaseEngine.query()` interface.\n */\nexport class IncludeResolver {\n private db: DatabaseEngine;\n\n constructor(db: DatabaseEngine) {\n this.db = db;\n }\n\n /** Main entry — resolve all includes for a set of records. */\n async resolve(\n records: Record<string, any>[],\n includes: IncludeSpec[],\n depth: number,\n parentModelName?: string\n ): Promise<void> {\n if (records.length === 0) return;\n if (depth >= 3) return;\n\n for (const spec of includes) {\n const error = this._validateIncludeSpec(spec);\n if (error) {\n console.warn(`[include] Skipping invalid spec: ${error}`);\n continue;\n }\n\n const resultKey = spec.as || spec.model;\n\n // Initialize _related on each record\n for (const rec of records) {\n if (!rec._related) rec._related = {};\n }\n\n if (spec.type === \"refersTo\") {\n await this._resolveRefersTo(records, spec, resultKey);\n } else if (spec.type === \"refersToMany\") {\n await this._resolveRefersToMany(records, spec, resultKey, parentModelName);\n } else {\n await this._resolveHasMany(records, spec, resultKey);\n }\n\n // Nested includes\n if (spec.include && spec.include.length > 0) {\n const allRelated: Record<string, any>[] = [];\n for (const rec of records) {\n const val = rec._related[resultKey];\n if (Array.isArray(val)) {\n allRelated.push(...val);\n } else if (val) {\n allRelated.push(val);\n }\n }\n if (allRelated.length > 0) {\n await this.resolve(allRelated, spec.include, depth + 1, spec.model);\n }\n }\n }\n }\n\n /** Resolve a refersTo include (parent FK → single related record). */\n private async _resolveRefersTo(\n records: Record<string, any>[],\n spec: IncludeSpec,\n resultKey: string\n ): Promise<void> {\n const sourceField = spec.sourceField!;\n const uniqueValues = [\n ...new Set(\n records\n .map((r) => r[sourceField])\n .filter((v) => v != null && v !== \"\")\n ),\n ];\n\n if (uniqueValues.length === 0) {\n for (const rec of records) {\n rec._related[resultKey] = null;\n }\n return;\n }\n\n const related = await this._chunkedIn(\n spec.model,\n \"id\",\n uniqueValues,\n spec.filter,\n { projection: spec.projection }\n );\n\n const lookupMap = new Map<string, Record<string, any>>();\n for (const r of related) {\n lookupMap.set(r.id, r);\n }\n\n for (const rec of records) {\n const fk = rec[sourceField];\n rec._related[resultKey] = (fk && lookupMap.get(fk)) || null;\n }\n }\n\n /** Resolve a refersToMany include (parent StringSet field → array of related records). */\n private async _resolveRefersToMany(\n records: Record<string, any>[],\n spec: IncludeSpec,\n resultKey: string,\n parentModelName?: string\n ): Promise<void> {\n const sourceField = spec.sourceField!;\n\n if (!parentModelName) {\n console.warn(\n `[include] refersToMany requires parentModelName, skipping`\n );\n for (const rec of records) {\n rec._related[resultKey] = [];\n }\n return;\n }\n\n // Junction table: model_{parentModelName}_{sourceField}\n const junctionTable = `model_${parentModelName.toLowerCase()}_${sourceField.toLowerCase()}`;\n const fkColumn = `${parentModelName.toLowerCase()}_id`;\n\n // Collect unique parent IDs\n const parentIds = [\n ...new Set(\n records\n .map((r) => r.id)\n .filter((v) => v != null && v !== \"\")\n ),\n ];\n\n if (parentIds.length === 0) {\n for (const rec of records) {\n rec._related[resultKey] = [];\n }\n return;\n }\n\n // Query the junction table for all StringSet values in chunks\n const junctionMap = new Map<string, string[]>();\n const chunkSize = 99; // leave room for query overhead\n\n for (let i = 0; i < parentIds.length; i += chunkSize) {\n const chunk = parentIds.slice(i, i + chunkSize);\n const inPlaceholders = chunk.map(() => \"?\").join(\", \");\n const sql = `SELECT ${quoteIdentifier(fkColumn)}, \"value\" FROM ${quoteIdentifier(junctionTable)} WHERE ${quoteIdentifier(fkColumn)} IN (${inPlaceholders})`;\n const rows = await this.db.query(sql, chunk);\n\n for (const row of rows) {\n const parentId = row[fkColumn];\n const value = row.value;\n if (!junctionMap.has(parentId)) junctionMap.set(parentId, []);\n junctionMap.get(parentId)!.push(value);\n }\n }\n\n // Collect all unique target IDs across all parents\n const allTargetIds = [...new Set(\n Array.from(junctionMap.values()).flat()\n )];\n\n if (allTargetIds.length === 0) {\n for (const rec of records) {\n rec._related[resultKey] = [];\n }\n return;\n }\n\n // Fetch all target records\n const targets = await this._chunkedIn(\n spec.model,\n \"id\",\n allTargetIds,\n spec.filter,\n { projection: spec.projection }\n );\n\n // Build lookup map\n const targetLookup = new Map<string, Record<string, any>>();\n for (const t of targets) {\n targetLookup.set(t.id, t);\n }\n\n // Assign to each parent\n for (const rec of records) {\n const targetIds = junctionMap.get(rec.id) || [];\n rec._related[resultKey] = targetIds\n .map((id) => targetLookup.get(id))\n .filter(Boolean);\n }\n }\n\n /** Resolve a hasMany include (target FK → array of related records). */\n private async _resolveHasMany(\n records: Record<string, any>[],\n spec: IncludeSpec,\n resultKey: string\n ): Promise<void> {\n const localField = spec.localField || \"id\";\n const uniqueValues = [\n ...new Set(\n records\n .map((r) => r[localField])\n .filter((v) => v != null && v !== \"\")\n ),\n ];\n\n if (uniqueValues.length === 0) {\n for (const rec of records) {\n rec._related[resultKey] = [];\n }\n return;\n }\n\n let related: Record<string, any>[];\n if (spec.limit) {\n related = await this._resolveHasManyWithLimit(spec, uniqueValues);\n } else {\n related = await this._resolveHasManySimple(spec, uniqueValues);\n }\n\n // Group by foreignKey value\n const foreignKey = spec.foreignKey!;\n const grouped = new Map<string, Record<string, any>[]>();\n for (const r of related) {\n const fk = r[foreignKey];\n if (!grouped.has(fk)) grouped.set(fk, []);\n grouped.get(fk)!.push(r);\n }\n\n // Apply projection post-grouping (foreignKey needed for grouping)\n if (spec.projection) {\n for (const [key, list] of grouped) {\n grouped.set(\n key,\n list.map((r) => this._applyProjection(r, spec.projection!))\n );\n }\n }\n\n for (const rec of records) {\n rec._related[resultKey] = grouped.get(rec[localField]) || [];\n }\n }\n\n /** Simple hasMany without per-parent limit. */\n private async _resolveHasManySimple(\n spec: IncludeSpec,\n parentValues: any[]\n ): Promise<Record<string, any>[]> {\n return this._chunkedIn(spec.model, spec.foreignKey!, parentValues, spec.filter, {\n sort: spec.sort,\n });\n }\n\n /** hasMany with per-parent limit using ROW_NUMBER(). */\n private async _resolveHasManyWithLimit(\n spec: IncludeSpec,\n parentValues: any[]\n ): Promise<Record<string, any>[]> {\n const foreignKey = spec.foreignKey!;\n assertValidIdentifier(foreignKey, \"include foreignKey\");\n\n const modelInfo = ModelRegistry.getInstance().getModelInfo(spec.model);\n if (!modelInfo) {\n console.warn(\n `[include] Target model \"${spec.model}\" not found in registry, skipping`\n );\n return [];\n }\n\n const tableName = `model_${spec.model.toLowerCase()}`;\n const quotedTable = quoteIdentifier(tableName);\n\n // Build sort clause using quoted identifiers\n let sortClause = `\"id\" ASC`;\n if (spec.sort) {\n const parts: string[] = [];\n for (const [field, dir] of Object.entries(spec.sort)) {\n assertValidIdentifier(field, \"include sort field\");\n parts.push(`${quoteIdentifier(field)} ${dir === -1 ? \"DESC\" : \"ASC\"}`);\n }\n sortClause = parts.join(\", \");\n }\n\n // Translate additional filter if present\n const translator = new DocumentQueryTranslator(\n spec.model,\n modelInfo.fields\n );\n let filterClause = \"\";\n let filterParams: any[] = [];\n if (spec.filter) {\n const translated = translator.translateFilter(spec.filter);\n if (translated.sql) {\n filterClause = ` AND ${translated.sql}`;\n filterParams = translated.params;\n }\n }\n\n // Compute chunk size: 100 - (filterParams + 1 for limit)\n const reservedParams = filterParams.length + 1;\n const chunkSize = Math.max(1, 100 - reservedParams);\n\n const allResults: Record<string, any>[] = [];\n\n for (let i = 0; i < parentValues.length; i += chunkSize) {\n const chunk = parentValues.slice(i, i + chunkSize);\n const inPlaceholders = chunk.map(() => \"?\").join(\", \");\n\n const sql = `\n SELECT * FROM (\n SELECT *,\n ROW_NUMBER() OVER (\n PARTITION BY ${quoteIdentifier(foreignKey)}\n ORDER BY ${sortClause}\n ) as _rn\n FROM ${quotedTable}\n WHERE ${quoteIdentifier(foreignKey)} IN (${inPlaceholders})\n ${filterClause}\n ) WHERE _rn <= ?\n `;\n\n const params = [...chunk, ...filterParams, spec.limit!];\n const rows = await this.db.query(sql, params);\n // Strip _rn from results\n for (const row of rows) {\n delete row._rn;\n allResults.push(row);\n }\n }\n\n return allResults;\n }\n\n /**\n * Execute IN queries in chunks to stay under SQLite's parameter limit.\n * Uses 100-param chunks for consistency with the DO backend.\n */\n private async _chunkedIn(\n model: string,\n field: string,\n values: any[],\n extraFilter?: DocumentFilter,\n extraOptions?: { sort?: SortSpec; projection?: ProjectionSpec }\n ): Promise<Record<string, any>[]> {\n const modelInfo = ModelRegistry.getInstance().getModelInfo(model);\n if (!modelInfo) {\n console.warn(\n `[include] Target model \"${model}\" not found in registry, skipping`\n );\n return [];\n }\n\n const translator = new DocumentQueryTranslator(model, modelInfo.fields);\n\n // Count params consumed by extraFilter\n let filterParamCount = 0;\n if (extraFilter) {\n const translated = translator.translateFilter(extraFilter);\n filterParamCount = translated.params.length;\n }\n\n const chunkSize = Math.max(1, 100 - filterParamCount);\n\n const allResults: Record<string, any>[] = [];\n\n for (let i = 0; i < values.length; i += chunkSize) {\n const chunk = values.slice(i, i + chunkSize);\n\n const filter: DocumentFilter = {\n [field]: { $in: chunk },\n ...(extraFilter || {}),\n };\n const options: QueryOptions = {};\n if (extraOptions?.sort) options.sort = extraOptions.sort;\n if (extraOptions?.projection) options.projection = extraOptions.projection;\n\n const { sql, params } = translator.translateFind(filter, options);\n const rows = await this.db.query(sql, params);\n allResults.push(...rows);\n }\n\n return allResults;\n }\n\n /** Validate an include spec, returning an error string or null if valid. */\n private _validateIncludeSpec(spec: IncludeSpec): string | null {\n if (!spec.model || typeof spec.model !== \"string\") {\n return \"include: 'model' is required\";\n }\n try {\n assertValidIdentifier(spec.model, \"include model\");\n } catch {\n return `include: invalid model name '${spec.model}'`;\n }\n\n if (spec.type !== \"refersTo\" && spec.type !== \"hasMany\" && spec.type !== \"refersToMany\") {\n return `include: 'type' must be 'refersTo', 'hasMany', or 'refersToMany', got '${spec.type}'`;\n }\n\n if (spec.type === \"refersTo\") {\n if (!spec.sourceField) {\n return \"include refersTo: 'sourceField' is required\";\n }\n try {\n assertValidIdentifier(spec.sourceField, \"include sourceField\");\n } catch {\n return `include refersTo: invalid sourceField '${spec.sourceField}'`;\n }\n }\n\n if (spec.type === \"refersToMany\") {\n if (!spec.sourceField) {\n return \"include refersToMany: 'sourceField' is required\";\n }\n try {\n assertValidIdentifier(spec.sourceField, \"include sourceField\");\n } catch {\n return `include refersToMany: invalid sourceField '${spec.sourceField}'`;\n }\n }\n\n if (spec.type === \"hasMany\") {\n if (!spec.foreignKey) {\n return \"include hasMany: 'foreignKey' is required\";\n }\n try {\n assertValidIdentifier(spec.foreignKey, \"include foreignKey\");\n } catch {\n return `include hasMany: invalid foreignKey '${spec.foreignKey}'`;\n }\n if (spec.localField) {\n try {\n assertValidIdentifier(spec.localField, \"include localField\");\n } catch {\n return `include hasMany: invalid localField '${spec.localField}'`;\n }\n }\n }\n\n if (spec.as) {\n try {\n assertValidIdentifier(spec.as, \"include as\");\n } catch {\n return `include: invalid alias '${spec.as}'`;\n }\n }\n\n if (spec.sort) {\n for (const field of Object.keys(spec.sort)) {\n try {\n assertValidIdentifier(field, \"include sort field\");\n } catch {\n return `include: invalid sort field '${field}'`;\n }\n }\n }\n\n return null;\n }\n\n /** Apply projection to a single record. */\n private _applyProjection(\n record: Record<string, any>,\n projection: ProjectionSpec\n ): Record<string, any> {\n const fields = Object.entries(projection);\n if (fields.length === 0) return record;\n\n const isInclusion = fields.some(([, v]) => v === 1);\n\n if (isInclusion) {\n const result: Record<string, any> = {};\n if (record.id !== undefined) result.id = record.id;\n if (record.type !== undefined) result.type = record.type;\n if (record._related !== undefined) result._related = record._related;\n for (const [f, v] of fields) {\n if (v === 1 && record[f] !== undefined) {\n result[f] = record[f];\n }\n }\n return result;\n } else {\n const result = { ...record };\n for (const [f, v] of fields) {\n if (v === 0) delete result[f];\n }\n return result;\n }\n }\n}\n"],"mappings":"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","names":[]}
|