canopycms 0.0.67-int.90 → 0.0.67

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.
Files changed (539) hide show
  1. package/README.md +30 -3
  2. package/dist/ai/generate.js +1 -27
  3. package/dist/ai/handler.d.ts +0 -4
  4. package/dist/ai/handler.js +0 -11
  5. package/dist/ai/index.d.ts +1 -8
  6. package/dist/ai/index.js +1 -8
  7. package/dist/ai/json-to-markdown.js +15 -58
  8. package/dist/ai/resolve-branch.d.ts +2 -7
  9. package/dist/ai/resolve-branch.js +2 -8
  10. package/dist/ai/strip-mdx.d.ts +5 -13
  11. package/dist/ai/strip-mdx.js +5 -17
  12. package/dist/ai/to-plain-text.d.ts +10 -28
  13. package/dist/ai/to-plain-text.js +10 -28
  14. package/dist/ai/transform-components.d.ts +2 -4
  15. package/dist/ai/transform-components.js +8 -19
  16. package/dist/ai/types.d.ts +5 -10
  17. package/dist/api/admin-branch-health.d.ts +4 -12
  18. package/dist/api/admin-branch-health.js +26 -66
  19. package/dist/api/admin.d.ts +4 -12
  20. package/dist/api/admin.js +10 -26
  21. package/dist/api/assets.d.ts +0 -25
  22. package/dist/api/assets.js +50 -91
  23. package/dist/api/branch-merge.d.ts +1 -1
  24. package/dist/api/branch-merge.js +0 -2
  25. package/dist/api/branch-review.d.ts +0 -8
  26. package/dist/api/branch-review.js +0 -13
  27. package/dist/api/branch-status.d.ts +0 -3
  28. package/dist/api/branch-status.js +8 -33
  29. package/dist/api/branch-withdraw.d.ts +0 -1
  30. package/dist/api/branch-withdraw.js +6 -9
  31. package/dist/api/branch.d.ts +7 -12
  32. package/dist/api/branch.js +7 -44
  33. package/dist/api/client.d.ts +71 -224
  34. package/dist/api/client.js +82 -233
  35. package/dist/api/comments.d.ts +2 -4
  36. package/dist/api/comments.js +0 -17
  37. package/dist/api/content.d.ts +0 -6
  38. package/dist/api/content.js +66 -142
  39. package/dist/api/entries-constants.d.ts +5 -9
  40. package/dist/api/entries-constants.js +5 -9
  41. package/dist/api/entries.d.ts +8 -13
  42. package/dist/api/entries.js +20 -51
  43. package/dist/api/github-sync.d.ts +2 -4
  44. package/dist/api/github-sync.js +4 -8
  45. package/dist/api/groups.d.ts +3 -0
  46. package/dist/api/groups.js +8 -21
  47. package/dist/api/guards.d.ts +4 -16
  48. package/dist/api/guards.js +3 -22
  49. package/dist/api/index.d.ts +4 -4
  50. package/dist/api/permissions.d.ts +2 -0
  51. package/dist/api/permissions.js +18 -40
  52. package/dist/api/reference-options.d.ts +0 -1
  53. package/dist/api/reference-options.js +3 -11
  54. package/dist/api/request-body-hash.d.ts +12 -23
  55. package/dist/api/request-body-hash.js +12 -23
  56. package/dist/api/resolve-references.d.ts +0 -1
  57. package/dist/api/resolve-references.js +13 -28
  58. package/dist/api/route-builder.d.ts +3 -7
  59. package/dist/api/route-builder.js +1 -1
  60. package/dist/api/routes.d.ts +12 -0
  61. package/dist/api/routes.js +47 -0
  62. package/dist/api/schema.d.ts +23 -33
  63. package/dist/api/schema.js +20 -74
  64. package/dist/api/settings-helpers.js +4 -5
  65. package/dist/api/types.d.ts +0 -5
  66. package/dist/api/user.js +0 -5
  67. package/dist/api/validators.d.ts +14 -52
  68. package/dist/api/validators.js +14 -52
  69. package/dist/assets/asset-prefixes.d.ts +4 -8
  70. package/dist/assets/asset-prefixes.js +4 -8
  71. package/dist/assets/asset-src.d.ts +5 -22
  72. package/dist/assets/asset-src.js +5 -21
  73. package/dist/assets/factory.d.ts +1 -2
  74. package/dist/assets/factory.js +1 -2
  75. package/dist/assets/finalize.d.ts +3 -1
  76. package/dist/assets/finalize.js +3 -1
  77. package/dist/assets/index.d.ts +3 -3
  78. package/dist/assets/index.js +1 -1
  79. package/dist/assets/keys.d.ts +5 -3
  80. package/dist/assets/keys.js +6 -3
  81. package/dist/assets/pipeline.d.ts +3 -2
  82. package/dist/assets/pipeline.js +22 -56
  83. package/dist/assets/sharp-loader.d.ts +2 -10
  84. package/dist/assets/sharp-loader.js +2 -10
  85. package/dist/assets/store-local.d.ts +1 -2
  86. package/dist/assets/store-local.js +1 -2
  87. package/dist/assets/store-s3.d.ts +3 -4
  88. package/dist/assets/svg-sanitizer.d.ts +6 -14
  89. package/dist/assets/svg-sanitizer.js +6 -14
  90. package/dist/assets/transform-directives.d.ts +3 -4
  91. package/dist/assets/transform-directives.js +1 -1
  92. package/dist/assets/transform.d.ts +19 -30
  93. package/dist/assets/transform.js +18 -31
  94. package/dist/auth/cache.d.ts +2 -3
  95. package/dist/auth/cache.js +2 -3
  96. package/dist/auth/caching-auth-plugin.d.ts +11 -22
  97. package/dist/auth/caching-auth-plugin.js +12 -14
  98. package/dist/auth/context-helpers.d.ts +6 -20
  99. package/dist/auth/context-helpers.js +5 -17
  100. package/dist/auth/file-based-auth-cache.d.ts +10 -21
  101. package/dist/auth/file-based-auth-cache.js +23 -51
  102. package/dist/auth/plugin.d.ts +29 -61
  103. package/dist/auth/plugin.js +9 -13
  104. package/dist/auth/types.d.ts +11 -20
  105. package/dist/authorization/branch.d.ts +30 -49
  106. package/dist/authorization/branch.js +16 -42
  107. package/dist/authorization/content.d.ts +11 -40
  108. package/dist/authorization/content.js +13 -45
  109. package/dist/authorization/groups/index.d.ts +0 -3
  110. package/dist/authorization/groups/index.js +0 -3
  111. package/dist/authorization/groups/loader.d.ts +14 -28
  112. package/dist/authorization/groups/loader.js +14 -38
  113. package/dist/authorization/groups/schema.d.ts +0 -6
  114. package/dist/authorization/groups/schema.js +4 -7
  115. package/dist/authorization/helpers.d.ts +9 -28
  116. package/dist/authorization/helpers.js +9 -28
  117. package/dist/authorization/index.d.ts +3 -25
  118. package/dist/authorization/index.js +3 -35
  119. package/dist/authorization/path.d.ts +6 -13
  120. package/dist/authorization/path.js +8 -25
  121. package/dist/authorization/permissions/index.d.ts +0 -3
  122. package/dist/authorization/permissions/index.js +0 -3
  123. package/dist/authorization/permissions/loader.d.ts +14 -27
  124. package/dist/authorization/permissions/loader.js +14 -32
  125. package/dist/authorization/permissions/schema.d.ts +1 -6
  126. package/dist/authorization/permissions/schema.js +6 -13
  127. package/dist/authorization/protected-branch.d.ts +36 -65
  128. package/dist/authorization/protected-branch.js +29 -41
  129. package/dist/authorization/settings-file-store.d.ts +38 -81
  130. package/dist/authorization/settings-file-store.js +38 -79
  131. package/dist/authorization/types.d.ts +4 -26
  132. package/dist/authorization/types.js +0 -6
  133. package/dist/authorization/validation.d.ts +1 -23
  134. package/dist/authorization/validation.js +1 -24
  135. package/dist/branch-health.d.ts +39 -67
  136. package/dist/branch-health.js +27 -43
  137. package/dist/branch-metadata-file.d.ts +16 -32
  138. package/dist/branch-metadata-file.js +13 -26
  139. package/dist/branch-metadata.d.ts +49 -80
  140. package/dist/branch-metadata.js +57 -87
  141. package/dist/branch-registry.d.ts +26 -64
  142. package/dist/branch-registry.js +44 -93
  143. package/dist/branch-schema-cache.d.ts +34 -124
  144. package/dist/branch-schema-cache.js +49 -155
  145. package/dist/branch-workspace.d.ts +4 -4
  146. package/dist/branch-workspace.js +15 -19
  147. package/dist/build/generate-ai-content.d.ts +1 -0
  148. package/dist/build/generate-ai-content.js +12 -25
  149. package/dist/build-canopy.d.ts +17 -27
  150. package/dist/build-canopy.js +16 -23
  151. package/dist/build-mode.d.ts +25 -27
  152. package/dist/build-mode.js +25 -29
  153. package/dist/cli/cli.d.ts +15 -26
  154. package/dist/cli/cli.js +601 -978
  155. package/dist/cli/generate-ai-content.js +351 -628
  156. package/dist/cli/github-app-manifest.d.ts +84 -0
  157. package/dist/cli/github-app-manifest.js +161 -0
  158. package/dist/cli/init-github-app.d.ts +110 -444
  159. package/dist/cli/init-github-app.js +236 -773
  160. package/dist/cli/init.js +39 -80
  161. package/dist/cli/migrate.d.ts +13 -10
  162. package/dist/cli/migrate.js +22 -30
  163. package/dist/cli/project-detect.d.ts +17 -31
  164. package/dist/cli/project-detect.js +31 -42
  165. package/dist/cli/project-root.d.ts +4 -11
  166. package/dist/cli/project-root.js +4 -11
  167. package/dist/cli/prompt.d.ts +10 -0
  168. package/dist/cli/prompt.js +67 -0
  169. package/dist/cli/sync.d.ts +0 -8
  170. package/dist/cli/sync.js +8 -28
  171. package/dist/cli/template-files/middleware.ts.template +15 -29
  172. package/dist/cli/templates.d.ts +2 -12
  173. package/dist/cli/templates.js +4 -4
  174. package/dist/client.d.ts +1 -1
  175. package/dist/comment-store.d.ts +32 -53
  176. package/dist/comment-store.js +32 -52
  177. package/dist/config/flatten.d.ts +0 -3
  178. package/dist/config/flatten.js +2 -12
  179. package/dist/config/helpers.js +2 -2
  180. package/dist/config/index.d.ts +2 -2
  181. package/dist/config/index.js +1 -4
  182. package/dist/config/schemas/collection.d.ts +0 -8
  183. package/dist/config/schemas/collection.js +0 -10
  184. package/dist/config/schemas/config.d.ts +6 -78
  185. package/dist/config/schemas/config.js +38 -54
  186. package/dist/config/schemas/field.d.ts +2 -242
  187. package/dist/config/schemas/field.js +9 -20
  188. package/dist/config/schemas/media.js +1 -1
  189. package/dist/config/schemas/permissions.js +0 -2
  190. package/dist/config/schemas/url.d.ts +5 -6
  191. package/dist/config/schemas/url.js +5 -6
  192. package/dist/config/types.d.ts +45 -33
  193. package/dist/config/types.js +0 -1
  194. package/dist/config.d.ts +1 -8
  195. package/dist/config.js +1 -9
  196. package/dist/content-id-index.d.ts +76 -188
  197. package/dist/content-id-index.js +79 -230
  198. package/dist/content-index-generation.d.ts +27 -48
  199. package/dist/content-index-generation.js +35 -84
  200. package/dist/content-index-registry.d.ts +15 -24
  201. package/dist/content-index-registry.js +5 -9
  202. package/dist/content-listing.d.ts +64 -107
  203. package/dist/content-listing.js +68 -118
  204. package/dist/content-reader.d.ts +20 -29
  205. package/dist/content-reader.js +10 -18
  206. package/dist/content-store.d.ts +210 -336
  207. package/dist/content-store.js +413 -657
  208. package/dist/content-tree.d.ts +44 -86
  209. package/dist/content-tree.js +26 -54
  210. package/dist/context.d.ts +67 -94
  211. package/dist/context.js +53 -72
  212. package/dist/dev-content-watcher.d.ts +18 -18
  213. package/dist/dev-content-watcher.js +41 -44
  214. package/dist/editor/BranchManager.d.ts +3 -6
  215. package/dist/editor/BranchManager.js +24 -44
  216. package/dist/editor/CanopyEditor.js +5 -2
  217. package/dist/editor/CommentsPanel.js +0 -2
  218. package/dist/editor/Editor.js +19 -76
  219. package/dist/editor/EditorAuthGate.d.ts +27 -0
  220. package/dist/editor/EditorAuthGate.js +140 -0
  221. package/dist/editor/EntryNavigator.js +0 -20
  222. package/dist/editor/FormRenderer.d.ts +1 -2
  223. package/dist/editor/FormRenderer.js +8 -23
  224. package/dist/editor/admin/SystemHealthPanel.js +20 -37
  225. package/dist/editor/admin/useSystemHealth.d.ts +2 -3
  226. package/dist/editor/admin/useSystemHealth.js +10 -12
  227. package/dist/editor/client-reference-resolver.d.ts +2 -5
  228. package/dist/editor/client-reference-resolver.js +2 -19
  229. package/dist/editor/comments/BranchComments.js +0 -4
  230. package/dist/editor/comments/EntryComments.js +0 -4
  231. package/dist/editor/comments/FieldWrapper.js +0 -4
  232. package/dist/editor/comments/InlineCommentThread.js +0 -2
  233. package/dist/editor/comments/ThreadCarousel.d.ts +0 -58
  234. package/dist/editor/comments/ThreadCarousel.js +0 -12
  235. package/dist/editor/components/EditorFooter.d.ts +0 -8
  236. package/dist/editor/components/EditorFooter.js +0 -5
  237. package/dist/editor/components/EditorHeader.d.ts +0 -31
  238. package/dist/editor/components/EditorHeader.js +21 -71
  239. package/dist/editor/components/EditorSidebar.d.ts +0 -19
  240. package/dist/editor/components/EditorSidebar.js +0 -16
  241. package/dist/editor/components/EntryCreateModal.d.ts +4 -4
  242. package/dist/editor/components/EntryCreateModal.js +7 -22
  243. package/dist/editor/components/RenameEntryModal.js +0 -4
  244. package/dist/editor/components/UserBadge.js +0 -10
  245. package/dist/editor/components/index.js +0 -2
  246. package/dist/editor/context/ApiClientContext.d.ts +8 -7
  247. package/dist/editor/context/ApiClientContext.js +39 -11
  248. package/dist/editor/context/AssetContext.d.ts +7 -17
  249. package/dist/editor/context/AssetContext.js +7 -17
  250. package/dist/editor/context/EditorIdentityContext.d.ts +13 -0
  251. package/dist/editor/context/EditorIdentityContext.js +7 -0
  252. package/dist/editor/context/EditorStateContext.d.ts +10 -41
  253. package/dist/editor/context/EditorStateContext.js +7 -23
  254. package/dist/editor/context/SWRProvider.d.ts +3 -6
  255. package/dist/editor/context/index.d.ts +1 -20
  256. package/dist/editor/context/index.js +1 -20
  257. package/dist/editor/editor-utils.d.ts +8 -47
  258. package/dist/editor/editor-utils.js +19 -72
  259. package/dist/editor/fields/BlockField.d.ts +2 -2
  260. package/dist/editor/fields/BlockField.js +1 -2
  261. package/dist/editor/fields/CodeField.d.ts +0 -1
  262. package/dist/editor/fields/CodeField.js +0 -1
  263. package/dist/editor/fields/DateTimeField.d.ts +15 -22
  264. package/dist/editor/fields/DateTimeField.js +17 -26
  265. package/dist/editor/fields/ImageField.d.ts +2 -2
  266. package/dist/editor/fields/ImageField.js +8 -14
  267. package/dist/editor/fields/InlineGroupField.d.ts +0 -1
  268. package/dist/editor/fields/InlineGroupField.js +0 -1
  269. package/dist/editor/fields/MarkdownField.d.ts +0 -1
  270. package/dist/editor/fields/MarkdownField.js +6 -11
  271. package/dist/editor/fields/MdxImageDialog.d.ts +1 -1
  272. package/dist/editor/fields/MdxImageDialog.js +0 -1
  273. package/dist/editor/fields/NumberField.d.ts +9 -19
  274. package/dist/editor/fields/NumberField.js +11 -22
  275. package/dist/editor/fields/NumberListField.d.ts +7 -10
  276. package/dist/editor/fields/NumberListField.js +7 -10
  277. package/dist/editor/fields/ObjectField.d.ts +5 -8
  278. package/dist/editor/fields/ObjectField.js +0 -1
  279. package/dist/editor/fields/ReferenceField.d.ts +1 -1
  280. package/dist/editor/fields/ReferenceField.js +11 -16
  281. package/dist/editor/fields/SelectField.d.ts +2 -2
  282. package/dist/editor/fields/SelectField.js +0 -1
  283. package/dist/editor/fields/StringListField.d.ts +0 -4
  284. package/dist/editor/fields/StringListField.js +4 -9
  285. package/dist/editor/fields/TextField.d.ts +0 -1
  286. package/dist/editor/fields/TextField.js +0 -1
  287. package/dist/editor/fields/ToggleField.d.ts +0 -1
  288. package/dist/editor/fields/ToggleField.js +0 -1
  289. package/dist/editor/fields/entry-link/EntryLinkContext.js +2 -4
  290. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -3
  291. package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -4
  292. package/dist/editor/group-manager/ExternalGroupsTab.d.ts +0 -3
  293. package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
  294. package/dist/editor/group-manager/GroupCard.d.ts +0 -3
  295. package/dist/editor/group-manager/GroupForm.d.ts +0 -3
  296. package/dist/editor/group-manager/InternalGroupsTab.d.ts +0 -3
  297. package/dist/editor/group-manager/InternalGroupsTab.js +0 -2
  298. package/dist/editor/group-manager/MemberList.d.ts +0 -3
  299. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +0 -3
  300. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -3
  301. package/dist/editor/group-manager/hooks/useGroupState.d.ts +0 -3
  302. package/dist/editor/group-manager/hooks/useGroupState.js +0 -5
  303. package/dist/editor/group-manager/hooks/useUserSearch.d.ts +0 -3
  304. package/dist/editor/group-manager/hooks/useUserSearch.js +0 -3
  305. package/dist/editor/group-manager/index.d.ts +0 -6
  306. package/dist/editor/group-manager/index.js +0 -10
  307. package/dist/editor/group-manager/types.d.ts +2 -5
  308. package/dist/editor/group-manager/types.js +0 -3
  309. package/dist/editor/hooks/index.d.ts +6 -11
  310. package/dist/editor/hooks/index.js +6 -12
  311. package/dist/editor/hooks/useBranchActions.d.ts +0 -14
  312. package/dist/editor/hooks/useBranchActions.js +0 -22
  313. package/dist/editor/hooks/useBranchManager.d.ts +2 -48
  314. package/dist/editor/hooks/useBranchManager.js +4 -64
  315. package/dist/editor/hooks/useBranchesData.d.ts +7 -6
  316. package/dist/editor/hooks/useBranchesData.js +7 -6
  317. package/dist/editor/hooks/useCommentSystem.d.ts +0 -49
  318. package/dist/editor/hooks/useCommentSystem.js +1 -36
  319. package/dist/editor/hooks/useDraftManager.d.ts +2 -35
  320. package/dist/editor/hooks/useDraftManager.js +23 -64
  321. package/dist/editor/hooks/useEditorLayout.d.ts +0 -20
  322. package/dist/editor/hooks/useEditorLayout.js +0 -23
  323. package/dist/editor/hooks/useEntriesData.d.ts +1 -0
  324. package/dist/editor/hooks/useEntriesData.js +2 -3
  325. package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -2
  326. package/dist/editor/hooks/useEntryLinkResolution.js +2 -2
  327. package/dist/editor/hooks/useEntryManager.d.ts +2 -27
  328. package/dist/editor/hooks/useEntryManager.js +29 -78
  329. package/dist/editor/hooks/useGroupManager.d.ts +0 -21
  330. package/dist/editor/hooks/useGroupManager.js +0 -21
  331. package/dist/editor/hooks/usePermissionManager.d.ts +0 -20
  332. package/dist/editor/hooks/usePermissionManager.js +0 -20
  333. package/dist/editor/hooks/useReferenceResolution.d.ts +2 -33
  334. package/dist/editor/hooks/useReferenceResolution.js +1 -48
  335. package/dist/editor/hooks/useSchemaManager.d.ts +8 -27
  336. package/dist/editor/hooks/useSchemaManager.js +0 -19
  337. package/dist/editor/hooks/useUserContext.d.ts +2 -9
  338. package/dist/editor/hooks/useUserContext.js +10 -10
  339. package/dist/editor/hooks/useUserMetadata.d.ts +0 -2
  340. package/dist/editor/hooks/useUserMetadata.js +0 -3
  341. package/dist/editor/media/AssetCard.d.ts +0 -1
  342. package/dist/editor/media/AssetCard.js +1 -2
  343. package/dist/editor/media/CropStep.d.ts +0 -1
  344. package/dist/editor/media/CropStep.js +0 -1
  345. package/dist/editor/media/MediaLibrary.d.ts +1 -2
  346. package/dist/editor/media/MediaLibrary.js +1 -2
  347. package/dist/editor/media/MediaLibraryBody.d.ts +0 -1
  348. package/dist/editor/media/MediaLibraryBody.js +0 -1
  349. package/dist/editor/media/crop-math.d.ts +1 -1
  350. package/dist/editor/media/crop-math.js +1 -1
  351. package/dist/editor/permission-manager/GroupSelector.d.ts +0 -3
  352. package/dist/editor/permission-manager/PermissionEditor.d.ts +0 -4
  353. package/dist/editor/permission-manager/PermissionEditor.js +0 -1
  354. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +0 -7
  355. package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -4
  356. package/dist/editor/permission-manager/PermissionTree.d.ts +0 -4
  357. package/dist/editor/permission-manager/UserSelector.d.ts +0 -3
  358. package/dist/editor/permission-manager/constants.d.ts +0 -3
  359. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +0 -3
  360. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -10
  361. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +0 -14
  362. package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -11
  363. package/dist/editor/permission-manager/index.d.ts +0 -7
  364. package/dist/editor/permission-manager/index.js +0 -16
  365. package/dist/editor/permission-manager/types.d.ts +2 -12
  366. package/dist/editor/permission-manager/types.js +0 -3
  367. package/dist/editor/permission-manager/utils.d.ts +0 -28
  368. package/dist/editor/permission-manager/utils.js +9 -23
  369. package/dist/editor/preview-bridge.d.ts +5 -1
  370. package/dist/editor/preview-bridge.js +4 -4
  371. package/dist/editor/schema-editor/CollectionEditor.d.ts +0 -5
  372. package/dist/editor/schema-editor/CollectionEditor.js +9 -31
  373. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +0 -6
  374. package/dist/editor/schema-editor/EntryTypeEditor.js +3 -24
  375. package/dist/editor/schema-editor/index.d.ts +0 -7
  376. package/dist/editor/schema-editor/index.js +0 -7
  377. package/dist/editor/utils/env.d.ts +3 -18
  378. package/dist/editor/utils/env.js +6 -25
  379. package/dist/entry-link-resolver.d.ts +13 -35
  380. package/dist/entry-link-resolver.js +20 -53
  381. package/dist/entry-schema-registry.d.ts +20 -60
  382. package/dist/entry-schema-registry.js +20 -69
  383. package/dist/entry-schema.d.ts +118 -223
  384. package/dist/entry-schema.js +46 -91
  385. package/dist/git-manager.d.ts +101 -200
  386. package/dist/git-manager.js +211 -384
  387. package/dist/github-service.d.ts +34 -75
  388. package/dist/github-service.js +37 -91
  389. package/dist/http/handler.d.ts +6 -37
  390. package/dist/http/handler.js +65 -120
  391. package/dist/http/router.d.ts +15 -32
  392. package/dist/http/router.js +34 -99
  393. package/dist/http/types.d.ts +15 -40
  394. package/dist/http/types.js +3 -7
  395. package/dist/id.d.ts +4 -10
  396. package/dist/id.js +4 -10
  397. package/dist/index.js +6 -9
  398. package/dist/operating-mode/client-safe-strategy.d.ts +7 -14
  399. package/dist/operating-mode/client-safe-strategy.js +8 -28
  400. package/dist/operating-mode/client-unsafe-strategy.d.ts +6 -16
  401. package/dist/operating-mode/client-unsafe-strategy.js +9 -34
  402. package/dist/operating-mode/client.d.ts +3 -10
  403. package/dist/operating-mode/client.js +3 -8
  404. package/dist/operating-mode/deployment-name-fixtures.d.ts +14 -18
  405. package/dist/operating-mode/deployment-name-fixtures.js +14 -18
  406. package/dist/operating-mode/deployment-name.d.ts +19 -29
  407. package/dist/operating-mode/deployment-name.js +29 -47
  408. package/dist/operating-mode/index.d.ts +4 -25
  409. package/dist/operating-mode/index.js +9 -31
  410. package/dist/operating-mode/mode-env.d.ts +32 -54
  411. package/dist/operating-mode/mode-env.js +36 -58
  412. package/dist/operating-mode/types.d.ts +24 -72
  413. package/dist/operating-mode/types.js +1 -7
  414. package/dist/paths/branch-name.d.ts +30 -40
  415. package/dist/paths/branch-name.js +30 -40
  416. package/dist/paths/branch.d.ts +4 -18
  417. package/dist/paths/branch.js +6 -22
  418. package/dist/paths/index.d.ts +2 -13
  419. package/dist/paths/index.js +3 -23
  420. package/dist/paths/normalize-server.d.ts +3 -15
  421. package/dist/paths/normalize-server.js +3 -15
  422. package/dist/paths/normalize.d.ts +13 -33
  423. package/dist/paths/normalize.js +13 -33
  424. package/dist/paths/resolve.d.ts +4 -17
  425. package/dist/paths/resolve.js +4 -22
  426. package/dist/paths/types.d.ts +12 -34
  427. package/dist/paths/types.js +5 -7
  428. package/dist/paths/validation.d.ts +22 -119
  429. package/dist/paths/validation.js +30 -148
  430. package/dist/reference-resolver.d.ts +0 -13
  431. package/dist/reference-resolver.js +0 -18
  432. package/dist/resolve-canopy-user.d.ts +16 -18
  433. package/dist/resolve-canopy-user.js +17 -26
  434. package/dist/resource-generation.d.ts +16 -25
  435. package/dist/resource-generation.js +34 -97
  436. package/dist/schema/meta-loader.d.ts +2 -14
  437. package/dist/schema/meta-loader.js +15 -64
  438. package/dist/schema/resolver.d.ts +1 -4
  439. package/dist/schema/resolver.js +1 -6
  440. package/dist/schema/schema-store-types.d.ts +0 -12
  441. package/dist/schema/schema-store.d.ts +61 -138
  442. package/dist/schema/schema-store.js +85 -282
  443. package/dist/schema/types.d.ts +0 -4
  444. package/dist/server.d.ts +62 -93
  445. package/dist/server.js +63 -94
  446. package/dist/services.d.ts +23 -47
  447. package/dist/services.js +55 -98
  448. package/dist/settings-workspace.d.ts +17 -30
  449. package/dist/settings-workspace.js +51 -81
  450. package/dist/static/index.d.ts +26 -52
  451. package/dist/static/index.js +33 -67
  452. package/dist/{worker/task-queue.d.ts → task-queue/cms-task-queue.d.ts} +7 -9
  453. package/dist/task-queue/cms-task-queue.js +13 -0
  454. package/dist/task-queue/task-queue-config.d.ts +3 -0
  455. package/dist/{worker → task-queue}/task-queue-config.js +1 -6
  456. package/dist/task-queue/task-queue.d.ts +16 -22
  457. package/dist/task-queue/task-queue.js +20 -44
  458. package/dist/task-queue/types.d.ts +0 -1
  459. package/dist/task-queue/worker-status.d.ts +35 -0
  460. package/dist/task-queue/worker-status.js +40 -0
  461. package/dist/types.d.ts +40 -51
  462. package/dist/url-collision.d.ts +26 -40
  463. package/dist/url-collision.js +47 -63
  464. package/dist/url-exclusivity-fixtures.d.ts +15 -20
  465. package/dist/url-exclusivity-fixtures.js +14 -19
  466. package/dist/url-path-resolver.d.ts +8 -16
  467. package/dist/url-path-resolver.js +19 -29
  468. package/dist/user.d.ts +8 -21
  469. package/dist/user.js +10 -19
  470. package/dist/utils/async-mutex.js +4 -6
  471. package/dist/utils/atomic-write.d.ts +2 -11
  472. package/dist/utils/atomic-write.js +2 -11
  473. package/dist/utils/body-field.d.ts +8 -15
  474. package/dist/utils/body-field.js +8 -15
  475. package/dist/utils/content-serialize.d.ts +18 -24
  476. package/dist/utils/content-serialize.js +109 -160
  477. package/dist/utils/content-write-lock.d.ts +51 -106
  478. package/dist/utils/content-write-lock.js +66 -111
  479. package/dist/utils/debug.d.ts +7 -23
  480. package/dist/utils/debug.js +7 -23
  481. package/dist/utils/entry-url.d.ts +11 -23
  482. package/dist/utils/entry-url.js +11 -26
  483. package/dist/utils/error.d.ts +25 -90
  484. package/dist/utils/error.js +60 -147
  485. package/dist/utils/flatten-group-fields.d.ts +5 -7
  486. package/dist/utils/flatten-group-fields.js +5 -7
  487. package/dist/utils/format.d.ts +1 -5
  488. package/dist/utils/format.js +1 -5
  489. package/dist/utils/fs.d.ts +3 -4
  490. package/dist/utils/fs.js +3 -4
  491. package/dist/utils/git.d.ts +52 -83
  492. package/dist/utils/git.js +79 -125
  493. package/dist/utils/logger.d.ts +35 -50
  494. package/dist/utils/logger.js +35 -52
  495. package/dist/utils/provisioning-lock.d.ts +23 -35
  496. package/dist/utils/provisioning-lock.js +49 -69
  497. package/dist/utils/sanitize-href.d.ts +5 -10
  498. package/dist/utils/sanitize-href.js +8 -18
  499. package/dist/utils/title-field.d.ts +17 -20
  500. package/dist/utils/title-field.js +18 -27
  501. package/dist/utils/typed-filename.d.ts +20 -48
  502. package/dist/utils/typed-filename.js +25 -61
  503. package/dist/utils/url-prefix.d.ts +3 -3
  504. package/dist/utils/url-prefix.js +3 -3
  505. package/dist/validation/block-structural-keys.d.ts +16 -31
  506. package/dist/validation/block-structural-keys.js +19 -29
  507. package/dist/validation/deletion-checker.d.ts +0 -34
  508. package/dist/validation/deletion-checker.js +1 -43
  509. package/dist/validation/entry-link-validator.d.ts +6 -13
  510. package/dist/validation/entry-link-validator.js +4 -13
  511. package/dist/validation/entry-type-reference-validator.d.ts +13 -18
  512. package/dist/validation/entry-type-reference-validator.js +13 -18
  513. package/dist/validation/entry-validator.d.ts +13 -25
  514. package/dist/validation/entry-validator.js +15 -32
  515. package/dist/validation/field-traversal.d.ts +3 -30
  516. package/dist/validation/field-traversal.js +1 -32
  517. package/dist/validation/reference-validator.d.ts +5 -24
  518. package/dist/validation/reference-validator.js +5 -30
  519. package/dist/worker/cms-worker.d.ts +172 -232
  520. package/dist/worker/cms-worker.js +241 -333
  521. package/dist/worker/git-sync.d.ts +32 -133
  522. package/dist/worker/git-sync.js +129 -191
  523. package/dist/worker/github-auth.d.ts +13 -19
  524. package/dist/worker/github-auth.js +13 -19
  525. package/dist/worker/history-rewrite.d.ts +53 -66
  526. package/dist/worker/history-rewrite.js +54 -60
  527. package/dist/worker/log.d.ts +24 -41
  528. package/dist/worker/log.js +25 -42
  529. package/dist/worker/rebase.d.ts +20 -110
  530. package/dist/worker/rebase.js +279 -354
  531. package/dist/worker/task-runner.d.ts +32 -66
  532. package/dist/worker/task-runner.js +126 -162
  533. package/dist/worker/worker-context.d.ts +41 -81
  534. package/package.json +4 -4
  535. package/dist/cli/template-files/middleware-clerk.ts.template +0 -37
  536. package/dist/worker/task-queue-config.d.ts +0 -8
  537. package/dist/worker/task-queue.js +0 -19
  538. package/dist/worker/worker-status.d.ts +0 -47
  539. package/dist/worker/worker-status.js +0 -52
