canopycms 0.3.3 → 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 +91 -41
- package/dist/ai/generate.d.ts +3 -0
- package/dist/ai/generate.js +23 -33
- package/dist/ai/handler.js +1 -0
- package/dist/ai/types.d.ts +6 -11
- 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 +176 -66
- package/dist/cli/generate-ai-content.js +122 -62
- 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 +47 -8
- package/dist/config/validation.d.ts +6 -0
- package/dist/config/validation.js +33 -0
- package/dist/content-store.d.ts +5 -4
- package/dist/content-store.js +12 -7
- 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 +21 -9
- package/dist/entry-schema.js +5 -2
- 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/utils/body-field.d.ts +9 -1
- package/dist/utils/body-field.js +18 -1
- 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
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { ContentStore } from '../content-store.js';
|
|
3
3
|
import { defineEndpoint } from './route-builder.js';
|
|
4
4
|
import { ReferenceResolver } from '../reference-resolver.js';
|
|
5
|
+
import { referenceSortFieldSchema } from '../config/schemas/field.js';
|
|
5
6
|
import { parseLogicalPath } from '../paths/index.js';
|
|
6
7
|
import { branchNameSchema } from './validators.js';
|
|
7
8
|
const getReferenceOptionsParamsSchema = z.object({
|
|
@@ -16,9 +17,18 @@ const getReferenceOptionsHandler = async (gc, ctx, req, _params) => {
|
|
|
16
17
|
entryTypes: z.string().optional(),
|
|
17
18
|
displayField: z.string().optional(),
|
|
18
19
|
search: z.string().optional(),
|
|
20
|
+
sort: z.enum(['label', 'collection', 'field']).optional(),
|
|
21
|
+
sortField: referenceSortFieldSchema.optional(),
|
|
22
|
+
sortDir: z.enum(['asc', 'desc']).optional(),
|
|
19
23
|
})
|
|
20
24
|
.refine((data) => data.collections || data.entryTypes, {
|
|
21
25
|
message: 'At least one of "collections" or "entryTypes" query parameters is required',
|
|
26
|
+
})
|
|
27
|
+
.refine((data) => (data.sort === 'field') === (data.sortField !== undefined), {
|
|
28
|
+
message: '"sortField" is required with sort=field, and only allowed with it',
|
|
29
|
+
})
|
|
30
|
+
.refine((data) => data.sortDir === undefined || data.sort === 'field', {
|
|
31
|
+
message: '"sortDir" is only allowed with sort=field',
|
|
22
32
|
});
|
|
23
33
|
const queryResult = querySchema.safeParse(req.query ?? {});
|
|
24
34
|
if (!queryResult.success) {
|
|
@@ -32,6 +42,13 @@ const getReferenceOptionsHandler = async (gc, ctx, req, _params) => {
|
|
|
32
42
|
const entryTypesParam = queryResult.data.entryTypes;
|
|
33
43
|
const displayField = queryResult.data.displayField || 'title';
|
|
34
44
|
const search = queryResult.data.search;
|
|
45
|
+
// The refinements above pair sortField with sort=field, so sortField alone decides.
|
|
46
|
+
const { sort: sortParam, sortField, sortDir } = queryResult.data;
|
|
47
|
+
const sort = sortField !== undefined
|
|
48
|
+
? { field: sortField, direction: sortDir }
|
|
49
|
+
: sortParam === 'field'
|
|
50
|
+
? undefined
|
|
51
|
+
: sortParam;
|
|
35
52
|
const flatSchema = branchContext.flatSchema;
|
|
36
53
|
const store = new ContentStore(branchContext.branchRoot, flatSchema, {
|
|
37
54
|
contentRootName: ctx.services.config.contentRoot || 'content',
|
|
@@ -70,7 +87,7 @@ const getReferenceOptionsHandler = async (gc, ctx, req, _params) => {
|
|
|
70
87
|
// for an option the caller isn't allowed to see.
|
|
71
88
|
const checkAccess = await ctx.services.createContentAccessChecker(branchContext, branchContext.branchRoot, req.user);
|
|
72
89
|
const resolver = new ReferenceResolver(store, idIndex);
|
|
73
|
-
const options = await resolver.loadReferenceOptions(collections, displayField, search, entryTypes, (logicalPath) => checkAccess(logicalPath, 'read').allowed);
|
|
90
|
+
const options = await resolver.loadReferenceOptions(collections, displayField, search, entryTypes, (logicalPath) => checkAccess(logicalPath, 'read').allowed, { seo: ctx.services.config.seo, sort });
|
|
74
91
|
return {
|
|
75
92
|
ok: true,
|
|
76
93
|
status: 200,
|
|
@@ -81,7 +98,9 @@ const getReferenceOptionsHandler = async (gc, ctx, req, _params) => {
|
|
|
81
98
|
* Get reference options for a field
|
|
82
99
|
* GET /:branch/reference-options
|
|
83
100
|
* Query params: collections (comma-separated, optional), entryTypes (comma-separated, optional),
|
|
84
|
-
* displayField, search. At least one of collections or entryTypes is required.
|
|
101
|
+
* displayField, search. At least one of collections or entryTypes is required. `sort` is
|
|
102
|
+
* label (default), collection or field; field takes `sortField` (a dotted path) and `sortDir`
|
|
103
|
+
* (asc, the default, or desc). See `ReferenceOptionSort`.
|
|
85
104
|
*/
|
|
86
105
|
const getReferenceOptions = defineEndpoint({
|
|
87
106
|
namespace: 'content',
|
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";
|
|
@@ -4552,16 +4577,30 @@ function flattenGroupFields(fields) {
|
|
|
4552
4577
|
return result;
|
|
4553
4578
|
}
|
|
4554
4579
|
function findBodyFieldName(fields) {
|
|
4580
|
+
return findExplicitBodyFieldName(fields) ?? "body";
|
|
4581
|
+
}
|
|
4582
|
+
function findExplicitBodyFieldName(fields) {
|
|
4555
4583
|
for (const field of flattenGroupFields(fields)) {
|
|
4556
4584
|
if ("isBody" in field && field.isBody)
|
|
4557
4585
|
return field.name;
|
|
4558
4586
|
}
|
|
4559
|
-
return
|
|
4587
|
+
return void 0;
|
|
4588
|
+
}
|
|
4589
|
+
function linkOnlyReferenceData(data, format, fields) {
|
|
4590
|
+
const bodyFieldName = isDataOnlyFormat(format) && fields ? findExplicitBodyFieldName(fields) : void 0;
|
|
4591
|
+
if (bodyFieldName === void 0 || !(bodyFieldName in data))
|
|
4592
|
+
return data;
|
|
4593
|
+
const { [bodyFieldName]: _body, ...rest } = data;
|
|
4594
|
+
return rest;
|
|
4560
4595
|
}
|
|
4561
4596
|
function extractSeoFields(entryData, opts = {}) {
|
|
4562
|
-
const names = { ...DEFAULT_SEO_FIELD_NAMES
|
|
4563
|
-
const
|
|
4564
|
-
|
|
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
|
+
}
|
|
4602
|
+
const entry = asRecord2(entryData);
|
|
4603
|
+
const source = opts.group ? asRecord2(entry?.[opts.group]) : entry;
|
|
4565
4604
|
return {
|
|
4566
4605
|
title: asText(source?.[names.title]) ?? asText(opts.fallbackTitle),
|
|
4567
4606
|
description: asText(source?.[names.description]) ?? asText(opts.fallbackDescription),
|
|
@@ -6244,7 +6283,7 @@ function createReferenceRendering(idIndex, flatSchema, entryLinkUrl, markdownCop
|
|
|
6244
6283
|
};
|
|
6245
6284
|
}
|
|
6246
6285
|
async function generateAIContent(options) {
|
|
6247
|
-
const { store, flatSchema, contentRoot, config, entryLinkUrl, generatedAt, buildId } = options;
|
|
6286
|
+
const { store, flatSchema, contentRoot, config, entryLinkUrl, seo, generatedAt, buildId } = options;
|
|
6248
6287
|
assertValidNoindexConfig(config);
|
|
6249
6288
|
assertValidReferenceLink(config);
|
|
6250
6289
|
const files = /* @__PURE__ */ new Map();
|
|
@@ -6255,6 +6294,7 @@ async function generateAIContent(options) {
|
|
|
6255
6294
|
config,
|
|
6256
6295
|
idIndex,
|
|
6257
6296
|
entryLinkUrl,
|
|
6297
|
+
seo,
|
|
6258
6298
|
resolveTarget: createReferenceTargetResolver(store)
|
|
6259
6299
|
};
|
|
6260
6300
|
const collections = flatSchema.filter((item) => item.type === "collection");
|
|
@@ -6285,14 +6325,18 @@ async function generateAIContent(options) {
|
|
|
6285
6325
|
const pending = pendingById.get(id);
|
|
6286
6326
|
if (!pending || !exported.has(id))
|
|
6287
6327
|
return void 0;
|
|
6288
|
-
const {
|
|
6289
|
-
const
|
|
6328
|
+
const { format } = pending;
|
|
6329
|
+
const { slug, fields } = pending.entry;
|
|
6330
|
+
const storedData = structuredClone(pending.storedData);
|
|
6331
|
+
const target = buildResolvedReference(linkOnlyReferenceData(storedData, format, fields), {
|
|
6290
6332
|
id,
|
|
6291
6333
|
slug,
|
|
6292
6334
|
collection: pending.collectionPath,
|
|
6293
6335
|
urlPath: computeEntryUrl(pending.collectionPath, slug, contentRoot)
|
|
6294
6336
|
});
|
|
6295
|
-
|
|
6337
|
+
const dataBodyField = isDataOnlyFormat(format) ? findExplicitBodyFieldName(fields) : void 0;
|
|
6338
|
+
const dataBody = dataBodyField === void 0 ? void 0 : storedData[dataBodyField];
|
|
6339
|
+
return { target, body: pending.body ?? (typeof dataBody === "string" ? dataBody : void 0) };
|
|
6296
6340
|
};
|
|
6297
6341
|
const linkFor = config?.referenceLink ? await decideReferenceLinks(config.referenceLink, allPending, subjectOf, entryLinkUrl) : void 0;
|
|
6298
6342
|
const bodyLinkUrl = linkFor ? (location) => {
|
|
@@ -6402,7 +6446,7 @@ async function collectCollection(read, collection, flatSchema) {
|
|
|
6402
6446
|
return { collection, cleanPath, entries, subcollections };
|
|
6403
6447
|
}
|
|
6404
6448
|
async function collectEntries(read, collection, cleanPath) {
|
|
6405
|
-
const { store, contentRoot, config, idIndex, entryLinkUrl, resolveTarget: resolveTarget2 } = read;
|
|
6449
|
+
const { store, contentRoot, config, idIndex, entryLinkUrl, seo, resolveTarget: resolveTarget2 } = read;
|
|
6406
6450
|
const skipNoindex = config?.exclude?.noindex !== false;
|
|
6407
6451
|
const pending = [];
|
|
6408
6452
|
const listed = await store.getCollectionEntryPaths(collection.logicalPath);
|
|
@@ -6420,7 +6464,7 @@ async function collectEntries(read, collection, cleanPath) {
|
|
|
6420
6464
|
const doc = await store.read(listEntry.collection, listEntry.slug, {
|
|
6421
6465
|
resolveReferences: false
|
|
6422
6466
|
});
|
|
6423
|
-
if (skipNoindex && isNoindexEntry(doc.data,
|
|
6467
|
+
if (skipNoindex && isNoindexEntry(doc.data, seo))
|
|
6424
6468
|
continue;
|
|
6425
6469
|
const storedData = doc.data;
|
|
6426
6470
|
doc.data = await resolveReferenceFields(doc.data, entryTypeConfig.schema, resolveTarget2);
|
|
@@ -6434,6 +6478,7 @@ async function collectEntries(read, collection, cleanPath) {
|
|
|
6434
6478
|
entry: aiEntry,
|
|
6435
6479
|
body,
|
|
6436
6480
|
storedData,
|
|
6481
|
+
format: doc.format,
|
|
6437
6482
|
linkedIds: [],
|
|
6438
6483
|
contentId: extractIdFromFilename(path13.basename(listEntry.relativePath)),
|
|
6439
6484
|
absolutePath: doc.absolutePath,
|
|
@@ -6486,24 +6531,8 @@ function assertValidNoindexConfig(config) {
|
|
|
6486
6531
|
if (noindex !== void 0 && typeof noindex !== "boolean") {
|
|
6487
6532
|
throw new Error(`AI content config: exclude.noindex must be a boolean, got ${typeof noindex}`);
|
|
6488
6533
|
}
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
return;
|
|
6492
|
-
if (typeof seo !== "object" || seo === null || Array.isArray(seo)) {
|
|
6493
|
-
throw new Error("AI content config: seo must be an object of { group?, fields? }");
|
|
6494
|
-
}
|
|
6495
|
-
const { group, fields } = seo;
|
|
6496
|
-
if (group !== void 0 && typeof group !== "string") {
|
|
6497
|
-
throw new Error("AI content config: seo.group must be a string");
|
|
6498
|
-
}
|
|
6499
|
-
if (fields === void 0)
|
|
6500
|
-
return;
|
|
6501
|
-
if (typeof fields !== "object" || fields === null || Array.isArray(fields) || Object.values(fields).some((name) => typeof name !== "string")) {
|
|
6502
|
-
throw new Error("AI content config: seo.fields must map SEO fields to field-name strings");
|
|
6503
|
-
}
|
|
6504
|
-
const unknown = Object.keys(fields).filter((key) => !(key in DEFAULT_SEO_FIELD_NAMES));
|
|
6505
|
-
if (unknown.length > 0) {
|
|
6506
|
-
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.");
|
|
6507
6536
|
}
|
|
6508
6537
|
}
|
|
6509
6538
|
function stripContentRoot(logicalPath, contentRoot) {
|
|
@@ -7477,6 +7506,14 @@ function validateEntryData(fields, data, pathPrefix = "", options = {}) {
|
|
|
7477
7506
|
}
|
|
7478
7507
|
if (value === void 0 || value === null)
|
|
7479
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
|
+
}
|
|
7480
7517
|
if (field.type === "block") {
|
|
7481
7518
|
const blockField = field;
|
|
7482
7519
|
if (!Array.isArray(value)) {
|
|
@@ -7575,14 +7612,34 @@ function assertBuildEntriesValid(items, phaseLabel) {
|
|
|
7575
7612
|
const invalid = findInvalidEntries(items);
|
|
7576
7613
|
if (invalid.length === 0)
|
|
7577
7614
|
return;
|
|
7578
|
-
const lines =
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
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
|
+
}
|
|
7583
7640
|
throw new Error(`CanopyCMS static build: found ${invalid.length} schema-invalid ${invalid.length === 1 ? "entry" : "entries"} during ${phaseLabel}:
|
|
7584
7641
|
${lines.join("\n")}
|
|
7585
|
-
|
|
7642
|
+
` + advice.join("\n"));
|
|
7586
7643
|
}
|
|
7587
7644
|
async function readGeneratedRecord(outputDir) {
|
|
7588
7645
|
try {
|
|
@@ -7674,6 +7731,7 @@ ${cached.issues.map((issue) => ` - ${issue.message}`).join("\n")}`);
|
|
|
7674
7731
|
contentRoot: contentRootName,
|
|
7675
7732
|
config: aiConfig2,
|
|
7676
7733
|
entryLinkUrl: config.entryLinkUrl,
|
|
7734
|
+
seo: config.seo,
|
|
7677
7735
|
...resolveBuildStamp()
|
|
7678
7736
|
});
|
|
7679
7737
|
const absoluteOutputDir = path25.resolve(outputDir) + path25.sep;
|
|
@@ -7770,7 +7828,7 @@ async function generateAIContentCLI(options) {
|
|
|
7770
7828
|
console.log(` Output: ${result.outputDir}
|
|
7771
7829
|
`);
|
|
7772
7830
|
}
|
|
7773
|
-
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,
|
|
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;
|
|
7774
7832
|
var init_generate_ai_content = __esm({
|
|
7775
7833
|
"dist/cli/generate-ai-content.js"() {
|
|
7776
7834
|
"use strict";
|
|
@@ -8053,7 +8111,10 @@ var init_generate_ai_content = __esm({
|
|
|
8053
8111
|
required: z22.boolean().optional(),
|
|
8054
8112
|
list: z22.boolean().optional(),
|
|
8055
8113
|
isTitle: z22.boolean().optional(),
|
|
8056
|
-
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()
|
|
8057
8118
|
});
|
|
8058
8119
|
selectOptionSchema2 = z22.union([
|
|
8059
8120
|
z22.string(),
|
|
@@ -8111,6 +8172,7 @@ var init_generate_ai_content = __esm({
|
|
|
8111
8172
|
type: z22.literal("select"),
|
|
8112
8173
|
options: z22.array(selectOptionSchema2).min(1)
|
|
8113
8174
|
});
|
|
8175
|
+
referenceSortFieldSchema2 = z22.string().regex(/^[^.]+(\.[^.]+)*$/, 'must be a field name or a dotted path such as "meta.publishedAt"');
|
|
8114
8176
|
referenceFieldSchema2 = fieldBaseSchema2.extend({
|
|
8115
8177
|
type: z22.literal("reference"),
|
|
8116
8178
|
collections: z22.array(z22.string().min(1)).min(1).optional(),
|
|
@@ -8121,7 +8183,14 @@ var init_generate_ai_content = __esm({
|
|
|
8121
8183
|
// default, so a consumer that adopts this schema's parse output would silently delete a
|
|
8122
8184
|
// runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
|
|
8123
8185
|
// absent on purpose — it is type-inference-only and stripping it changes no behavior.)
|
|
8124
|
-
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()
|
|
8125
8194
|
});
|
|
8126
8195
|
fieldHolder2 = [z22.never()];
|
|
8127
8196
|
blockSchema2 = z22.object({
|
|
@@ -8299,6 +8368,19 @@ var init_generate_ai_content = __esm({
|
|
|
8299
8368
|
// Optional: provider sign-in UI for signed-out users (see EditorSignInProps)
|
|
8300
8369
|
SignInComponent: z6.custom().optional()
|
|
8301
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();
|
|
8302
8384
|
CanopyConfigSchema2 = z6.object({
|
|
8303
8385
|
media: mediaSchema2.optional(),
|
|
8304
8386
|
// No outer .optional(): both schemas already .default('deny'), so the field is optional on
|
|
@@ -8348,6 +8430,7 @@ var init_generate_ai_content = __esm({
|
|
|
8348
8430
|
entryLinkUrl: z6.custom().optional(),
|
|
8349
8431
|
validateEntry: z6.custom().optional(),
|
|
8350
8432
|
mdxAllow: mdxAllowlistSchema2.optional(),
|
|
8433
|
+
seo: seoSchema2.optional(),
|
|
8351
8434
|
dev: devConfigSchema2.optional()
|
|
8352
8435
|
}).strict();
|
|
8353
8436
|
DEFAULT_PROD_WORKSPACE2 = "/mnt/efs";
|
|
@@ -9216,6 +9299,17 @@ var init_generate_ai_content = __esm({
|
|
|
9216
9299
|
});
|
|
9217
9300
|
CONTENT_WRITE_LOCK_STALE_MS = 3e4;
|
|
9218
9301
|
heldRoots = new AsyncLocalStorage();
|
|
9302
|
+
getFormatExtension = (format) => {
|
|
9303
|
+
if (format === "md")
|
|
9304
|
+
return ".md";
|
|
9305
|
+
if (format === "mdx")
|
|
9306
|
+
return ".mdx";
|
|
9307
|
+
if (format === "yaml")
|
|
9308
|
+
return ".yaml";
|
|
9309
|
+
return ".json";
|
|
9310
|
+
};
|
|
9311
|
+
isDataOnlyFormat = (format) => format === "json" || format === "yaml";
|
|
9312
|
+
asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
9219
9313
|
init_item_keys();
|
|
9220
9314
|
DEFAULT_SEO_FIELD_NAMES = {
|
|
9221
9315
|
title: "metaTitle",
|
|
@@ -9226,7 +9320,7 @@ var init_generate_ai_content = __esm({
|
|
|
9226
9320
|
noindex: "noindex",
|
|
9227
9321
|
twitterCard: "twitterCard"
|
|
9228
9322
|
};
|
|
9229
|
-
|
|
9323
|
+
asRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
9230
9324
|
asText = (value) => {
|
|
9231
9325
|
if (typeof value !== "string")
|
|
9232
9326
|
return void 0;
|
|
@@ -9238,7 +9332,7 @@ var init_generate_ai_content = __esm({
|
|
|
9238
9332
|
const text3 = asText(value);
|
|
9239
9333
|
if (text3)
|
|
9240
9334
|
return text3;
|
|
9241
|
-
return asText(
|
|
9335
|
+
return asText(asRecord2(value)?.src);
|
|
9242
9336
|
};
|
|
9243
9337
|
asOgType = (value) => {
|
|
9244
9338
|
const text3 = asText(value);
|
|
@@ -9939,17 +10033,6 @@ var init_generate_ai_content = __esm({
|
|
|
9939
10033
|
RESOURCE = "content-index";
|
|
9940
10034
|
init_error3();
|
|
9941
10035
|
init_error3();
|
|
9942
|
-
getFormatExtension = (format) => {
|
|
9943
|
-
if (format === "md")
|
|
9944
|
-
return ".md";
|
|
9945
|
-
if (format === "mdx")
|
|
9946
|
-
return ".mdx";
|
|
9947
|
-
if (format === "yaml")
|
|
9948
|
-
return ".yaml";
|
|
9949
|
-
return ".json";
|
|
9950
|
-
};
|
|
9951
|
-
isDataOnlyFormat = (format) => format === "json" || format === "yaml";
|
|
9952
|
-
asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
9953
10036
|
init_normalize();
|
|
9954
10037
|
init_operating_mode();
|
|
9955
10038
|
RESERVED_SETTINGS_BRANCH_PREFIX = "canopycms-settings-";
|
|
@@ -10448,7 +10531,7 @@ var init_generate_ai_content = __esm({
|
|
|
10448
10531
|
fields = entryTypeConfig?.schema || [];
|
|
10449
10532
|
}
|
|
10450
10533
|
if (format === "json") {
|
|
10451
|
-
const data =
|
|
10534
|
+
const data = asRecord(JSON.parse(raw));
|
|
10452
10535
|
doc = {
|
|
10453
10536
|
collection: schemaItem.logicalPath,
|
|
10454
10537
|
collectionName: schemaItem.name,
|
|
@@ -10458,7 +10541,7 @@ var init_generate_ai_content = __esm({
|
|
|
10458
10541
|
absolutePath
|
|
10459
10542
|
};
|
|
10460
10543
|
} else if (format === "yaml") {
|
|
10461
|
-
const data =
|
|
10544
|
+
const data = asRecord(yamlParse(raw));
|
|
10462
10545
|
doc = {
|
|
10463
10546
|
collection: schemaItem.logicalPath,
|
|
10464
10547
|
collectionName: schemaItem.name,
|
|
@@ -11061,10 +11144,11 @@ var init_generate_ai_content = __esm({
|
|
|
11061
11144
|
* Measured (2000 occurrences of one target, local disk, the friendliest case for NOT
|
|
11062
11145
|
* caching): cloning a snippet-sized target is ~63x cheaper than re-reading and parsing it, a
|
|
11063
11146
|
* 265KB JSON target ~0.8x -- a wash at the pathological end, never a blow-up, and on the
|
|
11064
|
-
* EFS/NFS this targets the syscall the memo removes dominates both.
|
|
11065
|
-
*
|
|
11066
|
-
*
|
|
11067
|
-
* cloned once per referencing
|
|
11147
|
+
* EFS/NFS this targets the syscall the memo removes dominates both. A target resolves
|
|
11148
|
+
* without its body (an md/mdx target to its frontmatter, a json/yaml one without its `isBody`
|
|
11149
|
+
* field) unless the field sets `includeBody`, which is why `includeBody` is opt-in per
|
|
11150
|
+
* field: an embedded body sits inside the memoized object and is cloned once per referencing
|
|
11151
|
+
* entry.
|
|
11068
11152
|
*/
|
|
11069
11153
|
resolveSingleReference(id, idIndex, includeBody, cache, access) {
|
|
11070
11154
|
if (!cache)
|
|
@@ -11159,7 +11243,8 @@ var init_generate_ai_content = __esm({
|
|
|
11159
11243
|
}));
|
|
11160
11244
|
}
|
|
11161
11245
|
const bodyForEmbed = includeBody && "body" in doc ? { fieldName: doc.bodyFieldName, value: doc.body } : void 0;
|
|
11162
|
-
|
|
11246
|
+
const data = includeBody ? doc.data : linkOnlyReferenceData(doc.data, doc.format, this.entryTypeOf(location.collection, location.relativePath)?.schema);
|
|
11247
|
+
return buildResolvedReference(data, meta, bodyForEmbed);
|
|
11163
11248
|
} catch (error) {
|
|
11164
11249
|
if (isNodeError3(error) && error.code === "ENOENT")
|
|
11165
11250
|
return STALE_LOOKUP;
|
|
@@ -13182,10 +13267,10 @@ var init_generate_ai_content = __esm({
|
|
|
13182
13267
|
try {
|
|
13183
13268
|
const raw = await fs20.readFile(filePath, "utf8");
|
|
13184
13269
|
if (format === "json") {
|
|
13185
|
-
return
|
|
13270
|
+
return asRecord(JSON.parse(raw));
|
|
13186
13271
|
}
|
|
13187
13272
|
if (format === "yaml") {
|
|
13188
|
-
return
|
|
13273
|
+
return asRecord(yamlParse2(raw));
|
|
13189
13274
|
}
|
|
13190
13275
|
const parsed = matter3(raw);
|
|
13191
13276
|
const data = { ...parsed.data ?? {} };
|
|
@@ -15305,7 +15390,7 @@ var init_mdx_allowlist3 = __esm({
|
|
|
15305
15390
|
|
|
15306
15391
|
// dist/config/schemas/field.js
|
|
15307
15392
|
import { z as z10 } from "zod";
|
|
15308
|
-
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;
|
|
15309
15394
|
var init_field3 = __esm({
|
|
15310
15395
|
"dist/config/schemas/field.js"() {
|
|
15311
15396
|
"use strict";
|
|
@@ -15318,7 +15403,10 @@ var init_field3 = __esm({
|
|
|
15318
15403
|
required: z10.boolean().optional(),
|
|
15319
15404
|
list: z10.boolean().optional(),
|
|
15320
15405
|
isTitle: z10.boolean().optional(),
|
|
15321
|
-
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()
|
|
15322
15410
|
});
|
|
15323
15411
|
selectOptionSchema3 = z10.union([
|
|
15324
15412
|
z10.string(),
|
|
@@ -15376,6 +15464,7 @@ var init_field3 = __esm({
|
|
|
15376
15464
|
type: z10.literal("select"),
|
|
15377
15465
|
options: z10.array(selectOptionSchema3).min(1)
|
|
15378
15466
|
});
|
|
15467
|
+
referenceSortFieldSchema3 = z10.string().regex(/^[^.]+(\.[^.]+)*$/, 'must be a field name or a dotted path such as "meta.publishedAt"');
|
|
15379
15468
|
referenceFieldSchema3 = fieldBaseSchema3.extend({
|
|
15380
15469
|
type: z10.literal("reference"),
|
|
15381
15470
|
collections: z10.array(z10.string().min(1)).min(1).optional(),
|
|
@@ -15386,7 +15475,14 @@ var init_field3 = __esm({
|
|
|
15386
15475
|
// default, so a consumer that adopts this schema's parse output would silently delete a
|
|
15387
15476
|
// runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
|
|
15388
15477
|
// absent on purpose — it is type-inference-only and stripping it changes no behavior.)
|
|
15389
|
-
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()
|
|
15390
15486
|
});
|
|
15391
15487
|
fieldHolder3 = [z10.never()];
|
|
15392
15488
|
blockSchema3 = z10.object({
|
|
@@ -15620,7 +15716,7 @@ var init_deployment_name2 = __esm({
|
|
|
15620
15716
|
|
|
15621
15717
|
// dist/config/schemas/config.js
|
|
15622
15718
|
import { z as z14 } from "zod";
|
|
15623
|
-
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;
|
|
15624
15720
|
var init_config4 = __esm({
|
|
15625
15721
|
"dist/config/schemas/config.js"() {
|
|
15626
15722
|
"use strict";
|
|
@@ -15667,6 +15763,19 @@ var init_config4 = __esm({
|
|
|
15667
15763
|
// Optional: provider sign-in UI for signed-out users (see EditorSignInProps)
|
|
15668
15764
|
SignInComponent: z14.custom().optional()
|
|
15669
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();
|
|
15670
15779
|
CanopyConfigSchema3 = z14.object({
|
|
15671
15780
|
media: mediaSchema3.optional(),
|
|
15672
15781
|
// No outer .optional(): both schemas already .default('deny'), so the field is optional on
|
|
@@ -15716,6 +15825,7 @@ var init_config4 = __esm({
|
|
|
15716
15825
|
entryLinkUrl: z14.custom().optional(),
|
|
15717
15826
|
validateEntry: z14.custom().optional(),
|
|
15718
15827
|
mdxAllow: mdxAllowlistSchema3.optional(),
|
|
15828
|
+
seo: seoSchema3.optional(),
|
|
15719
15829
|
dev: devConfigSchema3.optional()
|
|
15720
15830
|
}).strict();
|
|
15721
15831
|
DEFAULT_PROD_WORKSPACE3 = "/mnt/efs";
|