js-bao 0.2.10 → 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.
Files changed (67) hide show
  1. package/README.md +174 -0
  2. package/dist/BaseModel-5YQCROYE.js +17 -0
  3. package/dist/BaseModel-5YQCROYE.js.map +1 -0
  4. package/dist/BaseModel-FCNWDJBH.js +17 -0
  5. package/dist/BaseModel-FCNWDJBH.js.map +1 -0
  6. package/dist/BrowserDatabaseFactory-PXOTK2DQ.js +119 -0
  7. package/dist/BrowserDatabaseFactory-PXOTK2DQ.js.map +1 -0
  8. package/dist/BrowserDatabaseFactory-WD4VX2VZ.js +119 -0
  9. package/dist/BrowserDatabaseFactory-WD4VX2VZ.js.map +1 -0
  10. package/dist/IncludeResolver-RCKQGNPZ.js +385 -0
  11. package/dist/IncludeResolver-RCKQGNPZ.js.map +1 -0
  12. package/dist/IncludeResolver-WGSQDMS7.js +385 -0
  13. package/dist/IncludeResolver-WGSQDMS7.js.map +1 -0
  14. package/dist/NodeDatabaseFactory-J4Z36UF3.js +165 -0
  15. package/dist/NodeDatabaseFactory-J4Z36UF3.js.map +1 -0
  16. package/dist/NodeDatabaseFactory-QIEKAXBM.js +10 -0
  17. package/dist/NodeDatabaseFactory-QIEKAXBM.js.map +1 -0
  18. package/dist/NodeSqliteEngine-HJSAYE4E.js +383 -0
  19. package/dist/NodeSqliteEngine-HJSAYE4E.js.map +1 -0
  20. package/dist/NodeSqliteEngine-I5SLWLME.js +383 -0
  21. package/dist/NodeSqliteEngine-I5SLWLME.js.map +1 -0
  22. package/dist/browser.cjs +3779 -3370
  23. package/dist/browser.d.cts +18 -1
  24. package/dist/browser.d.ts +18 -1
  25. package/dist/browser.js +3750 -3341
  26. package/dist/chunk-3PZWHUZO.js +4153 -0
  27. package/dist/chunk-3PZWHUZO.js.map +1 -0
  28. package/dist/chunk-53MS4MN7.js +373 -0
  29. package/dist/chunk-53MS4MN7.js.map +1 -0
  30. package/dist/chunk-65G2P4GL.js +709 -0
  31. package/dist/chunk-65G2P4GL.js.map +1 -0
  32. package/dist/chunk-6UX3YSCW.js +4151 -0
  33. package/dist/chunk-6UX3YSCW.js.map +1 -0
  34. package/dist/chunk-DANSD6BE.js +709 -0
  35. package/dist/chunk-DANSD6BE.js.map +1 -0
  36. package/dist/chunk-DF3JEQXA.js +373 -0
  37. package/dist/chunk-DF3JEQXA.js.map +1 -0
  38. package/dist/chunk-GO3APTPX.js +61 -0
  39. package/dist/chunk-GO3APTPX.js.map +1 -0
  40. package/dist/chunk-ID4U6IQC.js +53 -0
  41. package/dist/chunk-ID4U6IQC.js.map +1 -0
  42. package/dist/chunk-RQVS3LVL.js +165 -0
  43. package/dist/chunk-RQVS3LVL.js.map +1 -0
  44. package/dist/client.cjs +837 -0
  45. package/dist/client.d.cts +1101 -0
  46. package/dist/client.d.ts +1101 -0
  47. package/dist/client.js +806 -0
  48. package/dist/cloudflare-do.cjs +3637 -0
  49. package/dist/cloudflare-do.d.cts +1366 -0
  50. package/dist/cloudflare-do.d.ts +1366 -0
  51. package/dist/cloudflare-do.js +3614 -0
  52. package/dist/cloudflare.cjs +1048 -0
  53. package/dist/cloudflare.d.cts +1381 -0
  54. package/dist/cloudflare.d.ts +1381 -0
  55. package/dist/cloudflare.js +1017 -0
  56. package/dist/codegen.cjs +259 -18
  57. package/dist/environment-TOTQICSE.js +17 -0
  58. package/dist/environment-TOTQICSE.js.map +1 -0
  59. package/dist/index.cjs +1906 -1493
  60. package/dist/index.d.cts +19 -2
  61. package/dist/index.d.ts +19 -2
  62. package/dist/index.js +1871 -1458
  63. package/dist/node.cjs +4779 -4366
  64. package/dist/node.d.cts +18 -1
  65. package/dist/node.d.ts +18 -1
  66. package/dist/node.js +4602 -4189
  67. package/package.json +41 -12
