canopycms 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -42
- package/dist/ai/generate.d.ts +3 -0
- package/dist/ai/generate.js +12 -29
- package/dist/ai/handler.js +1 -0
- package/dist/ai/types.d.ts +3 -9
- package/dist/api/reference-options.d.ts +2 -5
- package/dist/api/reference-options.js +21 -2
- package/dist/api/schema.d.ts +4 -4
- package/dist/build/generate-ai-content.js +1 -0
- package/dist/cli/cli.js +131 -38
- package/dist/cli/generate-ai-content.js +75 -32
- package/dist/cli/init.js +29 -4
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schemas/config.d.ts +74 -0
- package/dist/config/schemas/config.js +21 -0
- package/dist/config/schemas/field.d.ts +30 -0
- package/dist/config/schemas/field.js +22 -0
- package/dist/config/types.d.ts +39 -0
- package/dist/config/validation.d.ts +6 -0
- package/dist/config/validation.js +33 -0
- package/dist/editor/FormRenderer.js +15 -13
- package/dist/editor/copy.d.ts +5 -0
- package/dist/editor/copy.js +5 -0
- package/dist/editor/fields/BlockField.d.ts +2 -0
- package/dist/editor/fields/BlockField.js +15 -8
- package/dist/editor/fields/ItemList.d.ts +21 -3
- package/dist/editor/fields/ItemList.js +16 -8
- package/dist/editor/fields/NumberListField.d.ts +2 -0
- package/dist/editor/fields/NumberListField.js +6 -2
- package/dist/editor/fields/ObjectListField.js +9 -3
- package/dist/editor/fields/ReferenceField.d.ts +6 -7
- package/dist/editor/fields/ReferenceField.js +28 -122
- package/dist/editor/fields/ReferenceListField.d.ts +41 -0
- package/dist/editor/fields/ReferenceListField.js +139 -0
- package/dist/editor/fields/SelectField.d.ts +2 -0
- package/dist/editor/fields/SelectField.js +6 -3
- package/dist/editor/fields/StringListField.d.ts +10 -1
- package/dist/editor/fields/StringListField.js +18 -8
- package/dist/editor/fields/item-expansion.d.ts +7 -3
- package/dist/editor/fields/item-expansion.js +6 -4
- package/dist/editor/fields/list-item-keys.d.ts +6 -5
- package/dist/editor/fields/list-item-keys.js +45 -13
- package/dist/editor/fields/reference-noindex.d.ts +37 -0
- package/dist/editor/fields/reference-noindex.js +48 -0
- package/dist/editor/fields/use-reference-options.d.ts +36 -0
- package/dist/editor/fields/use-reference-options.js +121 -0
- package/dist/editor/form-outline.d.ts +2 -2
- package/dist/editor/form-outline.js +4 -2
- package/dist/editor/theme.js +5 -0
- package/dist/entry-schema-registry.d.ts +1 -1
- package/dist/entry-schema-registry.js +3 -2
- package/dist/entry-schema.d.ts +5 -3
- package/dist/entry-schema.js +2 -1
- package/dist/reference-resolver.d.ts +18 -1
- package/dist/reference-resolver.js +175 -8
- package/dist/schema/schema-store.d.ts +2 -2
- package/dist/static/index.d.ts +8 -4
- package/dist/static/index.js +38 -13
- package/dist/static/seo.d.ts +7 -5
- package/dist/static/seo.js +13 -6
- package/dist/validation/entry-validator.d.ts +5 -0
- package/dist/validation/entry-validator.js +12 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/api/schema.d.ts
CHANGED
|
@@ -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
|
@@ -1223,7 +1223,7 @@ CanopyCMS worker run-once (mode: ${mode}, auth: ${authMode})
|
|
|
1223
1223
|
}
|
|
1224
1224
|
console.log("\nDone");
|
|
1225
1225
|
}
|
|
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;
|
|
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, 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
1227
|
var init_init = __esm({
|
|
1228
1228
|
"dist/cli/init.js"() {
|
|
1229
1229
|
"use strict";
|
|
@@ -1361,7 +1361,10 @@ var init_init = __esm({
|
|
|
1361
1361
|
required: z.boolean().optional(),
|
|
1362
1362
|
list: z.boolean().optional(),
|
|
1363
1363
|
isTitle: z.boolean().optional(),
|
|
1364
|
-
isBody: z.boolean().optional()
|
|
1364
|
+
isBody: z.boolean().optional(),
|
|
1365
|
+
// Listed so a parse keeps it (zod strips unknown keys); which fields may carry it is
|
|
1366
|
+
// `ensureMaxItemsPlacement`'s call (config/validation.ts).
|
|
1367
|
+
maxItems: z.number().int().positive().optional()
|
|
1365
1368
|
});
|
|
1366
1369
|
selectOptionSchema = z.union([
|
|
1367
1370
|
z.string(),
|
|
@@ -1419,6 +1422,7 @@ var init_init = __esm({
|
|
|
1419
1422
|
type: z.literal("select"),
|
|
1420
1423
|
options: z.array(selectOptionSchema).min(1)
|
|
1421
1424
|
});
|
|
1425
|
+
referenceSortFieldSchema = z.string().regex(/^[^.]+(\.[^.]+)*$/, 'must be a field name or a dotted path such as "meta.publishedAt"');
|
|
1422
1426
|
referenceFieldSchema = fieldBaseSchema.extend({
|
|
1423
1427
|
type: z.literal("reference"),
|
|
1424
1428
|
collections: z.array(z.string().min(1)).min(1).optional(),
|
|
@@ -1429,7 +1433,14 @@ var init_init = __esm({
|
|
|
1429
1433
|
// default, so a consumer that adopts this schema's parse output would silently delete a
|
|
1430
1434
|
// runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
|
|
1431
1435
|
// absent on purpose — it is type-inference-only and stripping it changes no behavior.)
|
|
1432
|
-
includeBody: z.boolean().optional()
|
|
1436
|
+
includeBody: z.boolean().optional(),
|
|
1437
|
+
optionSort: z.union([
|
|
1438
|
+
z.enum(["label", "collection"]),
|
|
1439
|
+
z.object({
|
|
1440
|
+
field: referenceSortFieldSchema,
|
|
1441
|
+
direction: z.enum(["asc", "desc"]).optional()
|
|
1442
|
+
}).strict()
|
|
1443
|
+
]).optional()
|
|
1433
1444
|
});
|
|
1434
1445
|
fieldHolder = [z.never()];
|
|
1435
1446
|
blockSchema = z.object({
|
|
@@ -1614,6 +1625,19 @@ var init_init = __esm({
|
|
|
1614
1625
|
// Optional: provider sign-in UI for signed-out users (see EditorSignInProps)
|
|
1615
1626
|
SignInComponent: z5.custom().optional()
|
|
1616
1627
|
});
|
|
1628
|
+
seoFieldNameSchema = z5.string().min(1).optional();
|
|
1629
|
+
seoSchema = z5.object({
|
|
1630
|
+
group: z5.string().min(1).optional(),
|
|
1631
|
+
fields: z5.object({
|
|
1632
|
+
title: seoFieldNameSchema,
|
|
1633
|
+
description: seoFieldNameSchema,
|
|
1634
|
+
ogImage: seoFieldNameSchema,
|
|
1635
|
+
ogType: seoFieldNameSchema,
|
|
1636
|
+
canonical: seoFieldNameSchema,
|
|
1637
|
+
noindex: seoFieldNameSchema,
|
|
1638
|
+
twitterCard: seoFieldNameSchema
|
|
1639
|
+
}).strict().optional()
|
|
1640
|
+
}).strict();
|
|
1617
1641
|
CanopyConfigSchema = z5.object({
|
|
1618
1642
|
media: mediaSchema.optional(),
|
|
1619
1643
|
// No outer .optional(): both schemas already .default('deny'), so the field is optional on
|
|
@@ -1663,6 +1687,7 @@ var init_init = __esm({
|
|
|
1663
1687
|
entryLinkUrl: z5.custom().optional(),
|
|
1664
1688
|
validateEntry: z5.custom().optional(),
|
|
1665
1689
|
mdxAllow: mdxAllowlistSchema.optional(),
|
|
1690
|
+
seo: seoSchema.optional(),
|
|
1666
1691
|
dev: devConfigSchema.optional()
|
|
1667
1692
|
}).strict();
|
|
1668
1693
|
DEFAULT_PROD_WORKSPACE = "/mnt/efs";
|
|
@@ -4569,7 +4594,11 @@ function linkOnlyReferenceData(data, format, fields) {
|
|
|
4569
4594
|
return rest;
|
|
4570
4595
|
}
|
|
4571
4596
|
function extractSeoFields(entryData, opts = {}) {
|
|
4572
|
-
const names = { ...DEFAULT_SEO_FIELD_NAMES
|
|
4597
|
+
const names = { ...DEFAULT_SEO_FIELD_NAMES };
|
|
4598
|
+
for (const [key, name] of Object.entries(opts.fields ?? {})) {
|
|
4599
|
+
if (typeof name === "string" && name !== "")
|
|
4600
|
+
names[key] = name;
|
|
4601
|
+
}
|
|
4573
4602
|
const entry = asRecord2(entryData);
|
|
4574
4603
|
const source = opts.group ? asRecord2(entry?.[opts.group]) : entry;
|
|
4575
4604
|
return {
|
|
@@ -6254,7 +6283,7 @@ function createReferenceRendering(idIndex, flatSchema, entryLinkUrl, markdownCop
|
|
|
6254
6283
|
};
|
|
6255
6284
|
}
|
|
6256
6285
|
async function generateAIContent(options) {
|
|
6257
|
-
const { store, flatSchema, contentRoot, config, entryLinkUrl, generatedAt, buildId } = options;
|
|
6286
|
+
const { store, flatSchema, contentRoot, config, entryLinkUrl, seo, generatedAt, buildId } = options;
|
|
6258
6287
|
assertValidNoindexConfig(config);
|
|
6259
6288
|
assertValidReferenceLink(config);
|
|
6260
6289
|
const files = /* @__PURE__ */ new Map();
|
|
@@ -6265,6 +6294,7 @@ async function generateAIContent(options) {
|
|
|
6265
6294
|
config,
|
|
6266
6295
|
idIndex,
|
|
6267
6296
|
entryLinkUrl,
|
|
6297
|
+
seo,
|
|
6268
6298
|
resolveTarget: createReferenceTargetResolver(store)
|
|
6269
6299
|
};
|
|
6270
6300
|
const collections = flatSchema.filter((item) => item.type === "collection");
|
|
@@ -6416,7 +6446,7 @@ async function collectCollection(read, collection, flatSchema) {
|
|
|
6416
6446
|
return { collection, cleanPath, entries, subcollections };
|
|
6417
6447
|
}
|
|
6418
6448
|
async function collectEntries(read, collection, cleanPath) {
|
|
6419
|
-
const { store, contentRoot, config, idIndex, entryLinkUrl, resolveTarget: resolveTarget2 } = read;
|
|
6449
|
+
const { store, contentRoot, config, idIndex, entryLinkUrl, seo, resolveTarget: resolveTarget2 } = read;
|
|
6420
6450
|
const skipNoindex = config?.exclude?.noindex !== false;
|
|
6421
6451
|
const pending = [];
|
|
6422
6452
|
const listed = await store.getCollectionEntryPaths(collection.logicalPath);
|
|
@@ -6434,7 +6464,7 @@ async function collectEntries(read, collection, cleanPath) {
|
|
|
6434
6464
|
const doc = await store.read(listEntry.collection, listEntry.slug, {
|
|
6435
6465
|
resolveReferences: false
|
|
6436
6466
|
});
|
|
6437
|
-
if (skipNoindex && isNoindexEntry(doc.data,
|
|
6467
|
+
if (skipNoindex && isNoindexEntry(doc.data, seo))
|
|
6438
6468
|
continue;
|
|
6439
6469
|
const storedData = doc.data;
|
|
6440
6470
|
doc.data = await resolveReferenceFields(doc.data, entryTypeConfig.schema, resolveTarget2);
|
|
@@ -6501,24 +6531,8 @@ function assertValidNoindexConfig(config) {
|
|
|
6501
6531
|
if (noindex !== void 0 && typeof noindex !== "boolean") {
|
|
6502
6532
|
throw new Error(`AI content config: exclude.noindex must be a boolean, got ${typeof noindex}`);
|
|
6503
6533
|
}
|
|
6504
|
-
|
|
6505
|
-
|
|
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(", ")}`);
|
|
6534
|
+
if (config && "seo" in config) {
|
|
6535
|
+
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
6536
|
}
|
|
6523
6537
|
}
|
|
6524
6538
|
function stripContentRoot(logicalPath, contentRoot) {
|
|
@@ -7492,6 +7506,14 @@ function validateEntryData(fields, data, pathPrefix = "", options = {}) {
|
|
|
7492
7506
|
}
|
|
7493
7507
|
if (value === void 0 || value === null)
|
|
7494
7508
|
continue;
|
|
7509
|
+
if (Array.isArray(value) && "maxItems" in field && typeof field.maxItems === "number" && value.length > field.maxItems) {
|
|
7510
|
+
const cap = field.maxItems;
|
|
7511
|
+
errors.push({
|
|
7512
|
+
fieldPath: path272,
|
|
7513
|
+
message: `At most ${cap} ${cap === 1 ? "item" : "items"}; this list has ${value.length}`,
|
|
7514
|
+
maxItems: { count: value.length, cap }
|
|
7515
|
+
});
|
|
7516
|
+
}
|
|
7495
7517
|
if (field.type === "block") {
|
|
7496
7518
|
const blockField = field;
|
|
7497
7519
|
if (!Array.isArray(value)) {
|
|
@@ -7590,14 +7612,34 @@ function assertBuildEntriesValid(items, phaseLabel) {
|
|
|
7590
7612
|
const invalid = findInvalidEntries(items);
|
|
7591
7613
|
if (invalid.length === 0)
|
|
7592
7614
|
return;
|
|
7593
|
-
const lines =
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7615
|
+
const lines = [];
|
|
7616
|
+
let hasOverCap = false;
|
|
7617
|
+
let hasOrdinary = false;
|
|
7618
|
+
for (const { entryPath, errors } of invalid) {
|
|
7619
|
+
const ordinary = errors.filter((e) => e.maxItems === void 0);
|
|
7620
|
+
if (ordinary.length > 0) {
|
|
7621
|
+
hasOrdinary = true;
|
|
7622
|
+
const [first, ...rest] = ordinary;
|
|
7623
|
+
const summary = rest.length > 0 ? `${first.fieldPath}: ${first.message} (+${rest.length} more)` : `${first.fieldPath}: ${first.message}`;
|
|
7624
|
+
lines.push(` - ${entryPath} \u2014 ${summary}`);
|
|
7625
|
+
}
|
|
7626
|
+
for (const { fieldPath, maxItems } of errors) {
|
|
7627
|
+
if (maxItems === void 0)
|
|
7628
|
+
continue;
|
|
7629
|
+
hasOverCap = true;
|
|
7630
|
+
lines.push(` - ${entryPath} \u2014 ${fieldPath}: ${maxItems.count} items, but maxItems is ${maxItems.cap}`);
|
|
7631
|
+
}
|
|
7632
|
+
}
|
|
7633
|
+
const advice = [];
|
|
7634
|
+
if (hasOverCap) {
|
|
7635
|
+
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.`);
|
|
7636
|
+
}
|
|
7637
|
+
if (hasOrdinary) {
|
|
7638
|
+
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.`);
|
|
7639
|
+
}
|
|
7598
7640
|
throw new Error(`CanopyCMS static build: found ${invalid.length} schema-invalid ${invalid.length === 1 ? "entry" : "entries"} during ${phaseLabel}:
|
|
7599
7641
|
${lines.join("\n")}
|
|
7600
|
-
|
|
7642
|
+
` + advice.join("\n"));
|
|
7601
7643
|
}
|
|
7602
7644
|
async function readGeneratedRecord(outputDir) {
|
|
7603
7645
|
try {
|
|
@@ -7689,6 +7731,7 @@ ${cached.issues.map((issue) => ` - ${issue.message}`).join("\n")}`);
|
|
|
7689
7731
|
contentRoot: contentRootName,
|
|
7690
7732
|
config: aiConfig2,
|
|
7691
7733
|
entryLinkUrl: config.entryLinkUrl,
|
|
7734
|
+
seo: config.seo,
|
|
7692
7735
|
...resolveBuildStamp()
|
|
7693
7736
|
});
|
|
7694
7737
|
const absoluteOutputDir = path25.resolve(outputDir) + path25.sep;
|
|
@@ -7785,7 +7828,7 @@ async function generateAIContentCLI(options) {
|
|
|
7785
7828
|
console.log(` Output: ${result.outputDir}
|
|
7786
7829
|
`);
|
|
7787
7830
|
}
|
|
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;
|
|
7831
|
+
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
7832
|
var init_generate_ai_content = __esm({
|
|
7790
7833
|
"dist/cli/generate-ai-content.js"() {
|
|
7791
7834
|
"use strict";
|
|
@@ -8068,7 +8111,10 @@ var init_generate_ai_content = __esm({
|
|
|
8068
8111
|
required: z22.boolean().optional(),
|
|
8069
8112
|
list: z22.boolean().optional(),
|
|
8070
8113
|
isTitle: z22.boolean().optional(),
|
|
8071
|
-
isBody: z22.boolean().optional()
|
|
8114
|
+
isBody: z22.boolean().optional(),
|
|
8115
|
+
// Listed so a parse keeps it (zod strips unknown keys); which fields may carry it is
|
|
8116
|
+
// `ensureMaxItemsPlacement`'s call (config/validation.ts).
|
|
8117
|
+
maxItems: z22.number().int().positive().optional()
|
|
8072
8118
|
});
|
|
8073
8119
|
selectOptionSchema2 = z22.union([
|
|
8074
8120
|
z22.string(),
|
|
@@ -8126,6 +8172,7 @@ var init_generate_ai_content = __esm({
|
|
|
8126
8172
|
type: z22.literal("select"),
|
|
8127
8173
|
options: z22.array(selectOptionSchema2).min(1)
|
|
8128
8174
|
});
|
|
8175
|
+
referenceSortFieldSchema2 = z22.string().regex(/^[^.]+(\.[^.]+)*$/, 'must be a field name or a dotted path such as "meta.publishedAt"');
|
|
8129
8176
|
referenceFieldSchema2 = fieldBaseSchema2.extend({
|
|
8130
8177
|
type: z22.literal("reference"),
|
|
8131
8178
|
collections: z22.array(z22.string().min(1)).min(1).optional(),
|
|
@@ -8136,7 +8183,14 @@ var init_generate_ai_content = __esm({
|
|
|
8136
8183
|
// default, so a consumer that adopts this schema's parse output would silently delete a
|
|
8137
8184
|
// runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
|
|
8138
8185
|
// absent on purpose — it is type-inference-only and stripping it changes no behavior.)
|
|
8139
|
-
includeBody: z22.boolean().optional()
|
|
8186
|
+
includeBody: z22.boolean().optional(),
|
|
8187
|
+
optionSort: z22.union([
|
|
8188
|
+
z22.enum(["label", "collection"]),
|
|
8189
|
+
z22.object({
|
|
8190
|
+
field: referenceSortFieldSchema2,
|
|
8191
|
+
direction: z22.enum(["asc", "desc"]).optional()
|
|
8192
|
+
}).strict()
|
|
8193
|
+
]).optional()
|
|
8140
8194
|
});
|
|
8141
8195
|
fieldHolder2 = [z22.never()];
|
|
8142
8196
|
blockSchema2 = z22.object({
|
|
@@ -8314,6 +8368,19 @@ var init_generate_ai_content = __esm({
|
|
|
8314
8368
|
// Optional: provider sign-in UI for signed-out users (see EditorSignInProps)
|
|
8315
8369
|
SignInComponent: z6.custom().optional()
|
|
8316
8370
|
});
|
|
8371
|
+
seoFieldNameSchema2 = z6.string().min(1).optional();
|
|
8372
|
+
seoSchema2 = z6.object({
|
|
8373
|
+
group: z6.string().min(1).optional(),
|
|
8374
|
+
fields: z6.object({
|
|
8375
|
+
title: seoFieldNameSchema2,
|
|
8376
|
+
description: seoFieldNameSchema2,
|
|
8377
|
+
ogImage: seoFieldNameSchema2,
|
|
8378
|
+
ogType: seoFieldNameSchema2,
|
|
8379
|
+
canonical: seoFieldNameSchema2,
|
|
8380
|
+
noindex: seoFieldNameSchema2,
|
|
8381
|
+
twitterCard: seoFieldNameSchema2
|
|
8382
|
+
}).strict().optional()
|
|
8383
|
+
}).strict();
|
|
8317
8384
|
CanopyConfigSchema2 = z6.object({
|
|
8318
8385
|
media: mediaSchema2.optional(),
|
|
8319
8386
|
// No outer .optional(): both schemas already .default('deny'), so the field is optional on
|
|
@@ -8363,6 +8430,7 @@ var init_generate_ai_content = __esm({
|
|
|
8363
8430
|
entryLinkUrl: z6.custom().optional(),
|
|
8364
8431
|
validateEntry: z6.custom().optional(),
|
|
8365
8432
|
mdxAllow: mdxAllowlistSchema2.optional(),
|
|
8433
|
+
seo: seoSchema2.optional(),
|
|
8366
8434
|
dev: devConfigSchema2.optional()
|
|
8367
8435
|
}).strict();
|
|
8368
8436
|
DEFAULT_PROD_WORKSPACE2 = "/mnt/efs";
|
|
@@ -15322,7 +15390,7 @@ var init_mdx_allowlist3 = __esm({
|
|
|
15322
15390
|
|
|
15323
15391
|
// dist/config/schemas/field.js
|
|
15324
15392
|
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;
|
|
15393
|
+
var fieldBaseSchema3, selectOptionSchema3, referenceOptionSchema3, primitiveFieldSchema3, mdxPropAllowSchema3, mdxAllowlistSchema3, markdownFieldSchema3, ASPECT_RATIO_RE3, imageFieldSchema3, selectFieldSchema3, referenceSortFieldSchema3, referenceFieldSchema3, fieldHolder3, blockSchema3, blockFieldSchema3, objectFieldSchema3, inlineGroupFieldSchema3, customFieldSchema3, knownFieldSchema3, fieldSchema3;
|
|
15326
15394
|
var init_field3 = __esm({
|
|
15327
15395
|
"dist/config/schemas/field.js"() {
|
|
15328
15396
|
"use strict";
|
|
@@ -15335,7 +15403,10 @@ var init_field3 = __esm({
|
|
|
15335
15403
|
required: z10.boolean().optional(),
|
|
15336
15404
|
list: z10.boolean().optional(),
|
|
15337
15405
|
isTitle: z10.boolean().optional(),
|
|
15338
|
-
isBody: z10.boolean().optional()
|
|
15406
|
+
isBody: z10.boolean().optional(),
|
|
15407
|
+
// Listed so a parse keeps it (zod strips unknown keys); which fields may carry it is
|
|
15408
|
+
// `ensureMaxItemsPlacement`'s call (config/validation.ts).
|
|
15409
|
+
maxItems: z10.number().int().positive().optional()
|
|
15339
15410
|
});
|
|
15340
15411
|
selectOptionSchema3 = z10.union([
|
|
15341
15412
|
z10.string(),
|
|
@@ -15393,6 +15464,7 @@ var init_field3 = __esm({
|
|
|
15393
15464
|
type: z10.literal("select"),
|
|
15394
15465
|
options: z10.array(selectOptionSchema3).min(1)
|
|
15395
15466
|
});
|
|
15467
|
+
referenceSortFieldSchema3 = z10.string().regex(/^[^.]+(\.[^.]+)*$/, 'must be a field name or a dotted path such as "meta.publishedAt"');
|
|
15396
15468
|
referenceFieldSchema3 = fieldBaseSchema3.extend({
|
|
15397
15469
|
type: z10.literal("reference"),
|
|
15398
15470
|
collections: z10.array(z10.string().min(1)).min(1).optional(),
|
|
@@ -15403,7 +15475,14 @@ var init_field3 = __esm({
|
|
|
15403
15475
|
// default, so a consumer that adopts this schema's parse output would silently delete a
|
|
15404
15476
|
// runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
|
|
15405
15477
|
// absent on purpose — it is type-inference-only and stripping it changes no behavior.)
|
|
15406
|
-
includeBody: z10.boolean().optional()
|
|
15478
|
+
includeBody: z10.boolean().optional(),
|
|
15479
|
+
optionSort: z10.union([
|
|
15480
|
+
z10.enum(["label", "collection"]),
|
|
15481
|
+
z10.object({
|
|
15482
|
+
field: referenceSortFieldSchema3,
|
|
15483
|
+
direction: z10.enum(["asc", "desc"]).optional()
|
|
15484
|
+
}).strict()
|
|
15485
|
+
]).optional()
|
|
15407
15486
|
});
|
|
15408
15487
|
fieldHolder3 = [z10.never()];
|
|
15409
15488
|
blockSchema3 = z10.object({
|
|
@@ -15637,7 +15716,7 @@ var init_deployment_name2 = __esm({
|
|
|
15637
15716
|
|
|
15638
15717
|
// dist/config/schemas/config.js
|
|
15639
15718
|
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;
|
|
15719
|
+
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
15720
|
var init_config4 = __esm({
|
|
15642
15721
|
"dist/config/schemas/config.js"() {
|
|
15643
15722
|
"use strict";
|
|
@@ -15684,6 +15763,19 @@ var init_config4 = __esm({
|
|
|
15684
15763
|
// Optional: provider sign-in UI for signed-out users (see EditorSignInProps)
|
|
15685
15764
|
SignInComponent: z14.custom().optional()
|
|
15686
15765
|
});
|
|
15766
|
+
seoFieldNameSchema3 = z14.string().min(1).optional();
|
|
15767
|
+
seoSchema3 = z14.object({
|
|
15768
|
+
group: z14.string().min(1).optional(),
|
|
15769
|
+
fields: z14.object({
|
|
15770
|
+
title: seoFieldNameSchema3,
|
|
15771
|
+
description: seoFieldNameSchema3,
|
|
15772
|
+
ogImage: seoFieldNameSchema3,
|
|
15773
|
+
ogType: seoFieldNameSchema3,
|
|
15774
|
+
canonical: seoFieldNameSchema3,
|
|
15775
|
+
noindex: seoFieldNameSchema3,
|
|
15776
|
+
twitterCard: seoFieldNameSchema3
|
|
15777
|
+
}).strict().optional()
|
|
15778
|
+
}).strict();
|
|
15687
15779
|
CanopyConfigSchema3 = z14.object({
|
|
15688
15780
|
media: mediaSchema3.optional(),
|
|
15689
15781
|
// No outer .optional(): both schemas already .default('deny'), so the field is optional on
|
|
@@ -15733,6 +15825,7 @@ var init_config4 = __esm({
|
|
|
15733
15825
|
entryLinkUrl: z14.custom().optional(),
|
|
15734
15826
|
validateEntry: z14.custom().optional(),
|
|
15735
15827
|
mdxAllow: mdxAllowlistSchema3.optional(),
|
|
15828
|
+
seo: seoSchema3.optional(),
|
|
15736
15829
|
dev: devConfigSchema3.optional()
|
|
15737
15830
|
}).strict();
|
|
15738
15831
|
DEFAULT_PROD_WORKSPACE3 = "/mnt/efs";
|
|
@@ -599,7 +599,7 @@ var init_mdx_allowlist = __esm({
|
|
|
599
599
|
|
|
600
600
|
// dist/config/schemas/field.js
|
|
601
601
|
import { z as z2 } from "zod";
|
|
602
|
-
var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, mdxPropAllowSchema, mdxAllowlistSchema, markdownFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
|
|
602
|
+
var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, mdxPropAllowSchema, mdxAllowlistSchema, markdownFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceSortFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
|
|
603
603
|
var init_field = __esm({
|
|
604
604
|
"dist/config/schemas/field.js"() {
|
|
605
605
|
"use strict";
|
|
@@ -612,7 +612,10 @@ var init_field = __esm({
|
|
|
612
612
|
required: z2.boolean().optional(),
|
|
613
613
|
list: z2.boolean().optional(),
|
|
614
614
|
isTitle: z2.boolean().optional(),
|
|
615
|
-
isBody: z2.boolean().optional()
|
|
615
|
+
isBody: z2.boolean().optional(),
|
|
616
|
+
// Listed so a parse keeps it (zod strips unknown keys); which fields may carry it is
|
|
617
|
+
// `ensureMaxItemsPlacement`'s call (config/validation.ts).
|
|
618
|
+
maxItems: z2.number().int().positive().optional()
|
|
616
619
|
});
|
|
617
620
|
selectOptionSchema = z2.union([
|
|
618
621
|
z2.string(),
|
|
@@ -670,6 +673,7 @@ var init_field = __esm({
|
|
|
670
673
|
type: z2.literal("select"),
|
|
671
674
|
options: z2.array(selectOptionSchema).min(1)
|
|
672
675
|
});
|
|
676
|
+
referenceSortFieldSchema = z2.string().regex(/^[^.]+(\.[^.]+)*$/, 'must be a field name or a dotted path such as "meta.publishedAt"');
|
|
673
677
|
referenceFieldSchema = fieldBaseSchema.extend({
|
|
674
678
|
type: z2.literal("reference"),
|
|
675
679
|
collections: z2.array(z2.string().min(1)).min(1).optional(),
|
|
@@ -680,7 +684,14 @@ var init_field = __esm({
|
|
|
680
684
|
// default, so a consumer that adopts this schema's parse output would silently delete a
|
|
681
685
|
// runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
|
|
682
686
|
// absent on purpose — it is type-inference-only and stripping it changes no behavior.)
|
|
683
|
-
includeBody: z2.boolean().optional()
|
|
687
|
+
includeBody: z2.boolean().optional(),
|
|
688
|
+
optionSort: z2.union([
|
|
689
|
+
z2.enum(["label", "collection"]),
|
|
690
|
+
z2.object({
|
|
691
|
+
field: referenceSortFieldSchema,
|
|
692
|
+
direction: z2.enum(["asc", "desc"]).optional()
|
|
693
|
+
}).strict()
|
|
694
|
+
]).optional()
|
|
684
695
|
});
|
|
685
696
|
fieldHolder = [z2.never()];
|
|
686
697
|
blockSchema = z2.object({
|
|
@@ -828,7 +839,7 @@ var init_media = __esm({
|
|
|
828
839
|
|
|
829
840
|
// dist/config/schemas/config.js
|
|
830
841
|
import { z as z6 } from "zod";
|
|
831
|
-
var defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE;
|
|
842
|
+
var 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;
|
|
832
843
|
var init_config = __esm({
|
|
833
844
|
"dist/config/schemas/config.js"() {
|
|
834
845
|
"use strict";
|
|
@@ -875,6 +886,19 @@ var init_config = __esm({
|
|
|
875
886
|
// Optional: provider sign-in UI for signed-out users (see EditorSignInProps)
|
|
876
887
|
SignInComponent: z6.custom().optional()
|
|
877
888
|
});
|
|
889
|
+
seoFieldNameSchema = z6.string().min(1).optional();
|
|
890
|
+
seoSchema = z6.object({
|
|
891
|
+
group: z6.string().min(1).optional(),
|
|
892
|
+
fields: z6.object({
|
|
893
|
+
title: seoFieldNameSchema,
|
|
894
|
+
description: seoFieldNameSchema,
|
|
895
|
+
ogImage: seoFieldNameSchema,
|
|
896
|
+
ogType: seoFieldNameSchema,
|
|
897
|
+
canonical: seoFieldNameSchema,
|
|
898
|
+
noindex: seoFieldNameSchema,
|
|
899
|
+
twitterCard: seoFieldNameSchema
|
|
900
|
+
}).strict().optional()
|
|
901
|
+
}).strict();
|
|
878
902
|
CanopyConfigSchema = z6.object({
|
|
879
903
|
media: mediaSchema.optional(),
|
|
880
904
|
// No outer .optional(): both schemas already .default('deny'), so the field is optional on
|
|
@@ -924,6 +948,7 @@ var init_config = __esm({
|
|
|
924
948
|
entryLinkUrl: z6.custom().optional(),
|
|
925
949
|
validateEntry: z6.custom().optional(),
|
|
926
950
|
mdxAllow: mdxAllowlistSchema.optional(),
|
|
951
|
+
seo: seoSchema.optional(),
|
|
927
952
|
dev: devConfigSchema.optional()
|
|
928
953
|
}).strict();
|
|
929
954
|
DEFAULT_PROD_WORKSPACE = "/mnt/efs";
|
|
@@ -2980,7 +3005,11 @@ var asTwitterCard = (value) => {
|
|
|
2980
3005
|
return text === "summary" || text === "summary_large_image" ? text : void 0;
|
|
2981
3006
|
};
|
|
2982
3007
|
function extractSeoFields(entryData, opts = {}) {
|
|
2983
|
-
const names = { ...DEFAULT_SEO_FIELD_NAMES
|
|
3008
|
+
const names = { ...DEFAULT_SEO_FIELD_NAMES };
|
|
3009
|
+
for (const [key, name] of Object.entries(opts.fields ?? {})) {
|
|
3010
|
+
if (typeof name === "string" && name !== "")
|
|
3011
|
+
names[key] = name;
|
|
3012
|
+
}
|
|
2984
3013
|
const entry = asRecord2(entryData);
|
|
2985
3014
|
const source = opts.group ? asRecord2(entry?.[opts.group]) : entry;
|
|
2986
3015
|
return {
|
|
@@ -6723,7 +6752,7 @@ function createReferenceRendering(idIndex, flatSchema, entryLinkUrl, markdownCop
|
|
|
6723
6752
|
|
|
6724
6753
|
// dist/ai/generate.js
|
|
6725
6754
|
async function generateAIContent(options) {
|
|
6726
|
-
const { store, flatSchema, contentRoot, config, entryLinkUrl, generatedAt, buildId } = options;
|
|
6755
|
+
const { store, flatSchema, contentRoot, config, entryLinkUrl, seo, generatedAt, buildId } = options;
|
|
6727
6756
|
assertValidNoindexConfig(config);
|
|
6728
6757
|
assertValidReferenceLink(config);
|
|
6729
6758
|
const files = /* @__PURE__ */ new Map();
|
|
@@ -6734,6 +6763,7 @@ async function generateAIContent(options) {
|
|
|
6734
6763
|
config,
|
|
6735
6764
|
idIndex,
|
|
6736
6765
|
entryLinkUrl,
|
|
6766
|
+
seo,
|
|
6737
6767
|
resolveTarget: createReferenceTargetResolver(store)
|
|
6738
6768
|
};
|
|
6739
6769
|
const collections = flatSchema.filter((item) => item.type === "collection");
|
|
@@ -6885,7 +6915,7 @@ async function collectCollection(read, collection, flatSchema) {
|
|
|
6885
6915
|
return { collection, cleanPath, entries, subcollections };
|
|
6886
6916
|
}
|
|
6887
6917
|
async function collectEntries(read, collection, cleanPath) {
|
|
6888
|
-
const { store, contentRoot, config, idIndex, entryLinkUrl, resolveTarget } = read;
|
|
6918
|
+
const { store, contentRoot, config, idIndex, entryLinkUrl, seo, resolveTarget } = read;
|
|
6889
6919
|
const skipNoindex = config?.exclude?.noindex !== false;
|
|
6890
6920
|
const pending = [];
|
|
6891
6921
|
const listed = await store.getCollectionEntryPaths(collection.logicalPath);
|
|
@@ -6903,7 +6933,7 @@ async function collectEntries(read, collection, cleanPath) {
|
|
|
6903
6933
|
const doc = await store.read(listEntry.collection, listEntry.slug, {
|
|
6904
6934
|
resolveReferences: false
|
|
6905
6935
|
});
|
|
6906
|
-
if (skipNoindex && isNoindexEntry(doc.data,
|
|
6936
|
+
if (skipNoindex && isNoindexEntry(doc.data, seo))
|
|
6907
6937
|
continue;
|
|
6908
6938
|
const storedData = doc.data;
|
|
6909
6939
|
doc.data = await resolveReferenceFields(doc.data, entryTypeConfig.schema, resolveTarget);
|
|
@@ -6970,24 +7000,8 @@ function assertValidNoindexConfig(config) {
|
|
|
6970
7000
|
if (noindex !== void 0 && typeof noindex !== "boolean") {
|
|
6971
7001
|
throw new Error(`AI content config: exclude.noindex must be a boolean, got ${typeof noindex}`);
|
|
6972
7002
|
}
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
return;
|
|
6976
|
-
if (typeof seo !== "object" || seo === null || Array.isArray(seo)) {
|
|
6977
|
-
throw new Error("AI content config: seo must be an object of { group?, fields? }");
|
|
6978
|
-
}
|
|
6979
|
-
const { group, fields } = seo;
|
|
6980
|
-
if (group !== void 0 && typeof group !== "string") {
|
|
6981
|
-
throw new Error("AI content config: seo.group must be a string");
|
|
6982
|
-
}
|
|
6983
|
-
if (fields === void 0)
|
|
6984
|
-
return;
|
|
6985
|
-
if (typeof fields !== "object" || fields === null || Array.isArray(fields) || Object.values(fields).some((name) => typeof name !== "string")) {
|
|
6986
|
-
throw new Error("AI content config: seo.fields must map SEO fields to field-name strings");
|
|
6987
|
-
}
|
|
6988
|
-
const unknown = Object.keys(fields).filter((key) => !(key in DEFAULT_SEO_FIELD_NAMES));
|
|
6989
|
-
if (unknown.length > 0) {
|
|
6990
|
-
throw new Error(`AI content config: unknown seo.fields key(s): ${unknown.join(", ")}`);
|
|
7003
|
+
if (config && "seo" in config) {
|
|
7004
|
+
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.");
|
|
6991
7005
|
}
|
|
6992
7006
|
}
|
|
6993
7007
|
function stripContentRoot(logicalPath, contentRoot) {
|
|
@@ -10138,6 +10152,14 @@ function validateEntryData(fields, data, pathPrefix = "", options = {}) {
|
|
|
10138
10152
|
}
|
|
10139
10153
|
if (value === void 0 || value === null)
|
|
10140
10154
|
continue;
|
|
10155
|
+
if (Array.isArray(value) && "maxItems" in field && typeof field.maxItems === "number" && value.length > field.maxItems) {
|
|
10156
|
+
const cap = field.maxItems;
|
|
10157
|
+
errors.push({
|
|
10158
|
+
fieldPath: path27,
|
|
10159
|
+
message: `At most ${cap} ${cap === 1 ? "item" : "items"}; this list has ${value.length}`,
|
|
10160
|
+
maxItems: { count: value.length, cap }
|
|
10161
|
+
});
|
|
10162
|
+
}
|
|
10141
10163
|
if (field.type === "block") {
|
|
10142
10164
|
const blockField = field;
|
|
10143
10165
|
if (!Array.isArray(value)) {
|
|
@@ -10238,14 +10260,34 @@ function assertBuildEntriesValid(items, phaseLabel) {
|
|
|
10238
10260
|
const invalid = findInvalidEntries(items);
|
|
10239
10261
|
if (invalid.length === 0)
|
|
10240
10262
|
return;
|
|
10241
|
-
const lines =
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10263
|
+
const lines = [];
|
|
10264
|
+
let hasOverCap = false;
|
|
10265
|
+
let hasOrdinary = false;
|
|
10266
|
+
for (const { entryPath, errors } of invalid) {
|
|
10267
|
+
const ordinary = errors.filter((e) => e.maxItems === void 0);
|
|
10268
|
+
if (ordinary.length > 0) {
|
|
10269
|
+
hasOrdinary = true;
|
|
10270
|
+
const [first, ...rest] = ordinary;
|
|
10271
|
+
const summary = rest.length > 0 ? `${first.fieldPath}: ${first.message} (+${rest.length} more)` : `${first.fieldPath}: ${first.message}`;
|
|
10272
|
+
lines.push(` - ${entryPath} \u2014 ${summary}`);
|
|
10273
|
+
}
|
|
10274
|
+
for (const { fieldPath, maxItems } of errors) {
|
|
10275
|
+
if (maxItems === void 0)
|
|
10276
|
+
continue;
|
|
10277
|
+
hasOverCap = true;
|
|
10278
|
+
lines.push(` - ${entryPath} \u2014 ${fieldPath}: ${maxItems.count} items, but maxItems is ${maxItems.cap}`);
|
|
10279
|
+
}
|
|
10280
|
+
}
|
|
10281
|
+
const advice = [];
|
|
10282
|
+
if (hasOverCap) {
|
|
10283
|
+
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.`);
|
|
10284
|
+
}
|
|
10285
|
+
if (hasOrdinary) {
|
|
10286
|
+
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.`);
|
|
10287
|
+
}
|
|
10246
10288
|
throw new Error(`CanopyCMS static build: found ${invalid.length} schema-invalid ${invalid.length === 1 ? "entry" : "entries"} during ${phaseLabel}:
|
|
10247
10289
|
${lines.join("\n")}
|
|
10248
|
-
|
|
10290
|
+
` + advice.join("\n"));
|
|
10249
10291
|
}
|
|
10250
10292
|
|
|
10251
10293
|
// dist/build/generate-ai-content.js
|
|
@@ -10342,6 +10384,7 @@ ${cached.issues.map((issue) => ` - ${issue.message}`).join("\n")}`);
|
|
|
10342
10384
|
contentRoot: contentRootName,
|
|
10343
10385
|
config: aiConfig,
|
|
10344
10386
|
entryLinkUrl: config.entryLinkUrl,
|
|
10387
|
+
seo: config.seo,
|
|
10345
10388
|
...resolveBuildStamp()
|
|
10346
10389
|
});
|
|
10347
10390
|
const absoluteOutputDir = path25.resolve(outputDir) + path25.sep;
|