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,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for the editing workflow.
|
|
3
|
-
* Tests the complete lifecycle: create branch → edit content → save → commit
|
|
4
|
-
* Tests go through the HTTP API layer.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
8
|
-
import { simpleGit } from 'simple-git'
|
|
9
|
-
|
|
10
|
-
import { createTestWorkspace, type TestWorkspace } from '../test-utils/test-workspace'
|
|
11
|
-
import { createMockAuthPlugin } from '../test-utils/multi-user'
|
|
12
|
-
import { createApiClient } from '../test-utils/api-client'
|
|
13
|
-
import { BLOG_SCHEMA } from '../fixtures/schemas'
|
|
14
|
-
import type { BranchResponse } from '../../api/branch'
|
|
15
|
-
import { initTestRepo } from '../../test-utils'
|
|
16
|
-
|
|
17
|
-
describe('Editing Workflow Integration', () => {
|
|
18
|
-
let workspace: TestWorkspace
|
|
19
|
-
let editorClient: Awaited<ReturnType<typeof createApiClient>>
|
|
20
|
-
|
|
21
|
-
beforeEach(async () => {
|
|
22
|
-
workspace = await createTestWorkspace({
|
|
23
|
-
schema: BLOG_SCHEMA,
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
editorClient = await createApiClient({
|
|
27
|
-
config: workspace.config,
|
|
28
|
-
authPlugin: createMockAuthPlugin('editor'),
|
|
29
|
-
schema: BLOG_SCHEMA,
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
afterEach(async () => {
|
|
34
|
-
await workspace.cleanup()
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('completes full editing cycle: create → edit → save → commit', async () => {
|
|
38
|
-
// Create branch via API
|
|
39
|
-
const createResponse = await editorClient.post('/api/canopycms/branches', {
|
|
40
|
-
branch: 'feature/new-post',
|
|
41
|
-
title: 'Add blog post',
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
expect(createResponse.status).toBe(200)
|
|
45
|
-
const createData = await createResponse.json<BranchResponse>()
|
|
46
|
-
expect(createData.data?.branch.status).toBe('editing')
|
|
47
|
-
expect(createData.data?.branch.createdBy).toBe('test-editor')
|
|
48
|
-
|
|
49
|
-
// Get branch workspace path from response
|
|
50
|
-
const branchRoot = (createData.data?.branch as { workspaceRoot?: string })?.workspaceRoot
|
|
51
|
-
|
|
52
|
-
// Write content via API (will fail due to collection path bug, but test the structure)
|
|
53
|
-
const writeResponse = await editorClient.put(
|
|
54
|
-
'/api/canopycms/feature-new-post/content/posts/hello-world',
|
|
55
|
-
{
|
|
56
|
-
collection: 'content/posts',
|
|
57
|
-
slug: 'hello-world',
|
|
58
|
-
format: 'mdx',
|
|
59
|
-
data: {
|
|
60
|
-
title: 'Hello World',
|
|
61
|
-
author: 'Test Author',
|
|
62
|
-
date: '2024-01-01T00:00:00Z',
|
|
63
|
-
tags: ['intro', 'test'],
|
|
64
|
-
},
|
|
65
|
-
body: 'This is my first post!',
|
|
66
|
-
},
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
// TODO: This will fail until collection path bug is fixed
|
|
70
|
-
// expect(writeResponse.status).toBe(200)
|
|
71
|
-
|
|
72
|
-
// Commit changes (if write succeeded)
|
|
73
|
-
if (writeResponse.status === 200 && branchRoot) {
|
|
74
|
-
await initTestRepo(branchRoot)
|
|
75
|
-
const git = simpleGit({ baseDir: branchRoot })
|
|
76
|
-
await git.add(['.'])
|
|
77
|
-
await git.commit('Add hello world post')
|
|
78
|
-
|
|
79
|
-
// Verify commit exists
|
|
80
|
-
const log = await git.log()
|
|
81
|
-
expect(log.latest?.message).toBe('Add hello world post')
|
|
82
|
-
}
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('handles concurrent edits on different branches', async () => {
|
|
86
|
-
const editor1Client = editorClient
|
|
87
|
-
const editor2Client = await createApiClient({
|
|
88
|
-
config: workspace.config,
|
|
89
|
-
authPlugin: createMockAuthPlugin('admin'),
|
|
90
|
-
schema: BLOG_SCHEMA,
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
// Both editors create branches simultaneously
|
|
94
|
-
const [branchA, branchB] = await Promise.all([
|
|
95
|
-
editor1Client.post('/api/canopycms/branches', {
|
|
96
|
-
branch: 'feature/editor1-post',
|
|
97
|
-
title: 'Editor 1 Post',
|
|
98
|
-
}),
|
|
99
|
-
editor2Client.post('/api/canopycms/branches', {
|
|
100
|
-
branch: 'feature/editor2-post',
|
|
101
|
-
title: 'Editor 2 Post',
|
|
102
|
-
}),
|
|
103
|
-
])
|
|
104
|
-
|
|
105
|
-
expect(branchA.status).toBe(200)
|
|
106
|
-
expect(branchB.status).toBe(200)
|
|
107
|
-
|
|
108
|
-
// Both write content simultaneously (will fail due to collection bug)
|
|
109
|
-
await Promise.all([
|
|
110
|
-
editor1Client.put('/api/canopycms/feature-editor1-post/content/posts/post-a', {
|
|
111
|
-
collection: 'content/posts',
|
|
112
|
-
slug: 'post-a',
|
|
113
|
-
format: 'mdx',
|
|
114
|
-
data: {
|
|
115
|
-
title: 'Post A',
|
|
116
|
-
author: 'Editor 1',
|
|
117
|
-
date: '2024-01-01',
|
|
118
|
-
tags: ['test'],
|
|
119
|
-
},
|
|
120
|
-
body: 'Content A',
|
|
121
|
-
}),
|
|
122
|
-
editor2Client.put('/api/canopycms/feature-editor2-post/content/posts/post-b', {
|
|
123
|
-
collection: 'content/posts',
|
|
124
|
-
slug: 'post-b',
|
|
125
|
-
format: 'mdx',
|
|
126
|
-
data: {
|
|
127
|
-
title: 'Post B',
|
|
128
|
-
author: 'Editor 2',
|
|
129
|
-
date: '2024-01-02',
|
|
130
|
-
tags: ['test'],
|
|
131
|
-
},
|
|
132
|
-
body: 'Content B',
|
|
133
|
-
}),
|
|
134
|
-
])
|
|
135
|
-
|
|
136
|
-
// TODO: Verify isolation once collection bug is fixed
|
|
137
|
-
// Each user should only see their own content
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('supports multi-file editing with proper git commits', async () => {
|
|
141
|
-
const adminClient = await createApiClient({
|
|
142
|
-
config: workspace.config,
|
|
143
|
-
authPlugin: createMockAuthPlugin('admin'),
|
|
144
|
-
schema: BLOG_SCHEMA,
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
// Create branch
|
|
148
|
-
const createResponse = await adminClient.post('/api/canopycms/branches', {
|
|
149
|
-
branch: 'feature/multi-file',
|
|
150
|
-
title: 'Multi-file editing',
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
expect(createResponse.status).toBe(200)
|
|
154
|
-
const createData = await createResponse.json<BranchResponse>()
|
|
155
|
-
const branchRoot = (createData.data?.branch as { workspaceRoot?: string })?.workspaceRoot
|
|
156
|
-
|
|
157
|
-
// Write multiple posts via API
|
|
158
|
-
await Promise.all([
|
|
159
|
-
adminClient.put('/api/canopycms/feature-multi-file/content/posts/post-1', {
|
|
160
|
-
collection: 'content/posts',
|
|
161
|
-
slug: 'post-1',
|
|
162
|
-
format: 'mdx',
|
|
163
|
-
data: {
|
|
164
|
-
title: 'Post 1',
|
|
165
|
-
author: 'Admin',
|
|
166
|
-
date: '2024-01-01',
|
|
167
|
-
tags: ['test'],
|
|
168
|
-
},
|
|
169
|
-
body: 'First post',
|
|
170
|
-
}),
|
|
171
|
-
adminClient.put('/api/canopycms/feature-multi-file/content/posts/post-2', {
|
|
172
|
-
collection: 'content/posts',
|
|
173
|
-
slug: 'post-2',
|
|
174
|
-
format: 'mdx',
|
|
175
|
-
data: {
|
|
176
|
-
title: 'Post 2',
|
|
177
|
-
author: 'Admin',
|
|
178
|
-
date: '2024-01-02',
|
|
179
|
-
tags: ['test'],
|
|
180
|
-
},
|
|
181
|
-
body: 'Second post',
|
|
182
|
-
}),
|
|
183
|
-
adminClient.put('/api/canopycms/feature-multi-file/content/posts/post-3', {
|
|
184
|
-
collection: 'content/posts',
|
|
185
|
-
slug: 'post-3',
|
|
186
|
-
format: 'mdx',
|
|
187
|
-
data: {
|
|
188
|
-
title: 'Post 3',
|
|
189
|
-
author: 'Admin',
|
|
190
|
-
date: '2024-01-03',
|
|
191
|
-
tags: ['test'],
|
|
192
|
-
},
|
|
193
|
-
body: 'Third post',
|
|
194
|
-
}),
|
|
195
|
-
])
|
|
196
|
-
|
|
197
|
-
// TODO: Once collection bug is fixed, commit and verify
|
|
198
|
-
if (branchRoot) {
|
|
199
|
-
const git = simpleGit({ baseDir: branchRoot })
|
|
200
|
-
await git.status()
|
|
201
|
-
// Should have files if writes succeeded
|
|
202
|
-
// expect(status.files.length).toBeGreaterThan(0)
|
|
203
|
-
}
|
|
204
|
-
})
|
|
205
|
-
})
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for the review workflow.
|
|
3
|
-
* Tests submission, review comments, request changes, and approval.
|
|
4
|
-
* Tests go through the HTTP API layer.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
8
|
-
|
|
9
|
-
import { createTestWorkspace, type TestWorkspace } from '../test-utils/test-workspace'
|
|
10
|
-
import { createMockAuthPlugin } from '../test-utils/multi-user'
|
|
11
|
-
import { createApiClient } from '../test-utils/api-client'
|
|
12
|
-
import { BLOG_SCHEMA } from '../fixtures/schemas'
|
|
13
|
-
import type { BranchResponse } from '../../api/branch'
|
|
14
|
-
import type {
|
|
15
|
-
CommentsResponse,
|
|
16
|
-
AddCommentResponse,
|
|
17
|
-
ResolveCommentResponse,
|
|
18
|
-
} from '../../api/comments'
|
|
19
|
-
|
|
20
|
-
describe('Review Workflow Integration', () => {
|
|
21
|
-
let workspace: TestWorkspace
|
|
22
|
-
let editorClient: Awaited<ReturnType<typeof createApiClient>>
|
|
23
|
-
let reviewerClient: Awaited<ReturnType<typeof createApiClient>>
|
|
24
|
-
let adminClient: Awaited<ReturnType<typeof createApiClient>>
|
|
25
|
-
|
|
26
|
-
beforeEach(async () => {
|
|
27
|
-
workspace = await createTestWorkspace({
|
|
28
|
-
schema: BLOG_SCHEMA,
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
editorClient = await createApiClient({
|
|
32
|
-
config: workspace.config,
|
|
33
|
-
authPlugin: createMockAuthPlugin('editor'),
|
|
34
|
-
schema: BLOG_SCHEMA,
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
reviewerClient = await createApiClient({
|
|
38
|
-
config: workspace.config,
|
|
39
|
-
authPlugin: createMockAuthPlugin('reviewer'),
|
|
40
|
-
schema: BLOG_SCHEMA,
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
adminClient = await createApiClient({
|
|
44
|
-
config: workspace.config,
|
|
45
|
-
authPlugin: createMockAuthPlugin('admin'),
|
|
46
|
-
schema: BLOG_SCHEMA,
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
afterEach(async () => {
|
|
51
|
-
await workspace.cleanup()
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('completes full review cycle: submit → review → request changes → resubmit → approve', async () => {
|
|
55
|
-
// STEP 1: Editor creates branch
|
|
56
|
-
const createResponse = await editorClient.post('/api/canopycms/branches', {
|
|
57
|
-
branch: 'feature/review-test',
|
|
58
|
-
title: 'Test Review Workflow',
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
expect(createResponse.status).toBe(200)
|
|
62
|
-
const createData = await createResponse.json<BranchResponse>()
|
|
63
|
-
expect(createData.data?.branch.status).toBe('editing')
|
|
64
|
-
|
|
65
|
-
// STEP 2: Editor writes content (will fail due to collection bug)
|
|
66
|
-
await editorClient.put('/api/canopycms/feature-review-test/content/posts/test-post', {
|
|
67
|
-
collection: 'content/posts',
|
|
68
|
-
slug: 'test-post',
|
|
69
|
-
format: 'mdx',
|
|
70
|
-
data: {
|
|
71
|
-
title: 'Test Post',
|
|
72
|
-
author: 'Test Editor',
|
|
73
|
-
date: '2024-01-01',
|
|
74
|
-
tags: ['test'],
|
|
75
|
-
},
|
|
76
|
-
body: 'This needs review',
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
// STEP 3: Editor submits for review
|
|
80
|
-
const submitResponse = await editorClient.post('/api/canopycms/feature-review-test/submit', {
|
|
81
|
-
message: 'Ready for review',
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
expect(submitResponse.status).toBe(200)
|
|
85
|
-
const submitData = await submitResponse.json<BranchResponse>()
|
|
86
|
-
expect(submitData.data?.branch.status).toBe('submitted')
|
|
87
|
-
|
|
88
|
-
// STEP 4: Reviewer adds comment
|
|
89
|
-
const commentResponse = await reviewerClient.post(
|
|
90
|
-
'/api/canopycms/feature-review-test/comments',
|
|
91
|
-
{
|
|
92
|
-
text: 'Please add more details to the introduction',
|
|
93
|
-
type: 'entry',
|
|
94
|
-
entryPath: 'content/posts/test-post',
|
|
95
|
-
},
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
expect(commentResponse.status).toBe(200)
|
|
99
|
-
|
|
100
|
-
// STEP 5: Reviewer requests changes
|
|
101
|
-
const requestChangesResponse = await reviewerClient.post(
|
|
102
|
-
'/api/canopycms/feature-review-test/request-changes',
|
|
103
|
-
{
|
|
104
|
-
message: 'Needs more details',
|
|
105
|
-
},
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
expect(requestChangesResponse.status).toBe(200)
|
|
109
|
-
const requestChangesData = await requestChangesResponse.json<BranchResponse>()
|
|
110
|
-
expect(requestChangesData.data?.branch.status).toBe('editing')
|
|
111
|
-
|
|
112
|
-
// STEP 6: Editor updates content and resubmits
|
|
113
|
-
await editorClient.put('/api/canopycms/feature-review-test/content/posts/test-post', {
|
|
114
|
-
collection: 'content/posts',
|
|
115
|
-
slug: 'test-post',
|
|
116
|
-
format: 'mdx',
|
|
117
|
-
data: {
|
|
118
|
-
title: 'Test Post',
|
|
119
|
-
author: 'Test Editor',
|
|
120
|
-
date: '2024-01-01',
|
|
121
|
-
tags: ['test'],
|
|
122
|
-
},
|
|
123
|
-
body: 'This needs review. Added more details as requested.',
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
const resubmitResponse = await editorClient.post('/api/canopycms/feature-review-test/submit', {
|
|
127
|
-
message: 'Updated with more details',
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
expect(resubmitResponse.status).toBe(200)
|
|
131
|
-
|
|
132
|
-
// STEP 7: Reviewer approves
|
|
133
|
-
const approveResponse = await reviewerClient.post(
|
|
134
|
-
'/api/canopycms/feature-review-test/approve',
|
|
135
|
-
{
|
|
136
|
-
message: 'Looks good!',
|
|
137
|
-
},
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
expect(approveResponse.status).toBe(200)
|
|
141
|
-
const approveData = await approveResponse.json<BranchResponse>()
|
|
142
|
-
expect(approveData.data?.branch.status).toBe('approved')
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
it('allows multiple reviewers to comment', async () => {
|
|
146
|
-
// Create and submit branch
|
|
147
|
-
await editorClient.post('/api/canopycms/branches', {
|
|
148
|
-
branch: 'feature/multi-reviewer',
|
|
149
|
-
title: 'Multi Reviewer Test',
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
await editorClient.post('/api/canopycms/feature-multi-reviewer/submit', {
|
|
153
|
-
message: 'Ready for review',
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
// Both reviewer and admin add comments concurrently
|
|
157
|
-
// (CommentStore now handles concurrency with optimistic locking)
|
|
158
|
-
const [reviewerComment, adminComment] = await Promise.all([
|
|
159
|
-
reviewerClient.post('/api/canopycms/feature-multi-reviewer/comments', {
|
|
160
|
-
text: 'Reviewer comment',
|
|
161
|
-
type: 'branch',
|
|
162
|
-
}),
|
|
163
|
-
adminClient.post('/api/canopycms/feature-multi-reviewer/comments', {
|
|
164
|
-
text: 'Admin comment',
|
|
165
|
-
type: 'branch',
|
|
166
|
-
}),
|
|
167
|
-
])
|
|
168
|
-
|
|
169
|
-
expect(reviewerComment.status).toBe(200)
|
|
170
|
-
expect(adminComment.status).toBe(200)
|
|
171
|
-
|
|
172
|
-
// List all comments
|
|
173
|
-
const listResponse = await reviewerClient.get('/api/canopycms/feature-multi-reviewer/comments')
|
|
174
|
-
|
|
175
|
-
expect(listResponse.status).toBe(200)
|
|
176
|
-
const listData = await listResponse.json<CommentsResponse>()
|
|
177
|
-
expect(listData.data?.threads.length).toBeGreaterThanOrEqual(2)
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
it('enforces reviewer permissions', async () => {
|
|
181
|
-
// Editor creates and submits branch
|
|
182
|
-
await editorClient.post('/api/canopycms/branches', {
|
|
183
|
-
branch: 'feature/permission-test',
|
|
184
|
-
title: 'Permission Test',
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
await editorClient.post('/api/canopycms/feature-permission-test/submit', {
|
|
188
|
-
message: 'Ready',
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
// Editor (not a reviewer) tries to approve - should fail
|
|
192
|
-
const approveResponse = await editorClient.post(
|
|
193
|
-
'/api/canopycms/feature-permission-test/approve',
|
|
194
|
-
{
|
|
195
|
-
message: 'Approving my own work',
|
|
196
|
-
},
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
expect(approveResponse.status).toBe(403)
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
it('supports comment threads and resolution', async () => {
|
|
203
|
-
// Create and submit branch
|
|
204
|
-
await editorClient.post('/api/canopycms/branches', {
|
|
205
|
-
branch: 'feature/comment-threads',
|
|
206
|
-
title: 'Comment Threads Test',
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
await editorClient.post('/api/canopycms/feature-comment-threads/submit', {
|
|
210
|
-
message: 'Ready',
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
// Reviewer adds a comment
|
|
214
|
-
const commentResponse = await reviewerClient.post(
|
|
215
|
-
'/api/canopycms/feature-comment-threads/comments',
|
|
216
|
-
{
|
|
217
|
-
text: 'This needs fixing',
|
|
218
|
-
type: 'branch',
|
|
219
|
-
},
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
expect(commentResponse.status).toBe(200)
|
|
223
|
-
const commentData = await commentResponse.json<AddCommentResponse>()
|
|
224
|
-
const threadId = commentData.data?.threadId
|
|
225
|
-
|
|
226
|
-
// Reviewer resolves the comment thread
|
|
227
|
-
const resolveResponse = await reviewerClient.post(
|
|
228
|
-
`/api/canopycms/feature-comment-threads/comments/${threadId}/resolve`,
|
|
229
|
-
{},
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
expect(resolveResponse.status).toBe(200)
|
|
233
|
-
const resolveData = await resolveResponse.json<ResolveCommentResponse>()
|
|
234
|
-
expect(resolveData.data?.resolved).toBe(true)
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
it('allows withdrawal from review', async () => {
|
|
238
|
-
// Create and submit branch
|
|
239
|
-
await editorClient.post('/api/canopycms/branches', {
|
|
240
|
-
branch: 'feature/withdraw-test',
|
|
241
|
-
title: 'Withdraw Test',
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
const submitResponse = await editorClient.post('/api/canopycms/feature-withdraw-test/submit', {
|
|
245
|
-
message: 'Ready',
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
expect(submitResponse.status).toBe(200)
|
|
249
|
-
|
|
250
|
-
// Editor withdraws submission
|
|
251
|
-
const withdrawResponse = await editorClient.post(
|
|
252
|
-
'/api/canopycms/feature-withdraw-test/withdraw',
|
|
253
|
-
{},
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
expect(withdrawResponse.status).toBe(200)
|
|
257
|
-
const withdrawData = await withdrawResponse.json<BranchResponse>()
|
|
258
|
-
expect(withdrawData.data?.branch.status).toBe('editing')
|
|
259
|
-
})
|
|
260
|
-
})
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import os from 'node:os'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
|
|
6
|
-
|
|
7
|
-
import { defineCanopyTestConfig } from '../../config-test'
|
|
8
|
-
import { flattenSchema, type RootCollectionConfig } from '../../config'
|
|
9
|
-
import { ContentStore } from '../../content-store'
|
|
10
|
-
import { unsafeAsLogicalPath, unsafeAsEntrySlug } from '../../paths/test-utils'
|
|
11
|
-
import { generateAIContentFiles } from '../../build/generate-ai-content'
|
|
12
|
-
import type { AIManifest } from '../types'
|
|
13
|
-
|
|
14
|
-
const tmpDir = () => fs.mkdtemp(path.join(os.tmpdir(), 'canopycms-ai-build-'))
|
|
15
|
-
|
|
16
|
-
const testSchema: RootCollectionConfig = {
|
|
17
|
-
collections: [
|
|
18
|
-
{
|
|
19
|
-
name: 'posts',
|
|
20
|
-
path: 'posts',
|
|
21
|
-
entries: [
|
|
22
|
-
{
|
|
23
|
-
name: 'post',
|
|
24
|
-
format: 'md' as const,
|
|
25
|
-
schema: [
|
|
26
|
-
{ name: 'title', type: 'string' as const },
|
|
27
|
-
{ name: 'published', type: 'boolean' as const },
|
|
28
|
-
],
|
|
29
|
-
default: true,
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: 'settings',
|
|
35
|
-
path: 'settings',
|
|
36
|
-
entries: [
|
|
37
|
-
{
|
|
38
|
-
name: 'setting',
|
|
39
|
-
format: 'json' as const,
|
|
40
|
-
schema: [{ name: 'siteName', type: 'string' as const }],
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function setupContent(root: string, schema: RootCollectionConfig) {
|
|
48
|
-
const config = defineCanopyTestConfig({ schema })
|
|
49
|
-
const flat = flattenSchema(schema, config.contentRoot)
|
|
50
|
-
const store = new ContentStore(root, flat)
|
|
51
|
-
|
|
52
|
-
await store.write(unsafeAsLogicalPath('content/posts'), unsafeAsEntrySlug('hello-world'), {
|
|
53
|
-
format: 'md',
|
|
54
|
-
data: { title: 'Hello World', published: true },
|
|
55
|
-
body: '# Hello\n\nFirst post.',
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
await store.write(unsafeAsLogicalPath('content/posts'), unsafeAsEntrySlug('second'), {
|
|
59
|
-
format: 'md',
|
|
60
|
-
data: { title: 'Second', published: false },
|
|
61
|
-
body: 'Second post.',
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
await store.write(unsafeAsLogicalPath('content/settings'), unsafeAsEntrySlug('site'), {
|
|
65
|
-
format: 'json',
|
|
66
|
-
data: { siteName: 'TestSite' },
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
return { config, flat, store }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
describe('generateAIContentFiles', () => {
|
|
73
|
-
let contentRoot: string
|
|
74
|
-
let outputDir: string
|
|
75
|
-
|
|
76
|
-
beforeEach(async () => {
|
|
77
|
-
contentRoot = await tmpDir()
|
|
78
|
-
outputDir = await tmpDir()
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
afterEach(() => {
|
|
82
|
-
vi.restoreAllMocks()
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('writes all expected files to disk', async () => {
|
|
86
|
-
const { config, flat } = await setupContent(contentRoot, testSchema)
|
|
87
|
-
vi.spyOn(process, 'cwd').mockReturnValue(contentRoot)
|
|
88
|
-
|
|
89
|
-
const result = await generateAIContentFiles({
|
|
90
|
-
config: { ...config, mode: 'dev' },
|
|
91
|
-
entrySchemaRegistry: {},
|
|
92
|
-
outputDir,
|
|
93
|
-
_testFlatSchema: flat,
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
expect(result.fileCount).toBeGreaterThan(0)
|
|
97
|
-
|
|
98
|
-
// manifest.json exists and is valid
|
|
99
|
-
const manifestPath = path.join(outputDir, 'manifest.json')
|
|
100
|
-
const manifestContent = await fs.readFile(manifestPath, 'utf-8')
|
|
101
|
-
const manifest = JSON.parse(manifestContent) as AIManifest
|
|
102
|
-
expect(manifest.generated).toBeTruthy()
|
|
103
|
-
expect(manifest.collections.length).toBeGreaterThan(0)
|
|
104
|
-
|
|
105
|
-
// Individual entry files
|
|
106
|
-
const postFile = path.join(outputDir, 'posts', 'hello-world.md')
|
|
107
|
-
const postContent = await fs.readFile(postFile, 'utf-8')
|
|
108
|
-
expect(postContent).toContain('Hello World')
|
|
109
|
-
|
|
110
|
-
// Collection all.md
|
|
111
|
-
const allPostsFile = path.join(outputDir, 'posts', 'all.md')
|
|
112
|
-
const allPostsContent = await fs.readFile(allPostsFile, 'utf-8')
|
|
113
|
-
expect(allPostsContent).toContain('Hello World')
|
|
114
|
-
expect(allPostsContent).toContain('Second')
|
|
115
|
-
|
|
116
|
-
// Settings
|
|
117
|
-
const settingFile = path.join(outputDir, 'settings', 'site.md')
|
|
118
|
-
const settingContent = await fs.readFile(settingFile, 'utf-8')
|
|
119
|
-
expect(settingContent).toContain('TestSite')
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('file count matches actual files on disk', async () => {
|
|
123
|
-
const { config, flat } = await setupContent(contentRoot, testSchema)
|
|
124
|
-
vi.spyOn(process, 'cwd').mockReturnValue(contentRoot)
|
|
125
|
-
|
|
126
|
-
const result = await generateAIContentFiles({
|
|
127
|
-
config: { ...config, mode: 'dev' },
|
|
128
|
-
entrySchemaRegistry: {},
|
|
129
|
-
outputDir,
|
|
130
|
-
_testFlatSchema: flat,
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
// Count files recursively on disk
|
|
134
|
-
const countFiles = async (dir: string): Promise<number> => {
|
|
135
|
-
let count = 0
|
|
136
|
-
const entries = await fs.readdir(dir, { withFileTypes: true })
|
|
137
|
-
for (const entry of entries) {
|
|
138
|
-
if (entry.isDirectory()) {
|
|
139
|
-
count += await countFiles(path.join(dir, entry.name))
|
|
140
|
-
} else {
|
|
141
|
-
count++
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return count
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const filesOnDisk = await countFiles(outputDir)
|
|
148
|
-
expect(filesOnDisk).toBe(result.fileCount)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('writes bundles to bundles/ subdirectory', async () => {
|
|
152
|
-
const { config, flat } = await setupContent(contentRoot, testSchema)
|
|
153
|
-
vi.spyOn(process, 'cwd').mockReturnValue(contentRoot)
|
|
154
|
-
|
|
155
|
-
await generateAIContentFiles({
|
|
156
|
-
config: { ...config, mode: 'dev' },
|
|
157
|
-
entrySchemaRegistry: {},
|
|
158
|
-
outputDir,
|
|
159
|
-
_testFlatSchema: flat,
|
|
160
|
-
aiConfig: {
|
|
161
|
-
bundles: [
|
|
162
|
-
{
|
|
163
|
-
name: 'published',
|
|
164
|
-
description: 'Published posts',
|
|
165
|
-
filter: {
|
|
166
|
-
collections: ['posts'],
|
|
167
|
-
where: (entry) => entry.data.published === true,
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
],
|
|
171
|
-
},
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
const bundleFile = path.join(outputDir, 'bundles', 'published.md')
|
|
175
|
-
const bundleContent = await fs.readFile(bundleFile, 'utf-8')
|
|
176
|
-
expect(bundleContent).toContain('Hello World')
|
|
177
|
-
expect(bundleContent).not.toContain('Second')
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
it('rejects path traversal in bundle names', async () => {
|
|
181
|
-
const { config, flat } = await setupContent(contentRoot, testSchema)
|
|
182
|
-
vi.spyOn(process, 'cwd').mockReturnValue(contentRoot)
|
|
183
|
-
|
|
184
|
-
await expect(
|
|
185
|
-
generateAIContentFiles({
|
|
186
|
-
config: { ...config, mode: 'dev' },
|
|
187
|
-
entrySchemaRegistry: {},
|
|
188
|
-
outputDir,
|
|
189
|
-
_testFlatSchema: flat,
|
|
190
|
-
aiConfig: {
|
|
191
|
-
bundles: [
|
|
192
|
-
{
|
|
193
|
-
name: '../../etc/malicious',
|
|
194
|
-
filter: { collections: ['posts'] },
|
|
195
|
-
},
|
|
196
|
-
],
|
|
197
|
-
},
|
|
198
|
-
}),
|
|
199
|
-
).rejects.toThrow('Invalid bundle name')
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
it('creates correct directory structure', async () => {
|
|
203
|
-
const { config, flat } = await setupContent(contentRoot, testSchema)
|
|
204
|
-
vi.spyOn(process, 'cwd').mockReturnValue(contentRoot)
|
|
205
|
-
|
|
206
|
-
await generateAIContentFiles({
|
|
207
|
-
config: { ...config, mode: 'dev' },
|
|
208
|
-
entrySchemaRegistry: {},
|
|
209
|
-
outputDir,
|
|
210
|
-
_testFlatSchema: flat,
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
// Check directory structure
|
|
214
|
-
const topLevel = await fs.readdir(outputDir)
|
|
215
|
-
expect(topLevel).toContain('manifest.json')
|
|
216
|
-
expect(topLevel).toContain('posts')
|
|
217
|
-
expect(topLevel).toContain('settings')
|
|
218
|
-
|
|
219
|
-
const postsDir = await fs.readdir(path.join(outputDir, 'posts'))
|
|
220
|
-
expect(postsDir).toContain('all.md')
|
|
221
|
-
expect(postsDir).toContain('hello-world.md')
|
|
222
|
-
expect(postsDir).toContain('second.md')
|
|
223
|
-
})
|
|
224
|
-
})
|