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
|
@@ -1,52 +1,153 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reading `branch.json` — the file format, nothing else.
|
|
3
3
|
*
|
|
4
|
-
* Deliberately a LEAF, importing only node built-ins,
|
|
5
|
-
*
|
|
4
|
+
* Deliberately a LEAF, importing only node built-ins, zod, types, and the error
|
|
5
|
+
* modules. That is what keeps `branch-registry.ts` (which reads every branch's
|
|
6
6
|
* `branch.json`) out of a runtime import cycle with `branch-metadata.ts` (which
|
|
7
7
|
* writes it and then invalidates the registry cache).
|
|
8
8
|
*/
|
|
9
9
|
import fs from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
-
import {
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { getErrorMessage, isNotFoundError } from './utils/error.js';
|
|
13
|
+
import { BranchMetadataCorruptError } from './branch-metadata-error.js';
|
|
14
|
+
export { BranchMetadataCorruptError };
|
|
12
15
|
export const BRANCH_META_DIR = '.canopy-meta';
|
|
13
16
|
export const BRANCH_META_FILE = 'branch.json';
|
|
17
|
+
/** Absolute path to a branch workspace's `branch.json`. */
|
|
18
|
+
const branchMetadataFilePath = (branchRoot) => path.join(path.resolve(branchRoot), BRANCH_META_DIR, BRANCH_META_FILE);
|
|
19
|
+
/** The value a timestamp missing from a hand-written file reads as: honestly unknown, and stable. */
|
|
20
|
+
const UNKNOWN_TIME = new Date(0).toISOString();
|
|
21
|
+
/** A zod enum of every member of `T`: the mapped type refuses to compile if one is left out. */
|
|
22
|
+
const enumOf = (values) => z.nativeEnum(values);
|
|
14
23
|
/**
|
|
15
|
-
* branch.json
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
24
|
+
* `branch.json`'s shape, checked on every read.
|
|
25
|
+
*
|
|
26
|
+
* Strict where a guard or an access check reads the field: `status` (writes and
|
|
27
|
+
* submits are gated on it), `name` (the protected-base-branch test compares it)
|
|
28
|
+
* and `access` (defaulted to `{}`, a missing one would read as "no ACL" and
|
|
29
|
+
* widen access). Lenient,
|
|
30
|
+
* with defaults, where a hand-written file can omit bookkeeping: the envelope's
|
|
31
|
+
* `version` (0, as occ-json-write reads it) and `schemaVersion`, and
|
|
32
|
+
* `createdBy` ('unknown', which matches no user, so the creator grant fails
|
|
33
|
+
* closed) and the timestamps. Every optional field is type-checked.
|
|
34
|
+
*
|
|
35
|
+
* Unknown keys pass through, so a field a newer deployment writes survives an
|
|
36
|
+
* older one's save. Code reads only the typed {@link BranchMetadataFile}.
|
|
20
37
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
const branchMetadataShape = {
|
|
39
|
+
name: z.string().min(1),
|
|
40
|
+
title: z.string().optional(),
|
|
41
|
+
description: z.string().optional(),
|
|
42
|
+
status: enumOf({
|
|
43
|
+
editing: 'editing',
|
|
44
|
+
submitted: 'submitted',
|
|
45
|
+
approved: 'approved',
|
|
46
|
+
archived: 'archived',
|
|
47
|
+
}),
|
|
48
|
+
access: z
|
|
49
|
+
.object({
|
|
50
|
+
allowedUsers: z.array(z.string()).optional(),
|
|
51
|
+
allowedGroups: z.array(z.string()).optional(),
|
|
52
|
+
managerOrAdminAllowed: z.boolean().optional(),
|
|
53
|
+
})
|
|
54
|
+
.passthrough(),
|
|
55
|
+
createdBy: z.string().default('unknown'),
|
|
56
|
+
createdAt: z.string().default(UNKNOWN_TIME),
|
|
57
|
+
updatedAt: z.string().default(UNKNOWN_TIME),
|
|
58
|
+
baseBranch: z.string().optional(),
|
|
59
|
+
pullRequestUrl: z.string().optional(),
|
|
60
|
+
pullRequestNumber: z.number().int().optional(),
|
|
61
|
+
submittedAt: z.string().optional(),
|
|
62
|
+
pushedToGitHubAt: z.string().optional(),
|
|
63
|
+
syncStatus: enumOf({
|
|
64
|
+
synced: 'synced',
|
|
65
|
+
'pending-sync': 'pending-sync',
|
|
66
|
+
'sync-failed': 'sync-failed',
|
|
67
|
+
}).optional(),
|
|
68
|
+
conflictStatus: enumOf({
|
|
69
|
+
clean: 'clean',
|
|
70
|
+
'conflicts-detected': 'conflicts-detected',
|
|
71
|
+
}).optional(),
|
|
72
|
+
conflictFiles: z.array(z.custom((v) => typeof v === 'string')).optional(),
|
|
73
|
+
pullRequestState: enumOf({
|
|
74
|
+
open: 'open',
|
|
75
|
+
closed: 'closed',
|
|
76
|
+
merged: 'merged',
|
|
77
|
+
}).optional(),
|
|
78
|
+
mergedAt: z.string().optional(),
|
|
79
|
+
rebaseFailure: z
|
|
80
|
+
.object({ message: z.string(), firstAt: z.string(), lastAt: z.string() })
|
|
81
|
+
.optional(),
|
|
82
|
+
historyRewrittenFrom: z.string().optional(),
|
|
83
|
+
syncFailureReason: z.string().optional(),
|
|
84
|
+
editors: z.array(z.string()).optional(),
|
|
85
|
+
uncommittedEditors: z.array(z.string()).optional(),
|
|
86
|
+
};
|
|
87
|
+
/** The `branch` object, also embedded in the registry snapshot (branch-registry.ts). */
|
|
88
|
+
export const branchMetadataSchema = z.object(branchMetadataShape).passthrough();
|
|
89
|
+
const branchMetadataFileSchema = z
|
|
90
|
+
.object({
|
|
91
|
+
schemaVersion: z.number().int().default(1),
|
|
92
|
+
version: z.number().int().nonnegative().default(0),
|
|
93
|
+
writeId: z.string().optional(),
|
|
94
|
+
branch: branchMetadataSchema,
|
|
95
|
+
})
|
|
96
|
+
.passthrough();
|
|
97
|
+
/** The schema failures, by field and without values, so no file content reaches a client. */
|
|
98
|
+
function describeSchemaIssues(issues) {
|
|
99
|
+
const missing = new Set();
|
|
100
|
+
const invalid = new Set();
|
|
101
|
+
for (const issue of issues) {
|
|
102
|
+
const field = issue.path.length > 0 ? issue.path.join('.') : 'the file';
|
|
103
|
+
const isMissing = issue.code === z.ZodIssueCode.invalid_type && issue.received === z.ZodParsedType.undefined;
|
|
104
|
+
(isMissing ? missing : invalid).add(field);
|
|
27
105
|
}
|
|
106
|
+
const parts = [];
|
|
107
|
+
if (missing.size > 0)
|
|
108
|
+
parts.push(`Missing: ${[...missing].join(', ')}`);
|
|
109
|
+
if (invalid.size > 0)
|
|
110
|
+
parts.push(`Invalid: ${[...invalid].join(', ')}`);
|
|
111
|
+
return `Not branch metadata. ${parts.join('. ')}`;
|
|
28
112
|
}
|
|
29
|
-
/** Absolute path to a branch workspace's `branch.json`. */
|
|
30
|
-
const branchMetadataFilePath = (branchRoot) => path.join(path.resolve(branchRoot), BRANCH_META_DIR, BRANCH_META_FILE);
|
|
31
113
|
/**
|
|
32
|
-
* Read and
|
|
33
|
-
* Callers distinguish three outcomes: `null` when the file does not
|
|
34
|
-
* un-provisioned or non-branch directory),
|
|
35
|
-
* malformed JSON
|
|
114
|
+
* Read, parse and validate `branch.json`, with no locking, no OCC and no side
|
|
115
|
+
* effects. Callers distinguish three outcomes: `null` when the file does not
|
|
116
|
+
* exist (an un-provisioned or non-branch directory),
|
|
117
|
+
* `BranchMetadataCorruptError` on malformed JSON or a failed schema check, and
|
|
118
|
+
* every other IO failure rethrown unchanged.
|
|
36
119
|
*/
|
|
37
120
|
export async function readBranchMetadataFile(branchRoot) {
|
|
38
121
|
const resolvedRoot = path.resolve(branchRoot);
|
|
122
|
+
let raw;
|
|
39
123
|
try {
|
|
40
|
-
|
|
41
|
-
return JSON.parse(raw);
|
|
124
|
+
raw = await fs.readFile(branchMetadataFilePath(resolvedRoot), 'utf8');
|
|
42
125
|
}
|
|
43
126
|
catch (err) {
|
|
44
127
|
if (isNotFoundError(err)) {
|
|
45
128
|
return null;
|
|
46
129
|
}
|
|
47
|
-
if (err instanceof SyntaxError) {
|
|
48
|
-
throw new BranchMetadataCorruptError(resolvedRoot, err.message);
|
|
49
|
-
}
|
|
50
130
|
throw err;
|
|
51
131
|
}
|
|
132
|
+
return parseBranchMetadataFile(resolvedRoot, raw);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Parse and validate `branch.json` text already read from `branchRoot`.
|
|
136
|
+
*
|
|
137
|
+
* @throws BranchMetadataCorruptError when it is not JSON or fails the schema
|
|
138
|
+
*/
|
|
139
|
+
function parseBranchMetadataFile(branchRoot, raw) {
|
|
140
|
+
let json;
|
|
141
|
+
try {
|
|
142
|
+
json = JSON.parse(raw);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
throw new BranchMetadataCorruptError(branchRoot, `Not valid JSON: ${getErrorMessage(err)}`);
|
|
146
|
+
}
|
|
147
|
+
const result = branchMetadataFileSchema.safeParse(json);
|
|
148
|
+
if (!result.success) {
|
|
149
|
+
throw new BranchMetadataCorruptError(branchRoot, describeSchemaIssues(result.error.issues));
|
|
150
|
+
}
|
|
151
|
+
const file = result.data;
|
|
152
|
+
return file;
|
|
52
153
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { BranchContext, BranchMetadata } from './types.js';
|
|
1
|
+
import type { BranchContext, BranchMetadata, BranchPaths, CanopyUserId } from './types.js';
|
|
2
|
+
import type { CanopyUser } from './user.js';
|
|
2
3
|
import { BranchMetadataCorruptError, type BranchMetadataFile } from './branch-metadata-file.js';
|
|
3
4
|
import { type OperatingMode } from './operating-mode/index.js';
|
|
4
5
|
export { BranchMetadataCorruptError, type BranchMetadataFile };
|
|
@@ -37,6 +38,12 @@ export declare class BranchMetadataFileManager {
|
|
|
37
38
|
static get(branchRoot: string, baseRoot: string, options?: {
|
|
38
39
|
settleMs?: number;
|
|
39
40
|
}): BranchMetadataFileManager;
|
|
41
|
+
/**
|
|
42
|
+
* The file the save cycle merges over. A corrupt one throws
|
|
43
|
+
* {@link BranchMetadataCorruptError} rather than reading as absent, so a save
|
|
44
|
+
* never lays defaults (status 'editing', no ACL) over a branch whose real
|
|
45
|
+
* state is unknown; repair-metadata archives the file before it saves.
|
|
46
|
+
*/
|
|
40
47
|
private load;
|
|
41
48
|
/**
|
|
42
49
|
* Write branch.json via the shared OCC helper, applying the schemaVersion
|
|
@@ -63,16 +70,48 @@ export declare class BranchMetadataFileManager {
|
|
|
63
70
|
* entry with no clone behind it. It runs BEFORE the lock stack so a doomed
|
|
64
71
|
* save fails fast instead of paying for a lock.
|
|
65
72
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* lockfile taken next lives INSIDE `branchRoot`, so it can promise no more
|
|
70
|
-
* than "the directory existed a moment ago".
|
|
73
|
+
* A save that passes the check just before a delete still cannot write: the
|
|
74
|
+
* delete renames the tree away under the same lockfile, and that lockfile
|
|
75
|
+
* creates only its own directory (`withOccFileLock`), so the save fails there.
|
|
71
76
|
*/
|
|
72
77
|
save(incoming: BranchMetadataUpdate): Promise<BranchMetadataFile>;
|
|
78
|
+
/**
|
|
79
|
+
* {@link save}, applied only when `onlyIf` accepts the metadata on disk; resolves null when it
|
|
80
|
+
* does not, having written nothing. `onlyIf` runs inside the lock stack against the version
|
|
81
|
+
* the write is checked against, so the decision and the write are one compare-and-set.
|
|
82
|
+
*/
|
|
83
|
+
saveIf(incoming: BranchMetadataUpdate, onlyIf: (existing: BranchMetadataFile | null) => boolean): Promise<BranchMetadataFile | null>;
|
|
84
|
+
/**
|
|
85
|
+
* A save whose update `compute` derives from the metadata on disk, inside the lock stack, so
|
|
86
|
+
* an update built from the existing value (adding to a list) cannot lose a concurrent one.
|
|
87
|
+
* `compute` returning null writes nothing and resolves null. `keepUpdatedAt` leaves
|
|
88
|
+
* `updatedAt` as it was, for bookkeeping writes that are not a change to the branch.
|
|
89
|
+
*/
|
|
90
|
+
update(compute: (existing: BranchMetadataFile | null) => BranchMetadataUpdate | null, options?: {
|
|
91
|
+
keepUpdatedAt?: boolean;
|
|
92
|
+
}): Promise<BranchMetadataFile | null>;
|
|
93
|
+
/**
|
|
94
|
+
* Adds `userId` to `editors` and `uncommittedEditors`. A lock-free read skips the write when
|
|
95
|
+
* both already hold it, so only a user's first save since the last submit commit pays for the lock.
|
|
96
|
+
* Never creates branch.json.
|
|
97
|
+
*/
|
|
98
|
+
recordEditor(userId: CanopyUserId): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Removes `userIds` from `uncommittedEditors` once a submit commit's trailers name them. Ids
|
|
101
|
+
* recorded after the commit read them stay, so the next commit names them.
|
|
102
|
+
*/
|
|
103
|
+
markEditorsCommitted(userIds: readonly CanopyUserId[]): Promise<void>;
|
|
73
104
|
/** Invalidate the registry cache so the next list() regenerates. */
|
|
74
105
|
private invalidateRegistry;
|
|
75
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Records `user` as an editor of the branch, for the submit's trailers and PR body. Called once a
|
|
109
|
+
* change to the branch's working tree has succeeded, so a failure here is logged and never fails
|
|
110
|
+
* that change. Anonymous users are not recorded.
|
|
111
|
+
*/
|
|
112
|
+
export declare function recordBranchEditor(context: BranchPaths, user: CanopyUser): Promise<void>;
|
|
113
|
+
/** The branch.json a brand-new branch starts with: {@link mergeBranchMetadata} over nothing. */
|
|
114
|
+
export declare function buildInitialBranchMetadata(incoming: BranchMetadataUpdate, now?: string): BranchMetadataFile;
|
|
76
115
|
/**
|
|
77
116
|
* Fields save() accepts. `createdBy` applies on creation only and is ignored
|
|
78
117
|
* once metadata exists; createdAt/updatedAt are managed here, not by callers.
|
package/dist/branch-metadata.js
CHANGED
|
@@ -3,7 +3,8 @@ import path from 'node:path';
|
|
|
3
3
|
import { BranchRegistry } from './branch-registry.js';
|
|
4
4
|
import { BRANCH_META_DIR, BRANCH_META_FILE, BranchMetadataCorruptError, readBranchMetadataFile, } from './branch-metadata-file.js';
|
|
5
5
|
import { resolveBranchPath } from './paths/index.js';
|
|
6
|
-
import { isNotFoundError } from './utils/error.js';
|
|
6
|
+
import { getErrorMessage, isNotFoundError } from './utils/error.js';
|
|
7
|
+
import { canopyLogWarn } from './utils/logger.js';
|
|
7
8
|
import { withLock } from './utils/async-mutex.js';
|
|
8
9
|
import { writeOccJsonFile, withOccRetry, withOccFileLock, OccWriteConflictError, } from './utils/occ-json-write.js';
|
|
9
10
|
// The file format lives in the leaf so branch-registry.ts can read branch.json
|
|
@@ -52,19 +53,15 @@ export class BranchMetadataFileManager {
|
|
|
52
53
|
static get(branchRoot, baseRoot, options) {
|
|
53
54
|
return new BranchMetadataFileManager(branchRoot, baseRoot, options);
|
|
54
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The file the save cycle merges over. A corrupt one throws
|
|
58
|
+
* {@link BranchMetadataCorruptError} rather than reading as absent, so a save
|
|
59
|
+
* never lays defaults (status 'editing', no ACL) over a branch whose real
|
|
60
|
+
* state is unknown; repair-metadata archives the file before it saves.
|
|
61
|
+
*/
|
|
55
62
|
async load() {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const parsed = JSON.parse(raw);
|
|
59
|
-
const version = parsed.version ?? 0;
|
|
60
|
-
return { meta: parsed, version };
|
|
61
|
-
}
|
|
62
|
-
catch (err) {
|
|
63
|
-
if (isNotFoundError(err)) {
|
|
64
|
-
return { meta: null, version: null };
|
|
65
|
-
}
|
|
66
|
-
throw err;
|
|
67
|
-
}
|
|
63
|
+
const meta = await readBranchMetadataFile(this.branchRoot);
|
|
64
|
+
return { meta, version: meta?.version ?? null };
|
|
68
65
|
}
|
|
69
66
|
/**
|
|
70
67
|
* Write branch.json via the shared OCC helper, applying the schemaVersion
|
|
@@ -101,13 +98,31 @@ export class BranchMetadataFileManager {
|
|
|
101
98
|
* entry with no clone behind it. It runs BEFORE the lock stack so a doomed
|
|
102
99
|
* save fails fast instead of paying for a lock.
|
|
103
100
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* lockfile taken next lives INSIDE `branchRoot`, so it can promise no more
|
|
108
|
-
* than "the directory existed a moment ago".
|
|
101
|
+
* A save that passes the check just before a delete still cannot write: the
|
|
102
|
+
* delete renames the tree away under the same lockfile, and that lockfile
|
|
103
|
+
* creates only its own directory (`withOccFileLock`), so the save fails there.
|
|
109
104
|
*/
|
|
110
105
|
async save(incoming) {
|
|
106
|
+
const saved = await this.saveIf(incoming, () => true);
|
|
107
|
+
if (saved === null)
|
|
108
|
+
throw new Error('unreachable: an unconditional save was skipped');
|
|
109
|
+
return saved;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* {@link save}, applied only when `onlyIf` accepts the metadata on disk; resolves null when it
|
|
113
|
+
* does not, having written nothing. `onlyIf` runs inside the lock stack against the version
|
|
114
|
+
* the write is checked against, so the decision and the write are one compare-and-set.
|
|
115
|
+
*/
|
|
116
|
+
async saveIf(incoming, onlyIf) {
|
|
117
|
+
return this.update((existing) => (onlyIf(existing) ? incoming : null));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* A save whose update `compute` derives from the metadata on disk, inside the lock stack, so
|
|
121
|
+
* an update built from the existing value (adding to a list) cannot lose a concurrent one.
|
|
122
|
+
* `compute` returning null writes nothing and resolves null. `keepUpdatedAt` leaves
|
|
123
|
+
* `updatedAt` as it was, for bookkeeping writes that are not a change to the branch.
|
|
124
|
+
*/
|
|
125
|
+
async update(compute, options = {}) {
|
|
111
126
|
try {
|
|
112
127
|
await fs.stat(this.branchRoot);
|
|
113
128
|
}
|
|
@@ -121,37 +136,12 @@ export class BranchMetadataFileManager {
|
|
|
121
136
|
try {
|
|
122
137
|
saved = await withLock(this.filePath, () => withOccFileLock(this.filePath, () => withOccRetry(async () => {
|
|
123
138
|
const { meta: existing, version } = await this.load();
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
createdAt: now,
|
|
131
|
-
updatedAt: now,
|
|
132
|
-
};
|
|
133
|
-
const merged = {
|
|
134
|
-
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
135
|
-
version: version ?? 0,
|
|
136
|
-
branch: {
|
|
137
|
-
...defaults,
|
|
138
|
-
...existing?.branch,
|
|
139
|
-
...incoming.branch,
|
|
140
|
-
access: {
|
|
141
|
-
...existing?.branch?.access,
|
|
142
|
-
...incoming.branch?.access,
|
|
143
|
-
},
|
|
144
|
-
// Immutable after creation
|
|
145
|
-
createdBy: existing?.branch.createdBy ?? incoming.branch?.createdBy ?? defaults.createdBy,
|
|
146
|
-
createdAt: existing?.branch.createdAt ?? defaults.createdAt,
|
|
147
|
-
// Fork point is recorded once at creation; later saves must not move it
|
|
148
|
-
baseBranch: existing?.branch.baseBranch ?? incoming.branch?.baseBranch,
|
|
149
|
-
// Always stamped fresh; the spreads above would otherwise let
|
|
150
|
-
// the creation-time value win forever, freezing the timestamp
|
|
151
|
-
// the editor's Branches panel sorts and displays by
|
|
152
|
-
updatedAt: now,
|
|
153
|
-
},
|
|
154
|
-
};
|
|
139
|
+
const incoming = compute(existing);
|
|
140
|
+
if (incoming === null)
|
|
141
|
+
return null;
|
|
142
|
+
const merged = mergeBranchMetadata(existing, version, incoming);
|
|
143
|
+
if (options.keepUpdatedAt && existing)
|
|
144
|
+
merged.branch.updatedAt = existing.branch.updatedAt;
|
|
155
145
|
const written = await this.write(merged, version);
|
|
156
146
|
merged.version = written.version;
|
|
157
147
|
merged.writeId = written.writeId;
|
|
@@ -169,15 +159,114 @@ export class BranchMetadataFileManager {
|
|
|
169
159
|
// does, and the registry's eager regeneration is O(branch count) fs reads
|
|
170
160
|
// on EFS — holding the lock through it would stretch every save's critical
|
|
171
161
|
// section for no correctness gain.
|
|
172
|
-
|
|
162
|
+
if (saved !== null)
|
|
163
|
+
await this.invalidateRegistry();
|
|
173
164
|
return saved;
|
|
174
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Adds `userId` to `editors` and `uncommittedEditors`. A lock-free read skips the write when
|
|
168
|
+
* both already hold it, so only a user's first save since the last submit commit pays for the lock.
|
|
169
|
+
* Never creates branch.json.
|
|
170
|
+
*/
|
|
171
|
+
async recordEditor(userId) {
|
|
172
|
+
const current = await readBranchMetadataFile(this.branchRoot);
|
|
173
|
+
if (!current || isRecordedEditor(current.branch, userId))
|
|
174
|
+
return;
|
|
175
|
+
await this.update((existing) => existing && !isRecordedEditor(existing.branch, userId)
|
|
176
|
+
? {
|
|
177
|
+
branch: {
|
|
178
|
+
editors: withMember(existing.branch.editors, userId),
|
|
179
|
+
uncommittedEditors: withMember(existing.branch.uncommittedEditors, userId),
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
: null, { keepUpdatedAt: true });
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Removes `userIds` from `uncommittedEditors` once a submit commit's trailers name them. Ids
|
|
186
|
+
* recorded after the commit read them stay, so the next commit names them.
|
|
187
|
+
*/
|
|
188
|
+
async markEditorsCommitted(userIds) {
|
|
189
|
+
const committed = new Set(userIds);
|
|
190
|
+
if (committed.size === 0)
|
|
191
|
+
return;
|
|
192
|
+
await this.update((existing) => {
|
|
193
|
+
const pending = existing?.branch.uncommittedEditors ?? [];
|
|
194
|
+
const remaining = pending.filter((id) => !committed.has(id));
|
|
195
|
+
if (remaining.length === pending.length)
|
|
196
|
+
return null;
|
|
197
|
+
return { branch: { uncommittedEditors: remaining.length > 0 ? remaining : undefined } };
|
|
198
|
+
}, { keepUpdatedAt: true });
|
|
199
|
+
}
|
|
175
200
|
/** Invalidate the registry cache so the next list() regenerates. */
|
|
176
201
|
async invalidateRegistry() {
|
|
177
202
|
const registry = new BranchRegistry(this.baseRoot);
|
|
178
203
|
await registry.invalidate();
|
|
179
204
|
}
|
|
180
205
|
}
|
|
206
|
+
function isRecordedEditor(branch, userId) {
|
|
207
|
+
return ((branch.editors?.includes(userId) ?? false) &&
|
|
208
|
+
(branch.uncommittedEditors?.includes(userId) ?? false));
|
|
209
|
+
}
|
|
210
|
+
function withMember(list, id) {
|
|
211
|
+
return list?.includes(id) ? [...list] : [...(list ?? []), id];
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Records `user` as an editor of the branch, for the submit's trailers and PR body. Called once a
|
|
215
|
+
* change to the branch's working tree has succeeded, so a failure here is logged and never fails
|
|
216
|
+
* that change. Anonymous users are not recorded.
|
|
217
|
+
*/
|
|
218
|
+
export async function recordBranchEditor(context, user) {
|
|
219
|
+
if (user.type !== 'authenticated')
|
|
220
|
+
return;
|
|
221
|
+
try {
|
|
222
|
+
await getBranchMetadataFileManager(context.branchRoot, context.baseRoot).recordEditor(user.userId);
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
canopyLogWarn(`CanopyCMS: Could not record an editor of ${path.basename(context.branchRoot)}; the next submit will not credit them:`, getErrorMessage(err));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* The branch.json that {@link BranchMetadataFileManager.save} writes for `incoming` over
|
|
230
|
+
* `existing`. Pure, so a staged workspace's first branch.json (branch-provisioning.ts) is built by
|
|
231
|
+
* the same rules as a save and the two cannot drift.
|
|
232
|
+
*/
|
|
233
|
+
function mergeBranchMetadata(existing, version, incoming, now = new Date().toISOString()) {
|
|
234
|
+
const defaults = {
|
|
235
|
+
name: 'unknown',
|
|
236
|
+
status: 'editing',
|
|
237
|
+
access: {},
|
|
238
|
+
createdBy: 'unknown',
|
|
239
|
+
createdAt: now,
|
|
240
|
+
updatedAt: now,
|
|
241
|
+
};
|
|
242
|
+
return {
|
|
243
|
+
// Envelope keys this code does not know survive, as unknown branch keys do below.
|
|
244
|
+
...existing,
|
|
245
|
+
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
246
|
+
version: version ?? 0,
|
|
247
|
+
branch: {
|
|
248
|
+
...defaults,
|
|
249
|
+
...existing?.branch,
|
|
250
|
+
...incoming.branch,
|
|
251
|
+
access: {
|
|
252
|
+
...existing?.branch?.access,
|
|
253
|
+
...incoming.branch?.access,
|
|
254
|
+
},
|
|
255
|
+
// Immutable after creation
|
|
256
|
+
createdBy: existing?.branch.createdBy ?? incoming.branch?.createdBy ?? defaults.createdBy,
|
|
257
|
+
createdAt: existing?.branch.createdAt ?? defaults.createdAt,
|
|
258
|
+
// Fork point is recorded once at creation; later saves must not move it
|
|
259
|
+
baseBranch: existing?.branch.baseBranch ?? incoming.branch?.baseBranch,
|
|
260
|
+
// Always stamped fresh; the spreads above would otherwise let the creation-time value win
|
|
261
|
+
// forever, freezing the timestamp the editor's Branches panel sorts and displays by
|
|
262
|
+
updatedAt: now,
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/** The branch.json a brand-new branch starts with: {@link mergeBranchMetadata} over nothing. */
|
|
267
|
+
export function buildInitialBranchMetadata(incoming, now) {
|
|
268
|
+
return mergeBranchMetadata(null, null, incoming, now);
|
|
269
|
+
}
|
|
181
270
|
/**
|
|
182
271
|
* The metadata update for archiving a branch whose PR merged. Shared by the
|
|
183
272
|
* worker's merge-poll (CmsWorker.pollMergeState) and the manual markAsMerged
|
|
@@ -193,6 +282,9 @@ export function buildMergedBranchUpdate(branchName, now = new Date()) {
|
|
|
193
282
|
status: 'archived',
|
|
194
283
|
pullRequestState: 'merged',
|
|
195
284
|
mergedAt: now.toISOString(),
|
|
285
|
+
// The merge settled every conflict the record named.
|
|
286
|
+
conflictStatus: 'clean',
|
|
287
|
+
conflictFiles: [],
|
|
196
288
|
};
|
|
197
289
|
}
|
|
198
290
|
/** {@link BranchMetadataFileManager.get} as a function. */
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { type BranchMetadataFile } from './branch-metadata-file.js';
|
|
2
|
+
import { type CloneRepoOptions } from './git-manager.js';
|
|
3
|
+
import type { ProvisionLog } from './utils/provision-log.js';
|
|
4
|
+
/** How long residue must have been untouched before a create request moves it aside. */
|
|
5
|
+
export declare const CREATE_RESIDUE_MIN_QUIET_MS = 60000;
|
|
6
|
+
/**
|
|
7
|
+
* Retriable: another process is setting this branch up, or what is at its path is too recently
|
|
8
|
+
* touched (or under an admin repair) to judge. Callers answer 503.
|
|
9
|
+
*/
|
|
10
|
+
export declare class BranchProvisioningBusyError extends Error {
|
|
11
|
+
readonly dirName: string;
|
|
12
|
+
constructor(dirName: string, message?: string);
|
|
13
|
+
}
|
|
14
|
+
/** The branch's path holds a git repository this deployment did not create (prod only). */
|
|
15
|
+
export declare class BranchDirOccupiedError extends Error {
|
|
16
|
+
readonly dirName: string;
|
|
17
|
+
constructor(dirName: string);
|
|
18
|
+
}
|
|
19
|
+
/** `YYYYMMDDTHHMMSSZ` in UTC, with no colons. */
|
|
20
|
+
export declare function formatDirStamp(date: Date): string;
|
|
21
|
+
/**
|
|
22
|
+
* The date in a name ending `-{STAMP}`, or null. Leftovers are aged by this alone, never by
|
|
23
|
+
* mtime: `rename()` keeps a directory's mtime, so a months-old residue would look old the
|
|
24
|
+
* moment it was moved.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseDirStamp(name: string): Date | null;
|
|
27
|
+
/**
|
|
28
|
+
* A branch directory name cut to a bounded, still-unique form: branch names run to 250 chars.
|
|
29
|
+
* @internal Exported for tests.
|
|
30
|
+
*/
|
|
31
|
+
export declare function shortDirName(dirName: string): string;
|
|
32
|
+
/** Where a branch directory goes the moment it is deleted, before its `rm`. */
|
|
33
|
+
export declare const deletingDirName: (dirName: string, now?: Date) => string;
|
|
34
|
+
/** The settings workspace's staging and trash siblings, under the workspace root. */
|
|
35
|
+
export declare const settingsStagingDirName: (now?: Date) => string;
|
|
36
|
+
export declare const settingsTrashDirName: (now?: Date) => string;
|
|
37
|
+
interface ResidueSignature {
|
|
38
|
+
hasGitDir: boolean;
|
|
39
|
+
configLock: boolean;
|
|
40
|
+
hasIndex: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What occupies a branch's final path.
|
|
44
|
+
*
|
|
45
|
+
* - `live` / `corrupt`: branch.json is there. Never touched by provisioning.
|
|
46
|
+
* - `protected`: an admin repair-metadata is mid-way (`branch.json.corrupt-*` with no
|
|
47
|
+
* branch.json), or branch.json vanished between the listing and the read. Left alone.
|
|
48
|
+
* - `residue`: nothing this deployment could still be using — no `.git`, or a `.git` whose config
|
|
49
|
+
* is missing or unreadable (an interrupted clone), or one whose remote is this deployment's own
|
|
50
|
+
* (an interrupted clone or delete).
|
|
51
|
+
* - `foreign`: any other repository, such as a dev CLI-sync workspace, which has no remote.
|
|
52
|
+
*/
|
|
53
|
+
export type FinalDirState = {
|
|
54
|
+
kind: 'vacant';
|
|
55
|
+
} | {
|
|
56
|
+
kind: 'live';
|
|
57
|
+
} | {
|
|
58
|
+
kind: 'corrupt';
|
|
59
|
+
} | {
|
|
60
|
+
kind: 'protected';
|
|
61
|
+
} | {
|
|
62
|
+
kind: 'foreign';
|
|
63
|
+
} | {
|
|
64
|
+
kind: 'residue';
|
|
65
|
+
signature: ResidueSignature;
|
|
66
|
+
};
|
|
67
|
+
/** Classify what is at `dirPath`; see {@link FinalDirState}. */
|
|
68
|
+
export declare function classifyFinalDir(dirPath: string, expectedRemoteUrl: string): Promise<FinalDirState>;
|
|
69
|
+
/**
|
|
70
|
+
* Milliseconds since anything was last created or removed in the directory, its `.git` or its
|
|
71
|
+
* `.canopy-meta`. A clone killed inside `git config` touched `.git` when it made `config.lock`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function quietAgeMs(dirPath: string, now?: number): Promise<number>;
|
|
74
|
+
type KeptReason = 'live' | 'corrupt' | 'protected' | 'foreign' | 'too-young' | 'replaced';
|
|
75
|
+
export type QuarantineResult = {
|
|
76
|
+
kind: 'quarantined';
|
|
77
|
+
trashName: string;
|
|
78
|
+
} | {
|
|
79
|
+
kind: 'vacant';
|
|
80
|
+
} | {
|
|
81
|
+
kind: 'kept';
|
|
82
|
+
reason: KeptReason;
|
|
83
|
+
};
|
|
84
|
+
export interface QuarantineOptions {
|
|
85
|
+
minQuietMs: number;
|
|
86
|
+
expectedRemoteUrl: string;
|
|
87
|
+
now?: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Move residue at `baseRoot/dirName` to `.trash-*`. The caller MUST hold the branch's
|
|
91
|
+
* provisioning lock: admin repair-metadata holds it while it legitimately leaves a directory
|
|
92
|
+
* with real edits and no branch.json.
|
|
93
|
+
*
|
|
94
|
+
* Move-then-verify: the name is renamed aside first and only what arrived at the new name is
|
|
95
|
+
* judged — the same inode the old name showed, still residue, quiet for `minQuietMs` — because
|
|
96
|
+
* a look through the old name can come from a stale NFS dentry for a directory another host has
|
|
97
|
+
* since replaced with a live clone. Anything that fails a check, or cannot be checked, is
|
|
98
|
+
* renamed straight back: the sweep would trash a `.repair-*` without branch.json.
|
|
99
|
+
*/
|
|
100
|
+
export declare function quarantineResidueAt(baseRoot: string, dirName: string, options: QuarantineOptions): Promise<QuarantineResult>;
|
|
101
|
+
export interface StageBranchOptions {
|
|
102
|
+
baseRoot: string;
|
|
103
|
+
dirName: string;
|
|
104
|
+
branchName: string;
|
|
105
|
+
baseBranch: string;
|
|
106
|
+
remoteUrl: string;
|
|
107
|
+
clone: Pick<CloneRepoOptions, 'remoteName' | 'config'>;
|
|
108
|
+
gitExcludePattern: string;
|
|
109
|
+
metadata: BranchMetadataFile;
|
|
110
|
+
provisionLog: ProvisionLog;
|
|
111
|
+
/** The sparse-checkout cone (branch-sparse.ts), set before the checkout; none for a full clone. */
|
|
112
|
+
sparseCone?: readonly string[] | null;
|
|
113
|
+
}
|
|
114
|
+
export type StageResult = {
|
|
115
|
+
kind: 'staged';
|
|
116
|
+
stagingPath: string;
|
|
117
|
+
meta: BranchMetadataFile;
|
|
118
|
+
} | {
|
|
119
|
+
kind: 'exists';
|
|
120
|
+
};
|
|
121
|
+
/** Remove a staging or deleted directory, leaving it to the worker's sweep on failure. */
|
|
122
|
+
export declare function removeLeftoverDir(dirPath: string): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Build a complete branch workspace, branch.json included, in a staging sibling of the final
|
|
125
|
+
* path. Takes no lock. Returns `exists` without checking out when a competitor published first.
|
|
126
|
+
* A failure removes the staging directory; a kill leaves it for the worker.
|
|
127
|
+
*/
|
|
128
|
+
export declare function stageBranchWorkspace(options: StageBranchOptions): Promise<StageResult>;
|
|
129
|
+
/**
|
|
130
|
+
* The branch's provisioning lock with a bounded wait; contention is
|
|
131
|
+
* {@link BranchProvisioningBusyError}.
|
|
132
|
+
*/
|
|
133
|
+
export declare function lockBranchName(baseRoot: string, dirName: string): Promise<() => Promise<void>>;
|
|
134
|
+
/** What blocks a refused publish rename, as {@link publishStaging} sees it. */
|
|
135
|
+
export type BlockingKind = 'live' | 'protected' | 'residue';
|
|
136
|
+
export interface PublishOptions {
|
|
137
|
+
baseRoot: string;
|
|
138
|
+
dirName: string;
|
|
139
|
+
stagingPath: string;
|
|
140
|
+
/** branch.json's `writeId` in the staged workspace: proof the rename was ours. */
|
|
141
|
+
writeId: string | undefined;
|
|
142
|
+
expectedRemoteUrl: string;
|
|
143
|
+
/** Test seam replacing the look at what blocks a refused rename. */
|
|
144
|
+
inspectBlocked?: (finalPath: string) => Promise<BlockingKind>;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Publish a staged workspace at its final path, under the branch's provisioning lock.
|
|
148
|
+
*
|
|
149
|
+
* A refused rename is inspected: branch.json there means another request won (`exists`);
|
|
150
|
+
* residue is quarantined and the rename retried once. ENOENT can be an NFS retransmission of a
|
|
151
|
+
* rename that already landed, so it counts as published when the final branch.json carries our
|
|
152
|
+
* `writeId`. The caller removes the staging directory on any outcome but `published`.
|
|
153
|
+
*/
|
|
154
|
+
export declare function publishStaging(options: PublishOptions): Promise<'published' | 'exists'>;
|
|
155
|
+
export interface LeftoverAction {
|
|
156
|
+
name: string;
|
|
157
|
+
action: 'removed' | 'restored' | 'trashed' | 'failed';
|
|
158
|
+
detail?: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Clear what killed processes left beside the branch directories (`baseRoot`) and the settings
|
|
162
|
+
* workspace (`workspaceRoot`): stale `.prov-*` builds, `.deleting-*` directories whose `rm` never
|
|
163
|
+
* finished, `.repair-*` from an interrupted quarantine, and expired settings trash. Ages come
|
|
164
|
+
* from the stamp in each name.
|
|
165
|
+
*/
|
|
166
|
+
export declare function sweepProvisioningLeftovers(baseRoot: string, workspaceRoot: string, now?: number): Promise<LeftoverAction[]>;
|
|
167
|
+
export {};
|