@contentrain/query 3.1.0 → 4.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/dist/index.js CHANGED
@@ -1,669 +1,30 @@
1
- 'use strict';
2
-
3
- var promises = require('fs/promises');
4
- var path = require('path');
5
- var tinyLru = require('tiny-lru');
6
-
7
- var __defProp = Object.defineProperty;
8
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
- var _MemoryCache = class _MemoryCache {
10
- constructor(options = {}) {
11
- this.stats = {
12
- hits: 0,
13
- misses: 0,
14
- size: 0,
15
- lastCleanup: Date.now()
16
- };
17
- this.options = {
18
- maxSize: 100,
19
- // 100 MB
20
- defaultTTL: 60 * 1e3,
21
- // 1 dakika
22
- ...options
23
- };
24
- const maxItems = Math.floor(this.options.maxSize * 1024 * 1024 / 1e3);
25
- this.cache = tinyLru.lru(maxItems);
26
- }
27
- calculateSize(data) {
28
- const str = JSON.stringify(data);
29
- return new TextEncoder().encode(str).length;
30
- }
31
- async set(key, data, ttl) {
32
- await this.cleanupCache();
33
- const size = this.calculateSize(data);
34
- const now = Date.now();
35
- const expireAt = now + (ttl || this.options.defaultTTL);
36
- while (size + this.stats.size > this.options.maxSize * 1024 * 1024) {
37
- const oldestKey = this.findOldestKey();
38
- if (!oldestKey)
39
- break;
40
- await this.delete(oldestKey);
41
- }
42
- const entry = {
43
- data,
44
- expireAt,
45
- size,
46
- createdAt: now
47
- };
48
- const oldEntry = this.cache.get(key);
49
- if (oldEntry) {
50
- this.stats.size -= oldEntry.size;
51
- }
52
- this.cache.set(key, entry);
53
- this.stats.size += size;
54
- }
55
- findOldestKey() {
56
- let oldestKey = null;
57
- let oldestTime = Infinity;
58
- for (const key of this.cache.keys()) {
59
- const entry = this.cache.get(key);
60
- if (entry.createdAt < oldestTime) {
61
- oldestTime = entry.createdAt;
62
- oldestKey = key;
63
- }
64
- }
65
- return oldestKey;
66
- }
67
- async get(key) {
68
- const entry = this.cache.get(key);
69
- if (!entry) {
70
- this.stats.misses++;
71
- return null;
72
- }
73
- if (Date.now() >= entry.expireAt) {
74
- await this.delete(key);
75
- this.stats.misses++;
76
- return null;
77
- }
78
- this.stats.hits++;
79
- return entry.data;
80
- }
81
- async delete(key) {
82
- const entry = this.cache.get(key);
83
- if (entry) {
84
- this.stats.size -= entry.size;
85
- this.cache.delete(key);
86
- }
87
- }
88
- async clear() {
89
- this.cache.clear();
90
- this.stats = {
91
- hits: 0,
92
- misses: 0,
93
- size: 0,
94
- lastCleanup: Date.now()
95
- };
96
- }
97
- async cleanupCache() {
98
- const now = Date.now();
99
- const expiredKeys = [];
100
- let totalSize = 0;
101
- for (const key of this.cache.keys()) {
102
- const entry = this.cache.get(key);
103
- if (entry.expireAt <= now) {
104
- expiredKeys.push(key);
105
- } else {
106
- totalSize += entry.size;
107
- }
108
- }
109
- for (const key of expiredKeys) {
110
- await this.delete(key);
111
- }
112
- while (totalSize > this.options.maxSize * 1024 * 1024) {
113
- const oldestKey = this.findOldestKey();
114
- if (!oldestKey)
115
- break;
116
- const entry = this.cache.get(oldestKey);
117
- await this.delete(oldestKey);
118
- totalSize -= entry.size;
119
- }
120
- this.stats.lastCleanup = now;
121
- }
122
- getStats() {
123
- return { ...this.stats };
124
- }
125
- };
126
- __name(_MemoryCache, "MemoryCache");
127
- var MemoryCache = _MemoryCache;
128
-
129
- // src/loader/content.ts
130
- var _ContentLoader = class _ContentLoader {
131
- constructor(options) {
132
- this.modelConfigs = /* @__PURE__ */ new Map();
133
- this.relations = /* @__PURE__ */ new Map();
134
- this.options = {
135
- defaultLocale: "en",
136
- cache: true,
137
- ttl: 60 * 1e3,
138
- // 1 dakika
139
- maxCacheSize: 100,
140
- // 100 MB
141
- ...options
142
- };
143
- this.cache = new MemoryCache({
144
- maxSize: this.options.maxCacheSize,
145
- defaultTTL: this.options.ttl
146
- });
147
- }
148
- getCacheKey(model) {
149
- return `${model}`;
150
- }
151
- getModelTTL(model) {
152
- return this.options.modelTTL?.[model] || this.options.ttl || 0;
153
- }
154
- async clearCache() {
155
- await this.cache.clear();
156
- }
157
- async refreshCache(model) {
158
- const cacheKey = this.getCacheKey(model);
159
- await this.cache.delete(cacheKey);
160
- await this.load(model);
161
- }
162
- getCacheStats() {
163
- return this.cache.getStats();
164
- }
165
- async loadModelConfig(model) {
166
- try {
167
- const metadataPath = path.join(this.options.contentDir, "models", "metadata.json");
168
- const metadataContent = await promises.readFile(metadataPath, "utf-8");
169
- const allMetadata = JSON.parse(metadataContent);
170
- const modelMetadata = allMetadata.find((m) => m.modelId === model);
171
- if (!modelMetadata) {
172
- throw new Error(`Model metadata not found for ${model}`);
173
- }
174
- const modelPath = path.join(this.options.contentDir, "models", `${model}.json`);
175
- const modelContent = await promises.readFile(modelPath, "utf-8");
176
- const modelFields = JSON.parse(modelContent);
177
- return {
178
- metadata: modelMetadata,
179
- fields: modelFields
180
- };
181
- } catch (error) {
182
- throw new Error(`Failed to load model config for ${model}: ${error?.message || "Unknown error"}`);
183
- }
184
- }
185
- async loadContentFile(model, locale = "default") {
186
- try {
187
- const modelConfig = await this.loadModelConfig(model);
188
- let contentPath;
189
- if (modelConfig.metadata.localization) {
190
- if (!locale || locale === "default") {
191
- if (!this.options.defaultLocale) {
192
- throw new Error(`Default locale is required for localized model "${model}"`);
193
- }
194
- locale = this.options.defaultLocale;
195
- }
196
- contentPath = path.join(this.options.contentDir, model, `${locale}.json`);
197
- } else {
198
- if (locale !== "default") {
199
- console.warn(`Locale "${locale}" specified for non-localized model "${model}". This parameter will be ignored.`);
200
- }
201
- contentPath = path.join(this.options.contentDir, model, `${model}.json`);
202
- }
203
- const content = await promises.readFile(contentPath, "utf-8");
204
- try {
205
- const data = JSON.parse(content);
206
- return {
207
- model,
208
- locale: modelConfig.metadata.localization ? locale : void 0,
209
- data
210
- };
211
- } catch {
212
- throw new Error(`Failed to load content: Invalid JSON format in ${contentPath}`);
213
- }
214
- } catch (error) {
215
- if (error.message.includes("Invalid JSON format")) {
216
- throw error;
217
- }
218
- throw new Error(
219
- `Failed to load content file for ${model}${locale ? ` (${locale})` : ""}: ${error?.message || "Unknown error"}`
220
- );
221
- }
222
- }
223
- async loadRelations(model) {
224
- try {
225
- const modelConfig = this.modelConfigs.get(model);
226
- if (!modelConfig) {
227
- throw new Error(`Model config not found for ${model}`);
228
- }
229
- const relationFields = modelConfig.fields.filter((field) => {
230
- return field.fieldType === "relation";
231
- });
232
- return relationFields.map((field) => {
233
- const options = field.options;
234
- const reference = options?.reference?.form?.reference?.value;
235
- if (!reference) {
236
- throw new Error(`Reference not found for relation field: ${field.name}`);
237
- }
238
- return {
239
- model: reference,
240
- type: field.componentId === "one-to-one" ? "one-to-one" : "one-to-many",
241
- foreignKey: field.fieldId
242
- };
243
- });
244
- } catch (error) {
245
- throw new Error(`Failed to load relations for ${model}: ${error?.message || "Unknown error"}`);
246
- }
247
- }
248
- async getModelLocales(model, modelConfig) {
249
- try {
250
- if (!modelConfig.metadata.localization) {
251
- return ["default"];
252
- }
253
- const modelDir = path.join(this.options.contentDir, model);
254
- const files = await promises.readdir(modelDir);
255
- const locales = files.filter((file) => file.endsWith(".json")).map((file) => file.replace(".json", "")).filter((locale) => locale !== model);
256
- if (locales.length === 0) {
257
- if (!this.options.defaultLocale) {
258
- throw new Error(`No locale files found for localized model "${model}" and no default locale specified`);
259
- }
260
- return [this.options.defaultLocale];
261
- }
262
- return locales;
263
- } catch (error) {
264
- if (!this.options.defaultLocale) {
265
- throw new Error(`Failed to read locales for model ${model} and no default locale specified: ${error?.message}`);
266
- }
267
- console.warn(`Failed to read locales for model ${model}: ${error?.message}`);
268
- return [this.options.defaultLocale];
269
- }
270
- }
271
- async load(model) {
272
- const cacheKey = `${model}`;
273
- if (this.options.cache) {
274
- const cached = await this.cache.get(cacheKey);
275
- if (cached)
276
- return cached;
277
- }
278
- const modelConfig = await this.loadModelConfig(model);
279
- this.modelConfigs.set(model, modelConfig);
280
- const relations = await this.loadRelations(model);
281
- this.relations.set(model, relations);
282
- const content = {};
283
- if (modelConfig.metadata.localization) {
284
- const locales = await this.getModelLocales(model, modelConfig);
285
- for (const locale of locales) {
286
- try {
287
- const file = await this.loadContentFile(model, locale);
288
- content[locale] = file.data;
289
- } catch (error) {
290
- console.warn(`Failed to load content for locale ${locale}: ${error?.message}`);
291
- if (locale === this.options.defaultLocale) {
292
- throw error;
293
- }
294
- }
295
- }
296
- } else {
297
- const file = await this.loadContentFile(model);
298
- content.default = file.data;
299
- }
300
- let assets;
301
- try {
302
- const assetsPath = path.join(this.options.contentDir, "assets.json");
303
- const assetsContent = await promises.readFile(assetsPath, "utf-8");
304
- assets = JSON.parse(assetsContent);
305
- } catch (error) {
306
- console.warn("Assets file not found or cannot be read:", error);
307
- }
308
- const result = {
309
- model: modelConfig,
310
- content,
311
- assets
312
- };
313
- if (this.options.cache) {
314
- const ttl = this.getModelTTL(model);
315
- await this.cache.set(cacheKey, result, ttl);
316
- }
317
- return result;
318
- }
319
- async resolveRelation(model, relationField, data, locale) {
320
- try {
321
- const relations = this.relations.get(model);
322
- if (!relations)
323
- throw new Error(`No relations found for model: ${model}`);
324
- const relation = relations.find((r) => r.foreignKey === relationField);
325
- if (!relation)
326
- throw new Error(`No relation found for field: ${String(relationField)}`);
327
- const relatedContent = await this.load(relation.model);
328
- const relatedData = locale ? relatedContent.content[locale] : relatedContent.content.en;
329
- if (!relatedData) {
330
- throw new Error(`Failed to resolve relation: No data found for model ${relation.model}`);
331
- }
332
- if (relation.type === "one-to-one") {
333
- return data.map((item) => {
334
- const relatedItem = relatedData.find((r) => r.ID === item[relationField]);
335
- if (!relatedItem) {
336
- throw new Error(`Failed to resolve relation: No matching item found for ID ${String(item[relationField])}`);
337
- }
338
- return relatedItem;
339
- });
340
- } else {
341
- const uniqueIds = new Set(
342
- data.flatMap(
343
- (item) => Array.isArray(item[relationField]) ? item[relationField] : [item[relationField]]
344
- )
345
- );
346
- const items = Array.from(uniqueIds).map((id) => relatedData.find((r) => r.ID === id)).filter(Boolean);
347
- if (items.length !== uniqueIds.size) {
348
- throw new Error("Failed to resolve relation: Some related items not found");
349
- }
350
- return items;
351
- }
352
- } catch (error) {
353
- throw new Error(`Failed to resolve relation: ${error.message}`);
354
- }
355
- }
356
- };
357
- __name(_ContentLoader, "ContentLoader");
358
- var ContentLoader = _ContentLoader;
359
-
360
- // src/query/builder.ts
361
- var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
362
- constructor(model, executor, loader) {
363
- this.filters = [];
364
- this.includes = {};
365
- this.sorting = [];
366
- this.pagination = {};
367
- this.options = {};
368
- this.model = model;
369
- this.executor = executor;
370
- this.loader = loader;
371
- }
372
- where(field, operator, value) {
373
- this.filters.push({
374
- field,
375
- operator,
376
- value
377
- });
378
- return this;
379
- }
380
- include(relation) {
381
- if (typeof relation === "string") {
382
- this.includes[relation] = {};
383
- } else if (Array.isArray(relation)) {
384
- relation.forEach((r) => {
385
- this.includes[r] = {};
386
- });
387
- }
388
- return this;
389
- }
390
- orderBy(field, direction = "asc") {
391
- this.sorting.push({
392
- field,
393
- direction
394
- });
395
- return this;
396
- }
397
- limit(count) {
398
- this.pagination.limit = count;
399
- return this;
400
- }
401
- offset(count) {
402
- this.pagination.offset = count;
403
- return this;
404
- }
405
- locale(code) {
406
- this.options.locale = code;
407
- return this;
408
- }
409
- cache(ttl) {
410
- this.options.cache = true;
411
- if (ttl)
412
- this.options.ttl = ttl;
413
- return this;
414
- }
415
- noCache() {
416
- this.options.cache = false;
417
- return this;
418
- }
419
- bypassCache() {
420
- this.options.cache = false;
421
- this.options.ttl = 0;
422
- return this;
423
- }
424
- toJSON() {
425
- return {
426
- model: this.model,
427
- filters: this.filters,
428
- includes: this.includes,
429
- sorting: this.sorting,
430
- pagination: this.pagination,
431
- options: this.options
432
- };
433
- }
434
- async get() {
435
- const result = await this.loader.load(this.model);
436
- const modelConfig = result.model;
437
- let data;
438
- if (modelConfig.metadata.localization) {
439
- const locale = this.options.locale || "en";
440
- data = result.content[locale];
441
- if (!data) {
442
- throw new Error(`Content not found for locale: ${locale}`);
443
- }
444
- } else {
445
- if (!result.content.default) {
446
- throw new Error(`Content not found for model: ${this.model}`);
447
- }
448
- data = result.content.default;
449
- }
450
- return this.executor.execute({
451
- model: this.model,
452
- data,
453
- filters: this.filters,
454
- includes: this.includes,
455
- sorting: this.sorting,
456
- pagination: this.pagination,
457
- options: this.options
458
- });
459
- }
460
- async first() {
461
- const result = await this.limit(1).get();
462
- return result.data[0] || null;
463
- }
464
- async count() {
465
- const result = await this.get();
466
- return result.total;
467
- }
468
- };
469
- __name(_ContentrainQueryBuilder, "ContentrainQueryBuilder");
470
- var ContentrainQueryBuilder = _ContentrainQueryBuilder;
471
-
472
- // src/query/executor.ts
473
- var _QueryExecutor = class _QueryExecutor {
474
- constructor(loader) {
475
- this.loader = loader;
476
- }
477
- applyFilters(data, filters) {
478
- return data.filter((item) => {
479
- return filters.every(({ field, operator, value }) => {
480
- const itemValue = item[field];
481
- const validOperators = ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "contains", "startsWith", "endsWith"];
482
- if (!validOperators.includes(operator)) {
483
- throw new Error(`Invalid operator: ${operator}`);
484
- }
485
- if (typeof itemValue === "string" && typeof value === "string") {
486
- return this.applyStringOperation(itemValue, operator, value);
487
- }
488
- if (Array.isArray(value)) {
489
- switch (operator) {
490
- case "in":
491
- return value.includes(itemValue);
492
- case "nin":
493
- return !value.includes(itemValue);
494
- default:
495
- throw new Error(`Invalid array operator: ${operator}`);
496
- }
497
- }
498
- if (Array.isArray(itemValue)) {
499
- switch (operator) {
500
- case "in":
501
- return value.some((v) => itemValue.includes(v));
502
- case "nin":
503
- return !value.some((v) => itemValue.includes(v));
504
- default:
505
- throw new Error(`Invalid array operator: ${operator}`);
506
- }
507
- }
508
- if (typeof itemValue === "number" && typeof value === "number") {
509
- switch (operator) {
510
- case "eq":
511
- return itemValue === value;
512
- case "ne":
513
- return itemValue !== value;
514
- case "gt":
515
- return itemValue > value;
516
- case "gte":
517
- return itemValue >= value;
518
- case "lt":
519
- return itemValue < value;
520
- case "lte":
521
- return itemValue <= value;
522
- }
523
- }
524
- return false;
525
- });
526
- });
527
- }
528
- applySorting(data, sorting) {
529
- return [...data].sort((a, b) => {
530
- for (const { field, direction } of sorting) {
531
- if (!(field in a)) {
532
- throw new Error(`Invalid sort field: ${field}`);
533
- }
534
- const aValue = a[field];
535
- const bValue = b[field];
536
- if (aValue === bValue)
537
- continue;
538
- const compareResult = aValue < bValue ? -1 : 1;
539
- return direction === "asc" ? compareResult : -compareResult;
540
- }
541
- return 0;
542
- });
543
- }
544
- applyPagination(data, limit, offset = 0) {
545
- if (!limit)
546
- return data.slice(offset);
547
- return data.slice(offset, offset + limit);
548
- }
549
- async resolveIncludes(model, data, includes, options) {
550
- const result = [...data];
551
- for (const [field, config] of Object.entries(includes)) {
552
- const relations = await this.loader.resolveRelation(
553
- model,
554
- field,
555
- result,
556
- options.locale
557
- );
558
- if (config.include && relations.length) {
559
- await this.resolveIncludes(
560
- field,
561
- relations,
562
- config.include,
563
- options
564
- );
565
- }
566
- result.forEach((item) => {
567
- const value = item[field];
568
- const relatedItems = relations.filter((r) => {
569
- if (Array.isArray(value)) {
570
- return value.includes(r.ID);
571
- }
572
- return r.ID === value;
573
- });
574
- if (!item._relations) {
575
- item._relations = {};
576
- }
577
- item._relations[field] = Array.isArray(value) ? relatedItems : relatedItems[0];
578
- });
579
- }
580
- return result;
581
- }
582
- applyStringOperation(value, operator, searchValue) {
583
- switch (operator) {
584
- case "eq":
585
- return value === searchValue;
586
- case "ne":
587
- return value !== searchValue;
588
- case "contains":
589
- return value.toLowerCase().includes(searchValue.toLowerCase());
590
- case "startsWith":
591
- return value.toLowerCase().startsWith(searchValue.toLowerCase());
592
- case "endsWith":
593
- return value.toLowerCase().endsWith(searchValue.toLowerCase());
594
- default: {
595
- const _exhaustiveCheck = operator;
596
- return _exhaustiveCheck;
597
- }
598
- }
599
- }
600
- async execute({
601
- model,
602
- data,
603
- filters = [],
604
- includes = {},
605
- sorting = [],
606
- pagination = {},
607
- options = {}
608
- }) {
609
- let result = [...data];
610
- if (filters.length) {
611
- result = this.applyFilters(result, filters);
612
- }
613
- if (Object.keys(includes).length) {
614
- result = await this.resolveIncludes(model, result, includes, options);
615
- }
616
- if (sorting.length) {
617
- result = this.applySorting(result, sorting);
618
- }
619
- const paginatedData = this.applyPagination(result, pagination.limit, pagination.offset);
620
- return {
621
- data: paginatedData,
622
- total: result.length,
623
- pagination: pagination.limit ? {
624
- limit: pagination.limit,
625
- offset: pagination.offset || 0,
626
- hasMore: (pagination.offset || 0) + paginatedData.length < result.length
627
- } : undefined
628
- };
629
- }
630
- };
631
- __name(_QueryExecutor, "QueryExecutor");
632
- var QueryExecutor = _QueryExecutor;
633
-
634
- // src/index.ts
635
- var _ContentrainSDK = class _ContentrainSDK {
636
- constructor(options) {
637
- this.loader = new ContentLoader(options);
638
- this.executor = new QueryExecutor(this.loader);
639
- }
640
- query(model) {
641
- return new ContentrainQueryBuilder(
642
- model,
643
- this.executor,
644
- this.loader
645
- );
646
- }
647
- async load(model) {
648
- return this.loader.load(model);
649
- }
650
- async clearCache() {
651
- return this.loader.clearCache();
652
- }
653
- async refreshCache(model) {
654
- return this.loader.refreshCache(model);
655
- }
656
- getCacheStats() {
657
- return this.loader.getCacheStats();
658
- }
659
- };
660
- __name(_ContentrainSDK, "ContentrainSDK");
661
- var ContentrainSDK = _ContentrainSDK;
662
-
663
- exports.ContentLoader = ContentLoader;
664
- exports.ContentrainQueryBuilder = ContentrainQueryBuilder;
665
- exports.ContentrainSDK = ContentrainSDK;
666
- exports.MemoryCache = MemoryCache;
667
- exports.QueryExecutor = QueryExecutor;
668
- //# sourceMappingURL=index.js.map
669
- //# sourceMappingURL=index.js.map
1
+ 'use strict';var tinyLru=require('tiny-lru'),Pe=require('process'),Me=require('debug'),promises=require('fs/promises'),path=require('path'),ze=require('better-sqlite3');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Pe__default=/*#__PURE__*/_interopDefault(Pe);var Me__default=/*#__PURE__*/_interopDefault(Me);var ze__default=/*#__PURE__*/_interopDefault(ze);var Qe=Object.defineProperty;var c=(N,t)=>Qe(N,"name",{value:t,configurable:true});var ne=class ne{constructor(t={}){this.stats={hits:0,misses:0,size:0,lastCleanup:Date.now()};this.options={maxSize:100,defaultTTL:60*1e3,...t};let e=Math.max(1,Math.floor(Math.max(1,this.options.maxSize)*1024*1024/1e3));this.cache=tinyLru.lru(e);}calculateSize(t){let e=JSON.stringify(t);return new TextEncoder().encode(e).length}async set(t,e,r){await this.cleanupCache();let o=this.calculateSize(e),a=Date.now(),s=a+(r||this.options.defaultTTL);for(;o+this.stats.size>this.options.maxSize*1024*1024;){let d=this.findOldestKey();if(!d)break;await this.delete(d);}let n={data:e,expireAt:s,size:o,createdAt:a},l=this.cache.get(t);l&&(this.stats.size-=l.size),this.cache.set(t,n),this.stats.size+=o;}findOldestKey(){let t=null,e=1/0;for(let r of this.cache.keys()){let o=this.cache.get(r);o.createdAt<e&&(e=o.createdAt,t=r);}return t}async get(t){let e=this.cache.get(t);return e?Date.now()>=e.expireAt?(await this.delete(t),this.stats.misses++,null):(this.stats.hits++,e.data):(this.stats.misses++,null)}async delete(t){let e=this.cache.get(t);e&&(this.stats.size-=e.size,this.cache.delete(t));}async clear(){this.cache.clear(),this.stats={hits:0,misses:0,size:0,lastCleanup:Date.now()};}async cleanupCache(){let t=Date.now(),e=[],r=0;for(let o of this.cache.keys()){let a=this.cache.get(o);a.expireAt<=t?e.push(o):r+=a.size;}for(let o of e)await this.delete(o);for(;r>this.options.maxSize*1024*1024;){let o=this.findOldestKey();if(!o)break;let a=this.cache.get(o);await this.delete(o),r-=a.size;}this.stats.lastCleanup=t;}getStats(){return {...this.stats}}};c(ne,"MemoryCache");var A=ne;var Ae=Pe__default.default.env.NODE_ENV!=="production",L={CACHE:"cache",QUERY:"query",LOADER:"loader",SQLITE:"sqlite",RELATION:"relation",TRANSLATION:"translation",EXECUTOR:"executor",DEFAULT:"default"};function I(N){let t=Me__default.default(`contentrain:query:${N}`);return {debug:c((e,r)=>{Ae&&t({message:e,...r});},"debug"),info:c((e,r)=>{t({message:e,...r});},"info"),warn:c((e,r)=>{t({message:e,...r});},"warn"),error:c((e,r)=>{t({message:e,...r});},"error")}}c(I,"createLogger");var g={cache:I(L.CACHE),query:I(L.QUERY),loader:I(L.LOADER),sqlite:I(L.SQLITE),relation:I(L.RELATION),translation:I(L.TRANSLATION),executor:I(L.EXECUTOR),default:I(L.DEFAULT)};Ae&&Me__default.default.enable("contentrain:query:*");var R=g.loader,se=class se{constructor(t,e){this.contentDir=t;this.defaultLocale=e;this.modelConfigCache=new Map;this.contentCache=new Map;}getContentCacheKey(t,e){return `${t}:${e}`}async loadModelConfig(t){let e=this.modelConfigCache.get(t);if(e)return e;try{let r=path.join(this.contentDir,"models","metadata.json"),o=await promises.readFile(r,"utf-8"),a=JSON.parse(o),s=a.find(u=>u.modelId===t);if(!s)throw R.error("Model metadata not found:",{modelId:t,metadataPath:r,availableModels:a.map(u=>u.modelId),context:{contentDir:this.contentDir,operation:"loadModelConfig"}}),new Error(`Model metadata not found for ${t}`);let n=path.join(this.contentDir,"models",`${t}.json`),l=await promises.readFile(n,"utf-8"),d;try{d=JSON.parse(l);}catch(u){throw R.error("Failed to parse model fields:",{modelId:t,modelPath:n,error:u?.message||"Unknown error",content:l.slice(0,100),context:{contentDir:this.contentDir,operation:"loadModelConfig"}}),new Error(`Invalid JSON format in model fields: ${n}`)}let i={metadata:s,fields:d};return this.modelConfigCache.set(t,i),i}catch(r){throw R.error("Failed to load model config:",{modelId:t,error:r?.message||"Unknown error",stack:r?.stack,context:{contentDir:this.contentDir,operation:"loadModelConfig"}}),r}}async loadModelContent(t,e="default"){let r=this.getContentCacheKey(t,e),o=this.contentCache.get(r);if(o)return o;try{let a=await this.loadModelConfig(t),s,n=e;if(a.metadata.localization){if(e==="default"){if(!this.defaultLocale)throw R.error("Default locale required:",{modelId:t,locale:e,isLocalized:!0,context:{contentDir:this.contentDir,operation:"loadModelContent"}}),new Error(`Default locale is required for localized model "${t}"`);n=this.defaultLocale;}s=path.join(this.contentDir,t,`${n}.json`);}else s=path.join(this.contentDir,t,`${t}.json`);let l=await promises.readFile(s,"utf-8"),d;try{if(d=JSON.parse(l),!Array.isArray(d))throw new TypeError("Content must be an array");let i={model:t,locale:a.metadata.localization?n:void 0,data:d};return this.contentCache.set(r,i),i}catch(i){throw R.error("JSON parse error:",{modelId:t,locale:n,contentPath:s,error:i?.message||"Unknown error",content:l.slice(0,100),context:{contentDir:this.contentDir,operation:"loadModelContent"}}),new Error(`Failed to load content: Invalid JSON format in ${s}`)}}catch(a){throw a.message.includes("Invalid JSON format")||R.error("Content load error:",{modelId:t,locale:e,error:a?.message||"Unknown error",stack:a?.stack,context:{contentDir:this.contentDir,operation:"loadModelContent"}}),a}}async loadAssets(){try{let t=path.join(this.contentDir,"assets.json"),e=await promises.readFile(t,"utf-8");return JSON.parse(e)}catch(t){return R.warn("Assets file not found or cannot be read:",{error:t?.message||"Unknown error",path:path.join(this.contentDir,"assets.json"),context:{contentDir:this.contentDir,operation:"loadAssets"}}),[]}}async getModelLocales(t){try{if(!(await this.loadModelConfig(t)).metadata.localization)return ["default"];let r=path.join(this.contentDir,t),o=await promises.readdir(r),a=o.filter(s=>s.endsWith(".json")).map(s=>s.replace(".json","")).filter(s=>s!==t);if(a.length===0)throw R.error("No locale files found:",{modelId:t,modelDir:r,files:o}),new Error(`No locale files found for localized model "${t}"`);return a}catch(e){throw R.error("Failed to read locales:",{modelId:t,error:e?.message||"Unknown error",stack:e?.stack,context:{contentDir:this.contentDir,operation:"getModelLocales"}}),e}}async clearCache(){this.modelConfigCache.clear(),this.contentCache.clear();}getCacheStats(){return {modelConfigs:this.modelConfigCache.size,contents:this.contentCache.size}}};c(se,"JSONContentManager");var $=se;var ke=g.default,ie=class ie extends Error{constructor(t,e){super(t),this.name="ContentrainError",this.code=e.code,this.operation=e.operation,this.severity=e.severity||"error",this.context=e.context,this.timestamp=e.timestamp||Date.now(),ke.error(t,{name:this.name,code:this.code,operation:this.operation,severity:this.severity,context:this.context,timestamp:this.timestamp,stack:this.stack}),Error.captureStackTrace(this,this.constructor);}toJSON(){return {name:this.name,message:this.message,code:this.code,operation:this.operation,severity:this.severity,context:this.context,timestamp:this.timestamp,stack:this.stack}}};c(ie,"ContentrainError");var O=ie;var le=class le extends O{constructor(t,e,r){let o={code:"DATABASE_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="DatabaseError";}};c(le,"DatabaseError");var T=le;var ce=class ce extends O{constructor(t,e,r){let o={code:"LOADER_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="LoaderError";}};c(ce,"LoaderError");var Y=ce;var de=class de extends O{constructor(t,e,r){let o={code:"RELATION_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="RelationError";}};c(de,"RelationError");var y=de,he=class he extends O{constructor(t,e,r){let o={code:"TRANSLATION_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="TranslationError";}};c(he,"TranslationError");var D=he;var ue=class ue extends O{constructor(t,e,r){let o={code:"QUERY_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="QueryBuilderError";}};c(ue,"QueryBuilderError");var m=ue,ge=class ge extends O{constructor(t,e,r){let o={code:"QUERY_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="QueryExecutorError";}};c(ge,"QueryExecutorError");var w=ge;var M=g.loader,pe=class pe extends ${constructor(e,r){super(e,r);this.relationCache=new Map;}async loadRelations(e){try{let r=this.relationCache.get(e);if(r)return r;let s=(await this.loadModelConfig(e)).fields.filter(n=>n.fieldType==="relation").map(n=>{let l=n.options,d=l?.reference?.form?.reference?.value;if(!d)throw M.error("Reference not found for relation field",{modelId:e,fieldName:n.name,fieldId:n.fieldId,options:l}),new y(`Reference not found for relation field: ${n.name}`,"load",{modelId:e,fieldName:n.name,fieldId:n.fieldId});return {model:d,type:n.componentId==="one-to-one"?"one-to-one":"one-to-many",foreignKey:n.fieldId}});return this.relationCache.set(e,s),s}catch(r){throw M.error("Failed to load relations",{modelId:e,error:r?.message,stack:r?.stack}),new y("Failed to load relations","load",{modelId:e,originalError:r?.message})}}async resolveRelation(e,r,o,a){try{let s=await this.loadRelations(e),n=s.find(i=>i.foreignKey===r);if(!n)throw M.error("Relation not found",{modelId:e,relationField:String(r),availableFields:s.map(i=>i.foreignKey)}),new y(`No relation found for field: ${String(r)}`,"resolve",{modelId:e,relationField:String(r)});let l=a||this.defaultLocale||"default",d=await this.loadModelContent(n.model,l);if(n.type==="one-to-one")return o.filter(h=>h[r]!==void 0&&h[r]!==null).map(h=>{let p=d.data.find(f=>f.ID===h[r]);if(!p)throw M.error("Related item not found",{modelId:e,relationField:String(r),sourceId:h.ID,targetId:h[r]}),new y(`Failed to resolve relation: No matching item found for ID ${String(h[r])}`,"resolve",{modelId:e,relationField:String(r),sourceId:h.ID,targetId:h[r]});return p});{let i=new Set(o.flatMap(h=>h[r]!==void 0&&h[r]!==null?Array.isArray(h[r])?h[r]:[h[r]]:[])),u=Array.from(i).map(h=>d.data.find(p=>p.ID===h)).filter(h=>h!==void 0);if(u.length!==i.size){let h=u.map(f=>f.ID),p=Array.from(i).filter(f=>!h.includes(f));throw M.error("Some related items not found",{modelId:e,relationField:String(r),missingIds:p,totalExpected:i.size,totalFound:u.length}),new y("Failed to resolve relation: Some related items not found","resolve",{modelId:e,relationField:String(r),missingIds:p})}return u}}catch(s){throw M.error("Failed to resolve relation",{modelId:e,relationField:String(r),error:s?.message,stack:s?.stack}),new y("Failed to resolve relation","resolve",{modelId:e,relationField:String(r),originalError:s?.message})}}async clearCache(){try{this.relationCache.clear(),await super.clearCache();}catch(e){throw M.error("Failed to clear relation cache",{error:e?.message,stack:e?.stack}),e}}};c(pe,"JSONRelationManager");var G=pe;var me=g.loader,fe=class fe extends ${constructor(e){super(e.contentDir,e.defaultLocale);this.options=e;this.modelConfigs=new Map;this.relationManager=new G(e.contentDir,e.defaultLocale),e.cache&&(this.cache=new A({maxSize:e.maxCacheSize||100,defaultTTL:e.ttl||60*1e3}));}getCacheKey(e){return `json:${e}`}getModelTTL(e){return this.options.modelTTL?.[e]||this.options.ttl||0}async load(e){let r=this.getCacheKey(e);if(this.options.cache&&this.cache){let o=await this.cache.get(r);if(o)return o}try{let o=this.modelConfigs.get(e);o||(o=await this.loadModelConfig(e),this.modelConfigs.set(e,o));let a=await this.getModelLocales(e),s={};if(o.metadata.localization)for(let d of a)try{let i=await this.loadModelContent(e,d);s[d]=i.data;}catch(i){if(me.warn("Failed to load content for locale:",{locale:d,modelId:e,error:i?.message||"Unknown error",stack:i?.stack}),d===this.options.defaultLocale)throw i}else {let d=await this.loadModelContent(e);s.default=d.data;}let n=await this.loadAssets(),l={model:o,content:s,assets:n};if(this.options.cache&&this.cache){let d=this.getModelTTL(e);await this.cache.set(r,l,d);}return l}catch(o){throw me.error("Load error:",{modelId:e,error:o?.message||"Unknown error",stack:o?.stack,context:{options:this.options,operation:"load"}}),o}}async resolveRelations(e,r,o){try{return await this.relationManager.resolveRelation(e,r,o,this.options.defaultLocale)}catch(a){throw me.error("Resolve relations error:",{modelId:e,relationKey:String(r),error:a?.message||"Unknown error",stack:a?.stack,context:{options:this.options,operation:"resolveRelations"}}),a}}async clearCache(){this.cache&&(await this.cache.clear(),this.modelConfigs.clear(),await this.relationManager.clearCache());}async refreshCache(e){let r=this.getCacheKey(e);this.cache&&(await this.cache.delete(r),this.modelConfigs.delete(e),await this.load(e));}getCacheStats(){let{modelConfigs:e,contents:r}=super.getCacheStats();return {modelConfigs:e,contents:r,cache:this.cache?.getStats()||{size:0,hits:0,misses:0,lastCleanup:0}}}};c(fe,"JSONLoader");var Q=fe;function C(N){return `tbl_${N.replace(/[-_]/g,"_")}`}c(C,"normalizeTableName");function x(N){return `tbl_${N.replace(/[-_]/g,"_")}_translations`}c(x,"normalizeTranslationTableName");var q=g.sqlite,ye=class ye{constructor(t){try{this.db=new ze__default.default(t,{readonly:!0,fileMustExist:!0}),this.setupDatabase();}catch(e){throw q.error("Failed to establish database connection",{databasePath:t,error:e?.message,code:e?.code}),new T("Failed to establish database connection","constructor",{databasePath:t,originalError:e?.message,errorCode:e?.code})}}setupDatabase(){try{this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("foreign_keys = ON");}catch(t){throw q.error("Failed to setup database configuration",{error:t?.message,code:t?.code}),new T("Failed to setup database configuration","constructor",{originalError:t?.message,errorCode:t?.code})}}async query(t,e=[]){try{return await new Promise((r,o)=>{try{let a=this.db.prepare(t).all(e);r(a);}catch(a){o(a);}})}catch(r){throw q.error("Failed to execute query",{sql:t,params:e,error:r?.message,code:r?.code}),new T("Failed to execute query","query",{sql:t,params:e,originalError:r?.message,errorCode:r?.code})}}async get(t,e=[]){try{return await new Promise((r,o)=>{try{let a=this.db.prepare(t).get(e);r(a);}catch(a){o(a);}})}catch(r){throw q.error("Failed to execute single row query",{sql:t,params:e,error:r?.message,code:r?.code}),new T("Failed to execute single row query","query",{sql:t,params:e,originalError:r?.message,errorCode:r?.code})}}async close(){try{return await new Promise((t,e)=>{try{this.db.close(),t();}catch(r){e(r);}})}catch(t){throw q.error("Failed to close database connection",{error:t?.message,code:t?.code}),new T("Failed to close database connection","constructor",{originalError:t?.message,errorCode:t?.code})}}};c(ye,"SQLiteConnection");var X=ye;var B=g.sqlite,we=class we{constructor(t){this.databasePath=t;try{this.connection=new X(t);}catch(e){throw B.error("Failed to initialize content manager",{databasePath:t,error:e?.message,code:e?.code}),new T("Failed to initialize content manager","initialize",{databasePath:t,originalError:e?.message,errorCode:e?.code})}}async query(t,e=[]){try{return await this.connection.query(t,e)}catch(r){throw B.error("Query error:",{sql:t,params:e,error:r?.message,code:r?.code}),r}}async findById(t,e){let r=C(t);try{return await this.connection.get(`SELECT * FROM ${r} WHERE id = ?`,[e])}catch(o){throw B.error("Failed to find record by ID",{model:t,id:e,error:o?.message,code:o?.code}),new T("Failed to find record by ID","read",{model:t,id:e,originalError:o?.message,errorCode:o?.code})}}async findAll(t,e={}){let r=C(t);try{let o=Object.keys(e).length?`WHERE ${Object.keys(e).map(s=>`${s} = ?`).join(" AND ")}`:"";return await this.connection.query(`SELECT * FROM ${r} ${o}`,Object.values(e))}catch(o){throw B.error("Failed to find records",{model:t,conditions:e,error:o?.message,code:o?.code}),new T("Failed to find records","read",{model:t,conditions:e,originalError:o?.message,errorCode:o?.code})}}async getTableCount(){try{return (await this.connection.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")).length}catch(t){return B.error("Failed to get table count",{error:t?.message,code:t?.code}),0}}async close(){try{await this.connection.close();}catch(t){throw B.error("Failed to close content manager",{error:t?.message,code:t?.code}),new T("Failed to close content manager","disconnect",{originalError:t?.message,errorCode:t?.code})}}};c(we,"SQLiteContentManager");var b=we;var Te=g.sqlite,Ce=class Ce extends b{constructor(t){super(t);}async hasTranslations(t){try{let e=x(t);return !!await this.connection.get(`SELECT name FROM sqlite_master
2
+ WHERE type='table'
3
+ AND name=?`,[e])}catch(e){throw new D("Failed to check translations","read",{model:t,originalError:e?.message,errorCode:e?.code})}}async loadTranslations(t,e,r){try{if(!e.length)return {};let o=C(t),s=`
4
+ SELECT t.*, m.created_at, m.updated_at, m.status
5
+ FROM ${x(t)} t
6
+ JOIN ${o} m ON t.id = m.id
7
+ WHERE t.id IN (${e.map(()=>"?").join(",")})
8
+ ${r?"AND t.locale = ?":""}
9
+ `,n=[...e];return r&&n.push(r),(await this.connection.query(s,n)).reduce((i,u)=>(i[u.id]=u,i),{})}catch(o){throw new D("Failed to load translations","read",{model:t,locale:r,idsCount:e.length,originalError:o?.message,errorCode:o?.code})}}async getLocales(t){try{let r=`
10
+ SELECT DISTINCT locale
11
+ FROM ${x(t)}
12
+ ORDER BY locale ASC
13
+ `;return (await this.connection.query(r)).map(s=>s.locale)}catch(e){throw new D("Failed to get locales","read",{model:t,originalError:e?.message,errorCode:e?.code})}}async getMainColumns(t){try{let e=C(t);return (await this.connection.query(`PRAGMA table_info(${e})`)).map(o=>o.name)}catch(e){throw Te.error("Failed to get main columns",{model:t,error:e?.message,code:e?.code}),new D("Failed to get main columns","read",{model:t,originalError:e?.message,errorCode:e?.code})}}async getTranslationColumns(t){try{let e=x(t);return (await this.connection.query(`PRAGMA table_info(${e})`)).map(a=>a.name).filter(a=>!["id","locale"].includes(a))}catch(e){throw Te.error("Failed to get translation columns",{model:t,error:e?.message,code:e?.code}),new D("Failed to get translation columns","read",{model:t,originalError:e?.message,errorCode:e?.code})}}async getAllColumns(t){try{let e=C(t);return (await this.connection.query(`PRAGMA table_info(${e})`)).map(o=>o.name)}catch(e){throw Te.error("Failed to get all columns",{model:t,error:e?.message,code:e?.code}),new D("Failed to get all columns","read",{model:t,originalError:e?.message,errorCode:e?.code})}}};c(Ce,"SQLiteTranslationManager");var P=Ce;var W=g.sqlite,Se=class Se extends b{constructor(t){super(t);try{this.translationManager=new P(t);}catch(e){throw W.error("Failed to initialize relation manager",{databasePath:t,error:e?.message,code:e?.code}),new T("Failed to initialize relation manager","initialize",{databasePath:t,originalError:e?.message,errorCode:e?.code})}}async loadRelations(t,e,r){if(!e.length)return [];try{let o=`
14
+ SELECT * FROM tbl_contentrain_relations
15
+ WHERE source_model = ?
16
+ AND source_id IN (${e.map(()=>"?").join(",")})
17
+ AND field_id = ?
18
+ `,a=await this.connection.query(o,[t,...e,r]);if(a.length===0)throw new y(`No relations found for field: ${r}`,"read",{model:t,fieldId:r,sourceIds:e});return a}catch(o){throw o instanceof y?o:(W.error("Failed to load relations",{model:t,sourceIds:e,fieldId:r,error:o?.message,code:o?.code}),new y("Failed to load relations","read",{model:t,sourceIds:e,fieldId:r,originalError:o?.message,errorCode:o?.code}))}}async loadRelatedContent(t,e){if(!t.length)return [];try{let r=t[0].target_model,o=t.map(d=>d.target_id),a=C(r),s=await this.translationManager.hasTranslations(r),n=`
19
+ SELECT * FROM ${a}
20
+ WHERE id IN (${o.map(()=>"?").join(",")})
21
+ `,l=await this.connection.query(n,o);if(e&&s){let d=await this.translationManager.loadTranslations(r,o,e);return l.map(u=>({...u,...d[u.id]}))}return l}catch(r){throw W.error("Failed to load related content",{relations:t,locale:e,error:r?.message,code:r?.code}),new y("Failed to load related content","read",{relations:t,locale:e,originalError:r?.message,errorCode:r?.code})}}async getRelationTypes(t){try{return (await this.connection.query(`
22
+ SELECT DISTINCT field_id, type
23
+ FROM tbl_contentrain_relations
24
+ WHERE source_model = ?
25
+ `,[t])).reduce((o,{field_id:a,type:s})=>(o[a]=s,o),{})}catch(e){throw W.error("Failed to get relation types",{model:t,error:e?.message,code:e?.code}),new y("Failed to get relation types","read",{model:t,originalError:e?.message,errorCode:e?.code})}}async getRelationFields(t){try{return (await this.connection.query(`
26
+ SELECT DISTINCT field_id
27
+ FROM tbl_contentrain_relations
28
+ WHERE source_model = ?
29
+ `,[t])).map(o=>o.field_id)}catch(e){throw W.error("Failed to get relation fields",{model:t,error:e?.message,code:e?.code}),new y("Failed to get relation fields","read",{model:t,originalError:e?.message,errorCode:e?.code})}}};c(Se,"SQLiteRelationManager");var Z=Se;var Ee=class Ee{constructor(t){this.options=t;this.modelTTL={};this.modelConfigs=new Map;this.contentManager=new b(t.databasePath),this.relationManager=new Z(t.databasePath),this.translationManager=new P(t.databasePath),t.cache&&(this.cache=new A({maxSize:t.maxCacheSize,defaultTTL:typeof t.modelTTL=="number"?t.modelTTL:undefined})),t.modelTTL&&Object.assign(this.modelTTL,t.modelTTL);}async query(t,e=[]){return this.contentManager.query(t,e)}async findById(t,e){return this.contentManager.findById(t,e)}async findAll(t,e={}){return this.contentManager.findAll(t,e)}getCacheKey(t){return `sqlite:${t}`}getModelTTL(t){return this.modelTTL[t]||0}async loadWithTranslations(t,e){let r=await this.translationManager.hasTranslations(t),o=await this.getModelFields(t);if(!r)return {model:{metadata:{modelId:t,name:t,type:"SQLite",localization:false,isServerless:false},fields:o},content:{default:e,translations:{}}};let a=await this.translationManager.getLocales(t),s={};for(let n of a){let l=await this.translationManager.loadTranslations(t,e.map(d=>d.id),n);s[n]=Object.values(l);}return {model:{metadata:{modelId:t,name:t,type:"SQLite",localization:true,isServerless:false},fields:o},content:{default:e,translations:s}}}async getModelFields(t){let e=await this.translationManager.getMainColumns(t),r=await this.relationManager.getRelationFields(t),a=await this.translationManager.hasTranslations(t)?await this.translationManager.getTranslationColumns(t):[];return [...this.mapSystemFields(e),...this.mapRelationFields(r),...this.mapTranslationFields(a)]}mapSystemFields(t){return t.map(e=>({name:e,fieldId:e,type:this.getFieldType(e)}))}mapRelationFields(t){return t.map(e=>({name:e,fieldId:e,type:"relation"}))}mapTranslationFields(t){return t.map(e=>({name:e,fieldId:e,type:this.getFieldType(e)}))}getFieldType(t){return t==="id"||t.endsWith("_id")?"string":t==="created_at"||t==="updated_at"?"date":t==="status"?"string":t==="field_order"?"number":"string"}async resolveRelations(t,e,r){let o=await this.relationManager.loadRelations(t,r.map(a=>a.id),e);return this.relationManager.loadRelatedContent(o,this.options.defaultLocale)}async load(t){let e=this.getCacheKey(t);if(this.options.cache&&this.cache){let r=await this.cache.get(e);if(r)return r}try{let r=await this.findAll(t),o=await this.loadWithTranslations(t,r);if(this.options.cache&&this.cache){let a=this.getModelTTL(t);await this.cache.set(e,o,a);}return o}catch(r){throw new Y("Failed to load content","load",{model:t,originalError:r?.message,errorCode:r?.code})}}async clearCache(){this.cache&&await this.cache.clear();}async refreshCache(t){let e=this.getCacheKey(t);this.cache&&(await this.cache.delete(e),await this.load(t));}async getCacheStats(){return {modelConfigs:this.modelConfigs.size,contents:await this.contentManager.getTableCount(),cache:this.cache?.getStats()||undefined}}async close(){await this.contentManager.close(),await this.relationManager.close(),await this.translationManager.close();}};c(Ee,"SQLiteLoader");var J=Ee;var Re=g.query,Oe=class Oe{constructor(t){this.model=t;this.filters=[];this.includes={};this.sorting=[];this.pagination={};this.options={};}where(t,e,r){try{return this.filters.push({field:String(t),operator:e,value:r}),this}catch(o){throw Re.error("Failed to add filter",{field:String(t),operator:e,value:r,error:o?.message}),new m("Failed to add filter","filter",{field:String(t),operator:e,value:r,originalError:o?.message})}}orderBy(t,e="asc"){try{return this.sorting.push({field:String(t),direction:e}),this}catch(r){throw Re.error("Failed to add sort",{field:String(t),direction:e,error:r?.message}),new m("Failed to add sort","sort",{field:String(t),direction:e,originalError:r?.message})}}limit(t){if(t<0)throw new m("Limit cannot be negative","validate",{count:t});return this.pagination.limit=t,this}offset(t){if(t<0)throw new m("Offset cannot be negative","validate",{count:t});return this.pagination.offset=t,this}cache(t){try{return this.options.cache=!0,t&&(this.options.ttl=t),this}catch(e){throw Re.error("Failed to set cache",{ttl:t,error:e?.message}),new m("Failed to set cache","cache",{ttl:t,originalError:e?.message})}}noCache(){return this.options.cache=false,this}bypassCache(){return this.options.cache=false,this.options.ttl=0,this}toJSON(){return {model:this.model,filters:this.filters,includes:this.includes,sorting:this.sorting,pagination:this.pagination,options:this.options}}};c(Oe,"BaseQueryBuilder");var k=Oe;var z=g.executor,De=class De{applyStringOperation(t,e,r){try{switch(e){case "eq":return t===r;case "ne":return t!==r;case "contains":return t.toLowerCase().includes(r.toLowerCase());case "startsWith":return t.toLowerCase().startsWith(r.toLowerCase());case "endsWith":return t.toLowerCase().endsWith(r.toLowerCase());default:return e}}catch(o){throw z.error("Failed to apply string operation",{operator:e,error:o?.message}),new w("Failed to apply string operation","filter",{operator:e,originalError:o?.message})}}async resolveIncludes(t,e,r,o){try{let a=[...e];for(let[s,n]of Object.entries(r)){let l=await this.resolveRelation(t,s,a,o);n.include&&l.length&&await this.resolveIncludes(s,l,n.include,o),a.forEach(d=>{let i=d[s],u=l.filter(h=>Array.isArray(i)?i.includes(h.ID):h.ID===i);d._relations||(d._relations={}),d._relations[s]=Array.isArray(i)?u:u[0];});}return a}catch(a){throw z.error("Failed to resolve includes",{model:t,error:a?.message,errorCode:a?.code}),new w("Failed to resolve includes","resolve",{model:t,originalError:a?.message,errorCode:a?.code})}}applyFilters(t,e=[]){try{return t.filter(o=>e.every(({field:a,operator:s,value:n})=>{let l=o[a];return this.compareValues(l,s,n)}))}catch(r){throw z.error("Failed to apply filters",{error:r?.message,errorCode:r?.code}),new w("Failed to apply filters","filter",{originalError:r?.message,errorCode:r?.code})}}applySorting(t,e=[]){try{return [...t].sort((o,a)=>{for(let{field:s,direction:n}of e){let l=o[s],d=a[s];if(l===d)continue;let i=l<d?-1:1;return n==="asc"?i:-i}return 0})}catch(r){throw z.error("Failed to apply sorting",{error:r?.message,errorCode:r?.code}),new w("Failed to apply sorting","sort",{originalError:r?.message,errorCode:r?.code})}}applyPagination(t,e,r=0){try{return e?t.slice(r,r+e):t.slice(r)}catch(o){throw z.error("Failed to apply pagination",{limit:e,offset:r,error:o?.message,errorCode:o?.code}),new w("Failed to apply pagination","paginate",{limit:e,offset:r,originalError:o?.message,errorCode:o?.code})}}compareValues(t,e,r){try{switch(e){case "eq":return t===r;case "ne":return t!==r;case "gt":return t>r;case "gte":return t>=r;case "lt":return t<r;case "lte":return t<=r;case "in":return Array.isArray(r)?r.includes(t):!1;case "nin":return Array.isArray(r)?!r.includes(t):!1;case "contains":return typeof t=="string"&&typeof r=="string"?this.applyStringOperation(t,"contains",r):!1;case "startsWith":return typeof t=="string"&&typeof r=="string"?this.applyStringOperation(t,"startsWith",r):!1;case "endsWith":return typeof t=="string"&&typeof r=="string"?this.applyStringOperation(t,"endsWith",r):!1;default:return !1}}catch(o){throw z.error("Failed to compare values",{operator:e,error:o?.message}),new w("Failed to compare values","filter",{operator:e,originalError:o?.message})}}getPaginationInfo(t,e){if(t?.limit)return {limit:t.limit,offset:t.offset||0,hasMore:(t.offset||0)+t.limit<e}}};c(De,"BaseQueryExecutor");var j=De;var V=g.query,Le=class Le extends j{constructor(e){super();this.loader=e;}async resolveRelation(e,r,o,a){try{return await this.loader.resolveRelations(e,r,o)}catch(s){throw V.error("Failed to resolve relation",{model:e,field:r,error:s?.message,stack:s?.stack}),new w("Failed to resolve relation","resolve",{model:e,field:r,originalError:s?.message})}}async execute(e){try{let r=await this.loader.load(e.model),o=e.options?.locale||"default",a=this.getContent(r,o);e.filters?.length&&(a=this.applyFilters(a,e.filters)),e.includes&&Object.keys(e.includes).length&&(a=await this.resolveIncludes(e.model,a,e.includes,e.options||{})),e.sorting?.length&&(a=this.applySorting(a,e.sorting));let s=a.length;return e.pagination&&(a=this.applyPagination(a,e.pagination.limit,e.pagination.offset)),{data:a,total:s,pagination:this.getPaginationInfo(e.pagination,s)}}catch(r){throw V.error("Query execution failed",{model:e.model,error:r?.message,stack:r?.stack}),new w("Failed to execute query","execute",{model:e.model,originalError:r?.message})}}getContent(e,r){if(e.model.metadata.localization){let o=e.content[r];return o||(V.warn("Content not found for locale, falling back to en",{locale:r}),e.content.en||[])}return e.content.en||[]}applySorting(e,r=[]){try{return [...e].sort((a,s)=>{for(let{field:n,direction:l}of r){if(!(n in a)||!(n in s))throw new w(`Invalid sort field: ${n}`,"sort",{field:n,availableFields:Object.keys(a)});let d=a[n],i=s[n];if(d===i)continue;let u=d<i?-1:1;return l==="asc"?u:-u}return 0})}catch(o){throw V.error("Failed to apply sorting",{error:o?.message,stack:o?.stack,operation:"sort"}),o}}};c(Le,"JSONQueryExecutor");var ee=Le;var E=g.query,Ie=class Ie extends k{constructor(t,e){super(t);try{this.executor=new ee(e),E.info("Query builder initialized successfully");}catch(r){throw E.error("Failed to initialize query builder",{model:t,error:r?.message,stack:r?.stack}),r}}where(t,e,r){try{return this.validateField(t),this.validateOperator(e),this.filters.push({field:String(t),operator:e,value:r}),this}catch(o){throw E.error("Failed to add filter",{field:String(t),operator:e,value:r,error:o?.message}),o}}include(t){try{return typeof t=="string"?this.includes[t]={}:t.forEach(e=>{this.includes[e]={};}),this}catch(e){throw E.error("Failed to add relation",{relations:t,error:e?.message,errorCode:e?.code}),new m("Failed to add relation","include",{relations:t,originalError:e?.message,errorCode:e?.code})}}orderBy(t,e="asc"){try{return this.validateField(t),this.sorting.push({field:String(t),direction:e}),this}catch(r){throw E.error("Failed to add sort",{field:String(t),direction:e,error:r?.message}),r}}limit(t){try{if(t<0)throw new m("Limit cannot be negative","validate",{count:t});return this.pagination.limit=t,this}catch(e){throw E.error("Failed to set limit",{count:t,error:e?.message}),e}}offset(t){try{if(t<0)throw new m("Offset cannot be negative","validate",{count:t});return this.pagination.offset=t,this}catch(e){throw E.error("Failed to set offset",{count:t,error:e?.message}),e}}locale(t){try{if(!t)throw new m("Locale code cannot be empty","validate",{code:t});return this.options.locale=t,this}catch(e){throw E.error("Failed to set locale",{code:t,error:e?.message}),e}}validateField(t){if(!t)throw new m("Field cannot be empty","validate",{field:t});if(typeof t!="string")throw new m("Field must be a string","validate",{field:t,type:typeof t})}validateOperator(t){if(typeof t!="string")throw new m("Operator must be a string","validate",{operator:t,type:typeof t});let e=["eq","ne","gt","gte","lt","lte","in","nin","contains","startsWith","endsWith"];if(!e.includes(t))throw new m(`Invalid operator: ${t}`,"validate",{operator:t,validOperators:e})}async get(){try{return await this.executor.execute({model:this.model,filters:this.filters,includes:this.includes,sorting:this.sorting,pagination:this.pagination,options:this.options})}catch(t){throw E.error("Failed to execute query",{model:this.model,error:t?.message,stack:t?.stack}),t}}async first(){try{return (await this.limit(1).get()).data[0]||null}catch(t){throw E.error("Failed to get first record",{model:this.model,error:t?.message}),t}}async count(){try{return (await this.get()).total}catch(t){throw E.error("Failed to get record count",{model:this.model,error:t?.message}),t}}};c(Ie,"JSONQueryBuilder");var te=Ie;var F=g.query,xe=class xe extends k{constructor(e,r){super(e);this.model=e;this.executor=r;this.options={};}setLoader(e){return this.executor.setLoader(e),this}include(e){try{return typeof e=="string"?this.options.includes=[{relation:e}]:Array.isArray(e)?this.options.includes=e.map(r=>typeof r=="string"?{relation:r}:r):this.options.includes=[e],this}catch(r){throw F.error("Failed to add relation",{relations:e,error:r?.message}),new m("Failed to add relation","include",{relations:e,originalError:r?.message})}}locale(e){try{return this.options.locale=e,this}catch(r){throw F.error("Failed to set locale",{code:e,error:r?.message}),new m("Failed to set locale","localize",{code:e,originalError:r?.message})}}orderBy(e,r="asc"){try{return this.sorting.push({field:e,direction:r}),this}catch(o){throw F.error("Failed to add sorting",{field:e,direction:r,error:o?.message}),new m("Failed to add sorting","sort",{field:e,direction:r,originalError:o?.message})}}limit(e){try{if(e<0)throw new Error("Limit count must be positive");return this.pagination.limit=e,this}catch(r){throw F.error("Failed to set limit",{count:e,error:r?.message}),new m("Failed to set limit","paginate",{count:e,originalError:r?.message})}}offset(e){try{if(e<0)throw new Error("Offset count must be positive");return this.pagination.offset=e,this}catch(r){throw F.error("Failed to set offset",{count:e,error:r?.message}),new m("Failed to set offset","paginate",{count:e,originalError:r?.message})}}async get(){try{return await this.executor.execute({model:this.model,filters:this.filters,sorting:this.sorting,pagination:this.pagination,options:this.options})}catch(e){throw F.error("Failed to execute query",{model:this.model,error:e?.message}),new m("Failed to execute query","execute",{model:this.model,originalError:e?.message})}}async first(){try{return (await this.limit(1).get()).data[0]||null}catch(e){throw F.error("Failed to get first record",{model:this.model,error:e?.message}),new m("Failed to get first record","query",{model:this.model,originalError:e?.message})}}async count(){try{return (await this.get()).total}catch(e){throw F.error("Failed to get record count",{model:this.model,error:e?.message}),new m("Failed to get record count","query",{model:this.model,originalError:e?.message})}}};c(xe,"SQLiteQueryBuilder");var re=xe;var _=g.query,be=class be extends j{constructor(e){super();this.loader=e;this.query=null;this.mainColumns=[];this.translationColumns=[];}setLoader(e){this.loader=e;}async resolveRelation(e,r,o,a){try{let n=(await this.loader.relationManager.getRelationTypes(e))[r];if(!n)return _.warn("No relation type found",{model:e,field:r}),o;let l=await this.loader.relationManager.loadRelations(e,o.map(u=>u.id),r);if(!l.length)return o;let d=await this.loader.relationManager.loadRelatedContent(l,a?.locale),i={};return l.forEach(u=>{i[u.source_id]||(i[u.source_id]=[]);let h=d.find(p=>p.id===u.target_id);h&&i[u.source_id].push(h);}),o.forEach(u=>{u._relations||(u._relations={});let h=i[u.id]||[];if(u._relations[r]=n==="one-to-many"?h:h[0],h.length>0){let p=h[0];Object.keys(p).forEach(f=>{f!=="id"&&f!=="created_at"&&f!=="updated_at"&&f!=="status"&&(u[`${r}_${f}`]=p[f]);});}}),o}catch(s){if(s instanceof y)return _.warn("Relation not found",{model:e,field:r,error:s.message}),o;throw _.error("Failed to resolve relation",{error:s}),new w("Failed to resolve relation","resolve",{model:e,field:r,originalError:s?.message})}}async execute(e){try{let r=await this.buildSQLQuery(e),o=this.buildSQL(r),a=await this.loader.query(o,r.parameters);if(e.options?.includes?.length)for(let n of e.options.includes){let l=await this.loader.relationManager.loadRelations(e.model,a.map(p=>p.id),n.relation);if(!l.length)continue;let i=l[0].type==="one-to-many",u=await this.loader.relationManager.loadRelatedContent(l,n.locale||e.options?.locale||void 0),h={};l.forEach(p=>{h[p.source_id]||(h[p.source_id]=[]);let f=u.find(ae=>ae.id===p.target_id);f&&h[p.source_id].push(f);}),a.forEach(p=>{p._relations||(p._relations={});let f=h[p.id]||[];p._relations[n.relation]=i?f:f[0];});}let s=await this.getTotal(r);return {data:a,total:s,pagination:this.getPaginationInfo(e.pagination,s)}}catch(r){throw _.error("Query execution failed",{operation:"execute",model:e.model,error:r?.message,stack:r?.stack}),new w("Failed to execute query","execute",{model:e.model,originalError:r?.message})}}async buildSQLQuery(e){let r=C(e.model),o={select:[],from:r,joins:[],where:[],orderBy:e.sorting||[],parameters:[],pagination:e.pagination,options:e.options};if(this.query=o,await this.loader.translationManager.hasTranslations(e.model)?(this.mainColumns=await this.loader.translationManager.getMainColumns(e.model),this.translationColumns=await this.loader.translationManager.getTranslationColumns(e.model),o.select.push(...this.mainColumns.map(s=>`m.${s}`)),e.options?.locale&&(e.sorting?.some(n=>this.translationColumns.includes(n.field))||e.options?.translations!==false)&&await this.addTranslationJoin(o,e.model,e.options.locale)):(this.mainColumns=await this.loader.translationManager.getAllColumns(e.model),this.translationColumns=[],o.select.push(...this.mainColumns.map(s=>`m.${s}`))),e.options?.includes?.length){let s=await this.loader.relationManager.getRelationTypes(e.model);for(let n of e.options.includes){if(!s[n.relation]){_.warn("No relation type found",{model:e.model,field:n.relation});continue}let d=await this.loader.relationManager.loadRelations(e.model,[],n.relation);if(d.length>0){let i=d[0],u=C(i.target_model),h=`r_${n.relation}`;o.joins.push({type:"LEFT",table:u,alias:h,conditions:[`m.${n.relation}_id = ${h}.id`]});let p=await this.loader.translationManager.getMainColumns(i.target_model);if(o.select.push(...p.map(f=>`${h}.${f} as ${n.relation}_${f}`)),n.locale&&await this.loader.translationManager.hasTranslations(i.target_model)){let ae=x(i.target_model),Ne=await this.loader.translationManager.getTranslationColumns(i.target_model);if(Ne.length>0){let K=`t_${n.relation}`;o.joins.push({type:"LEFT",table:ae,alias:K,conditions:[`${h}.id = ${K}.id`,`${K}.locale = ?`]}),o.select.push(...Ne.map(ve=>`${K}.${ve} as ${n.relation}_${ve}`)),o.parameters.push(n.locale);}}}}}return e.filters?.length&&(o.where=e.filters.map(s=>{let n=s.field.endsWith("_id")?s.field:`${s.field}_id`;return {...s,field:this.mainColumns.includes(n)?n:s.field}}),o.where.forEach(s=>{s.value!==null&&(Array.isArray(s.value)?o.parameters.push(...s.value):s.operator==="startsWith"?o.parameters.push(`${String(s.value)}%`):s.operator==="contains"?o.parameters.push(`%${String(s.value)}%`):s.operator==="endsWith"?o.parameters.push(`%${String(s.value)}`):o.parameters.push(s.value));})),o}buildSQL(e){let r=[];if(e.pagination?.limit){if(r.push("WITH paginated_base AS ("),r.push(" SELECT DISTINCT m.id"),r.push(` FROM ${e.from} AS m`),e.joins.length&&r.push(e.joins.map(o=>`${o.type} JOIN ${o.table} AS ${o.alias} ON ${o.conditions.join(" AND ")}`).join(" ")),e.options?.locale&&e.parameters.push(e.options.locale),e.where.length){let o=e.where.map(a=>this.buildConditionWithoutParams(a));r.push(` WHERE ${o.join(" AND ")}`);}if(e.orderBy.length){let o=e.orderBy.map(a=>{if(["id","created_at","updated_at","status"].includes(a.field))return `m.${a.field} ${a.direction.toUpperCase()}`;let n=this.translationColumns?.includes(a.field),l=!!e.options?.locale;return n&&l?`COALESCE(t.${a.field}, '') COLLATE NOCASE ${a.direction.toUpperCase()}`:`COALESCE(m.${a.field}, '') COLLATE NOCASE ${a.direction.toUpperCase()}`});r.push(` ORDER BY ${o.join(", ")}`);}r.push(` LIMIT ${e.pagination.limit}`),e.pagination.offset&&r.push(` OFFSET ${e.pagination.offset}`),r.push(")"),r.push(`SELECT ${e.select.join(", ")}`),r.push("FROM paginated_base pb"),r.push(`JOIN ${e.from} AS m ON pb.id = m.id`),e.joins.length&&r.push(e.joins.map(o=>`${o.type} JOIN ${o.table} AS ${o.alias} ON ${o.conditions.join(" AND ")}`).join(" "));}else {if(r.push(`SELECT ${e.select.join(", ")}`),r.push(`FROM ${e.from} AS m`),e.joins.length&&r.push(e.joins.map(o=>`${o.type} JOIN ${o.table} AS ${o.alias} ON ${o.conditions.join(" AND ")}`).join(" ")),e.where.length){let o=e.where.map(a=>this.buildConditionWithoutParams(a));r.push(`WHERE ${o.join(" AND ")}`);}if(e.orderBy.length){let o=e.orderBy.map(a=>{if(["id","created_at","updated_at","status"].includes(a.field))return `m.${a.field} ${a.direction.toUpperCase()}`;let n=this.translationColumns?.includes(a.field),l=!!e.options?.locale;return n&&l?`COALESCE(t.${a.field}, '') COLLATE NOCASE ${a.direction.toUpperCase()}`:`COALESCE(m.${a.field}, '') COLLATE NOCASE ${a.direction.toUpperCase()}`});r.push(`ORDER BY ${o.join(", ")}`);}}return r.join(`
30
+ `)}async addTranslationJoin(e,r,o){if(await this.loader.translationManager.hasTranslations(r)){let s=x(r),n=await this.loader.translationManager.getTranslationColumns(r);if(n.length>0){e.select.push(...n.map(i=>`t.${i} as ${i}`));let l=e.where.some(i=>this.translationColumns.includes(i.field)),d=e.orderBy.some(i=>this.translationColumns.includes(i.field));e.joins.push({type:l||d?"INNER":"LEFT",table:s,alias:"t",conditions:["m.id = t.id","t.locale = ?"]}),e.parameters.push(o);}}}buildConditionWithoutParams(e){let{field:r,operator:o,value:a}=e,s=this.translationColumns?.includes(r),n=r.includes(".")?r:`${s&&this.query?.options?.locale?"t":"m"}.${r}`;if(a===null)return o==="eq"?`${n} IS NULL`:`${n} IS NOT NULL`;if(Array.isArray(a)){let l=a.map(()=>"?").join(",");return o==="in"?`${n} IN (${l})`:`${n} NOT IN (${l})`}switch(o){case "eq":return `${n} = ?`;case "ne":return `${n} != ?`;case "gt":return `${n} > ?`;case "gte":return `${n} >= ?`;case "lt":return `${n} < ?`;case "lte":return `${n} <= ?`;case "contains":return `${n} COLLATE BINARY LIKE ? ESCAPE '\\'`;case "startsWith":return `${n} COLLATE BINARY LIKE ? ESCAPE '\\'`;case "endsWith":return `${n} COLLATE BINARY LIKE ? ESCAPE '\\'`;case "in":return `${n} IN (?)`;case "nin":return `${n} NOT IN (?)`;default:throw new w("Unsupported operator","query",{field:r,operator:o,value:a})}}async getTotal(e){try{let r=[];e.options?.locale&&r.push(e.options.locale),e.where.length&&e.where.forEach(n=>{n.value!==null&&(Array.isArray(n.value)?r.push(...n.value):n.operator==="startsWith"?r.push(`${String(n.value)}%`):n.operator==="contains"?r.push(`%${String(n.value)}%`):n.operator==="endsWith"?r.push(`%${String(n.value)}`):r.push(n.value));});let o={select:["COUNT(DISTINCT m.id) as total"],from:e.from,joins:e.joins,where:e.where,orderBy:[],parameters:r,pagination:void 0,options:void 0},a=this.buildSQL(o);return (await this.loader.query(a,r))[0].total}catch(r){throw _.error("Failed to get total count",{error:r}),new w("Failed to get total count","execute",{model:e.from,originalError:r?.message})}}getPaginationInfo(e,r){if(e?.limit)return {limit:e.limit,offset:e.offset||0,hasMore:(e.offset||0)+e.limit<r}}};c(be,"SQLiteQueryExecutor");var oe=be;var je=g.query,Fe=class Fe{static setLoader(t){this.loader=t;}static createSQLiteBuilder(t,e){let r=e||this.loader;if(!r)throw new Error("No loader instance available");let o=new oe(r);return new re(t,o)}static createJSONBuilder(t,e){let r=e||this.loader;if(!r)throw new Error("No loader instance available");return new te(t,r)}static createBuilder(t,e){let r=e||this.loader;if(!r)throw new Error("No loader instance available");if(r instanceof J)return this.createSQLiteBuilder(t,r);if(r instanceof Q)return this.createJSONBuilder(t,r);throw je.error("Unsupported loader type",{model:t}),new Error("Unsupported loader type")}};c(Fe,"QueryFactory");var U=Fe;var S=class S{constructor(t,e){if(this.options=e,this.type=t,!S.loader){if(t==="json"){if(!e.contentDir)throw new Error("contentDir is required for JSON loader");S.loader=new Q({contentDir:e.contentDir,cache:e.cache,ttl:e.ttl,maxCacheSize:e.maxCacheSize,defaultLocale:e.defaultLocale,modelTTL:e.modelTTL});}else {if(!e.databasePath)throw new Error("databasePath is required for SQLite loader");S.loader=new J({databasePath:e.databasePath,cache:e.cache,maxCacheSize:e.maxCacheSize,defaultLocale:e.defaultLocale,modelTTL:e.modelTTL});}U.setLoader(S.loader);}}query(t){return U.createBuilder(t,S.loader)}async load(t){let e=await S.loader.load(t),r=this.type==="json"?"default":"en",o=this.options.defaultLocale||r;if(this.type==="json")return e.content[o][0];let a=e.content;return a.translations&&o!==r?a.translations[o][0]:a.default[0]}async clearCache(){return S.loader.clearCache()}async refreshCache(t){return S.loader.refreshCache(t)}async getCacheStats(){return S.loader.getCacheStats()}};c(S,"ContentrainSDK");var $e=S;exports.ContentrainSDK=$e;exports.JSONLoader=Q;exports.MemoryCache=A;exports.QueryFactory=U;exports.SQLiteLoader=J;exports.loggers=g;