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/static/index.d.ts
CHANGED
|
@@ -164,13 +164,13 @@ export interface EntryWithUnknownKeys {
|
|
|
164
164
|
*
|
|
165
165
|
* The inverse of `findInvalidEntries`, and non-fatal by design: an unknown key is stale data,
|
|
166
166
|
* not broken data. A build must not go red for it — the page still renders, it is just quietly
|
|
167
|
-
* missing whatever the renamed field
|
|
167
|
+
* missing whatever value the renamed field would supply. `warnUnknownEntryKeys` is the reporting
|
|
168
168
|
* half. Both sit at module scope alongside `findInvalidEntries`/`assertBuildEntriesValid` and
|
|
169
169
|
* are deliberately NOT on `canopycms/server`, matching those two — the build wires them itself.
|
|
170
170
|
*
|
|
171
171
|
* Skips items with no resolved schema, and items whose schema is empty — "no schema" is not
|
|
172
172
|
* "every key is unknown". Same `normalizeDatesDeep` pass as the validity scan, so a hand-authored
|
|
173
|
-
* `date:
|
|
173
|
+
* `date: YYYY-MM-DD` is a plain value here too.
|
|
174
174
|
*/
|
|
175
175
|
export declare function findEntriesWithUnknownKeys(items: readonly BuildScanItem[]): EntryWithUnknownKeys[];
|
|
176
176
|
/**
|
|
@@ -203,28 +203,18 @@ type UrlScanItem = Pick<ListEntriesItem, 'entryPath' | 'urlPath'>;
|
|
|
203
203
|
/**
|
|
204
204
|
* Find every `urlPath` claimed by two or more entries.
|
|
205
205
|
*
|
|
206
|
-
* `listEntries`
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
206
|
+
* `listEntries` gives each entry one `urlPath`, documented round-trip safe with `readByUrlPath`
|
|
207
|
+
* — but that guarantee is per-entry, not per-URL: two different entries can compute the same
|
|
208
|
+
* one, leaving exactly one reachable and the other silently unrouted. Known causes: an entry
|
|
209
|
+
* beside a sibling collection that also has an `index` entry (collapses onto the same path —
|
|
210
|
+
* the write boundary now refuses authoring this too, but merge/PR/retrofit content bypasses it;
|
|
211
|
+
* a sibling with NO index entry is a different, uncontested shape); slugs differing only by case
|
|
212
|
+
* (`urlPath` is lowercased); or two entries sharing a slug in one collection (also
|
|
213
|
+
* write-boundary-refused via `ContentStore.buildPaths`, same bypass routes).
|
|
210
214
|
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* other way — the same merge/PR/retrofit routes as the same-slug case below. (An entry beside a
|
|
215
|
-
* sibling collection with NO index entry is a different, legitimate shape: a landing page plus
|
|
216
|
-
* a folder of children, nothing contested.)
|
|
217
|
-
* - Two entries whose slugs differ only by case, since `urlPath` is lowercased.
|
|
218
|
-
* - Two entries with the same slug in one collection. The write boundary already refuses this
|
|
219
|
-
* (`ContentStore.buildPaths` resolves by slug across entry types, and the create-intent guard
|
|
220
|
-
* turns a hit into a conflict), but content also arrives by merge, by PR, and by adopters
|
|
221
|
-
* retrofitting an existing repo — none of which pass through that boundary.
|
|
222
|
-
*
|
|
223
|
-
* Exported so adopters can assert on it directly instead of hand-rolling a content-integrity
|
|
224
|
-
* test; `assertNoDuplicateUrlPaths` is what the build itself uses.
|
|
225
|
-
*
|
|
226
|
-
* Pure. Results are sorted by `urlPath`, with each `entryPaths` sorted too, so messages are
|
|
227
|
-
* stable across runs (`listEntries` resolves collections in parallel, so its own order is not).
|
|
215
|
+
* Exported for adopters to assert on directly instead of hand-rolling a content-integrity test;
|
|
216
|
+
* `assertNoDuplicateUrlPaths` is what the build uses. Pure — results and each `entryPaths` are
|
|
217
|
+
* sorted, since `listEntries` resolves collections in parallel and its own order is not stable.
|
|
228
218
|
*/
|
|
229
219
|
export declare function findDuplicateUrlPaths(items: readonly UrlScanItem[]): DuplicateUrlPath[];
|
|
230
220
|
/**
|
|
@@ -240,7 +230,7 @@ export declare function findDuplicateUrlPaths(items: readonly UrlScanItem[]): Du
|
|
|
240
230
|
* collision at RESOLUTION time regardless of routing. `readByUrlPath` picks one of the two
|
|
241
231
|
* whatever any `generateStaticParams` filter says, so the un-routed entry is not innocent; it is
|
|
242
232
|
* shadowing (or being shadowed by) the routed one at the same URL. `rootPath` narrows the scan
|
|
243
|
-
* because it narrows what was loaded
|
|
233
|
+
* because it narrows what was loaded; `filter` deliberately does not.
|
|
244
234
|
*
|
|
245
235
|
* Related but not redundant: `canopycms-next`'s `dedupeSitemapItems` warns on the same collision
|
|
246
236
|
* at the sitemap. It stays, because it also covers entry-vs-`extraUrls` collisions (adopter-supplied
|
|
@@ -258,36 +248,20 @@ export interface UnroutableSlugEntry {
|
|
|
258
248
|
/** What the slug-routability scan needs off a listing item. */
|
|
259
249
|
type SlugScanItem = Pick<ListEntriesItem, 'entryPath' | 'urlPath' | 'slug'>;
|
|
260
250
|
/**
|
|
261
|
-
* Find every listed entry whose `slug` cannot pass `parseSlug` — the
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
* The filename grammar `parseTypedFilename` parses (`{type}.{slug}.{id}.{ext}`) deliberately
|
|
265
|
-
* allows a slug to contain dots, anchoring the split on the type and ID instead (see that
|
|
266
|
-
* function's doc comment) — and it does not run the parsed slug through `parseSlug` before
|
|
267
|
-
* handing it back, by design: recovering `{type, slug, id}` structurally, without validating
|
|
268
|
-
* every part, is its whole contract. `listCollectionEntries` inherits that: it lists the file and
|
|
269
|
-
* computes a `urlPath` for it, same as any other entry.
|
|
270
|
-
*
|
|
271
|
-
* But `parseSlug` requires `^[a-z0-9][a-z0-9-]*$` — no dots — so a listed entry whose slug
|
|
272
|
-
* contains one is unreachable through the one path everything else assumes works:
|
|
273
|
-
* `readByUrlPath` tries the URL's last segment as a candidate slug and skips any candidate that
|
|
274
|
-
* fails `parseSlug` (treating it as a miss, not an error) before it ever calls `read()`. The
|
|
275
|
-
* entry still builds, still gets a `generateStaticParams` entry, still gets a sitemap `<loc>` —
|
|
276
|
-
* and 404s the moment anything actually visits it. That breaks the `Round-trip safe` contract
|
|
277
|
-
* `content-listing.ts` documents on `urlPath` (`readByUrlPath(item.urlPath)` resolves to the same
|
|
278
|
-
* entry) and is exactly the silent-page-loss failure mode this guard's siblings
|
|
279
|
-
* (`assertBuildEntriesValid`, `assertNoDuplicateUrlPaths`) exist to make loud instead.
|
|
251
|
+
* Find every listed entry whose `slug` cannot pass `parseSlug` — the check `readByUrlPath` runs
|
|
252
|
+
* on every URL-resolution candidate it tries (`context.ts`).
|
|
280
253
|
*
|
|
281
|
-
* The
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
254
|
+
* The filename grammar (`parseTypedFilename`) deliberately allows a slug to contain dots and
|
|
255
|
+
* hands it back unvalidated, so a listed entry can carry one. But `parseSlug` requires
|
|
256
|
+
* `^[a-z0-9][a-z0-9-]*$` — no dots — so `readByUrlPath` skips that candidate as a miss before
|
|
257
|
+
* ever calling `read()`. The entry still builds, gets a `generateStaticParams` entry and a
|
|
258
|
+
* sitemap `<loc>`, and then 404s on every visit — breaking the `Round-trip safe` contract
|
|
259
|
+
* `content-listing.ts` documents on `urlPath`.
|
|
286
260
|
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
261
|
+
* The write boundary (`parseSlug` in `api/content.ts` and `ContentStore.write`/`renameEntry`)
|
|
262
|
+
* refuses such a slug on create and rename only, so this guard covers every slug it never saw:
|
|
263
|
+
* hand-authored files, migrations, git-merged content, and a repo being retrofitted onto
|
|
264
|
+
* CanopyCMS.
|
|
291
265
|
*/
|
|
292
266
|
export declare function findUnroutableSlugs(items: readonly SlugScanItem[]): UnroutableSlugEntry[];
|
|
293
267
|
/**
|
package/dist/static/index.js
CHANGED
|
@@ -27,20 +27,12 @@ async function enumerateRoutableEntries(ctx, rootPath, phaseLabel, resolveRefere
|
|
|
27
27
|
// an abandoned schema-invalid scaffold shipping into a static build silently drops that page's
|
|
28
28
|
// route (or worse, renders broken), which is worse than a red build.
|
|
29
29
|
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// is not.
|
|
37
|
-
//
|
|
38
|
-
// Slug routability is asserted first, ahead of schema validity and duplicate URLs: an entry
|
|
39
|
-
// whose slug cannot round-trip through `readByUrlPath` has no URL at all as far as any OTHER
|
|
40
|
-
// guard is concerned, so it is the most fundamental of the three problems a listed entry can
|
|
41
|
-
// have. Schema validity is asserted before duplicate URLs for the same reason one level down:
|
|
42
|
-
// a schema-invalid entry is the more fundamental problem, and an adopter fixing it may remove
|
|
43
|
-
// the duplicate on the way.
|
|
30
|
+
// These four guards run in a fixed order on the RAW listing, before the caller's `filter` —
|
|
31
|
+
// `rootPath` narrows them, `filter` deliberately does not. The unknown-key warning runs first,
|
|
32
|
+
// so a build about to go red still reports everything found. Slug routability runs next: an
|
|
33
|
+
// unroutable entry has no URL at all as far as the other three are concerned, the most
|
|
34
|
+
// fundamental problem a listed entry can have. Schema validity runs before duplicate URLs for
|
|
35
|
+
// the same "most fundamental first" reason, one level down.
|
|
44
36
|
if (isBuildMode()) {
|
|
45
37
|
warnUnknownEntryKeys(entries, phaseLabel);
|
|
46
38
|
assertRoutableSlugs(entries, phaseLabel);
|
|
@@ -109,7 +101,7 @@ export async function collectStaticPaths(ctx, options = {}) {
|
|
|
109
101
|
* Deep-walk a plain data value (objects and arrays only — the shapes YAML/JSON parsing can
|
|
110
102
|
* produce), converting every `Date` instance to its ISO string.
|
|
111
103
|
*
|
|
112
|
-
* gray-matter parses unquoted YAML dates in hand-authored frontmatter (`date:
|
|
104
|
+
* gray-matter parses unquoted YAML dates in hand-authored frontmatter (`date: YYYY-MM-DD`) into
|
|
113
105
|
* JS `Date` objects rather than strings. The shared validator's datetime check requires a string
|
|
114
106
|
* (see `validateScalar` in entry-validator.ts), so a legitimate hand-authored or migrated entry
|
|
115
107
|
* would otherwise fail this build guard. CMS-authored entries round-trip as quoted strings, so
|
|
@@ -175,13 +167,13 @@ const UNKNOWN_KEY_REPORT_LIMIT = 20;
|
|
|
175
167
|
*
|
|
176
168
|
* The inverse of `findInvalidEntries`, and non-fatal by design: an unknown key is stale data,
|
|
177
169
|
* not broken data. A build must not go red for it — the page still renders, it is just quietly
|
|
178
|
-
* missing whatever the renamed field
|
|
170
|
+
* missing whatever value the renamed field would supply. `warnUnknownEntryKeys` is the reporting
|
|
179
171
|
* half. Both sit at module scope alongside `findInvalidEntries`/`assertBuildEntriesValid` and
|
|
180
172
|
* are deliberately NOT on `canopycms/server`, matching those two — the build wires them itself.
|
|
181
173
|
*
|
|
182
174
|
* Skips items with no resolved schema, and items whose schema is empty — "no schema" is not
|
|
183
175
|
* "every key is unknown". Same `normalizeDatesDeep` pass as the validity scan, so a hand-authored
|
|
184
|
-
* `date:
|
|
176
|
+
* `date: YYYY-MM-DD` is a plain value here too.
|
|
185
177
|
*/
|
|
186
178
|
export function findEntriesWithUnknownKeys(items) {
|
|
187
179
|
const found = [];
|
|
@@ -254,28 +246,18 @@ export function assertBuildEntriesValid(items, phaseLabel) {
|
|
|
254
246
|
/**
|
|
255
247
|
* Find every `urlPath` claimed by two or more entries.
|
|
256
248
|
*
|
|
257
|
-
* `listEntries`
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
* other way — the same merge/PR/retrofit routes as the same-slug case below. (An entry beside a
|
|
266
|
-
* sibling collection with NO index entry is a different, legitimate shape: a landing page plus
|
|
267
|
-
* a folder of children, nothing contested.)
|
|
268
|
-
* - Two entries whose slugs differ only by case, since `urlPath` is lowercased.
|
|
269
|
-
* - Two entries with the same slug in one collection. The write boundary already refuses this
|
|
270
|
-
* (`ContentStore.buildPaths` resolves by slug across entry types, and the create-intent guard
|
|
271
|
-
* turns a hit into a conflict), but content also arrives by merge, by PR, and by adopters
|
|
272
|
-
* retrofitting an existing repo — none of which pass through that boundary.
|
|
249
|
+
* `listEntries` gives each entry one `urlPath`, documented round-trip safe with `readByUrlPath`
|
|
250
|
+
* — but that guarantee is per-entry, not per-URL: two different entries can compute the same
|
|
251
|
+
* one, leaving exactly one reachable and the other silently unrouted. Known causes: an entry
|
|
252
|
+
* beside a sibling collection that also has an `index` entry (collapses onto the same path —
|
|
253
|
+
* the write boundary now refuses authoring this too, but merge/PR/retrofit content bypasses it;
|
|
254
|
+
* a sibling with NO index entry is a different, uncontested shape); slugs differing only by case
|
|
255
|
+
* (`urlPath` is lowercased); or two entries sharing a slug in one collection (also
|
|
256
|
+
* write-boundary-refused via `ContentStore.buildPaths`, same bypass routes).
|
|
273
257
|
*
|
|
274
|
-
* Exported
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
* Pure. Results are sorted by `urlPath`, with each `entryPaths` sorted too, so messages are
|
|
278
|
-
* stable across runs (`listEntries` resolves collections in parallel, so its own order is not).
|
|
258
|
+
* Exported for adopters to assert on directly instead of hand-rolling a content-integrity test;
|
|
259
|
+
* `assertNoDuplicateUrlPaths` is what the build uses. Pure — results and each `entryPaths` are
|
|
260
|
+
* sorted, since `listEntries` resolves collections in parallel and its own order is not stable.
|
|
279
261
|
*/
|
|
280
262
|
export function findDuplicateUrlPaths(items) {
|
|
281
263
|
const byUrl = new Map();
|
|
@@ -307,7 +289,7 @@ export function findDuplicateUrlPaths(items) {
|
|
|
307
289
|
* collision at RESOLUTION time regardless of routing. `readByUrlPath` picks one of the two
|
|
308
290
|
* whatever any `generateStaticParams` filter says, so the un-routed entry is not innocent; it is
|
|
309
291
|
* shadowing (or being shadowed by) the routed one at the same URL. `rootPath` narrows the scan
|
|
310
|
-
* because it narrows what was loaded
|
|
292
|
+
* because it narrows what was loaded; `filter` deliberately does not.
|
|
311
293
|
*
|
|
312
294
|
* Related but not redundant: `canopycms-next`'s `dedupeSitemapItems` warns on the same collision
|
|
313
295
|
* at the sitemap. It stays, because it also covers entry-vs-`extraUrls` collisions (adopter-supplied
|
|
@@ -334,36 +316,20 @@ export function assertNoDuplicateUrlPaths(items, phaseLabel) {
|
|
|
334
316
|
'lowercased). Rename or remove one of the colliding entries, then rebuild.');
|
|
335
317
|
}
|
|
336
318
|
/**
|
|
337
|
-
* Find every listed entry whose `slug` cannot pass `parseSlug` — the
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
* The filename grammar `parseTypedFilename` parses (`{type}.{slug}.{id}.{ext}`) deliberately
|
|
341
|
-
* allows a slug to contain dots, anchoring the split on the type and ID instead (see that
|
|
342
|
-
* function's doc comment) — and it does not run the parsed slug through `parseSlug` before
|
|
343
|
-
* handing it back, by design: recovering `{type, slug, id}` structurally, without validating
|
|
344
|
-
* every part, is its whole contract. `listCollectionEntries` inherits that: it lists the file and
|
|
345
|
-
* computes a `urlPath` for it, same as any other entry.
|
|
346
|
-
*
|
|
347
|
-
* But `parseSlug` requires `^[a-z0-9][a-z0-9-]*$` — no dots — so a listed entry whose slug
|
|
348
|
-
* contains one is unreachable through the one path everything else assumes works:
|
|
349
|
-
* `readByUrlPath` tries the URL's last segment as a candidate slug and skips any candidate that
|
|
350
|
-
* fails `parseSlug` (treating it as a miss, not an error) before it ever calls `read()`. The
|
|
351
|
-
* entry still builds, still gets a `generateStaticParams` entry, still gets a sitemap `<loc>` —
|
|
352
|
-
* and 404s the moment anything actually visits it. That breaks the `Round-trip safe` contract
|
|
353
|
-
* `content-listing.ts` documents on `urlPath` (`readByUrlPath(item.urlPath)` resolves to the same
|
|
354
|
-
* entry) and is exactly the silent-page-loss failure mode this guard's siblings
|
|
355
|
-
* (`assertBuildEntriesValid`, `assertNoDuplicateUrlPaths`) exist to make loud instead.
|
|
319
|
+
* Find every listed entry whose `slug` cannot pass `parseSlug` — the check `readByUrlPath` runs
|
|
320
|
+
* on every URL-resolution candidate it tries (`context.ts`).
|
|
356
321
|
*
|
|
357
|
-
* The
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
322
|
+
* The filename grammar (`parseTypedFilename`) deliberately allows a slug to contain dots and
|
|
323
|
+
* hands it back unvalidated, so a listed entry can carry one. But `parseSlug` requires
|
|
324
|
+
* `^[a-z0-9][a-z0-9-]*$` — no dots — so `readByUrlPath` skips that candidate as a miss before
|
|
325
|
+
* ever calling `read()`. The entry still builds, gets a `generateStaticParams` entry and a
|
|
326
|
+
* sitemap `<loc>`, and then 404s on every visit — breaking the `Round-trip safe` contract
|
|
327
|
+
* `content-listing.ts` documents on `urlPath`.
|
|
362
328
|
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
329
|
+
* The write boundary (`parseSlug` in `api/content.ts` and `ContentStore.write`/`renameEntry`)
|
|
330
|
+
* refuses such a slug on create and rename only, so this guard covers every slug it never saw:
|
|
331
|
+
* hand-authored files, migrations, git-merged content, and a repo being retrofitted onto
|
|
332
|
+
* CanopyCMS.
|
|
367
333
|
*/
|
|
368
334
|
export function findUnroutableSlugs(items) {
|
|
369
335
|
const found = [];
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
import type { Task, TaskQueueLogger } from '../task-queue/index.js';
|
|
6
|
-
/**
|
|
7
|
-
* Actions the EC2 worker can execute on behalf of Lambda.
|
|
2
|
+
* The CMS queue contract: the generic queue in ./task-queue plus the CMS
|
|
3
|
+
* action vocabulary, a WorkerTask alias and the debug logger. Published as
|
|
4
|
+
* `canopycms/worker/task-queue`.
|
|
8
5
|
*/
|
|
6
|
+
import type { Task, TaskQueueLogger } from './types.js';
|
|
7
|
+
/** Actions the EC2 worker can execute on behalf of Lambda. */
|
|
9
8
|
export type TaskAction = 'push-and-create-pr' | 'push-and-update-pr' | 'push-and-create-or-update-pr' | 'convert-to-draft' | 'close-pr' | 'delete-remote-branch' | 'push-branch';
|
|
10
9
|
/** A task with a CMS-specific action. */
|
|
11
10
|
export type WorkerTask = Task & {
|
|
12
11
|
action: TaskAction;
|
|
13
12
|
};
|
|
14
13
|
export declare const cmsTaskQueueLogger: TaskQueueLogger;
|
|
15
|
-
export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask,
|
|
16
|
-
|
|
17
|
-
export type { Task, TaskStatus, QueueStats, TaskQueueLogger, CorruptTaskFile } from '../task-queue/index.js';
|
|
14
|
+
export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
|
|
15
|
+
export type { Task, TaskStatus, QueueStats, TaskQueueLogger, CorruptTaskFile } from './types.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CMS queue contract: the generic queue in ./task-queue plus the CMS
|
|
3
|
+
* action vocabulary, a WorkerTask alias and the debug logger. Published as
|
|
4
|
+
* `canopycms/worker/task-queue`.
|
|
5
|
+
*/
|
|
6
|
+
import { createDebugLogger } from '../utils/debug.js';
|
|
7
|
+
const debugLogger = createDebugLogger({ prefix: 'TaskQueue' });
|
|
8
|
+
export const cmsTaskQueueLogger = {
|
|
9
|
+
debug(message, data) {
|
|
10
|
+
debugLogger.debug('task', message, data);
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { DEFAULT_PROD_WORKSPACE } from '../config/index.js';
|
|
3
|
-
/**
|
|
4
|
-
* Get the task queue directory for async worker operations.
|
|
5
|
-
*
|
|
6
|
-
* In prod mode: {CANOPYCMS_WORKSPACE_ROOT}/.tasks
|
|
7
|
-
* In dev mode: {cwd}/.canopy-dev/.tasks
|
|
8
|
-
*/
|
|
3
|
+
/** The task queue directory for async worker operations. */
|
|
9
4
|
export function getTaskQueueDir(config) {
|
|
10
5
|
switch (config.mode) {
|
|
11
6
|
case 'prod': {
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* Designed for shared filesystems (EFS/NFS) where one process enqueues
|
|
12
12
|
* and another dequeues. No external dependencies — only Node.js stdlib.
|
|
13
13
|
*
|
|
14
|
-
* IMPORTANT:
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* IMPORTANT: single-consumer only — dequeue is not atomic across processes;
|
|
15
|
+
* the worker lock (acquireLock) must serialize consumers, so never run more
|
|
16
|
+
* than one dequeue consumer concurrently.
|
|
17
17
|
*/
|
|
18
18
|
import type { Task, TaskStatus, QueueStats, TaskQueueLogger, CorruptTaskFile } from './types.js';
|
|
19
19
|
/**
|
|
@@ -50,28 +50,22 @@ export declare function failTask(taskDir: string, taskId: string, error: string,
|
|
|
50
50
|
*/
|
|
51
51
|
export declare function retryTask(taskDir: string, taskId: string, error: string, logger?: TaskQueueLogger): Promise<void>;
|
|
52
52
|
/**
|
|
53
|
-
* Requeue a permanently-failed task
|
|
54
|
-
*
|
|
55
|
-
* `
|
|
56
|
-
*
|
|
57
|
-
* ID: a pending file that reused the original ID would be silently deleted
|
|
58
|
-
* (without running) the moment it was scanned, since its ID already exists
|
|
59
|
-
* in failed/.
|
|
53
|
+
* Requeue a permanently-failed task: reads failed/{taskId}.json and creates a
|
|
54
|
+
* brand-new pending task with a fresh `id`, never the original — `dequeueTask`
|
|
55
|
+
* and `recoverOrphanedTasks` dedup completed/failed by ID, so reusing it would
|
|
56
|
+
* get the pending copy silently deleted, unrun, the moment it's scanned.
|
|
60
57
|
*
|
|
61
|
-
* The new task carries `payload.requeuedFrom`
|
|
62
|
-
* `
|
|
63
|
-
* of the FIFO queue rather than jumping ahead on the original timestamp).
|
|
58
|
+
* The new task carries `payload.requeuedFrom`, `retryCount: 0`, and a fresh
|
|
59
|
+
* `createdAt` (it re-joins the back of the FIFO queue, not the original slot).
|
|
64
60
|
*
|
|
65
|
-
* Write-then-unlink
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* (duplicate history, not a lost task); the reverse order could lose the
|
|
70
|
-
* task entirely if the process died after the unlink but before the write.
|
|
61
|
+
* Write-then-unlink: the new pending file is written FIRST; the failed
|
|
62
|
+
* original is unlinked only after (best-effort). A crash between the two
|
|
63
|
+
* leaves both — harmless duplication — but the reverse order could lose the
|
|
64
|
+
* task if the process dies after the unlink and before the write.
|
|
71
65
|
*
|
|
72
|
-
* Returns `{ error: 'not-found' }` if
|
|
73
|
-
* `{ error: 'unparseable' }` if
|
|
74
|
-
*
|
|
66
|
+
* Returns `{ error: 'not-found' }` if failed/{taskId}.json doesn't exist, or
|
|
67
|
+
* `{ error: 'unparseable' }` if it isn't valid task JSON (tell the admin to
|
|
68
|
+
* delete it rather than retry).
|
|
75
69
|
*/
|
|
76
70
|
export declare function requeueFailedTask(taskDir: string, taskId: string, logger?: TaskQueueLogger): Promise<{
|
|
77
71
|
newTaskId: string;
|
|
@@ -11,24 +11,20 @@
|
|
|
11
11
|
* Designed for shared filesystems (EFS/NFS) where one process enqueues
|
|
12
12
|
* and another dequeues. No external dependencies — only Node.js stdlib.
|
|
13
13
|
*
|
|
14
|
-
* IMPORTANT:
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* IMPORTANT: single-consumer only — dequeue is not atomic across processes;
|
|
15
|
+
* the worker lock (acquireLock) must serialize consumers, so never run more
|
|
16
|
+
* than one dequeue consumer concurrently.
|
|
17
17
|
*/
|
|
18
18
|
import fs from 'node:fs/promises';
|
|
19
19
|
import path from 'node:path';
|
|
20
20
|
import crypto from 'node:crypto';
|
|
21
21
|
const DEFAULT_MAX_RETRIES = 3;
|
|
22
|
-
// Silent no-op logger
|
|
23
22
|
const nullLogger = { debug: () => { } };
|
|
24
23
|
import { atomicWriteFile } from '../utils/atomic-write.js';
|
|
25
24
|
// Local helper — only stdlib dependency, keeps task-queue easy to extract.
|
|
26
25
|
function isNotFoundError(err) {
|
|
27
26
|
return err instanceof Error && 'code' in err && err.code === 'ENOENT';
|
|
28
27
|
}
|
|
29
|
-
// ============================================================================
|
|
30
|
-
// Core queue operations
|
|
31
|
-
// ============================================================================
|
|
32
28
|
/**
|
|
33
29
|
* Enqueue a task. Writes a JSON file to pending/{id}.json.
|
|
34
30
|
* Returns the generated task ID.
|
|
@@ -219,28 +215,22 @@ export async function retryTask(taskDir, taskId, error, logger = nullLogger) {
|
|
|
219
215
|
logger.debug('Retrying task', { id: taskId, retryCount, backoffMs });
|
|
220
216
|
}
|
|
221
217
|
/**
|
|
222
|
-
* Requeue a permanently-failed task
|
|
223
|
-
*
|
|
224
|
-
* `
|
|
225
|
-
*
|
|
226
|
-
* ID: a pending file that reused the original ID would be silently deleted
|
|
227
|
-
* (without running) the moment it was scanned, since its ID already exists
|
|
228
|
-
* in failed/.
|
|
218
|
+
* Requeue a permanently-failed task: reads failed/{taskId}.json and creates a
|
|
219
|
+
* brand-new pending task with a fresh `id`, never the original — `dequeueTask`
|
|
220
|
+
* and `recoverOrphanedTasks` dedup completed/failed by ID, so reusing it would
|
|
221
|
+
* get the pending copy silently deleted, unrun, the moment it's scanned.
|
|
229
222
|
*
|
|
230
|
-
* The new task carries `payload.requeuedFrom`
|
|
231
|
-
* `
|
|
232
|
-
* of the FIFO queue rather than jumping ahead on the original timestamp).
|
|
223
|
+
* The new task carries `payload.requeuedFrom`, `retryCount: 0`, and a fresh
|
|
224
|
+
* `createdAt` (it re-joins the back of the FIFO queue, not the original slot).
|
|
233
225
|
*
|
|
234
|
-
* Write-then-unlink
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* (duplicate history, not a lost task); the reverse order could lose the
|
|
239
|
-
* task entirely if the process died after the unlink but before the write.
|
|
226
|
+
* Write-then-unlink: the new pending file is written FIRST; the failed
|
|
227
|
+
* original is unlinked only after (best-effort). A crash between the two
|
|
228
|
+
* leaves both — harmless duplication — but the reverse order could lose the
|
|
229
|
+
* task if the process dies after the unlink and before the write.
|
|
240
230
|
*
|
|
241
|
-
* Returns `{ error: 'not-found' }` if
|
|
242
|
-
* `{ error: 'unparseable' }` if
|
|
243
|
-
*
|
|
231
|
+
* Returns `{ error: 'not-found' }` if failed/{taskId}.json doesn't exist, or
|
|
232
|
+
* `{ error: 'unparseable' }` if it isn't valid task JSON (tell the admin to
|
|
233
|
+
* delete it rather than retry).
|
|
244
234
|
*/
|
|
245
235
|
export async function requeueFailedTask(taskDir, taskId, logger = nullLogger) {
|
|
246
236
|
const failedPath = path.join(taskDir, 'failed', `${taskId}.json`);
|
|
@@ -272,9 +262,6 @@ export async function requeueFailedTask(taskDir, taskId, logger = nullLogger) {
|
|
|
272
262
|
logger.debug('Requeued failed task', { originalId: taskId, newTaskId });
|
|
273
263
|
return { newTaskId };
|
|
274
264
|
}
|
|
275
|
-
// ============================================================================
|
|
276
|
-
// Recovery & maintenance
|
|
277
|
-
// ============================================================================
|
|
278
265
|
/**
|
|
279
266
|
* Recover orphaned tasks stuck in processing/.
|
|
280
267
|
* Moves tasks whose file mtime is older than maxAgeMs back to pending/.
|
|
@@ -339,15 +326,10 @@ export async function recoverOrphanedTasks(taskDir, maxAgeMs = 5 * 60_000, logge
|
|
|
339
326
|
export async function cleanupOldTasks(taskDir, maxAgeMs = 30 * 24 * 60 * 60_000, logger = nullLogger) {
|
|
340
327
|
const now = Date.now();
|
|
341
328
|
let cleaned = 0;
|
|
342
|
-
// `corrupt`
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
// producer of malformed task JSON -- a partial write surviving a crash, a
|
|
347
|
-
// bad deploy writing schema-drifted tasks for a week -- accumulated files no
|
|
348
|
-
// automated path removed. Same stamp-based retention as the other two: a
|
|
349
|
-
// quarantined file older than the window has long since been triaged or
|
|
350
|
-
// forgotten.
|
|
329
|
+
// `corrupt` files (quarantined by dequeue and orphan recovery, shown in
|
|
330
|
+
// the admin listing) get the same stamp-based retention as completed and
|
|
331
|
+
// failed, because a quarantined file older than the window has been
|
|
332
|
+
// triaged or forgotten.
|
|
351
333
|
for (const subdir of ['completed', 'failed', 'corrupt']) {
|
|
352
334
|
const dir = path.join(taskDir, subdir);
|
|
353
335
|
let files;
|
|
@@ -376,9 +358,6 @@ export async function cleanupOldTasks(taskDir, maxAgeMs = 30 * 24 * 60 * 60_000,
|
|
|
376
358
|
}
|
|
377
359
|
return cleaned;
|
|
378
360
|
}
|
|
379
|
-
// ============================================================================
|
|
380
|
-
// Query operations (for status UIs, monitoring)
|
|
381
|
-
// ============================================================================
|
|
382
361
|
/**
|
|
383
362
|
* Get a specific task by ID. Searches all status directories.
|
|
384
363
|
*/
|
|
@@ -487,9 +466,6 @@ export async function listCorruptTaskFiles(taskDir, limit = 50, logger = nullLog
|
|
|
487
466
|
entries.sort((a, b) => new Date(b.mtime).getTime() - new Date(a.mtime).getTime());
|
|
488
467
|
return entries.slice(0, limit);
|
|
489
468
|
}
|
|
490
|
-
// ============================================================================
|
|
491
|
-
// Internal helpers
|
|
492
|
-
// ============================================================================
|
|
493
469
|
/** Read up to `maxBytes` from the start of a file, as utf-8 text. */
|
|
494
470
|
async function readSnippet(filePath, maxBytes) {
|
|
495
471
|
const handle = await fs.open(filePath, 'r');
|
|
@@ -31,7 +31,6 @@ export interface QueueStats {
|
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Optional logger interface.
|
|
34
|
-
* Pass your own logger to `createTaskQueue()`, or omit for silent operation.
|
|
35
34
|
*/
|
|
36
35
|
export interface TaskQueueLogger {
|
|
37
36
|
debug(message: string, data?: Record<string, unknown>): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worker-status.json: the CmsWorker daemon's self-reported liveness/health
|
|
3
|
+
* snapshot, written under the task queue directory
|
|
4
|
+
* (`{taskDir}/worker-status.json`) and read tolerantly by the admin API
|
|
5
|
+
* (api/admin.ts's `readWorkerStatus`). Wire shape: `WorkerStatusReport` in
|
|
6
|
+
* ../types.
|
|
7
|
+
*
|
|
8
|
+
* Single-writer by intent (only the lock-holding CmsWorker -- see
|
|
9
|
+
* cms-worker.ts's `acquireLock`, DEP-C2), but correctness does not rest on
|
|
10
|
+
* that: every write is a FULL regeneration landed by an atomic temp-file rename
|
|
11
|
+
* (utils/atomic-write.ts), never a read-modify-write, so whichever write lands
|
|
12
|
+
* last is one writer's complete, self-consistent snapshot. That covers the one
|
|
13
|
+
* window where two holders overlap -- after a lock compromise the old holder's
|
|
14
|
+
* `stop()` drains for up to `taskTimeoutMs` while a new holder is already
|
|
15
|
+
* running against the same workspace.
|
|
16
|
+
*
|
|
17
|
+
* Readers are stale-tolerant by design: this is a liveness signal, not
|
|
18
|
+
* authoritative state, and NFS/EFS attribute-cache staleness (see
|
|
19
|
+
* docs/concurrency.md) means a reader can see a snapshot a cache window old
|
|
20
|
+
* however carefully it was written.
|
|
21
|
+
*/
|
|
22
|
+
import type { WorkerStatusReport } from '../types.js';
|
|
23
|
+
export declare const WORKER_STATUS_FILE = "worker-status.json";
|
|
24
|
+
/**
|
|
25
|
+
* Write the worker's status report to `{taskDir}/worker-status.json`.
|
|
26
|
+
*
|
|
27
|
+
* Full-file regeneration, never a partial update: pass the complete report,
|
|
28
|
+
* not a delta. Stamps `updatedAt`; every other field is the caller's.
|
|
29
|
+
*
|
|
30
|
+
* Throws rather than swallowing. Callers that cannot let a status-write failure
|
|
31
|
+
* break their own operation wrap this at the call site (`syncGit`,
|
|
32
|
+
* `processTaskQueue`, `start()`); any that don't fall back on scheduleLoop's
|
|
33
|
+
* per-cycle catch, which logs and continues.
|
|
34
|
+
*/
|
|
35
|
+
export declare function writeWorkerStatus(taskDir: string, report: WorkerStatusReport): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worker-status.json: the CmsWorker daemon's self-reported liveness/health
|
|
3
|
+
* snapshot, written under the task queue directory
|
|
4
|
+
* (`{taskDir}/worker-status.json`) and read tolerantly by the admin API
|
|
5
|
+
* (api/admin.ts's `readWorkerStatus`). Wire shape: `WorkerStatusReport` in
|
|
6
|
+
* ../types.
|
|
7
|
+
*
|
|
8
|
+
* Single-writer by intent (only the lock-holding CmsWorker -- see
|
|
9
|
+
* cms-worker.ts's `acquireLock`, DEP-C2), but correctness does not rest on
|
|
10
|
+
* that: every write is a FULL regeneration landed by an atomic temp-file rename
|
|
11
|
+
* (utils/atomic-write.ts), never a read-modify-write, so whichever write lands
|
|
12
|
+
* last is one writer's complete, self-consistent snapshot. That covers the one
|
|
13
|
+
* window where two holders overlap -- after a lock compromise the old holder's
|
|
14
|
+
* `stop()` drains for up to `taskTimeoutMs` while a new holder is already
|
|
15
|
+
* running against the same workspace.
|
|
16
|
+
*
|
|
17
|
+
* Readers are stale-tolerant by design: this is a liveness signal, not
|
|
18
|
+
* authoritative state, and NFS/EFS attribute-cache staleness (see
|
|
19
|
+
* docs/concurrency.md) means a reader can see a snapshot a cache window old
|
|
20
|
+
* however carefully it was written.
|
|
21
|
+
*/
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { atomicWriteFile } from '../utils/atomic-write.js';
|
|
24
|
+
export const WORKER_STATUS_FILE = 'worker-status.json';
|
|
25
|
+
/**
|
|
26
|
+
* Write the worker's status report to `{taskDir}/worker-status.json`.
|
|
27
|
+
*
|
|
28
|
+
* Full-file regeneration, never a partial update: pass the complete report,
|
|
29
|
+
* not a delta. Stamps `updatedAt`; every other field is the caller's.
|
|
30
|
+
*
|
|
31
|
+
* Throws rather than swallowing. Callers that cannot let a status-write failure
|
|
32
|
+
* break their own operation wrap this at the call site (`syncGit`,
|
|
33
|
+
* `processTaskQueue`, `start()`); any that don't fall back on scheduleLoop's
|
|
34
|
+
* per-cycle catch, which logs and continues.
|
|
35
|
+
*/
|
|
36
|
+
export async function writeWorkerStatus(taskDir, report) {
|
|
37
|
+
const filePath = path.join(taskDir, WORKER_STATUS_FILE);
|
|
38
|
+
const stamped = { ...report, updatedAt: new Date().toISOString() };
|
|
39
|
+
await atomicWriteFile(filePath, JSON.stringify(stamped, null, 2));
|
|
40
|
+
}
|