canopycms 0.4.0 → 0.6.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 (69) hide show
  1. package/CHANGELOG.md +78 -42
  2. package/dist/ai/generate.d.ts +3 -0
  3. package/dist/ai/generate.js +12 -29
  4. package/dist/ai/handler.js +1 -0
  5. package/dist/ai/types.d.ts +3 -9
  6. package/dist/api/reference-options.d.ts +2 -5
  7. package/dist/api/reference-options.js +21 -2
  8. package/dist/api/schema.d.ts +4 -4
  9. package/dist/build/generate-ai-content.js +1 -0
  10. package/dist/cli/cli.js +136 -39
  11. package/dist/cli/generate-ai-content.js +75 -32
  12. package/dist/cli/init.js +34 -5
  13. package/dist/cli/template-files/cms-stack.ts.template +3 -0
  14. package/dist/cli/templates.js +5 -1
  15. package/dist/config/index.d.ts +1 -1
  16. package/dist/config/schemas/config.d.ts +74 -0
  17. package/dist/config/schemas/config.js +21 -0
  18. package/dist/config/schemas/field.d.ts +30 -0
  19. package/dist/config/schemas/field.js +22 -0
  20. package/dist/config/types.d.ts +39 -0
  21. package/dist/config/validation.d.ts +6 -0
  22. package/dist/config/validation.js +33 -0
  23. package/dist/editor/FormRenderer.js +15 -13
  24. package/dist/editor/copy.d.ts +5 -0
  25. package/dist/editor/copy.js +5 -0
  26. package/dist/editor/fields/BlockField.d.ts +2 -0
  27. package/dist/editor/fields/BlockField.js +15 -8
  28. package/dist/editor/fields/ItemList.d.ts +21 -3
  29. package/dist/editor/fields/ItemList.js +16 -8
  30. package/dist/editor/fields/NumberListField.d.ts +2 -0
  31. package/dist/editor/fields/NumberListField.js +6 -2
  32. package/dist/editor/fields/ObjectListField.js +9 -3
  33. package/dist/editor/fields/ReferenceField.d.ts +6 -7
  34. package/dist/editor/fields/ReferenceField.js +28 -122
  35. package/dist/editor/fields/ReferenceListField.d.ts +41 -0
  36. package/dist/editor/fields/ReferenceListField.js +139 -0
  37. package/dist/editor/fields/SelectField.d.ts +2 -0
  38. package/dist/editor/fields/SelectField.js +6 -3
  39. package/dist/editor/fields/StringListField.d.ts +10 -1
  40. package/dist/editor/fields/StringListField.js +18 -8
  41. package/dist/editor/fields/item-expansion.d.ts +7 -3
  42. package/dist/editor/fields/item-expansion.js +6 -4
  43. package/dist/editor/fields/list-item-keys.d.ts +6 -5
  44. package/dist/editor/fields/list-item-keys.js +45 -13
  45. package/dist/editor/fields/reference-noindex.d.ts +37 -0
  46. package/dist/editor/fields/reference-noindex.js +48 -0
  47. package/dist/editor/fields/use-reference-options.d.ts +36 -0
  48. package/dist/editor/fields/use-reference-options.js +121 -0
  49. package/dist/editor/form-outline.d.ts +2 -2
  50. package/dist/editor/form-outline.js +4 -2
  51. package/dist/editor/theme.js +5 -0
  52. package/dist/entry-schema-registry.d.ts +1 -1
  53. package/dist/entry-schema-registry.js +3 -2
  54. package/dist/entry-schema.d.ts +5 -3
  55. package/dist/entry-schema.js +2 -1
  56. package/dist/reference-resolver.d.ts +18 -1
  57. package/dist/reference-resolver.js +175 -8
  58. package/dist/schema/schema-store.d.ts +2 -2
  59. package/dist/static/index.d.ts +8 -4
  60. package/dist/static/index.js +38 -13
  61. package/dist/static/seo.d.ts +7 -5
  62. package/dist/static/seo.js +13 -6
  63. package/dist/validation/entry-validator.d.ts +5 -0
  64. package/dist/validation/entry-validator.js +12 -0
  65. package/dist/version.d.ts +1 -1
  66. package/dist/version.js +1 -1
  67. package/dist/worker/github-gateway-http.js +2 -2
  68. package/dist/worker/github-gateway.js +2 -2
  69. package/package.json +1 -1
@@ -363,14 +363,14 @@ export declare const updateEntryType: import("./route-builder.js").RouteDefiniti
363
363
  default?: boolean | undefined;
364
364
  format?: "mdx" | "md" | "json" | "yaml" | undefined;
365
365
  label?: string | undefined;
366
- schema?: string | undefined;
367
366
  maxItems?: number | undefined;
367
+ schema?: string | undefined;
368
368
  }, {
369
369
  default?: boolean | undefined;
370
370
  format?: "mdx" | "md" | "json" | "yaml" | undefined;
371
371
  label?: string | undefined;
372
- schema?: string | undefined;
373
372
  maxItems?: number | undefined;
373
+ schema?: string | undefined;
374
374
  }>, UpdateEntryTypeApiResponse>;
