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/context.js
CHANGED
|
@@ -13,58 +13,46 @@ function isLookupFailure(err) {
|
|
|
13
13
|
return err.code === 'NOT_FOUND' || err.code === 'NO_SCHEMA_ITEM';
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* True when a ContentStoreError should render as "not found" to page-level
|
|
17
|
-
* readByUrlPath rather than escape as a thrown error. FORBIDDEN is
|
|
18
|
-
* denied
|
|
19
|
-
* (404) instead of an unhandled 500 from the
|
|
20
|
-
*
|
|
16
|
+
* True when a ContentStoreError should render as "not found" to page-level
|
|
17
|
+
* callers of readByUrlPath rather than escape as a thrown error. FORBIDDEN is
|
|
18
|
+
* included so a denied or anonymous read reaches the adopter's ordinary
|
|
19
|
+
* `if (!result) return notFound()` (404) instead of an unhandled 500 from the
|
|
20
|
+
* server component. The strict `read()` API still throws.
|
|
21
21
|
*/
|
|
22
22
|
function isPageSwallowable(err) {
|
|
23
23
|
return isLookupFailure(err) || err.code === 'FORBIDDEN';
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
* Create a Canopy context
|
|
27
|
-
*
|
|
26
|
+
* Create a Canopy context managing auth + content reading. Framework-agnostic:
|
|
27
|
+
* the adapter supplies extractUser.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* NOTE: This function is synchronous because in practice, services are always
|
|
32
|
-
* provided pre-created (async) by the framework adapter. The fallback path
|
|
33
|
-
* that creates services from config cannot work correctly since createCanopyServices
|
|
34
|
-
* is now async. Always pass services, not config.
|
|
29
|
+
* Synchronous, and takes pre-created `services` rather than config — building
|
|
30
|
+
* services is async, so there is no working fallback path from config here.
|
|
35
31
|
*/
|
|
36
32
|
export function createCanopyContext(options) {
|
|
37
33
|
const services = options.services;
|
|
38
|
-
/**
|
|
39
|
-
* Get the current user.
|
|
40
|
-
* Returns STATIC_DEPLOY_USER for static deployments or during build, otherwise delegates to adapter.
|
|
41
|
-
*/
|
|
34
|
+
/** STATIC_DEPLOY_USER on a static deployment or during build, else the adapter's user. */
|
|
42
35
|
const getUser = async () => {
|
|
43
|
-
//
|
|
36
|
+
// No request context in either phase, so use the synthetic admin.
|
|
44
37
|
if (isDeployedStatic(services.config) || isBuildMode()) {
|
|
45
38
|
return STATIC_DEPLOY_USER;
|
|
46
39
|
}
|
|
47
|
-
// Runtime: delegate to adapter-provided user extractor
|
|
48
|
-
// (adapter should use authResultToCanopyUser to apply bootstrap admins)
|
|
49
40
|
return await options.extractUser();
|
|
50
41
|
};
|
|
51
|
-
/**
|
|
52
|
-
* Get the context for the current request.
|
|
53
|
-
* Call this in server components/routes to get auth-aware reader.
|
|
54
|
-
*/
|
|
42
|
+
/** The auth-aware context for this request; call it in server components and routes. */
|
|
55
43
|
const getContext = async () => {
|
|
56
44
|
// Dev mode follows the developer's git HEAD (no-op in prod/static or when
|
|
57
45
|
// defaultActiveBranch is explicit). Same contract as the HTTP API handler —
|
|
58
46
|
// switching branches mid-session updates what getCanopy() serves.
|
|
59
47
|
await services.refreshActiveBranch();
|
|
60
48
|
const user = await getUser();
|
|
61
|
-
// Create base content reader
|
|
62
49
|
const baseReader = createContentReader({ services });
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
// just readByUrlPath's URL-addressability gate.
|
|
66
|
-
// an optional second parameter on the
|
|
67
|
-
//
|
|
50
|
+
// Injects the user and validates strings → branded types at this boundary.
|
|
51
|
+
// `extra` carries options that are NOT part of the public `read` surface —
|
|
52
|
+
// today just readByUrlPath's URL-addressability gate. A separate inner
|
|
53
|
+
// function rather than an optional second parameter on the
|
|
54
|
+
// `CanopyContext['read']`-typed closure, so the two call sites stay visible
|
|
55
|
+
// and nobody widens the public API by accident.
|
|
68
56
|
const readWithOptions = async (input, extra) => {
|
|
69
57
|
const entryPath = createLogicalPath(input.entryPath);
|
|
70
58
|
let slug;
|
|
@@ -93,9 +81,9 @@ export function createCanopyContext(options) {
|
|
|
93
81
|
return null;
|
|
94
82
|
const { branch, resolveReferences } = options ?? {};
|
|
95
83
|
for (const candidate of candidates) {
|
|
96
|
-
// Skip
|
|
97
|
-
//
|
|
98
|
-
//
|
|
84
|
+
// Skip a candidate whose slug isn't valid (/favicon.ico, Next internals
|
|
85
|
+
// the [...slug] route catches). None can match an entry, so treat it as
|
|
86
|
+
// a miss rather than let read() throw "Invalid slug".
|
|
99
87
|
if (!parseSlug(candidate.slug).ok)
|
|
100
88
|
continue;
|
|
101
89
|
try {
|
|
@@ -105,17 +93,17 @@ export function createCanopyContext(options) {
|
|
|
105
93
|
branch,
|
|
106
94
|
resolveReferences,
|
|
107
95
|
},
|
|
108
|
-
//
|
|
109
|
-
// See ReadContentInput.urlAddressableOnly for the two rules and
|
|
110
|
-
// reader (which holds the branch-correct
|
|
111
|
-
//
|
|
96
|
+
// A read BY PUBLISHED URL accepts only what listEntries publishes.
|
|
97
|
+
// See ReadContentInput.urlAddressableOnly for the two rules, and
|
|
98
|
+
// why they live in the reader (which holds the branch-correct
|
|
99
|
+
// schema) rather than the candidate builder (pure and schema-free).
|
|
112
100
|
{ urlAddressableOnly: true });
|
|
113
101
|
}
|
|
114
102
|
catch (err) {
|
|
115
|
-
// Swallow "not found"
|
|
116
|
-
// or anonymous read renders as
|
|
117
|
-
//
|
|
118
|
-
//
|
|
103
|
+
// Swallow a candidate path's "not found", and FORBIDDEN so a denied
|
|
104
|
+
// or anonymous read renders as the adopter's 404 rather than an
|
|
105
|
+
// unhandled 500. Real errors (validation, corruption, anything not a
|
|
106
|
+
// ContentStoreError) rethrow.
|
|
119
107
|
if (err instanceof ContentStoreError && isPageSwallowable(err)) {
|
|
120
108
|
if (err.code === 'FORBIDDEN') {
|
|
121
109
|
log.debug('readByUrlPath', 'Read denied, treating as not-found: ' + err.message, {
|
|
@@ -155,41 +143,34 @@ export function createCanopyContext(options) {
|
|
|
155
143
|
return schemaContextPromise;
|
|
156
144
|
};
|
|
157
145
|
/**
|
|
158
|
-
* Path-ACL predicate for the batch reads (listEntries / buildContentTree)
|
|
159
|
-
* per getContext call
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* permissions: `read`/`readByUrlPath` go through the content reader, which checks per
|
|
163
|
-
* entry, while the listing primitives took no user at all. Since `CanopyContext` is the
|
|
164
|
-
* request-scoped, ACL-enforcing context that page code is told to use, an unfiltered
|
|
165
|
-
* listing there disclosed full entry `data` for paths the user cannot `read()` directly.
|
|
146
|
+
* Path-ACL predicate for the batch reads (listEntries / buildContentTree),
|
|
147
|
+
* memoized per getContext call like the schema context above. Without it an
|
|
148
|
+
* unfiltered listing on this request-scoped, ACL-enforcing context would
|
|
149
|
+
* disclose full entry `data` for paths the user cannot `read()` directly.
|
|
166
150
|
*
|
|
167
|
-
* `services.createContentAccessChecker` is the
|
|
168
|
-
* uses
|
|
169
|
-
* settings/permissions root,
|
|
170
|
-
* per-path check
|
|
171
|
-
* per configured rule, with no
|
|
151
|
+
* `services.createContentAccessChecker` is the shared batch primitive
|
|
152
|
+
* (api/entries.ts uses it too): it resolves the request-constant work —
|
|
153
|
+
* branch access, the settings/permissions root, the rule set — once, and
|
|
154
|
+
* returns a synchronous per-path check, so the per-entry cost is an admin
|
|
155
|
+
* short-circuit or one minimatch per configured rule, with no extra I/O.
|
|
172
156
|
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* this one every such script paid for a settings-workspace clone it never needed and,
|
|
184
|
-
* in an environment where that workspace cannot be provisioned, would hard-fail on.
|
|
157
|
+
* The empty object (no predicate → unfiltered) at build time, on static
|
|
158
|
+
* deployments, and for the synthetic admin is load-bearing, not an
|
|
159
|
+
* optimization. That user has unconditional access anyway (path checks
|
|
160
|
+
* bypass entirely for an Admins-group user — authorization/path.ts), but
|
|
161
|
+
* BUILDING the checker costs a getSettingsBranchRoot() call, which in modes
|
|
162
|
+
* with a separate settings branch provisions that branch's git workspace: an
|
|
163
|
+
* EFS round trip in prod. `createBuildCanopy` (build-canopy.ts) runs outside
|
|
164
|
+
* a request or Next.js build phase, so neither other guard fires for it, and
|
|
165
|
+
* without this one every such script pays for a settings-workspace clone it
|
|
166
|
+
* never needed — and hard-fails where that workspace cannot be provisioned.
|
|
185
167
|
*
|
|
186
|
-
* Compared by reference to the
|
|
187
|
-
* membership
|
|
188
|
-
*
|
|
189
|
-
* the real check, same as any other user.
|
|
168
|
+
* Compared by reference to the STATIC_DEPLOY_USER singleton, not by group
|
|
169
|
+
* membership, so it stays scoped to the synthetic build identity: a real
|
|
170
|
+
* authenticated admin at request time still goes through the real check.
|
|
190
171
|
*
|
|
191
|
-
* Deliberately NOT wrapped in
|
|
192
|
-
* contract, and swallowing
|
|
172
|
+
* Deliberately NOT wrapped in try/catch — createContentAccessChecker is
|
|
173
|
+
* fail-loud by contract, and swallowing would serve an unfiltered listing.
|
|
193
174
|
*/
|
|
194
175
|
let visibilityPromise = null;
|
|
195
176
|
const resolveVisibilityImpl = async () => {
|
|
@@ -3,26 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* In dev, the editor and dev server read content from a branch clone under
|
|
5
5
|
* `.canopy-dev/content-branches/<branch>/`, while the static build reads the working tree directly.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* A developer editing working-tree `content/**` outside the editor therefore gets stale content
|
|
7
|
+
* from the dev server until `canopycms sync push` runs. This watcher reports that divergence
|
|
8
|
+
* ('warn', the default: a warning naming the diverged files, at startup and on content changes) or
|
|
9
|
+
* does nothing ('off').
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
+
* There is intentionally NO auto-push mode: overwriting the branch clone from the working tree
|
|
12
|
+
* would clobber uncommitted editor "Save" state with no Canopy-level recovery. Reconciliation goes
|
|
13
|
+
* through the interactive, conflict-aware `canopycms sync push` instead.
|
|
11
14
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
+
* Divergence is a CONDITION, not an event: it holds until someone runs `sync push`, so the report
|
|
16
|
+
* must not repeat into a scrolling request log (`reportOnce`) yet must be hard to scroll past (the
|
|
17
|
+
* gutter block). A log cannot hold a condition; see
|
|
18
|
+
* `.claude/future-tasks/dev-divergence-in-app-surface.md` for the in-app surface that would
|
|
19
|
+
* replace the terminal as its primary home.
|
|
15
20
|
*
|
|
16
|
-
* All logic lives here
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* ## Divergence is a CONDITION, not an event
|
|
20
|
-
*
|
|
21
|
-
* It stays true until someone runs `sync push`, so the two things this module has to get right are
|
|
22
|
-
* (a) not re-printing the same condition into a scrolling request log, and (b) making the one printing
|
|
23
|
-
* of it hard to scroll past. Both are handled below by `reportOnce` + the gutter formatting; see
|
|
24
|
-
* `.claude/future-tasks/dev-divergence-in-app-surface.md` for the in-app surface that would replace
|
|
25
|
-
* the terminal as the primary home for this.
|
|
21
|
+
* All logic lives here; framework adapters call startDevContentWatcher() once at dev startup (see
|
|
22
|
+
* packages/canopycms-next/src/context-wrapper.ts for the Next wiring).
|
|
26
23
|
*/
|
|
27
24
|
import type { CanopyServices } from './services.js';
|
|
28
25
|
import type { DevContentSyncMode } from './config/types.js';
|
|
@@ -36,7 +33,10 @@ export interface StartDevContentWatcherOptions {
|
|
|
36
33
|
/** Warning sink. Defaults to console.warn. */
|
|
37
34
|
warn?: (message: string) => void;
|
|
38
35
|
}
|
|
39
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Test-only: dispose every armed watcher and drop all cross-module dedupe state.
|
|
38
|
+
* @internal Exported for tests.
|
|
39
|
+
*/
|
|
40
40
|
export declare function resetDevContentWatchersForTests(): void;
|
|
41
41
|
/**
|
|
42
42
|
* Start watching the working-tree content directory for divergence from the served dev branch clone.
|
|
@@ -3,26 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* In dev, the editor and dev server read content from a branch clone under
|
|
5
5
|
* `.canopy-dev/content-branches/<branch>/`, while the static build reads the working tree directly.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* A developer editing working-tree `content/**` outside the editor therefore gets stale content
|
|
7
|
+
* from the dev server until `canopycms sync push` runs. This watcher reports that divergence
|
|
8
|
+
* ('warn', the default: a warning naming the diverged files, at startup and on content changes) or
|
|
9
|
+
* does nothing ('off').
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
+
* There is intentionally NO auto-push mode: overwriting the branch clone from the working tree
|
|
12
|
+
* would clobber uncommitted editor "Save" state with no Canopy-level recovery. Reconciliation goes
|
|
13
|
+
* through the interactive, conflict-aware `canopycms sync push` instead.
|
|
11
14
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
+
* Divergence is a CONDITION, not an event: it holds until someone runs `sync push`, so the report
|
|
16
|
+
* must not repeat into a scrolling request log (`reportOnce`) yet must be hard to scroll past (the
|
|
17
|
+
* gutter block). A log cannot hold a condition; see
|
|
18
|
+
* `.claude/future-tasks/dev-divergence-in-app-surface.md` for the in-app surface that would
|
|
19
|
+
* replace the terminal as its primary home.
|
|
15
20
|
*
|
|
16
|
-
* All logic lives here
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* ## Divergence is a CONDITION, not an event
|
|
20
|
-
*
|
|
21
|
-
* It stays true until someone runs `sync push`, so the two things this module has to get right are
|
|
22
|
-
* (a) not re-printing the same condition into a scrolling request log, and (b) making the one printing
|
|
23
|
-
* of it hard to scroll past. Both are handled below by `reportOnce` + the gutter formatting; see
|
|
24
|
-
* `.claude/future-tasks/dev-divergence-in-app-surface.md` for the in-app surface that would replace
|
|
25
|
-
* the terminal as the primary home for this.
|
|
21
|
+
* All logic lives here; framework adapters call startDevContentWatcher() once at dev startup (see
|
|
22
|
+
* packages/canopycms-next/src/context-wrapper.ts for the Next wiring).
|
|
26
23
|
*/
|
|
27
24
|
import fsSync from 'node:fs';
|
|
28
25
|
import path from 'node:path';
|
|
@@ -33,18 +30,16 @@ import { getErrorMessage } from './utils/error.js';
|
|
|
33
30
|
import { diffContentTrees, isContentTreeDiffEmpty } from './sync-core.js';
|
|
34
31
|
/**
|
|
35
32
|
* Per-category cap on listed files. Deliberately small: the block is a prompt to run `sync push`,
|
|
36
|
-
* not a diff viewer, and a
|
|
37
|
-
*
|
|
33
|
+
* not a diff viewer, and a wall of paths is as easy to scroll past as a one-line warning. Each
|
|
34
|
+
* list carries its true count, so nothing is hidden -- only elided.
|
|
38
35
|
*/
|
|
39
36
|
const MAX_LISTED_FILES = 5;
|
|
40
37
|
/**
|
|
41
|
-
* The registry lives on `globalThis`, NOT in module scope
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* "CanopyCMS dev-auth: Auto-configured ..." line in a dev log is the same effect on a different
|
|
47
|
-
* module-level latch.) A `globalThis` property is the one thing shared across those evaluations.
|
|
38
|
+
* The registry lives on `globalThis`, NOT in module scope: Next's dev server compiles the server
|
|
39
|
+
* graph per route bundle and evaluates each copy in its own module scope, so a module-level `Map`
|
|
40
|
+
* is re-created empty per bundle -- every copy then believes it is the first watcher, arms its own,
|
|
41
|
+
* and re-prints the startup warning. A `globalThis` property is the one thing shared across those
|
|
42
|
+
* evaluations.
|
|
48
43
|
*/
|
|
49
44
|
const REGISTRY_KEY = '__canopycmsDevContentWatchers__';
|
|
50
45
|
function watcherRegistry() {
|
|
@@ -56,7 +51,10 @@ function watcherRegistry() {
|
|
|
56
51
|
host[REGISTRY_KEY] = created;
|
|
57
52
|
return created;
|
|
58
53
|
}
|
|
59
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Test-only: dispose every armed watcher and drop all cross-module dedupe state.
|
|
56
|
+
* @internal Exported for tests.
|
|
57
|
+
*/
|
|
60
58
|
export function resetDevContentWatchersForTests() {
|
|
61
59
|
const registry = watcherRegistry();
|
|
62
60
|
for (const state of registry.values())
|
|
@@ -64,9 +62,9 @@ export function resetDevContentWatchersForTests() {
|
|
|
64
62
|
registry.clear();
|
|
65
63
|
}
|
|
66
64
|
/**
|
|
67
|
-
* One body line of the notice block.
|
|
68
|
-
*
|
|
69
|
-
*
|
|
65
|
+
* One body line of the notice block. A left gutter rather than a full box: content paths are long
|
|
66
|
+
* and unpredictable, and a right border would force either wrapping math or truncation of the very
|
|
67
|
+
* filenames the block exists to name.
|
|
70
68
|
*/
|
|
71
69
|
function body(line) {
|
|
72
70
|
const gutter = pc.yellow('|');
|
|
@@ -84,8 +82,8 @@ function formatList(label, files) {
|
|
|
84
82
|
}
|
|
85
83
|
function formatDivergenceWarning(branch, diff) {
|
|
86
84
|
const total = diff.changed.length + diff.added.length + diff.removed.length;
|
|
87
|
-
// Blank lines top and bottom:
|
|
88
|
-
//
|
|
85
|
+
// Blank lines top and bottom: whitespace separation does more to make this block findable in a
|
|
86
|
+
// scrolling request log than any amount of in-block decoration.
|
|
89
87
|
return [
|
|
90
88
|
'',
|
|
91
89
|
`${pc.bgYellow(pc.black(' CanopyCMS '))} ${pc.bold(pc.yellow('working-tree content has diverged'))}`,
|
|
@@ -121,17 +119,16 @@ export function startDevContentWatcher(services, options = {}) {
|
|
|
121
119
|
const sourceRoot = options.sourceRoot ?? services.config.sourceRoot ?? process.cwd();
|
|
122
120
|
const contentRoot = services.config.contentRoot || 'content';
|
|
123
121
|
const strategy = operatingStrategy('dev');
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
// silently no-op for a non-default contentRoot before this fix).
|
|
122
|
+
// Pass the resolved contentRoot local above: getContentRoot has no default of its own, by
|
|
123
|
+
// design -- a default here silently no-ops this watcher for a non-default contentRoot.
|
|
127
124
|
const workingTreeContentDir = strategy.getContentRoot(contentRoot, sourceRoot);
|
|
128
125
|
const warn = options.warn ?? ((message) => console.warn(message));
|
|
129
126
|
// Nothing to watch if the working tree has no content directory (e.g. unit-test configs).
|
|
130
127
|
if (!fsSync.existsSync(workingTreeContentDir))
|
|
131
128
|
return noop;
|
|
132
|
-
// Resolve the branch fresh each check: the dev server
|
|
129
|
+
// Resolve the branch fresh each check: the dev server follows git-HEAD switches
|
|
133
130
|
// (services.refreshActiveBranch reassigns config.defaultActiveBranch), so the comparison target
|
|
134
|
-
// must track whatever branch is currently
|
|
131
|
+
// must track whatever branch is currently served. An explicit options.branch pins it.
|
|
135
132
|
const resolveBranch = () => options.branch ??
|
|
136
133
|
services.config.defaultActiveBranch ??
|
|
137
134
|
services.config.defaultBaseBranch ??
|
|
@@ -147,9 +144,9 @@ export function startDevContentWatcher(services, options = {}) {
|
|
|
147
144
|
// ENTRY deliberately survives, carrying lastMessage/reportedDivergence into the new watcher.
|
|
148
145
|
watcherState.dispose?.();
|
|
149
146
|
/**
|
|
150
|
-
* Emit `message` unless it is verbatim what was emitted last for this directory
|
|
151
|
-
*
|
|
152
|
-
*
|
|
147
|
+
* Emit `message` unless it is verbatim what was emitted last for this directory -- divergence
|
|
148
|
+
* block, resolved notice and error paths alike, since in every case a repeat means "the
|
|
149
|
+
* condition is unchanged", which the reader already knows.
|
|
153
150
|
*/
|
|
154
151
|
const reportOnce = (message) => {
|
|
155
152
|
if (watcherState.lastMessage === message)
|
|
@@ -174,8 +171,8 @@ export function startDevContentWatcher(services, options = {}) {
|
|
|
174
171
|
return;
|
|
175
172
|
const diff = await diffContentTrees(workingTreeContentDir, branchContentDir);
|
|
176
173
|
if (isContentTreeDiffEmpty(diff)) {
|
|
177
|
-
//
|
|
178
|
-
//
|
|
174
|
+
// An announced condition needs its retraction announced too, or the reader is left
|
|
175
|
+
// believing the dev server is still serving stale content.
|
|
179
176
|
if (watcherState.reportedDivergence) {
|
|
180
177
|
watcherState.reportedDivergence = false;
|
|
181
178
|
reportOnce(formatResolvedNotice(branch));
|
|
@@ -201,9 +198,9 @@ export function startDevContentWatcher(services, options = {}) {
|
|
|
201
198
|
watcher.on('add', () => void check());
|
|
202
199
|
watcher.on('change', () => void check());
|
|
203
200
|
watcher.on('unlink', () => void check());
|
|
204
|
-
//
|
|
201
|
+
// Watcher errors (e.g. inotify ENOSPC/EMFILE) must be handled, or an unhandled 'error' event
|
|
202
|
+
// crashes dev.
|
|
205
203
|
watcher.on('error', (err) => reportOnce(`CanopyCMS: dev content-sync watcher error: ${getErrorMessage(err)}`));
|
|
206
|
-
// Initial divergence check at startup.
|
|
207
204
|
void check();
|
|
208
205
|
const dispose = () => {
|
|
209
206
|
void watcher.close();
|
|
@@ -3,6 +3,7 @@ import type { OperatingMode } from '../operating-mode/index.js';
|
|
|
3
3
|
import type { ConflictStatus, PullRequestState, SyncStatus } from '../types.js';
|
|
4
4
|
import type { CommentThread } from '../comment-store.js';
|
|
5
5
|
import type { UserSearchResult } from '../auth/types.js';
|
|
6
|
+
/** @internal Exported for tests. */
|
|
6
7
|
export interface BranchSummary {
|
|
7
8
|
name: string;
|
|
8
9
|
status: string;
|
|
@@ -32,12 +33,7 @@ export interface BranchSummary {
|
|
|
32
33
|
* status) -- see `useBranchManager.tsx`'s `BranchSummary.submitBlocked` and
|
|
33
34
|
* `BranchWriteProtection.submitBlockedIncludingStatus` for the full
|
|
34
35
|
* rationale. `getBranchPermissions`'s `canSubmit` consumes this directly
|
|
35
|
-
* instead of re-deriving `status === 'editing' && !isProtected
|
|
36
|
-
* PR #189's stated invariant is "consume the server flag, don't
|
|
37
|
-
* re-derive", and shipping only the base-branch-only `submitBlocked` flag
|
|
38
|
-
* (rejected in #205, since that's the same value as `isProtected` and
|
|
39
|
-
* would still have left the status half re-derived here) would not have
|
|
40
|
-
* removed the re-derivation this replaces.
|
|
36
|
+
* instead of re-deriving `status === 'editing' && !isProtected`.
|
|
41
37
|
*/
|
|
42
38
|
submitBlocked?: boolean;
|
|
43
39
|
}
|
|
@@ -52,6 +48,7 @@ export interface UserContext {
|
|
|
52
48
|
* - Users in branch ACL can submit/withdraw
|
|
53
49
|
* - System branches can be submitted/withdrawn by anyone with access
|
|
54
50
|
* - Admins/Reviewers always have access
|
|
51
|
+
* @internal Exported for tests.
|
|
55
52
|
*/
|
|
56
53
|
export declare const getBranchPermissions: (branch: BranchSummary, user: UserContext | undefined) => {
|
|
57
54
|
canSubmit: boolean;
|
|
@@ -15,6 +15,7 @@ import { formatRelativeTime } from './relative-time.js';
|
|
|
15
15
|
* - Users in branch ACL can submit/withdraw
|
|
16
16
|
* - System branches can be submitted/withdrawn by anyone with access
|
|
17
17
|
* - Admins/Reviewers always have access
|
|
18
|
+
* @internal Exported for tests.
|
|
18
19
|
*/
|
|
19
20
|
export const getBranchPermissions = (branch, user) => {
|
|
20
21
|
if (!user) {
|
|
@@ -32,44 +33,30 @@ export const getBranchPermissions = (branch, user) => {
|
|
|
32
33
|
// auto-provision marker (createdBy: 'canopycms-system') would otherwise let
|
|
33
34
|
// anyone with general access submit/withdraw/delete it.
|
|
34
35
|
const isSystemBranch = branch.createdBy === 'canopycms-system' && !branch.isProtected;
|
|
35
|
-
// Check if user is in branch ACL
|
|
36
36
|
const userInACL = (branch.access?.users?.includes(user.userId) ||
|
|
37
37
|
user.groups?.some((g) => branch.access?.groups?.includes(g))) ??
|
|
38
38
|
false;
|
|
39
|
-
// Can perform workflow actions if: creator OR in ACL OR (system branch AND has basic access) OR privileged
|
|
40
39
|
const canPerformWorkflowActions = userIsCreator || userInACL || isSystemBranch || userIsAdmin || userIsReviewer;
|
|
41
40
|
// Submit: can perform workflow actions AND the server says submit isn't
|
|
42
41
|
// blocked. `submitBlocked` is the server's own compound answer -- status
|
|
43
42
|
// past 'editing' OR the protected base branch -- computed once in
|
|
44
43
|
// `getBranchWriteProtection` (protected-branch.ts) and threaded through
|
|
45
|
-
// unchanged
|
|
46
|
-
// (`branch.status === 'editing' && !branch.isProtected`), which is exactly
|
|
47
|
-
// the drift hazard PR #189's "consume the server flag, don't re-derive"
|
|
48
|
-
// invariant exists to prevent. See `BranchSummary.submitBlocked`'s doc
|
|
49
|
-
// comment above for why a bare `submitBlocked` flag (the base-branch-only
|
|
50
|
-
// meaning, as rejected in #205) would not have been enough on its own.
|
|
44
|
+
// unchanged.
|
|
51
45
|
//
|
|
52
|
-
// `?? true
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// wire omits it, and a second, contradictory default here would quietly undo
|
|
58
|
-
// that for anything constructing a BranchSummary directly.
|
|
46
|
+
// `?? true`, not a bare `!branch.submitBlocked`: the field is optional (like
|
|
47
|
+
// `isProtected`/`readOnly` beside it), so a missing value must mean BLOCKED,
|
|
48
|
+
// not allowed -- matching the `?? true` default `useBranchManager.tsx`
|
|
49
|
+
// already applies to the same flag. A `?? false` default here would
|
|
50
|
+
// fail-open for anything constructing a BranchSummary directly.
|
|
59
51
|
const canSubmit = canPerformWorkflowActions && !(branch.submitBlocked ?? true);
|
|
60
|
-
// Withdraw:
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
// 'approved' is included because withdraw is that status's ONLY
|
|
70
|
-
// non-destructive exit (request-changes still requires 'submitted', and the
|
|
71
|
-
// submit status gate now refuses a non-editing branch). Surfacing it here is
|
|
72
|
-
// what makes the exit reachable -- the server accepting it is not enough.
|
|
52
|
+
// Withdraw: can perform workflow actions AND branch is submitted or
|
|
53
|
+
// approved -- 'approved' is included because withdraw is that status's only
|
|
54
|
+
// non-destructive exit, and getBranchPermissions must grant canWithdraw for
|
|
55
|
+
// it to be reachable. Allowed even when the PR was closed without merging:
|
|
56
|
+
// that's the deliberate recovery path for a closed-unmerged PR (server-side
|
|
57
|
+
// skip in api/branch-withdraw.ts). Not additionally gated on
|
|
58
|
+
// !branch.isProtected either: withdraw is also how a creator/ACL/privileged
|
|
59
|
+
// user recovers a protected branch wrongly stuck in 'submitted'.
|
|
73
60
|
const canWithdraw = canPerformWorkflowActions && (branch.status === 'submitted' || branch.status === 'approved');
|
|
74
61
|
// Delete: Admin or creator (but not if submitted or approved -- both mean
|
|
75
62
|
// an open PR, the latter already reviewed and awaiting merge -- and never
|
|
@@ -125,28 +112,21 @@ export const BranchManager = ({ branches, mode, user, onSelect, onCreate, onDele
|
|
|
125
112
|
cursor: 'pointer',
|
|
126
113
|
}, children: "View PR" })] }))] }), b.access && (_jsxs(Group, { gap: 6, children: [b.access.users?.map((u) => onGetUserMetadata ? (_jsx(UserBadge, { userId: u, getUserMetadata: onGetUserMetadata, variant: "avatar-only", size: "xs", showEmailTooltip: true }, u)) : (_jsx(Badge, { variant: "outline", color: "neutral", children: u }, u))), b.access.groups?.map((g) => (_jsx(Badge, { variant: "outline", color: "neutral", children: g }, g)))] }))] }), _jsxs(Group, { gap: 8, children: [_jsx(Button, { size: "xs", variant: "light", onClick: () => onSelect?.(b.name), "data-testid": `switch-to-branch-button-${b.name}`, children: "Open" }), b.status === 'submitted' || b.status === 'approved' ? (_jsx(Tooltip, { label: "Only the branch creator can withdraw", disabled: perms.canWithdraw, children: _jsx(Button, { size: "xs", variant: "light", color: "orange", onClick: () => onWithdraw?.(b.name), disabled: !perms.canWithdraw, "data-testid": `withdraw-branch-button-${b.name}`, children: "Withdraw" }) })) : (_jsx(Tooltip, { label: b.isProtected
|
|
127
114
|
? 'The base branch cannot be submitted'
|
|
128
|
-
: // A status arm
|
|
129
|
-
// the
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
// Submit reading "Only the branch creator can
|
|
133
|
-
// submit" -- to the creator. That is precisely
|
|
134
|
-
// the permission-vs-no-available-transition
|
|
135
|
-
// confusion #205 removed from EditorHeader, and
|
|
136
|
-
// folding status into canSubmit reintroduced it
|
|
137
|
-
// one component over.
|
|
115
|
+
: // A status arm: canSubmit folds in the server's status rule as
|
|
116
|
+
// well as the base-branch one, so an archived (or any
|
|
117
|
+
// non-editing) branch reads its own reason instead of the
|
|
118
|
+
// generic "Only the branch creator can submit".
|
|
138
119
|
b.status !== 'editing'
|
|
139
120
|
? `A branch with status "${b.status}" cannot be submitted`
|
|
140
121
|
: 'Only the branch creator can submit', disabled: perms.canSubmit, children: _jsx(Button, { size: "xs", variant: "light", color: "green", onClick: () => onSubmit?.(b.name), disabled: !perms.canSubmit, "data-testid": `submit-branch-button-${b.name}`, children: "Submit" }) })), _jsx(Tooltip, { label: b.pullRequestState === 'closed'
|
|
141
122
|
? 'PR was closed on GitHub without merging'
|
|
142
123
|
: 'Only Reviewers or Admins can request changes', disabled: perms.canRequestChanges, children: _jsx(Button, { size: "xs", variant: "outline", color: "neutral", onClick: () => onRequestChanges?.(b.name), disabled: !perms.canRequestChanges, "data-testid": `request-changes-branch-button-${b.name}`, children: "Request changes" }) }), _jsx(Tooltip, { label: b.isProtected
|
|
143
124
|
? 'The base branch cannot be deleted'
|
|
144
|
-
: // A status arm, mirroring the Submit tooltip
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
// both mean an open PR is blocking deletion.
|
|
125
|
+
: // A status arm, mirroring the Submit tooltip above: without it,
|
|
126
|
+
// a branch blocked by its own status reads the generic "Only
|
|
127
|
+
// Admin or branch creator can delete" instead of naming the open
|
|
128
|
+
// PR. 'approved' is included alongside 'submitted' since both
|
|
129
|
+
// mean an open PR is blocking deletion.
|
|
150
130
|
b.status === 'submitted' || b.status === 'approved'
|
|
151
131
|
? `Cannot delete branch with open PR (status "${b.status}")`
|
|
152
132
|
: 'Only Admin or branch creator can delete', disabled: perms.canDelete, children: _jsx(Button, { size: "xs", variant: "outline", color: "red", onClick: () => onDelete?.(b.name), disabled: !perms.canDelete, "data-testid": `delete-branch-button-${b.name}`, children: "Delete" }) })] })] }) }, b.name));
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
4
|
import { Editor } from './Editor.js';
|
|
5
|
-
import { ApiClientProvider
|
|
5
|
+
import { ApiClientProvider } from './context/index.js';
|
|
6
|
+
import { EditorAuthGate } from './EditorAuthGate.js';
|
|
6
7
|
import { buildEditorCollections, buildPreviewBaseByCollection } from './editor-config.js';
|
|
7
8
|
export const CanopyEditor = ({ config, entries = [], initialSelectedId, initialValues, renderPreview, onCreateEntry, branchName, customRenderers, }) => {
|
|
8
9
|
const collections = useMemo(() => buildEditorCollections(config.flatSchema), [config.flatSchema]);
|
|
@@ -16,6 +17,8 @@ export const CanopyEditor = ({ config, entries = [], initialSelectedId, initialV
|
|
|
16
17
|
const resolvedTitle = config.editor?.title ?? 'CanopyCMS Editor';
|
|
17
18
|
const resolvedSubtitle = config.editor?.subtitle;
|
|
18
19
|
const resolvedTheme = config.editor?.theme ?? undefined;
|
|
19
|
-
|
|
20
|
+
// The gate owns the SWRProvider (keyed by user id), so it sits between the API client and the
|
|
21
|
+
// editor: see EditorAuthGate for what a signed-out or lapsed session renders.
|
|
22
|
+
return (_jsx(ApiClientProvider, { basePath: config.basePath, children: _jsx(EditorAuthGate, { SignInComponent: config.editor?.SignInComponent, themeOptions: resolvedTheme, children: _jsx(Editor, { entries: entries, title: resolvedTitle, subtitle: resolvedSubtitle, branchName: resolvedBranchName, operatingMode: config.mode, initialSelectedId: initialSelectedId, initialValues: initialValues, renderPreview: renderPreview, onCreateEntry: onCreateEntry, customRenderers: customRenderers, collections: collections, contentRoot: config.contentRoot, entryLinkUrl: config.entryLinkUrl, previewBaseByCollection: previewBase, assetBaseUrl: config.assetBaseUrl, basePath: config.basePath, themeOptions: resolvedTheme, AccountComponent: config.editor?.AccountComponent, onAccountClick: config.editor?.onAccountClick, onLogoutClick: config.editor?.onLogoutClick }) }) }));
|
|
20
23
|
};
|
|
21
24
|
export default CanopyEditor;
|
|
@@ -16,14 +16,12 @@ export const CommentsPanel = ({ branchName, comments, canResolve, onAddComment,
|
|
|
16
16
|
setIsSubmitting(true);
|
|
17
17
|
try {
|
|
18
18
|
if (replyTo) {
|
|
19
|
-
// Reply to existing thread - get context from the thread
|
|
20
19
|
const thread = comments.find((t) => t.id === replyTo);
|
|
21
20
|
if (thread) {
|
|
22
21
|
await onAddComment(newCommentText, thread.type, thread.entryPath, thread.canopyPath, replyTo);
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
else {
|
|
26
|
-
// New branch-level comment (default for panel)
|
|
27
25
|
await onAddComment(newCommentText, 'branch', undefined, undefined, undefined);
|
|
28
26
|
}
|
|
29
27
|
setNewCommentText('');
|