@contentrain/query 3.0.0 → 3.2.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/dist/index.d.mts +45 -26
- package/dist/index.d.ts +45 -26
- package/dist/index.js +366 -63
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +367 -65
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
1
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { lru } from 'tiny-lru';
|
|
4
|
+
import { env } from 'node:process';
|
|
4
5
|
|
|
5
6
|
var __defProp = Object.defineProperty;
|
|
6
7
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
+
var isDevelopment = env.NODE_ENV === "development";
|
|
9
|
+
var logger = {
|
|
10
|
+
debug: /* @__PURE__ */ __name((...args) => {
|
|
11
|
+
if (isDevelopment) {
|
|
12
|
+
console.log(...args);
|
|
13
|
+
}
|
|
14
|
+
}, "debug"),
|
|
15
|
+
error: /* @__PURE__ */ __name((...args) => {
|
|
16
|
+
if (isDevelopment) {
|
|
17
|
+
console.error(...args);
|
|
18
|
+
}
|
|
19
|
+
}, "error")
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/cache/memory.ts
|
|
7
23
|
var _MemoryCache = class _MemoryCache {
|
|
8
24
|
constructor(options = {}) {
|
|
9
25
|
this.stats = {
|
|
@@ -27,6 +43,10 @@ var _MemoryCache = class _MemoryCache {
|
|
|
27
43
|
return new TextEncoder().encode(str).length;
|
|
28
44
|
}
|
|
29
45
|
async set(key, data, ttl) {
|
|
46
|
+
logger.debug("Saving data to cache:", {
|
|
47
|
+
key,
|
|
48
|
+
ttl
|
|
49
|
+
});
|
|
30
50
|
await this.cleanupCache();
|
|
31
51
|
const size = this.calculateSize(data);
|
|
32
52
|
const now = Date.now();
|
|
@@ -49,6 +69,10 @@ var _MemoryCache = class _MemoryCache {
|
|
|
49
69
|
}
|
|
50
70
|
this.cache.set(key, entry);
|
|
51
71
|
this.stats.size += size;
|
|
72
|
+
logger.debug("Data saved to cache:", {
|
|
73
|
+
key,
|
|
74
|
+
expiry: expireAt ? new Date(expireAt).toISOString() : "no expiry"
|
|
75
|
+
});
|
|
52
76
|
}
|
|
53
77
|
findOldestKey() {
|
|
54
78
|
let oldestKey = null;
|
|
@@ -63,8 +87,10 @@ var _MemoryCache = class _MemoryCache {
|
|
|
63
87
|
return oldestKey;
|
|
64
88
|
}
|
|
65
89
|
async get(key) {
|
|
90
|
+
logger.debug("Getting data from cache:", { key });
|
|
66
91
|
const entry = this.cache.get(key);
|
|
67
92
|
if (!entry) {
|
|
93
|
+
logger.debug("Data not found in cache:", { key });
|
|
68
94
|
this.stats.misses++;
|
|
69
95
|
return null;
|
|
70
96
|
}
|
|
@@ -73,17 +99,24 @@ var _MemoryCache = class _MemoryCache {
|
|
|
73
99
|
this.stats.misses++;
|
|
74
100
|
return null;
|
|
75
101
|
}
|
|
102
|
+
logger.debug("Data retrieved from cache:", {
|
|
103
|
+
key,
|
|
104
|
+
expiry: entry.expireAt ? new Date(entry.expireAt).toISOString() : "no expiry"
|
|
105
|
+
});
|
|
76
106
|
this.stats.hits++;
|
|
77
107
|
return entry.data;
|
|
78
108
|
}
|
|
79
109
|
async delete(key) {
|
|
110
|
+
logger.debug("Deleting data from cache:", { key });
|
|
80
111
|
const entry = this.cache.get(key);
|
|
81
112
|
if (entry) {
|
|
82
113
|
this.stats.size -= entry.size;
|
|
83
114
|
this.cache.delete(key);
|
|
84
115
|
}
|
|
116
|
+
logger.debug("Data deleted from cache:", { key });
|
|
85
117
|
}
|
|
86
118
|
async clear() {
|
|
119
|
+
logger.debug("Clearing cache");
|
|
87
120
|
this.cache.clear();
|
|
88
121
|
this.stats = {
|
|
89
122
|
hits: 0,
|
|
@@ -91,6 +124,7 @@ var _MemoryCache = class _MemoryCache {
|
|
|
91
124
|
size: 0,
|
|
92
125
|
lastCleanup: Date.now()
|
|
93
126
|
};
|
|
127
|
+
logger.debug("Cache cleared");
|
|
94
128
|
}
|
|
95
129
|
async cleanupCache() {
|
|
96
130
|
const now = Date.now();
|
|
@@ -133,7 +167,7 @@ var _ContentLoader = class _ContentLoader {
|
|
|
133
167
|
defaultLocale: "en",
|
|
134
168
|
cache: true,
|
|
135
169
|
ttl: 60 * 1e3,
|
|
136
|
-
// 1
|
|
170
|
+
// 1 minute
|
|
137
171
|
maxCacheSize: 100,
|
|
138
172
|
// 100 MB
|
|
139
173
|
...options
|
|
@@ -167,33 +201,46 @@ var _ContentLoader = class _ContentLoader {
|
|
|
167
201
|
const allMetadata = JSON.parse(metadataContent);
|
|
168
202
|
const modelMetadata = allMetadata.find((m) => m.modelId === model);
|
|
169
203
|
if (!modelMetadata) {
|
|
204
|
+
logger.error("Model metadata not found:", {
|
|
205
|
+
model,
|
|
206
|
+
metadataPath
|
|
207
|
+
});
|
|
170
208
|
throw new Error(`Model metadata not found for ${model}`);
|
|
171
209
|
}
|
|
172
210
|
const modelPath = join(this.options.contentDir, "models", `${model}.json`);
|
|
173
211
|
const modelContent = await readFile(modelPath, "utf-8");
|
|
174
212
|
const modelFields = JSON.parse(modelContent);
|
|
175
|
-
if (!Array.isArray(modelFields)) {
|
|
176
|
-
throw new TypeError(`Invalid field configuration for model ${model}: Expected an array of fields`);
|
|
177
|
-
}
|
|
178
|
-
modelFields.forEach((field, index) => {
|
|
179
|
-
if (!field.fieldId || !field.fieldType || !field.componentId) {
|
|
180
|
-
throw new Error(`Invalid field at index ${index} for model ${model}: Missing required properties`);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
213
|
return {
|
|
184
214
|
metadata: modelMetadata,
|
|
185
215
|
fields: modelFields
|
|
186
216
|
};
|
|
187
217
|
} catch (error) {
|
|
218
|
+
logger.error("Failed to load model config:", {
|
|
219
|
+
model,
|
|
220
|
+
error: error?.message || "Unknown error"
|
|
221
|
+
});
|
|
188
222
|
throw new Error(`Failed to load model config for ${model}: ${error?.message || "Unknown error"}`);
|
|
189
223
|
}
|
|
190
224
|
}
|
|
191
|
-
async loadContentFile(model, locale) {
|
|
225
|
+
async loadContentFile(model, locale = "default") {
|
|
192
226
|
try {
|
|
227
|
+
const modelConfig = await this.loadModelConfig(model);
|
|
193
228
|
let contentPath;
|
|
194
|
-
if (
|
|
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
|
+
}
|
|
195
239
|
contentPath = join(this.options.contentDir, model, `${locale}.json`);
|
|
196
240
|
} else {
|
|
241
|
+
if (locale !== "default") {
|
|
242
|
+
console.warn(`Locale "${locale}" specified for non-localized model "${model}". This parameter will be ignored.`);
|
|
243
|
+
}
|
|
197
244
|
contentPath = join(this.options.contentDir, model, `${model}.json`);
|
|
198
245
|
}
|
|
199
246
|
const content = await readFile(contentPath, "utf-8");
|
|
@@ -201,14 +248,23 @@ var _ContentLoader = class _ContentLoader {
|
|
|
201
248
|
const data = JSON.parse(content);
|
|
202
249
|
return {
|
|
203
250
|
model,
|
|
204
|
-
locale,
|
|
251
|
+
locale: modelConfig.metadata.localization ? locale : void 0,
|
|
205
252
|
data
|
|
206
253
|
};
|
|
207
254
|
} catch {
|
|
255
|
+
logger.error("Failed to load content:", {
|
|
256
|
+
model,
|
|
257
|
+
locale,
|
|
258
|
+
contentPath
|
|
259
|
+
});
|
|
208
260
|
throw new Error(`Failed to load content: Invalid JSON format in ${contentPath}`);
|
|
209
261
|
}
|
|
210
262
|
} catch (error) {
|
|
211
263
|
if (error.message.includes("Invalid JSON format")) {
|
|
264
|
+
logger.error("Failed to load content:", {
|
|
265
|
+
model,
|
|
266
|
+
locale
|
|
267
|
+
});
|
|
212
268
|
throw error;
|
|
213
269
|
}
|
|
214
270
|
throw new Error(
|
|
@@ -241,8 +297,31 @@ var _ContentLoader = class _ContentLoader {
|
|
|
241
297
|
throw new Error(`Failed to load relations for ${model}: ${error?.message || "Unknown error"}`);
|
|
242
298
|
}
|
|
243
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
|
+
}
|
|
244
323
|
async load(model) {
|
|
245
|
-
const cacheKey =
|
|
324
|
+
const cacheKey = `${model}`;
|
|
246
325
|
if (this.options.cache) {
|
|
247
326
|
const cached = await this.cache.get(cacheKey);
|
|
248
327
|
if (cached)
|
|
@@ -254,18 +333,34 @@ var _ContentLoader = class _ContentLoader {
|
|
|
254
333
|
this.relations.set(model, relations);
|
|
255
334
|
const content = {};
|
|
256
335
|
if (modelConfig.metadata.localization) {
|
|
257
|
-
const locales =
|
|
336
|
+
const locales = await this.getModelLocales(model, modelConfig);
|
|
258
337
|
for (const locale of locales) {
|
|
259
|
-
|
|
260
|
-
|
|
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
|
+
}
|
|
261
347
|
}
|
|
262
348
|
} else {
|
|
263
349
|
const file = await this.loadContentFile(model);
|
|
264
350
|
content.default = file.data;
|
|
265
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
|
+
}
|
|
266
360
|
const result = {
|
|
267
361
|
model: modelConfig,
|
|
268
|
-
content
|
|
362
|
+
content,
|
|
363
|
+
assets
|
|
269
364
|
};
|
|
270
365
|
if (this.options.cache) {
|
|
271
366
|
const ttl = this.getModelTTL(model);
|
|
@@ -275,19 +370,65 @@ var _ContentLoader = class _ContentLoader {
|
|
|
275
370
|
}
|
|
276
371
|
async resolveRelation(model, relationField, data, locale) {
|
|
277
372
|
try {
|
|
373
|
+
logger.debug("Debug - Starting relation resolution:", {
|
|
374
|
+
model,
|
|
375
|
+
relationField,
|
|
376
|
+
dataLength: data.length,
|
|
377
|
+
locale
|
|
378
|
+
});
|
|
278
379
|
const relations = this.relations.get(model);
|
|
380
|
+
logger.debug("Debug - Relations:", relations);
|
|
279
381
|
if (!relations)
|
|
280
382
|
throw new Error(`No relations found for model: ${model}`);
|
|
281
383
|
const relation = relations.find((r) => r.foreignKey === relationField);
|
|
384
|
+
logger.debug("Debug - Found relation:", relation);
|
|
282
385
|
if (!relation)
|
|
283
386
|
throw new Error(`No relation found for field: ${String(relationField)}`);
|
|
387
|
+
logger.debug("Debug - Related model loading:", relation.model);
|
|
284
388
|
const relatedContent = await this.load(relation.model);
|
|
285
|
-
|
|
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
|
+
});
|
|
286
424
|
if (!relatedData) {
|
|
287
425
|
throw new Error(`Failed to resolve relation: No data found for model ${relation.model}`);
|
|
288
426
|
}
|
|
289
427
|
if (relation.type === "one-to-one") {
|
|
290
|
-
|
|
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) => {
|
|
291
432
|
const relatedItem = relatedData.find((r) => r.ID === item[relationField]);
|
|
292
433
|
if (!relatedItem) {
|
|
293
434
|
throw new Error(`Failed to resolve relation: No matching item found for ID ${String(item[relationField])}`);
|
|
@@ -295,16 +436,22 @@ var _ContentLoader = class _ContentLoader {
|
|
|
295
436
|
return relatedItem;
|
|
296
437
|
});
|
|
297
438
|
} else {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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;
|
|
306
452
|
}
|
|
307
453
|
} catch (error) {
|
|
454
|
+
logger.error("Debug - Error occurred:", error);
|
|
308
455
|
throw new Error(`Failed to resolve relation: ${error.message}`);
|
|
309
456
|
}
|
|
310
457
|
}
|
|
@@ -325,6 +472,11 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
|
|
|
325
472
|
this.loader = loader;
|
|
326
473
|
}
|
|
327
474
|
where(field, operator, value) {
|
|
475
|
+
logger.debug("Adding filter:", {
|
|
476
|
+
field,
|
|
477
|
+
operator,
|
|
478
|
+
value
|
|
479
|
+
});
|
|
328
480
|
this.filters.push({
|
|
329
481
|
field,
|
|
330
482
|
operator,
|
|
@@ -333,6 +485,7 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
|
|
|
333
485
|
return this;
|
|
334
486
|
}
|
|
335
487
|
include(relation) {
|
|
488
|
+
logger.debug("Adding relation:", relation);
|
|
336
489
|
if (typeof relation === "string") {
|
|
337
490
|
this.includes[relation] = {};
|
|
338
491
|
} else if (Array.isArray(relation)) {
|
|
@@ -343,6 +496,10 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
|
|
|
343
496
|
return this;
|
|
344
497
|
}
|
|
345
498
|
orderBy(field, direction = "asc") {
|
|
499
|
+
logger.debug("Adding sorting:", {
|
|
500
|
+
field,
|
|
501
|
+
direction
|
|
502
|
+
});
|
|
346
503
|
this.sorting.push({
|
|
347
504
|
field,
|
|
348
505
|
direction
|
|
@@ -350,28 +507,37 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
|
|
|
350
507
|
return this;
|
|
351
508
|
}
|
|
352
509
|
limit(count) {
|
|
510
|
+
logger.debug("Adding limit:", count);
|
|
353
511
|
this.pagination.limit = count;
|
|
354
512
|
return this;
|
|
355
513
|
}
|
|
356
514
|
offset(count) {
|
|
515
|
+
logger.debug("Adding offset:", count);
|
|
357
516
|
this.pagination.offset = count;
|
|
358
517
|
return this;
|
|
359
518
|
}
|
|
360
519
|
locale(code) {
|
|
520
|
+
logger.debug("Setting locale:", code);
|
|
361
521
|
this.options.locale = code;
|
|
362
522
|
return this;
|
|
363
523
|
}
|
|
364
524
|
cache(ttl) {
|
|
525
|
+
logger.debug("Setting cache:", {
|
|
526
|
+
enabled: true,
|
|
527
|
+
ttl
|
|
528
|
+
});
|
|
365
529
|
this.options.cache = true;
|
|
366
530
|
if (ttl)
|
|
367
531
|
this.options.ttl = ttl;
|
|
368
532
|
return this;
|
|
369
533
|
}
|
|
370
534
|
noCache() {
|
|
535
|
+
logger.debug("Disabling cache");
|
|
371
536
|
this.options.cache = false;
|
|
372
537
|
return this;
|
|
373
538
|
}
|
|
374
539
|
bypassCache() {
|
|
540
|
+
logger.debug("Bypassing cache");
|
|
375
541
|
this.options.cache = false;
|
|
376
542
|
this.options.ttl = 0;
|
|
377
543
|
return this;
|
|
@@ -387,8 +553,56 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
|
|
|
387
553
|
};
|
|
388
554
|
}
|
|
389
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
|
+
});
|
|
390
564
|
const result = await this.loader.load(this.model);
|
|
391
|
-
const
|
|
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
|
+
});
|
|
392
606
|
return this.executor.execute({
|
|
393
607
|
model: this.model,
|
|
394
608
|
data,
|
|
@@ -417,51 +631,80 @@ var _QueryExecutor = class _QueryExecutor {
|
|
|
417
631
|
this.loader = loader;
|
|
418
632
|
}
|
|
419
633
|
applyFilters(data, filters) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
634
|
+
logger.debug("Starting to apply filters:", {
|
|
635
|
+
dataLength: data.length,
|
|
636
|
+
filters
|
|
637
|
+
});
|
|
638
|
+
const result = data.filter((item) => {
|
|
639
|
+
return filters.every(({ field, operator, value }) => {
|
|
640
|
+
const itemValue = item[field];
|
|
423
641
|
const validOperators = ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "contains", "startsWith", "endsWith"];
|
|
424
|
-
if (!validOperators.includes(
|
|
425
|
-
|
|
642
|
+
if (!validOperators.includes(operator)) {
|
|
643
|
+
logger.error("Invalid operator:", operator);
|
|
644
|
+
throw new Error(`Invalid operator: ${operator}`);
|
|
645
|
+
}
|
|
646
|
+
if (typeof itemValue === "string" && typeof value === "string") {
|
|
647
|
+
return this.applyStringOperation(itemValue, operator, value);
|
|
648
|
+
}
|
|
649
|
+
if (Array.isArray(value)) {
|
|
650
|
+
switch (operator) {
|
|
651
|
+
case "in":
|
|
652
|
+
return value.includes(itemValue);
|
|
653
|
+
case "nin":
|
|
654
|
+
return !value.includes(itemValue);
|
|
655
|
+
default:
|
|
656
|
+
logger.error("Invalid array operator:", operator);
|
|
657
|
+
throw new Error(`Invalid array operator: ${operator}`);
|
|
658
|
+
}
|
|
426
659
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
+
}
|
|
452
686
|
}
|
|
687
|
+
return false;
|
|
453
688
|
});
|
|
454
689
|
});
|
|
690
|
+
logger.debug("Filter application completed:", {
|
|
691
|
+
initialCount: data.length,
|
|
692
|
+
resultCount: result.length
|
|
693
|
+
});
|
|
694
|
+
return result;
|
|
455
695
|
}
|
|
456
696
|
applySorting(data, sorting) {
|
|
457
697
|
return [...data].sort((a, b) => {
|
|
458
|
-
for (const
|
|
459
|
-
|
|
460
|
-
|
|
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];
|
|
461
704
|
if (aValue === bValue)
|
|
462
705
|
continue;
|
|
463
|
-
const
|
|
464
|
-
return
|
|
706
|
+
const compareResult = aValue < bValue ? -1 : 1;
|
|
707
|
+
return direction === "asc" ? compareResult : -compareResult;
|
|
465
708
|
}
|
|
466
709
|
return 0;
|
|
467
710
|
});
|
|
@@ -472,15 +715,26 @@ var _QueryExecutor = class _QueryExecutor {
|
|
|
472
715
|
return data.slice(offset, offset + limit);
|
|
473
716
|
}
|
|
474
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
|
+
});
|
|
475
724
|
const result = [...data];
|
|
476
725
|
for (const [field, config] of Object.entries(includes)) {
|
|
726
|
+
logger.debug(`Resolving relation "${field}"`);
|
|
477
727
|
const relations = await this.loader.resolveRelation(
|
|
478
728
|
model,
|
|
479
729
|
field,
|
|
480
730
|
result,
|
|
481
731
|
options.locale
|
|
482
732
|
);
|
|
733
|
+
logger.debug(`Relation "${field}" resolved:`, {
|
|
734
|
+
foundRelationsCount: relations.length
|
|
735
|
+
});
|
|
483
736
|
if (config.include && relations.length) {
|
|
737
|
+
logger.debug(`Resolving nested relations for "${field}":`, config.include);
|
|
484
738
|
await this.resolveIncludes(
|
|
485
739
|
field,
|
|
486
740
|
relations,
|
|
@@ -501,9 +755,28 @@ var _QueryExecutor = class _QueryExecutor {
|
|
|
501
755
|
}
|
|
502
756
|
item._relations[field] = Array.isArray(value) ? relatedItems : relatedItems[0];
|
|
503
757
|
});
|
|
758
|
+
logger.debug(`Data added for relation "${field}"`);
|
|
504
759
|
}
|
|
505
760
|
return result;
|
|
506
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
|
+
}
|
|
507
780
|
async execute({
|
|
508
781
|
model,
|
|
509
782
|
data,
|
|
@@ -513,17 +786,37 @@ var _QueryExecutor = class _QueryExecutor {
|
|
|
513
786
|
pagination = {},
|
|
514
787
|
options = {}
|
|
515
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
|
+
});
|
|
516
798
|
let result = [...data];
|
|
517
799
|
if (filters.length) {
|
|
800
|
+
logger.debug("Applying filters:", filters);
|
|
518
801
|
result = this.applyFilters(result, filters);
|
|
802
|
+
logger.debug("Remaining items after filtering:", result.length);
|
|
519
803
|
}
|
|
520
804
|
if (Object.keys(includes).length) {
|
|
805
|
+
logger.debug("Resolving relations:", includes);
|
|
521
806
|
result = await this.resolveIncludes(model, result, includes, options);
|
|
807
|
+
logger.debug("Items after relation resolution:", result.length);
|
|
522
808
|
}
|
|
523
809
|
if (sorting.length) {
|
|
810
|
+
logger.debug("Applying sorting:", sorting);
|
|
524
811
|
result = this.applySorting(result, sorting);
|
|
525
812
|
}
|
|
526
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
|
+
});
|
|
527
820
|
return {
|
|
528
821
|
data: paginatedData,
|
|
529
822
|
total: result.length,
|
|
@@ -554,10 +847,19 @@ var _ContentrainSDK = class _ContentrainSDK {
|
|
|
554
847
|
async load(model) {
|
|
555
848
|
return this.loader.load(model);
|
|
556
849
|
}
|
|
850
|
+
async clearCache() {
|
|
851
|
+
return this.loader.clearCache();
|
|
852
|
+
}
|
|
853
|
+
async refreshCache(model) {
|
|
854
|
+
return this.loader.refreshCache(model);
|
|
855
|
+
}
|
|
856
|
+
getCacheStats() {
|
|
857
|
+
return this.loader.getCacheStats();
|
|
858
|
+
}
|
|
557
859
|
};
|
|
558
860
|
__name(_ContentrainSDK, "ContentrainSDK");
|
|
559
861
|
var ContentrainSDK = _ContentrainSDK;
|
|
560
862
|
|
|
561
|
-
export { ContentLoader, ContentrainQueryBuilder, ContentrainSDK, MemoryCache, QueryExecutor };
|
|
863
|
+
export { ContentLoader, ContentrainQueryBuilder, ContentrainSDK, MemoryCache, QueryExecutor, logger };
|
|
562
864
|
//# sourceMappingURL=index.mjs.map
|
|
563
865
|
//# sourceMappingURL=index.mjs.map
|