@@ -15,7 +15,6 @@ import { GroupManager } from './GroupManager.js';
15
15
  import { PermissionManager } from './PermissionManager.js';
16
16
  import { SystemHealthPanel } from './admin/SystemHealthPanel.js';
17
17
  // Import directly from helpers to avoid server-only code in authorization barrel
18
- // (same rationale as BranchManager.tsx's identical import)
19
18
  import { isAdmin } from '../authorization/helpers.js';
20
19
  import { buildPreviewSrc, buildCollectionLabels, buildBreadcrumbSegments } from './editor-utils.js';
21
20
  import { useEditorLayout, useDraftManager, useEntryManager, useGroupManager, usePermissionManager, useCommentSystem, useBranchManager, useUserContext, useSchemaManager, } from './hooks/index.js';
@@ -34,7 +33,6 @@ import { MediaLibrary } from './media/MediaLibrary.js';
34
33
  * saving/loading, and preview rendering using entry definitions.
35
34
  */
36
35
  export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtitle = subtitle, branchName = '', collections, contentRoot, entryLinkUrl, initialSelectedId, initialValues, renderPreview, onCreateEntry, themeOptions, operatingMode, previewBaseByCollection, currentUser = 'current-user', canResolveComments = true, assetBaseUrl, basePath, AccountComponent, onAccountClick, onLogoutClick, customRenderers, }) => {
37
- // Per-resource loading states
38
36
  const [branchesLoading, setBranchesLoading] = useState(false);
39
37
  const [entriesLoading, setEntriesLoading] = useState(false);
40
38
  const [commentsLoading] = useState(false);
@@ -44,17 +42,14 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
44
42
  const [branchManagerOpen, setBranchManagerOpen] = useState(false);
45
43
  const [mediaLibraryOpen, setMediaLibraryOpen] = useState(false);
46
44
  const [systemHealthOpen, setSystemHealthOpen] = useState(false);
47
- // Schema editor state
48
45
  const [collectionEditorOpen, setCollectionEditorOpen] = useState(false);
49
46
  const [editingCollection, setEditingCollection] = useState(null);
50
47
  const [collectionEditorParentPath, setCollectionEditorParentPath] = useState(undefined);
51
48
  const [collectionEditorError, setCollectionEditorError] = useState(null);
52
- // Rename entry modal state
53
49
  const [renameModalOpen, setRenameModalOpen] = useState(false);
54
50
  const [renamingEntry, setRenamingEntry] = useState(null);
55
51
  const [renameModalError, setRenameModalError] = useState(null);
56
52
  const [renameModalSaving, setRenameModalSaving] = useState(false);
57
- // Delete confirmation modal state
58
53
  const [deleteCollectionModalOpen, setDeleteCollectionModalOpen] = useState(false);
59
54
  const [deletingCollectionPath, setDeletingCollectionPath] = useState(null);
60
55
  const [deleteEntryModalOpen, setDeleteEntryModalOpen] = useState(false);
@@ -65,15 +60,12 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
65
60
  const [previewLoadingState, setPreviewLoadingState] = useState({});
66
61
  // Draft compile/render error reported by the preview page (null = renders cleanly)
67
62
  const [previewError, setPreviewError] = useState(null);
68
- // API client for schema operations
69
63
  const apiClient = useApiClient();
70
- // Fetch current user context for permission checks
71
64
  const { userContext } = useUserContext();
72
- // The System Health panel (PR-U1) is admin-only -- gate both the sidebar
65
+ // The System Health panel is admin-only -- gate both the sidebar
73
66
  // menu item and the modal mount on it, same pattern BranchManager.tsx uses
74
67
  // for its own admin-only actions.
75
68
  const showSystemHealth = isAdmin(userContext?.groups);
76
- // Use custom hooks for layout, entry, draft, group, permission, comment, and branch management
77
69
  const { layout, setLayout, highlightEnabled, setHighlightEnabled, headerRef, headerHeight } = useEditorLayout();
78
70
  // Comments state (shared between useCommentSystem and useBranchManager)
79
71
  const [commentsForBranchSummaries, setCommentsForBranchSummaries] = useState([]);
@@ -90,21 +82,11 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
90
82
  // computed server-side by the same getBranchProtection() call the writableBranch
91
83
  // guard uses, so the UI can never drift from what the API will accept.
92
84
  //
93
- // Fail CLOSED (`?? true`) when `currentBranch` is undefined or the wire didn't
94
- // carry `writeBlocked` at all (branches-list fetch still in flight/failed, or
95
- // editor/server version skew where an older server doesn't emit the flag). This
96
- // is a deliberately inverted default from the OLD client-side derivation this
97
- // flag replaced (`branchStatus !== 'editing'`), which locked correctly even with
98
- // zero data from the server -- "no answer yet" and "not editing" both compute to
99
- // locked when the fallback is itself a status comparison. Moving the decision
100
- // server-side was the right call (the API guard and the UI now read one source
101
- // of truth instead of two independent derivations that could drift), but an
102
- // `?? false` default silently flipped what "no answer yet" means: a client that
103
- // can't hear the server's answer now assumed UNLOCKED instead of LOCKED. A brief
104
- // flash of locked (corrected the moment the branch list loads) is strictly safer
105
- // than a flash of unlocked that invites a click the server is going to reject --
106
- // and the pane is already showing a loading state during that same window, so
107
- // the flash is not even visible in practice.
85
+ // `?? true`: with no answer from the server (branches fetch in flight or
86
+ // failed, or an older server without the flag) the editor must stay locked. A
87
+ // brief flash of locked, corrected when the list loads, is safer than a flash
88
+ // of unlocked that invites a click the server rejects -- and the pane shows a
89
+ // loading state during that window anyway.
108
90
  const branchContentLocked = currentBranch?.writeBlocked ?? true;
109
91
  // 2. Entry manager (depends on branchNameState, owns selectedPath)
110
92
  const { selectedPath, setSelectedPath, entries: entriesState, collections: collectionsFromApi, currentEntry, availableSchemas, entriesInitializing, navigatorOpen, setNavigatorOpen, refreshEntries, handleCreateEntry, renameEntry, loadEntry, saveEntry, getEntryVersion, createModalOpen, createModalCollection, createModalError, createModalCreating, createModalExistingSlugs, handleCreateModalSubmit, closeCreateModal, } = useEntryManager({
@@ -121,12 +103,10 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
121
103
  }),
122
104
  setBusy: setEntriesLoading,
123
105
  });
