canopycms 0.0.62 → 0.0.64
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 +153 -19
- package/dist/ai/generate.d.ts +4 -5
- package/dist/ai/generate.js +0 -1
- package/dist/ai/handler.d.ts +3 -4
- package/dist/ai/handler.js +0 -1
- package/dist/ai/index.d.ts +15 -7
- package/dist/ai/index.js +9 -1
- package/dist/ai/json-to-markdown.d.ts +1 -2
- package/dist/ai/json-to-markdown.js +11 -2
- package/dist/ai/resolve-branch.d.ts +1 -2
- package/dist/ai/resolve-branch.js +0 -1
- package/dist/ai/strip-mdx.d.ts +0 -1
- package/dist/ai/strip-mdx.js +0 -1
- package/dist/ai/to-plain-text.d.ts +60 -0
- package/dist/ai/to-plain-text.js +301 -0
- package/dist/ai/transform-components.d.ts +1 -2
- package/dist/ai/transform-components.js +70 -6
- package/dist/ai/types.d.ts +1 -2
- package/dist/ai/types.js +0 -1
- package/dist/api/admin-branch-health.d.ts +7 -8
- package/dist/api/admin-branch-health.js +0 -1
- package/dist/api/admin.d.ts +16 -17
- package/dist/api/admin.js +0 -1
- package/dist/api/assets.d.ts +8 -9
- package/dist/api/assets.js +0 -1
- package/dist/api/branch-merge.d.ts +3 -4
- package/dist/api/branch-merge.js +0 -1
- package/dist/api/branch-review.d.ts +5 -6
- package/dist/api/branch-review.js +0 -1
- package/dist/api/branch-status.d.ts +15 -16
- package/dist/api/branch-status.js +0 -1
- package/dist/api/branch-withdraw.d.ts +3 -4
- package/dist/api/branch-withdraw.js +0 -1
- package/dist/api/branch.d.ts +13 -14
- package/dist/api/branch.js +0 -1
- package/dist/api/client.d.ts +14 -15
- package/dist/api/client.js +0 -1
- package/dist/api/comments.d.ts +11 -12
- package/dist/api/comments.js +0 -1
- package/dist/api/content.d.ts +22 -24
- package/dist/api/content.js +132 -18
- package/dist/api/entries-constants.d.ts +0 -1
- package/dist/api/entries-constants.js +0 -1
- package/dist/api/entries.d.ts +12 -13
- package/dist/api/entries.js +12 -2
- package/dist/api/github-sync.d.ts +2 -3
- package/dist/api/github-sync.js +0 -1
- package/dist/api/groups.d.ts +6 -7
- package/dist/api/groups.js +0 -1
- package/dist/api/guards.d.ts +2 -3
- package/dist/api/guards.js +0 -1
- package/dist/api/index.d.ts +18 -19
- package/dist/api/index.js +0 -1
- package/dist/api/permissions.d.ts +10 -11
- package/dist/api/permissions.js +0 -1
- package/dist/api/reference-options.d.ts +3 -4
- package/dist/api/reference-options.js +0 -1
- package/dist/api/request-body-hash.d.ts +0 -1
- package/dist/api/request-body-hash.js +0 -1
- package/dist/api/resolve-references.d.ts +6 -7
- package/dist/api/resolve-references.js +24 -6
- package/dist/api/route-builder.d.ts +2 -3
- package/dist/api/route-builder.js +0 -1
- package/dist/api/schema.d.ts +43 -44
- package/dist/api/schema.js +0 -1
- package/dist/api/settings-helpers.d.ts +2 -3
- package/dist/api/settings-helpers.js +0 -1
- package/dist/api/types.d.ts +6 -7
- package/dist/api/types.js +0 -1
- package/dist/api/user.d.ts +2 -3
- package/dist/api/user.js +0 -1
- package/dist/api/validators.d.ts +3 -4
- package/dist/api/validators.js +0 -1
- package/dist/assets/asset-prefixes.d.ts +1 -2
- package/dist/assets/asset-prefixes.js +0 -1
- package/dist/assets/asset-src.d.ts +17 -5
- package/dist/assets/asset-src.js +15 -3
- package/dist/assets/asset-url.d.ts +28 -7
- package/dist/assets/asset-url.js +20 -15
- package/dist/assets/factory.d.ts +2 -3
- package/dist/assets/factory.js +0 -2
- package/dist/assets/finalize.d.ts +2 -3
- package/dist/assets/finalize.js +0 -1
- package/dist/assets/index.d.ts +12 -13
- package/dist/assets/index.js +0 -1
- package/dist/assets/keys.d.ts +1 -2
- package/dist/assets/keys.js +0 -1
- package/dist/assets/pipeline.d.ts +1 -2
- package/dist/assets/pipeline.js +0 -1
- package/dist/assets/store-local.d.ts +1 -2
- package/dist/assets/store-local.js +0 -1
- package/dist/assets/store-s3.d.ts +2 -4
- package/dist/assets/store-s3.js +0 -1
- package/dist/assets/svg-sanitizer.d.ts +0 -1
- package/dist/assets/svg-sanitizer.js +0 -1
- package/dist/assets/transform-directives.d.ts +0 -1
- package/dist/assets/transform-directives.js +0 -1
- package/dist/assets/transform.d.ts +1 -2
- package/dist/assets/transform.js +0 -1
- package/dist/assets/types.d.ts +0 -1
- package/dist/assets/types.js +0 -1
- package/dist/auth/cache.d.ts +2 -3
- package/dist/auth/cache.js +0 -1
- package/dist/auth/caching-auth-plugin.d.ts +3 -4
- package/dist/auth/caching-auth-plugin.js +0 -1
- package/dist/auth/context-helpers.d.ts +1 -2
- package/dist/auth/context-helpers.js +0 -1
- package/dist/auth/file-based-auth-cache.d.ts +3 -4
- package/dist/auth/file-based-auth-cache.js +0 -1
- package/dist/auth/index.d.ts +6 -7
- package/dist/auth/index.js +0 -1
- package/dist/auth/plugin.d.ts +3 -4
- package/dist/auth/plugin.js +0 -1
- package/dist/auth/types.d.ts +1 -2
- package/dist/auth/types.js +0 -1
- package/dist/authorization/branch.d.ts +58 -7
- package/dist/authorization/branch.js +55 -10
- package/dist/authorization/content.d.ts +6 -7
- package/dist/authorization/content.js +0 -1
- package/dist/authorization/groups/index.d.ts +2 -3
- package/dist/authorization/groups/index.js +0 -1
- package/dist/authorization/groups/loader.d.ts +3 -4
- package/dist/authorization/groups/loader.js +0 -1
- package/dist/authorization/groups/schema.d.ts +1 -2
- package/dist/authorization/groups/schema.js +0 -1
- package/dist/authorization/helpers.d.ts +0 -1
- package/dist/authorization/helpers.js +0 -1
- package/dist/authorization/index.d.ts +11 -12
- package/dist/authorization/index.js +0 -1
- package/dist/authorization/path.d.ts +4 -5
- package/dist/authorization/path.js +0 -1
- package/dist/authorization/permissions/index.d.ts +2 -3
- package/dist/authorization/permissions/index.js +0 -1
- package/dist/authorization/permissions/loader.d.ts +4 -5
- package/dist/authorization/permissions/loader.js +0 -1
- package/dist/authorization/permissions/schema.d.ts +2 -3
- package/dist/authorization/permissions/schema.js +0 -1
- package/dist/authorization/protected-branch.d.ts +2 -3
- package/dist/authorization/protected-branch.js +0 -1
- package/dist/authorization/settings-file-store.d.ts +1 -2
- package/dist/authorization/settings-file-store.js +0 -1
- package/dist/authorization/types.d.ts +7 -8
- package/dist/authorization/types.js +0 -1
- package/dist/authorization/validation.d.ts +1 -2
- package/dist/authorization/validation.js +0 -1
- package/dist/branch-health.d.ts +2 -3
- package/dist/branch-health.js +0 -1
- package/dist/branch-metadata.d.ts +2 -3
- package/dist/branch-metadata.js +0 -1
- package/dist/branch-registry.d.ts +1 -2
- package/dist/branch-registry.js +0 -1
- package/dist/branch-schema-cache.d.ts +4 -5
- package/dist/branch-schema-cache.js +0 -1
- package/dist/branch-workspace.d.ts +4 -5
- package/dist/branch-workspace.js +0 -1
- package/dist/build/generate-ai-content.d.ts +21 -5
- package/dist/build/generate-ai-content.js +98 -6
- package/dist/build/index.d.ts +2 -3
- package/dist/build/index.js +0 -1
- package/dist/build-canopy.d.ts +48 -0
- package/dist/build-canopy.js +50 -0
- package/dist/build-mode.d.ts +1 -2
- package/dist/build-mode.js +0 -1
- package/dist/cli/cli.d.ts +1 -2
- package/dist/cli/cli.js +1192 -495
- package/dist/cli/generate-ai-content.d.ts +0 -1
- package/dist/cli/generate-ai-content.js +1062 -370
- package/dist/cli/init.d.ts +1 -2
- package/dist/cli/init.js +12 -2
- package/dist/cli/migrate.d.ts +5 -3
- package/dist/cli/migrate.js +5 -3
- package/dist/cli/project-detect.d.ts +5 -2
- package/dist/cli/project-detect.js +5 -1
- package/dist/cli/project-root.d.ts +0 -1
- package/dist/cli/project-root.js +0 -1
- package/dist/cli/sync.d.ts +0 -1
- package/dist/cli/sync.js +0 -1
- package/dist/cli/template-files/canopycms.config.ts.template +18 -1
- package/dist/cli/template-files/deploy-cms.yml.template +8 -6
- package/dist/cli/templates.d.ts +0 -1
- package/dist/cli/templates.js +0 -1
- package/dist/client.d.ts +13 -14
- package/dist/client.js +0 -1
- package/dist/comment-store.d.ts +0 -1
- package/dist/comment-store.js +0 -1
- package/dist/config/flatten.d.ts +1 -2
- package/dist/config/flatten.js +0 -1
- package/dist/config/helpers.d.ts +3 -4
- package/dist/config/helpers.js +2 -2
- package/dist/config/index.d.ts +10 -11
- package/dist/config/index.js +0 -1
- package/dist/config/schemas/collection.d.ts +0 -1
- package/dist/config/schemas/collection.js +0 -1
- package/dist/config/schemas/config.d.ts +7 -4
- package/dist/config/schemas/config.js +12 -1
- package/dist/config/schemas/field.d.ts +3 -1
- package/dist/config/schemas/field.js +5 -1
- package/dist/config/schemas/media.d.ts +0 -1
- package/dist/config/schemas/media.js +0 -1
- package/dist/config/schemas/permissions.d.ts +0 -1
- package/dist/config/schemas/permissions.js +0 -1
- package/dist/config/types.d.ts +55 -9
- package/dist/config/types.js +0 -1
- package/dist/config/validation.d.ts +1 -2
- package/dist/config/validation.js +0 -1
- package/dist/config.d.ts +2 -3
- package/dist/config.js +0 -1
- package/dist/content-id-index.d.ts +1 -2
- package/dist/content-id-index.js +0 -1
- package/dist/content-index-generation.d.ts +0 -1
- package/dist/content-index-generation.js +0 -1
- package/dist/content-index-registry.d.ts +0 -1
- package/dist/content-index-registry.js +0 -1
- package/dist/content-listing.d.ts +137 -16
- package/dist/content-listing.js +175 -41
- package/dist/content-reader.d.ts +44 -8
- package/dist/content-reader.js +39 -7
- package/dist/content-store.d.ts +147 -7
- package/dist/content-store.js +294 -22
- package/dist/content-tree.d.ts +44 -5
- package/dist/content-tree.js +47 -11
- package/dist/context.d.ts +38 -15
- package/dist/context.js +55 -5
- package/dist/dev-content-watcher.d.ts +2 -3
- package/dist/dev-content-watcher.js +0 -1
- package/dist/editor/BranchManager.d.ts +4 -5
- package/dist/editor/BranchManager.js +0 -1
- package/dist/editor/CanopyEditor.d.ts +2 -3
- package/dist/editor/CanopyEditor.js +1 -2
- package/dist/editor/CanopyEditorPage.d.ts +2 -3
- package/dist/editor/CanopyEditorPage.js +0 -1
- package/dist/editor/CommentsPanel.d.ts +2 -3
- package/dist/editor/CommentsPanel.js +0 -1
- package/dist/editor/Editor.d.ts +13 -8
- package/dist/editor/Editor.js +4 -5
- package/dist/editor/EditorPanes.d.ts +0 -1
- package/dist/editor/EditorPanes.js +0 -1
- package/dist/editor/EntryNavigator.d.ts +1 -2
- package/dist/editor/EntryNavigator.js +0 -1
- package/dist/editor/FormRenderer.d.ts +2 -3
- package/dist/editor/FormRenderer.js +22 -3
- package/dist/editor/GroupManager.d.ts +2 -3
- package/dist/editor/GroupManager.js +0 -1
- package/dist/editor/PermissionManager.d.ts +2 -3
- package/dist/editor/PermissionManager.js +0 -1
- package/dist/editor/admin/SystemHealthPanel.d.ts +0 -1
- package/dist/editor/admin/SystemHealthPanel.js +0 -1
- package/dist/editor/admin/useSystemHealth.d.ts +2 -3
- package/dist/editor/admin/useSystemHealth.js +0 -1
- package/dist/editor/canopy-path.d.ts +0 -1
- package/dist/editor/canopy-path.js +0 -1
- package/dist/editor/client-reference-resolver.d.ts +13 -4
- package/dist/editor/client-reference-resolver.js +18 -8
- package/dist/editor/comments/BranchComments.d.ts +2 -3
- package/dist/editor/comments/BranchComments.js +0 -1
- package/dist/editor/comments/EntryComments.d.ts +1 -2
- package/dist/editor/comments/EntryComments.js +0 -1
- package/dist/editor/comments/FieldWrapper.d.ts +1 -2
- package/dist/editor/comments/FieldWrapper.js +0 -1
- package/dist/editor/comments/InlineCommentThread.d.ts +2 -3
- package/dist/editor/comments/InlineCommentThread.js +0 -1
- package/dist/editor/comments/ThreadCarousel.d.ts +2 -3
- package/dist/editor/comments/ThreadCarousel.js +0 -1
- package/dist/editor/components/ConfirmDeleteModal.d.ts +0 -1
- package/dist/editor/components/ConfirmDeleteModal.js +0 -1
- package/dist/editor/components/EditorFooter.d.ts +0 -1
- package/dist/editor/components/EditorFooter.js +0 -1
- package/dist/editor/components/EditorHeader.d.ts +4 -5
- package/dist/editor/components/EditorHeader.js +0 -1
- package/dist/editor/components/EditorSidebar.d.ts +1 -2
- package/dist/editor/components/EditorSidebar.js +0 -1
- package/dist/editor/components/EntryCreateModal.d.ts +0 -1
- package/dist/editor/components/EntryCreateModal.js +21 -10
- package/dist/editor/components/RenameEntryModal.d.ts +0 -1
- package/dist/editor/components/RenameEntryModal.js +0 -1
- package/dist/editor/components/UserBadge.d.ts +2 -3
- package/dist/editor/components/UserBadge.js +0 -1
- package/dist/editor/components/index.d.ts +3 -4
- package/dist/editor/components/index.js +0 -1
- package/dist/editor/context/ApiClientContext.d.ts +9 -3
- package/dist/editor/context/ApiClientContext.js +8 -4
- package/dist/editor/context/AssetContext.d.ts +26 -2
- package/dist/editor/context/AssetContext.js +23 -3
- package/dist/editor/context/EditorStateContext.d.ts +1 -2
- package/dist/editor/context/EditorStateContext.js +0 -1
- package/dist/editor/context/SWRProvider.d.ts +0 -1
- package/dist/editor/context/SWRProvider.js +0 -1
- package/dist/editor/context/index.d.ts +4 -5
- package/dist/editor/context/index.js +0 -1
- package/dist/editor/editor-config.d.ts +2 -3
- package/dist/editor/editor-config.js +0 -1
- package/dist/editor/editor-utils.d.ts +22 -10
- package/dist/editor/editor-utils.js +41 -10
- package/dist/editor/fields/BlockField.d.ts +1 -2
- package/dist/editor/fields/BlockField.js +0 -1
- package/dist/editor/fields/CodeField.d.ts +0 -1
- package/dist/editor/fields/CodeField.js +0 -1
- package/dist/editor/fields/DateTimeField.d.ts +0 -1
- package/dist/editor/fields/DateTimeField.js +0 -1
- package/dist/editor/fields/ImageField.d.ts +1 -2
- package/dist/editor/fields/ImageField.js +0 -1
- package/dist/editor/fields/InlineGroupField.d.ts +2 -3
- package/dist/editor/fields/InlineGroupField.js +0 -1
- package/dist/editor/fields/MarkdownField.d.ts +0 -1
- package/dist/editor/fields/MarkdownField.js +0 -1
- package/dist/editor/fields/MdxImageDialog.d.ts +0 -1
- package/dist/editor/fields/MdxImageDialog.js +0 -1
- package/dist/editor/fields/NumberField.d.ts +0 -1
- package/dist/editor/fields/NumberField.js +0 -1
- package/dist/editor/fields/NumberListField.d.ts +0 -1
- package/dist/editor/fields/NumberListField.js +0 -1
- package/dist/editor/fields/ObjectField.d.ts +11 -2
- package/dist/editor/fields/ObjectField.js +3 -4
- package/dist/editor/fields/ReferenceField.d.ts +0 -1
- package/dist/editor/fields/ReferenceField.js +8 -3
- package/dist/editor/fields/SelectField.d.ts +0 -1
- package/dist/editor/fields/SelectField.js +0 -1
- package/dist/editor/fields/StringListField.d.ts +0 -1
- package/dist/editor/fields/StringListField.js +0 -1
- 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.d.ts +1 -2
- package/dist/editor/fields/entry-link/EntryLinkContext.js +0 -1
- package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -1
- package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -1
- package/dist/editor/fields/entry-link/index.d.ts +2 -3
- package/dist/editor/fields/entry-link/index.js +0 -1
- package/dist/editor/group-manager/ExternalGroupsTab.d.ts +1 -2
- package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
- package/dist/editor/group-manager/GroupCard.d.ts +1 -2
- package/dist/editor/group-manager/GroupCard.js +0 -1
- package/dist/editor/group-manager/GroupForm.d.ts +1 -2
- package/dist/editor/group-manager/GroupForm.js +0 -1
- package/dist/editor/group-manager/InternalGroupsTab.d.ts +1 -2
- package/dist/editor/group-manager/InternalGroupsTab.js +0 -1
- package/dist/editor/group-manager/MemberList.d.ts +1 -2
- package/dist/editor/group-manager/MemberList.js +0 -1
- package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +1 -2
- package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -1
- package/dist/editor/group-manager/hooks/useGroupState.d.ts +1 -2
- package/dist/editor/group-manager/hooks/useGroupState.js +0 -1
- package/dist/editor/group-manager/hooks/useUserSearch.d.ts +1 -2
- package/dist/editor/group-manager/hooks/useUserSearch.js +0 -1
- package/dist/editor/group-manager/index.d.ts +5 -6
- package/dist/editor/group-manager/index.js +0 -1
- package/dist/editor/group-manager/types.d.ts +4 -5
- package/dist/editor/group-manager/types.js +0 -1
- package/dist/editor/hooks/index.d.ts +14 -15
- package/dist/editor/hooks/index.js +0 -1
- package/dist/editor/hooks/useBranchActions.d.ts +0 -1
- package/dist/editor/hooks/useBranchActions.js +0 -1
- package/dist/editor/hooks/useBranchManager.d.ts +4 -5
- package/dist/editor/hooks/useBranchManager.js +0 -1
- package/dist/editor/hooks/useBranchesData.d.ts +2 -3
- package/dist/editor/hooks/useBranchesData.js +0 -1
- package/dist/editor/hooks/useCommentSystem.d.ts +2 -3
- package/dist/editor/hooks/useCommentSystem.js +0 -1
- package/dist/editor/hooks/useCommentsData.d.ts +2 -3
- package/dist/editor/hooks/useCommentsData.js +0 -1
- package/dist/editor/hooks/useDraftManager.d.ts +3 -4
- package/dist/editor/hooks/useDraftManager.js +0 -1
- package/dist/editor/hooks/useEditorLayout.d.ts +1 -2
- package/dist/editor/hooks/useEditorLayout.js +0 -1
- package/dist/editor/hooks/useEntriesData.d.ts +3 -5
- package/dist/editor/hooks/useEntriesData.js +0 -3
- package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -3
- package/dist/editor/hooks/useEntryLinkResolution.js +0 -1
- package/dist/editor/hooks/useEntryManager.d.ts +5 -7
- package/dist/editor/hooks/useEntryManager.js +8 -3
- package/dist/editor/hooks/useGroupManager.d.ts +2 -3
- package/dist/editor/hooks/useGroupManager.js +0 -1
- package/dist/editor/hooks/usePermissionManager.d.ts +2 -3
- package/dist/editor/hooks/usePermissionManager.js +0 -1
- package/dist/editor/hooks/useReferenceResolution.d.ts +1 -2
- package/dist/editor/hooks/useReferenceResolution.js +8 -3
- package/dist/editor/hooks/useSchemaManager.d.ts +2 -3
- package/dist/editor/hooks/useSchemaManager.js +0 -1
- package/dist/editor/hooks/useUserContext.d.ts +1 -2
- package/dist/editor/hooks/useUserContext.js +8 -3
- package/dist/editor/hooks/useUserMetadata.d.ts +2 -3
- package/dist/editor/hooks/useUserMetadata.js +0 -1
- package/dist/editor/media/AssetCard.d.ts +1 -2
- package/dist/editor/media/AssetCard.js +0 -1
- package/dist/editor/media/CropStep.d.ts +1 -2
- package/dist/editor/media/CropStep.js +0 -1
- package/dist/editor/media/MediaLibrary.d.ts +1 -2
- package/dist/editor/media/MediaLibrary.js +0 -1
- package/dist/editor/media/MediaLibraryBody.d.ts +1 -2
- package/dist/editor/media/MediaLibraryBody.js +0 -1
- package/dist/editor/media/crop-math.d.ts +1 -2
- package/dist/editor/media/crop-math.js +0 -1
- package/dist/editor/media/upload-asset.d.ts +2 -3
- package/dist/editor/media/upload-asset.js +0 -1
- package/dist/editor/media/upload-constants.d.ts +0 -1
- package/dist/editor/media/upload-constants.js +0 -1
- package/dist/editor/media/useAssetUpload.d.ts +1 -2
- package/dist/editor/media/useAssetUpload.js +0 -1
- package/dist/editor/media/xhr-upload.d.ts +0 -1
- package/dist/editor/media/xhr-upload.js +0 -1
- package/dist/editor/permission-manager/GroupSelector.d.ts +1 -2
- package/dist/editor/permission-manager/GroupSelector.js +0 -1
- package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -2
- package/dist/editor/permission-manager/PermissionEditor.js +0 -1
- package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +1 -2
- package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -1
- package/dist/editor/permission-manager/PermissionTree.d.ts +1 -2
- package/dist/editor/permission-manager/PermissionTree.js +0 -1
- package/dist/editor/permission-manager/UserSelector.d.ts +1 -2
- package/dist/editor/permission-manager/UserSelector.js +0 -1
- package/dist/editor/permission-manager/constants.d.ts +1 -2
- package/dist/editor/permission-manager/constants.js +0 -1
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +1 -2
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -1
- package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +2 -3
- package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -1
- package/dist/editor/permission-manager/index.d.ts +4 -5
- package/dist/editor/permission-manager/index.js +0 -1
- package/dist/editor/permission-manager/types.d.ts +3 -4
- package/dist/editor/permission-manager/types.js +0 -1
- package/dist/editor/permission-manager/utils.d.ts +2 -3
- package/dist/editor/permission-manager/utils.js +0 -1
- package/dist/editor/preview-bridge.d.ts +1 -2
- package/dist/editor/preview-bridge.js +0 -1
- package/dist/editor/relative-time.d.ts +0 -1
- package/dist/editor/relative-time.js +0 -1
- package/dist/editor/schema-editor/CollectionEditor.d.ts +4 -5
- package/dist/editor/schema-editor/CollectionEditor.js +0 -1
- package/dist/editor/schema-editor/EntryTypeEditor.d.ts +2 -3
- package/dist/editor/schema-editor/EntryTypeEditor.js +0 -1
- package/dist/editor/schema-editor/index.d.ts +2 -3
- package/dist/editor/schema-editor/index.js +0 -1
- package/dist/editor/theme.d.ts +0 -1
- package/dist/editor/theme.js +0 -1
- package/dist/editor/utils/env.d.ts +0 -1
- package/dist/editor/utils/env.js +0 -1
- package/dist/entry-link-resolver.d.ts +1 -2
- package/dist/entry-link-resolver.js +0 -1
- package/dist/entry-schema-registry.d.ts +3 -4
- package/dist/entry-schema-registry.js +6 -2
- package/dist/entry-schema.d.ts +342 -11
- package/dist/entry-schema.js +170 -1
- package/dist/git-manager.d.ts +1 -2
- package/dist/git-manager.js +0 -1
- package/dist/github-service.d.ts +1 -2
- package/dist/github-service.js +0 -1
- package/dist/http/handler.d.ts +6 -7
- package/dist/http/handler.js +0 -1
- package/dist/http/index.d.ts +6 -7
- package/dist/http/index.js +0 -1
- package/dist/http/router.d.ts +2 -3
- package/dist/http/router.js +0 -1
- package/dist/http/types.d.ts +0 -1
- package/dist/http/types.js +0 -1
- package/dist/id.d.ts +2 -3
- package/dist/id.js +0 -1
- package/dist/index.d.ts +17 -17
- package/dist/index.js +7 -1
- package/dist/operating-mode/client-safe-strategy.d.ts +1 -2
- package/dist/operating-mode/client-safe-strategy.js +0 -1
- package/dist/operating-mode/client-unsafe-strategy.d.ts +1 -2
- package/dist/operating-mode/client-unsafe-strategy.js +0 -1
- package/dist/operating-mode/client.d.ts +3 -4
- package/dist/operating-mode/client.js +0 -1
- package/dist/operating-mode/deployment-name-fixtures.d.ts +0 -1
- package/dist/operating-mode/deployment-name-fixtures.js +0 -1
- package/dist/operating-mode/deployment-name.d.ts +0 -1
- package/dist/operating-mode/deployment-name.js +0 -1
- package/dist/operating-mode/index.d.ts +5 -6
- package/dist/operating-mode/index.js +0 -1
- package/dist/operating-mode/mode-env.d.ts +1 -2
- package/dist/operating-mode/mode-env.js +0 -1
- package/dist/operating-mode/types.d.ts +2 -3
- package/dist/operating-mode/types.js +0 -1
- package/dist/paths/branch-name.d.ts +1 -2
- package/dist/paths/branch-name.js +0 -1
- package/dist/paths/branch.d.ts +3 -4
- package/dist/paths/branch.js +0 -1
- package/dist/paths/index.d.ts +8 -9
- package/dist/paths/index.js +0 -1
- package/dist/paths/normalize-server.d.ts +1 -2
- package/dist/paths/normalize-server.js +0 -1
- package/dist/paths/normalize.d.ts +1 -2
- package/dist/paths/normalize.js +0 -1
- package/dist/paths/resolve.d.ts +2 -3
- package/dist/paths/resolve.js +0 -1
- package/dist/paths/types.d.ts +0 -1
- package/dist/paths/types.js +0 -1
- package/dist/paths/validation.d.ts +1 -2
- package/dist/paths/validation.js +0 -1
- package/dist/reference-resolver.d.ts +3 -4
- package/dist/reference-resolver.js +0 -1
- package/dist/resolve-canopy-user.d.ts +3 -4
- package/dist/resolve-canopy-user.js +0 -1
- package/dist/resource-generation.d.ts +0 -1
- package/dist/resource-generation.js +0 -1
- package/dist/schema/index.d.ts +3 -4
- package/dist/schema/index.js +0 -1
- package/dist/schema/meta-loader.d.ts +3 -4
- package/dist/schema/meta-loader.js +3 -3
- package/dist/schema/resolver.d.ts +2 -3
- package/dist/schema/resolver.js +0 -1
- package/dist/schema/schema-store-types.d.ts +2 -3
- package/dist/schema/schema-store-types.js +0 -1
- package/dist/schema/schema-store.d.ts +6 -7
- package/dist/schema/schema-store.js +33 -1
- package/dist/schema/types.d.ts +1 -2
- package/dist/schema/types.js +0 -1
- package/dist/server.d.ts +97 -37
- package/dist/server.js +60 -3
- package/dist/services.d.ts +10 -12
- package/dist/services.js +2 -7
- package/dist/settings-workspace.d.ts +12 -15
- package/dist/settings-workspace.js +10 -13
- package/dist/static/index.d.ts +227 -9
- package/dist/static/index.js +302 -14
- package/dist/static/seo.d.ts +128 -0
- package/dist/static/seo.js +162 -0
- package/dist/sync-core.d.ts +0 -1
- package/dist/sync-core.js +0 -1
- package/dist/task-queue/index.d.ts +2 -3
- package/dist/task-queue/index.js +0 -1
- package/dist/task-queue/task-queue.d.ts +1 -2
- package/dist/task-queue/task-queue.js +0 -1
- package/dist/task-queue/types.d.ts +0 -1
- package/dist/task-queue/types.js +0 -1
- package/dist/types.d.ts +3 -4
- package/dist/types.js +0 -1
- package/dist/url-collision.d.ts +86 -0
- package/dist/url-collision.js +186 -0
- package/dist/url-path-resolver.d.ts +11 -2
- package/dist/url-path-resolver.js +37 -5
- package/dist/user.d.ts +3 -4
- package/dist/user.js +0 -1
- package/dist/utils/async-mutex.d.ts +0 -1
- package/dist/utils/async-mutex.js +0 -1
- package/dist/utils/atomic-write.d.ts +0 -1
- package/dist/utils/atomic-write.js +0 -1
- package/dist/utils/body-field.d.ts +12 -2
- package/dist/utils/body-field.js +17 -1
- package/dist/utils/content-serialize.d.ts +37 -0
- package/dist/utils/content-serialize.js +429 -0
- package/dist/utils/content-write-lock.d.ts +12 -16
- package/dist/utils/content-write-lock.js +38 -22
- package/dist/utils/debug.d.ts +0 -1
- package/dist/utils/debug.js +0 -1
- package/dist/utils/entry-url.d.ts +17 -1
- package/dist/utils/entry-url.js +21 -3
- package/dist/utils/error.d.ts +0 -1
- package/dist/utils/error.js +0 -1
- package/dist/utils/flatten-group-fields.d.ts +1 -2
- package/dist/utils/flatten-group-fields.js +0 -1
- package/dist/utils/format.d.ts +1 -2
- package/dist/utils/format.js +0 -1
- package/dist/utils/fs.d.ts +7 -1
- package/dist/utils/fs.js +16 -1
- package/dist/utils/git.d.ts +1 -2
- package/dist/utils/git.js +0 -1
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/logger.js +0 -1
- package/dist/utils/occ-json-write.d.ts +0 -1
- package/dist/utils/occ-json-write.js +0 -1
- package/dist/utils/provisioning-lock.d.ts +17 -4
- package/dist/utils/provisioning-lock.js +94 -14
- package/dist/utils/sanitize-href.d.ts +46 -1
- package/dist/utils/sanitize-href.js +63 -3
- package/dist/utils/title-field.d.ts +1 -2
- package/dist/utils/title-field.js +0 -1
- package/dist/utils/typed-filename.d.ts +61 -0
- package/dist/utils/typed-filename.js +92 -0
- package/dist/utils/url-prefix.d.ts +121 -0
- package/dist/utils/url-prefix.js +158 -0
- package/dist/validation/block-structural-keys.d.ts +42 -0
- package/dist/validation/block-structural-keys.js +44 -0
- package/dist/validation/deletion-checker.d.ts +4 -5
- package/dist/validation/deletion-checker.js +0 -1
- package/dist/validation/entry-link-validator.d.ts +2 -3
- package/dist/validation/entry-link-validator.js +0 -1
- package/dist/validation/entry-type-reference-validator.d.ts +1 -2
- package/dist/validation/entry-type-reference-validator.js +0 -1
- package/dist/validation/entry-validator.d.ts +23 -2
- package/dist/validation/entry-validator.js +34 -2
- package/dist/validation/field-traversal.d.ts +28 -4
- package/dist/validation/field-traversal.js +24 -7
- package/dist/validation/reference-validator.d.ts +2 -3
- package/dist/validation/reference-validator.js +0 -1
- package/dist/worker/cms-worker.d.ts +9 -2
- package/dist/worker/cms-worker.js +59 -2
- package/dist/worker/log.d.ts +0 -1
- package/dist/worker/log.js +0 -1
- package/dist/worker/task-queue-config.d.ts +1 -2
- package/dist/worker/task-queue-config.js +0 -1
- package/dist/worker/task-queue.d.ts +3 -4
- package/dist/worker/task-queue.js +0 -1
- package/dist/worker/worker-status.d.ts +1 -2
- package/dist/worker/worker-status.js +0 -1
- package/package.json +2 -6
- package/dist/ai/generate.d.ts.map +0 -1
- package/dist/ai/generate.js.map +0 -1
- package/dist/ai/handler.d.ts.map +0 -1
- package/dist/ai/handler.js.map +0 -1
- package/dist/ai/index.d.ts.map +0 -1
- package/dist/ai/index.js.map +0 -1
- package/dist/ai/json-to-markdown.d.ts.map +0 -1
- package/dist/ai/json-to-markdown.js.map +0 -1
- package/dist/ai/resolve-branch.d.ts.map +0 -1
- package/dist/ai/resolve-branch.js.map +0 -1
- package/dist/ai/strip-mdx.d.ts.map +0 -1
- package/dist/ai/strip-mdx.js.map +0 -1
- package/dist/ai/transform-components.d.ts.map +0 -1
- package/dist/ai/transform-components.js.map +0 -1
- package/dist/ai/types.d.ts.map +0 -1
- package/dist/ai/types.js.map +0 -1
- package/dist/api/__test__/mock-client.d.ts +0 -231
- package/dist/api/__test__/mock-client.d.ts.map +0 -1
- package/dist/api/__test__/mock-client.js +0 -388
- package/dist/api/__test__/mock-client.js.map +0 -1
- package/dist/api/admin-branch-health.d.ts.map +0 -1
- package/dist/api/admin-branch-health.js.map +0 -1
- package/dist/api/admin.d.ts.map +0 -1
- package/dist/api/admin.js.map +0 -1
- package/dist/api/assets.d.ts.map +0 -1
- package/dist/api/assets.js.map +0 -1
- package/dist/api/branch-merge.d.ts.map +0 -1
- package/dist/api/branch-merge.js.map +0 -1
- package/dist/api/branch-review.d.ts.map +0 -1
- package/dist/api/branch-review.js.map +0 -1
- package/dist/api/branch-status.d.ts.map +0 -1
- package/dist/api/branch-status.js.map +0 -1
- package/dist/api/branch-withdraw.d.ts.map +0 -1
- package/dist/api/branch-withdraw.js.map +0 -1
- package/dist/api/branch.d.ts.map +0 -1
- package/dist/api/branch.js.map +0 -1
- package/dist/api/client.d.ts.map +0 -1
- package/dist/api/client.js.map +0 -1
- package/dist/api/comments.d.ts.map +0 -1
- package/dist/api/comments.js.map +0 -1
- package/dist/api/content.d.ts.map +0 -1
- package/dist/api/content.js.map +0 -1
- package/dist/api/entries-constants.d.ts.map +0 -1
- package/dist/api/entries-constants.js.map +0 -1
- package/dist/api/entries.d.ts.map +0 -1
- package/dist/api/entries.js.map +0 -1
- package/dist/api/github-sync.d.ts.map +0 -1
- package/dist/api/github-sync.js.map +0 -1
- package/dist/api/groups.d.ts.map +0 -1
- package/dist/api/groups.js.map +0 -1
- package/dist/api/guards.d.ts.map +0 -1
- package/dist/api/guards.js.map +0 -1
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/index.js.map +0 -1
- package/dist/api/permissions.d.ts.map +0 -1
- package/dist/api/permissions.js.map +0 -1
- package/dist/api/reference-options.d.ts.map +0 -1
- package/dist/api/reference-options.js.map +0 -1
- package/dist/api/request-body-hash.d.ts.map +0 -1
- package/dist/api/request-body-hash.js.map +0 -1
- package/dist/api/resolve-references.d.ts.map +0 -1
- package/dist/api/resolve-references.js.map +0 -1
- package/dist/api/route-builder.d.ts.map +0 -1
- package/dist/api/route-builder.js.map +0 -1
- package/dist/api/schema.d.ts.map +0 -1
- package/dist/api/schema.js.map +0 -1
- package/dist/api/settings-helpers.d.ts.map +0 -1
- package/dist/api/settings-helpers.js.map +0 -1
- package/dist/api/types.d.ts.map +0 -1
- package/dist/api/types.js.map +0 -1
- package/dist/api/user.d.ts.map +0 -1
- package/dist/api/user.js.map +0 -1
- package/dist/api/validators.d.ts.map +0 -1
- package/dist/api/validators.js.map +0 -1
- package/dist/assets/asset-prefixes.d.ts.map +0 -1
- package/dist/assets/asset-prefixes.js.map +0 -1
- package/dist/assets/asset-src.d.ts.map +0 -1
- package/dist/assets/asset-src.js.map +0 -1
- package/dist/assets/asset-url.d.ts.map +0 -1
- package/dist/assets/asset-url.js.map +0 -1
- package/dist/assets/factory.d.ts.map +0 -1
- package/dist/assets/factory.js.map +0 -1
- package/dist/assets/finalize.d.ts.map +0 -1
- package/dist/assets/finalize.js.map +0 -1
- package/dist/assets/index.d.ts.map +0 -1
- package/dist/assets/index.js.map +0 -1
- package/dist/assets/keys.d.ts.map +0 -1
- package/dist/assets/keys.js.map +0 -1
- package/dist/assets/pipeline.d.ts.map +0 -1
- package/dist/assets/pipeline.js.map +0 -1
- package/dist/assets/store-local.d.ts.map +0 -1
- package/dist/assets/store-local.js.map +0 -1
- package/dist/assets/store-s3.d.ts.map +0 -1
- package/dist/assets/store-s3.js.map +0 -1
- package/dist/assets/svg-sanitizer.d.ts.map +0 -1
- package/dist/assets/svg-sanitizer.js.map +0 -1
- package/dist/assets/transform-directives.d.ts.map +0 -1
- package/dist/assets/transform-directives.js.map +0 -1
- package/dist/assets/transform.d.ts.map +0 -1
- package/dist/assets/transform.js.map +0 -1
- package/dist/assets/types.d.ts.map +0 -1
- package/dist/assets/types.js.map +0 -1
- package/dist/auth/cache.d.ts.map +0 -1
- package/dist/auth/cache.js.map +0 -1
- package/dist/auth/caching-auth-plugin.d.ts.map +0 -1
- package/dist/auth/caching-auth-plugin.js.map +0 -1
- package/dist/auth/context-helpers.d.ts.map +0 -1
- package/dist/auth/context-helpers.js.map +0 -1
- package/dist/auth/file-based-auth-cache.d.ts.map +0 -1
- package/dist/auth/file-based-auth-cache.js.map +0 -1
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/plugin.d.ts.map +0 -1
- package/dist/auth/plugin.js.map +0 -1
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/auth/types.js.map +0 -1
- package/dist/authorization/branch.d.ts.map +0 -1
- package/dist/authorization/branch.js.map +0 -1
- package/dist/authorization/content.d.ts.map +0 -1
- package/dist/authorization/content.js.map +0 -1
- package/dist/authorization/groups/index.d.ts.map +0 -1
- package/dist/authorization/groups/index.js.map +0 -1
- package/dist/authorization/groups/loader.d.ts.map +0 -1
- package/dist/authorization/groups/loader.js.map +0 -1
- package/dist/authorization/groups/schema.d.ts.map +0 -1
- package/dist/authorization/groups/schema.js.map +0 -1
- package/dist/authorization/helpers.d.ts.map +0 -1
- package/dist/authorization/helpers.js.map +0 -1
- package/dist/authorization/index.d.ts.map +0 -1
- package/dist/authorization/index.js.map +0 -1
- package/dist/authorization/path.d.ts.map +0 -1
- package/dist/authorization/path.js.map +0 -1
- package/dist/authorization/permissions/index.d.ts.map +0 -1
- package/dist/authorization/permissions/index.js.map +0 -1
- package/dist/authorization/permissions/loader.d.ts.map +0 -1
- package/dist/authorization/permissions/loader.js.map +0 -1
- package/dist/authorization/permissions/schema.d.ts.map +0 -1
- package/dist/authorization/permissions/schema.js.map +0 -1
- package/dist/authorization/protected-branch.d.ts.map +0 -1
- package/dist/authorization/protected-branch.js.map +0 -1
- package/dist/authorization/settings-file-store.d.ts.map +0 -1
- package/dist/authorization/settings-file-store.js.map +0 -1
- package/dist/authorization/test-utils.d.ts +0 -26
- package/dist/authorization/test-utils.d.ts.map +0 -1
- package/dist/authorization/test-utils.js +0 -23
- package/dist/authorization/test-utils.js.map +0 -1
- package/dist/authorization/types.d.ts.map +0 -1
- package/dist/authorization/types.js.map +0 -1
- package/dist/authorization/validation.d.ts.map +0 -1
- package/dist/authorization/validation.js.map +0 -1
- package/dist/branch-health.d.ts.map +0 -1
- package/dist/branch-health.js.map +0 -1
- package/dist/branch-metadata.d.ts.map +0 -1
- package/dist/branch-metadata.js.map +0 -1
- package/dist/branch-registry.d.ts.map +0 -1
- package/dist/branch-registry.js.map +0 -1
- package/dist/branch-schema-cache.d.ts.map +0 -1
- package/dist/branch-schema-cache.js.map +0 -1
- package/dist/branch-workspace.d.ts.map +0 -1
- package/dist/branch-workspace.js.map +0 -1
- package/dist/build/generate-ai-content.d.ts.map +0 -1
- package/dist/build/generate-ai-content.js.map +0 -1
- package/dist/build/index.d.ts.map +0 -1
- package/dist/build/index.js.map +0 -1
- package/dist/build-mode.d.ts.map +0 -1
- package/dist/build-mode.js.map +0 -1
- package/dist/cli/cli.d.ts.map +0 -1
- package/dist/cli/cli.js.map +0 -1
- package/dist/cli/generate-ai-content.d.ts.map +0 -1
- package/dist/cli/generate-ai-content.js.map +0 -1
- package/dist/cli/init.d.ts.map +0 -1
- package/dist/cli/init.js.map +0 -1
- package/dist/cli/migrate.d.ts.map +0 -1
- package/dist/cli/migrate.js.map +0 -1
- package/dist/cli/project-detect.d.ts.map +0 -1
- package/dist/cli/project-detect.js.map +0 -1
- package/dist/cli/project-root.d.ts.map +0 -1
- package/dist/cli/project-root.js.map +0 -1
- package/dist/cli/sync.d.ts.map +0 -1
- package/dist/cli/sync.js.map +0 -1
- package/dist/cli/templates.d.ts.map +0 -1
- package/dist/cli/templates.js.map +0 -1
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/comment-store.d.ts.map +0 -1
- package/dist/comment-store.js.map +0 -1
- package/dist/config/flatten.d.ts.map +0 -1
- package/dist/config/flatten.js.map +0 -1
- package/dist/config/helpers.d.ts.map +0 -1
- package/dist/config/helpers.js.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js.map +0 -1
- package/dist/config/schemas/collection.d.ts.map +0 -1
- package/dist/config/schemas/collection.js.map +0 -1
- package/dist/config/schemas/config.d.ts.map +0 -1
- package/dist/config/schemas/config.js.map +0 -1
- package/dist/config/schemas/field.d.ts.map +0 -1
- package/dist/config/schemas/field.js.map +0 -1
- package/dist/config/schemas/media.d.ts.map +0 -1
- package/dist/config/schemas/media.js.map +0 -1
- package/dist/config/schemas/permissions.d.ts.map +0 -1
- package/dist/config/schemas/permissions.js.map +0 -1
- package/dist/config/types.d.ts.map +0 -1
- package/dist/config/types.js.map +0 -1
- package/dist/config/validation.d.ts.map +0 -1
- package/dist/config/validation.js.map +0 -1
- package/dist/config-test.d.ts +0 -20
- package/dist/config-test.d.ts.map +0 -1
- package/dist/config-test.js +0 -49
- package/dist/config-test.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/content-id-index.d.ts.map +0 -1
- package/dist/content-id-index.js.map +0 -1
- package/dist/content-index-generation.d.ts.map +0 -1
- package/dist/content-index-generation.js.map +0 -1
- package/dist/content-index-registry.d.ts.map +0 -1
- package/dist/content-index-registry.js.map +0 -1
- package/dist/content-listing.d.ts.map +0 -1
- package/dist/content-listing.js.map +0 -1
- package/dist/content-reader.d.ts.map +0 -1
- package/dist/content-reader.js.map +0 -1
- package/dist/content-store.d.ts.map +0 -1
- package/dist/content-store.js.map +0 -1
- package/dist/content-tree.d.ts.map +0 -1
- package/dist/content-tree.js.map +0 -1
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js.map +0 -1
- package/dist/dev-content-watcher.d.ts.map +0 -1
- package/dist/dev-content-watcher.js.map +0 -1
- package/dist/editor/BranchManager.d.ts.map +0 -1
- package/dist/editor/BranchManager.js.map +0 -1
- package/dist/editor/CanopyEditor.d.ts.map +0 -1
- package/dist/editor/CanopyEditor.js.map +0 -1
- package/dist/editor/CanopyEditorPage.d.ts.map +0 -1
- package/dist/editor/CanopyEditorPage.js.map +0 -1
- package/dist/editor/CommentsPanel.d.ts.map +0 -1
- package/dist/editor/CommentsPanel.js.map +0 -1
- package/dist/editor/Editor.d.ts.map +0 -1
- package/dist/editor/Editor.js.map +0 -1
- package/dist/editor/EditorPanes.d.ts.map +0 -1
- package/dist/editor/EditorPanes.js.map +0 -1
- package/dist/editor/EntryNavigator.d.ts.map +0 -1
- package/dist/editor/EntryNavigator.js.map +0 -1
- package/dist/editor/FormRenderer.d.ts.map +0 -1
- package/dist/editor/FormRenderer.js.map +0 -1
- package/dist/editor/GroupManager.d.ts.map +0 -1
- package/dist/editor/GroupManager.js.map +0 -1
- package/dist/editor/PermissionManager.d.ts.map +0 -1
- package/dist/editor/PermissionManager.js.map +0 -1
- package/dist/editor/admin/SystemHealthPanel.d.ts.map +0 -1
- package/dist/editor/admin/SystemHealthPanel.js.map +0 -1
- package/dist/editor/admin/useSystemHealth.d.ts.map +0 -1
- package/dist/editor/admin/useSystemHealth.js.map +0 -1
- package/dist/editor/canopy-path.d.ts.map +0 -1
- package/dist/editor/canopy-path.js.map +0 -1
- package/dist/editor/client-reference-resolver.d.ts.map +0 -1
- package/dist/editor/client-reference-resolver.js.map +0 -1
- package/dist/editor/comments/BranchComments.d.ts.map +0 -1
- package/dist/editor/comments/BranchComments.js.map +0 -1
- package/dist/editor/comments/EntryComments.d.ts.map +0 -1
- package/dist/editor/comments/EntryComments.js.map +0 -1
- package/dist/editor/comments/FieldWrapper.d.ts.map +0 -1
- package/dist/editor/comments/FieldWrapper.js.map +0 -1
- package/dist/editor/comments/InlineCommentThread.d.ts.map +0 -1
- package/dist/editor/comments/InlineCommentThread.js.map +0 -1
- package/dist/editor/comments/ThreadCarousel.d.ts.map +0 -1
- package/dist/editor/comments/ThreadCarousel.js.map +0 -1
- package/dist/editor/components/ConfirmDeleteModal.d.ts.map +0 -1
- package/dist/editor/components/ConfirmDeleteModal.js.map +0 -1
- package/dist/editor/components/EditorFooter.d.ts.map +0 -1
- package/dist/editor/components/EditorFooter.js.map +0 -1
- package/dist/editor/components/EditorHeader.d.ts.map +0 -1
- package/dist/editor/components/EditorHeader.js.map +0 -1
- package/dist/editor/components/EditorSidebar.d.ts.map +0 -1
- package/dist/editor/components/EditorSidebar.js.map +0 -1
- package/dist/editor/components/EntryCreateModal.d.ts.map +0 -1
- package/dist/editor/components/EntryCreateModal.js.map +0 -1
- package/dist/editor/components/RenameEntryModal.d.ts.map +0 -1
- package/dist/editor/components/RenameEntryModal.js.map +0 -1
- package/dist/editor/components/UserBadge.d.ts.map +0 -1
- package/dist/editor/components/UserBadge.js.map +0 -1
- package/dist/editor/components/index.d.ts.map +0 -1
- package/dist/editor/components/index.js.map +0 -1
- package/dist/editor/context/ApiClientContext.d.ts.map +0 -1
- package/dist/editor/context/ApiClientContext.js.map +0 -1
- package/dist/editor/context/AssetContext.d.ts.map +0 -1
- package/dist/editor/context/AssetContext.js.map +0 -1
- package/dist/editor/context/EditorStateContext.d.ts.map +0 -1
- package/dist/editor/context/EditorStateContext.js.map +0 -1
- package/dist/editor/context/SWRProvider.d.ts.map +0 -1
- package/dist/editor/context/SWRProvider.js.map +0 -1
- package/dist/editor/context/index.d.ts.map +0 -1
- package/dist/editor/context/index.js.map +0 -1
- package/dist/editor/editor-config.d.ts.map +0 -1
- package/dist/editor/editor-config.js.map +0 -1
- package/dist/editor/editor-utils.d.ts.map +0 -1
- package/dist/editor/editor-utils.js.map +0 -1
- package/dist/editor/fields/BlockField.d.ts.map +0 -1
- package/dist/editor/fields/BlockField.js.map +0 -1
- package/dist/editor/fields/CodeField.d.ts.map +0 -1
- package/dist/editor/fields/CodeField.js.map +0 -1
- package/dist/editor/fields/DateTimeField.d.ts.map +0 -1
- package/dist/editor/fields/DateTimeField.js.map +0 -1
- package/dist/editor/fields/ImageField.d.ts.map +0 -1
- package/dist/editor/fields/ImageField.js.map +0 -1
- package/dist/editor/fields/InlineGroupField.d.ts.map +0 -1
- package/dist/editor/fields/InlineGroupField.js.map +0 -1
- package/dist/editor/fields/MarkdownField.d.ts.map +0 -1
- package/dist/editor/fields/MarkdownField.js.map +0 -1
- package/dist/editor/fields/MdxImageDialog.d.ts.map +0 -1
- package/dist/editor/fields/MdxImageDialog.js.map +0 -1
- package/dist/editor/fields/NumberField.d.ts.map +0 -1
- package/dist/editor/fields/NumberField.js.map +0 -1
- package/dist/editor/fields/NumberListField.d.ts.map +0 -1
- package/dist/editor/fields/NumberListField.js.map +0 -1
- package/dist/editor/fields/ObjectField.d.ts.map +0 -1
- package/dist/editor/fields/ObjectField.js.map +0 -1
- package/dist/editor/fields/ReferenceField.d.ts.map +0 -1
- package/dist/editor/fields/ReferenceField.js.map +0 -1
- package/dist/editor/fields/SelectField.d.ts.map +0 -1
- package/dist/editor/fields/SelectField.js.map +0 -1
- package/dist/editor/fields/StringListField.d.ts.map +0 -1
- package/dist/editor/fields/StringListField.js.map +0 -1
- package/dist/editor/fields/TextField.d.ts.map +0 -1
- package/dist/editor/fields/TextField.js.map +0 -1
- package/dist/editor/fields/ToggleField.d.ts.map +0 -1
- package/dist/editor/fields/ToggleField.js.map +0 -1
- package/dist/editor/fields/entry-link/EntryLinkContext.d.ts.map +0 -1
- package/dist/editor/fields/entry-link/EntryLinkContext.js.map +0 -1
- package/dist/editor/fields/entry-link/InsertEntryLink.d.ts.map +0 -1
- package/dist/editor/fields/entry-link/InsertEntryLink.js.map +0 -1
- package/dist/editor/fields/entry-link/index.d.ts.map +0 -1
- package/dist/editor/fields/entry-link/index.js.map +0 -1
- package/dist/editor/group-manager/ExternalGroupsTab.d.ts.map +0 -1
- package/dist/editor/group-manager/ExternalGroupsTab.js.map +0 -1
- package/dist/editor/group-manager/GroupCard.d.ts.map +0 -1
- package/dist/editor/group-manager/GroupCard.js.map +0 -1
- package/dist/editor/group-manager/GroupForm.d.ts.map +0 -1
- package/dist/editor/group-manager/GroupForm.js.map +0 -1
- package/dist/editor/group-manager/InternalGroupsTab.d.ts.map +0 -1
- package/dist/editor/group-manager/InternalGroupsTab.js.map +0 -1
- package/dist/editor/group-manager/MemberList.d.ts.map +0 -1
- package/dist/editor/group-manager/MemberList.js.map +0 -1
- package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts.map +0 -1
- package/dist/editor/group-manager/hooks/useExternalGroupSearch.js.map +0 -1
- package/dist/editor/group-manager/hooks/useGroupState.d.ts.map +0 -1
- package/dist/editor/group-manager/hooks/useGroupState.js.map +0 -1
- package/dist/editor/group-manager/hooks/useUserSearch.d.ts.map +0 -1
- package/dist/editor/group-manager/hooks/useUserSearch.js.map +0 -1
- package/dist/editor/group-manager/index.d.ts.map +0 -1
- package/dist/editor/group-manager/index.js.map +0 -1
- package/dist/editor/group-manager/types.d.ts.map +0 -1
- package/dist/editor/group-manager/types.js.map +0 -1
- package/dist/editor/hooks/__test__/test-utils.d.ts +0 -164
- package/dist/editor/hooks/__test__/test-utils.d.ts.map +0 -1
- package/dist/editor/hooks/__test__/test-utils.js +0 -203
- package/dist/editor/hooks/__test__/test-utils.js.map +0 -1
- package/dist/editor/hooks/index.d.ts.map +0 -1
- package/dist/editor/hooks/index.js.map +0 -1
- package/dist/editor/hooks/useBranchActions.d.ts.map +0 -1
- package/dist/editor/hooks/useBranchActions.js.map +0 -1
- package/dist/editor/hooks/useBranchManager.d.ts.map +0 -1
- package/dist/editor/hooks/useBranchManager.js.map +0 -1
- package/dist/editor/hooks/useBranchesData.d.ts.map +0 -1
- package/dist/editor/hooks/useBranchesData.js.map +0 -1
- package/dist/editor/hooks/useCommentSystem.d.ts.map +0 -1
- package/dist/editor/hooks/useCommentSystem.js.map +0 -1
- package/dist/editor/hooks/useCommentsData.d.ts.map +0 -1
- package/dist/editor/hooks/useCommentsData.js.map +0 -1
- package/dist/editor/hooks/useDraftManager.d.ts.map +0 -1
- package/dist/editor/hooks/useDraftManager.js.map +0 -1
- package/dist/editor/hooks/useEditorLayout.d.ts.map +0 -1
- package/dist/editor/hooks/useEditorLayout.js.map +0 -1
- package/dist/editor/hooks/useEntriesData.d.ts.map +0 -1
- package/dist/editor/hooks/useEntriesData.js.map +0 -1
- package/dist/editor/hooks/useEntryLinkResolution.d.ts.map +0 -1
- package/dist/editor/hooks/useEntryLinkResolution.js.map +0 -1
- package/dist/editor/hooks/useEntryManager.d.ts.map +0 -1
- package/dist/editor/hooks/useEntryManager.js.map +0 -1
- package/dist/editor/hooks/useGroupManager.d.ts.map +0 -1
- package/dist/editor/hooks/useGroupManager.js.map +0 -1
- package/dist/editor/hooks/usePermissionManager.d.ts.map +0 -1
- package/dist/editor/hooks/usePermissionManager.js.map +0 -1
- package/dist/editor/hooks/useReferenceResolution.d.ts.map +0 -1
- package/dist/editor/hooks/useReferenceResolution.js.map +0 -1
- package/dist/editor/hooks/useSchemaManager.d.ts.map +0 -1
- package/dist/editor/hooks/useSchemaManager.js.map +0 -1
- package/dist/editor/hooks/useUserContext.d.ts.map +0 -1
- package/dist/editor/hooks/useUserContext.js.map +0 -1
- package/dist/editor/hooks/useUserMetadata.d.ts.map +0 -1
- package/dist/editor/hooks/useUserMetadata.js.map +0 -1
- package/dist/editor/media/AssetCard.d.ts.map +0 -1
- package/dist/editor/media/AssetCard.js.map +0 -1
- package/dist/editor/media/CropStep.d.ts.map +0 -1
- package/dist/editor/media/CropStep.js.map +0 -1
- package/dist/editor/media/MediaLibrary.d.ts.map +0 -1
- package/dist/editor/media/MediaLibrary.js.map +0 -1
- package/dist/editor/media/MediaLibraryBody.d.ts.map +0 -1
- package/dist/editor/media/MediaLibraryBody.js.map +0 -1
- package/dist/editor/media/crop-math.d.ts.map +0 -1
- package/dist/editor/media/crop-math.js.map +0 -1
- package/dist/editor/media/upload-asset.d.ts.map +0 -1
- package/dist/editor/media/upload-asset.js.map +0 -1
- package/dist/editor/media/upload-constants.d.ts.map +0 -1
- package/dist/editor/media/upload-constants.js.map +0 -1
- package/dist/editor/media/useAssetUpload.d.ts.map +0 -1
- package/dist/editor/media/useAssetUpload.js.map +0 -1
- package/dist/editor/media/xhr-upload.d.ts.map +0 -1
- package/dist/editor/media/xhr-upload.js.map +0 -1
- package/dist/editor/permission-manager/GroupSelector.d.ts.map +0 -1
- package/dist/editor/permission-manager/GroupSelector.js.map +0 -1
- package/dist/editor/permission-manager/PermissionEditor.d.ts.map +0 -1
- package/dist/editor/permission-manager/PermissionEditor.js.map +0 -1
- package/dist/editor/permission-manager/PermissionLevelBadge.d.ts.map +0 -1
- package/dist/editor/permission-manager/PermissionLevelBadge.js.map +0 -1
- package/dist/editor/permission-manager/PermissionTree.d.ts.map +0 -1
- package/dist/editor/permission-manager/PermissionTree.js.map +0 -1
- package/dist/editor/permission-manager/UserSelector.d.ts.map +0 -1
- package/dist/editor/permission-manager/UserSelector.js.map +0 -1
- package/dist/editor/permission-manager/constants.d.ts.map +0 -1
- package/dist/editor/permission-manager/constants.js.map +0 -1
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts.map +0 -1
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js.map +0 -1
- package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts.map +0 -1
- package/dist/editor/permission-manager/hooks/usePermissionTree.js.map +0 -1
- package/dist/editor/permission-manager/index.d.ts.map +0 -1
- package/dist/editor/permission-manager/index.js.map +0 -1
- package/dist/editor/permission-manager/types.d.ts.map +0 -1
- package/dist/editor/permission-manager/types.js.map +0 -1
- package/dist/editor/permission-manager/utils.d.ts.map +0 -1
- package/dist/editor/permission-manager/utils.js.map +0 -1
- package/dist/editor/preview-bridge.d.ts.map +0 -1
- package/dist/editor/preview-bridge.js.map +0 -1
- package/dist/editor/relative-time.d.ts.map +0 -1
- package/dist/editor/relative-time.js.map +0 -1
- package/dist/editor/schema-editor/CollectionEditor.d.ts.map +0 -1
- package/dist/editor/schema-editor/CollectionEditor.js.map +0 -1
- package/dist/editor/schema-editor/EntryTypeEditor.d.ts.map +0 -1
- package/dist/editor/schema-editor/EntryTypeEditor.js.map +0 -1
- package/dist/editor/schema-editor/index.d.ts.map +0 -1
- package/dist/editor/schema-editor/index.js.map +0 -1
- package/dist/editor/setup-test-dom.d.ts +0 -1
- package/dist/editor/setup-test-dom.d.ts.map +0 -1
- package/dist/editor/setup-test-dom.js +0 -12
- package/dist/editor/setup-test-dom.js.map +0 -1
- package/dist/editor/test-setup.d.ts +0 -6
- package/dist/editor/test-setup.d.ts.map +0 -1
- package/dist/editor/test-setup.js +0 -42
- package/dist/editor/test-setup.js.map +0 -1
- package/dist/editor/theme.d.ts.map +0 -1
- package/dist/editor/theme.js.map +0 -1
- package/dist/editor/utils/env.d.ts.map +0 -1
- package/dist/editor/utils/env.js.map +0 -1
- package/dist/entry-link-resolver.d.ts.map +0 -1
- package/dist/entry-link-resolver.js.map +0 -1
- package/dist/entry-schema-registry.d.ts.map +0 -1
- package/dist/entry-schema-registry.js.map +0 -1
- package/dist/entry-schema.d.ts.map +0 -1
- package/dist/entry-schema.js.map +0 -1
- package/dist/git-manager.d.ts.map +0 -1
- package/dist/git-manager.js.map +0 -1
- package/dist/github-service.d.ts.map +0 -1
- package/dist/github-service.js.map +0 -1
- package/dist/http/handler.d.ts.map +0 -1
- package/dist/http/handler.js.map +0 -1
- package/dist/http/index.d.ts.map +0 -1
- package/dist/http/index.js.map +0 -1
- package/dist/http/router.d.ts.map +0 -1
- package/dist/http/router.js.map +0 -1
- package/dist/http/types.d.ts.map +0 -1
- package/dist/http/types.js.map +0 -1
- package/dist/id.d.ts.map +0 -1
- package/dist/id.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/operating-mode/client-safe-strategy.d.ts.map +0 -1
- package/dist/operating-mode/client-safe-strategy.js.map +0 -1
- package/dist/operating-mode/client-unsafe-strategy.d.ts.map +0 -1
- package/dist/operating-mode/client-unsafe-strategy.js.map +0 -1
- package/dist/operating-mode/client.d.ts.map +0 -1
- package/dist/operating-mode/client.js.map +0 -1
- package/dist/operating-mode/deployment-name-fixtures.d.ts.map +0 -1
- package/dist/operating-mode/deployment-name-fixtures.js.map +0 -1
- package/dist/operating-mode/deployment-name.d.ts.map +0 -1
- package/dist/operating-mode/deployment-name.js.map +0 -1
- package/dist/operating-mode/index.d.ts.map +0 -1
- package/dist/operating-mode/index.js.map +0 -1
- package/dist/operating-mode/mode-env.d.ts.map +0 -1
- package/dist/operating-mode/mode-env.js.map +0 -1
- package/dist/operating-mode/types.d.ts.map +0 -1
- package/dist/operating-mode/types.js.map +0 -1
- package/dist/paths/branch-name.d.ts.map +0 -1
- package/dist/paths/branch-name.js.map +0 -1
- package/dist/paths/branch.d.ts.map +0 -1
- package/dist/paths/branch.js.map +0 -1
- package/dist/paths/index.d.ts.map +0 -1
- package/dist/paths/index.js.map +0 -1
- package/dist/paths/normalize-server.d.ts.map +0 -1
- package/dist/paths/normalize-server.js.map +0 -1
- package/dist/paths/normalize.d.ts.map +0 -1
- package/dist/paths/normalize.js.map +0 -1
- package/dist/paths/resolve.d.ts.map +0 -1
- package/dist/paths/resolve.js.map +0 -1
- package/dist/paths/test-utils.d.ts +0 -22
- package/dist/paths/test-utils.d.ts.map +0 -1
- package/dist/paths/test-utils.js +0 -21
- package/dist/paths/test-utils.js.map +0 -1
- package/dist/paths/types.d.ts.map +0 -1
- package/dist/paths/types.js.map +0 -1
- package/dist/paths/validation.d.ts.map +0 -1
- package/dist/paths/validation.js.map +0 -1
- package/dist/reference-resolver.d.ts.map +0 -1
- package/dist/reference-resolver.js.map +0 -1
- package/dist/resolve-canopy-user.d.ts.map +0 -1
- package/dist/resolve-canopy-user.js.map +0 -1
- package/dist/resource-generation.d.ts.map +0 -1
- package/dist/resource-generation.js.map +0 -1
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/index.js.map +0 -1
- package/dist/schema/meta-loader.d.ts.map +0 -1
- package/dist/schema/meta-loader.js.map +0 -1
- package/dist/schema/resolver.d.ts.map +0 -1
- package/dist/schema/resolver.js.map +0 -1
- package/dist/schema/schema-store-types.d.ts.map +0 -1
- package/dist/schema/schema-store-types.js.map +0 -1
- package/dist/schema/schema-store.d.ts.map +0 -1
- package/dist/schema/schema-store.js.map +0 -1
- package/dist/schema/types.d.ts.map +0 -1
- package/dist/schema/types.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/services.d.ts.map +0 -1
- package/dist/services.js.map +0 -1
- package/dist/settings-workspace.d.ts.map +0 -1
- package/dist/settings-workspace.js.map +0 -1
- package/dist/static/index.d.ts.map +0 -1
- package/dist/static/index.js.map +0 -1
- package/dist/sync-core.d.ts.map +0 -1
- package/dist/sync-core.js.map +0 -1
- package/dist/task-queue/index.d.ts.map +0 -1
- package/dist/task-queue/index.js.map +0 -1
- package/dist/task-queue/task-queue.d.ts.map +0 -1
- package/dist/task-queue/task-queue.js.map +0 -1
- package/dist/task-queue/types.d.ts.map +0 -1
- package/dist/task-queue/types.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/url-path-resolver.d.ts.map +0 -1
- package/dist/url-path-resolver.js.map +0 -1
- package/dist/user.d.ts.map +0 -1
- package/dist/user.js.map +0 -1
- package/dist/utils/async-mutex.d.ts.map +0 -1
- package/dist/utils/async-mutex.js.map +0 -1
- package/dist/utils/atomic-write.d.ts.map +0 -1
- package/dist/utils/atomic-write.js.map +0 -1
- package/dist/utils/body-field.d.ts.map +0 -1
- package/dist/utils/body-field.js.map +0 -1
- package/dist/utils/content-write-lock.d.ts.map +0 -1
- package/dist/utils/content-write-lock.js.map +0 -1
- package/dist/utils/debug.d.ts.map +0 -1
- package/dist/utils/debug.js.map +0 -1
- package/dist/utils/entry-url.d.ts.map +0 -1
- package/dist/utils/entry-url.js.map +0 -1
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js.map +0 -1
- package/dist/utils/flatten-group-fields.d.ts.map +0 -1
- package/dist/utils/flatten-group-fields.js.map +0 -1
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/format.js.map +0 -1
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/occ-json-write.d.ts.map +0 -1
- package/dist/utils/occ-json-write.js.map +0 -1
- package/dist/utils/provisioning-lock.d.ts.map +0 -1
- package/dist/utils/provisioning-lock.js.map +0 -1
- package/dist/utils/sanitize-href.d.ts.map +0 -1
- package/dist/utils/sanitize-href.js.map +0 -1
- package/dist/utils/title-field.d.ts.map +0 -1
- package/dist/utils/title-field.js.map +0 -1
- package/dist/validation/deletion-checker.d.ts.map +0 -1
- package/dist/validation/deletion-checker.js.map +0 -1
- package/dist/validation/entry-link-validator.d.ts.map +0 -1
- package/dist/validation/entry-link-validator.js.map +0 -1
- package/dist/validation/entry-type-reference-validator.d.ts.map +0 -1
- package/dist/validation/entry-type-reference-validator.js.map +0 -1
- package/dist/validation/entry-validator.d.ts.map +0 -1
- package/dist/validation/entry-validator.js.map +0 -1
- package/dist/validation/field-traversal.d.ts.map +0 -1
- package/dist/validation/field-traversal.js.map +0 -1
- package/dist/validation/reference-validator.d.ts.map +0 -1
- package/dist/validation/reference-validator.js.map +0 -1
- package/dist/worker/cms-worker.d.ts.map +0 -1
- package/dist/worker/cms-worker.js.map +0 -1
- package/dist/worker/log.d.ts.map +0 -1
- package/dist/worker/log.js.map +0 -1
- package/dist/worker/task-queue-config.d.ts.map +0 -1
- package/dist/worker/task-queue-config.js.map +0 -1
- package/dist/worker/task-queue.d.ts.map +0 -1
- package/dist/worker/task-queue.js.map +0 -1
- package/dist/worker/worker-status.d.ts.map +0 -1
- package/dist/worker/worker-status.js.map +0 -1
|
@@ -8,12 +8,27 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// dist/utils/logger.js
|
|
12
|
+
function canopyLogWarn(...args) {
|
|
13
|
+
active.warn(...args);
|
|
14
|
+
}
|
|
15
|
+
function canopyLogError(...args) {
|
|
16
|
+
active.error(...args);
|
|
17
|
+
}
|
|
18
|
+
var active;
|
|
19
|
+
var init_logger = __esm({
|
|
20
|
+
"dist/utils/logger.js"() {
|
|
21
|
+
"use strict";
|
|
22
|
+
active = console;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
11
26
|
// dist/paths/normalize.js
|
|
12
|
-
function normalizeFilesystemPath(
|
|
13
|
-
return
|
|
27
|
+
function normalizeFilesystemPath(path21) {
|
|
28
|
+
return path21.split(/[\\/]+/).filter(Boolean).join("/");
|
|
14
29
|
}
|
|
15
|
-
function hasTraversalSequence(
|
|
16
|
-
const normalized = normalizeFilesystemPath(
|
|
30
|
+
function hasTraversalSequence(path21) {
|
|
31
|
+
const normalized = normalizeFilesystemPath(path21);
|
|
17
32
|
return normalized.includes("..");
|
|
18
33
|
}
|
|
19
34
|
function createLogicalPath(...segments) {
|
|
@@ -23,14 +38,14 @@ function createLogicalPath(...segments) {
|
|
|
23
38
|
}
|
|
24
39
|
return normalized;
|
|
25
40
|
}
|
|
26
|
-
function trimSlashes(
|
|
41
|
+
function trimSlashes(path21) {
|
|
27
42
|
let start = 0;
|
|
28
|
-
let end =
|
|
29
|
-
while (start < end &&
|
|
43
|
+
let end = path21.length;
|
|
44
|
+
while (start < end && path21[start] === "/")
|
|
30
45
|
start++;
|
|
31
|
-
while (end > start &&
|
|
46
|
+
while (end > start && path21[end - 1] === "/")
|
|
32
47
|
end--;
|
|
33
|
-
return
|
|
48
|
+
return path21.slice(start, end);
|
|
34
49
|
}
|
|
35
50
|
var init_normalize = __esm({
|
|
36
51
|
"dist/paths/normalize.js"() {
|
|
@@ -38,21 +53,6 @@ var init_normalize = __esm({
|
|
|
38
53
|
}
|
|
39
54
|
});
|
|
40
55
|
|
|
41
|
-
// dist/utils/logger.js
|
|
42
|
-
function canopyLogWarn(...args) {
|
|
43
|
-
active.warn(...args);
|
|
44
|
-
}
|
|
45
|
-
function canopyLogError(...args) {
|
|
46
|
-
active.error(...args);
|
|
47
|
-
}
|
|
48
|
-
var active;
|
|
49
|
-
var init_logger = __esm({
|
|
50
|
-
"dist/utils/logger.js"() {
|
|
51
|
-
"use strict";
|
|
52
|
-
active = console;
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
56
|
// dist/paths/types.js
|
|
57
57
|
var ROOT_COLLECTION_ID;
|
|
58
58
|
var init_types = __esm({
|
|
@@ -239,7 +239,12 @@ var init_field = __esm({
|
|
|
239
239
|
collections: z2.array(z2.string().min(1)).min(1).optional(),
|
|
240
240
|
entryTypes: z2.array(z2.string().min(1)).min(1).optional(),
|
|
241
241
|
displayField: z2.string().min(1).optional(),
|
|
242
|
-
options: z2.array(referenceOptionSchema).optional()
|
|
242
|
+
options: z2.array(referenceOptionSchema).optional(),
|
|
243
|
+
// Must be listed here, not just on ReferenceFieldConfig: zod strips unknown keys by
|
|
244
|
+
// default, so a consumer that adopts this schema's parse output would silently delete a
|
|
245
|
+
// runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
|
|
246
|
+
// absent on purpose — it is type-inference-only and stripping it changes no behavior.)
|
|
247
|
+
includeBody: z2.boolean().optional()
|
|
243
248
|
});
|
|
244
249
|
fieldHolder = [z2.never()];
|
|
245
250
|
blockSchema = z2.object({
|
|
@@ -349,7 +354,7 @@ var init_media = __esm({
|
|
|
349
354
|
|
|
350
355
|
// dist/config/schemas/config.js
|
|
351
356
|
import { z as z5 } from "zod";
|
|
352
|
-
var defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE;
|
|
357
|
+
var defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE;
|
|
353
358
|
var init_config = __esm({
|
|
354
359
|
"dist/config/schemas/config.js"() {
|
|
355
360
|
"use strict";
|
|
@@ -373,6 +378,9 @@ var init_config = __esm({
|
|
|
373
378
|
deployedAsSchema = z5.enum(["static", "server"]).default("server");
|
|
374
379
|
contentRootSchema = relativePathSchema.default("content");
|
|
375
380
|
sourceRootSchema = z5.string().min(1).optional();
|
|
381
|
+
basePathSchema = z5.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
|
|
382
|
+
message: "basePath must not be empty or only whitespace"
|
|
383
|
+
}).optional();
|
|
376
384
|
deploymentNameSchema = z5.string().default("prod");
|
|
377
385
|
devContentSyncSchema = z5.enum(["off", "warn"]).default("warn");
|
|
378
386
|
devConfigSchema = z5.object({
|
|
@@ -435,6 +443,7 @@ var init_config = __esm({
|
|
|
435
443
|
deploymentName: deploymentNameSchema.optional(),
|
|
436
444
|
contentRoot: contentRootSchema.default("content"),
|
|
437
445
|
sourceRoot: sourceRootSchema.optional(),
|
|
446
|
+
basePath: basePathSchema,
|
|
438
447
|
editor: editorConfigSchema.optional(),
|
|
439
448
|
authPlugin: z5.custom().optional(),
|
|
440
449
|
entryLinkUrl: z5.custom().optional(),
|
|
@@ -672,7 +681,7 @@ var init_config2 = __esm({
|
|
|
672
681
|
});
|
|
673
682
|
|
|
674
683
|
// dist/operating-mode/client-unsafe-strategy.js
|
|
675
|
-
import
|
|
684
|
+
import path9 from "node:path";
|
|
676
685
|
function operatingStrategy(mode) {
|
|
677
686
|
const cached = strategyCache.get(mode);
|
|
678
687
|
if (cached)
|
|
@@ -710,32 +719,32 @@ var init_client_unsafe_strategy = __esm({
|
|
|
710
719
|
// - shouldCommit(), shouldPush()
|
|
711
720
|
// Add client-unsafe methods (use Node.js APIs)
|
|
712
721
|
getWorkspaceRoot(_sourceRoot) {
|
|
713
|
-
return
|
|
722
|
+
return path9.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE);
|
|
714
723
|
}
|
|
715
724
|
getContentRoot(contentRoot, sourceRoot) {
|
|
716
|
-
return
|
|
725
|
+
return path9.resolve(sourceRoot ?? process.cwd(), contentRoot);
|
|
717
726
|
}
|
|
718
727
|
getContentBranchesRoot(sourceRoot) {
|
|
719
|
-
return
|
|
728
|
+
return path9.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
|
|
720
729
|
}
|
|
721
730
|
getContentBranchRoot(branchName, sourceRoot) {
|
|
722
|
-
return
|
|
731
|
+
return path9.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
|
|
723
732
|
}
|
|
724
733
|
getGitExcludePattern() {
|
|
725
734
|
return ".canopy-meta/";
|
|
726
735
|
}
|
|
727
736
|
getPermissionsFilePath(root) {
|
|
728
|
-
return
|
|
737
|
+
return path9.join(root, this.getPermissionsFileName());
|
|
729
738
|
}
|
|
730
739
|
getGroupsFilePath(root) {
|
|
731
|
-
return
|
|
740
|
+
return path9.join(root, this.getGroupsFileName());
|
|
732
741
|
}
|
|
733
742
|
getRemoteUrlConfig() {
|
|
734
743
|
return {
|
|
735
744
|
shouldAutoInitLocal: false,
|
|
736
745
|
defaultRemotePath: "",
|
|
737
746
|
envVarName: "CANOPYCMS_REMOTE_URL",
|
|
738
|
-
autoDetectRemotePath:
|
|
747
|
+
autoDetectRemotePath: path9.join(this.getWorkspaceRoot(), "remote.git")
|
|
739
748
|
};
|
|
740
749
|
}
|
|
741
750
|
requiresExistingRepo() {
|
|
@@ -747,7 +756,7 @@ var init_client_unsafe_strategy = __esm({
|
|
|
747
756
|
return `canopycms-settings-${resolveDeploymentName(config, "prod")}`;
|
|
748
757
|
}
|
|
749
758
|
getSettingsRoot(sourceRoot) {
|
|
750
|
-
return
|
|
759
|
+
return path9.join(this.getWorkspaceRoot(sourceRoot), "settings");
|
|
751
760
|
}
|
|
752
761
|
usesSeparateSettingsBranch() {
|
|
753
762
|
return true;
|
|
@@ -764,25 +773,25 @@ var init_client_unsafe_strategy = __esm({
|
|
|
764
773
|
DevStrategy = class extends DevClientSafeStrategy {
|
|
765
774
|
// Inherits client-safe methods from DevClientSafeStrategy
|
|
766
775
|
getWorkspaceRoot(sourceRoot) {
|
|
767
|
-
return
|
|
776
|
+
return path9.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
|
|
768
777
|
}
|
|
769
778
|
getContentRoot(contentRoot, sourceRoot) {
|
|
770
|
-
return
|
|
779
|
+
return path9.resolve(sourceRoot ?? process.cwd(), contentRoot);
|
|
771
780
|
}
|
|
772
781
|
getContentBranchesRoot(sourceRoot) {
|
|
773
|
-
return
|
|
782
|
+
return path9.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
|
|
774
783
|
}
|
|
775
784
|
getContentBranchRoot(branchName, sourceRoot) {
|
|
776
|
-
return
|
|
785
|
+
return path9.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
|
|
777
786
|
}
|
|
778
787
|
getGitExcludePattern() {
|
|
779
788
|
return ".canopy-meta/";
|
|
780
789
|
}
|
|
781
790
|
getPermissionsFilePath(root) {
|
|
782
|
-
return
|
|
791
|
+
return path9.join(root, this.getPermissionsFileName());
|
|
783
792
|
}
|
|
784
793
|
getGroupsFilePath(root) {
|
|
785
|
-
return
|
|
794
|
+
return path9.join(root, this.getGroupsFileName());
|
|
786
795
|
}
|
|
787
796
|
getRemoteUrlConfig() {
|
|
788
797
|
return {
|
|
@@ -800,7 +809,7 @@ var init_client_unsafe_strategy = __esm({
|
|
|
800
809
|
return `canopycms-settings-${resolveDeploymentName(config, "local")}`;
|
|
801
810
|
}
|
|
802
811
|
getSettingsRoot(sourceRoot) {
|
|
803
|
-
return
|
|
812
|
+
return path9.join(this.getWorkspaceRoot(sourceRoot), "settings");
|
|
804
813
|
}
|
|
805
814
|
usesSeparateSettingsBranch() {
|
|
806
815
|
return true;
|
|
@@ -839,18 +848,38 @@ var init_operating_mode = __esm({
|
|
|
839
848
|
});
|
|
840
849
|
|
|
841
850
|
// dist/cli/generate-ai-content.js
|
|
842
|
-
import
|
|
851
|
+
import path20 from "node:path";
|
|
843
852
|
import { createJiti } from "jiti";
|
|
844
853
|
|
|
845
854
|
// dist/build/generate-ai-content.js
|
|
846
|
-
import
|
|
847
|
-
import
|
|
855
|
+
import fs17 from "node:fs/promises";
|
|
856
|
+
import path19 from "node:path";
|
|
857
|
+
|
|
858
|
+
// dist/utils/error.js
|
|
859
|
+
function getErrorMessage(err) {
|
|
860
|
+
if (err instanceof Error) {
|
|
861
|
+
return err.message;
|
|
862
|
+
}
|
|
863
|
+
if (typeof err === "string") {
|
|
864
|
+
return err;
|
|
865
|
+
}
|
|
866
|
+
return String(err);
|
|
867
|
+
}
|
|
868
|
+
function isNodeError(err) {
|
|
869
|
+
return err instanceof Error && "code" in err;
|
|
870
|
+
}
|
|
871
|
+
function isNotFoundError(err) {
|
|
872
|
+
return isNodeError(err) && err.code === "ENOENT";
|
|
873
|
+
}
|
|
874
|
+
function isFileExistsError(err) {
|
|
875
|
+
return isNodeError(err) && err.code === "EEXIST";
|
|
876
|
+
}
|
|
848
877
|
|
|
849
878
|
// dist/content-store.js
|
|
850
|
-
import
|
|
851
|
-
import
|
|
852
|
-
import
|
|
853
|
-
import { parse as yamlParse
|
|
879
|
+
import fs10 from "node:fs/promises";
|
|
880
|
+
import path11 from "node:path";
|
|
881
|
+
import matter2 from "gray-matter";
|
|
882
|
+
import { parse as yamlParse } from "yaml";
|
|
854
883
|
|
|
855
884
|
// dist/utils/atomic-write.js
|
|
856
885
|
import fs from "node:fs/promises";
|
|
@@ -869,6 +898,207 @@ async function atomicWriteFile(filePath, content) {
|
|
|
869
898
|
}
|
|
870
899
|
}
|
|
871
900
|
|
|
901
|
+
// dist/utils/content-serialize.js
|
|
902
|
+
import matter from "gray-matter";
|
|
903
|
+
import { isCollection, isMap, isNode, isScalar, isSeq, parseDocument, stringify as yamlStringify } from "yaml";
|
|
904
|
+
|
|
905
|
+
// dist/validation/block-structural-keys.js
|
|
906
|
+
var BLOCK_DISCRIMINATOR_KEYS = ["template", "_type"];
|
|
907
|
+
var BLOCK_STRUCTURAL_KEYS = new Set(BLOCK_DISCRIMINATOR_KEYS);
|
|
908
|
+
function isBlockStructuralKey(key) {
|
|
909
|
+
return BLOCK_STRUCTURAL_KEYS.has(key);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// dist/utils/content-serialize.js
|
|
913
|
+
function isPlainRecord(value) {
|
|
914
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
915
|
+
return false;
|
|
916
|
+
const proto = Object.getPrototypeOf(value);
|
|
917
|
+
return proto === Object.prototype || proto === null;
|
|
918
|
+
}
|
|
919
|
+
function recordKeyOf(keyNode) {
|
|
920
|
+
if (!isScalar(keyNode))
|
|
921
|
+
return void 0;
|
|
922
|
+
const { value } = keyNode;
|
|
923
|
+
if (value === null || value === void 0)
|
|
924
|
+
return void 0;
|
|
925
|
+
if (typeof value === "object")
|
|
926
|
+
return void 0;
|
|
927
|
+
return String(value);
|
|
928
|
+
}
|
|
929
|
+
function carryComments(from, to) {
|
|
930
|
+
if (!isNode(from) || !isNode(to))
|
|
931
|
+
return;
|
|
932
|
+
const source = from;
|
|
933
|
+
const target = to;
|
|
934
|
+
if (source.commentBefore !== void 0)
|
|
935
|
+
target.commentBefore = source.commentBefore;
|
|
936
|
+
if (source.comment !== void 0)
|
|
937
|
+
target.comment = source.comment;
|
|
938
|
+
if (source.spaceBefore !== void 0)
|
|
939
|
+
target.spaceBefore = source.spaceBefore;
|
|
940
|
+
}
|
|
941
|
+
function identityKey(value) {
|
|
942
|
+
try {
|
|
943
|
+
return JSON.stringify(value) ?? "undefined";
|
|
944
|
+
} catch {
|
|
945
|
+
return void 0;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
function nodePlainValue(node) {
|
|
949
|
+
if (!isNode(node))
|
|
950
|
+
return node;
|
|
951
|
+
try {
|
|
952
|
+
return node.toJSON();
|
|
953
|
+
} catch {
|
|
954
|
+
return void 0;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
function nodeIdentityKey(node) {
|
|
958
|
+
const plain = nodePlainValue(node);
|
|
959
|
+
return plain === void 0 && isNode(node) ? void 0 : identityKey(plain);
|
|
960
|
+
}
|
|
961
|
+
var EVIDENCE_MAX_DEPTH = 6;
|
|
962
|
+
function sharesFieldEvidence(existing, value, depth) {
|
|
963
|
+
for (const key of Object.keys(value)) {
|
|
964
|
+
if (isBlockStructuralKey(key))
|
|
965
|
+
continue;
|
|
966
|
+
if (!Object.prototype.hasOwnProperty.call(existing, key))
|
|
967
|
+
continue;
|
|
968
|
+
const before = existing[key];
|
|
969
|
+
const after = value[key];
|
|
970
|
+
if (depth < EVIDENCE_MAX_DEPTH && isPlainRecord(before) && isPlainRecord(after) && sharesFieldEvidence(before, after, depth + 1)) {
|
|
971
|
+
return true;
|
|
972
|
+
}
|
|
973
|
+
const beforeKey = identityKey(before);
|
|
974
|
+
if (beforeKey !== void 0 && beforeKey === identityKey(after))
|
|
975
|
+
return true;
|
|
976
|
+
}
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
function looksLikeSameItem(node, value) {
|
|
980
|
+
const existing = nodePlainValue(node);
|
|
981
|
+
if (!isPlainRecord(existing) || !isPlainRecord(value))
|
|
982
|
+
return true;
|
|
983
|
+
return sharesFieldEvidence(existing, value, 0);
|
|
984
|
+
}
|
|
985
|
+
function reconcileNode(doc, existing, value) {
|
|
986
|
+
if (isMap(existing) && isPlainRecord(value)) {
|
|
987
|
+
reconcileMap(doc, existing, value);
|
|
988
|
+
return existing;
|
|
989
|
+
}
|
|
990
|
+
if (isSeq(existing) && Array.isArray(value)) {
|
|
991
|
+
reconcileSeq(doc, existing, value);
|
|
992
|
+
return existing;
|
|
993
|
+
}
|
|
994
|
+
if (isScalar(existing) && Object.is(existing.value, value))
|
|
995
|
+
return existing;
|
|
996
|
+
const fresh = doc.createNode(value);
|
|
997
|
+
if (!isCollection(existing))
|
|
998
|
+
carryComments(existing, fresh);
|
|
999
|
+
return fresh;
|
|
1000
|
+
}
|
|
1001
|
+
function reconcileMap(doc, map, value) {
|
|
1002
|
+
const wanted = new Set(Object.keys(value).filter((key) => value[key] !== void 0));
|
|
1003
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1004
|
+
const retained = [];
|
|
1005
|
+
for (const pair of map.items) {
|
|
1006
|
+
const key = recordKeyOf(pair.key);
|
|
1007
|
+
if (key === void 0 || !wanted.has(key) || seen.has(key))
|
|
1008
|
+
continue;
|
|
1009
|
+
seen.add(key);
|
|
1010
|
+
pair.value = reconcileNode(doc, pair.value, value[key]);
|
|
1011
|
+
retained.push(pair);
|
|
1012
|
+
}
|
|
1013
|
+
map.items = retained;
|
|
1014
|
+
for (const key of Object.keys(value)) {
|
|
1015
|
+
if (seen.has(key) || !wanted.has(key))
|
|
1016
|
+
continue;
|
|
1017
|
+
map.set(doc.createNode(key), doc.createNode(value[key]));
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
function reconcileSeq(doc, seq, value) {
|
|
1021
|
+
const oldItems = seq.items;
|
|
1022
|
+
const byIdentity = /* @__PURE__ */ new Map();
|
|
1023
|
+
oldItems.forEach((node, index) => {
|
|
1024
|
+
const key = nodeIdentityKey(node);
|
|
1025
|
+
if (key === void 0)
|
|
1026
|
+
return;
|
|
1027
|
+
const bucket = byIdentity.get(key);
|
|
1028
|
+
if (bucket)
|
|
1029
|
+
bucket.push(index);
|
|
1030
|
+
else
|
|
1031
|
+
byIdentity.set(key, [index]);
|
|
1032
|
+
});
|
|
1033
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
1034
|
+
const matches = value.map((item) => {
|
|
1035
|
+
const key = identityKey(item);
|
|
1036
|
+
if (key === void 0)
|
|
1037
|
+
return void 0;
|
|
1038
|
+
const bucket = byIdentity.get(key);
|
|
1039
|
+
while (bucket && bucket.length > 0) {
|
|
1040
|
+
const index = bucket.shift();
|
|
1041
|
+
if (index !== void 0 && !consumed.has(index)) {
|
|
1042
|
+
consumed.add(index);
|
|
1043
|
+
return index;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return void 0;
|
|
1047
|
+
});
|
|
1048
|
+
seq.items = value.map((item, index) => {
|
|
1049
|
+
const matched = matches[index];
|
|
1050
|
+
if (matched !== void 0)
|
|
1051
|
+
return oldItems[matched];
|
|
1052
|
+
const candidate = index < oldItems.length && !consumed.has(index) ? oldItems[index] : void 0;
|
|
1053
|
+
const sameItem = candidate !== void 0 && looksLikeSameItem(candidate, item) ? candidate : void 0;
|
|
1054
|
+
return reconcileNode(doc, sameItem, item);
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
function applyDataToDocument(doc, data) {
|
|
1058
|
+
doc.contents = reconcileNode(doc, doc.contents, data);
|
|
1059
|
+
}
|
|
1060
|
+
function serializeYaml(data, existingRaw) {
|
|
1061
|
+
if (existingRaw === void 0)
|
|
1062
|
+
return yamlStringify(data);
|
|
1063
|
+
const doc = parseDocument(existingRaw);
|
|
1064
|
+
if (doc.errors.length > 0)
|
|
1065
|
+
return yamlStringify(data);
|
|
1066
|
+
applyDataToDocument(doc, data);
|
|
1067
|
+
return doc.toString();
|
|
1068
|
+
}
|
|
1069
|
+
function extractRawFrontmatter(raw) {
|
|
1070
|
+
let parsed;
|
|
1071
|
+
try {
|
|
1072
|
+
parsed = matter(raw, {});
|
|
1073
|
+
} catch {
|
|
1074
|
+
return void 0;
|
|
1075
|
+
}
|
|
1076
|
+
const frontmatter = parsed.matter;
|
|
1077
|
+
if (typeof frontmatter !== "string" || frontmatter.trim() === "")
|
|
1078
|
+
return void 0;
|
|
1079
|
+
return frontmatter;
|
|
1080
|
+
}
|
|
1081
|
+
function serializeFrontmatter(body, data, existingRaw) {
|
|
1082
|
+
if (existingRaw === void 0)
|
|
1083
|
+
return matter.stringify(body, data);
|
|
1084
|
+
const existingFrontmatter = extractRawFrontmatter(existingRaw);
|
|
1085
|
+
if (existingFrontmatter === void 0)
|
|
1086
|
+
return matter.stringify(body, data);
|
|
1087
|
+
const doc = parseDocument(existingFrontmatter);
|
|
1088
|
+
if (doc.errors.length > 0)
|
|
1089
|
+
return matter.stringify(body, data);
|
|
1090
|
+
applyDataToDocument(doc, data);
|
|
1091
|
+
const reconciled = doc.toString();
|
|
1092
|
+
return matter.stringify(body, data, {
|
|
1093
|
+
engines: {
|
|
1094
|
+
yaml: {
|
|
1095
|
+
parse: () => ({}),
|
|
1096
|
+
stringify: () => reconciled
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
|
|
872
1102
|
// dist/utils/async-mutex.js
|
|
873
1103
|
var locks = /* @__PURE__ */ new Map();
|
|
874
1104
|
async function withLock(key, fn) {
|
|
@@ -890,46 +1120,66 @@ async function withLock(key, fn) {
|
|
|
890
1120
|
|
|
891
1121
|
// dist/utils/content-write-lock.js
|
|
892
1122
|
import path3 from "node:path";
|
|
893
|
-
|
|
894
|
-
// dist/utils/error.js
|
|
895
|
-
function getErrorMessage(err) {
|
|
896
|
-
if (err instanceof Error) {
|
|
897
|
-
return err.message;
|
|
898
|
-
}
|
|
899
|
-
if (typeof err === "string") {
|
|
900
|
-
return err;
|
|
901
|
-
}
|
|
902
|
-
return String(err);
|
|
903
|
-
}
|
|
904
|
-
function isNodeError(err) {
|
|
905
|
-
return err instanceof Error && "code" in err;
|
|
906
|
-
}
|
|
907
|
-
function isNotFoundError(err) {
|
|
908
|
-
return isNodeError(err) && err.code === "ENOENT";
|
|
909
|
-
}
|
|
910
|
-
function isFileExistsError(err) {
|
|
911
|
-
return isNodeError(err) && err.code === "EEXIST";
|
|
912
|
-
}
|
|
1123
|
+
init_logger();
|
|
913
1124
|
|
|
914
1125
|
// dist/utils/provisioning-lock.js
|
|
915
1126
|
import fs2 from "node:fs/promises";
|
|
916
1127
|
import path2 from "node:path";
|
|
917
1128
|
import lockfile from "proper-lockfile";
|
|
918
|
-
|
|
1129
|
+
init_logger();
|
|
1130
|
+
function provisioningLockOptions(lockPath, retries, onCompromised) {
|
|
1131
|
+
return {
|
|
1132
|
+
lockfilePath: lockPath,
|
|
1133
|
+
realpath: false,
|
|
1134
|
+
retries,
|
|
1135
|
+
stale: 3e4,
|
|
1136
|
+
// proper-lockfile invokes this from inside its refresh timer, so ANY throw
|
|
1137
|
+
// escaping here is an uncaught exception that kills the process -- the very
|
|
1138
|
+
// failure the handler exists to prevent. Call sites are told not to throw
|
|
1139
|
+
// (see OnLockCompromised); this makes it structural rather than a
|
|
1140
|
+
// convention, and also covers the LOGGER throwing: under `CI=true`,
|
|
1141
|
+
// vitest's `onConsoleLog` turns any console write into a throw.
|
|
1142
|
+
onCompromised: (err) => {
|
|
1143
|
+
try {
|
|
1144
|
+
if (onCompromised) {
|
|
1145
|
+
onCompromised(err);
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
canopyLogWarn(`[canopy] Provisioning lock compromised mid-hold for ${lockPath}:`, getErrorMessage(err));
|
|
1149
|
+
} catch {
|
|
1150
|
+
try {
|
|
1151
|
+
process.stderr.write(`[canopy] lock compromised for ${lockPath}; its handler or logger threw
|
|
1152
|
+
`);
|
|
1153
|
+
} catch {
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
function releaseIgnoringAlreadyReleased(release, lockPath) {
|
|
1160
|
+
return async () => {
|
|
1161
|
+
try {
|
|
1162
|
+
await release();
|
|
1163
|
+
} catch (err) {
|
|
1164
|
+
if (isNodeError(err) && err.code === "ERELEASED") {
|
|
1165
|
+
canopyLogWarn(`[canopy] Lock at ${lockPath} was already released (compromised mid-hold, or released twice); nothing to release`);
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
throw err;
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
async function acquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
|
|
919
1173
|
await fs2.mkdir(lockTargetDir2, { recursive: true });
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
stale: 3e4
|
|
924
|
-
});
|
|
1174
|
+
const lockPath = path2.join(lockTargetDir2, lockName);
|
|
1175
|
+
const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, { retries: 600, factor: 1, minTimeout: 300, maxTimeout: 800, randomize: true }, onCompromised));
|
|
1176
|
+
return releaseIgnoringAlreadyReleased(release, lockPath);
|
|
925
1177
|
}
|
|
926
|
-
async function tryAcquireProvisioningLock(lockTargetDir2, lockName) {
|
|
1178
|
+
async function tryAcquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
|
|
927
1179
|
await fs2.mkdir(lockTargetDir2, { recursive: true });
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
stale: 3e4
|
|
932
|
-
});
|
|
1180
|
+
const lockPath = path2.join(lockTargetDir2, lockName);
|
|
1181
|
+
const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, 0, onCompromised));
|
|
1182
|
+
return releaseIgnoringAlreadyReleased(release, lockPath);
|
|
933
1183
|
}
|
|
934
1184
|
|
|
935
1185
|
// dist/utils/content-write-lock.js
|
|
@@ -948,14 +1198,14 @@ function lockTargetDir(branchRoot) {
|
|
|
948
1198
|
var sleep = (ms) => new Promise((resolve2) => {
|
|
949
1199
|
setTimeout(resolve2, ms);
|
|
950
1200
|
});
|
|
951
|
-
function tryAcquireContentWriteLock(branchRoot) {
|
|
952
|
-
return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME);
|
|
1201
|
+
function tryAcquireContentWriteLock(branchRoot, onCompromised) {
|
|
1202
|
+
return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME, onCompromised);
|
|
953
1203
|
}
|
|
954
|
-
async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS) {
|
|
1204
|
+
async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, onCompromised) {
|
|
955
1205
|
const startedAt = Date.now();
|
|
956
1206
|
for (let attempt = 0; ; attempt++) {
|
|
957
1207
|
try {
|
|
958
|
-
return await tryAcquireContentWriteLock(branchRoot);
|
|
1208
|
+
return await tryAcquireContentWriteLock(branchRoot, onCompromised);
|
|
959
1209
|
} catch (err) {
|
|
960
1210
|
if (!isNodeError(err) || err.code !== "ELOCKED")
|
|
961
1211
|
throw err;
|
|
@@ -968,12 +1218,21 @@ async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRIT
|
|
|
968
1218
|
}
|
|
969
1219
|
}
|
|
970
1220
|
async function withContentWriteLock(branchRoot, fn, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS) {
|
|
971
|
-
|
|
1221
|
+
let compromised = false;
|
|
1222
|
+
const release = await acquireContentWriteLock(branchRoot, waitMs, (err) => {
|
|
1223
|
+
compromised = true;
|
|
1224
|
+
canopyLogWarn(`[canopy] Content-write lock compromised mid-hold for ${branchRoot}:`, getErrorMessage(err));
|
|
1225
|
+
});
|
|
1226
|
+
let result;
|
|
972
1227
|
try {
|
|
973
|
-
|
|
1228
|
+
result = await fn();
|
|
974
1229
|
} finally {
|
|
975
1230
|
await release();
|
|
976
1231
|
}
|
|
1232
|
+
if (compromised) {
|
|
1233
|
+
throw new ContentWriteLockBusyError("This branch was being synced while your change was written, so the change may or may not have been recorded. Reload the entry to see the current state before saving again.");
|
|
1234
|
+
}
|
|
1235
|
+
return result;
|
|
977
1236
|
}
|
|
978
1237
|
|
|
979
1238
|
// dist/utils/flatten-group-fields.js
|
|
@@ -998,6 +1257,105 @@ function findBodyFieldName(fields) {
|
|
|
998
1257
|
return "body";
|
|
999
1258
|
}
|
|
1000
1259
|
|
|
1260
|
+
// dist/static/seo.js
|
|
1261
|
+
var DEFAULT_SEO_FIELD_NAMES = {
|
|
1262
|
+
title: "metaTitle",
|
|
1263
|
+
description: "metaDescription",
|
|
1264
|
+
ogImage: "ogImage",
|
|
1265
|
+
ogType: "ogType",
|
|
1266
|
+
canonical: "canonical",
|
|
1267
|
+
noindex: "noindex",
|
|
1268
|
+
twitterCard: "twitterCard"
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
// dist/entry-schema.js
|
|
1272
|
+
function buildResolvedReference(data, meta, body) {
|
|
1273
|
+
const resolved = { ...data };
|
|
1274
|
+
if (body && body.value)
|
|
1275
|
+
resolved[body.fieldName] = body.value;
|
|
1276
|
+
resolved.id = meta.id;
|
|
1277
|
+
resolved.slug = meta.slug;
|
|
1278
|
+
resolved.collection = meta.collection;
|
|
1279
|
+
resolved.urlPath = meta.urlPath;
|
|
1280
|
+
return resolved;
|
|
1281
|
+
}
|
|
1282
|
+
var SEO_GROUP_FIELDS = [
|
|
1283
|
+
{
|
|
1284
|
+
name: DEFAULT_SEO_FIELD_NAMES.title,
|
|
1285
|
+
type: "string",
|
|
1286
|
+
label: "Meta Title",
|
|
1287
|
+
description: "Overrides the page title in search results and social cards.",
|
|
1288
|
+
required: false
|
|
1289
|
+
},
|
|
1290
|
+
{
|
|
1291
|
+
name: DEFAULT_SEO_FIELD_NAMES.description,
|
|
1292
|
+
type: "string",
|
|
1293
|
+
label: "Meta Description",
|
|
1294
|
+
description: "Summary shown under the title in search results.",
|
|
1295
|
+
required: false
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
name: DEFAULT_SEO_FIELD_NAMES.ogImage,
|
|
1299
|
+
type: "string",
|
|
1300
|
+
label: "Social Image URL",
|
|
1301
|
+
description: "Image for social cards. Site-relative or absolute.",
|
|
1302
|
+
required: false
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
name: DEFAULT_SEO_FIELD_NAMES.ogType,
|
|
1306
|
+
type: "select",
|
|
1307
|
+
label: "OpenGraph Type",
|
|
1308
|
+
options: ["website", "article", "profile"],
|
|
1309
|
+
required: false
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
name: DEFAULT_SEO_FIELD_NAMES.canonical,
|
|
1313
|
+
type: "string",
|
|
1314
|
+
label: "Canonical URL",
|
|
1315
|
+
description: "Set only to point at a different canonical copy of this page.",
|
|
1316
|
+
required: false
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
name: DEFAULT_SEO_FIELD_NAMES.noindex,
|
|
1320
|
+
type: "boolean",
|
|
1321
|
+
label: "Hide from search engines",
|
|
1322
|
+
description: "Marks the page noindex AND drops it from the sitemap.",
|
|
1323
|
+
required: false
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
name: DEFAULT_SEO_FIELD_NAMES.twitterCard,
|
|
1327
|
+
type: "select",
|
|
1328
|
+
label: "Twitter Card",
|
|
1329
|
+
options: ["summary", "summary_large_image"],
|
|
1330
|
+
required: false
|
|
1331
|
+
}
|
|
1332
|
+
];
|
|
1333
|
+
|
|
1334
|
+
// dist/utils/entry-url.js
|
|
1335
|
+
init_normalize();
|
|
1336
|
+
function isIndexSlug(slug) {
|
|
1337
|
+
return slug?.toLowerCase() === "index";
|
|
1338
|
+
}
|
|
1339
|
+
function computeEntryUrl(collection, slug, contentRoot) {
|
|
1340
|
+
const root = trimSlashes(contentRoot);
|
|
1341
|
+
let stripped = collection;
|
|
1342
|
+
if (root && collection.startsWith(`${root}/`)) {
|
|
1343
|
+
stripped = collection.slice(root.length + 1);
|
|
1344
|
+
} else if (collection === root) {
|
|
1345
|
+
stripped = "";
|
|
1346
|
+
}
|
|
1347
|
+
const segments = stripped.split("/").filter(Boolean);
|
|
1348
|
+
if (slug && !isIndexSlug(slug)) {
|
|
1349
|
+
segments.push(slug);
|
|
1350
|
+
}
|
|
1351
|
+
const path21 = segments.length > 0 ? `/${segments.join("/")}` : "/";
|
|
1352
|
+
return path21.toLowerCase();
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// dist/url-collision.js
|
|
1356
|
+
import fs4 from "node:fs/promises";
|
|
1357
|
+
import path5 from "node:path";
|
|
1358
|
+
|
|
1001
1359
|
// dist/content-id-index.js
|
|
1002
1360
|
import fs3 from "node:fs/promises";
|
|
1003
1361
|
import path4 from "node:path";
|
|
@@ -1013,6 +1371,37 @@ var PHYSICAL_SEGMENT_PATTERN = new RegExp(`\\.${BASE58_PATTERN}{12}(?:\\.[a-z]+)
|
|
|
1013
1371
|
function isValidContentId(id) {
|
|
1014
1372
|
return CONTENT_ID_PATTERN.test(id);
|
|
1015
1373
|
}
|
|
1374
|
+
function parseSlug(slug) {
|
|
1375
|
+
if (!slug) {
|
|
1376
|
+
return {
|
|
1377
|
+
ok: false,
|
|
1378
|
+
error: "Slug is required"
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
if (slug.length > 64) {
|
|
1382
|
+
return { ok: false, error: "Slug too long (max 64 characters)" };
|
|
1383
|
+
}
|
|
1384
|
+
if (slug.includes("/") || slug.includes("\\")) {
|
|
1385
|
+
return {
|
|
1386
|
+
ok: false,
|
|
1387
|
+
error: "Slug cannot contain path separators"
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
if (slug === "." || slug === "..") {
|
|
1391
|
+
return {
|
|
1392
|
+
ok: false,
|
|
1393
|
+
error: "Slug cannot be a traversal sequence"
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
const normalized = slug.toLowerCase();
|
|
1397
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(normalized)) {
|
|
1398
|
+
return {
|
|
1399
|
+
ok: false,
|
|
1400
|
+
error: "Slug must start with a letter or number and contain only lowercase letters, numbers, and hyphens"
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
return { ok: true, slug: normalized };
|
|
1404
|
+
}
|
|
1016
1405
|
|
|
1017
1406
|
// dist/id.js
|
|
1018
1407
|
function generateId() {
|
|
@@ -1357,13 +1746,13 @@ function extractIdFromFilename(filename) {
|
|
|
1357
1746
|
return null;
|
|
1358
1747
|
}
|
|
1359
1748
|
async function resolveCollectionPath(root, logicalPath) {
|
|
1360
|
-
const
|
|
1361
|
-
const
|
|
1749
|
+
const fs18 = await import("node:fs/promises");
|
|
1750
|
+
const path21 = await import("node:path");
|
|
1362
1751
|
const segments = logicalPath.split("/").filter(Boolean);
|
|
1363
1752
|
let currentPath = root;
|
|
1364
1753
|
for (const segment of segments) {
|
|
1365
1754
|
try {
|
|
1366
|
-
const entries = await
|
|
1755
|
+
const entries = await fs18.readdir(currentPath, { withFileTypes: true });
|
|
1367
1756
|
const matchingDir = entries.find((entry) => {
|
|
1368
1757
|
if (!entry.isDirectory())
|
|
1369
1758
|
return false;
|
|
@@ -1371,7 +1760,7 @@ async function resolveCollectionPath(root, logicalPath) {
|
|
|
1371
1760
|
return logicalName === segment.toLowerCase();
|
|
1372
1761
|
});
|
|
1373
1762
|
if (matchingDir) {
|
|
1374
|
-
currentPath =
|
|
1763
|
+
currentPath = path21.join(currentPath, matchingDir.name);
|
|
1375
1764
|
} else {
|
|
1376
1765
|
return null;
|
|
1377
1766
|
}
|
|
@@ -1421,8 +1810,99 @@ function extractSlugFromFilename(filename, entryTypeName) {
|
|
|
1421
1810
|
return filename.toLowerCase();
|
|
1422
1811
|
}
|
|
1423
1812
|
|
|
1813
|
+
// dist/utils/typed-filename.js
|
|
1814
|
+
var parseTypedFilename = (filename, entryTypes) => {
|
|
1815
|
+
if (filename.startsWith("."))
|
|
1816
|
+
return null;
|
|
1817
|
+
const lastDot = filename.lastIndexOf(".");
|
|
1818
|
+
if (lastDot === -1)
|
|
1819
|
+
return null;
|
|
1820
|
+
const nameWithoutExt = filename.slice(0, lastDot);
|
|
1821
|
+
const parts = nameWithoutExt.split(".");
|
|
1822
|
+
if (parts.length < 3)
|
|
1823
|
+
return null;
|
|
1824
|
+
const potentialType = parts[0];
|
|
1825
|
+
if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
|
|
1826
|
+
return null;
|
|
1827
|
+
}
|
|
1828
|
+
const id = parts[parts.length - 1];
|
|
1829
|
+
if (!isValidId(id))
|
|
1830
|
+
return null;
|
|
1831
|
+
const slug = parts.slice(1, -1).join(".").toLowerCase();
|
|
1832
|
+
return {
|
|
1833
|
+
type: potentialType,
|
|
1834
|
+
slug,
|
|
1835
|
+
id
|
|
1836
|
+
};
|
|
1837
|
+
};
|
|
1838
|
+
|
|
1839
|
+
// dist/url-collision.js
|
|
1840
|
+
var CONTENT_EXTENSIONS = [".md", ".mdx", ".json", ".yaml"];
|
|
1841
|
+
async function readDirSafe(dir) {
|
|
1842
|
+
try {
|
|
1843
|
+
return await fs4.readdir(dir, { withFileTypes: true });
|
|
1844
|
+
} catch (err) {
|
|
1845
|
+
if (isNotFoundError(err))
|
|
1846
|
+
return [];
|
|
1847
|
+
throw err;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
async function findChildCollectionDir(dir, name) {
|
|
1851
|
+
const wanted = name.toLowerCase();
|
|
1852
|
+
for (const entry of await readDirSafe(dir)) {
|
|
1853
|
+
if (!entry.isDirectory())
|
|
1854
|
+
continue;
|
|
1855
|
+
if (extractSlugFromFilename(entry.name) === wanted)
|
|
1856
|
+
return path5.join(dir, entry.name);
|
|
1857
|
+
}
|
|
1858
|
+
return null;
|
|
1859
|
+
}
|
|
1860
|
+
function entrySlugOf(filename) {
|
|
1861
|
+
if (!CONTENT_EXTENSIONS.some((ext) => filename.endsWith(ext)))
|
|
1862
|
+
return null;
|
|
1863
|
+
return parseTypedFilename(filename)?.slug ?? null;
|
|
1864
|
+
}
|
|
1865
|
+
async function findIndexEntryIn(dir) {
|
|
1866
|
+
for (const entry of await readDirSafe(dir)) {
|
|
1867
|
+
if (entry.isDirectory())
|
|
1868
|
+
continue;
|
|
1869
|
+
if (isIndexSlug(entrySlugOf(entry.name) ?? void 0))
|
|
1870
|
+
return path5.join(dir, entry.name);
|
|
1871
|
+
}
|
|
1872
|
+
return null;
|
|
1873
|
+
}
|
|
1874
|
+
async function findEntryBySlugIn(dir, slug) {
|
|
1875
|
+
const wanted = slug.toLowerCase();
|
|
1876
|
+
for (const entry of await readDirSafe(dir)) {
|
|
1877
|
+
if (entry.isDirectory())
|
|
1878
|
+
continue;
|
|
1879
|
+
if (entrySlugOf(entry.name) === wanted)
|
|
1880
|
+
return path5.join(dir, entry.name);
|
|
1881
|
+
}
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
async function findUrlPathClaimant(opts) {
|
|
1885
|
+
const { collectionDir, slug, contentRoot } = opts;
|
|
1886
|
+
if (isIndexSlug(slug)) {
|
|
1887
|
+
const resolvedRoot = path5.resolve(contentRoot);
|
|
1888
|
+
if (path5.resolve(collectionDir) === resolvedRoot)
|
|
1889
|
+
return null;
|
|
1890
|
+
const parentDir = path5.dirname(collectionDir);
|
|
1891
|
+
const ownName = extractSlugFromFilename(path5.basename(collectionDir));
|
|
1892
|
+
if (isIndexSlug(ownName))
|
|
1893
|
+
return null;
|
|
1894
|
+
const claimant = await findEntryBySlugIn(parentDir, ownName);
|
|
1895
|
+
return claimant ? { kind: "parent-entry", physicalPath: claimant, name: ownName } : null;
|
|
1896
|
+
}
|
|
1897
|
+
const childDir = await findChildCollectionDir(collectionDir, slug);
|
|
1898
|
+
if (!childDir)
|
|
1899
|
+
return null;
|
|
1900
|
+
const indexEntry = await findIndexEntryIn(childDir);
|
|
1901
|
+
return indexEntry ? { kind: "sibling-collection-index", physicalPath: indexEntry, name: slug } : null;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1424
1904
|
// dist/content-index-registry.js
|
|
1425
|
-
import
|
|
1905
|
+
import path6 from "node:path";
|
|
1426
1906
|
var registry = /* @__PURE__ */ new Map();
|
|
1427
1907
|
var finalization = new FinalizationRegistry(({ rootKey, ref }) => {
|
|
1428
1908
|
const refs = registry.get(rootKey);
|
|
@@ -1433,7 +1913,7 @@ var finalization = new FinalizationRegistry(({ rootKey, ref }) => {
|
|
|
1433
1913
|
registry.delete(rootKey);
|
|
1434
1914
|
});
|
|
1435
1915
|
function registerContentIndexForInvalidation(root, target) {
|
|
1436
|
-
const rootKey =
|
|
1916
|
+
const rootKey = path6.resolve(root);
|
|
1437
1917
|
let refs = registry.get(rootKey);
|
|
1438
1918
|
if (!refs) {
|
|
1439
1919
|
refs = /* @__PURE__ */ new Set();
|
|
@@ -1444,8 +1924,8 @@ function registerContentIndexForInvalidation(root, target) {
|
|
|
1444
1924
|
finalization.register(target, { rootKey, ref });
|
|
1445
1925
|
}
|
|
1446
1926
|
function invalidateContentIndexesForRoot(root) {
|
|
1447
|
-
const rootKey =
|
|
1448
|
-
const prefix = rootKey +
|
|
1927
|
+
const rootKey = path6.resolve(root);
|
|
1928
|
+
const prefix = rootKey + path6.sep;
|
|
1449
1929
|
for (const [storeRoot, refs] of registry) {
|
|
1450
1930
|
if (storeRoot !== rootKey && !storeRoot.startsWith(prefix))
|
|
1451
1931
|
continue;
|
|
@@ -1463,8 +1943,8 @@ function invalidateContentIndexesForRoot(root) {
|
|
|
1463
1943
|
}
|
|
1464
1944
|
|
|
1465
1945
|
// dist/resource-generation.js
|
|
1466
|
-
import
|
|
1467
|
-
import
|
|
1946
|
+
import fs5 from "node:fs/promises";
|
|
1947
|
+
import path7 from "node:path";
|
|
1468
1948
|
import { randomUUID } from "node:crypto";
|
|
1469
1949
|
|
|
1470
1950
|
// dist/utils/debug.js
|
|
@@ -1573,7 +2053,7 @@ var testLogger = createDebugLogger({
|
|
|
1573
2053
|
var log = createDebugLogger({ prefix: "ResourceGeneration" });
|
|
1574
2054
|
var META_DIR2 = ".canopy-meta";
|
|
1575
2055
|
function resourceGenerationPath(root, resource) {
|
|
1576
|
-
return
|
|
2056
|
+
return path7.resolve(root, META_DIR2, `${resource}.generation`);
|
|
1577
2057
|
}
|
|
1578
2058
|
async function bumpResourceGeneration(root, resource, options) {
|
|
1579
2059
|
const token = randomUUID();
|
|
@@ -1591,7 +2071,7 @@ async function bumpResourceGeneration(root, resource, options) {
|
|
|
1591
2071
|
}
|
|
1592
2072
|
async function readResourceGeneration(root, resource) {
|
|
1593
2073
|
try {
|
|
1594
|
-
const token = await
|
|
2074
|
+
const token = await fs5.readFile(resourceGenerationPath(root, resource), "utf-8");
|
|
1595
2075
|
return { ok: true, token };
|
|
1596
2076
|
} catch (err) {
|
|
1597
2077
|
if (isNodeError(err) && err.code === "ENOENT")
|
|
@@ -1607,11 +2087,11 @@ function isGenerationCurrent(snapshotToken, read) {
|
|
|
1607
2087
|
}
|
|
1608
2088
|
|
|
1609
2089
|
// dist/branch-schema-cache.js
|
|
1610
|
-
import
|
|
1611
|
-
import
|
|
2090
|
+
import fs7 from "node:fs/promises";
|
|
2091
|
+
import path8 from "node:path";
|
|
1612
2092
|
|
|
1613
2093
|
// dist/schema/meta-loader.js
|
|
1614
|
-
import { promises as
|
|
2094
|
+
import { promises as fs6 } from "fs";
|
|
1615
2095
|
import { join } from "pathe";
|
|
1616
2096
|
import { z } from "zod";
|
|
1617
2097
|
import chokidar from "chokidar";
|
|
@@ -1648,7 +2128,7 @@ function stripEmbeddedIdFromName(name) {
|
|
|
1648
2128
|
async function scanForCollectionMeta(baseDir, relativePath = "") {
|
|
1649
2129
|
const collections = [];
|
|
1650
2130
|
try {
|
|
1651
|
-
const entries = await
|
|
2131
|
+
const entries = await fs6.readdir(baseDir, { withFileTypes: true });
|
|
1652
2132
|
for (const entry of entries) {
|
|
1653
2133
|
if (!entry.isDirectory())
|
|
1654
2134
|
continue;
|
|
@@ -1659,8 +2139,8 @@ async function scanForCollectionMeta(baseDir, relativePath = "") {
|
|
|
1659
2139
|
const absolutePath = join(baseDir, folderName);
|
|
1660
2140
|
const metaPath = join(absolutePath, ".collection.json");
|
|
1661
2141
|
try {
|
|
1662
|
-
await
|
|
1663
|
-
const content = await
|
|
2142
|
+
await fs6.access(metaPath);
|
|
2143
|
+
const content = await fs6.readFile(metaPath, "utf-8");
|
|
1664
2144
|
const parsed = JSON.parse(content);
|
|
1665
2145
|
const meta = collectionMetaSchema.parse(parsed);
|
|
1666
2146
|
collections.push({
|
|
@@ -1692,7 +2172,7 @@ async function loadCollectionMetaFiles(contentRoot) {
|
|
|
1692
2172
|
let root = null;
|
|
1693
2173
|
const rootMetaPath = join(contentRoot, ".collection.json");
|
|
1694
2174
|
try {
|
|
1695
|
-
await
|
|
2175
|
+
await fs6.access(rootMetaPath);
|
|
1696
2176
|
} catch (err) {
|
|
1697
2177
|
if (err.code === "ENOENT") {
|
|
1698
2178
|
} else {
|
|
@@ -1700,7 +2180,7 @@ async function loadCollectionMetaFiles(contentRoot) {
|
|
|
1700
2180
|
}
|
|
1701
2181
|
}
|
|
1702
2182
|
try {
|
|
1703
|
-
const content = await
|
|
2183
|
+
const content = await fs6.readFile(rootMetaPath, "utf-8");
|
|
1704
2184
|
const parsed = JSON.parse(content);
|
|
1705
2185
|
root = rootCollectionMetaSchema.parse(parsed);
|
|
1706
2186
|
} catch (err) {
|
|
@@ -1887,16 +2367,16 @@ var SCHEMA_GENERATION_RESOURCE = "schema";
|
|
|
1887
2367
|
async function isStaleByMtime(dir, cachedAt) {
|
|
1888
2368
|
let entries;
|
|
1889
2369
|
try {
|
|
1890
|
-
entries = await
|
|
2370
|
+
entries = await fs7.readdir(dir, { recursive: true, encoding: "utf-8" });
|
|
1891
2371
|
} catch {
|
|
1892
2372
|
return true;
|
|
1893
2373
|
}
|
|
1894
2374
|
for (const entry of entries) {
|
|
1895
2375
|
if (!entry.endsWith(".collection.json"))
|
|
1896
2376
|
continue;
|
|
1897
|
-
const full =
|
|
2377
|
+
const full = path8.join(dir, entry);
|
|
1898
2378
|
try {
|
|
1899
|
-
const stat = await
|
|
2379
|
+
const stat = await fs7.stat(full);
|
|
1900
2380
|
if (stat.mtimeMs > cachedAt.getTime())
|
|
1901
2381
|
return true;
|
|
1902
2382
|
} catch {
|
|
@@ -1920,7 +2400,7 @@ var BranchSchemaCache = class {
|
|
|
1920
2400
|
* the project root, regardless of which entrypoint produced the cwd branchRoot.
|
|
1921
2401
|
*/
|
|
1922
2402
|
skipDiskCache(branchRoot) {
|
|
1923
|
-
return isBuildMode() ||
|
|
2403
|
+
return isBuildMode() || path8.resolve(branchRoot) === path8.resolve(process.cwd());
|
|
1924
2404
|
}
|
|
1925
2405
|
/**
|
|
1926
2406
|
* Get schema for a branch (loads from cache or resolves fresh).
|
|
@@ -1946,14 +2426,14 @@ var BranchSchemaCache = class {
|
|
|
1946
2426
|
* Load schema from cache or resolve fresh if cache is missing or stale.
|
|
1947
2427
|
*/
|
|
1948
2428
|
async loadFromCacheOrResolve(branchRoot, entrySchemaRegistry, contentRootName) {
|
|
1949
|
-
const contentRoot =
|
|
2429
|
+
const contentRoot = path8.join(branchRoot, contentRootName);
|
|
1950
2430
|
const skipDiskCache = this.skipDiskCache(branchRoot);
|
|
1951
2431
|
if (!skipDiskCache) {
|
|
1952
|
-
const cacheDir =
|
|
1953
|
-
const cachePath =
|
|
2432
|
+
const cacheDir = path8.join(branchRoot, ".canopy-meta");
|
|
2433
|
+
const cachePath = path8.join(cacheDir, "schema-cache.json");
|
|
1954
2434
|
let cacheData = null;
|
|
1955
2435
|
try {
|
|
1956
|
-
const cacheContent = await
|
|
2436
|
+
const cacheContent = await fs7.readFile(cachePath, "utf-8");
|
|
1957
2437
|
cacheData = JSON.parse(cacheContent);
|
|
1958
2438
|
} catch {
|
|
1959
2439
|
cacheData = null;
|
|
@@ -1990,7 +2470,7 @@ var BranchSchemaCache = class {
|
|
|
1990
2470
|
*/
|
|
1991
2471
|
async resolveFreshAndPersist(branchRoot, entrySchemaRegistry, contentRootName, options) {
|
|
1992
2472
|
const { skipDiskCache } = options;
|
|
1993
|
-
const contentRoot =
|
|
2473
|
+
const contentRoot = path8.join(branchRoot, contentRootName);
|
|
1994
2474
|
const read = skipDiskCache ? null : await readResourceGeneration(branchRoot, SCHEMA_GENERATION_RESOURCE);
|
|
1995
2475
|
const result = await this.resolveFresh(contentRoot, entrySchemaRegistry);
|
|
1996
2476
|
if (!isValidSchema(result.schema)) {
|
|
@@ -2003,9 +2483,9 @@ var BranchSchemaCache = class {
|
|
|
2003
2483
|
}
|
|
2004
2484
|
const flatSchema = flattenSchema(result.schema, contentRootName);
|
|
2005
2485
|
if (!skipDiskCache) {
|
|
2006
|
-
const cacheDir =
|
|
2007
|
-
const cachePath =
|
|
2008
|
-
await
|
|
2486
|
+
const cacheDir = path8.join(branchRoot, ".canopy-meta");
|
|
2487
|
+
const cachePath = path8.join(cacheDir, "schema-cache.json");
|
|
2488
|
+
await fs7.unlink(path8.join(cacheDir, "schema-cache.stale")).catch(() => {
|
|
2009
2489
|
});
|
|
2010
2490
|
if (read && read.ok) {
|
|
2011
2491
|
const newCache = {
|
|
@@ -2015,13 +2495,13 @@ var BranchSchemaCache = class {
|
|
|
2015
2495
|
cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2016
2496
|
generation: read.token
|
|
2017
2497
|
};
|
|
2018
|
-
await
|
|
2019
|
-
const tmpPath =
|
|
2020
|
-
await
|
|
2498
|
+
await fs7.mkdir(cacheDir, { recursive: true });
|
|
2499
|
+
const tmpPath = path8.join(cacheDir, `schema-cache.tmp.${Date.now()}.${Math.random()}.json`);
|
|
2500
|
+
await fs7.writeFile(tmpPath, JSON.stringify(newCache, null, 2), "utf-8");
|
|
2021
2501
|
try {
|
|
2022
|
-
await
|
|
2502
|
+
await fs7.rename(tmpPath, cachePath);
|
|
2023
2503
|
} catch (err) {
|
|
2024
|
-
await
|
|
2504
|
+
await fs7.unlink(tmpPath).catch(() => {
|
|
2025
2505
|
});
|
|
2026
2506
|
throw err;
|
|
2027
2507
|
}
|
|
@@ -2093,10 +2573,10 @@ async function invalidateBranchContentCaches(root) {
|
|
|
2093
2573
|
}
|
|
2094
2574
|
|
|
2095
2575
|
// dist/utils/fs.js
|
|
2096
|
-
import
|
|
2576
|
+
import fs8 from "node:fs/promises";
|
|
2097
2577
|
async function filePathExists(filePath) {
|
|
2098
2578
|
try {
|
|
2099
|
-
await
|
|
2579
|
+
await fs8.stat(filePath);
|
|
2100
2580
|
return true;
|
|
2101
2581
|
} catch (err) {
|
|
2102
2582
|
if (isNotFoundError(err))
|
|
@@ -2104,6 +2584,15 @@ async function filePathExists(filePath) {
|
|
|
2104
2584
|
throw err;
|
|
2105
2585
|
}
|
|
2106
2586
|
}
|
|
2587
|
+
async function readFileIfExists(filePath) {
|
|
2588
|
+
try {
|
|
2589
|
+
return await fs8.readFile(filePath, "utf8");
|
|
2590
|
+
} catch (err) {
|
|
2591
|
+
if (isNotFoundError(err))
|
|
2592
|
+
return void 0;
|
|
2593
|
+
throw err;
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2107
2596
|
|
|
2108
2597
|
// dist/utils/format.js
|
|
2109
2598
|
var getFormatExtension = (format) => {
|
|
@@ -2142,8 +2631,8 @@ function validateAndNormalizePath(root, target) {
|
|
|
2142
2631
|
|
|
2143
2632
|
// dist/paths/branch.js
|
|
2144
2633
|
init_operating_mode();
|
|
2145
|
-
import
|
|
2146
|
-
import
|
|
2634
|
+
import fs9 from "node:fs/promises";
|
|
2635
|
+
import path10 from "node:path";
|
|
2147
2636
|
|
|
2148
2637
|
// dist/paths/branch-name.js
|
|
2149
2638
|
function sanitizeBranchName(branchName) {
|
|
@@ -2166,11 +2655,11 @@ function resolveBranchPath(options) {
|
|
|
2166
2655
|
const safeBranch = sanitizeBranchName(options.branchName);
|
|
2167
2656
|
const strategy = operatingStrategy(options.mode);
|
|
2168
2657
|
const baseRoot = resolveContentBranchesRoot(options.mode, options.basePathOverride);
|
|
2169
|
-
const normalizedBase =
|
|
2170
|
-
const baseWithSep = normalizedBase.endsWith(
|
|
2658
|
+
const normalizedBase = path10.resolve(baseRoot);
|
|
2659
|
+
const baseWithSep = normalizedBase.endsWith(path10.sep) ? normalizedBase : `${normalizedBase}${path10.sep}`;
|
|
2171
2660
|
const branchRoot = strategy.getContentBranchRoot(safeBranch, options.basePathOverride);
|
|
2172
2661
|
const withinBase = (target) => {
|
|
2173
|
-
const resolved =
|
|
2662
|
+
const resolved = path10.resolve(target);
|
|
2174
2663
|
return resolved === normalizedBase || resolved.startsWith(baseWithSep);
|
|
2175
2664
|
};
|
|
2176
2665
|
if (!withinBase(branchRoot)) {
|
|
@@ -2180,7 +2669,7 @@ function resolveBranchPath(options) {
|
|
|
2180
2669
|
}
|
|
2181
2670
|
async function ensureBranchRoot(options) {
|
|
2182
2671
|
const result = resolveBranchPath(options);
|
|
2183
|
-
await
|
|
2672
|
+
await fs9.mkdir(result.branchRoot, { recursive: true });
|
|
2184
2673
|
return result;
|
|
2185
2674
|
}
|
|
2186
2675
|
|
|
@@ -2226,6 +2715,13 @@ var DuplicateContentIdError = class extends ContentConflictError {
|
|
|
2226
2715
|
this.paths = sorted;
|
|
2227
2716
|
}
|
|
2228
2717
|
};
|
|
2718
|
+
var UrlPathConflictError = class extends ContentConflictError {
|
|
2719
|
+
constructor(message, conflictingPath) {
|
|
2720
|
+
super(message);
|
|
2721
|
+
this.name = "UrlPathConflictError";
|
|
2722
|
+
this.conflictingPath = conflictingPath;
|
|
2723
|
+
}
|
|
2724
|
+
};
|
|
2229
2725
|
function getDefaultEntryType(entries) {
|
|
2230
2726
|
if (!entries || entries.length === 0)
|
|
2231
2727
|
return void 0;
|
|
@@ -2242,6 +2738,7 @@ function validateSlug(slug) {
|
|
|
2242
2738
|
var DEFAULT_INDEX_FRESHNESS_INTERVAL_MS = 1e3;
|
|
2243
2739
|
var FORCED_REFRESH_MIN_INTERVAL_MS = 5e3;
|
|
2244
2740
|
var STALE_LOOKUP = Symbol("stale-index-lookup");
|
|
2741
|
+
var createReferenceResolveCache = () => /* @__PURE__ */ new Map();
|
|
2245
2742
|
var ContentStore = class {
|
|
2246
2743
|
constructor(root, flatSchema, options = {}) {
|
|
2247
2744
|
this.indexGeneration = 0;
|
|
@@ -2250,7 +2747,7 @@ var ContentStore = class {
|
|
|
2250
2747
|
this.lastDiskProbeMs = 0;
|
|
2251
2748
|
this.lastForcedRefreshMs = 0;
|
|
2252
2749
|
this.indexBuild = null;
|
|
2253
|
-
this.root =
|
|
2750
|
+
this.root = path11.resolve(root);
|
|
2254
2751
|
this.contentRootName = options.contentRootName || "content";
|
|
2255
2752
|
this.indexFreshnessIntervalMs = options.indexFreshnessIntervalMs ?? DEFAULT_INDEX_FRESHNESS_INTERVAL_MS;
|
|
2256
2753
|
this.contentWriteLockWaitMs = options.contentWriteLockWaitMs ?? DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS;
|
|
@@ -2398,7 +2895,7 @@ var ContentStore = class {
|
|
|
2398
2895
|
async findEntryPathsById(dir, id) {
|
|
2399
2896
|
let entries;
|
|
2400
2897
|
try {
|
|
2401
|
-
entries = await
|
|
2898
|
+
entries = await fs10.readdir(dir, { withFileTypes: true });
|
|
2402
2899
|
} catch (err) {
|
|
2403
2900
|
if (isNodeError(err) && err.code === "ENOENT")
|
|
2404
2901
|
return [];
|
|
@@ -2409,7 +2906,7 @@ var ContentStore = class {
|
|
|
2409
2906
|
if (entry.isDirectory())
|
|
2410
2907
|
continue;
|
|
2411
2908
|
if (extractIdFromFilename(entry.name) === id) {
|
|
2412
|
-
matches.push(
|
|
2909
|
+
matches.push(path11.relative(this.root, path11.join(dir, entry.name)));
|
|
2413
2910
|
}
|
|
2414
2911
|
}
|
|
2415
2912
|
return matches.sort();
|
|
@@ -2421,11 +2918,11 @@ var ContentStore = class {
|
|
|
2421
2918
|
getSchemaItems() {
|
|
2422
2919
|
return this.schemaIndex.values();
|
|
2423
2920
|
}
|
|
2424
|
-
assertSchemaItem(
|
|
2425
|
-
const normalized = normalizeFilesystemPath(
|
|
2921
|
+
assertSchemaItem(path21) {
|
|
2922
|
+
const normalized = normalizeFilesystemPath(path21);
|
|
2426
2923
|
const item = this.schemaIndex.get(normalized);
|
|
2427
2924
|
if (!item) {
|
|
2428
|
-
throw new ContentStoreError(`Unknown schema item: ${
|
|
2925
|
+
throw new ContentStoreError(`Unknown schema item: ${path21}`, "NO_SCHEMA_ITEM");
|
|
2429
2926
|
}
|
|
2430
2927
|
return item;
|
|
2431
2928
|
}
|
|
@@ -2492,6 +2989,26 @@ var ContentStore = class {
|
|
|
2492
2989
|
return this.createLockKey(schemaItem, slug);
|
|
2493
2990
|
return prePass.id ? this.idLockKey(prePass.id) : prePass.absolutePath;
|
|
2494
2991
|
}
|
|
2992
|
+
/**
|
|
2993
|
+
* Refuse a create/rename that would give a second entry a `urlPath` another entry already
|
|
2994
|
+
* holds. See url-collision.ts for the two shapes that count, and the legitimate
|
|
2995
|
+
* landing-page-beside-a-collection shape that deliberately does not.
|
|
2996
|
+
*
|
|
2997
|
+
* @param collectionDir Absolute physical directory the entry will live in.
|
|
2998
|
+
* @param slug The entry's slug, as it will be written.
|
|
2999
|
+
*/
|
|
3000
|
+
async assertUrlPathAvailable(collectionDir, slug) {
|
|
3001
|
+
const claimant = await findUrlPathClaimant({
|
|
3002
|
+
collectionDir,
|
|
3003
|
+
slug,
|
|
3004
|
+
contentRoot: path11.resolve(this.root, this.contentRootName)
|
|
3005
|
+
});
|
|
3006
|
+
if (!claimant)
|
|
3007
|
+
return;
|
|
3008
|
+
const relative2 = path11.relative(this.root, claimant.physicalPath);
|
|
3009
|
+
const message = claimant.kind === "sibling-collection-index" ? `An entry with slug "${slug}" would share a URL with the index entry of the "${claimant.name}" collection beside it ("${relative2}"), and only one of them could be served there. Rename this entry, or remove that collection's index entry.` : `An index entry here would share a URL with the "${claimant.name}" entry in the parent collection ("${relative2}"), and only one of them could be served there. Rename that entry, or give this collection's landing page a different slug.`;
|
|
3010
|
+
throw new UrlPathConflictError(message, claimant.physicalPath);
|
|
3011
|
+
}
|
|
2495
3012
|
/**
|
|
2496
3013
|
* Build absolute and relative paths with security validation.
|
|
2497
3014
|
* All entries use the unified filename pattern: {type}.{slug}.{id}.{ext}
|
|
@@ -2508,7 +3025,7 @@ var ContentStore = class {
|
|
|
2508
3025
|
* @param options.entryTypeName - For collections with multiple entry types, specify which one to use. Defaults to the default entry type.
|
|
2509
3026
|
*/
|
|
2510
3027
|
async buildPaths(schemaItem, slug, options = {}) {
|
|
2511
|
-
const rootWithSep = this.root.endsWith(
|
|
3028
|
+
const rootWithSep = this.root.endsWith(path11.sep) ? this.root : `${this.root}${path11.sep}`;
|
|
2512
3029
|
if (schemaItem.type === "entry-type") {
|
|
2513
3030
|
const parentPath = schemaItem.parentPath || "";
|
|
2514
3031
|
const parentCollection = this.schemaIndex.get(parentPath);
|
|
@@ -2541,7 +3058,7 @@ var ContentStore = class {
|
|
|
2541
3058
|
const entryTypeName = entryTypeConfig?.name || "entry";
|
|
2542
3059
|
let collectionRoot = await resolveCollectionPath(this.root, schemaItem.logicalPath);
|
|
2543
3060
|
if (!collectionRoot) {
|
|
2544
|
-
collectionRoot =
|
|
3061
|
+
collectionRoot = path11.resolve(this.root, schemaItem.logicalPath);
|
|
2545
3062
|
}
|
|
2546
3063
|
if (!collectionRoot.startsWith(rootWithSep)) {
|
|
2547
3064
|
throw new ContentStoreError("Path traversal detected", "VALIDATION");
|
|
@@ -2551,7 +3068,7 @@ var ContentStore = class {
|
|
|
2551
3068
|
let existingEntryType;
|
|
2552
3069
|
let foundExisting = false;
|
|
2553
3070
|
if (!id) {
|
|
2554
|
-
const entries = await
|
|
3071
|
+
const entries = await fs10.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
|
|
2555
3072
|
const existingFile = entries.find((entry) => {
|
|
2556
3073
|
if (entry.isDirectory())
|
|
2557
3074
|
return false;
|
|
@@ -2577,14 +3094,14 @@ var ContentStore = class {
|
|
|
2577
3094
|
}
|
|
2578
3095
|
filename = `${finalEntryTypeName}.${safeSlug}.${id}${ext}`;
|
|
2579
3096
|
}
|
|
2580
|
-
const resolved =
|
|
2581
|
-
const collectionRootWithSep = collectionRoot.endsWith(
|
|
3097
|
+
const resolved = path11.resolve(collectionRoot, filename);
|
|
3098
|
+
const collectionRootWithSep = collectionRoot.endsWith(path11.sep) ? collectionRoot : `${collectionRoot}${path11.sep}`;
|
|
2582
3099
|
if (!resolved.startsWith(collectionRootWithSep)) {
|
|
2583
3100
|
throw new ContentStoreError("Path traversal detected", "VALIDATION");
|
|
2584
3101
|
}
|
|
2585
3102
|
return {
|
|
2586
3103
|
absolutePath: resolved,
|
|
2587
|
-
relativePath:
|
|
3104
|
+
relativePath: path11.relative(this.root, resolved),
|
|
2588
3105
|
id,
|
|
2589
3106
|
entryTypeName: finalEntryTypeName,
|
|
2590
3107
|
existed
|
|
@@ -2620,8 +3137,8 @@ var ContentStore = class {
|
|
|
2620
3137
|
async read(collectionPath, slug = "", options = {}) {
|
|
2621
3138
|
const schemaItem = this.assertSchemaItem(collectionPath);
|
|
2622
3139
|
const { absolutePath, relativePath, entryTypeName: resolvedEntryTypeName } = await this.buildPaths(schemaItem, slug);
|
|
2623
|
-
const stat = await
|
|
2624
|
-
const raw = await
|
|
3140
|
+
const stat = await fs10.stat(absolutePath);
|
|
3141
|
+
const raw = await fs10.readFile(absolutePath, "utf8");
|
|
2625
3142
|
let doc;
|
|
2626
3143
|
let format;
|
|
2627
3144
|
let fields;
|
|
@@ -2660,7 +3177,7 @@ var ContentStore = class {
|
|
|
2660
3177
|
absolutePath
|
|
2661
3178
|
};
|
|
2662
3179
|
} else {
|
|
2663
|
-
const parsed =
|
|
3180
|
+
const parsed = matter2(raw);
|
|
2664
3181
|
doc = {
|
|
2665
3182
|
collection: schemaItem.logicalPath,
|
|
2666
3183
|
collectionName: schemaItem.name,
|
|
@@ -2723,7 +3240,7 @@ var ContentStore = class {
|
|
|
2723
3240
|
const indexedPath = guardIndex.findById(id)?.relativePath ?? null;
|
|
2724
3241
|
if (indexedPath && indexedPath !== relativePath) {
|
|
2725
3242
|
const quarantined = guardIndex.getDuplicateFor(id);
|
|
2726
|
-
const bothOnDisk = await filePathExists(absolutePath) && await filePathExists(
|
|
3243
|
+
const bothOnDisk = await filePathExists(absolutePath) && await filePathExists(path11.join(this.root, indexedPath));
|
|
2727
3244
|
if (quarantined || bothOnDisk) {
|
|
2728
3245
|
throw new DuplicateContentIdError(id, [
|
|
2729
3246
|
indexedPath,
|
|
@@ -2734,10 +3251,18 @@ var ContentStore = class {
|
|
|
2734
3251
|
}
|
|
2735
3252
|
}
|
|
2736
3253
|
}
|
|
2737
|
-
await
|
|
3254
|
+
if (!await filePathExists(absolutePath)) {
|
|
3255
|
+
const chosenSlug = extractSlugFromFilename(path11.basename(absolutePath));
|
|
3256
|
+
const routable = parseSlug(chosenSlug);
|
|
3257
|
+
if (!routable.ok) {
|
|
3258
|
+
throw new ContentStoreError(`Cannot create entry "${chosenSlug}": ${routable.error}. An entry whose slug is not addressable as a URL segment would build and then 404 on every visit.`, "VALIDATION");
|
|
3259
|
+
}
|
|
3260
|
+
await this.assertUrlPathAvailable(path11.dirname(absolutePath), chosenSlug);
|
|
3261
|
+
}
|
|
3262
|
+
await fs10.mkdir(path11.dirname(absolutePath), { recursive: true });
|
|
2738
3263
|
if (input.expectedVersion !== void 0) {
|
|
2739
3264
|
try {
|
|
2740
|
-
const existing = await
|
|
3265
|
+
const existing = await fs10.stat(absolutePath);
|
|
2741
3266
|
if (input.expectedVersion === null) {
|
|
2742
3267
|
throw new ContentConflictError("An entry with this slug already exists");
|
|
2743
3268
|
}
|
|
@@ -2754,7 +3279,7 @@ var ContentStore = class {
|
|
|
2754
3279
|
}
|
|
2755
3280
|
}
|
|
2756
3281
|
if (existingId && !await filePathExists(absolutePath)) {
|
|
2757
|
-
const actualRelPaths = await this.findEntryPathsById(
|
|
3282
|
+
const actualRelPaths = await this.findEntryPathsById(path11.dirname(absolutePath), existingId);
|
|
2758
3283
|
if (actualRelPaths.length > 1) {
|
|
2759
3284
|
throw new DuplicateContentIdError(existingId, actualRelPaths);
|
|
2760
3285
|
}
|
|
@@ -2764,14 +3289,15 @@ var ContentStore = class {
|
|
|
2764
3289
|
throw new ContentConflictError();
|
|
2765
3290
|
}
|
|
2766
3291
|
}
|
|
3292
|
+
const existingRaw = input.format === "json" ? void 0 : await readFileIfExists(absolutePath);
|
|
2767
3293
|
let content;
|
|
2768
3294
|
if (input.format === "json") {
|
|
2769
3295
|
content = `${JSON.stringify(input.data ?? {}, null, 2)}
|
|
2770
3296
|
`;
|
|
2771
3297
|
} else if (input.format === "yaml") {
|
|
2772
|
-
content =
|
|
3298
|
+
content = serializeYaml(input.data ?? {}, existingRaw);
|
|
2773
3299
|
} else {
|
|
2774
|
-
content =
|
|
3300
|
+
content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw);
|
|
2775
3301
|
}
|
|
2776
3302
|
await atomicWriteFile(absolutePath, content);
|
|
2777
3303
|
const liveIndex = this._idIndex;
|
|
@@ -2780,7 +3306,7 @@ var ContentStore = class {
|
|
|
2780
3306
|
const existing = liveIndex.findById(id);
|
|
2781
3307
|
if (existing) {
|
|
2782
3308
|
if (existing.relativePath !== relativePath) {
|
|
2783
|
-
staleOldAbsPath =
|
|
3309
|
+
staleOldAbsPath = path11.join(this.root, existing.relativePath);
|
|
2784
3310
|
liveIndex.updatePath(existing.id, relativePath);
|
|
2785
3311
|
}
|
|
2786
3312
|
} else {
|
|
@@ -2793,13 +3319,13 @@ var ContentStore = class {
|
|
|
2793
3319
|
}
|
|
2794
3320
|
}
|
|
2795
3321
|
if (staleOldAbsPath) {
|
|
2796
|
-
await
|
|
3322
|
+
await fs10.unlink(staleOldAbsPath).catch((err) => {
|
|
2797
3323
|
if (!isNodeError(err) || err.code !== "ENOENT")
|
|
2798
3324
|
throw err;
|
|
2799
3325
|
});
|
|
2800
3326
|
}
|
|
2801
3327
|
await this.recordOwnMutation(liveIndex);
|
|
2802
|
-
const afterStat = await
|
|
3328
|
+
const afterStat = await fs10.stat(absolutePath);
|
|
2803
3329
|
const base = {
|
|
2804
3330
|
collection: schemaItem.logicalPath,
|
|
2805
3331
|
collectionName: schemaItem.name,
|
|
@@ -2877,7 +3403,7 @@ var ContentStore = class {
|
|
|
2877
3403
|
const schemaItem = this.assertSchemaItem(collectionPath);
|
|
2878
3404
|
const { absolutePath } = await this.buildPaths(schemaItem, slug);
|
|
2879
3405
|
try {
|
|
2880
|
-
await
|
|
3406
|
+
await fs10.access(absolutePath);
|
|
2881
3407
|
return true;
|
|
2882
3408
|
} catch (err) {
|
|
2883
3409
|
if (isNodeError(err) && err.code === "ENOENT")
|
|
@@ -2916,7 +3442,7 @@ var ContentStore = class {
|
|
|
2916
3442
|
const collectionRoot = await resolveCollectionPath(this.root, collectionPath);
|
|
2917
3443
|
if (!collectionRoot)
|
|
2918
3444
|
return 0;
|
|
2919
|
-
const entries = await
|
|
3445
|
+
const entries = await fs10.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
|
|
2920
3446
|
return entries.filter((entry) => !entry.isDirectory() && extractEntryTypeFromFilename(entry.name) === entryTypeName).length;
|
|
2921
3447
|
}
|
|
2922
3448
|
/**
|
|
@@ -2940,7 +3466,7 @@ var ContentStore = class {
|
|
|
2940
3466
|
const collection = this.assertCollection(collectionPath);
|
|
2941
3467
|
const prePass = await this.buildPaths(collection, slug);
|
|
2942
3468
|
if (!prePass.existed) {
|
|
2943
|
-
await
|
|
3469
|
+
await fs10.unlink(prePass.absolutePath);
|
|
2944
3470
|
return;
|
|
2945
3471
|
}
|
|
2946
3472
|
let lockKey = this.entryLockKey(collection, slug, prePass);
|
|
@@ -2956,7 +3482,7 @@ var ContentStore = class {
|
|
|
2956
3482
|
return RETRY_KEY;
|
|
2957
3483
|
}
|
|
2958
3484
|
const { absolutePath, relativePath } = inLock;
|
|
2959
|
-
await
|
|
3485
|
+
await fs10.unlink(absolutePath);
|
|
2960
3486
|
const liveIndex = this._idIndex;
|
|
2961
3487
|
const id = liveIndex.findByPath(relativePath);
|
|
2962
3488
|
if (id) {
|
|
@@ -2992,6 +3518,10 @@ var ContentStore = class {
|
|
|
2992
3518
|
if (currentSlug === safeNewSlug) {
|
|
2993
3519
|
return { newPath: `${collectionPath}/${currentSlug}` };
|
|
2994
3520
|
}
|
|
3521
|
+
const routable = parseSlug(safeNewSlug);
|
|
3522
|
+
if (!routable.ok) {
|
|
3523
|
+
throw new ContentStoreError(`Cannot rename to "${safeNewSlug}": ${routable.error}. An entry whose slug is not addressable as a URL segment would build and then 404 on every visit.`, "VALIDATION");
|
|
3524
|
+
}
|
|
2995
3525
|
const prePass = await this.buildPaths(collection, currentSlug);
|
|
2996
3526
|
if (!prePass.existed) {
|
|
2997
3527
|
throw new ContentStoreError(`Entry not found: ${currentSlug}`, "NOT_FOUND");
|
|
@@ -3018,7 +3548,7 @@ var ContentStore = class {
|
|
|
3018
3548
|
}
|
|
3019
3549
|
destLockKey = currentDestKey;
|
|
3020
3550
|
const { absolutePath: currentPath, relativePath: currentRelPath } = inLock;
|
|
3021
|
-
const currentFilename =
|
|
3551
|
+
const currentFilename = path11.basename(currentPath);
|
|
3022
3552
|
const parts = currentFilename.split(".");
|
|
3023
3553
|
if (parts.length < 4) {
|
|
3024
3554
|
throw new ContentStoreError(`Invalid entry filename format: ${currentFilename}`, "VALIDATION");
|
|
@@ -3027,10 +3557,10 @@ var ContentStore = class {
|
|
|
3027
3557
|
const contentId = parts[parts.length - 2];
|
|
3028
3558
|
const ext = `.${parts[parts.length - 1]}`;
|
|
3029
3559
|
const newFilename = `${entryTypeName}.${safeNewSlug}.${contentId}${ext}`;
|
|
3030
|
-
const parentDir =
|
|
3031
|
-
const newPath =
|
|
3560
|
+
const parentDir = path11.dirname(currentPath);
|
|
3561
|
+
const newPath = path11.join(parentDir, newFilename);
|
|
3032
3562
|
try {
|
|
3033
|
-
const entries = await
|
|
3563
|
+
const entries = await fs10.readdir(parentDir, { withFileTypes: true });
|
|
3034
3564
|
for (const entry of entries) {
|
|
3035
3565
|
if (entry.isDirectory())
|
|
3036
3566
|
continue;
|
|
@@ -3043,16 +3573,17 @@ var ContentStore = class {
|
|
|
3043
3573
|
if (err instanceof ContentStoreError)
|
|
3044
3574
|
throw err;
|
|
3045
3575
|
}
|
|
3576
|
+
await this.assertUrlPathAvailable(parentDir, safeNewSlug);
|
|
3046
3577
|
try {
|
|
3047
|
-
await
|
|
3578
|
+
await fs10.link(currentPath, newPath);
|
|
3048
3579
|
} catch (err) {
|
|
3049
3580
|
if (isNodeError(err) && err.code === "EEXIST") {
|
|
3050
3581
|
throw new ContentStoreError(`Entry with slug "${safeNewSlug}" already exists in collection "${collectionPath}"`, "VALIDATION");
|
|
3051
3582
|
}
|
|
3052
3583
|
throw err;
|
|
3053
3584
|
}
|
|
3054
|
-
await
|
|
3055
|
-
const newRelativePath =
|
|
3585
|
+
await fs10.unlink(currentPath);
|
|
3586
|
+
const newRelativePath = path11.relative(this.root, newPath);
|
|
3056
3587
|
const liveIndex = this._idIndex;
|
|
3057
3588
|
const entryId = liveIndex.findByPath(currentRelPath);
|
|
3058
3589
|
if (entryId) {
|
|
@@ -3124,34 +3655,57 @@ var ContentStore = class {
|
|
|
3124
3655
|
}
|
|
3125
3656
|
return entries;
|
|
3126
3657
|
}
|
|
3658
|
+
/**
|
|
3659
|
+
* Resolve every `reference` field in `data` against `fields`, returning a copy.
|
|
3660
|
+
*
|
|
3661
|
+
* This is what `read()` applies automatically (unless `resolveReferences: false`), exposed
|
|
3662
|
+
* so BATCH surfaces can opt into the same resolution without duplicating the walk: the
|
|
3663
|
+
* listing primitives in content-listing.ts and content-tree.ts read entry files straight
|
|
3664
|
+
* off disk and never touch this store, so before this existed a `reference` field reached
|
|
3665
|
+
* `listEntries()`/`buildContentTree()` callers as a bare id string or `null`.
|
|
3666
|
+
*
|
|
3667
|
+
* Pass one {@link ReferenceResolveCache} across a whole batch so a target referenced by
|
|
3668
|
+
* many entries is read once rather than once per referencing entry. Omit `cache` and the
|
|
3669
|
+
* behavior is exactly what `read()` has always done, unmemoized.
|
|
3670
|
+
*
|
|
3671
|
+
* **Path ACLs are not consulted for the targets.** Resolution goes through this store's
|
|
3672
|
+
* own `read()`, below the per-entry permission check in content-reader.ts — so a resolved
|
|
3673
|
+
* target may be an entry the caller could not `read()` directly. That is pre-existing
|
|
3674
|
+
* `read()` behavior, matched here on purpose so one rule covers both; see the
|
|
3675
|
+
* `resolveReferences` option in content-listing.ts for the note adopters see.
|
|
3676
|
+
*/
|
|
3677
|
+
async resolveReferences(data, fields, cache) {
|
|
3678
|
+
return this.resolveReferencesInData(data, fields, cache);
|
|
3679
|
+
}
|
|
3127
3680
|
/**
|
|
3128
3681
|
* Recursively resolve reference fields in data.
|
|
3129
3682
|
* This traverses objects, arrays, and blocks to find and resolve all reference fields.
|
|
3130
3683
|
*/
|
|
3131
|
-
async resolveReferencesInData(data, fields) {
|
|
3684
|
+
async resolveReferencesInData(data, fields, cache) {
|
|
3132
3685
|
const resolved = { ...data };
|
|
3133
3686
|
const idIndex = await this.idIndex();
|
|
3134
3687
|
for (const field of fields) {
|
|
3135
3688
|
if (field.type === "group") {
|
|
3136
|
-
const groupResolved = await this.resolveReferencesInData(resolved, field.fields);
|
|
3689
|
+
const groupResolved = await this.resolveReferencesInData(resolved, field.fields, cache);
|
|
3137
3690
|
Object.assign(resolved, groupResolved);
|
|
3138
3691
|
continue;
|
|
3139
3692
|
}
|
|
3140
3693
|
const value = data[field.name];
|
|
3141
3694
|
if (field.type === "reference") {
|
|
3695
|
+
const includeBody = field.includeBody === true;
|
|
3142
3696
|
if (typeof value === "string" && value) {
|
|
3143
|
-
resolved[field.name] = await this.resolveSingleReference(value, idIndex);
|
|
3697
|
+
resolved[field.name] = await this.resolveSingleReference(value, idIndex, includeBody, cache);
|
|
3144
3698
|
} else if (field.list && Array.isArray(value)) {
|
|
3145
|
-
resolved[field.name] = await Promise.all(value.map((id) => typeof id === "string" ? this.resolveSingleReference(id, idIndex) : null));
|
|
3699
|
+
resolved[field.name] = await Promise.all(value.map((id) => typeof id === "string" ? this.resolveSingleReference(id, idIndex, includeBody, cache) : null));
|
|
3146
3700
|
}
|
|
3147
3701
|
} else if (field.type === "object" && value) {
|
|
3148
3702
|
const objectField = field;
|
|
3149
3703
|
if (!objectField.fields)
|
|
3150
3704
|
continue;
|
|
3151
3705
|
if (objectField.list && Array.isArray(value)) {
|
|
3152
|
-
resolved[field.name] = await Promise.all(value.map((item) => typeof item === "object" && item !== null ? this.resolveReferencesInData(item, objectField.fields) : item));
|
|
3706
|
+
resolved[field.name] = await Promise.all(value.map((item) => typeof item === "object" && item !== null ? this.resolveReferencesInData(item, objectField.fields, cache) : item));
|
|
3153
3707
|
} else if (typeof value === "object") {
|
|
3154
|
-
resolved[field.name] = await this.resolveReferencesInData(value, objectField.fields);
|
|
3708
|
+
resolved[field.name] = await this.resolveReferencesInData(value, objectField.fields, cache);
|
|
3155
3709
|
}
|
|
3156
3710
|
} else if (field.type === "block" && Array.isArray(value)) {
|
|
3157
3711
|
const blockField = field;
|
|
@@ -3164,7 +3718,7 @@ var ContentStore = class {
|
|
|
3164
3718
|
return block;
|
|
3165
3719
|
return {
|
|
3166
3720
|
...b,
|
|
3167
|
-
value: await this.resolveReferencesInData(b.value, template.fields)
|
|
3721
|
+
value: await this.resolveReferencesInData(b.value, template.fields, cache)
|
|
3168
3722
|
};
|
|
3169
3723
|
}));
|
|
3170
3724
|
}
|
|
@@ -3172,7 +3726,56 @@ var ContentStore = class {
|
|
|
3172
3726
|
return resolved;
|
|
3173
3727
|
}
|
|
3174
3728
|
/**
|
|
3175
|
-
* Resolve a single reference ID to full entry data
|
|
3729
|
+
* Resolve a single reference ID to full entry data, memoized when a batch cache is
|
|
3730
|
+
* supplied. Without a cache this is a straight pass-through to the uncached path, so
|
|
3731
|
+
* `read()` behaves exactly as it always has.
|
|
3732
|
+
*
|
|
3733
|
+
* Every occurrence gets its OWN deep copy, even on a cache hit. Without that, the memo
|
|
3734
|
+
* would hand one shared object to all 40 entries referencing the same block — a mutation
|
|
3735
|
+
* in one caller's `extract` (truncating a body for a search index, deleting a field) would
|
|
3736
|
+
* silently rewrite it for every sibling, and both `list: true` elements of `[id, id]` would
|
|
3737
|
+
* be the same instance. The copy is what keeps the cache a pure performance optimization
|
|
3738
|
+
* instead of a semantic change.
|
|
3739
|
+
*
|
|
3740
|
+
* Note the uncached path is NOT the clean baseline it looks like. For a json/yaml target it
|
|
3741
|
+
* genuinely reparses fresh per occurrence, but for md/mdx gray-matter serves `data` from a
|
|
3742
|
+
* process-global content-keyed cache, so `resolveSingleReferenceOnce`'s top-level spread
|
|
3743
|
+
* severs exactly one level and NESTED frontmatter objects alias across occurrences, calls and
|
|
3744
|
+
* requests. That is pre-existing `read()` behavior and out of scope here — deliberately, since
|
|
3745
|
+
* touching it would change `read()` — but it means the cached path is the safer of the two,
|
|
3746
|
+
* not a relaxation of a guarantee the uncached one provides. See
|
|
3747
|
+
* `.claude/future-tasks/graymatter-cache-shared-frontmatter.md`.
|
|
3748
|
+
*
|
|
3749
|
+
* What the copy costs, measured rather than assumed (2000 occurrences of one target, local
|
|
3750
|
+
* disk, so every read hits the page cache — the friendliest possible case for NOT caching):
|
|
3751
|
+
* a snippet-sized target is ~63x cheaper to clone than to re-read-and-parse, while a 265KB
|
|
3752
|
+
* JSON target is ~0.8x, i.e. cloning is marginally SLOWER than reparsing it. So the win is
|
|
3753
|
+
* large in the case this exists for and roughly a wash at the pathological end, never a
|
|
3754
|
+
* blow-up. Two things keep the bad end narrow: an md/mdx target resolves to its FRONTMATTER
|
|
3755
|
+
* only *unless the field sets `includeBody`* (`read()` puts the body on `doc.body`, which
|
|
3756
|
+
* `resolveSingleReferenceOnce` spreads in only for an embedding field), so by default body
|
|
3757
|
+
* size is irrelevant no matter how long the document and only a genuinely huge JSON/YAML
|
|
3758
|
+
* target reaches the wash. **`includeBody: true` is the case that CAN reach it on markdown**:
|
|
3759
|
+
* the body then sits inside the memoized object and is cloned once per referencing entry, so
|
|
3760
|
+
* a long document embedded by many pages pays that repeatedly — the reason `includeBody`
|
|
3761
|
+
* exists as an opt-in per field rather than as resolution's default. And in the deployment this
|
|
3762
|
+
* targets, content lives on EFS/NFS where the syscall the memo removes dominates parse and
|
|
3763
|
+
* clone alike, which the local-disk numbers above understate badly. Correctness is the
|
|
3764
|
+
* reason for the copy regardless; the numbers are here so nobody has to re-derive them
|
|
3765
|
+
* before touching this.
|
|
3766
|
+
*/
|
|
3767
|
+
resolveSingleReference(id, idIndex, includeBody, cache) {
|
|
3768
|
+
if (!cache)
|
|
3769
|
+
return this.resolveSingleReferenceUncached(id, idIndex, includeBody);
|
|
3770
|
+
const key = includeBody ? `${id}:body` : id;
|
|
3771
|
+
let pending = cache.get(key);
|
|
3772
|
+
if (!pending) {
|
|
3773
|
+
pending = this.resolveSingleReferenceUncached(id, idIndex, includeBody);
|
|
3774
|
+
cache.set(key, pending);
|
|
3775
|
+
}
|
|
3776
|
+
return pending.then((resolved) => resolved === null ? null : structuredClone(resolved));
|
|
3777
|
+
}
|
|
3778
|
+
/**
|
|
3176
3779
|
* Returns null if the reference is invalid or missing.
|
|
3177
3780
|
* Includes id, slug, and collection fields for debugging.
|
|
3178
3781
|
*
|
|
@@ -3186,16 +3789,21 @@ var ContentStore = class {
|
|
|
3186
3789
|
* idIndex() dedupes concurrent builds — so a sibling call that wins the
|
|
3187
3790
|
* throttle and rebuilds heals every other miss in the same batch, not just
|
|
3188
3791
|
* the first.
|
|
3792
|
+
*
|
|
3793
|
+
* A {@link ReferenceResolveCache} sits ABOVE this, never inside it, so every
|
|
3794
|
+
* distinct id still runs the full refresh-and-retry above. What a cache changes
|
|
3795
|
+
* is only that repeats of the SAME id in one batch share that one attempt's
|
|
3796
|
+
* outcome instead of each getting an independent throw of the dice.
|
|
3189
3797
|
*/
|
|
3190
|
-
async
|
|
3191
|
-
const first = await this.resolveSingleReferenceOnce(id, idIndex);
|
|
3798
|
+
async resolveSingleReferenceUncached(id, idIndex, includeBody) {
|
|
3799
|
+
const first = await this.resolveSingleReferenceOnce(id, idIndex, includeBody);
|
|
3192
3800
|
if (first !== STALE_LOOKUP)
|
|
3193
3801
|
return first;
|
|
3194
3802
|
await this.refreshIndexForSuspiciousLookup();
|
|
3195
|
-
const second = await this.resolveSingleReferenceOnce(id, await this.idIndex());
|
|
3803
|
+
const second = await this.resolveSingleReferenceOnce(id, await this.idIndex(), includeBody);
|
|
3196
3804
|
return second === STALE_LOOKUP ? null : second;
|
|
3197
3805
|
}
|
|
3198
|
-
async resolveSingleReferenceOnce(id, idIndex) {
|
|
3806
|
+
async resolveSingleReferenceOnce(id, idIndex, includeBody) {
|
|
3199
3807
|
try {
|
|
3200
3808
|
const location = idIndex.findById(id);
|
|
3201
3809
|
if (!location)
|
|
@@ -3206,12 +3814,13 @@ var ContentStore = class {
|
|
|
3206
3814
|
const doc = await this.read(location.collection, location.slug, {
|
|
3207
3815
|
resolveReferences: false
|
|
3208
3816
|
});
|
|
3209
|
-
|
|
3817
|
+
const bodyForEmbed = includeBody && "body" in doc ? { fieldName: doc.bodyFieldName, value: doc.body } : void 0;
|
|
3818
|
+
return buildResolvedReference(doc.data, {
|
|
3210
3819
|
id,
|
|
3211
3820
|
slug: location.slug,
|
|
3212
3821
|
collection: location.collection,
|
|
3213
|
-
|
|
3214
|
-
};
|
|
3822
|
+
urlPath: computeEntryUrl(location.collection, location.slug, this.contentRootName)
|
|
3823
|
+
}, bodyForEmbed);
|
|
3215
3824
|
} catch (error) {
|
|
3216
3825
|
if (isNodeError(error) && error.code === "ENOENT")
|
|
3217
3826
|
return STALE_LOOKUP;
|
|
@@ -3222,8 +3831,8 @@ var ContentStore = class {
|
|
|
3222
3831
|
};
|
|
3223
3832
|
|
|
3224
3833
|
// dist/ai/generate.js
|
|
3225
|
-
import
|
|
3226
|
-
import
|
|
3834
|
+
import path12 from "node:path";
|
|
3835
|
+
import fs11 from "node:fs/promises";
|
|
3227
3836
|
import { minimatch } from "minimatch";
|
|
3228
3837
|
|
|
3229
3838
|
// dist/ai/strip-mdx.js
|
|
@@ -3298,14 +3907,44 @@ var BLOCK_RESTORE_RE = /<<CODEBLOCK(\d+)>>/g;
|
|
|
3298
3907
|
var INLINE_PREFIX = "<<INLINECODE";
|
|
3299
3908
|
var INLINE_SUFFIX = ">>";
|
|
3300
3909
|
var INLINE_RESTORE_RE = /<<INLINECODE(\d+)>>/g;
|
|
3910
|
+
function isFenceCloseLine(line, marker) {
|
|
3911
|
+
return line.startsWith(marker) && /^\s*$/.test(line.slice(marker.length));
|
|
3912
|
+
}
|
|
3913
|
+
function maskFencedCodeBlocks(body, blocks) {
|
|
3914
|
+
const lines = body.split("\n");
|
|
3915
|
+
const lineCount = lines.length;
|
|
3916
|
+
const nextCloseLine = {
|
|
3917
|
+
"```": new Array(lineCount + 1).fill(-1),
|
|
3918
|
+
"~~~": new Array(lineCount + 1).fill(-1)
|
|
3919
|
+
};
|
|
3920
|
+
for (let i2 = lineCount - 1; i2 >= 0; i2--) {
|
|
3921
|
+
nextCloseLine["```"][i2] = isFenceCloseLine(lines[i2], "```") ? i2 : nextCloseLine["```"][i2 + 1];
|
|
3922
|
+
nextCloseLine["~~~"][i2] = isFenceCloseLine(lines[i2], "~~~") ? i2 : nextCloseLine["~~~"][i2 + 1];
|
|
3923
|
+
}
|
|
3924
|
+
const outLines = [];
|
|
3925
|
+
let i = 0;
|
|
3926
|
+
while (i < lineCount) {
|
|
3927
|
+
const line = lines[i];
|
|
3928
|
+
const marker = line.startsWith("```") ? "```" : line.startsWith("~~~") ? "~~~" : null;
|
|
3929
|
+
const searchFrom = i + 2;
|
|
3930
|
+
const closeIndex = marker && searchFrom < lineCount ? nextCloseLine[marker][searchFrom] : -1;
|
|
3931
|
+
if (!marker || closeIndex === -1) {
|
|
3932
|
+
outLines.push(line);
|
|
3933
|
+
i++;
|
|
3934
|
+
continue;
|
|
3935
|
+
}
|
|
3936
|
+
const block = lines.slice(i, closeIndex + 1).join("\n");
|
|
3937
|
+
const idx = blocks.length;
|
|
3938
|
+
blocks.push(block);
|
|
3939
|
+
outLines.push(`${BLOCK_PREFIX}${idx}${BLOCK_SUFFIX}`);
|
|
3940
|
+
i = closeIndex + 1;
|
|
3941
|
+
}
|
|
3942
|
+
return outLines.join("\n");
|
|
3943
|
+
}
|
|
3301
3944
|
function maskCodeBlocks(body) {
|
|
3302
3945
|
const blocks = [];
|
|
3303
3946
|
const inlines = [];
|
|
3304
|
-
let masked = body
|
|
3305
|
-
const idx = blocks.length;
|
|
3306
|
-
blocks.push(block);
|
|
3307
|
-
return `${BLOCK_PREFIX}${idx}${BLOCK_SUFFIX}`;
|
|
3308
|
-
});
|
|
3947
|
+
let masked = maskFencedCodeBlocks(body, blocks);
|
|
3309
3948
|
masked = masked.replace(/``[^`]+``|`[^`]+`/g, (span) => {
|
|
3310
3949
|
const idx = inlines.length;
|
|
3311
3950
|
inlines.push(span);
|
|
@@ -3475,7 +4114,7 @@ function renderField(field, value, depth, entry, config) {
|
|
|
3475
4114
|
const heading = "#".repeat(Math.min(depth, 6));
|
|
3476
4115
|
const descriptionLine = "description" in field && field.description ? `
|
|
3477
4116
|
|
|
3478
|
-
|
|
4117
|
+
_${field.description}_` : "";
|
|
3479
4118
|
if ("list" in field && field.list && Array.isArray(value)) {
|
|
3480
4119
|
return renderListField(field, value, depth, label, heading, descriptionLine, entry, config);
|
|
3481
4120
|
}
|
|
@@ -3740,30 +4379,14 @@ function formatInlineValue(field, value) {
|
|
|
3740
4379
|
return String(value);
|
|
3741
4380
|
}
|
|
3742
4381
|
function yamlValue(value) {
|
|
4382
|
+
if (value === "")
|
|
4383
|
+
return '""';
|
|
3743
4384
|
if (/[:#{}[\],&*?|>!%@`]/.test(value) || value.includes("\n")) {
|
|
3744
4385
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
3745
4386
|
}
|
|
3746
4387
|
return value;
|
|
3747
4388
|
}
|
|
3748
4389
|
|
|
3749
|
-
// dist/utils/entry-url.js
|
|
3750
|
-
init_normalize();
|
|
3751
|
-
function computeEntryUrl(collection, slug, contentRoot) {
|
|
3752
|
-
const root = trimSlashes(contentRoot);
|
|
3753
|
-
let stripped = collection;
|
|
3754
|
-
if (root && collection.startsWith(`${root}/`)) {
|
|
3755
|
-
stripped = collection.slice(root.length + 1);
|
|
3756
|
-
} else if (collection === root) {
|
|
3757
|
-
stripped = "";
|
|
3758
|
-
}
|
|
3759
|
-
const segments = stripped.split("/").filter(Boolean);
|
|
3760
|
-
if (slug && slug !== "index") {
|
|
3761
|
-
segments.push(slug);
|
|
3762
|
-
}
|
|
3763
|
-
const path20 = segments.length > 0 ? `/${segments.join("/")}` : "/";
|
|
3764
|
-
return path20.toLowerCase();
|
|
3765
|
-
}
|
|
3766
|
-
|
|
3767
4390
|
// dist/entry-link-resolver.js
|
|
3768
4391
|
var log2 = createDebugLogger({ prefix: "EntryLinks" });
|
|
3769
4392
|
var BASE58_CHAR = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]";
|
|
@@ -3936,7 +4559,7 @@ async function processCollection(store, collection, flatSchema, contentRoot, con
|
|
|
3936
4559
|
const listed = await store.getCollectionEntryPaths(collection.logicalPath);
|
|
3937
4560
|
const directEntries = listed.filter((e) => e.collection === collection.logicalPath);
|
|
3938
4561
|
for (const listEntry of directEntries) {
|
|
3939
|
-
const entryTypeName = extractEntryTypeFromFilename(
|
|
4562
|
+
const entryTypeName = extractEntryTypeFromFilename(path12.basename(listEntry.relativePath));
|
|
3940
4563
|
if (!entryTypeName)
|
|
3941
4564
|
continue;
|
|
3942
4565
|
if (config?.exclude?.entryTypes?.includes(entryTypeName))
|
|
@@ -3954,7 +4577,7 @@ async function processCollection(store, collection, flatSchema, contentRoot, con
|
|
|
3954
4577
|
}
|
|
3955
4578
|
if (config?.exclude?.where?.(aiEntry))
|
|
3956
4579
|
continue;
|
|
3957
|
-
await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(
|
|
4580
|
+
await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path12.basename(listEntry.relativePath)), config);
|
|
3958
4581
|
entries.push(aiEntry);
|
|
3959
4582
|
const entryFilePath = `${cleanPath}/${listEntry.slug}.md`;
|
|
3960
4583
|
const entryMarkdown = entryToMarkdown(aiEntry, config);
|
|
@@ -4005,7 +4628,7 @@ async function processRootEntries(store, rootCollection, contentRoot, config, id
|
|
|
4005
4628
|
const listed = await store.getCollectionEntryPaths(rootCollection.logicalPath);
|
|
4006
4629
|
const directEntries = listed.filter((e) => e.collection === rootCollection.logicalPath);
|
|
4007
4630
|
for (const listEntry of directEntries) {
|
|
4008
|
-
const entryTypeName = extractEntryTypeFromFilename(
|
|
4631
|
+
const entryTypeName = extractEntryTypeFromFilename(path12.basename(listEntry.relativePath));
|
|
4009
4632
|
if (!entryTypeName)
|
|
4010
4633
|
continue;
|
|
4011
4634
|
if (config?.exclude?.entryTypes?.includes(entryTypeName))
|
|
@@ -4023,7 +4646,7 @@ async function processRootEntries(store, rootCollection, contentRoot, config, id
|
|
|
4023
4646
|
}
|
|
4024
4647
|
if (config?.exclude?.where?.(aiEntry))
|
|
4025
4648
|
continue;
|
|
4026
|
-
await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(
|
|
4649
|
+
await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path12.basename(listEntry.relativePath)), config);
|
|
4027
4650
|
entries.push(aiEntry);
|
|
4028
4651
|
const entryFilePath = `${listEntry.slug}.md`;
|
|
4029
4652
|
const entryMarkdown = entryToMarkdown(aiEntry, config);
|
|
@@ -4071,19 +4694,19 @@ function docToAIEntry(doc, slug, entryTypeName, entryTypeConfig, cleanCollection
|
|
|
4071
4694
|
};
|
|
4072
4695
|
}
|
|
4073
4696
|
function makeReadSibling(dir) {
|
|
4074
|
-
const resolvedDir =
|
|
4697
|
+
const resolvedDir = path12.resolve(dir);
|
|
4075
4698
|
return async (name) => {
|
|
4076
|
-
if (!name || name.includes("/") || name.includes("\\") ||
|
|
4699
|
+
if (!name || name.includes("/") || name.includes("\\") || path12.isAbsolute(name) || hasTraversalSequence(name)) {
|
|
4077
4700
|
return null;
|
|
4078
4701
|
}
|
|
4079
|
-
const abs =
|
|
4080
|
-
if (
|
|
4702
|
+
const abs = path12.resolve(resolvedDir, name);
|
|
4703
|
+
if (path12.dirname(abs) !== resolvedDir)
|
|
4081
4704
|
return null;
|
|
4082
4705
|
try {
|
|
4083
|
-
const stats = await
|
|
4706
|
+
const stats = await fs11.lstat(abs);
|
|
4084
4707
|
if (!stats.isFile())
|
|
4085
4708
|
return null;
|
|
4086
|
-
return await
|
|
4709
|
+
return await fs11.readFile(abs, "utf8");
|
|
4087
4710
|
} catch (err) {
|
|
4088
4711
|
if (isNodeError(err) && (err.code === "ENOENT" || err.code === "ENOTDIR"))
|
|
4089
4712
|
return null;
|
|
@@ -4097,7 +4720,7 @@ async function runEntryTransform(entry, absolutePath, contentId, config) {
|
|
|
4097
4720
|
return;
|
|
4098
4721
|
const ctx = {
|
|
4099
4722
|
contentId: contentId ?? "",
|
|
4100
|
-
readSibling: makeReadSibling(
|
|
4723
|
+
readSibling: makeReadSibling(path12.dirname(absolutePath))
|
|
4101
4724
|
};
|
|
4102
4725
|
try {
|
|
4103
4726
|
const appended = await transform(entry, ctx);
|
|
@@ -4134,15 +4757,15 @@ function matchesBundleFilter(entry, filter, contentRoot) {
|
|
|
4134
4757
|
}
|
|
4135
4758
|
|
|
4136
4759
|
// dist/branch-workspace.js
|
|
4137
|
-
import
|
|
4760
|
+
import path17 from "node:path";
|
|
4138
4761
|
|
|
4139
4762
|
// dist/branch-metadata.js
|
|
4140
|
-
import
|
|
4141
|
-
import
|
|
4763
|
+
import fs14 from "node:fs/promises";
|
|
4764
|
+
import path15 from "node:path";
|
|
4142
4765
|
|
|
4143
4766
|
// dist/branch-registry.js
|
|
4144
|
-
import
|
|
4145
|
-
import
|
|
4767
|
+
import fs12 from "node:fs/promises";
|
|
4768
|
+
import path13 from "node:path";
|
|
4146
4769
|
init_logger();
|
|
4147
4770
|
var log3 = createDebugLogger({ prefix: "BranchRegistry" });
|
|
4148
4771
|
var REGISTRY_FILE = "branches.json";
|
|
@@ -4155,10 +4778,10 @@ var BranchRegistry = class {
|
|
|
4155
4778
|
constructor(root) {
|
|
4156
4779
|
this.regenInFlight = null;
|
|
4157
4780
|
this.lastForcedRefreshMs = 0;
|
|
4158
|
-
this.root =
|
|
4159
|
-
this.registryPath =
|
|
4160
|
-
this.stalePath =
|
|
4161
|
-
this.tempPath =
|
|
4781
|
+
this.root = path13.resolve(root);
|
|
4782
|
+
this.registryPath = path13.join(this.root, REGISTRY_FILE);
|
|
4783
|
+
this.stalePath = path13.join(this.root, REGISTRY_STALE_FILE);
|
|
4784
|
+
this.tempPath = path13.join(this.root, REGISTRY_TEMP_FILE);
|
|
4162
4785
|
}
|
|
4163
4786
|
/**
|
|
4164
4787
|
* Returns all branches. Uses the cached snapshot if its embedded generation
|
|
@@ -4167,7 +4790,7 @@ var BranchRegistry = class {
|
|
|
4167
4790
|
async list() {
|
|
4168
4791
|
let parsed;
|
|
4169
4792
|
try {
|
|
4170
|
-
const raw = await
|
|
4793
|
+
const raw = await fs12.readFile(this.registryPath, "utf8");
|
|
4171
4794
|
parsed = JSON.parse(raw);
|
|
4172
4795
|
} catch (err) {
|
|
4173
4796
|
if (isNotFoundError(err)) {
|
|
@@ -4256,7 +4879,7 @@ var BranchRegistry = class {
|
|
|
4256
4879
|
async performRegenerate() {
|
|
4257
4880
|
const read = await readResourceGeneration(this.root, RESOURCE2);
|
|
4258
4881
|
const branches = await this.scanBranchDirectories();
|
|
4259
|
-
await
|
|
4882
|
+
await fs12.unlink(this.stalePath).catch(() => {
|
|
4260
4883
|
});
|
|
4261
4884
|
if (!read.ok) {
|
|
4262
4885
|
return branches;
|
|
@@ -4267,12 +4890,12 @@ var BranchRegistry = class {
|
|
|
4267
4890
|
generation: read.token
|
|
4268
4891
|
};
|
|
4269
4892
|
const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
4270
|
-
await
|
|
4271
|
-
await
|
|
4893
|
+
await fs12.mkdir(this.root, { recursive: true });
|
|
4894
|
+
await fs12.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
|
|
4272
4895
|
try {
|
|
4273
|
-
await
|
|
4896
|
+
await fs12.rename(uniqueTempPath, this.registryPath);
|
|
4274
4897
|
} catch (err) {
|
|
4275
|
-
await
|
|
4898
|
+
await fs12.unlink(uniqueTempPath).catch(() => {
|
|
4276
4899
|
});
|
|
4277
4900
|
throw err;
|
|
4278
4901
|
}
|
|
@@ -4286,12 +4909,12 @@ var BranchRegistry = class {
|
|
|
4286
4909
|
async scanBranchDirectories() {
|
|
4287
4910
|
const branches = [];
|
|
4288
4911
|
try {
|
|
4289
|
-
const entries = await
|
|
4912
|
+
const entries = await fs12.readdir(this.root, { withFileTypes: true });
|
|
4290
4913
|
for (const entry of entries) {
|
|
4291
4914
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
4292
4915
|
continue;
|
|
4293
4916
|
}
|
|
4294
|
-
const branchRoot =
|
|
4917
|
+
const branchRoot = path13.join(this.root, entry.name);
|
|
4295
4918
|
let meta;
|
|
4296
4919
|
try {
|
|
4297
4920
|
meta = await BranchMetadataFileManager.loadOnly(branchRoot);
|
|
@@ -4318,8 +4941,8 @@ var BranchRegistry = class {
|
|
|
4318
4941
|
};
|
|
4319
4942
|
|
|
4320
4943
|
// dist/utils/occ-json-write.js
|
|
4321
|
-
import
|
|
4322
|
-
import
|
|
4944
|
+
import fs13 from "node:fs/promises";
|
|
4945
|
+
import path14 from "node:path";
|
|
4323
4946
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
4324
4947
|
import lockfile2 from "proper-lockfile";
|
|
4325
4948
|
var log4 = createDebugLogger({ prefix: "OccJsonWrite" });
|
|
@@ -4335,29 +4958,29 @@ async function writeOccJsonFile(filePath, payload, options) {
|
|
|
4335
4958
|
const writeId = randomUUID2();
|
|
4336
4959
|
const data = { ...payload, version: newVersion, writeId };
|
|
4337
4960
|
const content = JSON.stringify(data, null, 2) + (trailingNewline ? "\n" : "");
|
|
4338
|
-
const dir =
|
|
4339
|
-
await
|
|
4961
|
+
const dir = path14.dirname(filePath);
|
|
4962
|
+
await fs13.mkdir(dir, { recursive: true });
|
|
4340
4963
|
const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
4341
|
-
await
|
|
4964
|
+
await fs13.writeFile(tempPath, content, "utf-8");
|
|
4342
4965
|
if (expectedVersion === null) {
|
|
4343
4966
|
try {
|
|
4344
|
-
await
|
|
4967
|
+
await fs13.link(tempPath, filePath);
|
|
4345
4968
|
} catch (err) {
|
|
4346
|
-
await
|
|
4969
|
+
await fs13.unlink(tempPath).catch(() => {
|
|
4347
4970
|
});
|
|
4348
4971
|
if (isFileExistsError(err)) {
|
|
4349
4972
|
throw new OccWriteConflictError();
|
|
4350
4973
|
}
|
|
4351
4974
|
throw err;
|
|
4352
4975
|
}
|
|
4353
|
-
await
|
|
4976
|
+
await fs13.unlink(tempPath).catch(() => {
|
|
4354
4977
|
});
|
|
4355
4978
|
return { version: newVersion, writeId };
|
|
4356
4979
|
}
|
|
4357
4980
|
try {
|
|
4358
4981
|
let currentVersion = null;
|
|
4359
4982
|
try {
|
|
4360
|
-
const current = JSON.parse(await
|
|
4983
|
+
const current = JSON.parse(await fs13.readFile(filePath, "utf-8"));
|
|
4361
4984
|
currentVersion = current.version ?? 0;
|
|
4362
4985
|
} catch {
|
|
4363
4986
|
currentVersion = null;
|
|
@@ -4365,16 +4988,16 @@ async function writeOccJsonFile(filePath, payload, options) {
|
|
|
4365
4988
|
if (currentVersion !== expectedVersion) {
|
|
4366
4989
|
throw new OccWriteConflictError();
|
|
4367
4990
|
}
|
|
4368
|
-
await
|
|
4991
|
+
await fs13.rename(tempPath, filePath);
|
|
4369
4992
|
if (settleMs > 0) {
|
|
4370
4993
|
await new Promise((resolve2) => setTimeout(resolve2, settleMs));
|
|
4371
4994
|
}
|
|
4372
|
-
const afterWrite = JSON.parse(await
|
|
4995
|
+
const afterWrite = JSON.parse(await fs13.readFile(filePath, "utf-8"));
|
|
4373
4996
|
if (afterWrite.writeId !== writeId) {
|
|
4374
4997
|
throw new OccWriteConflictError();
|
|
4375
4998
|
}
|
|
4376
4999
|
} catch (err) {
|
|
4377
|
-
await
|
|
5000
|
+
await fs13.unlink(tempPath).catch(() => {
|
|
4378
5001
|
});
|
|
4379
5002
|
throw err;
|
|
4380
5003
|
}
|
|
@@ -4400,8 +5023,8 @@ async function withOccRetry(operation, options) {
|
|
|
4400
5023
|
throw new Error("Unreachable");
|
|
4401
5024
|
}
|
|
4402
5025
|
async function withOccFileLock(filePath, fn) {
|
|
4403
|
-
const dir =
|
|
4404
|
-
await
|
|
5026
|
+
const dir = path14.dirname(filePath);
|
|
5027
|
+
await fs13.mkdir(dir, { recursive: true });
|
|
4405
5028
|
const MAX_ATTEMPTS = 14;
|
|
4406
5029
|
let release = null;
|
|
4407
5030
|
for (let attempt = 1; release === null; attempt++) {
|
|
@@ -4464,8 +5087,8 @@ var BranchMetadataCorruptError = class extends Error {
|
|
|
4464
5087
|
};
|
|
4465
5088
|
var BranchMetadataFileManager = class _BranchMetadataFileManager {
|
|
4466
5089
|
constructor(branchRoot, baseRoot, options) {
|
|
4467
|
-
this.branchRoot =
|
|
4468
|
-
this.filePath =
|
|
5090
|
+
this.branchRoot = path15.resolve(branchRoot);
|
|
5091
|
+
this.filePath = path15.join(this.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE);
|
|
4469
5092
|
this.baseRoot = baseRoot;
|
|
4470
5093
|
this.settleMs = options?.settleMs;
|
|
4471
5094
|
}
|
|
@@ -4474,10 +5097,10 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
|
|
|
4474
5097
|
* Use this for read-only access (e.g., in registry scanning or loadBranchContext).
|
|
4475
5098
|
*/
|
|
4476
5099
|
static async loadOnly(branchRoot) {
|
|
4477
|
-
const resolvedRoot =
|
|
4478
|
-
const filePath =
|
|
5100
|
+
const resolvedRoot = path15.resolve(branchRoot);
|
|
5101
|
+
const filePath = path15.join(resolvedRoot, BRANCH_META_DIR, BRANCH_META_FILE);
|
|
4479
5102
|
try {
|
|
4480
|
-
const raw = await
|
|
5103
|
+
const raw = await fs14.readFile(filePath, "utf8");
|
|
4481
5104
|
return JSON.parse(raw);
|
|
4482
5105
|
} catch (err) {
|
|
4483
5106
|
if (isNotFoundError(err)) {
|
|
@@ -4498,7 +5121,7 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
|
|
|
4498
5121
|
}
|
|
4499
5122
|
async load() {
|
|
4500
5123
|
try {
|
|
4501
|
-
const raw = await
|
|
5124
|
+
const raw = await fs14.readFile(this.filePath, "utf8");
|
|
4502
5125
|
const parsed = JSON.parse(raw);
|
|
4503
5126
|
const version = parsed.version ?? 0;
|
|
4504
5127
|
return { meta: parsed, version };
|
|
@@ -4560,7 +5183,7 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
|
|
|
4560
5183
|
*/
|
|
4561
5184
|
async save(incoming) {
|
|
4562
5185
|
try {
|
|
4563
|
-
await
|
|
5186
|
+
await fs14.stat(this.branchRoot);
|
|
4564
5187
|
} catch (err) {
|
|
4565
5188
|
if (isNotFoundError(err)) {
|
|
4566
5189
|
throw new BranchMetadataConflictError("Branch no longer exists");
|
|
@@ -4648,8 +5271,8 @@ var loadBranchContext = async (options) => {
|
|
|
4648
5271
|
init_operating_mode();
|
|
4649
5272
|
|
|
4650
5273
|
// dist/git-manager.js
|
|
4651
|
-
import
|
|
4652
|
-
import
|
|
5274
|
+
import fs15 from "node:fs/promises";
|
|
5275
|
+
import path16 from "node:path";
|
|
4653
5276
|
import { simpleGit as simpleGit2 } from "simple-git";
|
|
4654
5277
|
|
|
4655
5278
|
// dist/utils/git.js
|
|
@@ -4709,11 +5332,11 @@ function gitChildEnv(overrides) {
|
|
|
4709
5332
|
var remoteInitLocks = /* @__PURE__ */ new Map();
|
|
4710
5333
|
var GITHUB_TRACKING_REF_PREFIX = "refs/remotes/github/";
|
|
4711
5334
|
async function ensureGitExcludePattern(repoPath, pattern) {
|
|
4712
|
-
const excludePath =
|
|
4713
|
-
await
|
|
5335
|
+
const excludePath = path16.join(repoPath, ".git", "info", "exclude");
|
|
5336
|
+
await fs15.mkdir(path16.dirname(excludePath), { recursive: true });
|
|
4714
5337
|
let content = "";
|
|
4715
5338
|
try {
|
|
4716
|
-
content = await
|
|
5339
|
+
content = await fs15.readFile(excludePath, "utf-8");
|
|
4717
5340
|
} catch (err) {
|
|
4718
5341
|
if (!isNotFoundError(err))
|
|
4719
5342
|
throw err;
|
|
@@ -4725,7 +5348,7 @@ async function ensureGitExcludePattern(repoPath, pattern) {
|
|
|
4725
5348
|
}
|
|
4726
5349
|
const needsLeadingNewline = content.length > 0 && !content.endsWith("\n");
|
|
4727
5350
|
const newContent = content + (needsLeadingNewline ? "\n" : "") + pattern + "\n";
|
|
4728
|
-
await
|
|
5351
|
+
await fs15.writeFile(excludePath, newContent, "utf-8");
|
|
4729
5352
|
log5.debug("git", "Added pattern to .git/info/exclude", { pattern });
|
|
4730
5353
|
}
|
|
4731
5354
|
var GitConflictError = class extends Error {
|
|
@@ -4746,12 +5369,12 @@ var GitRemoteRefMissingError = class extends Error {
|
|
|
4746
5369
|
};
|
|
4747
5370
|
var GitManager = class _GitManager {
|
|
4748
5371
|
constructor(options, gitOptions) {
|
|
4749
|
-
this.repoPath =
|
|
5372
|
+
this.repoPath = path16.resolve(options.repoPath);
|
|
4750
5373
|
this.baseBranch = options.baseBranch ?? "main";
|
|
4751
5374
|
this.remote = options.remote ?? "origin";
|
|
4752
5375
|
this.skipIndexMarker = options.skipIndexMarker ?? false;
|
|
4753
5376
|
this.git = simpleGit2({ baseDir: this.repoPath, ...gitOptions });
|
|
4754
|
-
this.git.env(gitChildEnv({ GIT_CEILING_DIRECTORIES:
|
|
5377
|
+
this.git.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(this.repoPath) }));
|
|
4755
5378
|
}
|
|
4756
5379
|
static async cloneRepo(remoteUrl, targetPath, baseBranch = "main") {
|
|
4757
5380
|
log5.debug("git", "Cloning repository", {
|
|
@@ -4793,11 +5416,11 @@ var GitManager = class _GitManager {
|
|
|
4793
5416
|
remotePath: options.remotePath,
|
|
4794
5417
|
baseBranch: options.baseBranch
|
|
4795
5418
|
});
|
|
4796
|
-
const remoteParent =
|
|
5419
|
+
const remoteParent = path16.dirname(options.remotePath);
|
|
4797
5420
|
releaseLock = await acquireProvisioningLock(remoteParent, ".remote-init.lock");
|
|
4798
5421
|
let remoteExists = false;
|
|
4799
5422
|
try {
|
|
4800
|
-
const stat = await
|
|
5423
|
+
const stat = await fs15.stat(options.remotePath);
|
|
4801
5424
|
remoteExists = stat.isDirectory();
|
|
4802
5425
|
} catch (err) {
|
|
4803
5426
|
if (!isNotFoundError(err))
|
|
@@ -5049,7 +5672,7 @@ var GitManager = class _GitManager {
|
|
|
5049
5672
|
*/
|
|
5050
5673
|
static async validateGitRepoExists(repoPath) {
|
|
5051
5674
|
try {
|
|
5052
|
-
const stat = await
|
|
5675
|
+
const stat = await fs15.stat(path16.join(repoPath, ".git"));
|
|
5053
5676
|
if (!stat.isDirectory()) {
|
|
5054
5677
|
throw new Error(`Expected git repo at ${repoPath}`);
|
|
5055
5678
|
}
|
|
@@ -5127,7 +5750,7 @@ var GitManager = class _GitManager {
|
|
|
5127
5750
|
}
|
|
5128
5751
|
if (config.autoDetectRemotePath) {
|
|
5129
5752
|
try {
|
|
5130
|
-
const stat = await
|
|
5753
|
+
const stat = await fs15.stat(config.autoDetectRemotePath);
|
|
5131
5754
|
if (stat.isDirectory()) {
|
|
5132
5755
|
log5.debug("git", "Auto-detected local remote", {
|
|
5133
5756
|
path: config.autoDetectRemotePath
|
|
@@ -5140,8 +5763,8 @@ var GitManager = class _GitManager {
|
|
|
5140
5763
|
if (config.shouldAutoInitLocal) {
|
|
5141
5764
|
const gitRoot = await this.findGitRoot();
|
|
5142
5765
|
const sourceRoot = options.sourceRoot;
|
|
5143
|
-
const sourcePath = sourceRoot ?
|
|
5144
|
-
const localRemotePath =
|
|
5766
|
+
const sourcePath = sourceRoot ? path16.resolve(gitRoot, sourceRoot) : gitRoot;
|
|
5767
|
+
const localRemotePath = path16.join(sourcePath, config.defaultRemotePath);
|
|
5145
5768
|
await this.ensureLocalSimulatedRemote({
|
|
5146
5769
|
remotePath: localRemotePath,
|
|
5147
5770
|
sourcePath: gitRoot,
|
|
@@ -5169,7 +5792,7 @@ var GitManager = class _GitManager {
|
|
|
5169
5792
|
static async repoExistsAt(workspacePath) {
|
|
5170
5793
|
try {
|
|
5171
5794
|
const checkGit = simpleGit2({ baseDir: workspacePath });
|
|
5172
|
-
checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES:
|
|
5795
|
+
checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(workspacePath) }));
|
|
5173
5796
|
await checkGit.raw(["rev-parse", "--git-dir"]);
|
|
5174
5797
|
return true;
|
|
5175
5798
|
} catch {
|
|
@@ -5191,19 +5814,19 @@ var GitManager = class _GitManager {
|
|
|
5191
5814
|
const baseBranch = await resolveBaseBranch({
|
|
5192
5815
|
defaultBaseBranch: options.baseBranch,
|
|
5193
5816
|
mode: options.mode,
|
|
5194
|
-
detectFrom: options.sourceRoot ?
|
|
5817
|
+
detectFrom: options.sourceRoot ? path16.resolve(process.cwd(), options.sourceRoot) : process.cwd()
|
|
5195
5818
|
});
|
|
5196
5819
|
const remoteName = options.remoteName ?? "origin";
|
|
5197
5820
|
const repoExists = await _GitManager.repoExistsAt(options.workspacePath);
|
|
5198
5821
|
if (!repoExists) {
|
|
5199
|
-
const gitPath =
|
|
5822
|
+
const gitPath = path16.join(options.workspacePath, ".git");
|
|
5200
5823
|
try {
|
|
5201
|
-
const stat = await
|
|
5824
|
+
const stat = await fs15.stat(gitPath);
|
|
5202
5825
|
if (stat.isDirectory()) {
|
|
5203
5826
|
log5.debug("git", "Removing corrupt .git directory", {
|
|
5204
5827
|
workspacePath: options.workspacePath
|
|
5205
5828
|
});
|
|
5206
|
-
await
|
|
5829
|
+
await fs15.rm(gitPath, { recursive: true });
|
|
5207
5830
|
}
|
|
5208
5831
|
} catch (cleanupErr) {
|
|
5209
5832
|
if (!isNotFoundError(cleanupErr))
|
|
@@ -5230,7 +5853,7 @@ var GitManager = class _GitManager {
|
|
|
5230
5853
|
}
|
|
5231
5854
|
justCloned = true;
|
|
5232
5855
|
const freshGit = simpleGit2({ baseDir: options.workspacePath });
|
|
5233
|
-
freshGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES:
|
|
5856
|
+
freshGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(options.workspacePath) }));
|
|
5234
5857
|
await freshGit.addConfig("canopycms.managed", "true");
|
|
5235
5858
|
await freshGit.addConfig("user.name", options.gitBotAuthorName);
|
|
5236
5859
|
await freshGit.addConfig("user.email", options.gitBotAuthorEmail);
|
|
@@ -5581,9 +6204,9 @@ var GitManager = class _GitManager {
|
|
|
5581
6204
|
} catch {
|
|
5582
6205
|
}
|
|
5583
6206
|
for (const [filePath, content] of Object.entries(initialFiles)) {
|
|
5584
|
-
const absolutePath =
|
|
5585
|
-
await
|
|
5586
|
-
await
|
|
6207
|
+
const absolutePath = path16.join(this.repoPath, filePath);
|
|
6208
|
+
await fs15.mkdir(path16.dirname(absolutePath), { recursive: true });
|
|
6209
|
+
await fs15.writeFile(absolutePath, content, "utf-8");
|
|
5587
6210
|
await this.git.add(filePath);
|
|
5588
6211
|
}
|
|
5589
6212
|
await this.git.commit("Initialize settings branch", ["--allow-empty"]);
|
|
@@ -5612,7 +6235,7 @@ var BranchWorkspaceManager = class {
|
|
|
5612
6235
|
branchName: options.branchName,
|
|
5613
6236
|
mode: options.mode
|
|
5614
6237
|
});
|
|
5615
|
-
releaseLock = await acquireProvisioningLock(
|
|
6238
|
+
releaseLock = await acquireProvisioningLock(path17.dirname(options.branchRoot), `.${path17.basename(options.branchRoot)}.init.lock`);
|
|
5616
6239
|
await GitManager.initializeWorkspace({
|
|
5617
6240
|
workspacePath: options.branchRoot,
|
|
5618
6241
|
branchName: options.branchName,
|
|
@@ -5653,7 +6276,7 @@ var BranchWorkspaceManager = class {
|
|
|
5653
6276
|
const baseBranch = await resolveBaseBranch({
|
|
5654
6277
|
defaultBaseBranch: this.config.defaultBaseBranch,
|
|
5655
6278
|
mode,
|
|
5656
|
-
detectFrom: this.config.sourceRoot ?
|
|
6279
|
+
detectFrom: this.config.sourceRoot ? path17.resolve(process.cwd(), this.config.sourceRoot) : void 0
|
|
5657
6280
|
});
|
|
5658
6281
|
await this.ensureGitWorkspace({
|
|
5659
6282
|
branchRoot,
|
|
@@ -5737,9 +6360,9 @@ async function resolveBranchRoot(config) {
|
|
|
5737
6360
|
}
|
|
5738
6361
|
|
|
5739
6362
|
// dist/content-listing.js
|
|
5740
|
-
import
|
|
5741
|
-
import
|
|
5742
|
-
import
|
|
6363
|
+
import fs16 from "node:fs/promises";
|
|
6364
|
+
import path18 from "node:path";
|
|
6365
|
+
import matter3 from "gray-matter";
|
|
5743
6366
|
import { parse as yamlParse2 } from "yaml";
|
|
5744
6367
|
var log7 = createDebugLogger({ prefix: "ContentListing" });
|
|
5745
6368
|
var normalizePath = (root, target) => {
|
|
@@ -5751,15 +6374,15 @@ var normalizePath = (root, target) => {
|
|
|
5751
6374
|
};
|
|
5752
6375
|
var readEntryData = async (filePath, format, bodyFieldName = "body") => {
|
|
5753
6376
|
try {
|
|
5754
|
-
const raw = await
|
|
6377
|
+
const raw = await fs16.readFile(filePath, "utf8");
|
|
5755
6378
|
if (format === "json") {
|
|
5756
6379
|
return asRecord(JSON.parse(raw));
|
|
5757
6380
|
}
|
|
5758
6381
|
if (format === "yaml") {
|
|
5759
6382
|
return asRecord(yamlParse2(raw));
|
|
5760
6383
|
}
|
|
5761
|
-
const parsed =
|
|
5762
|
-
const data = parsed.data ?? {};
|
|
6384
|
+
const parsed = matter3(raw);
|
|
6385
|
+
const data = { ...parsed.data ?? {} };
|
|
5763
6386
|
if (parsed.content) {
|
|
5764
6387
|
data[bodyFieldName] = parsed.content;
|
|
5765
6388
|
}
|
|
@@ -5771,47 +6394,47 @@ var readEntryData = async (filePath, format, bodyFieldName = "body") => {
|
|
|
5771
6394
|
return {};
|
|
5772
6395
|
}
|
|
5773
6396
|
};
|
|
5774
|
-
var
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
const
|
|
5780
|
-
if (
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
return {
|
|
5789
|
-
type: potentialType,
|
|
5790
|
-
slug,
|
|
5791
|
-
id
|
|
5792
|
-
};
|
|
5793
|
-
}
|
|
5794
|
-
}
|
|
5795
|
-
return null;
|
|
5796
|
-
};
|
|
5797
|
-
async function listEntries(branchRoot, flatSchema, contentRootName, options) {
|
|
6397
|
+
var createReferenceResolver = (branchRoot, flatSchema, contentRootName) => ({
|
|
6398
|
+
store: new ContentStore(branchRoot, flatSchema, { contentRootName }),
|
|
6399
|
+
cache: createReferenceResolveCache()
|
|
6400
|
+
});
|
|
6401
|
+
var resolveCollectionItemReferences = async (items, collection, resolver) => Promise.all(items.map(async (item) => {
|
|
6402
|
+
const fields = collection.entries?.find((e) => e.name === item.entryType)?.schema;
|
|
6403
|
+
if (!fields)
|
|
6404
|
+
return item;
|
|
6405
|
+
return {
|
|
6406
|
+
...item,
|
|
6407
|
+
data: await resolver.store.resolveReferences(item.data, fields, resolver.cache)
|
|
6408
|
+
};
|
|
6409
|
+
}));
|
|
6410
|
+
async function listEntries(branchRoot, flatSchema, contentRootName, options, visibility) {
|
|
5798
6411
|
const rootPath = options?.rootPath ?? contentRootName;
|
|
5799
6412
|
const extract = options?.extract;
|
|
5800
6413
|
const filter = options?.filter;
|
|
5801
6414
|
const customSort = options?.sort;
|
|
5802
6415
|
const collections = flatSchema.filter((item) => item.type === "collection" && item.entries !== void 0 && (item.logicalPath === rootPath || item.logicalPath.startsWith(`${rootPath}/`)));
|
|
6416
|
+
const shouldInclude = visibility?.shouldInclude;
|
|
6417
|
+
const skippedFiles = [];
|
|
6418
|
+
const resolver = options?.resolveReferences ? createReferenceResolver(branchRoot, flatSchema, contentRootName) : null;
|
|
5803
6419
|
const collectionResults = await Promise.all(collections.map(async (collection) => {
|
|
5804
|
-
const entries = await listCollectionEntries(branchRoot, collection);
|
|
5805
|
-
|
|
6420
|
+
const entries = await listCollectionEntries(branchRoot, collection, (file) => skippedFiles.push(file));
|
|
6421
|
+
const visible = shouldInclude ? entries.filter((e) => shouldInclude(e.physicalPath)) : entries;
|
|
6422
|
+
const resolved = resolver ? await resolveCollectionItemReferences(visible, collection, resolver) : visible;
|
|
6423
|
+
return resolved.map((entry) => ({ entry, collection }));
|
|
5806
6424
|
}));
|
|
6425
|
+
if (isBuildMode() && skippedFiles.length > 0) {
|
|
6426
|
+
const lines = skippedFiles.map(({ filename, collectionPath }) => ` - ${collectionPath}/${filename}`);
|
|
6427
|
+
throw new Error(`CanopyCMS static build: found ${skippedFiles.length} file(s) that look like malformed content entries inside a content collection:
|
|
6428
|
+
${lines.join("\n")}
|
|
6429
|
+
Expected {type}.{slug}.{id}.{ext} with a known entry type (a name declared in that collection's entries config) and a valid 12-char Base58 ID. This usually means a schema rename without a matching file rename, an entry type declared in one collection but not another, or a stray file placed directly in a collection directory. If this is meant to be a colocated sibling artifact (e.g. an entry's contentId-prefixed data file read via an entryTransforms readSibling call), keep its name to three or fewer dot-separated segments (id.suffix.ext) so it is never mistaken for an entry attempt. Otherwise rename or move the file, or fix the schema, then rebuild.`);
|
|
6430
|
+
}
|
|
5807
6431
|
const contentPrefix = contentRootName ? `${contentRootName}/` : "";
|
|
5808
6432
|
const items = [];
|
|
5809
6433
|
for (const results of collectionResults) {
|
|
5810
6434
|
for (const { entry, collection } of results) {
|
|
5811
6435
|
const pathWithoutRoot = entry.logicalPath.startsWith(contentPrefix) ? entry.logicalPath.slice(contentPrefix.length) : entry.logicalPath;
|
|
5812
6436
|
const pathSegments = pathWithoutRoot.split("/").filter(Boolean);
|
|
5813
|
-
const
|
|
5814
|
-
const urlPath = urlSegments.length > 0 ? `/${urlSegments.join("/")}`.toLowerCase() : "/";
|
|
6437
|
+
const urlPath = computeEntryUrl(entry.collectionPath, entry.slug, contentRootName);
|
|
5815
6438
|
const raw = entry.data;
|
|
5816
6439
|
const meta = {
|
|
5817
6440
|
entryPath: entry.logicalPath,
|
|
@@ -5830,7 +6453,8 @@ async function listEntries(branchRoot, flatSchema, contentRootName, options) {
|
|
|
5830
6453
|
entryType: entry.entryType,
|
|
5831
6454
|
format: entry.format,
|
|
5832
6455
|
data,
|
|
5833
|
-
schema: collection.entries?.find((e) => e.name === entry.entryType)?.schema
|
|
6456
|
+
schema: collection.entries?.find((e) => e.name === entry.entryType)?.schema,
|
|
6457
|
+
updatedAt: entry.updatedAt
|
|
5834
6458
|
};
|
|
5835
6459
|
if (filter && !filter(item))
|
|
5836
6460
|
continue;
|
|
@@ -5844,7 +6468,15 @@ async function listEntries(branchRoot, flatSchema, contentRootName, options) {
|
|
|
5844
6468
|
}
|
|
5845
6469
|
return items;
|
|
5846
6470
|
}
|
|
5847
|
-
var
|
|
6471
|
+
var looksLikeMalformedEntry = (filename, entryTypes) => {
|
|
6472
|
+
if (filename.startsWith(".") || filename.startsWith("_"))
|
|
6473
|
+
return false;
|
|
6474
|
+
const segments = filename.split(".");
|
|
6475
|
+
if (segments.length >= 4)
|
|
6476
|
+
return true;
|
|
6477
|
+
return segments.length === 3 && entryTypes.some((e) => e.name === segments[0]);
|
|
6478
|
+
};
|
|
6479
|
+
var listCollectionEntries = async (root, collection, onSkip) => {
|
|
5848
6480
|
if (collection.type !== "collection" || !collection.entries) {
|
|
5849
6481
|
return [];
|
|
5850
6482
|
}
|
|
@@ -5864,7 +6496,7 @@ var listCollectionEntries = async (root, collection) => {
|
|
|
5864
6496
|
normalizePath(root, collectionRoot);
|
|
5865
6497
|
let dirents;
|
|
5866
6498
|
try {
|
|
5867
|
-
dirents = await
|
|
6499
|
+
dirents = await fs16.readdir(collectionRoot, { withFileTypes: true });
|
|
5868
6500
|
} catch (err) {
|
|
5869
6501
|
if (isNotFoundError(err))
|
|
5870
6502
|
return [];
|
|
@@ -5872,11 +6504,14 @@ var listCollectionEntries = async (root, collection) => {
|
|
|
5872
6504
|
}
|
|
5873
6505
|
const files = dirents.filter((d) => d.isFile() && validExts.some((ext) => d.name.endsWith(ext)) && d.name !== ".collection.json").sort((a, b) => a.name.localeCompare(b.name));
|
|
5874
6506
|
const entries = await Promise.all(files.map(async (file) => {
|
|
5875
|
-
const absolutePath =
|
|
6507
|
+
const absolutePath = path18.join(collectionRoot, file.name);
|
|
5876
6508
|
const relativePath = normalizePath(root, absolutePath);
|
|
5877
6509
|
const parsed = parseTypedFilename(file.name, entryTypes);
|
|
5878
6510
|
if (!parsed) {
|
|
5879
6511
|
log7.warn("listCollectionEntries", `Skipping file with unrecognized filename format: ${file.name} (expected {type}.{slug}.{id}.{ext} with a known entry type and valid 12-char Base58 ID)`);
|
|
6512
|
+
if (looksLikeMalformedEntry(file.name, entryTypes)) {
|
|
6513
|
+
onSkip?.({ filename: file.name, collectionPath: collection.logicalPath });
|
|
6514
|
+
}
|
|
5880
6515
|
return null;
|
|
5881
6516
|
}
|
|
5882
6517
|
const { type: entryTypeName, slug, id: contentId } = parsed;
|
|
@@ -5884,7 +6519,7 @@ var listCollectionEntries = async (root, collection) => {
|
|
|
5884
6519
|
const format = entryType?.format || "json";
|
|
5885
6520
|
const bodyField = entryType?.schema ? findBodyFieldName(entryType.schema) : "body";
|
|
5886
6521
|
const [stats, data] = await Promise.all([
|
|
5887
|
-
|
|
6522
|
+
fs16.stat(absolutePath),
|
|
5888
6523
|
readEntryData(absolutePath, format, bodyField)
|
|
5889
6524
|
]);
|
|
5890
6525
|
const item = {
|
|
@@ -5922,7 +6557,7 @@ function isValidCropRect(x, y, w, h) {
|
|
|
5922
6557
|
}
|
|
5923
6558
|
|
|
5924
6559
|
// dist/validation/field-traversal.js
|
|
5925
|
-
function
|
|
6560
|
+
function isPlainRecord2(value) {
|
|
5926
6561
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5927
6562
|
}
|
|
5928
6563
|
function resolveBlockItem(blockField, item) {
|
|
@@ -5932,21 +6567,21 @@ function resolveBlockItem(blockField, item) {
|
|
|
5932
6567
|
const template = blockField.templates?.find((t) => t.name === templateName);
|
|
5933
6568
|
if (!template?.fields)
|
|
5934
6569
|
return void 0;
|
|
5935
|
-
const data =
|
|
6570
|
+
const data = isPlainRecord2(item.value) ? item.value : item;
|
|
5936
6571
|
return { fields: template.fields, data };
|
|
5937
6572
|
}
|
|
5938
6573
|
|
|
5939
6574
|
// dist/validation/entry-validator.js
|
|
5940
6575
|
var KNOWN_FIELD_TYPES = new Set(fieldTypes);
|
|
5941
6576
|
var STRING_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "markdown", "mdx", "code"]);
|
|
5942
|
-
function
|
|
6577
|
+
function isPlainRecord3(value) {
|
|
5943
6578
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5944
6579
|
}
|
|
5945
6580
|
function isPositiveInt(value) {
|
|
5946
6581
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
5947
6582
|
}
|
|
5948
6583
|
function isValidImageCropValue(value) {
|
|
5949
|
-
if (!
|
|
6584
|
+
if (!isPlainRecord3(value))
|
|
5950
6585
|
return false;
|
|
5951
6586
|
const { x, y, w, h } = value;
|
|
5952
6587
|
if (typeof x !== "number" || typeof y !== "number" || typeof w !== "number" || typeof h !== "number") {
|
|
@@ -5954,79 +6589,79 @@ function isValidImageCropValue(value) {
|
|
|
5954
6589
|
}
|
|
5955
6590
|
return isValidCropRect(x, y, w, h);
|
|
5956
6591
|
}
|
|
5957
|
-
function validateImageValue(field, value,
|
|
5958
|
-
if (!
|
|
5959
|
-
return [{ fieldPath:
|
|
6592
|
+
function validateImageValue(field, value, path21) {
|
|
6593
|
+
if (!isPlainRecord3(value)) {
|
|
6594
|
+
return [{ fieldPath: path21, message: "Expected an image object with { src, alt }" }];
|
|
5960
6595
|
}
|
|
5961
6596
|
const errors = [];
|
|
5962
6597
|
if (typeof value.src !== "string" || value.src.trim() === "") {
|
|
5963
|
-
errors.push({ fieldPath: `${
|
|
6598
|
+
errors.push({ fieldPath: `${path21}.src`, message: "Image src is required" });
|
|
5964
6599
|
}
|
|
5965
6600
|
if (typeof value.alt !== "string") {
|
|
5966
|
-
errors.push({ fieldPath: `${
|
|
6601
|
+
errors.push({ fieldPath: `${path21}.alt`, message: "Image alt text is required" });
|
|
5967
6602
|
} else if (value.alt.trim() === "" && field.altOptional !== true) {
|
|
5968
|
-
errors.push({ fieldPath: `${
|
|
6603
|
+
errors.push({ fieldPath: `${path21}.alt`, message: "Image alt text is required" });
|
|
5969
6604
|
}
|
|
5970
6605
|
if (value.width !== void 0 && !isPositiveInt(value.width)) {
|
|
5971
|
-
errors.push({ fieldPath: `${
|
|
6606
|
+
errors.push({ fieldPath: `${path21}.width`, message: "Image width must be a positive integer" });
|
|
5972
6607
|
}
|
|
5973
6608
|
if (value.height !== void 0 && !isPositiveInt(value.height)) {
|
|
5974
6609
|
errors.push({
|
|
5975
|
-
fieldPath: `${
|
|
6610
|
+
fieldPath: `${path21}.height`,
|
|
5976
6611
|
message: "Image height must be a positive integer"
|
|
5977
6612
|
});
|
|
5978
6613
|
}
|
|
5979
6614
|
if (value.crop !== void 0 && !isValidImageCropValue(value.crop)) {
|
|
5980
|
-
errors.push({ fieldPath: `${
|
|
6615
|
+
errors.push({ fieldPath: `${path21}.crop`, message: "Invalid image crop rect" });
|
|
5981
6616
|
}
|
|
5982
6617
|
return errors;
|
|
5983
6618
|
}
|
|
5984
6619
|
function referenceValueId(value) {
|
|
5985
6620
|
if (typeof value === "string")
|
|
5986
6621
|
return value;
|
|
5987
|
-
if (
|
|
6622
|
+
if (isPlainRecord3(value) && typeof value.id === "string")
|
|
5988
6623
|
return value.id;
|
|
5989
6624
|
return void 0;
|
|
5990
6625
|
}
|
|
5991
6626
|
var selectOptionValues = (field) => (field.options ?? []).map((opt) => typeof opt === "string" ? opt : opt.value);
|
|
5992
|
-
function validateScalar(field, value,
|
|
6627
|
+
function validateScalar(field, value, path21) {
|
|
5993
6628
|
switch (field.type) {
|
|
5994
6629
|
case "number":
|
|
5995
6630
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
5996
|
-
return [{ fieldPath:
|
|
6631
|
+
return [{ fieldPath: path21, message: "Expected a number" }];
|
|
5997
6632
|
}
|
|
5998
6633
|
return [];
|
|
5999
6634
|
case "boolean":
|
|
6000
6635
|
if (typeof value !== "boolean") {
|
|
6001
|
-
return [{ fieldPath:
|
|
6636
|
+
return [{ fieldPath: path21, message: "Expected true or false" }];
|
|
6002
6637
|
}
|
|
6003
6638
|
return [];
|
|
6004
6639
|
case "datetime":
|
|
6005
6640
|
if (typeof value !== "string" || value !== "" && Number.isNaN(Date.parse(value))) {
|
|
6006
|
-
return [{ fieldPath:
|
|
6641
|
+
return [{ fieldPath: path21, message: "Expected a valid date/time string" }];
|
|
6007
6642
|
}
|
|
6008
6643
|
return [];
|
|
6009
6644
|
case "select": {
|
|
6010
6645
|
if (typeof value !== "string") {
|
|
6011
|
-
return [{ fieldPath:
|
|
6646
|
+
return [{ fieldPath: path21, message: "Expected a selection" }];
|
|
6012
6647
|
}
|
|
6013
6648
|
const options = selectOptionValues(field);
|
|
6014
6649
|
if (value !== "" && !options.includes(value)) {
|
|
6015
|
-
return [{ fieldPath:
|
|
6650
|
+
return [{ fieldPath: path21, message: `Must be one of: ${options.join(", ")}` }];
|
|
6016
6651
|
}
|
|
6017
6652
|
return [];
|
|
6018
6653
|
}
|
|
6019
6654
|
case "reference":
|
|
6020
6655
|
if (referenceValueId(value) === void 0) {
|
|
6021
|
-
return [{ fieldPath:
|
|
6656
|
+
return [{ fieldPath: path21, message: "Expected a reference id" }];
|
|
6022
6657
|
}
|
|
6023
6658
|
return [];
|
|
6024
6659
|
case "image":
|
|
6025
|
-
return validateImageValue(field, value,
|
|
6660
|
+
return validateImageValue(field, value, path21);
|
|
6026
6661
|
default:
|
|
6027
6662
|
if (STRING_FIELD_TYPES.has(field.type)) {
|
|
6028
6663
|
if (typeof value !== "string") {
|
|
6029
|
-
return [{ fieldPath:
|
|
6664
|
+
return [{ fieldPath: path21, message: "Expected text" }];
|
|
6030
6665
|
}
|
|
6031
6666
|
}
|
|
6032
6667
|
return [];
|
|
@@ -6049,12 +6684,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
|
|
|
6049
6684
|
errors.push(...validateEntryData(field.fields, data, pathPrefix));
|
|
6050
6685
|
continue;
|
|
6051
6686
|
}
|
|
6052
|
-
const
|
|
6687
|
+
const path21 = pathPrefix ? `${pathPrefix}.${field.name}` : field.name;
|
|
6053
6688
|
const value = data[field.name];
|
|
6054
6689
|
const isRequired = "required" in field && field.required === true;
|
|
6055
6690
|
if (isRequired && isEmptyForRequired(field, value)) {
|
|
6056
6691
|
const message = Array.isArray(value) ? "At least one item is required" : "This field is required";
|
|
6057
|
-
errors.push({ fieldPath:
|
|
6692
|
+
errors.push({ fieldPath: path21, message });
|
|
6058
6693
|
continue;
|
|
6059
6694
|
}
|
|
6060
6695
|
if (value === void 0 || value === null)
|
|
@@ -6062,12 +6697,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
|
|
|
6062
6697
|
if (field.type === "block") {
|
|
6063
6698
|
const blockField = field;
|
|
6064
6699
|
if (!Array.isArray(value)) {
|
|
6065
|
-
errors.push({ fieldPath:
|
|
6700
|
+
errors.push({ fieldPath: path21, message: "Expected a list of blocks" });
|
|
6066
6701
|
continue;
|
|
6067
6702
|
}
|
|
6068
6703
|
value.forEach((item, index) => {
|
|
6069
|
-
const itemPath = `${
|
|
6070
|
-
if (!
|
|
6704
|
+
const itemPath = `${path21}[${index}]`;
|
|
6705
|
+
if (!isPlainRecord3(item)) {
|
|
6071
6706
|
errors.push({ fieldPath: itemPath, message: "Expected a block object" });
|
|
6072
6707
|
return;
|
|
6073
6708
|
}
|
|
@@ -6088,12 +6723,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
|
|
|
6088
6723
|
const objectField = field;
|
|
6089
6724
|
if (objectField.list) {
|
|
6090
6725
|
if (!Array.isArray(value)) {
|
|
6091
|
-
errors.push({ fieldPath:
|
|
6726
|
+
errors.push({ fieldPath: path21, message: "Expected a list of items" });
|
|
6092
6727
|
continue;
|
|
6093
6728
|
}
|
|
6094
6729
|
value.forEach((item, index) => {
|
|
6095
|
-
const itemPath = `${
|
|
6096
|
-
if (!
|
|
6730
|
+
const itemPath = `${path21}[${index}]`;
|
|
6731
|
+
if (!isPlainRecord3(item)) {
|
|
6097
6732
|
errors.push({ fieldPath: itemPath, message: "Expected an object" });
|
|
6098
6733
|
return;
|
|
6099
6734
|
}
|
|
@@ -6101,28 +6736,28 @@ function validateEntryData(fields, data, pathPrefix = "") {
|
|
|
6101
6736
|
errors.push(...validateEntryData(objectField.fields, item, itemPath));
|
|
6102
6737
|
}
|
|
6103
6738
|
});
|
|
6104
|
-
} else if (!
|
|
6105
|
-
errors.push({ fieldPath:
|
|
6739
|
+
} else if (!isPlainRecord3(value)) {
|
|
6740
|
+
errors.push({ fieldPath: path21, message: "Expected an object" });
|
|
6106
6741
|
} else if (objectField.fields) {
|
|
6107
|
-
errors.push(...validateEntryData(objectField.fields, value,
|
|
6742
|
+
errors.push(...validateEntryData(objectField.fields, value, path21));
|
|
6108
6743
|
}
|
|
6109
6744
|
continue;
|
|
6110
6745
|
}
|
|
6111
6746
|
if ("list" in field && field.list) {
|
|
6112
6747
|
if (!Array.isArray(value)) {
|
|
6113
|
-
errors.push({ fieldPath:
|
|
6748
|
+
errors.push({ fieldPath: path21, message: "Expected a list of values" });
|
|
6114
6749
|
continue;
|
|
6115
6750
|
}
|
|
6116
6751
|
value.forEach((item, index) => {
|
|
6117
|
-
errors.push(...validateScalar(field, item, `${
|
|
6752
|
+
errors.push(...validateScalar(field, item, `${path21}[${index}]`));
|
|
6118
6753
|
});
|
|
6119
6754
|
continue;
|
|
6120
6755
|
}
|
|
6121
6756
|
if (Array.isArray(value) && KNOWN_FIELD_TYPES.has(field.type)) {
|
|
6122
|
-
errors.push({ fieldPath:
|
|
6757
|
+
errors.push({ fieldPath: path21, message: "Expected a single value, not a list" });
|
|
6123
6758
|
continue;
|
|
6124
6759
|
}
|
|
6125
|
-
errors.push(...validateScalar(field, value,
|
|
6760
|
+
errors.push(...validateScalar(field, value, path21));
|
|
6126
6761
|
}
|
|
6127
6762
|
return errors;
|
|
6128
6763
|
}
|
|
@@ -6170,6 +6805,38 @@ These are likely abandoned create-scaffolds (empty entries left behind when a cr
|
|
|
6170
6805
|
}
|
|
6171
6806
|
|
|
6172
6807
|
// dist/build/generate-ai-content.js
|
|
6808
|
+
var GENERATED_RECORD_FILENAME = ".canopy-generated.json";
|
|
6809
|
+
async function readGeneratedRecord(outputDir) {
|
|
6810
|
+
try {
|
|
6811
|
+
const raw = await fs17.readFile(path19.join(outputDir, GENERATED_RECORD_FILENAME), "utf-8");
|
|
6812
|
+
const parsed = JSON.parse(raw);
|
|
6813
|
+
if (typeof parsed !== "object" || parsed === null || !("files" in parsed))
|
|
6814
|
+
return [];
|
|
6815
|
+
const { files } = parsed;
|
|
6816
|
+
if (!Array.isArray(files))
|
|
6817
|
+
return [];
|
|
6818
|
+
return files.filter((entry) => typeof entry === "string");
|
|
6819
|
+
} catch {
|
|
6820
|
+
return [];
|
|
6821
|
+
}
|
|
6822
|
+
}
|
|
6823
|
+
async function writeGeneratedRecord(outputDir, files) {
|
|
6824
|
+
const record = { files: [...files].sort() };
|
|
6825
|
+
await fs17.mkdir(outputDir, { recursive: true });
|
|
6826
|
+
await fs17.writeFile(path19.join(outputDir, GENERATED_RECORD_FILENAME), `${JSON.stringify(record, null, 2)}
|
|
6827
|
+
`, "utf-8");
|
|
6828
|
+
}
|
|
6829
|
+
async function removeEmptyDirsUpward(startDir, stopAt) {
|
|
6830
|
+
let current = startDir;
|
|
6831
|
+
while (current.startsWith(stopAt) && current !== stopAt) {
|
|
6832
|
+
try {
|
|
6833
|
+
await fs17.rmdir(current);
|
|
6834
|
+
} catch {
|
|
6835
|
+
return;
|
|
6836
|
+
}
|
|
6837
|
+
current = path19.dirname(current);
|
|
6838
|
+
}
|
|
6839
|
+
}
|
|
6173
6840
|
async function generateAIContentFiles(options) {
|
|
6174
6841
|
const { config, entrySchemaRegistry, outputDir, aiConfig, _testFlatSchema } = options;
|
|
6175
6842
|
const contentRootName = config.contentRoot || "content";
|
|
@@ -6192,18 +6859,40 @@ async function generateAIContentFiles(options) {
|
|
|
6192
6859
|
config: aiConfig,
|
|
6193
6860
|
entryLinkUrl: config.entryLinkUrl
|
|
6194
6861
|
});
|
|
6195
|
-
const absoluteOutputDir =
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
const absolutePath = path18.resolve(path18.join(absoluteOutputDir, filePath));
|
|
6862
|
+
const absoluteOutputDir = path19.resolve(outputDir) + path19.sep;
|
|
6863
|
+
const resolveOutputPath = (filePath) => {
|
|
6864
|
+
const absolutePath = path19.resolve(path19.join(absoluteOutputDir, filePath));
|
|
6199
6865
|
if (!absolutePath.startsWith(absoluteOutputDir)) {
|
|
6200
6866
|
throw new Error(`Path traversal detected in AI content output: ${filePath}`);
|
|
6201
6867
|
}
|
|
6202
|
-
|
|
6203
|
-
|
|
6868
|
+
return absolutePath;
|
|
6869
|
+
};
|
|
6870
|
+
const previousFiles = await readGeneratedRecord(absoluteOutputDir);
|
|
6871
|
+
const currentFiles = [...result.files.keys()];
|
|
6872
|
+
await writeGeneratedRecord(absoluteOutputDir, [.../* @__PURE__ */ new Set([...previousFiles, ...currentFiles])]);
|
|
6873
|
+
let fileCount = 0;
|
|
6874
|
+
for (const [filePath, content] of result.files) {
|
|
6875
|
+
const absolutePath = resolveOutputPath(filePath);
|
|
6876
|
+
await fs17.mkdir(path19.dirname(absolutePath), { recursive: true });
|
|
6877
|
+
await fs17.writeFile(absolutePath, content, "utf-8");
|
|
6204
6878
|
fileCount++;
|
|
6205
6879
|
}
|
|
6206
|
-
|
|
6880
|
+
const currentSet = new Set(currentFiles);
|
|
6881
|
+
const stale = previousFiles.filter((filePath) => !currentSet.has(filePath));
|
|
6882
|
+
let removedCount = 0;
|
|
6883
|
+
for (const filePath of stale) {
|
|
6884
|
+
const absolutePath = resolveOutputPath(filePath);
|
|
6885
|
+
try {
|
|
6886
|
+
await fs17.unlink(absolutePath);
|
|
6887
|
+
removedCount++;
|
|
6888
|
+
} catch (err) {
|
|
6889
|
+
if (!isNodeError(err) || err.code !== "ENOENT")
|
|
6890
|
+
throw err;
|
|
6891
|
+
}
|
|
6892
|
+
await removeEmptyDirsUpward(path19.dirname(absolutePath), absoluteOutputDir);
|
|
6893
|
+
}
|
|
6894
|
+
await writeGeneratedRecord(absoluteOutputDir, currentFiles);
|
|
6895
|
+
return { fileCount, removedCount, outputDir: absoluteOutputDir };
|
|
6207
6896
|
}
|
|
6208
6897
|
|
|
6209
6898
|
// dist/cli/generate-ai-content.js
|
|
@@ -6211,7 +6900,7 @@ var jiti = createJiti(import.meta.url);
|
|
|
6211
6900
|
async function generateAIContentCLI(options) {
|
|
6212
6901
|
const { projectDir, outputDir = "public/ai", configPath, appDir = "app" } = options;
|
|
6213
6902
|
console.log("\nCanopyCMS generate-ai-content\n");
|
|
6214
|
-
const canopyConfigPath =
|
|
6903
|
+
const canopyConfigPath = path20.join(projectDir, "canopycms.config.ts");
|
|
6215
6904
|
let canopyConfigModule;
|
|
6216
6905
|
try {
|
|
6217
6906
|
canopyConfigModule = await jiti.import(canopyConfigPath);
|
|
@@ -6222,7 +6911,7 @@ async function generateAIContentCLI(options) {
|
|
|
6222
6911
|
}
|
|
6223
6912
|
const configExport = canopyConfigModule.default ?? canopyConfigModule.config ?? canopyConfigModule;
|
|
6224
6913
|
const serverConfig = typeof configExport === "object" && configExport !== null && "server" in configExport ? configExport.server : configExport;
|
|
6225
|
-
const schemasPath =
|
|
6914
|
+
const schemasPath = path20.join(projectDir, appDir, "schemas.ts");
|
|
6226
6915
|
let entrySchemaRegistry = {};
|
|
6227
6916
|
try {
|
|
6228
6917
|
const schemasModule = await jiti.import(schemasPath);
|
|
@@ -6233,7 +6922,7 @@ async function generateAIContentCLI(options) {
|
|
|
6233
6922
|
let aiConfig;
|
|
6234
6923
|
if (configPath) {
|
|
6235
6924
|
try {
|
|
6236
|
-
const aiConfigModule = await jiti.import(
|
|
6925
|
+
const aiConfigModule = await jiti.import(path20.resolve(configPath));
|
|
6237
6926
|
aiConfig = aiConfigModule.aiContentConfig ?? aiConfigModule.default ?? aiConfigModule.config;
|
|
6238
6927
|
} catch (err) {
|
|
6239
6928
|
console.error(`Could not load AI config from ${configPath}`);
|
|
@@ -6250,7 +6939,7 @@ async function generateAIContentCLI(options) {
|
|
|
6250
6939
|
console.error("Make sure canopycms.config.ts uses defineCanopyConfig().");
|
|
6251
6940
|
process.exit(1);
|
|
6252
6941
|
}
|
|
6253
|
-
const resolvedOutput =
|
|
6942
|
+
const resolvedOutput = path20.resolve(projectDir, outputDir);
|
|
6254
6943
|
console.log(` Output: ${resolvedOutput}`);
|
|
6255
6944
|
console.log(` Mode: ${serverConfig.mode ?? "dev"}`);
|
|
6256
6945
|
const result = await generateAIContentFiles({
|
|
@@ -6261,6 +6950,9 @@ async function generateAIContentCLI(options) {
|
|
|
6261
6950
|
});
|
|
6262
6951
|
console.log(`
|
|
6263
6952
|
Generated ${result.fileCount} files`);
|
|
6953
|
+
if (result.removedCount > 0) {
|
|
6954
|
+
console.log(` Removed ${result.removedCount} stale files from the previous run`);
|
|
6955
|
+
}
|
|
6264
6956
|
console.log(` Output: ${result.outputDir}
|
|
6265
6957
|
`);
|
|
6266
6958
|
}
|