@contentrain/query 7.0.15 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +92 -6
  2. package/dist/astro/index.cjs +142 -0
  3. package/dist/astro/index.d.cts +81 -0
  4. package/dist/astro/index.d.cts.map +1 -0
  5. package/dist/astro/index.d.mts +81 -0
  6. package/dist/astro/index.d.mts.map +1 -0
  7. package/dist/astro/index.mjs +143 -0
  8. package/dist/astro/index.mjs.map +1 -0
  9. package/dist/cdn/index.cjs +2 -1
  10. package/dist/cdn/index.d.cts +2 -2
  11. package/dist/cdn/index.d.mts +2 -2
  12. package/dist/cdn/index.mjs +2 -2
  13. package/dist/{cdn-C8dqHhdG.mjs → cdn-C5yiVdfQ.mjs} +102 -27
  14. package/dist/cdn-C5yiVdfQ.mjs.map +1 -0
  15. package/dist/{cdn-DrQCLLj6.cjs → cdn-DJw9oksO.cjs} +106 -25
  16. package/dist/cli.cjs +1 -1
  17. package/dist/cli.mjs +1 -1
  18. package/dist/frontmatter-CVjPyhOq.mjs +248 -0
  19. package/dist/frontmatter-CVjPyhOq.mjs.map +1 -0
  20. package/dist/frontmatter-DSFLOTwS.cjs +287 -0
  21. package/dist/{generate-mpKW9Y8v.mjs → generate-B7jooE0f.mjs} +4 -244
  22. package/dist/generate-B7jooE0f.mjs.map +1 -0
  23. package/dist/{generate-DN3mAbvC.cjs → generate-D3C6itur.cjs} +14 -254
  24. package/dist/generator/generate.cjs +1 -1
  25. package/dist/generator/generate.mjs +1 -1
  26. package/dist/{index-TgljULTn.d.mts → index-BFurv7WX.d.cts} +134 -28
  27. package/dist/index-BFurv7WX.d.cts.map +1 -0
  28. package/dist/{index-mxo0dHvb.d.cts → index-VXFz-DDN.d.mts} +134 -28
  29. package/dist/index-VXFz-DDN.d.mts.map +1 -0
  30. package/dist/index.cjs +2 -1
  31. package/dist/index.d.cts +2 -2
  32. package/dist/index.d.mts +2 -2
  33. package/dist/index.mjs +2 -2
  34. package/dist/index.mjs.map +1 -1
  35. package/package.json +39 -15
  36. package/dist/cdn-C8dqHhdG.mjs.map +0 -1
  37. package/dist/generate-mpKW9Y8v.mjs.map +0 -1
  38. package/dist/index-TgljULTn.d.mts.map +0 -1
  39. package/dist/index-mxo0dHvb.d.cts.map +0 -1
package/README.md CHANGED
@@ -307,22 +307,55 @@ asset.meta.alt // 'Hero image'
307
307
 
308
308
  ### Forms
309
309
 
