@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
@@ -1,149 +1,6 @@
1
+ const require_frontmatter = require("./frontmatter-DSFLOTwS.cjs");
1
2
  let node_path = require("node:path");
2
3
  let node_fs_promises = require("node:fs/promises");
3
- //#region src/generator/utils.ts
4
- async function readJson(filePath) {
5
- try {
6
- const raw = await (0, node_fs_promises.readFile)(filePath, "utf-8");
7
- return JSON.parse(raw);
8
- } catch {
9
- return null;
10
- }
11
- }
12
- async function readDir(dirPath) {
13
- try {
14
- return await (0, node_fs_promises.readdir)(dirPath);
15
- } catch {
16
- return [];
17
- }
18
- }
19
- async function readText(filePath) {
20
- try {
21
- return await (0, node_fs_promises.readFile)(filePath, "utf-8");
22
- } catch {
23
- return null;
24
- }
25
- }
26
- async function writeText(filePath, content) {
27
- await (0, node_fs_promises.mkdir)((0, node_path.dirname)(filePath), { recursive: true });
28
- await (0, node_fs_promises.writeFile)(filePath, content, "utf-8");
29
- }
30
- function contentrainDir(projectRoot) {
31
- return (0, node_path.join)(projectRoot, ".contentrain");
32
- }
33
- //#endregion
34
- //#region src/generator/config-reader.ts
35
- async function readProjectManifest(projectRoot) {
36
- const crDir = contentrainDir(projectRoot);
37
- const rawConfig = await readJson((0, node_path.join)(crDir, "config.json"));
38
- const config = {
39
- version: rawConfig?.version ?? 1,
40
- stack: rawConfig?.stack ?? "other",
41
- workflow: rawConfig?.workflow ?? "auto-merge",
42
- locales: {
43
- default: rawConfig?.locales?.default ?? "en",
44
- supported: rawConfig?.locales?.supported ?? [rawConfig?.locales?.default ?? "en"]
45
- },
46
- domains: rawConfig?.domains ?? [],
47
- repository: rawConfig?.repository,
48
- assets_path: rawConfig?.assets_path,
49
- cdn: rawConfig?.cdn,
50
- branchRetention: rawConfig?.branchRetention
51
- };
52
- const modelsDir = (0, node_path.join)(crDir, "models");
53
- const modelFiles = (await readDir(modelsDir)).filter((f) => f.endsWith(".json"));
54
- const models = (await Promise.all(modelFiles.map((file) => readJson((0, node_path.join)(modelsDir, file))))).filter((m) => m != null && Boolean(m.id)).toSorted((a, b) => a.id.localeCompare(b.id, "en"));
55
- return {
56
- config,
57
- models,
58
- contentFiles: (await Promise.all(models.map((model) => mapContentFiles(projectRoot, model, config)))).flat()
59
- };
60
- }
61
- function getContentDir(projectRoot, model) {
62
- if (model.content_path) return (0, node_path.join)(projectRoot, model.content_path);
63
- return (0, node_path.join)(contentrainDir(projectRoot), "content", model.domain, model.id);
64
- }
65
- function getLocaleStrategy(model) {
66
- return model.locale_strategy ?? "file";
67
- }
68
- function jsonFilePath(dir, model, locale) {
69
- switch (getLocaleStrategy(model)) {
70
- case "suffix": return (0, node_path.join)(dir, `${model.id}.${locale}.json`);
71
- case "directory": return (0, node_path.join)(dir, locale, `${model.id}.json`);
72
- case "none": return (0, node_path.join)(dir, `${model.id}.json`);
73
- default: return (0, node_path.join)(dir, `${locale}.json`);
74
- }
75
- }
76
- async function mapContentFiles(projectRoot, model, config) {
77
- const dir = getContentDir(projectRoot, model);
78
- if (model.kind === "document") return mapDocumentFiles(dir, model, config);
79
- if (model.i18n) return (await Promise.all(config.locales.supported.map(async (locale) => {
80
- const filePath = jsonFilePath(dir, model, locale);
81
- if (await readJson(filePath) === null) return null;
82
- return {
83
- modelId: model.id,
84
- locale,
85
- filePath,
86
- kind: model.kind
87
- };
88
- }))).filter(Boolean);
89
- const filePath = (0, node_path.join)(dir, "data.json");
90
- if (await readJson(filePath) === null) return [];
91
- return [{
92
- modelId: model.id,
93
- locale: null,
94
- filePath,
95
- kind: model.kind
96
- }];
97
- }
98
- async function mapDocumentFiles(dir, model, config) {
99
- const strategy = getLocaleStrategy(model);
100
- if (!model.i18n) return (await readDir(dir)).filter((f) => f.endsWith(".md")).map((f) => ({
101
- modelId: model.id,
102
- locale: null,
103
- filePath: (0, node_path.join)(dir, f),
104
- kind: "document",
105
- slug: f.replace(".md", "")
106
- }));
107
- return (await Promise.all(config.locales.supported.map((locale) => mapDocumentLocale(dir, model, locale, strategy)))).flat();
108
- }
109
- async function mapDocumentLocale(dir, model, locale, strategy) {
110
- if (strategy === "file") {
111
- const entries = await readDir(dir);
112
- return (await Promise.all(entries.map(async (entry) => {
113
- const filePath = (0, node_path.join)(dir, entry, `${locale}.md`);
114
- if (await readText(filePath) === null) return null;
115
- return {
116
- modelId: model.id,
117
- locale,
118
- filePath,
119
- kind: "document",
120
- slug: entry
121
- };
122
- }))).filter(Boolean);
123
- }
124
- if (strategy === "directory") {
125
- const localeDir = (0, node_path.join)(dir, locale);
126
- return (await readDir(localeDir)).filter((f) => f.endsWith(".md")).map((f) => ({
127
- modelId: model.id,
128
- locale,
129
- filePath: (0, node_path.join)(localeDir, f),
130
- kind: "document",
131
- slug: f.replace(".md", "")
132
- }));
133
- }
134
- if (strategy === "suffix") {
135
- const suffix = `.${locale}.md`;
136
- return (await readDir(dir)).filter((f) => f.endsWith(suffix)).map((f) => ({
137
- modelId: model.id,
138
- locale,
139
- filePath: (0, node_path.join)(dir, f),
140
- kind: "document",
141
- slug: f.slice(0, -suffix.length)
142
- }));
143
- }
144
- return [];
145
- }
146
- //#endregion
147
4
  //#region src/generator/type-emitter.ts
