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,100 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { parsePermissionPath } from './validation'
|
|
3
|
-
|
|
4
|
-
describe('authorization validation', () => {
|
|
5
|
-
describe('parsePermissionPath', () => {
|
|
6
|
-
it('returns typed PermissionPath for valid paths', () => {
|
|
7
|
-
const result = parsePermissionPath('content/posts')
|
|
8
|
-
expect(result.ok).toBe(true)
|
|
9
|
-
if (result.ok) {
|
|
10
|
-
expect(result.path).toBe('content/posts')
|
|
11
|
-
}
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('accepts various valid permission paths', () => {
|
|
15
|
-
const validPaths = [
|
|
16
|
-
'content',
|
|
17
|
-
'content/posts',
|
|
18
|
-
'content/posts/published',
|
|
19
|
-
'content/settings/config',
|
|
20
|
-
'assets/images',
|
|
21
|
-
]
|
|
22
|
-
validPaths.forEach((path) => {
|
|
23
|
-
const result = parsePermissionPath(path)
|
|
24
|
-
expect(result.ok).toBe(true)
|
|
25
|
-
})
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('rejects empty paths', () => {
|
|
29
|
-
const result = parsePermissionPath('')
|
|
30
|
-
expect(result.ok).toBe(false)
|
|
31
|
-
if (!result.ok) {
|
|
32
|
-
expect(result.error).toContain('required')
|
|
33
|
-
}
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('rejects paths with traversal sequences', () => {
|
|
37
|
-
const result = parsePermissionPath('content/../admin')
|
|
38
|
-
expect(result.ok).toBe(false)
|
|
39
|
-
if (!result.ok) {
|
|
40
|
-
expect(result.error).toContain('traversal')
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('rejects paths with double-dot at start', () => {
|
|
45
|
-
const result = parsePermissionPath('../content')
|
|
46
|
-
expect(result.ok).toBe(false)
|
|
47
|
-
if (!result.ok) {
|
|
48
|
-
expect(result.error).toContain('traversal')
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('rejects paths with double-dot at end', () => {
|
|
53
|
-
const result = parsePermissionPath('content/..')
|
|
54
|
-
expect(result.ok).toBe(false)
|
|
55
|
-
if (!result.ok) {
|
|
56
|
-
expect(result.error).toContain('traversal')
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('rejects paths starting with slash', () => {
|
|
61
|
-
const result = parsePermissionPath('/content/posts')
|
|
62
|
-
expect(result.ok).toBe(false)
|
|
63
|
-
if (!result.ok) {
|
|
64
|
-
expect(result.error).toContain('start')
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('rejects paths ending with slash', () => {
|
|
69
|
-
const result = parsePermissionPath('content/posts/')
|
|
70
|
-
expect(result.ok).toBe(false)
|
|
71
|
-
if (!result.ok) {
|
|
72
|
-
expect(result.error).toContain('end')
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('rejects paths with consecutive slashes', () => {
|
|
77
|
-
const result = parsePermissionPath('content//posts')
|
|
78
|
-
expect(result.ok).toBe(false)
|
|
79
|
-
if (!result.ok) {
|
|
80
|
-
expect(result.error).toContain('consecutive')
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('normalizes backslashes to forward slashes', () => {
|
|
85
|
-
const result = parsePermissionPath('content\\posts')
|
|
86
|
-
expect(result.ok).toBe(true)
|
|
87
|
-
if (result.ok) {
|
|
88
|
-
expect(result.path).toBe('content/posts')
|
|
89
|
-
}
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('prevents bypass via backslashes with traversal', () => {
|
|
93
|
-
const result = parsePermissionPath('content\\..\\admin')
|
|
94
|
-
expect(result.ok).toBe(false)
|
|
95
|
-
if (!result.ok) {
|
|
96
|
-
expect(result.error).toContain('traversal')
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
})
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Authorization validation utilities.
|
|
3
|
-
*
|
|
4
|
-
* SECURITY CRITICAL: These functions validate permission paths
|
|
5
|
-
* to prevent path traversal attacks.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { hasTraversalSequence } from '../paths/normalize'
|
|
9
|
-
import type { PermissionPath } from './types'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Parse and validate a PermissionPath.
|
|
13
|
-
*
|
|
14
|
-
* SECURITY: Prevents path traversal attacks in permission rules.
|
|
15
|
-
*
|
|
16
|
-
* @param path - The permission path to validate
|
|
17
|
-
* @returns Object with success flag and either the typed path or an error
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* const result = parsePermissionPath(rule.path)
|
|
22
|
-
* if (!result.ok) {
|
|
23
|
-
* throw new Error(`Invalid permission path: ${result.error}`)
|
|
24
|
-
* }
|
|
25
|
-
* const permissionPath: PermissionPath = result.path
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export function parsePermissionPath(
|
|
29
|
-
path: string,
|
|
30
|
-
): { ok: true; path: PermissionPath } | { ok: false; error: string } {
|
|
31
|
-
if (!path || typeof path !== 'string') {
|
|
32
|
-
return { ok: false, error: 'Permission path is required' }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// SECURITY: Prevent path traversal attacks
|
|
36
|
-
if (hasTraversalSequence(path)) {
|
|
37
|
-
return {
|
|
38
|
-
ok: false,
|
|
39
|
-
error: 'Permission path contains traversal sequence (..)',
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Normalize separators to prevent bypass via backslashes
|
|
44
|
-
const normalized = path.replace(/\\/g, '/')
|
|
45
|
-
|
|
46
|
-
// Additional security checks
|
|
47
|
-
if (normalized.startsWith('/') || normalized.endsWith('/')) {
|
|
48
|
-
return {
|
|
49
|
-
ok: false,
|
|
50
|
-
error: 'Permission path cannot start or end with a slash',
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (normalized.includes('//')) {
|
|
55
|
-
return {
|
|
56
|
-
ok: false,
|
|
57
|
-
error: 'Permission path cannot contain consecutive slashes',
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return { ok: true, path: normalized as PermissionPath }
|
|
62
|
-
}
|
|
@@ -1,168 +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
|
-
|
|
7
|
-
import { BranchMetadataFileManager, getBranchMetadataFileManager } from './branch-metadata'
|
|
8
|
-
|
|
9
|
-
const tmpDir = async () => fs.mkdtemp(path.join(os.tmpdir(), 'canopycms-branchmeta-'))
|
|
10
|
-
|
|
11
|
-
describe('BranchMetadataFileManager', () => {
|
|
12
|
-
describe('loadOnly', () => {
|
|
13
|
-
it('loads metadata from disk', async () => {
|
|
14
|
-
const root = await tmpDir()
|
|
15
|
-
const now = new Date().toISOString()
|
|
16
|
-
|
|
17
|
-
// Manually write file to test loadOnly
|
|
18
|
-
const metaDir = path.join(root, '.canopy-meta')
|
|
19
|
-
await fs.mkdir(metaDir, { recursive: true })
|
|
20
|
-
await fs.writeFile(
|
|
21
|
-
path.join(metaDir, 'branch.json'),
|
|
22
|
-
JSON.stringify({
|
|
23
|
-
schemaVersion: 1,
|
|
24
|
-
branch: {
|
|
25
|
-
name: 'feature/x',
|
|
26
|
-
status: 'editing',
|
|
27
|
-
access: { allowedUsers: ['u1'], allowedGroups: ['g1'] },
|
|
28
|
-
createdBy: 'u1',
|
|
29
|
-
createdAt: now,
|
|
30
|
-
updatedAt: now,
|
|
31
|
-
},
|
|
32
|
-
}),
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
const loaded = await BranchMetadataFileManager.loadOnly(root)
|
|
36
|
-
expect(loaded?.branch.name).toBe('feature/x')
|
|
37
|
-
expect(loaded?.branch.access.allowedGroups).toContain('g1')
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('returns null for missing metadata', async () => {
|
|
41
|
-
const root = await tmpDir()
|
|
42
|
-
const loaded = await BranchMetadataFileManager.loadOnly(root)
|
|
43
|
-
expect(loaded).toBeNull()
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
describe('update', () => {
|
|
48
|
-
it('creates metadata when none exists', async () => {
|
|
49
|
-
const root = await tmpDir()
|
|
50
|
-
const registryDir = await tmpDir()
|
|
51
|
-
const meta = getBranchMetadataFileManager(root, registryDir)
|
|
52
|
-
|
|
53
|
-
const created = await meta.save({
|
|
54
|
-
branch: {
|
|
55
|
-
name: 'feature/x',
|
|
56
|
-
status: 'editing',
|
|
57
|
-
access: { allowedUsers: ['u1'], allowedGroups: ['g1'] },
|
|
58
|
-
createdBy: 'u1',
|
|
59
|
-
},
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
expect(created.branch.name).toBe('feature/x')
|
|
63
|
-
expect(created.branch.access.allowedGroups).toContain('g1')
|
|
64
|
-
|
|
65
|
-
const loaded = await BranchMetadataFileManager.loadOnly(root)
|
|
66
|
-
expect(loaded?.branch.name).toBe('feature/x')
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('updates existing metadata and stamps updatedAt', async () => {
|
|
70
|
-
const root = await tmpDir()
|
|
71
|
-
const registryDir = await tmpDir()
|
|
72
|
-
const meta = getBranchMetadataFileManager(root, registryDir)
|
|
73
|
-
|
|
74
|
-
// First create
|
|
75
|
-
const created = await meta.save({
|
|
76
|
-
branch: {
|
|
77
|
-
name: 'feature/y',
|
|
78
|
-
status: 'editing',
|
|
79
|
-
createdBy: 'u1',
|
|
80
|
-
},
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
// Then update
|
|
84
|
-
const updated = await meta.save({
|
|
85
|
-
branch: {
|
|
86
|
-
name: 'feature/y',
|
|
87
|
-
status: 'submitted',
|
|
88
|
-
access: { managerOrAdminAllowed: true },
|
|
89
|
-
pullRequestNumber: 10,
|
|
90
|
-
pullRequestUrl: 'https://example.com/pr/10',
|
|
91
|
-
},
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
expect(updated.branch.status).toBe('submitted')
|
|
95
|
-
expect(updated.branch.pullRequestNumber).toBe(10)
|
|
96
|
-
expect(updated.branch.access.managerOrAdminAllowed).toBe(true)
|
|
97
|
-
expect(updated.branch.createdAt).toBe(created.branch.createdAt) // createdAt unchanged
|
|
98
|
-
expect(new Date(updated.branch.updatedAt).getTime()).toBeGreaterThanOrEqual(
|
|
99
|
-
new Date(created.branch.createdAt).getTime(),
|
|
100
|
-
)
|
|
101
|
-
})
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
describe('registry invalidation', () => {
|
|
105
|
-
it('update() invalidates registry cache', async () => {
|
|
106
|
-
const branchRoot = await tmpDir()
|
|
107
|
-
const registryDir = await tmpDir()
|
|
108
|
-
|
|
109
|
-
// Create initial cache file
|
|
110
|
-
const cacheFile = path.join(registryDir, 'branches.json')
|
|
111
|
-
await fs.mkdir(path.dirname(cacheFile), { recursive: true })
|
|
112
|
-
await fs.writeFile(cacheFile, JSON.stringify({ version: 1, branches: [] }))
|
|
113
|
-
|
|
114
|
-
// Create metadata with registryDir
|
|
115
|
-
const meta = getBranchMetadataFileManager(branchRoot, registryDir)
|
|
116
|
-
|
|
117
|
-
// First update creates the metadata and invalidates cache
|
|
118
|
-
await meta.save({
|
|
119
|
-
branch: {
|
|
120
|
-
name: 'feature/z',
|
|
121
|
-
status: 'editing',
|
|
122
|
-
createdBy: 'u1',
|
|
123
|
-
},
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
// Recreate cache file to test invalidation on second update
|
|
127
|
-
await fs.writeFile(cacheFile, JSON.stringify({ version: 1, branches: [] }))
|
|
128
|
-
|
|
129
|
-
// Second update should also invalidate registry
|
|
130
|
-
await meta.save({
|
|
131
|
-
branch: { status: 'submitted' },
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
// Cache should be gone, stale file should exist
|
|
135
|
-
const cacheExists = await fs
|
|
136
|
-
.stat(cacheFile)
|
|
137
|
-
.then(() => true)
|
|
138
|
-
.catch(() => false)
|
|
139
|
-
const staleFile = path.join(registryDir, 'branches.stale.json')
|
|
140
|
-
const staleExists = await fs
|
|
141
|
-
.stat(staleFile)
|
|
142
|
-
.then(() => true)
|
|
143
|
-
.catch(() => false)
|
|
144
|
-
|
|
145
|
-
expect(cacheExists).toBe(false)
|
|
146
|
-
expect(staleExists).toBe(true)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it('getBranchMetadataFileManager factory creates metadata with registryDir', async () => {
|
|
150
|
-
const branchRoot = await tmpDir()
|
|
151
|
-
const registryDir = await tmpDir()
|
|
152
|
-
|
|
153
|
-
const meta = getBranchMetadataFileManager(branchRoot, registryDir)
|
|
154
|
-
|
|
155
|
-
// Create metadata via update
|
|
156
|
-
await meta.save({
|
|
157
|
-
branch: {
|
|
158
|
-
name: 'feature/factory',
|
|
159
|
-
status: 'editing',
|
|
160
|
-
createdBy: 'u1',
|
|
161
|
-
},
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
const loaded = await BranchMetadataFileManager.loadOnly(branchRoot)
|
|
165
|
-
expect(loaded?.branch.name).toBe('feature/factory')
|
|
166
|
-
})
|
|
167
|
-
})
|
|
168
|
-
})
|
package/src/branch-metadata.ts
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
|
|
4
|
-
import type { BranchContext, BranchMetadata, BranchStatus } from './types'
|
|
5
|
-
import { BranchRegistry } from './branch-registry'
|
|
6
|
-
import { resolveBranchPath } from './paths'
|
|
7
|
-
import { type OperatingMode } from './operating-mode'
|
|
8
|
-
import { isNotFoundError } from './utils/error'
|
|
9
|
-
|
|
10
|
-
const BRANCH_META_DIR = '.canopy-meta'
|
|
11
|
-
const BRANCH_META_FILE = 'branch.json'
|
|
12
|
-
|
|
13
|
-
export interface BranchMetadataFile {
|
|
14
|
-
schemaVersion: number
|
|
15
|
-
branch: BranchMetadata
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const CURRENT_SCHEMA_VERSION = 1
|
|
19
|
-
|
|
20
|
-
export class BranchMetadataFileManager {
|
|
21
|
-
private readonly branchRoot: string
|
|
22
|
-
private readonly filePath: string
|
|
23
|
-
private readonly baseRoot: string
|
|
24
|
-
|
|
25
|
-
private constructor(branchRoot: string, baseRoot: string) {
|
|
26
|
-
this.branchRoot = path.resolve(branchRoot)
|
|
27
|
-
this.filePath = path.join(this.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE)
|
|
28
|
-
this.baseRoot = baseRoot
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Load branch metadata without requiring baseRoot.
|
|
33
|
-
* Use this for read-only access (e.g., in registry scanning or loadBranchContext).
|
|
34
|
-
*/
|
|
35
|
-
static async loadOnly(branchRoot: string): Promise<BranchMetadataFile | null> {
|
|
36
|
-
const filePath = path.join(path.resolve(branchRoot), BRANCH_META_DIR, BRANCH_META_FILE)
|
|
37
|
-
try {
|
|
38
|
-
const raw = await fs.readFile(filePath, 'utf8')
|
|
39
|
-
return JSON.parse(raw) as BranchMetadataFile
|
|
40
|
-
} catch (err: unknown) {
|
|
41
|
-
if (isNotFoundError(err)) {
|
|
42
|
-
return null
|
|
43
|
-
}
|
|
44
|
-
throw err
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Get a BranchMetadataFileManager instance configured for registry invalidation.
|
|
50
|
-
* Use this in API handlers to ensure registry cache is invalidated on updates.
|
|
51
|
-
*/
|
|
52
|
-
static get(branchRoot: string, baseRoot: string): BranchMetadataFileManager {
|
|
53
|
-
return new BranchMetadataFileManager(branchRoot, baseRoot)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private async load(): Promise<BranchMetadataFile | null> {
|
|
57
|
-
try {
|
|
58
|
-
const raw = await fs.readFile(this.filePath, 'utf8')
|
|
59
|
-
const parsed = JSON.parse(raw) as BranchMetadataFile
|
|
60
|
-
return parsed
|
|
61
|
-
} catch (err: unknown) {
|
|
62
|
-
if (isNotFoundError(err)) {
|
|
63
|
-
return null
|
|
64
|
-
}
|
|
65
|
-
throw err
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private async write(meta: BranchMetadataFile): Promise<void> {
|
|
70
|
-
const payload = {
|
|
71
|
-
...meta,
|
|
72
|
-
schemaVersion: meta.schemaVersion ?? CURRENT_SCHEMA_VERSION,
|
|
73
|
-
}
|
|
74
|
-
await fs.mkdir(path.dirname(this.filePath), { recursive: true })
|
|
75
|
-
await fs.writeFile(this.filePath, JSON.stringify(payload, null, 2) + '\n', 'utf8')
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async save(incoming: BranchMetadataUpdate): Promise<BranchMetadataFile> {
|
|
79
|
-
const existing = await this.load()
|
|
80
|
-
const now = new Date().toISOString()
|
|
81
|
-
|
|
82
|
-
const defaults: BranchMetadata = {
|
|
83
|
-
name: 'unknown',
|
|
84
|
-
status: 'editing' as BranchStatus,
|
|
85
|
-
access: {},
|
|
86
|
-
createdBy: 'unknown',
|
|
87
|
-
createdAt: now,
|
|
88
|
-
updatedAt: now,
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const merged: BranchMetadataFile = {
|
|
92
|
-
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
93
|
-
branch: {
|
|
94
|
-
...defaults,
|
|
95
|
-
...existing?.branch,
|
|
96
|
-
...incoming.branch,
|
|
97
|
-
access: {
|
|
98
|
-
...existing?.branch?.access,
|
|
99
|
-
...incoming.branch?.access,
|
|
100
|
-
},
|
|
101
|
-
// Immutable after creation
|
|
102
|
-
createdBy: existing?.branch.createdBy ?? incoming.branch?.createdBy ?? defaults.createdBy,
|
|
103
|
-
createdAt: existing?.branch.createdAt ?? defaults.createdAt,
|
|
104
|
-
},
|
|
105
|
-
}
|
|
106
|
-
await this.write(merged)
|
|
107
|
-
await this.invalidateRegistry()
|
|
108
|
-
|
|
109
|
-
return merged
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Invalidates the registry cache so next list() call regenerates from branch.json files.
|
|
114
|
-
*/
|
|
115
|
-
private async invalidateRegistry(): Promise<void> {
|
|
116
|
-
const registry = new BranchRegistry(this.baseRoot)
|
|
117
|
-
await registry.invalidate()
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Fields that can be set via save().
|
|
123
|
-
* - createdBy: Only used on initial creation; ignored if metadata already exists
|
|
124
|
-
* - createdAt/updatedAt: Managed automatically
|
|
125
|
-
*/
|
|
126
|
-
export interface BranchMetadataUpdate {
|
|
127
|
-
branch?: Partial<Omit<BranchMetadata, 'createdAt' | 'updatedAt'>>
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Get a BranchMetadataFileManager instance configured for registry invalidation.
|
|
132
|
-
* Use this in API handlers to ensure registry cache is invalidated on updates.
|
|
133
|
-
*/
|
|
134
|
-
export const getBranchMetadataFileManager = (
|
|
135
|
-
branchRoot: string,
|
|
136
|
-
baseRoot: string,
|
|
137
|
-
): BranchMetadataFileManager => {
|
|
138
|
-
return BranchMetadataFileManager.get(branchRoot, baseRoot)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Load branch context from metadata file (source of truth).
|
|
143
|
-
* Returns null if the branch doesn't exist.
|
|
144
|
-
*/
|
|
145
|
-
export const loadBranchContext = async (options: {
|
|
146
|
-
branchName: string
|
|
147
|
-
mode: OperatingMode
|
|
148
|
-
basePathOverride?: string
|
|
149
|
-
}): Promise<BranchContext | null> => {
|
|
150
|
-
const { branchRoot, baseRoot } = resolveBranchPath({
|
|
151
|
-
branchName: options.branchName,
|
|
152
|
-
mode: options.mode,
|
|
153
|
-
basePathOverride: options.basePathOverride,
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
const meta = await BranchMetadataFileManager.loadOnly(branchRoot)
|
|
157
|
-
if (!meta) {
|
|
158
|
-
return null
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
branch: meta.branch,
|
|
163
|
-
branchRoot,
|
|
164
|
-
baseRoot,
|
|
165
|
-
}
|
|
166
|
-
}
|