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
package/src/api/guards.test.ts
DELETED
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { executeGuards } from './guards'
|
|
4
|
-
import type { GuardId } from './guards'
|
|
5
|
-
import { createMockApiContext, createMockBranchContext, createMockUser } from '../test-utils'
|
|
6
|
-
import type { FlatSchemaItem } from '../config'
|
|
7
|
-
|
|
8
|
-
// ---------------------------------------------------------------------------
|
|
9
|
-
// Helpers
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
|
|
12
|
-
/** Minimal flatSchema for tests requiring schema guard */
|
|
13
|
-
const fakeFlatSchema: FlatSchemaItem[] = [
|
|
14
|
-
{
|
|
15
|
-
type: 'collection',
|
|
16
|
-
name: 'posts',
|
|
17
|
-
logicalPath: 'content/posts' as any,
|
|
18
|
-
entries: [{ name: 'post', format: 'json' as const, schema: [] }],
|
|
19
|
-
},
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
function makeReq(role: 'admin' | 'reviewer' | 'user' = 'user') {
|
|
23
|
-
return { user: createMockUser(role) }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// executeGuards
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
|
|
30
|
-
describe('executeGuards', () => {
|
|
31
|
-
// ========================================================================
|
|
32
|
-
// Empty guard list
|
|
33
|
-
// ========================================================================
|
|
34
|
-
|
|
35
|
-
it('returns empty guard context when no guards are specified', async () => {
|
|
36
|
-
const ctx = createMockApiContext()
|
|
37
|
-
const result = await executeGuards([] as unknown as GuardId[], ctx, makeReq(), {})
|
|
38
|
-
|
|
39
|
-
expect(result.ok).toBe(true)
|
|
40
|
-
if (result.ok) {
|
|
41
|
-
expect(result.guardContext).toEqual({})
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
// ========================================================================
|
|
46
|
-
// Branch guard
|
|
47
|
-
// ========================================================================
|
|
48
|
-
|
|
49
|
-
describe('branch guard', () => {
|
|
50
|
-
it('returns branchContext on success', async () => {
|
|
51
|
-
const bc = createMockBranchContext({ branchName: 'feature/x' })
|
|
52
|
-
const ctx = createMockApiContext({ branchContext: bc })
|
|
53
|
-
|
|
54
|
-
const result = await executeGuards(['branch'] as const, ctx, makeReq(), {
|
|
55
|
-
branch: 'feature/x',
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
expect(result.ok).toBe(true)
|
|
59
|
-
if (result.ok) {
|
|
60
|
-
expect(result.guardContext.branchContext).toBe(bc)
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('returns 404 when branch not found', async () => {
|
|
65
|
-
const ctx = createMockApiContext({ branchContext: null })
|
|
66
|
-
|
|
67
|
-
const result = await executeGuards(['branch'] as const, ctx, makeReq(), {
|
|
68
|
-
branch: 'nonexistent',
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
expect(result.ok).toBe(false)
|
|
72
|
-
if (!result.ok) {
|
|
73
|
-
expect(result.response.status).toBe(404)
|
|
74
|
-
expect(result.response.error).toBe('Branch not found')
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('returns 400 when branch param is missing', async () => {
|
|
79
|
-
const ctx = createMockApiContext()
|
|
80
|
-
|
|
81
|
-
const result = await executeGuards(['branch'] as const, ctx, makeReq(), {})
|
|
82
|
-
|
|
83
|
-
expect(result.ok).toBe(false)
|
|
84
|
-
if (!result.ok) {
|
|
85
|
-
expect(result.response.status).toBe(400)
|
|
86
|
-
expect(result.response.error).toBe('Branch parameter required')
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('returns 400 when branch param is empty string', async () => {
|
|
91
|
-
const ctx = createMockApiContext()
|
|
92
|
-
|
|
93
|
-
const result = await executeGuards(['branch'] as const, ctx, makeReq(), { branch: '' })
|
|
94
|
-
|
|
95
|
-
expect(result.ok).toBe(false)
|
|
96
|
-
if (!result.ok) {
|
|
97
|
-
expect(result.response.status).toBe(400)
|
|
98
|
-
}
|
|
99
|
-
})
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
// ========================================================================
|
|
103
|
-
// BranchAccess guard
|
|
104
|
-
// ========================================================================
|
|
105
|
-
|
|
106
|
-
describe('branchAccess guard', () => {
|
|
107
|
-
it('returns branchContext when access is allowed', async () => {
|
|
108
|
-
const bc = createMockBranchContext({ branchName: 'feature/y' })
|
|
109
|
-
const ctx = createMockApiContext({
|
|
110
|
-
branchContext: bc,
|
|
111
|
-
allowBranchAccess: true,
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
const result = await executeGuards(['branchAccess'] as const, ctx, makeReq(), {
|
|
115
|
-
branch: 'feature/y',
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
expect(result.ok).toBe(true)
|
|
119
|
-
if (result.ok) {
|
|
120
|
-
expect(result.guardContext.branchContext).toBe(bc)
|
|
121
|
-
}
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('returns 403 when access is denied', async () => {
|
|
125
|
-
const bc = createMockBranchContext({ branchName: 'restricted' })
|
|
126
|
-
const ctx = createMockApiContext({
|
|
127
|
-
branchContext: bc,
|
|
128
|
-
allowBranchAccess: false,
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
const result = await executeGuards(['branchAccess'] as const, ctx, makeReq(), {
|
|
132
|
-
branch: 'restricted',
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
expect(result.ok).toBe(false)
|
|
136
|
-
if (!result.ok) {
|
|
137
|
-
expect(result.response.status).toBe(403)
|
|
138
|
-
}
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
it('returns 404 when branch not found', async () => {
|
|
142
|
-
const ctx = createMockApiContext({ branchContext: null })
|
|
143
|
-
|
|
144
|
-
const result = await executeGuards(['branchAccess'] as const, ctx, makeReq(), {
|
|
145
|
-
branch: 'gone',
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
expect(result.ok).toBe(false)
|
|
149
|
-
if (!result.ok) {
|
|
150
|
-
expect(result.response.status).toBe(404)
|
|
151
|
-
expect(result.response.error).toBe('Branch not found')
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
// ========================================================================
|
|
157
|
-
// Schema guard
|
|
158
|
-
// ========================================================================
|
|
159
|
-
|
|
160
|
-
describe('schema guard', () => {
|
|
161
|
-
it('returns branchContext with flatSchema on success', async () => {
|
|
162
|
-
const bc = { ...createMockBranchContext(), flatSchema: fakeFlatSchema }
|
|
163
|
-
const ctx = createMockApiContext({ branchContext: bc })
|
|
164
|
-
|
|
165
|
-
const result = await executeGuards(['schema'] as const, ctx, makeReq(), { branch: 'main' })
|
|
166
|
-
|
|
167
|
-
expect(result.ok).toBe(true)
|
|
168
|
-
if (result.ok) {
|
|
169
|
-
expect(result.guardContext.branchContext.flatSchema).toBe(fakeFlatSchema)
|
|
170
|
-
}
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
it('returns 500 when flatSchema is missing', async () => {
|
|
174
|
-
// Branch exists but flatSchema was not loaded
|
|
175
|
-
const bc = createMockBranchContext()
|
|
176
|
-
const ctx = createMockApiContext({ branchContext: bc })
|
|
177
|
-
|
|
178
|
-
const result = await executeGuards(['schema'] as const, ctx, makeReq(), { branch: 'main' })
|
|
179
|
-
|
|
180
|
-
expect(result.ok).toBe(false)
|
|
181
|
-
if (!result.ok) {
|
|
182
|
-
expect(result.response.status).toBe(500)
|
|
183
|
-
expect(result.response.error).toBe('Schema not loaded for branch')
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('returns 404 when branch not found', async () => {
|
|
188
|
-
const ctx = createMockApiContext({ branchContext: null })
|
|
189
|
-
|
|
190
|
-
const result = await executeGuards(['schema'] as const, ctx, makeReq(), { branch: 'missing' })
|
|
191
|
-
|
|
192
|
-
expect(result.ok).toBe(false)
|
|
193
|
-
if (!result.ok) {
|
|
194
|
-
expect(result.response.status).toBe(404)
|
|
195
|
-
}
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
// ========================================================================
|
|
200
|
-
// BranchAccessWithSchema guard
|
|
201
|
-
// ========================================================================
|
|
202
|
-
|
|
203
|
-
describe('branchAccessWithSchema guard', () => {
|
|
204
|
-
it('returns branchContext with flatSchema when access is allowed', async () => {
|
|
205
|
-
const bc = { ...createMockBranchContext(), flatSchema: fakeFlatSchema }
|
|
206
|
-
const ctx = createMockApiContext({
|
|
207
|
-
branchContext: bc,
|
|
208
|
-
allowBranchAccess: true,
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
const result = await executeGuards(['branchAccessWithSchema'] as const, ctx, makeReq(), {
|
|
212
|
-
branch: 'main',
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
expect(result.ok).toBe(true)
|
|
216
|
-
if (result.ok) {
|
|
217
|
-
expect(result.guardContext.branchContext.flatSchema).toBe(fakeFlatSchema)
|
|
218
|
-
}
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
it('returns 403 when access is denied', async () => {
|
|
222
|
-
const bc = { ...createMockBranchContext(), flatSchema: fakeFlatSchema }
|
|
223
|
-
const ctx = createMockApiContext({
|
|
224
|
-
branchContext: bc,
|
|
225
|
-
allowBranchAccess: false,
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
const result = await executeGuards(['branchAccessWithSchema'] as const, ctx, makeReq(), {
|
|
229
|
-
branch: 'main',
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
expect(result.ok).toBe(false)
|
|
233
|
-
if (!result.ok) {
|
|
234
|
-
expect(result.response.status).toBe(403)
|
|
235
|
-
}
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
it('returns 500 when flatSchema is missing despite access being allowed', async () => {
|
|
239
|
-
const bc = createMockBranchContext() // no flatSchema
|
|
240
|
-
const ctx = createMockApiContext({
|
|
241
|
-
branchContext: bc,
|
|
242
|
-
allowBranchAccess: true,
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
const result = await executeGuards(['branchAccessWithSchema'] as const, ctx, makeReq(), {
|
|
246
|
-
branch: 'main',
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
expect(result.ok).toBe(false)
|
|
250
|
-
if (!result.ok) {
|
|
251
|
-
expect(result.response.status).toBe(500)
|
|
252
|
-
expect(result.response.error).toBe('Schema not loaded for branch')
|
|
253
|
-
}
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
it('returns 404 when branch not found', async () => {
|
|
257
|
-
const ctx = createMockApiContext({ branchContext: null })
|
|
258
|
-
|
|
259
|
-
const result = await executeGuards(['branchAccessWithSchema'] as const, ctx, makeReq(), {
|
|
260
|
-
branch: 'nope',
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
expect(result.ok).toBe(false)
|
|
264
|
-
if (!result.ok) {
|
|
265
|
-
expect(result.response.status).toBe(404)
|
|
266
|
-
}
|
|
267
|
-
})
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
// ========================================================================
|
|
271
|
-
// Role guards: admin, reviewer, privileged
|
|
272
|
-
// ========================================================================
|
|
273
|
-
|
|
274
|
-
describe('admin guard', () => {
|
|
275
|
-
it('passes for admin users', async () => {
|
|
276
|
-
const ctx = createMockApiContext()
|
|
277
|
-
const result = await executeGuards(['admin'] as const, ctx, makeReq('admin'), {})
|
|
278
|
-
|
|
279
|
-
expect(result.ok).toBe(true)
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
it('returns 403 for reviewer users', async () => {
|
|
283
|
-
const ctx = createMockApiContext()
|
|
284
|
-
const result = await executeGuards(['admin'] as const, ctx, makeReq('reviewer'), {})
|
|
285
|
-
|
|
286
|
-
expect(result.ok).toBe(false)
|
|
287
|
-
if (!result.ok) {
|
|
288
|
-
expect(result.response.status).toBe(403)
|
|
289
|
-
expect(result.response.error).toBe('Admin access required')
|
|
290
|
-
}
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
it('returns 403 for regular users', async () => {
|
|
294
|
-
const ctx = createMockApiContext()
|
|
295
|
-
const result = await executeGuards(['admin'] as const, ctx, makeReq('user'), {})
|
|
296
|
-
|
|
297
|
-
expect(result.ok).toBe(false)
|
|
298
|
-
if (!result.ok) {
|
|
299
|
-
expect(result.response.status).toBe(403)
|
|
300
|
-
expect(result.response.error).toBe('Admin access required')
|
|
301
|
-
}
|
|
302
|
-
})
|
|
303
|
-
})
|
|
304
|
-
|
|
305
|
-
describe('reviewer guard', () => {
|
|
306
|
-
it('passes for admin users (admins can do everything)', async () => {
|
|
307
|
-
const ctx = createMockApiContext()
|
|
308
|
-
const result = await executeGuards(['reviewer'] as const, ctx, makeReq('admin'), {})
|
|
309
|
-
|
|
310
|
-
expect(result.ok).toBe(true)
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
it('passes for reviewer users', async () => {
|
|
314
|
-
const ctx = createMockApiContext()
|
|
315
|
-
const result = await executeGuards(['reviewer'] as const, ctx, makeReq('reviewer'), {})
|
|
316
|
-
|
|
317
|
-
expect(result.ok).toBe(true)
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
it('returns 403 for regular users', async () => {
|
|
321
|
-
const ctx = createMockApiContext()
|
|
322
|
-
const result = await executeGuards(['reviewer'] as const, ctx, makeReq('user'), {})
|
|
323
|
-
|
|
324
|
-
expect(result.ok).toBe(false)
|
|
325
|
-
if (!result.ok) {
|
|
326
|
-
expect(result.response.status).toBe(403)
|
|
327
|
-
expect(result.response.error).toBe('Reviewer access required')
|
|
328
|
-
}
|
|
329
|
-
})
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
describe('privileged guard', () => {
|
|
333
|
-
it('passes for admin users', async () => {
|
|
334
|
-
const ctx = createMockApiContext()
|
|
335
|
-
const result = await executeGuards(['privileged'] as const, ctx, makeReq('admin'), {})
|
|
336
|
-
|
|
337
|
-
expect(result.ok).toBe(true)
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
it('passes for reviewer users', async () => {
|
|
341
|
-
const ctx = createMockApiContext()
|
|
342
|
-
const result = await executeGuards(['privileged'] as const, ctx, makeReq('reviewer'), {})
|
|
343
|
-
|
|
344
|
-
expect(result.ok).toBe(true)
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
it('returns 403 for regular users', async () => {
|
|
348
|
-
const ctx = createMockApiContext()
|
|
349
|
-
const result = await executeGuards(['privileged'] as const, ctx, makeReq('user'), {})
|
|
350
|
-
|
|
351
|
-
expect(result.ok).toBe(false)
|
|
352
|
-
if (!result.ok) {
|
|
353
|
-
expect(result.response.status).toBe(403)
|
|
354
|
-
expect(result.response.error).toBe('Privileged access required')
|
|
355
|
-
}
|
|
356
|
-
})
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
// ========================================================================
|
|
360
|
-
// Guard chaining
|
|
361
|
-
// ========================================================================
|
|
362
|
-
|
|
363
|
-
describe('guard chaining', () => {
|
|
364
|
-
it('short-circuits on first failing guard', async () => {
|
|
365
|
-
const ctx = createMockApiContext()
|
|
366
|
-
const getBranchContext = ctx.getBranchContext as ReturnType<typeof vi.fn>
|
|
367
|
-
|
|
368
|
-
// admin guard runs first, fails => branch guard never runs
|
|
369
|
-
const result = await executeGuards(['admin', 'branch'] as const, ctx, makeReq('user'), {
|
|
370
|
-
branch: 'main',
|
|
371
|
-
})
|
|
372
|
-
|
|
373
|
-
expect(result.ok).toBe(false)
|
|
374
|
-
if (!result.ok) {
|
|
375
|
-
expect(result.response.status).toBe(403)
|
|
376
|
-
expect(result.response.error).toBe('Admin access required')
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// getBranchContext should never have been called
|
|
380
|
-
expect(getBranchContext).not.toHaveBeenCalled()
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
it('runs all guards when all pass', async () => {
|
|
384
|
-
const bc = { ...createMockBranchContext(), flatSchema: fakeFlatSchema }
|
|
385
|
-
const ctx = createMockApiContext({
|
|
386
|
-
branchContext: bc,
|
|
387
|
-
allowBranchAccess: true,
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
const result = await executeGuards(
|
|
391
|
-
['admin', 'branchAccess'] as const,
|
|
392
|
-
ctx,
|
|
393
|
-
makeReq('admin'),
|
|
394
|
-
{ branch: 'main' },
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
expect(result.ok).toBe(true)
|
|
398
|
-
if (result.ok) {
|
|
399
|
-
expect(result.guardContext.branchContext).toBe(bc)
|
|
400
|
-
}
|
|
401
|
-
})
|
|
402
|
-
|
|
403
|
-
it('combines admin + branchAccess guards correctly', async () => {
|
|
404
|
-
const bc = createMockBranchContext()
|
|
405
|
-
const ctx = createMockApiContext({
|
|
406
|
-
branchContext: bc,
|
|
407
|
-
allowBranchAccess: true,
|
|
408
|
-
})
|
|
409
|
-
|
|
410
|
-
// Admin + branchAccess: admin passes, branchAccess passes
|
|
411
|
-
const result = await executeGuards(
|
|
412
|
-
['admin', 'branchAccess'] as const,
|
|
413
|
-
ctx,
|
|
414
|
-
makeReq('admin'),
|
|
415
|
-
{ branch: 'main' },
|
|
416
|
-
)
|
|
417
|
-
expect(result.ok).toBe(true)
|
|
418
|
-
})
|
|
419
|
-
|
|
420
|
-
it('fails combined guards when second guard fails', async () => {
|
|
421
|
-
const bc = createMockBranchContext()
|
|
422
|
-
const ctx = createMockApiContext({
|
|
423
|
-
branchContext: bc,
|
|
424
|
-
allowBranchAccess: false,
|
|
425
|
-
})
|
|
426
|
-
|
|
427
|
-
// Admin passes, but branchAccess denies
|
|
428
|
-
const result = await executeGuards(
|
|
429
|
-
['admin', 'branchAccess'] as const,
|
|
430
|
-
ctx,
|
|
431
|
-
makeReq('admin'),
|
|
432
|
-
{ branch: 'main' },
|
|
433
|
-
)
|
|
434
|
-
|
|
435
|
-
expect(result.ok).toBe(false)
|
|
436
|
-
if (!result.ok) {
|
|
437
|
-
expect(result.response.status).toBe(403)
|
|
438
|
-
}
|
|
439
|
-
})
|
|
440
|
-
|
|
441
|
-
it('combines privileged + schema guards correctly', async () => {
|
|
442
|
-
const bc = { ...createMockBranchContext(), flatSchema: fakeFlatSchema }
|
|
443
|
-
const ctx = createMockApiContext({ branchContext: bc })
|
|
444
|
-
|
|
445
|
-
const result = await executeGuards(
|
|
446
|
-
['privileged', 'schema'] as const,
|
|
447
|
-
ctx,
|
|
448
|
-
makeReq('reviewer'),
|
|
449
|
-
{ branch: 'main' },
|
|
450
|
-
)
|
|
451
|
-
|
|
452
|
-
expect(result.ok).toBe(true)
|
|
453
|
-
if (result.ok) {
|
|
454
|
-
expect(result.guardContext.branchContext.flatSchema).toBe(fakeFlatSchema)
|
|
455
|
-
}
|
|
456
|
-
})
|
|
457
|
-
})
|
|
458
|
-
|
|
459
|
-
// ========================================================================
|
|
460
|
-
// Context reuse — guards should not double-fetch branch context
|
|
461
|
-
// ========================================================================
|
|
462
|
-
|
|
463
|
-
describe('context reuse', () => {
|
|
464
|
-
it('branch + branchAccess does not call getBranchContext twice', async () => {
|
|
465
|
-
const bc = createMockBranchContext()
|
|
466
|
-
const ctx = createMockApiContext({
|
|
467
|
-
branchContext: bc,
|
|
468
|
-
allowBranchAccess: true,
|
|
469
|
-
})
|
|
470
|
-
const getBranchContext = ctx.getBranchContext as ReturnType<typeof vi.fn>
|
|
471
|
-
|
|
472
|
-
const result = await executeGuards(['branch', 'branchAccess'] as const, ctx, makeReq(), {
|
|
473
|
-
branch: 'main',
|
|
474
|
-
})
|
|
475
|
-
|
|
476
|
-
expect(result.ok).toBe(true)
|
|
477
|
-
// branch guard fetches once, branchAccess should reuse
|
|
478
|
-
expect(getBranchContext).toHaveBeenCalledTimes(1)
|
|
479
|
-
})
|
|
480
|
-
|
|
481
|
-
it('admin + branch calls getBranchContext only once', async () => {
|
|
482
|
-
const bc = createMockBranchContext()
|
|
483
|
-
const ctx = createMockApiContext({ branchContext: bc })
|
|
484
|
-
const getBranchContext = ctx.getBranchContext as ReturnType<typeof vi.fn>
|
|
485
|
-
|
|
486
|
-
const result = await executeGuards(['admin', 'branch'] as const, ctx, makeReq('admin'), {
|
|
487
|
-
branch: 'main',
|
|
488
|
-
})
|
|
489
|
-
|
|
490
|
-
expect(result.ok).toBe(true)
|
|
491
|
-
// admin guard doesn't fetch; branch guard fetches once
|
|
492
|
-
expect(getBranchContext).toHaveBeenCalledTimes(1)
|
|
493
|
-
})
|
|
494
|
-
|
|
495
|
-
it('branch + schema re-fetches when accumulated lacks flatSchema', async () => {
|
|
496
|
-
const bcNoSchema = createMockBranchContext()
|
|
497
|
-
const bcWithSchema = { ...createMockBranchContext(), flatSchema: fakeFlatSchema }
|
|
498
|
-
const getBranchContext = vi
|
|
499
|
-
.fn()
|
|
500
|
-
.mockResolvedValueOnce(bcNoSchema) // first call: no schema
|
|
501
|
-
.mockResolvedValueOnce(bcWithSchema) // second call: with schema
|
|
502
|
-
const ctx = createMockApiContext({ branchContext: bcNoSchema })
|
|
503
|
-
// Override with our custom mock
|
|
504
|
-
;(ctx as any).getBranchContext = getBranchContext
|
|
505
|
-
|
|
506
|
-
const result = await executeGuards(['branch', 'schema'] as const, ctx, makeReq(), {
|
|
507
|
-
branch: 'main',
|
|
508
|
-
})
|
|
509
|
-
|
|
510
|
-
expect(result.ok).toBe(true)
|
|
511
|
-
// branch guard fetches once (no schema), schema guard must re-fetch
|
|
512
|
-
expect(getBranchContext).toHaveBeenCalledTimes(2)
|
|
513
|
-
expect(getBranchContext).toHaveBeenLastCalledWith('main', { loadSchema: true })
|
|
514
|
-
})
|
|
515
|
-
|
|
516
|
-
it('branch + schema reuses when accumulated already has flatSchema', async () => {
|
|
517
|
-
const bc = { ...createMockBranchContext(), flatSchema: fakeFlatSchema }
|
|
518
|
-
const ctx = createMockApiContext({ branchContext: bc })
|
|
519
|
-
const getBranchContext = ctx.getBranchContext as ReturnType<typeof vi.fn>
|
|
520
|
-
|
|
521
|
-
const result = await executeGuards(['branch', 'schema'] as const, ctx, makeReq(), {
|
|
522
|
-
branch: 'main',
|
|
523
|
-
})
|
|
524
|
-
|
|
525
|
-
expect(result.ok).toBe(true)
|
|
526
|
-
// branch guard fetches once with schema already present, schema guard reuses
|
|
527
|
-
expect(getBranchContext).toHaveBeenCalledTimes(1)
|
|
528
|
-
if (result.ok) {
|
|
529
|
-
expect(result.guardContext.branchContext.flatSchema).toBe(fakeFlatSchema)
|
|
530
|
-
}
|
|
531
|
-
})
|
|
532
|
-
})
|
|
533
|
-
})
|