124
- // Keep the entry-type list referentially stable for as long as the create
125
- // modal is showing the same collection. Building it inline in the JSX handed
126
- // EntryCreateModal a new array on every render of this component, which its
127
- // form-seeding effect used to treat as a reason to reset the user's input.
128
- // That effect no longer keys on the array, but a stable prop is still the
129
- // right thing to pass: it also keeps Mantine's Select `data` identity steady.
106
+ // Keeps the entry-type list referentially stable while the create modal shows
107
+ // the same collection: a new array on every render would churn Mantine's
108
+ // Select `data` identity and give EntryCreateModal's form-seeding effect a
109
+ // spurious reason to reset the user's input.
130
110
  const createModalEntryTypes = useMemo(() => createModalCollection?.entryTypes?.map((et) => ({
131
111
  name: et.name,
132
112
  label: et.label,
@@ -188,8 +168,6 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
188
168
  });
189
169
  const collectionLabels = useMemo(() => buildCollectionLabels(activeCollections), [activeCollections]);
190
170
  const schema = currentEntry?.schema ?? [];
191
- // Effect to load entry data when selection changes.
192
- //
193
171
  // Gated on `loadedValues[contentId]` (not `drafts[contentId]`) so a
194
172
  // restored-from-localStorage draft never skips the load: skipping left
195
173
  // `loadedValues[contentId]` permanently undefined, which made dirty-
@@ -252,7 +230,7 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
252
230
  if (currentBranchRef.current !== requestBranch)
253
231
  return;
254
232
  setLoadedValues((prev) => ({ ...prev, [contentId]: loaded }));
255
- // NOTE: no draft is seeded here. `effectiveValue` is
233
+ // No draft is seeded here. `effectiveValue` is
256
234
  // `drafts[id] ?? loadedValues[id]`, so the form renders from the line
257
235
  // above alone; seeding `drafts[id] = loaded` only manufactured a
258
236
  // pristine draft for every entry the user merely OPENED, which
@@ -286,17 +264,14 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
286
264
  });
