@contentrain/query 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +40 -26
- package/dist/index.d.ts +40 -26
- package/dist/index.js +159 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +160 -60
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
interface ContentrainConfig {
|
|
2
2
|
contentDir: string;
|
|
3
|
+
defaultLocale?: string;
|
|
3
4
|
models: {
|
|
4
5
|
[modelId: string]: {
|
|
5
|
-
localized
|
|
6
|
+
localized: boolean;
|
|
6
7
|
defaultLocale?: string;
|
|
7
8
|
locales?: string[];
|
|
8
9
|
};
|
|
@@ -76,19 +77,7 @@ interface FieldValidations {
|
|
|
76
77
|
};
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
|
-
interface AssetMetadata {
|
|
80
|
-
path: string;
|
|
81
|
-
size: number;
|
|
82
|
-
type: string;
|
|
83
|
-
createdAt: string;
|
|
84
|
-
updatedAt: string;
|
|
85
|
-
}
|
|
86
80
|
type ContentrainLocales = string;
|
|
87
|
-
interface QueryConfig<TFields extends BaseContentrainType, TLocales extends ContentrainLocales, TRelations extends Record<string, BaseContentrainType>> {
|
|
88
|
-
fields: TFields;
|
|
89
|
-
locales: TLocales;
|
|
90
|
-
relations: TRelations;
|
|
91
|
-
}
|
|
92
81
|
|
|
93
82
|
interface ContentLoaderOptions {
|
|
94
83
|
contentDir: string;
|
|
@@ -109,11 +98,26 @@ interface ContentFile<T extends BaseContentrainType = BaseContentrainType> {
|
|
|
109
98
|
locale?: string;
|
|
110
99
|
data: T[];
|
|
111
100
|
}
|
|
101
|
+
interface AssetMetadata {
|
|
102
|
+
path: string;
|
|
103
|
+
mimetype: string;
|
|
104
|
+
size: number;
|
|
105
|
+
alt: string;
|
|
106
|
+
meta: {
|
|
107
|
+
user: {
|
|
108
|
+
name: string;
|
|
109
|
+
email: string;
|
|
110
|
+
avatar: string;
|
|
111
|
+
};
|
|
112
|
+
createdAt: string;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
112
115
|
interface LoaderResult<T extends BaseContentrainType = BaseContentrainType> {
|
|
113
116
|
model: ModelConfig;
|
|
114
117
|
content: {
|
|
115
118
|
[locale: string]: T[];
|
|
116
119
|
};
|
|
120
|
+
assets?: AssetMetadata[];
|
|
117
121
|
}
|
|
118
122
|
interface RelationConfig {
|
|
119
123
|
model: string;
|
|
@@ -132,12 +136,19 @@ interface CacheEntry<T> {
|
|
|
132
136
|
size: number;
|
|
133
137
|
createdAt: number;
|
|
134
138
|
}
|
|
139
|
+
interface MemoryCacheOptions {
|
|
140
|
+
maxSize?: number;
|
|
141
|
+
defaultTTL?: number;
|
|
142
|
+
}
|
|
135
143
|
|
|
136
|
-
type
|
|
137
|
-
|
|
144
|
+
type StringOperator = 'eq' | 'ne' | 'contains' | 'startsWith' | 'endsWith';
|
|
145
|
+
type NumericOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
|
|
146
|
+
type ArrayOperator = 'in' | 'nin';
|
|
147
|
+
type Operator = StringOperator | NumericOperator | ArrayOperator;
|
|
148
|
+
interface Filter<T = any> {
|
|
138
149
|
field: string;
|
|
139
150
|
operator: Operator;
|
|
140
|
-
value:
|
|
151
|
+
value: T extends Array<infer U> ? (ArrayOperator extends 'in' | 'nin' ? U[] : U) : T;
|
|
141
152
|
}
|
|
142
153
|
interface Sort {
|
|
143
154
|
field: string;
|
|
@@ -167,6 +178,11 @@ interface QueryResult<T> {
|
|
|
167
178
|
hasMore: boolean;
|
|
168
179
|
};
|
|
169
180
|
}
|
|
181
|
+
interface QueryConfig<TFields extends BaseContentrainType, TLocales extends ContentrainLocales = 'en' | 'tr', TRelations extends Record<string, BaseContentrainType> = Record<string, never>> {
|
|
182
|
+
fields: TFields;
|
|
183
|
+
locales: TLocales;
|
|
184
|
+
relations: TRelations;
|
|
185
|
+
}
|
|
170
186
|
|
|
171
187
|
declare class ContentLoader {
|
|
172
188
|
private options;
|
|
@@ -182,6 +198,7 @@ declare class ContentLoader {
|
|
|
182
198
|
private loadModelConfig;
|
|
183
199
|
private loadContentFile;
|
|
184
200
|
private loadRelations;
|
|
201
|
+
private getModelLocales;
|
|
185
202
|
load<T extends BaseContentrainType>(model: string): Promise<LoaderResult<T>>;
|
|
186
203
|
resolveRelation<T extends BaseContentrainType, R extends BaseContentrainType>(model: string, relationField: keyof T, data: T[], locale?: string): Promise<R[]>;
|
|
187
204
|
}
|
|
@@ -193,6 +210,7 @@ declare class QueryExecutor {
|
|
|
193
210
|
private applySorting;
|
|
194
211
|
private applyPagination;
|
|
195
212
|
private resolveIncludes;
|
|
213
|
+
private applyStringOperation;
|
|
196
214
|
execute<T extends BaseContentrainType>({ model, data, filters, includes, sorting, pagination, options, }: {
|
|
197
215
|
model: string;
|
|
198
216
|
data: T[];
|
|
@@ -228,13 +246,10 @@ declare class ContentrainQueryBuilder<TFields extends BaseContentrainType, TLoca
|
|
|
228
246
|
bypassCache(): this;
|
|
229
247
|
toJSON(): {
|
|
230
248
|
model: string;
|
|
231
|
-
filters: Filter[];
|
|
249
|
+
filters: Filter<any>[];
|
|
232
250
|
includes: Include;
|
|
233
251
|
sorting: Sort[];
|
|
234
|
-
pagination:
|
|
235
|
-
limit?: number;
|
|
236
|
-
offset?: number;
|
|
237
|
-
};
|
|
252
|
+
pagination: Pagination;
|
|
238
253
|
options: QueryOptions;
|
|
239
254
|
};
|
|
240
255
|
get(): Promise<QueryResult<TFields>>;
|
|
@@ -242,10 +257,6 @@ declare class ContentrainQueryBuilder<TFields extends BaseContentrainType, TLoca
|
|
|
242
257
|
count(): Promise<number>;
|
|
243
258
|
}
|
|
244
259
|
|
|
245
|
-
interface MemoryCacheOptions {
|
|
246
|
-
maxSize?: number;
|
|
247
|
-
defaultTTL?: number;
|
|
248
|
-
}
|
|
249
260
|
declare class MemoryCache {
|
|
250
261
|
private cache;
|
|
251
262
|
private options;
|
|
@@ -267,6 +278,9 @@ declare class ContentrainSDK {
|
|
|
267
278
|
constructor(options: ContentLoaderOptions);
|
|
268
279
|
query<T extends QueryConfig<BaseContentrainType, string, Record<string, BaseContentrainType>>>(model: string): ContentrainQueryBuilder<T['fields'], T['locales'], T['relations']>;
|
|
269
280
|
load<T extends BaseContentrainType>(model: string): Promise<LoaderResult<T>>;
|
|
281
|
+
clearCache(): Promise<void>;
|
|
282
|
+
refreshCache(model: string): Promise<void>;
|
|
283
|
+
getCacheStats(): CacheStats;
|
|
270
284
|
}
|
|
271
285
|
|
|
272
|
-
export { type AssetMetadata, type BaseContentrainType, type CacheEntry, type CacheStats, type ContentFile, ContentLoader, type ContentLoaderOptions, type ContentrainComponentId, type ContentrainConfig, type ContentrainFieldType, type ContentrainLocales, ContentrainQueryBuilder, ContentrainSDK, type ContentrainStatus, type FieldMetadata, type FieldOptions, type FieldValidations, type Filter, type Include, type LoaderResult, MemoryCache, type MemoryCacheOptions, type ModelConfig, type ModelMetadata, type Operator, type Pagination, type QueryConfig, QueryExecutor, type QueryOptions, type QueryResult, type RelationConfig, type Sort };
|
|
286
|
+
export { type ArrayOperator, type AssetMetadata, type BaseContentrainType, type CacheEntry, type CacheStats, type ContentFile, ContentLoader, type ContentLoaderOptions, type ContentrainComponentId, type ContentrainConfig, type ContentrainFieldType, type ContentrainLocales, ContentrainQueryBuilder, ContentrainSDK, type ContentrainStatus, type FieldMetadata, type FieldOptions, type FieldValidations, type Filter, type Include, type LoaderResult, MemoryCache, type MemoryCacheOptions, type ModelConfig, type ModelMetadata, type NumericOperator, type Operator, type Pagination, type QueryConfig, QueryExecutor, type QueryOptions, type QueryResult, type RelationConfig, type Sort, type StringOperator };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
interface ContentrainConfig {
|
|
2
2
|
contentDir: string;
|
|
3
|
+
defaultLocale?: string;
|
|
3
4
|
models: {
|
|
4
5
|
[modelId: string]: {
|
|
5
|
-
localized
|
|
6
|
+
localized: boolean;
|
|
6
7
|
defaultLocale?: string;
|
|
7
8
|
locales?: string[];
|
|
8
9
|
};
|
|
@@ -76,19 +77,7 @@ interface FieldValidations {
|
|
|
76
77
|
};
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
|
-
interface AssetMetadata {
|
|
80
|
-
path: string;
|
|
81
|
-
size: number;
|
|
82
|
-
type: string;
|
|
83
|
-
createdAt: string;
|
|
84
|
-
updatedAt: string;
|
|
85
|
-
}
|
|
86
80
|
type ContentrainLocales = string;
|
|
87
|
-
interface QueryConfig<TFields extends BaseContentrainType, TLocales extends ContentrainLocales, TRelations extends Record<string, BaseContentrainType>> {
|
|
88
|
-
fields: TFields;
|
|
89
|
-
locales: TLocales;
|
|
90
|
-
relations: TRelations;
|
|
91
|
-
}
|
|
92
81
|
|
|
93
82
|
interface ContentLoaderOptions {
|
|
94
83
|
contentDir: string;
|
|
@@ -109,11 +98,26 @@ interface ContentFile<T extends BaseContentrainType = BaseContentrainType> {
|
|
|
109
98
|
locale?: string;
|
|
110
99
|
data: T[];
|
|
111
100
|
}
|
|
101
|
+
interface AssetMetadata {
|
|
102
|
+
path: string;
|
|
103
|
+
mimetype: string;
|
|
104
|
+
size: number;
|
|
105
|
+
alt: string;
|
|
106
|
+
meta: {
|
|
107
|
+
user: {
|
|
108
|
+
name: string;
|
|
109
|
+
email: string;
|
|
110
|
+
avatar: string;
|
|
111
|
+
};
|
|
112
|
+
createdAt: string;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
112
115
|
interface LoaderResult<T extends BaseContentrainType = BaseContentrainType> {
|
|
113
116
|
model: ModelConfig;
|
|
114
117
|
content: {
|
|
115
118
|
[locale: string]: T[];
|
|
116
119
|
};
|
|
120
|
+
assets?: AssetMetadata[];
|
|
117
121
|
}
|
|
118
122
|
interface RelationConfig {
|
|
119
123
|
model: string;
|
|
@@ -132,12 +136,19 @@ interface CacheEntry<T> {
|
|
|
132
136
|
size: number;
|
|
133
137
|
createdAt: number;
|
|
134
138
|
}
|
|
139
|
+
interface MemoryCacheOptions {
|
|
140
|
+
maxSize?: number;
|
|
141
|
+
defaultTTL?: number;
|
|
142
|
+
}
|
|
135
143
|
|
|
136
|
-
type
|
|
137
|
-
|
|
144
|
+
type StringOperator = 'eq' | 'ne' | 'contains' | 'startsWith' | 'endsWith';
|
|
145
|
+
type NumericOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
|
|
146
|
+
type ArrayOperator = 'in' | 'nin';
|
|
147
|
+
type Operator = StringOperator | NumericOperator | ArrayOperator;
|
|
148
|
+
interface Filter<T = any> {
|
|
138
149
|
field: string;
|
|
139
150
|
operator: Operator;
|
|
140
|
-
value:
|
|
151
|
+
value: T extends Array<infer U> ? (ArrayOperator extends 'in' | 'nin' ? U[] : U) : T;
|
|
141
152
|
}
|
|
142
153
|
interface Sort {
|
|
143
154
|
field: string;
|
|
@@ -167,6 +178,11 @@ interface QueryResult<T> {
|
|
|
167
178
|
hasMore: boolean;
|
|
168
179
|
};
|
|
169
180
|
}
|
|
181
|
+
interface QueryConfig<TFields extends BaseContentrainType, TLocales extends ContentrainLocales = 'en' | 'tr', TRelations extends Record<string, BaseContentrainType> = Record<string, never>> {
|
|
182
|
+
fields: TFields;
|
|
183
|
+
locales: TLocales;
|
|
184
|
+
relations: TRelations;
|
|
185
|
+
}
|
|
170
186
|
|
|
171
187
|
declare class ContentLoader {
|
|
172
188
|
private options;
|
|
@@ -182,6 +198,7 @@ declare class ContentLoader {
|
|
|
182
198
|
private loadModelConfig;
|
|
183
199
|
private loadContentFile;
|
|
184
200
|
private loadRelations;
|
|
201
|
+
private getModelLocales;
|
|
185
202
|
load<T extends BaseContentrainType>(model: string): Promise<LoaderResult<T>>;
|
|
186
203
|
resolveRelation<T extends BaseContentrainType, R extends BaseContentrainType>(model: string, relationField: keyof T, data: T[], locale?: string): Promise<R[]>;
|
|
187
204
|
}
|
|
@@ -193,6 +210,7 @@ declare class QueryExecutor {
|
|
|
193
210
|
private applySorting;
|
|
194
211
|
private applyPagination;
|
|
195
212
|
private resolveIncludes;
|
|
213
|
+
private applyStringOperation;
|
|
196
214
|
execute<T extends BaseContentrainType>({ model, data, filters, includes, sorting, pagination, options, }: {
|
|
197
215
|
model: string;
|
|
198
216
|
data: T[];
|
|
@@ -228,13 +246,10 @@ declare class ContentrainQueryBuilder<TFields extends BaseContentrainType, TLoca
|
|
|
228
246
|
bypassCache(): this;
|
|
229
247
|
toJSON(): {
|
|
230
248
|
model: string;
|
|
231
|
-
filters: Filter[];
|
|
249
|
+
filters: Filter<any>[];
|
|
232
250
|
includes: Include;
|
|
233
251
|
sorting: Sort[];
|
|
234
|
-
pagination:
|
|
235
|
-
limit?: number;
|
|
236
|
-
offset?: number;
|
|
237
|
-
};
|
|
252
|
+
pagination: Pagination;
|
|
238
253
|
options: QueryOptions;
|
|
239
254
|
};
|
|
240
255
|
get(): Promise<QueryResult<TFields>>;
|
|
@@ -242,10 +257,6 @@ declare class ContentrainQueryBuilder<TFields extends BaseContentrainType, TLoca
|
|
|
242
257
|
count(): Promise<number>;
|
|
243
258
|
}
|
|
244
259
|
|
|
245
|
-
interface MemoryCacheOptions {
|
|
246
|
-
maxSize?: number;
|
|
247
|
-
defaultTTL?: number;
|
|
248
|
-
}
|
|
249
260
|
declare class MemoryCache {
|
|
250
261
|
private cache;
|
|
251
262
|
private options;
|
|
@@ -267,6 +278,9 @@ declare class ContentrainSDK {
|
|
|
267
278
|
constructor(options: ContentLoaderOptions);
|
|
268
279
|
query<T extends QueryConfig<BaseContentrainType, string, Record<string, BaseContentrainType>>>(model: string): ContentrainQueryBuilder<T['fields'], T['locales'], T['relations']>;
|
|
269
280
|
load<T extends BaseContentrainType>(model: string): Promise<LoaderResult<T>>;
|
|
281
|
+
clearCache(): Promise<void>;
|
|
282
|
+
refreshCache(model: string): Promise<void>;
|
|
283
|
+
getCacheStats(): CacheStats;
|
|
270
284
|
}
|
|
271
285
|
|
|
272
|
-
export { type AssetMetadata, type BaseContentrainType, type CacheEntry, type CacheStats, type ContentFile, ContentLoader, type ContentLoaderOptions, type ContentrainComponentId, type ContentrainConfig, type ContentrainFieldType, type ContentrainLocales, ContentrainQueryBuilder, ContentrainSDK, type ContentrainStatus, type FieldMetadata, type FieldOptions, type FieldValidations, type Filter, type Include, type LoaderResult, MemoryCache, type MemoryCacheOptions, type ModelConfig, type ModelMetadata, type Operator, type Pagination, type QueryConfig, QueryExecutor, type QueryOptions, type QueryResult, type RelationConfig, type Sort };
|
|
286
|
+
export { type ArrayOperator, type AssetMetadata, type BaseContentrainType, type CacheEntry, type CacheStats, type ContentFile, ContentLoader, type ContentLoaderOptions, type ContentrainComponentId, type ContentrainConfig, type ContentrainFieldType, type ContentrainLocales, ContentrainQueryBuilder, ContentrainSDK, type ContentrainStatus, type FieldMetadata, type FieldOptions, type FieldValidations, type Filter, type Include, type LoaderResult, MemoryCache, type MemoryCacheOptions, type ModelConfig, type ModelMetadata, type NumericOperator, type Operator, type Pagination, type QueryConfig, QueryExecutor, type QueryOptions, type QueryResult, type RelationConfig, type Sort, type StringOperator };
|
package/dist/index.js
CHANGED
|
@@ -174,14 +174,6 @@ var _ContentLoader = class _ContentLoader {
|
|
|
174
174
|
const modelPath = path.join(this.options.contentDir, "models", `${model}.json`);
|
|
175
175
|
const modelContent = await promises.readFile(modelPath, "utf-8");
|
|
176
176
|
const modelFields = JSON.parse(modelContent);
|
|
177
|
-
if (!Array.isArray(modelFields)) {
|
|
178
|
-
throw new TypeError(`Invalid field configuration for model ${model}: Expected an array of fields`);
|
|
179
|
-
}
|
|
180
|
-
modelFields.forEach((field, index) => {
|
|
181
|
-
if (!field.fieldId || !field.fieldType || !field.componentId) {
|
|
182
|
-
throw new Error(`Invalid field at index ${index} for model ${model}: Missing required properties`);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
177
|
return {
|
|
186
178
|
metadata: modelMetadata,
|
|
187
179
|
fields: modelFields
|
|
@@ -190,12 +182,22 @@ var _ContentLoader = class _ContentLoader {
|
|
|
190
182
|
throw new Error(`Failed to load model config for ${model}: ${error?.message || "Unknown error"}`);
|
|
191
183
|
}
|
|
192
184
|
}
|
|
193
|
-
async loadContentFile(model, locale) {
|
|
185
|
+
async loadContentFile(model, locale = "default") {
|
|
194
186
|
try {
|
|
187
|
+
const modelConfig = await this.loadModelConfig(model);
|
|
195
188
|
let contentPath;
|
|
196
|
-
if (
|
|
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
|
+
}
|
|
197
196
|
contentPath = path.join(this.options.contentDir, model, `${locale}.json`);
|
|
198
197
|
} else {
|
|
198
|
+
if (locale !== "default") {
|
|
199
|
+
console.warn(`Locale "${locale}" specified for non-localized model "${model}". This parameter will be ignored.`);
|
|
200
|
+
}
|
|
199
201
|
contentPath = path.join(this.options.contentDir, model, `${model}.json`);
|
|
200
202
|
}
|
|
201
203
|
const content = await promises.readFile(contentPath, "utf-8");
|
|
@@ -203,7 +205,7 @@ var _ContentLoader = class _ContentLoader {
|
|
|
203
205
|
const data = JSON.parse(content);
|
|
204
206
|
return {
|
|
205
207
|
model,
|
|
206
|
-
locale,
|
|
208
|
+
locale: modelConfig.metadata.localization ? locale : void 0,
|
|
207
209
|
data
|
|
208
210
|
};
|
|
209
211
|
} catch {
|
|
@@ -243,8 +245,31 @@ var _ContentLoader = class _ContentLoader {
|
|
|
243
245
|
throw new Error(`Failed to load relations for ${model}: ${error?.message || "Unknown error"}`);
|
|
244
246
|
}
|
|
245
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
|
+
}
|
|
246
271
|
async load(model) {
|
|
247
|
-
const cacheKey =
|
|
272
|
+
const cacheKey = `${model}`;
|
|
248
273
|
if (this.options.cache) {
|
|
249
274
|
const cached = await this.cache.get(cacheKey);
|
|
250
275
|
if (cached)
|
|
@@ -256,18 +281,34 @@ var _ContentLoader = class _ContentLoader {
|
|
|
256
281
|
this.relations.set(model, relations);
|
|
257
282
|
const content = {};
|
|
258
283
|
if (modelConfig.metadata.localization) {
|
|
259
|
-
const locales =
|
|
284
|
+
const locales = await this.getModelLocales(model, modelConfig);
|
|
260
285
|
for (const locale of locales) {
|
|
261
|
-
|
|
262
|
-
|
|
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
|
+
}
|
|
263
295
|
}
|
|
264
296
|
} else {
|
|
265
297
|
const file = await this.loadContentFile(model);
|
|
266
298
|
content.default = file.data;
|
|
267
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
|
+
}
|
|
268
308
|
const result = {
|
|
269
309
|
model: modelConfig,
|
|
270
|
-
content
|
|
310
|
+
content,
|
|
311
|
+
assets
|
|
271
312
|
};
|
|
272
313
|
if (this.options.cache) {
|
|
273
314
|
const ttl = this.getModelTTL(model);
|
|
@@ -297,14 +338,16 @@ var _ContentLoader = class _ContentLoader {
|
|
|
297
338
|
return relatedItem;
|
|
298
339
|
});
|
|
299
340
|
} else {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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;
|
|
308
351
|
}
|
|
309
352
|
} catch (error) {
|
|
310
353
|
throw new Error(`Failed to resolve relation: ${error.message}`);
|
|
@@ -390,7 +433,20 @@ var _ContentrainQueryBuilder = class _ContentrainQueryBuilder {
|
|
|
390
433
|
}
|
|
391
434
|
async get() {
|
|
392
435
|
const result = await this.loader.load(this.model);
|
|
393
|
-
const
|
|
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
|
+
}
|
|
394
450
|
return this.executor.execute({
|
|
395
451
|
model: this.model,
|
|
396
452
|
data,
|
|
@@ -420,50 +476,67 @@ var _QueryExecutor = class _QueryExecutor {
|
|
|
420
476
|
}
|
|
421
477
|
applyFilters(data, filters) {
|
|
422
478
|
return data.filter((item) => {
|
|
423
|
-
return filters.every((
|
|
424
|
-
const
|
|
479
|
+
return filters.every(({ field, operator, value }) => {
|
|
480
|
+
const itemValue = item[field];
|
|
425
481
|
const validOperators = ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "contains", "startsWith", "endsWith"];
|
|
426
|
-
if (!validOperators.includes(
|
|
427
|
-
throw new Error(`Invalid operator: ${
|
|
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
|
+
}
|
|
428
507
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
case "nin":
|
|
445
|
-
return Array.isArray(filter.value) && !filter.value.includes(value);
|
|
446
|
-
case "contains":
|
|
447
|
-
return typeof value === "string" && value.includes(filter.value);
|
|
448
|
-
case "startsWith":
|
|
449
|
-
return typeof value === "string" && value.startsWith(filter.value);
|
|
450
|
-
case "endsWith":
|
|
451
|
-
return typeof value === "string" && value.endsWith(filter.value);
|
|
452
|
-
default:
|
|
453
|
-
return false;
|
|
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
|
+
}
|
|
454
523
|
}
|
|
524
|
+
return false;
|
|
455
525
|
});
|
|
456
526
|
});
|
|
457
527
|
}
|
|
458
528
|
applySorting(data, sorting) {
|
|
459
529
|
return [...data].sort((a, b) => {
|
|
460
|
-
for (const
|
|
461
|
-
|
|
462
|
-
|
|
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];
|
|
463
536
|
if (aValue === bValue)
|
|
464
537
|
continue;
|
|
465
|
-
const
|
|
466
|
-
return
|
|
538
|
+
const compareResult = aValue < bValue ? -1 : 1;
|
|
539
|
+
return direction === "asc" ? compareResult : -compareResult;
|
|
467
540
|
}
|
|
468
541
|
return 0;
|
|
469
542
|
});
|
|
@@ -506,6 +579,24 @@ var _QueryExecutor = class _QueryExecutor {
|
|
|
506
579
|
}
|
|
507
580
|
return result;
|
|
508
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
|
+
}
|
|
509
600
|
async execute({
|
|
510
601
|
model,
|
|
511
602
|
data,
|
|
@@ -556,6 +647,15 @@ var _ContentrainSDK = class _ContentrainSDK {
|
|
|
556
647
|
async load(model) {
|
|
557
648
|
return this.loader.load(model);
|
|
558
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
|
+
}
|
|
559
659
|
};
|
|
560
660
|
__name(_ContentrainSDK, "ContentrainSDK");
|
|
561
661
|
var ContentrainSDK = _ContentrainSDK;
|