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/types.d.ts
CHANGED
|
@@ -7,9 +7,9 @@ export type CanopyGroupId = string;
|
|
|
7
7
|
* rule is expressed.
|
|
8
8
|
*
|
|
9
9
|
* There is deliberately no separate 'locked' state: 'submitted' already means
|
|
10
|
-
* "locked while
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* "locked while the PR is under review", with request-changes/withdraw as the
|
|
11
|
+
* unlocks. A future admin-freeze feature should add a status only alongside real
|
|
12
|
+
* semantics (worker rebase skip list, a set/unset endpoint, UI).
|
|
13
13
|
*/
|
|
14
14
|
export type BranchStatus = 'editing' | 'submitted' | 'approved' | 'archived';
|
|
15
15
|
export type SyncStatus = 'synced' | 'pending-sync' | 'sync-failed';
|
|
@@ -40,15 +40,14 @@ export interface BranchMetadata {
|
|
|
40
40
|
/** ContentIds of entries where --theirs was applied during rebase; cleared on clean rebase */
|
|
41
41
|
conflictFiles?: ContentId[];
|
|
42
42
|
/**
|
|
43
|
-
* Lifecycle state of the branch's PR as last observed by the worker's
|
|
44
|
-
*
|
|
45
|
-
*
|
|
43
|
+
* Lifecycle state of the branch's PR as last observed by the worker's merge-poll
|
|
44
|
+
* (or verified by markAsMerged). Absent until a PR exists and has been observed.
|
|
45
|
+
* Draft PRs read 'open'.
|
|
46
46
|
*/
|
|
47
47
|
pullRequestState?: PullRequestState;
|
|
48
48
|
/**
|
|
49
|
-
* ISO timestamp stamped when the branch was archived because its PR
|
|
50
|
-
*
|
|
51
|
-
* archived any other way.
|
|
49
|
+
* ISO timestamp stamped when the branch was archived because its PR merged
|
|
50
|
+
* (worker auto-poll or markAsMerged). Absent for branches archived any other way.
|
|
52
51
|
*/
|
|
53
52
|
mergedAt?: string;
|
|
54
53
|
/**
|
|
@@ -62,30 +61,29 @@ export interface BranchMetadata {
|
|
|
62
61
|
lastAt: string;
|
|
63
62
|
};
|
|
64
63
|
/**
|
|
65
|
-
* Set by the worker's rebase loop when it rewrote history this deployment
|
|
66
|
-
*
|
|
67
|
-
*
|
|
64
|
+
* Set by the worker's rebase loop when it rewrote history this deployment had
|
|
65
|
+
* ALREADY published: the commit `remote.git` (and therefore GitHub) held for
|
|
66
|
+
* this branch, which the rebase replaced.
|
|
68
67
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
68
|
+
* It is the `--force-with-lease` expected value on both hops, so a forced push
|
|
69
|
+
* can only move a ref off the exact commit our own rebase rewrote away, never
|
|
70
|
+
* over anyone else's work.
|
|
72
71
|
*
|
|
73
|
-
* Set once per rewrite episode and never advanced while still set: across
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* (CmsWorker.pushBranchToGitHub)
|
|
78
|
-
*
|
|
72
|
+
* Set once per rewrite episode and never advanced while still set: across two
|
|
73
|
+
* rebases before any push lands GitHub still holds the ORIGINAL commit, so
|
|
74
|
+
* advancing the marker would aim the lease at a commit GitHub never had.
|
|
75
|
+
* Cleared only once GitHub is confirmed to hold something else
|
|
76
|
+
* (CmsWorker.pushBranchToGitHub); while set, it is the sole trigger for the
|
|
77
|
+
* rebase loop's self-heal pass.
|
|
79
78
|
*/
|
|
80
79
|
historyRewrittenFrom?: string;
|
|
81
80
|
/**
|
|
82
|
-
* Short, sanitized reason the worker's last GitHub sync task failed
|
|
83
|
-
*
|
|
84
|
-
* CmsWorker.updateBranchMetadataOnFailure
|
|
85
|
-
* rejection naming the branch. Absent until a task has failed permanently
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* later successful push.
|
|
81
|
+
* Short, sanitized reason the worker's last GitHub sync task failed permanently,
|
|
82
|
+
* set alongside `syncStatus: 'sync-failed'` by
|
|
83
|
+
* CmsWorker.updateBranchMetadataOnFailure -- e.g. a non-fast-forward push
|
|
84
|
+
* rejection naming the branch. Absent until a task has failed permanently, and
|
|
85
|
+
* reset to undefined by the next successful sync task, so a stale reason never
|
|
86
|
+
* survives a later successful push.
|
|
89
87
|
*/
|
|
90
88
|
syncFailureReason?: string;
|
|
91
89
|
}
|
|
@@ -105,9 +103,9 @@ export interface BranchContextWithSchema extends BranchContext {
|
|
|
105
103
|
flatSchema: import('./config/index.js').FlatSchemaItem[];
|
|
106
104
|
}
|
|
107
105
|
/**
|
|
108
|
-
* Wire shape of the worker's self-reported status file (worker-status.json,
|
|
109
|
-
*
|
|
110
|
-
*
|
|
106
|
+
* Wire shape of the worker's self-reported status file (worker-status.json, under
|
|
107
|
+
* the task queue dir), written by the CmsWorker daemon. Read-only here: GET
|
|
108
|
+
* /admin/status parses it as-is.
|
|
111
109
|
*/
|
|
112
110
|
export interface WorkerStatusReport {
|
|
113
111
|
version: 1;
|
|
@@ -126,12 +124,9 @@ export interface WorkerStatusReport {
|
|
|
126
124
|
skippedDirty: string[];
|
|
127
125
|
/**
|
|
128
126
|
* [SYNC-C1] Branches skipped because a content write held the branch's
|
|
129
|
-
* cross-host content-write lock (utils/content-write-lock.ts)
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* Optional for the same reason as `tracked` below: absent from a
|
|
133
|
-
* worker-status.json written by a worker predating that lock. Readers must
|
|
134
|
-
* tolerate its absence.
|
|
127
|
+
* cross-host content-write lock (utils/content-write-lock.ts); the worker
|
|
128
|
+
* yields and retries next cycle. Optional for the same reason as `tracked`
|
|
129
|
+
* below, and readers must tolerate its absence.
|
|
135
130
|
*/
|
|
136
131
|
skippedLocked?: string[];
|
|
137
132
|
failed: {
|
|
@@ -139,15 +134,11 @@ export interface WorkerStatusReport {
|
|
|
139
134
|
error: string;
|
|
140
135
|
}[];
|
|
141
136
|
/**
|
|
142
|
-
* Outcome of reconciling remote.git's `refs/heads/*` against GitHub's
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Optional: absent from a worker-status.json written by a worker that
|
|
149
|
-
* predates the tracked-branch reconcile (PR #168). Readers must tolerate
|
|
150
|
-
* its absence rather than assume every status file has it.
|
|
137
|
+
* Outcome of reconciling remote.git's `refs/heads/*` against GitHub's fetched
|
|
138
|
+
* tips, non-destructively (worker/git-sync.ts's `reconcileTrackedBranches`).
|
|
139
|
+
* Optional: a status file written by a worker without that reconcile has no
|
|
140
|
+
* such key, and readers must tolerate its absence rather than assume every
|
|
141
|
+
* status file has it.
|
|
151
142
|
*/
|
|
152
143
|
tracked?: {
|
|
153
144
|
created: string[];
|
|
@@ -155,11 +146,9 @@ export interface WorkerStatusReport {
|
|
|
155
146
|
ahead: string[];
|
|
156
147
|
diverged: string[];
|
|
157
148
|
/**
|
|
158
|
-
* Optional for the same reason as `tracked` itself
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* rebased them and the GitHub push is still queued -- expected and
|
|
162
|
-
* self-resolving, unlike `diverged`.
|
|
149
|
+
* Optional for the same reason as `tracked` itself. Branches here diverged
|
|
150
|
+
* from GitHub because THIS worker rebased them and the GitHub push is still
|
|
151
|
+
* queued -- expected and self-resolving, unlike `diverged`.
|
|
163
152
|
*/
|
|
164
153
|
rewritten?: string[];
|
|
165
154
|
};
|
package/dist/url-collision.d.ts
CHANGED
|
@@ -3,48 +3,36 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The build-time half (`assertNoDuplicateUrlPaths`, static/index.ts) detects the state; this
|
|
5
5
|
* refuses to create it. Neither replaces the other: content is git-backed, so it also arrives by
|
|
6
|
-
* merge,
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* changing a format changes the listing's `validExts`, which can flip a previously-unlisted
|
|
12
|
-
* `guide.index.{id}.json` into a published entry and so into a contested pair. Another reason
|
|
13
|
-
* neither half of this invariant subsumes the other.
|
|
6
|
+
* merge, PR, direct commit and retrofit of an existing repo — none of which pass this boundary —
|
|
7
|
+
* while the build guard cannot help an editor about to author the collision right now. A SCHEMA
|
|
8
|
+
* edit alone can create one with no content write at all: `updateEntryType` changing a format
|
|
9
|
+
* changes the listing's `validExts`, which can turn a `guide.index.{id}.json` the listing skipped
|
|
10
|
+
* into a published entry, and so into a contested pair.
|
|
14
11
|
*
|
|
15
12
|
* Deliberately formulated on `urlPath`, not on names. An entry whose slug matches a sibling
|
|
16
13
|
* collection is only a problem when that collection ALSO has an index entry:
|
|
17
14
|
*
|
|
18
|
-
* content/docs/page.guides.{id}.json + content/docs/guides.{id}/ (no index) -> fine
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* -> contested. Both compute /docs/guides, so exactly one of them is reachable and the
|
|
23
|
-
* other silently has no route.
|
|
15
|
+
* content/docs/page.guides.{id}.json + content/docs/guides.{id}/ (no index) -> fine: a
|
|
16
|
+
* landing page plus a folder of children, both reachable, nothing contested.
|
|
17
|
+
* content/docs/page.guides.{id}.json + content/docs/guides.{id}/doc.index.{id}.md -> contested:
|
|
18
|
+
* both compute /docs/guides, so exactly one is reachable and the other has no route.
|
|
24
19
|
*
|
|
25
|
-
* A name-collision rule would forbid the first,
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* it needs the answer, and because the on-disk names carry the content IDs that logical paths
|
|
29
|
-
* hide (`guides.{id}/`, `doc.index.{id}.md`).
|
|
20
|
+
* A name-collision rule would forbid the first, a shape adopters legitimately build. Everything
|
|
21
|
+
* here works on PHYSICAL directories, because that is what the write path holds when it needs the
|
|
22
|
+
* answer, and the on-disk names carry the content IDs logical paths hide (`guides.{id}/`).
|
|
30
23
|
*
|
|
31
24
|
* SCOPE, and do not widen it by accident: this guards the URL an ENTRY claims -- the `urlPath`
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* `canopycms-next/src/static.ts`, does exactly that). Those URLs are
|
|
37
|
-
* invisible here by design -- there is no filesystem state to check them against at write time.
|
|
38
|
-
* A future change that tries to make this the authority on "the set of URLs this site claims"
|
|
39
|
-
* would be reaching past what the write boundary can actually see.
|
|
25
|
+
* `computeEntryUrl` derives and `listEntries` publishes -- not "every URL the site serves".
|
|
26
|
+
* Hand-written adopter routes, and a sitemap `<loc>` a framework adapter rewrites after
|
|
27
|
+
* enumeration (`generateContentSitemap`'s `pathFor`), are invisible here by design: at write time
|
|
28
|
+
* there is no filesystem state to check them against.
|
|
40
29
|
*/
|
|
41
30
|
/** One entry that already claims the `urlPath` a pending write would produce. */
|
|
42
31
|
export interface UrlPathClaimant {
|
|
43
32
|
/**
|
|
44
|
-
* Which of the two shapes was found. Callers word their own message from this
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* contested.
|
|
33
|
+
* Which of the two shapes was found. Callers word their own message from this: for
|
|
34
|
+
* `sibling-collection-index` the author should rename their new entry, for `parent-entry` they
|
|
35
|
+
* are adding the index entry that makes an existing pair contested.
|
|
48
36
|
*/
|
|
49
37
|
kind: 'sibling-collection-index' | 'parent-entry';
|
|
50
38
|
/** Absolute path of the file already claiming that URL. */
|
|
@@ -53,11 +41,10 @@ export interface UrlPathClaimant {
|
|
|
53
41
|
name: string;
|
|
54
42
|
}
|
|
55
43
|
/**
|
|
56
|
-
* The index entry file directly inside `dir`, or null.
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* which is deliberately cross-type because the URL does not care what type serves it.
|
|
44
|
+
* The index entry file directly inside `dir`, or null. Slug extraction is entry-type-agnostic
|
|
45
|
+
* (each candidate's type is read out of its own filename), so an index entry of ANY type counts —
|
|
46
|
+
* like the same-slug guard, deliberately cross-type because the URL does not care what type
|
|
47
|
+
* serves it.
|
|
61
48
|
*/
|
|
62
49
|
export declare function findIndexEntryIn(dir: string): Promise<string | null>;
|
|
63
50
|
/** The entry file directly inside `dir` whose slug is `slug`, or null. */
|
|
@@ -69,11 +56,10 @@ export declare function findEntryBySlugIn(dir: string, slug: string): Promise<st
|
|
|
69
56
|
* Only the cross-collection shapes are checked here. An entry colliding with another entry in the
|
|
70
57
|
* SAME collection is already refused upstream (`ContentStore.buildPaths` resolves a write by a
|
|
71
58
|
* type-agnostic slug scan, and the `expectedVersion: null` create-intent guard turns that into a
|
|
72
|
-
* conflict), so re-checking it would be
|
|
59
|
+
* conflict), so re-checking it would be a second source of truth.
|
|
73
60
|
*
|
|
74
|
-
* Two directory reads at worst, and only on create/rename — an ordinary save
|
|
75
|
-
*
|
|
76
|
-
* the write.
|
|
61
|
+
* Two directory reads at worst, and only on create/rename — an ordinary save never calls this,
|
|
62
|
+
* because the entry's URL is not changing and any collision predates the write.
|
|
77
63
|
*
|
|
78
64
|
* @param contentRoot Absolute path of the content root. A root-level index entry claims `/`, which
|
|
79
65
|
* nothing above it can contest, so the `parent-entry` check stops here rather than walking out
|
package/dist/url-collision.js
CHANGED
|
@@ -8,18 +8,16 @@ import { parseTypedFilename } from './utils/typed-filename.js';
|
|
|
8
8
|
* Every extension `getFormatExtension` can return.
|
|
9
9
|
*
|
|
10
10
|
* Compared CASE-SENSITIVELY, deliberately, because `listCollectionEntries` compares
|
|
11
|
-
* case-sensitively (`d.name.endsWith(ext)` against lowercase extensions). Lowercasing here
|
|
12
|
-
* the guard looser than the listing: a `doc.index.{id}.MD` file
|
|
13
|
-
* listing
|
|
14
|
-
* the build would have accepted.
|
|
11
|
+
* case-sensitively (`d.name.endsWith(ext)` against lowercase extensions). Lowercasing here makes
|
|
12
|
+
* the guard looser than the listing: a `doc.index.{id}.MD` file would count as a claimant while
|
|
13
|
+
* the listing skips it silently — publishing no URL, tripping no build error, and blocking a
|
|
14
|
+
* write the build would have accepted.
|
|
15
15
|
*
|
|
16
|
-
* KNOWN RESIDUAL LOOSENESS
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* call sites, and going from a physical directory back to a schema item to do it — a change with
|
|
22
|
-
* more room to introduce a new defect than the narrow one it fixes. Tracked in
|
|
16
|
+
* KNOWN RESIDUAL LOOSENESS: the listing accepts only the extensions of a collection's OWN
|
|
17
|
+
* configured entry-type formats, while this accepts all four, so a hand-authored
|
|
18
|
+
* `doc.index.{id}.json` inside an md-only collection claims no URL yet still blocks a sibling
|
|
19
|
+
* write. Closing it means threading each collection's configured formats into a deliberately
|
|
20
|
+
* schema-free module; tracked in
|
|
23
21
|
* .claude/future-tasks/url-collision-guard-superset-of-listing.md.
|
|
24
22
|
*
|
|
25
23
|
* A literal rather than derived: deriving it needs the `ContentFormat` union this module has no
|
|
@@ -55,53 +53,44 @@ async function findChildCollectionDir(dir, name) {
|
|
|
55
53
|
*
|
|
56
54
|
* This must recognise the set `listEntries` recognises, as closely as a schema-free module can,
|
|
57
55
|
* because the invariant is defined over the `urlPath`s `listEntries` publishes and the build-time
|
|
58
|
-
* half checks those.
|
|
59
|
-
* `
|
|
60
|
-
*
|
|
61
|
-
* ("what would I call this file?") and happily names a slug for things that are not entries.
|
|
56
|
+
* half checks those. Hence `parseTypedFilename` — the same `{type}.{slug}.{id}.{ext}` grammar
|
|
57
|
+
* `listCollectionEntries` uses — rather than `extractSlugFromFilename`, which answers the much
|
|
58
|
+
* looser "what would I call this file?" and happily names a slug for things that are not entries.
|
|
62
59
|
*
|
|
63
60
|
* Getting this wrong OVER-BLOCKS, which is worse here than under-blocking: a hand-authored
|
|
64
|
-
* `index.md` with no content ID, an `index.md~` editor backup
|
|
65
|
-
* non-entries that claim no URL, so the build guard ignores them — but a looser scan
|
|
66
|
-
* as claimants and
|
|
67
|
-
* index entry" about a file Canopy does not consider an entry.
|
|
68
|
-
* retrofitted onto CanopyCMS, which is precisely the audience this guard's migration note
|
|
69
|
-
* addresses.
|
|
61
|
+
* `index.md` with no content ID, an `index.md~` editor backup or a colocated `guides.png` are
|
|
62
|
+
* non-entries that claim no URL, so the build guard ignores them — but a looser scan counts them
|
|
63
|
+
* as claimants and refuses a legitimate write, telling the author to "remove that collection's
|
|
64
|
+
* index entry" about a file Canopy does not consider an entry. Worst for retrofitted repos.
|
|
70
65
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
66
|
+
* The claimant set is nonetheless a strict superset of the listing's in three ways, all
|
|
67
|
+
* over-blocking and all needing the same fix — the schema this module deliberately does not have
|
|
68
|
+
* — so they are one task (.claude/future-tasks/url-collision-guard-superset-of-listing.md):
|
|
74
69
|
*
|
|
75
70
|
* 1. extension — see `CONTENT_EXTENSIONS` above;
|
|
76
|
-
* 2. entry TYPE — a file whose type is not in
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* `looksLikeMalformedEntry` itself names as most common: renaming an entry type in
|
|
82
|
-
* `.collection.json` without renaming the files;
|
|
71
|
+
* 2. entry TYPE — parsing is structural (no `entryTypes` list), so a file whose type is not in
|
|
72
|
+
* the collection's config is skipped by the listing but counted here. In the editor, where
|
|
73
|
+
* this guard runs, the listing skips such a file with a debug-gated warning and nothing
|
|
74
|
+
* surfaces; only a BUILD reports it. Reached by renaming an entry type in `.collection.json`
|
|
75
|
+
* without renaming the files — the accident `looksLikeMalformedEntry` names as most common;
|
|
83
76
|
* 3. collection-hood — `findChildCollectionDir` matches any child directory by name, but a
|
|
84
77
|
* directory with no `.collection.json` is not a collection, so nothing inside it publishes
|
|
85
78
|
* a URL. Narrow (the file inside still needs valid entry grammar) but the same direction.
|
|
86
|
-
*
|
|
87
|
-
* All three need the same fix — the schema this module deliberately does not have — so they are
|
|
88
|
-
* one task, not three.
|
|
89
79
|
*/
|
|
90
80
|
function entrySlugOf(filename) {
|
|
91
81
|
// `parseTypedFilename` strips the extension without checking it, so an editor backup
|
|
92
82
|
// (`doc.index.{id}.md~`) parses exactly like the entry it shadows. `listCollectionEntries`
|
|
93
|
-
// additionally requires a
|
|
94
|
-
//
|
|
83
|
+
// additionally requires a configured format extension, so this must skip those too, or the
|
|
84
|
+
// guard refuses a write over a file the build guard never counted.
|
|
95
85
|
if (!CONTENT_EXTENSIONS.some((ext) => filename.endsWith(ext)))
|
|
96
86
|
return null;
|
|
97
87
|
return parseTypedFilename(filename)?.slug ?? null;
|
|
98
88
|
}
|
|
99
89
|
/**
|
|
100
|
-
* The index entry file directly inside `dir`, or null.
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* which is deliberately cross-type because the URL does not care what type serves it.
|
|
90
|
+
* The index entry file directly inside `dir`, or null. Slug extraction is entry-type-agnostic
|
|
91
|
+
* (each candidate's type is read out of its own filename), so an index entry of ANY type counts —
|
|
92
|
+
* like the same-slug guard, deliberately cross-type because the URL does not care what type
|
|
93
|
+
* serves it.
|
|
105
94
|
*/
|
|
106
95
|
export async function findIndexEntryIn(dir) {
|
|
107
96
|
for (const entry of await readDirSafe(dir)) {
|
|
@@ -130,11 +119,10 @@ export async function findEntryBySlugIn(dir, slug) {
|
|
|
130
119
|
* Only the cross-collection shapes are checked here. An entry colliding with another entry in the
|
|
131
120
|
* SAME collection is already refused upstream (`ContentStore.buildPaths` resolves a write by a
|
|
132
121
|
* type-agnostic slug scan, and the `expectedVersion: null` create-intent guard turns that into a
|
|
133
|
-
* conflict), so re-checking it would be
|
|
122
|
+
* conflict), so re-checking it would be a second source of truth.
|
|
134
123
|
*
|
|
135
|
-
* Two directory reads at worst, and only on create/rename — an ordinary save
|
|
136
|
-
*
|
|
137
|
-
* the write.
|
|
124
|
+
* Two directory reads at worst, and only on create/rename — an ordinary save never calls this,
|
|
125
|
+
* because the entry's URL is not changing and any collision predates the write.
|
|
138
126
|
*
|
|
139
127
|
* @param contentRoot Absolute path of the content root. A root-level index entry claims `/`, which
|
|
140
128
|
* nothing above it can contest, so the `parent-entry` check stops here rather than walking out
|
|
@@ -150,16 +138,13 @@ export async function findUrlPathClaimant(opts) {
|
|
|
150
138
|
return null; // a root index claims '/', uncontestable
|
|
151
139
|
const parentDir = path.dirname(collectionDir);
|
|
152
140
|
const ownName = extractSlugFromFilename(path.basename(collectionDir));
|
|
153
|
-
// A collection literally NAMED "index" collapses onto ITS OWN path (`<parentPath>/index`),
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
// "index" could never have a landing page under any parent that has one of its own --
|
|
161
|
-
// ironic, since `resolveUrlPathCandidates`' candidate 2 was deliberately engineered to keep a
|
|
162
|
-
// collection named "index" addressable in the first place.
|
|
141
|
+
// A collection literally NAMED "index" collapses onto ITS OWN path (`<parentPath>/index`), not
|
|
142
|
+
// onto `<parentPath>` -- see `computeEntryUrl`. The only entry that could contest it is a
|
|
143
|
+
// parent entry whose slug is also "index", but by that same forward rule such an entry IS the
|
|
144
|
+
// parent's own landing entry, at `<parentPath>`, a different URL. Nothing in the parent can
|
|
145
|
+
// claim `<parentPath>/index`, so skip the lookup rather than let it match the parent's index
|
|
146
|
+
// entry and report a collision that does not exist -- otherwise a collection named "index"
|
|
147
|
+
// could never have a landing page under a parent that has one of its own.
|
|
163
148
|
if (isIndexSlug(ownName))
|
|
164
149
|
return null;
|
|
165
150
|
const claimant = await findEntryBySlugIn(parentDir, ownName);
|
|
@@ -168,14 +153,13 @@ export async function findUrlPathClaimant(opts) {
|
|
|
168
153
|
// A plain entry sits at the same URL as a same-named child collection's index entry.
|
|
169
154
|
//
|
|
170
155
|
// KNOWN DEPENDENCY: this resolves ONE child collection by name, and the index direction above
|
|
171
|
-
// likewise looks only for a parent ENTRY. Neither considers a second collection sharing the
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
// tidy-up.
|
|
156
|
+
// likewise looks only for a parent ENTRY. Neither considers a second collection sharing the same
|
|
157
|
+
// name, because nothing stops one being created -- `createCollectionInner` does not check
|
|
158
|
+
// sibling names (tracked in collection-sibling-name-uniqueness.md). With two `guides.{id}`
|
|
159
|
+
// directories side by side, an index entry can be written into each: both writes pass this
|
|
160
|
+
// guard, two entries claim one URL, and only the build guard catches it. Fixing sibling-name
|
|
161
|
+
// uniqueness closes this without changing anything here, so that task is a prerequisite for
|
|
162
|
+
// this guard being complete.
|
|
179
163
|
const childDir = await findChildCollectionDir(collectionDir, slug);
|
|
180
164
|
if (!childDir)
|
|
181
165
|
return null;
|
|
@@ -2,22 +2,18 @@
|
|
|
2
2
|
* The one-URL-per-entry invariant, as a reusable probe + report.
|
|
3
3
|
*
|
|
4
4
|
* `listEntries` assigns every entry exactly one `urlPath` and documents the round trip through
|
|
5
|
-
* `readByUrlPath` as safe. The reverse direction
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `.claude/future-tasks/resolved/url-resolver-index-entry-extra-url.md`, then
|
|
9
|
-
* `resolved/readbyurlpath-entry-type-candidate-phantom-url.md`). This module exists so the whole
|
|
10
|
-
* invariant is asserted at once instead: enumerate, then probe every ADJACENT URL the resolver
|
|
11
|
-
* would try and require it to be a miss.
|
|
5
|
+
* `readByUrlPath` as safe. The reverse direction is the looser of the two and can answer at URLs
|
|
6
|
+
* no forward surface emits, so this module asserts the whole invariant at once: enumerate, then
|
|
7
|
+
* probe every ADJACENT URL the resolver would try and require it to be a miss.
|
|
12
8
|
*
|
|
13
|
-
* Deliberately free of `vitest` --
|
|
9
|
+
* Deliberately free of `vitest` -- a plain `src` module in the shape of
|
|
14
10
|
* `operating-mode/deployment-name-fixtures.ts`, so more than one test file can import it without
|
|
15
11
|
* dragging that file's `vi.mock` calls and `describe` blocks along. It therefore ASSERTS NOTHING:
|
|
16
12
|
* it returns a report and the caller does the expecting, which also puts the offending URLs in
|
|
17
13
|
* the assertion message rather than behind a boolean.
|
|
18
14
|
*
|
|
19
15
|
* Scope of the invariant it checks: entries written in the `{type}.{slug}.{id}.{ext}` grammar
|
|
20
|
-
* with a type their collection declares --
|
|
16
|
+
* with a type their collection declares -- exactly the set `listEntries` can see. A legacy
|
|
21
17
|
* untyped file (`overview.json`) is invisible to enumeration and is deliberately NOT probed here;
|
|
22
18
|
* see `.claude/future-tasks/legacy-untyped-files-url-addressable.md`.
|
|
23
19
|
*/
|
|
@@ -26,9 +22,8 @@ import type { ListEntriesItem } from './content-listing.js';
|
|
|
26
22
|
import type { CanopyContext } from './context.js';
|
|
27
23
|
import { type DuplicateUrlPath } from './static/index.js';
|
|
28
24
|
/**
|
|
29
|
-
* Every URL adjacent to the published set that the resolver would actually attempt
|
|
30
|
-
*
|
|
31
|
-
* Three families, each one a shape that HAS resolved at some point in this package's history:
|
|
25
|
+
* Every URL adjacent to the published set that the resolver would actually attempt, in three
|
|
26
|
+
* families:
|
|
32
27
|
*
|
|
33
28
|
* 1. `/<collection>/<entryTypeName>` -- `resolveUrlPathCandidates`' index-fallback candidate lands
|
|
34
29
|
* on a registered entry-TYPE schema item, which `ContentStore.buildPaths` delegates to the
|
|
@@ -40,16 +35,16 @@ import { type DuplicateUrlPath } from './static/index.js';
|
|
|
40
35
|
* spelling its collapsed URL replaced.
|
|
41
36
|
*
|
|
42
37
|
* Entry-type names are appended VERBATIM, after `computeEntryUrl` has lowercased the collection
|
|
43
|
-
* part. That asymmetry is real
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
38
|
+
* part. That asymmetry is real: `flattenSchema` puts `entryType.name` into the logical path
|
|
39
|
+
* unchanged and `normalizeFilesystemPath` does not lowercase, so only the declared spelling can
|
|
40
|
+
* reach the entry-type schema item at all. A lowercased probe would miss the schema item, return
|
|
41
|
+
* null for the wrong reason, and pass vacuously.
|
|
47
42
|
*/
|
|
48
43
|
export declare const buildProbeUrls: (schema: RootCollectionConfig, items: readonly ListEntriesItem[], contentRoot?: string) => string[];
|
|
49
44
|
export interface UrlExclusivityReport {
|
|
50
45
|
/** Every `urlPath` the listing published, sorted. */
|
|
51
46
|
published: string[];
|
|
52
|
-
/** URLs claimed by more than one entry. A precondition failure, not a resolver
|
|
47
|
+
/** URLs claimed by more than one entry. A precondition failure, not a resolver result. */
|
|
53
48
|
duplicates: DuplicateUrlPath[];
|
|
54
49
|
/** Published URLs `readByUrlPath` did NOT resolve -- the round trip broken going forward. */
|
|
55
50
|
unresolved: string[];
|
|
@@ -65,12 +60,12 @@ export interface UrlExclusivityReport {
|
|
|
65
60
|
phantoms: string[];
|
|
66
61
|
}
|
|
67
62
|
/**
|
|
68
|
-
* Enumerate, round-trip, then probe. See `UrlExclusivityReport` for
|
|
63
|
+
* Enumerate, round-trip, then probe. See `UrlExclusivityReport` for the fields and
|
|
69
64
|
* `buildProbeUrls` for which URLs are probed.
|
|
70
65
|
*
|
|
71
66
|
* A probe whose LOWERCASED form is published is skipped rather than asserted on: a collection
|
|
72
67
|
* literally named `index` publishes `/docs/index`, which family 3 also generates, and whether
|
|
73
|
-
* `/docs/Index` should resolve is a separate
|
|
74
|
-
*
|
|
68
|
+
* `/docs/Index` should resolve is a separate question about collection-path case sensitivity that
|
|
69
|
+
* this invariant does not speak to.
|
|
75
70
|
*/
|
|
76
71
|
export declare const collectUrlExclusivityReport: (ctx: Pick<CanopyContext, "listEntries" | "readByUrlPath">, schema: RootCollectionConfig, contentRoot?: string) => Promise<UrlExclusivityReport>;
|
|
@@ -2,22 +2,18 @@
|
|
|
2
2
|
* The one-URL-per-entry invariant, as a reusable probe + report.
|
|
3
3
|
*
|
|
4
4
|
* `listEntries` assigns every entry exactly one `urlPath` and documents the round trip through
|
|
5
|
-
* `readByUrlPath` as safe. The reverse direction
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `.claude/future-tasks/resolved/url-resolver-index-entry-extra-url.md`, then
|
|
9
|
-
* `resolved/readbyurlpath-entry-type-candidate-phantom-url.md`). This module exists so the whole
|
|
10
|
-
* invariant is asserted at once instead: enumerate, then probe every ADJACENT URL the resolver
|
|
11
|
-
* would try and require it to be a miss.
|
|
5
|
+
* `readByUrlPath` as safe. The reverse direction is the looser of the two and can answer at URLs
|
|
6
|
+
* no forward surface emits, so this module asserts the whole invariant at once: enumerate, then
|
|
7
|
+
* probe every ADJACENT URL the resolver would try and require it to be a miss.
|
|
12
8
|
*
|
|
13
|
-
* Deliberately free of `vitest` --
|
|
9
|
+
* Deliberately free of `vitest` -- a plain `src` module in the shape of
|
|
14
10
|
* `operating-mode/deployment-name-fixtures.ts`, so more than one test file can import it without
|
|
15
11
|
* dragging that file's `vi.mock` calls and `describe` blocks along. It therefore ASSERTS NOTHING:
|
|
16
12
|
* it returns a report and the caller does the expecting, which also puts the offending URLs in
|
|
17
13
|
* the assertion message rather than behind a boolean.
|
|
18
14
|
*
|
|
19
15
|
* Scope of the invariant it checks: entries written in the `{type}.{slug}.{id}.{ext}` grammar
|
|
20
|
-
* with a type their collection declares --
|
|
16
|
+
* with a type their collection declares -- exactly the set `listEntries` can see. A legacy
|
|
21
17
|
* untyped file (`overview.json`) is invisible to enumeration and is deliberately NOT probed here;
|
|
22
18
|
* see `.claude/future-tasks/legacy-untyped-files-url-addressable.md`.
|
|
23
19
|
*/
|
|
@@ -27,9 +23,8 @@ import { computeEntryUrl } from './utils/entry-url.js';
|
|
|
27
23
|
/** Append raw segments to a URL base, without normalizing their case (see buildProbeUrls). */
|
|
28
24
|
const joinUrl = (base, ...segments) => (base === '/' ? '' : base) + segments.map((s) => `/${s}`).join('');
|
|
29
25
|
/**
|
|
30
|
-
* Every URL adjacent to the published set that the resolver would actually attempt
|
|
31
|
-
*
|
|
32
|
-
* Three families, each one a shape that HAS resolved at some point in this package's history:
|
|
26
|
+
* Every URL adjacent to the published set that the resolver would actually attempt, in three
|
|
27
|
+
* families:
|
|
33
28
|
*
|
|
34
29
|
* 1. `/<collection>/<entryTypeName>` -- `resolveUrlPathCandidates`' index-fallback candidate lands
|
|
35
30
|
* on a registered entry-TYPE schema item, which `ContentStore.buildPaths` delegates to the
|
|
@@ -41,10 +36,10 @@ const joinUrl = (base, ...segments) => (base === '/' ? '' : base) + segments.map
|
|
|
41
36
|
* spelling its collapsed URL replaced.
|
|
42
37
|
*
|
|
43
38
|
* Entry-type names are appended VERBATIM, after `computeEntryUrl` has lowercased the collection
|
|
44
|
-
* part. That asymmetry is real
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
39
|
+
* part. That asymmetry is real: `flattenSchema` puts `entryType.name` into the logical path
|
|
40
|
+
* unchanged and `normalizeFilesystemPath` does not lowercase, so only the declared spelling can
|
|
41
|
+
* reach the entry-type schema item at all. A lowercased probe would miss the schema item, return
|
|
42
|
+
* null for the wrong reason, and pass vacuously.
|
|
48
43
|
*/
|
|
49
44
|
export const buildProbeUrls = (schema, items, contentRoot = 'content') => {
|
|
50
45
|
const collections = flattenSchema(schema, contentRoot).filter((i) => i.type === 'collection');
|
|
@@ -73,13 +68,13 @@ export const buildProbeUrls = (schema, items, contentRoot = 'content') => {
|
|
|
73
68
|
return [...probes];
|
|
74
69
|
};
|
|
75
70
|
/**
|
|
76
|
-
* Enumerate, round-trip, then probe. See `UrlExclusivityReport` for
|
|
71
|
+
* Enumerate, round-trip, then probe. See `UrlExclusivityReport` for the fields and
|
|
77
72
|
* `buildProbeUrls` for which URLs are probed.
|
|
78
73
|
*
|
|
79
74
|
* A probe whose LOWERCASED form is published is skipped rather than asserted on: a collection
|
|
80
75
|
* literally named `index` publishes `/docs/index`, which family 3 also generates, and whether
|
|
81
|
-
* `/docs/Index` should resolve is a separate
|
|
82
|
-
*
|
|
76
|
+
* `/docs/Index` should resolve is a separate question about collection-path case sensitivity that
|
|
77
|
+
* this invariant does not speak to.
|
|
83
78
|
*/
|
|
84
79
|
export const collectUrlExclusivityReport = async (ctx, schema, contentRoot = 'content') => {
|
|
85
80
|
const items = await ctx.listEntries();
|
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Resolve a URL path to the candidate entryPath/slug pairs a lookup should try, in order:
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 2. Index fallback: full path is collection, slug is 'index'
|
|
4
|
+
* 1. Direct entry: last segment is the slug, the rest is the collection path — SKIPPED when that
|
|
5
|
+
* slug is an index slug (see below).
|
|
6
|
+
* 2. Index fallback: the whole path is a collection and the slug is 'index'.
|
|
8
7
|
*
|
|
9
|
-
* This is the REVERSE of the forward collection+slug -> url rule
|
|
10
|
-
* many URLs an entry answers at. The forward rule
|
|
8
|
+
* This is the REVERSE of the forward collection+slug -> url rule and must agree with it about how
|
|
9
|
+
* many URLs an entry answers at. The forward rule — `computeEntryUrl` (utils/entry-url.ts), which
|
|
11
10
|
* `listEntries` publishes as `item.urlPath` and reference resolution stamps on every resolved
|
|
12
|
-
* reference
|
|
13
|
-
* onto its collection's path and never emits a trailing `/index`.
|
|
14
|
-
* anyway, so an index entry ALSO answered at a `.../index` URL that no forward surface publishes
|
|
15
|
-
* — enumeration and resolution disagreeing about how many URLs exist. Adopters paid for that with
|
|
16
|
-
* per-route entryType gates whose only job was to reject the phantom.
|
|
17
|
-
*
|
|
18
|
-
* @param urlPath - URL path like '/docs/guides/getting-started' or 'docs/guides'
|
|
19
|
-
* @param contentRoot - Content root directory name (default: 'content')
|
|
20
|
-
* @returns Array of { entryPath, slug } candidates to try in order
|
|
11
|
+
* reference, plus `defaultBuildPath` (content-tree.ts) for tree nodes — collapses an `index` slug
|
|
12
|
+
* onto its collection's path and never emits a trailing `/index`.
|
|
21
13
|
*/
|
|
22
14
|
export declare function resolveUrlPathCandidates(urlPath: string, contentRoot: string): Array<{
|
|
23
15
|
entryPath: string;
|