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,189 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
|
|
4
|
-
import type { RootCollectionConfig } from './config'
|
|
5
|
-
import type { FlatSchemaItem } from './config/types'
|
|
6
|
-
import type { EntrySchemaRegistry } from './schema/types'
|
|
7
|
-
import type { OperatingMode } from './operating-mode'
|
|
8
|
-
import { resolveSchema, isValidSchema } from './schema/resolver'
|
|
9
|
-
import { flattenSchema } from './config/flatten'
|
|
10
|
-
|
|
11
|
-
/** Bump when BranchSchemaCacheEntry shape changes to auto-invalidate stale caches */
|
|
12
|
-
const SCHEMA_CACHE_VERSION = 2
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Schema cache structure stored in {branchRoot}/.canopy-meta/schema-cache.json
|
|
16
|
-
*/
|
|
17
|
-
export interface BranchSchemaCacheEntry {
|
|
18
|
-
version: number
|
|
19
|
-
schema: RootCollectionConfig
|
|
20
|
-
flatSchema: FlatSchemaItem[]
|
|
21
|
-
cachedAt: string // ISO timestamp
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Manages per-branch schema caching with lazy loading and automatic invalidation.
|
|
26
|
-
*
|
|
27
|
-
* Caching Strategy:
|
|
28
|
-
* - Prod/Prod-sim: File-based cache at {branchRoot}/.canopy-meta/schema-cache.json
|
|
29
|
-
* - Dev mode: In-memory singleton (no file I/O)
|
|
30
|
-
* - Invalidation: Writers create .stale marker, causing cache regeneration on next access
|
|
31
|
-
*
|
|
32
|
-
* Multi-User Support:
|
|
33
|
-
* - User A modifies schema via SchemaOps → writes .stale marker
|
|
34
|
-
* - User B loads schema later → sees .stale marker → regenerates cache
|
|
35
|
-
* - Atomic file operations prevent corruption during concurrent access
|
|
36
|
-
*/
|
|
37
|
-
export class BranchSchemaCache {
|
|
38
|
-
private devModeCache?: {
|
|
39
|
-
schema: RootCollectionConfig
|
|
40
|
-
flatSchema: FlatSchemaItem[]
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
constructor(private readonly mode: OperatingMode) {}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get schema for a branch (loads from cache or resolves fresh).
|
|
47
|
-
*
|
|
48
|
-
* @param branchRoot - Root directory of the branch (e.g., .canopy-prod-sim/content-branches/main)
|
|
49
|
-
* @param entrySchemaRegistry - Map of schema names to field definitions
|
|
50
|
-
* @param contentRootName - Name of content directory (e.g., "content") from config
|
|
51
|
-
* @returns Resolved schema tree and flattened schema
|
|
52
|
-
*/
|
|
53
|
-
async getSchema(
|
|
54
|
-
branchRoot: string,
|
|
55
|
-
entrySchemaRegistry: EntrySchemaRegistry,
|
|
56
|
-
contentRootName: string = 'content',
|
|
57
|
-
): Promise<{ schema: RootCollectionConfig; flatSchema: FlatSchemaItem[] }> {
|
|
58
|
-
// Dev mode: use in-memory singleton
|
|
59
|
-
if (this.mode === 'dev') {
|
|
60
|
-
if (!this.devModeCache) {
|
|
61
|
-
const contentRoot = path.join(branchRoot, contentRootName)
|
|
62
|
-
const result = await resolveSchema(contentRoot, entrySchemaRegistry)
|
|
63
|
-
|
|
64
|
-
// Validate schema has content
|
|
65
|
-
if (!isValidSchema(result.schema)) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
`No schema found in ${contentRoot}. Create .collection.json files ` +
|
|
68
|
-
'with references to field schemas defined in your entry schema registry.',
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Use configured contentRoot name as base path for logical paths
|
|
73
|
-
const flatSchema = flattenSchema(result.schema, contentRootName)
|
|
74
|
-
this.devModeCache = {
|
|
75
|
-
schema: result.schema,
|
|
76
|
-
flatSchema,
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return this.devModeCache
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Prod/prod-sim: use file-based cache with stale marker invalidation
|
|
83
|
-
return this.loadFromCacheOrResolve(branchRoot, entrySchemaRegistry, contentRootName)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Load schema from cache or resolve fresh if cache is missing or stale.
|
|
88
|
-
*/
|
|
89
|
-
private async loadFromCacheOrResolve(
|
|
90
|
-
branchRoot: string,
|
|
91
|
-
entrySchemaRegistry: EntrySchemaRegistry,
|
|
92
|
-
contentRootName: string,
|
|
93
|
-
): Promise<{ schema: RootCollectionConfig; flatSchema: FlatSchemaItem[] }> {
|
|
94
|
-
const contentRoot = path.join(branchRoot, contentRootName)
|
|
95
|
-
const cacheDir = path.join(branchRoot, '.canopy-meta')
|
|
96
|
-
const cachePath = path.join(cacheDir, 'schema-cache.json')
|
|
97
|
-
const stalePath = path.join(cacheDir, 'schema-cache.stale')
|
|
98
|
-
|
|
99
|
-
// Check if cache exists and is not marked stale
|
|
100
|
-
let cacheData: BranchSchemaCacheEntry | null = null
|
|
101
|
-
try {
|
|
102
|
-
const staleExists = await fs
|
|
103
|
-
.access(stalePath)
|
|
104
|
-
.then(() => true)
|
|
105
|
-
.catch(() => false)
|
|
106
|
-
if (!staleExists) {
|
|
107
|
-
const cacheContent = await fs.readFile(cachePath, 'utf-8')
|
|
108
|
-
cacheData = JSON.parse(cacheContent) as BranchSchemaCacheEntry
|
|
109
|
-
}
|
|
110
|
-
} catch {
|
|
111
|
-
// Cache doesn't exist or can't be read
|
|
112
|
-
cacheData = null
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (cacheData && cacheData.version === SCHEMA_CACHE_VERSION) {
|
|
116
|
-
return { schema: cacheData.schema, flatSchema: cacheData.flatSchema }
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Cache miss or stale - regenerate
|
|
120
|
-
const result = await resolveSchema(contentRoot, entrySchemaRegistry)
|
|
121
|
-
|
|
122
|
-
// Validate schema has content
|
|
123
|
-
if (!isValidSchema(result.schema)) {
|
|
124
|
-
throw new Error(
|
|
125
|
-
`No schema found in ${contentRoot}. Create .collection.json files ` +
|
|
126
|
-
'with references to field schemas defined in your entry schema registry.',
|
|
127
|
-
)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Use configured contentRoot name as base path for logical paths
|
|
131
|
-
const flatSchema = flattenSchema(result.schema, contentRootName)
|
|
132
|
-
|
|
133
|
-
// Save to cache
|
|
134
|
-
await fs.mkdir(cacheDir, { recursive: true })
|
|
135
|
-
const newCache: BranchSchemaCacheEntry = {
|
|
136
|
-
version: SCHEMA_CACHE_VERSION,
|
|
137
|
-
schema: result.schema,
|
|
138
|
-
flatSchema,
|
|
139
|
-
cachedAt: new Date().toISOString(),
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Atomic write: write to temp file, then rename
|
|
143
|
-
const tmpPath = path.join(cacheDir, `schema-cache.tmp.${Date.now()}.${Math.random()}.json`)
|
|
144
|
-
await fs.writeFile(tmpPath, JSON.stringify(newCache, null, 2), 'utf-8')
|
|
145
|
-
await fs.rename(tmpPath, cachePath)
|
|
146
|
-
|
|
147
|
-
// Remove stale marker if exists
|
|
148
|
-
try {
|
|
149
|
-
await fs.unlink(stalePath)
|
|
150
|
-
} catch {
|
|
151
|
-
// Stale marker may not exist - that's fine
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return { schema: result.schema, flatSchema }
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Invalidate cache for a branch (creates .stale marker).
|
|
159
|
-
*
|
|
160
|
-
* @param branchRoot - Root directory of the branch
|
|
161
|
-
*/
|
|
162
|
-
async invalidate(branchRoot: string): Promise<void> {
|
|
163
|
-
if (this.mode === 'dev') {
|
|
164
|
-
// Clear in-memory cache
|
|
165
|
-
this.devModeCache = undefined
|
|
166
|
-
return
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Prod/prod-sim: create stale marker (empty file)
|
|
170
|
-
const cacheDir = path.join(branchRoot, '.canopy-meta')
|
|
171
|
-
const stalePath = path.join(cacheDir, 'schema-cache.stale')
|
|
172
|
-
|
|
173
|
-
await fs.mkdir(cacheDir, { recursive: true })
|
|
174
|
-
await fs.writeFile(stalePath, '', 'utf-8')
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Clear all caches (for testing).
|
|
179
|
-
* In dev mode, clears in-memory cache.
|
|
180
|
-
* In prod/prod-sim modes, this would need to traverse all branch directories.
|
|
181
|
-
*/
|
|
182
|
-
async clearAll(): Promise<void> {
|
|
183
|
-
if (this.mode === 'dev') {
|
|
184
|
-
this.devModeCache = undefined
|
|
185
|
-
}
|
|
186
|
-
// For prod/prod-sim, clearing all caches would require knowing all branch roots
|
|
187
|
-
// For now, just clear dev mode cache. Tests can invalidate specific branches.
|
|
188
|
-
}
|
|
189
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import os from 'node:os'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
|
|
5
|
-
import { describe, expect, it } from 'vitest'
|
|
6
|
-
import { simpleGit } from 'simple-git'
|
|
7
|
-
|
|
8
|
-
import { BranchWorkspaceManager, loadBranchContext } from './branch-workspace'
|
|
9
|
-
import { defineCanopyTestConfig } from './config-test'
|
|
10
|
-
import { BranchRegistry } from './branch-registry'
|
|
11
|
-
import { initBareRepo } from './__integration__/test-utils/test-workspace'
|
|
12
|
-
|
|
13
|
-
const tmpDir = async () => fs.mkdtemp(path.join(os.tmpdir(), 'canopycms-branchws-'))
|
|
14
|
-
|
|
15
|
-
describe('BranchWorkspaceManager', () => {
|
|
16
|
-
it('dev mode does not support branching', async () => {
|
|
17
|
-
const root = await tmpDir()
|
|
18
|
-
const git = simpleGit({ baseDir: root })
|
|
19
|
-
await git.init()
|
|
20
|
-
const manager = new BranchWorkspaceManager(
|
|
21
|
-
defineCanopyTestConfig({
|
|
22
|
-
schema: {
|
|
23
|
-
collections: [
|
|
24
|
-
{
|
|
25
|
-
name: 'posts',
|
|
26
|
-
path: 'posts',
|
|
27
|
-
entries: [
|
|
28
|
-
{
|
|
29
|
-
name: 'post',
|
|
30
|
-
format: 'md',
|
|
31
|
-
schema: [{ name: 'title', type: 'string' }],
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
}),
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
// Dev mode should throw when trying to use branching functions
|
|
41
|
-
await expect(
|
|
42
|
-
manager.openOrCreateBranch({
|
|
43
|
-
branchName: 'feature/foo',
|
|
44
|
-
mode: 'dev',
|
|
45
|
-
basePathOverride: root,
|
|
46
|
-
createdBy: 'user-1',
|
|
47
|
-
title: 'Foo Feature',
|
|
48
|
-
}),
|
|
49
|
-
).rejects.toThrow('No branching in dev mode')
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('creates metadata and registry entry when opening a branch in multi-branch mode', async () => {
|
|
53
|
-
const root = await tmpDir()
|
|
54
|
-
const remotePath = path.join(root, 'remote.git')
|
|
55
|
-
const seedPath = path.join(root, 'seed')
|
|
56
|
-
|
|
57
|
-
// Set up a bare remote repo
|
|
58
|
-
await initBareRepo(remotePath)
|
|
59
|
-
await fs.mkdir(seedPath, { recursive: true })
|
|
60
|
-
const seedGit = simpleGit({ baseDir: seedPath })
|
|
61
|
-
await seedGit.init()
|
|
62
|
-
await seedGit.raw(['branch', '-M', 'main'])
|
|
63
|
-
await fs.writeFile(path.join(seedPath, 'README.md'), '# seed\n', 'utf8')
|
|
64
|
-
await seedGit.add(['.'])
|
|
65
|
-
await seedGit.commit('init')
|
|
66
|
-
await seedGit.addRemote('origin', remotePath)
|
|
67
|
-
await seedGit.push('origin', 'main', { '--set-upstream': null })
|
|
68
|
-
|
|
69
|
-
const manager = new BranchWorkspaceManager(
|
|
70
|
-
defineCanopyTestConfig({
|
|
71
|
-
defaultBaseBranch: 'main',
|
|
72
|
-
defaultRemoteUrl: remotePath,
|
|
73
|
-
schema: {
|
|
74
|
-
collections: [
|
|
75
|
-
{
|
|
76
|
-
name: 'posts',
|
|
77
|
-
path: 'posts',
|
|
78
|
-
entries: [
|
|
79
|
-
{
|
|
80
|
-
name: 'post',
|
|
81
|
-
format: 'md',
|
|
82
|
-
schema: [{ name: 'title', type: 'string' }],
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
},
|
|
86
|
-
],
|
|
87
|
-
},
|
|
88
|
-
}),
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
const workspace = await manager.openOrCreateBranch({
|
|
92
|
-
branchName: 'feature/foo',
|
|
93
|
-
mode: 'prod-sim',
|
|
94
|
-
basePathOverride: root,
|
|
95
|
-
createdBy: 'user-1',
|
|
96
|
-
title: 'Foo Feature',
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
// In prod-sim, strategy creates .canopy-prod-sim/content-branches structure
|
|
100
|
-
const expectedBranchesRoot = path.join(root, '.canopy-prod-sim', 'content-branches')
|
|
101
|
-
|
|
102
|
-
// Note: Still using .canopycms for now - Phase 2 will migrate to .canopy-meta
|
|
103
|
-
const metaFile = path.join(workspace.branchRoot, '.canopy-meta/branch.json')
|
|
104
|
-
const meta = JSON.parse(await fs.readFile(metaFile, 'utf8'))
|
|
105
|
-
expect(meta.branch.name).toBe('feature-foo')
|
|
106
|
-
expect(meta.branch.title).toBe('Foo Feature')
|
|
107
|
-
expect(workspace.branchRoot).toBeDefined()
|
|
108
|
-
expect(workspace.baseRoot).toBe(expectedBranchesRoot)
|
|
109
|
-
|
|
110
|
-
// In multi-branch mode, registry can scan subdirectories and find the branch
|
|
111
|
-
const registry = new BranchRegistry(expectedBranchesRoot)
|
|
112
|
-
const entry = await registry.get('feature-foo')
|
|
113
|
-
expect(entry?.branch.name).toBe('feature-foo')
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('clones a remote and checks out the branch when remoteUrl is provided', async () => {
|
|
117
|
-
const root = await tmpDir()
|
|
118
|
-
const remotePath = path.join(root, 'remote.git')
|
|
119
|
-
const seedPath = path.join(root, 'seed')
|
|
120
|
-
await initBareRepo(remotePath)
|
|
121
|
-
|
|
122
|
-
await fs.mkdir(seedPath, { recursive: true })
|
|
123
|
-
const seedGit = simpleGit({ baseDir: seedPath })
|
|
124
|
-
await seedGit.init()
|
|
125
|
-
await seedGit.raw(['branch', '-M', 'main'])
|
|
126
|
-
await fs.writeFile(path.join(seedPath, 'README.md'), '# seed\n', 'utf8')
|
|
127
|
-
await seedGit.add(['.'])
|
|
128
|
-
await seedGit.commit('init')
|
|
129
|
-
await seedGit.addRemote('origin', remotePath)
|
|
130
|
-
await seedGit.push('origin', 'main', { '--set-upstream': null })
|
|
131
|
-
|
|
132
|
-
const branchesRoot = path.join(root, 'branches')
|
|
133
|
-
const manager = new BranchWorkspaceManager(
|
|
134
|
-
defineCanopyTestConfig({
|
|
135
|
-
defaultBaseBranch: 'main',
|
|
136
|
-
defaultRemoteUrl: remotePath,
|
|
137
|
-
schema: {
|
|
138
|
-
collections: [
|
|
139
|
-
{
|
|
140
|
-
name: 'posts',
|
|
141
|
-
path: 'posts',
|
|
142
|
-
entries: [
|
|
143
|
-
{
|
|
144
|
-
name: 'post',
|
|
145
|
-
format: 'md',
|
|
146
|
-
schema: [{ name: 'title', type: 'string' }],
|
|
147
|
-
},
|
|
148
|
-
],
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
},
|
|
152
|
-
}),
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
const workspace = await manager.openOrCreateBranch({
|
|
156
|
-
branchName: 'feature/foo',
|
|
157
|
-
mode: 'prod-sim',
|
|
158
|
-
basePathOverride: branchesRoot,
|
|
159
|
-
createdBy: 'user-1',
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
const git = simpleGit({ baseDir: workspace.branchRoot })
|
|
163
|
-
const status = await git.status()
|
|
164
|
-
expect(status.current).toBe('feature-foo')
|
|
165
|
-
const remotes = await git.getRemotes(true)
|
|
166
|
-
expect(remotes.find((r) => r.name === 'origin')?.refs.fetch).toBe(remotePath)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
it('dev mode throws when trying to load branch state', async () => {
|
|
170
|
-
const root = await tmpDir()
|
|
171
|
-
const git = simpleGit({ baseDir: root })
|
|
172
|
-
await git.init()
|
|
173
|
-
|
|
174
|
-
// Dev mode doesn't support branching, so loadBranchContext should throw
|
|
175
|
-
await expect(
|
|
176
|
-
loadBranchContext({
|
|
177
|
-
branchName: 'main',
|
|
178
|
-
mode: 'dev',
|
|
179
|
-
basePathOverride: root,
|
|
180
|
-
}),
|
|
181
|
-
).rejects.toThrow('No branching in dev mode')
|
|
182
|
-
})
|
|
183
|
-
})
|
package/src/branch-workspace.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import type { CanopyConfig } from './config'
|
|
2
|
-
import { ensureBranchRoot } from './paths'
|
|
3
|
-
import { getBranchMetadataFileManager } from './branch-metadata'
|
|
4
|
-
import type { BranchAccessControl, BranchContext, CanopyUserId } from './types'
|
|
5
|
-
import type { OperatingMode } from './operating-mode'
|
|
6
|
-
import { GitManager } from './git-manager'
|
|
7
|
-
import { createDebugLogger } from './utils/debug'
|
|
8
|
-
|
|
9
|
-
const log = createDebugLogger({ prefix: 'BranchWorkspace' })
|
|
10
|
-
|
|
11
|
-
// In-memory lock to prevent concurrent workspace initialization
|
|
12
|
-
const workspaceInitLocks = new Map<string, Promise<void>>()
|
|
13
|
-
|
|
14
|
-
export interface OpenBranchOptions {
|
|
15
|
-
branchName: string
|
|
16
|
-
mode: OperatingMode
|
|
17
|
-
basePathOverride?: string
|
|
18
|
-
title?: string
|
|
19
|
-
description?: string
|
|
20
|
-
access?: BranchAccessControl
|
|
21
|
-
createdBy: CanopyUserId
|
|
22
|
-
remoteUrl?: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Manages per-branch filesystem workspace: resolves root, ensures metadata,
|
|
27
|
-
* and updates the branch registry.
|
|
28
|
-
*/
|
|
29
|
-
export class BranchWorkspaceManager {
|
|
30
|
-
private readonly config: CanopyConfig
|
|
31
|
-
|
|
32
|
-
constructor(config: CanopyConfig) {
|
|
33
|
-
this.config = config
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
private async ensureGitWorkspace(options: {
|
|
37
|
-
branchRoot: string
|
|
38
|
-
branchName: string
|
|
39
|
-
mode: OperatingMode
|
|
40
|
-
remoteUrl?: string
|
|
41
|
-
}) {
|
|
42
|
-
return log.timed('workspace', 'ensureGitWorkspace', async () => {
|
|
43
|
-
// Serialize access per branch workspace to prevent race conditions
|
|
44
|
-
const existingLock = workspaceInitLocks.get(options.branchRoot)
|
|
45
|
-
if (existingLock) {
|
|
46
|
-
await existingLock
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Create new lock promise
|
|
51
|
-
const lockPromise = (async () => {
|
|
52
|
-
try {
|
|
53
|
-
log.debug('workspace', 'Ensuring git workspace', {
|
|
54
|
-
branchName: options.branchName,
|
|
55
|
-
mode: options.mode,
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
// Delegate git initialization to GitManager
|
|
59
|
-
await GitManager.initializeWorkspace({
|
|
60
|
-
workspacePath: options.branchRoot,
|
|
61
|
-
branchName: options.branchName,
|
|
62
|
-
mode: options.mode,
|
|
63
|
-
baseBranch: this.config.defaultBaseBranch,
|
|
64
|
-
sourceRoot: this.config.sourceRoot,
|
|
65
|
-
defaultRemoteUrl: this.config.defaultRemoteUrl,
|
|
66
|
-
remoteUrl: options.remoteUrl,
|
|
67
|
-
remoteName: this.config.defaultRemoteName,
|
|
68
|
-
branchType: 'content',
|
|
69
|
-
})
|
|
70
|
-
} finally {
|
|
71
|
-
// Always clean up the lock when done (success or failure)
|
|
72
|
-
workspaceInitLocks.delete(options.branchRoot)
|
|
73
|
-
}
|
|
74
|
-
})()
|
|
75
|
-
|
|
76
|
-
// Store the lock promise
|
|
77
|
-
workspaceInitLocks.set(options.branchRoot, lockPromise)
|
|
78
|
-
|
|
79
|
-
// Wait for initialization to complete
|
|
80
|
-
await lockPromise
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async openOrCreateBranch(options: OpenBranchOptions): Promise<BranchContext> {
|
|
85
|
-
const { branchName, mode, basePathOverride, title, description, access, createdBy, remoteUrl } =
|
|
86
|
-
options
|
|
87
|
-
const {
|
|
88
|
-
branchRoot,
|
|
89
|
-
baseRoot,
|
|
90
|
-
branchName: safeName,
|
|
91
|
-
} = await ensureBranchRoot({
|
|
92
|
-
mode,
|
|
93
|
-
branchName,
|
|
94
|
-
basePathOverride,
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
await this.ensureGitWorkspace({
|
|
98
|
-
branchRoot,
|
|
99
|
-
branchName: safeName,
|
|
100
|
-
mode,
|
|
101
|
-
remoteUrl,
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
// save() handles both creation and updates, preserving existing values and invalidating registry
|
|
105
|
-
const metadata = getBranchMetadataFileManager(branchRoot, baseRoot)
|
|
106
|
-
const meta = await metadata.save({
|
|
107
|
-
branch: {
|
|
108
|
-
name: safeName,
|
|
109
|
-
title,
|
|
110
|
-
description,
|
|
111
|
-
access,
|
|
112
|
-
createdBy,
|
|
113
|
-
},
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
branch: meta.branch,
|
|
118
|
-
branchRoot,
|
|
119
|
-
baseRoot,
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export { loadBranchContext } from './branch-metadata'
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Static build utility for AI content generation.
|
|
3
|
-
*
|
|
4
|
-
* Writes generated AI content to disk as static files.
|
|
5
|
-
* Used during `npm run build` or via the CLI.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import fs from 'node:fs/promises'
|
|
9
|
-
import path from 'node:path'
|
|
10
|
-
|
|
11
|
-
import { ContentStore } from '../content-store'
|
|
12
|
-
import { BranchSchemaCache } from '../branch-schema-cache'
|
|
13
|
-
import type { CanopyConfig, FlatSchemaItem } from '../config'
|
|
14
|
-
import type { EntrySchemaRegistry } from '../schema/types'
|
|
15
|
-
import { generateAIContent } from '../ai/generate'
|
|
16
|
-
import { resolveBranchRoot } from '../ai/resolve-branch'
|
|
17
|
-
import type { AIContentConfig } from '../ai/types'
|
|
18
|
-
|
|
19
|
-
export interface GenerateAIContentFilesOptions {
|
|
20
|
-
config: CanopyConfig
|
|
21
|
-
entrySchemaRegistry: EntrySchemaRegistry
|
|
22
|
-
/** Output directory (e.g., 'public/ai') */
|
|
23
|
-
outputDir: string
|
|
24
|
-
aiConfig?: AIContentConfig
|
|
25
|
-
/** @internal Test-only: pre-resolved schema to bypass BranchSchemaCache */
|
|
26
|
-
_testFlatSchema?: FlatSchemaItem[]
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Generate AI content files and write them to disk.
|
|
31
|
-
*
|
|
32
|
-
* @returns Count of files written and the output directory.
|
|
33
|
-
*/
|
|
34
|
-
export async function generateAIContentFiles(
|
|
35
|
-
options: GenerateAIContentFilesOptions,
|
|
36
|
-
): Promise<{ fileCount: number; outputDir: string }> {
|
|
37
|
-
const { config, entrySchemaRegistry, outputDir, aiConfig, _testFlatSchema } = options
|
|
38
|
-
const contentRootName = config.contentRoot || 'content'
|
|
39
|
-
|
|
40
|
-
// Resolve branch root
|
|
41
|
-
const branchRoot = await resolveBranchRoot(config)
|
|
42
|
-
|
|
43
|
-
// Load schema
|
|
44
|
-
let flatSchema: FlatSchemaItem[]
|
|
45
|
-
if (_testFlatSchema) {
|
|
46
|
-
flatSchema = _testFlatSchema
|
|
47
|
-
} else {
|
|
48
|
-
const schemaCache = new BranchSchemaCache(config.mode)
|
|
49
|
-
const cached = await schemaCache.getSchema(branchRoot, entrySchemaRegistry, contentRootName)
|
|
50
|
-
flatSchema = cached.flatSchema
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Create store and generate
|
|
54
|
-
const store = new ContentStore(branchRoot, flatSchema)
|
|
55
|
-
const result = await generateAIContent({
|
|
56
|
-
store,
|
|
57
|
-
flatSchema,
|
|
58
|
-
contentRoot: contentRootName,
|
|
59
|
-
config: aiConfig,
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
// Write files to disk
|
|
63
|
-
const absoluteOutputDir = path.resolve(outputDir) + path.sep
|
|
64
|
-
let fileCount = 0
|
|
65
|
-
|
|
66
|
-
for (const [filePath, content] of result.files) {
|
|
67
|
-
const absolutePath = path.resolve(path.join(absoluteOutputDir, filePath))
|
|
68
|
-
// Security: prevent path traversal in output (e.g., malicious bundle names)
|
|
69
|
-
if (!absolutePath.startsWith(absoluteOutputDir)) {
|
|
70
|
-
throw new Error(`Path traversal detected in AI content output: ${filePath}`)
|
|
71
|
-
}
|
|
72
|
-
await fs.mkdir(path.dirname(absolutePath), { recursive: true })
|
|
73
|
-
await fs.writeFile(absolutePath, content, 'utf-8')
|
|
74
|
-
fileCount++
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return { fileCount, outputDir: absoluteOutputDir }
|
|
78
|
-
}
|
package/src/build/index.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Public exports for canopycms/build entrypoint.
|
|
3
|
-
*
|
|
4
|
-
* Provides static build utilities for pre-generating content.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export { generateAIContentFiles } from './generate-ai-content'
|
|
8
|
-
export type { GenerateAIContentFilesOptions } from './generate-ai-content'
|
package/src/build-mode.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { AuthenticatedUser } from './user'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Detect if running in static build mode.
|
|
5
|
-
* Framework-agnostic - checks common build environment variables.
|
|
6
|
-
*/
|
|
7
|
-
export const isBuildMode = (): boolean => {
|
|
8
|
-
// Next.js build phase
|
|
9
|
-
if (process.env.NEXT_PHASE === 'phase-production-build') return true
|
|
10
|
-
|
|
11
|
-
// Generic build mode flag (can be set by any framework)
|
|
12
|
-
if (process.env.CANOPY_BUILD_MODE === 'true') return true
|
|
13
|
-
|
|
14
|
-
return false
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Special user for build-time content access.
|
|
19
|
-
* Has Admin privileges to bypass all permission checks during static generation.
|
|
20
|
-
*/
|
|
21
|
-
export const BUILD_USER: AuthenticatedUser = Object.freeze({
|
|
22
|
-
type: 'authenticated',
|
|
23
|
-
userId: '__build__',
|
|
24
|
-
groups: ['Admins'],
|
|
25
|
-
email: 'build@canopycms',
|
|
26
|
-
name: 'Build Process',
|
|
27
|
-
}) as AuthenticatedUser
|