375
375
  /**
376
376
  * DELETE /:branch/schema/entry-types/:entryTypeName/...collectionPath - Remove entry type
@@ -640,14 +640,14 @@ export declare const SCHEMA_ROUTES: {
640
640
  default?: boolean | undefined;
641
641
  format?: "mdx" | "md" | "json" | "yaml" | undefined;
642
642
  label?: string | undefined;
643
- schema?: string | undefined;
644
643
  maxItems?: number | undefined;
644
+ schema?: string | undefined;
645
645
  }, {
646
646
  default?: boolean | undefined;
647
647
  format?: "mdx" | "md" | "json" | "yaml" | undefined;
648
648
  label?: string | undefined;
649
- schema?: string | undefined;
650
649
  maxItems?: number | undefined;
650
+ schema?: string | undefined;
651
651
  }>, UpdateEntryTypeApiResponse>;
652
652
  readonly removeEntryType: import("./route-builder.js").RouteDefinition<z.ZodObject<{
653
653
  branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
@@ -200,6 +200,7 @@ export async function generateAIContentFiles(options) {
200
200
  contentRoot: contentRootName,
201
201
  config: aiConfig,
202
202
  entryLinkUrl: config.entryLinkUrl,
203
+ seo: config.seo,
203
204
  ...resolveBuildStamp(),
204
205
  });
205
206
  const absoluteOutputDir = path.resolve(outputDir) + path.sep;
package/dist/cli/cli.js CHANGED
@@ -233,9 +233,13 @@ async function canopyContext(options) {
233
233
  "// authenticated request instead of silently falling back to unauthenticated dev auth.",
234
234
  "// CLERK_SECRET_KEY is read only where Clerk's backend API is called: the auth-cache",
235
235
  "// refresh (the worker in prod, lazily on the dev server in dev).",
236
+ "// useOrganizationsAsGroups needs Organizations enabled on the Clerk instance and offers",
237
+ "// every organization in the instance as a group. In an AWS deployment the worker reads",
238
+ "// the setting from `clerkOrganizationsAsGroups` in the CDK stack, which governs there;",
239
+ "// keep the two equal.",
236
240
  "const authPlugin =",
237
241
  " config.server.mode === 'prod' || process.env.CANOPY_AUTH_MODE === 'clerk'",
238
- " ? createClerkAuthPlugin({ useOrganizationsAsGroups: true })",
242
+ " ? createClerkAuthPlugin({ useOrganizationsAsGroups: false })",
239
243
  " : createDevAuthPlugin()"
240
244
  ].join("\n") : "const authPlugin = createDevAuthPlugin()";
241
245
  return template.replace("{{AUTH_IMPORTS}}", authImports).replace("{{AUTH_PLUGIN_SETUP}}", authPluginSetup).replace("{{CONFIG_IMPORT}}", options.configImport);
@@ -1223,7 +1227,7 @@ CanopyCMS worker run-once (mode: ${mode}, auth: ${authMode})
1223
1227
  }
1224
1228
  console.log("\nDone");
1225
1229
  }
1226
- var __defProp2, __getOwnPropNames2, __esm2, __export2, active, init_logger, scalarFieldTypes, markdownFieldTypes, primitiveFieldTypes, fieldTypes, init_types, SAFE_HTML_TAGS, COMPONENT_NAME, MDX_BINDINGS, FORBIDDEN_ATTRIBUTES, init_mdx_allowlist, fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, mdxPropAllowSchema, mdxAllowlistSchema, markdownFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema, init_field, relativePathSchema, entryTypeSchema, collectionSchema, init_collection, SENTINEL_BASE, DOT_SEGMENT, init_sanitize_href, uploadTargetUrlSchema, previewPrefixSchema, init_url, mediaSchema, init_media, defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, init_config2, init_error2, init_fs2, templates_exports, __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME, init_templates, project_detect_exports, COMMANDS, MAX_WORKSPACE_DEPTH, PLACEHOLDER_GITHUB_REPO, CDK_DEPENDENCIES, init_project_detect, task_queue_config_exports, init_task_queue_config, envFlag, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, cms_task_queue_exports, debugLogger, cmsTaskQueueLogger, init_cms_task_queue, ProdClientSafeStrategy, DevClientSafeStrategy, warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName, ProdStrategy, DevStrategy, strategyCache;
1230
+ var __defProp2, __getOwnPropNames2, __esm2, __export2, active, init_logger, scalarFieldTypes, markdownFieldTypes, primitiveFieldTypes, fieldTypes, init_types, SAFE_HTML_TAGS, COMPONENT_NAME, MDX_BINDINGS, FORBIDDEN_ATTRIBUTES, init_mdx_allowlist, fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, mdxPropAllowSchema, mdxAllowlistSchema, markdownFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceSortFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema, init_field, relativePathSchema, entryTypeSchema, collectionSchema, init_collection, SENTINEL_BASE, DOT_SEGMENT, init_sanitize_href, uploadTargetUrlSchema, previewPrefixSchema, init_url, mediaSchema, init_media, defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, seoFieldNameSchema, seoSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, init_config2, init_error2, init_fs2, templates_exports, __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME, init_templates, project_detect_exports, COMMANDS, MAX_WORKSPACE_DEPTH, PLACEHOLDER_GITHUB_REPO, CDK_DEPENDENCIES, init_project_detect, task_queue_config_exports, init_task_queue_config, envFlag, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, cms_task_queue_exports, debugLogger, cmsTaskQueueLogger, init_cms_task_queue, ProdClientSafeStrategy, DevClientSafeStrategy, warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName, ProdStrategy, DevStrategy, strategyCache;
1227
1231
  var init_init = __esm({
1228
1232
  "dist/cli/init.js"() {
1229
1233
  "use strict";
@@ -1361,7 +1365,10 @@ var init_init = __esm({
1361
1365
  required: z.boolean().optional(),
1362
1366
  list: z.boolean().optional(),
1363
1367
  isTitle: z.boolean().optional(),
1364
- isBody: z.boolean().optional()
1368
+ isBody: z.boolean().optional(),
1369
+ // Listed so a parse keeps it (zod strips unknown keys); which fields may carry it is
1370
+ // `ensureMaxItemsPlacement`'s call (config/validation.ts).
1371
+ maxItems: z.number().int().positive().optional()
1365
1372
  });
1366
1373
  selectOptionSchema = z.union([
1367
1374
  z.string(),
@@ -1419,6 +1426,7 @@ var init_init = __esm({
1419
1426
  type: z.literal("select"),
1420
1427
  options: z.array(selectOptionSchema).min(1)
1421
1428
  });
1429
+ referenceSortFieldSchema = z.string().regex(/^[^.]+(\.[^.]+)*$/, 'must be a field name or a dotted path such as "meta.publishedAt"');
1422
1430
  referenceFieldSchema = fieldBaseSchema.extend({
1423
1431
  type: z.literal("reference"),
1424
1432
  collections: z.array(z.string().min(1)).min(1).optional(),
@@ -1429,7 +1437,14 @@ var init_init = __esm({
1429
1437
  // default, so a consumer that adopts this schema's parse output would silently delete a
1430
1438
  // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
1431
1439
  // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
1432
- includeBody: z.boolean().optional()
1440
+ includeBody: z.boolean().optional(),
1441
+ optionSort: z.union([
1442
+ z.enum(["label", "collection"]),
1443
+ z.object({
1444
+ field: referenceSortFieldSchema,
1445
+ direction: z.enum(["asc", "desc"]).optional()
1446
+ }).strict()
1447
+ ]).optional()
1433
1448
  });
1434
1449
  fieldHolder = [z.never()];
1435
1450
  blockSchema = z.object({
@@ -1614,6 +1629,19 @@ var init_init = __esm({
1614
1629
  // Optional: provider sign-in UI for signed-out users (see EditorSignInProps)
1615
1630
  SignInComponent: z5.custom().optional()
1616
1631
  });
1632
+ seoFieldNameSchema = z5.string().min(1).optional();
1633
+ seoSchema = z5.object({
1634
+ group: z5.string().min(1).optional(),
1635
+ fields: z5.object({
1636
+ title: seoFieldNameSchema,
1637
+ description: seoFieldNameSchema,
1638
+ ogImage: seoFieldNameSchema,
1639
+ ogType: seoFieldNameSchema,
1640
+ canonical: seoFieldNameSchema,
1641
+ noindex: seoFieldNameSchema,
1642
+ twitterCard: seoFieldNameSchema
1643
+ }).strict().optional()
1644
+ }).strict();
1617
1645
  CanopyConfigSchema = z5.object({
1618
1646
  media: mediaSchema.optional(),
1619
1647
  // No outer .optional(): both schemas already .default('deny'), so the field is optional on
@@ -1663,6 +1691,7 @@ var init_init = __esm({
1663
1691
  entryLinkUrl: z5.custom().optional(),
1664
1692
  validateEntry: z5.custom().optional(),
1665
1693
  mdxAllow: mdxAllowlistSchema.optional(),
1694
+ seo: seoSchema.optional(),
1666
1695
  dev: devConfigSchema.optional()
1667
1696
  }).strict();
1668
1697
  DEFAULT_PROD_WORKSPACE = "/mnt/efs";
@@ -4569,7 +4598,11 @@ function linkOnlyReferenceData(data, format, fields) {
4569
4598
  return rest;
4570
4599
  }
4571
4600
  function extractSeoFields(entryData, opts = {}) {
4572
- const names = { ...DEFAULT_SEO_FIELD_NAMES, ...opts.fields };
4601
+ const names = { ...DEFAULT_SEO_FIELD_NAMES };
4602
+ for (const [key, name] of Object.entries(opts.fields ?? {})) {
4603
+ if (typeof name === "string" && name !== "")
4604
+ names[key] = name;
4605
+ }
4573
4606
  const entry = asRecord2(entryData);
4574
4607
  const source = opts.group ? asRecord2(entry?.[opts.group]) : entry;
4575
4608
  return {
@@ -6254,7 +6287,7 @@ function createReferenceRendering(idIndex, flatSchema, entryLinkUrl, markdownCop
6254
6287
  };
6255
6288
  }
6256
6289
  async function generateAIContent(options) {
6257
- const { store, flatSchema, contentRoot, config, entryLinkUrl, generatedAt, buildId } = options;
6290
+ const { store, flatSchema, contentRoot, config, entryLinkUrl, seo, generatedAt, buildId } = options;
6258
6291
  assertValidNoindexConfig(config);
6259
6292
  assertValidReferenceLink(config);
6260
6293
  const files = /* @__PURE__ */ new Map();
