canopycms 0.0.67-int.90 → 0.0.67
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/README.md +30 -3
- package/dist/ai/generate.js +1 -27
- package/dist/ai/handler.d.ts +0 -4
- package/dist/ai/handler.js +0 -11
- package/dist/ai/index.d.ts +1 -8
- package/dist/ai/index.js +1 -8
- package/dist/ai/json-to-markdown.js +15 -58
- package/dist/ai/resolve-branch.d.ts +2 -7
- package/dist/ai/resolve-branch.js +2 -8
- package/dist/ai/strip-mdx.d.ts +5 -13
- package/dist/ai/strip-mdx.js +5 -17
- package/dist/ai/to-plain-text.d.ts +10 -28
- package/dist/ai/to-plain-text.js +10 -28
- package/dist/ai/transform-components.d.ts +2 -4
- package/dist/ai/transform-components.js +8 -19
- package/dist/ai/types.d.ts +5 -10
- package/dist/api/admin-branch-health.d.ts +4 -12
- package/dist/api/admin-branch-health.js +26 -66
- package/dist/api/admin.d.ts +4 -12
- package/dist/api/admin.js +10 -26
- package/dist/api/assets.d.ts +0 -25
- package/dist/api/assets.js +50 -91
- package/dist/api/branch-merge.d.ts +1 -1
- package/dist/api/branch-merge.js +0 -2
- package/dist/api/branch-review.d.ts +0 -8
- package/dist/api/branch-review.js +0 -13
- package/dist/api/branch-status.d.ts +0 -3
- package/dist/api/branch-status.js +8 -33
- package/dist/api/branch-withdraw.d.ts +0 -1
- package/dist/api/branch-withdraw.js +6 -9
- package/dist/api/branch.d.ts +7 -12
- package/dist/api/branch.js +7 -44
- package/dist/api/client.d.ts +71 -224
- package/dist/api/client.js +82 -233
- package/dist/api/comments.d.ts +2 -4
- package/dist/api/comments.js +0 -17
- package/dist/api/content.d.ts +0 -6
- package/dist/api/content.js +66 -142
- package/dist/api/entries-constants.d.ts +5 -9
- package/dist/api/entries-constants.js +5 -9
- package/dist/api/entries.d.ts +8 -13
- package/dist/api/entries.js +20 -51
- package/dist/api/github-sync.d.ts +2 -4
- package/dist/api/github-sync.js +4 -8
- package/dist/api/groups.d.ts +3 -0
- package/dist/api/groups.js +8 -21
- package/dist/api/guards.d.ts +4 -16
- package/dist/api/guards.js +3 -22
- package/dist/api/index.d.ts +4 -4
- package/dist/api/permissions.d.ts +2 -0
- package/dist/api/permissions.js +18 -40
- package/dist/api/reference-options.d.ts +0 -1
- package/dist/api/reference-options.js +3 -11
- package/dist/api/request-body-hash.d.ts +12 -23
- package/dist/api/request-body-hash.js +12 -23
- package/dist/api/resolve-references.d.ts +0 -1
- package/dist/api/resolve-references.js +13 -28
- package/dist/api/route-builder.d.ts +3 -7
- package/dist/api/route-builder.js +1 -1
- package/dist/api/routes.d.ts +12 -0
- package/dist/api/routes.js +47 -0
- package/dist/api/schema.d.ts +23 -33
- package/dist/api/schema.js +20 -74
- package/dist/api/settings-helpers.js +4 -5
- package/dist/api/types.d.ts +0 -5
- package/dist/api/user.js +0 -5
- package/dist/api/validators.d.ts +14 -52
- package/dist/api/validators.js +14 -52
- package/dist/assets/asset-prefixes.d.ts +4 -8
- package/dist/assets/asset-prefixes.js +4 -8
- package/dist/assets/asset-src.d.ts +5 -22
- package/dist/assets/asset-src.js +5 -21
- package/dist/assets/factory.d.ts +1 -2
- package/dist/assets/factory.js +1 -2
- package/dist/assets/finalize.d.ts +3 -1
- package/dist/assets/finalize.js +3 -1
- package/dist/assets/index.d.ts +3 -3
- package/dist/assets/index.js +1 -1
- package/dist/assets/keys.d.ts +5 -3
- package/dist/assets/keys.js +6 -3
- package/dist/assets/pipeline.d.ts +3 -2
- package/dist/assets/pipeline.js +22 -56
- package/dist/assets/sharp-loader.d.ts +2 -10
- package/dist/assets/sharp-loader.js +2 -10
- package/dist/assets/store-local.d.ts +1 -2
- package/dist/assets/store-local.js +1 -2
- package/dist/assets/store-s3.d.ts +3 -4
- package/dist/assets/svg-sanitizer.d.ts +6 -14
- package/dist/assets/svg-sanitizer.js +6 -14
- package/dist/assets/transform-directives.d.ts +3 -4
- package/dist/assets/transform-directives.js +1 -1
- package/dist/assets/transform.d.ts +19 -30
- package/dist/assets/transform.js +18 -31
- package/dist/auth/cache.d.ts +2 -3
- package/dist/auth/cache.js +2 -3
- package/dist/auth/caching-auth-plugin.d.ts +11 -22
- package/dist/auth/caching-auth-plugin.js +12 -14
- package/dist/auth/context-helpers.d.ts +6 -20
- package/dist/auth/context-helpers.js +5 -17
- package/dist/auth/file-based-auth-cache.d.ts +10 -21
- package/dist/auth/file-based-auth-cache.js +23 -51
- package/dist/auth/plugin.d.ts +29 -61
- package/dist/auth/plugin.js +9 -13
- package/dist/auth/types.d.ts +11 -20
- package/dist/authorization/branch.d.ts +30 -49
- package/dist/authorization/branch.js +16 -42
- package/dist/authorization/content.d.ts +11 -40
- package/dist/authorization/content.js +13 -45
- package/dist/authorization/groups/index.d.ts +0 -3
- package/dist/authorization/groups/index.js +0 -3
- package/dist/authorization/groups/loader.d.ts +14 -28
- package/dist/authorization/groups/loader.js +14 -38
- package/dist/authorization/groups/schema.d.ts +0 -6
- package/dist/authorization/groups/schema.js +4 -7
- package/dist/authorization/helpers.d.ts +9 -28
- package/dist/authorization/helpers.js +9 -28
- package/dist/authorization/index.d.ts +3 -25
- package/dist/authorization/index.js +3 -35
- package/dist/authorization/path.d.ts +6 -13
- package/dist/authorization/path.js +8 -25
- package/dist/authorization/permissions/index.d.ts +0 -3
- package/dist/authorization/permissions/index.js +0 -3
- package/dist/authorization/permissions/loader.d.ts +14 -27
- package/dist/authorization/permissions/loader.js +14 -32
- package/dist/authorization/permissions/schema.d.ts +1 -6
- package/dist/authorization/permissions/schema.js +6 -13
- package/dist/authorization/protected-branch.d.ts +36 -65
- package/dist/authorization/protected-branch.js +29 -41
- package/dist/authorization/settings-file-store.d.ts +38 -81
- package/dist/authorization/settings-file-store.js +38 -79
- package/dist/authorization/types.d.ts +4 -26
- package/dist/authorization/types.js +0 -6
- package/dist/authorization/validation.d.ts +1 -23
- package/dist/authorization/validation.js +1 -24
- package/dist/branch-health.d.ts +39 -67
- package/dist/branch-health.js +27 -43
- package/dist/branch-metadata-file.d.ts +16 -32
- package/dist/branch-metadata-file.js +13 -26
- package/dist/branch-metadata.d.ts +49 -80
- package/dist/branch-metadata.js +57 -87
- package/dist/branch-registry.d.ts +26 -64
- package/dist/branch-registry.js +44 -93
- package/dist/branch-schema-cache.d.ts +34 -124
- package/dist/branch-schema-cache.js +49 -155
- package/dist/branch-workspace.d.ts +4 -4
- package/dist/branch-workspace.js +15 -19
- package/dist/build/generate-ai-content.d.ts +1 -0
- package/dist/build/generate-ai-content.js +12 -25
- package/dist/build-canopy.d.ts +17 -27
- package/dist/build-canopy.js +16 -23
- package/dist/build-mode.d.ts +25 -27
- package/dist/build-mode.js +25 -29
- package/dist/cli/cli.d.ts +15 -26
- package/dist/cli/cli.js +601 -978
- package/dist/cli/generate-ai-content.js +351 -628
- package/dist/cli/github-app-manifest.d.ts +84 -0
- package/dist/cli/github-app-manifest.js +161 -0
- package/dist/cli/init-github-app.d.ts +110 -444
- package/dist/cli/init-github-app.js +236 -773
- package/dist/cli/init.js +39 -80
- package/dist/cli/migrate.d.ts +13 -10
- package/dist/cli/migrate.js +22 -30
- package/dist/cli/project-detect.d.ts +17 -31
- package/dist/cli/project-detect.js +31 -42
- package/dist/cli/project-root.d.ts +4 -11
- package/dist/cli/project-root.js +4 -11
- package/dist/cli/prompt.d.ts +10 -0
- package/dist/cli/prompt.js +67 -0
- package/dist/cli/sync.d.ts +0 -8
- package/dist/cli/sync.js +8 -28
- package/dist/cli/template-files/middleware.ts.template +15 -29
- package/dist/cli/templates.d.ts +2 -12
- package/dist/cli/templates.js +4 -4
- package/dist/client.d.ts +1 -1
- package/dist/comment-store.d.ts +32 -53
- package/dist/comment-store.js +32 -52
- package/dist/config/flatten.d.ts +0 -3
- package/dist/config/flatten.js +2 -12
- package/dist/config/helpers.js +2 -2
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +1 -4
- package/dist/config/schemas/collection.d.ts +0 -8
- package/dist/config/schemas/collection.js +0 -10
- package/dist/config/schemas/config.d.ts +6 -78
- package/dist/config/schemas/config.js +38 -54
- package/dist/config/schemas/field.d.ts +2 -242
- package/dist/config/schemas/field.js +9 -20
- package/dist/config/schemas/media.js +1 -1
- package/dist/config/schemas/permissions.js +0 -2
- package/dist/config/schemas/url.d.ts +5 -6
- package/dist/config/schemas/url.js +5 -6
- package/dist/config/types.d.ts +45 -33
- package/dist/config/types.js +0 -1
- package/dist/config.d.ts +1 -8
- package/dist/config.js +1 -9
- package/dist/content-id-index.d.ts +76 -188
- package/dist/content-id-index.js +79 -230
- package/dist/content-index-generation.d.ts +27 -48
- package/dist/content-index-generation.js +35 -84
- package/dist/content-index-registry.d.ts +15 -24
- package/dist/content-index-registry.js +5 -9
- package/dist/content-listing.d.ts +64 -107
- package/dist/content-listing.js +68 -118
- package/dist/content-reader.d.ts +20 -29
- package/dist/content-reader.js +10 -18
- package/dist/content-store.d.ts +210 -336
- package/dist/content-store.js +413 -657
- package/dist/content-tree.d.ts +44 -86
- package/dist/content-tree.js +26 -54
- package/dist/context.d.ts +67 -94
- package/dist/context.js +53 -72
- package/dist/dev-content-watcher.d.ts +18 -18
- package/dist/dev-content-watcher.js +41 -44
- package/dist/editor/BranchManager.d.ts +3 -6
- package/dist/editor/BranchManager.js +24 -44
- package/dist/editor/CanopyEditor.js +5 -2
- package/dist/editor/CommentsPanel.js +0 -2
- package/dist/editor/Editor.js +19 -76
- package/dist/editor/EditorAuthGate.d.ts +27 -0
- package/dist/editor/EditorAuthGate.js +140 -0
- package/dist/editor/EntryNavigator.js +0 -20
- package/dist/editor/FormRenderer.d.ts +1 -2
- package/dist/editor/FormRenderer.js +8 -23
- package/dist/editor/admin/SystemHealthPanel.js +20 -37
- package/dist/editor/admin/useSystemHealth.d.ts +2 -3
- package/dist/editor/admin/useSystemHealth.js +10 -12
- package/dist/editor/client-reference-resolver.d.ts +2 -5
- package/dist/editor/client-reference-resolver.js +2 -19
- package/dist/editor/comments/BranchComments.js +0 -4
- package/dist/editor/comments/EntryComments.js +0 -4
- package/dist/editor/comments/FieldWrapper.js +0 -4
- package/dist/editor/comments/InlineCommentThread.js +0 -2
- package/dist/editor/comments/ThreadCarousel.d.ts +0 -58
- package/dist/editor/comments/ThreadCarousel.js +0 -12
- package/dist/editor/components/EditorFooter.d.ts +0 -8
- package/dist/editor/components/EditorFooter.js +0 -5
- package/dist/editor/components/EditorHeader.d.ts +0 -31
- package/dist/editor/components/EditorHeader.js +21 -71
- package/dist/editor/components/EditorSidebar.d.ts +0 -19
- package/dist/editor/components/EditorSidebar.js +0 -16
- package/dist/editor/components/EntryCreateModal.d.ts +4 -4
- package/dist/editor/components/EntryCreateModal.js +7 -22
- package/dist/editor/components/RenameEntryModal.js +0 -4
- package/dist/editor/components/UserBadge.js +0 -10
- package/dist/editor/components/index.js +0 -2
- package/dist/editor/context/ApiClientContext.d.ts +8 -7
- package/dist/editor/context/ApiClientContext.js +39 -11
- package/dist/editor/context/AssetContext.d.ts +7 -17
- package/dist/editor/context/AssetContext.js +7 -17
- package/dist/editor/context/EditorIdentityContext.d.ts +13 -0
- package/dist/editor/context/EditorIdentityContext.js +7 -0
- package/dist/editor/context/EditorStateContext.d.ts +10 -41
- package/dist/editor/context/EditorStateContext.js +7 -23
- package/dist/editor/context/SWRProvider.d.ts +3 -6
- package/dist/editor/context/index.d.ts +1 -20
- package/dist/editor/context/index.js +1 -20
- package/dist/editor/editor-utils.d.ts +8 -47
- package/dist/editor/editor-utils.js +19 -72
- package/dist/editor/fields/BlockField.d.ts +2 -2
- package/dist/editor/fields/BlockField.js +1 -2
- package/dist/editor/fields/CodeField.d.ts +0 -1
- package/dist/editor/fields/CodeField.js +0 -1
- package/dist/editor/fields/DateTimeField.d.ts +15 -22
- package/dist/editor/fields/DateTimeField.js +17 -26
- package/dist/editor/fields/ImageField.d.ts +2 -2
- package/dist/editor/fields/ImageField.js +8 -14
- package/dist/editor/fields/InlineGroupField.d.ts +0 -1
- package/dist/editor/fields/InlineGroupField.js +0 -1
- package/dist/editor/fields/MarkdownField.d.ts +0 -1
- package/dist/editor/fields/MarkdownField.js +6 -11
- package/dist/editor/fields/MdxImageDialog.d.ts +1 -1
- package/dist/editor/fields/MdxImageDialog.js +0 -1
- package/dist/editor/fields/NumberField.d.ts +9 -19
- package/dist/editor/fields/NumberField.js +11 -22
- package/dist/editor/fields/NumberListField.d.ts +7 -10
- package/dist/editor/fields/NumberListField.js +7 -10
- package/dist/editor/fields/ObjectField.d.ts +5 -8
- package/dist/editor/fields/ObjectField.js +0 -1
- package/dist/editor/fields/ReferenceField.d.ts +1 -1
- package/dist/editor/fields/ReferenceField.js +11 -16
- package/dist/editor/fields/SelectField.d.ts +2 -2
- package/dist/editor/fields/SelectField.js +0 -1
- package/dist/editor/fields/StringListField.d.ts +0 -4
- package/dist/editor/fields/StringListField.js +4 -9
- package/dist/editor/fields/TextField.d.ts +0 -1
- package/dist/editor/fields/TextField.js +0 -1
- package/dist/editor/fields/ToggleField.d.ts +0 -1
- package/dist/editor/fields/ToggleField.js +0 -1
- package/dist/editor/fields/entry-link/EntryLinkContext.js +2 -4
- package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -3
- package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -4
- package/dist/editor/group-manager/ExternalGroupsTab.d.ts +0 -3
- package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
- package/dist/editor/group-manager/GroupCard.d.ts +0 -3
- package/dist/editor/group-manager/GroupForm.d.ts +0 -3
- package/dist/editor/group-manager/InternalGroupsTab.d.ts +0 -3
- package/dist/editor/group-manager/InternalGroupsTab.js +0 -2
- package/dist/editor/group-manager/MemberList.d.ts +0 -3
- package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +0 -3
- package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -3
- package/dist/editor/group-manager/hooks/useGroupState.d.ts +0 -3
- package/dist/editor/group-manager/hooks/useGroupState.js +0 -5
- package/dist/editor/group-manager/hooks/useUserSearch.d.ts +0 -3
- package/dist/editor/group-manager/hooks/useUserSearch.js +0 -3
- package/dist/editor/group-manager/index.d.ts +0 -6
- package/dist/editor/group-manager/index.js +0 -10
- package/dist/editor/group-manager/types.d.ts +2 -5
- package/dist/editor/group-manager/types.js +0 -3
- package/dist/editor/hooks/index.d.ts +6 -11
- package/dist/editor/hooks/index.js +6 -12
- package/dist/editor/hooks/useBranchActions.d.ts +0 -14
- package/dist/editor/hooks/useBranchActions.js +0 -22
- package/dist/editor/hooks/useBranchManager.d.ts +2 -48
- package/dist/editor/hooks/useBranchManager.js +4 -64
- package/dist/editor/hooks/useBranchesData.d.ts +7 -6
- package/dist/editor/hooks/useBranchesData.js +7 -6
- package/dist/editor/hooks/useCommentSystem.d.ts +0 -49
- package/dist/editor/hooks/useCommentSystem.js +1 -36
- package/dist/editor/hooks/useDraftManager.d.ts +2 -35
- package/dist/editor/hooks/useDraftManager.js +23 -64
- package/dist/editor/hooks/useEditorLayout.d.ts +0 -20
- package/dist/editor/hooks/useEditorLayout.js +0 -23
- package/dist/editor/hooks/useEntriesData.d.ts +1 -0
- package/dist/editor/hooks/useEntriesData.js +2 -3
- package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -2
- package/dist/editor/hooks/useEntryLinkResolution.js +2 -2
- package/dist/editor/hooks/useEntryManager.d.ts +2 -27
- package/dist/editor/hooks/useEntryManager.js +29 -78
- package/dist/editor/hooks/useGroupManager.d.ts +0 -21
- package/dist/editor/hooks/useGroupManager.js +0 -21
- package/dist/editor/hooks/usePermissionManager.d.ts +0 -20
- package/dist/editor/hooks/usePermissionManager.js +0 -20
- package/dist/editor/hooks/useReferenceResolution.d.ts +2 -33
- package/dist/editor/hooks/useReferenceResolution.js +1 -48
- package/dist/editor/hooks/useSchemaManager.d.ts +8 -27
- package/dist/editor/hooks/useSchemaManager.js +0 -19
- package/dist/editor/hooks/useUserContext.d.ts +2 -9
- package/dist/editor/hooks/useUserContext.js +10 -10
- package/dist/editor/hooks/useUserMetadata.d.ts +0 -2
- package/dist/editor/hooks/useUserMetadata.js +0 -3
- package/dist/editor/media/AssetCard.d.ts +0 -1
- package/dist/editor/media/AssetCard.js +1 -2
- package/dist/editor/media/CropStep.d.ts +0 -1
- package/dist/editor/media/CropStep.js +0 -1
- package/dist/editor/media/MediaLibrary.d.ts +1 -2
- package/dist/editor/media/MediaLibrary.js +1 -2
- package/dist/editor/media/MediaLibraryBody.d.ts +0 -1
- package/dist/editor/media/MediaLibraryBody.js +0 -1
- package/dist/editor/media/crop-math.d.ts +1 -1
- package/dist/editor/media/crop-math.js +1 -1
- package/dist/editor/permission-manager/GroupSelector.d.ts +0 -3
- package/dist/editor/permission-manager/PermissionEditor.d.ts +0 -4
- package/dist/editor/permission-manager/PermissionEditor.js +0 -1
- package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +0 -7
- package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -4
- package/dist/editor/permission-manager/PermissionTree.d.ts +0 -4
- package/dist/editor/permission-manager/UserSelector.d.ts +0 -3
- package/dist/editor/permission-manager/constants.d.ts +0 -3
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +0 -3
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -10
- package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +0 -14
- package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -11
- package/dist/editor/permission-manager/index.d.ts +0 -7
- package/dist/editor/permission-manager/index.js +0 -16
- package/dist/editor/permission-manager/types.d.ts +2 -12
- package/dist/editor/permission-manager/types.js +0 -3
- package/dist/editor/permission-manager/utils.d.ts +0 -28
- package/dist/editor/permission-manager/utils.js +9 -23
- package/dist/editor/preview-bridge.d.ts +5 -1
- package/dist/editor/preview-bridge.js +4 -4
- package/dist/editor/schema-editor/CollectionEditor.d.ts +0 -5
- package/dist/editor/schema-editor/CollectionEditor.js +9 -31
- package/dist/editor/schema-editor/EntryTypeEditor.d.ts +0 -6
- package/dist/editor/schema-editor/EntryTypeEditor.js +3 -24
- package/dist/editor/schema-editor/index.d.ts +0 -7
- package/dist/editor/schema-editor/index.js +0 -7
- package/dist/editor/utils/env.d.ts +3 -18
- package/dist/editor/utils/env.js +6 -25
- package/dist/entry-link-resolver.d.ts +13 -35
- package/dist/entry-link-resolver.js +20 -53
- package/dist/entry-schema-registry.d.ts +20 -60
- package/dist/entry-schema-registry.js +20 -69
- package/dist/entry-schema.d.ts +118 -223
- package/dist/entry-schema.js +46 -91
- package/dist/git-manager.d.ts +101 -200
- package/dist/git-manager.js +211 -384
- package/dist/github-service.d.ts +34 -75
- package/dist/github-service.js +37 -91
- package/dist/http/handler.d.ts +6 -37
- package/dist/http/handler.js +65 -120
- package/dist/http/router.d.ts +15 -32
- package/dist/http/router.js +34 -99
- package/dist/http/types.d.ts +15 -40
- package/dist/http/types.js +3 -7
- package/dist/id.d.ts +4 -10
- package/dist/id.js +4 -10
- package/dist/index.js +6 -9
- package/dist/operating-mode/client-safe-strategy.d.ts +7 -14
- package/dist/operating-mode/client-safe-strategy.js +8 -28
- package/dist/operating-mode/client-unsafe-strategy.d.ts +6 -16
- package/dist/operating-mode/client-unsafe-strategy.js +9 -34
- package/dist/operating-mode/client.d.ts +3 -10
- package/dist/operating-mode/client.js +3 -8
- package/dist/operating-mode/deployment-name-fixtures.d.ts +14 -18
- package/dist/operating-mode/deployment-name-fixtures.js +14 -18
- package/dist/operating-mode/deployment-name.d.ts +19 -29
- package/dist/operating-mode/deployment-name.js +29 -47
- package/dist/operating-mode/index.d.ts +4 -25
- package/dist/operating-mode/index.js +9 -31
- package/dist/operating-mode/mode-env.d.ts +32 -54
- package/dist/operating-mode/mode-env.js +36 -58
- package/dist/operating-mode/types.d.ts +24 -72
- package/dist/operating-mode/types.js +1 -7
- package/dist/paths/branch-name.d.ts +30 -40
- package/dist/paths/branch-name.js +30 -40
- package/dist/paths/branch.d.ts +4 -18
- package/dist/paths/branch.js +6 -22
- package/dist/paths/index.d.ts +2 -13
- package/dist/paths/index.js +3 -23
- package/dist/paths/normalize-server.d.ts +3 -15
- package/dist/paths/normalize-server.js +3 -15
- package/dist/paths/normalize.d.ts +13 -33
- package/dist/paths/normalize.js +13 -33
- package/dist/paths/resolve.d.ts +4 -17
- package/dist/paths/resolve.js +4 -22
- package/dist/paths/types.d.ts +12 -34
- package/dist/paths/types.js +5 -7
- package/dist/paths/validation.d.ts +22 -119
- package/dist/paths/validation.js +30 -148
- package/dist/reference-resolver.d.ts +0 -13
- package/dist/reference-resolver.js +0 -18
- package/dist/resolve-canopy-user.d.ts +16 -18
- package/dist/resolve-canopy-user.js +17 -26
- package/dist/resource-generation.d.ts +16 -25
- package/dist/resource-generation.js +34 -97
- package/dist/schema/meta-loader.d.ts +2 -14
- package/dist/schema/meta-loader.js +15 -64
- package/dist/schema/resolver.d.ts +1 -4
- package/dist/schema/resolver.js +1 -6
- package/dist/schema/schema-store-types.d.ts +0 -12
- package/dist/schema/schema-store.d.ts +61 -138
- package/dist/schema/schema-store.js +85 -282
- package/dist/schema/types.d.ts +0 -4
- package/dist/server.d.ts +62 -93
- package/dist/server.js +63 -94
- package/dist/services.d.ts +23 -47
- package/dist/services.js +55 -98
- package/dist/settings-workspace.d.ts +17 -30
- package/dist/settings-workspace.js +51 -81
- package/dist/static/index.d.ts +26 -52
- package/dist/static/index.js +33 -67
- package/dist/{worker/task-queue.d.ts → task-queue/cms-task-queue.d.ts} +7 -9
- package/dist/task-queue/cms-task-queue.js +13 -0
- package/dist/task-queue/task-queue-config.d.ts +3 -0
- package/dist/{worker → task-queue}/task-queue-config.js +1 -6
- package/dist/task-queue/task-queue.d.ts +16 -22
- package/dist/task-queue/task-queue.js +20 -44
- package/dist/task-queue/types.d.ts +0 -1
- package/dist/task-queue/worker-status.d.ts +35 -0
- package/dist/task-queue/worker-status.js +40 -0
- package/dist/types.d.ts +40 -51
- package/dist/url-collision.d.ts +26 -40
- package/dist/url-collision.js +47 -63
- package/dist/url-exclusivity-fixtures.d.ts +15 -20
- package/dist/url-exclusivity-fixtures.js +14 -19
- package/dist/url-path-resolver.d.ts +8 -16
- package/dist/url-path-resolver.js +19 -29
- package/dist/user.d.ts +8 -21
- package/dist/user.js +10 -19
- package/dist/utils/async-mutex.js +4 -6
- package/dist/utils/atomic-write.d.ts +2 -11
- package/dist/utils/atomic-write.js +2 -11
- package/dist/utils/body-field.d.ts +8 -15
- package/dist/utils/body-field.js +8 -15
- package/dist/utils/content-serialize.d.ts +18 -24
- package/dist/utils/content-serialize.js +109 -160
- package/dist/utils/content-write-lock.d.ts +51 -106
- package/dist/utils/content-write-lock.js +66 -111
- package/dist/utils/debug.d.ts +7 -23
- package/dist/utils/debug.js +7 -23
- package/dist/utils/entry-url.d.ts +11 -23
- package/dist/utils/entry-url.js +11 -26
- package/dist/utils/error.d.ts +25 -90
- package/dist/utils/error.js +60 -147
- package/dist/utils/flatten-group-fields.d.ts +5 -7
- package/dist/utils/flatten-group-fields.js +5 -7
- package/dist/utils/format.d.ts +1 -5
- package/dist/utils/format.js +1 -5
- package/dist/utils/fs.d.ts +3 -4
- package/dist/utils/fs.js +3 -4
- package/dist/utils/git.d.ts +52 -83
- package/dist/utils/git.js +79 -125
- package/dist/utils/logger.d.ts +35 -50
- package/dist/utils/logger.js +35 -52
- package/dist/utils/provisioning-lock.d.ts +23 -35
- package/dist/utils/provisioning-lock.js +49 -69
- package/dist/utils/sanitize-href.d.ts +5 -10
- package/dist/utils/sanitize-href.js +8 -18
- package/dist/utils/title-field.d.ts +17 -20
- package/dist/utils/title-field.js +18 -27
- package/dist/utils/typed-filename.d.ts +20 -48
- package/dist/utils/typed-filename.js +25 -61
- package/dist/utils/url-prefix.d.ts +3 -3
- package/dist/utils/url-prefix.js +3 -3
- package/dist/validation/block-structural-keys.d.ts +16 -31
- package/dist/validation/block-structural-keys.js +19 -29
- package/dist/validation/deletion-checker.d.ts +0 -34
- package/dist/validation/deletion-checker.js +1 -43
- package/dist/validation/entry-link-validator.d.ts +6 -13
- package/dist/validation/entry-link-validator.js +4 -13
- package/dist/validation/entry-type-reference-validator.d.ts +13 -18
- package/dist/validation/entry-type-reference-validator.js +13 -18
- package/dist/validation/entry-validator.d.ts +13 -25
- package/dist/validation/entry-validator.js +15 -32
- package/dist/validation/field-traversal.d.ts +3 -30
- package/dist/validation/field-traversal.js +1 -32
- package/dist/validation/reference-validator.d.ts +5 -24
- package/dist/validation/reference-validator.js +5 -30
- package/dist/worker/cms-worker.d.ts +172 -232
- package/dist/worker/cms-worker.js +241 -333
- package/dist/worker/git-sync.d.ts +32 -133
- package/dist/worker/git-sync.js +129 -191
- package/dist/worker/github-auth.d.ts +13 -19
- package/dist/worker/github-auth.js +13 -19
- package/dist/worker/history-rewrite.d.ts +53 -66
- package/dist/worker/history-rewrite.js +54 -60
- package/dist/worker/log.d.ts +24 -41
- package/dist/worker/log.js +25 -42
- package/dist/worker/rebase.d.ts +20 -110
- package/dist/worker/rebase.js +279 -354
- package/dist/worker/task-runner.d.ts +32 -66
- package/dist/worker/task-runner.js +126 -162
- package/dist/worker/worker-context.d.ts +41 -81
- package/package.json +4 -4
- package/dist/cli/template-files/middleware-clerk.ts.template +0 -37
- package/dist/worker/task-queue-config.d.ts +0 -8
- package/dist/worker/task-queue.js +0 -19
- package/dist/worker/worker-status.d.ts +0 -47
- package/dist/worker/worker-status.js +0 -52
package/dist/services.js
CHANGED
|
@@ -7,18 +7,15 @@ import { getDefaultBranchBase, sanitizeBranchName } from './paths/index.js';
|
|
|
7
7
|
import { createGitHubService } from './github-service.js';
|
|
8
8
|
import { operatingStrategy } from './operating-mode/index.js';
|
|
9
9
|
import { BranchSchemaCache } from './branch-schema-cache.js';
|
|
10
|
-
import { enqueueTask } from './
|
|
11
|
-
import { getTaskQueueDir } from './
|
|
10
|
+
import { enqueueTask } from './task-queue/cms-task-queue.js';
|
|
11
|
+
import { getTaskQueueDir } from './task-queue/task-queue-config.js';
|
|
12
12
|
import { detectHeadBranch } from './utils/git.js';
|
|
13
13
|
import { readsFromCheckout } from './build-mode.js';
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* - Static deployments and builds read the checkout: defaultBaseBranch ?? 'main', no git.
|
|
20
|
-
* - In dev mode, auto-detect from the current git HEAD branch.
|
|
21
|
-
* - In prod mode, fall back to defaultBaseBranch ?? 'main'.
|
|
15
|
+
* A per-instance active-branch detector with its own 5s TTL cache, in priority
|
|
16
|
+
* order: an explicitly configured value; `defaultBaseBranch ?? 'main'` with no
|
|
17
|
+
* git for static deployments and builds; git HEAD in dev; and
|
|
18
|
+
* `defaultBaseBranch ?? 'main'` in prod.
|
|
22
19
|
*/
|
|
23
20
|
function createActiveBranchDetector() {
|
|
24
21
|
let cache = null;
|
|
@@ -33,10 +30,9 @@ function createActiveBranchDetector() {
|
|
|
33
30
|
if (cache && now < cache.expiresAt) {
|
|
34
31
|
return cache.value;
|
|
35
32
|
}
|
|
36
|
-
//
|
|
37
|
-
// sourceRoot
|
|
38
|
-
//
|
|
39
|
-
// base-branch fallback instead of throwing.
|
|
33
|
+
// Detect from cwd, never sourceRoot: git walks up to find .git, and
|
|
34
|
+
// sourceRoot locates content, not the repo root. On a detached HEAD or
|
|
35
|
+
// outside a repo, detectHeadBranch falls back instead of throwing.
|
|
40
36
|
const branch = await detectHeadBranch(process.cwd(), config.defaultBaseBranch ?? 'main');
|
|
41
37
|
cache = { value: branch, expiresAt: now + 5000 };
|
|
42
38
|
return branch;
|
|
@@ -45,8 +41,8 @@ function createActiveBranchDetector() {
|
|
|
45
41
|
};
|
|
46
42
|
}
|
|
47
43
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
44
|
+
* Bootstrap admin IDs from the environment. These users are treated as Admins
|
|
45
|
+
* whatever their group membership.
|
|
50
46
|
*/
|
|
51
47
|
export const getBootstrapAdminIds = () => {
|
|
52
48
|
const envVar = process.env.CANOPY_BOOTSTRAP_ADMIN_IDS;
|
|
@@ -58,28 +54,17 @@ export const getBootstrapAdminIds = () => {
|
|
|
58
54
|
.filter(Boolean));
|
|
59
55
|
};
|
|
60
56
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* Schema is now loaded per-branch via BranchSchemaCache, not at startup.
|
|
66
|
-
*
|
|
67
|
-
* @param config - Validated Canopy configuration
|
|
68
|
-
* @param options - Optional settings including entry schema registry
|
|
57
|
+
* Build the reusable helpers from a validated CanopyConfig — once at startup,
|
|
58
|
+
* then injected into request handlers and loaders. Schema is not loaded here;
|
|
59
|
+
* BranchSchemaCache loads it per branch.
|
|
69
60
|
*/
|
|
70
61
|
export const createCanopyServices = async (config, options = {}) => {
|
|
71
62
|
return _createCanopyServicesInternal(config, options);
|
|
72
63
|
};
|
|
73
|
-
/**
|
|
74
|
-
* Create services for testing.
|
|
75
|
-
* Schema is loaded per-branch via BranchSchemaCache.
|
|
76
|
-
*
|
|
77
|
-
* @param config - Validated Canopy configuration
|
|
78
|
-
* @param options - Optional settings including entry schema registry
|
|
79
|
-
*/
|
|
64
|
+
/** {@link createCanopyServices} for tests, with branch identity pinned. */
|
|
80
65
|
export const createTestCanopyServices = async (config, options = {}) => {
|
|
81
|
-
//
|
|
82
|
-
//
|
|
66
|
+
// Pin both branch identity fields rather than auto-detect from git HEAD,
|
|
67
|
+
// which varies with the developer's working branch.
|
|
83
68
|
const testConfig = {
|
|
84
69
|
...config,
|
|
85
70
|
defaultBaseBranch: config.defaultBaseBranch ?? 'main',
|
|
@@ -87,35 +72,28 @@ export const createTestCanopyServices = async (config, options = {}) => {
|
|
|
87
72
|
};
|
|
88
73
|
return _createCanopyServicesInternal(testConfig, options);
|
|
89
74
|
};
|
|
90
|
-
/**
|
|
91
|
-
* Internal implementation shared by both production and test functions.
|
|
92
|
-
* Not exported - use createCanopyServices() or createTestCanopyServices().
|
|
93
|
-
*/
|
|
75
|
+
/** Shared implementation; call createCanopyServices or createTestCanopyServices. */
|
|
94
76
|
async function _createCanopyServicesInternal(config, options) {
|
|
95
|
-
//
|
|
77
|
+
// Mode-specific requirements, e.g. prod's git bot credentials for GitHub.
|
|
96
78
|
const strategy = operatingStrategy(config.mode);
|
|
97
79
|
strategy.validateConfig(config);
|
|
98
|
-
// Resolve branch identity once
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
// Bake both into config so all downstream code reads one consistent value.
|
|
103
|
-
// The detector has its own per-instance 5s TTL cache for git HEAD checks.
|
|
80
|
+
// Resolve branch identity once and bake both into config, so downstream code
|
|
81
|
+
// reads one consistent value (see ARCHITECTURE.md "Branch Identity"): the
|
|
82
|
+
// active branch is the workspace to serve from, the base branch the fork
|
|
83
|
+
// point for new editing branches.
|
|
104
84
|
const detectActiveBranch = createActiveBranchDetector();
|
|
105
85
|
const explicitActiveBranch = config.defaultActiveBranch;
|
|
106
86
|
const explicitBaseBranch = config.defaultBaseBranch;
|
|
107
87
|
const defaultActiveBranch = await detectActiveBranch(config);
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
88
|
+
// Unset, the base branch follows the same dev-mode HEAD detection, matching
|
|
89
|
+
// resolveBaseBranch in utils/git.ts — the canonical definition workspace
|
|
90
|
+
// provisioning uses.
|
|
111
91
|
const defaultBaseBranch = explicitBaseBranch ??
|
|
112
92
|
(config.mode === 'dev' && !readsFromCheckout(config)
|
|
113
93
|
? await detectActiveBranch({ ...config, defaultActiveBranch: undefined })
|
|
114
94
|
: 'main');
|
|
115
95
|
config = { ...config, defaultActiveBranch, defaultBaseBranch };
|
|
116
|
-
// Load bootstrap admin IDs from environment
|
|
117
96
|
const bootstrapAdminIds = getBootstrapAdminIds();
|
|
118
|
-
// Create per-branch schema cache (or use provided one for testing)
|
|
119
97
|
const branchSchemaCache = options.branchSchemaCache ?? new BranchSchemaCache(config.mode);
|
|
120
98
|
const checkBranchAccess = createCheckBranchAccess(config.defaultBranchAccess ?? 'deny', config);
|
|
121
99
|
// Content access loads permissions dynamically from the settings branch (orphan git branch)
|
|
@@ -124,8 +102,6 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
124
102
|
const strategy = operatingStrategy(config.mode);
|
|
125
103
|
const settingsRoot = strategy.getSettingsRoot();
|
|
126
104
|
const branchName = strategy.getSettingsBranchName(config);
|
|
127
|
-
// Use SettingsWorkspaceManager to ensure git workspace for settings
|
|
128
|
-
// This is Lambda-safe because the lock is in-memory per process
|
|
129
105
|
const manager = new SettingsWorkspaceManager(config);
|
|
130
106
|
await manager.ensureGitWorkspace({
|
|
131
107
|
settingsRoot,
|
|
@@ -182,14 +158,12 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
182
158
|
});
|
|
183
159
|
await git.checkoutBranch(options.context.branch.name);
|
|
184
160
|
const status = await git.status();
|
|
185
|
-
// Commit and push
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
// there's something new to send -- we just committed, or the local
|
|
192
|
-
// branch already had unpushed commits from an earlier failed attempt.
|
|
161
|
+
// Commit and push answer two DIFFERENT questions. Committing cleans the
|
|
162
|
+
// working tree, so one combined "tree is dirty" gate makes a retry after a
|
|
163
|
+
// failed push a silent no-op: nothing left to commit, the block is skipped,
|
|
164
|
+
// and success is reported though the commit never reached the remote. Push
|
|
165
|
+
// whenever there is something new to send — we just committed, or the local
|
|
166
|
+
// branch already had unpushed commits from an earlier attempt.
|
|
193
167
|
let committed = false;
|
|
194
168
|
if (status.files.length > 0) {
|
|
195
169
|
await git.add('.');
|
|
@@ -200,7 +174,6 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
200
174
|
await git.push(options.context.branch.name);
|
|
201
175
|
}
|
|
202
176
|
};
|
|
203
|
-
// Create GitHub service if applicable (only for modes that support pull requests)
|
|
204
177
|
// Must be initialized before closures that reference it (commitToSettingsBranch)
|
|
205
178
|
let githubService;
|
|
206
179
|
if (operatingStrategy(config.mode).supportsPullRequests()) {
|
|
@@ -220,7 +193,6 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
220
193
|
}
|
|
221
194
|
const commitToSettingsBranch = async (options) => {
|
|
222
195
|
const mode = config.mode;
|
|
223
|
-
// Check if this mode supports git operations
|
|
224
196
|
if (!operatingStrategy(mode).shouldCommit()) {
|
|
225
197
|
return { committed: false, pushed: false };
|
|
226
198
|
}
|
|
@@ -228,9 +200,9 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
228
200
|
// Settings workspace: no ContentStore roots here, skip the index marker
|
|
229
201
|
const git = createGitManagerFor(options.branchRoot, { skipIndexMarker: true });
|
|
230
202
|
try {
|
|
231
|
-
// Pull
|
|
232
|
-
//
|
|
233
|
-
//
|
|
203
|
+
// Pull the remote SETTINGS branch, never the base branch: settings
|
|
204
|
+
// branches are orphans and must never merge from main.
|
|
205
|
+
// BranchWorkspaceManager has already put us on the settings branch.
|
|
234
206
|
try {
|
|
235
207
|
await git.pullCurrentBranch();
|
|
236
208
|
}
|
|
@@ -239,17 +211,11 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
239
211
|
// pushed, so the remote has no ref to pull. Anything else — a
|
|
240
212
|
// GitConflictError, a merge that cannot proceed, a broken workspace —
|
|
241
213
|
// must surface (the outer catch turns it into an error result).
|
|
242
|
-
// A blanket catch here previously logged every failure as "normal for
|
|
243
|
-
// first commit", which is how pullCurrentBranch could stay broken on
|
|
244
|
-
// every call without anyone noticing: the settings branch then silently
|
|
245
|
-
// never converged with the remote, and each save reported
|
|
246
|
-
// `committed: true, pushed: false` from the push below, forever.
|
|
247
214
|
if (!(err instanceof GitRemoteRefMissingError))
|
|
248
215
|
throw err;
|
|
249
216
|
console.info('CanopyCMS: settings branch has no remote ref yet, nothing to pull ' +
|
|
250
217
|
'(normal for the first settings commit)');
|
|
251
218
|
}
|
|
252
|
-
// Commit
|
|
253
219
|
await git.ensureAuthor({
|
|
254
220
|
name: config.gitBotAuthorName,
|
|
255
221
|
email: config.gitBotAuthorEmail,
|
|
@@ -269,16 +235,11 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
269
235
|
}
|
|
270
236
|
// Create or update PR — dual-path like content branches (api/github-sync.ts)
|
|
271
237
|
if (options.createPR !== false) {
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
// `base` for review/audit history. Previously worded as "will be
|
|
278
|
-
// persisted when this PR is merged", which implied merging was what
|
|
279
|
-
// made the change durable/live — false for both settings files, and
|
|
280
|
-
// for groups specifically the read side didn't even look at this
|
|
281
|
-
// branch until that bug was fixed (see resolve-canopy-user.ts).
|
|
238
|
+
// Permissions and groups are read live from the settings workspace
|
|
239
|
+
// (getSettingsBranchRoot), never from this PR's base branch, so the
|
|
240
|
+
// change took effect when it was committed and pushed above, before
|
|
241
|
+
// this PR existed. Merging re-activates nothing; it only records the
|
|
242
|
+
// change on `base` for review and audit history.
|
|
282
243
|
const settingsPRBody = 'Automated PR for permission and group changes. These changes already took ' +
|
|
283
244
|
'effect in the CMS when they were saved — merging this PR does not change ' +
|
|
284
245
|
"what's live; it only records the change here for review and audit history.";
|
|
@@ -286,10 +247,9 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
286
247
|
if (githubService) {
|
|
287
248
|
let prUrl;
|
|
288
249
|
try {
|
|
289
|
-
// Settings-branch PRs
|
|
290
|
-
//
|
|
291
|
-
//
|
|
292
|
-
// draft until an admin is ready — don't "helpfully" enable this.
|
|
250
|
+
// Settings-branch PRs never pass markReadyIfDraft: a settings sync
|
|
251
|
+
// has no explicit "submit for review" step the way a content submit
|
|
252
|
+
// does, so an existing draft PR stays draft until an admin says so.
|
|
293
253
|
const result = await githubService.createOrUpdatePR({
|
|
294
254
|
head: settingsBranch,
|
|
295
255
|
base: config.defaultBaseBranch ?? 'main',
|
|
@@ -335,8 +295,6 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
335
295
|
};
|
|
336
296
|
const operatingMode = config.mode;
|
|
337
297
|
const modeStrategy = operatingStrategy(operatingMode);
|
|
338
|
-
// Create branch registry only in branching modes
|
|
339
|
-
// Settings are now in separate directory, no filtering needed
|
|
340
298
|
const registry = modeStrategy.supportsBranching()
|
|
341
299
|
? new BranchRegistry(getDefaultBranchBase(operatingMode))
|
|
342
300
|
: undefined;
|
|
@@ -361,16 +319,15 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
361
319
|
// Static deployments and builds serve from the checkout — no git HEAD to track
|
|
362
320
|
if (readsFromCheckout(services.config))
|
|
363
321
|
return;
|
|
364
|
-
//
|
|
365
|
-
//
|
|
322
|
+
// An explicitly configured value is never overridden by HEAD detection;
|
|
323
|
+
// only what the adopter left unset is re-detected.
|
|
366
324
|
if (explicitActiveBranch && explicitBaseBranch)
|
|
367
325
|
return;
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
// provisioned mid-session fork from the developer's current branch.
|
|
326
|
+
// The switch is silent, so the dev site tracks the current branch the way
|
|
327
|
+
// code hot-reloads; the editor is pinned to its own branch via URL params,
|
|
328
|
+
// so only non-editor content serving is affected. The base branch follows
|
|
329
|
+
// HEAD too when unset, so a workspace provisioned mid-session forks from
|
|
330
|
+
// the developer's current branch.
|
|
374
331
|
const fresh = await detectActiveBranch({
|
|
375
332
|
...services.config,
|
|
376
333
|
defaultActiveBranch: undefined,
|
|
@@ -386,11 +343,11 @@ async function _createCanopyServicesInternal(config, options) {
|
|
|
386
343
|
changed = true;
|
|
387
344
|
}
|
|
388
345
|
if (changed) {
|
|
389
|
-
//
|
|
390
|
-
// createGitManagerFor,
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
346
|
+
// The closures above (getSettingsBranchRoot, checkContentAccess,
|
|
347
|
+
// createGitManagerFor, …) captured the original `config` local. Only
|
|
348
|
+
// branch identity changes here: git operations on existing branches use
|
|
349
|
+
// the fork point in branch metadata, and anything needing the fresh
|
|
350
|
+
// values must read services.config.
|
|
394
351
|
services.config = next;
|
|
395
352
|
}
|
|
396
353
|
},
|
|
@@ -2,24 +2,17 @@ import type { CanopyConfig } from './config/index.js';
|
|
|
2
2
|
import type { OperatingMode } from './operating-mode/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Directory the cross-process init lock is anchored on — a dedicated sibling of
|
|
5
|
-
* the settings root,
|
|
5
|
+
* the settings root, NOT the settings root itself.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* in-process registry key is the on-disk lock identity and two live locks
|
|
17
|
-
* can no longer clobber each other's bookkeeping (see docs/concurrency.md,
|
|
18
|
-
* "Anchor path matters"). A dedicated dot-directory is still the right home
|
|
19
|
-
* for this marker: `path.dirname(settingsRoot)` is where
|
|
20
|
-
* `ensureLocalSimulatedRemote` puts `.remote-init.lock`, and settings init
|
|
21
|
-
* calls into it while holding this lock, so keeping the two markers in
|
|
22
|
-
* separate directories keeps that nesting obvious rather than incidental.
|
|
7
|
+
* It must not pre-create the settings root: `acquireProvisioningLock` mkdir's
|
|
8
|
+
* its target, and `GitManager.initializeWorkspace` clones INTO the settings
|
|
9
|
+
* root, which `git clone` refuses if anything is already there. A dedicated
|
|
10
|
+
* dot-directory also keeps this marker out of `path.dirname(settingsRoot)`,
|
|
11
|
+
* where `ensureLocalSimulatedRemote` puts `.remote-init.lock` — settings init
|
|
12
|
+
* calls into that while holding this lock, so separate directories make the
|
|
13
|
+
* nesting obvious rather than incidental. For how a lock's anchor path is
|
|
14
|
+
* chosen, see utils/provisioning-lock.ts and docs/concurrency.md.
|
|
15
|
+
* @internal Exported for tests.
|
|
23
16
|
*/
|
|
24
17
|
export declare function settingsInitLockTarget(settingsRoot: string): string;
|
|
25
18
|
export interface EnsureSettingsWorkspaceOptions {
|
|
@@ -29,20 +22,14 @@ export interface EnsureSettingsWorkspaceOptions {
|
|
|
29
22
|
remoteUrl?: string;
|
|
30
23
|
}
|
|
31
24
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* Unlike BranchWorkspaceManager, this does not:
|
|
38
|
-
* - Create or manage metadata files
|
|
39
|
-
* - Interact with the branch registry
|
|
40
|
-
* - Check for special cases (settings are always settings)
|
|
25
|
+
* The settings filesystem workspace and its git operations. Settings live on an
|
|
26
|
+
* orphan git branch, sharing no history with content, and unlike
|
|
27
|
+
* BranchWorkspaceManager this writes no metadata files and never touches the
|
|
28
|
+
* branch registry.
|
|
41
29
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* synchronization of the init itself (multiple Lambda containers on EFS)
|
|
30
|
+
* Two locking layers, mirroring BranchWorkspaceManager: an in-memory promise
|
|
31
|
+
* lock within the process, and `acquireProvisioningLock` (proper-lockfile)
|
|
32
|
+
* across processes and hosts.
|
|
46
33
|
*/
|
|
47
34
|
export declare class SettingsWorkspaceManager {
|
|
48
35
|
private readonly config;
|
|
@@ -6,41 +6,34 @@ import { getErrorMessage } from './utils/error.js';
|
|
|
6
6
|
import { acquireProvisioningLock } from './utils/provisioning-lock.js';
|
|
7
7
|
import { RESERVED_SETTINGS_BRANCH_PREFIX } from './paths/index.js';
|
|
8
8
|
const log = createDebugLogger({ prefix: 'SettingsWorkspace' });
|
|
9
|
-
// In-memory lock
|
|
10
|
-
//
|
|
9
|
+
// In-memory lock against concurrent init within one process. One lock suffices,
|
|
10
|
+
// unlike content branches, which need one per branch.
|
|
11
11
|
let settingsInitLock = null;
|
|
12
12
|
const SETTINGS_INIT_LOCK_DIR = '.settings-init';
|
|
13
13
|
const SETTINGS_INIT_LOCK_NAME = 'lock';
|
|
14
14
|
/**
|
|
15
15
|
* Directory the cross-process init lock is anchored on — a dedicated sibling of
|
|
16
|
-
* the settings root,
|
|
16
|
+
* the settings root, NOT the settings root itself.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* in-process registry key is the on-disk lock identity and two live locks
|
|
28
|
-
* can no longer clobber each other's bookkeeping (see docs/concurrency.md,
|
|
29
|
-
* "Anchor path matters"). A dedicated dot-directory is still the right home
|
|
30
|
-
* for this marker: `path.dirname(settingsRoot)` is where
|
|
31
|
-
* `ensureLocalSimulatedRemote` puts `.remote-init.lock`, and settings init
|
|
32
|
-
* calls into it while holding this lock, so keeping the two markers in
|
|
33
|
-
* separate directories keeps that nesting obvious rather than incidental.
|
|
18
|
+
* It must not pre-create the settings root: `acquireProvisioningLock` mkdir's
|
|
19
|
+
* its target, and `GitManager.initializeWorkspace` clones INTO the settings
|
|
20
|
+
* root, which `git clone` refuses if anything is already there. A dedicated
|
|
21
|
+
* dot-directory also keeps this marker out of `path.dirname(settingsRoot)`,
|
|
22
|
+
* where `ensureLocalSimulatedRemote` puts `.remote-init.lock` — settings init
|
|
23
|
+
* calls into that while holding this lock, so separate directories make the
|
|
24
|
+
* nesting obvious rather than incidental. For how a lock's anchor path is
|
|
25
|
+
* chosen, see utils/provisioning-lock.ts and docs/concurrency.md.
|
|
26
|
+
* @internal Exported for tests.
|
|
34
27
|
*/
|
|
35
28
|
export function settingsInitLockTarget(settingsRoot) {
|
|
36
29
|
return path.join(path.dirname(path.resolve(settingsRoot)), SETTINGS_INIT_LOCK_DIR);
|
|
37
30
|
}
|
|
38
31
|
/**
|
|
39
|
-
* Whether a settings workspace
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
32
|
+
* Whether a settings workspace holds settings data yet. The rename guard uses
|
|
33
|
+
* it to tell an already-populated workspace (refuse) from a clone interrupted
|
|
34
|
+
* before its orphan branch existed (harmless — let init finish). File names
|
|
35
|
+
* come from the operating-mode strategy so this stays in step with whatever
|
|
36
|
+
* the mode calls them.
|
|
44
37
|
*/
|
|
45
38
|
async function settingsFilesPresent(settingsRoot) {
|
|
46
39
|
const names = ['permissions.json', 'groups.json'];
|
|
@@ -56,34 +49,28 @@ async function settingsFilesPresent(settingsRoot) {
|
|
|
56
49
|
return false;
|
|
57
50
|
}
|
|
58
51
|
/**
|
|
59
|
-
* Rename guard (settings-branch protection)
|
|
52
|
+
* Rename guard (settings-branch protection): refuse to boot when an
|
|
60
53
|
* ALREADY-POPULATED settings workspace would be re-orphaned under a different
|
|
61
54
|
* branch name.
|
|
62
55
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* commit — orphan branches share no history, so this isn't a "migration", it
|
|
56
|
+
* Without it, GitManager.initializeWorkspace sees an existing .git, skips the
|
|
57
|
+
* clone, and calls createOrphanSettingsBranch(branchName); for an unknown name
|
|
58
|
+
* git then runs `checkout --orphan <name>` + `rm -rf .` + an empty commit.
|
|
59
|
+
* Orphan branches share no history, so that is not a migration — it
|
|
68
60
|
* PERMANENTLY WIPES permissions.json/groups.json with nothing to recover from.
|
|
69
|
-
*
|
|
70
|
-
* CANOPYCMS_DEPLOYMENT_NAME
|
|
61
|
+
* The trigger is almost always deploymentName / settingsBranch /
|
|
62
|
+
* CANOPYCMS_DEPLOYMENT_NAME changing on a live deployment (see
|
|
71
63
|
* resolveDeploymentName in operating-mode/deployment-name.ts).
|
|
72
64
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* branch
|
|
76
|
-
*
|
|
77
|
-
* clone,
|
|
78
|
-
* on the base branch with
|
|
79
|
-
*
|
|
80
|
-
* protect data that was never written. So the refusal additionally requires
|
|
81
|
-
* evidence that this really is a populated settings workspace: either it is
|
|
82
|
-
* checked out on some OTHER settings branch, or the settings files are actually
|
|
83
|
-
* on disk.
|
|
65
|
+
* The refusal therefore needs evidence of a populated workspace — checked out
|
|
66
|
+
* on some OTHER settings branch, or settings files on disk — not just a
|
|
67
|
+
* differing branch name. initializeWorkspace clones at the BASE branch and
|
|
68
|
+
* creates the orphan branch several steps later, so an interrupted first init
|
|
69
|
+
* (Lambda timeout on a slow EFS clone, OOM, spot interruption) leaves a valid
|
|
70
|
+
* repo on the base branch with no settings files, and refusing on name alone
|
|
71
|
+
* would brick that deployment on every boot to protect data never written.
|
|
84
72
|
*
|
|
85
|
-
*
|
|
86
|
-
* sites in ensureGitWorkspace and the comment there.
|
|
73
|
+
* NEVER gated on holding the init lock — see its two call sites below.
|
|
87
74
|
*/
|
|
88
75
|
async function assertSettingsWorkspaceIdentity(options) {
|
|
89
76
|
const repoExists = await GitManager.repoExistsAt(options.settingsRoot);
|
|
@@ -121,20 +108,14 @@ async function assertSettingsWorkspaceIdentity(options) {
|
|
|
121
108
|
}
|
|
122
109
|
}
|
|
123
110
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* Unlike BranchWorkspaceManager, this does not:
|
|
130
|
-
* - Create or manage metadata files
|
|
131
|
-
* - Interact with the branch registry
|
|
132
|
-
* - Check for special cases (settings are always settings)
|
|
111
|
+
* The settings filesystem workspace and its git operations. Settings live on an
|
|
112
|
+
* orphan git branch, sharing no history with content, and unlike
|
|
113
|
+
* BranchWorkspaceManager this writes no metadata files and never touches the
|
|
114
|
+
* branch registry.
|
|
133
115
|
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* synchronization of the init itself (multiple Lambda containers on EFS)
|
|
116
|
+
* Two locking layers, mirroring BranchWorkspaceManager: an in-memory promise
|
|
117
|
+
* lock within the process, and `acquireProvisioningLock` (proper-lockfile)
|
|
118
|
+
* across processes and hosts.
|
|
138
119
|
*/
|
|
139
120
|
export class SettingsWorkspaceManager {
|
|
140
121
|
constructor(config) {
|
|
@@ -147,7 +128,6 @@ export class SettingsWorkspaceManager {
|
|
|
147
128
|
await settingsInitLock;
|
|
148
129
|
return;
|
|
149
130
|
}
|
|
150
|
-
// Create new in-memory lock promise
|
|
151
131
|
settingsInitLock = (async () => {
|
|
152
132
|
try {
|
|
153
133
|
log.debug('workspace', 'Ensuring settings git workspace', {
|
|
@@ -156,34 +136,26 @@ export class SettingsWorkspaceManager {
|
|
|
156
136
|
});
|
|
157
137
|
// Rename guard, run LOCK-FREE and unconditionally, before any waiting.
|
|
158
138
|
// A deployment whose settings-branch name no longer matches the
|
|
159
|
-
// workspace on disk is misconfigured, not contended
|
|
160
|
-
// immediately rather than queue behind a live provisioner for
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
// having run in THIS process.
|
|
139
|
+
// workspace on disk is misconfigured, not contended, so it must refuse
|
|
140
|
+
// immediately rather than queue behind a live provisioner for minutes.
|
|
141
|
+
// Running it here also means no path reaches initializeWorkspace
|
|
142
|
+
// without the guard having run in THIS process.
|
|
164
143
|
await assertSettingsWorkspaceIdentity(options);
|
|
165
144
|
// Layer 2: cross-process/cross-host lock around the init itself
|
|
166
145
|
// (proper-lockfile: heartbeat-refreshed while the holder lives, so a
|
|
167
146
|
// slow EFS clone is not mistaken for a crash, and patient retries so a
|
|
168
147
|
// loser WAITS instead of racing into a concurrent clone).
|
|
169
|
-
//
|
|
170
|
-
// This replaced a bespoke O_CREAT|O_EXCL + 30s-mtime scheme whose
|
|
171
|
-
// return value was only ever used to decide whether to release: the
|
|
172
|
-
// loser proceeded into initializeWorkspace anyway, concurrently with
|
|
173
|
-
// the holder, where it could see a half-written .git, classify it
|
|
174
|
-
// corrupt, and `rm -rf` it out from under the in-flight clone.
|
|
175
148
|
const releaseLock = await acquireProvisioningLock(settingsInitLockTarget(options.settingsRoot), SETTINGS_INIT_LOCK_NAME);
|
|
176
149
|
try {
|
|
177
|
-
// Re-run the guard on the now-stable state
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
// or has already thrown.
|
|
150
|
+
// Re-run the guard on the now-stable state: a previous holder may
|
|
151
|
+
// have created the workspace, or moved it onto its own settings
|
|
152
|
+
// branch, after we sampled it above, and acting on that stale
|
|
153
|
+
// sample is the destructive path the guard exists to stop. Not
|
|
154
|
+
// gated on any "did I win the race" flag — by design there is none;
|
|
155
|
+
// every process here either holds the lock or has already thrown.
|
|
184
156
|
await assertSettingsWorkspaceIdentity(options);
|
|
185
|
-
//
|
|
186
|
-
//
|
|
157
|
+
// initializeWorkspace is idempotent (it checks for .git), so this
|
|
158
|
+
// is safe even if another process just finished init.
|
|
187
159
|
await GitManager.initializeWorkspace({
|
|
188
160
|
workspacePath: options.settingsRoot,
|
|
189
161
|
branchName: options.branchName,
|
|
@@ -209,11 +181,9 @@ export class SettingsWorkspaceManager {
|
|
|
209
181
|
}
|
|
210
182
|
}
|
|
211
183
|
finally {
|
|
212
|
-
// Always clean up the in-memory lock when done (success or failure)
|
|
213
184
|
settingsInitLock = null;
|
|
214
185
|
}
|
|
215
186
|
})();
|
|
216
|
-
// Wait for initialization to complete
|
|
217
187
|
await settingsInitLock;
|
|
218
188
|
});
|
|
219
189
|
}
|