287
265
  // eslint-disable-next-line react-hooks/exhaustive-deps -- stable setters, run only on entry/path/branch/loadedValues change
288
266
  }, [currentEntry, loadedValues, selectedPath, branchNameState]);
289
- // Schema editor handlers
290
267
  const handleOpenCollectionEditor = async (collection, parentPath) => {
291
268
  if (collection) {
292
- // Edit mode - fetch full collection data with usage counts
293
269
  try {
294
270
  const result = await apiClient.schema.getCollection({
295
271
  branch: branchNameState,
296
272
  collectionPath: collection.path,
297
273
  });
298
274
  if (result.ok && result.data && result.data.collection) {
299
- // Use entry types with usage counts from API if available
300
275
  const entries = result.data.entryTypesWithUsage
301
276
  ? result.data.entryTypesWithUsage.map((et) => ({
302
277
  name: et.name,
@@ -324,7 +299,6 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
324
299
  setEditingCollection(existingCollection);
325
300
  }
326
301
  else {
327
- // Fallback to using EditorCollection data
328
302
  const existingCollection = {
329
303
  name: collection.name,
330
304
  label: collection.label,
@@ -342,7 +316,6 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
342
316
  }
343
317
  }
344
318
  catch {
345
- // Fallback on error
346
319
  const existingCollection = {
347
320
  name: collection.name,
348
321
  label: collection.label,
@@ -361,7 +334,6 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
361
334
  setCollectionEditorParentPath(undefined);
362
335
  }
363
336
  else {
364
- // Create mode
365
337
  setEditingCollection(null);
366
338
  setCollectionEditorParentPath(parentPath);
367
339
  }
@@ -426,7 +398,6 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
426
398
  try {
427
399
  const result = await deleteEntry(deletingEntryPath);
428
400
  if (result.ok && selectedPath === deletingEntryPath) {
429
- // If we deleted the currently selected entry, clear selection
430
401
  setSelectedPath('');
431
402
  }
432
403
  setDeleteEntryModalOpen(false);
@@ -466,7 +437,6 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
466
437
  }
467
438
  };
468
439
  const handleReorderEntry = async (collectionPath, contentId, direction) => {
469
- // Find the collection to get its current order array
470
440
  const findCollection = (cols, path) => {
471
441
  if (!cols)
472
442
  return undefined;
@@ -479,21 +449,13 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
479
449
  }
480
450
  return undefined;
481
451
  };
482
- // Resolve against `collectionsFromApi`, NOT `activeCollections`. The
483
- // latter falls back to the build-time `collections` prop whenever the
484
- // fetched list is empty (`activeCollections = collectionsFromApi.length >
485
- // 0 ? collectionsFromApi : collections`, above) -- which is exactly the
486
- // state mid-branch-switch, before the new branch's entries/collections
487
- // fetch has committed. Resolving a WRITE against that fallback meant this
488
- // guard could never fire during a switch: it would happily find the OLD
489
- // branch's collection in the stale build-time props and send ITS `order`
490
- // array as a PATCH to the NEW branch. Reading `collectionsFromApi`
491
- // directly makes "not yet loaded for this branch" and "loaded, has no
492
- // such collection" the same (correct) not-found outcome, so the guard
493
- // below is reachable again. See the correction to PR #196's "unreachable
494
- // with entries empty" claim in
495
- // .claude/future-tasks/resolved/program-b-final-review-followups.md for the write
496
- // hazard this closes -- found by the 2026-08-12 adversarial review.
452
+ // Resolve against `collectionsFromApi`, NOT `activeCollections`: the latter
453
+ // falls back to the build-time `collections` prop whenever the fetch is
454
+ // still empty -- exactly the state mid-branch-switch. A write resolved
455
+ // against that fallback would silently PATCH the OLD branch's `order` onto
456
+ // the NEW branch instead of hitting the not-found guard below; reading
457
+ // `collectionsFromApi` directly makes "not yet loaded" and "loaded, no such
458
+ // collection" the same not-found outcome.
497
459
  const collection = findCollection(collectionsFromApi, collectionPath);
498
460
  if (!collection) {
499
461
  // A silent no-op on a clicked menu item reads as a broken button --
@@ -507,14 +469,11 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
507
469
  });
508
470
  return;
509
471
  }
510
- // Use the collection's order array as the source of truth
511
- // If no order array exists, build one from current entries and children
512
472
  let currentOrder;
513
473
  if (collection.order && collection.order.length > 0) {
514
474
  currentOrder = [...collection.order];
515
475
  }
516
476
  else {
517
- // Fallback: build order from entries and children
518
477
  const collectionEntries = entriesState.filter((e) => e.collectionPath === collectionPath);
519
478
  const entryIds = collectionEntries
520
479
  .map((e) => e.contentId)
@@ -524,18 +483,14 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
524
483
  .filter((id) => !!id);
525
484
  currentOrder = [...entryIds, ...subCollectionIds];
526
485
  }
527
- // Find current position
528
486
  const currentIndex = currentOrder.indexOf(contentId);
529
487
  if (currentIndex === -1)
530
488
  return;
531
- // Calculate new position
532
489
  const newIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
533
490
  if (newIndex < 0 || newIndex >= currentOrder.length)
534
491
  return;
535
- // Swap positions
536
492
  const newOrder = [...currentOrder];
537
493
  [newOrder[currentIndex], newOrder[newIndex]] = [newOrder[newIndex], newOrder[currentIndex]];
538
- // Update via API
539
494
  await updateOrder(collectionPath, newOrder);
540
495
  };
541
496
  // Determine if we should hide the root collection (but keep its context for ordering)
@@ -565,7 +520,6 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
565
520
  const build = (node) => {
566
521
  const entries = grouped.get(node.path) ?? [];
567
522
  const children = node.children?.map((child) => build(child)) ?? [];
568
- // Pass entries, children, and order to EntryNavigator for interleaved ordering
569
523
  return {
570
524
  path: node.path,
571
525
  label: node.label ?? node.name,
@@ -597,7 +551,6 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
597
551
  ]);
598
552
  // Tree expansion state - persists across drawer close/open
599
553
  const treeExpandedStateRef = useRef({});
600
- // Tree controller ref for collapse/expand all functionality
601
554
  const treeControllerRef = useRef(null);
602
555
  const handleTreeControllerReady = (controller) => {
603
556
  treeControllerRef.current = controller;
@@ -607,15 +560,12 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
607
560
  };
608
561
  const handleCollapseAll = () => {
609
562
  treeControllerRef.current?.collapseAllNodes();
610
- // Sync the ref to empty state
611
563
  treeExpandedStateRef.current = {};
612
564
  };
613
565
  const handleExpandAll = () => {
614
566
  treeControllerRef.current?.expandAllNodes();
615
- // Sync the ref with all expanded nodes from controller
616
567
  treeExpandedStateRef.current = treeControllerRef.current?.expandedState ?? {};
617
568
  };
618
- // Resolve entry:ID links in body content for live preview
619
569
  const { resolveEntryLinks } = useEntryLinkResolution({
620
570
  entries: entriesState,
621
571
  contentRoot,
@@ -637,7 +587,6 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
637
587
  collectionName: e.collectionName,
638
588
  })),
639
589
  }), [entriesState]);
