canopycms 0.0.0 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/plugin.d.ts +8 -0
- package/dist/auth/plugin.d.ts.map +1 -1
- package/dist/build-mode.d.ts +15 -5
- package/dist/build-mode.d.ts.map +1 -1
- package/dist/build-mode.js +18 -8
- package/dist/build-mode.js.map +1 -1
- package/dist/cli/init.d.ts +2 -2
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +37 -36
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/template-files/ai-config.ts.template +21 -0
- package/dist/cli/template-files/ai-route.ts.template +10 -0
- package/dist/cli/template-files/canopy.ts.template +24 -0
- package/dist/cli/templates.d.ts +5 -1
- package/dist/cli/templates.d.ts.map +1 -1
- package/dist/cli/templates.js +9 -2
- package/dist/cli/templates.js.map +1 -1
- package/dist/config/schemas/config.d.ts +4 -0
- package/dist/config/schemas/config.d.ts.map +1 -1
- package/dist/config/schemas/config.js +2 -0
- package/dist/config/schemas/config.js.map +1 -1
- package/dist/config/types.d.ts +5 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/content-reader.js +2 -2
- package/dist/content-reader.js.map +1 -1
- package/dist/context.js +5 -5
- package/dist/context.js.map +1 -1
- package/dist/operating-mode/client-unsafe-strategy.d.ts.map +1 -1
- package/dist/operating-mode/client-unsafe-strategy.js +15 -18
- package/dist/operating-mode/client-unsafe-strategy.js.map +1 -1
- package/dist/operating-mode/types.d.ts +8 -0
- package/dist/operating-mode/types.d.ts.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -1
- package/package.json +5 -4
- package/src/cli/init.ts +43 -38
- package/dist/__integration__/fixtures/content-seeds.d.ts +0 -43
- package/dist/__integration__/fixtures/content-seeds.d.ts.map +0 -1
- package/dist/__integration__/fixtures/content-seeds.js +0 -99
- package/dist/__integration__/fixtures/content-seeds.js.map +0 -1
- package/dist/__integration__/fixtures/schemas.d.ts +0 -12
- package/dist/__integration__/fixtures/schemas.d.ts.map +0 -1
- package/dist/__integration__/fixtures/schemas.js +0 -65
- package/dist/__integration__/fixtures/schemas.js.map +0 -1
- package/dist/__integration__/test-utils/api-client.d.ts +0 -123
- package/dist/__integration__/test-utils/api-client.d.ts.map +0 -1
- package/dist/__integration__/test-utils/api-client.js +0 -118
- package/dist/__integration__/test-utils/api-client.js.map +0 -1
- package/dist/__integration__/test-utils/multi-user.d.ts +0 -25
- package/dist/__integration__/test-utils/multi-user.d.ts.map +0 -1
- package/dist/__integration__/test-utils/multi-user.js +0 -105
- package/dist/__integration__/test-utils/multi-user.js.map +0 -1
- package/dist/__integration__/test-utils/test-workspace.d.ts +0 -25
- package/dist/__integration__/test-utils/test-workspace.d.ts.map +0 -1
- package/dist/__integration__/test-utils/test-workspace.js +0 -102
- package/dist/__integration__/test-utils/test-workspace.js.map +0 -1
- package/dist/editor/BranchManager.stories.d.ts +0 -8
- package/dist/editor/BranchManager.stories.d.ts.map +0 -1
- package/dist/editor/BranchManager.stories.js +0 -74
- package/dist/editor/BranchManager.stories.js.map +0 -1
- package/dist/editor/CanopyEditor.stories.d.ts +0 -7
- package/dist/editor/CanopyEditor.stories.d.ts.map +0 -1
- package/dist/editor/CanopyEditor.stories.js +0 -99
- package/dist/editor/CanopyEditor.stories.js.map +0 -1
- package/dist/editor/CommentsPanel.stories.d.ts +0 -10
- package/dist/editor/CommentsPanel.stories.d.ts.map +0 -1
- package/dist/editor/CommentsPanel.stories.js +0 -175
- package/dist/editor/CommentsPanel.stories.js.map +0 -1
- package/dist/editor/Editor.stories.d.ts +0 -7
- package/dist/editor/Editor.stories.d.ts.map +0 -1
- package/dist/editor/Editor.stories.js +0 -95
- package/dist/editor/Editor.stories.js.map +0 -1
- package/dist/editor/EditorPanes.stories.d.ts +0 -7
- package/dist/editor/EditorPanes.stories.d.ts.map +0 -1
- package/dist/editor/EditorPanes.stories.js +0 -116
- package/dist/editor/EditorPanes.stories.js.map +0 -1
- package/dist/editor/EntryNavigator.stories.d.ts +0 -8
- package/dist/editor/EntryNavigator.stories.d.ts.map +0 -1
- package/dist/editor/EntryNavigator.stories.js +0 -42
- package/dist/editor/EntryNavigator.stories.js.map +0 -1
- package/dist/editor/FormRenderer.stories.d.ts +0 -7
- package/dist/editor/FormRenderer.stories.d.ts.map +0 -1
- package/dist/editor/FormRenderer.stories.js +0 -115
- package/dist/editor/FormRenderer.stories.js.map +0 -1
- package/dist/editor/GroupManager.stories.d.ts +0 -19
- package/dist/editor/GroupManager.stories.d.ts.map +0 -1
- package/dist/editor/GroupManager.stories.js +0 -265
- package/dist/editor/GroupManager.stories.js.map +0 -1
- package/dist/editor/PermissionManager.stories.d.ts +0 -20
- package/dist/editor/PermissionManager.stories.d.ts.map +0 -1
- package/dist/editor/PermissionManager.stories.js +0 -506
- package/dist/editor/PermissionManager.stories.js.map +0 -1
- package/dist/editor/comments/FieldWrapper.stories.d.ts +0 -10
- package/dist/editor/comments/FieldWrapper.stories.d.ts.map +0 -1
- package/dist/editor/comments/FieldWrapper.stories.js +0 -173
- package/dist/editor/comments/FieldWrapper.stories.js.map +0 -1
- package/dist/editor/fields/BlockField.stories.d.ts +0 -7
- package/dist/editor/fields/BlockField.stories.d.ts.map +0 -1
- package/dist/editor/fields/BlockField.stories.js +0 -50
- package/dist/editor/fields/BlockField.stories.js.map +0 -1
- package/dist/editor/fields/fields.stories.d.ts +0 -8
- package/dist/editor/fields/fields.stories.d.ts.map +0 -1
- package/dist/editor/fields/fields.stories.js +0 -34
- package/dist/editor/fields/fields.stories.js.map +0 -1
- package/dist/test-utils/api-test-helpers.d.ts +0 -238
- package/dist/test-utils/api-test-helpers.d.ts.map +0 -1
- package/dist/test-utils/api-test-helpers.js +0 -347
- package/dist/test-utils/api-test-helpers.js.map +0 -1
- package/dist/test-utils/console-spy.d.ts +0 -56
- package/dist/test-utils/console-spy.d.ts.map +0 -1
- package/dist/test-utils/console-spy.js +0 -81
- package/dist/test-utils/console-spy.js.map +0 -1
- package/dist/test-utils/git-helpers.d.ts +0 -21
- package/dist/test-utils/git-helpers.d.ts.map +0 -1
- package/dist/test-utils/git-helpers.js +0 -23
- package/dist/test-utils/git-helpers.js.map +0 -1
- package/dist/test-utils/index.d.ts +0 -5
- package/dist/test-utils/index.d.ts.map +0 -1
- package/dist/test-utils/index.js +0 -4
- package/dist/test-utils/index.js.map +0 -1
- package/src/__integration__/errors/invalid-content.test.ts +0 -238
- package/src/__integration__/errors/permission-denied.test.ts +0 -220
- package/src/__integration__/fixtures/content-seeds.ts +0 -105
- package/src/__integration__/fixtures/schemas.ts +0 -67
- package/src/__integration__/initialization/prod-sim-init.test.ts +0 -139
- package/src/__integration__/permissions/path-permissions.test.ts +0 -314
- package/src/__integration__/permissions/role-permissions.test.ts +0 -354
- package/src/__integration__/permissions/settings-branch-isolation.test.ts +0 -317
- package/src/__integration__/settings/groups-api.test.ts +0 -403
- package/src/__integration__/test-utils/api-client.ts +0 -167
- package/src/__integration__/test-utils/multi-user.ts +0 -129
- package/src/__integration__/test-utils/test-workspace.ts +0 -130
- package/src/__integration__/user/user-context.test.ts +0 -174
- package/src/__integration__/validation/input-validation.test.ts +0 -166
- package/src/__integration__/workflows/api-editing-workflow.test.ts +0 -244
- package/src/__integration__/workflows/conflict-resolution.test.ts +0 -259
- package/src/__integration__/workflows/editing-workflow.test.ts +0 -205
- package/src/__integration__/workflows/review-workflow.test.ts +0 -260
- package/src/ai/__tests__/build.integration.test.ts +0 -224
- package/src/ai/__tests__/generate.integration.test.ts +0 -495
- package/src/ai/__tests__/handler.integration.test.ts +0 -212
- package/src/ai/__tests__/json-to-markdown.test.ts +0 -553
- package/src/ai/generate.ts +0 -410
- package/src/ai/handler.ts +0 -123
- package/src/ai/index.ts +0 -26
- package/src/ai/json-to-markdown.ts +0 -424
- package/src/ai/resolve-branch.ts +0 -34
- package/src/ai/types.ts +0 -160
- package/src/api/AGENTS.md +0 -81
- package/src/api/__test__/mock-client.ts +0 -404
- package/src/api/assets.test.ts +0 -140
- package/src/api/assets.ts +0 -154
- package/src/api/branch-merge.test.ts +0 -163
- package/src/api/branch-merge.ts +0 -113
- package/src/api/branch-review.test.ts +0 -297
- package/src/api/branch-review.ts +0 -136
- package/src/api/branch-status.test.ts +0 -85
- package/src/api/branch-status.ts +0 -153
- package/src/api/branch-withdraw.test.ts +0 -146
- package/src/api/branch-withdraw.ts +0 -81
- package/src/api/branch-workflow.integration.test.ts +0 -578
- package/src/api/branch.test.ts +0 -620
- package/src/api/branch.ts +0 -492
- package/src/api/client.test.ts +0 -349
- package/src/api/client.ts +0 -506
- package/src/api/comments.test.ts +0 -285
- package/src/api/comments.ts +0 -210
- package/src/api/content.test.ts +0 -345
- package/src/api/content.ts +0 -454
- package/src/api/entries.test.ts +0 -1339
- package/src/api/entries.ts +0 -650
- package/src/api/github-sync.ts +0 -144
- package/src/api/groups.test.ts +0 -1013
- package/src/api/groups.ts +0 -375
- package/src/api/guards.test.ts +0 -533
- package/src/api/guards.ts +0 -271
- package/src/api/index.ts +0 -87
- package/src/api/permissions.test.ts +0 -766
- package/src/api/permissions.ts +0 -334
- package/src/api/reference-options.ts +0 -118
- package/src/api/resolve-references.ts +0 -107
- package/src/api/route-builder.ts +0 -289
- package/src/api/schema.test.ts +0 -840
- package/src/api/schema.ts +0 -936
- package/src/api/security.test.ts +0 -233
- package/src/api/settings-helpers.ts +0 -84
- package/src/api/types.ts +0 -40
- package/src/api/user.test.ts +0 -127
- package/src/api/user.ts +0 -42
- package/src/api/validators.test.ts +0 -275
- package/src/api/validators.ts +0 -176
- package/src/asset-store.test.ts +0 -37
- package/src/asset-store.ts +0 -110
- package/src/auth/cache.ts +0 -7
- package/src/auth/caching-auth-plugin.test.ts +0 -154
- package/src/auth/caching-auth-plugin.ts +0 -109
- package/src/auth/context-helpers.ts +0 -75
- package/src/auth/file-based-auth-cache.test.ts +0 -257
- package/src/auth/file-based-auth-cache.ts +0 -279
- package/src/auth/index.ts +0 -12
- package/src/auth/plugin.ts +0 -51
- package/src/auth/types.ts +0 -38
- package/src/authorization/__tests__/branch.test.ts +0 -260
- package/src/authorization/__tests__/content.test.ts +0 -142
- package/src/authorization/__tests__/path.test.ts +0 -133
- package/src/authorization/__tests__/permissions-loader.test.ts +0 -200
- package/src/authorization/branch.ts +0 -94
- package/src/authorization/content.ts +0 -93
- package/src/authorization/groups/index.ts +0 -11
- package/src/authorization/groups/loader.ts +0 -127
- package/src/authorization/groups/schema.ts +0 -48
- package/src/authorization/helpers.ts +0 -48
- package/src/authorization/index.ts +0 -84
- package/src/authorization/path.ts +0 -112
- package/src/authorization/permissions/index.ts +0 -11
- package/src/authorization/permissions/loader.ts +0 -116
- package/src/authorization/permissions/schema.ts +0 -66
- package/src/authorization/test-utils.ts +0 -15
- package/src/authorization/types.ts +0 -66
- package/src/authorization/validation.test.ts +0 -100
- package/src/authorization/validation.ts +0 -62
- package/src/branch-metadata.test.ts +0 -168
- package/src/branch-metadata.ts +0 -166
- package/src/branch-registry.test.ts +0 -248
- package/src/branch-registry.ts +0 -152
- package/src/branch-schema-cache.test.ts +0 -275
- package/src/branch-schema-cache.ts +0 -189
- package/src/branch-workspace.test.ts +0 -183
- package/src/branch-workspace.ts +0 -124
- package/src/build/generate-ai-content.ts +0 -78
- package/src/build/index.ts +0 -8
- package/src/build-mode.ts +0 -27
- package/src/cli/generate-ai-content.ts +0 -100
- package/src/cli/init.test.ts +0 -240
- package/src/cli/templates/canopy.ts.template +0 -55
- package/src/cli/templates.ts +0 -47
- package/src/client.ts +0 -12
- package/src/comment-store.test.ts +0 -442
- package/src/comment-store.ts +0 -301
- package/src/config/__tests__/config.test.ts +0 -513
- package/src/config/flatten.ts +0 -174
- package/src/config/helpers.ts +0 -167
- package/src/config/index.ts +0 -86
- package/src/config/schemas/collection.ts +0 -67
- package/src/config/schemas/config.ts +0 -77
- package/src/config/schemas/field.ts +0 -108
- package/src/config/schemas/media.ts +0 -27
- package/src/config/schemas/permissions.ts +0 -21
- package/src/config/types.ts +0 -321
- package/src/config/validation.ts +0 -70
- package/src/config-test.ts +0 -65
- package/src/config.ts +0 -11
- package/src/content-id-index.test.ts +0 -512
- package/src/content-id-index.ts +0 -479
- package/src/content-reader.test.ts +0 -478
- package/src/content-reader.ts +0 -214
- package/src/content-store.test.ts +0 -1126
- package/src/content-store.ts +0 -793
- package/src/context.ts +0 -111
- package/src/editor/BranchManager.stories.tsx +0 -80
- package/src/editor/BranchManager.test.tsx +0 -324
- package/src/editor/BranchManager.tsx +0 -461
- package/src/editor/CanopyEditor.stories.tsx +0 -128
- package/src/editor/CanopyEditor.test.tsx +0 -81
- package/src/editor/CanopyEditor.tsx +0 -73
- package/src/editor/CanopyEditorPage.test.tsx +0 -59
- package/src/editor/CanopyEditorPage.tsx +0 -25
- package/src/editor/CommentsPanel.stories.tsx +0 -184
- package/src/editor/CommentsPanel.tsx +0 -338
- package/src/editor/Editor.integration.test.tsx +0 -227
- package/src/editor/Editor.stories.tsx +0 -119
- package/src/editor/Editor.tsx +0 -1221
- package/src/editor/EditorPanes.stories.tsx +0 -256
- package/src/editor/EditorPanes.test.tsx +0 -77
- package/src/editor/EditorPanes.tsx +0 -180
- package/src/editor/EntryNavigator.stories.tsx +0 -65
- package/src/editor/EntryNavigator.test.tsx +0 -598
- package/src/editor/EntryNavigator.tsx +0 -665
- package/src/editor/FormRenderer.stories.tsx +0 -212
- package/src/editor/FormRenderer.test.tsx +0 -194
- package/src/editor/FormRenderer.tsx +0 -432
- package/src/editor/GroupManager.stories.tsx +0 -301
- package/src/editor/GroupManager.test.tsx +0 -682
- package/src/editor/GroupManager.tsx +0 -9
- package/src/editor/PermissionManager.stories.tsx +0 -539
- package/src/editor/PermissionManager.test.tsx +0 -864
- package/src/editor/PermissionManager.tsx +0 -12
- package/src/editor/canopy-path.test.ts +0 -23
- package/src/editor/canopy-path.ts +0 -52
- package/src/editor/client-reference-resolver.ts +0 -118
- package/src/editor/comments/BranchComments.tsx +0 -93
- package/src/editor/comments/EntryComments.tsx +0 -94
- package/src/editor/comments/FieldWrapper.stories.tsx +0 -210
- package/src/editor/comments/FieldWrapper.tsx +0 -129
- package/src/editor/comments/InlineCommentThread.test.tsx +0 -384
- package/src/editor/comments/InlineCommentThread.tsx +0 -246
- package/src/editor/comments/ThreadCarousel.test.tsx +0 -393
- package/src/editor/comments/ThreadCarousel.tsx +0 -525
- package/src/editor/components/ConfirmDeleteModal.tsx +0 -49
- package/src/editor/components/EditorContext.tsx +0 -49
- package/src/editor/components/EditorFooter.tsx +0 -47
- package/src/editor/components/EditorHeader.tsx +0 -492
- package/src/editor/components/EditorSidebar.tsx +0 -193
- package/src/editor/components/EntryCreateModal.tsx +0 -193
- package/src/editor/components/RenameEntryModal.tsx +0 -152
- package/src/editor/components/UserBadge.test.tsx +0 -274
- package/src/editor/components/UserBadge.tsx +0 -240
- package/src/editor/components/index.ts +0 -6
- package/src/editor/context/ApiClientContext.tsx +0 -56
- package/src/editor/context/EditorStateContext.tsx +0 -221
- package/src/editor/context/index.ts +0 -40
- package/src/editor/editor-config.test.ts +0 -385
- package/src/editor/editor-config.ts +0 -94
- package/src/editor/editor-utils.test.ts +0 -772
- package/src/editor/editor-utils.ts +0 -303
- package/src/editor/env.ts +0 -4
- package/src/editor/fields/BlockField.stories.tsx +0 -79
- package/src/editor/fields/BlockField.tsx +0 -267
- package/src/editor/fields/CodeField.tsx +0 -41
- package/src/editor/fields/MarkdownField.tsx +0 -205
- package/src/editor/fields/ObjectField.tsx +0 -71
- package/src/editor/fields/ReferenceField.tsx +0 -138
- package/src/editor/fields/SelectField.tsx +0 -76
- package/src/editor/fields/TextField.tsx +0 -35
- package/src/editor/fields/ToggleField.tsx +0 -37
- package/src/editor/fields/fields.stories.tsx +0 -40
- package/src/editor/group-manager/ExternalGroupsTab.tsx +0 -114
- package/src/editor/group-manager/GroupCard.tsx +0 -102
- package/src/editor/group-manager/GroupForm.tsx +0 -66
- package/src/editor/group-manager/InternalGroupsTab.tsx +0 -147
- package/src/editor/group-manager/MemberList.tsx +0 -184
- package/src/editor/group-manager/hooks/useExternalGroupSearch.ts +0 -63
- package/src/editor/group-manager/hooks/useGroupState.ts +0 -134
- package/src/editor/group-manager/hooks/useUserSearch.ts +0 -84
- package/src/editor/group-manager/index.tsx +0 -210
- package/src/editor/group-manager/types.ts +0 -28
- package/src/editor/hooks/README.md +0 -26
- package/src/editor/hooks/__test__/test-utils.tsx +0 -183
- package/src/editor/hooks/index.ts +0 -23
- package/src/editor/hooks/useBranchActions.test.tsx +0 -267
- package/src/editor/hooks/useBranchActions.tsx +0 -121
- package/src/editor/hooks/useBranchManager.test.tsx +0 -391
- package/src/editor/hooks/useBranchManager.tsx +0 -326
- package/src/editor/hooks/useCommentSystem.test.ts +0 -615
- package/src/editor/hooks/useCommentSystem.ts +0 -347
- package/src/editor/hooks/useDraftManager.test.ts +0 -375
- package/src/editor/hooks/useDraftManager.ts +0 -259
- package/src/editor/hooks/useEditorLayout.test.ts +0 -147
- package/src/editor/hooks/useEditorLayout.ts +0 -67
- package/src/editor/hooks/useEntryManager.test.ts +0 -588
- package/src/editor/hooks/useEntryManager.ts +0 -387
- package/src/editor/hooks/useGroupManager.test.ts +0 -277
- package/src/editor/hooks/useGroupManager.ts +0 -139
- package/src/editor/hooks/usePermissionManager.test.ts +0 -211
- package/src/editor/hooks/usePermissionManager.ts +0 -113
- package/src/editor/hooks/useReferenceResolution.ts +0 -248
- package/src/editor/hooks/useSchemaManager.test.ts +0 -370
- package/src/editor/hooks/useSchemaManager.ts +0 -310
- package/src/editor/hooks/useUserContext.tsx +0 -57
- package/src/editor/hooks/useUserMetadata.test.ts +0 -191
- package/src/editor/hooks/useUserMetadata.ts +0 -71
- package/src/editor/permission-manager/GroupSelector.tsx +0 -73
- package/src/editor/permission-manager/PermissionEditor.tsx +0 -321
- package/src/editor/permission-manager/PermissionLevelBadge.tsx +0 -53
- package/src/editor/permission-manager/PermissionTree.tsx +0 -237
- package/src/editor/permission-manager/UserSelector.tsx +0 -95
- package/src/editor/permission-manager/constants.tsx +0 -18
- package/src/editor/permission-manager/hooks/useGroupsAndUsers.ts +0 -153
- package/src/editor/permission-manager/hooks/usePermissionTree.ts +0 -200
- package/src/editor/permission-manager/index.tsx +0 -294
- package/src/editor/permission-manager/types.ts +0 -58
- package/src/editor/permission-manager/utils.ts +0 -179
- package/src/editor/preview-bridge.test.tsx +0 -50
- package/src/editor/preview-bridge.tsx +0 -294
- package/src/editor/schema-editor/CollectionEditor.test.tsx +0 -238
- package/src/editor/schema-editor/CollectionEditor.tsx +0 -520
- package/src/editor/schema-editor/EntryTypeEditor.test.tsx +0 -215
- package/src/editor/schema-editor/EntryTypeEditor.tsx +0 -367
- package/src/editor/schema-editor/index.ts +0 -19
- package/src/editor/setup-test-dom.ts +0 -10
- package/src/editor/test-setup.ts +0 -33
- package/src/editor/theme.tsx +0 -119
- package/src/editor/utils/env.ts +0 -39
- package/src/entry-schema-registry.test.ts +0 -281
- package/src/entry-schema-registry.ts +0 -121
- package/src/entry-schema.ts +0 -84
- package/src/git-manager.test.ts +0 -552
- package/src/git-manager.ts +0 -667
- package/src/github-service.test.ts +0 -312
- package/src/github-service.ts +0 -295
- package/src/http/handler.test.ts +0 -275
- package/src/http/handler.ts +0 -280
- package/src/http/index.ts +0 -11
- package/src/http/router.ts +0 -164
- package/src/http/types.ts +0 -44
- package/src/id.test.ts +0 -48
- package/src/id.ts +0 -22
- package/src/index.ts +0 -26
- package/src/operating-mode/__tests__/strategies.test.ts +0 -511
- package/src/operating-mode/client-safe-strategy.ts +0 -184
- package/src/operating-mode/client-unsafe-strategy.ts +0 -303
- package/src/operating-mode/client.ts +0 -13
- package/src/operating-mode/index.ts +0 -34
- package/src/operating-mode/types.ts +0 -186
- package/src/paths/__tests__/branch.test.ts +0 -53
- package/src/paths/__tests__/normalize.test.ts +0 -141
- package/src/paths/__tests__/resolve.test.ts +0 -207
- package/src/paths/__tests__/validation.test.ts +0 -61
- package/src/paths/branch.ts +0 -115
- package/src/paths/index.ts +0 -73
- package/src/paths/normalize-server.ts +0 -40
- package/src/paths/normalize.ts +0 -107
- package/src/paths/resolve.ts +0 -61
- package/src/paths/test-utils.ts +0 -37
- package/src/paths/types.ts +0 -68
- package/src/paths/validation.test.ts +0 -480
- package/src/paths/validation.ts +0 -391
- package/src/reference-resolver.test.ts +0 -107
- package/src/reference-resolver.ts +0 -157
- package/src/schema/index.ts +0 -29
- package/src/schema/meta-loader.ts +0 -366
- package/src/schema/resolver.ts +0 -83
- package/src/schema/schema-store-types.ts +0 -56
- package/src/schema/schema-store.test.ts +0 -816
- package/src/schema/schema-store.ts +0 -795
- package/src/schema/types.ts +0 -33
- package/src/schema-meta-loader.test.ts +0 -447
- package/src/server.ts +0 -15
- package/src/services.test.ts +0 -559
- package/src/services.ts +0 -373
- package/src/settings-branch-utils.ts +0 -53
- package/src/settings-workspace.ts +0 -156
- package/src/task-queue/README.md +0 -144
- package/src/task-queue/index.ts +0 -14
- package/src/task-queue/task-queue.test.ts +0 -524
- package/src/task-queue/task-queue.ts +0 -514
- package/src/task-queue/types.ts +0 -41
- package/src/test-utils/api-test-helpers.ts +0 -445
- package/src/test-utils/console-spy.test.ts +0 -14
- package/src/test-utils/console-spy.ts +0 -125
- package/src/test-utils/git-helpers.ts +0 -31
- package/src/test-utils/index.ts +0 -4
- package/src/types.ts +0 -54
- package/src/user.ts +0 -118
- package/src/utils/debug.test.ts +0 -114
- package/src/utils/debug.ts +0 -127
- package/src/utils/error.test.ts +0 -92
- package/src/utils/error.ts +0 -83
- package/src/utils/format.ts +0 -12
- package/src/validation/__tests__/field-traversal.test.ts +0 -263
- package/src/validation/deletion-checker.ts +0 -234
- package/src/validation/field-traversal.ts +0 -146
- package/src/validation/reference-validator.ts +0 -168
- package/src/worker/cms-worker-rebase.test.ts +0 -473
- package/src/worker/cms-worker.ts +0 -777
- package/src/worker/integration.test.ts +0 -289
- package/src/worker/task-queue-config.ts +0 -25
- package/src/worker/task-queue.test.ts +0 -452
- package/src/worker/task-queue.ts +0 -58
- /package/{src/cli/templates → dist/cli/template-files}/Dockerfile.cms.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/canopycms.config.ts.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/deploy-cms.yml.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/edit-page.tsx.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/route.ts.template +0 -0
- /package/{src/cli/templates → dist/cli/template-files}/schemas.ts.template +0 -0
package/src/editor/theme.tsx
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import React, { useMemo } from 'react'
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
DEFAULT_THEME,
|
|
7
|
-
MantineProvider,
|
|
8
|
-
type MantineColorScheme,
|
|
9
|
-
type MantineColorsTuple,
|
|
10
|
-
type MantineThemeOverride,
|
|
11
|
-
createTheme,
|
|
12
|
-
} from '@mantine/core'
|
|
13
|
-
import { Notifications } from '@mantine/notifications'
|
|
14
|
-
import { ModalsProvider } from '@mantine/modals'
|
|
15
|
-
import { generateColors } from '@mantine/colors-generator'
|
|
16
|
-
|
|
17
|
-
import '@mantine/core/styles.css'
|
|
18
|
-
import '@mantine/notifications/styles.css'
|
|
19
|
-
import '@mdxeditor/editor/style.css'
|
|
20
|
-
|
|
21
|
-
export type CanopyColorInput = MantineColorsTuple | string
|
|
22
|
-
|
|
23
|
-
export interface CanopyColorConfig {
|
|
24
|
-
brand?: CanopyColorInput
|
|
25
|
-
primary?: CanopyColorInput
|
|
26
|
-
neutral?: CanopyColorInput
|
|
27
|
-
accent?: CanopyColorInput
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface CanopyThemeOptions {
|
|
31
|
-
colors?: CanopyColorConfig
|
|
32
|
-
colorScheme?: MantineColorScheme
|
|
33
|
-
themeOverride?: MantineThemeOverride
|
|
34
|
-
withNotifications?: boolean
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const defaultCanopyColors: Required<CanopyColorConfig> = {
|
|
38
|
-
brand: '#3b82f6',
|
|
39
|
-
primary: '#2563eb',
|
|
40
|
-
neutral: '#1f2937',
|
|
41
|
-
accent: '#f59e0b',
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const CANOPY_DEFAULT_COLORS = defaultCanopyColors
|
|
45
|
-
|
|
46
|
-
const toScale = (
|
|
47
|
-
input: CanopyColorInput | undefined,
|
|
48
|
-
fallbackKey: keyof typeof DEFAULT_THEME.colors,
|
|
49
|
-
fallback: CanopyColorInput,
|
|
50
|
-
) => {
|
|
51
|
-
const fallbackScale =
|
|
52
|
-
DEFAULT_THEME.colors[fallbackKey] ??
|
|
53
|
-
(Array.isArray(fallback) && fallback.length === 10
|
|
54
|
-
? (fallback as MantineColorsTuple)
|
|
55
|
-
: generateColors(String(fallback)))
|
|
56
|
-
if (!input) return fallbackScale
|
|
57
|
-
if (Array.isArray(input) && input.length === 10) return input as MantineColorsTuple
|
|
58
|
-
|
|
59
|
-
if (typeof input === 'string') {
|
|
60
|
-
const fromTheme = DEFAULT_THEME.colors[input as keyof typeof DEFAULT_THEME.colors]
|
|
61
|
-
if (fromTheme) return fromTheme
|
|
62
|
-
try {
|
|
63
|
-
return generateColors(input)
|
|
64
|
-
} catch {
|
|
65
|
-
return fallbackScale
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return fallbackScale
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const createCanopyTheme = (
|
|
73
|
-
options?: Pick<CanopyThemeOptions, 'colors' | 'themeOverride'>,
|
|
74
|
-
) => {
|
|
75
|
-
const { colors, themeOverride } = options ?? {}
|
|
76
|
-
const brand = toScale(colors?.brand, 'indigo', defaultCanopyColors.brand)
|
|
77
|
-
const primary = toScale(colors?.primary ?? colors?.brand, 'blue', defaultCanopyColors.primary)
|
|
78
|
-
const neutral = toScale(colors?.neutral, 'gray', defaultCanopyColors.neutral)
|
|
79
|
-
const accent = toScale(colors?.accent, 'teal', defaultCanopyColors.accent)
|
|
80
|
-
|
|
81
|
-
return createTheme({
|
|
82
|
-
primaryColor: 'brand',
|
|
83
|
-
primaryShade: { light: 6, dark: 5 },
|
|
84
|
-
defaultRadius: 'md',
|
|
85
|
-
colors: {
|
|
86
|
-
...DEFAULT_THEME.colors,
|
|
87
|
-
brand,
|
|
88
|
-
primary,
|
|
89
|
-
neutral,
|
|
90
|
-
accent,
|
|
91
|
-
...(themeOverride?.colors ?? {}),
|
|
92
|
-
},
|
|
93
|
-
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif',
|
|
94
|
-
...themeOverride,
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export interface CanopyCMSProviderProps extends CanopyThemeOptions {
|
|
99
|
-
children: React.ReactNode
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export const CanopyCMSProvider: React.FC<CanopyCMSProviderProps> = ({
|
|
103
|
-
children,
|
|
104
|
-
colors,
|
|
105
|
-
colorScheme = 'light',
|
|
106
|
-
themeOverride,
|
|
107
|
-
withNotifications = true,
|
|
108
|
-
}) => {
|
|
109
|
-
const theme = useMemo(() => createCanopyTheme({ colors, themeOverride }), [colors, themeOverride])
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<MantineProvider theme={theme} defaultColorScheme={colorScheme}>
|
|
113
|
-
<ModalsProvider>
|
|
114
|
-
{withNotifications ? <Notifications position="bottom-left" /> : null}
|
|
115
|
-
{children}
|
|
116
|
-
</ModalsProvider>
|
|
117
|
-
</MantineProvider>
|
|
118
|
-
)
|
|
119
|
-
}
|
package/src/editor/utils/env.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detect if we're running in a test environment.
|
|
3
|
-
* Playwright and other E2E frameworks often set specific globals.
|
|
4
|
-
*/
|
|
5
|
-
export function isTestEnvironment(): boolean {
|
|
6
|
-
if (typeof window === 'undefined') return false
|
|
7
|
-
|
|
8
|
-
// Check for common E2E testing indicators
|
|
9
|
-
return (
|
|
10
|
-
// Playwright sets window.playwright
|
|
11
|
-
'playwright' in window ||
|
|
12
|
-
// Cypress sets window.Cypress
|
|
13
|
-
'Cypress' in window ||
|
|
14
|
-
// Check if running in test mode via env variable (set by test runner)
|
|
15
|
-
(window as unknown as Record<string, unknown>).__E2E_TEST__ === true ||
|
|
16
|
-
// Some test frameworks set navigator.webdriver
|
|
17
|
-
navigator.webdriver === true
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Get notification duration based on environment.
|
|
23
|
-
* Tests get longer durations to be more reliable.
|
|
24
|
-
*
|
|
25
|
-
* @param defaultMs - Default duration in milliseconds for production (default: 4000)
|
|
26
|
-
* @returns Duration in milliseconds (15000ms for tests, defaultMs for production)
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```typescript
|
|
30
|
-
* notifications.show({
|
|
31
|
-
* message: 'Saved',
|
|
32
|
-
* color: 'green',
|
|
33
|
-
* autoClose: getNotificationDuration(4000), // 4s in production, 15s in tests
|
|
34
|
-
* })
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
export function getNotificationDuration(defaultMs = 4000): number {
|
|
38
|
-
return isTestEnvironment() ? 15000 : defaultMs
|
|
39
|
-
}
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
2
|
-
import { mkdir, writeFile, rm } from 'fs/promises'
|
|
3
|
-
import { join } from 'pathe'
|
|
4
|
-
import { createEntrySchemaRegistry, validateEntrySchemaRegistry } from './entry-schema-registry'
|
|
5
|
-
|
|
6
|
-
describe('createEntrySchemaRegistry', () => {
|
|
7
|
-
it('accepts valid schema registry', () => {
|
|
8
|
-
const registry = createEntrySchemaRegistry({
|
|
9
|
-
postSchema: [{ type: 'string', name: 'title', label: 'Title', required: true }],
|
|
10
|
-
authorSchema: [{ type: 'string', name: 'name', label: 'Name', required: true }],
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
expect(registry).toBeDefined()
|
|
14
|
-
expect(Object.keys(registry)).toEqual(['postSchema', 'authorSchema'])
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('throws error for non-object registry', () => {
|
|
18
|
-
expect(() => createEntrySchemaRegistry(null as any)).toThrow(
|
|
19
|
-
'Entry schema registry must be an object',
|
|
20
|
-
)
|
|
21
|
-
expect(() => createEntrySchemaRegistry(undefined as any)).toThrow(
|
|
22
|
-
'Entry schema registry must be an object',
|
|
23
|
-
)
|
|
24
|
-
expect(() => createEntrySchemaRegistry('invalid' as any)).toThrow(
|
|
25
|
-
'Entry schema registry must be an object',
|
|
26
|
-
)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('throws error for empty registry', () => {
|
|
30
|
-
expect(() => createEntrySchemaRegistry({})).toThrow('Entry schema registry cannot be empty')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('throws error for non-array schema', () => {
|
|
34
|
-
expect(() =>
|
|
35
|
-
createEntrySchemaRegistry({
|
|
36
|
-
postSchema: 'not an array' as any,
|
|
37
|
-
}),
|
|
38
|
-
).toThrow('Entry schema registry entry "postSchema" must be an array of FieldConfig')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('throws error for empty schema array', () => {
|
|
42
|
-
expect(() =>
|
|
43
|
-
createEntrySchemaRegistry({
|
|
44
|
-
postSchema: [],
|
|
45
|
-
}),
|
|
46
|
-
).toThrow('Entry schema registry entry "postSchema" cannot be empty')
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
describe('validateEntrySchemaRegistry', () => {
|
|
51
|
-
const testDir = join(process.cwd(), '.test-content-validate')
|
|
52
|
-
|
|
53
|
-
beforeEach(async () => {
|
|
54
|
-
await mkdir(testDir, { recursive: true })
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
afterEach(async () => {
|
|
58
|
-
await rm(testDir, { recursive: true, force: true })
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('validates registry with matching schema references', async () => {
|
|
62
|
-
const registry = {
|
|
63
|
-
postSchema: [
|
|
64
|
-
{
|
|
65
|
-
type: 'string' as const,
|
|
66
|
-
name: 'title',
|
|
67
|
-
label: 'Title',
|
|
68
|
-
required: true,
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Create a collection that references postSchema
|
|
74
|
-
await mkdir(join(testDir, 'posts'), { recursive: true })
|
|
75
|
-
await writeFile(
|
|
76
|
-
join(testDir, 'posts', '.collection.json'),
|
|
77
|
-
JSON.stringify({
|
|
78
|
-
name: 'posts',
|
|
79
|
-
entries: [
|
|
80
|
-
{
|
|
81
|
-
name: 'post',
|
|
82
|
-
format: 'mdx',
|
|
83
|
-
schema: 'postSchema',
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
order: [],
|
|
87
|
-
}),
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
await expect(validateEntrySchemaRegistry(registry, testDir)).resolves.toBeUndefined()
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
it('throws error for missing schema reference in collection', async () => {
|
|
94
|
-
const registry = {
|
|
95
|
-
postSchema: [
|
|
96
|
-
{
|
|
97
|
-
type: 'string' as const,
|
|
98
|
-
name: 'title',
|
|
99
|
-
label: 'Title',
|
|
100
|
-
required: true,
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Create a collection that references non-existent schema
|
|
106
|
-
await mkdir(join(testDir, 'posts'), { recursive: true })
|
|
107
|
-
await writeFile(
|
|
108
|
-
join(testDir, 'posts', '.collection.json'),
|
|
109
|
-
JSON.stringify({
|
|
110
|
-
name: 'posts',
|
|
111
|
-
entries: [
|
|
112
|
-
{
|
|
113
|
-
name: 'post',
|
|
114
|
-
format: 'mdx',
|
|
115
|
-
schema: 'authorSchema',
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
order: [],
|
|
119
|
-
}),
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
await expect(validateEntrySchemaRegistry(registry, testDir)).rejects.toThrow(
|
|
123
|
-
/Entry type "post" in collection "posts".*references entry schema "authorSchema".*does not exist/,
|
|
124
|
-
)
|
|
125
|
-
await expect(validateEntrySchemaRegistry(registry, testDir)).rejects.toThrow(
|
|
126
|
-
/Available: postSchema/,
|
|
127
|
-
)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('throws error for missing schema reference in root entry type', async () => {
|
|
131
|
-
const registry = {
|
|
132
|
-
postSchema: [
|
|
133
|
-
{
|
|
134
|
-
type: 'string' as const,
|
|
135
|
-
name: 'title',
|
|
136
|
-
label: 'Title',
|
|
137
|
-
required: true,
|
|
138
|
-
},
|
|
139
|
-
],
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Create root collection with entry type referencing non-existent schema
|
|
143
|
-
await writeFile(
|
|
144
|
-
join(testDir, '.collection.json'),
|
|
145
|
-
JSON.stringify({
|
|
146
|
-
entries: [
|
|
147
|
-
{
|
|
148
|
-
name: 'home',
|
|
149
|
-
format: 'json',
|
|
150
|
-
schema: 'homeSchema',
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
order: [],
|
|
154
|
-
}),
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
await expect(validateEntrySchemaRegistry(registry, testDir)).rejects.toThrow(
|
|
158
|
-
/Root entry type "home".*references entry schema "homeSchema".*does not exist/,
|
|
159
|
-
)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it('throws error for missing schema reference in collection entry type', async () => {
|
|
163
|
-
const registry = {
|
|
164
|
-
postSchema: [
|
|
165
|
-
{
|
|
166
|
-
type: 'string' as const,
|
|
167
|
-
name: 'title',
|
|
168
|
-
label: 'Title',
|
|
169
|
-
required: true,
|
|
170
|
-
},
|
|
171
|
-
],
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Create collection with entry type referencing non-existent schema
|
|
175
|
-
await mkdir(join(testDir, 'docs'), { recursive: true })
|
|
176
|
-
await writeFile(
|
|
177
|
-
join(testDir, 'docs', '.collection.json'),
|
|
178
|
-
JSON.stringify({
|
|
179
|
-
name: 'docs',
|
|
180
|
-
entries: [
|
|
181
|
-
{
|
|
182
|
-
name: 'doc',
|
|
183
|
-
format: 'json',
|
|
184
|
-
schema: 'configSchema',
|
|
185
|
-
},
|
|
186
|
-
],
|
|
187
|
-
order: [],
|
|
188
|
-
}),
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
await expect(validateEntrySchemaRegistry(registry, testDir)).rejects.toThrow(
|
|
192
|
-
/Entry type "doc" in collection "docs".*references entry schema "configSchema".*does not exist/,
|
|
193
|
-
)
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
it('validates nested collections correctly', async () => {
|
|
197
|
-
const registry = {
|
|
198
|
-
postSchema: [
|
|
199
|
-
{
|
|
200
|
-
type: 'string' as const,
|
|
201
|
-
name: 'title',
|
|
202
|
-
label: 'Title',
|
|
203
|
-
required: true,
|
|
204
|
-
},
|
|
205
|
-
],
|
|
206
|
-
docSchema: [
|
|
207
|
-
{
|
|
208
|
-
type: 'string' as const,
|
|
209
|
-
name: 'title',
|
|
210
|
-
label: 'Title',
|
|
211
|
-
required: true,
|
|
212
|
-
},
|
|
213
|
-
],
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Create nested collections
|
|
217
|
-
await mkdir(join(testDir, 'docs', 'api'), { recursive: true })
|
|
218
|
-
await writeFile(
|
|
219
|
-
join(testDir, 'docs', '.collection.json'),
|
|
220
|
-
JSON.stringify({
|
|
221
|
-
name: 'docs',
|
|
222
|
-
entries: [
|
|
223
|
-
{
|
|
224
|
-
name: 'doc',
|
|
225
|
-
format: 'mdx',
|
|
226
|
-
schema: 'docSchema',
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
order: [],
|
|
230
|
-
}),
|
|
231
|
-
)
|
|
232
|
-
await writeFile(
|
|
233
|
-
join(testDir, 'docs', 'api', '.collection.json'),
|
|
234
|
-
JSON.stringify({
|
|
235
|
-
name: 'api',
|
|
236
|
-
entries: [
|
|
237
|
-
{
|
|
238
|
-
name: 'doc',
|
|
239
|
-
format: 'mdx',
|
|
240
|
-
schema: 'docSchema',
|
|
241
|
-
},
|
|
242
|
-
],
|
|
243
|
-
order: [],
|
|
244
|
-
}),
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
await expect(validateEntrySchemaRegistry(registry, testDir)).resolves.toBeUndefined()
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
it('throws error for non-existent content directory', async () => {
|
|
251
|
-
const registry = {
|
|
252
|
-
postSchema: [
|
|
253
|
-
{
|
|
254
|
-
type: 'string' as const,
|
|
255
|
-
name: 'title',
|
|
256
|
-
label: 'Title',
|
|
257
|
-
required: true,
|
|
258
|
-
},
|
|
259
|
-
],
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
await expect(validateEntrySchemaRegistry(registry, '/nonexistent')).rejects.toThrow(
|
|
263
|
-
/Content directory not found/,
|
|
264
|
-
)
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
it('validates with no .collection.json files (empty content dir)', async () => {
|
|
268
|
-
const registry = {
|
|
269
|
-
postSchema: [
|
|
270
|
-
{
|
|
271
|
-
type: 'string' as const,
|
|
272
|
-
name: 'title',
|
|
273
|
-
label: 'Title',
|
|
274
|
-
required: true,
|
|
275
|
-
},
|
|
276
|
-
],
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
await expect(validateEntrySchemaRegistry(registry, testDir)).resolves.toBeUndefined()
|
|
280
|
-
})
|
|
281
|
-
})
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import type { EntrySchema } from './config'
|
|
2
|
-
import type { EntrySchemaRegistry } from './schema/types'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Creates a type-safe entry schema registry with runtime validation.
|
|
6
|
-
*
|
|
7
|
-
* Maps entry schema names to their field definitions. These names are
|
|
8
|
-
* referenced by `.collection.json` files via the `"schema"` property.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```typescript
|
|
12
|
-
* import { createEntrySchemaRegistry } from 'canopycms/server'
|
|
13
|
-
*
|
|
14
|
-
* export const entrySchemaRegistry = createEntrySchemaRegistry({
|
|
15
|
-
* postSchema: [
|
|
16
|
-
* { type: 'string', name: 'title', label: 'Title', required: true },
|
|
17
|
-
* { type: 'markdown', name: 'body', label: 'Body' },
|
|
18
|
-
* ],
|
|
19
|
-
* authorSchema: [
|
|
20
|
-
* { type: 'string', name: 'name', label: 'Name', required: true },
|
|
21
|
-
* ],
|
|
22
|
-
* })
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
export function createEntrySchemaRegistry<T extends Record<string, EntrySchema>>(registry: T): T {
|
|
26
|
-
// Validate that registry is not empty
|
|
27
|
-
if (!registry || typeof registry !== 'object') {
|
|
28
|
-
throw new Error('Entry schema registry must be an object')
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const keys = Object.keys(registry)
|
|
32
|
-
if (keys.length === 0) {
|
|
33
|
-
throw new Error('Entry schema registry cannot be empty')
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Validate each entry schema
|
|
37
|
-
for (const [key, schema] of Object.entries(registry)) {
|
|
38
|
-
if (!Array.isArray(schema)) {
|
|
39
|
-
throw new Error(`Entry schema registry entry "${key}" must be an array of FieldConfig`)
|
|
40
|
-
}
|
|
41
|
-
if (schema.length === 0) {
|
|
42
|
-
throw new Error(`Entry schema registry entry "${key}" cannot be empty`)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return registry
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Validates that entry schema references in .collection.json files exist in the registry.
|
|
51
|
-
*
|
|
52
|
-
* Useful for build-time validation to catch schema reference errors early
|
|
53
|
-
* rather than at runtime on first request.
|
|
54
|
-
*
|
|
55
|
-
* @param entrySchemaRegistry - The entry schema registry mapping names to field definitions
|
|
56
|
-
* @param contentPath - Path to the content directory containing .collection.json files
|
|
57
|
-
* @returns Promise that resolves if validation passes, rejects with descriptive error if not
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```typescript
|
|
61
|
-
* import { validateEntrySchemaRegistry } from 'canopycms/server'
|
|
62
|
-
* import { entrySchemaRegistry } from './schemas'
|
|
63
|
-
*
|
|
64
|
-
* await validateEntrySchemaRegistry(entrySchemaRegistry, './content')
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
67
|
-
export async function validateEntrySchemaRegistry(
|
|
68
|
-
entrySchemaRegistry: EntrySchemaRegistry,
|
|
69
|
-
contentPath: string,
|
|
70
|
-
): Promise<void> {
|
|
71
|
-
const { loadCollectionMetaFiles } = await import('./schema')
|
|
72
|
-
const { access } = await import('fs/promises')
|
|
73
|
-
|
|
74
|
-
// Check if content directory exists
|
|
75
|
-
try {
|
|
76
|
-
await access(contentPath)
|
|
77
|
-
} catch (err) {
|
|
78
|
-
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
79
|
-
throw new Error(`Content directory not found: ${contentPath}`)
|
|
80
|
-
}
|
|
81
|
-
throw err
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Load all .collection.json files
|
|
85
|
-
const metaFiles = await loadCollectionMetaFiles(contentPath)
|
|
86
|
-
|
|
87
|
-
const availableSchemas = Object.keys(entrySchemaRegistry)
|
|
88
|
-
const errors: string[] = []
|
|
89
|
-
|
|
90
|
-
// Validate root entry type references
|
|
91
|
-
if (metaFiles.root?.entries) {
|
|
92
|
-
for (const entryType of metaFiles.root.entries) {
|
|
93
|
-
if (!entrySchemaRegistry[entryType.schema]) {
|
|
94
|
-
errors.push(
|
|
95
|
-
`Root entry type "${entryType.name}" references entry schema "${entryType.schema}" which does not exist in registry. ` +
|
|
96
|
-
`Available: ${availableSchemas.join(', ')}`,
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Validate collection entry type references
|
|
103
|
-
for (const collection of metaFiles.collections) {
|
|
104
|
-
if (collection.entries) {
|
|
105
|
-
for (const entryType of collection.entries) {
|
|
106
|
-
if (!entrySchemaRegistry[entryType.schema]) {
|
|
107
|
-
errors.push(
|
|
108
|
-
`Entry type "${entryType.name}" in collection "${collection.name}" (${collection.path}) references entry schema "${entryType.schema}" which does not exist in registry. ` +
|
|
109
|
-
`Available: ${availableSchemas.join(', ')}`,
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (errors.length > 0) {
|
|
117
|
-
throw new Error(
|
|
118
|
-
`Entry schema registry validation failed:\n${errors.map((e) => ` - ${e}`).join('\n')}`,
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
}
|
package/src/entry-schema.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
type InferableField = {
|
|
2
|
-
name: string
|
|
3
|
-
type: string
|
|
4
|
-
required?: boolean
|
|
5
|
-
list?: boolean
|
|
6
|
-
fields?: readonly InferableField[]
|
|
7
|
-
templates?: ReadonlyArray<{ name: string; fields: readonly InferableField[] }>
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
type RequiredValue<F extends InferableField, V> = F['required'] extends false ? V | undefined : V
|
|
11
|
-
|
|
12
|
-
type ScalarValue<F extends InferableField, V> = RequiredValue<F, F['list'] extends true ? V[] : V>
|
|
13
|
-
|
|
14
|
-
type ObjectValue<F extends InferableField & { fields: readonly InferableField[] }> = RequiredValue<
|
|
15
|
-
F,
|
|
16
|
-
F['list'] extends true ? Array<InferContentShape<F['fields']>> : InferContentShape<F['fields']>
|
|
17
|
-
>
|
|
18
|
-
|
|
19
|
-
type BlockValue<
|
|
20
|
-
F extends InferableField & {
|
|
21
|
-
templates: ReadonlyArray<{
|
|
22
|
-
name: string
|
|
23
|
-
fields: readonly InferableField[]
|
|
24
|
-
}>
|
|
25
|
-
},
|
|
26
|
-
> = RequiredValue<
|
|
27
|
-
F,
|
|
28
|
-
Array<
|
|
29
|
-
F['templates'][number] extends { name: infer N; fields: infer Fields }
|
|
30
|
-
? {
|
|
31
|
-
template: N & string
|
|
32
|
-
value: InferContentShape<Extract<Fields, readonly InferableField[]>>
|
|
33
|
-
}
|
|
34
|
-
: never
|
|
35
|
-
>
|
|
36
|
-
>
|
|
37
|
-
|
|
38
|
-
type FieldValue<F extends InferableField> = F extends {
|
|
39
|
-
type: 'object'
|
|
40
|
-
fields: infer Fields
|
|
41
|
-
}
|
|
42
|
-
? ObjectValue<F & { fields: Extract<Fields, readonly InferableField[]> }>
|
|
43
|
-
: F extends { type: 'block'; templates: infer Templates }
|
|
44
|
-
? BlockValue<
|
|
45
|
-
F & {
|
|
46
|
-
templates: NonNullable<
|
|
47
|
-
Extract<Templates, ReadonlyArray<{ name: string; fields: readonly InferableField[] }>>
|
|
48
|
-
>
|
|
49
|
-
}
|
|
50
|
-
>
|
|
51
|
-
: F extends { type: 'select' }
|
|
52
|
-
? ScalarValue<F, string | number>
|
|
53
|
-
: F extends { type: 'reference' }
|
|
54
|
-
? ScalarValue<F, Record<string, unknown> | null>
|
|
55
|
-
: F extends { type: 'boolean' }
|
|
56
|
-
? ScalarValue<F, boolean>
|
|
57
|
-
: F extends { type: 'number' }
|
|
58
|
-
? ScalarValue<F, number>
|
|
59
|
-
: F extends { type: 'date' }
|
|
60
|
-
? ScalarValue<F, string>
|
|
61
|
-
: ScalarValue<F, string>
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Infer a TypeScript data shape from a CanopyCMS FieldConfig-like array.
|
|
65
|
-
* - Objects become nested objects
|
|
66
|
-
* - Blocks become arrays of tagged templates with their value shapes
|
|
67
|
-
* - Lists become arrays of the scalar/object type
|
|
68
|
-
* - Non-required fields include `undefined`
|
|
69
|
-
*
|
|
70
|
-
* Works with any structurally compatible array; importing FieldConfig is not required.
|
|
71
|
-
*/
|
|
72
|
-
type InferContentShape<Fields extends readonly InferableField[]> = {
|
|
73
|
-
[F in Fields[number] as F['name']]: FieldValue<F>
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Helper to define entry schema field arrays with literal inference without sprinkling `as const`.
|
|
78
|
-
*/
|
|
79
|
-
export const defineEntrySchema = <const T extends readonly InferableField[]>(fields: T): T => fields
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Convenience alias to derive the content shape from a `defineEntrySchema` result.
|
|
83
|
-
*/
|
|
84
|
-
export type TypeFromEntrySchema<T extends readonly InferableField[]> = InferContentShape<T>
|