canopycms 0.8.3 → 0.9.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 +61 -47
- package/dist/ai/json-to-markdown.js +2 -0
- package/dist/api/branch-entry-changes.js +2 -4
- package/dist/cli/cli.js +83 -16
- package/dist/cli/generate-ai-content.js +82 -15
- package/dist/cli/init.js +1 -1
- package/dist/config/types.d.ts +4 -4
- package/dist/config/types.js +1 -1
- package/dist/config/validation.d.ts +9 -3
- package/dist/config/validation.js +30 -5
- package/dist/content-listing.js +2 -3
- package/dist/content-store.js +5 -5
- package/dist/editor/FormRenderer.js +5 -0
- package/dist/editor/components/EntryChangesDrawer.js +3 -2
- package/dist/editor/entry-diff.js +7 -1
- package/dist/editor/fields/DateField.d.ts +19 -0
- package/dist/editor/fields/DateField.js +53 -0
- package/dist/editor/fields/DateTimeField.d.ts +10 -0
- package/dist/editor/fields/DateTimeField.js +5 -15
- package/dist/editor/fields/list-item-title.js +2 -1
- package/dist/editor/theme.js +12 -4
- package/dist/entry-schema-registry.js +2 -1
- package/dist/static/index.js +5 -2
- package/dist/utils/calendar-day.d.ts +23 -0
- package/dist/utils/calendar-day.js +102 -0
- package/dist/utils/content-serialize.d.ts +4 -2
- package/dist/utils/content-serialize.js +8 -6
- package/dist/validation/entry-validator.js +7 -1
- package/dist/validation/field-traversal.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/cli/cli.js
CHANGED
|
@@ -1349,7 +1349,7 @@ var init_init = __esm({
|
|
|
1349
1349
|
init_types = __esm2({
|
|
1350
1350
|
"dist/config/types.js"() {
|
|
1351
1351
|
"use strict";
|
|
1352
|
-
scalarFieldTypes = ["string", "number", "boolean", "datetime", "code"];
|
|
1352
|
+
scalarFieldTypes = ["string", "number", "boolean", "date", "datetime", "code"];
|
|
1353
1353
|
markdownFieldTypes = ["markdown", "mdx"];
|
|
1354
1354
|
primitiveFieldTypes = [...scalarFieldTypes, ...markdownFieldTypes];
|
|
1355
1355
|
fieldTypes = [
|
|
@@ -3495,7 +3495,7 @@ async function atomicWriteFile2(filePath, content) {
|
|
|
3495
3495
|
}
|
|
3496
3496
|
}
|
|
3497
3497
|
function isPlainRecord(value) {
|
|
3498
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3498
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
3499
3499
|
}
|
|
3500
3500
|
function resolveBlockItem(blockField, item) {
|
|
3501
3501
|
const templateName = typeof item.template === "string" ? item.template : typeof item._type === "string" ? item._type : void 0;
|
|
@@ -4068,6 +4068,20 @@ async function writeWorkerStatus(taskDir, report) {
|
|
|
4068
4068
|
function isBlockStructuralKey(key) {
|
|
4069
4069
|
return BLOCK_STRUCTURAL_KEYS.has(key);
|
|
4070
4070
|
}
|
|
4071
|
+
function withCalendarDays(fields, data) {
|
|
4072
|
+
let out = data;
|
|
4073
|
+
for (const field of fields) {
|
|
4074
|
+
if (field.type === "group") {
|
|
4075
|
+
out = withCalendarDays(field.fields, out);
|
|
4076
|
+
continue;
|
|
4077
|
+
}
|
|
4078
|
+
const value = out[field.name];
|
|
4079
|
+
const next = valueWithDays(field, value);
|
|
4080
|
+
if (next !== value)
|
|
4081
|
+
out = { ...out, [field.name]: next };
|
|
4082
|
+
}
|
|
4083
|
+
return out;
|
|
4084
|
+
}
|
|
4071
4085
|
function createDebugLogger2(options) {
|
|
4072
4086
|
return new DebugLogger2(options);
|
|
4073
4087
|
}
|
|
@@ -4711,9 +4725,9 @@ function asJson(value) {
|
|
|
4711
4725
|
const key = identityKey(value);
|
|
4712
4726
|
return key === void 0 || key === "undefined" ? void 0 : JSON.parse(key);
|
|
4713
4727
|
}
|
|
4714
|
-
function frontmatterAsRead(raw) {
|
|
4728
|
+
function frontmatterAsRead(raw, fields) {
|
|
4715
4729
|
try {
|
|
4716
|
-
return asJson(matter(raw, {}).data);
|
|
4730
|
+
return asJson(withCalendarDays(fields, matter(raw, {}).data));
|
|
4717
4731
|
} catch {
|
|
4718
4732
|
return void 0;
|
|
4719
4733
|
}
|
|
@@ -4781,7 +4795,7 @@ ${asKey ? `${text3}: v` : `v: ${text3}`}
|
|
|
4781
4795
|
return true;
|
|
4782
4796
|
}
|
|
4783
4797
|
}
|
|
4784
|
-
function serializeFrontmatter(editorBody, data, existingRaw, format) {
|
|
4798
|
+
function serializeFrontmatter(editorBody, data, existingRaw, format, fields = []) {
|
|
4785
4799
|
const file = { content: bodyToWrite(editorBody, data, format, existingRaw) };
|
|
4786
4800
|
if (existingRaw === void 0)
|
|
4787
4801
|
return matter.stringify(file, data);
|
|
@@ -4789,7 +4803,7 @@ function serializeFrontmatter(editorBody, data, existingRaw, format) {
|
|
|
4789
4803
|
if (existingFrontmatter === void 0)
|
|
4790
4804
|
return matter.stringify(file, data);
|
|
4791
4805
|
const onDisk = parseDocument2(existingFrontmatter);
|
|
4792
|
-
const toReconcile = onDisk.errors.length === 0 ? withUnchangedAsOnDisk(data, frontmatterAsRead(existingRaw), onDisk.toJS()) : data;
|
|
4806
|
+
const toReconcile = onDisk.errors.length === 0 ? withUnchangedAsOnDisk(data, frontmatterAsRead(existingRaw, fields), onDisk.toJS()) : data;
|
|
4793
4807
|
const reconciled = reconcileYamlSource(existingFrontmatter, isPlainRecord2(toReconcile) ? toReconcile : data, { rootAtColumnZero: true, quote: readsAsAnotherValue });
|
|
4794
4808
|
if (reconciled === void 0)
|
|
4795
4809
|
return matter.stringify(file, data);
|
|
@@ -4801,7 +4815,7 @@ function serializeFrontmatter(editorBody, data, existingRaw, format) {
|
|
|
4801
4815
|
}
|
|
4802
4816
|
}
|
|
4803
4817
|
});
|
|
4804
|
-
if (isDeepStrictEqual2(frontmatterAsRead(written), asJson(data)))
|
|
4818
|
+
if (isDeepStrictEqual2(frontmatterAsRead(written, fields), asJson(data)))
|
|
4805
4819
|
return written;
|
|
4806
4820
|
log22.debug("content-serialize", "reconciled frontmatter reads back differently; writing as gray-matter prints it");
|
|
4807
4821
|
return matter.stringify(file, data);
|
|
@@ -6131,6 +6145,7 @@ _${field.description}_` : "";
|
|
|
6131
6145
|
switch (field.type) {
|
|
6132
6146
|
case "string":
|
|
6133
6147
|
case "number":
|
|
6148
|
+
case "date":
|
|
6134
6149
|
case "datetime":
|
|
6135
6150
|
return `${heading} ${label}${descriptionLine}
|
|
6136
6151
|
|
|
@@ -8054,6 +8069,11 @@ function validateScalar(field, value, path272) {
|
|
|
8054
8069
|
return [{ fieldPath: path272, message: "Expected true or false" }];
|
|
8055
8070
|
}
|
|
8056
8071
|
return [];
|
|
8072
|
+
case "date":
|
|
8073
|
+
if (value !== "" && !isCalendarDay(value)) {
|
|
8074
|
+
return [{ fieldPath: path272, message: "Expected a real date, written YYYY-MM-DD" }];
|
|
8075
|
+
}
|
|
8076
|
+
return [];
|
|
8057
8077
|
case "datetime":
|
|
8058
8078
|
if (typeof value !== "string" || value !== "" && Number.isNaN(Date.parse(value))) {
|
|
8059
8079
|
return [{ fieldPath: path272, message: "Expected a valid date/time string" }];
|
|
@@ -8206,7 +8226,7 @@ function findInvalidEntries(items) {
|
|
|
8206
8226
|
for (const item of items) {
|
|
8207
8227
|
if (!item.schema)
|
|
8208
8228
|
continue;
|
|
8209
|
-
const data = normalizeDatesDeep(item.data);
|
|
8229
|
+
const data = normalizeDatesDeep(withCalendarDays(item.schema, item.data));
|
|
8210
8230
|
const errors = validateEntryData(item.schema, data);
|
|
8211
8231
|
if (errors.length > 0) {
|
|
8212
8232
|
invalid.push({ entryPath: item.entryPath, errors });
|
|
@@ -8434,7 +8454,7 @@ async function generateAIContentCLI(options) {
|
|
|
8434
8454
|
console.log(` Output: ${result.outputDir}
|
|
8435
8455
|
`);
|
|
8436
8456
|
}
|
|
8437
|
-
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, RESERVED_SETTINGS_BRANCH_PREFIX2, init_branch_name, BASE58_PATTERN2, CONTENT_ID_PATTERN2, PHYSICAL_SEGMENT_PATTERN2, MAX_BRANCH_NAME_LENGTH2, init_validation, 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, editorUrlSchema2, 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, warned22, init_mode_env, forEachReferenceField, init_validation2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned3, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, warned4, DEPLOYMENT_PLACEHOLDER2, DEFAULT_BRANCH_NAMESPACE_TEMPLATE2, BRANCH_NAMESPACE_ENV2, init_branch_namespace, 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, MARKDOWN_EXPORT_OPTIONS, BULLETS, DELIMITERS, EMPHASIS, STRONG, RULE, WORD_CHAR, GAP, Restyler, 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, warned5, SchemaUnavailableError, isValidId, parseTypedFilename, EMPTY_LOGICAL_PATH, ENTRY_EXTENSIONS, ContentIdIndex, 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, SNAPSHOT_MEMO_MAX_BYTES, SCHEMA_CACHE_FILE, SCHEMA_GENERATION_RESOURCE, reportedIssues, BranchSchemaCache, RESOURCE, 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, GitProcessError, STDERR_CAP_BYTES, KILL_GRACE_MS, log9, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, GIT_NETWORK_ENV_PASSTHROUGH, NO_AUTO_GC_CONFIG, HASH_INACTIVITY_TIMEOUT_MS, MAX_HASH_RUNS, LS_REMOTE_INACTIVITY_TIMEOUT_MS, symlinkBlobSha, REMOTE_GIT_CONFIG, REMOTE_GIT_MAX_LOOSE_OBJECTS, REMOTE_GIT_MAX_PACKS, PARALLEL_CHECKOUT_ARGS, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, BaseBranchMissingError, 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, AI_USER_ID, log10, normalizePath, parseEntryParts, parseEntryData, 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;
|
|
8457
|
+
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, RESERVED_SETTINGS_BRANCH_PREFIX2, init_branch_name, BASE58_PATTERN2, CONTENT_ID_PATTERN2, PHYSICAL_SEGMENT_PATTERN2, MAX_BRANCH_NAME_LENGTH2, init_validation, 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, editorUrlSchema2, 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, warned22, init_mode_env, forEachReferenceField, init_validation2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned3, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, warned4, DEPLOYMENT_PLACEHOLDER2, DEFAULT_BRANCH_NAMESPACE_TEMPLATE2, BRANCH_NAMESPACE_ENV2, init_branch_namespace, 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, DAY, MS_PER_DAY, isRealDay, dayParts, isCalendarDay, dayOfBareDate, mapItems, blockItemWithDays, valueWithDays, entryAsRead, envFlag2, LOG_LEVELS2, DebugLogger2, testLogger2, MARKDOWN_EXPORT_OPTIONS, BULLETS, DELIMITERS, EMPHASIS, STRONG, RULE, WORD_CHAR, GAP, Restyler, 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, warned5, SchemaUnavailableError, isValidId, parseTypedFilename, EMPTY_LOGICAL_PATH, ENTRY_EXTENSIONS, ContentIdIndex, 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, SNAPSHOT_MEMO_MAX_BYTES, SCHEMA_CACHE_FILE, SCHEMA_GENERATION_RESOURCE, reportedIssues, BranchSchemaCache, RESOURCE, 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, GitProcessError, STDERR_CAP_BYTES, KILL_GRACE_MS, log9, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, GIT_NETWORK_ENV_PASSTHROUGH, NO_AUTO_GC_CONFIG, HASH_INACTIVITY_TIMEOUT_MS, MAX_HASH_RUNS, LS_REMOTE_INACTIVITY_TIMEOUT_MS, symlinkBlobSha, REMOTE_GIT_CONFIG, REMOTE_GIT_MAX_LOOSE_OBJECTS, REMOTE_GIT_MAX_PACKS, PARALLEL_CHECKOUT_ARGS, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, BaseBranchMissingError, 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, AI_USER_ID, log10, normalizePath, parseEntryParts, parseEntryData, 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;
|
|
8438
8458
|
var init_generate_ai_content = __esm({
|
|
8439
8459
|
"dist/cli/generate-ai-content.js"() {
|
|
8440
8460
|
"use strict";
|
|
@@ -8616,7 +8636,7 @@ var init_generate_ai_content = __esm({
|
|
|
8616
8636
|
init_types22 = __esm3({
|
|
8617
8637
|
"dist/config/types.js"() {
|
|
8618
8638
|
"use strict";
|
|
8619
|
-
scalarFieldTypes2 = ["string", "number", "boolean", "datetime", "code"];
|
|
8639
|
+
scalarFieldTypes2 = ["string", "number", "boolean", "date", "datetime", "code"];
|
|
8620
8640
|
markdownFieldTypes2 = ["markdown", "mdx"];
|
|
8621
8641
|
primitiveFieldTypes2 = [...scalarFieldTypes2, ...markdownFieldTypes2];
|
|
8622
8642
|
fieldTypes2 = [
|
|
@@ -9350,6 +9370,54 @@ var init_generate_ai_content = __esm({
|
|
|
9350
9370
|
BLOCK_DISCRIMINATOR_KEYS = ["template", "_type"];
|
|
9351
9371
|
BLOCK_STRUCTURAL_KEYS = new Set(BLOCK_DISCRIMINATOR_KEYS);
|
|
9352
9372
|
init_item_keys();
|
|
9373
|
+
init_item_keys();
|
|
9374
|
+
init_field_traversal();
|
|
9375
|
+
DAY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
9376
|
+
MS_PER_DAY = 864e5;
|
|
9377
|
+
isRealDay = (year, month, day) => {
|
|
9378
|
+
const lastOfMonth = /* @__PURE__ */ new Date(0);
|
|
9379
|
+
lastOfMonth.setUTCFullYear(year, month, 0);
|
|
9380
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= lastOfMonth.getUTCDate();
|
|
9381
|
+
};
|
|
9382
|
+
dayParts = (value) => {
|
|
9383
|
+
const match = typeof value === "string" ? DAY.exec(value) : null;
|
|
9384
|
+
if (!match)
|
|
9385
|
+
return void 0;
|
|
9386
|
+
const [year, month, day] = match.slice(1).map(Number);
|
|
9387
|
+
return isRealDay(year, month, day) ? [year, month, day] : void 0;
|
|
9388
|
+
};
|
|
9389
|
+
isCalendarDay = (value) => dayParts(value) !== void 0;
|
|
9390
|
+
dayOfBareDate = (value) => value.getTime() % MS_PER_DAY === 0 ? value.toISOString().slice(0, 10) : void 0;
|
|
9391
|
+
mapItems = (items, mapItem) => {
|
|
9392
|
+
const next = items.map((item) => isPlainRecord(item) ? mapItem(item) : item);
|
|
9393
|
+
return next.every((item, i) => item === items[i]) ? items : next;
|
|
9394
|
+
};
|
|
9395
|
+
blockItemWithDays = (field, item) => {
|
|
9396
|
+
const resolved = resolveBlockItem(field, item);
|
|
9397
|
+
if (!resolved)
|
|
9398
|
+
return item;
|
|
9399
|
+
const data = withCalendarDays(resolved.fields, resolved.data);
|
|
9400
|
+
if (data === resolved.data)
|
|
9401
|
+
return item;
|
|
9402
|
+
return resolved.data === item ? data : { ...item, value: data };
|
|
9403
|
+
};
|
|
9404
|
+
valueWithDays = (field, value) => {
|
|
9405
|
+
if (field.type === "date") {
|
|
9406
|
+
return value instanceof Date ? dayOfBareDate(value) ?? value : value;
|
|
9407
|
+
}
|
|
9408
|
+
if (field.type === "object") {
|
|
9409
|
+
const { fields, list } = field;
|
|
9410
|
+
if (list) {
|
|
9411
|
+
return Array.isArray(value) ? mapItems(value, (item) => withCalendarDays(fields ?? [], item)) : value;
|
|
9412
|
+
}
|
|
9413
|
+
return isPlainRecord(value) ? withCalendarDays(fields ?? [], value) : value;
|
|
9414
|
+
}
|
|
9415
|
+
if (field.type === "block" && Array.isArray(value)) {
|
|
9416
|
+
return mapItems(value, (item) => blockItemWithDays(field, item));
|
|
9417
|
+
}
|
|
9418
|
+
return value;
|
|
9419
|
+
};
|
|
9420
|
+
entryAsRead = (fields, data) => withItemKeys(fields, withCalendarDays(fields, data), "content");
|
|
9353
9421
|
envFlag2 = (name) => typeof process !== "undefined" && process.env[name] === "true";
|
|
9354
9422
|
LOG_LEVELS2 = {
|
|
9355
9423
|
DEBUG: 0,
|
|
@@ -10317,7 +10385,6 @@ var init_generate_ai_content = __esm({
|
|
|
10317
10385
|
};
|
|
10318
10386
|
isDataOnlyFormat = (format) => format === "json" || format === "yaml";
|
|
10319
10387
|
asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
10320
|
-
init_item_keys();
|
|
10321
10388
|
DEFAULT_SEO_FIELD_NAMES = {
|
|
10322
10389
|
title: "metaTitle",
|
|
10323
10390
|
description: "metaDescription",
|
|
@@ -11624,7 +11691,7 @@ var init_generate_ai_content = __esm({
|
|
|
11624
11691
|
absolutePath
|
|
11625
11692
|
};
|
|
11626
11693
|
}
|
|
11627
|
-
doc.data =
|
|
11694
|
+
doc.data = entryAsRead(fields, doc.data);
|
|
11628
11695
|
if (options.resolveReferences !== false) {
|
|
11629
11696
|
doc.data = await this.resolveReferencesInData(doc.data, fields, void 0, options.referenceAccess, {
|
|
11630
11697
|
entry: slug ? entryLogicalPath(schemaItem.logicalPath, slug) : schemaItem.logicalPath,
|
|
@@ -11728,7 +11795,7 @@ var init_generate_ai_content = __esm({
|
|
|
11728
11795
|
} else if (input.format === "yaml") {
|
|
11729
11796
|
content = serializeYaml(input.data ?? {}, existingRaw);
|
|
11730
11797
|
} else {
|
|
11731
|
-
content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw, input.format);
|
|
11798
|
+
content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw, input.format, fields);
|
|
11732
11799
|
}
|
|
11733
11800
|
await atomicWriteFile2(absolutePath, content);
|
|
11734
11801
|
const liveIndex = this._idIndex;
|
|
@@ -12324,6 +12391,7 @@ var init_generate_ai_content = __esm({
|
|
|
12324
12391
|
FLAT_CELL_TYPES = /* @__PURE__ */ new Set([
|
|
12325
12392
|
"string",
|
|
12326
12393
|
"number",
|
|
12394
|
+
"date",
|
|
12327
12395
|
"datetime",
|
|
12328
12396
|
"boolean",
|
|
12329
12397
|
"select",
|
|
@@ -14595,7 +14663,6 @@ var init_generate_ai_content = __esm({
|
|
|
14595
14663
|
};
|
|
14596
14664
|
AI_USER_ID = "canopycms-ai";
|
|
14597
14665
|
init_error3();
|
|
14598
|
-
init_item_keys();
|
|
14599
14666
|
log10 = createDebugLogger2({ prefix: "ContentListing" });
|
|
14600
14667
|
normalizePath = (root, target) => {
|
|
14601
14668
|
const result = validateAndNormalizePath(root, target);
|
|
@@ -14705,7 +14772,7 @@ var init_generate_ai_content = __esm({
|
|
|
14705
14772
|
fs20.stat(absolutePath),
|
|
14706
14773
|
readEntryData(absolutePath, format, bodyField)
|
|
14707
14774
|
]);
|
|
14708
|
-
const data =
|
|
14775
|
+
const data = entryAsRead(entryType?.schema ?? [], rawData);
|
|
14709
14776
|
const item = {
|
|
14710
14777
|
logicalPath: entryLogicalPath(collection.logicalPath, slug),
|
|
14711
14778
|
contentId,
|
|
@@ -16599,7 +16666,7 @@ var scalarFieldTypes3, markdownFieldTypes3, primitiveFieldTypes3, fieldTypes3;
|
|
|
16599
16666
|
var init_types3 = __esm({
|
|
16600
16667
|
"dist/config/types.js"() {
|
|
16601
16668
|
"use strict";
|
|
16602
|
-
scalarFieldTypes3 = ["string", "number", "boolean", "datetime", "code"];
|
|
16669
|
+
scalarFieldTypes3 = ["string", "number", "boolean", "date", "datetime", "code"];
|
|
16603
16670
|
markdownFieldTypes3 = ["markdown", "mdx"];
|
|
16604
16671
|
primitiveFieldTypes3 = [...scalarFieldTypes3, ...markdownFieldTypes3];
|
|
16605
16672
|
fieldTypes3 = [
|
|
@@ -68,7 +68,7 @@ var init_atomic_write = __esm({
|
|
|
68
68
|
|
|
69
69
|
// dist/validation/field-traversal.js
|
|
70
70
|
function isPlainRecord(value) {
|
|
71
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
71
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
72
72
|
}
|
|
73
73
|
function resolveBlockItem(blockField, item) {
|
|
74
74
|
const templateName = typeof item.template === "string" ? item.template : typeof item._type === "string" ? item._type : void 0;
|
|
@@ -573,7 +573,7 @@ var scalarFieldTypes, markdownFieldTypes, primitiveFieldTypes, fieldTypes;
|
|
|
573
573
|
var init_types2 = __esm({
|
|
574
574
|
"dist/config/types.js"() {
|
|
575
575
|
"use strict";
|
|
576
|
-
scalarFieldTypes = ["string", "number", "boolean", "datetime", "code"];
|
|
576
|
+
scalarFieldTypes = ["string", "number", "boolean", "date", "datetime", "code"];
|
|
577
577
|
markdownFieldTypes = ["markdown", "mdx"];
|
|
578
578
|
primitiveFieldTypes = [...scalarFieldTypes, ...markdownFieldTypes];
|
|
579
579
|
fieldTypes = [
|
|
@@ -1640,6 +1640,70 @@ function isBlockStructuralKey(key) {
|
|
|
1640
1640
|
// dist/utils/content-serialize.js
|
|
1641
1641
|
init_item_keys();
|
|
1642
1642
|
|
|
1643
|
+
// dist/utils/calendar-day.js
|
|
1644
|
+
init_item_keys();
|
|
1645
|
+
init_field_traversal();
|
|
1646
|
+
var DAY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
1647
|
+
var MS_PER_DAY = 864e5;
|
|
1648
|
+
var isRealDay = (year, month, day) => {
|
|
1649
|
+
const lastOfMonth = /* @__PURE__ */ new Date(0);
|
|
1650
|
+
lastOfMonth.setUTCFullYear(year, month, 0);
|
|
1651
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= lastOfMonth.getUTCDate();
|
|
1652
|
+
};
|
|
1653
|
+
var dayParts = (value) => {
|
|
1654
|
+
const match = typeof value === "string" ? DAY.exec(value) : null;
|
|
1655
|
+
if (!match)
|
|
1656
|
+
return void 0;
|
|
1657
|
+
const [year, month, day] = match.slice(1).map(Number);
|
|
1658
|
+
return isRealDay(year, month, day) ? [year, month, day] : void 0;
|
|
1659
|
+
};
|
|
1660
|
+
var isCalendarDay = (value) => dayParts(value) !== void 0;
|
|
1661
|
+
var dayOfBareDate = (value) => value.getTime() % MS_PER_DAY === 0 ? value.toISOString().slice(0, 10) : void 0;
|
|
1662
|
+
var mapItems = (items, mapItem) => {
|
|
1663
|
+
const next = items.map((item) => isPlainRecord(item) ? mapItem(item) : item);
|
|
1664
|
+
return next.every((item, i) => item === items[i]) ? items : next;
|
|
1665
|
+
};
|
|
1666
|
+
var blockItemWithDays = (field, item) => {
|
|
1667
|
+
const resolved = resolveBlockItem(field, item);
|
|
1668
|
+
if (!resolved)
|
|
1669
|
+
return item;
|
|
1670
|
+
const data = withCalendarDays(resolved.fields, resolved.data);
|
|
1671
|
+
if (data === resolved.data)
|
|
1672
|
+
return item;
|
|
1673
|
+
return resolved.data === item ? data : { ...item, value: data };
|
|
1674
|
+
};
|
|
1675
|
+
var valueWithDays = (field, value) => {
|
|
1676
|
+
if (field.type === "date") {
|
|
1677
|
+
return value instanceof Date ? dayOfBareDate(value) ?? value : value;
|
|
1678
|
+
}
|
|
1679
|
+
if (field.type === "object") {
|
|
1680
|
+
const { fields, list } = field;
|
|
1681
|
+
if (list) {
|
|
1682
|
+
return Array.isArray(value) ? mapItems(value, (item) => withCalendarDays(fields ?? [], item)) : value;
|
|
1683
|
+
}
|
|
1684
|
+
return isPlainRecord(value) ? withCalendarDays(fields ?? [], value) : value;
|
|
1685
|
+
}
|
|
1686
|
+
if (field.type === "block" && Array.isArray(value)) {
|
|
1687
|
+
return mapItems(value, (item) => blockItemWithDays(field, item));
|
|
1688
|
+
}
|
|
1689
|
+
return value;
|
|
1690
|
+
};
|
|
1691
|
+
function withCalendarDays(fields, data) {
|
|
1692
|
+
let out = data;
|
|
1693
|
+
for (const field of fields) {
|
|
1694
|
+
if (field.type === "group") {
|
|
1695
|
+
out = withCalendarDays(field.fields, out);
|
|
1696
|
+
continue;
|
|
1697
|
+
}
|
|
1698
|
+
const value = out[field.name];
|
|
1699
|
+
const next = valueWithDays(field, value);
|
|
1700
|
+
if (next !== value)
|
|
1701
|
+
out = { ...out, [field.name]: next };
|
|
1702
|
+
}
|
|
1703
|
+
return out;
|
|
1704
|
+
}
|
|
1705
|
+
var entryAsRead = (fields, data) => withItemKeys(fields, withCalendarDays(fields, data), "content");
|
|
1706
|
+
|
|
1643
1707
|
// dist/utils/debug.js
|
|
1644
1708
|
var envFlag = (name) => typeof process !== "undefined" && process.env[name] === "true";
|
|
1645
1709
|
var LOG_LEVELS = {
|
|
@@ -3235,9 +3299,9 @@ function asJson(value) {
|
|
|
3235
3299
|
const key = identityKey(value);
|
|
3236
3300
|
return key === void 0 || key === "undefined" ? void 0 : JSON.parse(key);
|
|
3237
3301
|
}
|
|
3238
|
-
function frontmatterAsRead(raw) {
|
|
3302
|
+
function frontmatterAsRead(raw, fields) {
|
|
3239
3303
|
try {
|
|
3240
|
-
return asJson(matter(raw, {}).data);
|
|
3304
|
+
return asJson(withCalendarDays(fields, matter(raw, {}).data));
|
|
3241
3305
|
} catch {
|
|
3242
3306
|
return void 0;
|
|
3243
3307
|
}
|
|
@@ -3305,7 +3369,7 @@ ${asKey ? `${text}: v` : `v: ${text}`}
|
|
|
3305
3369
|
return true;
|
|
3306
3370
|
}
|
|
3307
3371
|
}
|
|
3308
|
-
function serializeFrontmatter(editorBody, data, existingRaw, format) {
|
|
3372
|
+
function serializeFrontmatter(editorBody, data, existingRaw, format, fields = []) {
|
|
3309
3373
|
const file = { content: bodyToWrite(editorBody, data, format, existingRaw) };
|
|
3310
3374
|
if (existingRaw === void 0)
|
|
3311
3375
|
return matter.stringify(file, data);
|
|
@@ -3313,7 +3377,7 @@ function serializeFrontmatter(editorBody, data, existingRaw, format) {
|
|
|
3313
3377
|
if (existingFrontmatter === void 0)
|
|
3314
3378
|
return matter.stringify(file, data);
|
|
3315
3379
|
const onDisk = parseDocument2(existingFrontmatter);
|
|
3316
|
-
const toReconcile = onDisk.errors.length === 0 ? withUnchangedAsOnDisk(data, frontmatterAsRead(existingRaw), onDisk.toJS()) : data;
|
|
3380
|
+
const toReconcile = onDisk.errors.length === 0 ? withUnchangedAsOnDisk(data, frontmatterAsRead(existingRaw, fields), onDisk.toJS()) : data;
|
|
3317
3381
|
const reconciled = reconcileYamlSource(existingFrontmatter, isPlainRecord2(toReconcile) ? toReconcile : data, { rootAtColumnZero: true, quote: readsAsAnotherValue });
|
|
3318
3382
|
if (reconciled === void 0)
|
|
3319
3383
|
return matter.stringify(file, data);
|
|
@@ -3325,7 +3389,7 @@ function serializeFrontmatter(editorBody, data, existingRaw, format) {
|
|
|
3325
3389
|
}
|
|
3326
3390
|
}
|
|
3327
3391
|
});
|
|
3328
|
-
if (isDeepStrictEqual2(frontmatterAsRead(written), asJson(data)))
|
|
3392
|
+
if (isDeepStrictEqual2(frontmatterAsRead(written, fields), asJson(data)))
|
|
3329
3393
|
return written;
|
|
3330
3394
|
log2.debug("content-serialize", "reconciled frontmatter reads back differently; writing as gray-matter prints it");
|
|
3331
3395
|
return matter.stringify(file, data);
|
|
@@ -3639,9 +3703,6 @@ function linkOnlyReferenceData(data, format, fields) {
|
|
|
3639
3703
|
return rest;
|
|
3640
3704
|
}
|
|
3641
3705
|
|
|
3642
|
-
// dist/content-store.js
|
|
3643
|
-
init_item_keys();
|
|
3644
|
-
|
|
3645
3706
|
// dist/static/seo.js
|
|
3646
3707
|
var DEFAULT_SEO_FIELD_NAMES = {
|
|
3647
3708
|
title: "metaTitle",
|
|
@@ -5834,7 +5895,7 @@ var ContentStore = class {
|
|
|
5834
5895
|
absolutePath
|
|
5835
5896
|
};
|
|
5836
5897
|
}
|
|
5837
|
-
doc.data =
|
|
5898
|
+
doc.data = entryAsRead(fields, doc.data);
|
|
5838
5899
|
if (options.resolveReferences !== false) {
|
|
5839
5900
|
doc.data = await this.resolveReferencesInData(doc.data, fields, void 0, options.referenceAccess, {
|
|
5840
5901
|
entry: slug ? entryLogicalPath(schemaItem.logicalPath, slug) : schemaItem.logicalPath,
|
|
@@ -5938,7 +5999,7 @@ var ContentStore = class {
|
|
|
5938
5999
|
} else if (input.format === "yaml") {
|
|
5939
6000
|
content = serializeYaml(input.data ?? {}, existingRaw);
|
|
5940
6001
|
} else {
|
|
5941
|
-
content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw, input.format);
|
|
6002
|
+
content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw, input.format, fields);
|
|
5942
6003
|
}
|
|
5943
6004
|
await atomicWriteFile(absolutePath, content);
|
|
5944
6005
|
const liveIndex = this._idIndex;
|
|
@@ -6830,6 +6891,7 @@ _${field.description}_` : "";
|
|
|
6830
6891
|
switch (field.type) {
|
|
6831
6892
|
case "string":
|
|
6832
6893
|
case "number":
|
|
6894
|
+
case "date":
|
|
6833
6895
|
case "datetime":
|
|
6834
6896
|
return `${heading} ${label}${descriptionLine}
|
|
6835
6897
|
|
|
@@ -6908,6 +6970,7 @@ ${items.map((item) => `- ${item}`).join("\n")}`;
|
|
|
6908
6970
|
var FLAT_CELL_TYPES = /* @__PURE__ */ new Set([
|
|
6909
6971
|
"string",
|
|
6910
6972
|
"number",
|
|
6973
|
+
"date",
|
|
6911
6974
|
"datetime",
|
|
6912
6975
|
"boolean",
|
|
6913
6976
|
"select",
|
|
@@ -10970,7 +11033,6 @@ import path24 from "node:path";
|
|
|
10970
11033
|
import matter3 from "gray-matter";
|
|
10971
11034
|
import { parse as yamlParse2 } from "yaml";
|
|
10972
11035
|
init_error();
|
|
10973
|
-
init_item_keys();
|
|
10974
11036
|
var log10 = createDebugLogger({ prefix: "ContentListing" });
|
|
10975
11037
|
var normalizePath = (root, target) => {
|
|
10976
11038
|
const result = validateAndNormalizePath(root, target);
|
|
@@ -11141,7 +11203,7 @@ var listCollectionEntries = async (root, collection, onSkip, readsId) => {
|
|
|
11141
11203
|
fs20.stat(absolutePath),
|
|
11142
11204
|
readEntryData(absolutePath, format, bodyField)
|
|
11143
11205
|
]);
|
|
11144
|
-
const data =
|
|
11206
|
+
const data = entryAsRead(entryType?.schema ?? [], rawData);
|
|
11145
11207
|
const item = {
|
|
11146
11208
|
logicalPath: entryLogicalPath(collection.logicalPath, slug),
|
|
11147
11209
|
contentId,
|
|
@@ -11254,6 +11316,11 @@ function validateScalar(field, value, path27) {
|
|
|
11254
11316
|
return [{ fieldPath: path27, message: "Expected true or false" }];
|
|
11255
11317
|
}
|
|
11256
11318
|
return [];
|
|
11319
|
+
case "date":
|
|
11320
|
+
if (value !== "" && !isCalendarDay(value)) {
|
|
11321
|
+
return [{ fieldPath: path27, message: "Expected a real date, written YYYY-MM-DD" }];
|
|
11322
|
+
}
|
|
11323
|
+
return [];
|
|
11257
11324
|
case "datetime":
|
|
11258
11325
|
if (typeof value !== "string" || value !== "" && Number.isNaN(Date.parse(value))) {
|
|
11259
11326
|
return [{ fieldPath: path27, message: "Expected a valid date/time string" }];
|
|
@@ -11408,7 +11475,7 @@ function findInvalidEntries(items) {
|
|
|
11408
11475
|
for (const item of items) {
|
|
11409
11476
|
if (!item.schema)
|
|
11410
11477
|
continue;
|
|
11411
|
-
const data = normalizeDatesDeep(item.data);
|
|
11478
|
+
const data = normalizeDatesDeep(withCalendarDays(item.schema, item.data));
|
|
11412
11479
|
const errors = validateEntryData(item.schema, data);
|
|
11413
11480
|
if (errors.length > 0) {
|
|
11414
11481
|
invalid.push({ entryPath: item.entryPath, errors });
|
package/dist/cli/init.js
CHANGED
|
@@ -26,7 +26,7 @@ var scalarFieldTypes, markdownFieldTypes, primitiveFieldTypes, fieldTypes;
|
|
|
26
26
|
var init_types = __esm({
|
|
27
27
|
"dist/config/types.js"() {
|
|
28
28
|
"use strict";
|
|
29
|
-
scalarFieldTypes = ["string", "number", "boolean", "datetime", "code"];
|
|
29
|
+
scalarFieldTypes = ["string", "number", "boolean", "date", "datetime", "code"];
|
|
30
30
|
markdownFieldTypes = ["markdown", "mdx"];
|
|
31
31
|
primitiveFieldTypes = [...scalarFieldTypes, ...markdownFieldTypes];
|
|
32
32
|
fieldTypes = [
|
package/dist/config/types.d.ts
CHANGED
|
@@ -10,10 +10,10 @@ import type { PermissionPath } from '../authorization/types.js';
|
|
|
10
10
|
import type { EntryLinkUrlResolver } from '../entry-link-resolver.js';
|
|
11
11
|
import type { CropRect } from '../assets/transform-directives.js';
|
|
12
12
|
import type { SeoFieldLocation } from '../static/seo.js';
|
|
13
|
-
declare const scalarFieldTypes: readonly ["string", "number", "boolean", "datetime", "code"];
|
|
13
|
+
declare const scalarFieldTypes: readonly ["string", "number", "boolean", "date", "datetime", "code"];
|
|
14
14
|
export declare const markdownFieldTypes: readonly ["markdown", "mdx"];
|
|
15
|
-
export declare const primitiveFieldTypes: readonly ["string", "number", "boolean", "datetime", "code", "markdown", "mdx"];
|
|
16
|
-
export declare const fieldTypes: readonly ["string", "number", "boolean", "datetime", "code", "markdown", "mdx", "image", "select", "reference", "object", "block", "group"];
|
|
15
|
+
export declare const primitiveFieldTypes: readonly ["string", "number", "boolean", "date", "datetime", "code", "markdown", "mdx"];
|
|
16
|
+
export declare const fieldTypes: readonly ["string", "number", "boolean", "date", "datetime", "code", "markdown", "mdx", "image", "select", "reference", "object", "block", "group"];
|
|
17
17
|
export type PrimitiveFieldType = (typeof primitiveFieldTypes)[number];
|
|
18
18
|
export type FieldType = (typeof fieldTypes)[number];
|
|
19
19
|
export type ContentFormat = 'md' | 'mdx' | 'json' | 'yaml';
|
|
@@ -211,7 +211,7 @@ export interface ObjectFieldConfig extends BaseFieldConfig {
|
|
|
211
211
|
type: 'object';
|
|
212
212
|
fields: FieldConfig[];
|
|
213
213
|
/**
|
|
214
|
-
* On a `list: true` object, a direct `string` or `
|
|
214
|
+
* On a `list: true` object, a direct `string`, `number` or `date` child whose value titles each
|
|
215
215
|
* item's card; a blank or non-finite value falls back to the item's first non-blank `string`
|
|
216
216
|
* field, then "Item N".
|
|
217
217
|
*/
|
package/dist/config/types.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Type definitions for CanopyCMS configuration.
|
|
3
3
|
* These are pure TypeScript types - Zod schemas are in ./schemas/
|
|
4
4
|
*/
|
|
5
|
-
const scalarFieldTypes = ['string', 'number', 'boolean', 'datetime', 'code'];
|
|
5
|
+
const scalarFieldTypes = ['string', 'number', 'boolean', 'date', 'datetime', 'code'];
|
|
6
6
|
export const markdownFieldTypes = ['markdown', 'mdx'];
|
|
7
7
|
export const primitiveFieldTypes = [...scalarFieldTypes, ...markdownFieldTypes];
|
|
8
8
|
// 'image' is not a primitive: it has a structured value (ImageFieldValue) and
|
|
@@ -34,9 +34,9 @@ export declare const ensureReferenceFieldsHaveScope: (fields: unknown) => void;
|
|
|
34
34
|
export declare const forEachReferenceField: (fields: unknown, visit: (field: Record<string, unknown>) => void) => void;
|
|
35
35
|
/**
|
|
36
36
|
* Check that every `itemLabel` is non-blank text and, like every `itemTitleField`, sits on a
|
|
37
|
-
* `list: true` object, and that every `itemTitleField` names a direct child of type `string
|
|
38
|
-
* `number`, the only values the card heading shows as typed (a reference would show its
|
|
39
|
-
* select its stored value). Throws an error when it does not. Entry schemas reach the editor
|
|
37
|
+
* `list: true` object, and that every `itemTitleField` names a direct child of type `string`,
|
|
38
|
+
* `number` or `date`, the only values the card heading shows as typed (a reference would show its
|
|
39
|
+
* raw id, a select its stored value). Throws an error when it does not. Entry schemas reach the editor
|
|
40
40
|
* through these checks, not the zod field schema.
|
|
41
41
|
*/
|
|
42
42
|
export declare const ensureItemTitleFieldsExist: (fields: unknown) => void;
|
|
@@ -68,6 +68,12 @@ export declare const ensureStampOnPublishPlacement: (fields: unknown, scopeLabel
|
|
|
68
68
|
* `ensureStampOnPublishPlacement`. Throws on the first problem.
|
|
69
69
|
*/
|
|
70
70
|
export declare const ensureMaxItemsPlacement: (fields: unknown, scopeLabel?: string) => void;
|
|
71
|
+
/**
|
|
72
|
+
* Check that no `date` field is `list: true`: the editor has one picker for one day, and an object
|
|
73
|
+
* list with a `date` child gives a list of days its rows. Recurses into groups, objects and block
|
|
74
|
+
* templates like `ensureStampOnPublishPlacement`. Throws on the first problem.
|
|
75
|
+
*/
|
|
76
|
+
export declare const ensureSingleDateFields: (fields: unknown, scopeLabel?: string) => void;
|
|
71
77
|
/**
|
|
72
78
|
* Check every markdown and mdx field's `mdxAllow` against its schema and its `renderAs`,
|
|
73
79
|
* `mdxAllow` and `executable` against each other. Throws on the first problem.
|
|
@@ -110,9 +110,9 @@ export const forEachReferenceField = (fields, visit) => {
|
|
|
110
110
|
};
|
|
111
111
|
/**
|
|
112
112
|
* Check that every `itemLabel` is non-blank text and, like every `itemTitleField`, sits on a
|
|
113
|
-
* `list: true` object, and that every `itemTitleField` names a direct child of type `string
|
|
114
|
-
* `number`, the only values the card heading shows as typed (a reference would show its
|
|
115
|
-
* select its stored value). Throws an error when it does not. Entry schemas reach the editor
|
|
113
|
+
* `list: true` object, and that every `itemTitleField` names a direct child of type `string`,
|
|
114
|
+
* `number` or `date`, the only values the card heading shows as typed (a reference would show its
|
|
115
|
+
* raw id, a select its stored value). Throws an error when it does not. Entry schemas reach the editor
|
|
116
116
|
* through these checks, not the zod field schema.
|
|
117
117
|
*/
|
|
118
118
|
export const ensureItemTitleFieldsExist = (fields) => {
|
|
@@ -140,8 +140,8 @@ export const ensureItemTitleFieldsExist = (fields) => {
|
|
|
140
140
|
if (typeof f.itemTitleField !== 'string' || !child) {
|
|
141
141
|
throw new Error(`${label}, which is not one of its child fields`);
|
|
142
142
|
}
|
|
143
|
-
if (child.type !== 'string' && child.type !== 'number') {
|
|
144
|
-
throw new Error(`${label}, which must name a string or
|
|
143
|
+
if (child.type !== 'string' && child.type !== 'number' && child.type !== 'date') {
|
|
144
|
+
throw new Error(`${label}, which must name a string, number or date field`);
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
ensureItemTitleFieldsExist(children);
|
|
@@ -327,6 +327,31 @@ export const ensureMaxItemsPlacement = (fields, scopeLabel = 'entry schema') =>
|
|
|
327
327
|
};
|
|
328
328
|
check(fields);
|
|
329
329
|
};
|
|
330
|
+
/**
|
|
331
|
+
* Check that no `date` field is `list: true`: the editor has one picker for one day, and an object
|
|
332
|
+
* list with a `date` child gives a list of days its rows. Recurses into groups, objects and block
|
|
333
|
+
* templates like `ensureStampOnPublishPlacement`. Throws on the first problem.
|
|
334
|
+
*/
|
|
335
|
+
export const ensureSingleDateFields = (fields, scopeLabel = 'entry schema') => {
|
|
336
|
+
const check = (scopeFields) => {
|
|
337
|
+
if (!Array.isArray(scopeFields))
|
|
338
|
+
return;
|
|
339
|
+
for (const field of scopeFields) {
|
|
340
|
+
const f = field;
|
|
341
|
+
if (f?.type === 'date' && f.list === true) {
|
|
342
|
+
throw new Error(`${scopeLabel}: field "${String(f.name)}" is a date with list: true; use an object list with a date field inside`);
|
|
343
|
+
}
|
|
344
|
+
if (f?.type === 'group' || f?.type === 'object')
|
|
345
|
+
check(f.fields);
|
|
346
|
+
if (f?.type === 'block' && Array.isArray(f.templates)) {
|
|
347
|
+
for (const template of f.templates) {
|
|
348
|
+
check(template.fields);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
check(fields);
|
|
354
|
+
};
|
|
330
355
|
/**
|
|
331
356
|
* Check every markdown and mdx field's `mdxAllow` against its schema and its `renderAs`,
|
|
332
357
|
* `mdxAllow` and `executable` against each other. Throws on the first problem.
|
package/dist/content-listing.js
CHANGED
|
@@ -6,6 +6,7 @@ import path from 'node:path';
|
|
|
6
6
|
import matter from 'gray-matter';
|
|
7
7
|
import { parse as yamlParse } from 'yaml';
|
|
8
8
|
import { findBodyFieldName } from './utils/body-field.js';
|
|
9
|
+
import { entryAsRead } from './utils/calendar-day.js';
|
|
9
10
|
import { computeEntryUrl } from './utils/entry-url.js';
|
|
10
11
|
import { asRecord, getFormatExtension } from './utils/format.js';
|
|
11
12
|
import { resolveCollectionPath, toLogicalCollectionPath } from './content-id-index.js';
|
|
@@ -15,7 +16,6 @@ import { isNotFoundError, getErrorMessage } from './utils/error.js';
|
|
|
15
16
|
import { createDebugLogger } from './utils/debug.js';
|
|
16
17
|
import { ContentStore, ContentStoreError, createReferenceResolveCache, } from './content-store.js';
|
|
17
18
|
import { isBuildMode } from './build-mode.js';
|
|
18
|
-
import { withItemKeys } from './item-keys.js';
|
|
19
19
|
const log = createDebugLogger({ prefix: 'ContentListing' });
|
|
20
20
|
/** Validate and normalize `target` under `root`; throws ContentStoreError on a traversal. */
|
|
21
21
|
const normalizePath = (root, target) => {
|
|
@@ -444,8 +444,7 @@ export const listCollectionEntries = async (root, collection, onSkip, readsId) =
|
|
|
444
444
|
fs.stat(absolutePath),
|
|
445
445
|
readEntryData(absolutePath, format, bodyField),
|
|
446
446
|
]);
|
|
447
|
-
|
|
448
|
-
const data = withItemKeys(entryType?.schema ?? [], rawData, 'content');
|
|
447
|
+
const data = entryAsRead(entryType?.schema ?? [], rawData);
|
|
449
448
|
const item = {
|
|
450
449
|
logicalPath: entryLogicalPath(collection.logicalPath, slug),
|
|
451
450
|
contentId,
|
package/dist/content-store.js
CHANGED
|
@@ -21,7 +21,7 @@ import { serializeJson } from './utils/json-source-splice.js';
|
|
|
21
21
|
import { withLock } from './utils/async-mutex.js';
|
|
22
22
|
import { assertContentWriteLockHeld, ContentWriteLockBusyError, DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, withContentWriteLock, } from './utils/content-write-lock.js';
|
|
23
23
|
import { findBodyFieldName, linkOnlyReferenceData } from './utils/body-field.js';
|
|
24
|
-
import {
|
|
24
|
+
import { entryAsRead } from './utils/calendar-day.js';
|
|
25
25
|
import { buildMissingReference, buildResolvedReference, buildRestrictedReference, } from './entry-schema.js';
|
|
26
26
|
import { warnDanglingReference } from './dangling-reference-log.js';
|
|
27
27
|
import { canopyLogError } from './utils/logger.js';
|
|
@@ -786,9 +786,9 @@ export class ContentStore {
|
|
|
786
786
|
absolutePath,
|
|
787
787
|
};
|
|
788
788
|
}
|
|
789
|
-
//
|
|
790
|
-
// listing and the write path do (`item-keys.ts`).
|
|
791
|
-
doc.data =
|
|
789
|
+
// Before references resolve into it. An item saved without a key is keyed by its content, as
|
|
790
|
+
// the listing and the write path do (`item-keys.ts`).
|
|
791
|
+
doc.data = entryAsRead(fields, doc.data);
|
|
792
792
|
if (options.resolveReferences !== false) {
|
|
793
793
|
doc.data = await this.resolveReferencesInData(doc.data, fields, undefined, options.referenceAccess, {
|
|
794
794
|
entry: slug ? entryLogicalPath(schemaItem.logicalPath, slug) : schemaItem.logicalPath,
|
|
@@ -1031,7 +1031,7 @@ export class ContentStore {
|
|
|
1031
1031
|
content = serializeYaml(input.data ?? {}, existingRaw);
|
|
1032
1032
|
}
|
|
1033
1033
|
else {
|
|
1034
|
-
content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw, input.format);
|
|
1034
|
+
content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw, input.format, fields);
|
|
1035
1035
|
}
|
|
1036
1036
|
await atomicWriteFile(absolutePath, content);
|
|
1037
1037
|
// Update the ID index after a successful write. Look up and mutate the LIVE index
|