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,263 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { traverseFields, findFieldsByType } from '../field-traversal'
|
|
3
|
-
import type { FieldConfig } from '../../config'
|
|
4
|
-
|
|
5
|
-
describe('field-traversal', () => {
|
|
6
|
-
describe('traverseFields', () => {
|
|
7
|
-
it('visits simple fields', () => {
|
|
8
|
-
const schema: FieldConfig[] = [
|
|
9
|
-
{ name: 'title', type: 'string', label: 'Title' },
|
|
10
|
-
{ name: 'count', type: 'number', label: 'Count' },
|
|
11
|
-
]
|
|
12
|
-
const data = { title: 'Hello', count: 42 }
|
|
13
|
-
|
|
14
|
-
const visited: string[] = []
|
|
15
|
-
traverseFields(schema, data, ({ field, path }) => {
|
|
16
|
-
visited.push(`${path}:${field.type}`)
|
|
17
|
-
return []
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
expect(visited).toEqual(['title:string', 'count:number'])
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('skips undefined and null values', () => {
|
|
24
|
-
const schema: FieldConfig[] = [
|
|
25
|
-
{ name: 'title', type: 'string', label: 'Title' },
|
|
26
|
-
{ name: 'subtitle', type: 'string', label: 'Subtitle' },
|
|
27
|
-
{ name: 'count', type: 'number', label: 'Count' },
|
|
28
|
-
]
|
|
29
|
-
const data = { title: 'Hello', subtitle: null, count: undefined }
|
|
30
|
-
|
|
31
|
-
const visited: string[] = []
|
|
32
|
-
traverseFields(schema, data, ({ path }) => {
|
|
33
|
-
visited.push(path)
|
|
34
|
-
return []
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
expect(visited).toEqual(['title'])
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('recurses into object fields', () => {
|
|
41
|
-
const schema: FieldConfig[] = [
|
|
42
|
-
{
|
|
43
|
-
name: 'author',
|
|
44
|
-
type: 'object',
|
|
45
|
-
label: 'Author',
|
|
46
|
-
fields: [
|
|
47
|
-
{ name: 'name', type: 'string', label: 'Name' },
|
|
48
|
-
{ name: 'email', type: 'string', label: 'Email' },
|
|
49
|
-
],
|
|
50
|
-
},
|
|
51
|
-
]
|
|
52
|
-
const data = {
|
|
53
|
-
author: { name: 'Alice', email: 'alice@example.com' },
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const visited: string[] = []
|
|
57
|
-
traverseFields(schema, data, ({ path, field }) => {
|
|
58
|
-
visited.push(`${path}:${field.type}`)
|
|
59
|
-
return []
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
expect(visited).toEqual(['author:object', 'author.name:string', 'author.email:string'])
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('recurses into block fields', () => {
|
|
66
|
-
const schema: FieldConfig[] = [
|
|
67
|
-
{
|
|
68
|
-
name: 'blocks',
|
|
69
|
-
type: 'block',
|
|
70
|
-
label: 'Blocks',
|
|
71
|
-
templates: [
|
|
72
|
-
{
|
|
73
|
-
name: 'text',
|
|
74
|
-
label: 'Text Block',
|
|
75
|
-
fields: [{ name: 'content', type: 'string', label: 'Content' }],
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
name: 'image',
|
|
79
|
-
label: 'Image Block',
|
|
80
|
-
fields: [{ name: 'src', type: 'string', label: 'Source' }],
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
},
|
|
84
|
-
]
|
|
85
|
-
const data = {
|
|
86
|
-
blocks: [
|
|
87
|
-
{ _type: 'text', content: 'Hello' },
|
|
88
|
-
{ _type: 'image', src: '/img.png' },
|
|
89
|
-
],
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const visited: string[] = []
|
|
93
|
-
traverseFields(schema, data, ({ path, field }) => {
|
|
94
|
-
visited.push(`${path}:${field.type}`)
|
|
95
|
-
return []
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
expect(visited).toEqual(['blocks:block', 'blocks[0].content:string', 'blocks[1].src:string'])
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('collects results from visitor', () => {
|
|
102
|
-
const schema: FieldConfig[] = [
|
|
103
|
-
{
|
|
104
|
-
name: 'author',
|
|
105
|
-
type: 'reference',
|
|
106
|
-
label: 'Author',
|
|
107
|
-
collections: ['authors'],
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
name: 'tags',
|
|
111
|
-
type: 'reference',
|
|
112
|
-
label: 'Tags',
|
|
113
|
-
collections: ['tags'],
|
|
114
|
-
},
|
|
115
|
-
{ name: 'title', type: 'string', label: 'Title' },
|
|
116
|
-
]
|
|
117
|
-
const data = {
|
|
118
|
-
author: 'author-123',
|
|
119
|
-
tags: ['tag-1', 'tag-2'],
|
|
120
|
-
title: 'My Post',
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const refs = traverseFields(schema, data, ({ field, value, path }) => {
|
|
124
|
-
if (field.type === 'reference') {
|
|
125
|
-
return [{ path, value }]
|
|
126
|
-
}
|
|
127
|
-
return []
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
expect(refs).toEqual([
|
|
131
|
-
{ path: 'author', value: 'author-123' },
|
|
132
|
-
{ path: 'tags', value: ['tag-1', 'tag-2'] },
|
|
133
|
-
])
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('handles deeply nested structures', () => {
|
|
137
|
-
const schema: FieldConfig[] = [
|
|
138
|
-
{
|
|
139
|
-
name: 'sections',
|
|
140
|
-
type: 'block',
|
|
141
|
-
label: 'Sections',
|
|
142
|
-
templates: [
|
|
143
|
-
{
|
|
144
|
-
name: 'content',
|
|
145
|
-
label: 'Content Section',
|
|
146
|
-
fields: [
|
|
147
|
-
{
|
|
148
|
-
name: 'metadata',
|
|
149
|
-
type: 'object',
|
|
150
|
-
label: 'Metadata',
|
|
151
|
-
fields: [
|
|
152
|
-
{
|
|
153
|
-
name: 'ref',
|
|
154
|
-
type: 'reference',
|
|
155
|
-
label: 'Reference',
|
|
156
|
-
collections: ['docs'],
|
|
157
|
-
},
|
|
158
|
-
],
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
},
|
|
164
|
-
]
|
|
165
|
-
const data = {
|
|
166
|
-
sections: [
|
|
167
|
-
{
|
|
168
|
-
_type: 'content',
|
|
169
|
-
metadata: { ref: 'doc-1' },
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
_type: 'content',
|
|
173
|
-
metadata: { ref: 'doc-2' },
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const refs = traverseFields(schema, data, ({ field, value, path }) => {
|
|
179
|
-
if (field.type === 'reference') {
|
|
180
|
-
return [{ path, value }]
|
|
181
|
-
}
|
|
182
|
-
return []
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
expect(refs).toEqual([
|
|
186
|
-
{ path: 'sections[0].metadata.ref', value: 'doc-1' },
|
|
187
|
-
{ path: 'sections[1].metadata.ref', value: 'doc-2' },
|
|
188
|
-
])
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
it('handles missing block template gracefully', () => {
|
|
192
|
-
const schema: FieldConfig[] = [
|
|
193
|
-
{
|
|
194
|
-
name: 'blocks',
|
|
195
|
-
type: 'block',
|
|
196
|
-
label: 'Blocks',
|
|
197
|
-
templates: [
|
|
198
|
-
{
|
|
199
|
-
name: 'text',
|
|
200
|
-
label: 'Text',
|
|
201
|
-
fields: [{ name: 'content', type: 'string', label: 'Content' }],
|
|
202
|
-
},
|
|
203
|
-
],
|
|
204
|
-
},
|
|
205
|
-
]
|
|
206
|
-
const data = {
|
|
207
|
-
blocks: [
|
|
208
|
-
{ _type: 'unknown', content: 'Hello' }, // Unknown type
|
|
209
|
-
{ _type: 'text', content: 'World' },
|
|
210
|
-
],
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const visited: string[] = []
|
|
214
|
-
traverseFields(schema, data, ({ path }) => {
|
|
215
|
-
visited.push(path)
|
|
216
|
-
return []
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
// Only the valid block should have its content visited
|
|
220
|
-
expect(visited).toEqual(['blocks', 'blocks[1].content'])
|
|
221
|
-
})
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
describe('findFieldsByType', () => {
|
|
225
|
-
it('finds all reference fields', () => {
|
|
226
|
-
const schema: FieldConfig[] = [
|
|
227
|
-
{ name: 'title', type: 'string', label: 'Title' },
|
|
228
|
-
{
|
|
229
|
-
name: 'author',
|
|
230
|
-
type: 'reference',
|
|
231
|
-
label: 'Author',
|
|
232
|
-
collections: ['authors'],
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
name: 'meta',
|
|
236
|
-
type: 'object',
|
|
237
|
-
label: 'Meta',
|
|
238
|
-
fields: [
|
|
239
|
-
{
|
|
240
|
-
name: 'reviewer',
|
|
241
|
-
type: 'reference',
|
|
242
|
-
label: 'Reviewer',
|
|
243
|
-
collections: ['users'],
|
|
244
|
-
},
|
|
245
|
-
],
|
|
246
|
-
},
|
|
247
|
-
]
|
|
248
|
-
const data = {
|
|
249
|
-
title: 'My Post',
|
|
250
|
-
author: 'author-1',
|
|
251
|
-
meta: { reviewer: 'user-1' },
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const refs = findFieldsByType(schema, data, 'reference')
|
|
255
|
-
|
|
256
|
-
expect(refs).toHaveLength(2)
|
|
257
|
-
expect(refs[0].path).toBe('author')
|
|
258
|
-
expect(refs[0].value).toBe('author-1')
|
|
259
|
-
expect(refs[1].path).toBe('meta.reviewer')
|
|
260
|
-
expect(refs[1].value).toBe('user-1')
|
|
261
|
-
})
|
|
262
|
-
})
|
|
263
|
-
})
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
import type { ContentStore } from '../content-store'
|
|
2
|
-
import type { ContentIdIndex } from '../content-id-index'
|
|
3
|
-
import type { FieldConfig, ObjectFieldConfig, BlockFieldConfig } from '../config'
|
|
4
|
-
import { type LogicalPath, type EntrySlug, type PhysicalPath } from '../paths'
|
|
5
|
-
|
|
6
|
-
export interface ReferenceInfo {
|
|
7
|
-
entryPath: string
|
|
8
|
-
entryTitle?: string
|
|
9
|
-
collection: LogicalPath
|
|
10
|
-
slug: EntrySlug
|
|
11
|
-
fields: string[] // Field paths where the reference was found
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface DeletionCheckResult {
|
|
15
|
-
canDelete: boolean
|
|
16
|
-
referencedBy: ReferenceInfo[]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* DeletionChecker finds all references to an entry before deletion.
|
|
21
|
-
*
|
|
22
|
-
* This class provides referential integrity checking by:
|
|
23
|
-
* - Scanning all entries for references to a target ID
|
|
24
|
-
* - Identifying which entries and fields reference the target
|
|
25
|
-
* - Preventing deletion of entries that are still referenced
|
|
26
|
-
*
|
|
27
|
-
* Usage:
|
|
28
|
-
* const checker = new DeletionChecker(store, idIndex, schema)
|
|
29
|
-
* const result = await checker.canDelete(targetId)
|
|
30
|
-
* if (!result.canDelete) {
|
|
31
|
-
* console.log('Cannot delete:', result.referencedBy)
|
|
32
|
-
* }
|
|
33
|
-
*/
|
|
34
|
-
export class DeletionChecker {
|
|
35
|
-
constructor(
|
|
36
|
-
private store: ContentStore,
|
|
37
|
-
private idIndex: ContentIdIndex,
|
|
38
|
-
private collections: Map<LogicalPath, { fields: FieldConfig[] }>,
|
|
39
|
-
) {}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Check if an entry can be safely deleted.
|
|
43
|
-
*
|
|
44
|
-
* @param id - The content ID to check
|
|
45
|
-
* @returns Result indicating if deletion is safe and what references exist
|
|
46
|
-
*/
|
|
47
|
-
async canDelete(id: string): Promise<DeletionCheckResult> {
|
|
48
|
-
const referencedBy = await this.findReferences(id)
|
|
49
|
-
return {
|
|
50
|
-
canDelete: referencedBy.length === 0,
|
|
51
|
-
referencedBy,
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Find all entries that reference the target ID.
|
|
57
|
-
*
|
|
58
|
-
* @param targetId - The content ID to search for
|
|
59
|
-
* @returns Array of reference info for each referencing entry
|
|
60
|
-
*/
|
|
61
|
-
async findReferences(targetId: string): Promise<ReferenceInfo[]> {
|
|
62
|
-
const references: ReferenceInfo[] = []
|
|
63
|
-
|
|
64
|
-
// Scan all collections for references
|
|
65
|
-
for (const [collectionPath, collectionDef] of this.collections.entries()) {
|
|
66
|
-
const refs = await this.scanCollection(collectionPath, collectionDef.fields, targetId)
|
|
67
|
-
references.push(...refs)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return references
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Scan a single collection for references to the target ID.
|
|
75
|
-
*/
|
|
76
|
-
private async scanCollection(
|
|
77
|
-
collectionPath: LogicalPath,
|
|
78
|
-
fields: FieldConfig[],
|
|
79
|
-
targetId: string,
|
|
80
|
-
): Promise<ReferenceInfo[]> {
|
|
81
|
-
const references: ReferenceInfo[] = []
|
|
82
|
-
|
|
83
|
-
// Get all entries in this collection from the ID index
|
|
84
|
-
const entries = this.listEntriesInCollection(collectionPath)
|
|
85
|
-
|
|
86
|
-
for (const entry of entries) {
|
|
87
|
-
try {
|
|
88
|
-
const doc = await this.store.read(entry.collection, entry.slug)
|
|
89
|
-
const refs = this.findIdInData(doc.data, targetId, fields)
|
|
90
|
-
|
|
91
|
-
if (refs.length > 0) {
|
|
92
|
-
const id = this.idIndex.findByPath(entry.relativePath)
|
|
93
|
-
references.push({
|
|
94
|
-
entryPath: id || '',
|
|
95
|
-
entryTitle: doc.data.title as string | undefined,
|
|
96
|
-
collection: entry.collection,
|
|
97
|
-
slug: entry.slug,
|
|
98
|
-
fields: refs,
|
|
99
|
-
})
|
|
100
|
-
}
|
|
101
|
-
} catch {
|
|
102
|
-
// Skip entries that can't be read
|
|
103
|
-
continue
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return references
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Find all occurrences of target ID in entry data.
|
|
112
|
-
* Returns field paths where the ID was found.
|
|
113
|
-
*/
|
|
114
|
-
private findIdInData(
|
|
115
|
-
data: Record<string, unknown>,
|
|
116
|
-
targetId: string,
|
|
117
|
-
schema: FieldConfig[],
|
|
118
|
-
pathPrefix = '',
|
|
119
|
-
): string[] {
|
|
120
|
-
const found: string[] = []
|
|
121
|
-
|
|
122
|
-
for (const field of schema) {
|
|
123
|
-
const fieldPath = pathPrefix ? `${pathPrefix}.${field.name}` : field.name
|
|
124
|
-
const value = data[field.name]
|
|
125
|
-
|
|
126
|
-
if (value === undefined || value === null) continue
|
|
127
|
-
|
|
128
|
-
if (field.type === 'reference') {
|
|
129
|
-
// Check if this reference field contains the target ID
|
|
130
|
-
if (Array.isArray(value)) {
|
|
131
|
-
if (value.includes(targetId)) {
|
|
132
|
-
found.push(fieldPath)
|
|
133
|
-
}
|
|
134
|
-
} else if (value === targetId) {
|
|
135
|
-
found.push(fieldPath)
|
|
136
|
-
}
|
|
137
|
-
} else if (field.type === 'object') {
|
|
138
|
-
// Recurse into object fields
|
|
139
|
-
const objectField = field as ObjectFieldConfig
|
|
140
|
-
if (objectField.fields && typeof value === 'object' && !Array.isArray(value)) {
|
|
141
|
-
found.push(
|
|
142
|
-
...this.findIdInData(
|
|
143
|
-
value as Record<string, unknown>,
|
|
144
|
-
targetId,
|
|
145
|
-
objectField.fields,
|
|
146
|
-
fieldPath,
|
|
147
|
-
),
|
|
148
|
-
)
|
|
149
|
-
}
|
|
150
|
-
} else if (field.type === 'block') {
|
|
151
|
-
// Handle block fields
|
|
152
|
-
const blockField = field as BlockFieldConfig
|
|
153
|
-
if (Array.isArray(value)) {
|
|
154
|
-
value.forEach((item, index) => {
|
|
155
|
-
if (typeof item === 'object' && item !== null) {
|
|
156
|
-
const blockType = (item as Record<string, unknown>)._type
|
|
157
|
-
const blockDef = blockField.templates?.find((b) => b.name === blockType)
|
|
158
|
-
if (blockDef && blockDef.fields) {
|
|
159
|
-
found.push(
|
|
160
|
-
...this.findIdInData(
|
|
161
|
-
item as Record<string, unknown>,
|
|
162
|
-
targetId,
|
|
163
|
-
blockDef.fields,
|
|
164
|
-
`${fieldPath}[${index}]`,
|
|
165
|
-
),
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
} else if (field.type === 'array') {
|
|
172
|
-
// Handle array fields
|
|
173
|
-
const arrayField = field as FieldConfig & {
|
|
174
|
-
of?: { type: string; fields?: FieldConfig[] }
|
|
175
|
-
}
|
|
176
|
-
if (Array.isArray(value) && arrayField.of) {
|
|
177
|
-
const ofFields = arrayField.of.fields
|
|
178
|
-
if (arrayField.of.type === 'object' && ofFields) {
|
|
179
|
-
value.forEach((item, index) => {
|
|
180
|
-
if (typeof item === 'object' && item !== null) {
|
|
181
|
-
found.push(
|
|
182
|
-
...this.findIdInData(
|
|
183
|
-
item as Record<string, unknown>,
|
|
184
|
-
targetId,
|
|
185
|
-
ofFields,
|
|
186
|
-
`${fieldPath}[${index}]`,
|
|
187
|
-
),
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
})
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return found
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Helper to list all entries in a collection from the ID index.
|
|
201
|
-
*/
|
|
202
|
-
private listEntriesInCollection(collectionPath: LogicalPath): Array<{
|
|
203
|
-
relativePath: PhysicalPath
|
|
204
|
-
collection: LogicalPath
|
|
205
|
-
slug: EntrySlug
|
|
206
|
-
}> {
|
|
207
|
-
const entries: Array<{
|
|
208
|
-
relativePath: PhysicalPath
|
|
209
|
-
collection: LogicalPath
|
|
210
|
-
slug: EntrySlug
|
|
211
|
-
}> = []
|
|
212
|
-
|
|
213
|
-
// Get all locations from the index
|
|
214
|
-
const allLocations = this.idIndex.getAllLocations()
|
|
215
|
-
|
|
216
|
-
for (const location of allLocations) {
|
|
217
|
-
if (location.type === 'entry') {
|
|
218
|
-
// Check if this entry is in the target collection
|
|
219
|
-
if (
|
|
220
|
-
location.collection === collectionPath ||
|
|
221
|
-
location.collection?.startsWith(collectionPath + '/')
|
|
222
|
-
) {
|
|
223
|
-
entries.push({
|
|
224
|
-
relativePath: location.relativePath,
|
|
225
|
-
collection: location.collection,
|
|
226
|
-
slug: location.slug!,
|
|
227
|
-
})
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return entries
|
|
233
|
-
}
|
|
234
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared field traversal utility for schema-aware data traversal.
|
|
3
|
-
*
|
|
4
|
-
* This module provides a generic way to traverse nested data structures
|
|
5
|
-
* according to a schema, handling objects, blocks, and arrays with their
|
|
6
|
-
* own schemas.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { FieldConfig, ObjectFieldConfig, BlockFieldConfig } from '../config'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Context provided to the visitor function for each field.
|
|
13
|
-
*/
|
|
14
|
-
export interface TraversalContext {
|
|
15
|
-
/** The field configuration from the schema */
|
|
16
|
-
field: FieldConfig
|
|
17
|
-
/** The value at this field in the data */
|
|
18
|
-
value: unknown
|
|
19
|
-
/** The dot-notation path to this field (e.g., "author.name" or "blocks[0].title") */
|
|
20
|
-
path: string
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Visitor function that receives each field and can return results.
|
|
25
|
-
* Return an empty array to skip this field, or return items to collect.
|
|
26
|
-
*/
|
|
27
|
-
export type FieldVisitor<T> = (context: TraversalContext) => T[]
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Get the nested fields for a block template by looking up _type.
|
|
31
|
-
*/
|
|
32
|
-
function getBlockTemplateFields(
|
|
33
|
-
blockField: BlockFieldConfig,
|
|
34
|
-
item: Record<string, unknown>,
|
|
35
|
-
): FieldConfig[] | undefined {
|
|
36
|
-
const blockType = item._type as string | undefined
|
|
37
|
-
if (!blockType) return undefined
|
|
38
|
-
|
|
39
|
-
// Block fields use 'templates' property
|
|
40
|
-
const template = blockField.templates?.find((t) => t.name === blockType)
|
|
41
|
-
return template?.fields
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Recursively traverse fields in data according to schema.
|
|
46
|
-
*
|
|
47
|
-
* This function walks through data following the schema structure, calling
|
|
48
|
-
* the visitor function for each field. It handles:
|
|
49
|
-
* - Simple fields (string, number, boolean, reference, etc.)
|
|
50
|
-
* - Object fields with nested schemas
|
|
51
|
-
* - Block fields (arrays of typed objects with different schemas)
|
|
52
|
-
* - Array fields containing objects with schemas
|
|
53
|
-
*
|
|
54
|
-
* @param fields - The schema fields to traverse
|
|
55
|
-
* @param data - The data object to traverse
|
|
56
|
-
* @param visitor - Function called for each field, returns items to collect
|
|
57
|
-
* @param pathPrefix - Current path prefix for nested fields
|
|
58
|
-
* @returns Array of all items returned by the visitor
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* ```ts
|
|
62
|
-
* // Find all reference field values
|
|
63
|
-
* const refs = traverseFields(schema, data, ({ field, value, path }) => {
|
|
64
|
-
* if (field.type === 'reference') {
|
|
65
|
-
* return [{ path, ids: Array.isArray(value) ? value : [value] }]
|
|
66
|
-
* }
|
|
67
|
-
* return []
|
|
68
|
-
* })
|
|
69
|
-
* ```
|
|
70
|
-
*/
|
|
71
|
-
export function traverseFields<T>(
|
|
72
|
-
fields: readonly FieldConfig[],
|
|
73
|
-
data: Record<string, unknown>,
|
|
74
|
-
visitor: FieldVisitor<T>,
|
|
75
|
-
pathPrefix = '',
|
|
76
|
-
): T[] {
|
|
77
|
-
const results: T[] = []
|
|
78
|
-
|
|
79
|
-
for (const field of fields) {
|
|
80
|
-
const fieldPath = pathPrefix ? `${pathPrefix}.${field.name}` : field.name
|
|
81
|
-
const value = data[field.name]
|
|
82
|
-
|
|
83
|
-
// Skip undefined/null values
|
|
84
|
-
if (value === undefined || value === null) continue
|
|
85
|
-
|
|
86
|
-
// Let visitor handle this field first
|
|
87
|
-
results.push(...visitor({ field, value, path: fieldPath }))
|
|
88
|
-
|
|
89
|
-
// Then recurse into nested structures
|
|
90
|
-
if (field.type === 'object') {
|
|
91
|
-
const objectField = field as ObjectFieldConfig
|
|
92
|
-
if (objectField.fields && typeof value === 'object' && !Array.isArray(value)) {
|
|
93
|
-
results.push(
|
|
94
|
-
...traverseFields(
|
|
95
|
-
objectField.fields,
|
|
96
|
-
value as Record<string, unknown>,
|
|
97
|
-
visitor,
|
|
98
|
-
fieldPath,
|
|
99
|
-
),
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
} else if (field.type === 'block') {
|
|
103
|
-
const blockField = field as BlockFieldConfig
|
|
104
|
-
if (Array.isArray(value)) {
|
|
105
|
-
value.forEach((item, index) => {
|
|
106
|
-
if (typeof item === 'object' && item !== null) {
|
|
107
|
-
const blockFields = getBlockTemplateFields(blockField, item as Record<string, unknown>)
|
|
108
|
-
if (blockFields) {
|
|
109
|
-
results.push(
|
|
110
|
-
...traverseFields(
|
|
111
|
-
blockFields,
|
|
112
|
-
item as Record<string, unknown>,
|
|
113
|
-
visitor,
|
|
114
|
-
`${fieldPath}[${index}]`,
|
|
115
|
-
),
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return results
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Find all fields of a specific type in the data.
|
|
129
|
-
*
|
|
130
|
-
* @param fields - The schema fields
|
|
131
|
-
* @param data - The data to search
|
|
132
|
-
* @param fieldType - The field type to find (e.g., 'reference', 'string')
|
|
133
|
-
* @returns Array of { field, value, path } for matching fields
|
|
134
|
-
*/
|
|
135
|
-
export function findFieldsByType(
|
|
136
|
-
fields: readonly FieldConfig[],
|
|
137
|
-
data: Record<string, unknown>,
|
|
138
|
-
fieldType: string,
|
|
139
|
-
): TraversalContext[] {
|
|
140
|
-
return traverseFields(fields, data, (ctx) => {
|
|
141
|
-
if (ctx.field.type === fieldType) {
|
|
142
|
-
return [ctx]
|
|
143
|
-
}
|
|
144
|
-
return []
|
|
145
|
-
})
|
|
146
|
-
}
|