canopycms 0.0.67-int.90 → 0.0.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -3
- package/dist/ai/generate.js +1 -27
- package/dist/ai/handler.d.ts +0 -4
- package/dist/ai/handler.js +0 -11
- package/dist/ai/index.d.ts +1 -8
- package/dist/ai/index.js +1 -8
- package/dist/ai/json-to-markdown.js +15 -58
- package/dist/ai/resolve-branch.d.ts +2 -7
- package/dist/ai/resolve-branch.js +2 -8
- package/dist/ai/strip-mdx.d.ts +5 -13
- package/dist/ai/strip-mdx.js +5 -17
- package/dist/ai/to-plain-text.d.ts +10 -28
- package/dist/ai/to-plain-text.js +10 -28
- package/dist/ai/transform-components.d.ts +2 -4
- package/dist/ai/transform-components.js +8 -19
- package/dist/ai/types.d.ts +5 -10
- package/dist/api/admin-branch-health.d.ts +4 -12
- package/dist/api/admin-branch-health.js +26 -66
- package/dist/api/admin.d.ts +4 -12
- package/dist/api/admin.js +10 -26
- package/dist/api/assets.d.ts +0 -25
- package/dist/api/assets.js +50 -91
- package/dist/api/branch-merge.d.ts +1 -1
- package/dist/api/branch-merge.js +0 -2
- package/dist/api/branch-review.d.ts +0 -8
- package/dist/api/branch-review.js +0 -13
- package/dist/api/branch-status.d.ts +0 -3
- package/dist/api/branch-status.js +8 -33
- package/dist/api/branch-withdraw.d.ts +0 -1
- package/dist/api/branch-withdraw.js +6 -9
- package/dist/api/branch.d.ts +7 -12
- package/dist/api/branch.js +7 -44
- package/dist/api/client.d.ts +71 -224
- package/dist/api/client.js +82 -233
- package/dist/api/comments.d.ts +2 -4
- package/dist/api/comments.js +0 -17
- package/dist/api/content.d.ts +0 -6
- package/dist/api/content.js +66 -142
- package/dist/api/entries-constants.d.ts +5 -9
- package/dist/api/entries-constants.js +5 -9
- package/dist/api/entries.d.ts +8 -13
- package/dist/api/entries.js +20 -51
- package/dist/api/github-sync.d.ts +2 -4
- package/dist/api/github-sync.js +4 -8
- package/dist/api/groups.d.ts +3 -0
- package/dist/api/groups.js +8 -21
- package/dist/api/guards.d.ts +4 -16
- package/dist/api/guards.js +3 -22
- package/dist/api/index.d.ts +4 -4
- package/dist/api/permissions.d.ts +2 -0
- package/dist/api/permissions.js +18 -40
- package/dist/api/reference-options.d.ts +0 -1
- package/dist/api/reference-options.js +3 -11
- package/dist/api/request-body-hash.d.ts +12 -23
- package/dist/api/request-body-hash.js +12 -23
- package/dist/api/resolve-references.d.ts +0 -1
- package/dist/api/resolve-references.js +13 -28
- package/dist/api/route-builder.d.ts +3 -7
- package/dist/api/route-builder.js +1 -1
- package/dist/api/routes.d.ts +12 -0
- package/dist/api/routes.js +47 -0
- package/dist/api/schema.d.ts +23 -33
- package/dist/api/schema.js +20 -74
- package/dist/api/settings-helpers.js +4 -5
- package/dist/api/types.d.ts +0 -5
- package/dist/api/user.js +0 -5
- package/dist/api/validators.d.ts +14 -52
- package/dist/api/validators.js +14 -52
- package/dist/assets/asset-prefixes.d.ts +4 -8
- package/dist/assets/asset-prefixes.js +4 -8
- package/dist/assets/asset-src.d.ts +5 -22
- package/dist/assets/asset-src.js +5 -21
- package/dist/assets/factory.d.ts +1 -2
- package/dist/assets/factory.js +1 -2
- package/dist/assets/finalize.d.ts +3 -1
- package/dist/assets/finalize.js +3 -1
- package/dist/assets/index.d.ts +3 -3
- package/dist/assets/index.js +1 -1
- package/dist/assets/keys.d.ts +5 -3
- package/dist/assets/keys.js +6 -3
- package/dist/assets/pipeline.d.ts +3 -2
- package/dist/assets/pipeline.js +22 -56
- package/dist/assets/sharp-loader.d.ts +2 -10
- package/dist/assets/sharp-loader.js +2 -10
- package/dist/assets/store-local.d.ts +1 -2
- package/dist/assets/store-local.js +1 -2
- package/dist/assets/store-s3.d.ts +3 -4
- package/dist/assets/svg-sanitizer.d.ts +6 -14
- package/dist/assets/svg-sanitizer.js +6 -14
- package/dist/assets/transform-directives.d.ts +3 -4
- package/dist/assets/transform-directives.js +1 -1
- package/dist/assets/transform.d.ts +19 -30
- package/dist/assets/transform.js +18 -31
- package/dist/auth/cache.d.ts +2 -3
- package/dist/auth/cache.js +2 -3
- package/dist/auth/caching-auth-plugin.d.ts +11 -22
- package/dist/auth/caching-auth-plugin.js +12 -14
- package/dist/auth/context-helpers.d.ts +6 -20
- package/dist/auth/context-helpers.js +5 -17
- package/dist/auth/file-based-auth-cache.d.ts +10 -21
- package/dist/auth/file-based-auth-cache.js +23 -51
- package/dist/auth/plugin.d.ts +29 -61
- package/dist/auth/plugin.js +9 -13
- package/dist/auth/types.d.ts +11 -20
- package/dist/authorization/branch.d.ts +30 -49
- package/dist/authorization/branch.js +16 -42
- package/dist/authorization/content.d.ts +11 -40
- package/dist/authorization/content.js +13 -45
- package/dist/authorization/groups/index.d.ts +0 -3
- package/dist/authorization/groups/index.js +0 -3
- package/dist/authorization/groups/loader.d.ts +14 -28
- package/dist/authorization/groups/loader.js +14 -38
- package/dist/authorization/groups/schema.d.ts +0 -6
- package/dist/authorization/groups/schema.js +4 -7
- package/dist/authorization/helpers.d.ts +9 -28
- package/dist/authorization/helpers.js +9 -28
- package/dist/authorization/index.d.ts +3 -25
- package/dist/authorization/index.js +3 -35
- package/dist/authorization/path.d.ts +6 -13
- package/dist/authorization/path.js +8 -25
- package/dist/authorization/permissions/index.d.ts +0 -3
- package/dist/authorization/permissions/index.js +0 -3
- package/dist/authorization/permissions/loader.d.ts +14 -27
- package/dist/authorization/permissions/loader.js +14 -32
- package/dist/authorization/permissions/schema.d.ts +1 -6
- package/dist/authorization/permissions/schema.js +6 -13
- package/dist/authorization/protected-branch.d.ts +36 -65
- package/dist/authorization/protected-branch.js +29 -41
- package/dist/authorization/settings-file-store.d.ts +38 -81
- package/dist/authorization/settings-file-store.js +38 -79
- package/dist/authorization/types.d.ts +4 -26
- package/dist/authorization/types.js +0 -6
- package/dist/authorization/validation.d.ts +1 -23
- package/dist/authorization/validation.js +1 -24
- package/dist/branch-health.d.ts +39 -67
- package/dist/branch-health.js +27 -43
- package/dist/branch-metadata-file.d.ts +16 -32
- package/dist/branch-metadata-file.js +13 -26
- package/dist/branch-metadata.d.ts +49 -80
- package/dist/branch-metadata.js +57 -87
- package/dist/branch-registry.d.ts +26 -64
- package/dist/branch-registry.js +44 -93
- package/dist/branch-schema-cache.d.ts +34 -124
- package/dist/branch-schema-cache.js +49 -155
- package/dist/branch-workspace.d.ts +4 -4
- package/dist/branch-workspace.js +15 -19
- package/dist/build/generate-ai-content.d.ts +1 -0
- package/dist/build/generate-ai-content.js +12 -25
- package/dist/build-canopy.d.ts +17 -27
- package/dist/build-canopy.js +16 -23
- package/dist/build-mode.d.ts +25 -27
- package/dist/build-mode.js +25 -29
- package/dist/cli/cli.d.ts +15 -26
- package/dist/cli/cli.js +601 -978
- package/dist/cli/generate-ai-content.js +351 -628
- package/dist/cli/github-app-manifest.d.ts +84 -0
- package/dist/cli/github-app-manifest.js +161 -0
- package/dist/cli/init-github-app.d.ts +110 -444
- package/dist/cli/init-github-app.js +236 -773
- package/dist/cli/init.js +39 -80
- package/dist/cli/migrate.d.ts +13 -10
- package/dist/cli/migrate.js +22 -30
- package/dist/cli/project-detect.d.ts +17 -31
- package/dist/cli/project-detect.js +31 -42
- package/dist/cli/project-root.d.ts +4 -11
- package/dist/cli/project-root.js +4 -11
- package/dist/cli/prompt.d.ts +10 -0
- package/dist/cli/prompt.js +67 -0
- package/dist/cli/sync.d.ts +0 -8
- package/dist/cli/sync.js +8 -28
- package/dist/cli/template-files/middleware.ts.template +15 -29
- package/dist/cli/templates.d.ts +2 -12
- package/dist/cli/templates.js +4 -4
- package/dist/client.d.ts +1 -1
- package/dist/comment-store.d.ts +32 -53
- package/dist/comment-store.js +32 -52
- package/dist/config/flatten.d.ts +0 -3
- package/dist/config/flatten.js +2 -12
- package/dist/config/helpers.js +2 -2
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +1 -4
- package/dist/config/schemas/collection.d.ts +0 -8
- package/dist/config/schemas/collection.js +0 -10
- package/dist/config/schemas/config.d.ts +6 -78
- package/dist/config/schemas/config.js +38 -54
- package/dist/config/schemas/field.d.ts +2 -242
- package/dist/config/schemas/field.js +9 -20
- package/dist/config/schemas/media.js +1 -1
- package/dist/config/schemas/permissions.js +0 -2
- package/dist/config/schemas/url.d.ts +5 -6
- package/dist/config/schemas/url.js +5 -6
- package/dist/config/types.d.ts +45 -33
- package/dist/config/types.js +0 -1
- package/dist/config.d.ts +1 -8
- package/dist/config.js +1 -9
- package/dist/content-id-index.d.ts +76 -188
- package/dist/content-id-index.js +79 -230
- package/dist/content-index-generation.d.ts +27 -48
- package/dist/content-index-generation.js +35 -84
- package/dist/content-index-registry.d.ts +15 -24
- package/dist/content-index-registry.js +5 -9
- package/dist/content-listing.d.ts +64 -107
- package/dist/content-listing.js +68 -118
- package/dist/content-reader.d.ts +20 -29
- package/dist/content-reader.js +10 -18
- package/dist/content-store.d.ts +210 -336
- package/dist/content-store.js +413 -657
- package/dist/content-tree.d.ts +44 -86
- package/dist/content-tree.js +26 -54
- package/dist/context.d.ts +67 -94
- package/dist/context.js +53 -72
- package/dist/dev-content-watcher.d.ts +18 -18
- package/dist/dev-content-watcher.js +41 -44
- package/dist/editor/BranchManager.d.ts +3 -6
- package/dist/editor/BranchManager.js +24 -44
- package/dist/editor/CanopyEditor.js +5 -2
- package/dist/editor/CommentsPanel.js +0 -2
- package/dist/editor/Editor.js +19 -76
- package/dist/editor/EditorAuthGate.d.ts +27 -0
- package/dist/editor/EditorAuthGate.js +140 -0
- package/dist/editor/EntryNavigator.js +0 -20
- package/dist/editor/FormRenderer.d.ts +1 -2
- package/dist/editor/FormRenderer.js +8 -23
- package/dist/editor/admin/SystemHealthPanel.js +20 -37
- package/dist/editor/admin/useSystemHealth.d.ts +2 -3
- package/dist/editor/admin/useSystemHealth.js +10 -12
- package/dist/editor/client-reference-resolver.d.ts +2 -5
- package/dist/editor/client-reference-resolver.js +2 -19
- package/dist/editor/comments/BranchComments.js +0 -4
- package/dist/editor/comments/EntryComments.js +0 -4
- package/dist/editor/comments/FieldWrapper.js +0 -4
- package/dist/editor/comments/InlineCommentThread.js +0 -2
- package/dist/editor/comments/ThreadCarousel.d.ts +0 -58
- package/dist/editor/comments/ThreadCarousel.js +0 -12
- package/dist/editor/components/EditorFooter.d.ts +0 -8
- package/dist/editor/components/EditorFooter.js +0 -5
- package/dist/editor/components/EditorHeader.d.ts +0 -31
- package/dist/editor/components/EditorHeader.js +21 -71
- package/dist/editor/components/EditorSidebar.d.ts +0 -19
- package/dist/editor/components/EditorSidebar.js +0 -16
- package/dist/editor/components/EntryCreateModal.d.ts +4 -4
- package/dist/editor/components/EntryCreateModal.js +7 -22
- package/dist/editor/components/RenameEntryModal.js +0 -4
- package/dist/editor/components/UserBadge.js +0 -10
- package/dist/editor/components/index.js +0 -2
- package/dist/editor/context/ApiClientContext.d.ts +8 -7
- package/dist/editor/context/ApiClientContext.js +39 -11
- package/dist/editor/context/AssetContext.d.ts +7 -17
- package/dist/editor/context/AssetContext.js +7 -17
- package/dist/editor/context/EditorIdentityContext.d.ts +13 -0
- package/dist/editor/context/EditorIdentityContext.js +7 -0
- package/dist/editor/context/EditorStateContext.d.ts +10 -41
- package/dist/editor/context/EditorStateContext.js +7 -23
- package/dist/editor/context/SWRProvider.d.ts +3 -6
- package/dist/editor/context/index.d.ts +1 -20
- package/dist/editor/context/index.js +1 -20
- package/dist/editor/editor-utils.d.ts +8 -47
- package/dist/editor/editor-utils.js +19 -72
- package/dist/editor/fields/BlockField.d.ts +2 -2
- package/dist/editor/fields/BlockField.js +1 -2
- package/dist/editor/fields/CodeField.d.ts +0 -1
- package/dist/editor/fields/CodeField.js +0 -1
- package/dist/editor/fields/DateTimeField.d.ts +15 -22
- package/dist/editor/fields/DateTimeField.js +17 -26
- package/dist/editor/fields/ImageField.d.ts +2 -2
- package/dist/editor/fields/ImageField.js +8 -14
- package/dist/editor/fields/InlineGroupField.d.ts +0 -1
- package/dist/editor/fields/InlineGroupField.js +0 -1
- package/dist/editor/fields/MarkdownField.d.ts +0 -1
- package/dist/editor/fields/MarkdownField.js +6 -11
- package/dist/editor/fields/MdxImageDialog.d.ts +1 -1
- package/dist/editor/fields/MdxImageDialog.js +0 -1
- package/dist/editor/fields/NumberField.d.ts +9 -19
- package/dist/editor/fields/NumberField.js +11 -22
- package/dist/editor/fields/NumberListField.d.ts +7 -10
- package/dist/editor/fields/NumberListField.js +7 -10
- package/dist/editor/fields/ObjectField.d.ts +5 -8
- package/dist/editor/fields/ObjectField.js +0 -1
- package/dist/editor/fields/ReferenceField.d.ts +1 -1
- package/dist/editor/fields/ReferenceField.js +11 -16
- package/dist/editor/fields/SelectField.d.ts +2 -2
- package/dist/editor/fields/SelectField.js +0 -1
- package/dist/editor/fields/StringListField.d.ts +0 -4
- package/dist/editor/fields/StringListField.js +4 -9
- package/dist/editor/fields/TextField.d.ts +0 -1
- package/dist/editor/fields/TextField.js +0 -1
- package/dist/editor/fields/ToggleField.d.ts +0 -1
- package/dist/editor/fields/ToggleField.js +0 -1
- package/dist/editor/fields/entry-link/EntryLinkContext.js +2 -4
- package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -3
- package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -4
- package/dist/editor/group-manager/ExternalGroupsTab.d.ts +0 -3
- package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
- package/dist/editor/group-manager/GroupCard.d.ts +0 -3
- package/dist/editor/group-manager/GroupForm.d.ts +0 -3
- package/dist/editor/group-manager/InternalGroupsTab.d.ts +0 -3
- package/dist/editor/group-manager/InternalGroupsTab.js +0 -2
- package/dist/editor/group-manager/MemberList.d.ts +0 -3
- package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +0 -3
- package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -3
- package/dist/editor/group-manager/hooks/useGroupState.d.ts +0 -3
- package/dist/editor/group-manager/hooks/useGroupState.js +0 -5
- package/dist/editor/group-manager/hooks/useUserSearch.d.ts +0 -3
- package/dist/editor/group-manager/hooks/useUserSearch.js +0 -3
- package/dist/editor/group-manager/index.d.ts +0 -6
- package/dist/editor/group-manager/index.js +0 -10
- package/dist/editor/group-manager/types.d.ts +2 -5
- package/dist/editor/group-manager/types.js +0 -3
- package/dist/editor/hooks/index.d.ts +6 -11
- package/dist/editor/hooks/index.js +6 -12
- package/dist/editor/hooks/useBranchActions.d.ts +0 -14
- package/dist/editor/hooks/useBranchActions.js +0 -22
- package/dist/editor/hooks/useBranchManager.d.ts +2 -48
- package/dist/editor/hooks/useBranchManager.js +4 -64
- package/dist/editor/hooks/useBranchesData.d.ts +7 -6
- package/dist/editor/hooks/useBranchesData.js +7 -6
- package/dist/editor/hooks/useCommentSystem.d.ts +0 -49
- package/dist/editor/hooks/useCommentSystem.js +1 -36
- package/dist/editor/hooks/useDraftManager.d.ts +2 -35
- package/dist/editor/hooks/useDraftManager.js +23 -64
- package/dist/editor/hooks/useEditorLayout.d.ts +0 -20
- package/dist/editor/hooks/useEditorLayout.js +0 -23
- package/dist/editor/hooks/useEntriesData.d.ts +1 -0
- package/dist/editor/hooks/useEntriesData.js +2 -3
- package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -2
- package/dist/editor/hooks/useEntryLinkResolution.js +2 -2
- package/dist/editor/hooks/useEntryManager.d.ts +2 -27
- package/dist/editor/hooks/useEntryManager.js +29 -78
- package/dist/editor/hooks/useGroupManager.d.ts +0 -21
- package/dist/editor/hooks/useGroupManager.js +0 -21
- package/dist/editor/hooks/usePermissionManager.d.ts +0 -20
- package/dist/editor/hooks/usePermissionManager.js +0 -20
- package/dist/editor/hooks/useReferenceResolution.d.ts +2 -33
- package/dist/editor/hooks/useReferenceResolution.js +1 -48
- package/dist/editor/hooks/useSchemaManager.d.ts +8 -27
- package/dist/editor/hooks/useSchemaManager.js +0 -19
- package/dist/editor/hooks/useUserContext.d.ts +2 -9
- package/dist/editor/hooks/useUserContext.js +10 -10
- package/dist/editor/hooks/useUserMetadata.d.ts +0 -2
- package/dist/editor/hooks/useUserMetadata.js +0 -3
- package/dist/editor/media/AssetCard.d.ts +0 -1
- package/dist/editor/media/AssetCard.js +1 -2
- package/dist/editor/media/CropStep.d.ts +0 -1
- package/dist/editor/media/CropStep.js +0 -1
- package/dist/editor/media/MediaLibrary.d.ts +1 -2
- package/dist/editor/media/MediaLibrary.js +1 -2
- package/dist/editor/media/MediaLibraryBody.d.ts +0 -1
- package/dist/editor/media/MediaLibraryBody.js +0 -1
- package/dist/editor/media/crop-math.d.ts +1 -1
- package/dist/editor/media/crop-math.js +1 -1
- package/dist/editor/permission-manager/GroupSelector.d.ts +0 -3
- package/dist/editor/permission-manager/PermissionEditor.d.ts +0 -4
- package/dist/editor/permission-manager/PermissionEditor.js +0 -1
- package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +0 -7
- package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -4
- package/dist/editor/permission-manager/PermissionTree.d.ts +0 -4
- package/dist/editor/permission-manager/UserSelector.d.ts +0 -3
- package/dist/editor/permission-manager/constants.d.ts +0 -3
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +0 -3
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -10
- package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +0 -14
- package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -11
- package/dist/editor/permission-manager/index.d.ts +0 -7
- package/dist/editor/permission-manager/index.js +0 -16
- package/dist/editor/permission-manager/types.d.ts +2 -12
- package/dist/editor/permission-manager/types.js +0 -3
- package/dist/editor/permission-manager/utils.d.ts +0 -28
- package/dist/editor/permission-manager/utils.js +9 -23
- package/dist/editor/preview-bridge.d.ts +5 -1
- package/dist/editor/preview-bridge.js +4 -4
- package/dist/editor/schema-editor/CollectionEditor.d.ts +0 -5
- package/dist/editor/schema-editor/CollectionEditor.js +9 -31
- package/dist/editor/schema-editor/EntryTypeEditor.d.ts +0 -6
- package/dist/editor/schema-editor/EntryTypeEditor.js +3 -24
- package/dist/editor/schema-editor/index.d.ts +0 -7
- package/dist/editor/schema-editor/index.js +0 -7
- package/dist/editor/utils/env.d.ts +3 -18
- package/dist/editor/utils/env.js +6 -25
- package/dist/entry-link-resolver.d.ts +13 -35
- package/dist/entry-link-resolver.js +20 -53
- package/dist/entry-schema-registry.d.ts +20 -60
- package/dist/entry-schema-registry.js +20 -69
- package/dist/entry-schema.d.ts +118 -223
- package/dist/entry-schema.js +46 -91
- package/dist/git-manager.d.ts +101 -200
- package/dist/git-manager.js +211 -384
- package/dist/github-service.d.ts +34 -75
- package/dist/github-service.js +37 -91
- package/dist/http/handler.d.ts +6 -37
- package/dist/http/handler.js +65 -120
- package/dist/http/router.d.ts +15 -32
- package/dist/http/router.js +34 -99
- package/dist/http/types.d.ts +15 -40
- package/dist/http/types.js +3 -7
- package/dist/id.d.ts +4 -10
- package/dist/id.js +4 -10
- package/dist/index.js +6 -9
- package/dist/operating-mode/client-safe-strategy.d.ts +7 -14
- package/dist/operating-mode/client-safe-strategy.js +8 -28
- package/dist/operating-mode/client-unsafe-strategy.d.ts +6 -16
- package/dist/operating-mode/client-unsafe-strategy.js +9 -34
- package/dist/operating-mode/client.d.ts +3 -10
- package/dist/operating-mode/client.js +3 -8
- package/dist/operating-mode/deployment-name-fixtures.d.ts +14 -18
- package/dist/operating-mode/deployment-name-fixtures.js +14 -18
- package/dist/operating-mode/deployment-name.d.ts +19 -29
- package/dist/operating-mode/deployment-name.js +29 -47
- package/dist/operating-mode/index.d.ts +4 -25
- package/dist/operating-mode/index.js +9 -31
- package/dist/operating-mode/mode-env.d.ts +32 -54
- package/dist/operating-mode/mode-env.js +36 -58
- package/dist/operating-mode/types.d.ts +24 -72
- package/dist/operating-mode/types.js +1 -7
- package/dist/paths/branch-name.d.ts +30 -40
- package/dist/paths/branch-name.js +30 -40
- package/dist/paths/branch.d.ts +4 -18
- package/dist/paths/branch.js +6 -22
- package/dist/paths/index.d.ts +2 -13
- package/dist/paths/index.js +3 -23
- package/dist/paths/normalize-server.d.ts +3 -15
- package/dist/paths/normalize-server.js +3 -15
- package/dist/paths/normalize.d.ts +13 -33
- package/dist/paths/normalize.js +13 -33
- package/dist/paths/resolve.d.ts +4 -17
- package/dist/paths/resolve.js +4 -22
- package/dist/paths/types.d.ts +12 -34
- package/dist/paths/types.js +5 -7
- package/dist/paths/validation.d.ts +22 -119
- package/dist/paths/validation.js +30 -148
- package/dist/reference-resolver.d.ts +0 -13
- package/dist/reference-resolver.js +0 -18
- package/dist/resolve-canopy-user.d.ts +16 -18
- package/dist/resolve-canopy-user.js +17 -26
- package/dist/resource-generation.d.ts +16 -25
- package/dist/resource-generation.js +34 -97
- package/dist/schema/meta-loader.d.ts +2 -14
- package/dist/schema/meta-loader.js +15 -64
- package/dist/schema/resolver.d.ts +1 -4
- package/dist/schema/resolver.js +1 -6
- package/dist/schema/schema-store-types.d.ts +0 -12
- package/dist/schema/schema-store.d.ts +61 -138
- package/dist/schema/schema-store.js +85 -282
- package/dist/schema/types.d.ts +0 -4
- package/dist/server.d.ts +62 -93
- package/dist/server.js +63 -94
- package/dist/services.d.ts +23 -47
- package/dist/services.js +55 -98
- package/dist/settings-workspace.d.ts +17 -30
- package/dist/settings-workspace.js +51 -81
- package/dist/static/index.d.ts +26 -52
- package/dist/static/index.js +33 -67
- package/dist/{worker/task-queue.d.ts → task-queue/cms-task-queue.d.ts} +7 -9
- package/dist/task-queue/cms-task-queue.js +13 -0
- package/dist/task-queue/task-queue-config.d.ts +3 -0
- package/dist/{worker → task-queue}/task-queue-config.js +1 -6
- package/dist/task-queue/task-queue.d.ts +16 -22
- package/dist/task-queue/task-queue.js +20 -44
- package/dist/task-queue/types.d.ts +0 -1
- package/dist/task-queue/worker-status.d.ts +35 -0
- package/dist/task-queue/worker-status.js +40 -0
- package/dist/types.d.ts +40 -51
- package/dist/url-collision.d.ts +26 -40
- package/dist/url-collision.js +47 -63
- package/dist/url-exclusivity-fixtures.d.ts +15 -20
- package/dist/url-exclusivity-fixtures.js +14 -19
- package/dist/url-path-resolver.d.ts +8 -16
- package/dist/url-path-resolver.js +19 -29
- package/dist/user.d.ts +8 -21
- package/dist/user.js +10 -19
- package/dist/utils/async-mutex.js +4 -6
- package/dist/utils/atomic-write.d.ts +2 -11
- package/dist/utils/atomic-write.js +2 -11
- package/dist/utils/body-field.d.ts +8 -15
- package/dist/utils/body-field.js +8 -15
- package/dist/utils/content-serialize.d.ts +18 -24
- package/dist/utils/content-serialize.js +109 -160
- package/dist/utils/content-write-lock.d.ts +51 -106
- package/dist/utils/content-write-lock.js +66 -111
- package/dist/utils/debug.d.ts +7 -23
- package/dist/utils/debug.js +7 -23
- package/dist/utils/entry-url.d.ts +11 -23
- package/dist/utils/entry-url.js +11 -26
- package/dist/utils/error.d.ts +25 -90
- package/dist/utils/error.js +60 -147
- package/dist/utils/flatten-group-fields.d.ts +5 -7
- package/dist/utils/flatten-group-fields.js +5 -7
- package/dist/utils/format.d.ts +1 -5
- package/dist/utils/format.js +1 -5
- package/dist/utils/fs.d.ts +3 -4
- package/dist/utils/fs.js +3 -4
- package/dist/utils/git.d.ts +52 -83
- package/dist/utils/git.js +79 -125
- package/dist/utils/logger.d.ts +35 -50
- package/dist/utils/logger.js +35 -52
- package/dist/utils/provisioning-lock.d.ts +23 -35
- package/dist/utils/provisioning-lock.js +49 -69
- package/dist/utils/sanitize-href.d.ts +5 -10
- package/dist/utils/sanitize-href.js +8 -18
- package/dist/utils/title-field.d.ts +17 -20
- package/dist/utils/title-field.js +18 -27
- package/dist/utils/typed-filename.d.ts +20 -48
- package/dist/utils/typed-filename.js +25 -61
- package/dist/utils/url-prefix.d.ts +3 -3
- package/dist/utils/url-prefix.js +3 -3
- package/dist/validation/block-structural-keys.d.ts +16 -31
- package/dist/validation/block-structural-keys.js +19 -29
- package/dist/validation/deletion-checker.d.ts +0 -34
- package/dist/validation/deletion-checker.js +1 -43
- package/dist/validation/entry-link-validator.d.ts +6 -13
- package/dist/validation/entry-link-validator.js +4 -13
- package/dist/validation/entry-type-reference-validator.d.ts +13 -18
- package/dist/validation/entry-type-reference-validator.js +13 -18
- package/dist/validation/entry-validator.d.ts +13 -25
- package/dist/validation/entry-validator.js +15 -32
- package/dist/validation/field-traversal.d.ts +3 -30
- package/dist/validation/field-traversal.js +1 -32
- package/dist/validation/reference-validator.d.ts +5 -24
- package/dist/validation/reference-validator.js +5 -30
- package/dist/worker/cms-worker.d.ts +172 -232
- package/dist/worker/cms-worker.js +241 -333
- package/dist/worker/git-sync.d.ts +32 -133
- package/dist/worker/git-sync.js +129 -191
- package/dist/worker/github-auth.d.ts +13 -19
- package/dist/worker/github-auth.js +13 -19
- package/dist/worker/history-rewrite.d.ts +53 -66
- package/dist/worker/history-rewrite.js +54 -60
- package/dist/worker/log.d.ts +24 -41
- package/dist/worker/log.js +25 -42
- package/dist/worker/rebase.d.ts +20 -110
- package/dist/worker/rebase.js +279 -354
- package/dist/worker/task-runner.d.ts +32 -66
- package/dist/worker/task-runner.js +126 -162
- package/dist/worker/worker-context.d.ts +41 -81
- package/package.json +4 -4
- package/dist/cli/template-files/middleware-clerk.ts.template +0 -37
- package/dist/worker/task-queue-config.d.ts +0 -8
- package/dist/worker/task-queue.js +0 -19
- package/dist/worker/worker-status.d.ts +0 -47
- package/dist/worker/worker-status.js +0 -52
package/dist/content-store.d.ts
CHANGED
|
@@ -52,38 +52,29 @@ export declare class ContentConflictError extends Error {
|
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* [SYNC-C1] Thrown when a mutation could not take the branch's cross-host
|
|
55
|
-
* content-write lock within its bounded wait --
|
|
56
|
-
* mid-rebase on this branch's working tree
|
|
55
|
+
* content-write lock within its bounded wait (utils/content-write-lock.ts) --
|
|
56
|
+
* usually the worker mid-rebase on this branch's working tree, though
|
|
57
|
+
* writer-vs-writer contention raises it too (see ContentWriteLockBusyError).
|
|
57
58
|
*
|
|
58
|
-
* A `ContentConflictError` subclass so every
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* another editor", which would be actively misleading. The default wording
|
|
62
|
-
* covers writer-vs-writer contention too, which this lock also produces --
|
|
63
|
-
* see ContentWriteLockBusyError.
|
|
59
|
+
* A `ContentConflictError` subclass so every 409 mapping keeps working; the
|
|
60
|
+
* distinct type lets the API surface THIS message ("the branch is busy, retry")
|
|
61
|
+
* instead of the misleading generic "modified by another editor".
|
|
64
62
|
*/
|
|
65
63
|
export declare class BranchSyncingError extends ContentConflictError {
|
|
66
64
|
constructor(message: string);
|
|
67
65
|
}
|
|
68
66
|
/**
|
|
69
|
-
* [F1] Thrown when a save's content ID is carried by MORE THAN ONE file in
|
|
70
|
-
*
|
|
71
|
-
* quarantines (see its "Duplicate-ID quarantine" section).
|
|
67
|
+
* [F1] Thrown when a save's content ID is carried by MORE THAN ONE file in the branch's
|
|
68
|
+
* content tree -- the duplicate-ID state `ContentIdIndex` quarantines.
|
|
72
69
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* that is invisible to every ID-based lookup (reads-by-id, references,
|
|
79
|
-
* listings all resolve to the OTHER copy) and that the repair action later
|
|
80
|
-
* archives away, so the editor's work would appear to evaporate with no error
|
|
81
|
-
* anywhere. Refusing mutates nothing under any interleaving, and says what is
|
|
82
|
-
* wrong and who can fix it.
|
|
70
|
+
* Refuse rather than write: with two files sharing one ID "this entry" is ambiguous and every
|
|
71
|
+
* way of proceeding is worse. Following the index would DELETE (via the slug-change cleanup) a
|
|
72
|
+
* file the caller never addressed; writing only the addressed file would succeed into a file
|
|
73
|
+
* invisible to every ID-based lookup and later archived by the repair action, so the editor's
|
|
74
|
+
* work evaporates with no error anywhere. Refusing mutates nothing under any interleaving.
|
|
83
75
|
*
|
|
84
|
-
* A `ContentConflictError` subclass so every
|
|
85
|
-
*
|
|
86
|
-
* rather than the generic "modified by another editor", which would send the
|
|
76
|
+
* A `ContentConflictError` subclass so every 409 mapping keeps working; the distinct type lets
|
|
77
|
+
* the API surface THIS message rather than "modified by another editor", which would send the
|
|
87
78
|
* editor into a reload-and-retry loop that cannot succeed.
|
|
88
79
|
*/
|
|
89
80
|
export declare class DuplicateContentIdError extends ContentConflictError {
|
|
@@ -94,27 +85,22 @@ export declare class DuplicateContentIdError extends ContentConflictError {
|
|
|
94
85
|
}
|
|
95
86
|
/**
|
|
96
87
|
* Thrown when a create or rename would give a SECOND entry a `urlPath` another entry already
|
|
97
|
-
* holds -- the write-boundary half of the invariant `assertNoDuplicateUrlPaths` enforces at
|
|
98
|
-
* time (
|
|
88
|
+
* holds -- the write-boundary half of the invariant `assertNoDuplicateUrlPaths` enforces at
|
|
89
|
+
* build time (url-collision.ts says which shapes count and which deliberately do not).
|
|
99
90
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* rather than by the author, not necessarily the page they were editing.
|
|
91
|
+
* Refuse rather than write: only one of the two can be served at that URL, so the other
|
|
92
|
+
* silently has no route anywhere -- and the loser is picked by resolver precedence rather than
|
|
93
|
+
* by the author, so not necessarily the page they were editing.
|
|
104
94
|
*
|
|
105
|
-
* A `ContentConflictError` subclass so every
|
|
106
|
-
*
|
|
107
|
-
* editor", which would send the editor into a reload-and-retry loop that cannot succeed.
|
|
95
|
+
* A `ContentConflictError` subclass so every 409 mapping keeps working; the distinct type lets
|
|
96
|
+
* the API surface THIS message rather than the generic "modified by another editor".
|
|
108
97
|
*/
|
|
109
98
|
export declare class UrlPathConflictError extends ContentConflictError {
|
|
110
99
|
/** Absolute path of the entry already holding the contested URL. */
|
|
111
100
|
readonly conflictingPath: string;
|
|
112
101
|
constructor(message: string, conflictingPath: string);
|
|
113
102
|
}
|
|
114
|
-
/**
|
|
115
|
-
* Get the default entry type from a collection's entries array.
|
|
116
|
-
* Returns the entry marked as default, or the first one, or undefined if no entries.
|
|
117
|
-
*/
|
|
103
|
+
/** The entry type marked `default`, else the first, else undefined. */
|
|
118
104
|
export declare function getDefaultEntryType(entries: readonly EntryTypeConfig[] | undefined): EntryTypeConfig | undefined;
|
|
119
105
|
export interface ContentStoreOptions {
|
|
120
106
|
/**
|
|
@@ -126,21 +112,17 @@ export interface ContentStoreOptions {
|
|
|
126
112
|
indexFreshnessIntervalMs?: number;
|
|
127
113
|
/**
|
|
128
114
|
* Directory name (relative to `root`) holding the content tree — i.e.
|
|
129
|
-
* `config.contentRoot`. The ID index scans from here, so
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* path-based reads keep working.
|
|
134
|
-
*
|
|
115
|
+
* `config.contentRoot`. The ID index scans from here, so a non-default content
|
|
116
|
+
* root would otherwise build the index from a directory that does not exist:
|
|
117
|
+
* empty, so every ID-based lookup (reference resolution, entry links, order
|
|
118
|
+
* cleanup, rename) silently misses while path-based reads keep working.
|
|
135
119
|
* Defaults to 'content'.
|
|
136
120
|
*
|
|
137
|
-
* ENFORCED BY LINT, not by the type
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* the
|
|
141
|
-
*
|
|
142
|
-
* required in the TYPE would have forced the argument on 61 test call sites
|
|
143
|
-
* that legitimately want the default, for no gain in the 11 production ones.
|
|
121
|
+
* ENFORCED BY LINT, not by the type -- the `no-restricted-syntax` rule on
|
|
122
|
+
* `new ContentStore(...)` in eslint.config.mjs, scoped to non-test sources.
|
|
123
|
+
* Omitting it is silent and data-shaped when wrong, so every production
|
|
124
|
+
* construction must pass it; making the field required in the TYPE would force
|
|
125
|
+
* the argument on dozens of test call sites that legitimately want the default.
|
|
144
126
|
*/
|
|
145
127
|
contentRootName?: string;
|
|
146
128
|
/**
|
|
@@ -152,41 +134,25 @@ export interface ContentStoreOptions {
|
|
|
152
134
|
contentWriteLockWaitMs?: number;
|
|
153
135
|
}
|
|
154
136
|
/**
|
|
155
|
-
* Per-batch memo for reference resolution, keyed by content ID.
|
|
156
|
-
*
|
|
157
|
-
* Exists because a batch surface resolves the SAME reference over and over: a shared
|
|
158
|
-
* block ("call to action", "promo card") referenced by 40 pages costs 40 separate
|
|
159
|
-
* `read()`s of one small file in a single `listEntries()` pass, and a search-index build
|
|
160
|
-
* over thousands of entries multiplies that. Pass one cache through a whole batch and
|
|
161
|
-
* each distinct target is read once.
|
|
137
|
+
* Per-batch memo for reference resolution, keyed by content ID (plus `includeBody`).
|
|
162
138
|
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
139
|
+
* A batch surface resolves the SAME reference over and over: a shared block referenced by 40
|
|
140
|
+
* pages costs 40 separate `read()`s of one small file in a single `listEntries()` pass. One
|
|
141
|
+
* cache per batch and each distinct target is read once.
|
|
166
142
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* why it does not undo the saving.
|
|
143
|
+
* Values are the in-flight **promise**, so concurrent lookups inside a `Promise.all` collapse
|
|
144
|
+
* onto one read. What is cached is the READ, not the object handed out: every occurrence gets
|
|
145
|
+
* its own deep copy (see `resolveSingleReference`).
|
|
171
146
|
*
|
|
172
|
-
*
|
|
147
|
+
* Lifetime is one `listEntries()` / `buildContentTree()` call, with no invalidation -- strictly
|
|
148
|
+
* shorter than the `ContentStore` whose memoized `idIndex()` it sits on, so it adds no
|
|
149
|
+
* staleness window and is out of scope for the generation-marker protocol in
|
|
150
|
+
* `docs/concurrency.md`. Never make one module-global, persist one, or reuse one across
|
|
151
|
+
* requests.
|
|
173
152
|
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* sits on top of, so it introduces no staleness window that store did not already have,
|
|
178
|
-
* and it is out of scope for the generation-marker protocol in
|
|
179
|
-
* `docs/concurrency.md` (which governs caches rebuilt by scanning that OUTLIVE the
|
|
180
|
-
* mutations they can miss). Never make one module-global, never persist one, and never
|
|
181
|
-
* reuse one across requests.
|
|
182
|
-
*
|
|
183
|
-
* Misses are memoized alongside hits, deliberately: one batch should be internally
|
|
184
|
-
* coherent, and a shared block resolving to data on page 1 and to `null` on page 40 of
|
|
185
|
-
* the same sitemap is worse than either consistent answer. The accepted cost is that a
|
|
186
|
-
* later occurrence in the same batch can no longer get incidentally lucky after a
|
|
187
|
-
* sibling lookup wins the stale-index refresh throttle. Each DISTINCT id still gets its
|
|
188
|
-
* full self-healing retry, because that retry happens inside the memoized promise (see
|
|
189
|
-
* `resolveSingleReference`).
|
|
153
|
+
* Misses are memoized alongside hits so one batch stays internally coherent: a shared block
|
|
154
|
+
* resolving to data on page 1 and `null` on page 40 of one sitemap is worse than either
|
|
155
|
+
* consistent answer. Each DISTINCT id still gets its full self-healing retry, inside the memo.
|
|
190
156
|
*/
|
|
191
157
|
export type ReferenceResolveCache = Map<string, Promise<Record<string, unknown> | null>>;
|
|
192
158
|
/** Create a cache for one batch of reference resolution. See {@link ReferenceResolveCache}. */
|
|
@@ -218,20 +184,16 @@ export declare class ContentStore {
|
|
|
218
184
|
constructor(root: string, flatSchema: FlatSchemaItem[], options?: ContentStoreOptions);
|
|
219
185
|
/**
|
|
220
186
|
* [SYNC-C1] Run a working-tree mutation under the branch's cross-host
|
|
221
|
-
* content-write lock (utils/content-write-lock.ts), on top of the
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
* nothing about the EC2 worker rebasing this same tree on shared EFS, which
|
|
226
|
-
* destroys an in-flight save (`checkout --theirs` overwrites it and the
|
|
227
|
-
* rebase then reports success; `rebase --abort` hard-resets it). This is the
|
|
228
|
-
* layer that actually excludes the two.
|
|
187
|
+
* content-write lock (utils/content-write-lock.ts), on top of the in-process
|
|
188
|
+
* locks the callee takes for itself. The in-process mutex serializes writers
|
|
189
|
+
* inside ONE process and says nothing about the EC2 worker rebasing this same
|
|
190
|
+
* tree on shared EFS, which destroys an in-flight save.
|
|
229
191
|
*
|
|
230
|
-
* Reads deliberately do NOT take it
|
|
231
|
-
*
|
|
192
|
+
* Reads deliberately do NOT take it: an extra EFS round-trip per read is not an
|
|
193
|
+
* acceptable cost, and a rebase cannot destroy a read.
|
|
232
194
|
*
|
|
233
|
-
* Acquisition order is always content lock -> `withLock`, never the reverse,
|
|
234
|
-
*
|
|
195
|
+
* Acquisition order is always content lock -> `withLock`, never the reverse, so
|
|
196
|
+
* the two cannot deadlock.
|
|
235
197
|
*/
|
|
236
198
|
private withContentWriteExclusion;
|
|
237
199
|
/**
|
|
@@ -239,18 +201,16 @@ export declare class ContentStore {
|
|
|
239
201
|
*
|
|
240
202
|
* Called (via content-index-registry) after in-process operations that change the
|
|
241
203
|
* working tree underneath this store — git checkout/merge/rebase, the worker's
|
|
242
|
-
* rebase loop,
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
* Cheap: only bumps a generation counter; the rebuild happens lazily.
|
|
204
|
+
* rebase loop, sync-core's content replacement. Without it, ID→path lookups keep
|
|
205
|
+
* resolving to pre-mutation paths and saves can target moved/deleted files.
|
|
206
|
+
* Cheap: bumps a generation counter, the rebuild is lazy.
|
|
246
207
|
*/
|
|
247
208
|
invalidateIndex(): void;
|
|
248
209
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
* mutations made by OTHER processes sharing this root (e.g. on EFS).
|
|
210
|
+
* The ID index, rebuilt first if stale. Loads lazily, rebuilds after
|
|
211
|
+
* invalidateIndex(), and otherwise reuses the built index apart from a throttled
|
|
212
|
+
* probe of the on-disk generation marker, which detects mutations by OTHER
|
|
213
|
+
* processes sharing this root (e.g. on EFS).
|
|
254
214
|
*/
|
|
255
215
|
idIndex(): Promise<ContentIdIndex>;
|
|
256
216
|
/**
|
|
@@ -260,157 +220,126 @@ export declare class ContentStore {
|
|
|
260
220
|
*/
|
|
261
221
|
private shouldProbeDiskGeneration;
|
|
262
222
|
/**
|
|
263
|
-
* After one of this store's own successful mutations: publish
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
* we leave the recorded token older and let the next probe observe our bump
|
|
270
|
-
* and trigger the healing rebuild.
|
|
223
|
+
* After one of this store's own successful mutations: publish it to other
|
|
224
|
+
* processes (bump the on-disk marker) and adopt the written token, since the
|
|
225
|
+
* in-memory index was already updated incrementally. Adoption is skipped unless
|
|
226
|
+
* the index is quiescent and `updatedIndex` is still the live instance: a rebuild
|
|
227
|
+
* that raced the mutation may have scanned before our file change, so leaving the
|
|
228
|
+
* recorded token older lets the next probe observe our bump and heal.
|
|
271
229
|
*/
|
|
272
230
|
private recordOwnMutation;
|
|
273
231
|
/**
|
|
274
|
-
* Backstop for the residual staleness windows of the cross-process marker
|
|
275
|
-
*
|
|
276
|
-
* content-index-generation.ts): force one rebuild in response to a
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
* the race rebuilds the index for everyone, and callers should still retry
|
|
283
|
-
* their lookup against the live index regardless of this return value.
|
|
232
|
+
* Backstop for the residual staleness windows of the cross-process marker (NFS
|
|
233
|
+
* attribute caching, probe throttle, self-adoption — see
|
|
234
|
+
* content-index-generation.ts): force one rebuild in response to a suspicious
|
|
235
|
+
* lookup (an ID miss, or an index hit whose file is gone), throttled so a
|
|
236
|
+
* genuinely dangling ID costs at most one rescan per window. Returns true only if
|
|
237
|
+
* THIS call refreshed; a caller that loses the throttle race must still retry its
|
|
238
|
+
* lookup, since idIndex() dedupes concurrent builds and the winner rebuilds for
|
|
239
|
+
* everyone.
|
|
284
240
|
*/
|
|
285
241
|
private refreshIndexForSuspiciousLookup;
|
|
286
242
|
/**
|
|
287
|
-
* Every file in `dir` whose filename embeds `id`, as root-relative paths
|
|
288
|
-
*
|
|
243
|
+
* Every file in `dir` whose filename embeds `id`, as root-relative paths (empty
|
|
244
|
+
* when there is no such file, or no such dir).
|
|
289
245
|
*
|
|
290
|
-
* [F1] Returns ALL matches, not the first: more than one match is a
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
* happened to yield first.
|
|
246
|
+
* [F1] Returns ALL matches, not the first: more than one match is a duplicate-ID
|
|
247
|
+
* pair, and callers must be able to tell that apart from a clean single hit rather
|
|
248
|
+
* than silently picking whichever one `readdir()` happened to yield first.
|
|
294
249
|
*/
|
|
295
250
|
private findEntryPathsById;
|
|
296
|
-
/**
|
|
297
|
-
* Get all schema items for iteration.
|
|
298
|
-
* Used internally by ReferenceResolver for path matching.
|
|
299
|
-
*/
|
|
300
251
|
getSchemaItems(): IterableIterator<FlatSchemaItem>;
|
|
301
252
|
private assertSchemaItem;
|
|
302
253
|
/**
|
|
303
254
|
* Is this path a COLLECTION schema item?
|
|
304
255
|
*
|
|
305
256
|
* The non-throwing form of `assertCollection`, reading the same `schemaIndex` -- which is the
|
|
306
|
-
* point
|
|
307
|
-
* Map is last-wins
|
|
308
|
-
*
|
|
309
|
-
* first-wins and would not.
|
|
257
|
+
* point: a caller that gates on this cannot disagree with what `buildPaths` will then do,
|
|
258
|
+
* because the Map is last-wins where a subcollection's path collides with a parent's
|
|
259
|
+
* entry-type name. A `find` over the flat schema LIST is first-wins and would not.
|
|
310
260
|
*
|
|
311
|
-
* Type-only, deliberately
|
|
312
|
-
* with subcollections and no entries of its own is legal, and
|
|
313
|
-
*
|
|
261
|
+
* Type-only, deliberately: `resolvePath` additionally requires `entries`, but a collection
|
|
262
|
+
* with subcollections and no entries of its own is legal, and testing for that here would
|
|
263
|
+
* reject something `buildPaths` accepts.
|
|
314
264
|
*
|
|
315
265
|
* Exists for `readByUrlPath`'s URL-addressability gate; see `ReadContentInput`'s
|
|
316
|
-
* `urlAddressableOnly
|
|
266
|
+
* `urlAddressableOnly`.
|
|
317
267
|
*/
|
|
318
268
|
isCollectionPath(collectionPath: LogicalPath): boolean;
|
|
319
269
|
/**
|
|
320
270
|
* Does this collection declare `entryTypeName` in its `entries` config?
|
|
321
271
|
*
|
|
322
|
-
* Mirrors `parseTypedFilename(filename, collection.entries)`, which is how `listEntries`
|
|
323
|
-
* whether a file on disk is one of the collection's entries at all. `buildPaths`'
|
|
324
|
-
* scan deliberately does NOT check this -- it matches on slug alone, so
|
|
325
|
-
* was renamed out of the schema stays findable and therefore
|
|
326
|
-
* deletable. Only URL resolution consults this, so what enumeration hides is
|
|
272
|
+
* Mirrors `parseTypedFilename(filename, collection.entries)`, which is how `listEntries`
|
|
273
|
+
* decides whether a file on disk is one of the collection's entries at all. `buildPaths`'
|
|
274
|
+
* own directory scan deliberately does NOT check this -- it matches on slug alone, so an
|
|
275
|
+
* entry whose type was renamed out of the schema stays findable and therefore editable,
|
|
276
|
+
* renameable and deletable. Only URL resolution consults this, so what enumeration hides is
|
|
277
|
+
* not served.
|
|
327
278
|
*
|
|
328
|
-
* Returns FALSE when the collection declares no `entries` at all
|
|
329
|
-
* `parseTypedFilename`'s own
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
* nothing, so a URL read that resolved a file sitting in one would be answering where nothing is
|
|
333
|
-
* advertised -- the exact disagreement this predicate exists to close. Such a file cannot have
|
|
334
|
-
* been created by the CMS (there is no entry type to create it as); it arrived by hand, by merge
|
|
335
|
-
* or by retrofit, and it is invisible to the sitemap and to static params either way.
|
|
279
|
+
* Returns FALSE when the collection declares no `entries` at all -- stricter than
|
|
280
|
+
* `parseTypedFilename`'s own guard, and deliberately so: the enumerating surface is
|
|
281
|
+
* `listCollectionEntries`, which returns `[]` outright for such a collection, so a URL read
|
|
282
|
+
* resolving a file sitting in one would answer where nothing is advertised.
|
|
336
283
|
*
|
|
337
|
-
* Returns true for a path that is not a collection at all, because that is rule 1's
|
|
338
|
-
*
|
|
284
|
+
* Returns true for a path that is not a collection at all, because that is rule 1's
|
|
285
|
+
* question, and under `urlAddressableOnly` rule 1 has already rejected it.
|
|
339
286
|
*/
|
|
340
287
|
declaresEntryType(collectionPath: LogicalPath, entryTypeName: string): boolean;
|
|
341
288
|
private assertCollection;
|
|
342
289
|
/**
|
|
343
|
-
* Lock key for an existing entry, addressed by its permanent content ID
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
* the keyspace scoped per-store. It also keeps this ID keyspace disjoint
|
|
350
|
-
* from other modules' raw-path-keyed locks sharing the same withLock()
|
|
351
|
-
* map (comment-store, branch-metadata, etc).
|
|
290
|
+
* Lock key for an existing entry, addressed by its permanent content ID --
|
|
291
|
+
* stable across renames, which is the whole point of this locking scheme.
|
|
292
|
+
* Namespaced by store root: the same content ID exists in every clone of a
|
|
293
|
+
* branch and one process can hold stores on several clones at once, so the
|
|
294
|
+
* prefix scopes the keyspace per store and keeps it disjoint from other
|
|
295
|
+
* modules' raw-path-keyed locks sharing the same withLock() map.
|
|
352
296
|
*/
|
|
353
297
|
private idLockKey;
|
|
354
298
|
/**
|
|
355
|
-
* Stable collection+slug identifier for lock-key namespacing. Mirrors
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
* defaulting to the entry type's own name (see buildPaths()'s
|
|
359
|
-
* `schemaItem.type === 'entry-type'` branch).
|
|
299
|
+
* Stable collection+slug identifier for lock-key namespacing. Mirrors
|
|
300
|
+
* buildPaths()' entry-type delegation: an entry-type item delegates to its
|
|
301
|
+
* parent collection with the slug defaulting to the entry type's own name.
|
|
360
302
|
*/
|
|
361
303
|
private collectionSlugKey;
|
|
362
304
|
/**
|
|
363
305
|
* Lock key for a not-yet-existing entry (a create). Serializes concurrent
|
|
364
|
-
* same-slug creates WITHIN this process: the second call's in-lock
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
* DIFFERENT process is NOT covered by this in-process
|
|
368
|
-
*
|
|
369
|
-
* writes
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* that would poison index rebuilds.
|
|
306
|
+
* same-slug creates WITHIN this process: the second call's in-lock buildPaths()
|
|
307
|
+
* re-resolution finds the first call's just-written file and folds in as an edit.
|
|
308
|
+
*
|
|
309
|
+
* A create racing from a DIFFERENT process is NOT covered by this in-process
|
|
310
|
+
* mutex, and that is accepted: each writer mints its own fresh ID and filename, so
|
|
311
|
+
* both writes succeed and the result is two same-slug files with different IDs (a
|
|
312
|
+
* slug-uniqueness violation surfaced on the next listing/lookup), never a
|
|
313
|
+
* duplicate-ID collision that would poison index rebuilds.
|
|
373
314
|
*/
|
|
374
315
|
private createLockKey;
|
|
375
316
|
/**
|
|
376
|
-
* Lock key for a write()/delete() pre-pass classification
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
* touch
|
|
380
|
-
*
|
|
381
|
-
* this store's original locking behavior for that narrow case. Not-yet-
|
|
382
|
-
* existing entries lock on a per-slug create-key (createLockKey()).
|
|
317
|
+
* Lock key for a write()/delete() pre-pass classification: an existing entry
|
|
318
|
+
* locks on its content ID (idLockKey()), a not-yet-existing one on a per-slug
|
|
319
|
+
* create-key (createLockKey()). An entry with no embedded ID falls back to its
|
|
320
|
+
* physical path -- renameEntry() refuses to touch those (its four-part filename
|
|
321
|
+
* check), so they have no rename race to guard against.
|
|
383
322
|
*/
|
|
384
323
|
private entryLockKey;
|
|
385
324
|
/**
|
|
386
325
|
* Refuse a create/rename that would give a second entry a `urlPath` another entry already
|
|
387
326
|
* holds. See url-collision.ts for the two shapes that count, and the legitimate
|
|
388
327
|
* landing-page-beside-a-collection shape that deliberately does not.
|
|
389
|
-
*
|
|
390
|
-
* @param collectionDir Absolute physical directory the entry will live in.
|
|
391
|
-
* @param slug The entry's slug, as it will be written.
|
|
392
328
|
*/
|
|
393
329
|
private assertUrlPathAvailable;
|
|
394
330
|
/**
|
|
395
|
-
* Build absolute and relative paths
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
* 1. Validating that resolved paths stay within the content root
|
|
400
|
-
* 2. Checking slugs for malicious patterns (via validateSlug)
|
|
401
|
-
* 3. Using path.resolve to normalize paths before validation
|
|
331
|
+
* Build absolute and relative paths for an entry, filename pattern
|
|
332
|
+
* `{type}.{slug}.{id}.{ext}`. `options.existingId` addresses a known entry; without it a
|
|
333
|
+
* new ID is generated. `options.entryTypeName` picks among a collection's entry types,
|
|
334
|
+
* defaulting to its default one.
|
|
402
335
|
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
* @param options.entryTypeName - For collections with multiple entry types, specify which one to use. Defaults to the default entry type.
|
|
336
|
+
* SECURITY BOUNDARY against path traversal: resolved paths must stay inside the content
|
|
337
|
+
* root, slugs go through `validateSlug`, and everything is `path.resolve`d before those
|
|
338
|
+
* checks. All of it runs BEFORE any file I/O in resolveDocumentPath(), so permission checks
|
|
339
|
+
* happen before filesystem access.
|
|
408
340
|
*/
|
|
409
341
|
private buildPaths;
|
|
410
|
-
/**
|
|
411
|
-
* Path resolution: resolves a URL path to a schema item
|
|
412
|
-
* - Try as collection + slug (last segment = slug)
|
|
413
|
-
*/
|
|
342
|
+
/** Resolve path segments to a collection schema item plus the trailing slug. */
|
|
414
343
|
resolvePath(pathSegments: string[]): {
|
|
415
344
|
schemaItem: FlatSchemaItem;
|
|
416
345
|
slug: Slug;
|
|
@@ -420,21 +349,17 @@ export declare class ContentStore {
|
|
|
420
349
|
relativePath: PhysicalPath;
|
|
421
350
|
id?: string;
|
|
422
351
|
/**
|
|
423
|
-
* Always populated for a valid schema item: the collection branch below
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
* non-entry-type schema items (impossible via the public API, which only
|
|
428
|
-
* ever resolves to one of those two) skip both branches, hitting the
|
|
429
|
-
* throw below instead of returning at all.
|
|
352
|
+
* Always populated for a valid schema item: the collection branch below resolves a name
|
|
353
|
+
* (falling back to `'entry'` when the collection has no matching/default entry type
|
|
354
|
+
* config) and the entry-type branch delegates to it with its own name. Anything else
|
|
355
|
+
* throws below rather than returning.
|
|
430
356
|
*/
|
|
431
357
|
entryTypeName: string;
|
|
432
358
|
/**
|
|
433
|
-
* True if this slug already had a file on disk (
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
* since a brand-new entry also gets an `id` (freshly generated below).
|
|
359
|
+
* True if this slug already had a file on disk (the directory scan found one, or
|
|
360
|
+
* `options.existingId` asserts one) -- i.e. this resolution is an edit, not a create.
|
|
361
|
+
* write()/delete()/renameEntry() use it to pick a stable lock key (see entryLockKey()).
|
|
362
|
+
* NOT the same as `id` being set: a brand-new entry also gets an `id`, generated below.
|
|
438
363
|
*/
|
|
439
364
|
existed: boolean;
|
|
440
365
|
}>;
|
|
@@ -453,7 +378,6 @@ export declare class ContentStore {
|
|
|
453
378
|
readById(id: ContentId): Promise<ContentDocument | null>;
|
|
454
379
|
private readByIdOnce;
|
|
455
380
|
/**
|
|
456
|
-
* Get the ID for an entry given its collection and slug.
|
|
457
381
|
* Returns null if no ID exists yet.
|
|
458
382
|
*/
|
|
459
383
|
getIdForEntry(collectionPath: LogicalPath, slug: Slug): Promise<ContentId | null>;
|
|
@@ -464,80 +388,60 @@ export declare class ContentStore {
|
|
|
464
388
|
*/
|
|
465
389
|
documentExists(collectionPath: LogicalPath, slug?: Slug | ''): Promise<boolean>;
|
|
466
390
|
/**
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
* (
|
|
470
|
-
* existing-file lookup in buildPaths(), which this mirrors. Returns
|
|
471
|
-
* undefined if no entry exists yet at this slug.
|
|
391
|
+
* The on-disk entry type of an existing collection entry, read from its filename
|
|
392
|
+
* (`{type}.{slug}.{id}.{ext}`), which is immutable after creation -- mirroring the
|
|
393
|
+
* existing-file lookup in buildPaths(). Undefined if no entry exists yet at this slug.
|
|
472
394
|
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
* with what will actually be written (post-review M2).
|
|
395
|
+
* The API write boundary (api/content.ts) validates an existing entry's payload against ITS
|
|
396
|
+
* real schema rather than a caller-supplied (or omitted, or spoofed) `entryType` param:
|
|
397
|
+
* ContentStore.write() preserves the on-disk type regardless of what is requested, so
|
|
398
|
+
* validation must agree with what will actually be written.
|
|
478
399
|
*
|
|
479
|
-
* Cheap: one buildPaths() resolution plus a stat, no file content is read
|
|
480
|
-
* same cost class as documentExists().
|
|
400
|
+
* Cheap: one buildPaths() resolution plus a stat, no file content is read.
|
|
481
401
|
*/
|
|
482
402
|
getExistingEntryType(collectionPath: LogicalPath, slug?: Slug | ''): Promise<string | undefined>;
|
|
483
403
|
/**
|
|
484
|
-
* Count existing entries of a given entry type in a collection, by filename
|
|
485
|
-
*
|
|
486
|
-
*
|
|
404
|
+
* Count existing entries of a given entry type in a collection, by filename (entry
|
|
405
|
+
* filenames embed their type: `{type}.{slug}.{id}.{ext}`). Enforces
|
|
406
|
+
* `EntryTypeConfig.maxItems` server-side at the create boundary.
|
|
487
407
|
*/
|
|
488
408
|
countEntriesOfType(collectionPath: LogicalPath, entryTypeName: string): Promise<number>;
|
|
489
409
|
/**
|
|
490
|
-
* Test-only seam: awaited right after the pre-pass buildPaths() resolves
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
* not-yet-acquired lock) to completion before this call proceeds to
|
|
496
|
-
* acquire its (possibly now-stale) lock key -- see the "Deterministic
|
|
497
|
-
* interleavings" testing pattern in docs/concurrency.md and
|
|
498
|
-
* branch-registry.test.ts's `BlockingRegistry` for the same idiom.
|
|
410
|
+
* Test-only seam: awaited right after the pre-pass buildPaths() resolves in delete() and
|
|
411
|
+
* renameEntry(), while neither has yet acquired a lock. No-op in production. A test subclass
|
|
412
|
+
* overrides it to pause in that exact window and run OTHER mutations to completion, making
|
|
413
|
+
* the stale-lock-key path deterministic -- see the "Deterministic interleavings" testing
|
|
414
|
+
* pattern in docs/concurrency.md and branch-registry.test.ts's `BlockingRegistry`.
|
|
499
415
|
*/
|
|
500
416
|
protected afterPrePassForTesting(): Promise<void>;
|
|
501
|
-
/**
|
|
502
|
-
* Delete an entry and remove it from the index.
|
|
503
|
-
*/
|
|
417
|
+
/** Delete an entry and remove it from the index. */
|
|
504
418
|
delete(collectionPath: LogicalPath, slug: Slug): Promise<void>;
|
|
505
419
|
/**
|
|
506
420
|
* Rename an entry by changing its slug (middle segment of filename).
|
|
507
421
|
* Entry filename pattern: {entryTypeName}.{slug}.{id}.{ext}
|
|
508
|
-
*
|
|
509
|
-
* @param collectionPath - Logical path to the collection
|
|
510
|
-
* @param currentSlug - Current slug of the entry
|
|
511
|
-
* @param newSlug - New slug (must be unique within collection)
|
|
512
|
-
* @returns Object with new logical path
|
|
513
|
-
* @throws ContentStoreError if entry doesn't exist, new slug conflicts, or validation fails
|
|
514
422
|
*/
|
|
515
423
|
renameEntry(collectionPath: LogicalPath, currentSlug: Slug, newSlug: Slug): Promise<{
|
|
516
424
|
newPath: LogicalPath;
|
|
517
425
|
}>;
|
|
518
|
-
/**
|
|
519
|
-
* List all entries in a collection tree (including subcollections).
|
|
520
|
-
* For example, passing 'content/data-catalog' returns entries from
|
|
521
|
-
* 'content/data-catalog', 'content/data-catalog/partner-a', etc.
|
|
522
|
-
* Returns array of entry metadata (relativePath, collection, slug).
|
|
523
|
-
* Returns empty array if the collection doesn't exist.
|
|
524
|
-
*/
|
|
525
426
|
/**
|
|
526
427
|
* Resolve a schema collection referenced by name or logical path.
|
|
527
428
|
*
|
|
528
|
-
* Accepts
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
* the dropdown and the write boundary can never disagree.
|
|
429
|
+
* Accepts a full logical path ("content/authors") or a bare collection name ("authors") --
|
|
430
|
+
* the contract reference fields use for `collections: [...]` (see README). The single
|
|
431
|
+
* normalization point shared by reference-option loading and reference validation, so the
|
|
432
|
+
* dropdown and the write boundary can never disagree.
|
|
533
433
|
*
|
|
534
|
-
* CAVEAT: the bare-name fallback matches on the LAST path segment and
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
* same-named collections.
|
|
434
|
+
* CAVEAT: the bare-name fallback matches on the LAST path segment and returns the first hit
|
|
435
|
+
* in schema order, so with two collections sharing a leaf name (content/blog/posts,
|
|
436
|
+
* content/news/posts) a bare 'posts' is ambiguous. Use the full logical path in schemas that
|
|
437
|
+
* nest same-named collections.
|
|
539
438
|
*/
|
|
540
439
|
resolveCollectionItem(collectionPath: string): FlatSchemaItem | undefined;
|
|
440
|
+
/**
|
|
441
|
+
* Every entry in a collection tree, including subcollections: 'content/data-catalog'
|
|
442
|
+
* returns entries from it and from 'content/data-catalog/partner-a' and so on. Empty when
|
|
443
|
+
* the collection does not exist.
|
|
444
|
+
*/
|
|
541
445
|
getCollectionEntryPaths(collectionPath: LogicalPath): Promise<Array<{
|
|
542
446
|
relativePath: PhysicalPath;
|
|
543
447
|
collection: LogicalPath;
|
|
@@ -546,87 +450,57 @@ export declare class ContentStore {
|
|
|
546
450
|
/**
|
|
547
451
|
* Resolve every `reference` field in `data` against `fields`, returning a copy.
|
|
548
452
|
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
* Pass one {@link ReferenceResolveCache} across a whole batch so a target referenced by
|
|
556
|
-
* many entries is read once rather than once per referencing entry. Omit `cache` and the
|
|
557
|
-
* behavior is exactly what `read()` has always done, unmemoized.
|
|
453
|
+
* What `read()` applies automatically (unless `resolveReferences: false`), exposed so BATCH
|
|
454
|
+
* surfaces can opt into the same resolution without duplicating the walk: the listing
|
|
455
|
+
* primitives in content-listing.ts and content-tree.ts read entry files straight off disk
|
|
456
|
+
* and never touch this store. Pass one {@link ReferenceResolveCache} across a whole batch so
|
|
457
|
+
* a target referenced by many entries is read once; omit `cache` for exactly `read()`'s
|
|
458
|
+
* unmemoized behavior.
|
|
558
459
|
*
|
|
559
|
-
* **Path ACLs are not consulted for the targets.** Resolution goes through this store's
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
* `resolveReferences` option in content-listing.ts for the note adopters see.
|
|
460
|
+
* **Path ACLs are not consulted for the targets.** Resolution goes through this store's own
|
|
461
|
+
* `read()`, below the per-entry permission check in content-reader.ts, so a resolved target
|
|
462
|
+
* may be an entry the caller could not `read()` directly -- pre-existing `read()` behavior,
|
|
463
|
+
* matched here on purpose so one rule covers both.
|
|
564
464
|
*/
|
|
565
465
|
resolveReferences(data: Record<string, unknown>, fields: EntrySchema, cache?: ReferenceResolveCache): Promise<Record<string, unknown>>;
|
|
566
|
-
/**
|
|
567
|
-
* Recursively resolve reference fields in data.
|
|
568
|
-
* This traverses objects, arrays, and blocks to find and resolve all reference fields.
|
|
569
|
-
*/
|
|
570
466
|
private resolveReferencesInData;
|
|
571
467
|
/**
|
|
572
|
-
* Resolve a single reference ID to full entry data, memoized when a batch cache is
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
* a snippet-sized target is ~63x cheaper to clone than to re-read-and-parse, while a 265KB
|
|
595
|
-
* JSON target is ~0.8x, i.e. cloning is marginally SLOWER than reparsing it. So the win is
|
|
596
|
-
* large in the case this exists for and roughly a wash at the pathological end, never a
|
|
597
|
-
* blow-up. Two things keep the bad end narrow: an md/mdx target resolves to its FRONTMATTER
|
|
598
|
-
* only *unless the field sets `includeBody`* (`read()` puts the body on `doc.body`, which
|
|
599
|
-
* `resolveSingleReferenceOnce` spreads in only for an embedding field), so by default body
|
|
600
|
-
* size is irrelevant no matter how long the document and only a genuinely huge JSON/YAML
|
|
601
|
-
* target reaches the wash. **`includeBody: true` is the case that CAN reach it on markdown**:
|
|
602
|
-
* the body then sits inside the memoized object and is cloned once per referencing entry, so
|
|
603
|
-
* a long document embedded by many pages pays that repeatedly — the reason `includeBody`
|
|
604
|
-
* exists as an opt-in per field rather than as resolution's default. And in the deployment this
|
|
605
|
-
* targets, content lives on EFS/NFS where the syscall the memo removes dominates parse and
|
|
606
|
-
* clone alike, which the local-disk numbers above understate badly. Correctness is the
|
|
607
|
-
* reason for the copy regardless; the numbers are here so nobody has to re-derive them
|
|
608
|
-
* before touching this.
|
|
468
|
+
* Resolve a single reference ID to full entry data, memoized when a batch cache is supplied.
|
|
469
|
+
* Without a cache this is a straight pass-through, so `read()` behaves as it always has.
|
|
470
|
+
*
|
|
471
|
+
* Every occurrence gets its OWN deep copy, even on a cache hit. Without that, the memo would
|
|
472
|
+
* hand one shared object to all 40 entries referencing the same block -- a mutation in one
|
|
473
|
+
* caller's `extract` would silently rewrite it for every sibling, and both `list: true`
|
|
474
|
+
* elements of `[id, id]` would be the same instance. The copy is what keeps the cache a pure
|
|
475
|
+
* performance optimization instead of a semantic change.
|
|
476
|
+
*
|
|
477
|
+
* The uncached path is NOT the clean baseline it looks like: for md/mdx, gray-matter serves
|
|
478
|
+
* `data` from a process-global content-keyed cache, so `resolveSingleReferenceOnce`'s
|
|
479
|
+
* top-level spread severs exactly one level and NESTED frontmatter objects alias across
|
|
480
|
+
* occurrences, calls and requests. Pre-existing `read()` behavior, deliberately out of scope
|
|
481
|
+
* here; see `.claude/future-tasks/graymatter-cache-shared-frontmatter.md`.
|
|
482
|
+
*
|
|
483
|
+
* Measured (2000 occurrences of one target, local disk, the friendliest case for NOT
|
|
484
|
+
* caching): cloning a snippet-sized target is ~63x cheaper than re-reading and parsing it, a
|
|
485
|
+
* 265KB JSON target ~0.8x -- a wash at the pathological end, never a blow-up, and on the
|
|
486
|
+
* EFS/NFS this targets the syscall the memo removes dominates both. An md/mdx target
|
|
487
|
+
* resolves to its FRONTMATTER unless the field sets `includeBody`, which is why
|
|
488
|
+
* `includeBody` is opt-in per field: an embedded body sits inside the memoized object and is
|
|
489
|
+
* cloned once per referencing entry.
|
|
609
490
|
*/
|
|
610
491
|
private resolveSingleReference;
|
|
611
492
|
/**
|
|
612
|
-
* Returns null if the reference is invalid or missing.
|
|
613
|
-
* Includes id, slug, and collection fields for debugging.
|
|
493
|
+
* Returns null if the reference is invalid or missing. Includes id, slug and collection.
|
|
614
494
|
*
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
*
|
|
621
|
-
* resolved via Promise.all, every miss shares the same stale snapshot, and
|
|
622
|
-
* idIndex() dedupes concurrent builds — so a sibling call that wins the
|
|
623
|
-
* throttle and rebuilds heals every other miss in the same batch, not just
|
|
624
|
-
* the first.
|
|
495
|
+
* A suspicious result (ID missing from the index, or an index hit whose file is gone)
|
|
496
|
+
* triggers one forced index refresh and a retry — self-healing for mutations by other
|
|
497
|
+
* processes inside the marker's residual windows. The forced refresh is itself throttled
|
|
498
|
+
* (see refreshIndexForSuspiciousLookup) but the retry against the live index always runs:
|
|
499
|
+
* every miss in a `list: true` Promise.all shares one stale snapshot, and idIndex() dedupes
|
|
500
|
+
* concurrent builds, so a sibling that wins the throttle heals the whole batch.
|
|
625
501
|
*
|
|
626
|
-
* A {@link ReferenceResolveCache} sits ABOVE this, never inside it, so every
|
|
627
|
-
*
|
|
628
|
-
* is only that repeats of the SAME id in one batch share that one attempt's
|
|
629
|
-
* outcome instead of each getting an independent throw of the dice.
|
|
502
|
+
* A {@link ReferenceResolveCache} sits ABOVE this, never inside it, so every distinct id
|
|
503
|
+
* still runs the full refresh-and-retry; a cache only makes repeats of the SAME id share it.
|
|
630
504
|
*/
|
|
631
505
|
private resolveSingleReferenceUncached;
|
|
632
506
|
private resolveSingleReferenceOnce;
|