@@ -0,0 +1,385 @@
1
+ import {
2
+ ModelRegistry
3
+ } from "./chunk-DANSD6BE.js";
4
+ import {
5
+ DocumentQueryTranslator,
6
+ assertValidIdentifier,
7
+ quoteIdentifier
8
+ } from "./chunk-6UX3YSCW.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-WGSQDMS7.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":[]}
@@ -0,0 +1,165 @@
1
+ import {
2
+ SqljsEngine
3
+ } from "./chunk-DF3JEQXA.js";
4
+ import {
5
+ features
6
+ } from "./chunk-ID4U6IQC.js";
7
+ import "./chunk-3PZWHUZO.js";
8
+
9
+ // src/engines/NodeDatabaseFactory.ts
10
+ var NodeSqliteEngine = null;
11
+ var NodeDatabaseFactory = class {
12
+ static engines = /* @__PURE__ */ new Map();
13
+ /**
14
+ * Dynamically loads Node.js engines
15
+ */
16
+ static async loadNodeEngines() {
17
+ try {
18
+ const hasBetterSqlite3 = await features.hasBetterSqlite3();
19
+ if (hasBetterSqlite3 && !NodeSqliteEngine) {
20
+ const module = await import("./NodeSqliteEngine-HJSAYE4E.js");
21
+ NodeSqliteEngine = module.NodeSqliteEngine;
22
+ console.log(
23
+ "[NodeDatabaseFactory] NodeSqliteEngine loaded successfully"
24
+ );
25
+ }
26
+ } catch (error) {
27
+ console.warn(
28
+ "[NodeDatabaseFactory] Failed to load NodeSqliteEngine:",
29
+ error
30
+ );
31
+ }
32
+ }
33
+ /**
34
+ * Auto-detects the best available engine for Node.js
35
+ */
36
+ static async getRecommendedEngineType() {
37
+ const hasBetterSqlite3 = await features.hasBetterSqlite3();
38
+ if (hasBetterSqlite3) {
39
+ return "node-sqlite";
40
+ }
41
+ if (features.hasWebAssembly) {
42
+ return "sqljs";
43
+ }
44
+ throw new Error(
45
+ "No compatible database engine found. Please install better-sqlite3."
46
+ );
47
+ }
48
+ /**
49
+ * Creates a fallback engine when the requested engine is not available
50
+ */
51
+ static async createFallbackEngine(requestedType, config) {
52
+ const fallbackType = await this.getRecommendedEngineType();
53
+ console.warn(
54
+ `[NodeDatabaseFactory] Requested engine '${requestedType}' is not available. Falling back to '${fallbackType}'.`
55
+ );
56
+ const fallbackConfig = { ...config, type: fallbackType };
57
+ return this.createEngine(fallbackConfig, false);
58
+ }
59
+ /**
60
+ * Creates the specified database engine (Node.js compatible only)
61
+ */
62
+ static async createEngine(config, allowFallback = true) {
63
+ const { type, options } = config;
64
+ switch (type) {
65
+ case "sqljs":
66
+ const sqljsEngine = new SqljsEngine(options);
67
+ await sqljsEngine.ensureReady();
68
+ return sqljsEngine;
69
+ case "node-sqlite":
70
+ if (!NodeSqliteEngine) {
71
+ const hasBetterSqlite3 = await features.hasBetterSqlite3();
72
+ if (!hasBetterSqlite3) {
73
+ if (allowFallback) {
74
+ console.warn(
75
+ "[NodeDatabaseFactory] better-sqlite3 not installed, falling back to alternative engine"
76
+ );
77
+ return this.createFallbackEngine(type, config);
78
+ }
79
+ throw new Error(
80
+ "better-sqlite3 package is required for node-sqlite engine. Install it with: npm install better-sqlite3"
81
+ );
82
+ }
83
+ throw new Error(
84
+ "NodeSqliteEngine not loaded. This should not happen."
85
+ );
86
+ }
87
+ const nodeSqliteEngine = new NodeSqliteEngine(options);
88
+ await nodeSqliteEngine.ensureReady();
89
+ return nodeSqliteEngine;
90
+ case "alasql":
91
+ throw new Error("AlaSQLEngine not yet implemented.");
92
+ default:
93
+ const typeStr = type;
94
+ if (typeStr === "duckdb") {
95
+ if (allowFallback) {
96
+ console.warn(
97
+ "[NodeDatabaseFactory] DuckDB-WASM not available in Node.js, falling back to alternative engine"
98
+ );
99
+ return this.createFallbackEngine(typeStr, config);
100
+ }
101
+ throw new Error(
102
+ "DuckDB-WASM engine is only available in browser environments"
103
+ );
104
+ }
105
+ if (typeStr === "node-duckdb") {
106
+ if (allowFallback) {
107
+ console.warn(
108
+ "[NodeDatabaseFactory] Node DuckDB engine is no longer supported, falling back to alternative engine"
109
+ );
110
+ return this.createFallbackEngine(typeStr, config);
111
+ }
112
+ throw new Error("Node DuckDB engine is no longer supported");
113
+ }
114
+ throw new Error(`Unsupported database engine type: ${typeStr}`);
115
+ }
116
+ }
117
+ static async getEngine(config) {
118
+ await this.loadNodeEngines();
119
+ const configKey = JSON.stringify(config);
120
+ if (this.engines.has(configKey)) {
121
+ console.log(
122
+ `[NodeDatabaseFactory] Returning cached engine for config: ${configKey}`
123
+ );
124
+ }
125
+ console.log(
126
+ `[NodeDatabaseFactory] Creating engine for type: ${config.type}`
127
+ );
128
+ try {
129
+ const engine = await this.createEngine(config);
130
+ console.log(
131
+ `[NodeDatabaseFactory] Engine for type ${config.type} is ready.`
132
+ );
133
+ return engine;
134
+ } catch (error) {
135
+ console.error(
136
+ `[NodeDatabaseFactory] Failed to create engine for type ${config.type}:`,
137
+ error
138
+ );
139
+ throw error;
140
+ }
141
+ }
142
+ /**
143
+ * Gets information about available engines in Node.js environment
144
+ */
145
+ static async getAvailableEngines() {
146
+ const hasBetterSqlite3 = await features.hasBetterSqlite3();
147
+ const engines = [
148
+ {
149
+ type: "sqljs",
150
+ available: features.hasWebAssembly,
151
+ reason: !features.hasWebAssembly ? "WebAssembly not supported" : void 0
152
+ },
153
+ {
154
+ type: "node-sqlite",
155
+ available: hasBetterSqlite3,
156
+ reason: !hasBetterSqlite3 ? "better-sqlite3 package not installed" : void 0
157
+ }
158
+ ];
159
+ return engines;
160
+ }
161
+ };
162
+ export {
163
+ NodeDatabaseFactory
164
+ };
165
+ //# sourceMappingURL=NodeDatabaseFactory-J4Z36UF3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/engines/NodeDatabaseFactory.ts"],"sourcesContent":["import { DatabaseConfig } from \"../types/databaseTypes\";\nimport { DatabaseEngine } from \"./DatabaseEngine\";\nimport { SqljsEngine } from \"./SqljsEngine\";\nimport { features } from \"../utils/environment\";\n\n// Dynamic imports for Node.js engines only\nlet NodeSqliteEngine: any = null;\n\nexport class NodeDatabaseFactory {\n private static engines: Map<string, DatabaseEngine> = new Map();\n\n /**\n * Dynamically loads Node.js engines\n */\n private static async loadNodeEngines() {\n try {\n // Try to load Node.js SQLite engine\n const hasBetterSqlite3 = await features.hasBetterSqlite3();\n if (hasBetterSqlite3 && !NodeSqliteEngine) {\n const module = await import(\"./node/NodeSqliteEngine\");\n NodeSqliteEngine = module.NodeSqliteEngine;\n console.log(\n \"[NodeDatabaseFactory] NodeSqliteEngine loaded successfully\"\n );\n }\n } catch (error) {\n console.warn(\n \"[NodeDatabaseFactory] Failed to load NodeSqliteEngine:\",\n error\n );\n }\n }\n\n /**\n * Auto-detects the best available engine for Node.js\n */\n private static async getRecommendedEngineType(): Promise<string> {\n // In Node.js, prefer native engines\n const hasBetterSqlite3 = await features.hasBetterSqlite3();\n if (hasBetterSqlite3) {\n return \"node-sqlite\";\n }\n\n // Fallback to WASM engines\n if (features.hasWebAssembly) {\n return \"sqljs\";\n }\n\n throw new Error(\n \"No compatible database engine found. Please install better-sqlite3.\"\n );\n }\n\n /**\n * Creates a fallback engine when the requested engine is not available\n */\n private static async createFallbackEngine(\n requestedType: string,\n config: DatabaseConfig\n ): Promise<DatabaseEngine> {\n const fallbackType = await this.getRecommendedEngineType();\n\n console.warn(\n `[NodeDatabaseFactory] Requested engine '${requestedType}' is not available. ` +\n `Falling back to '${fallbackType}'.`\n );\n\n const fallbackConfig = { ...config, type: fallbackType as any };\n return this.createEngine(fallbackConfig, false); // Prevent infinite recursion\n }\n\n /**\n * Creates the specified database engine (Node.js compatible only)\n */\n private static async createEngine(\n config: DatabaseConfig,\n allowFallback: boolean = true\n ): Promise<DatabaseEngine> {\n const { type, options } = config;\n\n switch (type) {\n case \"sqljs\":\n const sqljsEngine = new SqljsEngine(options as any);\n await sqljsEngine.ensureReady();\n return sqljsEngine;\n\n case \"node-sqlite\":\n if (!NodeSqliteEngine) {\n const hasBetterSqlite3 = await features.hasBetterSqlite3();\n if (!hasBetterSqlite3) {\n if (allowFallback) {\n console.warn(\n \"[NodeDatabaseFactory] better-sqlite3 not installed, falling back to alternative engine\"\n );\n return this.createFallbackEngine(type, config);\n }\n throw new Error(\n \"better-sqlite3 package is required for node-sqlite engine. Install it with: npm install better-sqlite3\"\n );\n }\n throw new Error(\n \"NodeSqliteEngine not loaded. This should not happen.\"\n );\n }\n\n const nodeSqliteEngine = new NodeSqliteEngine(options);\n await nodeSqliteEngine.ensureReady();\n return nodeSqliteEngine;\n\n case \"alasql\":\n throw new Error(\"AlaSQLEngine not yet implemented.\");\n\n default:\n // Handle removed engine types and unsupported types\n const typeStr = type as string;\n\n if (typeStr === \"duckdb\") {\n if (allowFallback) {\n console.warn(\n \"[NodeDatabaseFactory] DuckDB-WASM not available in Node.js, falling back to alternative engine\"\n );\n return this.createFallbackEngine(typeStr, config);\n }\n throw new Error(\n \"DuckDB-WASM engine is only available in browser environments\"\n );\n }\n\n if (typeStr === \"node-duckdb\") {\n if (allowFallback) {\n console.warn(\n \"[NodeDatabaseFactory] Node DuckDB engine is no longer supported, falling back to alternative engine\"\n );\n return this.createFallbackEngine(typeStr, config);\n }\n throw new Error(\"Node DuckDB engine is no longer supported\");\n }\n\n throw new Error(`Unsupported database engine type: ${typeStr}`);\n }\n }\n\n public static async getEngine(\n config: DatabaseConfig\n ): Promise<DatabaseEngine> {\n // Load Node.js engines\n await this.loadNodeEngines();\n\n const configKey = JSON.stringify(config);\n\n // For development, we don't cache engines to avoid issues with hot reloading\n if (this.engines.has(configKey)) {\n console.log(\n `[NodeDatabaseFactory] Returning cached engine for config: ${configKey}`\n );\n // return this.engines.get(configKey)!;\n }\n\n console.log(\n `[NodeDatabaseFactory] Creating engine for type: ${config.type}`\n );\n\n try {\n const engine = await this.createEngine(config);\n\n // Cache the engine (optional)\n // this.engines.set(configKey, engine);\n\n console.log(\n `[NodeDatabaseFactory] Engine for type ${config.type} is ready.`\n );\n return engine;\n } catch (error) {\n console.error(\n `[NodeDatabaseFactory] Failed to create engine for type ${config.type}:`,\n error\n );\n throw error;\n }\n }\n\n /**\n * Gets information about available engines in Node.js environment\n */\n public static async getAvailableEngines(): Promise<\n {\n type: string;\n available: boolean;\n reason?: string;\n }[]\n > {\n const hasBetterSqlite3 = await features.hasBetterSqlite3();\n\n const engines = [\n {\n type: \"sqljs\",\n available: features.hasWebAssembly,\n reason: !features.hasWebAssembly\n ? \"WebAssembly not supported\"\n : undefined,\n },\n {\n type: \"node-sqlite\",\n available: hasBetterSqlite3,\n reason: !hasBetterSqlite3\n ? \"better-sqlite3 package not installed\"\n : undefined,\n },\n ];\n\n return engines;\n }\n}\n"],"mappings":";;;;;;;;;AAMA,IAAI,mBAAwB;AAErB,IAAM,sBAAN,MAA0B;AAAA,EAC/B,OAAe,UAAuC,oBAAI,IAAI;AAAA;AAAA;AAAA;AAAA,EAK9D,aAAqB,kBAAkB;AACrC,QAAI;AAEF,YAAM,mBAAmB,MAAM,SAAS,iBAAiB;AACzD,UAAI,oBAAoB,CAAC,kBAAkB;AACzC,cAAM,SAAS,MAAM,OAAO,gCAAyB;AACrD,2BAAmB,OAAO;AAC1B,gBAAQ;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ;AAAA,QACN;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB,2BAA4C;AAE/D,UAAM,mBAAmB,MAAM,SAAS,iBAAiB;AACzD,QAAI,kBAAkB;AACpB,aAAO;AAAA,IACT;AAGA,QAAI,SAAS,gBAAgB;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB,qBACnB,eACA,QACyB;AACzB,UAAM,eAAe,MAAM,KAAK,yBAAyB;AAEzD,YAAQ;AAAA,MACN,2CAA2C,aAAa,wCAClC,YAAY;AAAA,IACpC;AAEA,UAAM,iBAAiB,EAAE,GAAG,QAAQ,MAAM,aAAoB;AAC9D,WAAO,KAAK,aAAa,gBAAgB,KAAK;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB,aACnB,QACA,gBAAyB,MACA;AACzB,UAAM,EAAE,MAAM,QAAQ,IAAI;AAE1B,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,cAAM,cAAc,IAAI,YAAY,OAAc;AAClD,cAAM,YAAY,YAAY;AAC9B,eAAO;AAAA,MAET,KAAK;AACH,YAAI,CAAC,kBAAkB;AACrB,gBAAM,mBAAmB,MAAM,SAAS,iBAAiB;AACzD,cAAI,CAAC,kBAAkB;AACrB,gBAAI,eAAe;AACjB,sBAAQ;AAAA,gBACN;AAAA,cACF;AACA,qBAAO,KAAK,qBAAqB,MAAM,MAAM;AAAA,YAC/C;AACA,kBAAM,IAAI;AAAA,cACR;AAAA,YACF;AAAA,UACF;AACA,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,cAAM,mBAAmB,IAAI,iBAAiB,OAAO;AACrD,cAAM,iBAAiB,YAAY;AACnC,eAAO;AAAA,MAET,KAAK;AACH,cAAM,IAAI,MAAM,mCAAmC;AAAA,MAErD;AAEE,cAAM,UAAU;AAEhB,YAAI,YAAY,UAAU;AACxB,cAAI,eAAe;AACjB,oBAAQ;AAAA,cACN;AAAA,YACF;AACA,mBAAO,KAAK,qBAAqB,SAAS,MAAM;AAAA,UAClD;AACA,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,YAAI,YAAY,eAAe;AAC7B,cAAI,eAAe;AACjB,oBAAQ;AAAA,cACN;AAAA,YACF;AACA,mBAAO,KAAK,qBAAqB,SAAS,MAAM;AAAA,UAClD;AACA,gBAAM,IAAI,MAAM,2CAA2C;AAAA,QAC7D;AAEA,cAAM,IAAI,MAAM,qCAAqC,OAAO,EAAE;AAAA,IAClE;AAAA,EACF;AAAA,EAEA,aAAoB,UAClB,QACyB;AAEzB,UAAM,KAAK,gBAAgB;AAE3B,UAAM,YAAY,KAAK,UAAU,MAAM;AAGvC,QAAI,KAAK,QAAQ,IAAI,SAAS,GAAG;AAC/B,cAAQ;AAAA,QACN,6DAA6D,SAAS;AAAA,MACxE;AAAA,IAEF;AAEA,YAAQ;AAAA,MACN,mDAAmD,OAAO,IAAI;AAAA,IAChE;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,aAAa,MAAM;AAK7C,cAAQ;AAAA,QACN,yCAAyC,OAAO,IAAI;AAAA,MACtD;AACA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,cAAQ;AAAA,QACN,0DAA0D,OAAO,IAAI;AAAA,QACrE;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAoB,sBAMlB;AACA,UAAM,mBAAmB,MAAM,SAAS,iBAAiB;AAEzD,UAAM,UAAU;AAAA,MACd;AAAA,QACE,MAAM;AAAA,QACN,WAAW,SAAS;AAAA,QACpB,QAAQ,CAAC,SAAS,iBACd,8BACA;AAAA,MACN;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,WAAW;AAAA,QACX,QAAQ,CAAC,mBACL,yCACA;AAAA,MACN;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,10 @@
1
+ import {
2
+ NodeDatabaseFactory
3
+ } from "./chunk-RQVS3LVL.js";
4
+ import "./chunk-53MS4MN7.js";
5
+ import "./chunk-GO3APTPX.js";
6
+ import "./chunk-6UX3YSCW.js";
7
+ export {
8
+ NodeDatabaseFactory
9
+ };
10
+ //# sourceMappingURL=NodeDatabaseFactory-QIEKAXBM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}