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,215 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { cleanup, render, screen } from '@testing-library/react'
|
|
4
|
-
import userEvent from '@testing-library/user-event'
|
|
5
|
-
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
|
|
6
|
-
import { EntryTypeEditor } from './EntryTypeEditor'
|
|
7
|
-
import { CanopyCMSProvider } from '../theme'
|
|
8
|
-
|
|
9
|
-
// Setup browser APIs
|
|
10
|
-
beforeAll(() => {
|
|
11
|
-
if (!window.matchMedia) {
|
|
12
|
-
window.matchMedia = ((query: string) =>
|
|
13
|
-
({
|
|
14
|
-
matches: false,
|
|
15
|
-
media: query,
|
|
16
|
-
onchange: null,
|
|
17
|
-
addListener: () => {},
|
|
18
|
-
removeListener: () => {},
|
|
19
|
-
addEventListener: () => {},
|
|
20
|
-
removeEventListener: () => {},
|
|
21
|
-
dispatchEvent: () => false,
|
|
22
|
-
}) as MediaQueryList) as typeof window.matchMedia
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (!window.ResizeObserver) {
|
|
26
|
-
class ResizeObserver {
|
|
27
|
-
observe() {}
|
|
28
|
-
unobserve() {}
|
|
29
|
-
disconnect() {}
|
|
30
|
-
}
|
|
31
|
-
;(window as unknown as { ResizeObserver: typeof ResizeObserver }).ResizeObserver =
|
|
32
|
-
ResizeObserver
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Mock scrollIntoView for Mantine Combobox
|
|
36
|
-
Element.prototype.scrollIntoView = vi.fn()
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
cleanup()
|
|
41
|
-
vi.restoreAllMocks()
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const renderEntryTypeEditor = (
|
|
45
|
-
props: Partial<React.ComponentProps<typeof EntryTypeEditor>> = {},
|
|
46
|
-
) => {
|
|
47
|
-
const defaultProps: React.ComponentProps<typeof EntryTypeEditor> = {
|
|
48
|
-
isOpen: true,
|
|
49
|
-
editingEntryType: null,
|
|
50
|
-
availableSchemas: ['postSchema', 'pageSchema', 'authorSchema'],
|
|
51
|
-
onSave: vi.fn(),
|
|
52
|
-
onClose: vi.fn(),
|
|
53
|
-
...props,
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
...render(
|
|
58
|
-
<CanopyCMSProvider>
|
|
59
|
-
<EntryTypeEditor {...defaultProps} />
|
|
60
|
-
</CanopyCMSProvider>,
|
|
61
|
-
),
|
|
62
|
-
props: defaultProps,
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
describe('EntryTypeEditor', () => {
|
|
67
|
-
describe('create mode', () => {
|
|
68
|
-
it('renders create modal with empty form', () => {
|
|
69
|
-
renderEntryTypeEditor()
|
|
70
|
-
|
|
71
|
-
// Use getAllByText since "Add Entry Type" appears in both title and button
|
|
72
|
-
const addEntryTypeElements = screen.getAllByText('Add Entry Type')
|
|
73
|
-
expect(addEntryTypeElements.length).toBeGreaterThan(0)
|
|
74
|
-
expect(screen.getByLabelText(/^Name/)).toBeTruthy()
|
|
75
|
-
expect(screen.getByLabelText(/^Label/)).toBeTruthy()
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('validates name is required', async () => {
|
|
79
|
-
const user = userEvent.setup()
|
|
80
|
-
const { props } = renderEntryTypeEditor()
|
|
81
|
-
|
|
82
|
-
// Click the submit button (use getAllBy since title and button have same text)
|
|
83
|
-
const buttons = screen.getAllByRole('button', { name: /Add Entry Type/i })
|
|
84
|
-
await user.click(buttons[0])
|
|
85
|
-
|
|
86
|
-
expect(screen.getByText(/Name is required/i)).toBeTruthy()
|
|
87
|
-
expect(props.onSave).not.toHaveBeenCalled()
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('validates name format', async () => {
|
|
91
|
-
const user = userEvent.setup()
|
|
92
|
-
const { props } = renderEntryTypeEditor()
|
|
93
|
-
|
|
94
|
-
await user.type(screen.getByLabelText(/^Name/), 'Invalid Name!')
|
|
95
|
-
const buttons = screen.getAllByRole('button', { name: /Add Entry Type/i })
|
|
96
|
-
await user.click(buttons[0])
|
|
97
|
-
|
|
98
|
-
expect(screen.getByText(/must start with a letter/i)).toBeTruthy()
|
|
99
|
-
expect(props.onSave).not.toHaveBeenCalled()
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('closes modal on cancel', async () => {
|
|
103
|
-
const user = userEvent.setup()
|
|
104
|
-
const { props } = renderEntryTypeEditor()
|
|
105
|
-
|
|
106
|
-
await user.click(screen.getByRole('button', { name: /Cancel/i }))
|
|
107
|
-
|
|
108
|
-
expect(props.onClose).toHaveBeenCalled()
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it('validates against duplicate entry type names', async () => {
|
|
112
|
-
const user = userEvent.setup()
|
|
113
|
-
const { props } = renderEntryTypeEditor({
|
|
114
|
-
existingEntryTypeNames: ['post', 'page', 'article'],
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
await user.type(screen.getByLabelText(/^Name/), 'post') // Duplicate name
|
|
118
|
-
const buttons = screen.getAllByRole('button', { name: /Add Entry Type/i })
|
|
119
|
-
await user.click(buttons[0])
|
|
120
|
-
|
|
121
|
-
expect(screen.getByText(/already exists in this collection/i)).toBeTruthy()
|
|
122
|
-
expect(props.onSave).not.toHaveBeenCalled()
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('allows name that does not duplicate existing entry types', async () => {
|
|
126
|
-
const user = userEvent.setup()
|
|
127
|
-
const { props } = renderEntryTypeEditor({
|
|
128
|
-
existingEntryTypeNames: ['post', 'page'],
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
await user.type(screen.getByLabelText(/^Name/), 'article') // Not a duplicate
|
|
132
|
-
const buttons = screen.getAllByRole('button', { name: /Add Entry Type/i })
|
|
133
|
-
await user.click(buttons[0])
|
|
134
|
-
|
|
135
|
-
expect(props.onSave).toHaveBeenCalled()
|
|
136
|
-
})
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
describe('edit mode', () => {
|
|
140
|
-
const existingEntryType = {
|
|
141
|
-
name: 'post',
|
|
142
|
-
label: 'Blog Post',
|
|
143
|
-
format: 'mdx' as const,
|
|
144
|
-
schema: 'postSchema',
|
|
145
|
-
default: true,
|
|
146
|
-
maxItems: 10,
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
it('renders edit modal with existing data', () => {
|
|
150
|
-
renderEntryTypeEditor({ editingEntryType: existingEntryType })
|
|
151
|
-
|
|
152
|
-
expect(screen.getByText(/Edit Entry Type: post/)).toBeTruthy()
|
|
153
|
-
const nameInput = screen.getByLabelText(/^Name/) as HTMLInputElement
|
|
154
|
-
expect(nameInput.value).toBe('post')
|
|
155
|
-
expect(nameInput.disabled).toBe(true)
|
|
156
|
-
const labelInput = screen.getByLabelText(/^Label/) as HTMLInputElement
|
|
157
|
-
expect(labelInput.value).toBe('Blog Post')
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
it('only includes changed fields in update', async () => {
|
|
161
|
-
const user = userEvent.setup()
|
|
162
|
-
const { props } = renderEntryTypeEditor({
|
|
163
|
-
editingEntryType: existingEntryType,
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
// Change only the label
|
|
167
|
-
const labelInput = screen.getByLabelText(/^Label/)
|
|
168
|
-
await user.clear(labelInput)
|
|
169
|
-
await user.type(labelInput, 'Updated Post')
|
|
170
|
-
|
|
171
|
-
await user.click(screen.getByRole('button', { name: /Save Changes/i }))
|
|
172
|
-
|
|
173
|
-
expect(props.onSave).toHaveBeenCalledWith({ label: 'Updated Post' }, false)
|
|
174
|
-
})
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
describe('error handling', () => {
|
|
178
|
-
it('displays external error', () => {
|
|
179
|
-
renderEntryTypeEditor({ error: 'Entry type already exists' })
|
|
180
|
-
|
|
181
|
-
expect(screen.getByText(/Entry type already exists/)).toBeTruthy()
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
it('shows saving state', () => {
|
|
185
|
-
renderEntryTypeEditor({ isSaving: true })
|
|
186
|
-
|
|
187
|
-
const saveButtons = screen.getAllByRole('button', {
|
|
188
|
-
name: /Add Entry Type/i,
|
|
189
|
-
})
|
|
190
|
-
expect(saveButtons[0].getAttribute('data-loading')).toBe('true')
|
|
191
|
-
})
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
describe('form defaults', () => {
|
|
195
|
-
it('uses first available schema as default', () => {
|
|
196
|
-
renderEntryTypeEditor({ availableSchemas: ['mySchema', 'otherSchema'] })
|
|
197
|
-
|
|
198
|
-
// The schema input should have the first schema pre-selected
|
|
199
|
-
const schemaInput = screen.getByRole('textbox', {
|
|
200
|
-
name: /Schema/,
|
|
201
|
-
}) as HTMLInputElement
|
|
202
|
-
expect(schemaInput.value).toBe('mySchema')
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
it('defaults format to json', () => {
|
|
206
|
-
renderEntryTypeEditor()
|
|
207
|
-
|
|
208
|
-
// Format select should show JSON as selected
|
|
209
|
-
const formatInput = screen.getByRole('textbox', {
|
|
210
|
-
name: /Format/,
|
|
211
|
-
}) as HTMLInputElement
|
|
212
|
-
expect(formatInput.value).toBe('JSON')
|
|
213
|
-
})
|
|
214
|
-
})
|
|
215
|
-
})
|
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* EntryTypeEditor - Modal for creating/editing entry types within a collection.
|
|
5
|
-
*
|
|
6
|
-
* Entry types define the structure of content items:
|
|
7
|
-
* - name: Machine-readable identifier (e.g., "post", "page")
|
|
8
|
-
* - label: Human-readable display name
|
|
9
|
-
* - format: Content format (md, mdx, json)
|
|
10
|
-
* - fields: Schema registry key for field definitions
|
|
11
|
-
* - default: Whether this is the default type for new items
|
|
12
|
-
* - maxItems: Optional limit on number of items (1 = singleton-like)
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { useState, useCallback, useEffect } from 'react'
|
|
16
|
-
import {
|
|
17
|
-
Modal,
|
|
18
|
-
Stack,
|
|
19
|
-
TextInput,
|
|
20
|
-
Select,
|
|
21
|
-
Switch,
|
|
22
|
-
NumberInput,
|
|
23
|
-
Group,
|
|
24
|
-
Button,
|
|
25
|
-
Text,
|
|
26
|
-
Alert,
|
|
27
|
-
Tooltip,
|
|
28
|
-
} from '@mantine/core'
|
|
29
|
-
import { IconAlertCircle, IconLock } from '@tabler/icons-react'
|
|
30
|
-
|
|
31
|
-
import type { ContentFormat } from '../../config'
|
|
32
|
-
import type { CreateEntryTypeInput, UpdateEntryTypeInput } from '../../schema/schema-store-types'
|
|
33
|
-
|
|
34
|
-
// ============================================================================
|
|
35
|
-
// Types
|
|
36
|
-
// ============================================================================
|
|
37
|
-
|
|
38
|
-
export interface EntryTypeFormData {
|
|
39
|
-
name: string
|
|
40
|
-
label: string
|
|
41
|
-
format: ContentFormat
|
|
42
|
-
schema: string
|
|
43
|
-
default: boolean
|
|
44
|
-
maxItems: number | undefined
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface EntryTypeEditorProps {
|
|
48
|
-
/** Whether the modal is open */
|
|
49
|
-
isOpen: boolean
|
|
50
|
-
/** Entry type being edited (null for create mode) */
|
|
51
|
-
editingEntryType: {
|
|
52
|
-
name: string
|
|
53
|
-
label?: string
|
|
54
|
-
format: ContentFormat
|
|
55
|
-
schema: string
|
|
56
|
-
default?: boolean
|
|
57
|
-
maxItems?: number
|
|
58
|
-
usageCount?: number
|
|
59
|
-
} | null
|
|
60
|
-
/** Available schema keys from the registry */
|
|
61
|
-
availableSchemas: string[]
|
|
62
|
-
/** Existing entry type names in the collection (for duplicate validation) */
|
|
63
|
-
existingEntryTypeNames?: string[]
|
|
64
|
-
/** Called when save is clicked */
|
|
65
|
-
onSave: (data: CreateEntryTypeInput | UpdateEntryTypeInput, isNew: boolean) => void
|
|
66
|
-
/** Called when modal is closed */
|
|
67
|
-
onClose: () => void
|
|
68
|
-
/** Whether a save operation is in progress */
|
|
69
|
-
isSaving?: boolean
|
|
70
|
-
/** Error message to display */
|
|
71
|
-
error?: string | null
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ============================================================================
|
|
75
|
-
// Constants
|
|
76
|
-
// ============================================================================
|
|
77
|
-
|
|
78
|
-
const FORMAT_OPTIONS: { value: ContentFormat; label: string }[] = [
|
|
79
|
-
{ value: 'json', label: 'JSON' },
|
|
80
|
-
{ value: 'md', label: 'Markdown' },
|
|
81
|
-
{ value: 'mdx', label: 'MDX' },
|
|
82
|
-
]
|
|
83
|
-
|
|
84
|
-
// ============================================================================
|
|
85
|
-
// Component
|
|
86
|
-
// ============================================================================
|
|
87
|
-
|
|
88
|
-
export function EntryTypeEditor({
|
|
89
|
-
isOpen,
|
|
90
|
-
editingEntryType,
|
|
91
|
-
availableSchemas,
|
|
92
|
-
existingEntryTypeNames = [],
|
|
93
|
-
onSave,
|
|
94
|
-
onClose,
|
|
95
|
-
isSaving = false,
|
|
96
|
-
error = null,
|
|
97
|
-
}: EntryTypeEditorProps) {
|
|
98
|
-
const isEditMode = editingEntryType !== null
|
|
99
|
-
const usageCount = editingEntryType?.usageCount ?? 0
|
|
100
|
-
const isLocked = isEditMode && usageCount > 0
|
|
101
|
-
|
|
102
|
-
// Form state
|
|
103
|
-
const [formData, setFormData] = useState<EntryTypeFormData>({
|
|
104
|
-
name: '',
|
|
105
|
-
label: '',
|
|
106
|
-
format: 'json',
|
|
107
|
-
schema: '',
|
|
108
|
-
default: false,
|
|
109
|
-
maxItems: undefined,
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
// Local validation error
|
|
113
|
-
const [validationError, setValidationError] = useState<string | null>(null)
|
|
114
|
-
|
|
115
|
-
// Reset form when modal opens or editing item changes
|
|
116
|
-
/* eslint-disable react-hooks/set-state-in-effect -- intentional: sync form state from props on open */
|
|
117
|
-
useEffect(() => {
|
|
118
|
-
if (isOpen) {
|
|
119
|
-
if (editingEntryType) {
|
|
120
|
-
setFormData({
|
|
121
|
-
name: editingEntryType.name,
|
|
122
|
-
label: editingEntryType.label || '',
|
|
123
|
-
format: editingEntryType.format,
|
|
124
|
-
schema: editingEntryType.schema,
|
|
125
|
-
default: editingEntryType.default || false,
|
|
126
|
-
maxItems: editingEntryType.maxItems,
|
|
127
|
-
})
|
|
128
|
-
} else {
|
|
129
|
-
setFormData({
|
|
130
|
-
name: '',
|
|
131
|
-
label: '',
|
|
132
|
-
format: 'json',
|
|
133
|
-
schema: availableSchemas[0] || '',
|
|
134
|
-
default: false,
|
|
135
|
-
maxItems: undefined,
|
|
136
|
-
})
|
|
137
|
-
}
|
|
138
|
-
setValidationError(null)
|
|
139
|
-
}
|
|
140
|
-
}, [isOpen, editingEntryType, availableSchemas])
|
|
141
|
-
/* eslint-enable react-hooks/set-state-in-effect */
|
|
142
|
-
|
|
143
|
-
// Update a form field
|
|
144
|
-
const updateField = useCallback(
|
|
145
|
-
<K extends keyof EntryTypeFormData>(field: K, value: EntryTypeFormData[K]) => {
|
|
146
|
-
setFormData((prev) => ({ ...prev, [field]: value }))
|
|
147
|
-
setValidationError(null)
|
|
148
|
-
},
|
|
149
|
-
[],
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
// Validate form
|
|
153
|
-
const validate = useCallback((): boolean => {
|
|
154
|
-
if (!isEditMode && !formData.name.trim()) {
|
|
155
|
-
setValidationError('Name is required')
|
|
156
|
-
return false
|
|
157
|
-
}
|
|
158
|
-
if (!isEditMode && !/^[a-z][a-z0-9-]*$/.test(formData.name)) {
|
|
159
|
-
setValidationError(
|
|
160
|
-
'Name must start with a letter and contain only lowercase letters, numbers, and hyphens',
|
|
161
|
-
)
|
|
162
|
-
return false
|
|
163
|
-
}
|
|
164
|
-
if (formData.name.length > 64) {
|
|
165
|
-
setValidationError('Name must be 64 characters or less')
|
|
166
|
-
return false
|
|
167
|
-
}
|
|
168
|
-
// Check for duplicate names (only in create mode)
|
|
169
|
-
if (!isEditMode && existingEntryTypeNames.includes(formData.name.trim())) {
|
|
170
|
-
setValidationError('Entry type with this name already exists in this collection')
|
|
171
|
-
return false
|
|
172
|
-
}
|
|
173
|
-
if (!formData.schema) {
|
|
174
|
-
setValidationError('Schema is required')
|
|
175
|
-
return false
|
|
176
|
-
}
|
|
177
|
-
return true
|
|
178
|
-
}, [formData, isEditMode, existingEntryTypeNames])
|
|
179
|
-
|
|
180
|
-
// Handle save
|
|
181
|
-
const handleSave = useCallback(() => {
|
|
182
|
-
if (!validate()) return
|
|
183
|
-
|
|
184
|
-
if (isEditMode) {
|
|
185
|
-
// Only include changed fields for update
|
|
186
|
-
const updates: UpdateEntryTypeInput = {}
|
|
187
|
-
if (formData.label !== (editingEntryType?.label || '')) {
|
|
188
|
-
updates.label = formData.label || undefined
|
|
189
|
-
}
|
|
190
|
-
if (formData.format !== editingEntryType?.format) {
|
|
191
|
-
updates.format = formData.format
|
|
192
|
-
}
|
|
193
|
-
if (formData.schema !== editingEntryType?.schema) {
|
|
194
|
-
updates.schema = formData.schema
|
|
195
|
-
}
|
|
196
|
-
if (formData.default !== (editingEntryType?.default || false)) {
|
|
197
|
-
updates.default = formData.default
|
|
198
|
-
}
|
|
199
|
-
if (formData.maxItems !== editingEntryType?.maxItems) {
|
|
200
|
-
updates.maxItems = formData.maxItems
|
|
201
|
-
}
|
|
202
|
-
onSave(updates, false)
|
|
203
|
-
} else {
|
|
204
|
-
// Create new entry type
|
|
205
|
-
const createData: CreateEntryTypeInput = {
|
|
206
|
-
name: formData.name.trim(),
|
|
207
|
-
format: formData.format,
|
|
208
|
-
schema: formData.schema,
|
|
209
|
-
}
|
|
210
|
-
if (formData.label.trim()) {
|
|
211
|
-
createData.label = formData.label.trim()
|
|
212
|
-
}
|
|
213
|
-
if (formData.default) {
|
|
214
|
-
createData.default = true
|
|
215
|
-
}
|
|
216
|
-
if (formData.maxItems !== undefined) {
|
|
217
|
-
createData.maxItems = formData.maxItems
|
|
218
|
-
}
|
|
219
|
-
onSave(createData, true)
|
|
220
|
-
}
|
|
221
|
-
}, [formData, isEditMode, editingEntryType, validate, onSave])
|
|
222
|
-
|
|
223
|
-
// Schema options for select
|
|
224
|
-
const schemaOptions = availableSchemas.map((key) => ({
|
|
225
|
-
value: key,
|
|
226
|
-
label: key,
|
|
227
|
-
}))
|
|
228
|
-
|
|
229
|
-
const displayError = error || validationError
|
|
230
|
-
|
|
231
|
-
return (
|
|
232
|
-
<Modal
|
|
233
|
-
opened={isOpen}
|
|
234
|
-
onClose={onClose}
|
|
235
|
-
title={isEditMode ? `Edit Entry Type: ${editingEntryType?.name}` : 'Add Entry Type'}
|
|
236
|
-
size="md"
|
|
237
|
-
>
|
|
238
|
-
<Stack gap="md">
|
|
239
|
-
{displayError && (
|
|
240
|
-
<Alert
|
|
241
|
-
icon={<IconAlertCircle size={16} />}
|
|
242
|
-
color="red"
|
|
243
|
-
title="Error"
|
|
244
|
-
withCloseButton
|
|
245
|
-
onClose={() => setValidationError(null)}
|
|
246
|
-
>
|
|
247
|
-
{displayError}
|
|
248
|
-
</Alert>
|
|
249
|
-
)}
|
|
250
|
-
|
|
251
|
-
{/* Name - only editable in create mode */}
|
|
252
|
-
<TextInput
|
|
253
|
-
label="Name"
|
|
254
|
-
description="Machine-readable identifier (e.g., post, page, article)"
|
|
255
|
-
placeholder="post"
|
|
256
|
-
value={formData.name}
|
|
257
|
-
onChange={(e) => updateField('name', e.target.value)}
|
|
258
|
-
disabled={isEditMode}
|
|
259
|
-
required={!isEditMode}
|
|
260
|
-
/>
|
|
261
|
-
|
|
262
|
-
{/* Label */}
|
|
263
|
-
<TextInput
|
|
264
|
-
label="Label"
|
|
265
|
-
description="Human-readable display name"
|
|
266
|
-
placeholder="Blog Post"
|
|
267
|
-
value={formData.label}
|
|
268
|
-
onChange={(e) => updateField('label', e.target.value)}
|
|
269
|
-
/>
|
|
270
|
-
|
|
271
|
-
{/* Format */}
|
|
272
|
-
<Tooltip
|
|
273
|
-
label={
|
|
274
|
-
isLocked
|
|
275
|
-
? `Cannot change format: ${usageCount} ${usageCount === 1 ? 'entry' : 'entries'} use this type`
|
|
276
|
-
: ''
|
|
277
|
-
}
|
|
278
|
-
disabled={!isLocked}
|
|
279
|
-
withinPortal
|
|
280
|
-
>
|
|
281
|
-
<Select
|
|
282
|
-
label="Format"
|
|
283
|
-
description={
|
|
284
|
-
isLocked
|
|
285
|
-
? `Locked (${usageCount} ${usageCount === 1 ? 'entry' : 'entries'} exist)`
|
|
286
|
-
: 'Content file format'
|
|
287
|
-
}
|
|
288
|
-
data={FORMAT_OPTIONS}
|
|
289
|
-
value={formData.format}
|
|
290
|
-
onChange={(value) => value && updateField('format', value as ContentFormat)}
|
|
291
|
-
allowDeselect={false}
|
|
292
|
-
disabled={isLocked}
|
|
293
|
-
rightSection={isLocked ? <IconLock size={14} /> : undefined}
|
|
294
|
-
/>
|
|
295
|
-
</Tooltip>
|
|
296
|
-
|
|
297
|
-
{/* Schema */}
|
|
298
|
-
<Tooltip
|
|
299
|
-
label={
|
|
300
|
-
isLocked
|
|
301
|
-
? `Cannot change schema: ${usageCount} ${usageCount === 1 ? 'entry' : 'entries'} use this type`
|
|
302
|
-
: ''
|
|
303
|
-
}
|
|
304
|
-
disabled={!isLocked}
|
|
305
|
-
withinPortal
|
|
306
|
-
>
|
|
307
|
-
<Select
|
|
308
|
-
label="Schema"
|
|
309
|
-
description={
|
|
310
|
-
isLocked
|
|
311
|
-
? `Locked (${usageCount} ${usageCount === 1 ? 'entry' : 'entries'} exist)`
|
|
312
|
-
: 'Field definitions for this entry type'
|
|
313
|
-
}
|
|
314
|
-
data={schemaOptions}
|
|
315
|
-
value={formData.schema}
|
|
316
|
-
onChange={(value) => value && updateField('schema', value)}
|
|
317
|
-
searchable
|
|
318
|
-
required
|
|
319
|
-
placeholder="Select a schema"
|
|
320
|
-
disabled={isLocked}
|
|
321
|
-
rightSection={isLocked ? <IconLock size={14} /> : undefined}
|
|
322
|
-
/>
|
|
323
|
-
</Tooltip>
|
|
324
|
-
|
|
325
|
-
{/* Default toggle */}
|
|
326
|
-
<Switch
|
|
327
|
-
label="Default entry type"
|
|
328
|
-
description="Use this type when adding new items to the collection"
|
|
329
|
-
checked={formData.default}
|
|
330
|
-
onChange={(e) => updateField('default', e.currentTarget.checked)}
|
|
331
|
-
/>
|
|
332
|
-
|
|
333
|
-
{/* Max items */}
|
|
334
|
-
<NumberInput
|
|
335
|
-
label="Max Items"
|
|
336
|
-
description="Limit number of items (leave empty for unlimited, use 1 for singleton-like behavior)"
|
|
337
|
-
placeholder="Unlimited"
|
|
338
|
-
value={formData.maxItems ?? ''}
|
|
339
|
-
onChange={(value) =>
|
|
340
|
-
updateField('maxItems', typeof value === 'number' ? value : undefined)
|
|
341
|
-
}
|
|
342
|
-
min={1}
|
|
343
|
-
allowNegative={false}
|
|
344
|
-
allowDecimal={false}
|
|
345
|
-
/>
|
|
346
|
-
|
|
347
|
-
{formData.maxItems === 1 && (
|
|
348
|
-
<Text size="xs" c="dimmed">
|
|
349
|
-
With maxItems=1, this entry type behaves like a singleton (e.g., homepage, settings)
|
|
350
|
-
</Text>
|
|
351
|
-
)}
|
|
352
|
-
|
|
353
|
-
{/* Actions */}
|
|
354
|
-
<Group justify="flex-end" gap="sm" mt="md">
|
|
355
|
-
<Button variant="subtle" onClick={onClose} disabled={isSaving}>
|
|
356
|
-
Cancel
|
|
357
|
-
</Button>
|
|
358
|
-
<Button onClick={handleSave} loading={isSaving}>
|
|
359
|
-
{isEditMode ? 'Save Changes' : 'Add Entry Type'}
|
|
360
|
-
</Button>
|
|
361
|
-
</Group>
|
|
362
|
-
</Stack>
|
|
363
|
-
</Modal>
|
|
364
|
-
)
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
export default EntryTypeEditor
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Schema Editor Components
|
|
3
|
-
*
|
|
4
|
-
* UI components for managing collection structure:
|
|
5
|
-
* - CollectionEditor: Create/edit collections and their entry types
|
|
6
|
-
* - EntryTypeEditor: Create/edit individual entry types
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export {
|
|
10
|
-
CollectionEditor,
|
|
11
|
-
type CollectionEditorProps,
|
|
12
|
-
type ExistingCollection,
|
|
13
|
-
type ExistingEntryType,
|
|
14
|
-
} from './CollectionEditor'
|
|
15
|
-
export {
|
|
16
|
-
EntryTypeEditor,
|
|
17
|
-
type EntryTypeEditorProps,
|
|
18
|
-
type EntryTypeFormData,
|
|
19
|
-
} from './EntryTypeEditor'
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Minimal DOM shims for Mantine in tests
|
|
2
|
-
if (typeof window !== 'undefined' && !('matchMedia' in window)) {
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
4
|
-
// @ts-ignore
|
|
5
|
-
window.matchMedia = () => ({
|
|
6
|
-
matches: false,
|
|
7
|
-
addListener: () => {},
|
|
8
|
-
removeListener: () => {},
|
|
9
|
-
})
|
|
10
|
-
}
|
package/src/editor/test-setup.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vitest setup file for editor tests running in jsdom environment.
|
|
3
|
-
* This runs BEFORE test modules are loaded, providing browser APIs
|
|
4
|
-
* that Mantine requires at module initialization time.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
if (typeof window !== 'undefined') {
|
|
8
|
-
// Mantine uses matchMedia for color scheme detection at module load time
|
|
9
|
-
Object.defineProperty(window, 'matchMedia', {
|
|
10
|
-
writable: true,
|
|
11
|
-
value: (query: string) => ({
|
|
12
|
-
matches: false,
|
|
13
|
-
media: query,
|
|
14
|
-
onchange: null,
|
|
15
|
-
addListener: () => {},
|
|
16
|
-
removeListener: () => {},
|
|
17
|
-
addEventListener: () => {},
|
|
18
|
-
removeEventListener: () => {},
|
|
19
|
-
dispatchEvent: () => false,
|
|
20
|
-
}),
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
// ResizeObserver is used by various Mantine components
|
|
24
|
-
if (!window.ResizeObserver) {
|
|
25
|
-
class ResizeObserver {
|
|
26
|
-
observe() {}
|
|
27
|
-
unobserve() {}
|
|
28
|
-
disconnect() {}
|
|
29
|
-
}
|
|
30
|
-
;(window as unknown as { ResizeObserver: typeof ResizeObserver }).ResizeObserver =
|
|
31
|
-
ResizeObserver
|
|
32
|
-
}
|
|
33
|
-
}
|