canopycms 0.0.0 → 0.0.2
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/auth/plugin.d.ts +8 -0
- package/dist/auth/plugin.d.ts.map +1 -1
- package/dist/build-mode.d.ts +15 -5
- package/dist/build-mode.d.ts.map +1 -1
- package/dist/build-mode.js +18 -8
- package/dist/build-mode.js.map +1 -1
- package/dist/cli/init.d.ts +2 -2
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +37 -36
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/template-files/ai-config.ts.template +21 -0
- package/dist/cli/template-files/ai-route.ts.template +10 -0
- package/dist/cli/template-files/canopy.ts.template +24 -0
- package/dist/cli/templates.d.ts +5 -1
- package/dist/cli/templates.d.ts.map +1 -1
- package/dist/cli/templates.js +9 -2
- package/dist/cli/templates.js.map +1 -1
- package/dist/config/schemas/config.d.ts +4 -0
- package/dist/config/schemas/config.d.ts.map +1 -1
- package/dist/config/schemas/config.js +2 -0
- package/dist/config/schemas/config.js.map +1 -1
- package/dist/config/types.d.ts +5 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/content-reader.js +2 -2
- package/dist/content-reader.js.map +1 -1
- package/dist/context.js +5 -5
- package/dist/context.js.map +1 -1
- package/dist/operating-mode/client-unsafe-strategy.d.ts.map +1 -1
- package/dist/operating-mode/client-unsafe-strategy.js +15 -18
- package/dist/operating-mode/client-unsafe-strategy.js.map +1 -1
- package/dist/operating-mode/types.d.ts +8 -0
- package/dist/operating-mode/types.d.ts.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -1
- package/package.json +5 -4
- package/src/cli/init.ts +43 -38
- package/dist/__integration__/fixtures/content-seeds.d.ts +0 -43
- package/dist/__integration__/fixtures/content-seeds.d.ts.map +0 -1
- package/dist/__integration__/fixtures/content-seeds.js +0 -99
- package/dist/__integration__/fixtures/content-seeds.js.map +0 -1
- package/dist/__integration__/fixtures/schemas.d.ts +0 -12
- package/dist/__integration__/fixtures/schemas.d.ts.map +0 -1
- package/dist/__integration__/fixtures/schemas.js +0 -65
- package/dist/__integration__/fixtures/schemas.js.map +0 -1
- package/dist/__integration__/test-utils/api-client.d.ts +0 -123
- package/dist/__integration__/test-utils/api-client.d.ts.map +0 -1
- package/dist/__integration__/test-utils/api-client.js +0 -118
- package/dist/__integration__/test-utils/api-client.js.map +0 -1
- package/dist/__integration__/test-utils/multi-user.d.ts +0 -25
- package/dist/__integration__/test-utils/multi-user.d.ts.map +0 -1
- package/dist/__integration__/test-utils/multi-user.js +0 -105
- package/dist/__integration__/test-utils/multi-user.js.map +0 -1
- package/dist/__integration__/test-utils/test-workspace.d.ts +0 -25
- package/dist/__integration__/test-utils/test-workspace.d.ts.map +0 -1
- package/dist/__integration__/test-utils/test-workspace.js +0 -102
- package/dist/__integration__/test-utils/test-workspace.js.map +0 -1
- package/dist/editor/BranchManager.stories.d.ts +0 -8
- package/dist/editor/BranchManager.stories.d.ts.map +0 -1
- package/dist/editor/BranchManager.stories.js +0 -74
- package/dist/editor/BranchManager.stories.js.map +0 -1
- package/dist/editor/CanopyEditor.stories.d.ts +0 -7
- package/dist/editor/CanopyEditor.stories.d.ts.map +0 -1
- package/dist/editor/CanopyEditor.stories.js +0 -99
- package/dist/editor/CanopyEditor.stories.js.map +0 -1
- package/dist/editor/CommentsPanel.stories.d.ts +0 -10
- package/dist/editor/CommentsPanel.stories.d.ts.map +0 -1
- package/dist/editor/CommentsPanel.stories.js +0 -175
- package/dist/editor/CommentsPanel.stories.js.map +0 -1
- package/dist/editor/Editor.stories.d.ts +0 -7
- package/dist/editor/Editor.stories.d.ts.map +0 -1
- package/dist/editor/Editor.stories.js +0 -95
- package/dist/editor/Editor.stories.js.map +0 -1
- package/dist/editor/EditorPanes.stories.d.ts +0 -7
- package/dist/editor/EditorPanes.stories.d.ts.map +0 -1
- package/dist/editor/EditorPanes.stories.js +0 -116
- package/dist/editor/EditorPanes.stories.js.map +0 -1
- package/dist/editor/EntryNavigator.stories.d.ts +0 -8
- package/dist/editor/EntryNavigator.stories.d.ts.map +0 -1
- package/dist/editor/EntryNavigator.stories.js +0 -42
- package/dist/editor/EntryNavigator.stories.js.map +0 -1
- package/dist/editor/FormRenderer.stories.d.ts +0 -7
- package/dist/editor/FormRenderer.stories.d.ts.map +0 -1
- package/dist/editor/FormRenderer.stories.js +0 -115
- package/dist/editor/FormRenderer.stories.js.map +0 -1
- package/dist/editor/GroupManager.stories.d.ts +0 -19
- package/dist/editor/GroupManager.stories.d.ts.map +0 -1
- package/dist/editor/GroupManager.stories.js +0 -265
- package/dist/editor/GroupManager.stories.js.map +0 -1
- package/dist/editor/PermissionManager.stories.d.ts +0 -20
- package/dist/editor/PermissionManager.stories.d.ts.map +0 -1
- package/dist/editor/PermissionManager.stories.js +0 -506
- package/dist/editor/PermissionManager.stories.js.map +0 -1
- package/dist/editor/comments/FieldWrapper.stories.d.ts +0 -10
- package/dist/editor/comments/FieldWrapper.stories.d.ts.map +0 -1
- package/dist/editor/comments/FieldWrapper.stories.js +0 -173
- package/dist/editor/comments/FieldWrapper.stories.js.map +0 -1
- package/dist/editor/fields/BlockField.stories.d.ts +0 -7
- package/dist/editor/fields/BlockField.stories.d.ts.map +0 -1
- package/dist/editor/fields/BlockField.stories.js +0 -50
- package/dist/editor/fields/BlockField.stories.js.map +0 -1
- package/dist/editor/fields/fields.stories.d.ts +0 -8
- package/dist/editor/fields/fields.stories.d.ts.map +0 -1
- package/dist/editor/fields/fields.stories.js +0 -34
- package/dist/editor/fields/fields.stories.js.map +0 -1
- package/dist/test-utils/api-test-helpers.d.ts +0 -238
- package/dist/test-utils/api-test-helpers.d.ts.map +0 -1
- package/dist/test-utils/api-test-helpers.js +0 -347
- package/dist/test-utils/api-test-helpers.js.map +0 -1
- package/dist/test-utils/console-spy.d.ts +0 -56
- package/dist/test-utils/console-spy.d.ts.map +0 -1
- package/dist/test-utils/console-spy.js +0 -81
- package/dist/test-utils/console-spy.js.map +0 -1
- package/dist/test-utils/git-helpers.d.ts +0 -21
- package/dist/test-utils/git-helpers.d.ts.map +0 -1
- package/dist/test-utils/git-helpers.js +0 -23
- package/dist/test-utils/git-helpers.js.map +0 -1
- package/dist/test-utils/index.d.ts +0 -5
- package/dist/test-utils/index.d.ts.map +0 -1
- package/dist/test-utils/index.js +0 -4
- package/dist/test-utils/index.js.map +0 -1
- package/src/__integration__/errors/invalid-content.test.ts +0 -238
- package/src/__integration__/errors/permission-denied.test.ts +0 -220
- package/src/__integration__/fixtures/content-seeds.ts +0 -105
- package/src/__integration__/fixtures/schemas.ts +0 -67
- package/src/__integration__/initialization/prod-sim-init.test.ts +0 -139
- package/src/__integration__/permissions/path-permissions.test.ts +0 -314
- package/src/__integration__/permissions/role-permissions.test.ts +0 -354
- package/src/__integration__/permissions/settings-branch-isolation.test.ts +0 -317
- package/src/__integration__/settings/groups-api.test.ts +0 -403
- package/src/__integration__/test-utils/api-client.ts +0 -167
- package/src/__integration__/test-utils/multi-user.ts +0 -129
- package/src/__integration__/test-utils/test-workspace.ts +0 -130
- package/src/__integration__/user/user-context.test.ts +0 -174
- package/src/__integration__/validation/input-validation.test.ts +0 -166
- package/src/__integration__/workflows/api-editing-workflow.test.ts +0 -244
- package/src/__integration__/workflows/conflict-resolution.test.ts +0 -259
- package/src/__integration__/workflows/editing-workflow.test.ts +0 -205
- package/src/__integration__/workflows/review-workflow.test.ts +0 -260
- package/src/ai/__tests__/build.integration.test.ts +0 -224
- package/src/ai/__tests__/generate.integration.test.ts +0 -495
- package/src/ai/__tests__/handler.integration.test.ts +0 -212
- package/src/ai/__tests__/json-to-markdown.test.ts +0 -553
- package/src/ai/generate.ts +0 -410
- package/src/ai/handler.ts +0 -123
- package/src/ai/index.ts +0 -26
- package/src/ai/json-to-markdown.ts +0 -424
- package/src/ai/resolve-branch.ts +0 -34
- package/src/ai/types.ts +0 -160
- package/src/api/AGENTS.md +0 -81
- package/src/api/__test__/mock-client.ts +0 -404
- package/src/api/assets.test.ts +0 -140
- package/src/api/assets.ts +0 -154
- package/src/api/branch-merge.test.ts +0 -163
- package/src/api/branch-merge.ts +0 -113
- package/src/api/branch-review.test.ts +0 -297
- package/src/api/branch-review.ts +0 -136
- package/src/api/branch-status.test.ts +0 -85
- package/src/api/branch-status.ts +0 -153
- package/src/api/branch-withdraw.test.ts +0 -146
- package/src/api/branch-withdraw.ts +0 -81
- package/src/api/branch-workflow.integration.test.ts +0 -578
- package/src/api/branch.test.ts +0 -620
- package/src/api/branch.ts +0 -492
- package/src/api/client.test.ts +0 -349
- package/src/api/client.ts +0 -506
- package/src/api/comments.test.ts +0 -285
- package/src/api/comments.ts +0 -210
- package/src/api/content.test.ts +0 -345
- package/src/api/content.ts +0 -454
- package/src/api/entries.test.ts +0 -1339
- package/src/api/entries.ts +0 -650
- package/src/api/github-sync.ts +0 -144
- package/src/api/groups.test.ts +0 -1013
- package/src/api/groups.ts +0 -375
- package/src/api/guards.test.ts +0 -533
- package/src/api/guards.ts +0 -271
- package/src/api/index.ts +0 -87
- package/src/api/permissions.test.ts +0 -766
- package/src/api/permissions.ts +0 -334
- package/src/api/reference-options.ts +0 -118
- package/src/api/resolve-references.ts +0 -107
- package/src/api/route-builder.ts +0 -289
- package/src/api/schema.test.ts +0 -840
- package/src/api/schema.ts +0 -936
- package/src/api/security.test.ts +0 -233
- package/src/api/settings-helpers.ts +0 -84
- package/src/api/types.ts +0 -40
- package/src/api/user.test.ts +0 -127
- package/src/api/user.ts +0 -42
- package/src/api/validators.test.ts +0 -275
- package/src/api/validators.ts +0 -176
- package/src/asset-store.test.ts +0 -37
- package/src/asset-store.ts +0 -110
- package/src/auth/cache.ts +0 -7
- package/src/auth/caching-auth-plugin.test.ts +0 -154
- package/src/auth/caching-auth-plugin.ts +0 -109
- package/src/auth/context-helpers.ts +0 -75
- package/src/auth/file-based-auth-cache.test.ts +0 -257
- package/src/auth/file-based-auth-cache.ts +0 -279
- package/src/auth/index.ts +0 -12
- package/src/auth/plugin.ts +0 -51
- package/src/auth/types.ts +0 -38
- package/src/authorization/__tests__/branch.test.ts +0 -260
- package/src/authorization/__tests__/content.test.ts +0 -142
- package/src/authorization/__tests__/path.test.ts +0 -133
- package/src/authorization/__tests__/permissions-loader.test.ts +0 -200
- package/src/authorization/branch.ts +0 -94
- package/src/authorization/content.ts +0 -93
- package/src/authorization/groups/index.ts +0 -11
- package/src/authorization/groups/loader.ts +0 -127
- package/src/authorization/groups/schema.ts +0 -48
- package/src/authorization/helpers.ts +0 -48
- package/src/authorization/index.ts +0 -84
- package/src/authorization/path.ts +0 -112
- package/src/authorization/permissions/index.ts +0 -11
- package/src/authorization/permissions/loader.ts +0 -116
- package/src/authorization/permissions/schema.ts +0 -66
- package/src/authorization/test-utils.ts +0 -15
- package/src/authorization/types.ts +0 -66
- package/src/authorization/validation.test.ts +0 -100
- package/src/authorization/validation.ts +0 -62
- package/src/branch-metadata.test.ts +0 -168
- package/src/branch-metadata.ts +0 -166
- package/src/branch-registry.test.ts +0 -248
- package/src/branch-registry.ts +0 -152
- package/src/branch-schema-cache.test.ts +0 -275
- package/src/branch-schema-cache.ts +0 -189
- package/src/branch-workspace.test.ts +0 -183
- package/src/branch-workspace.ts +0 -124
- package/src/build/generate-ai-content.ts +0 -78
- package/src/build/index.ts +0 -8
- package/src/build-mode.ts +0 -27
- package/src/cli/generate-ai-content.ts +0 -100
- package/src/cli/init.test.ts +0 -240
- package/src/cli/templates/canopy.ts.template +0 -55
- package/src/cli/templates.ts +0 -47
- package/src/client.ts +0 -12
- package/src/comment-store.test.ts +0 -442
- package/src/comment-store.ts +0 -301
- package/src/config/__tests__/config.test.ts +0 -513
- package/src/config/flatten.ts +0 -174
- package/src/config/helpers.ts +0 -167
- package/src/config/index.ts +0 -86
- package/src/config/schemas/collection.ts +0 -67
- package/src/config/schemas/config.ts +0 -77
- package/src/config/schemas/field.ts +0 -108
- package/src/config/schemas/media.ts +0 -27
- package/src/config/schemas/permissions.ts +0 -21
- package/src/config/types.ts +0 -321
- package/src/config/validation.ts +0 -70
- package/src/config-test.ts +0 -65
- package/src/config.ts +0 -11
- package/src/content-id-index.test.ts +0 -512
- package/src/content-id-index.ts +0 -479
- package/src/content-reader.test.ts +0 -478
- package/src/content-reader.ts +0 -214
- package/src/content-store.test.ts +0 -1126
- package/src/content-store.ts +0 -793
- package/src/context.ts +0 -111
- package/src/editor/BranchManager.stories.tsx +0 -80
- package/src/editor/BranchManager.test.tsx +0 -324
- package/src/editor/BranchManager.tsx +0 -461
- package/src/editor/CanopyEditor.stories.tsx +0 -128
- package/src/editor/CanopyEditor.test.tsx +0 -81
- package/src/editor/CanopyEditor.tsx +0 -73
- package/src/editor/CanopyEditorPage.test.tsx +0 -59
- package/src/editor/CanopyEditorPage.tsx +0 -25
- package/src/editor/CommentsPanel.stories.tsx +0 -184
- package/src/editor/CommentsPanel.tsx +0 -338
- package/src/editor/Editor.integration.test.tsx +0 -227
- package/src/editor/Editor.stories.tsx +0 -119
- package/src/editor/Editor.tsx +0 -1221
- package/src/editor/EditorPanes.stories.tsx +0 -256
- package/src/editor/EditorPanes.test.tsx +0 -77
- package/src/editor/EditorPanes.tsx +0 -180
- package/src/editor/EntryNavigator.stories.tsx +0 -65
- package/src/editor/EntryNavigator.test.tsx +0 -598
- package/src/editor/EntryNavigator.tsx +0 -665
- package/src/editor/FormRenderer.stories.tsx +0 -212
- package/src/editor/FormRenderer.test.tsx +0 -194
- package/src/editor/FormRenderer.tsx +0 -432
- package/src/editor/GroupManager.stories.tsx +0 -301
- package/src/editor/GroupManager.test.tsx +0 -682
- package/src/editor/GroupManager.tsx +0 -9
- package/src/editor/PermissionManager.stories.tsx +0 -539
- package/src/editor/PermissionManager.test.tsx +0 -864
- package/src/editor/PermissionManager.tsx +0 -12
- package/src/editor/canopy-path.test.ts +0 -23
- package/src/editor/canopy-path.ts +0 -52
- package/src/editor/client-reference-resolver.ts +0 -118
- package/src/editor/comments/BranchComments.tsx +0 -93
- package/src/editor/comments/EntryComments.tsx +0 -94
- package/src/editor/comments/FieldWrapper.stories.tsx +0 -210
- package/src/editor/comments/FieldWrapper.tsx +0 -129
- package/src/editor/comments/InlineCommentThread.test.tsx +0 -384
- package/src/editor/comments/InlineCommentThread.tsx +0 -246
- package/src/editor/comments/ThreadCarousel.test.tsx +0 -393
- package/src/editor/comments/ThreadCarousel.tsx +0 -525
- package/src/editor/components/ConfirmDeleteModal.tsx +0 -49
- package/src/editor/components/EditorContext.tsx +0 -49
- package/src/editor/components/EditorFooter.tsx +0 -47
- package/src/editor/components/EditorHeader.tsx +0 -492
- package/src/editor/components/EditorSidebar.tsx +0 -193
- package/src/editor/components/EntryCreateModal.tsx +0 -193
- package/src/editor/components/RenameEntryModal.tsx +0 -152
- package/src/editor/components/UserBadge.test.tsx +0 -274
- package/src/editor/components/UserBadge.tsx +0 -240
- package/src/editor/components/index.ts +0 -6
- package/src/editor/context/ApiClientContext.tsx +0 -56
- package/src/editor/context/EditorStateContext.tsx +0 -221
- package/src/editor/context/index.ts +0 -40
- package/src/editor/editor-config.test.ts +0 -385
- package/src/editor/editor-config.ts +0 -94
- package/src/editor/editor-utils.test.ts +0 -772
- package/src/editor/editor-utils.ts +0 -303
- package/src/editor/env.ts +0 -4
- package/src/editor/fields/BlockField.stories.tsx +0 -79
- package/src/editor/fields/BlockField.tsx +0 -267
- package/src/editor/fields/CodeField.tsx +0 -41
- package/src/editor/fields/MarkdownField.tsx +0 -205
- package/src/editor/fields/ObjectField.tsx +0 -71
- package/src/editor/fields/ReferenceField.tsx +0 -138
- package/src/editor/fields/SelectField.tsx +0 -76
- package/src/editor/fields/TextField.tsx +0 -35
- package/src/editor/fields/ToggleField.tsx +0 -37
- package/src/editor/fields/fields.stories.tsx +0 -40
- package/src/editor/group-manager/ExternalGroupsTab.tsx +0 -114
- package/src/editor/group-manager/GroupCard.tsx +0 -102
- package/src/editor/group-manager/GroupForm.tsx +0 -66
- package/src/editor/group-manager/InternalGroupsTab.tsx +0 -147
- package/src/editor/group-manager/MemberList.tsx +0 -184
- package/src/editor/group-manager/hooks/useExternalGroupSearch.ts +0 -63
- package/src/editor/group-manager/hooks/useGroupState.ts +0 -134
- package/src/editor/group-manager/hooks/useUserSearch.ts +0 -84
- package/src/editor/group-manager/index.tsx +0 -210
- package/src/editor/group-manager/types.ts +0 -28
- package/src/editor/hooks/README.md +0 -26
- package/src/editor/hooks/__test__/test-utils.tsx +0 -183
- package/src/editor/hooks/index.ts +0 -23
- package/src/editor/hooks/useBranchActions.test.tsx +0 -267
- package/src/editor/hooks/useBranchActions.tsx +0 -121
- package/src/editor/hooks/useBranchManager.test.tsx +0 -391
- package/src/editor/hooks/useBranchManager.tsx +0 -326
- package/src/editor/hooks/useCommentSystem.test.ts +0 -615
- package/src/editor/hooks/useCommentSystem.ts +0 -347
- package/src/editor/hooks/useDraftManager.test.ts +0 -375
- package/src/editor/hooks/useDraftManager.ts +0 -259
- package/src/editor/hooks/useEditorLayout.test.ts +0 -147
- package/src/editor/hooks/useEditorLayout.ts +0 -67
- package/src/editor/hooks/useEntryManager.test.ts +0 -588
- package/src/editor/hooks/useEntryManager.ts +0 -387
- package/src/editor/hooks/useGroupManager.test.ts +0 -277
- package/src/editor/hooks/useGroupManager.ts +0 -139
- package/src/editor/hooks/usePermissionManager.test.ts +0 -211
- package/src/editor/hooks/usePermissionManager.ts +0 -113
- package/src/editor/hooks/useReferenceResolution.ts +0 -248
- package/src/editor/hooks/useSchemaManager.test.ts +0 -370
- package/src/editor/hooks/useSchemaManager.ts +0 -310
- package/src/editor/hooks/useUserContext.tsx +0 -57
- package/src/editor/hooks/useUserMetadata.test.ts +0 -191
- package/src/editor/hooks/useUserMetadata.ts +0 -71
- package/src/editor/permission-manager/GroupSelector.tsx +0 -73
- package/src/editor/permission-manager/PermissionEditor.tsx +0 -321
- package/src/editor/permission-manager/PermissionLevelBadge.tsx +0 -53
- package/src/editor/permission-manager/PermissionTree.tsx +0 -237
- package/src/editor/permission-manager/UserSelector.tsx +0 -95
- package/src/editor/permission-manager/constants.tsx +0 -18
- package/src/editor/permission-manager/hooks/useGroupsAndUsers.ts +0 -153
- package/src/editor/permission-manager/hooks/usePermissionTree.ts +0 -200
- package/src/editor/permission-manager/index.tsx +0 -294
- package/src/editor/permission-manager/types.ts +0 -58
- package/src/editor/permission-manager/utils.ts +0 -179
- package/src/editor/preview-bridge.test.tsx +0 -50
- package/src/editor/preview-bridge.tsx +0 -294
- package/src/editor/schema-editor/CollectionEditor.test.tsx +0 -238
- package/src/editor/schema-editor/CollectionEditor.tsx +0 -520
- package/src/editor/schema-editor/EntryTypeEditor.test.tsx +0 -215
- package/src/editor/schema-editor/EntryTypeEditor.tsx +0 -367
- package/src/editor/schema-editor/index.ts +0 -19
- package/src/editor/setup-test-dom.ts +0 -10
- package/src/editor/test-setup.ts +0 -33
- package/src/editor/theme.tsx +0 -119
- package/src/editor/utils/env.ts +0 -39
- package/src/entry-schema-registry.test.ts +0 -281
- package/src/entry-schema-registry.ts +0 -121
- package/src/entry-schema.ts +0 -84
- package/src/git-manager.test.ts +0 -552
- package/src/git-manager.ts +0 -667
- package/src/github-service.test.ts +0 -312
- package/src/github-service.ts +0 -295
- package/src/http/handler.test.ts +0 -275
- package/src/http/handler.ts +0 -280
- package/src/http/index.ts +0 -11
- package/src/http/router.ts +0 -164
- package/src/http/types.ts +0 -44
- package/src/id.test.ts +0 -48
- package/src/id.ts +0 -22
- package/src/index.ts +0 -26
- package/src/operating-mode/__tests__/strategies.test.ts +0 -511
- package/src/operating-mode/client-safe-strategy.ts +0 -184
- package/src/operating-mode/client-unsafe-strategy.ts +0 -303
- package/src/operating-mode/client.ts +0 -13
- package/src/operating-mode/index.ts +0 -34
- package/src/operating-mode/types.ts +0 -186
- package/src/paths/__tests__/branch.test.ts +0 -53
- package/src/paths/__tests__/normalize.test.ts +0 -141
- package/src/paths/__tests__/resolve.test.ts +0 -207
- package/src/paths/__tests__/validation.test.ts +0 -61
- package/src/paths/branch.ts +0 -115
- package/src/paths/index.ts +0 -73
- package/src/paths/normalize-server.ts +0 -40
- package/src/paths/normalize.ts +0 -107
- package/src/paths/resolve.ts +0 -61
- package/src/paths/test-utils.ts +0 -37
- package/src/paths/types.ts +0 -68
- package/src/paths/validation.test.ts +0 -480
- package/src/paths/validation.ts +0 -391
- package/src/reference-resolver.test.ts +0 -107
- package/src/reference-resolver.ts +0 -157
- package/src/schema/index.ts +0 -29
- package/src/schema/meta-loader.ts +0 -366
- package/src/schema/resolver.ts +0 -83
- package/src/schema/schema-store-types.ts +0 -56
- package/src/schema/schema-store.test.ts +0 -816
- package/src/schema/schema-store.ts +0 -795
- package/src/schema/types.ts +0 -33
- package/src/schema-meta-loader.test.ts +0 -447
- package/src/server.ts +0 -15
- package/src/services.test.ts +0 -559
- package/src/services.ts +0 -373
- package/src/settings-branch-utils.ts +0 -53
- package/src/settings-workspace.ts +0 -156
- package/src/task-queue/README.md +0 -144
- package/src/task-queue/index.ts +0 -14
- package/src/task-queue/task-queue.test.ts +0 -524
- package/src/task-queue/task-queue.ts +0 -514
- package/src/task-queue/types.ts +0 -41
- package/src/test-utils/api-test-helpers.ts +0 -445
- package/src/test-utils/console-spy.test.ts +0 -14
- package/src/test-utils/console-spy.ts +0 -125
- package/src/test-utils/git-helpers.ts +0 -31
- package/src/test-utils/index.ts +0 -4
- package/src/types.ts +0 -54
- package/src/user.ts +0 -118
- package/src/utils/debug.test.ts +0 -114
- package/src/utils/debug.ts +0 -127
- package/src/utils/error.test.ts +0 -92
- package/src/utils/error.ts +0 -83
- package/src/utils/format.ts +0 -12
- package/src/validation/__tests__/field-traversal.test.ts +0 -263
- package/src/validation/deletion-checker.ts +0 -234
- package/src/validation/field-traversal.ts +0 -146
- package/src/validation/reference-validator.ts +0 -168
- package/src/worker/cms-worker-rebase.test.ts +0 -473
- package/src/worker/cms-worker.ts +0 -777
- package/src/worker/integration.test.ts +0 -289
- package/src/worker/task-queue-config.ts +0 -25
- package/src/worker/task-queue.test.ts +0 -452
- package/src/worker/task-queue.ts +0 -58
- /package/{src/cli/templates → dist/cli/template-files}/Dockerfile.cms.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/canopycms.config.ts.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/deploy-cms.yml.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/edit-page.tsx.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/route.ts.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/schemas.ts.template +0 -0
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* PermissionManager - Main component for managing path-based permissions.
|
|
5
|
-
*
|
|
6
|
-
* This component provides a tree-based UI for configuring read/edit/review
|
|
7
|
-
* permissions on content paths. Supports both schema-based and collection-based
|
|
8
|
-
* tree building.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import React, { useState, useCallback } from 'react'
|
|
12
|
-
import { Alert, Button, Group, Loader, ScrollArea, Stack, Text } from '@mantine/core'
|
|
13
|
-
import { IconAlertCircle } from '@tabler/icons-react'
|
|
14
|
-
import type { PermissionManagerProps, PermissionLevel } from './types'
|
|
15
|
-
import { usePermissionTree } from './hooks/usePermissionTree'
|
|
16
|
-
import { useGroupsAndUsers } from './hooks/useGroupsAndUsers'
|
|
17
|
-
import { PermissionTree } from './PermissionTree'
|
|
18
|
-
import { findTreeNode } from './utils'
|
|
19
|
-
|
|
20
|
-
export const PermissionManager: React.FC<PermissionManagerProps> = ({
|
|
21
|
-
collections,
|
|
22
|
-
contentRoot = 'content',
|
|
23
|
-
permissions,
|
|
24
|
-
canEdit,
|
|
25
|
-
onSave,
|
|
26
|
-
onSearchUsers,
|
|
27
|
-
onGetUserMetadata,
|
|
28
|
-
onListGroups,
|
|
29
|
-
onClose: __,
|
|
30
|
-
loading = false,
|
|
31
|
-
contentTree,
|
|
32
|
-
}) => {
|
|
33
|
-
const [isSaving, setIsSaving] = useState(false)
|
|
34
|
-
const [error, setError] = useState<string | null>(null)
|
|
35
|
-
const [activeLevel, setActiveLevel] = useState<PermissionLevel>('read')
|
|
36
|
-
|
|
37
|
-
// Tree state management
|
|
38
|
-
const {
|
|
39
|
-
annotatedTree,
|
|
40
|
-
expandedNodes,
|
|
41
|
-
selectedNode,
|
|
42
|
-
localPermissions,
|
|
43
|
-
isDirty,
|
|
44
|
-
toggleNode,
|
|
45
|
-
expandAll,
|
|
46
|
-
collapseAll,
|
|
47
|
-
selectNode,
|
|
48
|
-
updateNodePermission,
|
|
49
|
-
resetPermissions,
|
|
50
|
-
setIsDirty,
|
|
51
|
-
} = usePermissionTree({
|
|
52
|
-
collections,
|
|
53
|
-
contentRoot,
|
|
54
|
-
permissions,
|
|
55
|
-
contentTree,
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
// Groups and user search
|
|
59
|
-
const {
|
|
60
|
-
groupSelectData,
|
|
61
|
-
filteredGroups,
|
|
62
|
-
groupLoadError,
|
|
63
|
-
groupSearchQuery,
|
|
64
|
-
showGroupSearch,
|
|
65
|
-
setGroupSearchQuery,
|
|
66
|
-
setShowGroupSearch,
|
|
67
|
-
clearGroupLoadError,
|
|
68
|
-
userSearchResults,
|
|
69
|
-
isSearchingUsers,
|
|
70
|
-
userSearchQuery,
|
|
71
|
-
showUserSearch,
|
|
72
|
-
userSearchError,
|
|
73
|
-
setUserSearchQuery,
|
|
74
|
-
toggleUserSearch,
|
|
75
|
-
} = useGroupsAndUsers({
|
|
76
|
-
onListGroups,
|
|
77
|
-
onSearchUsers,
|
|
78
|
-
canEdit,
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
// Save handler
|
|
82
|
-
const handleSave = useCallback(async () => {
|
|
83
|
-
if (!onSave) return
|
|
84
|
-
|
|
85
|
-
setIsSaving(true)
|
|
86
|
-
setError(null)
|
|
87
|
-
try {
|
|
88
|
-
await onSave(localPermissions)
|
|
89
|
-
setIsDirty(false)
|
|
90
|
-
} catch (err) {
|
|
91
|
-
setError(err instanceof Error ? err.message : 'Failed to save permissions')
|
|
92
|
-
} finally {
|
|
93
|
-
setIsSaving(false)
|
|
94
|
-
}
|
|
95
|
-
}, [onSave, localPermissions, setIsDirty])
|
|
96
|
-
|
|
97
|
-
// Discard handler
|
|
98
|
-
const handleDiscard = useCallback(() => {
|
|
99
|
-
resetPermissions()
|
|
100
|
-
setError(null)
|
|
101
|
-
}, [resetPermissions])
|
|
102
|
-
|
|
103
|
-
// User add handler with search state cleanup
|
|
104
|
-
const handleAddUser = useCallback(
|
|
105
|
-
(nodePath: string, level: PermissionLevel, userId: string) => {
|
|
106
|
-
const treeNode = findTreeNode(annotatedTree, nodePath)
|
|
107
|
-
const permissionPath = treeNode?.type === 'folder' ? `${nodePath}/**` : nodePath
|
|
108
|
-
const existingPerm = localPermissions.find((p) => p.path === permissionPath)
|
|
109
|
-
const currentTarget = existingPerm?.[level]
|
|
110
|
-
const currentUsers = currentTarget?.allowedUsers ?? []
|
|
111
|
-
|
|
112
|
-
if (!currentUsers.includes(userId)) {
|
|
113
|
-
updateNodePermission(nodePath, level, {
|
|
114
|
-
allowedUsers: [...currentUsers, userId],
|
|
115
|
-
allowedGroups: currentTarget?.allowedGroups,
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Clear search state
|
|
120
|
-
setUserSearchQuery('')
|
|
121
|
-
toggleUserSearch(false)
|
|
122
|
-
},
|
|
123
|
-
[annotatedTree, localPermissions, updateNodePermission, setUserSearchQuery, toggleUserSearch],
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
// User remove handler
|
|
127
|
-
const handleRemoveUser = useCallback(
|
|
128
|
-
(nodePath: string, level: PermissionLevel, userId: string) => {
|
|
129
|
-
const treeNode = findTreeNode(annotatedTree, nodePath)
|
|
130
|
-
const permissionPath = treeNode?.type === 'folder' ? `${nodePath}/**` : nodePath
|
|
131
|
-
const existingPerm = localPermissions.find((p) => p.path === permissionPath)
|
|
132
|
-
const currentTarget = existingPerm?.[level]
|
|
133
|
-
if (!currentTarget) return
|
|
134
|
-
|
|
135
|
-
updateNodePermission(nodePath, level, {
|
|
136
|
-
allowedUsers: (currentTarget.allowedUsers ?? []).filter((u) => u !== userId),
|
|
137
|
-
allowedGroups: currentTarget.allowedGroups,
|
|
138
|
-
})
|
|
139
|
-
},
|
|
140
|
-
[annotatedTree, localPermissions, updateNodePermission],
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
// Group add handler
|
|
144
|
-
const handleAddGroup = useCallback(
|
|
145
|
-
(nodePath: string, level: PermissionLevel, groupId: string) => {
|
|
146
|
-
const treeNode = findTreeNode(annotatedTree, nodePath)
|
|
147
|
-
const permissionPath = treeNode?.type === 'folder' ? `${nodePath}/**` : nodePath
|
|
148
|
-
const existingPerm = localPermissions.find((p) => p.path === permissionPath)
|
|
149
|
-
const currentTarget = existingPerm?.[level]
|
|
150
|
-
const currentGroups = currentTarget?.allowedGroups ?? []
|
|
151
|
-
|
|
152
|
-
if (!currentGroups.includes(groupId)) {
|
|
153
|
-
updateNodePermission(nodePath, level, {
|
|
154
|
-
allowedGroups: [...currentGroups, groupId],
|
|
155
|
-
allowedUsers: currentTarget?.allowedUsers,
|
|
156
|
-
})
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
setGroupSearchQuery('')
|
|
160
|
-
setShowGroupSearch(false)
|
|
161
|
-
},
|
|
162
|
-
[
|
|
163
|
-
annotatedTree,
|
|
164
|
-
localPermissions,
|
|
165
|
-
updateNodePermission,
|
|
166
|
-
setGroupSearchQuery,
|
|
167
|
-
setShowGroupSearch,
|
|
168
|
-
],
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
// Group remove handler
|
|
172
|
-
const handleRemoveGroup = useCallback(
|
|
173
|
-
(nodePath: string, level: PermissionLevel, groupId: string) => {
|
|
174
|
-
const treeNode = findTreeNode(annotatedTree, nodePath)
|
|
175
|
-
const permissionPath = treeNode?.type === 'folder' ? `${nodePath}/**` : nodePath
|
|
176
|
-
const existingPerm = localPermissions.find((p) => p.path === permissionPath)
|
|
177
|
-
const currentTarget = existingPerm?.[level]
|
|
178
|
-
if (!currentTarget) return
|
|
179
|
-
|
|
180
|
-
updateNodePermission(nodePath, level, {
|
|
181
|
-
allowedGroups: (currentTarget.allowedGroups ?? []).filter((g) => g !== groupId),
|
|
182
|
-
allowedUsers: currentTarget.allowedUsers,
|
|
183
|
-
})
|
|
184
|
-
},
|
|
185
|
-
[annotatedTree, localPermissions, updateNodePermission],
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
return (
|
|
189
|
-
<Stack h="100%" style={{ display: 'flex', flexDirection: 'column' }} gap={0}>
|
|
190
|
-
{!canEdit && (
|
|
191
|
-
<Alert icon={<IconAlertCircle size={16} />} color="yellow" mb="sm" title="Read-only">
|
|
192
|
-
You need admin access to edit permissions
|
|
193
|
-
</Alert>
|
|
194
|
-
)}
|
|
195
|
-
|
|
196
|
-
{error && (
|
|
197
|
-
<Alert
|
|
198
|
-
icon={<IconAlertCircle size={16} />}
|
|
199
|
-
color="red"
|
|
200
|
-
mb="sm"
|
|
201
|
-
title="Error"
|
|
202
|
-
withCloseButton
|
|
203
|
-
onClose={() => setError(null)}
|
|
204
|
-
>
|
|
205
|
-
{error}
|
|
206
|
-
</Alert>
|
|
207
|
-
)}
|
|
208
|
-
|
|
209
|
-
{groupLoadError && (
|
|
210
|
-
<Alert
|
|
211
|
-
icon={<IconAlertCircle size={16} />}
|
|
212
|
-
color="orange"
|
|
213
|
-
mb="sm"
|
|
214
|
-
title="Warning"
|
|
215
|
-
withCloseButton
|
|
216
|
-
onClose={clearGroupLoadError}
|
|
217
|
-
>
|
|
218
|
-
{groupLoadError}
|
|
219
|
-
</Alert>
|
|
220
|
-
)}
|
|
221
|
-
|
|
222
|
-
<Group gap="xs" pb="sm">
|
|
223
|
-
<Button size="xs" variant="subtle" onClick={expandAll}>
|
|
224
|
-
Expand All
|
|
225
|
-
</Button>
|
|
226
|
-
<Button size="xs" variant="subtle" onClick={collapseAll}>
|
|
227
|
-
Collapse All
|
|
228
|
-
</Button>
|
|
229
|
-
</Group>
|
|
230
|
-
|
|
231
|
-
<ScrollArea style={{ flex: 1 }} pb="md">
|
|
232
|
-
{loading ? (
|
|
233
|
-
<Group justify="center" py="xl">
|
|
234
|
-
<Loader size="md" />
|
|
235
|
-
<Text size="sm" c="dimmed">
|
|
236
|
-
Loading permissions...
|
|
237
|
-
</Text>
|
|
238
|
-
</Group>
|
|
239
|
-
) : (
|
|
240
|
-
<PermissionTree
|
|
241
|
-
node={annotatedTree}
|
|
242
|
-
expandedNodes={expandedNodes}
|
|
243
|
-
selectedNode={selectedNode}
|
|
244
|
-
canEdit={canEdit}
|
|
245
|
-
groups={groupSelectData}
|
|
246
|
-
activeLevel={activeLevel}
|
|
247
|
-
userSearchResults={userSearchResults}
|
|
248
|
-
isSearchingUsers={isSearchingUsers}
|
|
249
|
-
showUserSearch={showUserSearch}
|
|
250
|
-
userSearchQuery={userSearchQuery}
|
|
251
|
-
userSearchError={userSearchError}
|
|
252
|
-
showGroupSearch={showGroupSearch}
|
|
253
|
-
groupSearchQuery={groupSearchQuery}
|
|
254
|
-
filteredGroups={filteredGroups}
|
|
255
|
-
onToggle={toggleNode}
|
|
256
|
-
onSelect={selectNode}
|
|
257
|
-
onSetActiveLevel={setActiveLevel}
|
|
258
|
-
onUpdatePermission={updateNodePermission}
|
|
259
|
-
onSearchUsers={setUserSearchQuery}
|
|
260
|
-
onGetUserMetadata={onGetUserMetadata}
|
|
261
|
-
onToggleUserSearch={toggleUserSearch}
|
|
262
|
-
onAddUser={handleAddUser}
|
|
263
|
-
onRemoveUser={handleRemoveUser}
|
|
264
|
-
onSearchGroups={setGroupSearchQuery}
|
|
265
|
-
onToggleGroupSearch={setShowGroupSearch}
|
|
266
|
-
onAddGroup={handleAddGroup}
|
|
267
|
-
onRemoveGroup={handleRemoveGroup}
|
|
268
|
-
/>
|
|
269
|
-
)}
|
|
270
|
-
</ScrollArea>
|
|
271
|
-
|
|
272
|
-
{canEdit && isDirty && (
|
|
273
|
-
<Group
|
|
274
|
-
justify="flex-end"
|
|
275
|
-
py="sm"
|
|
276
|
-
gap="sm"
|
|
277
|
-
style={{ borderTop: '1px solid var(--mantine-color-gray-3)' }}
|
|
278
|
-
>
|
|
279
|
-
<Button variant="subtle" color="neutral" onClick={handleDiscard} disabled={isSaving}>
|
|
280
|
-
Discard Changes
|
|
281
|
-
</Button>
|
|
282
|
-
<Button onClick={handleSave} loading={isSaving} disabled={isSaving}>
|
|
283
|
-
Save Permissions
|
|
284
|
-
</Button>
|
|
285
|
-
</Group>
|
|
286
|
-
)}
|
|
287
|
-
</Stack>
|
|
288
|
-
)
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Re-export types and components for external use
|
|
292
|
-
export type { PermissionManagerProps, ContentNode, TreeNode } from './types'
|
|
293
|
-
export { usePermissionTree } from './hooks/usePermissionTree'
|
|
294
|
-
export { useGroupsAndUsers } from './hooks/useGroupsAndUsers'
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Types for PermissionManager component
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { PathPermission, PermissionLevel, PermissionTarget } from '../../config'
|
|
6
|
-
import type { UserSearchResult, GroupMetadata } from '../../auth/types'
|
|
7
|
-
import type { EditorCollection } from '../Editor'
|
|
8
|
-
|
|
9
|
-
export interface PermissionManagerProps {
|
|
10
|
-
/** Collections from API */
|
|
11
|
-
collections?: EditorCollection[]
|
|
12
|
-
/** Content root path (default: 'content') */
|
|
13
|
-
contentRoot?: string
|
|
14
|
-
/** Current permissions */
|
|
15
|
-
permissions: PathPermission[]
|
|
16
|
-
/** Whether user can edit permissions (admin only) */
|
|
17
|
-
canEdit: boolean
|
|
18
|
-
/** Handler to save updated permissions */
|
|
19
|
-
onSave?: (permissions: PathPermission[]) => Promise<void>
|
|
20
|
-
/** Handler to search users */
|
|
21
|
-
onSearchUsers?: (query: string, limit?: number) => Promise<UserSearchResult[]>
|
|
22
|
-
/** Handler to get user metadata by ID */
|
|
23
|
-
onGetUserMetadata?: (userId: string) => Promise<UserSearchResult | null>
|
|
24
|
-
/** Handler to list groups */
|
|
25
|
-
onListGroups?: () => Promise<GroupMetadata[]>
|
|
26
|
-
/** Close handler */
|
|
27
|
-
onClose?: () => void
|
|
28
|
-
/** Loading state */
|
|
29
|
-
loading?: boolean
|
|
30
|
-
/** Optional: actual filesystem content tree (for entries not in schema) */
|
|
31
|
-
contentTree?: ContentNode
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface ContentNode {
|
|
35
|
-
path: string
|
|
36
|
-
name: string
|
|
37
|
-
type: 'folder' | 'file'
|
|
38
|
-
children?: ContentNode[]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface TreeNode {
|
|
42
|
-
path: string
|
|
43
|
-
name: string
|
|
44
|
-
type: 'folder' | 'file'
|
|
45
|
-
children: TreeNode[]
|
|
46
|
-
/** Permissions directly assigned to this node */
|
|
47
|
-
directPermission?: PathPermission
|
|
48
|
-
/** Permissions inherited from parent */
|
|
49
|
-
inheritedPermission?: PathPermission
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface GroupSelectItem {
|
|
53
|
-
value: string
|
|
54
|
-
label: string
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Re-export for convenience */
|
|
58
|
-
export type { PathPermission, PermissionLevel, PermissionTarget, UserSearchResult, GroupMetadata }
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for PermissionManager
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { TreeNode, ContentNode, PathPermission } from './types'
|
|
6
|
-
import type { EditorCollection } from '../Editor'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Search utility: Find a tree node by exact path match (recursive search).
|
|
10
|
-
* Used to locate nodes when updating permissions.
|
|
11
|
-
*
|
|
12
|
-
* @param node - Root node to start searching from
|
|
13
|
-
* @param path - The exact path to search for
|
|
14
|
-
* @returns The matching TreeNode or null if not found
|
|
15
|
-
*/
|
|
16
|
-
export function findTreeNode(node: TreeNode, path: string): TreeNode | null {
|
|
17
|
-
if (node.path === path) return node
|
|
18
|
-
for (const child of node.children) {
|
|
19
|
-
const found = findTreeNode(child, path)
|
|
20
|
-
if (found) return found
|
|
21
|
-
}
|
|
22
|
-
return null
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Transforms API/editor collections into permission tree nodes.
|
|
27
|
-
*
|
|
28
|
-
* Handles the case where buildEditorCollections returns a structure with the content root
|
|
29
|
-
* as a top-level collection. Avoids double-wrapping by detecting when a collection IS the
|
|
30
|
-
* content root itself and processing its children directly.
|
|
31
|
-
*
|
|
32
|
-
* @param collections - Array of EditorCollection from API or buildEditorCollections
|
|
33
|
-
* @param contentRoot - The content root path (e.g., "content")
|
|
34
|
-
* @param parentPath - Optional parent path for recursive calls
|
|
35
|
-
* @returns Array of TreeNode for the permission tree
|
|
36
|
-
*/
|
|
37
|
-
export function convertCollectionsToTreeNodes(
|
|
38
|
-
collections: EditorCollection[],
|
|
39
|
-
contentRoot: string,
|
|
40
|
-
parentPath?: string,
|
|
41
|
-
): TreeNode[] {
|
|
42
|
-
// Special case: If we're at the top level (no parentPath) and the collections array
|
|
43
|
-
// contains exactly one item that IS the content root itself, skip creating a duplicate
|
|
44
|
-
// node for it and just process its children directly.
|
|
45
|
-
if (!parentPath && collections.length === 1 && collections[0].path === contentRoot) {
|
|
46
|
-
const rootCollection = collections[0]
|
|
47
|
-
return rootCollection.children
|
|
48
|
-
? convertCollectionsToTreeNodes(rootCollection.children, contentRoot, contentRoot)
|
|
49
|
-
: []
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const nodes: TreeNode[] = []
|
|
53
|
-
|
|
54
|
-
for (const collection of collections) {
|
|
55
|
-
// Build the logical path - use collection.path directly since it already includes
|
|
56
|
-
// the content root prefix from buildEditorCollections
|
|
57
|
-
const logicalPath = collection.path
|
|
58
|
-
|
|
59
|
-
const node: TreeNode = {
|
|
60
|
-
path: logicalPath,
|
|
61
|
-
name: collection.label || collection.name,
|
|
62
|
-
type: collection.type === 'collection' ? 'folder' : 'file',
|
|
63
|
-
children: [],
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Recursively process nested collections
|
|
67
|
-
if (collection.children) {
|
|
68
|
-
node.children = convertCollectionsToTreeNodes(collection.children, contentRoot, logicalPath)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
nodes.push(node)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return nodes
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Merges filesystem content into schema tree for files not defined in the schema.
|
|
79
|
-
*
|
|
80
|
-
* Used to add files that exist in the filesystem but aren't explicitly defined in
|
|
81
|
-
* the schema (e.g., entries created manually via the filesystem). Only adds files,
|
|
82
|
-
* not folders - folders are expected to come from the schema.
|
|
83
|
-
*
|
|
84
|
-
* @param schemaNode - TreeNode from schema to merge into (mutated)
|
|
85
|
-
* @param contentNode - Actual filesystem content tree from API
|
|
86
|
-
*/
|
|
87
|
-
export function mergeContentTree(schemaNode: TreeNode, contentNode: ContentNode): void {
|
|
88
|
-
contentNode.children?.forEach((child) => {
|
|
89
|
-
const existing = schemaNode.children.find((n) => n.name === child.name)
|
|
90
|
-
if (existing) {
|
|
91
|
-
// If this is a folder/collection that exists in schema, recursively merge its children
|
|
92
|
-
if (child.type === 'folder' && child.children) {
|
|
93
|
-
mergeContentTree(existing, child)
|
|
94
|
-
}
|
|
95
|
-
} else if (child.type === 'file') {
|
|
96
|
-
// Add file not in schema (e.g., entry created via filesystem)
|
|
97
|
-
schemaNode.children.push({
|
|
98
|
-
path: child.path,
|
|
99
|
-
name: child.name,
|
|
100
|
-
type: child.type,
|
|
101
|
-
children: [],
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Build tree structure from EditorCollections.
|
|
109
|
-
*
|
|
110
|
-
* @param contentTree - Actual filesystem content from API (optional)
|
|
111
|
-
* @param contentRoot - The content root path, defaults to "content"
|
|
112
|
-
* @param collections - Optional EditorCollection[] from API
|
|
113
|
-
* @returns Root TreeNode for the permission tree
|
|
114
|
-
*/
|
|
115
|
-
export function buildTree(
|
|
116
|
-
contentTree: ContentNode | undefined,
|
|
117
|
-
contentRoot = 'content',
|
|
118
|
-
collections?: EditorCollection[],
|
|
119
|
-
): TreeNode {
|
|
120
|
-
const root: TreeNode = {
|
|
121
|
-
path: contentRoot,
|
|
122
|
-
name: contentRoot,
|
|
123
|
-
type: 'folder',
|
|
124
|
-
children: [],
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Build from collections if provided
|
|
128
|
-
if (collections && collections.length > 0) {
|
|
129
|
-
root.children = convertCollectionsToTreeNodes(collections, contentRoot)
|
|
130
|
-
|
|
131
|
-
// Merge contentTree if provided
|
|
132
|
-
if (contentTree) {
|
|
133
|
-
mergeContentTree(root, contentTree)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return root
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Decorates tree with permission data (direct + inherited).
|
|
142
|
-
*
|
|
143
|
-
* Recursively walks the tree and attaches permission information to each node:
|
|
144
|
-
* - directPermission: Exact match for this path (or path/** for folders)
|
|
145
|
-
* - inheritedPermission: Nearest parent's folder wildcard permission (if no direct permission)
|
|
146
|
-
*
|
|
147
|
-
* @param node - TreeNode to annotate
|
|
148
|
-
* @param permissions - Array of PathPermission from API
|
|
149
|
-
* @returns Annotated TreeNode with permission data
|
|
150
|
-
*/
|
|
151
|
-
export function annotateTreeWithPermissions(
|
|
152
|
-
node: TreeNode,
|
|
153
|
-
permissions: PathPermission[],
|
|
154
|
-
): TreeNode {
|
|
155
|
-
const folderPath = node.type === 'folder' ? `${node.path}/**` : node.path
|
|
156
|
-
|
|
157
|
-
// Find direct permission
|
|
158
|
-
const directPerm = permissions.find((p) => p.path === folderPath || p.path === node.path)
|
|
159
|
-
|
|
160
|
-
// Find inherited permission from parent
|
|
161
|
-
let inheritedPerm: PathPermission | undefined
|
|
162
|
-
const pathParts = node.path.split('/')
|
|
163
|
-
for (let i = pathParts.length - 1; i >= 0; i--) {
|
|
164
|
-
const parentPath = pathParts.slice(0, i + 1).join('/')
|
|
165
|
-
const parentFolderPath = `${parentPath}/**`
|
|
166
|
-
const parentPerm = permissions.find((p) => p.path === parentFolderPath)
|
|
167
|
-
if (parentPerm) {
|
|
168
|
-
inheritedPerm = parentPerm
|
|
169
|
-
break
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
...node,
|
|
175
|
-
directPermission: directPerm,
|
|
176
|
-
inheritedPermission: !directPerm ? inheritedPerm : undefined,
|
|
177
|
-
children: node.children.map((child) => annotateTreeWithPermissions(child, permissions)),
|
|
178
|
-
}
|
|
179
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { cleanup, render, waitFor } from '@testing-library/react'
|
|
3
|
-
import { afterEach, describe, expect, it } from 'vitest'
|
|
4
|
-
|
|
5
|
-
import { CANOPY_PREVIEW_MESSAGE, useCanopyPreview } from './preview-bridge'
|
|
6
|
-
|
|
7
|
-
afterEach(() => cleanup())
|
|
8
|
-
|
|
9
|
-
const PreviewValue = ({ initialData, path }: { initialData: { value: string }; path?: string }) => {
|
|
10
|
-
const { data } = useCanopyPreview<{ value: string }>({ initialData, path })
|
|
11
|
-
return <div data-testid="value">{data.value}</div>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe('useCanopyPreview', () => {
|
|
15
|
-
it('uses the current location when path is omitted', async () => {
|
|
16
|
-
window.history.pushState({}, '', '/posts/hello-world?branch=main')
|
|
17
|
-
const { getByTestId } = render(<PreviewValue initialData={{ value: 'initial' }} />)
|
|
18
|
-
|
|
19
|
-
window.dispatchEvent(
|
|
20
|
-
new MessageEvent('message', {
|
|
21
|
-
data: {
|
|
22
|
-
type: CANOPY_PREVIEW_MESSAGE,
|
|
23
|
-
path: '/posts/hello-world?branch=main',
|
|
24
|
-
data: { value: 'updated' },
|
|
25
|
-
},
|
|
26
|
-
}),
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
await waitFor(() => expect(getByTestId('value').textContent).toBe('updated'))
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('prefers the provided path over the current location', async () => {
|
|
33
|
-
window.history.pushState({}, '', '/posts/different?branch=main')
|
|
34
|
-
const { getByTestId } = render(
|
|
35
|
-
<PreviewValue initialData={{ value: 'initial' }} path="/posts/override?branch=main" />,
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
window.dispatchEvent(
|
|
39
|
-
new MessageEvent('message', {
|
|
40
|
-
data: {
|
|
41
|
-
type: CANOPY_PREVIEW_MESSAGE,
|
|
42
|
-
path: '/posts/override?branch=main',
|
|
43
|
-
data: { value: 'updated' },
|
|
44
|
-
},
|
|
45
|
-
}),
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
await waitFor(() => expect(getByTestId('value').textContent).toBe('updated'))
|
|
49
|
-
})
|
|
50
|
-
})
|