148
5
  function emitTypes(models, hasMedia = false) {
149
6
  const lines = [
@@ -358,7 +215,7 @@ async function emitSingleModule(ref, models) {
358
215
  const localeSuffix = ref.locale ? `.${ref.locale}` : "";
359
216
  switch (model.kind) {
360
217
  case "collection": {
361
- const raw = await readJson(ref.filePath);
218
+ const raw = await require_frontmatter.readJson(ref.filePath);
362
219
  if (!raw) return null;
363
220
  const entries = Object.entries(raw).toSorted(([a], [b]) => a.localeCompare(b, "en")).map(([id, fields]) => Object.assign({ id }, fields));
364
221
  return {
@@ -367,7 +224,7 @@ async function emitSingleModule(ref, models) {
367
224
  };
368
225
  }
369
226
  case "singleton": {
370
- const raw = await readJson(ref.filePath);
227
+ const raw = await require_frontmatter.readJson(ref.filePath);
371
228
  if (!raw) return null;
372
229
  return {
373
230
  fileName: `${model.id}${localeSuffix}.mjs`,
@@ -375,7 +232,7 @@ async function emitSingleModule(ref, models) {
375
232
  };
376
233
  }
377
234
  case "dictionary": {
378
- const raw = await readJson(ref.filePath);
235
+ const raw = await require_frontmatter.readJson(ref.filePath);
379
236
  if (!raw) return null;
380
237
  return {
381
238
  fileName: `${model.id}${localeSuffix}.mjs`,
@@ -383,9 +240,9 @@ async function emitSingleModule(ref, models) {
383
240
  };
384
241
  }
385
242
  case "document": {
386
- const rawText = await readText(ref.filePath);
243
+ const rawText = await require_frontmatter.readText(ref.filePath);
387
244
  if (!rawText) return null;
388
- const { frontmatter, body } = parseFrontmatter(rawText, stringLikeFieldKeys(model));
245
+ const { frontmatter, body } = require_frontmatter.parseFrontmatter(rawText, require_frontmatter.stringLikeFieldKeys(model));
389
246
  const slug = ref.slug ?? model.id;
390
247
  const data = {
391
248
  slug,
@@ -400,103 +257,6 @@ async function emitSingleModule(ref, models) {
400
257
  default: return null;
401
258
  }
402
259
  }
403
- const STRING_LIKE_TYPES = new Set([
404
- "string",
405
- "text",
406
- "email",
407
- "url",
408
- "slug",
409
- "color",
410
- "phone",
411
- "code",
412
- "icon",
413
- "markdown",
414
- "richtext",
415
- "date",
416
- "datetime",
417
- "image",
418
- "video",
419
- "file",
420
- "select",
421
- "relation"
422
- ]);
423
- function stringLikeFieldKeys(model) {
424
- const keys = /* @__PURE__ */ new Set();
425
- if (model.fields) {
426
- for (const [name, field] of Object.entries(model.fields)) if (STRING_LIKE_TYPES.has(field.type)) keys.add(name);
427
- }
428
- return keys;
429
- }
430
- function parseFrontmatter(text, stringKeys = /* @__PURE__ */ new Set()) {
431
- const normalized = text.replace(/\r\n/g, "\n");
432
- const match = normalized.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
433
- if (!match) return {
434
- frontmatter: {},
435
- body: normalized
436
- };
437
- const fmStr = match[1];
438
- const body = match[2].trim();
439
- const frontmatter = {};
440
- const lines = fmStr.split("\n");
441
- const stack = [{
442
- obj: frontmatter,
443
- indent: -1
444
- }];
445
- for (const line of lines) {
446
- if (line.trim() === "") continue;
447
- const arrayMatch = line.match(/^(\s*)-\s+(.*)$/);
448
- if (arrayMatch) {
449
- const arrIndent = arrayMatch[1].length;
450
- const value = arrayMatch[2].trim();
451
- while (stack.length > 1 && stack[stack.length - 1].indent >= arrIndent) stack.pop();
452
- const parent = stack[stack.length - 1].obj;
453
- const lastKey = Object.keys(parent).pop();
454
- if (lastKey && Array.isArray(parent[lastKey])) parent[lastKey].push(parseValue(value));
455
- continue;
456
- }
457
- const kvMatch = line.match(/^(\s*)([\w][\w.-]*)\s*:\s*(.*)$/);
458
- if (!kvMatch) continue;
459
- const kvIndent = kvMatch[1].length;
460
- const key = kvMatch[2];
461
- const rawValue = kvMatch[3].trim();
462
- while (stack.length > 1 && stack[stack.length - 1].indent >= kvIndent) stack.pop();
463
- const current = stack[stack.length - 1].obj;
464
- if (rawValue === "") {
465
- const nextLineIdx = lines.indexOf(line) + 1;
466
- if ((nextLineIdx < lines.length ? lines[nextLineIdx] : "").trim().startsWith("-")) current[key] = [];
467
- else {
468
- const nested = {};
469
- current[key] = nested;
470
- stack.push({
471
- obj: nested,
472
- indent: kvIndent
473
- });
474
- }
475
- continue;
476
- }
477
- if (rawValue.startsWith("[") && rawValue.endsWith("]")) {
478
- current[key] = rawValue.slice(1, -1).split(",").map((s) => s.trim()).filter(Boolean);
479
- continue;
480
- }
481
- current[key] = parseValue(rawValue, current === frontmatter && stringKeys.has(key));
482
- }
483
- return {
484
- frontmatter,
485
- body
486
- };
487
- }
488
- function parseValue(raw, forceString = false) {
489
- const isQuoted = raw.startsWith("\"") && raw.endsWith("\"") || raw.startsWith("'") && raw.endsWith("'");
490
- const unquoted = isQuoted ? raw.slice(1, -1) : raw;
491
- if (forceString) return unquoted;
492
- if (isQuoted) return unquoted;
493
- if (raw === "true") return true;
494
- if (raw === "false") return false;
495
- if (raw === "null") return null;
496
- if (/^-?\d+$/.test(raw)) return parseInt(raw, 10);
497
- if (/^-?\d+\.\d+$/.test(raw)) return parseFloat(raw);
498
- return raw;
499
- }
500
260
  //#endregion
501
261
  //#region src/generator/runtime-emitter.ts
502
262
  function emitRuntimeModule(models, dataModules, defaultLocale, cdnBaseUrl) {
@@ -891,7 +651,7 @@ const IMPORTS_CONFIG = {
891
651
  };
892
652
  async function injectImports(projectRoot) {
893
653
  const pkgPath = (0, node_path.join)(projectRoot, "package.json");
894
- const pkg = await readJson(pkgPath);
654
+ const pkg = await require_frontmatter.readJson(pkgPath);
895
655
  if (!pkg) return false;
896
656
  const existing = pkg["imports"] ?? {};
897
657
  const updated = {
@@ -900,7 +660,7 @@ async function injectImports(projectRoot) {
900
660
  };
901
661
  if (JSON.stringify(existing["#contentrain"]) === JSON.stringify(IMPORTS_CONFIG["#contentrain"]) && JSON.stringify(existing["#contentrain/*"]) === JSON.stringify(IMPORTS_CONFIG["#contentrain/*"])) return false;
902
662
  pkg["imports"] = updated;
903
- await writeText(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
663
+ await require_frontmatter.writeText(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
904
664
  return true;
905
665
  }
906
666
  //#endregion
@@ -909,7 +669,7 @@ async function generate(options) {
909
669
  const { projectRoot } = options;
910
670
  const clientDir = (0, node_path.join)(projectRoot, ".contentrain", "client");
911
671
  const dataDir = (0, node_path.join)(clientDir, "data");
912
- const manifest = await readProjectManifest(projectRoot);
672
+ const manifest = await require_frontmatter.readProjectManifest(projectRoot);
913
673
  const cdnBaseUrl = options.cdnBaseUrl ?? manifest.config.cdn?.url;
914
674
  const hasMedia = Boolean(cdnBaseUrl);
915
675
  const dataModules = await emitDataModules(manifest.models, manifest.contentFiles);
@@ -918,15 +678,15 @@ async function generate(options) {
918
678
  const cjsContent = emitCjsWrapper(manifest.models, hasMedia);
919
679
  const newFileNames = new Set(dataModules.map((dm) => dm.fileName));
920
680
  try {
921
- const existing = await readDir(dataDir);
681
+ const existing = await require_frontmatter.readDir(dataDir);
922
682
  await Promise.all(existing.filter((f) => !newFileNames.has(f)).map((f) => (0, node_fs_promises.rm)((0, node_path.join)(dataDir, f), { force: true })));
923
683
  } catch {}
924
684
  const dataFileNames = dataModules.map((dm) => `data/${dm.fileName}`);
925
685
  await Promise.all([
926
- writeText((0, node_path.join)(clientDir, "index.d.ts"), typesContent),
927
- writeText((0, node_path.join)(clientDir, "index.mjs"), runtimeContent),
928
- writeText((0, node_path.join)(clientDir, "index.cjs"), cjsContent),
929
- ...dataModules.map((dm) => writeText((0, node_path.join)(dataDir, dm.fileName), dm.content))
686
+ require_frontmatter.writeText((0, node_path.join)(clientDir, "index.d.ts"), typesContent),
687
+ require_frontmatter.writeText((0, node_path.join)(clientDir, "index.mjs"), runtimeContent),
688
+ require_frontmatter.writeText((0, node_path.join)(clientDir, "index.cjs"), cjsContent),
689
+ ...dataModules.map((dm) => require_frontmatter.writeText((0, node_path.join)(dataDir, dm.fileName), dm.content))
930
690
  ]);
931
691
  const packageJsonUpdated = await injectImports(projectRoot);
932
692
  return {
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_generate = require("../generate-DN3mAbvC.cjs");
2
+ const require_generate = require("../generate-D3C6itur.cjs");
3
3
  exports.generate = require_generate.generate;
@@ -1,2 +1,2 @@
1
- import { t as generate } from "../generate-mpKW9Y8v.mjs";
1
+ import { t as generate } from "../generate-B7jooE0f.mjs";
2
2
  export { generate };
@@ -1,3 +1,5 @@
1
+ import { FieldDef } from "@contentrain/types";
2
+
1
3
  //#region src/cdn/data-source.d.ts
2
4
  interface CollectionDataSource<T> {
3
5
  getAll(locale: string): Promise<T[]>;
@@ -194,49 +196,152 @@ declare class MediaAccessor {
194
196
  }
195
197
  //#endregion
196
198
  //#region src/cdn/forms-client.d.ts
197
- interface FormFieldConfig {
198
- id: string;
199
- type: string;
200
- required?: boolean;
201
- label?: string;
202
- placeholder?: string;
203
- options?: Array<{
204
- value: string;
205
- label: string;
206
- }>;
207
- pattern?: string;
208
- min?: number;
209
- max?: number;
210
- }
199
+ /** One exposed field, as the model defines it. */
200
+ type FormFieldConfig = FieldDef;
211
201
  interface FormConfig {
212
202
  modelId: string;
213
- fields: FormFieldConfig[];
214
- captchaType?: 'turnstile' | null;
203
+ /** The project's default locale — what a submission is validated against and written to. */
204
+ locale: string;
205
+ /** Exposed fields only, keyed by field id. */
206
+ fields: Record<string, FormFieldConfig>;
207
+ captcha: 'turnstile' | null;
208
+ /** Turnstile widget key, when `captcha` is set and the operator configured one. */
209
+ captchaSiteKey: string | null;
215
210
  successMessage?: string;
216
- honeypotField?: string;
211
+ /** Name of the hidden honeypot input to render (and leave empty), or null. */
212
+ honeypotField: string | null;
213
+ }
214
+ interface FormFieldError {
215
+ field: string;
216
+ message: string;
217
217
  }
218
218
  interface FormSubmitResult {
219
219
  success: boolean;
220
+ /** The model's `successMessage` on success. */
220
221
  message?: string;
221
- errors?: Array<{
222
- field: string;
223
- message: string;
224
- }>;
222
+ /** Validation errors against the exposed fields, or `captcha`. */
223
+ errors?: FormFieldError[];
224
+ }
225
+ interface FormSubmitOptions {
226
+ /** Turnstile token, when the config's `captcha` is set. */
227
+ captchaToken?: string;
228
+ /** Value of the honeypot input. A human leaves it empty; a filled one is silently dropped server-side. */
229
+ honeypot?: string;
225
230
  }
226
231
  interface FormsClientConfig {
232
+ /** Public API root — `https://studio.contentrain.io/api/forms/v1`. */
227
233
  baseUrl: string;
228
234
  projectId: string;
229
- apiKey?: string;
230
235
  }
231
236
  declare class FormsClient {
232
237
  private _baseUrl;
233
238
  private _projectId;
234
- private _apiKey;
235
239
  constructor(config: FormsClientConfig);
236
240
  config(modelId: string): Promise<FormConfig>;
237
- submit(modelId: string, data: Record<string, unknown>, options?: {
238
- captchaToken?: string;
239
- }): Promise<FormSubmitResult>;
241
+ /**
242
+ * Resolves with the server's verdict for 2xx responses — including
243
+ * `{ success: false, errors }` for validation failures, which are not
244
+ * exceptions. Rejects with `ContentrainError` on 403/404/429/5xx.
245
+ */
246
+ submit(modelId: string, data: Record<string, unknown>, options?: FormSubmitOptions): Promise<FormSubmitResult>;
247
+ }
248
+ //#endregion
249
+ //#region src/cdn/comments-client.d.ts
250
+ type CommentType = 'comment' | 'pingback' | 'trackback';
251
+ interface CommentAuthor {
252
+ name: string;
253
+ url: string | null;
254
+ /** Written from Studio by a workspace member. */
255
+ isModerator: boolean;
256
+ }
257
+ interface PublicComment {
258
+ id: string;
259
+ parentId: string | null;
260
+ depth: number;
261
+ author: CommentAuthor;
262
+ /** Plain text. */
263
+ body: string;
264
+ type: CommentType;
265
+ /** ISO 8601. */
266
+ createdAt: string;
267
+ replies: PublicComment[];
268
+ }
269
+ interface CommentThreadConfig {
270
+ /** Thread closed → render the comments, hide the form. */
271
+ closed: boolean;
272
+ requireApproval: boolean;
273
+ requireEmail: boolean;
274
+ /** Reply nesting cap for new submissions (0 = flat). */
275
+ maxDepth: number;
276
+ maxBodyLength: number;
277
+ captcha: 'turnstile' | null;
278
+ captchaSiteKey: string | null;
279
+ honeypotField: string | null;
280
+ }
281
+ interface CommentThread {
282
+ entry: {
283
+ modelId: string;
284
+ entryId: string;
285
+ locale: string;
286
+ };
287
+ config: CommentThreadConfig;
288
+ /** Root comments of this page, replies nested. */
289
+ comments: PublicComment[];
290
+ /** Root comments in total. */
291
+ total: number;
292
+ page: number;
293
+ limit: number;
294
+ }
295
+ interface CommentThreadQuery {
296
+ locale?: string;
297
+ page?: number;
298
+ limit?: number;
299
+ sort?: 'oldest' | 'newest';
300
+ }
301
+ interface CommentSubmission {
302
+ author: {
303
+ name: string;
304
+ email?: string;
305
+ url?: string;
306
+ };
307
+ body: string;
308
+ /** Reply target — an approved comment's id. */
309
+ parentId?: string;
310
+ captchaToken?: string;
311
+ /** Value of the honeypot input (`config.honeypotField`); a human leaves it empty. */
312
+ honeypot?: string;
313
+ }
314
+ interface CommentFieldError {
315
+ /** `author.name` · `author.email` · `author.url` · `body` · `parentId` · `captcha` */
316
+ field: string;
317
+ message: string;
318
+ }
319
+ interface CommentSubmitResult {
320
+ success: boolean;
321
+ /** `approved` renders now; `pending` is only echoed back to its author. */
322
+ status?: 'pending' | 'approved';
323
+ /** The stored comment, in the public shape. Absent when the honeypot swallowed the post. */
324
+ comment?: PublicComment;
325
+ errors?: CommentFieldError[];
326
+ }
327
+ interface CommentsClientConfig {
328
+ /** Public API root — `https://studio.contentrain.io/api/comments/v1`. */
329
+ baseUrl: string;
330
+ projectId: string;
331
+ }
332
+ declare class CommentsClient {
333
+ private _baseUrl;
334
+ private _projectId;
335
+ constructor(config: CommentsClientConfig);
336
+ thread(modelId: string, entryId: string, query?: CommentThreadQuery): Promise<CommentThread>;
337
+ /**
338
+ * Resolves with the server's verdict for 2xx responses — including
339
+ * `{ success: false, errors }`. Rejects with `ContentrainError` on
340
+ * 403 (thread closed / plan), 404, 429 and 5xx.
341
+ */
342
+ submit(modelId: string, entryId: string, submission: CommentSubmission, options?: {
343
+ locale?: string;
344
+ }): Promise<CommentSubmitResult>;
240
345
  }
241
346
  //#endregion
242
347
  //#region src/cdn/conversation-client.d.ts
@@ -320,6 +425,7 @@ declare function createContentrain(config: ContentrainCDNConfig): {
320
425
  document: <T extends object = Record<string, unknown>>(modelId: string) => CdnDocumentQuery<T>;
321
426
  media: () => MediaAccessor;
322
427
  form: () => FormsClient;
428
+ comments: () => CommentsClient;
323
429
  conversation: () => ConversationClient;
324
430
  manifest: () => Promise<unknown>;
325
431
  models: () => Promise<unknown[]>;
@@ -327,5 +433,5 @@ declare function createContentrain(config: ContentrainCDNConfig): {
327
433
  preload: (locale?: string) => Promise<boolean>;
328
434
  };
329
435
  //#endregion
330
- export { applyWhere as A, CdnDocumentQuery as C, EntryMeta as D, CdnCollectionQuery as E, DocumentDataSource as F, DocumentIndexEntry as I, SingletonDataSource as L, TransportConfig as M, CollectionDataSource as N, WhereClause as O, DictionaryDataSource as P, MediaManifest as S, CdnSingletonAccessor as T, FormsClient as _, ConversationClient as a, MediaAsset as b, ConversationHistory as c, ConversationSendOptions as d, ConversationToolResult as f, FormSubmitResult as g, FormFieldConfig as h, ContentrainError as i, HttpTransport as j, WhereOp as k, ConversationMessage as l, FormConfig as m, ContentrainCDNConfig as n, ConversationClientConfig as o, ConversationUsage as p, createContentrain as r, ConversationContext as s, ContentrainCDNClient as t, ConversationResponse as u, FormsClientConfig as v, CdnDictionaryAccessor as w, MediaAssetMeta as x, MediaAccessor as y };
331
- //# sourceMappingURL=index-TgljULTn.d.mts.map
436
+ export { FormsClient as A, EntryMeta as B, CommentsClientConfig as C, FormFieldError as D, FormFieldConfig as E, MediaManifest as F, TransportConfig as G, WhereOp as H, CdnDocumentQuery as I, DocumentDataSource as J, CollectionDataSource as K, CdnDictionaryAccessor as L, MediaAccessor as M, MediaAsset as N, FormSubmitOptions as O, MediaAssetMeta as P, CdnSingletonAccessor as R, CommentsClient as S, FormConfig as T, applyWhere as U, WhereClause as V, HttpTransport as W, SingletonDataSource as X, DocumentIndexEntry as Y, CommentSubmitResult as _, ConversationClient as a, CommentThreadQuery as b, ConversationHistory as c, ConversationSendOptions as d, ConversationToolResult as f, CommentSubmission as g, CommentFieldError as h, ContentrainError as i, FormsClientConfig as j, FormSubmitResult as k, ConversationMessage as l, CommentAuthor as m, ContentrainCDNConfig as n, ConversationClientConfig as o, ConversationUsage as p, DictionaryDataSource as q, createContentrain as r, ConversationContext as s, ContentrainCDNClient as t, ConversationResponse as u, CommentThread as v, PublicComment as w, CommentType as x, CommentThreadConfig as y, CdnCollectionQuery as z };
437
+ //# sourceMappingURL=index-BFurv7WX.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-BFurv7WX.d.cts","names":[],"sources":["../src/cdn/data-source.ts","../src/cdn/http-transport.ts","../src/shared/where.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/comments-client.ts","../src/cdn/conversation-client.ts","../src/cdn/errors.ts","../src/cdn/index.ts"],"mappings":"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"}