310
- Fetch form schema and submit data from external sites:
310
+ The browser side of Studio's public `/api/forms/v1` — no API key travels with
311
+ these requests (the endpoints are unauthenticated by design, and a page cannot
312
+ keep a secret; Studio's CORS for them allows only `Content-Type`):
311
313
 
312
314
  ```ts
313
315
  const form = client.form()
314
316
 
315
- // Get form field configuration
317
+ // Public form config: exposed fields keyed by id, captcha, honeypot
316
318
  const config = await form.config('contact')
317
- // config.fields → [{ id: 'name', type: 'string', required: true }, ...]
319
+ // config.locale → 'en' (what the submission is validated against)
320
+ // config.fields → { name: { type: 'string', required: true }, email: { type: 'email', … } }
321
+ // config.captcha → 'turnstile' | null (mount the widget with config.captchaSiteKey)
322
+ // config.honeypotField → '_hp' | null (render a hidden input with that name, leave it empty)
318
323
 
319
- // Submit form data
324
+ // Submit: values under `data`; captcha token and honeypot value beside it
320
325
  const result = await form.submit('contact', {
321
326
  name: 'Alice',
322
327
  email: 'alice@example.com',
323
328
  message: 'Hello!',
324
- }, { captchaToken: 'tok_xxx' })
325
- // result → { success: true, message: 'Thank you!' }
329
+ }, { captchaToken: 'tok_xxx', honeypot: '' })
330
+ // → { success: true, message: 'Thank you!' }
331
+ // → { success: false, errors: [{ field: 'email', message }] } (a verdict, not an exception)
332
+ // 403 / 404 / 429 reject with ContentrainError { status, message }
333
+ ```
334
+
335
+ ### Comments
336
+
337
+ The browser side of Studio's public `/api/comments/v1`. Only approved comments
338
+ come back, nested under their roots; email, IP, user agent and referrer never
339
+ leave the server. `body` is plain text — render it escaped.
340
+
341
+ ```ts
342
+ const comments = client.comments()
343
+
344
+ const thread = await comments.thread('posts', entryId, { locale: 'en', page: 1, limit: 20, sort: 'oldest' })
345
+ // thread.config → { closed, requireApproval, requireEmail, maxDepth, maxBodyLength, captcha, captchaSiteKey, honeypotField }
346
+ // thread.comments → [{ id, parentId, depth, author: { name, url, isModerator }, body, type, createdAt, replies: [...] }]
347
+ // thread.total → root comments in total (pagination is over roots)
348
+
349
+ const result = await comments.submit('posts', entryId, {
350
+ author: { name: 'Ada', email: 'ada@example.com', url: 'https://ada.dev' },
351
+ body: 'Great post',
352
+ parentId: thread.comments[0]?.id, // reply; omit for a root comment
353
+ captchaToken: 'tok_xxx',
354
+ honeypot: '',
355
+ }, { locale: 'en' })
356
+ // → { success: true, status: 'pending' | 'approved', comment } (pending is only echoed to its author)
357
+ // → { success: false, errors: [{ field: 'author.name' | 'author.email' | 'author.url' | 'body' | 'parentId' | 'captcha', message }] }
358
+ // 403 (thread closed) / 404 / 429 reject with ContentrainError
326
359
  ```
327
360
 
328
361
  ### Conversation API
@@ -398,6 +431,55 @@ Behavior:
398
431
  | Caching | In-memory (embedded) | ETag-based HTTP cache + optional bundle preload |
399
432
  | Use case | SSG, build-time | SSR, client-side, serverless |
400
433
 
434
+ ## 🚀 Astro Content Layer
435
+
436
+ `contentrain generate` gives application code a typed client. Astro would
437
+ rather own the content itself, so pages can use `getCollection()`, `getEntry()`,
438
+ `reference()` and render documents with `<Content />`. `@contentrain/query/astro`
439
+ reads the same `.contentrain` files the generator reads — one project, one
440
+ answer about what its content is.
441
+
442
+ ```ts
443
+ // src/content.config.ts
444
+ import { defineCollection, z } from 'astro:content'
445
+ import { contentrainLoader } from '@contentrain/query/astro'
446
+
447
+ export const collections = {
448
+ posts: defineCollection({
449
+ loader: contentrainLoader({ model: 'blog-post', locale: 'en' }),
450
+ schema: z.object({ title: z.string(), publishedAt: z.string() }).passthrough(),
451
+ }),
452
+ articles: defineCollection({ loader: contentrainLoader({ model: 'blog-article' }) }),
453
+ }
454
+ ```
455
+
456
+ ```astro
457
+ ---
458
+ import { getCollection, getEntry, render } from 'astro:content'
459
+ const posts = await getCollection('posts')
460
+ const article = await getEntry('articles', 'en/welcome-post')
461
+ const { Content } = await render(article)
462
+ ---
463
+ <ul>{posts.map((p) => <li>{p.data.title}</li>)}</ul>
464
+ <Content />
465
+ ```
466
+
467
+ | Model kind | Becomes |
468
+ |---|---|
469
+ | `collection` | One entry per record; `data.id` is the entry id |
470
+ | `document` | One entry per file — frontmatter in `data`, markdown in `body`, rendered so `<Content />` works |
471
+ | `dictionary` | One entry per key (`{ key, value }`) — a key is a semantic address, and a single blob would make `getEntry()` useless on it |
472
+ | `singleton` | One entry, id `<model>` (or the locale) |
473
+
474
+ `locale` picks one language; omit it on an i18n model and every supported locale
475
+ loads with ids prefixed (`en/my-post`) — Astro ids are unique per collection, so
476
+ without the prefix one language would overwrite the other. `root` points at the
477
+ project holding `.contentrain` (default: `process.cwd()`).
478
+
479
+ Astro is not a dependency, not even a peer: the loader is structurally what
480
+ `defineCollection` accepts, so any Astro 5 version works and non-Astro consumers
481
+ of this package carry nothing extra.
482
+
401
483
  ## CommonJS Usage
402
484
 
403
485
  Generated clients support CommonJS through `init()`:
@@ -450,6 +532,10 @@ CDN transport:
450
532
 
451
533
  - `@contentrain/query/cdn` — CDN client with `HttpTransport`, async query classes, `MediaAccessor`, `FormsClient`, `ConversationClient`
452
534
 
535
+ Astro content layer:
536
+
537
+ - `@contentrain/query/astro` — `contentrainLoader()` for `defineCollection({ loader })`
538
+
453
539
  ## 🧠 Design Constraints
454
540
 
455
541
  This package intentionally:
@@ -0,0 +1,142 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_frontmatter = require("../frontmatter-DSFLOTwS.cjs");
3
+ let node_path = require("node:path");
4
+ let node_url = require("node:url");
5
+ //#region src/astro/loader.ts
6
+ /**
7
+ * A loader for one Contentrain model.
8
+ *
9
+ * ```ts
10
+ * // src/content.config.ts
11
+ * import { defineCollection } from 'astro:content'
12
+ * import { contentrainLoader } from '@contentrain/query/astro'
13
+ *
14
+ * export const collections = {
15
+ * posts: defineCollection({ loader: contentrainLoader({ model: 'blog-post', locale: 'en' }) }),
16
+ * articles: defineCollection({ loader: contentrainLoader({ model: 'blog-article' }) }),
17
+ * }
18
+ * ```
19
+ *
20
+ * Each model kind maps to the shape Astro can actually use:
21
+ * collection → one entry per record · document → one entry per file, markdown
22
+ * in `body` (and rendered, so `<Content />` works) · dictionary → one entry per
23
+ * key, because a single blob would make `getEntry()` useless · singleton → one
24
+ * entry, id `<model>` or the locale.
25
+ */
26
+ function contentrainLoader(options) {
27
+ const root = options.root ?? process.cwd();
28
+ let watching = false;
29
+ return {
30
+ name: `contentrain:${options.model}`,
31
+ async load(context) {
32
+ const manifest = await require_frontmatter.readProjectManifest(root);
33
+ const model = manifest.models.find((m) => m.id === options.model);
34
+ if (!model) {
35
+ const known = manifest.models.map((m) => m.id).join(", ") || "none";
36
+ throw new Error(`contentrainLoader: model "${options.model}" not found in ${(0, node_path.join)(root, ".contentrain", "models")}. Models here: ${known}.`);
37
+ }
38
+ const refs = manifest.contentFiles.filter((ref) => ref.modelId === model.id && (options.locale === void 0 || ref.locale === options.locale));
39
+ if (options.locale !== void 0 && refs.length === 0 && model.i18n) context.logger?.warn(`contentrainLoader: model "${model.id}" has no content for locale "${options.locale}" — collection is empty.`);
40
+ const prefixLocale = model.i18n === true && options.locale === void 0;
41
+ const entries = (await Promise.all(refs.map((ref) => entriesOf(ref, model, prefixLocale)))).flat();
42
+ const siteRoot = toPath(context.config?.root);
43
+ context.store.clear();
44
+ for (const entry of entries) {
45
+ const data = context.parseData ? await context.parseData({
46
+ id: entry.id,
47
+ data: entry.data,
48
+ filePath: entry.filePath
49
+ }) : entry.data;
50
+ const filePath = siteRoot === void 0 ? void 0 : (0, node_path.relative)(siteRoot, entry.filePath);
51
+ context.store.set({
52
+ id: entry.id,
53
+ data,
54
+ ...filePath === void 0 || filePath.startsWith("..") || (0, node_path.isAbsolute)(filePath) ? {} : { filePath },
55
+ ...entry.body === void 0 ? {} : { body: entry.body },
56
+ ...context.generateDigest ? { digest: context.generateDigest(entry.data) } : {},
57
+ ...entry.body !== void 0 && context.renderMarkdown ? { rendered: await context.renderMarkdown(entry.body) } : {}
58
+ });
59
+ }
60
+ context.logger?.info(`Loaded ${entries.length} ${model.kind} ${entries.length === 1 ? "entry" : "entries"} from ${model.id}`);
61
+ if (context.watcher && !watching) {
62
+ watching = true;
63
+ const paths = new Set(refs.map((ref) => ref.filePath));
64
+ for (const path of paths) context.watcher.add(path);
65
+ context.watcher.add((0, node_path.join)(root, ".contentrain", "models", `${model.id}.json`));
66
+ const reload = (path) => {
67
+ if (!paths.has(path) && !path.endsWith(`${model.id}.json`)) return;
68
+ this.load(context);
69
+ };
70
+ context.watcher.on("change", reload);
71
+ context.watcher.on("add", reload);
72
+ context.watcher.on("unlink", reload);
73
+ }
74
+ }
75
+ };
76
+ }
77
+ /** Astro hands `config.root` as a file URL; tests and other hosts may use a path. */
78
+ function toPath(root) {
79
+ if (root === void 0) return void 0;
80
+ if (typeof root !== "string") return (0, node_url.fileURLToPath)(root);
81
+ return root.startsWith("file:") ? (0, node_url.fileURLToPath)(root) : root;
82
+ }
83
+ async function entriesOf(ref, model, prefixLocale) {
84
+ const prefix = prefixLocale && ref.locale ? `${ref.locale}/` : "";
85
+ const withLocale = (data) => ref.locale === null ? data : {
86
+ ...data,
87
+ locale: ref.locale
88
+ };
89
+ switch (ref.kind) {
90
+ case "collection": {
91
+ const raw = await require_frontmatter.readJson(ref.filePath);
92
+ if (!raw) return [];
93
+ return Object.entries(raw).toSorted(([a], [b]) => a.localeCompare(b, "en")).map(([id, fields]) => ({
94
+ id: `${prefix}${id}`,
95
+ data: withLocale({
96
+ id,
97
+ ...fields
98
+ }),
99
+ filePath: ref.filePath
100
+ }));
101
+ }
102
+ case "singleton": {
103
+ const raw = await require_frontmatter.readJson(ref.filePath);
104
+ if (!raw) return [];
105
+ return [{
106
+ id: prefix ? `${ref.locale}` : model.id,
107
+ data: withLocale(raw),
108
+ filePath: ref.filePath
109
+ }];
110
+ }
111
+ case "dictionary": {
112
+ const raw = await require_frontmatter.readJson(ref.filePath);
113
+ if (!raw) return [];
114
+ return Object.entries(raw).toSorted(([a], [b]) => a.localeCompare(b, "en")).map(([key, value]) => ({
115
+ id: `${prefix}${key}`,
116
+ data: withLocale({
117
+ key,
118
+ value
119
+ }),
120
+ filePath: ref.filePath
121
+ }));
122
+ }
123
+ case "document": {
124
+ const text = await require_frontmatter.readText(ref.filePath);
125
+ if (text === null) return [];
126
+ const { frontmatter, body } = require_frontmatter.parseFrontmatter(text, require_frontmatter.stringLikeFieldKeys(model));
127
+ const slug = ref.slug ?? model.id;
128
+ return [{
129
+ id: `${prefix}${slug}`,
130
+ data: withLocale({
131
+ slug,
132
+ ...frontmatter
133
+ }),
134
+ body,
135
+ filePath: ref.filePath
136
+ }];
137
+ }
138
+ default: return [];
139
+ }
140
+ }
141
+ //#endregion
142
+ exports.contentrainLoader = contentrainLoader;
@@ -0,0 +1,81 @@
1
+ //#region src/astro/loader.d.ts
2
+ /** The slice of Astro's `LoaderContext` this loader uses. */
3
+ interface ContentrainLoaderContext {
4
+ store: {
5
+ clear: () => void;
6
+ set: (entry: {
7
+ id: string;
8
+ data: Record<string, unknown>;
9
+ body?: string;
10
+ digest?: string;
11
+ rendered?: {
12
+ html: string;
13
+ metadata?: Record<string, unknown>;
14
+ };
15
+ filePath?: string;
16
+ }) => void;
17
+ };
18
+ logger?: {
19
+ info: (msg: string) => void;
20
+ warn: (msg: string) => void;
21
+ };
22
+ /** Astro's resolved config; only `root` is read, to make file paths relative. */
23
+ config?: {
24
+ root?: URL | string;
25
+ };
26
+ generateDigest?: (data: unknown) => string;
27
+ parseData?: (entry: {
28
+ id: string;
29
+ data: Record<string, unknown>;
30
+ filePath?: string;
31
+ }) => Promise<Record<string, unknown>>;
32
+ renderMarkdown?: (content: string) => Promise<{
33
+ html: string;
34
+ metadata?: Record<string, unknown>;
35
+ }>;
36
+ watcher?: {
37
+ add: (path: string) => void;
38
+ on: (event: string, cb: (path: string) => void) => void;
39
+ };
40
+ }
41
+ /** Structurally Astro's `Loader`. */
42
+ interface ContentrainLoader {
43
+ name: string;
44
+ load: (context: ContentrainLoaderContext) => Promise<void>;
45
+ }
46
+ interface ContentrainLoaderOptions {
47
+ /** Model id — the `id` in `.contentrain/models/<id>.json`. */
48
+ model: string;
49
+ /** Project root holding `.contentrain`. Default: `process.cwd()`. */
50
+ root?: string;
51
+ /**
52
+ * Load only this locale. Omitted on an i18n model, every supported locale is
53
+ * loaded and entry ids are prefixed (`en/my-post`) so the two languages of one
54
+ * post do not collide — Astro ids are unique per collection.
55
+ */
56
+ locale?: string;
57
+ }
58
+ /**
59
+ * A loader for one Contentrain model.
60
+ *
61
+ * ```ts
62
+ * // src/content.config.ts
63
+ * import { defineCollection } from 'astro:content'
64
+ * import { contentrainLoader } from '@contentrain/query/astro'
65
+ *
66
+ * export const collections = {
67
+ * posts: defineCollection({ loader: contentrainLoader({ model: 'blog-post', locale: 'en' }) }),
68
+ * articles: defineCollection({ loader: contentrainLoader({ model: 'blog-article' }) }),
69
+ * }
70
+ * ```
71
+ *
72
+ * Each model kind maps to the shape Astro can actually use:
73
+ * collection → one entry per record · document → one entry per file, markdown
74
+ * in `body` (and rendered, so `<Content />` works) · dictionary → one entry per
75
+ * key, because a single blob would make `getEntry()` useless · singleton → one
76
+ * entry, id `<model>` or the locale.
77
+ */
78
+ declare function contentrainLoader(options: ContentrainLoaderOptions): ContentrainLoader;
79
+ //#endregion
80
+ export { type ContentrainLoader, type ContentrainLoaderContext, type ContentrainLoaderOptions, contentrainLoader };
81
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/astro/loader.ts"],"mappings":";;UAsBiB,wBAAA;EACf,KAAA;IACE,KAAA;IACA,GAAA,GAAM,KAAA;MACJ,EAAA;MACA,IAAA,EAAM,MAAA;MACN,IAAA;MACA,MAAA;MACA,QAAA;QAAa,IAAA;QAAc,QAAA,GAAW,MAAA;MAAA;MACtC,QAAA;IAAA;EAAA;EAGJ,MAAA;IAAW,IAAA,GAAO,GAAA;IAAsB,IAAA,GAAO,GAAA;EAAA;EAN3C;EAQJ,MAAA;IAAW,IAAA,GAAO,GAAA;EAAA;EAClB,cAAA,IAAkB,IAAA;EAClB,SAAA,IAAa,KAAA;IAAS,EAAA;IAAY,IAAA,EAAM,MAAA;IAAyB,QAAA;EAAA,MAAwB,OAAA,CAAQ,MAAA;EACjG,cAAA,IAAkB,OAAA,aAAoB,OAAA;IAAU,IAAA;IAAc,QAAA,GAAW,MAAA;EAAA;EACzE,OAAA;IAAY,GAAA,GAAM,IAAA;IAAuB,EAAA,GAAK,KAAA,UAAe,EAAA,GAAK,IAAA;EAAA;AAAA;;UAInD,iBAAA;EACf,IAAA;EACA,IAAA,GAAO,OAAA,EAAS,wBAAA,KAA6B,OAAA;AAAA;AAAA,UAG9B,wBAAA;EAXkF;EAajG,KAAA;EAZkB;EAclB,IAAA;EAdgD;;;;;EAoBhD,MAAA;AAAA;;;;;;AAfF;;;;;;;;;;;AAKA;;;;iBAwCgB,iBAAA,CAAkB,OAAA,EAAS,wBAAA,GAA2B,iBAAA"}
@@ -0,0 +1,81 @@
1
+ //#region src/astro/loader.d.ts
2
+ /** The slice of Astro's `LoaderContext` this loader uses. */
3
+ interface ContentrainLoaderContext {
4
+ store: {
5
+ clear: () => void;
6
+ set: (entry: {
7
+ id: string;
8
+ data: Record<string, unknown>;
9
+ body?: string;
10
+ digest?: string;
11
+ rendered?: {
12
+ html: string;
13
+ metadata?: Record<string, unknown>;
14
+ };
15
+ filePath?: string;
16
+ }) => void;
17
+ };
18
+ logger?: {
19
+ info: (msg: string) => void;
20
+ warn: (msg: string) => void;
21
+ };
22
+ /** Astro's resolved config; only `root` is read, to make file paths relative. */
23
+ config?: {
24
+ root?: URL | string;
25
+ };
26
+ generateDigest?: (data: unknown) => string;
27
+ parseData?: (entry: {
28
+ id: string;
29
+ data: Record<string, unknown>;
30
+ filePath?: string;
31
+ }) => Promise<Record<string, unknown>>;
32
+ renderMarkdown?: (content: string) => Promise<{
33
+ html: string;
34
+ metadata?: Record<string, unknown>;
35
+ }>;
36
+ watcher?: {
37
+ add: (path: string) => void;
38
+ on: (event: string, cb: (path: string) => void) => void;
39
+ };
40
+ }
41
+ /** Structurally Astro's `Loader`. */
42
+ interface ContentrainLoader {
43
+ name: string;
44
+ load: (context: ContentrainLoaderContext) => Promise<void>;
45
+ }
46
+ interface ContentrainLoaderOptions {
47
+ /** Model id — the `id` in `.contentrain/models/<id>.json`. */
48
+ model: string;
49
+ /** Project root holding `.contentrain`. Default: `process.cwd()`. */
50
+ root?: string;
51
+ /**
52
+ * Load only this locale. Omitted on an i18n model, every supported locale is
53
+ * loaded and entry ids are prefixed (`en/my-post`) so the two languages of one
54
+ * post do not collide — Astro ids are unique per collection.
55
+ */
56
+ locale?: string;
57
+ }
58
+ /**
59
+ * A loader for one Contentrain model.
60
+ *
61
+ * ```ts
62
+ * // src/content.config.ts
63
+ * import { defineCollection } from 'astro:content'
64
+ * import { contentrainLoader } from '@contentrain/query/astro'
65
+ *
66
+ * export const collections = {
67
+ * posts: defineCollection({ loader: contentrainLoader({ model: 'blog-post', locale: 'en' }) }),
68
+ * articles: defineCollection({ loader: contentrainLoader({ model: 'blog-article' }) }),
69
+ * }
70
+ * ```
71
+ *
72
+ * Each model kind maps to the shape Astro can actually use:
73
+ * collection → one entry per record · document → one entry per file, markdown
74
+ * in `body` (and rendered, so `<Content />` works) · dictionary → one entry per
75
+ * key, because a single blob would make `getEntry()` useless · singleton → one
76
+ * entry, id `<model>` or the locale.
77
+ */
78
+ declare function contentrainLoader(options: ContentrainLoaderOptions): ContentrainLoader;
79
+ //#endregion
80
+ export { type ContentrainLoader, type ContentrainLoaderContext, type ContentrainLoaderOptions, contentrainLoader };
81
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/astro/loader.ts"],"mappings":";;UAsBiB,wBAAA;EACf,KAAA;IACE,KAAA;IACA,GAAA,GAAM,KAAA;MACJ,EAAA;MACA,IAAA,EAAM,MAAA;MACN,IAAA;MACA,MAAA;MACA,QAAA;QAAa,IAAA;QAAc,QAAA,GAAW,MAAA;MAAA;MACtC,QAAA;IAAA;EAAA;EAGJ,MAAA;IAAW,IAAA,GAAO,GAAA;IAAsB,IAAA,GAAO,GAAA;EAAA;EAN3C;EAQJ,MAAA;IAAW,IAAA,GAAO,GAAA;EAAA;EAClB,cAAA,IAAkB,IAAA;EAClB,SAAA,IAAa,KAAA;IAAS,EAAA;IAAY,IAAA,EAAM,MAAA;IAAyB,QAAA;EAAA,MAAwB,OAAA,CAAQ,MAAA;EACjG,cAAA,IAAkB,OAAA,aAAoB,OAAA;IAAU,IAAA;IAAc,QAAA,GAAW,MAAA;EAAA;EACzE,OAAA;IAAY,GAAA,GAAM,IAAA;IAAuB,EAAA,GAAK,KAAA,UAAe,EAAA,GAAK,IAAA;EAAA;AAAA;;UAInD,iBAAA;EACf,IAAA;EACA,IAAA,GAAO,OAAA,EAAS,wBAAA,KAA6B,OAAA;AAAA;AAAA,UAG9B,wBAAA;EAXkF;EAajG,KAAA;EAZkB;EAclB,IAAA;EAdgD;;;;;EAoBhD,MAAA;AAAA;;;;;;AAfF;;;;;;;;;;;AAKA;;;;iBAwCgB,iBAAA,CAAkB,OAAA,EAAS,wBAAA,GAA2B,iBAAA"}
@@ -0,0 +1,143 @@
1
+ import { a as readJson, n as stringLikeFieldKeys, o as readText, r as readProjectManifest, t as parseFrontmatter } from "../frontmatter-CVjPyhOq.mjs";
2
+ import { isAbsolute, join, relative } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ //#region src/astro/loader.ts
5
+ /**
6
+ * A loader for one Contentrain model.
7
+ *
8
+ * ```ts
9
+ * // src/content.config.ts
10
+ * import { defineCollection } from 'astro:content'
11
+ * import { contentrainLoader } from '@contentrain/query/astro'
12
+ *
13
+ * export const collections = {
14
+ * posts: defineCollection({ loader: contentrainLoader({ model: 'blog-post', locale: 'en' }) }),
15
+ * articles: defineCollection({ loader: contentrainLoader({ model: 'blog-article' }) }),
16
+ * }
17
+ * ```
18
+ *
19
+ * Each model kind maps to the shape Astro can actually use:
20
+ * collection → one entry per record · document → one entry per file, markdown
21
+ * in `body` (and rendered, so `<Content />` works) · dictionary → one entry per
22
+ * key, because a single blob would make `getEntry()` useless · singleton → one
23
+ * entry, id `<model>` or the locale.
24
+ */
25
+ function contentrainLoader(options) {
26
+ const root = options.root ?? process.cwd();
27
+ let watching = false;
28
+ return {
29
+ name: `contentrain:${options.model}`,
30
+ async load(context) {
31
+ const manifest = await readProjectManifest(root);
32
+ const model = manifest.models.find((m) => m.id === options.model);
33
+ if (!model) {
34
+ const known = manifest.models.map((m) => m.id).join(", ") || "none";
35
+ throw new Error(`contentrainLoader: model "${options.model}" not found in ${join(root, ".contentrain", "models")}. Models here: ${known}.`);
36
+ }
37
+ const refs = manifest.contentFiles.filter((ref) => ref.modelId === model.id && (options.locale === void 0 || ref.locale === options.locale));
38
+ if (options.locale !== void 0 && refs.length === 0 && model.i18n) context.logger?.warn(`contentrainLoader: model "${model.id}" has no content for locale "${options.locale}" — collection is empty.`);
39
+ const prefixLocale = model.i18n === true && options.locale === void 0;
40
+ const entries = (await Promise.all(refs.map((ref) => entriesOf(ref, model, prefixLocale)))).flat();
41
+ const siteRoot = toPath(context.config?.root);
42
+ context.store.clear();
43
+ for (const entry of entries) {
44
+ const data = context.parseData ? await context.parseData({
45
+ id: entry.id,
46
+ data: entry.data,
47
+ filePath: entry.filePath
48
+ }) : entry.data;
49
+ const filePath = siteRoot === void 0 ? void 0 : relative(siteRoot, entry.filePath);
50
+ context.store.set({
51
+ id: entry.id,
52
+ data,
53
+ ...filePath === void 0 || filePath.startsWith("..") || isAbsolute(filePath) ? {} : { filePath },
54
+ ...entry.body === void 0 ? {} : { body: entry.body },
55
+ ...context.generateDigest ? { digest: context.generateDigest(entry.data) } : {},
56
+ ...entry.body !== void 0 && context.renderMarkdown ? { rendered: await context.renderMarkdown(entry.body) } : {}
57
+ });
58
+ }
59
+ context.logger?.info(`Loaded ${entries.length} ${model.kind} ${entries.length === 1 ? "entry" : "entries"} from ${model.id}`);
60
+ if (context.watcher && !watching) {
61
+ watching = true;
62
+ const paths = new Set(refs.map((ref) => ref.filePath));
63
+ for (const path of paths) context.watcher.add(path);
64
+ context.watcher.add(join(root, ".contentrain", "models", `${model.id}.json`));
65
+ const reload = (path) => {
66
+ if (!paths.has(path) && !path.endsWith(`${model.id}.json`)) return;
67
+ this.load(context);
68
+ };
69
+ context.watcher.on("change", reload);
70
+ context.watcher.on("add", reload);
71
+ context.watcher.on("unlink", reload);
72
+ }
73
+ }
74
+ };
75
+ }
76
+ /** Astro hands `config.root` as a file URL; tests and other hosts may use a path. */
77
+ function toPath(root) {
78
+ if (root === void 0) return void 0;
79
+ if (typeof root !== "string") return fileURLToPath(root);
80
+ return root.startsWith("file:") ? fileURLToPath(root) : root;
81
+ }
82
+ async function entriesOf(ref, model, prefixLocale) {
83
+ const prefix = prefixLocale && ref.locale ? `${ref.locale}/` : "";
84
+ const withLocale = (data) => ref.locale === null ? data : {
85
+ ...data,
86
+ locale: ref.locale
87
+ };
88
+ switch (ref.kind) {
89
+ case "collection": {
90
+ const raw = await readJson(ref.filePath);
91
+ if (!raw) return [];
92
+ return Object.entries(raw).toSorted(([a], [b]) => a.localeCompare(b, "en")).map(([id, fields]) => ({
93
+ id: `${prefix}${id}`,
94
+ data: withLocale({
95
+ id,
96
+ ...fields
97
+ }),
98
+ filePath: ref.filePath
99
+ }));
100
+ }
101
+ case "singleton": {
102
+ const raw = await readJson(ref.filePath);
103
+ if (!raw) return [];
104
+ return [{
105
+ id: prefix ? `${ref.locale}` : model.id,
106
+ data: withLocale(raw),
107
+ filePath: ref.filePath
108
+ }];
109
+ }
110
+ case "dictionary": {
111
+ const raw = await readJson(ref.filePath);
112
+ if (!raw) return [];
113
+ return Object.entries(raw).toSorted(([a], [b]) => a.localeCompare(b, "en")).map(([key, value]) => ({
114
+ id: `${prefix}${key}`,
115
+ data: withLocale({
116
+ key,
117
+ value
118
+ }),
119
+ filePath: ref.filePath
120
+ }));
121
+ }
122
+ case "document": {
123
+ const text = await readText(ref.filePath);
124
+ if (text === null) return [];
125
+ const { frontmatter, body } = parseFrontmatter(text, stringLikeFieldKeys(model));
126
+ const slug = ref.slug ?? model.id;
127
+ return [{
128
+ id: `${prefix}${slug}`,
129
+ data: withLocale({
130
+ slug,
131
+ ...frontmatter
132
+ }),
133
+ body,
134
+ filePath: ref.filePath
135
+ }];
136
+ }
137
+ default: return [];
138
+ }
139
+ }
140
+ //#endregion
141
+ export { contentrainLoader };
142
+
143
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/astro/loader.ts"],"sourcesContent":["// Astro content-layer loader for a `.contentrain` project.\n//\n// `contentrain generate` produces a typed client for application code; Astro\n// wants its content in the content layer instead, so pages can query it with\n// `getCollection()` and render documents with `<Content />`. This loader reads\n// the same `.contentrain` files the generator reads — through the same manifest\n// — so a project never has two answers about what its content is.\n//\n// Astro is NOT a dependency here, not even a peer: the returned object is\n// structurally what `defineCollection({ loader })` accepts, and the context is\n// typed by the small surface this loader actually uses. That keeps\n// @contentrain/query dependency-free and usable from any Astro 5 version.\n\nimport type { ModelDefinition } from '@contentrain/types'\nimport { isAbsolute, join, relative } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { ContentFileRef } from '../generator/config-reader.js'\nimport { readProjectManifest } from '../generator/config-reader.js'\nimport { readJson, readText } from '../generator/utils.js'\nimport { parseFrontmatter, stringLikeFieldKeys } from '../shared/frontmatter.js'\n\n/** The slice of Astro's `LoaderContext` this loader uses. */\nexport interface ContentrainLoaderContext {\n store: {\n clear: () => void\n set: (entry: {\n id: string\n data: Record<string, unknown>\n body?: string\n digest?: string\n rendered?: { html: string, metadata?: Record<string, unknown> }\n filePath?: string\n }) => void\n }\n logger?: { info: (msg: string) => void, warn: (msg: string) => void }\n /** Astro's resolved config; only `root` is read, to make file paths relative. */\n config?: { root?: URL | string }\n generateDigest?: (data: unknown) => string\n parseData?: (entry: { id: string, data: Record<string, unknown>, filePath?: string }) => Promise<Record<string, unknown>>\n renderMarkdown?: (content: string) => Promise<{ html: string, metadata?: Record<string, unknown> }>\n watcher?: { add: (path: string) => void, on: (event: string, cb: (path: string) => void) => void }\n}\n\n/** Structurally Astro's `Loader`. */\nexport interface ContentrainLoader {\n name: string\n load: (context: ContentrainLoaderContext) => Promise<void>\n}\n\nexport interface ContentrainLoaderOptions {\n /** Model id — the `id` in `.contentrain/models/<id>.json`. */\n model: string\n /** Project root holding `.contentrain`. Default: `process.cwd()`. */\n root?: string\n /**\n * Load only this locale. Omitted on an i18n model, every supported locale is\n * loaded and entry ids are prefixed (`en/my-post`) so the two languages of one\n * post do not collide — Astro ids are unique per collection.\n */\n locale?: string\n}\n\ninterface Entry {\n id: string\n data: Record<string, unknown>\n body?: string\n filePath: string\n}\n\n/**\n * A loader for one Contentrain model.\n *\n * ```ts\n * // src/content.config.ts\n * import { defineCollection } from 'astro:content'\n * import { contentrainLoader } from '@contentrain/query/astro'\n *\n * export const collections = {\n * posts: defineCollection({ loader: contentrainLoader({ model: 'blog-post', locale: 'en' }) }),\n * articles: defineCollection({ loader: contentrainLoader({ model: 'blog-article' }) }),\n * }\n * ```\n *\n * Each model kind maps to the shape Astro can actually use:\n * collection → one entry per record · document → one entry per file, markdown\n * in `body` (and rendered, so `<Content />` works) · dictionary → one entry per\n * key, because a single blob would make `getEntry()` useless · singleton → one\n * entry, id `<model>` or the locale.\n */\nexport function contentrainLoader(options: ContentrainLoaderOptions): ContentrainLoader {\n const root = options.root ?? process.cwd()\n let watching = false\n\n return {\n name: `contentrain:${options.model}`,\n async load(context) {\n const manifest = await readProjectManifest(root)\n const model = manifest.models.find(m => m.id === options.model)\n if (!model) {\n const known = manifest.models.map(m => m.id).join(', ') || 'none'\n throw new Error(\n `contentrainLoader: model \"${options.model}\" not found in ${join(root, '.contentrain', 'models')}. Models here: ${known}.`,\n )\n }\n\n const refs = manifest.contentFiles.filter(ref =>\n ref.modelId === model.id && (options.locale === undefined || ref.locale === options.locale),\n )\n if (options.locale !== undefined && refs.length === 0 && model.i18n) {\n context.logger?.warn(\n `contentrainLoader: model \"${model.id}\" has no content for locale \"${options.locale}\" — collection is empty.`,\n )\n }\n // Prefix ids only when one collection really holds several languages;\n // a single-locale load keeps the natural id so routes stay clean.\n const prefixLocale = model.i18n === true && options.locale === undefined\n\n const perFile = await Promise.all(refs.map(ref => entriesOf(ref, model, prefixLocale)))\n const entries = perFile.flat()\n // Astro rejects an absolute filePath (\"must be relative to the site\n // root\"), and a `.contentrain` outside the Astro root has no relative\n // form it accepts — there we send none rather than a path it refuses.\n const siteRoot = toPath(context.config?.root)\n\n context.store.clear()\n for (const entry of entries) {\n const data = context.parseData\n ? await context.parseData({ id: entry.id, data: entry.data, filePath: entry.filePath })\n : entry.data\n const filePath = siteRoot === undefined ? undefined : relative(siteRoot, entry.filePath)\n context.store.set({\n id: entry.id,\n data,\n ...(filePath === undefined || filePath.startsWith('..') || isAbsolute(filePath) ? {} : { filePath }),\n ...(entry.body === undefined ? {} : { body: entry.body }),\n ...(context.generateDigest ? { digest: context.generateDigest(entry.data) } : {}),\n ...(entry.body !== undefined && context.renderMarkdown\n ? { rendered: await context.renderMarkdown(entry.body) }\n : {}),\n })\n }\n context.logger?.info(`Loaded ${entries.length} ${model.kind} ${entries.length === 1 ? 'entry' : 'entries'} from ${model.id}`)\n\n // Dev server: reload when the files behind this collection change. The\n // listener is registered once — `load` runs again on every change, and a\n // listener per run would multiply the work on every save.\n if (context.watcher && !watching) {\n watching = true\n const paths = new Set(refs.map(ref => ref.filePath))\n for (const path of paths) context.watcher.add(path)\n context.watcher.add(join(root, '.contentrain', 'models', `${model.id}.json`))\n const reload = (path: string) => {\n if (!paths.has(path) && !path.endsWith(`${model.id}.json`)) return\n void this.load(context)\n }\n context.watcher.on('change', reload)\n context.watcher.on('add', reload)\n context.watcher.on('unlink', reload)\n }\n },\n }\n}\n\n/** Astro hands `config.root` as a file URL; tests and other hosts may use a path. */\nfunction toPath(root: URL | string | undefined): string | undefined {\n if (root === undefined) return undefined\n if (typeof root !== 'string') return fileURLToPath(root)\n return root.startsWith('file:') ? fileURLToPath(root) : root\n}\n\nasync function entriesOf(ref: ContentFileRef, model: ModelDefinition, prefixLocale: boolean): Promise<Entry[]> {\n const prefix = prefixLocale && ref.locale ? `${ref.locale}/` : ''\n const withLocale = (data: Record<string, unknown>): Record<string, unknown> =>\n ref.locale === null ? data : { ...data, locale: ref.locale }\n\n switch (ref.kind) {\n case 'collection': {\n const raw = await readJson<Record<string, Record<string, unknown>>>(ref.filePath)\n if (!raw) return []\n return Object.entries(raw)\n .toSorted(([a], [b]) => a.localeCompare(b, 'en'))\n .map(([id, fields]) => ({\n id: `${prefix}${id}`,\n data: withLocale({ id, ...fields }),\n filePath: ref.filePath,\n }))\n }\n\n case 'singleton': {\n const raw = await readJson<Record<string, unknown>>(ref.filePath)\n if (!raw) return []\n return [{\n id: prefix ? `${ref.locale}` : model.id,\n data: withLocale(raw),\n filePath: ref.filePath,\n }]\n }\n\n case 'dictionary': {\n // One entry per key: a dictionary key is a semantic address, and a single\n // blob entry would make getEntry() and reference() useless on it.\n const raw = await readJson<Record<string, string>>(ref.filePath)\n if (!raw) return []\n return Object.entries(raw)\n .toSorted(([a], [b]) => a.localeCompare(b, 'en'))\n .map(([key, value]) => ({\n id: `${prefix}${key}`,\n data: withLocale({ key, value }),\n filePath: ref.filePath,\n }))\n }\n\n case 'document': {\n const text = await readText(ref.filePath)\n if (text === null) return []\n const { frontmatter, body } = parseFrontmatter(text, stringLikeFieldKeys(model))\n const slug = ref.slug ?? model.id\n return [{\n id: `${prefix}${slug}`,\n data: withLocale({ slug, ...frontmatter }),\n body,\n filePath: ref.filePath,\n }]\n }\n\n default:\n return []\n }\n}\n"],"mappings":"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"}
@@ -1,9 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_cdn = require("../cdn-DrQCLLj6.cjs");
2
+ const require_cdn = require("../cdn-DJw9oksO.cjs");
3
3
  exports.CdnCollectionQuery = require_cdn.CdnCollectionQuery;
4
4
  exports.CdnDictionaryAccessor = require_cdn.CdnDictionaryAccessor;
5
5
  exports.CdnDocumentQuery = require_cdn.CdnDocumentQuery;
6
6
  exports.CdnSingletonAccessor = require_cdn.CdnSingletonAccessor;
7
+ exports.CommentsClient = require_cdn.CommentsClient;
7
8
  exports.ContentrainError = require_cdn.ContentrainError;
8
9
  exports.ConversationClient = require_cdn.ConversationClient;
9
10
  exports.FormsClient = require_cdn.FormsClient;
@@ -1,2 +1,2 @@
1
- import { C as CdnDocumentQuery, E as CdnCollectionQuery, F as DocumentDataSource, I as DocumentIndexEntry, L as SingletonDataSource, M as TransportConfig, N as CollectionDataSource, P as DictionaryDataSource, 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-mxo0dHvb.cjs";
2
- export { CdnCollectionQuery, CdnDictionaryAccessor, CdnDocumentQuery, CdnSingletonAccessor, CollectionDataSource, ContentrainCDNClient, ContentrainCDNConfig, ContentrainError, ConversationClient, ConversationClientConfig, ConversationContext, ConversationHistory, ConversationMessage, ConversationResponse, ConversationSendOptions, ConversationToolResult, ConversationUsage, DictionaryDataSource, DocumentDataSource, DocumentIndexEntry, FormConfig, FormFieldConfig, FormSubmitResult, FormsClient, FormsClientConfig, HttpTransport, MediaAccessor, MediaAsset, MediaAssetMeta, MediaManifest, SingletonDataSource, TransportConfig, createContentrain };
1
+ import { A as FormsClient, C as CommentsClientConfig, D as FormFieldError, E as FormFieldConfig, F as MediaManifest, G as TransportConfig, I as CdnDocumentQuery, J as DocumentDataSource, K as CollectionDataSource, L as CdnDictionaryAccessor, M as MediaAccessor, N as MediaAsset, O as FormSubmitOptions, P as MediaAssetMeta, R as CdnSingletonAccessor, S as CommentsClient, T as FormConfig, W as HttpTransport, X as SingletonDataSource, Y as DocumentIndexEntry, _ as CommentSubmitResult, a as ConversationClient, b as CommentThreadQuery, c as ConversationHistory, d as ConversationSendOptions, f as ConversationToolResult, g as CommentSubmission, h as CommentFieldError, i as ContentrainError, j as FormsClientConfig, k as FormSubmitResult, l as ConversationMessage, m as CommentAuthor, n as ContentrainCDNConfig, o as ConversationClientConfig, p as ConversationUsage, q as DictionaryDataSource, r as createContentrain, s as ConversationContext, t as ContentrainCDNClient, u as ConversationResponse, v as CommentThread, w as PublicComment, x as CommentType, y as CommentThreadConfig, z as CdnCollectionQuery } from "../index-BFurv7WX.cjs";
2
+ export { CdnCollectionQuery, CdnDictionaryAccessor, CdnDocumentQuery, CdnSingletonAccessor, CollectionDataSource, CommentAuthor, CommentFieldError, CommentSubmission, CommentSubmitResult, CommentThread, CommentThreadConfig, CommentThreadQuery, CommentType, CommentsClient, CommentsClientConfig, ContentrainCDNClient, ContentrainCDNConfig, ContentrainError, ConversationClient, ConversationClientConfig, ConversationContext, ConversationHistory, ConversationMessage, ConversationResponse, ConversationSendOptions, ConversationToolResult, ConversationUsage, DictionaryDataSource, DocumentDataSource, DocumentIndexEntry, FormConfig, FormFieldConfig, FormFieldError, FormSubmitOptions, FormSubmitResult, FormsClient, FormsClientConfig, HttpTransport, MediaAccessor, MediaAsset, MediaAssetMeta, MediaManifest, PublicComment, SingletonDataSource, TransportConfig, createContentrain };
@@ -1,2 +1,2 @@
1
- import { C as CdnDocumentQuery, E as CdnCollectionQuery, F as DocumentDataSource, I as DocumentIndexEntry, L as SingletonDataSource, M as TransportConfig, N as CollectionDataSource, P as DictionaryDataSource, 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-TgljULTn.mjs";
2
- export { CdnCollectionQuery, CdnDictionaryAccessor, CdnDocumentQuery, CdnSingletonAccessor, CollectionDataSource, ContentrainCDNClient, ContentrainCDNConfig, ContentrainError, ConversationClient, ConversationClientConfig, ConversationContext, ConversationHistory, ConversationMessage, ConversationResponse, ConversationSendOptions, ConversationToolResult, ConversationUsage, DictionaryDataSource, DocumentDataSource, DocumentIndexEntry, FormConfig, FormFieldConfig, FormSubmitResult, FormsClient, FormsClientConfig, HttpTransport, MediaAccessor, MediaAsset, MediaAssetMeta, MediaManifest, SingletonDataSource, TransportConfig, createContentrain };
1
+ import { A as FormsClient, C as CommentsClientConfig, D as FormFieldError, E as FormFieldConfig, F as MediaManifest, G as TransportConfig, I as CdnDocumentQuery, J as DocumentDataSource, K as CollectionDataSource, L as CdnDictionaryAccessor, M as MediaAccessor, N as MediaAsset, O as FormSubmitOptions, P as MediaAssetMeta, R as CdnSingletonAccessor, S as CommentsClient, T as FormConfig, W as HttpTransport, X as SingletonDataSource, Y as DocumentIndexEntry, _ as CommentSubmitResult, a as ConversationClient, b as CommentThreadQuery, c as ConversationHistory, d as ConversationSendOptions, f as ConversationToolResult, g as CommentSubmission, h as CommentFieldError, i as ContentrainError, j as FormsClientConfig, k as FormSubmitResult, l as ConversationMessage, m as CommentAuthor, n as ContentrainCDNConfig, o as ConversationClientConfig, p as ConversationUsage, q as DictionaryDataSource, r as createContentrain, s as ConversationContext, t as ContentrainCDNClient, u as ConversationResponse, v as CommentThread, w as PublicComment, x as CommentType, y as CommentThreadConfig, z as CdnCollectionQuery } from "../index-VXFz-DDN.mjs";
2
+ export { CdnCollectionQuery, CdnDictionaryAccessor, CdnDocumentQuery, CdnSingletonAccessor, CollectionDataSource, CommentAuthor, CommentFieldError, CommentSubmission, CommentSubmitResult, CommentThread, CommentThreadConfig, CommentThreadQuery, CommentType, CommentsClient, CommentsClientConfig, ContentrainCDNClient, ContentrainCDNConfig, ContentrainError, ConversationClient, ConversationClientConfig, ConversationContext, ConversationHistory, ConversationMessage, ConversationResponse, ConversationSendOptions, ConversationToolResult, ConversationUsage, DictionaryDataSource, DocumentDataSource, DocumentIndexEntry, FormConfig, FormFieldConfig, FormFieldError, FormSubmitOptions, FormSubmitResult, FormsClient, FormsClientConfig, HttpTransport, MediaAccessor, MediaAsset, MediaAssetMeta, MediaManifest, PublicComment, SingletonDataSource, TransportConfig, createContentrain };
@@ -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-C8dqHhdG.mjs";
2
- export { CdnCollectionQuery, CdnDictionaryAccessor, CdnDocumentQuery, CdnSingletonAccessor, ContentrainError, ConversationClient, FormsClient, HttpTransport, MediaAccessor, createContentrain };
1
+ import { a as MediaAccessor, c as CdnSingletonAccessor, d as ContentrainError, i as FormsClient, l as CdnCollectionQuery, n as ConversationClient, o as CdnDocumentQuery, r as CommentsClient, s as CdnDictionaryAccessor, t as createContentrain, u as HttpTransport } from "../cdn-C5yiVdfQ.mjs";
2
+ export { CdnCollectionQuery, CdnDictionaryAccessor, CdnDocumentQuery, CdnSingletonAccessor, CommentsClient, ContentrainError, ConversationClient, FormsClient, HttpTransport, MediaAccessor, createContentrain };