canopycms 0.0.61-int.75 → 0.0.62
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/dist/ai/handler.js +1 -1
- package/dist/ai/handler.js.map +1 -1
- package/dist/ai/json-to-markdown.js +1 -2
- package/dist/ai/json-to-markdown.js.map +1 -1
- package/dist/api/__test__/mock-client.d.ts +5 -1
- package/dist/api/__test__/mock-client.d.ts.map +1 -1
- package/dist/api/__test__/mock-client.js +9 -2
- package/dist/api/__test__/mock-client.js.map +1 -1
- package/dist/api/admin-branch-health.d.ts +36 -1
- package/dist/api/admin-branch-health.d.ts.map +1 -1
- package/dist/api/admin-branch-health.js +176 -2
- package/dist/api/admin-branch-health.js.map +1 -1
- package/dist/api/admin.d.ts +8 -1
- package/dist/api/admin.d.ts.map +1 -1
- package/dist/api/admin.js.map +1 -1
- package/dist/api/assets.d.ts.map +1 -1
- package/dist/api/assets.js +36 -4
- package/dist/api/assets.js.map +1 -1
- package/dist/api/branch-status.d.ts.map +1 -1
- package/dist/api/branch-status.js +56 -0
- package/dist/api/branch-status.js.map +1 -1
- package/dist/api/branch-withdraw.d.ts.map +1 -1
- package/dist/api/branch-withdraw.js +14 -3
- package/dist/api/branch-withdraw.js.map +1 -1
- package/dist/api/branch.d.ts +31 -2
- package/dist/api/branch.d.ts.map +1 -1
- package/dist/api/branch.js +268 -15
- package/dist/api/branch.js.map +1 -1
- package/dist/api/client.d.ts +5 -1
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +6 -0
- package/dist/api/client.js.map +1 -1
- package/dist/api/comments.d.ts +2 -2
- package/dist/api/content.d.ts +11 -5
- package/dist/api/content.d.ts.map +1 -1
- package/dist/api/content.js +91 -10
- package/dist/api/content.js.map +1 -1
- package/dist/api/entries.d.ts +10 -0
- package/dist/api/entries.d.ts.map +1 -1
- package/dist/api/entries.js +51 -24
- package/dist/api/entries.js.map +1 -1
- package/dist/api/guards.d.ts.map +1 -1
- package/dist/api/guards.js +12 -5
- package/dist/api/guards.js.map +1 -1
- package/dist/api/permissions.d.ts +2 -2
- package/dist/api/permissions.d.ts.map +1 -1
- package/dist/api/permissions.js +51 -3
- package/dist/api/permissions.js.map +1 -1
- package/dist/api/reference-options.d.ts.map +1 -1
- package/dist/api/reference-options.js +10 -17
- package/dist/api/reference-options.js.map +1 -1
- package/dist/api/resolve-references.d.ts.map +1 -1
- package/dist/api/resolve-references.js +3 -1
- package/dist/api/resolve-references.js.map +1 -1
- package/dist/api/schema.d.ts +4 -4
- package/dist/api/schema.d.ts.map +1 -1
- package/dist/api/schema.js +16 -15
- package/dist/api/schema.js.map +1 -1
- package/dist/api/settings-helpers.d.ts.map +1 -1
- package/dist/api/settings-helpers.js +6 -5
- package/dist/api/settings-helpers.js.map +1 -1
- package/dist/assets/pipeline.d.ts +11 -2
- package/dist/assets/pipeline.d.ts.map +1 -1
- package/dist/assets/pipeline.js +114 -2
- package/dist/assets/pipeline.js.map +1 -1
- package/dist/assets/transform-directives.d.ts +18 -0
- package/dist/assets/transform-directives.d.ts.map +1 -1
- package/dist/assets/transform-directives.js +18 -0
- package/dist/assets/transform-directives.js.map +1 -1
- package/dist/assets/transform.d.ts.map +1 -1
- package/dist/assets/transform.js +20 -16
- package/dist/assets/transform.js.map +1 -1
- package/dist/auth/types.d.ts +29 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/authorization/index.d.ts +1 -1
- package/dist/authorization/index.d.ts.map +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/index.js.map +1 -1
- package/dist/authorization/protected-branch.d.ts +75 -0
- package/dist/authorization/protected-branch.d.ts.map +1 -1
- package/dist/authorization/protected-branch.js +39 -0
- package/dist/authorization/protected-branch.js.map +1 -1
- package/dist/branch-health.d.ts +13 -0
- package/dist/branch-health.d.ts.map +1 -1
- package/dist/branch-health.js +23 -0
- package/dist/branch-health.js.map +1 -1
- package/dist/branch-registry.d.ts.map +1 -1
- package/dist/branch-registry.js +5 -1
- package/dist/branch-registry.js.map +1 -1
- package/dist/branch-schema-cache.d.ts.map +1 -1
- package/dist/branch-schema-cache.js +9 -0
- package/dist/branch-schema-cache.js.map +1 -1
- package/dist/build/generate-ai-content.js +1 -1
- package/dist/build/generate-ai-content.js.map +1 -1
- package/dist/cli/cli.js +2215 -1280
- package/dist/cli/generate-ai-content.js +1158 -585
- package/dist/cli/init.d.ts +8 -2
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +459 -152
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/project-detect.d.ts +89 -0
- package/dist/cli/project-detect.d.ts.map +1 -0
- package/dist/cli/project-detect.js +240 -0
- package/dist/cli/project-detect.js.map +1 -0
- package/dist/cli/template-files/Dockerfile.cms.template +30 -7
- package/dist/cli/template-files/canopycms.config.ts.template +7 -0
- package/dist/cli/template-files/cdk-app.ts.template +82 -0
- package/dist/cli/template-files/cdk.json.template +9 -0
- package/dist/cli/template-files/cms-stack.ts.template +130 -0
- package/dist/cli/template-files/deploy-cms.yml.template +125 -13
- package/dist/cli/template-files/dockerignore.template +2 -1
- package/dist/cli/templates.d.ts +26 -2
- package/dist/cli/templates.d.ts.map +1 -1
- package/dist/cli/templates.js +36 -4
- package/dist/cli/templates.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/config/schemas/config.d.ts +2 -3
- package/dist/config/schemas/config.d.ts.map +1 -1
- package/dist/config/schemas/config.js +21 -1
- package/dist/config/schemas/config.js.map +1 -1
- package/dist/config/schemas/field.d.ts +15 -15
- package/dist/config/types.d.ts +2 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +0 -1
- package/dist/config/types.js.map +1 -1
- package/dist/config/validation.d.ts +18 -0
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +36 -12
- package/dist/config/validation.js.map +1 -1
- package/dist/content-id-index.d.ts +97 -5
- package/dist/content-id-index.d.ts.map +1 -1
- package/dist/content-id-index.js +170 -18
- package/dist/content-id-index.js.map +1 -1
- package/dist/content-reader.js +1 -1
- package/dist/content-reader.js.map +1 -1
- package/dist/content-store.d.ts +92 -7
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +445 -259
- package/dist/content-store.js.map +1 -1
- package/dist/dev-content-watcher.d.ts.map +1 -1
- package/dist/dev-content-watcher.js +4 -1
- package/dist/dev-content-watcher.js.map +1 -1
- package/dist/editor/BranchManager.d.ts +15 -0
- package/dist/editor/BranchManager.d.ts.map +1 -1
- package/dist/editor/BranchManager.js +61 -15
- package/dist/editor/BranchManager.js.map +1 -1
- package/dist/editor/CanopyEditor.d.ts.map +1 -1
- package/dist/editor/CanopyEditor.js +3 -3
- package/dist/editor/CanopyEditor.js.map +1 -1
- package/dist/editor/CanopyEditorPage.d.ts +9 -1
- package/dist/editor/CanopyEditorPage.d.ts.map +1 -1
- package/dist/editor/CanopyEditorPage.js +9 -2
- package/dist/editor/CanopyEditorPage.js.map +1 -1
- package/dist/editor/Editor.d.ts +9 -1
- package/dist/editor/Editor.d.ts.map +1 -1
- package/dist/editor/Editor.js +156 -60
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FormRenderer.d.ts.map +1 -1
- package/dist/editor/FormRenderer.js +12 -0
- package/dist/editor/FormRenderer.js.map +1 -1
- package/dist/editor/admin/SystemHealthPanel.d.ts.map +1 -1
- package/dist/editor/admin/SystemHealthPanel.js +14 -9
- package/dist/editor/admin/SystemHealthPanel.js.map +1 -1
- package/dist/editor/components/EditorHeader.d.ts +6 -0
- package/dist/editor/components/EditorHeader.d.ts.map +1 -1
- package/dist/editor/components/EditorHeader.js +76 -23
- package/dist/editor/components/EditorHeader.js.map +1 -1
- package/dist/editor/components/EntryCreateModal.d.ts +9 -1
- package/dist/editor/components/EntryCreateModal.d.ts.map +1 -1
- package/dist/editor/components/EntryCreateModal.js +25 -7
- package/dist/editor/components/EntryCreateModal.js.map +1 -1
- package/dist/editor/context/SWRProvider.d.ts +31 -0
- package/dist/editor/context/SWRProvider.d.ts.map +1 -0
- package/dist/editor/context/SWRProvider.js +12 -0
- package/dist/editor/context/SWRProvider.js.map +1 -0
- package/dist/editor/context/index.d.ts +3 -0
- package/dist/editor/context/index.d.ts.map +1 -1
- package/dist/editor/context/index.js +3 -0
- package/dist/editor/context/index.js.map +1 -1
- package/dist/editor/editor-utils.d.ts.map +1 -1
- package/dist/editor/editor-utils.js +8 -2
- package/dist/editor/editor-utils.js.map +1 -1
- package/dist/editor/fields/DateTimeField.d.ts +46 -0
- package/dist/editor/fields/DateTimeField.d.ts.map +1 -0
- package/dist/editor/fields/DateTimeField.js +66 -0
- package/dist/editor/fields/DateTimeField.js.map +1 -0
- package/dist/editor/fields/ImageField.d.ts.map +1 -1
- package/dist/editor/fields/ImageField.js +27 -3
- package/dist/editor/fields/ImageField.js.map +1 -1
- package/dist/editor/fields/NumberField.d.ts +34 -0
- package/dist/editor/fields/NumberField.d.ts.map +1 -0
- package/dist/editor/fields/NumberField.js +53 -0
- package/dist/editor/fields/NumberField.js.map +1 -0
- package/dist/editor/fields/NumberListField.d.ts +25 -0
- package/dist/editor/fields/NumberListField.d.ts.map +1 -0
- package/dist/editor/fields/NumberListField.js +53 -0
- package/dist/editor/fields/NumberListField.js.map +1 -0
- package/dist/editor/fields/ReferenceField.d.ts.map +1 -1
- package/dist/editor/fields/ReferenceField.js +52 -31
- package/dist/editor/fields/ReferenceField.js.map +1 -1
- package/dist/editor/hooks/__test__/test-utils.d.ts +38 -3
- package/dist/editor/hooks/__test__/test-utils.d.ts.map +1 -1
- package/dist/editor/hooks/__test__/test-utils.js +50 -4
- package/dist/editor/hooks/__test__/test-utils.js.map +1 -1
- package/dist/editor/hooks/index.d.ts +4 -1
- package/dist/editor/hooks/index.d.ts.map +1 -1
- package/dist/editor/hooks/index.js +4 -1
- package/dist/editor/hooks/index.js.map +1 -1
- package/dist/editor/hooks/useBranchManager.d.ts +21 -1
- package/dist/editor/hooks/useBranchManager.d.ts.map +1 -1
- package/dist/editor/hooks/useBranchManager.js +183 -58
- package/dist/editor/hooks/useBranchManager.js.map +1 -1
- package/dist/editor/hooks/useBranchesData.d.ts +57 -0
- package/dist/editor/hooks/useBranchesData.d.ts.map +1 -0
- package/dist/editor/hooks/useBranchesData.js +69 -0
- package/dist/editor/hooks/useBranchesData.js.map +1 -0
- package/dist/editor/hooks/useCommentSystem.d.ts.map +1 -1
- package/dist/editor/hooks/useCommentSystem.js +24 -22
- package/dist/editor/hooks/useCommentSystem.js.map +1 -1
- package/dist/editor/hooks/useCommentsData.d.ts +29 -0
- package/dist/editor/hooks/useCommentsData.d.ts.map +1 -0
- package/dist/editor/hooks/useCommentsData.js +40 -0
- package/dist/editor/hooks/useCommentsData.js.map +1 -0
- package/dist/editor/hooks/useDraftManager.d.ts +8 -0
- package/dist/editor/hooks/useDraftManager.d.ts.map +1 -1
- package/dist/editor/hooks/useDraftManager.js +290 -36
- package/dist/editor/hooks/useDraftManager.js.map +1 -1
- package/dist/editor/hooks/useEntriesData.d.ts +35 -0
- package/dist/editor/hooks/useEntriesData.d.ts.map +1 -0
- package/dist/editor/hooks/useEntriesData.js +96 -0
- package/dist/editor/hooks/useEntriesData.js.map +1 -0
- package/dist/editor/hooks/useEntryManager.d.ts +29 -15
- package/dist/editor/hooks/useEntryManager.d.ts.map +1 -1
- package/dist/editor/hooks/useEntryManager.js +284 -119
- package/dist/editor/hooks/useEntryManager.js.map +1 -1
- package/dist/editor/hooks/usePermissionManager.d.ts +2 -2
- package/dist/editor/hooks/usePermissionManager.d.ts.map +1 -1
- package/dist/editor/hooks/usePermissionManager.js +11 -9
- package/dist/editor/hooks/usePermissionManager.js.map +1 -1
- package/dist/editor/hooks/useReferenceResolution.d.ts.map +1 -1
- package/dist/editor/hooks/useReferenceResolution.js +24 -1
- package/dist/editor/hooks/useReferenceResolution.js.map +1 -1
- package/dist/editor/media/MediaLibraryBody.d.ts.map +1 -1
- package/dist/editor/media/MediaLibraryBody.js +12 -2
- package/dist/editor/media/MediaLibraryBody.js.map +1 -1
- package/dist/editor/permission-manager/GroupSelector.d.ts.map +1 -1
- package/dist/editor/permission-manager/GroupSelector.js +7 -2
- package/dist/editor/permission-manager/GroupSelector.js.map +1 -1
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +3 -3
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts.map +1 -1
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +1 -0
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js.map +1 -1
- package/dist/editor/permission-manager/types.d.ts +6 -4
- package/dist/editor/permission-manager/types.d.ts.map +1 -1
- package/dist/editor/test-setup.js +8 -0
- package/dist/editor/test-setup.js.map +1 -1
- package/dist/editor/theme.d.ts +8 -8
- package/dist/git-manager.d.ts +175 -14
- package/dist/git-manager.d.ts.map +1 -1
- package/dist/git-manager.js +354 -39
- package/dist/git-manager.js.map +1 -1
- package/dist/github-service.d.ts.map +1 -1
- package/dist/github-service.js +13 -7
- package/dist/github-service.js.map +1 -1
- package/dist/http/handler.d.ts.map +1 -1
- package/dist/http/handler.js +78 -29
- package/dist/http/handler.js.map +1 -1
- package/dist/http/router.d.ts.map +1 -1
- package/dist/http/router.js +40 -3
- package/dist/http/router.js.map +1 -1
- package/dist/operating-mode/client-unsafe-strategy.d.ts.map +1 -1
- package/dist/operating-mode/client-unsafe-strategy.js +7 -8
- package/dist/operating-mode/client-unsafe-strategy.js.map +1 -1
- package/dist/operating-mode/deployment-name-fixtures.d.ts +29 -0
- package/dist/operating-mode/deployment-name-fixtures.d.ts.map +1 -0
- package/dist/operating-mode/deployment-name-fixtures.js +55 -0
- package/dist/operating-mode/deployment-name-fixtures.js.map +1 -0
- package/dist/operating-mode/deployment-name.d.ts +37 -0
- package/dist/operating-mode/deployment-name.d.ts.map +1 -0
- package/dist/operating-mode/deployment-name.js +86 -0
- package/dist/operating-mode/deployment-name.js.map +1 -0
- package/dist/operating-mode/index.d.ts +6 -1
- package/dist/operating-mode/index.d.ts.map +1 -1
- package/dist/operating-mode/index.js +10 -1
- package/dist/operating-mode/index.js.map +1 -1
- package/dist/operating-mode/mode-env.d.ts +58 -0
- package/dist/operating-mode/mode-env.d.ts.map +1 -0
- package/dist/operating-mode/mode-env.js +97 -0
- package/dist/operating-mode/mode-env.js.map +1 -0
- package/dist/operating-mode/types.d.ts +13 -3
- package/dist/operating-mode/types.d.ts.map +1 -1
- package/dist/paths/branch-name.d.ts +38 -0
- package/dist/paths/branch-name.d.ts.map +1 -1
- package/dist/paths/branch-name.js +46 -0
- package/dist/paths/branch-name.js.map +1 -1
- package/dist/paths/index.d.ts +1 -1
- package/dist/paths/index.d.ts.map +1 -1
- package/dist/paths/index.js +1 -1
- package/dist/paths/index.js.map +1 -1
- package/dist/reference-resolver.d.ts +6 -2
- package/dist/reference-resolver.d.ts.map +1 -1
- package/dist/reference-resolver.js +8 -1
- package/dist/reference-resolver.js.map +1 -1
- package/dist/resolve-canopy-user.d.ts +34 -0
- package/dist/resolve-canopy-user.d.ts.map +1 -0
- package/dist/resolve-canopy-user.js +55 -0
- package/dist/resolve-canopy-user.js.map +1 -0
- package/dist/schema/meta-loader.d.ts.map +1 -1
- package/dist/schema/meta-loader.js +10 -2
- package/dist/schema/meta-loader.js.map +1 -1
- package/dist/schema/schema-store.d.ts +64 -4
- package/dist/schema/schema-store.d.ts.map +1 -1
- package/dist/schema/schema-store.js +113 -30
- package/dist/schema/schema-store.js.map +1 -1
- package/dist/server.d.ts +9 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +9 -0
- package/dist/server.js.map +1 -1
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +40 -6
- package/dist/services.js.map +1 -1
- package/dist/settings-workspace.d.ts +27 -2
- package/dist/settings-workspace.d.ts.map +1 -1
- package/dist/settings-workspace.js +142 -58
- package/dist/settings-workspace.js.map +1 -1
- package/dist/types.d.ts +73 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/content-write-lock.d.ts +127 -0
- package/dist/utils/content-write-lock.d.ts.map +1 -0
- package/dist/utils/content-write-lock.js +170 -0
- package/dist/utils/content-write-lock.js.map +1 -0
- package/dist/utils/debug.d.ts +11 -0
- package/dist/utils/debug.d.ts.map +1 -1
- package/dist/utils/debug.js +12 -1
- package/dist/utils/debug.js.map +1 -1
- package/dist/utils/error.d.ts +1 -1
- package/dist/utils/error.js +1 -1
- package/dist/utils/git.d.ts +54 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +91 -0
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/logger.d.ts +71 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +85 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/sanitize-href.d.ts +28 -3
- package/dist/utils/sanitize-href.d.ts.map +1 -1
- package/dist/utils/sanitize-href.js +64 -6
- package/dist/utils/sanitize-href.js.map +1 -1
- package/dist/validation/entry-link-validator.d.ts +1 -1
- package/dist/validation/entry-link-validator.js +3 -3
- package/dist/validation/entry-link-validator.js.map +1 -1
- package/dist/validation/entry-type-reference-validator.d.ts +33 -0
- package/dist/validation/entry-type-reference-validator.d.ts.map +1 -0
- package/dist/validation/entry-type-reference-validator.js +110 -0
- package/dist/validation/entry-type-reference-validator.js.map +1 -0
- package/dist/validation/entry-validator.js +1 -1
- package/dist/validation/entry-validator.js.map +1 -1
- package/dist/worker/cms-worker.d.ts +245 -3
- package/dist/worker/cms-worker.d.ts.map +1 -1
- package/dist/worker/cms-worker.js +1084 -253
- package/dist/worker/cms-worker.js.map +1 -1
- package/dist/worker/log.d.ts +61 -0
- package/dist/worker/log.d.ts.map +1 -0
- package/dist/worker/log.js +74 -0
- package/dist/worker/log.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AuthenticationResult } from './auth/types';
|
|
2
|
+
import type { CanopyUser } from './user';
|
|
3
|
+
import type { OperatingMode } from './operating-mode';
|
|
4
|
+
/** Test-only: reset the once-per-process "no admins configured" warning latch. */
|
|
5
|
+
export declare function resetResolveCanopyUserWarningForTests(): void;
|
|
6
|
+
export interface ResolveCanopyUserDeps {
|
|
7
|
+
/**
|
|
8
|
+
* Resolves (and ensures) the settings workspace root. Internal groups are
|
|
9
|
+
* the single source of truth for group-based privileges and MUST be
|
|
10
|
+
* loaded from here — the same root `createContentAccessChecker`
|
|
11
|
+
* (authorization/content.ts) uses for path permissions — never from a
|
|
12
|
+
* content branch clone.
|
|
13
|
+
*
|
|
14
|
+
* Must THROW if the settings workspace cannot be ensured, matching
|
|
15
|
+
* `createContentAccessChecker`'s fail-loud contract: a caught error here
|
|
16
|
+
* and a silent fallback to an empty group list would read as "no
|
|
17
|
+
* privileges", which is a silent authorization change, not a safe
|
|
18
|
+
* degradation.
|
|
19
|
+
*/
|
|
20
|
+
getSettingsBranchRoot: () => Promise<string>;
|
|
21
|
+
mode: OperatingMode;
|
|
22
|
+
bootstrapAdminIds: Set<string>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the CanopyUser for a request: load internal groups from the
|
|
26
|
+
* settings workspace and merge them (plus bootstrap admins) into an
|
|
27
|
+
* authentication result via `authResultToCanopyUser`.
|
|
28
|
+
*
|
|
29
|
+
* Framework-agnostic: callers supply the already-computed `authResult` (from
|
|
30
|
+
* whatever auth-plugin invocation their transport uses) and a small deps
|
|
31
|
+
* bag drawn from `CanopyServices`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveCanopyUser(authResult: AuthenticationResult, deps: ResolveCanopyUserDeps): Promise<CanopyUser>;
|
|
34
|
+
//# sourceMappingURL=resolve-canopy-user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-canopy-user.d.ts","sourceRoot":"","sources":["../src/resolve-canopy-user.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAGxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAKrD,kFAAkF;AAClF,wBAAgB,qCAAqC,IAAI,IAAI,CAE5D;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;;;OAYG;IACH,qBAAqB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IAC5C,IAAI,EAAE,aAAa,CAAA;IACnB,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAC/B;AAcD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,oBAAoB,EAChC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,UAAU,CAAC,CAOrB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "authenticate -> resolve internal groups -> merge into CanopyUser"
|
|
3
|
+
* pipeline.
|
|
4
|
+
*
|
|
5
|
+
* This used to be duplicated between `http/handler.ts` (the core API
|
|
6
|
+
* handler) and `canopycms-next`'s `context-wrapper.ts` (Next.js SSR user
|
|
7
|
+
* extraction) - including a module-level `warnedNoAdmins` flag in each copy.
|
|
8
|
+
* The duplication let the two drift: `http/handler.ts` loaded internal
|
|
9
|
+
* groups from the BASE BRANCH content clone's `groups.json`, which nothing
|
|
10
|
+
* in the product ever writes (writes go to the settings workspace via
|
|
11
|
+
* `mutateGroupsFile` - `api/groups.ts`), so group-based privileges never
|
|
12
|
+
* actually took effect. Consolidating the pipeline here means both callers
|
|
13
|
+
* read from the same place and cannot silently diverge again.
|
|
14
|
+
*
|
|
15
|
+
* canopyLogWarn, not console.warn: this module has no worker-specific logic,
|
|
16
|
+
* but nothing prevents it from becoming reachable from the worker's runtime
|
|
17
|
+
* import closure later, and the existing "no admins configured" warning is
|
|
18
|
+
* exactly the kind of operationally interesting line that must not be
|
|
19
|
+
* silently folded into an unrelated worker.log event (see utils/logger.ts).
|
|
20
|
+
*/
|
|
21
|
+
import { canopyLogWarn } from './utils/logger.js';
|
|
22
|
+
import { authResultToCanopyUser } from './user.js';
|
|
23
|
+
import { loadInternalGroups, RESERVED_GROUPS } from './authorization/index.js';
|
|
24
|
+
/** Module-level, matching the pre-extraction behavior: warn at most once per process. */
|
|
25
|
+
let warnedNoAdmins = false;
|
|
26
|
+
/** Test-only: reset the once-per-process "no admins configured" warning latch. */
|
|
27
|
+
export function resetResolveCanopyUserWarningForTests() {
|
|
28
|
+
warnedNoAdmins = false;
|
|
29
|
+
}
|
|
30
|
+
function warnIfNoAdmins(internalGroups, bootstrapAdminIds) {
|
|
31
|
+
if (warnedNoAdmins)
|
|
32
|
+
return;
|
|
33
|
+
const adminsGroup = internalGroups.find((g) => g.id === RESERVED_GROUPS.ADMINS);
|
|
34
|
+
const hasAdmins = (adminsGroup?.members.length ?? 0) > 0 || bootstrapAdminIds.size > 0;
|
|
35
|
+
if (!hasAdmins) {
|
|
36
|
+
canopyLogWarn('CanopyCMS: No admin users configured. Set CANOPY_BOOTSTRAP_ADMIN_IDS or add members to the Admins group.');
|
|
37
|
+
}
|
|
38
|
+
warnedNoAdmins = true;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the CanopyUser for a request: load internal groups from the
|
|
42
|
+
* settings workspace and merge them (plus bootstrap admins) into an
|
|
43
|
+
* authentication result via `authResultToCanopyUser`.
|
|
44
|
+
*
|
|
45
|
+
* Framework-agnostic: callers supply the already-computed `authResult` (from
|
|
46
|
+
* whatever auth-plugin invocation their transport uses) and a small deps
|
|
47
|
+
* bag drawn from `CanopyServices`.
|
|
48
|
+
*/
|
|
49
|
+
export async function resolveCanopyUser(authResult, deps) {
|
|
50
|
+
const settingsRoot = await deps.getSettingsBranchRoot();
|
|
51
|
+
const internalGroups = await loadInternalGroups(settingsRoot, deps.mode, deps.bootstrapAdminIds);
|
|
52
|
+
warnIfNoAdmins(internalGroups, deps.bootstrapAdminIds);
|
|
53
|
+
return authResultToCanopyUser(authResult, deps.bootstrapAdminIds, internalGroups);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=resolve-canopy-user.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-canopy-user.js","sourceRoot":"","sources":["../src/resolve-canopy-user.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAG9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAsB,MAAM,iBAAiB,CAAA;AAGzF,yFAAyF;AACzF,IAAI,cAAc,GAAG,KAAK,CAAA;AAE1B,kFAAkF;AAClF,MAAM,UAAU,qCAAqC;IACnD,cAAc,GAAG,KAAK,CAAA;AACxB,CAAC;AAqBD,SAAS,cAAc,CAAC,cAA+B,EAAE,iBAA8B;IACrF,IAAI,cAAc;QAAE,OAAM;IAC1B,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,eAAe,CAAC,MAAM,CAAC,CAAA;IAC/E,MAAM,SAAS,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,iBAAiB,CAAC,IAAI,GAAG,CAAC,CAAA;IACtF,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,aAAa,CACX,0GAA0G,CAC3G,CAAA;IACH,CAAC;IACD,cAAc,GAAG,IAAI,CAAA;AACvB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAgC,EAChC,IAA2B;IAE3B,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAA;IACvD,MAAM,cAAc,GAAG,MAAM,kBAAkB,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAA;IAEhG,cAAc,CAAC,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAA;IAEtD,OAAO,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAA;AACnF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meta-loader.d.ts","sourceRoot":"","sources":["../../src/schema/meta-loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"meta-loader.d.ts","sourceRoot":"","sources":["../../src/schema/meta-loader.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAGV,oBAAoB,EAErB,MAAM,WAAW,CAAA;AAClB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAoDlD,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;IACtC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,aAAa,EAAE,CAAA;IACzB,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,aAAa,EAAE,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAyFD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1E,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC/B,WAAW,EAAE,KAAK,CAAC,cAAc,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC,CAAA;CAC7E,CAAC,CAiCD;AA2ED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE;IACT,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC/B,WAAW,EAAE,KAAK,CAAC,cAAc,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC,CAAA;CAC7E,EACD,mBAAmB,EAAE,mBAAmB,GACvC,oBAAoB,CAiCtB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAe9F"}
|
|
@@ -3,6 +3,14 @@ import { join } from 'pathe';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import chokidar from 'chokidar';
|
|
5
5
|
import { getErrorMessage } from '../utils/error.js';
|
|
6
|
+
// canopyLogError/canopyLogWarn, not console.*: this module sits in the
|
|
7
|
+
// worker's runtime import closure (worker/cms-worker.ts ->
|
|
8
|
+
// content-index-generation.ts -> branch-schema-cache.ts -> schema/resolver.ts
|
|
9
|
+
// -> here), so a bare console line would land in worker.log without the
|
|
10
|
+
// ISO-8601 prefix the CloudWatch agent's multi_line_start_pattern needs (see
|
|
11
|
+
// worker/log.ts). Plain console under Lambda/dev, as everywhere else -- see
|
|
12
|
+
// utils/logger.ts.
|
|
13
|
+
import { canopyLogError, canopyLogWarn } from '../utils/logger.js';
|
|
6
14
|
import { extractSlugFromFilename, extractIdFromFilename } from '../content-id-index.js';
|
|
7
15
|
/**
|
|
8
16
|
* Zod schema for entry type metadata in .collection.json files.
|
|
@@ -109,7 +117,7 @@ async function scanForCollectionMeta(baseDir, relativePath = '') {
|
|
|
109
117
|
catch (err) {
|
|
110
118
|
if (err.code !== 'ENOENT') {
|
|
111
119
|
// File exists but is invalid
|
|
112
|
-
|
|
120
|
+
canopyLogError(`Error loading ${metaPath}:`, err);
|
|
113
121
|
throw new Error(`Invalid .collection.json in ${folderPath}: ${err.message}`);
|
|
114
122
|
}
|
|
115
123
|
// No .collection.json - still scan subfolders in case they have collections
|
|
@@ -274,7 +282,7 @@ export function watchCollectionMetaFiles(contentRoot, onChange) {
|
|
|
274
282
|
watcher.on('change', onChange);
|
|
275
283
|
watcher.on('unlink', onChange);
|
|
276
284
|
// Handle watcher errors (e.g. inotify ENOSPC/EMFILE) so an unhandled 'error' event can't crash dev.
|
|
277
|
-
watcher.on('error', (err) =>
|
|
285
|
+
watcher.on('error', (err) => canopyLogWarn(`CanopyCMS: .collection.json watcher error: ${getErrorMessage(err)}`));
|
|
278
286
|
// Return cleanup function
|
|
279
287
|
return () => watcher.close();
|
|
280
288
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meta-loader.js","sourceRoot":"","sources":["../../src/schema/meta-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAC5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,QAAQ,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"meta-loader.js","sourceRoot":"","sources":["../../src/schema/meta-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -130,6 +130,7 @@ declare const createCollectionInputSchema: z.ZodObject<{
|
|
|
130
130
|
maxItems?: number | undefined;
|
|
131
131
|
}>, "many">;
|
|
132
132
|
}, "strip", z.ZodTypeAny, {
|
|
133
|
+
name: string;
|
|
133
134
|
entries: {
|
|
134
135
|
name: string;
|
|
135
136
|
format: "mdx" | "md" | "json" | "yaml";
|
|
@@ -138,10 +139,10 @@ declare const createCollectionInputSchema: z.ZodObject<{
|
|
|
138
139
|
label?: string | undefined;
|
|
139
140
|
maxItems?: number | undefined;
|
|
140
141
|
}[];
|
|
141
|
-
name: string;
|
|
142
142
|
label?: string | undefined;
|
|
143
143
|
parentPath?: string | undefined;
|
|
144
144
|
}, {
|
|
145
|
+
name: string;
|
|
145
146
|
entries: {
|
|
146
147
|
name: string;
|
|
147
148
|
format: "mdx" | "md" | "json" | "yaml";
|
|
@@ -150,7 +151,6 @@ declare const createCollectionInputSchema: z.ZodObject<{
|
|
|
150
151
|
label?: string | undefined;
|
|
151
152
|
maxItems?: number | undefined;
|
|
152
153
|
}[];
|
|
153
|
-
name: string;
|
|
154
154
|
label?: string | undefined;
|
|
155
155
|
parentPath?: string | undefined;
|
|
156
156
|
}>;
|
|
@@ -203,11 +203,20 @@ export declare class SchemaOps {
|
|
|
203
203
|
private readonly contentRoot;
|
|
204
204
|
private readonly entrySchemaRegistry;
|
|
205
205
|
private readonly services?;
|
|
206
|
-
/** Branch root
|
|
206
|
+
/** Branch root. Resolved once so the lock path is stable. */
|
|
207
207
|
private readonly branchRoot;
|
|
208
|
+
/**
|
|
209
|
+
* The content root's path relative to `branchRoot` — "content" normally, but
|
|
210
|
+
* "cms/content" for a multi-segment `config.contentRoot`. `flattenSchema` uses
|
|
211
|
+
* the configured contentRoot as the base of every logical path, so
|
|
212
|
+
* root-collection detection must compare against this, NOT
|
|
213
|
+
* `path.basename(contentRoot)` (which would yield "content" for "cms/content"
|
|
214
|
+
* and never match the root collection's logical path).
|
|
215
|
+
*/
|
|
216
|
+
private readonly contentRootName;
|
|
208
217
|
/** Coarse per-branch surrogate lock path — see the module doc comment. */
|
|
209
218
|
private readonly schemaLockPath;
|
|
210
|
-
constructor(contentRoot: string, entrySchemaRegistry: EntrySchemaRegistry, services?: CanopyServices | undefined);
|
|
219
|
+
constructor(contentRoot: string, entrySchemaRegistry: EntrySchemaRegistry, services?: CanopyServices | undefined, branchRoot?: string);
|
|
211
220
|
/**
|
|
212
221
|
* Serialize an entire read-modify-write schema mutation behind the coarse
|
|
213
222
|
* per-branch surrogate lock described in the module doc comment. Layers,
|
|
@@ -285,6 +294,51 @@ export declare class SchemaOps {
|
|
|
285
294
|
* bulk-mutation call sites that use the combined helper.
|
|
286
295
|
*/
|
|
287
296
|
private invalidateContentIdIndexes;
|
|
297
|
+
/**
|
|
298
|
+
* The single normalisation boundary for logical collection paths entering
|
|
299
|
+
* this class. Every public method that accepts a logical collection path
|
|
300
|
+
* (or, for `createCollection`, `input.parentPath`) calls this exactly once
|
|
301
|
+
* before doing anything else with the path — that is the whole fix for the
|
|
302
|
+
* "Collection not found" bug: `flattenSchema` (branch-schema-cache.ts)
|
|
303
|
+
* produces content-root-prefixed logical paths (e.g. "content/posts", or
|
|
304
|
+
* "cms/content/posts" for a multi-segment `contentRoot: 'cms/content'`),
|
|
305
|
+
* the editor round-trips those straight back into every mutator
|
|
306
|
+
* (`CollectionEditor.tsx` passes `editingCollection.logicalPath` as-is),
|
|
307
|
+
* but `resolveCollectionPath(this.contentRoot, ...)` treats its second
|
|
308
|
+
* argument as relative to `this.contentRoot` — which already embeds the
|
|
309
|
+
* content-root segment(s). A prefixed path therefore resolved one level
|
|
310
|
+
* too deep and was reported as not found. `updateCollectionInner` used to
|
|
311
|
+
* strip the prefix itself (the only mutator that did); that bespoke strip
|
|
312
|
+
* is gone now that every entry point normalises here instead.
|
|
313
|
+
*
|
|
314
|
+
* Prefix-only: strips ONE leading `"{contentRootName}/"` if present (exact
|
|
315
|
+
* string match against the full, possibly multi-segment, `contentRootName`
|
|
316
|
+
* — never `path.basename()`, which would break a multi-segment root like
|
|
317
|
+
* "cms/content"), otherwise returns the path unchanged. So both prefixed
|
|
318
|
+
* (production/editor) and unprefixed (existing store tests, adopters
|
|
319
|
+
* calling SchemaOps directly) input work. Deliberately does NOT touch the
|
|
320
|
+
* bare root-collection sentinel (`collectionPath === this.contentRootName`,
|
|
321
|
+
* no trailing segment): that has no trailing "/" to match, so it passes
|
|
322
|
+
* through unchanged and the `=== this.contentRootName` checks in
|
|
323
|
+
* `updateCollectionInner`/`updateOrderInner` keep working.
|
|
324
|
+
*
|
|
325
|
+
* CALL EXACTLY ONCE PER ENTRY POINT — it is NOT idempotent, and an earlier
|
|
326
|
+
* version of this comment claimed it was. A sub-collection literally named
|
|
327
|
+
* after the content root defeats the "already normalised" reasoning: with
|
|
328
|
+
* `contentRoot: 'content'`, "content/content/x" normalises to "content/x"
|
|
329
|
+
* and again to "x", and "content/content" normalises to "content", which
|
|
330
|
+
* the `=== this.contentRootName` checks above then treat as the ROOT
|
|
331
|
+
* collection — so a second call mutates a DIFFERENT collection rather than
|
|
332
|
+
* reporting not-found. Every entry point calls this exactly once today, so
|
|
333
|
+
* the ambiguity is unreachable; removing it needs the editor to send an
|
|
334
|
+
* unambiguously-scoped path, tracked in
|
|
335
|
+
* .claude/future-tasks/collection-path-content-root-ambiguity.md.
|
|
336
|
+
*
|
|
337
|
+
* A future public method that accepts a logical collection path must call
|
|
338
|
+
* this first, and only once, the same way every existing one does — that is
|
|
339
|
+
* the "safe by construction" contract this boundary is meant to provide.
|
|
340
|
+
*/
|
|
341
|
+
private normalizeCollectionPath;
|
|
288
342
|
/**
|
|
289
343
|
* Validate that a schema reference exists in the registry
|
|
290
344
|
*/
|
|
@@ -334,6 +388,12 @@ export declare class SchemaOps {
|
|
|
334
388
|
* read-modify-write. Called directly (not via the public `updateCollection`)
|
|
335
389
|
* by `updateOrderInner` for non-root collections — `withSchemaLock` is NOT
|
|
336
390
|
* re-entrant, so going through the public method there would deadlock.
|
|
391
|
+
*
|
|
392
|
+
* `collectionPath` here is always already normalized (content-root prefix
|
|
393
|
+
* stripped, if it had one) by whichever public method reached this — either
|
|
394
|
+
* `updateCollection` above or `updateOrder` via `updateOrderInner` — so this
|
|
395
|
+
* no longer re-strips the prefix itself; see normalizeCollectionPath's doc
|
|
396
|
+
* comment for the single boundary that owns that now.
|
|
337
397
|
*/
|
|
338
398
|
private updateCollectionInner;
|
|
339
399
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-store.d.ts","sourceRoot":"","sources":["../../src/schema/schema-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAIH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAOvB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"schema-store.d.ts","sourceRoot":"","sources":["../../src/schema/schema-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -54,7 +54,7 @@ import { withOccFileLock, OccWriteConflictError } from '../utils/occ-json-write.
|
|
|
54
54
|
import { resolveCollectionPath } from '../content-id-index.js';
|
|
55
55
|
import { invalidateBranchContentCaches } from '../content-index-generation.js';
|
|
56
56
|
import { generateId, isValidId } from '../id.js';
|
|
57
|
-
import { createLogicalPath, validateAndNormalizePath } from '../paths/index.js';
|
|
57
|
+
import { createLogicalPath, normalizeCollectionPath as stripContentRootPrefix, validateAndNormalizePath, } from '../paths/index.js';
|
|
58
58
|
// ============================================================================
|
|
59
59
|
// Zod Schemas for Validation
|
|
60
60
|
// ============================================================================
|
|
@@ -133,11 +133,23 @@ export class SchemaStoreBusyError extends Error {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
export class SchemaOps {
|
|
136
|
-
constructor(contentRoot, entrySchemaRegistry, services) {
|
|
136
|
+
constructor(contentRoot, entrySchemaRegistry, services, branchRoot) {
|
|
137
137
|
this.contentRoot = contentRoot;
|
|
138
138
|
this.entrySchemaRegistry = entrySchemaRegistry;
|
|
139
139
|
this.services = services;
|
|
140
|
-
|
|
140
|
+
// Prefer an explicitly supplied branchRoot. Deriving it as dirname(contentRoot)
|
|
141
|
+
// is only correct when config.contentRoot is a single segment, and
|
|
142
|
+
// `contentRoot: 'content/posts'` is documented as valid (config/helpers.ts) —
|
|
143
|
+
// for that, dirname() lands one level too deep and would put the schema lock
|
|
144
|
+
// and .canopy-meta in the wrong directory. Callers that know the branch root
|
|
145
|
+
// (api/schema.ts, api/entries.ts) pass it; the fallback keeps the derivation
|
|
146
|
+
// identical to before for callers that don't.
|
|
147
|
+
const resolvedContentRoot = path.resolve(contentRoot);
|
|
148
|
+
this.branchRoot = branchRoot ? path.resolve(branchRoot) : path.dirname(resolvedContentRoot);
|
|
149
|
+
this.contentRootName = path
|
|
150
|
+
.relative(this.branchRoot, resolvedContentRoot)
|
|
151
|
+
.split(path.sep)
|
|
152
|
+
.join('/');
|
|
141
153
|
this.schemaLockPath = path.join(this.branchRoot, '.canopy-meta', 'schema');
|
|
142
154
|
}
|
|
143
155
|
// --------------------------------------------------------------------------
|
|
@@ -230,7 +242,7 @@ export class SchemaOps {
|
|
|
230
242
|
return;
|
|
231
243
|
await this.services.branchSchemaCache.invalidate(this.branchRoot);
|
|
232
244
|
try {
|
|
233
|
-
await this.services.branchSchemaCache.resolveAndPersist(this.branchRoot, this.entrySchemaRegistry,
|
|
245
|
+
await this.services.branchSchemaCache.resolveAndPersist(this.branchRoot, this.entrySchemaRegistry, this.contentRootName);
|
|
234
246
|
}
|
|
235
247
|
catch (err) {
|
|
236
248
|
log.warn('schema-cache', `Eager schema re-resolve after invalidation failed`, {
|
|
@@ -258,6 +270,56 @@ export class SchemaOps {
|
|
|
258
270
|
await invalidateBranchContentCaches(this.branchRoot);
|
|
259
271
|
}
|
|
260
272
|
// --------------------------------------------------------------------------
|
|
273
|
+
// Path Normalization
|
|
274
|
+
// --------------------------------------------------------------------------
|
|
275
|
+
/**
|
|
276
|
+
* The single normalisation boundary for logical collection paths entering
|
|
277
|
+
* this class. Every public method that accepts a logical collection path
|
|
278
|
+
* (or, for `createCollection`, `input.parentPath`) calls this exactly once
|
|
279
|
+
* before doing anything else with the path — that is the whole fix for the
|
|
280
|
+
* "Collection not found" bug: `flattenSchema` (branch-schema-cache.ts)
|
|
281
|
+
* produces content-root-prefixed logical paths (e.g. "content/posts", or
|
|
282
|
+
* "cms/content/posts" for a multi-segment `contentRoot: 'cms/content'`),
|
|
283
|
+
* the editor round-trips those straight back into every mutator
|
|
284
|
+
* (`CollectionEditor.tsx` passes `editingCollection.logicalPath` as-is),
|
|
285
|
+
* but `resolveCollectionPath(this.contentRoot, ...)` treats its second
|
|
286
|
+
* argument as relative to `this.contentRoot` — which already embeds the
|
|
287
|
+
* content-root segment(s). A prefixed path therefore resolved one level
|
|
288
|
+
* too deep and was reported as not found. `updateCollectionInner` used to
|
|
289
|
+
* strip the prefix itself (the only mutator that did); that bespoke strip
|
|
290
|
+
* is gone now that every entry point normalises here instead.
|
|
291
|
+
*
|
|
292
|
+
* Prefix-only: strips ONE leading `"{contentRootName}/"` if present (exact
|
|
293
|
+
* string match against the full, possibly multi-segment, `contentRootName`
|
|
294
|
+
* — never `path.basename()`, which would break a multi-segment root like
|
|
295
|
+
* "cms/content"), otherwise returns the path unchanged. So both prefixed
|
|
296
|
+
* (production/editor) and unprefixed (existing store tests, adopters
|
|
297
|
+
* calling SchemaOps directly) input work. Deliberately does NOT touch the
|
|
298
|
+
* bare root-collection sentinel (`collectionPath === this.contentRootName`,
|
|
299
|
+
* no trailing segment): that has no trailing "/" to match, so it passes
|
|
300
|
+
* through unchanged and the `=== this.contentRootName` checks in
|
|
301
|
+
* `updateCollectionInner`/`updateOrderInner` keep working.
|
|
302
|
+
*
|
|
303
|
+
* CALL EXACTLY ONCE PER ENTRY POINT — it is NOT idempotent, and an earlier
|
|
304
|
+
* version of this comment claimed it was. A sub-collection literally named
|
|
305
|
+
* after the content root defeats the "already normalised" reasoning: with
|
|
306
|
+
* `contentRoot: 'content'`, "content/content/x" normalises to "content/x"
|
|
307
|
+
* and again to "x", and "content/content" normalises to "content", which
|
|
308
|
+
* the `=== this.contentRootName` checks above then treat as the ROOT
|
|
309
|
+
* collection — so a second call mutates a DIFFERENT collection rather than
|
|
310
|
+
* reporting not-found. Every entry point calls this exactly once today, so
|
|
311
|
+
* the ambiguity is unreachable; removing it needs the editor to send an
|
|
312
|
+
* unambiguously-scoped path, tracked in
|
|
313
|
+
* .claude/future-tasks/collection-path-content-root-ambiguity.md.
|
|
314
|
+
*
|
|
315
|
+
* A future public method that accepts a logical collection path must call
|
|
316
|
+
* this first, and only once, the same way every existing one does — that is
|
|
317
|
+
* the "safe by construction" contract this boundary is meant to provide.
|
|
318
|
+
*/
|
|
319
|
+
normalizeCollectionPath(collectionPath) {
|
|
320
|
+
return createLogicalPath(stripContentRootPrefix(collectionPath, this.contentRootName));
|
|
321
|
+
}
|
|
322
|
+
// --------------------------------------------------------------------------
|
|
261
323
|
// Validation Helpers
|
|
262
324
|
// --------------------------------------------------------------------------
|
|
263
325
|
/**
|
|
@@ -298,8 +360,9 @@ export class SchemaOps {
|
|
|
298
360
|
* Read a collection's .collection.json file
|
|
299
361
|
*/
|
|
300
362
|
async readCollectionMeta(collectionPath) {
|
|
363
|
+
const normalizedPath = this.normalizeCollectionPath(collectionPath);
|
|
301
364
|
// Resolve logical path to physical path with embedded IDs
|
|
302
|
-
const physicalPath = await resolveCollectionPath(this.contentRoot,
|
|
365
|
+
const physicalPath = await resolveCollectionPath(this.contentRoot, normalizedPath);
|
|
303
366
|
if (!physicalPath) {
|
|
304
367
|
return null;
|
|
305
368
|
}
|
|
@@ -335,7 +398,8 @@ export class SchemaOps {
|
|
|
335
398
|
* Check if a collection is empty (has no content files or child collections)
|
|
336
399
|
*/
|
|
337
400
|
async isCollectionEmpty(collectionPath) {
|
|
338
|
-
const
|
|
401
|
+
const normalizedPath = this.normalizeCollectionPath(collectionPath);
|
|
402
|
+
const physicalPath = await resolveCollectionPath(this.contentRoot, normalizedPath);
|
|
339
403
|
if (!physicalPath) {
|
|
340
404
|
// Collection doesn't exist, consider it empty
|
|
341
405
|
return true;
|
|
@@ -410,7 +474,15 @@ export class SchemaOps {
|
|
|
410
474
|
if (!schemaValidation.valid) {
|
|
411
475
|
throw new Error(schemaValidation.error);
|
|
412
476
|
}
|
|
413
|
-
|
|
477
|
+
// Normalize parentPath (may be content-root-prefixed, e.g. "content/docs",
|
|
478
|
+
// exactly like every other logical collection path the editor sends) —
|
|
479
|
+
// see normalizeCollectionPath's doc comment for why this must happen
|
|
480
|
+
// exactly once, here at the public entry point, before createCollectionInner
|
|
481
|
+
// ever resolves it.
|
|
482
|
+
const normalizedInput = input.parentPath
|
|
483
|
+
? { ...input, parentPath: this.normalizeCollectionPath(input.parentPath) }
|
|
484
|
+
: input;
|
|
485
|
+
const result = await this.withSchemaLock(() => this.createCollectionInner(normalizedInput));
|
|
414
486
|
// Invalidate schema cache after mutation (outside the lock — see withSchemaLock's doc comment)
|
|
415
487
|
await this.invalidateSchemaCache();
|
|
416
488
|
return result;
|
|
@@ -485,7 +557,8 @@ export class SchemaOps {
|
|
|
485
557
|
if (!parseResult.success) {
|
|
486
558
|
throw new Error(`Invalid input: ${parseResult.error.message}`);
|
|
487
559
|
}
|
|
488
|
-
|
|
560
|
+
const normalizedPath = this.normalizeCollectionPath(collectionPath);
|
|
561
|
+
await this.withSchemaLock(() => this.updateCollectionInner(normalizedPath, updates));
|
|
489
562
|
// Invalidate schema cache after mutation (outside the lock — see withSchemaLock's doc comment)
|
|
490
563
|
await this.invalidateSchemaCache();
|
|
491
564
|
}
|
|
@@ -494,11 +567,17 @@ export class SchemaOps {
|
|
|
494
567
|
* read-modify-write. Called directly (not via the public `updateCollection`)
|
|
495
568
|
* by `updateOrderInner` for non-root collections — `withSchemaLock` is NOT
|
|
496
569
|
* re-entrant, so going through the public method there would deadlock.
|
|
570
|
+
*
|
|
571
|
+
* `collectionPath` here is always already normalized (content-root prefix
|
|
572
|
+
* stripped, if it had one) by whichever public method reached this — either
|
|
573
|
+
* `updateCollection` above or `updateOrder` via `updateOrderInner` — so this
|
|
574
|
+
* no longer re-strips the prefix itself; see normalizeCollectionPath's doc
|
|
575
|
+
* comment for the single boundary that owns that now.
|
|
497
576
|
*/
|
|
498
577
|
async updateCollectionInner(collectionPath, updates) {
|
|
499
|
-
// Check if this is the root collection (path equals
|
|
500
|
-
|
|
501
|
-
if (collectionPath === contentRootName) {
|
|
578
|
+
// Check if this is the root collection (path equals the configured content
|
|
579
|
+
// root, e.g. "content" or "cms/content")
|
|
580
|
+
if (collectionPath === this.contentRootName) {
|
|
502
581
|
// Update root collection meta
|
|
503
582
|
let meta = await this.readRootCollectionMeta();
|
|
504
583
|
if (!meta) {
|
|
@@ -514,18 +593,15 @@ export class SchemaOps {
|
|
|
514
593
|
await this.writeRootCollectionMeta(meta);
|
|
515
594
|
return;
|
|
516
595
|
}
|
|
517
|
-
//
|
|
518
|
-
//
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
: collectionPath;
|
|
522
|
-
// Resolve path for regular collection
|
|
523
|
-
const physicalPath = await resolveCollectionPath(this.contentRoot, createLogicalPath(relativePath));
|
|
596
|
+
// Resolve path for regular collection. collectionPath is already
|
|
597
|
+
// normalized (no content-root prefix) by the caller — see this method's
|
|
598
|
+
// doc comment.
|
|
599
|
+
const physicalPath = await resolveCollectionPath(this.contentRoot, collectionPath);
|
|
524
600
|
if (!physicalPath) {
|
|
525
601
|
throw new Error(`Collection not found: ${collectionPath}`);
|
|
526
602
|
}
|
|
527
603
|
// Read existing meta
|
|
528
|
-
const meta = await this.readCollectionMeta(
|
|
604
|
+
const meta = await this.readCollectionMeta(collectionPath);
|
|
529
605
|
if (!meta) {
|
|
530
606
|
throw new Error(`Collection meta not found: ${collectionPath}`);
|
|
531
607
|
}
|
|
@@ -596,7 +672,8 @@ export class SchemaOps {
|
|
|
596
672
|
* Delete a collection (must be empty)
|
|
597
673
|
*/
|
|
598
674
|
async deleteCollection(collectionPath) {
|
|
599
|
-
|
|
675
|
+
const normalizedPath = this.normalizeCollectionPath(collectionPath);
|
|
676
|
+
await this.withSchemaLock(() => this.deleteCollectionInner(normalizedPath));
|
|
600
677
|
// Invalidate schema cache after mutation (outside the lock — see withSchemaLock's doc comment)
|
|
601
678
|
await this.invalidateSchemaCache();
|
|
602
679
|
}
|
|
@@ -633,7 +710,8 @@ export class SchemaOps {
|
|
|
633
710
|
const available = Object.keys(this.entrySchemaRegistry).join(', ');
|
|
634
711
|
throw new Error(`Schema reference "${entryType.schema}" not found. Available: ${available}`);
|
|
635
712
|
}
|
|
636
|
-
|
|
713
|
+
const normalizedPath = this.normalizeCollectionPath(collectionPath);
|
|
714
|
+
await this.withSchemaLock(() => this.addEntryTypeInner(normalizedPath, entryType));
|
|
637
715
|
// Invalidate schema cache after mutation (outside the lock — see withSchemaLock's doc comment)
|
|
638
716
|
await this.invalidateSchemaCache();
|
|
639
717
|
}
|
|
@@ -679,7 +757,8 @@ export class SchemaOps {
|
|
|
679
757
|
const available = Object.keys(this.entrySchemaRegistry).join(', ');
|
|
680
758
|
throw new Error(`Schema reference "${updates.schema}" not found. Available: ${available}`);
|
|
681
759
|
}
|
|
682
|
-
|
|
760
|
+
const normalizedPath = this.normalizeCollectionPath(collectionPath);
|
|
761
|
+
await this.withSchemaLock(() => this.updateEntryTypeInner(normalizedPath, entryTypeName, updates));
|
|
683
762
|
// Invalidate schema cache after mutation (outside the lock — see withSchemaLock's doc comment)
|
|
684
763
|
await this.invalidateSchemaCache();
|
|
685
764
|
}
|
|
@@ -736,7 +815,8 @@ export class SchemaOps {
|
|
|
736
815
|
* Remove an entry type from a collection
|
|
737
816
|
*/
|
|
738
817
|
async removeEntryType(collectionPath, entryTypeName) {
|
|
739
|
-
|
|
818
|
+
const normalizedPath = this.normalizeCollectionPath(collectionPath);
|
|
819
|
+
await this.withSchemaLock(() => this.removeEntryTypeInner(normalizedPath, entryTypeName));
|
|
740
820
|
// Invalidate schema cache after mutation (outside the lock — see withSchemaLock's doc comment)
|
|
741
821
|
await this.invalidateSchemaCache();
|
|
742
822
|
}
|
|
@@ -791,8 +871,9 @@ export class SchemaOps {
|
|
|
791
871
|
* ```
|
|
792
872
|
*/
|
|
793
873
|
async countEntriesUsingType(collectionPath, entryTypeName) {
|
|
874
|
+
const normalizedPath = this.normalizeCollectionPath(collectionPath);
|
|
794
875
|
// Resolve collection physical path
|
|
795
|
-
const physicalPath = await resolveCollectionPath(this.contentRoot,
|
|
876
|
+
const physicalPath = await resolveCollectionPath(this.contentRoot, normalizedPath);
|
|
796
877
|
if (!physicalPath) {
|
|
797
878
|
// Collection doesn't exist yet - return 0
|
|
798
879
|
return 0;
|
|
@@ -840,14 +921,15 @@ export class SchemaOps {
|
|
|
840
921
|
* Update the order of items in a collection
|
|
841
922
|
*/
|
|
842
923
|
async updateOrder(collectionPath, order) {
|
|
843
|
-
|
|
924
|
+
const normalizedPath = this.normalizeCollectionPath(collectionPath);
|
|
925
|
+
await this.withSchemaLock(() => this.updateOrderInner(normalizedPath, order));
|
|
844
926
|
// Invalidate schema cache after mutation (outside the lock — see withSchemaLock's doc comment)
|
|
845
927
|
await this.invalidateSchemaCache();
|
|
846
928
|
}
|
|
847
929
|
async updateOrderInner(collectionPath, order) {
|
|
848
|
-
// Check if this is the root collection (path equals
|
|
849
|
-
|
|
850
|
-
if (collectionPath === contentRootName) {
|
|
930
|
+
// Check if this is the root collection (path equals the configured content
|
|
931
|
+
// root, e.g. "content" or "cms/content")
|
|
932
|
+
if (collectionPath === this.contentRootName) {
|
|
851
933
|
// Update root collection meta
|
|
852
934
|
let meta = await this.readRootCollectionMeta();
|
|
853
935
|
if (!meta) {
|
|
@@ -857,8 +939,9 @@ export class SchemaOps {
|
|
|
857
939
|
await this.writeRootCollectionMeta(meta);
|
|
858
940
|
return;
|
|
859
941
|
}
|
|
860
|
-
// Update regular collection
|
|
861
|
-
//
|
|
942
|
+
// Update regular collection. collectionPath is already normalized (no
|
|
943
|
+
// content-root prefix) by the public updateOrder above. Calls
|
|
944
|
+
// updateCollectionInner DIRECTLY, never the public updateCollection:
|
|
862
945
|
// we're already inside withSchemaLock's critical section here, and
|
|
863
946
|
// withLock is not re-entrant — going through updateCollection would call
|
|
864
947
|
// withSchemaLock again and deadlock waiting on the lock it itself holds.
|