@contentrain/query 3.2.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +246 -268
- package/dist/cli.cjs +78 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +81 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/generate-C5Qz8qKt.mjs +855 -0
- package/dist/generate-C5Qz8qKt.mjs.map +1 -0
- package/dist/generate-CPKYh6ZU.cjs +858 -0
- package/dist/generator/generate.cjs +3 -0
- package/dist/generator/generate.d.cts +14 -0
- package/dist/generator/generate.d.cts.map +1 -0
- package/dist/generator/generate.d.mts +14 -0
- package/dist/generator/generate.d.mts.map +1 -0
- package/dist/generator/generate.mjs +2 -0
- package/dist/index.cjs +356 -0
- package/dist/index.d.cts +108 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +107 -290
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +343 -859
- package/dist/index.mjs.map +1 -1
- package/package.json +46 -29
- package/dist/index.d.ts +0 -291
- package/dist/index.js +0 -872
- package/dist/index.js.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,865 +1,349 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.stats = {
|
|
122
|
-
hits: 0,
|
|
123
|
-
misses: 0,
|
|
124
|
-
size: 0,
|
|
125
|
-
lastCleanup: Date.now()
|
|
126
|
-
};
|
|
127
|
-
logger.debug("Cache cleared");
|
|
128
|
-
}
|
|
129
|
-
async cleanupCache() {
|
|
130
|
-
const now = Date.now();
|
|
131
|
-
const expiredKeys = [];
|
|
132
|
-
let totalSize = 0;
|
|
133
|
-
for (const key of this.cache.keys()) {
|
|
134
|
-
const entry = this.cache.get(key);
|
|
135
|
-
if (entry.expireAt <= now) {
|
|
136
|
-
expiredKeys.push(key);
|
|
137
|
-
} else {
|
|
138
|
-
totalSize += entry.size;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
for (const key of expiredKeys) {
|
|
142
|
-
await this.delete(key);
|
|
143
|
-
}
|
|
144
|
-
while (totalSize > this.options.maxSize * 1024 * 1024) {
|
|
145
|
-
const oldestKey = this.findOldestKey();
|
|
146
|
-
if (!oldestKey)
|
|
147
|
-
break;
|
|
148
|
-
const entry = this.cache.get(oldestKey);
|
|
149
|
-
await this.delete(oldestKey);
|
|
150
|
-
totalSize -= entry.size;
|
|
151
|
-
}
|
|
152
|
-
this.stats.lastCleanup = now;
|
|
153
|
-
}
|
|
154
|
-
getStats() {
|
|
155
|
-
return { ...this.stats };
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
__name(_MemoryCache, "MemoryCache");
|
|
159
|
-
var MemoryCache = _MemoryCache;
|
|
160
|
-
|
|
161
|
-
// src/loader/content.ts
|
|
162
|
-
var _ContentLoader = class _ContentLoader {
|
|
163
|
-
constructor(options) {
|
|
164
|
-
this.modelConfigs = /* @__PURE__ */ new Map();
|
|
165
|
-
this.relations = /* @__PURE__ */ new Map();
|
|
166
|
-
this.options = {
|
|
167
|
-
defaultLocale: "en",
|
|
168
|
-
cache: true,
|
|
169
|
-
ttl: 60 * 1e3,
|
|
170
|
-
// 1 minute
|
|
171
|
-
maxCacheSize: 100,
|
|
172
|
-
// 100 MB
|
|
173
|
-
...options
|
|
174
|
-
};
|
|
175
|
-
this.cache = new MemoryCache({
|
|
176
|
-
maxSize: this.options.maxCacheSize,
|
|
177
|
-
defaultTTL: this.options.ttl
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
getCacheKey(model) {
|
|
181
|
-
return `${model}`;
|
|
182
|
-
}
|
|
183
|
-
getModelTTL(model) {
|
|
184
|
-
return this.options.modelTTL?.[model] || this.options.ttl || 0;
|
|
185
|
-
}
|
|
186
|
-
async clearCache() {
|
|
187
|
-
await this.cache.clear();
|
|
188
|
-
}
|
|
189
|
-
async refreshCache(model) {
|
|
190
|
-
const cacheKey = this.getCacheKey(model);
|
|
191
|
-
await this.cache.delete(cacheKey);
|
|
192
|
-
await this.load(model);
|
|
193
|
-
}
|
|
194
|
-
getCacheStats() {
|
|
195
|
-
return this.cache.getStats();
|
|
196
|
-
}
|
|
197
|
-
async loadModelConfig(model) {
|
|
198
|
-
try {
|
|
199
|
-
const metadataPath = join(this.options.contentDir, "models", "metadata.json");
|
|
200
|
-
const metadataContent = await readFile(metadataPath, "utf-8");
|
|
201
|
-
const allMetadata = JSON.parse(metadataContent);
|
|
202
|
-
const modelMetadata = allMetadata.find((m) => m.modelId === model);
|
|
203
|
-
if (!modelMetadata) {
|
|
204
|
-
logger.error("Model metadata not found:", {
|
|
205
|
-
model,
|
|
206
|
-
metadataPath
|
|
207
|
-
});
|
|
208
|
-
throw new Error(`Model metadata not found for ${model}`);
|
|
209
|
-
}
|
|
210
|
-
const modelPath = join(this.options.contentDir, "models", `${model}.json`);
|
|
211
|
-
const modelContent = await readFile(modelPath, "utf-8");
|
|
212
|
-
const modelFields = JSON.parse(modelContent);
|
|
213
|
-
return {
|
|
214
|
-
metadata: modelMetadata,
|
|
215
|
-
fields: modelFields
|
|
216
|
-
};
|
|
217
|
-
} catch (error) {
|
|
218
|
-
logger.error("Failed to load model config:", {
|
|
219
|
-
model,
|
|
220
|
-
error: error?.message || "Unknown error"
|
|
221
|
-
});
|
|
222
|
-
throw new Error(`Failed to load model config for ${model}: ${error?.message || "Unknown error"}`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
async loadContentFile(model, locale = "default") {
|
|
226
|
-
try {
|
|
227
|
-
const modelConfig = await this.loadModelConfig(model);
|
|
228
|
-
let contentPath;
|
|
229
|
-
if (modelConfig.metadata.localization) {
|
|
230
|
-
if (!locale || locale === "default") {
|
|
231
|
-
if (!this.options.defaultLocale) {
|
|
232
|
-
logger.error("Default locale is required for localized model:", {
|
|
233
|
-
model
|
|
234
|
-
});
|
|
235
|
-
throw new Error(`Default locale is required for localized model "${model}"`);
|
|
236
|
-
}
|
|
237
|
-
locale = this.options.defaultLocale;
|
|
238
|
-
}
|
|
239
|
-
contentPath = join(this.options.contentDir, model, `${locale}.json`);
|
|
240
|
-
} else {
|
|
241
|
-
if (locale !== "default") {
|
|
242
|
-
console.warn(`Locale "${locale}" specified for non-localized model "${model}". This parameter will be ignored.`);
|
|
243
|
-
}
|
|
244
|
-
contentPath = join(this.options.contentDir, model, `${model}.json`);
|
|
245
|
-
}
|
|
246
|
-
const content = await readFile(contentPath, "utf-8");
|
|
247
|
-
try {
|
|
248
|
-
const data = JSON.parse(content);
|
|
249
|
-
return {
|
|
250
|
-
model,
|
|
251
|
-
locale: modelConfig.metadata.localization ? locale : void 0,
|
|
252
|
-
data
|
|
253
|
-
};
|
|
254
|
-
} catch {
|
|
255
|
-
logger.error("Failed to load content:", {
|
|
256
|
-
model,
|
|
257
|
-
locale,
|
|
258
|
-
contentPath
|
|
259
|
-
});
|
|
260
|
-
throw new Error(`Failed to load content: Invalid JSON format in ${contentPath}`);
|
|
261
|
-
}
|
|
262
|
-
} catch (error) {
|
|
263
|
-
if (error.message.includes("Invalid JSON format")) {
|
|
264
|
-
logger.error("Failed to load content:", {
|
|
265
|
-
model,
|
|
266
|
-
locale
|
|
267
|
-
});
|
|
268
|
-
throw error;
|
|
269
|
-
}
|
|
270
|
-
throw new Error(
|
|
271
|
-
`Failed to load content file for ${model}${locale ? ` (${locale})` : ""}: ${error?.message || "Unknown error"}`
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
async loadRelations(model) {
|
|
276
|
-
try {
|
|
277
|
-
const modelConfig = this.modelConfigs.get(model);
|
|
278
|
-
if (!modelConfig) {
|
|
279
|
-
throw new Error(`Model config not found for ${model}`);
|
|
280
|
-
}
|
|
281
|
-
const relationFields = modelConfig.fields.filter((field) => {
|
|
282
|
-
return field.fieldType === "relation";
|
|
283
|
-
});
|
|
284
|
-
return relationFields.map((field) => {
|
|
285
|
-
const options = field.options;
|
|
286
|
-
const reference = options?.reference?.form?.reference?.value;
|
|
287
|
-
if (!reference) {
|
|
288
|
-
throw new Error(`Reference not found for relation field: ${field.name}`);
|
|
289
|
-
}
|
|
290
|
-
return {
|
|
291
|
-
model: reference,
|
|
292
|
-
type: field.componentId === "one-to-one" ? "one-to-one" : "one-to-many",
|
|
293
|
-
foreignKey: field.fieldId
|
|
294
|
-
};
|
|
295
|
-
});
|
|
296
|
-
} catch (error) {
|
|
297
|
-
throw new Error(`Failed to load relations for ${model}: ${error?.message || "Unknown error"}`);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
async getModelLocales(model, modelConfig) {
|
|
301
|
-
try {
|
|
302
|
-
if (!modelConfig.metadata.localization) {
|
|
303
|
-
return ["default"];
|
|
304
|
-
}
|
|
305
|
-
const modelDir = join(this.options.contentDir, model);
|
|
306
|
-
const files = await readdir(modelDir);
|
|
307
|
-
const locales = files.filter((file) => file.endsWith(".json")).map((file) => file.replace(".json", "")).filter((locale) => locale !== model);
|
|
308
|
-
if (locales.length === 0) {
|
|
309
|
-
if (!this.options.defaultLocale) {
|
|
310
|
-
throw new Error(`No locale files found for localized model "${model}" and no default locale specified`);
|
|
311
|
-
}
|
|
312
|
-
return [this.options.defaultLocale];
|
|
313
|
-
}
|
|
314
|
-
return locales;
|
|
315
|
-
} catch (error) {
|
|
316
|
-
if (!this.options.defaultLocale) {
|
|
317
|
-
throw new Error(`Failed to read locales for model ${model} and no default locale specified: ${error?.message}`);
|
|
318
|
-
}
|
|
319
|
-
console.warn(`Failed to read locales for model ${model}: ${error?.message}`);
|
|
320
|
-
return [this.options.defaultLocale];
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
async load(model) {
|
|
324
|
-
const cacheKey = `${model}`;
|
|
325
|
-
if (this.options.cache) {
|
|
326
|
-
const cached = await this.cache.get(cacheKey);
|
|
327
|
-
if (cached)
|
|
328
|
-
return cached;
|
|
329
|
-
}
|
|
330
|
-
const modelConfig = await this.loadModelConfig(model);
|
|
331
|
-
this.modelConfigs.set(model, modelConfig);
|
|
332
|
-
const relations = await this.loadRelations(model);
|
|
333
|
-
this.relations.set(model, relations);
|
|
334
|
-
const content = {};
|
|
335
|
-
if (modelConfig.metadata.localization) {
|
|
336
|
-
const locales = await this.getModelLocales(model, modelConfig);
|
|
337
|
-
for (const locale of locales) {
|
|
338
|
-
try {
|
|
339
|
-
const file = await this.loadContentFile(model, locale);
|
|
340
|
-
content[locale] = file.data;
|
|
341
|
-
} catch (error) {
|
|
342
|
-
console.warn(`Failed to load content for locale ${locale}: ${error?.message}`);
|
|
343
|
-
if (locale === this.options.defaultLocale) {
|
|
344
|
-
throw error;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
} else {
|
|
349
|
-
const file = await this.loadContentFile(model);
|
|
350
|
-
content.default = file.data;
|
|
351
|
-
}
|
|
352
|
-
let assets = [];
|
|
353
|
-
try {
|
|
354
|
-
const assetsPath = join(this.options.contentDir, "assets.json");
|
|
355
|
-
const assetsContent = await readFile(assetsPath, "utf-8");
|
|
356
|
-
assets = JSON.parse(assetsContent);
|
|
357
|
-
} catch (error) {
|
|
358
|
-
console.warn("Assets file not found or cannot be read:", error);
|
|
359
|
-
}
|
|
360
|
-
const result = {
|
|
361
|
-
model: modelConfig,
|
|
362
|
-
content,
|
|
363
|
-
assets
|
|
364
|
-
};
|
|
365
|
-
if (this.options.cache) {
|
|
366
|
-
const ttl = this.getModelTTL(model);
|
|
367
|
-
await this.cache.set(cacheKey, result, ttl);
|
|
368
|
-
}
|
|
369
|
-
return result;
|
|
370
|
-
}
|
|
371
|
-
async resolveRelation(model, relationField, data, locale) {
|
|
372
|
-
try {
|
|
373
|
-
logger.debug("Debug - Starting relation resolution:", {
|
|
374
|
-
model,
|
|
375
|
-
relationField,
|
|
376
|
-
dataLength: data.length,
|
|
377
|
-
locale
|
|
378
|
-
});
|
|
379
|
-
const relations = this.relations.get(model);
|
|
380
|
-
logger.debug("Debug - Relations:", relations);
|
|
381
|
-
if (!relations)
|
|
382
|
-
throw new Error(`No relations found for model: ${model}`);
|
|
383
|
-
const relation = relations.find((r) => r.foreignKey === relationField);
|
|
384
|
-
logger.debug("Debug - Found relation:", relation);
|
|
385
|
-
if (!relation)
|
|
386
|
-
throw new Error(`No relation found for field: ${String(relationField)}`);
|
|
387
|
-
logger.debug("Debug - Related model loading:", relation.model);
|
|
388
|
-
const relatedContent = await this.load(relation.model);
|
|
389
|
-
logger.debug("Debug - \u0130li\u015Fkili model y\xFCklendi:", {
|
|
390
|
-
model: relation.model,
|
|
391
|
-
metadata: relatedContent.model.metadata,
|
|
392
|
-
contentKeys: Object.keys(relatedContent.content)
|
|
393
|
-
});
|
|
394
|
-
let relatedData;
|
|
395
|
-
if (relatedContent.model.metadata.localization) {
|
|
396
|
-
logger.debug("Debug - Processing localized model");
|
|
397
|
-
const localizedContent = locale ? relatedContent.content[locale] : relatedContent.content.en;
|
|
398
|
-
logger.debug("Debug - Localized content:", {
|
|
399
|
-
locale: locale || "en",
|
|
400
|
-
contentType: typeof localizedContent,
|
|
401
|
-
isArray: Array.isArray(localizedContent)
|
|
402
|
-
});
|
|
403
|
-
if (!Array.isArray(localizedContent)) {
|
|
404
|
-
throw new TypeError(`Invalid content format for localized model ${relation.model}`);
|
|
405
|
-
}
|
|
406
|
-
relatedData = localizedContent;
|
|
407
|
-
} else {
|
|
408
|
-
logger.debug("Debug - Processing non-localized model");
|
|
409
|
-
const nonLocalizedContent = relatedContent.content.default;
|
|
410
|
-
logger.debug("Debug - Raw content:", {
|
|
411
|
-
contentType: typeof nonLocalizedContent,
|
|
412
|
-
isArray: Array.isArray(nonLocalizedContent),
|
|
413
|
-
content: nonLocalizedContent
|
|
414
|
-
});
|
|
415
|
-
if (!Array.isArray(nonLocalizedContent)) {
|
|
416
|
-
throw new TypeError(`Invalid content format for non-localized model ${relation.model}`);
|
|
417
|
-
}
|
|
418
|
-
relatedData = nonLocalizedContent;
|
|
419
|
-
}
|
|
420
|
-
logger.debug("Debug - Related data ready:", {
|
|
421
|
-
dataLength: relatedData.length,
|
|
422
|
-
firstItem: relatedData[0]
|
|
423
|
-
});
|
|
424
|
-
if (!relatedData) {
|
|
425
|
-
throw new Error(`Failed to resolve relation: No data found for model ${relation.model}`);
|
|
426
|
-
}
|
|
427
|
-
if (relation.type === "one-to-one") {
|
|
428
|
-
logger.debug("Debug - Processing one-to-one relation");
|
|
429
|
-
const itemsWithRelation = data.filter((item) => item[relationField] !== void 0);
|
|
430
|
-
logger.debug("Debug - Items with relations:", itemsWithRelation.length);
|
|
431
|
-
return itemsWithRelation.map((item) => {
|
|
432
|
-
const relatedItem = relatedData.find((r) => r.ID === item[relationField]);
|
|
433
|
-
if (!relatedItem) {
|
|
434
|
-
throw new Error(`Failed to resolve relation: No matching item found for ID ${String(item[relationField])}`);
|
|
435
|
-
}
|
|
436
|
-
return relatedItem;
|
|
437
|
-
});
|
|
438
|
-
} else {
|
|
439
|
-
logger.debug("Debug - Processing one-to-many relation");
|
|
440
|
-
const uniqueIds = new Set(
|
|
441
|
-
data.flatMap(
|
|
442
|
-
(item) => item[relationField] !== void 0 ? Array.isArray(item[relationField]) ? item[relationField] : [item[relationField]] : []
|
|
443
|
-
)
|
|
444
|
-
);
|
|
445
|
-
logger.debug("Debug - Unique IDs:", Array.from(uniqueIds));
|
|
446
|
-
const items = Array.from(uniqueIds).map((id) => relatedData.find((r) => r.ID === id)).filter(Boolean);
|
|
447
|
-
logger.debug("Debug - Matching items:", items.length);
|
|
448
|
-
if (items.length !== uniqueIds.size) {
|
|
449
|
-
throw new Error("Failed to resolve relation: Some related items not found");
|
|
450
|
-
}
|
|
451
|
-
return items;
|
|
452
|
-
}
|
|
453
|
-
} catch (error) {
|
|
454
|
-
logger.error("Debug - Error occurred:", error);
|
|
455
|
-
throw new Error(`Failed to resolve relation: ${error.message}`);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
1
|
+
//#region src/runtime/query.ts
|
|
2
|
+
var QueryBuilder = class {
|
|
3
|
+
_data;
|
|
4
|
+
_locale = null;
|
|
5
|
+
_filters = [];
|
|
6
|
+
_sortField = null;
|
|
7
|
+
_sortOrder = "asc";
|
|
8
|
+
_limit = null;
|
|
9
|
+
_offset = 0;
|
|
10
|
+
_includes = [];
|
|
11
|
+
_relationMeta;
|
|
12
|
+
_resolver;
|
|
13
|
+
_defaultLocale;
|
|
14
|
+
constructor(data, relationMeta, resolver, defaultLocale) {
|
|
15
|
+
this._data = data;
|
|
16
|
+
this._relationMeta = relationMeta ?? {};
|
|
17
|
+
this._resolver = resolver ?? null;
|
|
18
|
+
this._defaultLocale = defaultLocale ?? null;
|
|
19
|
+
}
|
|
20
|
+
locale(lang) {
|
|
21
|
+
this._locale = lang;
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
where(field, value) {
|
|
25
|
+
this._filters.push((item) => {
|
|
26
|
+
const v = item[field];
|
|
27
|
+
if (Array.isArray(v)) return v.includes(value);
|
|
28
|
+
return v === value;
|
|
29
|
+
});
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
sort(field, order = "asc") {
|
|
33
|
+
this._sortField = field;
|
|
34
|
+
this._sortOrder = order;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
limit(n) {
|
|
38
|
+
this._limit = n;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
offset(n) {
|
|
42
|
+
this._offset = n;
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
include(...fields) {
|
|
46
|
+
this._includes.push(...fields);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
all() {
|
|
50
|
+
let items = this._resolveData();
|
|
51
|
+
for (const filter of this._filters) items = items.filter(filter);
|
|
52
|
+
if (this._sortField) {
|
|
53
|
+
const field = this._sortField;
|
|
54
|
+
const dir = this._sortOrder === "asc" ? 1 : -1;
|
|
55
|
+
items.sort((a, b) => {
|
|
56
|
+
const va = a[field];
|
|
57
|
+
const vb = b[field];
|
|
58
|
+
if (va == null && vb == null) return 0;
|
|
59
|
+
if (va == null) return dir;
|
|
60
|
+
if (vb == null) return -dir;
|
|
61
|
+
if (va < vb) return -dir;
|
|
62
|
+
if (va > vb) return dir;
|
|
63
|
+
return 0;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (this._offset > 0 || this._limit !== null) {
|
|
67
|
+
const end = this._limit !== null ? this._offset + this._limit : void 0;
|
|
68
|
+
items = items.slice(this._offset, end);
|
|
69
|
+
}
|
|
70
|
+
if (this._includes.length > 0 && this._resolver) items = items.map((item) => this._resolveIncludes(item));
|
|
71
|
+
return items;
|
|
72
|
+
}
|
|
73
|
+
first() {
|
|
74
|
+
return this.all()[0];
|
|
75
|
+
}
|
|
76
|
+
_resolveData() {
|
|
77
|
+
if (this._locale) return [...this._data.get(this._locale) ?? []];
|
|
78
|
+
if (this._defaultLocale) {
|
|
79
|
+
const defaultData = this._data.get(this._defaultLocale);
|
|
80
|
+
if (defaultData) return [...defaultData];
|
|
81
|
+
}
|
|
82
|
+
const firstKey = this._data.keys().next().value;
|
|
83
|
+
if (firstKey !== void 0) return [...this._data.get(firstKey) ?? []];
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
_resolveIncludes(item) {
|
|
87
|
+
const resolved = { ...item };
|
|
88
|
+
const src = item;
|
|
89
|
+
const dst = resolved;
|
|
90
|
+
for (const field of this._includes) {
|
|
91
|
+
const meta = this._relationMeta[field];
|
|
92
|
+
if (!meta) continue;
|
|
93
|
+
const targets = Array.isArray(meta.target) ? meta.target : [meta.target];
|
|
94
|
+
if (meta.multi) {
|
|
95
|
+
const ids = src[field];
|
|
96
|
+
if (Array.isArray(ids)) dst[field] = ids.map((id) => {
|
|
97
|
+
if (typeof id === "string") return this._resolveId(targets, id) ?? id;
|
|
98
|
+
if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
99
|
+
const polyObj = id;
|
|
100
|
+
return this._resolveId([polyObj.model], polyObj.ref) ?? id;
|
|
101
|
+
}
|
|
102
|
+
return id;
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
const id = src[field];
|
|
106
|
+
if (typeof id === "string") dst[field] = this._resolveId(targets, id) ?? id;
|
|
107
|
+
else if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
108
|
+
const polyObj = id;
|
|
109
|
+
dst[field] = this._resolveId([polyObj.model], polyObj.ref) ?? id;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return resolved;
|
|
114
|
+
}
|
|
115
|
+
_resolveId(targets, id) {
|
|
116
|
+
for (const target of targets) {
|
|
117
|
+
const result = this._resolver(target, id, this._locale);
|
|
118
|
+
if (result) return result;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
458
121
|
};
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
return this;
|
|
538
|
-
}
|
|
539
|
-
bypassCache() {
|
|
540
|
-
logger.debug("Bypassing cache");
|
|
541
|
-
this.options.cache = false;
|
|
542
|
-
this.options.ttl = 0;
|
|
543
|
-
return this;
|
|
544
|
-
}
|
|
545
|
-
toJSON() {
|
|
546
|
-
return {
|
|
547
|
-
model: this.model,
|
|
548
|
-
filters: this.filters,
|
|
549
|
-
includes: this.includes,
|
|
550
|
-
sorting: this.sorting,
|
|
551
|
-
pagination: this.pagination,
|
|
552
|
-
options: this.options
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
async get() {
|
|
556
|
-
logger.debug("Starting query:", {
|
|
557
|
-
model: this.model,
|
|
558
|
-
filterCount: this.filters.length,
|
|
559
|
-
includeCount: Object.keys(this.includes).length,
|
|
560
|
-
sortingCount: this.sorting.length,
|
|
561
|
-
pagination: this.pagination,
|
|
562
|
-
options: this.options
|
|
563
|
-
});
|
|
564
|
-
const result = await this.loader.load(this.model);
|
|
565
|
-
const modelConfig = result.model;
|
|
566
|
-
logger.debug("Model loaded:", {
|
|
567
|
-
model: this.model,
|
|
568
|
-
metadata: modelConfig.metadata,
|
|
569
|
-
contentKeys: Object.keys(result.content)
|
|
570
|
-
});
|
|
571
|
-
let data;
|
|
572
|
-
if (modelConfig.metadata.localization) {
|
|
573
|
-
const locale = this.options.locale || "en";
|
|
574
|
-
logger.debug("Selecting content for localized model:", {
|
|
575
|
-
model: this.model,
|
|
576
|
-
requestedLocale: locale,
|
|
577
|
-
availableLocales: Object.keys(result.content)
|
|
578
|
-
});
|
|
579
|
-
data = result.content[locale];
|
|
580
|
-
if (!data) {
|
|
581
|
-
logger.error("Content not found:", {
|
|
582
|
-
model: this.model,
|
|
583
|
-
locale,
|
|
584
|
-
availableLocales: Object.keys(result.content)
|
|
585
|
-
});
|
|
586
|
-
throw new Error(`Content not found for locale: ${locale}`);
|
|
587
|
-
}
|
|
588
|
-
} else {
|
|
589
|
-
logger.debug("Selecting content for non-localized model:", {
|
|
590
|
-
model: this.model,
|
|
591
|
-
contentKeys: Object.keys(result.content)
|
|
592
|
-
});
|
|
593
|
-
if (!result.content.default) {
|
|
594
|
-
logger.error("Content not found:", {
|
|
595
|
-
model: this.model,
|
|
596
|
-
contentKeys: Object.keys(result.content)
|
|
597
|
-
});
|
|
598
|
-
throw new Error(`Content not found for model: ${this.model}`);
|
|
599
|
-
}
|
|
600
|
-
data = result.content.default;
|
|
601
|
-
}
|
|
602
|
-
logger.debug("Executing query:", {
|
|
603
|
-
model: this.model,
|
|
604
|
-
dataLength: data.length
|
|
605
|
-
});
|
|
606
|
-
return this.executor.execute({
|
|
607
|
-
model: this.model,
|
|
608
|
-
data,
|
|
609
|
-
filters: this.filters,
|
|
610
|
-
includes: this.includes,
|
|
611
|
-
sorting: this.sorting,
|
|
612
|
-
pagination: this.pagination,
|
|
613
|
-
options: this.options
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
async first() {
|
|
617
|
-
const result = await this.limit(1).get();
|
|
618
|
-
return result.data[0] || null;
|
|
619
|
-
}
|
|
620
|
-
async count() {
|
|
621
|
-
const result = await this.get();
|
|
622
|
-
return result.total;
|
|
623
|
-
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/runtime/singleton.ts
|
|
124
|
+
var SingletonAccessor = class {
|
|
125
|
+
_data;
|
|
126
|
+
_locale = null;
|
|
127
|
+
_defaultLocale;
|
|
128
|
+
_includes = [];
|
|
129
|
+
_relationMeta = {};
|
|
130
|
+
_resolveEntry;
|
|
131
|
+
constructor(data, defaultLocale, relationMeta, resolveEntry) {
|
|
132
|
+
this._data = data;
|
|
133
|
+
this._defaultLocale = defaultLocale ?? null;
|
|
134
|
+
if (relationMeta) this._relationMeta = relationMeta;
|
|
135
|
+
this._resolveEntry = resolveEntry;
|
|
136
|
+
}
|
|
137
|
+
locale(lang) {
|
|
138
|
+
this._locale = lang;
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
include(...fields) {
|
|
142
|
+
this._includes.push(...fields);
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
get() {
|
|
146
|
+
if (this._locale) {
|
|
147
|
+
const d = this._data.get(this._locale);
|
|
148
|
+
if (!d) throw new Error(`No data for locale "${this._locale}"`);
|
|
149
|
+
if (this._includes.length > 0) return this._resolveIncludes(d, this._locale);
|
|
150
|
+
return d;
|
|
151
|
+
}
|
|
152
|
+
const locale = this._defaultLocale ?? void 0;
|
|
153
|
+
let data;
|
|
154
|
+
if (locale) data = this._data.get(locale);
|
|
155
|
+
if (!data) {
|
|
156
|
+
const firstKey = this._data.keys().next().value;
|
|
157
|
+
if (firstKey !== void 0) data = this._data.get(firstKey);
|
|
158
|
+
}
|
|
159
|
+
if (!data) throw new Error("No data available");
|
|
160
|
+
if (this._includes.length > 0) return this._resolveIncludes(data, locale ?? "en");
|
|
161
|
+
return data;
|
|
162
|
+
}
|
|
163
|
+
_resolveIncludes(item, locale) {
|
|
164
|
+
if (!this._resolveEntry || this._includes.length === 0) return item;
|
|
165
|
+
const resolved = { ...item };
|
|
166
|
+
const src = item;
|
|
167
|
+
const dst = resolved;
|
|
168
|
+
for (const field of this._includes) {
|
|
169
|
+
const meta = this._relationMeta[field];
|
|
170
|
+
if (!meta) continue;
|
|
171
|
+
const targets = Array.isArray(meta.target) ? meta.target : [meta.target];
|
|
172
|
+
if (meta.multi) {
|
|
173
|
+
const ids = src[field];
|
|
174
|
+
if (Array.isArray(ids)) dst[field] = ids.map((id) => {
|
|
175
|
+
if (typeof id === "string") return this._resolveId(targets, id, locale) ?? id;
|
|
176
|
+
if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
177
|
+
const polyObj = id;
|
|
178
|
+
return this._resolveId([polyObj.model], polyObj.ref, locale) ?? id;
|
|
179
|
+
}
|
|
180
|
+
return id;
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
const id = src[field];
|
|
184
|
+
if (typeof id === "string") dst[field] = this._resolveId(targets, id, locale) ?? id;
|
|
185
|
+
else if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
186
|
+
const polyObj = id;
|
|
187
|
+
dst[field] = this._resolveId([polyObj.model], polyObj.ref, locale) ?? id;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return resolved;
|
|
192
|
+
}
|
|
193
|
+
_resolveId(targets, id, locale) {
|
|
194
|
+
if (!this._resolveEntry) return void 0;
|
|
195
|
+
for (const model of targets) {
|
|
196
|
+
const result = this._resolveEntry(model, id, locale);
|
|
197
|
+
if (result) return result;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
624
200
|
};
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
throw new Error(`Invalid array operator: ${operator}`);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
if (Array.isArray(itemValue)) {
|
|
661
|
-
switch (operator) {
|
|
662
|
-
case "in":
|
|
663
|
-
return value.some((v) => itemValue.includes(v));
|
|
664
|
-
case "nin":
|
|
665
|
-
return !value.some((v) => itemValue.includes(v));
|
|
666
|
-
default:
|
|
667
|
-
logger.error("Invalid array operator:", operator);
|
|
668
|
-
throw new Error(`Invalid array operator: ${operator}`);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
if (typeof itemValue === "number" && typeof value === "number") {
|
|
672
|
-
switch (operator) {
|
|
673
|
-
case "eq":
|
|
674
|
-
return itemValue === value;
|
|
675
|
-
case "ne":
|
|
676
|
-
return itemValue !== value;
|
|
677
|
-
case "gt":
|
|
678
|
-
return itemValue > value;
|
|
679
|
-
case "gte":
|
|
680
|
-
return itemValue >= value;
|
|
681
|
-
case "lt":
|
|
682
|
-
return itemValue < value;
|
|
683
|
-
case "lte":
|
|
684
|
-
return itemValue <= value;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
return false;
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
logger.debug("Filter application completed:", {
|
|
691
|
-
initialCount: data.length,
|
|
692
|
-
resultCount: result.length
|
|
693
|
-
});
|
|
694
|
-
return result;
|
|
695
|
-
}
|
|
696
|
-
applySorting(data, sorting) {
|
|
697
|
-
return [...data].sort((a, b) => {
|
|
698
|
-
for (const { field, direction } of sorting) {
|
|
699
|
-
if (!(field in a)) {
|
|
700
|
-
throw new Error(`Invalid sort field: ${field}`);
|
|
701
|
-
}
|
|
702
|
-
const aValue = a[field];
|
|
703
|
-
const bValue = b[field];
|
|
704
|
-
if (aValue === bValue)
|
|
705
|
-
continue;
|
|
706
|
-
const compareResult = aValue < bValue ? -1 : 1;
|
|
707
|
-
return direction === "asc" ? compareResult : -compareResult;
|
|
708
|
-
}
|
|
709
|
-
return 0;
|
|
710
|
-
});
|
|
711
|
-
}
|
|
712
|
-
applyPagination(data, limit, offset = 0) {
|
|
713
|
-
if (!limit)
|
|
714
|
-
return data.slice(offset);
|
|
715
|
-
return data.slice(offset, offset + limit);
|
|
716
|
-
}
|
|
717
|
-
async resolveIncludes(model, data, includes, options) {
|
|
718
|
-
logger.debug("Starting to resolve relations:", {
|
|
719
|
-
model,
|
|
720
|
-
dataLength: data.length,
|
|
721
|
-
includes,
|
|
722
|
-
options
|
|
723
|
-
});
|
|
724
|
-
const result = [...data];
|
|
725
|
-
for (const [field, config] of Object.entries(includes)) {
|
|
726
|
-
logger.debug(`Resolving relation "${field}"`);
|
|
727
|
-
const relations = await this.loader.resolveRelation(
|
|
728
|
-
model,
|
|
729
|
-
field,
|
|
730
|
-
result,
|
|
731
|
-
options.locale
|
|
732
|
-
);
|
|
733
|
-
logger.debug(`Relation "${field}" resolved:`, {
|
|
734
|
-
foundRelationsCount: relations.length
|
|
735
|
-
});
|
|
736
|
-
if (config.include && relations.length) {
|
|
737
|
-
logger.debug(`Resolving nested relations for "${field}":`, config.include);
|
|
738
|
-
await this.resolveIncludes(
|
|
739
|
-
field,
|
|
740
|
-
relations,
|
|
741
|
-
config.include,
|
|
742
|
-
options
|
|
743
|
-
);
|
|
744
|
-
}
|
|
745
|
-
result.forEach((item) => {
|
|
746
|
-
const value = item[field];
|
|
747
|
-
const relatedItems = relations.filter((r) => {
|
|
748
|
-
if (Array.isArray(value)) {
|
|
749
|
-
return value.includes(r.ID);
|
|
750
|
-
}
|
|
751
|
-
return r.ID === value;
|
|
752
|
-
});
|
|
753
|
-
if (!item._relations) {
|
|
754
|
-
item._relations = {};
|
|
755
|
-
}
|
|
756
|
-
item._relations[field] = Array.isArray(value) ? relatedItems : relatedItems[0];
|
|
757
|
-
});
|
|
758
|
-
logger.debug(`Data added for relation "${field}"`);
|
|
759
|
-
}
|
|
760
|
-
return result;
|
|
761
|
-
}
|
|
762
|
-
applyStringOperation(value, operator, searchValue) {
|
|
763
|
-
switch (operator) {
|
|
764
|
-
case "eq":
|
|
765
|
-
return value === searchValue;
|
|
766
|
-
case "ne":
|
|
767
|
-
return value !== searchValue;
|
|
768
|
-
case "contains":
|
|
769
|
-
return value.toLowerCase().includes(searchValue.toLowerCase());
|
|
770
|
-
case "startsWith":
|
|
771
|
-
return value.toLowerCase().startsWith(searchValue.toLowerCase());
|
|
772
|
-
case "endsWith":
|
|
773
|
-
return value.toLowerCase().endsWith(searchValue.toLowerCase());
|
|
774
|
-
default: {
|
|
775
|
-
const _exhaustiveCheck = operator;
|
|
776
|
-
return _exhaustiveCheck;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
async execute({
|
|
781
|
-
model,
|
|
782
|
-
data,
|
|
783
|
-
filters = [],
|
|
784
|
-
includes = {},
|
|
785
|
-
sorting = [],
|
|
786
|
-
pagination = {},
|
|
787
|
-
options = {}
|
|
788
|
-
}) {
|
|
789
|
-
logger.debug("Starting execution:", {
|
|
790
|
-
model,
|
|
791
|
-
dataLength: data.length,
|
|
792
|
-
filterCount: filters.length,
|
|
793
|
-
includeCount: Object.keys(includes).length,
|
|
794
|
-
sortingCount: sorting.length,
|
|
795
|
-
pagination,
|
|
796
|
-
options
|
|
797
|
-
});
|
|
798
|
-
let result = [...data];
|
|
799
|
-
if (filters.length) {
|
|
800
|
-
logger.debug("Applying filters:", filters);
|
|
801
|
-
result = this.applyFilters(result, filters);
|
|
802
|
-
logger.debug("Remaining items after filtering:", result.length);
|
|
803
|
-
}
|
|
804
|
-
if (Object.keys(includes).length) {
|
|
805
|
-
logger.debug("Resolving relations:", includes);
|
|
806
|
-
result = await this.resolveIncludes(model, result, includes, options);
|
|
807
|
-
logger.debug("Items after relation resolution:", result.length);
|
|
808
|
-
}
|
|
809
|
-
if (sorting.length) {
|
|
810
|
-
logger.debug("Applying sorting:", sorting);
|
|
811
|
-
result = this.applySorting(result, sorting);
|
|
812
|
-
}
|
|
813
|
-
const paginatedData = this.applyPagination(result, pagination.limit, pagination.offset);
|
|
814
|
-
logger.debug("After pagination:", {
|
|
815
|
-
totalCount: result.length,
|
|
816
|
-
pageSize: paginatedData.length,
|
|
817
|
-
offset: pagination.offset || 0,
|
|
818
|
-
hasMore: (pagination.offset || 0) + paginatedData.length < result.length
|
|
819
|
-
});
|
|
820
|
-
return {
|
|
821
|
-
data: paginatedData,
|
|
822
|
-
total: result.length,
|
|
823
|
-
pagination: pagination.limit ? {
|
|
824
|
-
limit: pagination.limit,
|
|
825
|
-
offset: pagination.offset || 0,
|
|
826
|
-
hasMore: (pagination.offset || 0) + paginatedData.length < result.length
|
|
827
|
-
} : undefined
|
|
828
|
-
};
|
|
829
|
-
}
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/runtime/dictionary.ts
|
|
203
|
+
var DictionaryAccessor = class {
|
|
204
|
+
_data;
|
|
205
|
+
_locale = null;
|
|
206
|
+
_defaultLocale;
|
|
207
|
+
constructor(data, defaultLocale) {
|
|
208
|
+
this._data = data;
|
|
209
|
+
this._defaultLocale = defaultLocale ?? null;
|
|
210
|
+
}
|
|
211
|
+
locale(lang) {
|
|
212
|
+
this._locale = lang;
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
get(key, params) {
|
|
216
|
+
const dict = this._resolveData();
|
|
217
|
+
if (key === void 0) return dict;
|
|
218
|
+
const value = dict[key];
|
|
219
|
+
if (value === void 0) return void 0;
|
|
220
|
+
if (params) return interpolate(value, params);
|
|
221
|
+
return value;
|
|
222
|
+
}
|
|
223
|
+
_resolveData() {
|
|
224
|
+
if (this._locale) return this._data.get(this._locale) ?? {};
|
|
225
|
+
if (this._defaultLocale) {
|
|
226
|
+
const d = this._data.get(this._defaultLocale);
|
|
227
|
+
if (d) return d;
|
|
228
|
+
}
|
|
229
|
+
const firstKey = this._data.keys().next().value;
|
|
230
|
+
if (firstKey !== void 0) return this._data.get(firstKey) ?? {};
|
|
231
|
+
return {};
|
|
232
|
+
}
|
|
830
233
|
};
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
234
|
+
function interpolate(template, params) {
|
|
235
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
236
|
+
const val = params[key];
|
|
237
|
+
return val !== void 0 ? String(val) : match;
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/runtime/document.ts
|
|
242
|
+
var DocumentQuery = class {
|
|
243
|
+
_data;
|
|
244
|
+
_locale = null;
|
|
245
|
+
_filters = [];
|
|
246
|
+
_includes = [];
|
|
247
|
+
_relationMeta;
|
|
248
|
+
_resolver;
|
|
249
|
+
_defaultLocale;
|
|
250
|
+
constructor(data, relationMeta, resolver, defaultLocale) {
|
|
251
|
+
this._data = data;
|
|
252
|
+
this._relationMeta = relationMeta ?? {};
|
|
253
|
+
this._resolver = resolver ?? null;
|
|
254
|
+
this._defaultLocale = defaultLocale ?? null;
|
|
255
|
+
}
|
|
256
|
+
locale(lang) {
|
|
257
|
+
this._locale = lang;
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
where(field, value) {
|
|
261
|
+
this._filters.push((item) => item[field] === value);
|
|
262
|
+
return this;
|
|
263
|
+
}
|
|
264
|
+
include(...fields) {
|
|
265
|
+
this._includes.push(...fields);
|
|
266
|
+
return this;
|
|
267
|
+
}
|
|
268
|
+
bySlug(slug) {
|
|
269
|
+
const item = this._resolveData().find((i) => i["slug"] === slug);
|
|
270
|
+
if (item && this._includes.length > 0 && this._resolver) return this._resolveIncludes(item);
|
|
271
|
+
return item;
|
|
272
|
+
}
|
|
273
|
+
all() {
|
|
274
|
+
let items = this._resolveData();
|
|
275
|
+
for (const filter of this._filters) items = items.filter(filter);
|
|
276
|
+
if (this._includes.length > 0 && this._resolver) items = items.map((item) => this._resolveIncludes(item));
|
|
277
|
+
return items;
|
|
278
|
+
}
|
|
279
|
+
first() {
|
|
280
|
+
return this.all()[0];
|
|
281
|
+
}
|
|
282
|
+
_resolveData() {
|
|
283
|
+
if (this._locale) return [...this._data.get(this._locale) ?? []];
|
|
284
|
+
if (this._defaultLocale) {
|
|
285
|
+
const defaultData = this._data.get(this._defaultLocale);
|
|
286
|
+
if (defaultData) return [...defaultData];
|
|
287
|
+
}
|
|
288
|
+
const firstKey = this._data.keys().next().value;
|
|
289
|
+
if (firstKey !== void 0) return [...this._data.get(firstKey) ?? []];
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
_resolveIncludes(item) {
|
|
293
|
+
const resolved = { ...item };
|
|
294
|
+
const src = item;
|
|
295
|
+
const dst = resolved;
|
|
296
|
+
for (const field of this._includes) {
|
|
297
|
+
const meta = this._relationMeta[field];
|
|
298
|
+
if (!meta) continue;
|
|
299
|
+
const targets = Array.isArray(meta.target) ? meta.target : [meta.target];
|
|
300
|
+
if (meta.multi) {
|
|
301
|
+
const ids = src[field];
|
|
302
|
+
if (Array.isArray(ids)) dst[field] = ids.map((id) => {
|
|
303
|
+
if (typeof id === "string") return this._resolveId(targets, id) ?? id;
|
|
304
|
+
if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
305
|
+
const polyObj = id;
|
|
306
|
+
return this._resolveId([polyObj.model], polyObj.ref) ?? id;
|
|
307
|
+
}
|
|
308
|
+
return id;
|
|
309
|
+
});
|
|
310
|
+
} else {
|
|
311
|
+
const id = src[field];
|
|
312
|
+
if (typeof id === "string") dst[field] = this._resolveId(targets, id) ?? id;
|
|
313
|
+
else if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
314
|
+
const polyObj = id;
|
|
315
|
+
dst[field] = this._resolveId([polyObj.model], polyObj.ref) ?? id;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return resolved;
|
|
320
|
+
}
|
|
321
|
+
_resolveId(targets, id) {
|
|
322
|
+
for (const target of targets) {
|
|
323
|
+
const result = this._resolver(target, id, this._locale);
|
|
324
|
+
if (result) return result;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
859
327
|
};
|
|
860
|
-
|
|
861
|
-
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/index.ts
|
|
330
|
+
/**
|
|
331
|
+
* Factory for framework SDK authors.
|
|
332
|
+
* Returns the generated client module loaded from .contentrain/client/.
|
|
333
|
+
*
|
|
334
|
+
* Usage (Nuxt composable):
|
|
335
|
+
* ```ts
|
|
336
|
+
* import { createContentrainClient } from '@contentrain/query'
|
|
337
|
+
* const client = createContentrainClient()
|
|
338
|
+
* const posts = client.query('blog-post').locale('en').all()
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
async function createContentrainClient(projectRoot) {
|
|
342
|
+
const { resolve } = await import("node:path");
|
|
343
|
+
const { pathToFileURL } = await import("node:url");
|
|
344
|
+
return await import(pathToFileURL(resolve(projectRoot ?? process.cwd(), ".contentrain", "client", "index.mjs")).href);
|
|
345
|
+
}
|
|
346
|
+
//#endregion
|
|
347
|
+
export { DictionaryAccessor, DocumentQuery, QueryBuilder, SingletonAccessor, createContentrainClient, createContentrainClient as default };
|
|
862
348
|
|
|
863
|
-
export { ContentLoader, ContentrainQueryBuilder, ContentrainSDK, MemoryCache, QueryExecutor, logger };
|
|
864
|
-
//# sourceMappingURL=index.mjs.map
|
|
865
349
|
//# sourceMappingURL=index.mjs.map
|