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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FieldConfig } from '../config/index.js';
|
|
2
|
+
import type { CommentThread } from '../comment-store.js';
|
|
3
|
+
import { type CanopyPathSegment } from './canopy-path.js';
|
|
4
|
+
/**
|
|
5
|
+
* One list a removal sits inside (a block list, or an object list holding the removed item's
|
|
6
|
+
* list): the index of the item enclosing the removal, and every other item.
|
|
7
|
+
*/
|
|
8
|
+
export interface RemovalAnchor {
|
|
9
|
+
index: number;
|
|
10
|
+
siblings: unknown[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* What a form's Remove took out, for Undo. Paths are canopy paths (`blocks[2].items`), which skip
|
|
14
|
+
* a block's `value` wrapper; `restoreRemoval` resolves them against the value it restores into.
|
|
15
|
+
*/
|
|
16
|
+
export type FieldRemoval = ({
|
|
17
|
+
kind: 'list-item';
|
|
18
|
+
/** The list field's own path. */
|
|
19
|
+
listPath: CanopyPathSegment[];
|
|
20
|
+
index: number;
|
|
21
|
+
/** The removed item itself, so its list-item key comes back with it. */
|
|
22
|
+
item: unknown;
|
|
23
|
+
/** Names the item in the Undo toast. */
|
|
24
|
+
label: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'value';
|
|
27
|
+
path: CanopyPathSegment[];
|
|
28
|
+
value: unknown;
|
|
29
|
+
label: string;
|
|
30
|
+
}) & {
|
|
31
|
+
/**
|
|
32
|
+
* The enclosing lists as they were before the removal (`removalAnchors`). Undo puts nothing
|
|
33
|
+
* back once one has changed, so an item never lands in whichever block now holds its index.
|
|
34
|
+
*/
|
|
35
|
+
anchors?: RemovalAnchor[];
|
|
36
|
+
/** `clear` for an optional object's Clear, which its toast calls clearing, not removing. */
|
|
37
|
+
action?: 'remove' | 'clear';
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The canopy path `restoreRemoval` puts a removal back at: a list item returns to its old index,
|
|
41
|
+
* or to the list's end when the list is now shorter than that.
|
|
42
|
+
*/
|
|
43
|
+
export declare function restoredFieldPath(fields: readonly FieldConfig[], value: Record<string, unknown>, removal: FieldRemoval): CanopyPathSegment[];
|
|
44
|
+
/** The enclosing lists of a removal in `value`, taken before the removal applies; see `anchors`. */
|
|
45
|
+
export declare function removalAnchors(fields: readonly FieldConfig[], value: Record<string, unknown>, removal: FieldRemoval): RemovalAnchor[] | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* `value` with a removal put back, or undefined when it can't be: the list or field is gone, an
|
|
48
|
+
* enclosing list has changed, the item is already there, or the image field has been filled.
|
|
49
|
+
* Edits made after the removal are kept, because it changes only the one list or field, in the
|
|
50
|
+
* value as it is now.
|
|
51
|
+
*/
|
|
52
|
+
export declare function restoreRemoval(fields: readonly FieldConfig[], value: Record<string, unknown>, removal: FieldRemoval): Record<string, unknown> | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* How many open field threads a removal at `path` moves to the entry: those inside it keyed to an
|
|
55
|
+
* item at or below it. One keyed only above stays on its field, which a Clear leaves in place.
|
|
56
|
+
*/
|
|
57
|
+
export declare function openThreadsWithin(threads: readonly CommentThread[], entryPath: string, path: readonly CanopyPathSegment[]): number;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import equal from 'fast-deep-equal';
|
|
2
|
+
import { isPlainRecord } from '../validation/field-traversal.js';
|
|
3
|
+
import { normalizeCanopyPath, parseCanopyPath } from './canopy-path.js';
|
|
4
|
+
const findField = (fields, name) => {
|
|
5
|
+
for (const field of fields) {
|
|
6
|
+
if (field.type === 'group') {
|
|
7
|
+
const inGroup = findField(field.fields, name);
|
|
8
|
+
if (inGroup)
|
|
9
|
+
return inGroup;
|
|
10
|
+
}
|
|
11
|
+
else if (field.name === name) {
|
|
12
|
+
return field;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The path into the stored value for a canopy path, with the enclosing lists it passes through,
|
|
19
|
+
* or undefined when the value no longer has it.
|
|
20
|
+
*/
|
|
21
|
+
const resolvePath = (fields, value, path) => {
|
|
22
|
+
let scope = fields;
|
|
23
|
+
let node = value;
|
|
24
|
+
const out = [];
|
|
25
|
+
const anchors = [];
|
|
26
|
+
const anchor = (list, index) => anchors.push({ index, siblings: list.filter((_, i) => i !== index) });
|
|
27
|
+
let i = 0;
|
|
28
|
+
while (i < path.length) {
|
|
29
|
+
const name = path[i];
|
|
30
|
+
const field = typeof name === 'string' ? findField(scope, name) : undefined;
|
|
31
|
+
if (typeof name !== 'string' || !field)
|
|
32
|
+
return undefined;
|
|
33
|
+
out.push(name);
|
|
34
|
+
node = isPlainRecord(node) ? node[name] : undefined;
|
|
35
|
+
i += 1;
|
|
36
|
+
if (i === path.length)
|
|
37
|
+
return { path: out, anchors };
|
|
38
|
+
const index = path[i];
|
|
39
|
+
if (field.type === 'block') {
|
|
40
|
+
const block = typeof index === 'number' && Array.isArray(node) ? node[index] : undefined;
|
|
41
|
+
if (typeof index !== 'number' || !isPlainRecord(block))
|
|
42
|
+
return undefined;
|
|
43
|
+
const template = field.templates.find((t) => t.name === block.template);
|
|
44
|
+
if (!template || !Array.isArray(node))
|
|
45
|
+
return undefined;
|
|
46
|
+
anchor(node, index);
|
|
47
|
+
out.push(index, 'value');
|
|
48
|
+
node = block.value;
|
|
49
|
+
scope = template.fields;
|
|
50
|
+
i += 1;
|
|
51
|
+
}
|
|
52
|
+
else if (field.type === 'object') {
|
|
53
|
+
const objectField = field;
|
|
54
|
+
if (objectField.list) {
|
|
55
|
+
if (typeof index !== 'number' || !Array.isArray(node))
|
|
56
|
+
return undefined;
|
|
57
|
+
anchor(node, index);
|
|
58
|
+
out.push(index);
|
|
59
|
+
node = node[index];
|
|
60
|
+
i += 1;
|
|
61
|
+
}
|
|
62
|
+
scope = objectField.fields;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { path: out, anchors };
|
|
69
|
+
};
|
|
70
|
+
const getAt = (root, path) => path.reduce((node, segment) => {
|
|
71
|
+
if (typeof segment === 'number')
|
|
72
|
+
return Array.isArray(node) ? node[segment] : undefined;
|
|
73
|
+
return isPlainRecord(node) ? node[segment] : undefined;
|
|
74
|
+
}, root);
|
|
75
|
+
const setAt = (root, path, next) => {
|
|
76
|
+
if (path.length === 0)
|
|
77
|
+
return next;
|
|
78
|
+
const [head, ...rest] = path;
|
|
79
|
+
if (typeof head === 'number') {
|
|
80
|
+
const list = Array.isArray(root) ? [...root] : [];
|
|
81
|
+
list[head] = setAt(list[head], rest, next);
|
|
82
|
+
return list;
|
|
83
|
+
}
|
|
84
|
+
const record = isPlainRecord(root) ? root : {};
|
|
85
|
+
return { ...record, [head]: setAt(record[head], rest, next) };
|
|
86
|
+
};
|
|
87
|
+
/** Equal once stored: JSON drops `undefined` keys, as the server's copy of a draft does. */
|
|
88
|
+
const sameContent = (a, b) => equal(JSON.parse(JSON.stringify(a ?? null)), JSON.parse(JSON.stringify(b ?? null)));
|
|
89
|
+
const removalPath = (removal) => removal.kind === 'list-item' ? removal.listPath : removal.path;
|
|
90
|
+
const anchorsHold = (then, now) => then === undefined ||
|
|
91
|
+
(then.length === now.length &&
|
|
92
|
+
then.every((anchor, i) => anchor.index === now[i].index &&
|
|
93
|
+
// By content as stored, so the server's copy of the same items still holds.
|
|
94
|
+
sameContent(anchor.siblings, now[i].siblings)));
|
|
95
|
+
/**
|
|
96
|
+
* The canopy path `restoreRemoval` puts a removal back at: a list item returns to its old index,
|
|
97
|
+
* or to the list's end when the list is now shorter than that.
|
|
98
|
+
*/
|
|
99
|
+
export function restoredFieldPath(fields, value, removal) {
|
|
100
|
+
if (removal.kind !== 'list-item')
|
|
101
|
+
return removal.path;
|
|
102
|
+
const resolved = resolvePath(fields, value, removal.listPath);
|
|
103
|
+
const list = resolved ? getAt(value, resolved.path) : undefined;
|
|
104
|
+
return [...removal.listPath, Math.min(removal.index, Array.isArray(list) ? list.length : 0)];
|
|
105
|
+
}
|
|
106
|
+
/** The enclosing lists of a removal in `value`, taken before the removal applies; see `anchors`. */
|
|
107
|
+
export function removalAnchors(fields, value, removal) {
|
|
108
|
+
return resolvePath(fields, value, removalPath(removal))?.anchors;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* `value` with a removal put back, or undefined when it can't be: the list or field is gone, an
|
|
112
|
+
* enclosing list has changed, the item is already there, or the image field has been filled.
|
|
113
|
+
* Edits made after the removal are kept, because it changes only the one list or field, in the
|
|
114
|
+
* value as it is now.
|
|
115
|
+
*/
|
|
116
|
+
export function restoreRemoval(fields, value, removal) {
|
|
117
|
+
const resolved = resolvePath(fields, value, removalPath(removal));
|
|
118
|
+
if (!resolved || !anchorsHold(removal.anchors, resolved.anchors))
|
|
119
|
+
return undefined;
|
|
120
|
+
const { path } = resolved;
|
|
121
|
+
// An enclosing object cleared since would come back holding only this.
|
|
122
|
+
if (!isPlainRecord(path.length > 1 ? getAt(value, path.slice(0, -1)) : value))
|
|
123
|
+
return undefined;
|
|
124
|
+
const current = getAt(value, path);
|
|
125
|
+
let next;
|
|
126
|
+
if (removal.kind === 'list-item') {
|
|
127
|
+
if (current !== undefined && current !== null && !Array.isArray(current))
|
|
128
|
+
return undefined;
|
|
129
|
+
// Already back (a Discard or Reload restored it, or an earlier Undo did).
|
|
130
|
+
if (Array.isArray(current) && current.some((item) => sameContent(item, removal.item))) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const list = Array.isArray(current) ? [...current] : [];
|
|
134
|
+
list.splice(Math.min(removal.index, list.length), 0, removal.item);
|
|
135
|
+
next = list;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
if (current !== undefined && current !== null)
|
|
139
|
+
return undefined;
|
|
140
|
+
next = removal.value;
|
|
141
|
+
}
|
|
142
|
+
const restored = setAt(value, path, next);
|
|
143
|
+
return isPlainRecord(restored) ? restored : undefined;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* How many open field threads a removal at `path` moves to the entry: those inside it keyed to an
|
|
147
|
+
* item at or below it. One keyed only above stays on its field, which a Clear leaves in place.
|
|
148
|
+
*/
|
|
149
|
+
export function openThreadsWithin(threads, entryPath, path) {
|
|
150
|
+
const prefix = parseCanopyPath(normalizeCanopyPath(path));
|
|
151
|
+
const isIndex = (segment) => typeof segment === 'number';
|
|
152
|
+
const itemsAbove = prefix.filter(isIndex).length - (isIndex(prefix[prefix.length - 1]) ? 1 : 0);
|
|
153
|
+
return threads.filter((thread) => {
|
|
154
|
+
if (thread.type !== 'field' || thread.resolved || thread.onRemovedItem)
|
|
155
|
+
return false;
|
|
156
|
+
if (thread.entryPath !== entryPath || !thread.canopyPath)
|
|
157
|
+
return false;
|
|
158
|
+
if ((thread.itemKeys?.length ?? 0) <= itemsAbove)
|
|
159
|
+
return false;
|
|
160
|
+
const segments = parseCanopyPath(thread.canopyPath);
|
|
161
|
+
return prefix.length <= segments.length && prefix.every((s, i) => segments[i] === s);
|
|
162
|
+
}).length;
|
|
163
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** What the editor calls a user id CanopyCMS itself acts as (system-users.ts). */
|
|
2
|
+
export declare const SYSTEM_USER_LABEL = "CanopyCMS bot";
|
|
3
|
+
/** A user id as text where no name lookup is available: a bot by its label, anyone else by id. */
|
|
4
|
+
export declare const userIdLabel: (userId: string) => string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { isSystemUserId } from '../system-users.js';
|
|
2
|
+
/** What the editor calls a user id CanopyCMS itself acts as (system-users.ts). */
|
|
3
|
+
export const SYSTEM_USER_LABEL = 'CanopyCMS bot';
|
|
4
|
+
/** A user id as text where no name lookup is available: a bot by its label, anyone else by id. */
|
|
5
|
+
export const userIdLabel = (userId) => isSystemUserId(userId) ? SYSTEM_USER_LABEL : userId;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { modals } from '@mantine/modals';
|
|
2
|
+
type ConfirmModalProps = Parameters<typeof modals.openConfirmModal>[0];
|
|
3
|
+
/**
|
|
4
|
+
* `modals.openConfirmModal` with `sm` actions: its own Buttons would otherwise take the theme's
|
|
5
|
+
* compact `xs` default. A `size` the caller sets replaces it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function openConfirm(props: ConfirmModalProps): string;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { modals } from '@mantine/modals';
|
|
2
|
+
/**
|
|
3
|
+
* `modals.openConfirmModal` with `sm` actions: its own Buttons would otherwise take the theme's
|
|
4
|
+
* compact `xs` default. A `size` the caller sets replaces it.
|
|
5
|
+
*/
|
|
6
|
+
export function openConfirm(props) {
|
|
7
|
+
return modals.openConfirmModal({
|
|
8
|
+
...props,
|
|
9
|
+
confirmProps: Object.assign({ size: 'sm' }, props.confirmProps),
|
|
10
|
+
cancelProps: Object.assign({ size: 'sm' }, props.cancelProps),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Where in the editor an error was caught, for the log line and the copied details. */
|
|
2
|
+
export interface EditorErrorContext {
|
|
3
|
+
/** `editor` for the crash screen, `field` for a field boundary, `rich-text` for MDXEditor. */
|
|
4
|
+
boundary: 'editor' | 'field' | 'rich-text';
|
|
5
|
+
/** The field's canopy path, for a field or rich-text boundary. */
|
|
6
|
+
fieldPath?: string;
|
|
7
|
+
/** React's component stack, when React supplied one. */
|
|
8
|
+
componentStack?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The single point every error an editor boundary catches passes through. Logs it, because a
|
|
12
|
+
* boundary must never hide a crash; an adopter-facing report hook belongs here too.
|
|
13
|
+
*/
|
|
14
|
+
export declare function reportEditorError(error: unknown, context: EditorErrorContext): void;
|
|
15
|
+
/**
|
|
16
|
+
* The text "Copy error details" puts on the clipboard. Sanitised, because an author pastes it
|
|
17
|
+
* into a chat or a ticket: stack frames can name local paths, and a message can carry a token.
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatErrorDetails(error: unknown, context: EditorErrorContext): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getErrorMessage, sanitizeErrorMessage } from '../../utils/error.js';
|
|
2
|
+
/**
|
|
3
|
+
* The single point every error an editor boundary catches passes through. Logs it, because a
|
|
4
|
+
* boundary must never hide a crash; an adopter-facing report hook belongs here too.
|
|
5
|
+
*/
|
|
6
|
+
export function reportEditorError(error, context) {
|
|
7
|
+
const where = context.fieldPath ? `${context.boundary} ${context.fieldPath}` : context.boundary;
|
|
8
|
+
console.error(`[canopycms] editor error caught (${where})`, error, context.componentStack ?? '');
|
|
9
|
+
}
|
|
10
|
+
const MAX_DETAILS_LENGTH = 8000;
|
|
11
|
+
/**
|
|
12
|
+
* The text "Copy error details" puts on the clipboard. Sanitised, because an author pastes it
|
|
13
|
+
* into a chat or a ticket: stack frames can name local paths, and a message can carry a token.
|
|
14
|
+
*/
|
|
15
|
+
export function formatErrorDetails(error, context) {
|
|
16
|
+
const lines = [
|
|
17
|
+
'CanopyCMS editor error',
|
|
18
|
+
`Time: ${new Date().toISOString()}`,
|
|
19
|
+
`Caught by: ${context.boundary}${context.fieldPath ? ` (${context.fieldPath})` : ''}`,
|
|
20
|
+
`Error: ${error instanceof Error ? `${error.name}: ${error.message}` : getErrorMessage(error)}`,
|
|
21
|
+
];
|
|
22
|
+
if (typeof navigator !== 'undefined')
|
|
23
|
+
lines.push(`Browser: ${navigator.userAgent}`);
|
|
24
|
+
if (error instanceof Error && error.stack)
|
|
25
|
+
lines.push('', 'Stack:', error.stack);
|
|
26
|
+
if (context.componentStack)
|
|
27
|
+
lines.push('', 'Component stack:', context.componentStack.trim());
|
|
28
|
+
const details = sanitizeErrorMessage(lines.join('\n'));
|
|
29
|
+
return details.length > MAX_DETAILS_LENGTH
|
|
30
|
+
? `${details.slice(0, MAX_DETAILS_LENGTH)}\n[truncated]`
|
|
31
|
+
: details;
|
|
32
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type MouseEvent, type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Mantine's own `returnFocus` records the focused element at open, and a menu item has unmounted
|
|
4
|
+
* with its menu by close, so an overlay opened from one returns focus here instead.
|
|
5
|
+
* `FOCUS_RETURN_ATTR` marks an overlay whose exit callback does; `focusIfIdle` treats focus inside it as idle.
|
|
6
|
+
*/
|
|
7
|
+
export declare const FOCUS_RETURN_ATTR = "data-focus-return";
|
|
8
|
+
/**
|
|
9
|
+
* Focuses `target` unless the user has moved focus on: nothing is focused, or focus is inside an
|
|
10
|
+
* overlay `FOCUS_RETURN_ATTR` marks, as a closing one is until after its exit callback.
|
|
11
|
+
*/
|
|
12
|
+
export declare function focusIfIdle(target: HTMLElement | null): void;
|
|
13
|
+
/**
|
|
14
|
+
* Run by `useMenuItemFocusReturn` when its menu has closed after an item acted: focuses the menu's
|
|
15
|
+
* `target` trigger once the item has unmounted and dropped focus to the page. Mantine calls it just before
|
|
16
|
+
* unmounting the items, hence the frame's wait; `returnFocus` would instead steal focus from an
|
|
17
|
+
* overlay the item opened, which holds it by then.
|
|
18
|
+
* @internal Exported for tests.
|
|
19
|
+
*/
|
|
20
|
+
export declare function focusIfDropped(target: HTMLElement | null): void;
|
|
21
|
+
/**
|
|
22
|
+
* Props for a Mantine `Menu` and its `Menu.Dropdown` that run `focusIfDropped` on `trigger` after an
|
|
23
|
+
* item acted. A menu dismissed by a press elsewhere keeps focus where that press left it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function useMenuItemFocusReturn(trigger: RefObject<HTMLElement | null>): {
|
|
26
|
+
menu: {
|
|
27
|
+
onExitTransitionEnd: () => void;
|
|
28
|
+
};
|
|
29
|
+
dropdown: {
|
|
30
|
+
onClickCapture: (event: MouseEvent) => void;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/** The focused control to hand focus back to, or null when it is a menu item (gone once it acts) or the page. */
|
|
34
|
+
export declare function rememberOpener(): HTMLElement | null;
|
|
35
|
+
/** The remembered opener while it is still on the page, else the menu trigger that stands in for it. */
|
|
36
|
+
export declare function returnTarget(opener: HTMLElement | null, standIn: () => HTMLElement | null): HTMLElement | null;
|
|
37
|
+
/**
|
|
38
|
+
* Props that make a Mantine `Modal`, `Drawer` or `modals.openConfirmModal` focus `returnFocusTo()`
|
|
39
|
+
* once it has closed; none, keeping Mantine's default, without one.
|
|
40
|
+
*/
|
|
41
|
+
export declare function focusReturnProps(returnFocusTo?: () => HTMLElement | null): {
|
|
42
|
+
returnFocus?: false;
|
|
43
|
+
onExitTransitionEnd?: () => void;
|
|
44
|
+
[FOCUS_RETURN_ATTR]?: true;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Focus for a list of removable badges, each with a `Remove …` button. The button that removes an
|
|
48
|
+
* item is gone once it acts, so `focusAfterRemove(index, fallback)` moves focus, once the list has
|
|
49
|
+
* re-rendered without the item, to the Remove button now at that index (the next item's, else the
|
|
50
|
+
* previous one's), else to the `fallback` control: the section's "Add …" button, or whatever stands
|
|
51
|
+
* in for it while that is hidden or disabled. Put `listRef` on the element holding the badges.
|
|
52
|
+
*/
|
|
53
|
+
export declare function useFocusAfterRemove<L extends HTMLElement = HTMLDivElement>(): {
|
|
54
|
+
listRef: RefObject<L>;
|
|
55
|
+
focusAfterRemove: (index: number, fallback: RefObject<HTMLElement | null>) => void;
|
|
56
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Mantine's own `returnFocus` records the focused element at open, and a menu item has unmounted
|
|
4
|
+
* with its menu by close, so an overlay opened from one returns focus here instead.
|
|
5
|
+
* `FOCUS_RETURN_ATTR` marks an overlay whose exit callback does; `focusIfIdle` treats focus inside it as idle.
|
|
6
|
+
*/
|
|
7
|
+
export const FOCUS_RETURN_ATTR = 'data-focus-return';
|
|
8
|
+
/**
|
|
9
|
+
* Focuses `target` unless the user has moved focus on: nothing is focused, or focus is inside an
|
|
10
|
+
* overlay `FOCUS_RETURN_ATTR` marks, as a closing one is until after its exit callback.
|
|
11
|
+
*/
|
|
12
|
+
export function focusIfIdle(target) {
|
|
13
|
+
const active = document.activeElement;
|
|
14
|
+
if (!active || active === document.body || active.closest(`[${FOCUS_RETURN_ATTR}]`)) {
|
|
15
|
+
target?.focus({ preventScroll: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run by `useMenuItemFocusReturn` when its menu has closed after an item acted: focuses the menu's
|
|
20
|
+
* `target` trigger once the item has unmounted and dropped focus to the page. Mantine calls it just before
|
|
21
|
+
* unmounting the items, hence the frame's wait; `returnFocus` would instead steal focus from an
|
|
22
|
+
* overlay the item opened, which holds it by then.
|
|
23
|
+
* @internal Exported for tests.
|
|
24
|
+
*/
|
|
25
|
+
export function focusIfDropped(target) {
|
|
26
|
+
requestAnimationFrame(() => {
|
|
27
|
+
const active = document.activeElement;
|
|
28
|
+
if (!active || active === document.body)
|
|
29
|
+
target?.focus({ preventScroll: true });
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Props for a Mantine `Menu` and its `Menu.Dropdown` that run `focusIfDropped` on `trigger` after an
|
|
34
|
+
* item acted. A menu dismissed by a press elsewhere keeps focus where that press left it.
|
|
35
|
+
*/
|
|
36
|
+
export function useMenuItemFocusReturn(trigger) {
|
|
37
|
+
const acted = useRef(false);
|
|
38
|
+
return {
|
|
39
|
+
menu: {
|
|
40
|
+
onExitTransitionEnd: () => {
|
|
41
|
+
if (acted.current)
|
|
42
|
+
focusIfDropped(trigger.current);
|
|
43
|
+
acted.current = false;
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
dropdown: {
|
|
47
|
+
onClickCapture: (event) => {
|
|
48
|
+
if (event.target instanceof Element &&
|
|
49
|
+
event.target.closest('[role="menuitem"]:not([aria-disabled="true"])')) {
|
|
50
|
+
acted.current = true;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** The focused control to hand focus back to, or null when it is a menu item (gone once it acts) or the page. */
|
|
57
|
+
export function rememberOpener() {
|
|
58
|
+
const active = document.activeElement;
|
|
59
|
+
return active instanceof HTMLElement &&
|
|
60
|
+
active !== document.body &&
|
|
61
|
+
!active.closest('[role="menuitem"]')
|
|
62
|
+
? active
|
|
63
|
+
: null;
|
|
64
|
+
}
|
|
65
|
+
/** The remembered opener while it is still on the page, else the menu trigger that stands in for it. */
|
|
66
|
+
export function returnTarget(opener, standIn) {
|
|
67
|
+
return opener?.isConnected ? opener : standIn();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Props that make a Mantine `Modal`, `Drawer` or `modals.openConfirmModal` focus `returnFocusTo()`
|
|
71
|
+
* once it has closed; none, keeping Mantine's default, without one.
|
|
72
|
+
*/
|
|
73
|
+
export function focusReturnProps(returnFocusTo) {
|
|
74
|
+
if (!returnFocusTo)
|
|
75
|
+
return {};
|
|
76
|
+
return {
|
|
77
|
+
returnFocus: false,
|
|
78
|
+
onExitTransitionEnd: () => focusIfIdle(returnFocusTo()),
|
|
79
|
+
[FOCUS_RETURN_ATTR]: true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Focus for a list of removable badges, each with a `Remove …` button. The button that removes an
|
|
84
|
+
* item is gone once it acts, so `focusAfterRemove(index, fallback)` moves focus, once the list has
|
|
85
|
+
* re-rendered without the item, to the Remove button now at that index (the next item's, else the
|
|
86
|
+
* previous one's), else to the `fallback` control: the section's "Add …" button, or whatever stands
|
|
87
|
+
* in for it while that is hidden or disabled. Put `listRef` on the element holding the badges.
|
|
88
|
+
*/
|
|
89
|
+
export function useFocusAfterRemove() {
|
|
90
|
+
const listRef = useRef(null);
|
|
91
|
+
const [pending, setPending] = useState(null);
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (!pending)
|
|
94
|
+
return;
|
|
95
|
+
const removeButtons = listRef.current?.querySelectorAll('button[aria-label^="Remove "]');
|
|
96
|
+
const target = removeButtons && removeButtons.length > 0
|
|
97
|
+
? removeButtons[Math.min(pending.index, removeButtons.length - 1)]
|
|
98
|
+
: pending.fallback.current;
|
|
99
|
+
target?.focus();
|
|
100
|
+
setPending(null);
|
|
101
|
+
}, [pending]);
|
|
102
|
+
return {
|
|
103
|
+
listRef,
|
|
104
|
+
focusAfterRemove: (index, fallback) => setPending({ index, fallback }),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -23,12 +23,22 @@ export type EntryLinkUrlResolver = (entry: {
|
|
|
23
23
|
}) => string;
|
|
24
24
|
/** URL path for an entry's location in the content tree; delegates to `computeEntryUrl`. */
|
|
25
25
|
export declare function resolveEntryUrl(location: Pick<IdLocation, 'collection' | 'slug'>, contentRoot: string): string;
|
|
26
|
+
export interface ResolveEntryLinksOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Whether a link to this content id may show its target. When set, a link to a target it
|
|
29
|
+
* refuses, or to a missing one, shows nothing of that target's URL or slug: an inline
|
|
30
|
+
* markdown link (`[text](entry:ID)`, or an image) becomes its bare text, and any other form
|
|
31
|
+
* becomes `#`.
|
|
32
|
+
*/
|
|
33
|
+
isLinkable?: (id: string) => boolean;
|
|
34
|
+
}
|
|
26
35
|
/**
|
|
27
36
|
* Replace `entry:CONTENT_ID` tokens with resolved URL paths, skipping code blocks and
|
|
28
37
|
* spans so examples mentioning the syntax survive. A missing ID becomes "#" (dead link)
|
|
29
38
|
* plus a warning; an anchor fragment is preserved (`entry:ID#heading` => `/path#heading`).
|
|
39
|
+
* See {@link ResolveEntryLinksOptions.isLinkable} for links to a target the caller withholds.
|
|
30
40
|
*/
|
|
31
|
-
export declare function resolveEntryLinksInText(text: string, idIndex: ContentIdIndex, contentRoot: string, customResolver?: EntryLinkUrlResolver): string;
|
|
41
|
+
export declare function resolveEntryLinksInText(text: string, idIndex: ContentIdIndex, contentRoot: string, customResolver?: EntryLinkUrlResolver, options?: ResolveEntryLinksOptions): string;
|
|
32
42
|
/** Quick-check pattern for early bail-out (no `g` flag, safe for `.test()`). */
|
|
33
43
|
export declare const ENTRY_LINK_QUICK_CHECK: RegExp;
|
|
34
44
|
/**
|
|
@@ -27,19 +27,26 @@ export function resolveEntryUrl(location, contentRoot) {
|
|
|
27
27
|
* Replace `entry:CONTENT_ID` tokens with resolved URL paths, skipping code blocks and
|
|
28
28
|
* spans so examples mentioning the syntax survive. A missing ID becomes "#" (dead link)
|
|
29
29
|
* plus a warning; an anchor fragment is preserved (`entry:ID#heading` => `/path#heading`).
|
|
30
|
+
* See {@link ResolveEntryLinksOptions.isLinkable} for links to a target the caller withholds.
|
|
30
31
|
*/
|
|
31
|
-
export function resolveEntryLinksInText(text, idIndex, contentRoot, customResolver) {
|
|
32
|
+
export function resolveEntryLinksInText(text, idIndex, contentRoot, customResolver, options = {}) {
|
|
33
|
+
const { isLinkable } = options;
|
|
32
34
|
const parts = splitByCodeRegions(text);
|
|
33
35
|
return parts
|
|
34
36
|
.map((part) => {
|
|
35
37
|
if (part.isCode)
|
|
36
38
|
return part.text;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
const partText = isLinkable
|
|
40
|
+
? unwrapInlineLinks(part.text, (id) => !isLinkable(id) || !findEntry(idIndex, id))
|
|
41
|
+
: part.text;
|
|
42
|
+
return partText.replace(ENTRY_LINK_PATTERN, (_match, id, anchor) => {
|
|
43
|
+
const location = findEntry(idIndex, id);
|
|
44
|
+
if (!location) {
|
|
40
45
|
log.warn('resolve', `Entry link target not found: entry:${id}`);
|
|
41
|
-
return anchor ?? '#';
|
|
46
|
+
return isLinkable ? '#' : (anchor ?? '#');
|
|
42
47
|
}
|
|
48
|
+
if (isLinkable && !isLinkable(id))
|
|
49
|
+
return '#';
|
|
43
50
|
let url;
|
|
44
51
|
if (customResolver) {
|
|
45
52
|
url = customResolver({
|
|
@@ -56,6 +63,59 @@ export function resolveEntryLinksInText(text, idIndex, contentRoot, customResolv
|
|
|
56
63
|
})
|
|
57
64
|
.join('');
|
|
58
65
|
}
|
|
66
|
+
function findEntry(idIndex, id) {
|
|
67
|
+
const location = idIndex.findById(id);
|
|
68
|
+
if (!location || location.type !== 'entry' || !location.collection || !location.slug) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return location;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* `](entry:ID#anchor "title")`: the target half of an inline markdown link. A match starts only at
|
|
75
|
+
* a literal `](entry:`, its anchor stops at `]` and its title at the next `"`, so failed matches
|
|
76
|
+
* scan stretches that don't overlap, keeping the scan linear.
|
|
77
|
+
*/
|
|
78
|
+
const INLINE_LINK_TARGET = new RegExp(`\\]\\(entry:(${BASE58_CHAR}{12})(?:#[^\\s)>"'\\]]*)?(?:[ \\t]+"[^"\\n]*")?\\)`, 'g');
|
|
79
|
+
/**
|
|
80
|
+
* Replace each inline markdown link (or image) whose `entry:` target `shouldUnwrap` names with
|
|
81
|
+
* its bracketed text. A link whose opening `[` can't be found is left alone, for the caller's
|
|
82
|
+
* token pass to neutralize.
|
|
83
|
+
*/
|
|
84
|
+
function unwrapInlineLinks(text, shouldUnwrap) {
|
|
85
|
+
let out = '';
|
|
86
|
+
let cursor = 0;
|
|
87
|
+
// A failed search from `close` saw no `[` it could match before it, and a later `]` only adds
|
|
88
|
+
// depth, so later searches stop there: the bracket scans stay linear in the text.
|
|
89
|
+
let scanFloor = 0;
|
|
90
|
+
for (const match of text.matchAll(INLINE_LINK_TARGET)) {
|
|
91
|
+
const close = match.index;
|
|
92
|
+
if (close < cursor || !shouldUnwrap(match[1]))
|
|
93
|
+
continue;
|
|
94
|
+
const open = findOpeningBracket(text, close, Math.max(cursor, scanFloor));
|
|
95
|
+
if (open === -1) {
|
|
96
|
+
scanFloor = close;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const isImage = open > cursor && text[open - 1] === '!' && text[open - 2] !== '\\';
|
|
100
|
+
const start = isImage ? open - 1 : open;
|
|
101
|
+
out += text.slice(cursor, start) + text.slice(open + 1, close);
|
|
102
|
+
cursor = close + match[0].length;
|
|
103
|
+
}
|
|
104
|
+
return out + text.slice(cursor);
|
|
105
|
+
}
|
|
106
|
+
/** The `[` matching the `]` at `close`, searching back no further than `floor`; -1 if none. */
|
|
107
|
+
function findOpeningBracket(text, close, floor) {
|
|
108
|
+
let depth = 1;
|
|
109
|
+
for (let i = close - 1; i >= floor; i--) {
|
|
110
|
+
if (i > floor && text[i - 1] === '\\')
|
|
111
|
+
continue;
|
|
112
|
+
if (text[i] === ']')
|
|
113
|
+
depth++;
|
|
114
|
+
else if (text[i] === '[' && --depth === 0)
|
|
115
|
+
return i;
|
|
116
|
+
}
|
|
117
|
+
return -1;
|
|
118
|
+
}
|
|
59
119
|
/** Quick-check pattern for early bail-out (no `g` flag, safe for `.test()`). */
|
|
60
120
|
export const ENTRY_LINK_QUICK_CHECK = /entry:[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{12}/;
|
|
61
121
|
/**
|
|
@@ -16,9 +16,9 @@ import type { EntrySchemaRegistry } from './schema/types.js';
|
|
|
16
16
|
* array; more than one `isTitle` per schema, or one on a non-string field or inside a list;
|
|
17
17
|
* more than one `isBody`, or one on a field that is not markdown/mdx or is named one of
|
|
18
18
|
* `RESOLVED_REFERENCE_KEYS`; a top-level or inline-group field named `unavailable`. This is also the one
|
|
19
|
-
* place the shared field-shape checks run: select fields must have options,
|
|
20
|
-
* reference fields must have `collections` or `entryTypes`, no inline groups
|
|
21
|
-
*
|
|
19
|
+
* place the shared field-shape checks run: field names a path can spell, select fields must have options,
|
|
20
|
+
* reference fields must have `collections` or `entryTypes`, no inline groups inside object/block fields,
|
|
21
|
+
* no field-name collisions after group flattening, valid `mdxAllow`, `stampOnPublish` placement.
|
|
22
22
|
*
|
|
23
23
|
* @example
|
|
24
24
|
* ```typescript
|
|
@@ -2,7 +2,7 @@ import { countTitleFields, findInvalidTitleFields, findTitleFieldsInLists, } fro
|
|
|
2
2
|
import { countBodyFields, findInvalidBodyFields, findReservedBodyFieldName, } from './utils/body-field.js';
|
|
3
3
|
import { RESOLVED_REFERENCE_KEYS, RESTRICTED_REFERENCE_MARKER } from './entry-schema.js';
|
|
4
4
|
import { flattenGroupFields } from './utils/flatten-group-fields.js';
|
|
5
|
-
import { ensureSelectFieldsHaveOptions, ensureReferenceFieldsHaveScope, ensureItemTitleFieldsExist, ensureNoFlattenedFieldNameCollisions, ensureNoGroupsInsideComplexFields, } from './config/validation.js';
|
|
5
|
+
import { ensureFieldNamesSpellInPaths, ensureSelectFieldsHaveOptions, ensureReferenceFieldsHaveScope, ensureItemTitleFieldsExist, ensureNoFlattenedFieldNameCollisions, ensureNoGroupsInsideComplexFields, ensureMarkdownFieldOptions, ensureStampOnPublishPlacement, } from './config/validation.js';
|
|
6
6
|
/** Look up a field's type by dotted path (e.g., "meta.order").
|
|
7
7
|
* Groups are transparent — their children are searched at the same path level. */
|
|
8
8
|
function findFieldType(fields, dottedPath) {
|
|
@@ -39,9 +39,9 @@ function findFieldType(fields, dottedPath) {
|
|
|
39
39
|
* array; more than one `isTitle` per schema, or one on a non-string field or inside a list;
|
|
40
40
|
* more than one `isBody`, or one on a field that is not markdown/mdx or is named one of
|
|
41
41
|
* `RESOLVED_REFERENCE_KEYS`; a top-level or inline-group field named `unavailable`. This is also the one
|
|
42
|
-
* place the shared field-shape checks run: select fields must have options,
|
|
43
|
-
* reference fields must have `collections` or `entryTypes`, no inline groups
|
|
44
|
-
*
|
|
42
|
+
* place the shared field-shape checks run: field names a path can spell, select fields must have options,
|
|
43
|
+
* reference fields must have `collections` or `entryTypes`, no inline groups inside object/block fields,
|
|
44
|
+
* no field-name collisions after group flattening, valid `mdxAllow`, `stampOnPublish` placement.
|
|
45
45
|
*
|
|
46
46
|
* @example
|
|
47
47
|
* ```typescript
|
|
@@ -95,10 +95,13 @@ export function createEntrySchemaRegistry(registry) {
|
|
|
95
95
|
if (flattenGroupFields(schema).some((f) => f.name === RESTRICTED_REFERENCE_MARKER)) {
|
|
96
96
|
throw new Error(`Entry schema registry entry "${key}": field "${RESTRICTED_REFERENCE_MARKER}" is reserved — a resolved reference carries "${RESTRICTED_REFERENCE_MARKER}: true" only when the reader may not read its target, so a field with that name cannot be delivered on a reference to this entry type. Rename the field.`);
|
|
97
97
|
}
|
|
98
|
+
ensureFieldNamesSpellInPaths(schema);
|
|
98
99
|
ensureSelectFieldsHaveOptions(schema);
|
|
99
100
|
ensureReferenceFieldsHaveScope(schema);
|
|
100
101
|
ensureItemTitleFieldsExist(schema);
|
|
101
102
|
ensureNoGroupsInsideComplexFields(schema);
|
|
103
|
+
ensureMarkdownFieldOptions(schema);
|
|
104
|
+
ensureStampOnPublishPlacement(schema, `Entry schema registry entry "${key}"`);
|
|
102
105
|
ensureNoFlattenedFieldNameCollisions(schema, `entry schema "${key}"`);
|
|
103
106
|
}
|
|
104
107
|
return registry;
|