@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.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 dakika
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 (locale) {
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 = this.getCacheKey(model);
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 = ["en", "tr"];
336
+ const locales = await this.getModelLocales(model, modelConfig);
258
337
  for (const locale of locales) {
259
- const file = await this.loadContentFile(model, locale);
260
- content[locale] = file.data;
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
- const relatedData = locale ? relatedContent.content[locale] : relatedContent.content.en;
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
- return data.map((item) => {
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
- return data.flatMap((item) => {
299
- const ids = Array.isArray(item[relationField]) ? item[relationField] : [item[relationField]];
300
- const items = ids.map((id) => relatedData.find((r) => r.ID === id)).filter(Boolean);
301
- if (items.length !== ids.length) {
302
- throw new Error("Failed to resolve relation: Some related items not found");
303
- }
304
- return items;
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 data = this.options.locale ? result.content[this.options.locale] : result.content.en;
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
- return data.filter((item) => {
421
- return filters.every((filter) => {
422
- const value = item[filter.field];
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(filter.operator)) {
425
- throw new Error(`Invalid operator: ${filter.operator}`);
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
- switch (filter.operator) {
428
- case "eq":
429
- return value === filter.value;
430
- case "ne":
431
- return value !== filter.value;
432
- case "gt":
433
- return value > filter.value;
434
- case "gte":
435
- return value >= filter.value;
436
- case "lt":
437
- return value < filter.value;
438
- case "lte":
439
- return value <= filter.value;
440
- case "in":
441
- return Array.isArray(filter.value) && filter.value.includes(value);
442
- case "nin":
443
- return Array.isArray(filter.value) && !filter.value.includes(value);
444
- case "contains":
445
- return typeof value === "string" && value.includes(filter.value);
446
- case "startsWith":
447
- return typeof value === "string" && value.startsWith(filter.value);
448
- case "endsWith":
449
- return typeof value === "string" && value.endsWith(filter.value);
450
- default:
451
- return false;
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 sort of sorting) {
459
- const aValue = a[sort.field];
460
- const bValue = b[sort.field];
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 direction = sort.direction === "asc" ? 1 : -1;
464
- return aValue > bValue ? direction : -direction;
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