@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.js CHANGED
@@ -3,9 +3,25 @@
3
3
  var promises = require('fs/promises');
4
4
  var path = require('path');
5
5
  var tinyLru = require('tiny-lru');
6
+ var process = require('process');
6
7
 
7
8
  var __defProp = Object.defineProperty;
8
9
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
+ var isDevelopment = process.env.NODE_ENV === "development";
11
+ var logger = {
12
+ debug: /* @__PURE__ */ __name((...args) => {
13
+ if (isDevelopment) {
14
+ console.log(...args);
15
+ }
16
+ }, "debug"),
17
+ error: /* @__PURE__ */ __name((...args) => {
18
+ if (isDevelopment) {
19
+ console.error(...args);
20
+ }
21
+ }, "error")
22
+ };
23
+
24
+ // src/cache/memory.ts
9
25
  var _MemoryCache = class _MemoryCache {
10
26
  constructor(options = {}) {
11
27
  this.stats = {
@@ -29,6 +45,10 @@ var _MemoryCache = class _MemoryCache {
29
45
  return new TextEncoder().encode(str).length;
30
46
  }
31
47
  async set(key, data, ttl) {
48
+ logger.debug("Saving data to cache:", {
49
+ key,
50
+ ttl
51
+ });
32
52
  await this.cleanupCache();
33
53
  const size = this.calculateSize(data);
34
54
  const now = Date.now();
@@ -51,6 +71,10 @@ var _MemoryCache = class _MemoryCache {
51
71
  }
52
72
  this.cache.set(key, entry);
53
73
  this.stats.size += size;
74
+ logger.debug("Data saved to cache:", {
75
+ key,
76
+ expiry: expireAt ? new Date(expireAt).toISOString() : "no expiry"
77
+ });
54
78
  }
55
79
  findOldestKey() {
56
80
  let oldestKey = null;
@@ -65,8 +89,10 @@ var _MemoryCache = class _MemoryCache {
65
89
  return oldestKey;
66
90
  }
67
91
  async get(key) {
92
+ logger.debug("Getting data from cache:", { key });
68
93
  const entry = this.cache.get(key);
69
94
  if (!entry) {
95
+ logger.debug("Data not found in cache:", { key });
70
96
  this.stats.misses++;
71
97
  return null;
72
98
  }
@@ -75,17 +101,24 @@ var _MemoryCache = class _MemoryCache {
75
101
  this.stats.misses++;
76
102
  return null;
77
103
  }
104
+ logger.debug("Data retrieved from cache:", {
105
+ key,
106
+ expiry: entry.expireAt ? new Date(entry.expireAt).toISOString() : "no expiry"
107
+ });
78
108
  this.stats.hits++;
79
109
  return entry.data;
80
110
  }
81
111
  async delete(key) {
112
+ logger.debug("Deleting data from cache:", { key });
82
113
  const entry = this.cache.get(key);
83
114
  if (entry) {
84
115
  this.stats.size -= entry.size;
85
116
  this.cache.delete(key);
86
117
  }
118
+ logger.debug("Data deleted from cache:", { key });
87
119
  }
88
120
  async clear() {
121
+ logger.debug("Clearing cache");
89
122
  this.cache.clear();
90
123
  this.stats = {
91
124
  hits: 0,
@@ -93,6 +126,7 @@ var _MemoryCache = class _MemoryCache {
93
126
  size: 0,
94
127
  lastCleanup: Date.now()
95
128
  };
129
+ logger.debug("Cache cleared");
96
130
  }
97
131
  async cleanupCache() {
98
132
  const now = Date.now();
@@ -135,7 +169,7 @@ var _ContentLoader = class _ContentLoader {
135
169
  defaultLocale: "en",
136
170
  cache: true,
137
171
  ttl: 60 * 1e3,
138
- // 1 dakika
172
+ // 1 minute
139
173
  maxCacheSize: 100,
140
174
  // 100 MB
141
175
  ...options
@@ -169,33 +203,46 @@ var _ContentLoader = class _ContentLoader {
169
203
  const allMetadata = JSON.parse(metadataContent);
170
204
  const modelMetadata = allMetadata.find((m) => m.modelId === model);
171
205
  if (!modelMetadata) {
206
+ logger.error("Model metadata not found:", {
207
+ model,
208
+ metadataPath
209
+ });
172
210
  throw new Error(`Model metadata not found for ${model}`);
173
211
  }
174
212
  const modelPath = path.join(this.options.contentDir, "models", `${model}.json`);
175
213
  const modelContent = await promises.readFile(modelPath, "utf-8");
176
214
  const modelFields = JSON.parse(modelContent);
177
- if (!Array.isArray(modelFields)) {
178
- throw new TypeError(`Invalid field configuration for model ${model}: Expected an array of fields`);
179
- }
180
- modelFields.forEach((field, index) => {
181
- if (!field.fieldId || !field.fieldType || !field.componentId) {
182
- throw new Error(`Invalid field at index ${index} for model ${model}: Missing required properties`);
183
- }
184
- });
185
215
  return {
186
216
  metadata: modelMetadata,
187
217
  fields: modelFields
188
218
  };
189
219
  } catch (error) {
220
+ logger.error("Failed to load model config:", {
221
+ model,
222
+ error: error?.message || "Unknown error"
223
+ });
190
224
  throw new Error(`Failed to load model config for ${model}: ${error?.message || "Unknown error"}`);
191
225
  }
192
226
  }
193
- async loadContentFile(model, locale) {
227
+ async loadContentFile(model, locale = "default") {
194
228
  try {
229
+ const modelConfig = await this.loadModelConfig(model);
195
230
  let contentPath;
196
- if (locale) {
231
+ if (modelConfig.metadata.localization) {
232
+ if (!locale || locale === "default") {
233
+ if (!this.options.defaultLocale) {
234
+ logger.error("Default locale is required for localized model:", {
235
+ model
236
+ });
237
+ throw new Error(`Default locale is required for localized model "${model}"`);
238
+ }
239
+ locale = this.options.defaultLocale;
240
+ }
197
241
  contentPath = path.join(this.options.contentDir, model, `${locale}.json`);
198
242
  } else {
243
+ if (locale !== "default") {
244
+ console.warn(`Locale "${locale}" specified for non-localized model "${model}". This parameter will be ignored.`);
245
+ }
199
246
  contentPath = path.join(this.options.contentDir, model, `${model}.json`);
200
247
  }
201
248
  const content = await promises.readFile(contentPath, "utf-8");
@@ -203,14 +250,23 @@ var _ContentLoader = class _ContentLoader {
203
250
  const data = JSON.parse(content);
204
251
  return {
205
252
  model,
206
- locale,
253
+ locale: modelConfig.metadata.localization ? locale : void 0,
207
254
  data
208
255
  };
209
256
  } catch {
257
+ logger.error("Failed to load content:", {
258
+ model,
259
+ locale,
260
+ contentPath
261
+ });
210
262
  throw new Error(`Failed to load content: Invalid JSON format in ${contentPath}`);
211
263
  }
212
264
  } catch (error) {
213
265
  if (error.message.includes("Invalid JSON format")) {
266
+ logger.error("Failed to load content:", {
267
+ model,
268
+ locale
269
+ });
214
270
  throw error;
215
271
  }
216
272
  throw new Error(
@@ -243,8 +299,31 @@ var _ContentLoader = class _ContentLoader {
243
299
  throw new Error(`Failed to load relations for ${model}: ${error?.message || "Unknown error"}`);
244
300
  }
245
301
  }
302
+ async getModelLocales(model, modelConfig) {
303
+ try {
304
+ if (!modelConfig.metadata.localization) {
305
+ return ["default"];
306
+ }
307
+ const modelDir = path.join(this.options.contentDir, model);
308
+ const files = await promises.readdir(modelDir);
309
+ const locales = files.filter((file) => file.endsWith(".json")).map((file) => file.replace(".json", "")).filter((locale) => locale !== model);
310
+ if (locales.length === 0) {
311
+ if (!this.options.defaultLocale) {
312
+ throw new Error(`No locale files found for localized model "${model}" and no default locale specified`);
313
+ }
314
+ return [this.options.defaultLocale];
315
+ }
316
+ return locales;
317
+ } catch (error) {
318
+ if (!this.options.defaultLocale) {
319
+ throw new Error(`Failed to read locales for model ${model} and no default locale specified: ${error?.message}`);
320
+ }
321
+ console.warn(`Failed to read locales for model ${model}: ${error?.message}`);
322
+ return [this.options.defaultLocale];
323
+ }
324
+ }
246
325
  async load(model) {
247
- const cacheKey = this.getCacheKey(model);
326
+ const cacheKey = `${model}`;
248
327
  if (this.options.cache) {
249
328
  const cached = await this.cache.get(cacheKey);
250
329
  if (cached)
@@ -256,18 +335,34 @@ var _ContentLoader = class _ContentLoader {
256
335
  this.relations.set(model, relations);
257
336
  const content = {};
258
337
  if (modelConfig.metadata.localization) {
259
- const locales = ["en", "tr"];
338
+ const locales = await this.getModelLocales(model, modelConfig);
260
339
  for (const locale of locales) {
261
- const file = await this.loadContentFile(model, locale);
262
- content[locale] = file.data;
340
+ try {
341
+ const file = await this.loadContentFile(model, locale);
342
+ content[locale] = file.data;
343
+ } catch (error) {
344
+ console.warn(`Failed to load content for locale ${locale}: ${error?.message}`);
345
+ if (locale === this.options.defaultLocale) {
346
+ throw error;
347
+ }
348
+ }
263
349
  }
264
350
  } else {
265
351
  const file = await this.loadContentFile(model);
266
352
  content.default = file.data;
267
353
  }
354
+ let assets = [];
355
+ try {
356
+ const assetsPath = path.join(this.options.contentDir, "assets.json");
357
+ const assetsContent = await promises.readFile(assetsPath, "utf-8");
358
+ assets = JSON.parse(assetsContent);
359
+ } catch (error) {
360
+ console.warn("Assets file not found or cannot be read:", error);
361
+ }
268
362
  const result = {
269
363
  model: modelConfig,
270
- content
364
+ content,
365
+ assets
271
366
  };
272
367
  if (this.options.cache) {
273
368
  const ttl = this.getModelTTL(model);
@@ -277,19 +372,65 @@ var _ContentLoader = class _ContentLoader {
277
372
  }
278
373
  async resolveRelation(model, relationField, data, locale) {
279
374
  try {
375
+ logger.debug("Debug - Starting relation resolution:", {
376
+ model,
377
+ relationField,
378
+ dataLength: data.length,
379
+ locale
380
+ });
280
381
  const relations = this.relations.get(model);
382
+ logger.debug("Debug - Relations:", relations);
281
383
  if (!relations)
282
384
  throw new Error(`No relations found for model: ${model}`);
283
385
  const relation = relations.find((r) => r.foreignKey === relationField);
386
+ logger.debug("Debug - Found relation:", relation);
284
387
  if (!relation)
285
388
  throw new Error(`No relation found for field: ${String(relationField)}`);
389
+ logger.debug("Debug - Related model loading:", relation.model);
286
390
  const relatedContent = await this.load(relation.model);
287
- const relatedData = locale ? relatedContent.content[locale] : relatedContent.content.en;
391
+ logger.debug("Debug - \u0130li\u015Fkili model y\xFCklendi:", {
392
+ model: relation.model,
393
+ metadata: relatedContent.model.metadata,
394
+ contentKeys: Object.keys(relatedContent.content)
395
+ });
396
+ let relatedData;
397
+ if (relatedContent.model.metadata.localization) {
398
+ logger.debug("Debug - Processing localized model");
399
+ const localizedContent = locale ? relatedContent.content[locale] : relatedContent.content.en;
400
+ logger.debug("Debug - Localized content:", {
401
+ locale: locale || "en",
402
+ contentType: typeof localizedContent,
403
+ isArray: Array.isArray(localizedContent)
404
+ });
405
+ if (!Array.isArray(localizedContent)) {
406
+ throw new TypeError(`Invalid content format for localized model ${relation.model}`);
407
+ }
408
+ relatedData = localizedContent;
409
+ } else {
410
+ logger.debug("Debug - Processing non-localized model");
411
+ const nonLocalizedContent = relatedContent.content.default;
412
+ logger.debug("Debug - Raw content:", {
413
+ contentType: typeof nonLocalizedContent,
414
+ isArray: Array.isArray(nonLocalizedContent),
415
+ content: nonLocalizedContent
416
+ });
417
+ if (!Array.isArray(nonLocalizedContent)) {
418
+ throw new TypeError(`Invalid content format for non-localized model ${relation.model}`);
419
+ }
420
+ relatedData = nonLocalizedContent;
421
+ }
422
+ logger.debug("Debug - Related data ready:", {
423
+ dataLength: relatedData.length,
424
+ firstItem: relatedData[0]
425
+ });
288
426
  if (!relatedData) {
289
427
  throw new Error(`Failed to resolve relation: No data found for model ${relation.model}`);
290
428
  }
291
429
  if (relation.type === "one-to-one") {
292
- return data.map((item) => {
430
+ logger.debug("Debug - Processing one-to-one relation");
431
+ const itemsWithRelation = data.filter((item) => item[relationField] !== void 0);
432
+ logger.debug("Debug - Items with relations:", itemsWithRelation.length);
433
+ return itemsWithRelation.map((item) => {
293
434
  const relatedItem = relatedData.find((r) => r.ID === item[relationField]);
294
435
  if (!relatedItem) {
295
436
  throw new Error(`Failed to resolve relation: No matching item found for ID ${String(item[relationField])}`);
@@ -297,16 +438,22 @@ var _ContentLoader = class _ContentLoader {
297
438
  return relatedItem;
298
439
  });
299
440
  } else {
300
- return data.flatMap((item) => {
301
- const ids = Array.isArray(item[relationField]) ? item[relationField] : [item[relationField]];
302
- const items = ids.map((id) => relatedData.find((r) => r.ID === id)).filter(Boolean);
303
- if (items.length !== ids.length) {
304
- throw new Error("Failed to resolve relation: Some related items not found");
305
- }
306
- return items;
307
- });
441
+ logger.debug("Debug - Processing one-to-many relation");
442
+ const uniqueIds = new Set(
443
+ data.flatMap(
444
+ (item) => item[relationField] !== void 0 ? Array.isArray(item[relationField]) ? item[relationField] : [item[relationField]] : []
445
+ )
446
+ );
447
+ logger.debug("Debug - Unique IDs:", Array.from(uniqueIds));
448
+ const items = Array.from(uniqueIds).map((id) => relatedData.find((r) => r.ID === id)).filter(Boolean);
449
+ logger.debug("Debug - Matching items:", items.length);
450
+ if (items.length !== uniqueIds.size) {
451
+ throw new Error("Failed to resolve relation: Some related items not found");
452
+ }
453
+ return items;
308
454
  }
309
455
  } catch (error) {
456
+ logger.error("Debug - Error occurred:", error);
310
457
  throw new Error(`Failed to resolve relation: ${error.message}`);
311
458
  }
312
459
  }
@@ -327,6 +474,11 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
327
474
  this.loader = loader;
328
475
  }
329
476
  where(field, operator, value) {
477
+ logger.debug("Adding filter:", {
478
+ field,
479
+ operator,
480
+ value
481
+ });
330
482
  this.filters.push({
331
483
  field,
332
484
  operator,
@@ -335,6 +487,7 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
335
487
  return this;
336
488
  }
337
489
  include(relation) {
490
+ logger.debug("Adding relation:", relation);
338
491
  if (typeof relation === "string") {
339
492
  this.includes[relation] = {};
340
493
  } else if (Array.isArray(relation)) {
@@ -345,6 +498,10 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
345
498
  return this;
346
499
  }
347
500
  orderBy(field, direction = "asc") {
501
+ logger.debug("Adding sorting:", {
502
+ field,
503
+ direction
504
+ });
348
505
  this.sorting.push({
349
506
  field,
350
507
  direction
@@ -352,28 +509,37 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
352
509
  return this;
353
510
  }
354
511
  limit(count) {
512
+ logger.debug("Adding limit:", count);
355
513
  this.pagination.limit = count;
356
514
  return this;
357
515
  }
358
516
  offset(count) {
517
+ logger.debug("Adding offset:", count);
359
518
  this.pagination.offset = count;
360
519
  return this;
361
520
  }
362
521
  locale(code) {
522
+ logger.debug("Setting locale:", code);
363
523
  this.options.locale = code;
364
524
  return this;
365
525
  }
366
526
  cache(ttl) {
527
+ logger.debug("Setting cache:", {
528
+ enabled: true,
529
+ ttl
530
+ });
367
531
  this.options.cache = true;
368
532
  if (ttl)
369
533
  this.options.ttl = ttl;
370
534
  return this;
371
535
  }
372
536
  noCache() {
537
+ logger.debug("Disabling cache");
373
538
  this.options.cache = false;
374
539
  return this;
375
540
  }
376
541
  bypassCache() {
542
+ logger.debug("Bypassing cache");
377
543
  this.options.cache = false;
378
544
  this.options.ttl = 0;
379
545
  return this;
@@ -389,8 +555,56 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
389
555
  };
390
556
  }
391
557
  async get() {
558
+ logger.debug("Starting query:", {
559
+ model: this.model,
560
+ filterCount: this.filters.length,
561
+ includeCount: Object.keys(this.includes).length,
562
+ sortingCount: this.sorting.length,
563
+ pagination: this.pagination,
564
+ options: this.options
565
+ });
392
566
  const result = await this.loader.load(this.model);
393
- const data = this.options.locale ? result.content[this.options.locale] : result.content.en;
567
+ const modelConfig = result.model;
568
+ logger.debug("Model loaded:", {
569
+ model: this.model,
570
+ metadata: modelConfig.metadata,
571
+ contentKeys: Object.keys(result.content)
572
+ });
573
+ let data;
574
+ if (modelConfig.metadata.localization) {
575
+ const locale = this.options.locale || "en";
576
+ logger.debug("Selecting content for localized model:", {
577
+ model: this.model,
578
+ requestedLocale: locale,
579
+ availableLocales: Object.keys(result.content)
580
+ });
581
+ data = result.content[locale];
582
+ if (!data) {
583
+ logger.error("Content not found:", {
584
+ model: this.model,
585
+ locale,
586
+ availableLocales: Object.keys(result.content)
587
+ });
588
+ throw new Error(`Content not found for locale: ${locale}`);
589
+ }
590
+ } else {
591
+ logger.debug("Selecting content for non-localized model:", {
592
+ model: this.model,
593
+ contentKeys: Object.keys(result.content)
594
+ });
595
+ if (!result.content.default) {
596
+ logger.error("Content not found:", {
597
+ model: this.model,
598
+ contentKeys: Object.keys(result.content)
599
+ });
600
+ throw new Error(`Content not found for model: ${this.model}`);
601
+ }
602
+ data = result.content.default;
603
+ }
604
+ logger.debug("Executing query:", {
605
+ model: this.model,
606
+ dataLength: data.length
607
+ });
394
608
  return this.executor.execute({
395
609
  model: this.model,
396
610
  data,
@@ -419,51 +633,80 @@ var _QueryExecutor = class _QueryExecutor {
419
633
  this.loader = loader;
420
634
  }
421
635
  applyFilters(data, filters) {
422
- return data.filter((item) => {
423
- return filters.every((filter) => {
424
- const value = item[filter.field];
636
+ logger.debug("Starting to apply filters:", {
637
+ dataLength: data.length,
638
+ filters
639
+ });
640
+ const result = data.filter((item) => {
641
+ return filters.every(({ field, operator, value }) => {
642
+ const itemValue = item[field];
425
643
  const validOperators = ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "contains", "startsWith", "endsWith"];
426
- if (!validOperators.includes(filter.operator)) {
427
- throw new Error(`Invalid operator: ${filter.operator}`);
644
+ if (!validOperators.includes(operator)) {
645
+ logger.error("Invalid operator:", operator);
646
+ throw new Error(`Invalid operator: ${operator}`);
647
+ }
648
+ if (typeof itemValue === "string" && typeof value === "string") {
649
+ return this.applyStringOperation(itemValue, operator, value);
650
+ }
651
+ if (Array.isArray(value)) {
652
+ switch (operator) {
653
+ case "in":
654
+ return value.includes(itemValue);
655
+ case "nin":
656
+ return !value.includes(itemValue);
657
+ default:
658
+ logger.error("Invalid array operator:", operator);
659
+ throw new Error(`Invalid array operator: ${operator}`);
660
+ }
428
661
  }
429
- switch (filter.operator) {
430
- case "eq":
431
- return value === filter.value;
432
- case "ne":
433
- return value !== filter.value;
434
- case "gt":
435
- return value > filter.value;
436
- case "gte":
437
- return value >= filter.value;
438
- case "lt":
439
- return value < filter.value;
440
- case "lte":
441
- return value <= filter.value;
442
- case "in":
443
- return Array.isArray(filter.value) && filter.value.includes(value);
444
- case "nin":
445
- return Array.isArray(filter.value) && !filter.value.includes(value);
446
- case "contains":
447
- return typeof value === "string" && value.includes(filter.value);
448
- case "startsWith":
449
- return typeof value === "string" && value.startsWith(filter.value);
450
- case "endsWith":
451
- return typeof value === "string" && value.endsWith(filter.value);
452
- default:
453
- return false;
662
+ if (Array.isArray(itemValue)) {
663
+ switch (operator) {
664
+ case "in":
665
+ return value.some((v) => itemValue.includes(v));
666
+ case "nin":
667
+ return !value.some((v) => itemValue.includes(v));
668
+ default:
669
+ logger.error("Invalid array operator:", operator);
670
+ throw new Error(`Invalid array operator: ${operator}`);
671
+ }
672
+ }
673
+ if (typeof itemValue === "number" && typeof value === "number") {
674
+ switch (operator) {
675
+ case "eq":
676
+ return itemValue === value;
677
+ case "ne":
678
+ return itemValue !== value;
679
+ case "gt":
680
+ return itemValue > value;
681
+ case "gte":
682
+ return itemValue >= value;
683
+ case "lt":
684
+ return itemValue < value;
685
+ case "lte":
686
+ return itemValue <= value;
687
+ }
454
688
  }
689
+ return false;
455
690
  });
456
691
  });
692
+ logger.debug("Filter application completed:", {
693
+ initialCount: data.length,
694
+ resultCount: result.length
695
+ });
696
+ return result;
457
697
  }
458
698
  applySorting(data, sorting) {
459
699
  return [...data].sort((a, b) => {
460
- for (const sort of sorting) {
461
- const aValue = a[sort.field];
462
- const bValue = b[sort.field];
700
+ for (const { field, direction } of sorting) {
701
+ if (!(field in a)) {
702
+ throw new Error(`Invalid sort field: ${field}`);
703
+ }
704
+ const aValue = a[field];
705
+ const bValue = b[field];
463
706
  if (aValue === bValue)
464
707
  continue;
465
- const direction = sort.direction === "asc" ? 1 : -1;
466
- return aValue > bValue ? direction : -direction;
708
+ const compareResult = aValue < bValue ? -1 : 1;
709
+ return direction === "asc" ? compareResult : -compareResult;
467
710
  }
468
711
  return 0;
469
712
  });
@@ -474,15 +717,26 @@ var _QueryExecutor = class _QueryExecutor {
474
717
  return data.slice(offset, offset + limit);
475
718
  }
476
719
  async resolveIncludes(model, data, includes, options) {
720
+ logger.debug("Starting to resolve relations:", {
721
+ model,
722
+ dataLength: data.length,
723
+ includes,
724
+ options
725
+ });
477
726
  const result = [...data];
478
727
  for (const [field, config] of Object.entries(includes)) {
728
+ logger.debug(`Resolving relation "${field}"`);
479
729
  const relations = await this.loader.resolveRelation(
480
730
  model,
481
731
  field,
482
732
  result,
483
733
  options.locale
484
734
  );
735
+ logger.debug(`Relation "${field}" resolved:`, {
736
+ foundRelationsCount: relations.length
737
+ });
485
738
  if (config.include && relations.length) {
739
+ logger.debug(`Resolving nested relations for "${field}":`, config.include);
486
740
  await this.resolveIncludes(
487
741
  field,
488
742
  relations,
@@ -503,9 +757,28 @@ var _QueryExecutor = class _QueryExecutor {
503
757
  }
504
758
  item._relations[field] = Array.isArray(value) ? relatedItems : relatedItems[0];
505
759
  });
760
+ logger.debug(`Data added for relation "${field}"`);
506
761
  }
507
762
  return result;
508
763
  }
764
+ applyStringOperation(value, operator, searchValue) {
765
+ switch (operator) {
766
+ case "eq":
767
+ return value === searchValue;
768
+ case "ne":
769
+ return value !== searchValue;
770
+ case "contains":
771
+ return value.toLowerCase().includes(searchValue.toLowerCase());
772
+ case "startsWith":
773
+ return value.toLowerCase().startsWith(searchValue.toLowerCase());
774
+ case "endsWith":
775
+ return value.toLowerCase().endsWith(searchValue.toLowerCase());
776
+ default: {
777
+ const _exhaustiveCheck = operator;
778
+ return _exhaustiveCheck;
779
+ }
780
+ }
781
+ }
509
782
  async execute({
510
783
  model,
511
784
  data,
@@ -515,17 +788,37 @@ var _QueryExecutor = class _QueryExecutor {
515
788
  pagination = {},
516
789
  options = {}
517
790
  }) {
791
+ logger.debug("Starting execution:", {
792
+ model,
793
+ dataLength: data.length,
794
+ filterCount: filters.length,
795
+ includeCount: Object.keys(includes).length,
796
+ sortingCount: sorting.length,
797
+ pagination,
798
+ options
799
+ });
518
800
  let result = [...data];
519
801
  if (filters.length) {
802
+ logger.debug("Applying filters:", filters);
520
803
  result = this.applyFilters(result, filters);
804
+ logger.debug("Remaining items after filtering:", result.length);
521
805
  }
522
806
  if (Object.keys(includes).length) {
807
+ logger.debug("Resolving relations:", includes);
523
808
  result = await this.resolveIncludes(model, result, includes, options);
809
+ logger.debug("Items after relation resolution:", result.length);
524
810
  }
525
811
  if (sorting.length) {
812
+ logger.debug("Applying sorting:", sorting);
526
813
  result = this.applySorting(result, sorting);
527
814
  }
528
815
  const paginatedData = this.applyPagination(result, pagination.limit, pagination.offset);
816
+ logger.debug("After pagination:", {
817
+ totalCount: result.length,
818
+ pageSize: paginatedData.length,
819
+ offset: pagination.offset || 0,
820
+ hasMore: (pagination.offset || 0) + paginatedData.length < result.length
821
+ });
529
822
  return {
530
823
  data: paginatedData,
531
824
  total: result.length,
@@ -556,6 +849,15 @@ var _ContentrainSDK = class _ContentrainSDK {
556
849
  async load(model) {
557
850
  return this.loader.load(model);
558
851
  }
852
+ async clearCache() {
853
+ return this.loader.clearCache();
854
+ }
855
+ async refreshCache(model) {
856
+ return this.loader.refreshCache(model);
857
+ }
858
+ getCacheStats() {
859
+ return this.loader.getCacheStats();
860
+ }
559
861
  };
560
862
  __name(_ContentrainSDK, "ContentrainSDK");
561
863
  var ContentrainSDK = _ContentrainSDK;
@@ -565,5 +867,6 @@ exports.ContentrainQueryBuilder = ContentrainQueryBuilder;
565
867
  exports.ContentrainSDK = ContentrainSDK;
566
868
  exports.MemoryCache = MemoryCache;
567
869
  exports.QueryExecutor = QueryExecutor;
870
+ exports.logger = logger;
568
871
  //# sourceMappingURL=index.js.map
569
872
  //# sourceMappingURL=index.js.map