@contentrain/query 5.1.5 → 6.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/cdn/index.cjs +1 -1
- package/dist/cdn/index.d.cts +1 -1
- package/dist/cdn/index.d.mts +1 -1
- package/dist/cdn/index.mjs +1 -1
- package/dist/{cdn-SOuikUcY.mjs → cdn-CNdH7sKk.mjs} +23 -2
- package/dist/cdn-CNdH7sKk.mjs.map +1 -0
- package/dist/{cdn-5ycdk2ET.cjs → cdn-aeCpH9Dt.cjs} +22 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{generate-B5P14n43.mjs → generate-ClWKsmfl.mjs} +81 -39
- package/dist/generate-ClWKsmfl.mjs.map +1 -0
- package/dist/{generate-DAaCl3Np.cjs → generate-FtawIQ_O.cjs} +80 -38
- package/dist/generator/generate.cjs +1 -1
- package/dist/generator/generate.mjs +1 -1
- package/dist/{index-BChs94uA.d.cts → index-mBPlkuGU.d.cts} +4 -1
- package/dist/index-mBPlkuGU.d.cts.map +1 -0
- package/dist/{index-CsCpovuB.d.mts → index-tyAxQaPt.d.mts} +4 -1
- package/dist/index-tyAxQaPt.d.mts.map +1 -0
- package/dist/index.cjs +24 -3
- package/dist/index.d.cts +4 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +4 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +24 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/skills/contentrain-query/references/bundler-config.md +10 -8
- package/dist/cdn-SOuikUcY.mjs.map +0 -1
- package/dist/generate-B5P14n43.mjs.map +0 -1
- package/dist/index-BChs94uA.d.cts.map +0 -1
- package/dist/index-CsCpovuB.d.mts.map +0 -1
package/dist/cdn/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_cdn = require("../cdn-
|
|
2
|
+
const require_cdn = require("../cdn-aeCpH9Dt.cjs");
|
|
3
3
|
exports.CdnCollectionQuery = require_cdn.CdnCollectionQuery;
|
|
4
4
|
exports.CdnDictionaryAccessor = require_cdn.CdnDictionaryAccessor;
|
|
5
5
|
exports.CdnDocumentQuery = require_cdn.CdnDocumentQuery;
|
package/dist/cdn/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as CdnDocumentQuery, E as CdnCollectionQuery, F as SingletonDataSource, M as CollectionDataSource, N as DictionaryDataSource, P as DocumentDataSource, S as MediaManifest, T as CdnSingletonAccessor, _ as FormsClient, a as ConversationClient, b as MediaAsset, c as ConversationHistory, d as ConversationSendOptions, f as ConversationToolResult, g as FormSubmitResult, h as FormFieldConfig, i as ContentrainError, j as HttpTransport, l as ConversationMessage, m as FormConfig, n as ContentrainCDNConfig, o as ConversationClientConfig, p as ConversationUsage, r as createContentrain, s as ConversationContext, t as ContentrainCDNClient, u as ConversationResponse, v as FormsClientConfig, w as CdnDictionaryAccessor, x as MediaAssetMeta, y as MediaAccessor } from "../index-
|
|
1
|
+
import { C as CdnDocumentQuery, E as CdnCollectionQuery, F as SingletonDataSource, M as CollectionDataSource, N as DictionaryDataSource, P as DocumentDataSource, S as MediaManifest, T as CdnSingletonAccessor, _ as FormsClient, a as ConversationClient, b as MediaAsset, c as ConversationHistory, d as ConversationSendOptions, f as ConversationToolResult, g as FormSubmitResult, h as FormFieldConfig, i as ContentrainError, j as HttpTransport, l as ConversationMessage, m as FormConfig, n as ContentrainCDNConfig, o as ConversationClientConfig, p as ConversationUsage, r as createContentrain, s as ConversationContext, t as ContentrainCDNClient, u as ConversationResponse, v as FormsClientConfig, w as CdnDictionaryAccessor, x as MediaAssetMeta, y as MediaAccessor } from "../index-mBPlkuGU.cjs";
|
|
2
2
|
export { CdnCollectionQuery, CdnDictionaryAccessor, CdnDocumentQuery, CdnSingletonAccessor, CollectionDataSource, ContentrainCDNClient, ContentrainCDNConfig, ContentrainError, ConversationClient, ConversationClientConfig, ConversationContext, ConversationHistory, ConversationMessage, ConversationResponse, ConversationSendOptions, ConversationToolResult, ConversationUsage, DictionaryDataSource, DocumentDataSource, FormConfig, FormFieldConfig, FormSubmitResult, FormsClient, FormsClientConfig, HttpTransport, MediaAccessor, MediaAsset, MediaAssetMeta, MediaManifest, SingletonDataSource, createContentrain };
|
package/dist/cdn/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as CdnDocumentQuery, E as CdnCollectionQuery, F as SingletonDataSource, M as CollectionDataSource, N as DictionaryDataSource, P as DocumentDataSource, S as MediaManifest, T as CdnSingletonAccessor, _ as FormsClient, a as ConversationClient, b as MediaAsset, c as ConversationHistory, d as ConversationSendOptions, f as ConversationToolResult, g as FormSubmitResult, h as FormFieldConfig, i as ContentrainError, j as HttpTransport, l as ConversationMessage, m as FormConfig, n as ContentrainCDNConfig, o as ConversationClientConfig, p as ConversationUsage, r as createContentrain, s as ConversationContext, t as ContentrainCDNClient, u as ConversationResponse, v as FormsClientConfig, w as CdnDictionaryAccessor, x as MediaAssetMeta, y as MediaAccessor } from "../index-
|
|
1
|
+
import { C as CdnDocumentQuery, E as CdnCollectionQuery, F as SingletonDataSource, M as CollectionDataSource, N as DictionaryDataSource, P as DocumentDataSource, S as MediaManifest, T as CdnSingletonAccessor, _ as FormsClient, a as ConversationClient, b as MediaAsset, c as ConversationHistory, d as ConversationSendOptions, f as ConversationToolResult, g as FormSubmitResult, h as FormFieldConfig, i as ContentrainError, j as HttpTransport, l as ConversationMessage, m as FormConfig, n as ContentrainCDNConfig, o as ConversationClientConfig, p as ConversationUsage, r as createContentrain, s as ConversationContext, t as ContentrainCDNClient, u as ConversationResponse, v as FormsClientConfig, w as CdnDictionaryAccessor, x as MediaAssetMeta, y as MediaAccessor } from "../index-tyAxQaPt.mjs";
|
|
2
2
|
export { CdnCollectionQuery, CdnDictionaryAccessor, CdnDocumentQuery, CdnSingletonAccessor, CollectionDataSource, ContentrainCDNClient, ContentrainCDNConfig, ContentrainError, ConversationClient, ConversationClientConfig, ConversationContext, ConversationHistory, ConversationMessage, ConversationResponse, ConversationSendOptions, ConversationToolResult, ConversationUsage, DictionaryDataSource, DocumentDataSource, FormConfig, FormFieldConfig, FormSubmitResult, FormsClient, FormsClientConfig, HttpTransport, MediaAccessor, MediaAsset, MediaAssetMeta, MediaManifest, SingletonDataSource, createContentrain };
|
package/dist/cdn/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as CdnDocumentQuery, c as CdnCollectionQuery, i as MediaAccessor, l as HttpTransport, n as ConversationClient, o as CdnDictionaryAccessor, r as FormsClient, s as CdnSingletonAccessor, t as createContentrain, u as ContentrainError } from "../cdn-
|
|
1
|
+
import { a as CdnDocumentQuery, c as CdnCollectionQuery, i as MediaAccessor, l as HttpTransport, n as ConversationClient, o as CdnDictionaryAccessor, r as FormsClient, s as CdnSingletonAccessor, t as createContentrain, u as ContentrainError } from "../cdn-CNdH7sKk.mjs";
|
|
2
2
|
export { CdnCollectionQuery, CdnDictionaryAccessor, CdnDocumentQuery, CdnSingletonAccessor, ContentrainError, ConversationClient, FormsClient, HttpTransport, MediaAccessor, createContentrain };
|
|
@@ -5,7 +5,9 @@ function applyWhere(item, clause) {
|
|
|
5
5
|
case "eq":
|
|
6
6
|
if (Array.isArray(val)) return val.includes(clause.value);
|
|
7
7
|
return val === clause.value;
|
|
8
|
-
case "ne":
|
|
8
|
+
case "ne":
|
|
9
|
+
if (Array.isArray(val)) return !val.includes(clause.value);
|
|
10
|
+
return val !== clause.value;
|
|
9
11
|
case "gt": return val > clause.value;
|
|
10
12
|
case "gte": return val >= clause.value;
|
|
11
13
|
case "lt": return val < clause.value;
|
|
@@ -275,6 +277,8 @@ var CdnDocumentQuery = class {
|
|
|
275
277
|
_source;
|
|
276
278
|
_locale = "en";
|
|
277
279
|
_filters = [];
|
|
280
|
+
_sortField = null;
|
|
281
|
+
_sortOrder = "asc";
|
|
278
282
|
constructor(source, defaultLocale) {
|
|
279
283
|
this._source = source;
|
|
280
284
|
if (defaultLocale) this._locale = defaultLocale;
|
|
@@ -291,9 +295,26 @@ var CdnDocumentQuery = class {
|
|
|
291
295
|
});
|
|
292
296
|
return this;
|
|
293
297
|
}
|
|
298
|
+
sort(field, order = "asc") {
|
|
299
|
+
this._sortField = field;
|
|
300
|
+
this._sortOrder = order;
|
|
301
|
+
return this;
|
|
302
|
+
}
|
|
294
303
|
async all() {
|
|
295
304
|
let items = await this._source.getIndex(this._locale);
|
|
296
305
|
for (const clause of this._filters) items = items.filter((item) => applyWhere(item, clause));
|
|
306
|
+
if (this._sortField) {
|
|
307
|
+
const sf = this._sortField;
|
|
308
|
+
const dir = this._sortOrder === "asc" ? 1 : -1;
|
|
309
|
+
items = items.toSorted((a, b) => {
|
|
310
|
+
const va = a[sf];
|
|
311
|
+
const vb = b[sf];
|
|
312
|
+
if (va == null && vb == null) return 0;
|
|
313
|
+
if (va == null) return dir;
|
|
314
|
+
if (vb == null) return -dir;
|
|
315
|
+
return va < vb ? -dir : va > vb ? dir : 0;
|
|
316
|
+
});
|
|
317
|
+
}
|
|
297
318
|
return items;
|
|
298
319
|
}
|
|
299
320
|
async count() {
|
|
@@ -441,4 +462,4 @@ function createContentrain(config) {
|
|
|
441
462
|
//#endregion
|
|
442
463
|
export { CdnDocumentQuery as a, CdnCollectionQuery as c, applyWhere as d, MediaAccessor as i, HttpTransport as l, ConversationClient as n, CdnDictionaryAccessor as o, FormsClient as r, CdnSingletonAccessor as s, createContentrain as t, ContentrainError as u };
|
|
443
464
|
|
|
444
|
-
//# sourceMappingURL=cdn-
|
|
465
|
+
//# sourceMappingURL=cdn-CNdH7sKk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdn-CNdH7sKk.mjs","names":[],"sources":["../src/shared/where.ts","../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/media-accessor.ts","../src/cdn/forms-client.ts","../src/cdn/conversation-client.ts","../src/cdn/index.ts"],"sourcesContent":["export type WhereOp = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'contains'\n\nexport interface WhereClause {\n field: string\n op: WhereOp\n value: unknown\n}\n\nexport function applyWhere<T>(item: T, clause: WhereClause): boolean {\n const val = (item as Record<string, unknown>)[clause.field]\n switch (clause.op) {\n case 'eq': {\n if (Array.isArray(val)) return val.includes(clause.value)\n return val === clause.value\n }\n case 'ne': {\n if (Array.isArray(val)) return !val.includes(clause.value)\n return val !== clause.value\n }\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","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 buildUrl(path: string): string {\n return `${this._baseUrl}/${this._projectId}/${path}`\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'\nimport type { WhereOp, WhereClause } from '../shared/where.js'\nimport { applyWhere } from '../shared/where.js'\n\nexport interface EntryMeta {\n status?: string\n publish_at?: string\n expire_at?: string\n updated_by?: string\n approved_by?: string\n [key: string]: 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 private _withMeta = false\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 withMeta(): this {\n this._withMeta = true\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 // Enrich with entry metadata\n if (this._withMeta) {\n items = await this._enrichMeta(items)\n }\n\n return items\n }\n\n async count(): Promise<number> {\n const items = await this.all()\n return items.length\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 private async _enrichMeta(items: T[]): Promise<T[]> {\n let metaMap: Record<string, EntryMeta> = {}\n try {\n metaMap = await this._transport.fetch<Record<string, EntryMeta>>(`meta/${this._modelId}/${this._locale}.json`)\n } catch {\n // No meta available — return items unchanged\n return items\n }\n return items.map(item => {\n const id = (item as Record<string, unknown>).id as string\n const meta = id ? metaMap[id] : undefined\n if (meta) {\n return { ...item, _meta: meta }\n }\n return item\n })\n }\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'\nimport type { WhereOp, WhereClause } from '../shared/where.js'\nimport { applyWhere } from '../shared/where.js'\n\nexport class CdnDocumentQuery<T extends object> {\n private _source: DocumentDataSource<T>\n private _locale: string = 'en'\n private _filters: WhereClause[] = []\n private _sortField: string | null = null\n private _sortOrder: 'asc' | 'desc' = 'asc'\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 sort(field: string, order: 'asc' | 'desc' = 'asc'): this {\n this._sortField = field\n this._sortOrder = order\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 if (this._sortField) {\n const sf = this._sortField\n const dir = this._sortOrder === 'asc' ? 1 : -1\n items = items.toSorted((a, b) => {\n const va = (a as Record<string, unknown>)[sf] as number | string | null | undefined\n const vb = (b as Record<string, unknown>)[sf] as number | string | null | undefined\n if (va == null && vb == null) return 0\n if (va == null) return dir\n if (vb == null) return -dir\n return va < vb ? -dir : va > vb ? dir : 0\n })\n }\n\n return items\n }\n\n async count(): Promise<number> {\n const items = await this.all()\n return items.length\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","import type { HttpTransport } from './http-transport.js'\n\nexport interface MediaAssetMeta {\n width?: number\n height?: number\n format?: string\n size?: number\n blurhash?: string | null\n alt?: string | null\n}\n\nexport interface MediaAsset {\n original: string\n variants: Record<string, string>\n meta: MediaAssetMeta\n}\n\nexport interface MediaManifest {\n version: string\n assets: Record<string, MediaAsset>\n}\n\nexport class MediaAccessor {\n private _transport: HttpTransport\n private _manifest: MediaManifest | null = null\n\n constructor(transport: HttpTransport) {\n this._transport = transport\n }\n\n async manifest(): Promise<MediaManifest> {\n if (!this._manifest) {\n this._manifest = await this._transport.fetch<MediaManifest>('_media_manifest.json')\n }\n return this._manifest\n }\n\n async assets(): Promise<Record<string, MediaAsset>> {\n const m = await this.manifest()\n return m.assets\n }\n\n async asset(path: string): Promise<MediaAsset | null> {\n const all = await this.assets()\n return all[path] ?? null\n }\n\n async list(): Promise<Array<{ path: string } & MediaAsset>> {\n const all = await this.assets()\n return Object.entries(all).map(([path, asset]) =>\n Object.assign({ path }, asset),\n )\n }\n\n resolve(asset: MediaAsset, variant?: string): string {\n if (variant && asset.variants[variant]) {\n return asset.variants[variant]\n }\n return asset.original\n }\n\n url(asset: MediaAsset, variant?: string): string {\n const path = this.resolve(asset, variant)\n return this._transport.buildUrl(path)\n }\n}\n","import { ContentrainError } from './errors.js'\n\nexport interface FormFieldConfig {\n id: string\n type: string\n required?: boolean\n label?: string\n placeholder?: string\n options?: Array<{ value: string; label: string }>\n pattern?: string\n min?: number\n max?: number\n}\n\nexport interface FormConfig {\n modelId: string\n fields: FormFieldConfig[]\n captchaType?: 'turnstile' | null\n successMessage?: string\n honeypotField?: string\n}\n\nexport interface FormSubmitResult {\n success: boolean\n message?: string\n errors?: Array<{ field: string; message: string }>\n}\n\nexport interface FormsClientConfig {\n baseUrl: string\n projectId: string\n apiKey?: string\n}\n\nexport class FormsClient {\n private _baseUrl: string\n private _projectId: string\n private _apiKey: string | undefined\n\n constructor(config: FormsClientConfig) {\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '')\n this._projectId = config.projectId\n this._apiKey = config.apiKey\n }\n\n async config(modelId: string): Promise<FormConfig> {\n const url = `${this._baseUrl}/${this._projectId}/${modelId}/config`\n const headers: Record<string, string> = {}\n if (this._apiKey) headers['Authorization'] = `Bearer ${this._apiKey}`\n\n const res = await globalThis.fetch(url, { headers })\n if (!res.ok) throw new ContentrainError(res.status, await res.text())\n return (await res.json()) as FormConfig\n }\n\n async submit(modelId: string, data: Record<string, unknown>, options?: {\n captchaToken?: string\n }): Promise<FormSubmitResult> {\n const url = `${this._baseUrl}/${this._projectId}/${modelId}/submit`\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n }\n if (this._apiKey) headers['Authorization'] = `Bearer ${this._apiKey}`\n\n const body: Record<string, unknown> = { ...data }\n if (options?.captchaToken) {\n body['cf-turnstile-response'] = options.captchaToken\n }\n\n const res = await globalThis.fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n })\n\n const result = (await res.json()) as FormSubmitResult\n\n if (!res.ok && !result.errors) {\n throw new ContentrainError(res.status, result.message ?? 'Form submission failed')\n }\n\n return result\n }\n}\n","import { ContentrainError } from './errors.js'\n\nexport interface ConversationContext {\n activeModelId?: string | null\n activeLocale?: string\n activeEntryId?: string | null\n panelState?: string\n activeBranch?: string | null\n}\n\nexport interface ConversationSendOptions {\n conversationId?: string\n context?: ConversationContext\n}\n\nexport interface ConversationToolResult {\n id: string\n name: string\n result: unknown\n}\n\nexport interface ConversationUsage {\n inputTokens: number\n outputTokens: number\n}\n\nexport interface ConversationResponse {\n conversationId: string\n message: string\n toolResults?: ConversationToolResult[]\n usage: ConversationUsage\n}\n\nexport interface ConversationMessage {\n id: string\n role: 'user' | 'assistant'\n content: string | unknown[]\n toolCalls?: unknown[]\n model?: string\n usage?: ConversationUsage\n createdAt: string\n}\n\nexport interface ConversationHistory {\n conversationId: string\n messages: ConversationMessage[]\n}\n\nexport interface ConversationClientConfig {\n baseUrl: string\n projectId: string\n apiKey: string\n}\n\nexport class ConversationClient {\n private _baseUrl: string\n private _projectId: string\n private _apiKey: string\n\n constructor(config: ConversationClientConfig) {\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '')\n this._projectId = config.projectId\n this._apiKey = config.apiKey\n }\n\n async send(message: string, options?: ConversationSendOptions): Promise<ConversationResponse> {\n const url = `${this._baseUrl}/${this._projectId}/message`\n\n const body: Record<string, unknown> = { message }\n if (options?.conversationId) body.conversationId = options.conversationId\n if (options?.context) body.context = options.context\n\n const res = await globalThis.fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this._apiKey}`,\n },\n body: JSON.stringify(body),\n })\n\n if (!res.ok) {\n throw new ContentrainError(res.status, await res.text())\n }\n\n return (await res.json()) as ConversationResponse\n }\n\n async history(conversationId: string, options?: { limit?: number }): Promise<ConversationHistory> {\n const params = new URLSearchParams({ conversationId })\n if (options?.limit) params.set('limit', String(options.limit))\n\n const url = `${this._baseUrl}/${this._projectId}/history?${params}`\n\n const res = await globalThis.fetch(url, {\n headers: {\n 'Authorization': `Bearer ${this._apiKey}`,\n },\n })\n\n if (!res.ok) {\n throw new ContentrainError(res.status, await res.text())\n }\n\n return (await res.json()) as ConversationHistory\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'\nimport { MediaAccessor } from './media-accessor.js'\nimport { FormsClient } from './forms-client.js'\nimport { ConversationClient } from './conversation-client.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 media: () => new MediaAccessor(transport),\n\n form: () => new FormsClient({\n baseUrl: (config.baseUrl ?? 'https://studio.contentrain.io/api/cdn/v1').replace('/cdn/v1', '/forms/v1'),\n projectId: config.projectId,\n apiKey: config.apiKey,\n }),\n\n conversation: () => new ConversationClient({\n baseUrl: (config.baseUrl ?? 'https://studio.contentrain.io/api/cdn/v1').replace('/cdn/v1', '/conversation/v1'),\n projectId: config.projectId,\n apiKey: config.apiKey,\n }),\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'\nexport { MediaAccessor } from './media-accessor.js'\nexport type { MediaAsset, MediaAssetMeta, MediaManifest } from './media-accessor.js'\nexport { FormsClient } from './forms-client.js'\nexport type { FormConfig, FormFieldConfig, FormSubmitResult, FormsClientConfig } from './forms-client.js'\nexport { ConversationClient } from './conversation-client.js'\nexport type {\n ConversationClientConfig,\n ConversationContext,\n ConversationSendOptions,\n ConversationResponse,\n ConversationToolResult,\n ConversationUsage,\n ConversationMessage,\n ConversationHistory,\n} from './conversation-client.js'\n"],"mappings":";AAQA,SAAgB,WAAc,MAAS,QAA8B;CACnE,MAAM,MAAO,KAAiC,OAAO;AACrD,SAAQ,OAAO,IAAf;EACE,KAAK;AACH,OAAI,MAAM,QAAQ,IAAI,CAAE,QAAO,IAAI,SAAS,OAAO,MAAM;AACzD,UAAO,QAAQ,OAAO;EAExB,KAAK;AACH,OAAI,MAAM,QAAQ,IAAI,CAAE,QAAO,CAAC,IAAI,SAAS,OAAO,MAAM;AAC1D,UAAO,QAAQ,OAAO;EAExB,KAAK,KAAM,QAAQ,MAAkB,OAAO;EAC5C,KAAK,MAAO,QAAQ,OAAmB,OAAO;EAC9C,KAAK,KAAM,QAAQ,MAAkB,OAAO;EAC5C,KAAK,MAAO,QAAQ,OAAmB,OAAO;EAC9C,KAAK,KAAM,QAAO,MAAM,QAAQ,OAAO,MAAM,IAAK,OAAO,MAAoB,SAAS,IAAI;EAC1F,KAAK;AACH,OAAI,OAAO,QAAQ,SAAU,QAAO,IAAI,SAAS,OAAO,MAAgB;AACxE,OAAI,MAAM,QAAQ,IAAI,CAAE,QAAO,IAAI,SAAS,OAAO,MAAM;AACzD,UAAO;EAET,QAAS,QAAO;;;;;AC7BpB,IAAa,mBAAb,cAAsC,MAAM;CAC1C,YACE,QACA,SACA;AACA,QAAM,QAAQ;AAHP,OAAA,SAAA;AAIP,OAAK,OAAO;;;;;ACEhB,IAAa,gBAAb,MAA2B;CACzB;CACA;CACA;CACA,yBAAiB,IAAI,KAAyB;CAE9C,YAAY,QAAgE;AAC1E,OAAK,WAAW,OAAO,QAAQ,QAAQ,QAAQ,GAAG;AAClD,OAAK,aAAa,OAAO;AACzB,OAAK,UAAU,OAAO;;CAGxB,SAAS,MAAsB;AAC7B,SAAO,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW,GAAG;;CAGhD,MAAM,MAAS,MAA0B;EACvC,MAAM,MAAM,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW,GAAG;EACnD,MAAM,SAAS,KAAK,OAAO,IAAI,KAAK;EAEpC,MAAM,UAAkC,EACtC,iBAAiB,UAAU,KAAK,WACjC;AACD,MAAI,QAAQ,KACV,SAAQ,mBAAmB,OAAO;EAGpC,MAAM,MAAM,MAAM,WAAW,MAAM,KAAK,EAAE,SAAS,CAAC;AAEpD,MAAI,IAAI,WAAW,OAAO,OAAQ,QAAO,OAAO;AAChD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,iBAAiB,IAAI,QAAQ,MAAM,IAAI,MAAM,CAAC;EAErE,MAAM,OAAQ,MAAM,IAAI,MAAM;EAC9B,MAAM,OAAO,IAAI,QAAQ,IAAI,OAAO,IAAI;AACxC,MAAI,KACF,MAAK,OAAO,IAAI,MAAM;GAAE;GAAM;GAAM,CAAC;AAEvC,SAAO;;CAGT,WAAc,SAA0C;AACtD,SAAO;GACL,QAAQ,OAAO,WAAW;IACxB,MAAM,MAAM,MAAM,KAAK,MAAyB,WAAW,QAAQ,GAAG,OAAO,OAAO;AACpF,WAAO,OAAO,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,OAAO,OAAO,EAAE,IAAI,EAAE,MAAgB,CAAM;;GAE9F,QAAQ,OAAO,IAAI,WAAW;IAE5B,MAAM,SADM,MAAM,KAAK,MAAyB,WAAW,QAAQ,GAAG,OAAO,OAAO,EAClE;AAClB,WAAO,QAAQ;KAAE;KAAI,GAAG;KAAiB,GAAQ;;GAEpD;;CAGH,UAAa,SAAyC;AACpD,SAAO,EACL,MAAM,WAAW,KAAK,MAAS,WAAW,QAAQ,GAAG,OAAO,OAAO,EACpE;;CAGH,WAAW,SAAuC;AAChD,SAAO,EACL,MAAM,WAAW,KAAK,MAA8B,WAAW,QAAQ,GAAG,OAAO,OAAO,EACzF;;CAGH,SAAY,SAAwC;AAClD,SAAO;GACL,WAAW,WAAW,KAAK,MAAW,aAAa,QAAQ,UAAU,OAAO,OAAO;GACnF,YAAY,MAAM,WAAW,KAAK,MAAM,aAAa,QAAQ,GAAG,KAAK,GAAG,OAAO,OAAO;GACvF;;;;;AChEL,IAAa,qBAAb,MAAkD;CAChD;CACA;CACA;CACA,UAA0B;CAC1B,WAAkC,EAAE;CACpC,aAAoC;CACpC,aAAqC;CACrC,SAAgC;CAChC,UAAkB;CAClB,YAA8B,EAAE;CAChC,YAAoB;CAEpB,YAAY,WAA0B,SAAiB,eAAwB;AAC7E,OAAK,aAAa;AAClB,OAAK,UAAU,UAAU,WAAc,QAAQ;AAC/C,OAAK,WAAW;AAChB,MAAI,cAAe,MAAK,UAAU;;CAGpC,OAAO,MAAoB;AACzB,OAAK,UAAU;AACf,SAAO;;CAGT,MAAM,OAAe,IAAa,OAAsB;AACtD,OAAK,SAAS,KAAK;GAAE;GAAO;GAAI;GAAO,CAAC;AACxC,SAAO;;CAGT,KAAK,OAAe,QAAwB,OAAa;AACvD,OAAK,aAAa;AAClB,OAAK,aAAa;AAClB,SAAO;;CAGT,MAAM,GAAiB;AACrB,OAAK,SAAS;AACd,SAAO;;CAGT,OAAO,GAAiB;AACtB,OAAK,UAAU;AACf,SAAO;;CAGT,QAAQ,GAAG,QAAwB;AACjC,OAAK,UAAU,KAAK,GAAG,OAAO;AAC9B,SAAO;;CAGT,WAAiB;AACf,OAAK,YAAY;AACjB,SAAO;;CAGT,MAAM,MAAoB;EACxB,IAAI,QAAQ,MAAM,KAAK,QAAQ,OAAO,KAAK,QAAQ;AAGnD,OAAK,MAAM,UAAU,KAAK,SACxB,SAAQ,MAAM,QAAO,SAAQ,WAAW,MAAM,OAAO,CAAC;AAIxD,MAAI,KAAK,YAAY;GACnB,MAAM,QAAQ,KAAK;GACnB,MAAM,MAAM,KAAK,eAAe,QAAQ,IAAI;AAC5C,WAAQ,MAAM,UAAU,GAAG,MAAM;IAC/B,MAAM,KAAM,EAA8B;IAC1C,MAAM,KAAM,EAA8B;AAC1C,QAAI,MAAM,QAAQ,MAAM,KAAM,QAAO;AACrC,QAAI,MAAM,KAAM,QAAO;AACvB,QAAI,MAAM,KAAM,QAAO,CAAC;AACxB,QAAI,KAAK,GAAI,QAAO,CAAC;AACrB,QAAI,KAAK,GAAI,QAAO;AACpB,WAAO;KACP;;AAIJ,MAAI,KAAK,UAAU,KAAK,KAAK,WAAW,MAAM;GAC5C,MAAM,MAAM,KAAK,WAAW,OAAO,KAAK,UAAU,KAAK,SAAS,KAAA;AAChE,WAAQ,MAAM,MAAM,KAAK,SAAS,IAAI;;AAIxC,MAAI,KAAK,UAAU,SAAS,EAC1B,SAAQ,MAAM,KAAK,iBAAiB,MAAM;AAI5C,MAAI,KAAK,UACP,SAAQ,MAAM,KAAK,YAAY,MAAM;AAGvC,SAAO;;CAGT,MAAM,QAAyB;AAE7B,UADc,MAAM,KAAK,KAAK,EACjB;;CAGf,MAAM,QAAgC;AAEpC,UADc,MAAM,KAAK,KAAK,EACjB;;CAGf,MAAc,iBAAiB,OAA0B;EAEvD,MAAM,wBAAQ,IAAI,KAAmD;AAErE,OAAK,MAAM,SAAS,KAAK,UAEvB,MAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,MAAO,KAAiC;GAC9C,MAAM,MAAM,MAAM,QAAQ,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,GAAG,EAAE;AACvD,QAAK,MAAM,MAAM,KAAK;AACpB,QAAI,OAAO,OAAO,SAAU;AAG5B,QAAI,CAAC,MAAM,IAAI,MAAM,EAAE;AACrB,SAAI;MACF,MAAM,UAAU,MAAM,KAAK,WAAW,MAA+B,WAAW,MAAM,GAAG,KAAK,QAAQ,OAAO;MAC7G,MAAM,sBAAM,IAAI,KAAsC;AACtD,WAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,QAAQ,CACpD,KAAI,IAAI,SAAS;OAAE,IAAI;OAAS,GAAG;OAAiB,CAAC;AAEvD,YAAM,IAAI,OAAO,IAAI;aACf;AACN,YAAM,IAAI,uBAAO,IAAI,KAAK,CAAC;;AAE7B;;;;AAMR,SAAO,MAAM,KAAI,SAAQ;GACvB,MAAM,WAAW,EAAE,GAAG,MAAM;GAC5B,MAAM,MAAM;AACZ,QAAK,MAAM,SAAS,KAAK,WAAW;IAClC,MAAM,UAAU,MAAM,IAAI,MAAM;AAChC,QAAI,CAAC,QAAS;IACd,MAAM,MAAO,KAAiC;AAC9C,QAAI,MAAM,QAAQ,IAAI,CACpB,KAAI,SAAS,IAAI,KAAI,OAAM,OAAO,OAAO,WAAY,QAAQ,IAAI,GAAG,IAAI,KAAM,GAAG;aACxE,OAAO,QAAQ,SACxB,KAAI,SAAS,QAAQ,IAAI,IAAI,IAAI;;AAGrC,UAAO;IACP;;CAGJ,MAAc,YAAY,OAA0B;EAClD,IAAI,UAAqC,EAAE;AAC3C,MAAI;AACF,aAAU,MAAM,KAAK,WAAW,MAAiC,QAAQ,KAAK,SAAS,GAAG,KAAK,QAAQ,OAAO;UACxG;AAEN,UAAO;;AAET,SAAO,MAAM,KAAI,SAAQ;GACvB,MAAM,KAAM,KAAiC;GAC7C,MAAM,OAAO,KAAK,QAAQ,MAAM,KAAA;AAChC,OAAI,KACF,QAAO;IAAE,GAAG;IAAM,OAAO;IAAM;AAEjC,UAAO;IACP;;;;;ACvLN,IAAa,uBAAb,MAAqE;CACnE;CACA,UAA0B;CAE1B,YAAY,QAAgC,eAAwB;AAClE,OAAK,UAAU;AACf,MAAI,cAAe,MAAK,UAAU;;CAGpC,OAAO,MAAoB;AACzB,OAAK,UAAU;AACf,SAAO;;CAGT,MAAM,MAAkB;AACtB,SAAO,KAAK,QAAQ,IAAI,KAAK,QAAQ;;;;;ACfzC,IAAa,wBAAb,MAAmC;CACjC;CACA,UAA0B;CAE1B,YAAY,QAA8B,eAAwB;AAChE,OAAK,UAAU;AACf,MAAI,cAAe,MAAK,UAAU;;CAGpC,OAAO,MAAoB;AACzB,OAAK,UAAU;AACf,SAAO;;CAMT,MAAM,IAAI,KAAc,QAAgG;EACtH,MAAM,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,QAAQ;AACjD,MAAI,QAAQ,KAAA,EAAW,QAAO;EAC9B,MAAM,QAAQ,KAAK;AACnB,MAAI,UAAU,KAAA,EAAW,QAAO,KAAA;AAChC,MAAI,OAAQ,QAAO,YAAY,OAAO,OAAO;AAC7C,SAAO;;;AAIX,SAAS,YAAY,UAAkB,QAAiD;AACtF,QAAO,SAAS,QAAQ,eAAe,OAAO,QAAgB;EAC5D,MAAM,MAAM,OAAO;AACnB,SAAO,QAAQ,KAAA,IAAY,OAAO,IAAI,GAAG;GACzC;;;;AC7BJ,IAAa,mBAAb,MAAgD;CAC9C;CACA,UAA0B;CAC1B,WAAkC,EAAE;CACpC,aAAoC;CACpC,aAAqC;CAErC,YAAY,QAA+B,eAAwB;AACjE,OAAK,UAAU;AACf,MAAI,cAAe,MAAK,UAAU;;CAGpC,OAAO,MAAoB;AACzB,OAAK,UAAU;AACf,SAAO;;CAGT,MAAM,OAAe,IAAa,OAAsB;AACtD,OAAK,SAAS,KAAK;GAAE;GAAO;GAAI;GAAO,CAAC;AACxC,SAAO;;CAGT,KAAK,OAAe,QAAwB,OAAa;AACvD,OAAK,aAAa;AAClB,OAAK,aAAa;AAClB,SAAO;;CAGT,MAAM,MAAoB;EACxB,IAAI,QAAQ,MAAM,KAAK,QAAQ,SAAS,KAAK,QAAQ;AAErD,OAAK,MAAM,UAAU,KAAK,SACxB,SAAQ,MAAM,QAAO,SAAQ,WAAW,MAAM,OAAO,CAAC;AAGxD,MAAI,KAAK,YAAY;GACnB,MAAM,KAAK,KAAK;GAChB,MAAM,MAAM,KAAK,eAAe,QAAQ,IAAI;AAC5C,WAAQ,MAAM,UAAU,GAAG,MAAM;IAC/B,MAAM,KAAM,EAA8B;IAC1C,MAAM,KAAM,EAA8B;AAC1C,QAAI,MAAM,QAAQ,MAAM,KAAM,QAAO;AACrC,QAAI,MAAM,KAAM,QAAO;AACvB,QAAI,MAAM,KAAM,QAAO,CAAC;AACxB,WAAO,KAAK,KAAK,CAAC,MAAM,KAAK,KAAK,MAAM;KACxC;;AAGJ,SAAO;;CAGT,MAAM,QAAyB;AAE7B,UADc,MAAM,KAAK,KAAK,EACjB;;CAGf,MAAM,QAAgC;AAEpC,UADc,MAAM,KAAK,KAAK,EACjB;;CAGf,MAAM,OAAO,MAA8E;AACzF,SAAO,KAAK,QAAQ,UAAU,MAAM,KAAK,QAAQ;;;;;AC5CrD,IAAa,gBAAb,MAA2B;CACzB;CACA,YAA0C;CAE1C,YAAY,WAA0B;AACpC,OAAK,aAAa;;CAGpB,MAAM,WAAmC;AACvC,MAAI,CAAC,KAAK,UACR,MAAK,YAAY,MAAM,KAAK,WAAW,MAAqB,uBAAuB;AAErF,SAAO,KAAK;;CAGd,MAAM,SAA8C;AAElD,UADU,MAAM,KAAK,UAAU,EACtB;;CAGX,MAAM,MAAM,MAA0C;AAEpD,UADY,MAAM,KAAK,QAAQ,EACpB,SAAS;;CAGtB,MAAM,OAAsD;EAC1D,MAAM,MAAM,MAAM,KAAK,QAAQ;AAC/B,SAAO,OAAO,QAAQ,IAAI,CAAC,KAAK,CAAC,MAAM,WACrC,OAAO,OAAO,EAAE,MAAM,EAAE,MAAM,CAC/B;;CAGH,QAAQ,OAAmB,SAA0B;AACnD,MAAI,WAAW,MAAM,SAAS,SAC5B,QAAO,MAAM,SAAS;AAExB,SAAO,MAAM;;CAGf,IAAI,OAAmB,SAA0B;EAC/C,MAAM,OAAO,KAAK,QAAQ,OAAO,QAAQ;AACzC,SAAO,KAAK,WAAW,SAAS,KAAK;;;;;AC7BzC,IAAa,cAAb,MAAyB;CACvB;CACA;CACA;CAEA,YAAY,QAA2B;AACrC,OAAK,WAAW,OAAO,QAAQ,QAAQ,QAAQ,GAAG;AAClD,OAAK,aAAa,OAAO;AACzB,OAAK,UAAU,OAAO;;CAGxB,MAAM,OAAO,SAAsC;EACjD,MAAM,MAAM,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW,GAAG,QAAQ;EAC3D,MAAM,UAAkC,EAAE;AAC1C,MAAI,KAAK,QAAS,SAAQ,mBAAmB,UAAU,KAAK;EAE5D,MAAM,MAAM,MAAM,WAAW,MAAM,KAAK,EAAE,SAAS,CAAC;AACpD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,iBAAiB,IAAI,QAAQ,MAAM,IAAI,MAAM,CAAC;AACrE,SAAQ,MAAM,IAAI,MAAM;;CAG1B,MAAM,OAAO,SAAiB,MAA+B,SAE/B;EAC5B,MAAM,MAAM,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW,GAAG,QAAQ;EAC3D,MAAM,UAAkC,EACtC,gBAAgB,oBACjB;AACD,MAAI,KAAK,QAAS,SAAQ,mBAAmB,UAAU,KAAK;EAE5D,MAAM,OAAgC,EAAE,GAAG,MAAM;AACjD,MAAI,SAAS,aACX,MAAK,2BAA2B,QAAQ;EAG1C,MAAM,MAAM,MAAM,WAAW,MAAM,KAAK;GACtC,QAAQ;GACR;GACA,MAAM,KAAK,UAAU,KAAK;GAC3B,CAAC;EAEF,MAAM,SAAU,MAAM,IAAI,MAAM;AAEhC,MAAI,CAAC,IAAI,MAAM,CAAC,OAAO,OACrB,OAAM,IAAI,iBAAiB,IAAI,QAAQ,OAAO,WAAW,yBAAyB;AAGpF,SAAO;;;;;AC3BX,IAAa,qBAAb,MAAgC;CAC9B;CACA;CACA;CAEA,YAAY,QAAkC;AAC5C,OAAK,WAAW,OAAO,QAAQ,QAAQ,QAAQ,GAAG;AAClD,OAAK,aAAa,OAAO;AACzB,OAAK,UAAU,OAAO;;CAGxB,MAAM,KAAK,SAAiB,SAAkE;EAC5F,MAAM,MAAM,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW;EAEhD,MAAM,OAAgC,EAAE,SAAS;AACjD,MAAI,SAAS,eAAgB,MAAK,iBAAiB,QAAQ;AAC3D,MAAI,SAAS,QAAS,MAAK,UAAU,QAAQ;EAE7C,MAAM,MAAM,MAAM,WAAW,MAAM,KAAK;GACtC,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,iBAAiB,UAAU,KAAK;IACjC;GACD,MAAM,KAAK,UAAU,KAAK;GAC3B,CAAC;AAEF,MAAI,CAAC,IAAI,GACP,OAAM,IAAI,iBAAiB,IAAI,QAAQ,MAAM,IAAI,MAAM,CAAC;AAG1D,SAAQ,MAAM,IAAI,MAAM;;CAG1B,MAAM,QAAQ,gBAAwB,SAA4D;EAChG,MAAM,SAAS,IAAI,gBAAgB,EAAE,gBAAgB,CAAC;AACtD,MAAI,SAAS,MAAO,QAAO,IAAI,SAAS,OAAO,QAAQ,MAAM,CAAC;EAE9D,MAAM,MAAM,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW,WAAW;EAE3D,MAAM,MAAM,MAAM,WAAW,MAAM,KAAK,EACtC,SAAS,EACP,iBAAiB,UAAU,KAAK,WACjC,EACF,CAAC;AAEF,MAAI,CAAC,IAAI,GACP,OAAM,IAAI,iBAAiB,IAAI,QAAQ,MAAM,IAAI,MAAM,CAAC;AAG1D,SAAQ,MAAM,IAAI,MAAM;;;;;ACtF5B,SAAgB,kBAAkB,QAA8B;CAC9D,MAAM,YAAY,IAAI,cAAc;EAClC,SAAS,OAAO,WAAW;EAC3B,WAAW,OAAO;EAClB,QAAQ,OAAO;EAChB,CAAC;CACF,MAAM,gBAAgB,OAAO;AAE7B,QAAO;EACL,aAAyD,YACvD,IAAI,mBAAsB,WAAW,SAAS,cAAc;EAE9D,YAAyE,YACvE,IAAI,qBAAwB,UAAU,UAAa,QAAQ,EAAE,cAAc;EAE7E,aAAa,YACX,IAAI,sBAAsB,UAAU,WAAW,QAAQ,EAAE,cAAc;EAEzE,WAAuD,YACrD,IAAI,iBAAoB,UAAU,SAAY,QAAQ,EAAE,cAAc;EAExE,aAAa,IAAI,cAAc,UAAU;EAEzC,YAAY,IAAI,YAAY;GAC1B,UAAU,OAAO,WAAW,4CAA4C,QAAQ,WAAW,YAAY;GACvG,WAAW,OAAO;GAClB,QAAQ,OAAO;GAChB,CAAC;EAEF,oBAAoB,IAAI,mBAAmB;GACzC,UAAU,OAAO,WAAW,4CAA4C,QAAQ,WAAW,mBAAmB;GAC9G,WAAW,OAAO;GAClB,QAAQ,OAAO;GAChB,CAAC;EAEF,gBAAgB,UAAU,MAAe,iBAAiB;EAC1D,cAAc,UAAU,MAAiB,qBAAqB;EAC9D,QAAQ,OAAe,UAAU,MAAe,UAAU,GAAG,OAAO;EACrE"}
|
|
@@ -5,7 +5,9 @@ function applyWhere(item, clause) {
|
|
|
5
5
|
case "eq":
|
|
6
6
|
if (Array.isArray(val)) return val.includes(clause.value);
|
|
7
7
|
return val === clause.value;
|
|
8
|
-
case "ne":
|
|
8
|
+
case "ne":
|
|
9
|
+
if (Array.isArray(val)) return !val.includes(clause.value);
|
|
10
|
+
return val !== clause.value;
|
|
9
11
|
case "gt": return val > clause.value;
|
|
10
12
|
case "gte": return val >= clause.value;
|
|
11
13
|
case "lt": return val < clause.value;
|
|
@@ -275,6 +277,8 @@ var CdnDocumentQuery = class {
|
|
|
275
277
|
_source;
|
|
276
278
|
_locale = "en";
|
|
277
279
|
_filters = [];
|
|
280
|
+
_sortField = null;
|
|
281
|
+
_sortOrder = "asc";
|
|
278
282
|
constructor(source, defaultLocale) {
|
|
279
283
|
this._source = source;
|
|
280
284
|
if (defaultLocale) this._locale = defaultLocale;
|
|
@@ -291,9 +295,26 @@ var CdnDocumentQuery = class {
|
|
|
291
295
|
});
|
|
292
296
|
return this;
|
|
293
297
|
}
|
|
298
|
+
sort(field, order = "asc") {
|
|
299
|
+
this._sortField = field;
|
|
300
|
+
this._sortOrder = order;
|
|
301
|
+
return this;
|
|
302
|
+
}
|
|
294
303
|
async all() {
|
|
295
304
|
let items = await this._source.getIndex(this._locale);
|
|
296
305
|
for (const clause of this._filters) items = items.filter((item) => applyWhere(item, clause));
|
|
306
|
+
if (this._sortField) {
|
|
307
|
+
const sf = this._sortField;
|
|
308
|
+
const dir = this._sortOrder === "asc" ? 1 : -1;
|
|
309
|
+
items = items.toSorted((a, b) => {
|
|
310
|
+
const va = a[sf];
|
|
311
|
+
const vb = b[sf];
|
|
312
|
+
if (va == null && vb == null) return 0;
|
|
313
|
+
if (va == null) return dir;
|
|
314
|
+
if (vb == null) return -dir;
|
|
315
|
+
return va < vb ? -dir : va > vb ? dir : 0;
|
|
316
|
+
});
|
|
317
|
+
}
|
|
297
318
|
return items;
|
|
298
319
|
}
|
|
299
320
|
async count() {
|
package/dist/cli.cjs
CHANGED
package/dist/cli.mjs
CHANGED
|
@@ -159,12 +159,19 @@ function emitTypes(models) {
|
|
|
159
159
|
if (model.kind === "dictionary") lines.push(`export type ${kebabToPascal(model.id)} = Record<string, string>`);
|
|
160
160
|
else {
|
|
161
161
|
lines.push(`export interface ${kebabToPascal(model.id)} {`);
|
|
162
|
-
|
|
162
|
+
const baseNames = /* @__PURE__ */ new Set();
|
|
163
|
+
if (model.kind === "collection") {
|
|
164
|
+
lines.push(" id: string");
|
|
165
|
+
baseNames.add("id");
|
|
166
|
+
}
|
|
163
167
|
if (model.kind === "document") {
|
|
164
168
|
lines.push(" slug: string");
|
|
165
|
-
lines.push("
|
|
169
|
+
lines.push(" body: string");
|
|
170
|
+
baseNames.add("slug");
|
|
171
|
+
baseNames.add("body");
|
|
166
172
|
}
|
|
167
173
|
if (model.fields) for (const [name, field] of Object.entries(model.fields)) {
|
|
174
|
+
if (baseNames.has(name)) continue;
|
|
168
175
|
const tsType = fieldToTS(field);
|
|
169
176
|
const optional = field.required ? "" : "?";
|
|
170
177
|
lines.push(` ${name}${optional}: ${tsType}`);
|
|
@@ -180,7 +187,7 @@ function emitTypes(models) {
|
|
|
180
187
|
sort<K extends keyof T>(field: K, order?: 'asc' | 'desc'): QueryBuilder<T>
|
|
181
188
|
limit(n: number): QueryBuilder<T>
|
|
182
189
|
offset(n: number): QueryBuilder<T>
|
|
183
|
-
include(...fields:
|
|
190
|
+
include<K extends keyof T & string>(...fields: K[]): QueryBuilder<T>
|
|
184
191
|
count(): number
|
|
185
192
|
first(): T | undefined
|
|
186
193
|
all(): T[]
|
|
@@ -188,7 +195,7 @@ function emitTypes(models) {
|
|
|
188
195
|
lines.push("");
|
|
189
196
|
lines.push(`export interface SingletonAccessor<T> {
|
|
190
197
|
locale(lang: string): SingletonAccessor<T>
|
|
191
|
-
include(...fields:
|
|
198
|
+
include<K extends keyof T & string>(...fields: K[]): SingletonAccessor<T>
|
|
192
199
|
get(): T
|
|
193
200
|
}`);
|
|
194
201
|
lines.push("");
|
|
@@ -203,7 +210,8 @@ function emitTypes(models) {
|
|
|
203
210
|
locale(lang: string): DocumentQuery<T>
|
|
204
211
|
where<K extends keyof T>(field: K, value: T[K]): DocumentQuery<T>
|
|
205
212
|
where<K extends keyof T>(field: K, op: WhereOp, value: unknown): DocumentQuery<T>
|
|
206
|
-
|
|
213
|
+
sort<K extends keyof T>(field: K, order?: 'asc' | 'desc'): DocumentQuery<T>
|
|
214
|
+
include<K extends keyof T & string>(...fields: K[]): DocumentQuery<T>
|
|
207
215
|
bySlug(slug: string): T | undefined
|
|
208
216
|
count(): number
|
|
209
217
|
first(): T | undefined
|
|
@@ -259,12 +267,16 @@ function fieldToTS(field) {
|
|
|
259
267
|
case "image":
|
|
260
268
|
case "video":
|
|
261
269
|
case "file":
|
|
262
|
-
case "relation":
|
|
270
|
+
case "relation": {
|
|
263
271
|
if (Array.isArray(field.model) && field.model.length > 1) return `{ model: ${field.model.map((m) => `'${m}'`).join(" | ")}; ref: string }`;
|
|
264
|
-
|
|
265
|
-
|
|
272
|
+
const target = Array.isArray(field.model) ? field.model[0] : field.model;
|
|
273
|
+
return target ? `string | ${kebabToPascal(target)}` : "string";
|
|
274
|
+
}
|
|
275
|
+
case "relations": {
|
|
266
276
|
if (Array.isArray(field.model) && field.model.length > 1) return `Array<{ model: ${field.model.map((m) => `'${m}'`).join(" | ")}; ref: string }>`;
|
|
267
|
-
|
|
277
|
+
const target = Array.isArray(field.model) ? field.model[0] : field.model;
|
|
278
|
+
return target ? `Array<string | ${kebabToPascal(target)}>` : "string[]";
|
|
279
|
+
}
|
|
268
280
|
case "number":
|
|
269
281
|
case "integer":
|
|
270
282
|
case "decimal":
|
|
@@ -367,12 +379,12 @@ async function emitSingleModule(ref, models) {
|
|
|
367
379
|
case "document": {
|
|
368
380
|
const rawText = await readText(ref.filePath);
|
|
369
381
|
if (!rawText) return null;
|
|
370
|
-
const { frontmatter, body } = parseFrontmatter(rawText);
|
|
382
|
+
const { frontmatter, body } = parseFrontmatter(rawText, stringLikeFieldKeys(model));
|
|
371
383
|
const slug = ref.slug ?? model.id;
|
|
372
384
|
const data = {
|
|
373
385
|
slug,
|
|
374
386
|
...frontmatter,
|
|
375
|
-
|
|
387
|
+
body
|
|
376
388
|
};
|
|
377
389
|
return {
|
|
378
390
|
fileName: `${model.id}--${slug}${localeSuffix}.mjs`,
|
|
@@ -382,7 +394,34 @@ async function emitSingleModule(ref, models) {
|
|
|
382
394
|
default: return null;
|
|
383
395
|
}
|
|
384
396
|
}
|
|
385
|
-
|
|
397
|
+
const STRING_LIKE_TYPES = new Set([
|
|
398
|
+
"string",
|
|
399
|
+
"text",
|
|
400
|
+
"email",
|
|
401
|
+
"url",
|
|
402
|
+
"slug",
|
|
403
|
+
"color",
|
|
404
|
+
"phone",
|
|
405
|
+
"code",
|
|
406
|
+
"icon",
|
|
407
|
+
"markdown",
|
|
408
|
+
"richtext",
|
|
409
|
+
"date",
|
|
410
|
+
"datetime",
|
|
411
|
+
"image",
|
|
412
|
+
"video",
|
|
413
|
+
"file",
|
|
414
|
+
"select",
|
|
415
|
+
"relation"
|
|
416
|
+
]);
|
|
417
|
+
function stringLikeFieldKeys(model) {
|
|
418
|
+
const keys = /* @__PURE__ */ new Set();
|
|
419
|
+
if (model.fields) {
|
|
420
|
+
for (const [name, field] of Object.entries(model.fields)) if (STRING_LIKE_TYPES.has(field.type)) keys.add(name);
|
|
421
|
+
}
|
|
422
|
+
return keys;
|
|
423
|
+
}
|
|
424
|
+
function parseFrontmatter(text, stringKeys = /* @__PURE__ */ new Set()) {
|
|
386
425
|
const normalized = text.replace(/\r\n/g, "\n");
|
|
387
426
|
const match = normalized.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
388
427
|
if (!match) return {
|
|
@@ -433,20 +472,23 @@ function parseFrontmatter(text) {
|
|
|
433
472
|
current[key] = rawValue.slice(1, -1).split(",").map((s) => s.trim()).filter(Boolean);
|
|
434
473
|
continue;
|
|
435
474
|
}
|
|
436
|
-
current[key] = parseValue(rawValue);
|
|
475
|
+
current[key] = parseValue(rawValue, current === frontmatter && stringKeys.has(key));
|
|
437
476
|
}
|
|
438
477
|
return {
|
|
439
478
|
frontmatter,
|
|
440
479
|
body
|
|
441
480
|
};
|
|
442
481
|
}
|
|
443
|
-
function parseValue(raw) {
|
|
482
|
+
function parseValue(raw, forceString = false) {
|
|
483
|
+
const isQuoted = raw.startsWith("\"") && raw.endsWith("\"") || raw.startsWith("'") && raw.endsWith("'");
|
|
484
|
+
const unquoted = isQuoted ? raw.slice(1, -1) : raw;
|
|
485
|
+
if (forceString) return unquoted;
|
|
486
|
+
if (isQuoted) return unquoted;
|
|
444
487
|
if (raw === "true") return true;
|
|
445
488
|
if (raw === "false") return false;
|
|
446
489
|
if (raw === "null") return null;
|
|
447
490
|
if (/^-?\d+$/.test(raw)) return parseInt(raw, 10);
|
|
448
491
|
if (/^-?\d+\.\d+$/.test(raw)) return parseFloat(raw);
|
|
449
|
-
if (raw.startsWith("\"") && raw.endsWith("\"") || raw.startsWith("'") && raw.endsWith("'")) return raw.slice(1, -1);
|
|
450
492
|
return raw;
|
|
451
493
|
}
|
|
452
494
|
//#endregion
|
|
@@ -489,15 +531,17 @@ function emitRuntimeModule(models, dataModules, defaultLocale) {
|
|
|
489
531
|
lines.push("// ─── Relation Resolver ───");
|
|
490
532
|
lines.push("");
|
|
491
533
|
lines.push("function _resolveEntry(model, id, locale) {");
|
|
492
|
-
lines.push(" const
|
|
534
|
+
lines.push(" const localeKeys = locale ? [locale, '_default'] : ['_default']");
|
|
535
|
+
lines.push(" for (const localeKey of localeKeys) {");
|
|
493
536
|
if (collections.length > 0) {
|
|
494
|
-
lines.push("
|
|
495
|
-
lines.push("
|
|
537
|
+
lines.push(" const colData = _collectionRegistry[model]?.get(localeKey)");
|
|
538
|
+
lines.push(" if (colData) { const e = colData.find(x => x.id === id); if (e) return e; }");
|
|
496
539
|
}
|
|
497
540
|
if (documents.length > 0) {
|
|
498
|
-
lines.push("
|
|
499
|
-
lines.push("
|
|
541
|
+
lines.push(" const docData = _documentRegistry[model]?.get(localeKey)");
|
|
542
|
+
lines.push(" if (docData) { const e = docData.find(x => x.slug === id); if (e) return e; }");
|
|
500
543
|
}
|
|
544
|
+
lines.push(" }");
|
|
501
545
|
lines.push(" return undefined");
|
|
502
546
|
lines.push("}");
|
|
503
547
|
lines.push("");
|
|
@@ -618,20 +662,15 @@ function emitCjsWrapper(models) {
|
|
|
618
662
|
return `/* eslint-disable */
|
|
619
663
|
/* oxlint-disable */
|
|
620
664
|
// Auto-generated CJS proxy — delegates to ESM via dynamic import()
|
|
621
|
-
//
|
|
622
|
-
//
|
|
665
|
+
// The generated client is ESM-first. From CommonJS, await init() once before
|
|
666
|
+
// accessing exports:
|
|
667
|
+
// const client = await require('#contentrain').init()
|
|
668
|
+
// client.query('model')
|
|
669
|
+
// Prefer native ESM (import) where possible.
|
|
623
670
|
'use strict'
|
|
624
671
|
let _mod = null
|
|
625
672
|
let _promise = null
|
|
626
673
|
|
|
627
|
-
function _ensure() {
|
|
628
|
-
if (_mod) return _mod
|
|
629
|
-
throw new Error(
|
|
630
|
-
'Contentrain client not initialized. Call .init() first, then access exports.\\n'
|
|
631
|
-
+ 'Example: require("#contentrain").init().then(c => c.query("model"))'
|
|
632
|
-
)
|
|
633
|
-
}
|
|
634
|
-
|
|
635
674
|
module.exports.init = function() {
|
|
636
675
|
if (!_promise) _promise = import('./index.mjs').then(function(m) {
|
|
637
676
|
_mod = m
|
|
@@ -701,7 +740,7 @@ function _applyWhere(item, clause) {
|
|
|
701
740
|
const val = item[clause.field];
|
|
702
741
|
switch (clause.op) {
|
|
703
742
|
case 'eq': return Array.isArray(val) ? val.includes(clause.value) : val === clause.value;
|
|
704
|
-
case 'ne': return val !== clause.value;
|
|
743
|
+
case 'ne': return Array.isArray(val) ? !val.includes(clause.value) : val !== clause.value;
|
|
705
744
|
case 'gt': return val > clause.value;
|
|
706
745
|
case 'gte': return val >= clause.value;
|
|
707
746
|
case 'lt': return val < clause.value;
|
|
@@ -738,11 +777,12 @@ class QueryBuilder {
|
|
|
738
777
|
first() { return this.all()[0]; }
|
|
739
778
|
_resolveIncludes(item) {
|
|
740
779
|
const resolved = { ...item };
|
|
780
|
+
const _loc = this._locale ?? this._defaultLocale;
|
|
741
781
|
for (const field of this._includes) {
|
|
742
782
|
const meta = this._relationMeta[field]; if (!meta) continue;
|
|
743
783
|
const targets = Array.isArray(meta.target) ? meta.target : [meta.target];
|
|
744
|
-
if (meta.multi) { const ids = item[field]; if (Array.isArray(ids)) { resolved[field] = ids.map(id => { if (typeof id === 'string') { for (const t of targets) { const r = this._resolver(t, id,
|
|
745
|
-
else { const id = item[field]; if (typeof id === 'string') { for (const t of targets) { const r = this._resolver(t, id,
|
|
784
|
+
if (meta.multi) { const ids = item[field]; if (Array.isArray(ids)) { resolved[field] = ids.map(id => { if (typeof id === 'string') { for (const t of targets) { const r = this._resolver(t, id, _loc); if (r) return r; } return id; } if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) { const r = this._resolver(id.model, id.ref, _loc); if (r) return r; } return id; }); } }
|
|
785
|
+
else { const id = item[field]; if (typeof id === 'string') { for (const t of targets) { const r = this._resolver(t, id, _loc); if (r) { resolved[field] = r; break; } } } else if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) { const r = this._resolver(id.model, id.ref, _loc); if (r) resolved[field] = r; } }
|
|
746
786
|
}
|
|
747
787
|
return resolved;
|
|
748
788
|
}
|
|
@@ -780,15 +820,16 @@ class DictionaryAccessor {
|
|
|
780
820
|
if (key === undefined) return dict;
|
|
781
821
|
const val = dict[key];
|
|
782
822
|
if (val === undefined) return undefined;
|
|
783
|
-
if (params) return val.replace(
|
|
823
|
+
if (params) return val.replace(/\\{(\\w+)\\}/g, (m, k) => { const v = params[k]; return v !== undefined ? String(v) : m; });
|
|
784
824
|
return val;
|
|
785
825
|
}
|
|
786
826
|
}
|
|
787
827
|
|
|
788
828
|
class DocumentQuery {
|
|
789
|
-
constructor(data, relationMeta, resolver, defaultLocale) { this._data = data; this._filters = []; this._locale = null; this._includes = []; this._relationMeta = relationMeta || {}; this._resolver = resolver || null; this._defaultLocale = defaultLocale || null; }
|
|
829
|
+
constructor(data, relationMeta, resolver, defaultLocale) { this._data = data; this._filters = []; this._sortField = null; this._sortOrder = 'asc'; this._locale = null; this._includes = []; this._relationMeta = relationMeta || {}; this._resolver = resolver || null; this._defaultLocale = defaultLocale || null; }
|
|
790
830
|
locale(lang) { this._locale = lang; return this; }
|
|
791
831
|
where(field, opOrValue, value) { if (value !== undefined) { this._filters.push({ field, op: opOrValue, value }); } else { this._filters.push({ field, op: 'eq', value: opOrValue }); } return this; }
|
|
832
|
+
sort(field, order = 'asc') { this._sortField = field; this._sortOrder = order; return this; }
|
|
792
833
|
include(...fields) { this._includes.push(...fields); return this; }
|
|
793
834
|
bySlug(slug) {
|
|
794
835
|
const items = this._resolveData(); const item = items.find(x => x.slug === slug);
|
|
@@ -797,15 +838,16 @@ class DocumentQuery {
|
|
|
797
838
|
}
|
|
798
839
|
count() { return this.all().length; }
|
|
799
840
|
first() { return this.all()[0]; }
|
|
800
|
-
all() { let items = this._resolveData(); for (const clause of this._filters) items = items.filter(item => _applyWhere(item, clause)); if (this._includes.length > 0 && this._resolver) { items = items.map(item => this._resolveIncludes(item)); } return items; }
|
|
841
|
+
all() { let items = this._resolveData(); for (const clause of this._filters) items = items.filter(item => _applyWhere(item, clause)); if (this._sortField) { const sf = this._sortField; const d = this._sortOrder === 'asc' ? 1 : -1; items.sort((a, b) => { const va = a[sf], vb = b[sf]; if (va == null && vb == null) return 0; if (va == null) return d; if (vb == null) return -d; return va < vb ? -d : va > vb ? d : 0; }); } if (this._includes.length > 0 && this._resolver) { items = items.map(item => this._resolveIncludes(item)); } return items; }
|
|
801
842
|
_resolveData() { let key; if (this._locale) { key = this._locale; } else if (this._defaultLocale && this._data.has(this._defaultLocale)) { key = this._defaultLocale; } else { key = this._data.keys().next().value; } return [...(this._data.get(key) ?? [])]; }
|
|
802
843
|
_resolveIncludes(item) {
|
|
803
844
|
const resolved = { ...item };
|
|
845
|
+
const _loc = this._locale ?? this._defaultLocale;
|
|
804
846
|
for (const field of this._includes) {
|
|
805
847
|
const meta = this._relationMeta[field]; if (!meta) continue;
|
|
806
848
|
const targets = Array.isArray(meta.target) ? meta.target : [meta.target];
|
|
807
|
-
if (meta.multi) { const ids = item[field]; if (Array.isArray(ids)) { resolved[field] = ids.map(id => { if (typeof id === 'string') { for (const t of targets) { const r = this._resolver(t, id,
|
|
808
|
-
else { const id = item[field]; if (typeof id === 'string') { for (const t of targets) { const r = this._resolver(t, id,
|
|
849
|
+
if (meta.multi) { const ids = item[field]; if (Array.isArray(ids)) { resolved[field] = ids.map(id => { if (typeof id === 'string') { for (const t of targets) { const r = this._resolver(t, id, _loc); if (r) return r; } return id; } if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) { const r = this._resolver(id.model, id.ref, _loc); if (r) return r; } return id; }); } }
|
|
850
|
+
else { const id = item[field]; if (typeof id === 'string') { for (const t of targets) { const r = this._resolver(t, id, _loc); if (r) { resolved[field] = r; break; } } } else if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) { const r = this._resolver(id.model, id.ref, _loc); if (r) resolved[field] = r; } }
|
|
809
851
|
}
|
|
810
852
|
return resolved;
|
|
811
853
|
}
|
|
@@ -880,4 +922,4 @@ async function generate(options) {
|
|
|
880
922
|
//#endregion
|
|
881
923
|
export { generate as t };
|
|
882
924
|
|
|
883
|
-
//# sourceMappingURL=generate-
|
|
925
|
+
//# sourceMappingURL=generate-ClWKsmfl.mjs.map
|