@@ -6265,6 +6298,7 @@ async function generateAIContent(options) {
6265
6298
  config,
6266
6299
  idIndex,
6267
6300
  entryLinkUrl,
6301
+ seo,
6268
6302
  resolveTarget: createReferenceTargetResolver(store)
6269
6303
  };
6270
6304
  const collections = flatSchema.filter((item) => item.type === "collection");
@@ -6416,7 +6450,7 @@ async function collectCollection(read, collection, flatSchema) {
6416
6450
  return { collection, cleanPath, entries, subcollections };
6417
6451
  }
6418
6452
  async function collectEntries(read, collection, cleanPath) {
6419
- const { store, contentRoot, config, idIndex, entryLinkUrl, resolveTarget: resolveTarget2 } = read;
6453
+ const { store, contentRoot, config, idIndex, entryLinkUrl, seo, resolveTarget: resolveTarget2 } = read;
6420
6454
  const skipNoindex = config?.exclude?.noindex !== false;
6421
6455
  const pending = [];
6422
6456
  const listed = await store.getCollectionEntryPaths(collection.logicalPath);
@@ -6434,7 +6468,7 @@ async function collectEntries(read, collection, cleanPath) {
6434
6468
  const doc = await store.read(listEntry.collection, listEntry.slug, {
6435
6469
  resolveReferences: false
6436
6470
  });
6437
- if (skipNoindex && isNoindexEntry(doc.data, config?.seo))
6471
+ if (skipNoindex && isNoindexEntry(doc.data, seo))
6438
6472
  continue;
6439
6473
  const storedData = doc.data;
6440
6474
  doc.data = await resolveReferenceFields(doc.data, entryTypeConfig.schema, resolveTarget2);
@@ -6501,24 +6535,8 @@ function assertValidNoindexConfig(config) {
6501
6535
  if (noindex !== void 0 && typeof noindex !== "boolean") {
6502
6536
  throw new Error(`AI content config: exclude.noindex must be a boolean, got ${typeof noindex}`);
6503
6537
  }
6504
- const seo = config?.seo;
6505
- if (seo === void 0)
6506
- return;
6507
- if (typeof seo !== "object" || seo === null || Array.isArray(seo)) {
6508
- throw new Error("AI content config: seo must be an object of { group?, fields? }");
6509
- }
6510
- const { group, fields } = seo;
6511
- if (group !== void 0 && typeof group !== "string") {
6512
- throw new Error("AI content config: seo.group must be a string");
6513
- }
6514
- if (fields === void 0)
6515
- return;
6516
- if (typeof fields !== "object" || fields === null || Array.isArray(fields) || Object.values(fields).some((name) => typeof name !== "string")) {
6517
- throw new Error("AI content config: seo.fields must map SEO fields to field-name strings");
6518
- }
6519
- const unknown = Object.keys(fields).filter((key) => !(key in DEFAULT_SEO_FIELD_NAMES));
6520
- if (unknown.length > 0) {
6521
- throw new Error(`AI content config: unknown seo.fields key(s): ${unknown.join(", ")}`);
6538
+ if (config && "seo" in config) {
6539
+ throw new Error("AI content config: seo is not an AI content option. Set it once as seo in defineCanopyConfig, where the sitemap, page metadata and AI content all read it.");
6522
6540
  }
6523
6541
  }
6524
6542
  function stripContentRoot(logicalPath, contentRoot) {
@@ -7492,6 +7510,14 @@ function validateEntryData(fields, data, pathPrefix = "", options = {}) {
7492
7510
  }
7493
7511
  if (value === void 0 || value === null)
7494
7512
  continue;
7513
+ if (Array.isArray(value) && "maxItems" in field && typeof field.maxItems === "number" && value.length > field.maxItems) {
7514
+ const cap = field.maxItems;
7515
+ errors.push({
7516
+ fieldPath: path272,
7517
+ message: `At most ${cap} ${cap === 1 ? "item" : "items"}; this list has ${value.length}`,
7518
+ maxItems: { count: value.length, cap }
7519
+ });
7520
+ }
7495
7521
  if (field.type === "block") {
7496
7522
  const blockField = field;
7497
7523
  if (!Array.isArray(value)) {
@@ -7590,14 +7616,34 @@ function assertBuildEntriesValid(items, phaseLabel) {
7590
7616
  const invalid = findInvalidEntries(items);
7591
7617
  if (invalid.length === 0)
7592
7618
  return;
7593
- const lines = invalid.map(({ entryPath, errors }) => {
7594
- const [first, ...rest] = errors;
7595
- const summary = rest.length > 0 ? `${first.fieldPath}: ${first.message} (+${rest.length} more)` : `${first.fieldPath}: ${first.message}`;
7596
- return ` - ${entryPath} \u2014 ${summary}`;
7597
- });
7619
+ const lines = [];
7620
+ let hasOverCap = false;
7621
+ let hasOrdinary = false;
7622
+ for (const { entryPath, errors } of invalid) {
7623
+ const ordinary = errors.filter((e) => e.maxItems === void 0);
7624
+ if (ordinary.length > 0) {
7625
+ hasOrdinary = true;
7626
+ const [first, ...rest] = ordinary;
7627
+ const summary = rest.length > 0 ? `${first.fieldPath}: ${first.message} (+${rest.length} more)` : `${first.fieldPath}: ${first.message}`;
7628
+ lines.push(` - ${entryPath} \u2014 ${summary}`);
7629
+ }
7630
+ for (const { fieldPath, maxItems } of errors) {
7631
+ if (maxItems === void 0)
7632
+ continue;
7633
+ hasOverCap = true;
7634
+ lines.push(` - ${entryPath} \u2014 ${fieldPath}: ${maxItems.count} items, but maxItems is ${maxItems.cap}`);
7635
+ }
7636
+ }
7637
+ const advice = [];
7638
+ if (hasOverCap) {
7639
+ advice.push(`A list over its maxItems: open the entry in the editor and remove a pick from that list (or remove items) until it holds at most maxItems, then rebuild. This usually happens when two branches each add one item to the same list and are merged one after the other.`);
7640
+ }
7641
+ if (hasOrdinary) {
7642
+ advice.push(`${hasOverCap ? "The other errors are" : "These are"} likely abandoned create-scaffolds (empty entries left behind when a create was started but never finished). Finish editing the entry (fill in its required fields) or delete the abandoned draft, then rebuild.`);
7643
+ }
7598
7644
  throw new Error(`CanopyCMS static build: found ${invalid.length} schema-invalid ${invalid.length === 1 ? "entry" : "entries"} during ${phaseLabel}:
7599
7645
  ${lines.join("\n")}
7600
- These are likely abandoned create-scaffolds (empty entries left behind when a create was started but never finished). Finish editing the entry (fill in its required fields) or delete the abandoned draft, then rebuild.`);
7646
+ ` + advice.join("\n"));
7601
7647
  }
7602
7648
  async function readGeneratedRecord(outputDir) {
7603
7649
  try {
@@ -7689,6 +7735,7 @@ ${cached.issues.map((issue) => ` - ${issue.message}`).join("\n")}`);
7689
7735
  contentRoot: contentRootName,
7690
7736
  config: aiConfig2,
7691
7737
  entryLinkUrl: config.entryLinkUrl,
7738
+ seo: config.seo,
7692
7739
  ...resolveBuildStamp()
7693
7740
  });
7694
7741
  const absoluteOutputDir = path25.resolve(outputDir) + path25.sep;
@@ -7785,7 +7832,7 @@ async function generateAIContentCLI(options) {
7785
7832
  console.log(` Output: ${result.outputDir}
7786
7833
  `);
7787
7834
  }
7788
- var __defProp3, __getOwnPropNames3, __esm3, __export3, init_error3, init_atomic_write2, init_field_traversal, ITEM_KEY, ITEM_KEY_PATTERN, isItemKey, BASE58, isListField, init_item_keys, active2, init_logger2, SENTINEL_BASE2, DOT_SEGMENT2, init_sanitize_href2, init_normalize, ROOT_COLLECTION_ID, init_types2, normalizePathValue, flattenSchema, init_flatten, scalarFieldTypes2, markdownFieldTypes2, primitiveFieldTypes2, fieldTypes2, init_types22, SAFE_HTML_TAGS2, COMPONENT_NAME2, MDX_BINDINGS2, FORBIDDEN_ATTRIBUTES2, init_mdx_allowlist2, fieldBaseSchema2, selectOptionSchema2, referenceOptionSchema2, primitiveFieldSchema2, mdxPropAllowSchema2, mdxAllowlistSchema2, markdownFieldSchema2, ASPECT_RATIO_RE2, imageFieldSchema2, selectFieldSchema2, referenceFieldSchema2, fieldHolder2, blockSchema2, blockFieldSchema2, objectFieldSchema2, inlineGroupFieldSchema2, customFieldSchema2, knownFieldSchema2, fieldSchema2, init_field2, relativePathSchema2, entryTypeSchema2, collectionSchema2, init_collection2, uploadTargetUrlSchema2, previewPrefixSchema2, init_url2, mediaSchema2, init_media2, defaultBranchAccessSchema2, defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, basePathSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, SERVER_MODE_ENV_VAR, BROWSER_MODE_ENV_VAR, warned2, init_mode_env, forEachReferenceField, init_validation, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned3, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, init_config22, ProdStrategy2, DevStrategy2, strategyCache2, init_client_unsafe_strategy, operating_mode_exports, init_operating_mode, task_queue_config_exports2, init_task_queue_config2, worker_status_exports, WORKER_STATUS_FILE, init_worker_status, BLOCK_DISCRIMINATOR_KEYS, BLOCK_STRUCTURAL_KEYS, envFlag2, LOG_LEVELS2, DebugLogger2, testLogger2, END, FRESH, LINE_WIDTH, MIN_LINE_WIDTH, SourceSplicer, log2, MAX_LCS_CELLS, Splicer, log22, EVIDENCE_MAX_DEPTH, log3, locks, PROVISIONING_LOCK_STALE_MS, sleep, META_DIR, CONTENT_WRITE_LOCK_NAME, DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, ContentWriteLockBusyError, sleep2, CONTENT_WRITE_LOCK_STALE_MS, heldRoots, getFormatExtension, isDataOnlyFormat, asRecord, DEFAULT_SEO_FIELD_NAMES, asRecord2, asText, asFlag, asImageUrl, asOgType, asTwitterCard, RESTRICTED_REFERENCE_MARKER, SEO_GROUP_FIELDS, MAX_WARNED, warned4, SchemaUnavailableError, BASE58_PATTERN, CONTENT_ID_PATTERN, PHYSICAL_SEGMENT_PATTERN, isValidId, EMPTY_LOGICAL_PATH, ContentIdIndex, parseTypedFilename, CONTENT_EXTENSIONS, registry, finalization, log4, META_DIR2, entryTypeMetaSchema, collectionMetaSchema, rootCollectionMetaSchema, fingerprints, editDistance, closestName, forEachEntryType, collectEntryTypeNames, validateReferenceEntryTypes, isDeployedStatic, isBuildMode, readsFromCheckout, STATIC_DEPLOY_USER, storage, log5, BaseBranchUnresolvedError, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, MISSING_REMOTE_REF_REASONS, CANOPY_META_DIR, SCHEMA_CACHE_VERSION, MTIME_CHECK_DEBOUNCE_MS, SCHEMA_CACHE_FILE, SCHEMA_GENERATION_RESOURCE, reportedIssues, BranchSchemaCache, RESOURCE, RESERVED_SETTINGS_BRANCH_PREFIX, BranchPathError, resolveContentBranchesRoot, ContentStoreError, ContentConflictError, EntryGoneError, BranchSyncingError, DuplicateContentIdError, UrlPathConflictError, isEmptyFieldValue, DEFAULT_INDEX_FRESHNESS_INTERVAL_MS, FORCED_REFRESH_MIN_INTERVAL_MS, STALE_LOOKUP, isMissingReference, createReferenceResolveCache, ContentStore, ATTR_CONTENT, BLOCK_PREFIX, BLOCK_SUFFIX, BLOCK_RESTORE_RE, INLINE_PREFIX, INLINE_SUFFIX, INLINE_RESTORE_RE, DEFAULT_REFERENCE_RENDERING, FLAT_CELL_TYPES, log6, BASE58_CHAR, ENTRY_LINK_PATTERN, INLINE_LINK_TARGET, BranchMetadataCorruptError, BRANCH_META_DIR, BRANCH_META_FILE, branchMetadataFilePath, UNKNOWN_TIME, enumOf, branchMetadataShape, branchMetadataSchema, branchMetadataFileSchema, log7, REGISTRY_FILE, REGISTRY_STALE_FILE, REGISTRY_TEMP_FILE, REGISTRY_VERSION, RESOURCE2, GET_MISS_REFRESH_MIN_INTERVAL_MS, snapshotSchema, reportedUnreadable, BranchRegistry, log8, OccWriteConflictError, CURRENT_SCHEMA_VERSION, BranchMetadataConflictError, BranchMetadataFileManager, loadBranchContext, log9, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, GIT_NETWORK_ENV_PASSTHROUGH, NO_AUTO_GC_CONFIG, REMOTE_GIT_CONFIG, REMOTE_GIT_MAX_LOOSE_OBJECTS, REMOTE_GIT_MAX_PACKS, PARALLEL_CHECKOUT_ARGS, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, GitConflictError, GitRemoteRefMissingError, RemoteNotReadyError, SettingsRemoteUnreadableError, SettingsBranchDivergedError, SETTINGS_BRANCH_FILES, SettingsBranchHasContentHistoryError, GitManager, PUBLISH_LOCK_WAIT_MS, CREATE_RESIDUE_MIN_QUIET_MS, STAGING_MAX_AGE_MS, REPAIR_MAX_AGE_MS, DELETING_MAX_AGE_MS, SETTINGS_TRASH_MAX_AGE_MS, BranchProvisioningBusyError, BranchDirOccupiedError, STAGING_PREFIX, REPAIR_PREFIX, TRASH_PREFIX, stagingDirName, NO_GIT, RENAME_BLOCKED, SPARSE_CONE_RECORD, defaultSink, sink, ProvisionLog, testHooks, provisionsInFlight, BranchWorkspaceManager, log10, normalizePath, readEntryData, createReferenceResolver, withoutUnavailableEntries, resolveCollectionItemReferences, looksLikeMalformedEntry, listCollectionEntries, MAX_INPUT_PIXELS, CROP_PRECISION, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, GENERATED_RECORD_FILENAME, SAFE_BUILD_ID, BUILD_ID_RULE, jiti;
7835
+ var __defProp3, __getOwnPropNames3, __esm3, __export3, init_error3, init_atomic_write2, init_field_traversal, ITEM_KEY, ITEM_KEY_PATTERN, isItemKey, BASE58, isListField, init_item_keys, active2, init_logger2, SENTINEL_BASE2, DOT_SEGMENT2, init_sanitize_href2, init_normalize, ROOT_COLLECTION_ID, init_types2, normalizePathValue, flattenSchema, init_flatten, scalarFieldTypes2, markdownFieldTypes2, primitiveFieldTypes2, fieldTypes2, init_types22, SAFE_HTML_TAGS2, COMPONENT_NAME2, MDX_BINDINGS2, FORBIDDEN_ATTRIBUTES2, init_mdx_allowlist2, fieldBaseSchema2, selectOptionSchema2, referenceOptionSchema2, primitiveFieldSchema2, mdxPropAllowSchema2, mdxAllowlistSchema2, markdownFieldSchema2, ASPECT_RATIO_RE2, imageFieldSchema2, selectFieldSchema2, referenceSortFieldSchema2, referenceFieldSchema2, fieldHolder2, blockSchema2, blockFieldSchema2, objectFieldSchema2, inlineGroupFieldSchema2, customFieldSchema2, knownFieldSchema2, fieldSchema2, init_field2, relativePathSchema2, entryTypeSchema2, collectionSchema2, init_collection2, uploadTargetUrlSchema2, previewPrefixSchema2, init_url2, mediaSchema2, init_media2, defaultBranchAccessSchema2, defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, basePathSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, seoFieldNameSchema2, seoSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, SERVER_MODE_ENV_VAR, BROWSER_MODE_ENV_VAR, warned2, init_mode_env, forEachReferenceField, init_validation, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned3, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, init_config22, ProdStrategy2, DevStrategy2, strategyCache2, init_client_unsafe_strategy, operating_mode_exports, init_operating_mode, task_queue_config_exports2, init_task_queue_config2, worker_status_exports, WORKER_STATUS_FILE, init_worker_status, BLOCK_DISCRIMINATOR_KEYS, BLOCK_STRUCTURAL_KEYS, envFlag2, LOG_LEVELS2, DebugLogger2, testLogger2, END, FRESH, LINE_WIDTH, MIN_LINE_WIDTH, SourceSplicer, log2, MAX_LCS_CELLS, Splicer, log22, EVIDENCE_MAX_DEPTH, log3, locks, PROVISIONING_LOCK_STALE_MS, sleep, META_DIR, CONTENT_WRITE_LOCK_NAME, DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, ContentWriteLockBusyError, sleep2, CONTENT_WRITE_LOCK_STALE_MS, heldRoots, getFormatExtension, isDataOnlyFormat, asRecord, DEFAULT_SEO_FIELD_NAMES, asRecord2, asText, asFlag, asImageUrl, asOgType, asTwitterCard, RESTRICTED_REFERENCE_MARKER, SEO_GROUP_FIELDS, MAX_WARNED, warned4, SchemaUnavailableError, BASE58_PATTERN, CONTENT_ID_PATTERN, PHYSICAL_SEGMENT_PATTERN, isValidId, EMPTY_LOGICAL_PATH, ContentIdIndex, parseTypedFilename, CONTENT_EXTENSIONS, registry, finalization, log4, META_DIR2, entryTypeMetaSchema, collectionMetaSchema, rootCollectionMetaSchema, fingerprints, editDistance, closestName, forEachEntryType, collectEntryTypeNames, validateReferenceEntryTypes, isDeployedStatic, isBuildMode, readsFromCheckout, STATIC_DEPLOY_USER, storage, log5, BaseBranchUnresolvedError, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, MISSING_REMOTE_REF_REASONS, CANOPY_META_DIR, SCHEMA_CACHE_VERSION, MTIME_CHECK_DEBOUNCE_MS, SCHEMA_CACHE_FILE, SCHEMA_GENERATION_RESOURCE, reportedIssues, BranchSchemaCache, RESOURCE, RESERVED_SETTINGS_BRANCH_PREFIX, BranchPathError, resolveContentBranchesRoot, ContentStoreError, ContentConflictError, EntryGoneError, BranchSyncingError, DuplicateContentIdError, UrlPathConflictError, isEmptyFieldValue, DEFAULT_INDEX_FRESHNESS_INTERVAL_MS, FORCED_REFRESH_MIN_INTERVAL_MS, STALE_LOOKUP, isMissingReference, createReferenceResolveCache, ContentStore, ATTR_CONTENT, BLOCK_PREFIX, BLOCK_SUFFIX, BLOCK_RESTORE_RE, INLINE_PREFIX, INLINE_SUFFIX, INLINE_RESTORE_RE, DEFAULT_REFERENCE_RENDERING, FLAT_CELL_TYPES, log6, BASE58_CHAR, ENTRY_LINK_PATTERN, INLINE_LINK_TARGET, BranchMetadataCorruptError, BRANCH_META_DIR, BRANCH_META_FILE, branchMetadataFilePath, UNKNOWN_TIME, enumOf, branchMetadataShape, branchMetadataSchema, branchMetadataFileSchema, log7, REGISTRY_FILE, REGISTRY_STALE_FILE, REGISTRY_TEMP_FILE, REGISTRY_VERSION, RESOURCE2, GET_MISS_REFRESH_MIN_INTERVAL_MS, snapshotSchema, reportedUnreadable, BranchRegistry, log8, OccWriteConflictError, CURRENT_SCHEMA_VERSION, BranchMetadataConflictError, BranchMetadataFileManager, loadBranchContext, log9, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, GIT_NETWORK_ENV_PASSTHROUGH, NO_AUTO_GC_CONFIG, REMOTE_GIT_CONFIG, REMOTE_GIT_MAX_LOOSE_OBJECTS, REMOTE_GIT_MAX_PACKS, PARALLEL_CHECKOUT_ARGS, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, GitConflictError, GitRemoteRefMissingError, RemoteNotReadyError, SettingsRemoteUnreadableError, SettingsBranchDivergedError, SETTINGS_BRANCH_FILES, SettingsBranchHasContentHistoryError, GitManager, PUBLISH_LOCK_WAIT_MS, CREATE_RESIDUE_MIN_QUIET_MS, STAGING_MAX_AGE_MS, REPAIR_MAX_AGE_MS, DELETING_MAX_AGE_MS, SETTINGS_TRASH_MAX_AGE_MS, BranchProvisioningBusyError, BranchDirOccupiedError, STAGING_PREFIX, REPAIR_PREFIX, TRASH_PREFIX, stagingDirName, NO_GIT, RENAME_BLOCKED, SPARSE_CONE_RECORD, defaultSink, sink, ProvisionLog, testHooks, provisionsInFlight, BranchWorkspaceManager, log10, normalizePath, readEntryData, createReferenceResolver, withoutUnavailableEntries, resolveCollectionItemReferences, looksLikeMalformedEntry, listCollectionEntries, MAX_INPUT_PIXELS, CROP_PRECISION, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, GENERATED_RECORD_FILENAME, SAFE_BUILD_ID, BUILD_ID_RULE, jiti;
7789
7836
  var init_generate_ai_content = __esm({
7790
7837
  "dist/cli/generate-ai-content.js"() {
7791
7838
  "use strict";
@@ -8068,7 +8115,10 @@ var init_generate_ai_content = __esm({
8068
8115
  required: z22.boolean().optional(),
8069
8116
  list: z22.boolean().optional(),
8070
8117
  isTitle: z22.boolean().optional(),
8071
- isBody: z22.boolean().optional()
8118
+ isBody: z22.boolean().optional(),
8119
+ // Listed so a parse keeps it (zod strips unknown keys); which fields may carry it is
8120
+ // `ensureMaxItemsPlacement`'s call (config/validation.ts).
8121
+ maxItems: z22.number().int().positive().optional()
8072
8122
  });
8073
8123
  selectOptionSchema2 = z22.union([
8074
8124
  z22.string(),
@@ -8126,6 +8176,7 @@ var init_generate_ai_content = __esm({
8126
8176
  type: z22.literal("select"),
8127
8177
  options: z22.array(selectOptionSchema2).min(1)
8128
8178
  });
8179
+ referenceSortFieldSchema2 = z22.string().regex(/^[^.]+(\.[^.]+)*$/, 'must be a field name or a dotted path such as "meta.publishedAt"');
8129
8180
  referenceFieldSchema2 = fieldBaseSchema2.extend({
8130
8181
  type: z22.literal("reference"),
8131
8182
  collections: z22.array(z22.string().min(1)).min(1).optional(),
@@ -8136,7 +8187,14 @@ var init_generate_ai_content = __esm({
8136
8187
  // default, so a consumer that adopts this schema's parse output would silently delete a
8137
8188
  // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
8138
8189
  // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
8139
- includeBody: z22.boolean().optional()
8190
+ includeBody: z22.boolean().optional(),
8191
+ optionSort: z22.union([
8192
+ z22.enum(["label", "collection"]),
8193
+ z22.object({
8194
+ field: referenceSortFieldSchema2,
8195
+ direction: z22.enum(["asc", "desc"]).optional()
8196
+ }).strict()
8197
+ ]).optional()
8140
8198
  });
8141
8199
  fieldHolder2 = [z22.never()];
8142
8200
  blockSchema2 = z22.object({
@@ -8314,6 +8372,19 @@ var init_generate_ai_content = __esm({
8314
8372
  // Optional: provider sign-in UI for signed-out users (see EditorSignInProps)
8315
8373
  SignInComponent: z6.custom().optional()
8316
8374
  });
8375
+ seoFieldNameSchema2 = z6.string().min(1).optional();
8376
+ seoSchema2 = z6.object({
8377
+ group: z6.string().min(1).optional(),
8378
+ fields: z6.object({
8379
+ title: seoFieldNameSchema2,
8380
+ description: seoFieldNameSchema2,
8381
+ ogImage: seoFieldNameSchema2,
8382
+ ogType: seoFieldNameSchema2,
8383
+ canonical: seoFieldNameSchema2,
8384
+ noindex: seoFieldNameSchema2,
8385
+ twitterCard: seoFieldNameSchema2
8386
+ }).strict().optional()
8387
+ }).strict();
8317
8388
  CanopyConfigSchema2 = z6.object({
8318
8389
  media: mediaSchema2.optional(),
8319
8390
  // No outer .optional(): both schemas already .default('deny'), so the field is optional on
@@ -8363,6 +8434,7 @@ var init_generate_ai_content = __esm({
8363
8434
  entryLinkUrl: z6.custom().optional(),
8364
8435
  validateEntry: z6.custom().optional(),
8365
8436
  mdxAllow: mdxAllowlistSchema2.optional(),
8437
+ seo: seoSchema2.optional(),
8366
8438
  dev: devConfigSchema2.optional()
8367
8439
  }).strict();
8368
8440
  DEFAULT_PROD_WORKSPACE2 = "/mnt/efs";
@@ -15322,7 +15394,7 @@ var init_mdx_allowlist3 = __esm({
15322
15394
 
15323
15395
  // dist/config/schemas/field.js
15324
15396
  import { z as z10 } from "zod";
15325
- var fieldBaseSchema3, selectOptionSchema3, referenceOptionSchema3, primitiveFieldSchema3, mdxPropAllowSchema3, mdxAllowlistSchema3, markdownFieldSchema3, ASPECT_RATIO_RE3, imageFieldSchema3, selectFieldSchema3, referenceFieldSchema3, fieldHolder3, blockSchema3, blockFieldSchema3, objectFieldSchema3, inlineGroupFieldSchema3, customFieldSchema3, knownFieldSchema3, fieldSchema3;
15397
+ var fieldBaseSchema3, selectOptionSchema3, referenceOptionSchema3, primitiveFieldSchema3, mdxPropAllowSchema3, mdxAllowlistSchema3, markdownFieldSchema3, ASPECT_RATIO_RE3, imageFieldSchema3, selectFieldSchema3, referenceSortFieldSchema3, referenceFieldSchema3, fieldHolder3, blockSchema3, blockFieldSchema3, objectFieldSchema3, inlineGroupFieldSchema3, customFieldSchema3, knownFieldSchema3, fieldSchema3;
15326
15398
  var init_field3 = __esm({
15327
15399
  "dist/config/schemas/field.js"() {
15328
15400
  "use strict";
@@ -15335,7 +15407,10 @@ var init_field3 = __esm({
15335
15407
  required: z10.boolean().optional(),
15336
15408
  list: z10.boolean().optional(),
15337
15409
  isTitle: z10.boolean().optional(),
15338
- isBody: z10.boolean().optional()
15410
+ isBody: z10.boolean().optional(),
15411
+ // Listed so a parse keeps it (zod strips unknown keys); which fields may carry it is
15412
+ // `ensureMaxItemsPlacement`'s call (config/validation.ts).
15413
+ maxItems: z10.number().int().positive().optional()
15339
15414
  });
15340
15415
  selectOptionSchema3 = z10.union([
15341
15416
  z10.string(),
@@ -15393,6 +15468,7 @@ var init_field3 = __esm({
15393
15468
  type: z10.literal("select"),
15394
15469
  options: z10.array(selectOptionSchema3).min(1)
15395
15470
  });
15471
+ referenceSortFieldSchema3 = z10.string().regex(/^[^.]+(\.[^.]+)*$/, 'must be a field name or a dotted path such as "meta.publishedAt"');
15396
15472
  referenceFieldSchema3 = fieldBaseSchema3.extend({
15397
15473
  type: z10.literal("reference"),
15398
15474
  collections: z10.array(z10.string().min(1)).min(1).optional(),
@@ -15403,7 +15479,14 @@ var init_field3 = __esm({
15403
15479
  // default, so a consumer that adopts this schema's parse output would silently delete a
15404
15480
  // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
15405
15481
  // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
15406
- includeBody: z10.boolean().optional()
15482
+ includeBody: z10.boolean().optional(),
15483
+ optionSort: z10.union([
15484
+ z10.enum(["label", "collection"]),
15485
+ z10.object({
15486
+ field: referenceSortFieldSchema3,
15487
+ direction: z10.enum(["asc", "desc"]).optional()
15488
+ }).strict()
15489
+ ]).optional()
15407
15490
  });
15408
15491
  fieldHolder3 = [z10.never()];
15409
15492
  blockSchema3 = z10.object({
@@ -15637,7 +15720,7 @@ var init_deployment_name2 = __esm({
15637
15720
 
15638
15721
  // dist/config/schemas/config.js
15639
15722
  import { z as z14 } from "zod";
15640
- var defaultBranchAccessSchema3, defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, basePathSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
15723
+ var defaultBranchAccessSchema3, defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, basePathSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, seoFieldNameSchema3, seoSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
15641
15724
  var init_config4 = __esm({
15642
15725
  "dist/config/schemas/config.js"() {
15643
15726
  "use strict";
@@ -15684,6 +15767,19 @@ var init_config4 = __esm({
15684
15767
  // Optional: provider sign-in UI for signed-out users (see EditorSignInProps)
15685
15768
  SignInComponent: z14.custom().optional()
15686
15769
  });
15770
+ seoFieldNameSchema3 = z14.string().min(1).optional();
15771
+ seoSchema3 = z14.object({
15772
+ group: z14.string().min(1).optional(),
15773
+ fields: z14.object({
15774
+ title: seoFieldNameSchema3,
15775
+ description: seoFieldNameSchema3,
15776
+ ogImage: seoFieldNameSchema3,
15777
+ ogType: seoFieldNameSchema3,
15778
+ canonical: seoFieldNameSchema3,
15779
+ noindex: seoFieldNameSchema3,
15780
+ twitterCard: seoFieldNameSchema3
15781
+ }).strict().optional()
15782
+ }).strict();
15687
15783
  CanopyConfigSchema3 = z14.object({
15688
15784
  media: mediaSchema3.optional(),
15689
15785
  // No outer .optional(): both schemas already .default('deny'), so the field is optional on
@@ -15733,6 +15829,7 @@ var init_config4 = __esm({
15733
15829
  entryLinkUrl: z14.custom().optional(),
15734
15830
  validateEntry: z14.custom().optional(),
15735
15831
  mdxAllow: mdxAllowlistSchema3.optional(),
15832
+ seo: seoSchema3.optional(),
15736
15833
  dev: devConfigSchema3.optional()
15737
15834
  }).strict();
15738
15835
  DEFAULT_PROD_WORKSPACE3 = "/mnt/efs";