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
package/dist/api/comments.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { CommentStore } from '../comment-store.js';
|
|
3
|
+
import { lookupUserProfiles } from '../auth/user-metadata-lookup.js';
|
|
4
|
+
import { getErrorMessage } from '../utils/error.js';
|
|
3
5
|
import { isReviewer } from '../authorization/index.js';
|
|
6
|
+
import { parseLogicalPath } from '../paths/validation.js';
|
|
7
|
+
import { normalizeFilesystemPath } from '../paths/normalize.js';
|
|
4
8
|
import { defineEndpoint } from './route-builder.js';
|
|
9
|
+
import { isItemKey } from '../item-keys.js';
|
|
10
|
+
import { parseCanopyPath } from '../editor/canopy-path.js';
|
|
5
11
|
import { branchParamSchema, branchNameSchema, logicalPathSchema } from './validators.js';
|
|
6
12
|
// ============================================================================
|
|
7
13
|
// Zod Schemas for Validation
|
|
@@ -16,14 +22,76 @@ const addCommentBodySchema = z.object({
|
|
|
16
22
|
type: z.enum(['field', 'entry', 'branch']),
|
|
17
23
|
entryPath: logicalPathSchema.optional(),
|
|
18
24
|
canopyPath: z.string().optional(), // Canopy field path, not a file path
|
|
25
|
+
itemKeys: z.array(z.string().refine(isItemKey, 'not an item key')).max(32).optional(),
|
|
19
26
|
});
|
|
20
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Whether `entryPath` is spelled the way path rules expect an entry's logical path: rooted at
|
|
29
|
+
* the content root, with no empty or `.` segment. Rules are globs over that spelling, so
|
|
30
|
+
* `secret/plan` (which the content API reads as `content/secret/plan`) and
|
|
31
|
+
* `content/./secret/plan` both miss a rule written for `content/secret/**`.
|
|
32
|
+
*/
|
|
33
|
+
const isCanonicalEntryPath = (entryPath, contentRoot) => entryPath.startsWith(`${normalizeFilesystemPath(contentRoot)}/`) &&
|
|
34
|
+
entryPath.split('/').every((segment) => segment !== '' && segment !== '.');
|
|
35
|
+
/**
|
|
36
|
+
* Whether the user may see a thread: its entry's path rules at `read`, the level a content
|
|
37
|
+
* read needs, so a thread discloses nothing about an entry its reader could not open. A
|
|
38
|
+
* field thread is governed by its entry. A thread with no entry path (a branch thread) is
|
|
39
|
+
* governed by branch access alone, which the `branchAccess` guard has already passed. An
|
|
40
|
+
* entry path that is not a canonical logical path is denied: comments.json is read without
|
|
41
|
+
* schema validation.
|
|
42
|
+
*/
|
|
43
|
+
const canReadThreadEntry = (checkAccess, contentRoot, entryPath) => {
|
|
44
|
+
if (entryPath === undefined)
|
|
45
|
+
return true;
|
|
46
|
+
const parsed = parseLogicalPath(entryPath);
|
|
47
|
+
return (parsed.ok &&
|
|
48
|
+
isCanonicalEntryPath(parsed.path, contentRoot) &&
|
|
49
|
+
checkAccess(parsed.path, 'read').allowed);
|
|
50
|
+
};
|
|
51
|
+
/** One checker per request, bound to the content root its paths are canonical against. */
|
|
52
|
+
const createThreadReadCheck = async (ctx, req, branchContext) => {
|
|
53
|
+
const checkAccess = await ctx.services.createContentAccessChecker(branchContext, branchContext.branchRoot, req.user);
|
|
54
|
+
const contentRoot = ctx.services.config.contentRoot || 'content';
|
|
55
|
+
return (entryPath) => canReadThreadEntry(checkAccess, contentRoot, entryPath);
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The profile of everyone who wrote or resolved one of `threads`, looked up only for threads
|
|
59
|
+
* already filtered to what the requester may read, so the list names no one the requester could
|
|
60
|
+
* not see in a thread. A failed lookup costs the names, never the threads.
|
|
61
|
+
*/
|
|
62
|
+
const lookupThreadUsers = async (ctx, threads) => {
|
|
63
|
+
const authPlugin = ctx.authPlugin;
|
|
64
|
+
if (!authPlugin)
|
|
65
|
+
return [];
|
|
66
|
+
try {
|
|
67
|
+
const ids = [];
|
|
68
|
+
for (const thread of threads) {
|
|
69
|
+
ids.push(thread.authorId, thread.resolvedBy);
|
|
70
|
+
// comments.json is parsed without validation, so a thread may lack its comment list.
|
|
71
|
+
if (Array.isArray(thread.comments)) {
|
|
72
|
+
for (const comment of thread.comments)
|
|
73
|
+
ids.push(comment?.userId);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return await lookupUserProfiles(authPlugin, ids);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
console.error('CanopyCMS: Failed to look up comment authors:', getErrorMessage(error));
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const listCommentsHandler = async (gc, ctx, req, _params) => {
|
|
21
84
|
const { branchContext } = gc;
|
|
22
85
|
const commentStore = new CommentStore(branchContext.branchRoot);
|
|
23
|
-
const
|
|
24
|
-
|
|
86
|
+
const [allThreads, canRead] = await Promise.all([
|
|
87
|
+
commentStore.listThreads({ includeResolved: true }),
|
|
88
|
+
createThreadReadCheck(ctx, req, branchContext),
|
|
89
|
+
]);
|
|
90
|
+
const threads = allThreads.filter((thread) => canRead(thread.entryPath));
|
|
91
|
+
const users = await lookupThreadUsers(ctx, threads);
|
|
92
|
+
return { ok: true, status: 200, data: { threads, users } };
|
|
25
93
|
};
|
|
26
|
-
const addCommentHandler = async (gc,
|
|
94
|
+
const addCommentHandler = async (gc, ctx, req, _params, body) => {
|
|
27
95
|
const { branchContext } = gc;
|
|
28
96
|
// Validate required fields based on type
|
|
29
97
|
if (body.type === 'field' && !body.canopyPath) {
|
|
@@ -33,6 +101,18 @@ const addCommentHandler = async (gc, _ctx, req, _params, body) => {
|
|
|
33
101
|
error: 'canopyPath required for field comments',
|
|
34
102
|
};
|
|
35
103
|
}
|
|
104
|
+
// A new field thread inside list items names each item on its path by key, one per list index,
|
|
105
|
+
// so it follows the item rather than the index. A reply lands on its thread and needs none.
|
|
106
|
+
const listIndexes = body.type === 'field' && body.canopyPath && !body.threadId
|
|
107
|
+
? parseCanopyPath(body.canopyPath).filter((s) => typeof s === 'number').length
|
|
108
|
+
: 0;
|
|
109
|
+
if ((body.itemKeys?.length ?? 0) !== listIndexes) {
|
|
110
|
+
return {
|
|
111
|
+
ok: false,
|
|
112
|
+
status: 400,
|
|
113
|
+
error: 'itemKeys must name one item per list index in a new field comment’s canopyPath',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
36
116
|
if ((body.type === 'field' || body.type === 'entry') && !body.entryPath) {
|
|
37
117
|
return {
|
|
38
118
|
ok: false,
|
|
@@ -40,22 +120,51 @@ const addCommentHandler = async (gc, _ctx, req, _params, body) => {
|
|
|
40
120
|
error: 'entryPath required for field/entry comments',
|
|
41
121
|
};
|
|
42
122
|
}
|
|
123
|
+
// Commenting needs read access to the entry the comment lands on. A reply lands on the
|
|
124
|
+
// stored thread, so it is checked against that thread's entry path, which never changes
|
|
125
|
+
// once the thread exists; a reply to a thread the user cannot read answers as missing, as
|
|
126
|
+
// resolve does.
|
|
43
127
|
const commentStore = new CommentStore(branchContext.branchRoot);
|
|
128
|
+
const [existing, canRead] = await Promise.all([
|
|
129
|
+
body.threadId ? commentStore.getThread(body.threadId) : Promise.resolve(null),
|
|
130
|
+
createThreadReadCheck(ctx, req, branchContext),
|
|
131
|
+
]);
|
|
132
|
+
if (body.threadId) {
|
|
133
|
+
if (!existing || !canRead(existing.entryPath)) {
|
|
134
|
+
return { ok: false, status: 404, error: 'Thread not found' };
|
|
135
|
+
}
|
|
136
|
+
const reply = await commentStore.addReply({
|
|
137
|
+
userId: req.user.userId,
|
|
138
|
+
text: body.text,
|
|
139
|
+
threadId: body.threadId,
|
|
140
|
+
});
|
|
141
|
+
if (!reply) {
|
|
142
|
+
return { ok: false, status: 404, error: 'Thread not found' };
|
|
143
|
+
}
|
|
144
|
+
return { ok: true, status: 201, data: reply };
|
|
145
|
+
}
|
|
146
|
+
if (!canRead(body.entryPath)) {
|
|
147
|
+
return { ok: false, status: 403, error: 'Forbidden' };
|
|
148
|
+
}
|
|
44
149
|
const result = await commentStore.addComment({
|
|
45
150
|
userId: req.user.userId,
|
|
46
151
|
text: body.text,
|
|
47
|
-
threadId: body.threadId,
|
|
48
152
|
type: body.type,
|
|
49
153
|
entryPath: body.entryPath,
|
|
50
154
|
canopyPath: body.canopyPath,
|
|
155
|
+
itemKeys: body.itemKeys,
|
|
51
156
|
});
|
|
52
157
|
return { ok: true, status: 201, data: result };
|
|
53
158
|
};
|
|
54
|
-
const resolveCommentHandler = async (gc,
|
|
159
|
+
const resolveCommentHandler = async (gc, ctx, req, params) => {
|
|
55
160
|
const { branchContext } = gc;
|
|
56
161
|
const commentStore = new CommentStore(branchContext.branchRoot);
|
|
57
|
-
const thread = await
|
|
58
|
-
|
|
162
|
+
const [thread, canRead] = await Promise.all([
|
|
163
|
+
commentStore.getThread(params.threadId),
|
|
164
|
+
createThreadReadCheck(ctx, req, branchContext),
|
|
165
|
+
]);
|
|
166
|
+
// A thread the user cannot read answers as missing, so its id does not confirm it exists.
|
|
167
|
+
if (!thread || !canRead(thread.entryPath)) {
|
|
59
168
|
return { ok: false, status: 404, error: 'Thread not found' };
|
|
60
169
|
}
|
|
61
170
|
// Check permissions: thread author, reviewer, or admin
|
|
@@ -85,7 +194,7 @@ const listComments = defineEndpoint({
|
|
|
85
194
|
params: branchParamSchema,
|
|
86
195
|
responseType: 'CommentsResponse',
|
|
87
196
|
response: {},
|
|
88
|
-
defaultMockData: { threads: [] },
|
|
197
|
+
defaultMockData: { threads: [], users: [] },
|
|
89
198
|
guards: ['branchAccess'],
|
|
90
199
|
handler: listCommentsHandler,
|
|
91
200
|
});
|
package/dist/api/content.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { ApiResponse } from './types.js';
|
|
3
3
|
import type { EntryValidationIssue } from '../config/index.js';
|
|
4
|
-
import { type Slug } from '../paths/index.js';
|
|
4
|
+
import { type LogicalPath, type Slug } from '../paths/index.js';
|
|
5
5
|
export type ContentReadResponse = ApiResponse<{
|
|
6
6
|
format: string;
|
|
7
7
|
data: Record<string, unknown>;
|
|
@@ -41,8 +41,8 @@ export interface WriteContentBody {
|
|
|
41
41
|
body?: string;
|
|
42
42
|
/**
|
|
43
43
|
* OCC token. A number from a prior read/write response makes this an update,
|
|
44
|
-
* rejected with 409 if the file's mtime no longer matches
|
|
45
|
-
*
|
|
44
|
+
* rejected with 409 if the file's mtime no longer matches, or with 409 `ENTRY_GONE` if the
|
|
45
|
+
* file is gone (deleted or moved since the read). `null` or omitted makes it
|
|
46
46
|
* a create, rejected with 409 if the entry already exists. There is no blind
|
|
47
47
|
* write: without a token, "no conflict detection" would be indistinguishable
|
|
48
48
|
* from "lost the token", so an update has to prove which version it read.
|
|
@@ -61,7 +61,7 @@ export interface RenameEntryBody {
|
|
|
61
61
|
export declare const CONTENT_ROUTES: {
|
|
62
62
|
readonly read: import("./route-builder.js").RouteDefinition<z.ZodObject<{
|
|
63
63
|
branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
|
|
64
|
-
path: z.ZodType<
|
|
64
|
+
path: z.ZodType<LogicalPath, z.ZodTypeDef, LogicalPath>;
|
|
65
65
|
}, "strip", z.ZodTypeAny, {
|
|
66
66
|
path: string & {
|
|
67
67
|
readonly __brand: "LogicalPath";
|
|
@@ -79,7 +79,7 @@ export declare const CONTENT_ROUTES: {
|
|
|
79
79
|
}>, undefined, ContentReadResponse>;
|
|
80
80
|
readonly write: import("./route-builder.js").RouteDefinition<z.ZodObject<{
|
|
81
81
|
branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
|
|
82
|
-
path: z.ZodType<
|
|
82
|
+
path: z.ZodType<LogicalPath, z.ZodTypeDef, LogicalPath>;
|
|
83
83
|
entryType: z.ZodOptional<z.ZodString>;
|
|
84
84
|
}, "strip", z.ZodTypeAny, {
|
|
85
85
|
path: string & {
|
|
@@ -104,18 +104,18 @@ export declare const CONTENT_ROUTES: {
|
|
|
104
104
|
expectedVersion: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
105
105
|
}, "strip", z.ZodTypeAny, {
|
|
106
106
|
format: "mdx" | "md" | "json" | "yaml";
|
|
107
|
+
data?: Record<string, unknown> | undefined;
|
|
107
108
|
expectedVersion?: number | null | undefined;
|
|
108
109
|
body?: string | undefined;
|
|
109
|
-
data?: Record<string, unknown> | undefined;
|
|
110
110
|
}, {
|
|
111
111
|
format: "mdx" | "md" | "json" | "yaml";
|
|
112
|
+
data?: Record<string, unknown> | undefined;
|
|
112
113
|
expectedVersion?: number | null | undefined;
|
|
113
114
|
body?: string | undefined;
|
|
114
|
-
data?: Record<string, unknown> | undefined;
|
|
115
115
|
}>, ContentWriteResponse>;
|
|
116
116
|
readonly validateReferences: import("./route-builder.js").RouteDefinition<z.ZodObject<{
|
|
117
117
|
branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
|
|
118
|
-
path: z.ZodType<
|
|
118
|
+
path: z.ZodType<LogicalPath, z.ZodTypeDef, LogicalPath>;
|
|
119
119
|
entryType: z.ZodOptional<z.ZodString>;
|
|
120
120
|
}, "strip", z.ZodTypeAny, {
|
|
121
121
|
path: string & {
|
|
@@ -142,7 +142,7 @@ export declare const CONTENT_ROUTES: {
|
|
|
142
142
|
}>, ReferenceValidationResponse>;
|
|
143
143
|
readonly renameEntry: import("./route-builder.js").RouteDefinition<z.ZodObject<{
|
|
144
144
|
branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
|
|
145
|
-
path: z.ZodType<
|
|
145
|
+
path: z.ZodType<LogicalPath, z.ZodTypeDef, LogicalPath>;
|
|
146
146
|
}, "strip", z.ZodTypeAny, {
|
|
147
147
|
path: string & {
|
|
148
148
|
readonly __brand: "LogicalPath";
|
package/dist/api/content.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { ENTRY_CHANGED_MESSAGE, ENTRY_GONE_MESSAGE } from './entries-constants.js';
|
|
3
|
+
import { BranchSyncingError, ContentStore, ContentStoreError, ContentConflictError, DuplicateContentIdError, EntryGoneError, UrlPathConflictError, getDefaultEntryType, } from '../content-store.js';
|
|
3
4
|
import { defineEndpoint } from './route-builder.js';
|
|
5
|
+
import { SchemaUnavailableError } from '../schema/schema-unavailable-error.js';
|
|
4
6
|
import { ReferenceValidator } from '../validation/reference-validator.js';
|
|
5
7
|
import { findUnknownKeys, mergeBodyIntoData, normalizeReferenceValues, validateEntryData, } from '../validation/entry-validator.js';
|
|
6
8
|
import { validateEntryLinks } from '../validation/entry-link-validator.js';
|
|
9
|
+
import { findMarkdownSafetyIssues, splitByStored } from '../validation/markdown-safety.js';
|
|
10
|
+
import { collectReferenceIds } from '../validation/field-traversal.js';
|
|
7
11
|
import { branchNameSchema, logicalPathSchema, slugSchema } from './validators.js';
|
|
8
12
|
import { entryLogicalPath, parseSlug } from '../paths/index.js';
|
|
9
13
|
import { getErrorMessage, isNotFoundError, sanitizeErrorMessage } from '../utils/error.js';
|
|
10
14
|
import { isDataOnlyFormat } from '../utils/format.js';
|
|
15
|
+
import { withItemKeys } from '../item-keys.js';
|
|
16
|
+
import { findBodyFieldName } from '../utils/body-field.js';
|
|
11
17
|
/**
|
|
12
18
|
* Parse an API path into logical path segments, prepending the content root if needed.
|
|
13
19
|
* Shared by all content API handlers.
|
|
@@ -110,6 +116,40 @@ const readContentHandler = async (gc, ctx, req, params) => {
|
|
|
110
116
|
throw err;
|
|
111
117
|
}
|
|
112
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* A reference's field and id, positions dropped (`blocks[2].author` → `blocks.author`), so
|
|
121
|
+
* reordering blocks or list items keeps a match while moving an id to a field of another name
|
|
122
|
+
* does not.
|
|
123
|
+
*/
|
|
124
|
+
const referenceSite = (fieldPath, id) => `${fieldPath.replace(/\[\d+\]/g, '')}\0${id}`;
|
|
125
|
+
/** Every reference site the stored entry holds, read raw so a missing target still yields its id. */
|
|
126
|
+
const storedReferenceSites = async (store, collectionPath, slug, fields) => {
|
|
127
|
+
const doc = await store.read(collectionPath, slug, { resolveReferences: false });
|
|
128
|
+
return new Set(collectReferenceIds(fields, doc.data).map((ref) => referenceSite(ref.path, ref.id)));
|
|
129
|
+
};
|
|
130
|
+
function withoutKey(record, key) {
|
|
131
|
+
if (!(key in record))
|
|
132
|
+
return record;
|
|
133
|
+
const { [key]: _dropped, ...rest } = record;
|
|
134
|
+
return rest;
|
|
135
|
+
}
|
|
136
|
+
/** The policy's issues in the stored entry, which a save may keep. */
|
|
137
|
+
const storedMarkdownSafetyIssues = async (store, collectionPath, slug, fields, siteAllow) => {
|
|
138
|
+
// A stored file that does not read keeps nothing, so the save's code is refused, not a 500.
|
|
139
|
+
const doc = await store
|
|
140
|
+
.read(collectionPath, slug, { resolveReferences: false })
|
|
141
|
+
.catch(() => undefined);
|
|
142
|
+
if (doc === undefined)
|
|
143
|
+
return [];
|
|
144
|
+
const data = 'body' in doc ? mergeBodyIntoData(fields, doc.data, doc.body) : doc.data;
|
|
145
|
+
return findMarkdownSafetyIssues(fields, doc.format, data, siteAllow);
|
|
146
|
+
};
|
|
147
|
+
const entryGoneResponse = () => ({
|
|
148
|
+
ok: false,
|
|
149
|
+
status: 409,
|
|
150
|
+
error: ENTRY_GONE_MESSAGE,
|
|
151
|
+
code: 'ENTRY_GONE',
|
|
152
|
+
});
|
|
113
153
|
const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
114
154
|
const { branchContext } = gc;
|
|
115
155
|
const flatSchema = branchContext.flatSchema;
|
|
@@ -135,15 +175,22 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
135
175
|
if (!access.allowed) {
|
|
136
176
|
return { ok: false, status: 403, error: 'Forbidden' };
|
|
137
177
|
}
|
|
178
|
+
// Update guard: an update whose entry is gone is refused before anything below judges it,
|
|
179
|
+
// since its entry type can no longer be read off the file and every check that follows would
|
|
180
|
+
// treat it as a create. store.write() re-enforces it in-lock (EntryGoneError).
|
|
181
|
+
const exists = await store.documentExists(schemaItem.logicalPath, slug);
|
|
182
|
+
if (body.expectedVersion != null && !exists)
|
|
183
|
+
return entryGoneResponse();
|
|
138
184
|
// Authoritative schema validation at the write boundary (COMPOUND-2): the server re-checks
|
|
139
185
|
// required fields, type/format correctness, and non-empty required references (rules shared
|
|
140
186
|
// with the editor via validation/entry-validator), plus reference EXISTENCE and
|
|
141
187
|
// EntryTypeConfig.maxItems (SCH-H3) — so a direct API call can't bypass what the editor's form
|
|
142
188
|
// enforces client-side.
|
|
143
189
|
//
|
|
144
|
-
// Carve-
|
|
145
|
-
//
|
|
146
|
-
//
|
|
190
|
+
// Carve-outs for a create (the target doesn't exist yet), because the editor creates an entry
|
|
191
|
+
// before the user fills its form: an entirely empty payload (`{}`, no body) skips field
|
|
192
|
+
// validation, and any other create skips only the required-field checks, so a create carrying
|
|
193
|
+
// just a title lands. Every save after that is checked in full.
|
|
147
194
|
// Resolve the entry-type config the store will write with (mirrors ContentStore.write)
|
|
148
195
|
let entryTypeConfig;
|
|
149
196
|
let fields = [];
|
|
@@ -198,20 +245,30 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
198
245
|
fields = entryTypeConfig?.schema ?? [];
|
|
199
246
|
maxItems = entryTypeConfig?.maxItems;
|
|
200
247
|
}
|
|
201
|
-
|
|
248
|
+
// Refused before validation, which an empty `fields` would wave through; mapped to a 503.
|
|
249
|
+
const resolvedEntryType = schemaItem.type === 'entry-type' ? schemaItem : entryTypeConfig;
|
|
250
|
+
if (resolvedEntryType?.unavailable) {
|
|
251
|
+
throw new SchemaUnavailableError(resolvedEntryType.name, resolvedEntryType.unavailable);
|
|
252
|
+
}
|
|
202
253
|
const isDataOnly = isDataOnlyFormat(body.format);
|
|
254
|
+
// An md/mdx file's body field is its body, so a frontmatter key of that name is dropped: no
|
|
255
|
+
// check would see it, and a read or a resolved reference would serve it as the body.
|
|
256
|
+
const requestData = isDataOnly || body.data === undefined
|
|
257
|
+
? body.data
|
|
258
|
+
: withoutKey(body.data, findBodyFieldName(fields));
|
|
259
|
+
const data = requestData ?? {};
|
|
203
260
|
// The store's own `undefined` (blind write) is never reachable from here: an omitted token is
|
|
204
261
|
// a create, so an update that lost its token 409s instead of overwriting unchecked.
|
|
205
262
|
const expectedVersion = body.expectedVersion ?? null;
|
|
206
263
|
const createConflictError = body.expectedVersion === null
|
|
207
264
|
? `An entry with slug "${slug}" already exists`
|
|
208
265
|
: `An entry with slug "${slug}" already exists; an update must send the expectedVersion from its last read`;
|
|
266
|
+
const keptWarnings = [];
|
|
209
267
|
try {
|
|
210
|
-
const exists = await store.documentExists(schemaItem.logicalPath, slug);
|
|
211
268
|
// Create guard: a create against a slug that already has content must never silently
|
|
212
269
|
// overwrite it — short-circuit with 409 before field validation runs, so the error names the
|
|
213
270
|
// real problem instead of "field is required" or a bare conflict. store.write() re-enforces
|
|
214
|
-
// this itself inside its per-entry lock against a fresh
|
|
271
|
+
// this itself inside its per-entry lock against a fresh open (the race-safe authoritative
|
|
215
272
|
// check); this is just a cheaper fast path for the common case.
|
|
216
273
|
if (expectedVersion === null && exists) {
|
|
217
274
|
return { ok: false, status: 409, error: createConflictError };
|
|
@@ -256,7 +313,27 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
256
313
|
// Pure rules (shared with the editor). For md/mdx the body is validated
|
|
257
314
|
// as the schema's isBody field.
|
|
258
315
|
const dataForValidation = isDataOnly ? data : mergeBodyIntoData(fields, data, body.body ?? '');
|
|
259
|
-
const fieldErrors = validateEntryData(fields, dataForValidation
|
|
316
|
+
const fieldErrors = validateEntryData(fields, dataForValidation, '', {
|
|
317
|
+
required: exists,
|
|
318
|
+
});
|
|
319
|
+
// Code in markdown or MDX (validation/markdown-safety.ts). A field the stored entry held
|
|
320
|
+
// with code is kept, with a warning, when saved unchanged; any other code is refused.
|
|
321
|
+
const { mdxAllow } = ctx.services.config;
|
|
322
|
+
const unsafe = findMarkdownSafetyIssues(fields, body.format, dataForValidation, mdxAllow);
|
|
323
|
+
if (unsafe.length > 0) {
|
|
324
|
+
const stored = exists
|
|
325
|
+
? await storedMarkdownSafetyIssues(store, schemaItem.logicalPath, slug, fields, mdxAllow)
|
|
326
|
+
: [];
|
|
327
|
+
const { refused, kept } = splitByStored(unsafe, stored);
|
|
328
|
+
fieldErrors.push(...refused);
|
|
329
|
+
for (const e of kept) {
|
|
330
|
+
keptWarnings.push({
|
|
331
|
+
level: 'warning',
|
|
332
|
+
fieldPath: e.fieldPath,
|
|
333
|
+
message: `holds content this field refuses, kept because the saved entry already had it: ${e.message}. Ask a developer to move it into a component, or to allow it.`,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
260
337
|
// Reference existence (server-only: reads the content ID index). Editor
|
|
261
338
|
// payloads may still carry resolved `{ id, ... }` objects from a prior
|
|
262
339
|
// read, so collapse them to id strings before checking.
|
|
@@ -264,7 +341,25 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
264
341
|
const idIndex = await store.idIndex();
|
|
265
342
|
const refValidator = new ReferenceValidator(idIndex, fields, (name) => store.resolveCollectionItem(name)?.logicalPath);
|
|
266
343
|
const refResult = await refValidator.validate(normalizeReferenceValues(fields, data));
|
|
267
|
-
|
|
344
|
+
// A dangling id the file already holds in that field is kept with a warning, so an entry
|
|
345
|
+
// whose target was deleted stays saveable and the id survives; the production build is
|
|
346
|
+
// what fails on it. A dangling id this save introduces, or moves to a field of another name, is
|
|
347
|
+
// refused.
|
|
348
|
+
const storedSites = exists && refResult.errors.some((e) => e.dangling)
|
|
349
|
+
? await storedReferenceSites(store, schemaItem.logicalPath, slug, fields)
|
|
350
|
+
: new Set();
|
|
351
|
+
for (const e of refResult.errors) {
|
|
352
|
+
if (e.dangling && storedSites.has(referenceSite(e.fieldPath, e.id))) {
|
|
353
|
+
keptWarnings.push({
|
|
354
|
+
level: 'warning',
|
|
355
|
+
fieldPath: e.fieldPath,
|
|
356
|
+
message: `references a missing entry (${e.id}). It is kept as it was; repoint or clear it.`,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
fieldErrors.push({ fieldPath: e.fieldPath, message: e.error });
|
|
361
|
+
}
|
|
362
|
+
}
|
|
268
363
|
}
|
|
269
364
|
if (fieldErrors.length > 0) {
|
|
270
365
|
return {
|
|
@@ -286,7 +381,7 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
286
381
|
// Adopter save-time validation, run BEFORE the file is written: 'error' issues
|
|
287
382
|
// refuse the save (e.g. a body that would break the site's production build),
|
|
288
383
|
// 'warning' issues are returned alongside the successful write.
|
|
289
|
-
let validationWarnings;
|
|
384
|
+
let validationWarnings = keptWarnings.length > 0 ? keptWarnings : undefined;
|
|
290
385
|
const validateEntry = ctx.services.config.validateEntry;
|
|
291
386
|
// Collapse resolved reference objects back to bare ID strings before persisting (the reference
|
|
292
387
|
// validator above gets its own copy). The editor's GET resolves references by default, so form
|
|
@@ -294,7 +389,13 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
294
389
|
// left unnormalized, a save would freeze that snapshot into the file (resolution only
|
|
295
390
|
// re-resolves plain ID strings), permanently severing the reference from its target. Idempotent:
|
|
296
391
|
// a payload that already holds ID strings is unchanged.
|
|
297
|
-
|
|
392
|
+
//
|
|
393
|
+
// Every list item and block leaves a save keyed (`item-keys.ts`). A key sent is kept, so a
|
|
394
|
+
// thread naming it still finds its item; one missing is filled from the item's content, as a
|
|
395
|
+
// read fills one.
|
|
396
|
+
const normalizedData = requestData === undefined
|
|
397
|
+
? undefined
|
|
398
|
+
: withItemKeys(fields, normalizeReferenceValues(fields, requestData), 'content');
|
|
298
399
|
// Computed before the validateEntry hook and the entry-link scan too, not just the write, so
|
|
299
400
|
// every consumer agrees on the same bytes — otherwise an adopter's hook could see a resolved
|
|
300
401
|
// object while the file got an ID string, depending on whether the post came from the editor.
|
|
@@ -313,6 +414,7 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
313
414
|
const overflow = unknownKeys.length - shown.length;
|
|
314
415
|
const list = overflow > 0 ? `${shown.join(', ')} (and ${overflow} more)` : shown.join(', ');
|
|
315
416
|
validationWarnings = [
|
|
417
|
+
...(validationWarnings ?? []),
|
|
316
418
|
{
|
|
317
419
|
level: 'warning',
|
|
318
420
|
message: `Saved. ${unknownKeys.length === 1 ? 'One field is' : `${unknownKeys.length} fields are`} ` +
|
|
@@ -328,7 +430,9 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
328
430
|
issues = await validateEntry({
|
|
329
431
|
entryPath: logicalPathSegments.join('/'),
|
|
330
432
|
branch: params.branch,
|
|
331
|
-
|
|
433
|
+
// The type the store writes with, so a request that omits `entryType` can't skip a
|
|
434
|
+
// type-gated rule.
|
|
435
|
+
...(entryTypeName ? { entryType: entryTypeName } : {}),
|
|
332
436
|
format: body.format,
|
|
333
437
|
data: normalizedData ?? {},
|
|
334
438
|
body: body.body,
|
|
@@ -352,8 +456,8 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
352
456
|
.join('; '),
|
|
353
457
|
};
|
|
354
458
|
}
|
|
355
|
-
// Appended, not assigned: the
|
|
356
|
-
//
|
|
459
|
+
// Appended, not assigned: the scans above may already have found some, and the editor shows
|
|
460
|
+
// the channel as one notification.
|
|
357
461
|
const warnings = issues.filter((issue) => issue.level === 'warning');
|
|
358
462
|
if (warnings.length > 0)
|
|
359
463
|
validationWarnings = [...(validationWarnings ?? []), ...warnings];
|
|
@@ -398,7 +502,12 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
398
502
|
// refused outright, but a lock compromised mid-write can still land it — each case carries
|
|
399
503
|
// its own message, so pass `err.message` through rather than the generic conflict below.
|
|
400
504
|
if (err instanceof BranchSyncingError) {
|
|
401
|
-
return {
|
|
505
|
+
return {
|
|
506
|
+
ok: false,
|
|
507
|
+
status: 409,
|
|
508
|
+
error: err.message,
|
|
509
|
+
code: err.outcome === 'unknown' ? 'WRITE_OUTCOME_UNKNOWN' : 'BRANCH_SYNCING',
|
|
510
|
+
};
|
|
402
511
|
}
|
|
403
512
|
// [F1] Also not an editor collision: this content ID is quarantined on two files
|
|
404
513
|
// (ContentIdIndex's duplicate-ID detection). The generic message below would send the
|
|
@@ -413,16 +522,15 @@ const writeContentHandler = async (gc, ctx, req, params, body) => {
|
|
|
413
522
|
if (err instanceof UrlPathConflictError) {
|
|
414
523
|
return { ok: false, status: 409, error: err.message };
|
|
415
524
|
}
|
|
525
|
+
// Gone since the `exists` check above: deleted or moved between it and the in-lock open.
|
|
526
|
+
if (err instanceof EntryGoneError)
|
|
527
|
+
return entryGoneResponse();
|
|
416
528
|
// Race-safe fallback: the early `exists` check above catches the common case; this covers a
|
|
417
|
-
// collision that lands between that check and store.write()'s in-lock
|
|
529
|
+
// collision that lands between that check and store.write()'s in-lock open.
|
|
418
530
|
if (expectedVersion === null) {
|
|
419
531
|
return { ok: false, status: 409, error: createConflictError };
|
|
420
532
|
}
|
|
421
|
-
return {
|
|
422
|
-
ok: false,
|
|
423
|
-
status: 409,
|
|
424
|
-
error: 'Content conflict: entry was modified by another editor',
|
|
425
|
-
};
|
|
533
|
+
return { ok: false, status: 409, error: ENTRY_CHANGED_MESSAGE };
|
|
426
534
|
}
|
|
427
535
|
// C2: a ContentStoreError is an expected client fault (bad slug, validation, etc.) and keeps
|
|
428
536
|
// its 400. Anything else — ENOSPC, EACCES, a bug — is a genuine server fault and must not be
|
|
@@ -542,7 +650,7 @@ const renameEntryHandler = async (gc, ctx, req, params, body) => {
|
|
|
542
650
|
return {
|
|
543
651
|
ok: false,
|
|
544
652
|
status: 409,
|
|
545
|
-
error: passThrough ? err.message :
|
|
653
|
+
error: passThrough ? err.message : ENTRY_CHANGED_MESSAGE,
|
|
546
654
|
};
|
|
547
655
|
}
|
|
548
656
|
// C2: same rule as writeContentHandler's catch above (ContentStoreError -> 400, everything
|
|
@@ -598,6 +706,7 @@ const validateReferences = defineEndpoint({
|
|
|
598
706
|
namespace: 'content',
|
|
599
707
|
name: 'validateReferences',
|
|
600
708
|
method: 'POST',
|
|
709
|
+
readOnly: true,
|
|
601
710
|
path: '/:branch/validate-references/...path',
|
|
602
711
|
params: validateReferencesParamsSchema,
|
|
603
712
|
body: validateReferencesBodySchema,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* imports) so the editor's browser bundle can import
|
|
4
|
-
* `entries.ts` and its `node:fs`-backed deps
|
|
2
|
+
* Constants the entries and content endpoints share with the editor. Kept dependency-free (no
|
|
3
|
+
* server-only imports) so the editor's browser bundle can import them without pulling in
|
|
4
|
+
* `entries.ts` and its `node:fs`-backed deps.
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Maximum entries the list endpoint returns per request; larger `limit` values are clamped to
|
|
@@ -10,3 +10,14 @@
|
|
|
10
10
|
export declare const MAX_ENTRIES_PER_PAGE = 200;
|
|
11
11
|
/** Default page size when a request omits `limit`. */
|
|
12
12
|
export declare const DEFAULT_ENTRIES_LIMIT = 50;
|
|
13
|
+
/**
|
|
14
|
+
* The generic 409 for a conflicting change to an entry, usually its version moving on since the
|
|
15
|
+
* caller read it. Shared with the editor, whose own pre-save staleness check reports the same.
|
|
16
|
+
*/
|
|
17
|
+
export declare const ENTRY_CHANGED_MESSAGE = "This entry changed since you opened it. Choose \"Refresh from server\" to see the latest version (your unsaved edits will be lost).";
|
|
18
|
+
/**
|
|
19
|
+
* The 409 for an update whose entry is gone: deleted or moved since the caller read it (API code
|
|
20
|
+
* `ENTRY_GONE`). The save is refused rather than re-creating the entry; re-creating it is a create
|
|
21
|
+
* the caller makes deliberately.
|
|
22
|
+
*/
|
|
23
|
+
export declare const ENTRY_GONE_MESSAGE = "This entry was deleted or moved since you opened it, so your changes were not saved.";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* imports) so the editor's browser bundle can import
|
|
4
|
-
* `entries.ts` and its `node:fs`-backed deps
|
|
2
|
+
* Constants the entries and content endpoints share with the editor. Kept dependency-free (no
|
|
3
|
+
* server-only imports) so the editor's browser bundle can import them without pulling in
|
|
4
|
+
* `entries.ts` and its `node:fs`-backed deps.
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Maximum entries the list endpoint returns per request; larger `limit` values are clamped to
|
|
@@ -10,3 +10,14 @@
|
|
|
10
10
|
export const MAX_ENTRIES_PER_PAGE = 200;
|
|
11
11
|
/** Default page size when a request omits `limit`. */
|
|
12
12
|
export const DEFAULT_ENTRIES_LIMIT = 50;
|
|
13
|
+
/**
|
|
14
|
+
* The generic 409 for a conflicting change to an entry, usually its version moving on since the
|
|
15
|
+
* caller read it. Shared with the editor, whose own pre-save staleness check reports the same.
|
|
16
|
+
*/
|
|
17
|
+
export const ENTRY_CHANGED_MESSAGE = 'This entry changed since you opened it. Choose "Refresh from server" to see the latest version (your unsaved edits will be lost).';
|
|
18
|
+
/**
|
|
19
|
+
* The 409 for an update whose entry is gone: deleted or moved since the caller read it (API code
|
|
20
|
+
* `ENTRY_GONE`). The save is refused rather than re-creating the entry; re-creating it is a create
|
|
21
|
+
* the caller makes deliberately.
|
|
22
|
+
*/
|
|
23
|
+
export const ENTRY_GONE_MESSAGE = 'This entry was deleted or moved since you opened it, so your changes were not saved.';
|