640
- // Helper component for centered messages
641
590
  const CenteredMessage = ({ children }) => (_jsx("div", { style: {
642
591
  display: 'flex',
643
592
  alignItems: 'center',
@@ -677,13 +626,7 @@ export const Editor = ({ entries, title, subtitle, siteTitle = title, siteSubtit
677
626
  // the one in-app retry for a failed branches fetch, and the fetch
678
627
  // is terminal on its own: SWRProvider sets shouldRetryOnError and
679
628
  // revalidateOnFocus false, and useBranchesData's refreshInterval
680
- // is 0 without data. This used to be gated on `!branchNameState`,
681
- // which was harmless while a failed fetch merely left the UI
682
- // unlocked -- but content writes now fail CLOSED on missing branch
683
- // data, so with a pinned branch (every ordinary adopter setup)
684
- // that gate turned one network blip into a session-long lockout
685
- // whose only escape was a page reload, behind a "Manage Branches"
686
- // button that pointedly did not retry. Reloading is idempotent.
629
+ // is 0 without data. Reloading is idempotent.
687
630
  loadBranches().catch(console.error);
688
631
  }, onCommentsPanelOpen: () => setCommentsPanelOpen(true), onSave: handleSave, onSubmit: () => branchNameState && handleSubmit(branchNameState), onWithdraw: () => branchNameState && handleWithdraw(branchNameState) }), _jsx(Box, { style: {
689
632
  paddingTop: headerHeight,
@@ -0,0 +1,27 @@
1
+ /**
2
+ * EditorAuthGate — decides, from the SERVER, whether the editor below it may mount.
3
+ *
4
+ * A 401 from the CMS API is the only signed-out signal trusted: the API accepts or rejects every
5
+ * request, and client-side auth state (Clerk's `isSignedIn`, a dev cookie) can disagree with it.
6
+ *
7
+ * - The editor mounts only once `whoami` succeeds, so it always belongs to a known identity. A
8
+ * 401 first shows the provider's `SignInComponent` full-screen; any other failure shows the
9
+ * error with Retry. The editor's first requests therefore wait one `whoami` round trip.
10
+ * - After mount, a 401 from ANY request overlays the sign-in UI on the still-mounted editor, so
11
+ * unsaved edits survive re-authentication. The request that 401'd is not retried.
12
+ * - Re-authenticating as the same user keeps the editor and revalidates its SWR cache; as a
13
+ * different user the editor and its cache remount, because the SWRProvider is keyed by user id.
14
+ * - A re-check that fails for a non-auth reason keeps the sign-in UI up with the error and Retry,
15
+ * so a provider that has already reported sign-in is never left waiting on nothing.
16
+ */
17
+ import React from 'react';
18
+ import type { EditorSignInProps } from '../config/index.js';
19
+ import { type CanopyThemeOptions } from './theme.js';
20
+ export interface EditorAuthGateProps {
21
+ children: React.ReactNode;
22
+ /** The auth provider's sign-in UI. Absent, a plain "sign in, then continue" notice is shown. */
23
+ SignInComponent?: React.ComponentType<EditorSignInProps>;
24
+ /** The editor's theme, so the gate's own screens match it. */
25
+ themeOptions?: CanopyThemeOptions;
26
+ }
27
+ export declare function EditorAuthGate({ children, SignInComponent, themeOptions }: EditorAuthGateProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,140 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * EditorAuthGate — decides, from the SERVER, whether the editor below it may mount.
5
+ *
6
+ * A 401 from the CMS API is the only signed-out signal trusted: the API accepts or rejects every
7
+ * request, and client-side auth state (Clerk's `isSignedIn`, a dev cookie) can disagree with it.
8
+ *
9
+ * - The editor mounts only once `whoami` succeeds, so it always belongs to a known identity. A
10
+ * 401 first shows the provider's `SignInComponent` full-screen; any other failure shows the
11
+ * error with Retry. The editor's first requests therefore wait one `whoami` round trip.
12
+ * - After mount, a 401 from ANY request overlays the sign-in UI on the still-mounted editor, so
13
+ * unsaved edits survive re-authentication. The request that 401'd is not retried.
14
+ * - Re-authenticating as the same user keeps the editor and revalidates its SWR cache; as a
15
+ * different user the editor and its cache remount, because the SWRProvider is keyed by user id.
16
+ * - A re-check that fails for a non-auth reason keeps the sign-in UI up with the error and Retry,
17
+ * so a provider that has already reported sign-in is never left waiting on nothing.
18
+ */
19
+ import { useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
20
+ import { Alert, Button, Center, Loader, Modal, Paper, Stack, Text, Title } from '@mantine/core';
21
+ import { useSWRConfig } from 'swr';
22
+ import { getErrorMessage } from '../utils/error.js';
23
+ import { useApiClient, useOnUnauthorized } from './context/ApiClientContext.js';
24
+ import { EditorIdentityContext } from './context/EditorIdentityContext.js';
25
+ import { SWRProvider } from './context/SWRProvider.js';
26
+ import { CanopyCMSProvider } from './theme.js';
27
+ const initialGateState = {
28
+ phase: 'checking',
29
+ user: undefined,
30
+ sessionRejected: false,
31
+ error: undefined,
32
+ reauthCount: 0,
33
+ };
34
+ function gateReducer(state, action) {
35
+ switch (action.type) {
36
+ case 'accepted':
37
+ return {
38
+ phase: 'signed-in',
39
+ user: action.user,
40
+ sessionRejected: false,
41
+ error: undefined,
42
+ reauthCount: state.phase === 'signed-out' ? state.reauthCount + 1 : state.reauthCount,
43
+ };
44
+ case 'unauthorized': {
45
+ // Sticky while signed out: the mounted editor keeps making requests behind the overlay,
46
+ // they 401 too, and they must not clear a rejection the provider is showing.
47
+ const sessionRejected = (state.phase === 'signed-out' && state.sessionRejected) || action.afterSignIn;
48
+ // A re-check's own 401 supersedes an earlier failure; a background 401 does not.
49
+ const error = action.afterSignIn ? undefined : state.error;
50
+ const unchanged = state.phase === 'signed-out' &&
51
+ sessionRejected === state.sessionRejected &&
52
+ error === state.error;
53
+ return unchanged ? state : { ...state, phase: 'signed-out', sessionRejected, error };
54
+ }
55
+ case 'failed':
56
+ if (state.phase === 'signed-in')
57
+ return state;
58
+ return {
59
+ ...state,
60
+ phase: state.phase === 'checking' ? 'error' : state.phase,
61
+ error: action.error,
62
+ };
63
+ case 'retry':
64
+ return state.phase === 'error' ? { ...state, phase: 'checking', error: undefined } : state;
65
+ }
66
+ }
67
+ export function EditorAuthGate({ children, SignInComponent, themeOptions }) {
68
+ const apiClient = useApiClient();
69
+ const [state, dispatch] = useReducer(gateReducer, initialGateState);
70
+ const handleUnauthorized = useCallback(() => {
71
+ dispatch({ type: 'unauthorized', afterSignIn: false });
72
+ }, []);
73
+ useOnUnauthorized(handleUnauthorized);
74
+ const check = useCallback(async (afterSignIn) => {
75
+ try {
76
+ const result = await apiClient.user.whoami();
77
+ if (result.ok && result.data) {
78
+ dispatch({
79
+ type: 'accepted',
80
+ user: { userId: result.data.userId, groups: result.data.groups },
81
+ });
82
+ }
83
+ else if (result.status === 401) {
84
+ dispatch({ type: 'unauthorized', afterSignIn });
85
+ }
86
+ else {
87
+ dispatch({ type: 'failed', error: result.error ?? 'Failed to load user context' });
88
+ }
89
+ }
90
+ catch (err) {
91
+ // Only non-auth failures land here: the client returns every 401 as a result.
92
+ dispatch({ type: 'failed', error: getErrorMessage(err) });
93
+ }
94
+ }, [apiClient]);
95
+ useEffect(() => {
96
+ void check(false);
97
+ }, [check]);
98
+ const onSignedIn = useCallback(() => {
99
+ void check(true);
100
+ }, [check]);
101
+ const retryInitial = useCallback(() => {
102
+ dispatch({ type: 'retry' });
103
+ void check(false);
104
+ }, [check]);
105
+ const identity = useMemo(() => (state.user ? { user: state.user } : null), [state.user]);
106
+ const signIn = (_jsxs(_Fragment, { children: [state.phase === 'signed-out' && state.error && (_jsx(RetryNotice, { title: "Couldn't confirm your sign-in", error: state.error, onRetry: onSignedIn })), SignInComponent ? (_jsx(SignInComponent, { onSignedIn: onSignedIn, sessionRejected: state.sessionRejected })) : (_jsx(DefaultSignedOutNotice, { onSignedIn: onSignedIn, sessionRejected: state.sessionRejected }))] }));
107
+ if (!state.user || !identity) {
108
+ return (_jsx(CanopyCMSProvider, { ...themeOptions, withNotifications: false, children: _jsx(Center, { mih: "100vh", p: "md", children: state.phase === 'signed-out' ? (_jsx(Stack, { align: "center", gap: "md", "data-testid": "canopy-sign-in-screen", children: signIn })) : state.phase === 'error' ? (_jsx(RetryNotice, { title: "Couldn't load the editor", error: state.error ?? 'Unknown error', onRetry: retryInitial })) : (_jsxs(Stack, { align: "center", gap: "xs", "data-testid": "canopy-auth-checking", children: [_jsx(Loader, {}), _jsx(Text, { size: "sm", c: "dimmed", children: "Checking sign-in\u2026" })] })) }) }));
109
+ }
110
+ return (_jsxs(EditorIdentityContext.Provider, { value: identity, children: [_jsxs(SWRProvider, { children: [_jsx(RevalidateOnReauth, { reauthCount: state.reauthCount }), children] }, state.user.userId), state.phase === 'signed-out' && (
111
+ // The editor's own MantineProvider is inside <Editor>, so the overlay brings its own.
112
+ // Notifications stay with the editor's provider; a second container would duplicate them.
113
+ _jsx(CanopyCMSProvider, { ...themeOptions, withNotifications: false, children: _jsx(Modal, { opened: true, onClose: keepOpen, withCloseButton: false, closeOnClickOutside: false, closeOnEscape: false, centered: true, title: "Sign in to continue", children: _jsxs(Stack, { gap: "md", "data-testid": "canopy-sign-in-overlay", children: [_jsx(Text, { size: "sm", children: "Your session has ended. Unsaved edits stay in place if you sign back in as the same user." }), signIn] }) }) }))] }));
114
+ }
115
+ /** The overlay closes only when the server accepts a session again. */
116
+ function keepOpen() { }
117
+ /**
118
+ * Revalidates every SWR key when the same identity is re-accepted. `revalidateOnFocus` is off
119
+ * (see SWRProvider), so without this the data that 401'd behind the overlay would stay stale.
120
+ * A different identity remounts the SWRProvider instead, and this with it.
121
+ */
122
+ function RevalidateOnReauth({ reauthCount }) {
123
+ const { mutate } = useSWRConfig();
124
+ const seen = useRef(reauthCount);
125
+ useEffect(() => {
126
+ if (seen.current === reauthCount)
127
+ return;
128
+ seen.current = reauthCount;
129
+ // Each data hook surfaces its own fetch error; nothing to add here.
130
+ mutate(() => true).catch(() => undefined);
131
+ }, [reauthCount, mutate]);
132
+ return null;
133
+ }
134
+ function RetryNotice(props) {
135
+ return (_jsx(Alert, { color: "red", title: props.title, maw: 480, "data-testid": "canopy-auth-error", children: _jsxs(Stack, { gap: "xs", align: "flex-start", children: [_jsx(Text, { size: "sm", children: props.error }), _jsx(Button, { size: "xs", variant: "light", onClick: props.onRetry, children: "Retry" })] }) }));
136
+ }
137
+ /** Re-checks rather than reloading: in the overlay, a reload would discard the unsaved edits. */
138
+ function DefaultSignedOutNotice({ onSignedIn, sessionRejected }) {
139
+ return (_jsx(Paper, { withBorder: true, p: "xl", radius: "md", maw: 420, children: _jsxs(Stack, { gap: "sm", children: [_jsx(Title, { order: 3, children: "Sign in required" }), _jsx(Text, { size: "sm", children: "You are not signed in to the CMS. Sign in through this site's auth provider, then continue." }), sessionRejected && (_jsx(Text, { size: "sm", c: "red", children: "Still not signed in." })), _jsx(Button, { onClick: onSignedIn, children: "Continue" })] }) }));
140
+ }
@@ -36,7 +36,6 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
36
36
  const childCollections = col.children ?? [];
37
37
  const totalChildren = entries.length + childCollections.length;
38
38
  const order = col.order ?? [];
39
- // Build entry nodes keyed by contentId
40
39
  const entryNodesByContentId = new Map();
41
40
  entries.forEach((entry) => {
42
41
  const node = {
@@ -57,7 +56,6 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
57
56
  entryNodesByContentId.set(entry.contentId, node);
58
57
  }
59
58
  });
60
- // Build child collection nodes keyed by contentId
61
59
  const childNodesByContentId = new Map();
62
60
  childCollections.forEach((child) => {
63
61
  const childTree = toTree(child);
@@ -75,10 +73,8 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
75
73
  childNodesByContentId.set(child.contentId, node);
76
74
  }
77
75
  });
78
- // Interleave entries and children based on order array
79
76
  const allChildren = [];
80
77
  const usedContentIds = new Set();
81
- // First, add items in order
82
78
  for (const contentId of order) {
83
79
  const entryNode = entryNodesByContentId.get(contentId);
84
80
  if (entryNode) {
@@ -100,7 +96,6 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
100
96
  usedContentIds.add(contentId);
101
97
  }
102
98
  }
103
- // Add any entries not in order (alphabetically)
104
99
  const unorderedEntries = entries
105
100
  .filter((e) => !e.contentId || !usedContentIds.has(e.contentId))
106
101
  .sort((a, b) => (a.label ?? '').localeCompare(b.label ?? ''));
@@ -121,7 +116,6 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
121
116
  };
122
117
  allChildren.push(node);
123
118
  }
124
- // Add any child collections not in order (alphabetically)
125
119
  const unorderedChildren = childCollections
126
120
  .filter((c) => !c.contentId || !usedContentIds.has(c.contentId))
127
121
  .sort((a, b) => (a.label ?? '').localeCompare(b.label ?? ''));
@@ -172,34 +166,27 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
172
166
  nodeProps: { status: item.status, isEntry: true, entryPath: item.path },
173
167
  }));
174
168
  }, [collections, items, hiddenRootPath]);
175
- // Initialize tree controller
176
169
  const tree = useTree({
177
170
  initialExpandedState: expandedStateRef?.current ?? {},
178
171
  onNodeExpand: (value) => {
179
- // Update local state synchronously
180
172
  localExpandedStateRef.current = {
181
173
  ...localExpandedStateRef.current,
182
174
  [value]: true,
183
175
  };
184
- // Notify parent immediately
185
176
  onExpandedStateChange?.(localExpandedStateRef.current);
186
177
  },
187
178
  onNodeCollapse: (value) => {
188
- // Update local state synchronously
189
179
  localExpandedStateRef.current = {
190
180
  ...localExpandedStateRef.current,
191
181
  [value]: false,
192
182
  };
193
- // Notify parent immediately
194
183
  onExpandedStateChange?.(localExpandedStateRef.current);
195
184
  },
196
185
  });
197
- // Forward tree controller to parent for collapse/expand all functionality
198
186
  useEffect(() => {
199
187
  onTreeControllerReady?.(tree);
200
188
  // eslint-disable-next-line react-hooks/exhaustive-deps -- fire only when callback ref changes, not on tree state
201
189
  }, [onTreeControllerReady]);
202
- // Cleanup: save current state when component unmounts
203
190
  // Empty dependency array ensures this only runs on mount/unmount, not on re-renders
204
191
  useEffect(() => {
205
192
  return () => {
@@ -207,12 +194,10 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
207
194
  };
208
195
  // eslint-disable-next-line react-hooks/exhaustive-deps
209
196
  }, []);
210
- // Restore state on mount and when selectedPath changes
211
197
  useEffect(() => {
212
198
  if (!selectedPath || !treeData)
213
199
  return;
214
200
  const savedState = expandedStateRef?.current ?? {};
215
- // Calculate path to current entry and merge with saved state
216
201
  const pathToEntry = calculatePathToEntry(selectedPath, treeData);
217
202
  const baseState = { ...savedState, ...pathToEntry };
218
203
  // Only update if state actually changed to avoid infinite loops
@@ -221,13 +206,11 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
221
206
  if (currentStateJson !== newStateJson) {
222
207
  tree.setExpandedState(baseState);
223
208
  localExpandedStateRef.current = baseState;
224
- // Notify parent of the merged state
225
209
  onExpandedStateChange?.(baseState);
226
210
  }
227
211
  // Dependencies limited to data changes only to prevent infinite update loops
228
212
  // eslint-disable-next-line react-hooks/exhaustive-deps
229
213
  }, [selectedPath, treeData]);
230
- // Auto-scroll to selected entry when drawer opens
231
214
  useEffect(() => {
232
215
  if (selectedPath && selectedNodeRef.current && !hasScrolledRef.current) {
233
216
  // Small delay to ensure tree expansion completes first
@@ -242,7 +225,6 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
242
225
  return () => clearTimeout(timeoutId);
243
226
  }
244
227
  }, [selectedPath, tree.expandedState]);
245
- // Reset scroll flag when component mounts (drawer opens)
246
228
  useEffect(() => {
247
229
  hasScrolledRef.current = false;
248
230
  }, []);
@@ -272,11 +254,9 @@ export const EntryNavigator = ({ items, collections, selectedPath, onSelect, onT
272
254
  // Mantine only provides hasChildren=true if children.length > 0, but we want
273
255
  // collections to always be expandable
274
256
  const showChevron = hasChildren || Boolean(isCollection);
275
- // Reordering is available for both entries and collections that have contentId and parent path
276
257
  const canReorder = onReorderEntry && contentId && parentCollectionPath && typeof childIndex === 'number';
277
258
  const canMoveUp = canReorder && childIndex > 0;
278
259
  const canMoveDown = canReorder && typeof totalChildrenCount === 'number' && childIndex < totalChildrenCount - 1;
279
- // Determine if we should show a context menu
280
260
  // Collections show menu for add/edit/delete actions OR for reordering (subcollections)
281
261
  const hasCollectionMenu = isCollection && (onAdd || onEdit || onAddSubCollection || onDelete || canReorder);
282
262
  const hasEntryMenu = isEntry && entryPath && (onDeleteEntry || onRenameEntry || onReorderEntry);
@@ -32,9 +32,8 @@ export interface FormRendererProps {
32
32
  * Per-field validation errors keyed by canonical canopy path (e.g.
33
33
  * `blocks[0].title`). Rendered as a summary alert plus an inline message
34
34
  * under each offending field. Produced by the save-path schema validation
35
- * in useDraftManager (ED-H1) and by server 422 rejections.
35
+ * in useDraftManager and by server 422 rejections.
36
36
  */
37
37
  fieldErrors?: Record<string, string>;
38
38
  }
39
39
  export declare const FormRenderer: React.FC<FormRendererProps>;
40
- export default FormRenderer;