@contentrain/query 5.0.2 → 5.1.1
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/README.md +60 -4
- package/dist/cdn/index.cjs +9 -0
- package/dist/cdn/index.d.cts +2 -0
- package/dist/cdn/index.d.mts +2 -0
- package/dist/cdn/index.mjs +2 -0
- package/dist/cdn-C8uXJe3A.cjs +351 -0
- package/dist/cdn-D66-Npqt.mjs +312 -0
- package/dist/cdn-D66-Npqt.mjs.map +1 -0
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{generate-C5Qz8qKt.mjs → generate-CAoJCGIe.mjs} +1 -1
- package/dist/{generate-C5Qz8qKt.mjs.map → generate-CAoJCGIe.mjs.map} +1 -1
- package/dist/generator/generate.cjs +1 -1
- package/dist/generator/generate.mjs +1 -1
- package/dist/index-D5zB3y75.d.mts +127 -0
- package/dist/index-D5zB3y75.d.mts.map +1 -0
- package/dist/index-Xjd0isXU.d.cts +127 -0
- package/dist/index-Xjd0isXU.d.cts.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -4
- /package/dist/{generate-CPKYh6ZU.cjs → generate-DWu2BzSt.cjs} +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
//#region src/cdn/errors.ts
|
|
2
|
+
var ContentrainError = class extends Error {
|
|
3
|
+
constructor(status, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "ContentrainError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/cdn/http-transport.ts
|
|
11
|
+
var HttpTransport = class {
|
|
12
|
+
_baseUrl;
|
|
13
|
+
_projectId;
|
|
14
|
+
_apiKey;
|
|
15
|
+
_cache = /* @__PURE__ */ new Map();
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this._baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
18
|
+
this._projectId = config.projectId;
|
|
19
|
+
this._apiKey = config.apiKey;
|
|
20
|
+
}
|
|
21
|
+
async fetch(path) {
|
|
22
|
+
const url = `${this._baseUrl}/${this._projectId}/${path}`;
|
|
23
|
+
const cached = this._cache.get(path);
|
|
24
|
+
const headers = { "Authorization": `Bearer ${this._apiKey}` };
|
|
25
|
+
if (cached?.etag) headers["If-None-Match"] = cached.etag;
|
|
26
|
+
const res = await globalThis.fetch(url, { headers });
|
|
27
|
+
if (res.status === 304 && cached) return cached.data;
|
|
28
|
+
if (!res.ok) throw new ContentrainError(res.status, await res.text());
|
|
29
|
+
const data = await res.json();
|
|
30
|
+
const etag = res.headers.get("etag") ?? "";
|
|
31
|
+
if (etag) this._cache.set(path, {
|
|
32
|
+
data,
|
|
33
|
+
etag
|
|
34
|
+
});
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
collection(modelId) {
|
|
38
|
+
return {
|
|
39
|
+
getAll: async (locale) => {
|
|
40
|
+
const map = await this.fetch(`content/${modelId}/${locale}.json`);
|
|
41
|
+
return Object.entries(map).map(([id, entry]) => Object.assign({ id }, entry));
|
|
42
|
+
},
|
|
43
|
+
getOne: async (id, locale) => {
|
|
44
|
+
const entry = (await this.fetch(`content/${modelId}/${locale}.json`))[id];
|
|
45
|
+
return entry ? {
|
|
46
|
+
id,
|
|
47
|
+
...entry
|
|
48
|
+
} : null;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
singleton(modelId) {
|
|
53
|
+
return { get: (locale) => this.fetch(`content/${modelId}/${locale}.json`) };
|
|
54
|
+
}
|
|
55
|
+
dictionary(modelId) {
|
|
56
|
+
return { get: (locale) => this.fetch(`content/${modelId}/${locale}.json`) };
|
|
57
|
+
}
|
|
58
|
+
document(modelId) {
|
|
59
|
+
return {
|
|
60
|
+
getIndex: (locale) => this.fetch(`documents/${modelId}/_index/${locale}.json`),
|
|
61
|
+
getBySlug: (slug, locale) => this.fetch(`documents/${modelId}/${slug}/${locale}.json`)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/cdn/collection-query.ts
|
|
67
|
+
var CdnCollectionQuery = class {
|
|
68
|
+
_transport;
|
|
69
|
+
_source;
|
|
70
|
+
_modelId;
|
|
71
|
+
_locale = "en";
|
|
72
|
+
_filters = [];
|
|
73
|
+
_sortField = null;
|
|
74
|
+
_sortOrder = "asc";
|
|
75
|
+
_limit = null;
|
|
76
|
+
_offset = 0;
|
|
77
|
+
_includes = [];
|
|
78
|
+
constructor(transport, modelId, defaultLocale) {
|
|
79
|
+
this._transport = transport;
|
|
80
|
+
this._source = transport.collection(modelId);
|
|
81
|
+
this._modelId = modelId;
|
|
82
|
+
if (defaultLocale) this._locale = defaultLocale;
|
|
83
|
+
}
|
|
84
|
+
locale(lang) {
|
|
85
|
+
this._locale = lang;
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
where(field, op, value) {
|
|
89
|
+
this._filters.push({
|
|
90
|
+
field,
|
|
91
|
+
op,
|
|
92
|
+
value
|
|
93
|
+
});
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
sort(field, order = "asc") {
|
|
97
|
+
this._sortField = field;
|
|
98
|
+
this._sortOrder = order;
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
limit(n) {
|
|
102
|
+
this._limit = n;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
offset(n) {
|
|
106
|
+
this._offset = n;
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
include(...fields) {
|
|
110
|
+
this._includes.push(...fields);
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
async all() {
|
|
114
|
+
let items = await this._source.getAll(this._locale);
|
|
115
|
+
for (const clause of this._filters) items = items.filter((item) => applyWhere$1(item, clause));
|
|
116
|
+
if (this._sortField) {
|
|
117
|
+
const field = this._sortField;
|
|
118
|
+
const dir = this._sortOrder === "asc" ? 1 : -1;
|
|
119
|
+
items = items.toSorted((a, b) => {
|
|
120
|
+
const va = a[field];
|
|
121
|
+
const vb = b[field];
|
|
122
|
+
if (va == null && vb == null) return 0;
|
|
123
|
+
if (va == null) return dir;
|
|
124
|
+
if (vb == null) return -dir;
|
|
125
|
+
if (va < vb) return -dir;
|
|
126
|
+
if (va > vb) return dir;
|
|
127
|
+
return 0;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (this._offset > 0 || this._limit !== null) {
|
|
131
|
+
const end = this._limit !== null ? this._offset + this._limit : void 0;
|
|
132
|
+
items = items.slice(this._offset, end);
|
|
133
|
+
}
|
|
134
|
+
if (this._includes.length > 0) items = await this._resolveIncludes(items);
|
|
135
|
+
return items;
|
|
136
|
+
}
|
|
137
|
+
async first() {
|
|
138
|
+
return (await this.all())[0];
|
|
139
|
+
}
|
|
140
|
+
async _resolveIncludes(items) {
|
|
141
|
+
const cache = /* @__PURE__ */ new Map();
|
|
142
|
+
for (const field of this._includes) for (const item of items) {
|
|
143
|
+
const val = item[field];
|
|
144
|
+
const ids = Array.isArray(val) ? val : val ? [val] : [];
|
|
145
|
+
for (const id of ids) {
|
|
146
|
+
if (typeof id !== "string") continue;
|
|
147
|
+
if (!cache.has(field)) {
|
|
148
|
+
try {
|
|
149
|
+
const related = await this._transport.fetch(`content/${field}/${this._locale}.json`);
|
|
150
|
+
const map = /* @__PURE__ */ new Map();
|
|
151
|
+
for (const [entryId, entry] of Object.entries(related)) map.set(entryId, {
|
|
152
|
+
id: entryId,
|
|
153
|
+
...entry
|
|
154
|
+
});
|
|
155
|
+
cache.set(field, map);
|
|
156
|
+
} catch {
|
|
157
|
+
cache.set(field, /* @__PURE__ */ new Map());
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return items.map((item) => {
|
|
164
|
+
const resolved = { ...item };
|
|
165
|
+
const dst = resolved;
|
|
166
|
+
for (const field of this._includes) {
|
|
167
|
+
const related = cache.get(field);
|
|
168
|
+
if (!related) continue;
|
|
169
|
+
const val = item[field];
|
|
170
|
+
if (Array.isArray(val)) dst[field] = val.map((id) => typeof id === "string" ? related.get(id) ?? id : id);
|
|
171
|
+
else if (typeof val === "string") dst[field] = related.get(val) ?? val;
|
|
172
|
+
}
|
|
173
|
+
return resolved;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
function applyWhere$1(item, clause) {
|
|
178
|
+
const val = item[clause.field];
|
|
179
|
+
switch (clause.op) {
|
|
180
|
+
case "eq": return val === clause.value;
|
|
181
|
+
case "ne": return val !== clause.value;
|
|
182
|
+
case "gt": return val > clause.value;
|
|
183
|
+
case "gte": return val >= clause.value;
|
|
184
|
+
case "lt": return val < clause.value;
|
|
185
|
+
case "lte": return val <= clause.value;
|
|
186
|
+
case "in": return Array.isArray(clause.value) && clause.value.includes(val);
|
|
187
|
+
case "contains":
|
|
188
|
+
if (typeof val === "string") return val.includes(clause.value);
|
|
189
|
+
if (Array.isArray(val)) return val.includes(clause.value);
|
|
190
|
+
return false;
|
|
191
|
+
default: return true;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/cdn/singleton-accessor.ts
|
|
196
|
+
var CdnSingletonAccessor = class {
|
|
197
|
+
_source;
|
|
198
|
+
_locale = "en";
|
|
199
|
+
constructor(source, defaultLocale) {
|
|
200
|
+
this._source = source;
|
|
201
|
+
if (defaultLocale) this._locale = defaultLocale;
|
|
202
|
+
}
|
|
203
|
+
locale(lang) {
|
|
204
|
+
this._locale = lang;
|
|
205
|
+
return this;
|
|
206
|
+
}
|
|
207
|
+
async get() {
|
|
208
|
+
return this._source.get(this._locale);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/cdn/dictionary-accessor.ts
|
|
213
|
+
var CdnDictionaryAccessor = class {
|
|
214
|
+
_source;
|
|
215
|
+
_locale = "en";
|
|
216
|
+
constructor(source, defaultLocale) {
|
|
217
|
+
this._source = source;
|
|
218
|
+
if (defaultLocale) this._locale = defaultLocale;
|
|
219
|
+
}
|
|
220
|
+
locale(lang) {
|
|
221
|
+
this._locale = lang;
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
async get(key, params) {
|
|
225
|
+
const dict = await this._source.get(this._locale);
|
|
226
|
+
if (key === void 0) return dict;
|
|
227
|
+
const value = dict[key];
|
|
228
|
+
if (value === void 0) return void 0;
|
|
229
|
+
if (params) return interpolate(value, params);
|
|
230
|
+
return value;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
function interpolate(template, params) {
|
|
234
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
235
|
+
const val = params[key];
|
|
236
|
+
return val !== void 0 ? String(val) : match;
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/cdn/document-query.ts
|
|
241
|
+
var CdnDocumentQuery = class {
|
|
242
|
+
_source;
|
|
243
|
+
_locale = "en";
|
|
244
|
+
_filters = [];
|
|
245
|
+
constructor(source, defaultLocale) {
|
|
246
|
+
this._source = source;
|
|
247
|
+
if (defaultLocale) this._locale = defaultLocale;
|
|
248
|
+
}
|
|
249
|
+
locale(lang) {
|
|
250
|
+
this._locale = lang;
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
where(field, op, value) {
|
|
254
|
+
this._filters.push({
|
|
255
|
+
field,
|
|
256
|
+
op,
|
|
257
|
+
value
|
|
258
|
+
});
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
async all() {
|
|
262
|
+
let items = await this._source.getIndex(this._locale);
|
|
263
|
+
for (const clause of this._filters) items = items.filter((item) => applyWhere(item, clause));
|
|
264
|
+
return items;
|
|
265
|
+
}
|
|
266
|
+
async first() {
|
|
267
|
+
return (await this.all())[0];
|
|
268
|
+
}
|
|
269
|
+
async bySlug(slug) {
|
|
270
|
+
return this._source.getBySlug(slug, this._locale);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
function applyWhere(item, clause) {
|
|
274
|
+
const val = item[clause.field];
|
|
275
|
+
switch (clause.op) {
|
|
276
|
+
case "eq": return val === clause.value;
|
|
277
|
+
case "ne": return val !== clause.value;
|
|
278
|
+
case "gt": return val > clause.value;
|
|
279
|
+
case "gte": return val >= clause.value;
|
|
280
|
+
case "lt": return val < clause.value;
|
|
281
|
+
case "lte": return val <= clause.value;
|
|
282
|
+
case "in": return Array.isArray(clause.value) && clause.value.includes(val);
|
|
283
|
+
case "contains":
|
|
284
|
+
if (typeof val === "string") return val.includes(clause.value);
|
|
285
|
+
if (Array.isArray(val)) return val.includes(clause.value);
|
|
286
|
+
return false;
|
|
287
|
+
default: return true;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/cdn/index.ts
|
|
292
|
+
function createContentrain(config) {
|
|
293
|
+
const transport = new HttpTransport({
|
|
294
|
+
baseUrl: config.baseUrl ?? "https://studio.contentrain.io/api/cdn/v1",
|
|
295
|
+
projectId: config.projectId,
|
|
296
|
+
apiKey: config.apiKey
|
|
297
|
+
});
|
|
298
|
+
const defaultLocale = config.defaultLocale;
|
|
299
|
+
return {
|
|
300
|
+
collection: (modelId) => new CdnCollectionQuery(transport, modelId, defaultLocale),
|
|
301
|
+
singleton: (modelId) => new CdnSingletonAccessor(transport.singleton(modelId), defaultLocale),
|
|
302
|
+
dictionary: (modelId) => new CdnDictionaryAccessor(transport.dictionary(modelId), defaultLocale),
|
|
303
|
+
document: (modelId) => new CdnDocumentQuery(transport.document(modelId), defaultLocale),
|
|
304
|
+
manifest: () => transport.fetch("_manifest.json"),
|
|
305
|
+
models: () => transport.fetch("models/_index.json"),
|
|
306
|
+
model: (id) => transport.fetch(`models/${id}.json`)
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
export { CdnCollectionQuery as a, CdnSingletonAccessor as i, CdnDocumentQuery as n, HttpTransport as o, CdnDictionaryAccessor as r, ContentrainError as s, createContentrain as t };
|
|
311
|
+
|
|
312
|
+
//# sourceMappingURL=cdn-D66-Npqt.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdn-D66-Npqt.mjs","names":["applyWhere"],"sources":["../src/cdn/errors.ts","../src/cdn/http-transport.ts","../src/cdn/collection-query.ts","../src/cdn/singleton-accessor.ts","../src/cdn/dictionary-accessor.ts","../src/cdn/document-query.ts","../src/cdn/index.ts"],"sourcesContent":["export class ContentrainError extends Error {\n constructor(\n public status: number,\n message: string,\n ) {\n super(message)\n this.name = 'ContentrainError'\n }\n}\n","import { ContentrainError } from './errors.js'\nimport type { CollectionDataSource, SingletonDataSource, DictionaryDataSource, DocumentDataSource } from './data-source.js'\n\ninterface CacheEntry {\n data: unknown\n etag: string\n}\n\nexport class HttpTransport {\n private _baseUrl: string\n private _projectId: string\n private _apiKey: string\n private _cache = new Map<string, CacheEntry>()\n\n constructor(config: { baseUrl: string; projectId: string; apiKey: string }) {\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '')\n this._projectId = config.projectId\n this._apiKey = config.apiKey\n }\n\n async fetch<T>(path: string): Promise<T> {\n const url = `${this._baseUrl}/${this._projectId}/${path}`\n const cached = this._cache.get(path)\n\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${this._apiKey}`,\n }\n if (cached?.etag) {\n headers['If-None-Match'] = cached.etag\n }\n\n const res = await globalThis.fetch(url, { headers })\n\n if (res.status === 304 && cached) return cached.data as T\n if (!res.ok) throw new ContentrainError(res.status, await res.text())\n\n const data = (await res.json()) as T\n const etag = res.headers.get('etag') ?? ''\n if (etag) {\n this._cache.set(path, { data, etag })\n }\n return data\n }\n\n collection<T>(modelId: string): CollectionDataSource<T> {\n return {\n getAll: async (locale) => {\n const map = await this.fetch<Record<string, T>>(`content/${modelId}/${locale}.json`)\n return Object.entries(map).map(([id, entry]) => Object.assign({ id }, entry as object) as T)\n },\n getOne: async (id, locale) => {\n const map = await this.fetch<Record<string, T>>(`content/${modelId}/${locale}.json`)\n const entry = map[id]\n return entry ? { id, ...entry as object } as T : null\n },\n }\n }\n\n singleton<T>(modelId: string): SingletonDataSource<T> {\n return {\n get: (locale) => this.fetch<T>(`content/${modelId}/${locale}.json`),\n }\n }\n\n dictionary(modelId: string): DictionaryDataSource {\n return {\n get: (locale) => this.fetch<Record<string, string>>(`content/${modelId}/${locale}.json`),\n }\n }\n\n document<T>(modelId: string): DocumentDataSource<T> {\n return {\n getIndex: (locale) => this.fetch<T[]>(`documents/${modelId}/_index/${locale}.json`),\n getBySlug: (slug, locale) => this.fetch(`documents/${modelId}/${slug}/${locale}.json`),\n }\n }\n}\n","import type { CollectionDataSource } from './data-source.js'\nimport type { HttpTransport } from './http-transport.js'\n\ntype WhereOp = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'contains'\n\ninterface WhereClause {\n field: string\n op: WhereOp\n value: unknown\n}\n\nexport class CdnCollectionQuery<T extends object> {\n private _transport: HttpTransport\n private _source: CollectionDataSource<T>\n private _modelId: string\n private _locale: string = 'en'\n private _filters: WhereClause[] = []\n private _sortField: string | null = null\n private _sortOrder: 'asc' | 'desc' = 'asc'\n private _limit: number | null = null\n private _offset = 0\n private _includes: string[] = []\n\n constructor(transport: HttpTransport, modelId: string, defaultLocale?: string) {\n this._transport = transport\n this._source = transport.collection<T>(modelId)\n this._modelId = modelId\n if (defaultLocale) this._locale = defaultLocale\n }\n\n locale(lang: string): this {\n this._locale = lang\n return this\n }\n\n where(field: string, op: WhereOp, value: unknown): this {\n this._filters.push({ field, op, value })\n return this\n }\n\n sort(field: string, order: 'asc' | 'desc' = 'asc'): this {\n this._sortField = field\n this._sortOrder = order\n return this\n }\n\n limit(n: number): this {\n this._limit = n\n return this\n }\n\n offset(n: number): this {\n this._offset = n\n return this\n }\n\n include(...fields: string[]): this {\n this._includes.push(...fields)\n return this\n }\n\n async all(): Promise<T[]> {\n let items = await this._source.getAll(this._locale)\n\n // Filter\n for (const clause of this._filters) {\n items = items.filter(item => applyWhere(item, clause))\n }\n\n // Sort\n if (this._sortField) {\n const field = this._sortField\n const dir = this._sortOrder === 'asc' ? 1 : -1\n items = items.toSorted((a, b) => {\n const va = (a as Record<string, unknown>)[field]\n const vb = (b as Record<string, unknown>)[field]\n if (va == null && vb == null) return 0\n if (va == null) return dir\n if (vb == null) return -dir\n if (va < vb) return -dir\n if (va > vb) return dir\n return 0\n })\n }\n\n // Pagination\n if (this._offset > 0 || this._limit !== null) {\n const end = this._limit !== null ? this._offset + this._limit : undefined\n items = items.slice(this._offset, end)\n }\n\n // Resolve relations\n if (this._includes.length > 0) {\n items = await this._resolveIncludes(items)\n }\n\n return items\n }\n\n async first(): Promise<T | undefined> {\n const items = await this.all()\n return items[0]\n }\n\n private async _resolveIncludes(items: T[]): Promise<T[]> {\n // Prefetch related collections to avoid N+1\n const cache = new Map<string, Map<string, Record<string, unknown>>>()\n\n for (const field of this._includes) {\n // Collect all IDs for this field\n for (const item of items) {\n const val = (item as Record<string, unknown>)[field]\n const ids = Array.isArray(val) ? val : val ? [val] : []\n for (const id of ids) {\n if (typeof id !== 'string') continue\n // We don't know the target model from here, so we use field name as hint\n // In production, model metadata would provide this mapping\n if (!cache.has(field)) {\n try {\n const related = await this._transport.fetch<Record<string, unknown>>(`content/${field}/${this._locale}.json`)\n const map = new Map<string, Record<string, unknown>>()\n for (const [entryId, entry] of Object.entries(related)) {\n map.set(entryId, { id: entryId, ...entry as object })\n }\n cache.set(field, map)\n } catch {\n cache.set(field, new Map())\n }\n break\n }\n }\n }\n }\n\n return items.map(item => {\n const resolved = { ...item }\n const dst = resolved as Record<string, unknown>\n for (const field of this._includes) {\n const related = cache.get(field)\n if (!related) continue\n const val = (item as Record<string, unknown>)[field]\n if (Array.isArray(val)) {\n dst[field] = val.map(id => typeof id === 'string' ? (related.get(id) ?? id) : id)\n } else if (typeof val === 'string') {\n dst[field] = related.get(val) ?? val\n }\n }\n return resolved\n })\n }\n}\n\nfunction applyWhere<T>(item: T, clause: WhereClause): boolean {\n const val = (item as Record<string, unknown>)[clause.field]\n switch (clause.op) {\n case 'eq': return val === clause.value\n case 'ne': return val !== clause.value\n case 'gt': return (val as number) > (clause.value as number)\n case 'gte': return (val as number) >= (clause.value as number)\n case 'lt': return (val as number) < (clause.value as number)\n case 'lte': return (val as number) <= (clause.value as number)\n case 'in': return Array.isArray(clause.value) && (clause.value as unknown[]).includes(val)\n case 'contains': {\n if (typeof val === 'string') return val.includes(clause.value as string)\n if (Array.isArray(val)) return val.includes(clause.value)\n return false\n }\n default: return true\n }\n}\n","import type { SingletonDataSource } from './data-source.js'\n\nexport class CdnSingletonAccessor<T extends Record<string, unknown>> {\n private _source: SingletonDataSource<T>\n private _locale: string = 'en'\n\n constructor(source: SingletonDataSource<T>, defaultLocale?: string) {\n this._source = source\n if (defaultLocale) this._locale = defaultLocale\n }\n\n locale(lang: string): this {\n this._locale = lang\n return this\n }\n\n async get(): Promise<T> {\n return this._source.get(this._locale)\n }\n}\n","import type { DictionaryDataSource } from './data-source.js'\n\nexport class CdnDictionaryAccessor {\n private _source: DictionaryDataSource\n private _locale: string = 'en'\n\n constructor(source: DictionaryDataSource, defaultLocale?: string) {\n this._source = source\n if (defaultLocale) this._locale = defaultLocale\n }\n\n locale(lang: string): this {\n this._locale = lang\n return this\n }\n\n async get(): Promise<Record<string, string>>\n async get(key: string): Promise<string | undefined>\n async get(key: string, params: Record<string, string | number>): Promise<string>\n async get(key?: string, params?: Record<string, string | number>): Promise<Record<string, string> | string | undefined> {\n const dict = await this._source.get(this._locale)\n if (key === undefined) return dict\n const value = dict[key]\n if (value === undefined) return undefined\n if (params) return interpolate(value, params)\n return value\n }\n}\n\nfunction interpolate(template: string, params: Record<string, string | number>): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => {\n const val = params[key]\n return val !== undefined ? String(val) : match\n })\n}\n","import type { DocumentDataSource } from './data-source.js'\n\ntype WhereOp = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'contains'\n\ninterface WhereClause {\n field: string\n op: WhereOp\n value: unknown\n}\n\nexport class CdnDocumentQuery<T extends object> {\n private _source: DocumentDataSource<T>\n private _locale: string = 'en'\n private _filters: WhereClause[] = []\n\n constructor(source: DocumentDataSource<T>, defaultLocale?: string) {\n this._source = source\n if (defaultLocale) this._locale = defaultLocale\n }\n\n locale(lang: string): this {\n this._locale = lang\n return this\n }\n\n where(field: string, op: WhereOp, value: unknown): this {\n this._filters.push({ field, op, value })\n return this\n }\n\n async all(): Promise<T[]> {\n let items = await this._source.getIndex(this._locale)\n\n for (const clause of this._filters) {\n items = items.filter(item => applyWhere(item, clause))\n }\n\n return items\n }\n\n async first(): Promise<T | undefined> {\n const items = await this.all()\n return items[0]\n }\n\n async bySlug(slug: string): Promise<{ frontmatter: T; body: string; html: string } | null> {\n return this._source.getBySlug(slug, this._locale)\n }\n}\n\nfunction applyWhere<T>(item: T, clause: WhereClause): boolean {\n const val = (item as Record<string, unknown>)[clause.field]\n switch (clause.op) {\n case 'eq': return val === clause.value\n case 'ne': return val !== clause.value\n case 'gt': return (val as number) > (clause.value as number)\n case 'gte': return (val as number) >= (clause.value as number)\n case 'lt': return (val as number) < (clause.value as number)\n case 'lte': return (val as number) <= (clause.value as number)\n case 'in': return Array.isArray(clause.value) && (clause.value as unknown[]).includes(val)\n case 'contains': {\n if (typeof val === 'string') return val.includes(clause.value as string)\n if (Array.isArray(val)) return val.includes(clause.value)\n return false\n }\n default: return true\n }\n}\n","import { HttpTransport } from './http-transport.js'\nimport { CdnCollectionQuery } from './collection-query.js'\nimport { CdnSingletonAccessor } from './singleton-accessor.js'\nimport { CdnDictionaryAccessor } from './dictionary-accessor.js'\nimport { CdnDocumentQuery } from './document-query.js'\n\nexport interface ContentrainCDNConfig {\n projectId: string\n apiKey: string\n baseUrl?: string\n defaultLocale?: string\n}\n\nexport type ContentrainCDNClient = ReturnType<typeof createContentrain>\n\nexport function createContentrain(config: ContentrainCDNConfig) {\n const transport = new HttpTransport({\n baseUrl: config.baseUrl ?? 'https://studio.contentrain.io/api/cdn/v1',\n projectId: config.projectId,\n apiKey: config.apiKey,\n })\n const defaultLocale = config.defaultLocale\n\n return {\n collection: <T extends object = Record<string, unknown>>(modelId: string) =>\n new CdnCollectionQuery<T>(transport, modelId, defaultLocale),\n\n singleton: <T extends Record<string, unknown> = Record<string, unknown>>(modelId: string) =>\n new CdnSingletonAccessor<T>(transport.singleton<T>(modelId), defaultLocale),\n\n dictionary: (modelId: string) =>\n new CdnDictionaryAccessor(transport.dictionary(modelId), defaultLocale),\n\n document: <T extends object = Record<string, unknown>>(modelId: string) =>\n new CdnDocumentQuery<T>(transport.document<T>(modelId), defaultLocale),\n\n manifest: () => transport.fetch<unknown>('_manifest.json'),\n models: () => transport.fetch<unknown[]>('models/_index.json'),\n model: (id: string) => transport.fetch<unknown>(`models/${id}.json`),\n }\n}\n\n// Re-exports\nexport { ContentrainError } from './errors.js'\nexport type { CollectionDataSource, SingletonDataSource, DictionaryDataSource, DocumentDataSource } from './data-source.js'\nexport { HttpTransport } from './http-transport.js'\nexport { CdnCollectionQuery } from './collection-query.js'\nexport { CdnSingletonAccessor } from './singleton-accessor.js'\nexport { CdnDictionaryAccessor } from './dictionary-accessor.js'\nexport { CdnDocumentQuery } from './document-query.js'\n"],"mappings":"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"}
|
package/dist/cli.cjs
CHANGED
package/dist/cli.mjs
CHANGED