canopycms 0.0.68-int.98 → 0.1.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 +2131 -0
- package/README.md +47 -63
- package/dist/ai/generate.d.ts +5 -0
- package/dist/ai/generate.js +244 -115
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/json-to-markdown.d.ts +21 -1
- package/dist/ai/json-to-markdown.js +120 -57
- package/dist/ai/references.d.ts +72 -0
- package/dist/ai/references.js +223 -0
- package/dist/ai/resolve-branch.js +9 -2
- package/dist/ai/to-plain-text.d.ts +17 -8
- package/dist/ai/to-plain-text.js +18 -15
- package/dist/ai/types.d.ts +84 -3
- package/dist/api/admin-branch-health.d.ts +24 -1
- package/dist/api/admin-branch-health.js +52 -20
- package/dist/api/admin.d.ts +23 -5
- package/dist/api/admin.js +36 -1
- package/dist/api/assets.js +88 -78
- package/dist/api/branch-create-window.d.ts +12 -0
- package/dist/api/branch-create-window.js +13 -0
- package/dist/api/branch-merge.d.ts +1 -1
- package/dist/api/branch-merge.js +1 -1
- package/dist/api/branch-review.d.ts +3 -3
- package/dist/api/branch-review.js +16 -7
- package/dist/api/branch-status.d.ts +16 -6
- package/dist/api/branch-status.js +57 -12
- package/dist/api/branch-withdraw.d.ts +2 -2
- package/dist/api/branch-withdraw.js +8 -3
- package/dist/api/branch.d.ts +25 -12
- package/dist/api/branch.js +164 -103
- package/dist/api/client.d.ts +78 -58
- package/dist/api/client.js +150 -117
- package/dist/api/comments.d.ts +12 -3
- package/dist/api/comments.js +118 -9
- package/dist/api/content.d.ts +9 -9
- package/dist/api/content.js +131 -22
- package/dist/api/entries-constants.d.ts +14 -3
- package/dist/api/entries-constants.js +14 -3
- package/dist/api/entries.d.ts +40 -2
- package/dist/api/entries.js +102 -6
- package/dist/api/github-sync.d.ts +19 -0
- package/dist/api/github-sync.js +50 -3
- package/dist/api/groups.d.ts +4 -4
- package/dist/api/groups.js +2 -1
- package/dist/api/guards.js +23 -3
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +1 -0
- package/dist/api/permissions.d.ts +21 -2
- package/dist/api/permissions.js +50 -3
- package/dist/api/request-deadline.d.ts +59 -0
- package/dist/api/request-deadline.js +73 -0
- package/dist/api/resolve-references.js +8 -7
- package/dist/api/route-builder.d.ts +14 -0
- package/dist/api/route-builder.js +14 -1
- package/dist/api/schema.d.ts +11 -9
- package/dist/api/schema.js +2 -0
- package/dist/api/settings-helpers.d.ts +5 -1
- package/dist/api/settings-helpers.js +10 -2
- package/dist/api/types.d.ts +18 -0
- package/dist/api/users-constants.d.ts +6 -0
- package/dist/api/users-constants.js +6 -0
- package/dist/assets/asset-url.d.ts +28 -16
- package/dist/assets/asset-url.js +51 -25
- package/dist/assets/finalize.js +3 -0
- package/dist/assets/index.d.ts +1 -1
- package/dist/assets/materialize.d.ts +153 -0
- package/dist/assets/materialize.js +402 -0
- package/dist/assets/store-local.d.ts +19 -5
- package/dist/assets/store-local.js +69 -10
- package/dist/assets/store-s3.d.ts +38 -7
- package/dist/assets/store-s3.js +128 -22
- package/dist/assets/transform-directives.d.ts +52 -13
- package/dist/assets/transform-directives.js +87 -27
- package/dist/assets/transform.d.ts +3 -3
- package/dist/assets/transform.js +13 -8
- package/dist/assets/types.d.ts +31 -4
- package/dist/auth/cache.d.ts +1 -1
- package/dist/auth/cache.js +1 -1
- package/dist/auth/caching-auth-plugin.d.ts +2 -0
- package/dist/auth/caching-auth-plugin.js +12 -2
- package/dist/auth/file-based-auth-cache.d.ts +8 -0
- package/dist/auth/file-based-auth-cache.js +18 -8
- package/dist/auth/person-name.d.ts +5 -0
- package/dist/auth/person-name.js +8 -0
- package/dist/auth/plugin.d.ts +10 -1
- package/dist/auth/types.d.ts +2 -0
- package/dist/auth/user-metadata-lookup.d.ts +21 -0
- package/dist/auth/user-metadata-lookup.js +116 -0
- package/dist/authorization/branch.d.ts +1 -1
- package/dist/authorization/branch.js +12 -13
- package/dist/authorization/groups/schema.d.ts +4 -4
- package/dist/authorization/helpers.d.ts +8 -0
- package/dist/authorization/helpers.js +8 -0
- package/dist/authorization/index.d.ts +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/protected-branch.d.ts +5 -4
- package/dist/authorization/protected-branch.js +7 -5
- package/dist/branch-health.d.ts +35 -8
- package/dist/branch-health.js +54 -18
- package/dist/branch-metadata-error.d.ts +27 -0
- package/dist/branch-metadata-error.js +23 -0
- package/dist/branch-metadata-file.d.ts +205 -24
- package/dist/branch-metadata-file.js +126 -25
- package/dist/branch-metadata.d.ts +45 -6
- package/dist/branch-metadata.js +142 -50
- package/dist/branch-provisioning.d.ts +167 -0
- package/dist/branch-provisioning.js +501 -0
- package/dist/branch-registry.d.ts +8 -0
- package/dist/branch-registry.js +61 -9
- package/dist/branch-schema-cache.d.ts +29 -12
- package/dist/branch-schema-cache.js +64 -21
- package/dist/branch-sparse.d.ts +23 -0
- package/dist/branch-sparse.js +118 -0
- package/dist/branch-workspace.d.ts +42 -3
- package/dist/branch-workspace.js +239 -100
- package/dist/build/asset-refs.d.ts +94 -0
- package/dist/build/asset-refs.js +267 -0
- package/dist/build/generate-ai-content.js +5 -0
- package/dist/build/inline-flight.d.ts +37 -0
- package/dist/build/inline-flight.js +142 -0
- package/dist/cli/asset-refs.d.ts +52 -0
- package/dist/cli/asset-refs.js +194 -0
- package/dist/cli/cli.d.ts +15 -1
- package/dist/cli/cli.js +8543 -3193
- package/dist/cli/configured-asset-store.d.ts +12 -0
- package/dist/cli/configured-asset-store.js +46 -0
- package/dist/cli/generate-ai-content.js +4244 -1072
- package/dist/cli/init.js +217 -126
- package/dist/cli/project-detect.d.ts +2 -0
- package/dist/cli/project-detect.js +2 -0
- package/dist/cli/template-files/Dockerfile.cms.template +13 -5
- package/dist/cli/template-files/cms-stack.ts.template +16 -13
- package/dist/client.d.ts +1 -2
- package/dist/client.js +1 -2
- package/dist/comment-store.d.ts +25 -0
- package/dist/comment-store.js +43 -8
- package/dist/config/flatten.js +2 -0
- package/dist/config/helpers.js +2 -2
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schemas/config.d.ts +12 -29
- package/dist/config/schemas/config.js +9 -12
- package/dist/config/schemas/field.d.ts +3 -0
- package/dist/config/schemas/field.js +58 -4
- package/dist/config/schemas/media.d.ts +4 -13
- package/dist/config/schemas/media.js +5 -8
- package/dist/config/schemas/url.d.ts +2 -20
- package/dist/config/schemas/url.js +2 -25
- package/dist/config/types.d.ts +116 -22
- package/dist/config/types.js +3 -9
- package/dist/config/validation.d.ts +25 -4
- package/dist/config/validation.js +116 -3
- package/dist/content-id-index.js +2 -2
- package/dist/content-index-generation.d.ts +1 -1
- package/dist/content-index-generation.js +1 -1
- package/dist/content-listing.d.ts +10 -1
- package/dist/content-listing.js +22 -5
- package/dist/content-reader.d.ts +9 -0
- package/dist/content-reader.js +16 -11
- package/dist/content-store.d.ts +75 -12
- package/dist/content-store.js +249 -86
- package/dist/content-tree.js +2 -2
- package/dist/context.js +5 -2
- package/dist/dangling-reference-log.d.ts +17 -0
- package/dist/dangling-reference-log.js +29 -0
- package/dist/dev-content-watcher.d.ts +1 -1
- package/dist/dev-content-watcher.js +1 -1
- package/dist/editor/BranchManager.d.ts +23 -7
- package/dist/editor/BranchManager.js +119 -54
- package/dist/editor/CanopyEditor.d.ts +1 -1
- package/dist/editor/CanopyEditor.js +1 -2
- package/dist/editor/CommentsPanel.d.ts +14 -0
- package/dist/editor/CommentsPanel.js +68 -43
- package/dist/editor/Editor.d.ts +11 -5
- package/dist/editor/Editor.js +891 -218
- package/dist/editor/EditorAuthGate.d.ts +7 -1
- package/dist/editor/EditorAuthGate.js +41 -9
- package/dist/editor/EntryNavigator.d.ts +22 -0
- package/dist/editor/EntryNavigator.js +353 -105
- package/dist/editor/FormHeader.d.ts +25 -0
- package/dist/editor/FormHeader.js +82 -0
- package/dist/editor/FormRenderer.d.ts +27 -3
- package/dist/editor/FormRenderer.js +171 -76
- package/dist/editor/PreviewFrame.d.ts +40 -0
- package/dist/editor/PreviewFrame.js +215 -0
- package/dist/editor/PreviewPane.d.ts +47 -0
- package/dist/editor/PreviewPane.js +147 -0
- package/dist/editor/PreviewToolbarTools.d.ts +22 -0
- package/dist/editor/PreviewToolbarTools.js +73 -0
- package/dist/editor/admin/SystemHealthPanel.d.ts +2 -1
- package/dist/editor/admin/SystemHealthPanel.js +163 -33
- package/dist/editor/admin/useSystemHealth.d.ts +18 -0
- package/dist/editor/admin/useSystemHealth.js +70 -1
- package/dist/editor/branch-status.d.ts +17 -0
- package/dist/editor/branch-status.js +29 -0
- package/dist/editor/canopy-path.d.ts +9 -6
- package/dist/editor/canopy-path.js +8 -11
- package/dist/editor/client-reference-resolver.d.ts +43 -16
- package/dist/editor/client-reference-resolver.js +155 -64
- package/dist/editor/comments/BranchComments.d.ts +1 -1
- package/dist/editor/comments/BranchComments.js +2 -2
- package/dist/editor/comments/CloseRefusedNotice.d.ts +4 -0
- package/dist/editor/comments/CloseRefusedNotice.js +7 -0
- package/dist/editor/comments/CommentThreadsPopover.d.ts +47 -0
- package/dist/editor/comments/CommentThreadsPopover.js +274 -0
- package/dist/editor/comments/CommentWriteAlert.d.ts +11 -0
- package/dist/editor/comments/CommentWriteAlert.js +13 -0
- package/dist/editor/comments/EntryComments.d.ts +10 -4
- package/dist/editor/comments/EntryComments.js +14 -17
- package/dist/editor/comments/FieldWrapper.d.ts +18 -4
- package/dist/editor/comments/FieldWrapper.js +62 -28
- package/dist/editor/comments/InlineCommentThread.d.ts +9 -2
- package/dist/editor/comments/InlineCommentThread.js +11 -47
- package/dist/editor/comments/ThreadCarousel.d.ts +1 -1
- package/dist/editor/comments/ThreadCarousel.js +18 -21
- package/dist/editor/comments/ThreadReplyControls.d.ts +28 -0
- package/dist/editor/comments/ThreadReplyControls.js +92 -0
- package/dist/editor/comments/field-comment-context.d.ts +14 -0
- package/dist/editor/comments/field-comment-context.js +14 -0
- package/dist/editor/comments/form-comments.d.ts +48 -0
- package/dist/editor/comments/form-comments.js +178 -0
- package/dist/editor/comments/item-anchor-copy.d.ts +6 -0
- package/dist/editor/comments/item-anchor-copy.js +6 -0
- package/dist/editor/comments/move-focus-if-unmoved.d.ts +6 -0
- package/dist/editor/comments/move-focus-if-unmoved.js +11 -0
- package/dist/editor/comments/popover-placement.d.ts +18 -0
- package/dist/editor/comments/popover-placement.js +58 -0
- package/dist/editor/comments/use-tracked-writes.d.ts +16 -0
- package/dist/editor/comments/use-tracked-writes.js +53 -0
- package/dist/editor/components/BranchAccessList.d.ts +15 -0
- package/dist/editor/components/BranchAccessList.js +52 -0
- package/dist/editor/components/BranchLockBanner.d.ts +22 -0
- package/dist/editor/components/BranchLockBanner.js +39 -0
- package/dist/editor/components/BranchPill.d.ts +32 -0
- package/dist/editor/components/BranchPill.js +33 -0
- package/dist/editor/components/BranchesDrawer.d.ts +11 -0
- package/dist/editor/components/BranchesDrawer.js +5 -0
- package/dist/editor/components/ConfirmDeleteModal.d.ts +6 -1
- package/dist/editor/components/ConfirmDeleteModal.js +3 -2
- package/dist/editor/components/DestructiveMenuItem.d.ts +7 -0
- package/dist/editor/components/DestructiveMenuItem.js +8 -0
- package/dist/editor/components/EditorCrashScreen.d.ts +17 -0
- package/dist/editor/components/EditorCrashScreen.js +22 -0
- package/dist/editor/components/EditorErrorBoundary.d.ts +38 -0
- package/dist/editor/components/EditorErrorBoundary.js +40 -0
- package/dist/editor/components/EditorHeader.d.ts +56 -106
- package/dist/editor/components/EditorHeader.js +130 -131
- package/dist/editor/components/EditorRail.d.ts +49 -0
- package/dist/editor/components/EditorRail.js +65 -0
- package/dist/editor/components/EntryBreadcrumb.d.ts +33 -0
- package/dist/editor/components/EntryBreadcrumb.js +56 -0
- package/dist/editor/components/EntryCreateModal.d.ts +12 -7
- package/dist/editor/components/EntryCreateModal.js +51 -57
- package/dist/editor/components/NoEditPermissionNotice.d.ts +6 -5
- package/dist/editor/components/NoEditPermissionNotice.js +5 -4
- package/dist/editor/components/QuickOpenPalette.d.ts +22 -0
- package/dist/editor/components/QuickOpenPalette.js +66 -0
- package/dist/editor/components/ReadOnlyDraftNotice.d.ts +11 -0
- package/dist/editor/components/ReadOnlyDraftNotice.js +12 -0
- package/dist/editor/components/ReasonedMenuItem.d.ts +15 -0
- package/dist/editor/components/ReasonedMenuItem.js +15 -0
- package/dist/editor/components/ReferencedByList.d.ts +11 -0
- package/dist/editor/components/ReferencedByList.js +28 -0
- package/dist/editor/components/RelativeTime.d.ts +10 -0
- package/dist/editor/components/RelativeTime.js +10 -0
- package/dist/editor/components/RenameEntryModal.d.ts +9 -1
- package/dist/editor/components/RenameEntryModal.js +19 -31
- package/dist/editor/components/RetryToastMessage.d.ts +9 -0
- package/dist/editor/components/RetryToastMessage.js +6 -0
- package/dist/editor/components/SaveAllResult.d.ts +25 -0
- package/dist/editor/components/SaveAllResult.js +86 -0
- package/dist/editor/components/SaveSplitButton.d.ts +30 -0
- package/dist/editor/components/SaveSplitButton.js +108 -0
- package/dist/editor/components/StagedChangesDrawer.d.ts +39 -0
- package/dist/editor/components/StagedChangesDrawer.js +92 -0
- package/dist/editor/components/UnavailableEntryNotice.d.ts +6 -0
- package/dist/editor/components/UnavailableEntryNotice.js +9 -0
- package/dist/editor/components/UnavailableTypeMessage.d.ts +6 -0
- package/dist/editor/components/UnavailableTypeMessage.js +9 -0
- package/dist/editor/components/UndoToastMessage.d.ts +18 -0
- package/dist/editor/components/UndoToastMessage.js +10 -0
- package/dist/editor/components/UserBadge.d.ts +2 -5
- package/dist/editor/components/UserBadge.js +62 -33
- package/dist/editor/components/ValidationSummary.d.ts +14 -0
- package/dist/editor/components/ValidationSummary.js +24 -0
- package/dist/editor/components/index.d.ts +2 -2
- package/dist/editor/components/index.js +2 -2
- package/dist/editor/context/ApiClientContext.d.ts +8 -2
- package/dist/editor/context/ApiClientContext.js +19 -8
- package/dist/editor/context/AssetContext.d.ts +13 -35
- package/dist/editor/context/AssetContext.js +12 -31
- package/dist/editor/context/EditorStateContext.d.ts +4 -4
- package/dist/editor/context/EditorStateContext.js +4 -4
- package/dist/editor/context/SWRProvider.d.ts +3 -3
- package/dist/editor/context/SiteMdxAllowContext.d.ts +4 -0
- package/dist/editor/context/SiteMdxAllowContext.js +7 -0
- package/dist/editor/context/index.d.ts +2 -1
- package/dist/editor/context/index.js +2 -1
- package/dist/editor/contrast.d.ts +19 -0
- package/dist/editor/contrast.js +41 -0
- package/dist/editor/copy.d.ts +18 -0
- package/dist/editor/copy.js +18 -0
- package/dist/editor/editor-config.js +16 -0
- package/dist/editor/editor-utils.d.ts +34 -8
- package/dist/editor/editor-utils.js +42 -20
- package/dist/editor/entry-search.d.ts +20 -0
- package/dist/editor/entry-search.js +34 -0
- package/dist/editor/field-breadcrumb.d.ts +12 -0
- package/dist/editor/field-breadcrumb.js +85 -0
- package/dist/editor/field-props.d.ts +86 -0
- package/dist/editor/field-props.js +27 -0
- package/dist/editor/field-target.d.ts +30 -0
- package/dist/editor/field-target.js +77 -0
- package/dist/editor/fields/BlockField.d.ts +12 -0
- package/dist/editor/fields/BlockField.js +89 -73
- package/dist/editor/fields/CodeField.d.ts +2 -0
- package/dist/editor/fields/CodeField.js +6 -2
- package/dist/editor/fields/DateTimeField.d.ts +4 -29
- package/dist/editor/fields/DateTimeField.js +79 -41
- package/dist/editor/fields/ExpandAllButton.d.ts +6 -0
- package/dist/editor/fields/ExpandAllButton.js +4 -0
- package/dist/editor/fields/FieldCrashFallback.d.ts +17 -0
- package/dist/editor/fields/FieldCrashFallback.js +30 -0
- package/dist/editor/fields/FieldLabel.d.ts +37 -0
- package/dist/editor/fields/FieldLabel.js +19 -0
- package/dist/editor/fields/ImageField.d.ts +4 -0
- package/dist/editor/fields/ImageField.js +29 -14
- package/dist/editor/fields/InlineGroupField.js +4 -3
- package/dist/editor/fields/ItemList.d.ts +42 -0
- package/dist/editor/fields/ItemList.js +130 -0
- package/dist/editor/fields/MarkdownField.d.ts +32 -0
- package/dist/editor/fields/MarkdownField.js +223 -46
- package/dist/editor/fields/MdxImageDialog.js +1 -1
- package/dist/editor/fields/NumberField.d.ts +2 -0
- package/dist/editor/fields/NumberField.js +8 -5
- package/dist/editor/fields/NumberListField.d.ts +2 -0
- package/dist/editor/fields/NumberListField.js +19 -16
- package/dist/editor/fields/ObjectField.d.ts +2 -7
- package/dist/editor/fields/ObjectField.js +15 -5
- package/dist/editor/fields/ObjectListField.d.ts +29 -0
- package/dist/editor/fields/ObjectListField.js +57 -0
- package/dist/editor/fields/ReferenceField.d.ts +2 -0
- package/dist/editor/fields/ReferenceField.js +34 -9
- package/dist/editor/fields/SelectField.d.ts +2 -0
- package/dist/editor/fields/SelectField.js +11 -2
- package/dist/editor/fields/StringListField.d.ts +4 -1
- package/dist/editor/fields/StringListField.js +22 -8
- package/dist/editor/fields/TextField.d.ts +2 -0
- package/dist/editor/fields/TextField.js +6 -2
- package/dist/editor/fields/ToggleField.d.ts +2 -0
- package/dist/editor/fields/ToggleField.js +8 -3
- package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +1 -1
- package/dist/editor/fields/entry-link/InsertEntryLink.js +1 -1
- package/dist/editor/fields/form-expansion.d.ts +9 -0
- package/dist/editor/fields/form-expansion.js +2 -0
- package/dist/editor/fields/item-expansion.d.ts +15 -0
- package/dist/editor/fields/item-expansion.js +60 -0
- package/dist/editor/fields/list-item-keys.d.ts +8 -0
- package/dist/editor/fields/list-item-keys.js +50 -0
- package/dist/editor/fields/list-item-title.d.ts +20 -0
- package/dist/editor/fields/list-item-title.js +85 -0
- package/dist/editor/fields/markdown-export-options.d.ts +2 -0
- package/dist/editor/fields/markdown-export-options.js +8 -0
- package/dist/editor/fields/markdown-fidelity-visitors.d.ts +11 -0
- package/dist/editor/fields/markdown-fidelity-visitors.js +160 -0
- package/dist/editor/fields/mdx-jsx-support.d.ts +5 -1
- package/dist/editor/fields/mdx-jsx-support.js +157 -69
- package/dist/editor/fields/rich-text-failures.d.ts +5 -0
- package/dist/editor/fields/rich-text-failures.js +26 -0
- package/dist/editor/fields/toolbar-wrap.d.ts +7 -0
- package/dist/editor/fields/toolbar-wrap.js +35 -0
- package/dist/editor/form-outline.d.ts +13 -0
- package/dist/editor/form-outline.js +70 -0
- package/dist/editor/group-manager/GroupCard.d.ts +4 -1
- package/dist/editor/group-manager/GroupCard.js +12 -3
- package/dist/editor/group-manager/GroupForm.js +1 -1
- package/dist/editor/group-manager/InternalGroupsTab.d.ts +2 -1
- package/dist/editor/group-manager/InternalGroupsTab.js +4 -4
- package/dist/editor/group-manager/MemberList.d.ts +2 -1
- package/dist/editor/group-manager/MemberList.js +15 -4
- package/dist/editor/group-manager/hooks/useGroupState.d.ts +5 -0
- package/dist/editor/group-manager/hooks/useGroupState.js +30 -7
- package/dist/editor/group-manager/index.js +34 -5
- package/dist/editor/group-manager/types.d.ts +5 -2
- package/dist/editor/hooks/create-branch-request.d.ts +20 -0
- package/dist/editor/hooks/create-branch-request.js +54 -0
- package/dist/editor/hooks/save-all.d.ts +68 -0
- package/dist/editor/hooks/save-all.js +61 -0
- package/dist/editor/hooks/useBranchActions.d.ts +11 -1
- package/dist/editor/hooks/useBranchActions.js +42 -19
- package/dist/editor/hooks/useBranchManager.d.ts +21 -0
- package/dist/editor/hooks/useBranchManager.js +321 -125
- package/dist/editor/hooks/useBranchesData.d.ts +8 -4
- package/dist/editor/hooks/useBranchesData.js +9 -5
- package/dist/editor/hooks/useCommentSystem.d.ts +14 -2
- package/dist/editor/hooks/useCommentSystem.js +79 -55
- package/dist/editor/hooks/useCommentsData.d.ts +11 -7
- package/dist/editor/hooks/useCommentsData.js +20 -19
- package/dist/editor/hooks/useDraftManager.d.ts +77 -3
- package/dist/editor/hooks/useDraftManager.js +887 -94
- package/dist/editor/hooks/useEditorLayout.d.ts +53 -7
- package/dist/editor/hooks/useEditorLayout.js +98 -20
- package/dist/editor/hooks/useEntriesData.d.ts +2 -1
- package/dist/editor/hooks/useEntriesData.js +18 -4
- package/dist/editor/hooks/useEntryManager.d.ts +41 -2
- package/dist/editor/hooks/useEntryManager.js +227 -41
- package/dist/editor/hooks/useGroupManager.js +13 -16
- package/dist/editor/hooks/usePermissionManager.js +14 -6
- package/dist/editor/hooks/usePreviewMarks.d.ts +21 -0
- package/dist/editor/hooks/usePreviewMarks.js +33 -0
- package/dist/editor/hooks/useReferenceResolution.d.ts +10 -3
- package/dist/editor/hooks/useReferenceResolution.js +34 -177
- package/dist/editor/hooks/useSchemaManager.d.ts +25 -6
- package/dist/editor/hooks/useSchemaManager.js +97 -39
- package/dist/editor/hooks/useUserMetadata.d.ts +14 -4
- package/dist/editor/hooks/useUserMetadata.js +76 -47
- package/dist/editor/hooks/user-metadata-batcher.d.ts +8 -0
- package/dist/editor/hooks/user-metadata-batcher.js +33 -0
- package/dist/editor/media/AssetCard.d.ts +6 -7
- package/dist/editor/media/AssetCard.js +24 -31
- package/dist/editor/media/AssetListRow.d.ts +16 -0
- package/dist/editor/media/AssetListRow.js +41 -0
- package/dist/editor/media/CropStep.js +6 -1
- package/dist/editor/media/MediaLibrary.d.ts +4 -3
- package/dist/editor/media/MediaLibrary.js +11 -4
- package/dist/editor/media/MediaLibraryBody.d.ts +3 -3
- package/dist/editor/media/MediaLibraryBody.js +195 -80
- package/dist/editor/media/MediaLibrarySkeleton.d.ts +6 -0
- package/dist/editor/media/MediaLibrarySkeleton.js +14 -0
- package/dist/editor/media/crop-math.d.ts +3 -4
- package/dist/editor/media/crop-math.js +11 -22
- package/dist/editor/media/editor-image-src.d.ts +8 -0
- package/dist/editor/media/editor-image-src.js +10 -0
- package/dist/editor/media/format-bytes.d.ts +2 -0
- package/dist/editor/media/format-bytes.js +23 -0
- package/dist/editor/media/media-library-prefs.d.ts +23 -0
- package/dist/editor/media/media-library-prefs.js +49 -0
- package/dist/editor/media/media-library-view.d.ts +34 -0
- package/dist/editor/media/media-library-view.js +40 -0
- package/dist/editor/media/useAssetLibrary.d.ts +30 -0
- package/dist/editor/media/useAssetLibrary.js +126 -0
- package/dist/editor/media/useStableAssetOrder.d.ts +11 -0
- package/dist/editor/media/useStableAssetOrder.js +37 -0
- package/dist/editor/media/useThumbnailFailure.d.ts +6 -0
- package/dist/editor/media/useThumbnailFailure.js +16 -0
- package/dist/editor/permission-manager/GroupSelector.js +2 -2
- package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -1
- package/dist/editor/permission-manager/PermissionEditor.js +15 -4
- package/dist/editor/permission-manager/PermissionLevelBadge.js +1 -1
- package/dist/editor/permission-manager/PermissionTree.d.ts +1 -1
- package/dist/editor/permission-manager/UserSelector.d.ts +1 -1
- package/dist/editor/permission-manager/UserSelector.js +3 -3
- package/dist/editor/permission-manager/index.js +12 -3
- package/dist/editor/permission-manager/types.d.ts +5 -2
- package/dist/editor/platform.d.ts +3 -0
- package/dist/editor/platform.js +9 -0
- package/dist/editor/preview-asset-base.d.ts +9 -0
- package/dist/editor/preview-asset-base.js +17 -0
- package/dist/editor/preview-bridge.d.ts +51 -27
- package/dist/editor/preview-bridge.js +103 -144
- package/dist/editor/preview-marks.d.ts +15 -0
- package/dist/editor/preview-marks.js +74 -0
- package/dist/editor/preview-path.d.ts +12 -0
- package/dist/editor/preview-path.js +45 -1
- package/dist/editor/preview-scale.d.ts +27 -0
- package/dist/editor/preview-scale.js +34 -0
- package/dist/editor/raw-asset-base.d.ts +5 -0
- package/dist/editor/raw-asset-base.js +14 -0
- package/dist/editor/relative-time.d.ts +4 -4
- package/dist/editor/relative-time.js +16 -8
- package/dist/editor/schema-editor/CollectionEditor.d.ts +20 -3
- package/dist/editor/schema-editor/CollectionEditor.js +361 -62
- package/dist/editor/schema-editor/EntryTypeEditor.d.ts +5 -1
- package/dist/editor/schema-editor/EntryTypeEditor.js +11 -6
- package/dist/editor/schema-editor/collection-draft.d.ts +22 -0
- package/dist/editor/schema-editor/collection-draft.js +40 -0
- package/dist/editor/schema-editor/index.d.ts +1 -1
- package/dist/editor/schema-label.d.ts +10 -0
- package/dist/editor/schema-label.js +17 -0
- package/dist/editor/settings-conflict.d.ts +5 -0
- package/dist/editor/settings-conflict.js +8 -0
- package/dist/editor/shell/ActivityRail.d.ts +37 -0
- package/dist/editor/shell/ActivityRail.js +91 -0
- package/dist/editor/shell/EditorShell.d.ts +17 -0
- package/dist/editor/shell/EditorShell.js +31 -0
- package/dist/editor/shell/PaneLayout.d.ts +28 -0
- package/dist/editor/shell/PaneLayout.js +116 -0
- package/dist/editor/shell/SidePanelHost.d.ts +35 -0
- package/dist/editor/shell/SidePanelHost.js +184 -0
- package/dist/editor/shell/TopBar.d.ts +12 -0
- package/dist/editor/shell/TopBar.js +14 -0
- package/dist/editor/shell/host-chrome.d.ts +16 -0
- package/dist/editor/shell/host-chrome.js +69 -0
- package/dist/editor/slug.d.ts +16 -0
- package/dist/editor/slug.js +43 -0
- package/dist/editor/theme.d.ts +2 -1
- package/dist/editor/theme.js +314 -13
- package/dist/editor/unanswered.d.ts +17 -0
- package/dist/editor/unanswered.js +26 -0
- package/dist/editor/unavailable-entry-type.d.ts +20 -0
- package/dist/editor/unavailable-entry-type.js +28 -0
- package/dist/editor/undo-removal.d.ts +57 -0
- package/dist/editor/undo-removal.js +163 -0
- package/dist/editor/user-display.d.ts +4 -0
- package/dist/editor/user-display.js +5 -0
- package/dist/editor/utils/confirm-modal.d.ts +8 -0
- package/dist/editor/utils/confirm-modal.js +12 -0
- package/dist/editor/utils/editor-errors.d.ts +19 -0
- package/dist/editor/utils/editor-errors.js +32 -0
- package/dist/editor/utils/focus-return.d.ts +56 -0
- package/dist/editor/utils/focus-return.js +106 -0
- package/dist/entry-link-resolver.d.ts +11 -1
- package/dist/entry-link-resolver.js +65 -5
- package/dist/entry-schema-registry.d.ts +3 -3
- package/dist/entry-schema-registry.js +7 -4
- package/dist/entry-schema.d.ts +49 -3
- package/dist/entry-schema.js +18 -0
- package/dist/git-manager.d.ts +172 -10
- package/dist/git-manager.js +449 -107
- package/dist/github-service.d.ts +39 -2
- package/dist/github-service.js +101 -7
- package/dist/http/handler.js +74 -9
- package/dist/http/types.d.ts +2 -0
- package/dist/http/worker-not-ready.d.ts +9 -4
- package/dist/http/worker-not-ready.js +59 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/item-keys.d.ts +46 -0
- package/dist/item-keys.js +235 -0
- package/dist/operating-mode/editor-mode-check.d.ts +15 -0
- package/dist/operating-mode/editor-mode-check.js +24 -0
- package/dist/operating-mode/mode-env.d.ts +13 -14
- package/dist/operating-mode/mode-env.js +13 -14
- package/dist/operating-mode/types.d.ts +2 -2
- package/dist/paths/branch.d.ts +0 -1
- package/dist/paths/branch.js +1 -8
- package/dist/paths/index.d.ts +1 -1
- package/dist/paths/index.js +1 -1
- package/dist/paths/validation.js +0 -2
- package/dist/preview.d.ts +8 -0
- package/dist/preview.js +7 -0
- package/dist/reference-resolver.js +1 -0
- package/dist/resolve-canopy-user.d.ts +1 -1
- package/dist/resolve-canopy-user.js +1 -1
- package/dist/schema/meta-loader.d.ts +18 -8
- package/dist/schema/meta-loader.js +42 -9
- package/dist/schema/registry-fingerprint.d.ts +14 -0
- package/dist/schema/registry-fingerprint.js +63 -0
- package/dist/schema/resolver.d.ts +7 -4
- package/dist/schema/resolver.js +7 -6
- package/dist/schema/schema-store.d.ts +7 -10
- package/dist/schema/schema-store.js +13 -11
- package/dist/schema/schema-unavailable-error.d.ts +11 -0
- package/dist/schema/schema-unavailable-error.js +15 -0
- package/dist/schema/types.d.ts +22 -0
- package/dist/schema-registry-record.d.ts +35 -0
- package/dist/schema-registry-record.js +134 -0
- package/dist/server.d.ts +79 -9
- package/dist/server.js +62 -8
- package/dist/services.d.ts +33 -5
- package/dist/services.js +155 -37
- package/dist/settings-workspace.d.ts +23 -3
- package/dist/settings-workspace.js +159 -45
- package/dist/stamp-on-publish.d.ts +36 -0
- package/dist/stamp-on-publish.js +71 -0
- package/dist/static/index.d.ts +58 -5
- package/dist/static/index.js +133 -3
- package/dist/static/seo.d.ts +5 -5
- package/dist/static/seo.js +5 -5
- package/dist/submission-attribution.d.ts +8 -0
- package/dist/submission-attribution.js +22 -0
- package/dist/system-users.d.ts +12 -0
- package/dist/system-users.js +12 -0
- package/dist/task-queue/cms-task-queue.d.ts +1 -1
- package/dist/task-queue/cms-task-queue.js +1 -1
- package/dist/task-queue/index.d.ts +1 -1
- package/dist/task-queue/index.js +1 -1
- package/dist/task-queue/task-queue.d.ts +6 -0
- package/dist/task-queue/task-queue.js +29 -0
- package/dist/task-queue/worker-status.d.ts +35 -9
- package/dist/task-queue/worker-status.js +84 -17
- package/dist/types.d.ts +90 -9
- package/dist/url-collision.js +3 -3
- package/dist/url-exclusivity-fixtures.d.ts +8 -1
- package/dist/url-exclusivity-fixtures.js +11 -1
- package/dist/user.js +2 -1
- package/dist/utils/atomic-write.js +2 -1
- package/dist/utils/base-branch.d.ts +13 -0
- package/dist/utils/base-branch.js +23 -0
- package/dist/utils/content-serialize.d.ts +15 -7
- package/dist/utils/content-serialize.js +353 -51
- package/dist/utils/content-write-lock.d.ts +6 -1
- package/dist/utils/content-write-lock.js +21 -6
- package/dist/utils/debug.d.ts +1 -1
- package/dist/utils/debug.js +5 -3
- package/dist/utils/error.js +13 -2
- package/dist/utils/escape-text.d.ts +7 -0
- package/dist/utils/escape-text.js +27 -0
- package/dist/utils/fs.d.ts +12 -4
- package/dist/utils/fs.js +18 -5
- package/dist/utils/git.d.ts +21 -1
- package/dist/utils/git.js +63 -8
- package/dist/utils/json-source-splice.d.ts +14 -0
- package/dist/utils/json-source-splice.js +120 -0
- package/dist/utils/markdown-body-splice.d.ts +39 -0
- package/dist/utils/markdown-body-splice.js +421 -0
- package/dist/utils/occ-json-write.js +18 -12
- package/dist/utils/provision-log.d.ts +25 -0
- package/dist/utils/provision-log.js +49 -0
- package/dist/utils/provisioning-lock.d.ts +32 -1
- package/dist/utils/provisioning-lock.js +78 -32
- package/dist/utils/sanitize-href.d.ts +9 -23
- package/dist/utils/sanitize-href.js +14 -28
- package/dist/utils/title-field.d.ts +10 -0
- package/dist/utils/title-field.js +47 -0
- package/dist/utils/yaml-source-splice.d.ts +58 -0
- package/dist/utils/yaml-source-splice.js +515 -0
- package/dist/validation/block-structural-keys.d.ts +1 -1
- package/dist/validation/block-structural-keys.js +1 -1
- package/dist/validation/deletion-checker.d.ts +21 -29
- package/dist/validation/deletion-checker.js +41 -117
- package/dist/validation/entry-validator.d.ts +6 -10
- package/dist/validation/entry-validator.js +19 -26
- package/dist/validation/field-traversal.d.ts +22 -0
- package/dist/validation/field-traversal.js +39 -8
- package/dist/validation/markdown-safety.d.ts +68 -0
- package/dist/validation/markdown-safety.js +596 -0
- package/dist/validation/mdx-allowlist.d.ts +55 -0
- package/dist/validation/mdx-allowlist.js +183 -0
- package/dist/validation/reference-validator.d.ts +2 -0
- package/dist/validation/reference-validator.js +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/branch-policy.d.ts +124 -0
- package/dist/worker/branch-policy.js +243 -0
- package/dist/worker/bundle-header.d.ts +117 -0
- package/dist/worker/bundle-header.js +253 -0
- package/dist/worker/cms-worker.d.ts +236 -142
- package/dist/worker/cms-worker.js +772 -285
- package/dist/worker/gateway-egress.d.ts +43 -0
- package/dist/worker/gateway-egress.js +209 -0
- package/dist/worker/gateway-instance.d.ts +42 -0
- package/dist/worker/gateway-instance.js +129 -0
- package/dist/worker/git-process.d.ts +81 -0
- package/dist/worker/git-process.js +234 -0
- package/dist/worker/git-sync.d.ts +34 -17
- package/dist/worker/git-sync.js +394 -131
- package/dist/worker/github-auth.d.ts +9 -9
- package/dist/worker/github-auth.js +13 -15
- package/dist/worker/github-gateway-http.d.ts +62 -0
- package/dist/worker/github-gateway-http.js +977 -0
- package/dist/worker/github-gateway-wire.d.ts +590 -0
- package/dist/worker/github-gateway-wire.js +488 -0
- package/dist/worker/github-gateway.d.ts +238 -0
- package/dist/worker/github-gateway.js +726 -0
- package/dist/worker/github-mirror-upkeep.d.ts +75 -0
- package/dist/worker/github-mirror-upkeep.js +266 -0
- package/dist/worker/github-mirror.d.ts +364 -0
- package/dist/worker/github-mirror.js +1030 -0
- package/dist/worker/github-transfer.d.ts +56 -0
- package/dist/worker/github-transfer.js +444 -0
- package/dist/worker/history-rewrite.d.ts +4 -5
- package/dist/worker/history-rewrite.js +12 -17
- package/dist/worker/provisioned-workspace.d.ts +5 -5
- package/dist/worker/provisioned-workspace.js +5 -5
- package/dist/worker/rebase.d.ts +1 -1
- package/dist/worker/rebase.js +58 -54
- package/dist/worker/remote-git-maintenance.d.ts +3 -0
- package/dist/worker/remote-git-maintenance.js +12 -0
- package/dist/worker/schema-gate.d.ts +49 -0
- package/dist/worker/schema-gate.js +226 -0
- package/dist/worker/self-checks.d.ts +107 -0
- package/dist/worker/self-checks.js +358 -0
- package/dist/worker/shared-repo-git.d.ts +96 -0
- package/dist/worker/shared-repo-git.js +459 -0
- package/dist/worker/sparse-cone.d.ts +20 -0
- package/dist/worker/sparse-cone.js +136 -0
- package/dist/worker/stale-mirror.d.ts +82 -0
- package/dist/worker/stale-mirror.js +130 -0
- package/dist/worker/task-runner.d.ts +5 -3
- package/dist/worker/task-runner.js +287 -186
- package/dist/worker/worker-context.d.ts +36 -37
- package/package.json +35 -7
- package/dist/editor/EditorPanes.d.ts +0 -10
- package/dist/editor/EditorPanes.js +0 -98
- package/dist/editor/components/EditorFooter.d.ts +0 -11
- package/dist/editor/components/EditorFooter.js +0 -9
- package/dist/editor/components/EditorSidebar.d.ts +0 -67
- package/dist/editor/components/EditorSidebar.js +0 -22
|
@@ -4,20 +4,38 @@
|
|
|
4
4
|
* Converts a single entry's data + schema fields to clean markdown.
|
|
5
5
|
* Handles all CanopyCMS field types including nested objects and blocks.
|
|
6
6
|
*/
|
|
7
|
+
import { RESTRICTED_REFERENCE_MARKER } from '../entry-schema.js';
|
|
7
8
|
import { flattenGroupFields } from '../utils/flatten-group-fields.js';
|
|
9
|
+
import { resolveEntryTitle } from '../utils/title-field.js';
|
|
8
10
|
import { stripMdxImports } from './strip-mdx.js';
|
|
9
11
|
import { applyComponentTransforms } from './transform-components.js';
|
|
12
|
+
const DEFAULT_REFERENCE_RENDERING = {
|
|
13
|
+
title: (target) => resolveEntryTitle(target, { slug: typeof target.slug === 'string' ? target.slug : undefined }),
|
|
14
|
+
url: (target) => typeof target.urlPath === 'string' && target.urlPath ? target.urlPath : undefined,
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The entry's `title` as frontmatter carries it. Only a scalar qualifies: a `reference` field
|
|
18
|
+
* named `title` holds an object and renders as a field instead.
|
|
19
|
+
*/
|
|
20
|
+
export function frontmatterTitle(data) {
|
|
21
|
+
const { title } = data;
|
|
22
|
+
return (typeof title === 'string' || typeof title === 'number') && title !== ''
|
|
23
|
+
? String(title)
|
|
24
|
+
: undefined;
|
|
25
|
+
}
|
|
10
26
|
/**
|
|
11
27
|
* Convert an entry to clean markdown suitable for AI consumption.
|
|
12
28
|
*
|
|
13
29
|
* For MD/MDX entries: renders frontmatter fields as metadata, appends body verbatim.
|
|
14
30
|
* For JSON entries: full schema-driven conversion of all fields.
|
|
15
31
|
*/
|
|
16
|
-
export function entryToMarkdown(entry, config) {
|
|
32
|
+
export function entryToMarkdown(entry, config, references = DEFAULT_REFERENCE_RENDERING) {
|
|
33
|
+
const opts = { config, references };
|
|
17
34
|
const parts = [];
|
|
35
|
+
const title = frontmatterTitle(entry.data);
|
|
18
36
|
parts.push('---');
|
|
19
|
-
if (
|
|
20
|
-
parts.push(`title: ${yamlValue(
|
|
37
|
+
if (title !== undefined) {
|
|
38
|
+
parts.push(`title: ${yamlValue(title)}`);
|
|
21
39
|
}
|
|
22
40
|
parts.push(`slug: ${yamlValue(entry.slug)}`);
|
|
23
41
|
parts.push(`collection: ${yamlValue(entry.collection)}`);
|
|
@@ -26,13 +44,13 @@ export function entryToMarkdown(entry, config) {
|
|
|
26
44
|
parts.push('');
|
|
27
45
|
// Fields already in frontmatter — skip from body rendering to avoid duplication
|
|
28
46
|
const skipFields = new Set();
|
|
29
|
-
if (
|
|
47
|
+
if (title !== undefined)
|
|
30
48
|
skipFields.add('title');
|
|
31
49
|
if (entry.format === 'md' || entry.format === 'mdx') {
|
|
32
|
-
parts.push(...renderMarkdownEntry(entry,
|
|
50
|
+
parts.push(...renderMarkdownEntry(entry, opts, skipFields));
|
|
33
51
|
}
|
|
34
52
|
else {
|
|
35
|
-
parts.push(...renderJsonEntry(entry,
|
|
53
|
+
parts.push(...renderJsonEntry(entry, opts, skipFields));
|
|
36
54
|
}
|
|
37
55
|
// Markdown appended by an entry transform (e.g. a folded-in sibling artifact). Computed once
|
|
38
56
|
// upstream, so it flows into the per-entry file, the collection all.md, and bundles alike.
|
|
@@ -45,27 +63,39 @@ export function entryToMarkdown(entry, config) {
|
|
|
45
63
|
/**
|
|
46
64
|
* Render a MD/MDX entry: metadata fields as a section, body verbatim.
|
|
47
65
|
*/
|
|
48
|
-
function renderMarkdownEntry(entry,
|
|
66
|
+
function renderMarkdownEntry(entry, opts, skipFields) {
|
|
67
|
+
const { config } = opts;
|
|
49
68
|
const parts = [];
|
|
50
69
|
// Render frontmatter fields (excluding body-like fields and already-rendered fields)
|
|
51
70
|
const bodyFieldTypes = new Set(['markdown', 'mdx']);
|
|
52
71
|
const metadataFields = flattenGroupFields(entry.fields).filter((f) => !bodyFieldTypes.has(f.type) && !skipFields.has(f.name));
|
|
72
|
+
// Blocks of output are separated by exactly one blank line, which is what Prettier leaves.
|
|
73
|
+
const endBlock = () => {
|
|
74
|
+
if (parts.length > 0 && parts[parts.length - 1] !== '')
|
|
75
|
+
parts.push('');
|
|
76
|
+
};
|
|
53
77
|
for (const field of metadataFields) {
|
|
54
78
|
const value = entry.data[field.name];
|
|
55
79
|
if (value === undefined || value === null)
|
|
56
80
|
continue;
|
|
57
|
-
|
|
81
|
+
// A structured field has no one-line form, so it gets a section as in a JSON entry.
|
|
82
|
+
const transformed = field.type === 'object' || field.type === 'block'
|
|
83
|
+
? renderField(field, value, 2, entry, opts)
|
|
84
|
+
: applyFieldTransform(entry, field, value, config);
|
|
58
85
|
if (transformed !== undefined) {
|
|
59
|
-
|
|
60
|
-
|
|
86
|
+
if (transformed) {
|
|
87
|
+
endBlock();
|
|
88
|
+
parts.push(transformed);
|
|
89
|
+
parts.push('');
|
|
90
|
+
}
|
|
61
91
|
continue;
|
|
62
92
|
}
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
parts.push('');
|
|
93
|
+
const inline = formatInlineValue(field, value, opts.references);
|
|
94
|
+
if (inline === '' && field.type === 'reference')
|
|
95
|
+
continue;
|
|
96
|
+
parts.push(`**${field.label || field.name}:** ${inline}`);
|
|
68
97
|
}
|
|
98
|
+
endBlock();
|
|
69
99
|
if (entry.body) {
|
|
70
100
|
let body = entry.format === 'mdx' ? stripMdxImports(entry.body) : entry.body;
|
|
71
101
|
if (config?.componentTransforms && Object.keys(config.componentTransforms).length > 0) {
|
|
@@ -83,7 +113,7 @@ function renderMarkdownEntry(entry, config, skipFields) {
|
|
|
83
113
|
/**
|
|
84
114
|
* Render a JSON entry: full schema-driven conversion of all fields.
|
|
85
115
|
*/
|
|
86
|
-
function renderJsonEntry(entry,
|
|
116
|
+
function renderJsonEntry(entry, opts, skipFields) {
|
|
87
117
|
const parts = [];
|
|
88
118
|
for (const field of flattenGroupFields(entry.fields)) {
|
|
89
119
|
if (skipFields.has(field.name))
|
|
@@ -91,7 +121,7 @@ function renderJsonEntry(entry, config, skipFields) {
|
|
|
91
121
|
const value = entry.data[field.name];
|
|
92
122
|
if (value === undefined || value === null)
|
|
93
123
|
continue;
|
|
94
|
-
const rendered = renderField(field, value, 2, entry,
|
|
124
|
+
const rendered = renderField(field, value, 2, entry, opts);
|
|
95
125
|
if (rendered) {
|
|
96
126
|
parts.push(rendered);
|
|
97
127
|
parts.push('');
|
|
@@ -104,8 +134,8 @@ function renderJsonEntry(entry, config, skipFields) {
|
|
|
104
134
|
*
|
|
105
135
|
* @param depth - Heading depth (2 = ##, 3 = ###, etc.)
|
|
106
136
|
*/
|
|
107
|
-
function renderField(field, value, depth, entry,
|
|
108
|
-
const transformed = applyFieldTransform(entry, field, value, config);
|
|
137
|
+
function renderField(field, value, depth, entry, opts) {
|
|
138
|
+
const transformed = applyFieldTransform(entry, field, value, opts.config);
|
|
109
139
|
if (transformed !== undefined) {
|
|
110
140
|
return transformed;
|
|
111
141
|
}
|
|
@@ -117,7 +147,7 @@ function renderField(field, value, depth, entry, config) {
|
|
|
117
147
|
// already agrees with is what keeps a regenerated bundle diff-free.
|
|
118
148
|
const descriptionLine = 'description' in field && field.description ? `\n\n_${field.description}_` : '';
|
|
119
149
|
if ('list' in field && field.list && Array.isArray(value)) {
|
|
120
|
-
return renderListField(field, value, depth, label, heading, descriptionLine, entry,
|
|
150
|
+
return renderListField(field, value, depth, label, heading, descriptionLine, entry, opts);
|
|
121
151
|
}
|
|
122
152
|
switch (field.type) {
|
|
123
153
|
case 'string':
|
|
@@ -136,11 +166,11 @@ function renderField(field, value, depth, entry, config) {
|
|
|
136
166
|
case 'select':
|
|
137
167
|
return renderSelectField(field, value, heading, label, descriptionLine);
|
|
138
168
|
case 'reference':
|
|
139
|
-
return renderReferenceField(value, heading, label, descriptionLine);
|
|
169
|
+
return renderReferenceField(field, value, heading, label, descriptionLine, opts.references);
|
|
140
170
|
case 'object':
|
|
141
|
-
return renderObjectField(field, value, depth, heading, label, descriptionLine, entry,
|
|
171
|
+
return renderObjectField(field, value, depth, heading, label, descriptionLine, entry, opts);
|
|
142
172
|
case 'block':
|
|
143
|
-
return renderBlockField(field, value, depth, heading, label, descriptionLine, entry,
|
|
173
|
+
return renderBlockField(field, value, depth, heading, label, descriptionLine, entry, opts);
|
|
144
174
|
default:
|
|
145
175
|
return `${heading} ${label}${descriptionLine}\n\n${String(value)}`;
|
|
146
176
|
}
|
|
@@ -148,7 +178,7 @@ function renderField(field, value, depth, entry, config) {
|
|
|
148
178
|
/**
|
|
149
179
|
* Render a list field (field with list: true).
|
|
150
180
|
*/
|
|
151
|
-
function renderListField(field, values, depth, label, heading, descriptionLine, entry,
|
|
181
|
+
function renderListField(field, values, depth, label, heading, descriptionLine, entry, opts) {
|
|
152
182
|
if (values.length === 0)
|
|
153
183
|
return '';
|
|
154
184
|
// Arrays of flat records (object items whose subfields are all single-line scalars) render far
|
|
@@ -156,7 +186,7 @@ function renderListField(field, values, depth, label, heading, descriptionLine,
|
|
|
156
186
|
// headings. Genuinely nested items (subfields that are lists/objects/blocks/long text) keep the
|
|
157
187
|
// heading-per-item fallback below.
|
|
158
188
|
if (field.type === 'object' && isFlatObjectList(field)) {
|
|
159
|
-
return renderObjectListTable(field, values, label, heading, descriptionLine);
|
|
189
|
+
return renderObjectListTable(field, values, label, heading, descriptionLine, opts.references);
|
|
160
190
|
}
|
|
161
191
|
const isComplex = field.type === 'object' || field.type === 'block';
|
|
162
192
|
if (isComplex) {
|
|
@@ -171,7 +201,7 @@ function renderListField(field, values, depth, label, heading, descriptionLine,
|
|
|
171
201
|
const v = item[f.name];
|
|
172
202
|
if (v === undefined || v === null)
|
|
173
203
|
return '';
|
|
174
|
-
return renderField(f, v, depth + 2, entry,
|
|
204
|
+
return renderField(f, v, depth + 2, entry, opts);
|
|
175
205
|
})
|
|
176
206
|
.filter(Boolean);
|
|
177
207
|
return `${itemHeading} ${itemLabel}\n\n${subFields.join('\n\n')}`;
|
|
@@ -181,8 +211,12 @@ function renderListField(field, values, depth, label, heading, descriptionLine,
|
|
|
181
211
|
.filter(Boolean);
|
|
182
212
|
return `${heading} ${label}${descriptionLine}\n\n${items.join('\n\n')}`;
|
|
183
213
|
}
|
|
184
|
-
const items = values
|
|
185
|
-
|
|
214
|
+
const items = values
|
|
215
|
+
.map((v) => formatInlineValue(field, v, opts.references))
|
|
216
|
+
.filter((item) => item !== '' || field.type !== 'reference');
|
|
217
|
+
if (items.length === 0)
|
|
218
|
+
return '';
|
|
219
|
+
return `${heading} ${label}${descriptionLine}\n\n${items.map((item) => `- ${item}`).join('\n')}`;
|
|
186
220
|
}
|
|
187
221
|
/** Subfield types that render to a single line and so fit cleanly in a table cell. */
|
|
188
222
|
const FLAT_CELL_TYPES = new Set([
|
|
@@ -213,7 +247,7 @@ function isFlatObjectList(field) {
|
|
|
213
247
|
* Render an array of flat object records as a GFM table. Columns are the subfields in schema order;
|
|
214
248
|
* absent values become empty cells; pipes and newlines in cells are escaped.
|
|
215
249
|
*/
|
|
216
|
-
function renderObjectListTable(field, values, label, heading, descriptionLine) {
|
|
250
|
+
function renderObjectListTable(field, values, label, heading, descriptionLine, references) {
|
|
217
251
|
const columns = field.fields;
|
|
218
252
|
const headerRow = `| ${columns.map((f) => escapeTableCell(f.label || f.name)).join(' | ')} |`;
|
|
219
253
|
const separatorRow = `| ${columns.map(() => '---').join(' | ')} |`;
|
|
@@ -223,7 +257,7 @@ function renderObjectListTable(field, values, label, heading, descriptionLine) {
|
|
|
223
257
|
const v = record[f.name];
|
|
224
258
|
if (v === undefined || v === null)
|
|
225
259
|
return '';
|
|
226
|
-
return escapeTableCell(formatCellValue(f, v));
|
|
260
|
+
return escapeTableCell(formatCellValue(f, v, references));
|
|
227
261
|
});
|
|
228
262
|
return `| ${cells.join(' | ')} |`;
|
|
229
263
|
});
|
|
@@ -231,12 +265,12 @@ function renderObjectListTable(field, values, label, heading, descriptionLine) {
|
|
|
231
265
|
return `${heading} ${label}${descriptionLine}\n\n${table}`;
|
|
232
266
|
}
|
|
233
267
|
/** Render a single scalar subfield value to its one-line table-cell form. */
|
|
234
|
-
function formatCellValue(field, value) {
|
|
268
|
+
function formatCellValue(field, value, references) {
|
|
235
269
|
switch (field.type) {
|
|
236
270
|
case 'boolean':
|
|
237
271
|
return value ? 'Yes' : 'No';
|
|
238
272
|
case 'reference':
|
|
239
|
-
return formatReference(value);
|
|
273
|
+
return formatReference(field, value, references);
|
|
240
274
|
case 'select':
|
|
241
275
|
return Array.isArray(value)
|
|
242
276
|
? value.map((v) => resolveSelectLabel(field, v)).join(', ')
|
|
@@ -283,12 +317,14 @@ function resolveSelectLabel(field, value) {
|
|
|
283
317
|
}
|
|
284
318
|
return strValue;
|
|
285
319
|
}
|
|
286
|
-
function renderReferenceField(value, heading, label, descriptionLine) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
320
|
+
function renderReferenceField(field, value, heading, label, descriptionLine, references) {
|
|
321
|
+
const items = (Array.isArray(value) ? value : [value])
|
|
322
|
+
.map((v) => formatReference(field, v, references))
|
|
323
|
+
.filter(Boolean);
|
|
324
|
+
if (items.length === 0)
|
|
325
|
+
return '';
|
|
326
|
+
const body = Array.isArray(value) ? items.map((item) => `- ${item}`).join('\n') : items[0];
|
|
327
|
+
return `${heading} ${label}${descriptionLine}\n\n${body}`;
|
|
292
328
|
}
|
|
293
329
|
function isImageValueLike(value) {
|
|
294
330
|
return (typeof value === 'object' &&
|
|
@@ -297,8 +333,8 @@ function isImageValueLike(value) {
|
|
|
297
333
|
typeof value.src === 'string');
|
|
298
334
|
}
|
|
299
335
|
/**
|
|
300
|
-
* Sanitizes
|
|
301
|
-
* `x](/a) [pwn](https://evil.com)` injects a second attacker-chosen link/image. Strips
|
|
336
|
+
* Sanitizes text for the `[...]` span of `` or `[title](url)`: unescaped, a crafted
|
|
337
|
+
* value like `x](/a) [pwn](https://evil.com)` injects a second attacker-chosen link/image. Strips
|
|
302
338
|
* (rather than escapes) `[`, `]`, backslash, and newlines — losing a stray bracket from
|
|
303
339
|
* descriptive text is harmless, and escaping would break under `escapeTableCell`'s blind
|
|
304
340
|
* backslash-doubling once this lands in a table cell (formatCellValue's image case).
|
|
@@ -339,20 +375,41 @@ function formatImageMarkdown(value, altFallback) {
|
|
|
339
375
|
return `})`;
|
|
340
376
|
}
|
|
341
377
|
/**
|
|
342
|
-
*
|
|
378
|
+
* One reference value as inline markdown: a target the export shows becomes `[title](url)`, its
|
|
379
|
+
* page, then its markdown copy labeled as such, so a reader can tell the two apart; one it does
|
|
380
|
+
* not becomes its id with a marker, so the copy shows the break instead of hiding it. An
|
|
381
|
+
* unavailable target renders from its id alone, whatever else it carries, so no title can leak.
|
|
382
|
+
* A bare string is an id that resolution never replaced.
|
|
343
383
|
*/
|
|
344
|
-
function formatReference(value) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
384
|
+
function formatReference(field, value, references) {
|
|
385
|
+
// `''` is what the editor saves when a reference is cleared: no value, not a broken one.
|
|
386
|
+
if (value === '')
|
|
387
|
+
return '';
|
|
388
|
+
if (typeof value === 'string')
|
|
389
|
+
return `(missing entry ${sanitizeMarkdownAltText(value)})`;
|
|
390
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
391
|
+
return '(missing entry)';
|
|
392
|
+
const target = value;
|
|
393
|
+
const id = typeof target.id === 'string' ? sanitizeMarkdownAltText(target.id) : '';
|
|
394
|
+
if (target[RESTRICTED_REFERENCE_MARKER] === true) {
|
|
395
|
+
const marker = target.reason === 'missing' ? 'missing entry' : 'unavailable entry';
|
|
396
|
+
return id ? `(${marker} ${id})` : `(${marker})`;
|
|
351
397
|
}
|
|
352
|
-
|
|
353
|
-
|
|
398
|
+
const displayField = field.displayField;
|
|
399
|
+
const displayValue = displayField ? target[displayField] : undefined;
|
|
400
|
+
const title = sanitizeMarkdownAltText(typeof displayValue === 'string' && displayValue.trim()
|
|
401
|
+
? displayValue
|
|
402
|
+
: references.title(target)) || id;
|
|
403
|
+
const url = references.url(target);
|
|
404
|
+
if (url === false)
|
|
405
|
+
return title;
|
|
406
|
+
const page = url ? `[${title}](${encodeMarkdownLinkDestination(url)})` : title;
|
|
407
|
+
const markdownUrl = references.markdownUrl?.(target);
|
|
408
|
+
return markdownUrl
|
|
409
|
+
? `${page} ([markdown version](${encodeMarkdownLinkDestination(markdownUrl)}))`
|
|
410
|
+
: page;
|
|
354
411
|
}
|
|
355
|
-
function renderObjectField(field, value, depth, heading, label, descriptionLine, entry,
|
|
412
|
+
function renderObjectField(field, value, depth, heading, label, descriptionLine, entry, opts) {
|
|
356
413
|
if (typeof value !== 'object' || value === null) {
|
|
357
414
|
return `${heading} ${label}${descriptionLine}\n\n${String(value)}`;
|
|
358
415
|
}
|
|
@@ -362,7 +419,7 @@ function renderObjectField(field, value, depth, heading, label, descriptionLine,
|
|
|
362
419
|
const v = obj[f.name];
|
|
363
420
|
if (v === undefined || v === null)
|
|
364
421
|
return '';
|
|
365
|
-
return renderField(f, v, depth + 1, entry,
|
|
422
|
+
return renderField(f, v, depth + 1, entry, opts);
|
|
366
423
|
})
|
|
367
424
|
.filter(Boolean);
|
|
368
425
|
if (subFields.length === 0)
|
|
@@ -372,7 +429,7 @@ function renderObjectField(field, value, depth, heading, label, descriptionLine,
|
|
|
372
429
|
/**
|
|
373
430
|
* Render a block field (array of typed block items).
|
|
374
431
|
*/
|
|
375
|
-
function renderBlockField(field, value, depth, heading, label, descriptionLine, entry,
|
|
432
|
+
function renderBlockField(field, value, depth, heading, label, descriptionLine, entry, opts) {
|
|
376
433
|
if (!Array.isArray(value))
|
|
377
434
|
return '';
|
|
378
435
|
const items = value
|
|
@@ -394,7 +451,7 @@ function renderBlockField(field, value, depth, heading, label, descriptionLine,
|
|
|
394
451
|
const v = blockItem[f.name] ?? blockItem.value?.[f.name];
|
|
395
452
|
if (v === undefined || v === null)
|
|
396
453
|
return '';
|
|
397
|
-
return renderField(f, v, depth + 2, entry,
|
|
454
|
+
return renderField(f, v, depth + 2, entry, opts);
|
|
398
455
|
})
|
|
399
456
|
.filter(Boolean);
|
|
400
457
|
if (blockFields.length === 0)
|
|
@@ -420,11 +477,17 @@ function applyFieldTransform(entry, field, value, config) {
|
|
|
420
477
|
/**
|
|
421
478
|
* Format a value for inline display (metadata lines, list items).
|
|
422
479
|
*/
|
|
423
|
-
function formatInlineValue(field, value) {
|
|
480
|
+
function formatInlineValue(field, value, references) {
|
|
424
481
|
if (field.type === 'boolean')
|
|
425
482
|
return value ? 'Yes' : 'No';
|
|
426
|
-
if (field.type === 'reference')
|
|
427
|
-
return
|
|
483
|
+
if (field.type === 'reference') {
|
|
484
|
+
return Array.isArray(value)
|
|
485
|
+
? value
|
|
486
|
+
.map((v) => formatReference(field, v, references))
|
|
487
|
+
.filter(Boolean)
|
|
488
|
+
.join(', ')
|
|
489
|
+
: formatReference(field, value, references);
|
|
490
|
+
}
|
|
428
491
|
// Without this, an `image` field falls through to `String(value)` below,
|
|
429
492
|
// which stringifies the structured `{ src, alt }` value object as the
|
|
430
493
|
// useless literal text "[object Object]" - hit by MD/MDX frontmatter
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference fields in AI content: resolving them, hiding targets the export leaves out, and the
|
|
3
|
+
* title and URL a resolved one renders with.
|
|
4
|
+
*
|
|
5
|
+
* A reference renders as a link to its target, never as the target's content, so a shared block
|
|
6
|
+
* referenced by forty pages appears once in the export rather than forty times.
|
|
7
|
+
*/
|
|
8
|
+
import type { ContentStore } from '../content-store.js';
|
|
9
|
+
import { type ContentIdIndex } from '../content-id-index.js';
|
|
10
|
+
import type { FieldConfig, FlatSchemaItem } from '../config/index.js';
|
|
11
|
+
import { type EntryLinkUrlResolver } from '../entry-link-resolver.js';
|
|
12
|
+
import type { ReferenceRendering } from './json-to-markdown.js';
|
|
13
|
+
import type { AIEntry, AIReferenceLinkFn, AIResolvedReference, AIReferenceValue } from './types.js';
|
|
14
|
+
export type ReferenceTargetResolver = (id: string) => Promise<AIReferenceValue>;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve reference ids to their targets, reading each target once.
|
|
17
|
+
*
|
|
18
|
+
* Create one per `generateAIContent` call and never keep it: the route handler is long-lived in
|
|
19
|
+
* prod, and a memo held across calls would serve a target as it was when first read.
|
|
20
|
+
* `resolveReferenceTarget` embeds no body, which keeps the target's content out of the export,
|
|
21
|
+
* and resolves an id naming no readable entry to a `MissingReference`, which keeps the id.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createReferenceTargetResolver(store: ContentStore): ReferenceTargetResolver;
|
|
24
|
+
/**
|
|
25
|
+
* A copy of `data` with every reference id replaced by its target, or by a `MissingReference`
|
|
26
|
+
* when the id names no readable entry, so the id survives into the output.
|
|
27
|
+
* A `list: true` field resolves each non-empty string element; any other value, including the
|
|
28
|
+
* `''` of a cleared reference, is left as stored.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveReferenceFields(data: Record<string, unknown>, fields: readonly FieldConfig[], resolveTarget: ReferenceTargetResolver): Promise<Record<string, unknown>>;
|
|
31
|
+
/**
|
|
32
|
+
* Replace, in place, every resolved target that is not itself in this export with an
|
|
33
|
+
* {@link AIExcludedReference}, so no title, URL or field of an excluded entry reaches the
|
|
34
|
+
* output. Runs before each round of entry transforms and rendering, so their final output never
|
|
35
|
+
* carries such a target. Returns whether it replaced anything.
|
|
36
|
+
*/
|
|
37
|
+
export declare function maskUnexportedTargets(entry: AIEntry, exported: {
|
|
38
|
+
has(id: string): boolean;
|
|
39
|
+
}): boolean;
|
|
40
|
+
/** A target's page link as `referenceLink` decided it: a URL, `false` for none, or the default. */
|
|
41
|
+
export type ReferenceLinkLookup = (id: string) => string | false | undefined;
|
|
42
|
+
/** What `referenceLink` is told about one exported target. */
|
|
43
|
+
export interface ReferenceLinkSubject {
|
|
44
|
+
target: AIResolvedReference;
|
|
45
|
+
body: string | undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Ask `referenceLink` once for each exported target the export's entries reference or link from
|
|
49
|
+
* their bodies, in entry order, and return the answers, each URL encoded by
|
|
50
|
+
* {@link encodeLinkUrl}. `subjectOf` builds a target from the export's own read of it, so the
|
|
51
|
+
* callback never sees a target outside the export set, and the body it is shown reaches no linking
|
|
52
|
+
* entry's output. A throw
|
|
53
|
+
* or a return other than a non-empty string, `false` or `undefined` is logged and becomes
|
|
54
|
+
* `false`, so a broken callback withholds a link rather than advertising a page the site may not
|
|
55
|
+
* have. A target whose `entryLinkUrl` throws is left undecided, unasked, so every link to it fails
|
|
56
|
+
* and is reported as it is without the hook.
|
|
57
|
+
*/
|
|
58
|
+
export declare function decideReferenceLinks(referenceLink: AIReferenceLinkFn, entries: ReadonlyArray<{
|
|
59
|
+
entry: AIEntry;
|
|
60
|
+
body?: string;
|
|
61
|
+
}>, subjectOf: (id: string) => ReferenceLinkSubject | undefined, entryLinkUrl: EntryLinkUrlResolver | undefined): Promise<ReferenceLinkLookup>;
|
|
62
|
+
/**
|
|
63
|
+
* Title and URLs for a resolved target. The title is `resolveEntryTitle` against the target's own
|
|
64
|
+
* entry type, the chain the entries API labels an entry with. The page URL is `linkFor`'s
|
|
65
|
+
* decision, else {@link defaultPageUrl}; body `entry:` links follow both, so the two kinds of link
|
|
66
|
+
* in one document agree. The markdown copy is the file
|
|
67
|
+
* this export wrote for the target (`files`: content id to output path), under `mountPath`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function createReferenceRendering(idIndex: ContentIdIndex, flatSchema: readonly FlatSchemaItem[], entryLinkUrl: EntryLinkUrlResolver | undefined, markdownCopies: {
|
|
70
|
+
mountPath: string;
|
|
71
|
+
files: ReadonlyMap<string, string>;
|
|
72
|
+
}, linkFor?: ReferenceLinkLookup): ReferenceRendering;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference fields in AI content: resolving them, hiding targets the export leaves out, and the
|
|
3
|
+
* title and URL a resolved one renders with.
|
|
4
|
+
*
|
|
5
|
+
* A reference renders as a link to its target, never as the target's content, so a shared block
|
|
6
|
+
* referenced by forty pages appears once in the export rather than forty times.
|
|
7
|
+
*/
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { getDefaultEntryType } from '../content-store.js';
|
|
10
|
+
import { extractEntryTypeFromFilename } from '../content-id-index.js';
|
|
11
|
+
import { RESTRICTED_REFERENCE_MARKER } from '../entry-schema.js';
|
|
12
|
+
import { extractEntryLinkIds } from '../entry-link-resolver.js';
|
|
13
|
+
import { trimSlashes } from '../paths/normalize.js';
|
|
14
|
+
import { getErrorMessage } from '../utils/error.js';
|
|
15
|
+
import { flattenGroupFields } from '../utils/flatten-group-fields.js';
|
|
16
|
+
import { resolveEntryTitle } from '../utils/title-field.js';
|
|
17
|
+
import { traverseFields } from '../validation/field-traversal.js';
|
|
18
|
+
/**
|
|
19
|
+
* Resolve reference ids to their targets, reading each target once.
|
|
20
|
+
*
|
|
21
|
+
* Create one per `generateAIContent` call and never keep it: the route handler is long-lived in
|
|
22
|
+
* prod, and a memo held across calls would serve a target as it was when first read.
|
|
23
|
+
* `resolveReferenceTarget` embeds no body, which keeps the target's content out of the export,
|
|
24
|
+
* and resolves an id naming no readable entry to a `MissingReference`, which keeps the id.
|
|
25
|
+
*/
|
|
26
|
+
export function createReferenceTargetResolver(store) {
|
|
27
|
+
const reads = new Map();
|
|
28
|
+
return async (id) => {
|
|
29
|
+
let read = reads.get(id);
|
|
30
|
+
if (!read) {
|
|
31
|
+
read = store.resolveReferenceTarget(id);
|
|
32
|
+
reads.set(id, read);
|
|
33
|
+
}
|
|
34
|
+
// Each occurrence gets its own copy, since masking and adopter transforms may mutate it.
|
|
35
|
+
return structuredClone(await read);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Each reference field's holding record, at every depth: objects, object lists, blocks, groups. */
|
|
39
|
+
function referenceSlots(fields, data) {
|
|
40
|
+
return traverseFields(fields, data, () => [], '', ({ fields: containerFields, data: record }) => flattenGroupFields(containerFields)
|
|
41
|
+
.filter((field) => field.type === 'reference')
|
|
42
|
+
.map((field) => ({ record, field })));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A copy of `data` with every reference id replaced by its target, or by a `MissingReference`
|
|
46
|
+
* when the id names no readable entry, so the id survives into the output.
|
|
47
|
+
* A `list: true` field resolves each non-empty string element; any other value, including the
|
|
48
|
+
* `''` of a cleared reference, is left as stored.
|
|
49
|
+
*/
|
|
50
|
+
export async function resolveReferenceFields(data, fields, resolveTarget) {
|
|
51
|
+
const resolved = structuredClone(data);
|
|
52
|
+
await Promise.all(referenceSlots(fields, resolved).map(async ({ record, field }) => {
|
|
53
|
+
const value = record[field.name];
|
|
54
|
+
if (typeof value === 'string' && value) {
|
|
55
|
+
record[field.name] = await resolveTarget(value);
|
|
56
|
+
}
|
|
57
|
+
else if (field.list && Array.isArray(value)) {
|
|
58
|
+
record[field.name] = await Promise.all(value.map((id) => (typeof id === 'string' && id ? resolveTarget(id) : id)));
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
return resolved;
|
|
62
|
+
}
|
|
63
|
+
function isShownTarget(value) {
|
|
64
|
+
return (typeof value === 'object' &&
|
|
65
|
+
value !== null &&
|
|
66
|
+
!Array.isArray(value) &&
|
|
67
|
+
typeof value.id === 'string' &&
|
|
68
|
+
value[RESTRICTED_REFERENCE_MARKER] !== true);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Replace, in place, every resolved target that is not itself in this export with an
|
|
72
|
+
* {@link AIExcludedReference}, so no title, URL or field of an excluded entry reaches the
|
|
73
|
+
* output. Runs before each round of entry transforms and rendering, so their final output never
|
|
74
|
+
* carries such a target. Returns whether it replaced anything.
|
|
75
|
+
*/
|
|
76
|
+
export function maskUnexportedTargets(entry, exported) {
|
|
77
|
+
let masked = false;
|
|
78
|
+
const mask = (value) => {
|
|
79
|
+
if (!isShownTarget(value) || exported.has(value.id))
|
|
80
|
+
return value;
|
|
81
|
+
masked = true;
|
|
82
|
+
return { id: value.id, unavailable: true, reason: 'excluded' };
|
|
83
|
+
};
|
|
84
|
+
for (const { record, field } of referenceSlots(entry.fields, entry.data)) {
|
|
85
|
+
const value = record[field.name];
|
|
86
|
+
if (value === undefined)
|
|
87
|
+
continue;
|
|
88
|
+
record[field.name] = Array.isArray(value) ? value.map(mask) : mask(value);
|
|
89
|
+
}
|
|
90
|
+
return masked;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The page URL a resolved target links by default: `entryLinkUrl`'s, else its `urlPath`, the rule
|
|
94
|
+
* body `entry:` links use.
|
|
95
|
+
*/
|
|
96
|
+
function defaultPageUrl(target, entryLinkUrl) {
|
|
97
|
+
const { id, slug, collection, urlPath } = target;
|
|
98
|
+
if (entryLinkUrl &&
|
|
99
|
+
typeof id === 'string' &&
|
|
100
|
+
typeof slug === 'string' &&
|
|
101
|
+
typeof collection === 'string') {
|
|
102
|
+
return entryLinkUrl({ collection, slug, id });
|
|
103
|
+
}
|
|
104
|
+
return typeof urlPath === 'string' && urlPath ? urlPath : undefined;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Percent-encode every character that would end a link destination, an autolink or an HTML
|
|
108
|
+
* attribute value early (whitespace, controls, quotes, angle brackets and parentheses) or escape
|
|
109
|
+
* the character after it (a backslash). Body links splice the URL into all three as it is.
|
|
110
|
+
*/
|
|
111
|
+
function encodeLinkUrl(url) {
|
|
112
|
+
let out = '';
|
|
113
|
+
for (const char of url) {
|
|
114
|
+
const code = char.charCodeAt(0);
|
|
115
|
+
out +=
|
|
116
|
+
code <= 0x20 || code === 0x7f || `"'<>()\\`.includes(char)
|
|
117
|
+
? `%${code.toString(16).toUpperCase().padStart(2, '0')}`
|
|
118
|
+
: char;
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Ask `referenceLink` once for each exported target the export's entries reference or link from
|
|
124
|
+
* their bodies, in entry order, and return the answers, each URL encoded by
|
|
125
|
+
* {@link encodeLinkUrl}. `subjectOf` builds a target from the export's own read of it, so the
|
|
126
|
+
* callback never sees a target outside the export set, and the body it is shown reaches no linking
|
|
127
|
+
* entry's output. A throw
|
|
128
|
+
* or a return other than a non-empty string, `false` or `undefined` is logged and becomes
|
|
129
|
+
* `false`, so a broken callback withholds a link rather than advertising a page the site may not
|
|
130
|
+
* have. A target whose `entryLinkUrl` throws is left undecided, unasked, so every link to it fails
|
|
131
|
+
* and is reported as it is without the hook.
|
|
132
|
+
*/
|
|
133
|
+
export async function decideReferenceLinks(referenceLink, entries, subjectOf, entryLinkUrl) {
|
|
134
|
+
const ids = new Set();
|
|
135
|
+
for (const { entry, body } of entries) {
|
|
136
|
+
for (const { record, field } of referenceSlots(entry.fields, entry.data)) {
|
|
137
|
+
const value = record[field.name];
|
|
138
|
+
for (const target of Array.isArray(value) ? value : [value]) {
|
|
139
|
+
if (isShownTarget(target))
|
|
140
|
+
ids.add(target.id);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
for (const { id } of body ? extractEntryLinkIds(body) : [])
|
|
144
|
+
ids.add(id);
|
|
145
|
+
}
|
|
146
|
+
const decisions = new Map();
|
|
147
|
+
for (const id of ids) {
|
|
148
|
+
const subject = subjectOf(id);
|
|
149
|
+
if (!subject)
|
|
150
|
+
continue;
|
|
151
|
+
let defaultUrl;
|
|
152
|
+
try {
|
|
153
|
+
defaultUrl = defaultPageUrl(subject.target, entryLinkUrl);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
let decision;
|
|
159
|
+
try {
|
|
160
|
+
decision = await referenceLink(subject.target, { body: subject.body, defaultUrl });
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
console.warn(`AI content: referenceLink threw for entry ${id}:`, getErrorMessage(err));
|
|
164
|
+
decision = false;
|
|
165
|
+
}
|
|
166
|
+
if (typeof decision === 'string' && decision.trim()) {
|
|
167
|
+
decisions.set(id, encodeLinkUrl(decision.trim()));
|
|
168
|
+
}
|
|
169
|
+
else if (decision === false) {
|
|
170
|
+
decisions.set(id, false);
|
|
171
|
+
}
|
|
172
|
+
else if (decision !== undefined) {
|
|
173
|
+
console.warn(`AI content: referenceLink returned ${typeof decision === 'string' ? 'an empty string' : typeof decision} for entry ${id}; expected a URL, false or undefined`);
|
|
174
|
+
decisions.set(id, false);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return (id) => decisions.get(id);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Title and URLs for a resolved target. The title is `resolveEntryTitle` against the target's own
|
|
181
|
+
* entry type, the chain the entries API labels an entry with. The page URL is `linkFor`'s
|
|
182
|
+
* decision, else {@link defaultPageUrl}; body `entry:` links follow both, so the two kinds of link
|
|
183
|
+
* in one document agree. The markdown copy is the file
|
|
184
|
+
* this export wrote for the target (`files`: content id to output path), under `mountPath`.
|
|
185
|
+
*/
|
|
186
|
+
export function createReferenceRendering(idIndex, flatSchema, entryLinkUrl, markdownCopies, linkFor) {
|
|
187
|
+
const mountSegment = trimSlashes(markdownCopies.mountPath);
|
|
188
|
+
const mount = mountSegment === '' ? '' : `/${mountSegment}`;
|
|
189
|
+
const entryTypeOf = (id) => {
|
|
190
|
+
const location = typeof id === 'string' ? idIndex.findById(id) : null;
|
|
191
|
+
if (!location?.collection)
|
|
192
|
+
return {};
|
|
193
|
+
const item = flatSchema.find((candidate) => candidate.logicalPath === location.collection);
|
|
194
|
+
if (!item)
|
|
195
|
+
return {};
|
|
196
|
+
if (item.type === 'entry-type')
|
|
197
|
+
return { schema: item.schema, label: item.label };
|
|
198
|
+
if (item.type !== 'collection')
|
|
199
|
+
return {};
|
|
200
|
+
const typeName = extractEntryTypeFromFilename(path.basename(location.relativePath));
|
|
201
|
+
const entries = item.entries;
|
|
202
|
+
const entryType = entries?.find((candidate) => candidate.name === typeName) ?? getDefaultEntryType(entries);
|
|
203
|
+
return { schema: entryType?.schema, label: entryType?.label };
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
title: (target) => {
|
|
207
|
+
const { schema, label } = entryTypeOf(target.id);
|
|
208
|
+
return resolveEntryTitle(target, {
|
|
209
|
+
schema,
|
|
210
|
+
entryTypeLabel: label,
|
|
211
|
+
slug: typeof target.slug === 'string' ? target.slug : undefined,
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
url: (target) => {
|
|
215
|
+
const decided = typeof target.id === 'string' ? linkFor?.(target.id) : undefined;
|
|
216
|
+
return decided ?? defaultPageUrl(target, entryLinkUrl);
|
|
217
|
+
},
|
|
218
|
+
markdownUrl: (target) => {
|
|
219
|
+
const file = typeof target.id === 'string' ? markdownCopies.files.get(target.id) : undefined;
|
|
220
|
+
return file ? `${mount}/${file}` : undefined;
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|