canopycms 0.0.68-int.98 → 0.1.0

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 (688) hide show
  1. package/CHANGELOG.md +2131 -0
  2. package/README.md +47 -63
  3. package/dist/ai/generate.d.ts +5 -0
  4. package/dist/ai/generate.js +244 -115
  5. package/dist/ai/index.d.ts +1 -1
  6. package/dist/ai/json-to-markdown.d.ts +21 -1
  7. package/dist/ai/json-to-markdown.js +120 -57
  8. package/dist/ai/references.d.ts +72 -0
  9. package/dist/ai/references.js +223 -0
  10. package/dist/ai/resolve-branch.js +9 -2
  11. package/dist/ai/to-plain-text.d.ts +17 -8
  12. package/dist/ai/to-plain-text.js +18 -15
  13. package/dist/ai/types.d.ts +84 -3
  14. package/dist/api/admin-branch-health.d.ts +24 -1
  15. package/dist/api/admin-branch-health.js +52 -20
  16. package/dist/api/admin.d.ts +23 -5
  17. package/dist/api/admin.js +36 -1
  18. package/dist/api/assets.js +88 -78
  19. package/dist/api/branch-create-window.d.ts +12 -0
  20. package/dist/api/branch-create-window.js +13 -0
  21. package/dist/api/branch-merge.d.ts +1 -1
  22. package/dist/api/branch-merge.js +1 -1
  23. package/dist/api/branch-review.d.ts +3 -3
  24. package/dist/api/branch-review.js +16 -7
  25. package/dist/api/branch-status.d.ts +16 -6
  26. package/dist/api/branch-status.js +57 -12
  27. package/dist/api/branch-withdraw.d.ts +2 -2
  28. package/dist/api/branch-withdraw.js +8 -3
  29. package/dist/api/branch.d.ts +25 -12
  30. package/dist/api/branch.js +164 -103
  31. package/dist/api/client.d.ts +78 -58
  32. package/dist/api/client.js +150 -117
  33. package/dist/api/comments.d.ts +12 -3
  34. package/dist/api/comments.js +118 -9
  35. package/dist/api/content.d.ts +9 -9
  36. package/dist/api/content.js +131 -22
  37. package/dist/api/entries-constants.d.ts +14 -3
  38. package/dist/api/entries-constants.js +14 -3
  39. package/dist/api/entries.d.ts +40 -2
  40. package/dist/api/entries.js +102 -6
  41. package/dist/api/github-sync.d.ts +19 -0
  42. package/dist/api/github-sync.js +50 -3
  43. package/dist/api/groups.d.ts +4 -4
  44. package/dist/api/groups.js +2 -1
  45. package/dist/api/guards.js +23 -3
  46. package/dist/api/index.d.ts +3 -1
  47. package/dist/api/index.js +1 -0
  48. package/dist/api/permissions.d.ts +21 -2
  49. package/dist/api/permissions.js +50 -3
  50. package/dist/api/request-deadline.d.ts +59 -0
  51. package/dist/api/request-deadline.js +73 -0
  52. package/dist/api/resolve-references.js +8 -7
  53. package/dist/api/route-builder.d.ts +14 -0
  54. package/dist/api/route-builder.js +14 -1
  55. package/dist/api/schema.d.ts +11 -9
  56. package/dist/api/schema.js +2 -0
  57. package/dist/api/settings-helpers.d.ts +5 -1
  58. package/dist/api/settings-helpers.js +10 -2
  59. package/dist/api/types.d.ts +18 -0
  60. package/dist/api/users-constants.d.ts +6 -0
  61. package/dist/api/users-constants.js +6 -0
  62. package/dist/assets/asset-url.d.ts +28 -16
  63. package/dist/assets/asset-url.js +51 -25
  64. package/dist/assets/finalize.js +3 -0
  65. package/dist/assets/index.d.ts +1 -1
  66. package/dist/assets/materialize.d.ts +153 -0
  67. package/dist/assets/materialize.js +402 -0
  68. package/dist/assets/store-local.d.ts +19 -5
  69. package/dist/assets/store-local.js +69 -10
  70. package/dist/assets/store-s3.d.ts +38 -7
  71. package/dist/assets/store-s3.js +128 -22
  72. package/dist/assets/transform-directives.d.ts +52 -13
  73. package/dist/assets/transform-directives.js +87 -27
  74. package/dist/assets/transform.d.ts +3 -3
  75. package/dist/assets/transform.js +13 -8
  76. package/dist/assets/types.d.ts +31 -4
  77. package/dist/auth/cache.d.ts +1 -1
  78. package/dist/auth/cache.js +1 -1
  79. package/dist/auth/caching-auth-plugin.d.ts +2 -0
  80. package/dist/auth/caching-auth-plugin.js +12 -2
  81. package/dist/auth/file-based-auth-cache.d.ts +8 -0
  82. package/dist/auth/file-based-auth-cache.js +18 -8
  83. package/dist/auth/person-name.d.ts +5 -0
  84. package/dist/auth/person-name.js +8 -0
  85. package/dist/auth/plugin.d.ts +10 -1
  86. package/dist/auth/types.d.ts +2 -0
  87. package/dist/auth/user-metadata-lookup.d.ts +21 -0
  88. package/dist/auth/user-metadata-lookup.js +116 -0
  89. package/dist/authorization/branch.d.ts +1 -1
  90. package/dist/authorization/branch.js +12 -13
  91. package/dist/authorization/groups/schema.d.ts +4 -4
  92. package/dist/authorization/helpers.d.ts +8 -0
  93. package/dist/authorization/helpers.js +8 -0
  94. package/dist/authorization/index.d.ts +1 -1
  95. package/dist/authorization/index.js +1 -1
  96. package/dist/authorization/protected-branch.d.ts +5 -4
  97. package/dist/authorization/protected-branch.js +7 -5
  98. package/dist/branch-health.d.ts +35 -8
  99. package/dist/branch-health.js +54 -18
  100. package/dist/branch-metadata-error.d.ts +27 -0
  101. package/dist/branch-metadata-error.js +23 -0
  102. package/dist/branch-metadata-file.d.ts +205 -24
  103. package/dist/branch-metadata-file.js +126 -25
  104. package/dist/branch-metadata.d.ts +45 -6
  105. package/dist/branch-metadata.js +142 -50
  106. package/dist/branch-provisioning.d.ts +167 -0
  107. package/dist/branch-provisioning.js +501 -0
  108. package/dist/branch-registry.d.ts +8 -0
  109. package/dist/branch-registry.js +61 -9
  110. package/dist/branch-schema-cache.d.ts +29 -12
  111. package/dist/branch-schema-cache.js +64 -21
  112. package/dist/branch-sparse.d.ts +23 -0
  113. package/dist/branch-sparse.js +118 -0
  114. package/dist/branch-workspace.d.ts +42 -3
  115. package/dist/branch-workspace.js +239 -100
  116. package/dist/build/asset-refs.d.ts +94 -0
  117. package/dist/build/asset-refs.js +267 -0
  118. package/dist/build/generate-ai-content.js +5 -0
  119. package/dist/build/inline-flight.d.ts +37 -0
  120. package/dist/build/inline-flight.js +142 -0
  121. package/dist/cli/asset-refs.d.ts +52 -0
  122. package/dist/cli/asset-refs.js +194 -0
  123. package/dist/cli/cli.d.ts +15 -1
  124. package/dist/cli/cli.js +8543 -3193
  125. package/dist/cli/configured-asset-store.d.ts +12 -0
  126. package/dist/cli/configured-asset-store.js +46 -0
  127. package/dist/cli/generate-ai-content.js +4244 -1072
  128. package/dist/cli/init.js +217 -126
  129. package/dist/cli/project-detect.d.ts +2 -0
  130. package/dist/cli/project-detect.js +2 -0
  131. package/dist/cli/template-files/Dockerfile.cms.template +13 -5
  132. package/dist/cli/template-files/cms-stack.ts.template +16 -13
  133. package/dist/client.d.ts +1 -2
  134. package/dist/client.js +1 -2
  135. package/dist/comment-store.d.ts +25 -0
  136. package/dist/comment-store.js +43 -8
  137. package/dist/config/flatten.js +2 -0
  138. package/dist/config/helpers.js +2 -2
  139. package/dist/config/index.d.ts +1 -1
  140. package/dist/config/schemas/config.d.ts +12 -29
  141. package/dist/config/schemas/config.js +9 -12
  142. package/dist/config/schemas/field.d.ts +3 -0
  143. package/dist/config/schemas/field.js +58 -4
  144. package/dist/config/schemas/media.d.ts +4 -13
  145. package/dist/config/schemas/media.js +5 -8
  146. package/dist/config/schemas/url.d.ts +2 -20
  147. package/dist/config/schemas/url.js +2 -25
  148. package/dist/config/types.d.ts +116 -22
  149. package/dist/config/types.js +3 -9
  150. package/dist/config/validation.d.ts +25 -4
  151. package/dist/config/validation.js +116 -3
  152. package/dist/content-id-index.js +2 -2
  153. package/dist/content-index-generation.d.ts +1 -1
  154. package/dist/content-index-generation.js +1 -1
  155. package/dist/content-listing.d.ts +10 -1
  156. package/dist/content-listing.js +22 -5
  157. package/dist/content-reader.d.ts +9 -0
  158. package/dist/content-reader.js +16 -11
  159. package/dist/content-store.d.ts +75 -12
  160. package/dist/content-store.js +249 -86
  161. package/dist/content-tree.js +2 -2
  162. package/dist/context.js +5 -2
  163. package/dist/dangling-reference-log.d.ts +17 -0
  164. package/dist/dangling-reference-log.js +29 -0
  165. package/dist/dev-content-watcher.d.ts +1 -1
  166. package/dist/dev-content-watcher.js +1 -1
  167. package/dist/editor/BranchManager.d.ts +23 -7
  168. package/dist/editor/BranchManager.js +119 -54
  169. package/dist/editor/CanopyEditor.d.ts +1 -1
  170. package/dist/editor/CanopyEditor.js +1 -2
  171. package/dist/editor/CommentsPanel.d.ts +14 -0
  172. package/dist/editor/CommentsPanel.js +68 -43
  173. package/dist/editor/Editor.d.ts +11 -5
  174. package/dist/editor/Editor.js +891 -218
  175. package/dist/editor/EditorAuthGate.d.ts +7 -1
  176. package/dist/editor/EditorAuthGate.js +41 -9
  177. package/dist/editor/EntryNavigator.d.ts +22 -0
  178. package/dist/editor/EntryNavigator.js +353 -105
  179. package/dist/editor/FormHeader.d.ts +25 -0
  180. package/dist/editor/FormHeader.js +82 -0
  181. package/dist/editor/FormRenderer.d.ts +27 -3
  182. package/dist/editor/FormRenderer.js +171 -76
  183. package/dist/editor/PreviewFrame.d.ts +40 -0
  184. package/dist/editor/PreviewFrame.js +215 -0
  185. package/dist/editor/PreviewPane.d.ts +47 -0
  186. package/dist/editor/PreviewPane.js +147 -0
  187. package/dist/editor/PreviewToolbarTools.d.ts +22 -0
  188. package/dist/editor/PreviewToolbarTools.js +73 -0
  189. package/dist/editor/admin/SystemHealthPanel.d.ts +2 -1
  190. package/dist/editor/admin/SystemHealthPanel.js +163 -33
  191. package/dist/editor/admin/useSystemHealth.d.ts +18 -0
  192. package/dist/editor/admin/useSystemHealth.js +70 -1
  193. package/dist/editor/branch-status.d.ts +17 -0
  194. package/dist/editor/branch-status.js +29 -0
  195. package/dist/editor/canopy-path.d.ts +9 -6
  196. package/dist/editor/canopy-path.js +8 -11
  197. package/dist/editor/client-reference-resolver.d.ts +43 -16
  198. package/dist/editor/client-reference-resolver.js +155 -64
  199. package/dist/editor/comments/BranchComments.d.ts +1 -1
  200. package/dist/editor/comments/BranchComments.js +2 -2
  201. package/dist/editor/comments/CloseRefusedNotice.d.ts +4 -0
  202. package/dist/editor/comments/CloseRefusedNotice.js +7 -0
  203. package/dist/editor/comments/CommentThreadsPopover.d.ts +47 -0
  204. package/dist/editor/comments/CommentThreadsPopover.js +274 -0
  205. package/dist/editor/comments/CommentWriteAlert.d.ts +11 -0
  206. package/dist/editor/comments/CommentWriteAlert.js +13 -0
  207. package/dist/editor/comments/EntryComments.d.ts +10 -4
  208. package/dist/editor/comments/EntryComments.js +14 -17
  209. package/dist/editor/comments/FieldWrapper.d.ts +18 -4
  210. package/dist/editor/comments/FieldWrapper.js +62 -28
  211. package/dist/editor/comments/InlineCommentThread.d.ts +9 -2
  212. package/dist/editor/comments/InlineCommentThread.js +11 -47
  213. package/dist/editor/comments/ThreadCarousel.d.ts +1 -1
  214. package/dist/editor/comments/ThreadCarousel.js +18 -21
  215. package/dist/editor/comments/ThreadReplyControls.d.ts +28 -0
  216. package/dist/editor/comments/ThreadReplyControls.js +92 -0
  217. package/dist/editor/comments/field-comment-context.d.ts +14 -0
  218. package/dist/editor/comments/field-comment-context.js +14 -0
  219. package/dist/editor/comments/form-comments.d.ts +48 -0
  220. package/dist/editor/comments/form-comments.js +178 -0
  221. package/dist/editor/comments/item-anchor-copy.d.ts +6 -0
  222. package/dist/editor/comments/item-anchor-copy.js +6 -0
  223. package/dist/editor/comments/move-focus-if-unmoved.d.ts +6 -0
  224. package/dist/editor/comments/move-focus-if-unmoved.js +11 -0
  225. package/dist/editor/comments/popover-placement.d.ts +18 -0
  226. package/dist/editor/comments/popover-placement.js +58 -0
  227. package/dist/editor/comments/use-tracked-writes.d.ts +16 -0
  228. package/dist/editor/comments/use-tracked-writes.js +53 -0
  229. package/dist/editor/components/BranchAccessList.d.ts +15 -0
  230. package/dist/editor/components/BranchAccessList.js +52 -0
  231. package/dist/editor/components/BranchLockBanner.d.ts +22 -0
  232. package/dist/editor/components/BranchLockBanner.js +39 -0
  233. package/dist/editor/components/BranchPill.d.ts +32 -0
  234. package/dist/editor/components/BranchPill.js +33 -0
  235. package/dist/editor/components/BranchesDrawer.d.ts +11 -0
  236. package/dist/editor/components/BranchesDrawer.js +5 -0
  237. package/dist/editor/components/ConfirmDeleteModal.d.ts +6 -1
  238. package/dist/editor/components/ConfirmDeleteModal.js +3 -2
  239. package/dist/editor/components/DestructiveMenuItem.d.ts +7 -0
  240. package/dist/editor/components/DestructiveMenuItem.js +8 -0
  241. package/dist/editor/components/EditorCrashScreen.d.ts +17 -0
  242. package/dist/editor/components/EditorCrashScreen.js +22 -0
  243. package/dist/editor/components/EditorErrorBoundary.d.ts +38 -0
  244. package/dist/editor/components/EditorErrorBoundary.js +40 -0
  245. package/dist/editor/components/EditorHeader.d.ts +56 -106
  246. package/dist/editor/components/EditorHeader.js +130 -131
  247. package/dist/editor/components/EditorRail.d.ts +49 -0
  248. package/dist/editor/components/EditorRail.js +65 -0
  249. package/dist/editor/components/EntryBreadcrumb.d.ts +33 -0
  250. package/dist/editor/components/EntryBreadcrumb.js +56 -0
  251. package/dist/editor/components/EntryCreateModal.d.ts +12 -7
  252. package/dist/editor/components/EntryCreateModal.js +51 -57
  253. package/dist/editor/components/NoEditPermissionNotice.d.ts +6 -5
  254. package/dist/editor/components/NoEditPermissionNotice.js +5 -4
  255. package/dist/editor/components/QuickOpenPalette.d.ts +22 -0
  256. package/dist/editor/components/QuickOpenPalette.js +66 -0
  257. package/dist/editor/components/ReadOnlyDraftNotice.d.ts +11 -0
  258. package/dist/editor/components/ReadOnlyDraftNotice.js +12 -0
  259. package/dist/editor/components/ReasonedMenuItem.d.ts +15 -0
  260. package/dist/editor/components/ReasonedMenuItem.js +15 -0
  261. package/dist/editor/components/ReferencedByList.d.ts +11 -0
  262. package/dist/editor/components/ReferencedByList.js +28 -0
  263. package/dist/editor/components/RelativeTime.d.ts +10 -0
  264. package/dist/editor/components/RelativeTime.js +10 -0
  265. package/dist/editor/components/RenameEntryModal.d.ts +9 -1
  266. package/dist/editor/components/RenameEntryModal.js +19 -31
  267. package/dist/editor/components/RetryToastMessage.d.ts +9 -0
  268. package/dist/editor/components/RetryToastMessage.js +6 -0
  269. package/dist/editor/components/SaveAllResult.d.ts +25 -0
  270. package/dist/editor/components/SaveAllResult.js +86 -0
  271. package/dist/editor/components/SaveSplitButton.d.ts +30 -0
  272. package/dist/editor/components/SaveSplitButton.js +108 -0
  273. package/dist/editor/components/StagedChangesDrawer.d.ts +39 -0
  274. package/dist/editor/components/StagedChangesDrawer.js +92 -0
  275. package/dist/editor/components/UnavailableEntryNotice.d.ts +6 -0
  276. package/dist/editor/components/UnavailableEntryNotice.js +9 -0
  277. package/dist/editor/components/UnavailableTypeMessage.d.ts +6 -0
  278. package/dist/editor/components/UnavailableTypeMessage.js +9 -0
  279. package/dist/editor/components/UndoToastMessage.d.ts +18 -0
  280. package/dist/editor/components/UndoToastMessage.js +10 -0
  281. package/dist/editor/components/UserBadge.d.ts +2 -5
  282. package/dist/editor/components/UserBadge.js +62 -33
  283. package/dist/editor/components/ValidationSummary.d.ts +14 -0
  284. package/dist/editor/components/ValidationSummary.js +24 -0
  285. package/dist/editor/components/index.d.ts +2 -2
  286. package/dist/editor/components/index.js +2 -2
  287. package/dist/editor/context/ApiClientContext.d.ts +8 -2
  288. package/dist/editor/context/ApiClientContext.js +19 -8
  289. package/dist/editor/context/AssetContext.d.ts +13 -35
  290. package/dist/editor/context/AssetContext.js +12 -31
  291. package/dist/editor/context/EditorStateContext.d.ts +4 -4
  292. package/dist/editor/context/EditorStateContext.js +4 -4
  293. package/dist/editor/context/SWRProvider.d.ts +3 -3
  294. package/dist/editor/context/SiteMdxAllowContext.d.ts +4 -0
  295. package/dist/editor/context/SiteMdxAllowContext.js +7 -0
  296. package/dist/editor/context/index.d.ts +2 -1
  297. package/dist/editor/context/index.js +2 -1
  298. package/dist/editor/contrast.d.ts +19 -0
  299. package/dist/editor/contrast.js +41 -0
  300. package/dist/editor/copy.d.ts +18 -0
  301. package/dist/editor/copy.js +18 -0
  302. package/dist/editor/editor-config.js +16 -0
  303. package/dist/editor/editor-utils.d.ts +34 -8
  304. package/dist/editor/editor-utils.js +42 -20
  305. package/dist/editor/entry-search.d.ts +20 -0
  306. package/dist/editor/entry-search.js +34 -0
  307. package/dist/editor/field-breadcrumb.d.ts +12 -0
  308. package/dist/editor/field-breadcrumb.js +85 -0
  309. package/dist/editor/field-props.d.ts +86 -0
  310. package/dist/editor/field-props.js +27 -0
  311. package/dist/editor/field-target.d.ts +30 -0
  312. package/dist/editor/field-target.js +77 -0
  313. package/dist/editor/fields/BlockField.d.ts +12 -0
  314. package/dist/editor/fields/BlockField.js +89 -73
  315. package/dist/editor/fields/CodeField.d.ts +2 -0
  316. package/dist/editor/fields/CodeField.js +6 -2
  317. package/dist/editor/fields/DateTimeField.d.ts +4 -29
  318. package/dist/editor/fields/DateTimeField.js +79 -41
  319. package/dist/editor/fields/ExpandAllButton.d.ts +6 -0
  320. package/dist/editor/fields/ExpandAllButton.js +4 -0
  321. package/dist/editor/fields/FieldCrashFallback.d.ts +17 -0
  322. package/dist/editor/fields/FieldCrashFallback.js +30 -0
  323. package/dist/editor/fields/FieldLabel.d.ts +37 -0
  324. package/dist/editor/fields/FieldLabel.js +19 -0
  325. package/dist/editor/fields/ImageField.d.ts +4 -0
  326. package/dist/editor/fields/ImageField.js +29 -14
  327. package/dist/editor/fields/InlineGroupField.js +4 -3
  328. package/dist/editor/fields/ItemList.d.ts +42 -0
  329. package/dist/editor/fields/ItemList.js +130 -0
  330. package/dist/editor/fields/MarkdownField.d.ts +32 -0
  331. package/dist/editor/fields/MarkdownField.js +223 -46
  332. package/dist/editor/fields/MdxImageDialog.js +1 -1
  333. package/dist/editor/fields/NumberField.d.ts +2 -0
  334. package/dist/editor/fields/NumberField.js +8 -5
  335. package/dist/editor/fields/NumberListField.d.ts +2 -0
  336. package/dist/editor/fields/NumberListField.js +19 -16
  337. package/dist/editor/fields/ObjectField.d.ts +2 -7
  338. package/dist/editor/fields/ObjectField.js +15 -5
  339. package/dist/editor/fields/ObjectListField.d.ts +29 -0
  340. package/dist/editor/fields/ObjectListField.js +57 -0
  341. package/dist/editor/fields/ReferenceField.d.ts +2 -0
  342. package/dist/editor/fields/ReferenceField.js +34 -9
  343. package/dist/editor/fields/SelectField.d.ts +2 -0
  344. package/dist/editor/fields/SelectField.js +11 -2
  345. package/dist/editor/fields/StringListField.d.ts +4 -1
  346. package/dist/editor/fields/StringListField.js +22 -8
  347. package/dist/editor/fields/TextField.d.ts +2 -0
  348. package/dist/editor/fields/TextField.js +6 -2
  349. package/dist/editor/fields/ToggleField.d.ts +2 -0
  350. package/dist/editor/fields/ToggleField.js +8 -3
  351. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +1 -1
  352. package/dist/editor/fields/entry-link/InsertEntryLink.js +1 -1
  353. package/dist/editor/fields/form-expansion.d.ts +9 -0
  354. package/dist/editor/fields/form-expansion.js +2 -0
  355. package/dist/editor/fields/item-expansion.d.ts +15 -0
  356. package/dist/editor/fields/item-expansion.js +60 -0
  357. package/dist/editor/fields/list-item-keys.d.ts +8 -0
  358. package/dist/editor/fields/list-item-keys.js +50 -0
  359. package/dist/editor/fields/list-item-title.d.ts +20 -0
  360. package/dist/editor/fields/list-item-title.js +85 -0
  361. package/dist/editor/fields/markdown-export-options.d.ts +2 -0
  362. package/dist/editor/fields/markdown-export-options.js +8 -0
  363. package/dist/editor/fields/markdown-fidelity-visitors.d.ts +11 -0
  364. package/dist/editor/fields/markdown-fidelity-visitors.js +160 -0
  365. package/dist/editor/fields/mdx-jsx-support.d.ts +5 -1
  366. package/dist/editor/fields/mdx-jsx-support.js +157 -69
  367. package/dist/editor/fields/rich-text-failures.d.ts +5 -0
  368. package/dist/editor/fields/rich-text-failures.js +26 -0
  369. package/dist/editor/fields/toolbar-wrap.d.ts +7 -0
  370. package/dist/editor/fields/toolbar-wrap.js +35 -0
  371. package/dist/editor/form-outline.d.ts +13 -0
  372. package/dist/editor/form-outline.js +70 -0
  373. package/dist/editor/group-manager/GroupCard.d.ts +4 -1
  374. package/dist/editor/group-manager/GroupCard.js +12 -3
  375. package/dist/editor/group-manager/GroupForm.js +1 -1
  376. package/dist/editor/group-manager/InternalGroupsTab.d.ts +2 -1
  377. package/dist/editor/group-manager/InternalGroupsTab.js +4 -4
  378. package/dist/editor/group-manager/MemberList.d.ts +2 -1
  379. package/dist/editor/group-manager/MemberList.js +15 -4
  380. package/dist/editor/group-manager/hooks/useGroupState.d.ts +5 -0
  381. package/dist/editor/group-manager/hooks/useGroupState.js +30 -7
  382. package/dist/editor/group-manager/index.js +34 -5
  383. package/dist/editor/group-manager/types.d.ts +5 -2
  384. package/dist/editor/hooks/create-branch-request.d.ts +20 -0
  385. package/dist/editor/hooks/create-branch-request.js +54 -0
  386. package/dist/editor/hooks/save-all.d.ts +68 -0
  387. package/dist/editor/hooks/save-all.js +61 -0
  388. package/dist/editor/hooks/useBranchActions.d.ts +11 -1
  389. package/dist/editor/hooks/useBranchActions.js +42 -19
  390. package/dist/editor/hooks/useBranchManager.d.ts +21 -0
  391. package/dist/editor/hooks/useBranchManager.js +321 -125
  392. package/dist/editor/hooks/useBranchesData.d.ts +8 -4
  393. package/dist/editor/hooks/useBranchesData.js +9 -5
  394. package/dist/editor/hooks/useCommentSystem.d.ts +14 -2
  395. package/dist/editor/hooks/useCommentSystem.js +79 -55
  396. package/dist/editor/hooks/useCommentsData.d.ts +11 -7
  397. package/dist/editor/hooks/useCommentsData.js +20 -19
  398. package/dist/editor/hooks/useDraftManager.d.ts +77 -3
  399. package/dist/editor/hooks/useDraftManager.js +887 -94
  400. package/dist/editor/hooks/useEditorLayout.d.ts +53 -7
  401. package/dist/editor/hooks/useEditorLayout.js +98 -20
  402. package/dist/editor/hooks/useEntriesData.d.ts +2 -1
  403. package/dist/editor/hooks/useEntriesData.js +18 -4
  404. package/dist/editor/hooks/useEntryManager.d.ts +41 -2
  405. package/dist/editor/hooks/useEntryManager.js +227 -41
  406. package/dist/editor/hooks/useGroupManager.js +13 -16
  407. package/dist/editor/hooks/usePermissionManager.js +14 -6
  408. package/dist/editor/hooks/usePreviewMarks.d.ts +21 -0
  409. package/dist/editor/hooks/usePreviewMarks.js +33 -0
  410. package/dist/editor/hooks/useReferenceResolution.d.ts +10 -3
  411. package/dist/editor/hooks/useReferenceResolution.js +34 -177
  412. package/dist/editor/hooks/useSchemaManager.d.ts +25 -6
  413. package/dist/editor/hooks/useSchemaManager.js +97 -39
  414. package/dist/editor/hooks/useUserMetadata.d.ts +14 -4
  415. package/dist/editor/hooks/useUserMetadata.js +76 -47
  416. package/dist/editor/hooks/user-metadata-batcher.d.ts +8 -0
  417. package/dist/editor/hooks/user-metadata-batcher.js +33 -0
  418. package/dist/editor/media/AssetCard.d.ts +6 -7
  419. package/dist/editor/media/AssetCard.js +24 -31
  420. package/dist/editor/media/AssetListRow.d.ts +16 -0
  421. package/dist/editor/media/AssetListRow.js +41 -0
  422. package/dist/editor/media/CropStep.js +6 -1
  423. package/dist/editor/media/MediaLibrary.d.ts +4 -3
  424. package/dist/editor/media/MediaLibrary.js +11 -4
  425. package/dist/editor/media/MediaLibraryBody.d.ts +3 -3
  426. package/dist/editor/media/MediaLibraryBody.js +195 -80
  427. package/dist/editor/media/MediaLibrarySkeleton.d.ts +6 -0
  428. package/dist/editor/media/MediaLibrarySkeleton.js +14 -0
  429. package/dist/editor/media/crop-math.d.ts +3 -4
  430. package/dist/editor/media/crop-math.js +11 -22
  431. package/dist/editor/media/editor-image-src.d.ts +8 -0
  432. package/dist/editor/media/editor-image-src.js +10 -0
  433. package/dist/editor/media/format-bytes.d.ts +2 -0
  434. package/dist/editor/media/format-bytes.js +23 -0
  435. package/dist/editor/media/media-library-prefs.d.ts +23 -0
  436. package/dist/editor/media/media-library-prefs.js +49 -0
  437. package/dist/editor/media/media-library-view.d.ts +34 -0
  438. package/dist/editor/media/media-library-view.js +40 -0
  439. package/dist/editor/media/useAssetLibrary.d.ts +30 -0
  440. package/dist/editor/media/useAssetLibrary.js +126 -0
  441. package/dist/editor/media/useStableAssetOrder.d.ts +11 -0
  442. package/dist/editor/media/useStableAssetOrder.js +37 -0
  443. package/dist/editor/media/useThumbnailFailure.d.ts +6 -0
  444. package/dist/editor/media/useThumbnailFailure.js +16 -0
  445. package/dist/editor/permission-manager/GroupSelector.js +2 -2
  446. package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -1
  447. package/dist/editor/permission-manager/PermissionEditor.js +15 -4
  448. package/dist/editor/permission-manager/PermissionLevelBadge.js +1 -1
  449. package/dist/editor/permission-manager/PermissionTree.d.ts +1 -1
  450. package/dist/editor/permission-manager/UserSelector.d.ts +1 -1
  451. package/dist/editor/permission-manager/UserSelector.js +3 -3
  452. package/dist/editor/permission-manager/index.js +12 -3
  453. package/dist/editor/permission-manager/types.d.ts +5 -2
  454. package/dist/editor/platform.d.ts +3 -0
  455. package/dist/editor/platform.js +9 -0
  456. package/dist/editor/preview-asset-base.d.ts +9 -0
  457. package/dist/editor/preview-asset-base.js +17 -0
  458. package/dist/editor/preview-bridge.d.ts +51 -27
  459. package/dist/editor/preview-bridge.js +103 -144
  460. package/dist/editor/preview-marks.d.ts +15 -0
  461. package/dist/editor/preview-marks.js +74 -0
  462. package/dist/editor/preview-path.d.ts +12 -0
  463. package/dist/editor/preview-path.js +45 -1
  464. package/dist/editor/preview-scale.d.ts +27 -0
  465. package/dist/editor/preview-scale.js +34 -0
  466. package/dist/editor/raw-asset-base.d.ts +5 -0
  467. package/dist/editor/raw-asset-base.js +14 -0
  468. package/dist/editor/relative-time.d.ts +4 -4
  469. package/dist/editor/relative-time.js +16 -8
  470. package/dist/editor/schema-editor/CollectionEditor.d.ts +20 -3
  471. package/dist/editor/schema-editor/CollectionEditor.js +361 -62
  472. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +5 -1
  473. package/dist/editor/schema-editor/EntryTypeEditor.js +11 -6
  474. package/dist/editor/schema-editor/collection-draft.d.ts +22 -0
  475. package/dist/editor/schema-editor/collection-draft.js +40 -0
  476. package/dist/editor/schema-editor/index.d.ts +1 -1
  477. package/dist/editor/schema-label.d.ts +10 -0
  478. package/dist/editor/schema-label.js +17 -0
  479. package/dist/editor/settings-conflict.d.ts +5 -0
  480. package/dist/editor/settings-conflict.js +8 -0
  481. package/dist/editor/shell/ActivityRail.d.ts +37 -0
  482. package/dist/editor/shell/ActivityRail.js +91 -0
  483. package/dist/editor/shell/EditorShell.d.ts +17 -0
  484. package/dist/editor/shell/EditorShell.js +31 -0
  485. package/dist/editor/shell/PaneLayout.d.ts +28 -0
  486. package/dist/editor/shell/PaneLayout.js +116 -0
  487. package/dist/editor/shell/SidePanelHost.d.ts +35 -0
  488. package/dist/editor/shell/SidePanelHost.js +184 -0
  489. package/dist/editor/shell/TopBar.d.ts +12 -0
  490. package/dist/editor/shell/TopBar.js +14 -0
  491. package/dist/editor/shell/host-chrome.d.ts +16 -0
  492. package/dist/editor/shell/host-chrome.js +69 -0
  493. package/dist/editor/slug.d.ts +16 -0
  494. package/dist/editor/slug.js +43 -0
  495. package/dist/editor/theme.d.ts +2 -1
  496. package/dist/editor/theme.js +314 -13
  497. package/dist/editor/unanswered.d.ts +17 -0
  498. package/dist/editor/unanswered.js +26 -0
  499. package/dist/editor/unavailable-entry-type.d.ts +20 -0
  500. package/dist/editor/unavailable-entry-type.js +28 -0
  501. package/dist/editor/undo-removal.d.ts +57 -0
  502. package/dist/editor/undo-removal.js +163 -0
  503. package/dist/editor/user-display.d.ts +4 -0
  504. package/dist/editor/user-display.js +5 -0
  505. package/dist/editor/utils/confirm-modal.d.ts +8 -0
  506. package/dist/editor/utils/confirm-modal.js +12 -0
  507. package/dist/editor/utils/editor-errors.d.ts +19 -0
  508. package/dist/editor/utils/editor-errors.js +32 -0
  509. package/dist/editor/utils/focus-return.d.ts +56 -0
  510. package/dist/editor/utils/focus-return.js +106 -0
  511. package/dist/entry-link-resolver.d.ts +11 -1
  512. package/dist/entry-link-resolver.js +65 -5
  513. package/dist/entry-schema-registry.d.ts +3 -3
  514. package/dist/entry-schema-registry.js +7 -4
  515. package/dist/entry-schema.d.ts +49 -3
  516. package/dist/entry-schema.js +18 -0
  517. package/dist/git-manager.d.ts +172 -10
  518. package/dist/git-manager.js +449 -107
  519. package/dist/github-service.d.ts +39 -2
  520. package/dist/github-service.js +101 -7
  521. package/dist/http/handler.js +74 -9
  522. package/dist/http/types.d.ts +2 -0
  523. package/dist/http/worker-not-ready.d.ts +9 -4
  524. package/dist/http/worker-not-ready.js +59 -6
  525. package/dist/index.d.ts +2 -0
  526. package/dist/index.js +3 -0
  527. package/dist/item-keys.d.ts +46 -0
  528. package/dist/item-keys.js +235 -0
  529. package/dist/operating-mode/editor-mode-check.d.ts +15 -0
  530. package/dist/operating-mode/editor-mode-check.js +24 -0
  531. package/dist/operating-mode/mode-env.d.ts +13 -14
  532. package/dist/operating-mode/mode-env.js +13 -14
  533. package/dist/operating-mode/types.d.ts +2 -2
  534. package/dist/paths/branch.d.ts +0 -1
  535. package/dist/paths/branch.js +1 -8
  536. package/dist/paths/index.d.ts +1 -1
  537. package/dist/paths/index.js +1 -1
  538. package/dist/paths/validation.js +0 -2
  539. package/dist/preview.d.ts +8 -0
  540. package/dist/preview.js +7 -0
  541. package/dist/reference-resolver.js +1 -0
  542. package/dist/resolve-canopy-user.d.ts +1 -1
  543. package/dist/resolve-canopy-user.js +1 -1
  544. package/dist/schema/meta-loader.d.ts +18 -8
  545. package/dist/schema/meta-loader.js +42 -9
  546. package/dist/schema/registry-fingerprint.d.ts +14 -0
  547. package/dist/schema/registry-fingerprint.js +63 -0
  548. package/dist/schema/resolver.d.ts +7 -4
  549. package/dist/schema/resolver.js +7 -6
  550. package/dist/schema/schema-store.d.ts +7 -10
  551. package/dist/schema/schema-store.js +13 -11
  552. package/dist/schema/schema-unavailable-error.d.ts +11 -0
  553. package/dist/schema/schema-unavailable-error.js +15 -0
  554. package/dist/schema/types.d.ts +22 -0
  555. package/dist/schema-registry-record.d.ts +35 -0
  556. package/dist/schema-registry-record.js +134 -0
  557. package/dist/server.d.ts +79 -9
  558. package/dist/server.js +62 -8
  559. package/dist/services.d.ts +33 -5
  560. package/dist/services.js +155 -37
  561. package/dist/settings-workspace.d.ts +23 -3
  562. package/dist/settings-workspace.js +159 -45
  563. package/dist/stamp-on-publish.d.ts +36 -0
  564. package/dist/stamp-on-publish.js +71 -0
  565. package/dist/static/index.d.ts +58 -5
  566. package/dist/static/index.js +133 -3
  567. package/dist/static/seo.d.ts +5 -5
  568. package/dist/static/seo.js +5 -5
  569. package/dist/submission-attribution.d.ts +8 -0
  570. package/dist/submission-attribution.js +22 -0
  571. package/dist/system-users.d.ts +12 -0
  572. package/dist/system-users.js +12 -0
  573. package/dist/task-queue/cms-task-queue.d.ts +1 -1
  574. package/dist/task-queue/cms-task-queue.js +1 -1
  575. package/dist/task-queue/index.d.ts +1 -1
  576. package/dist/task-queue/index.js +1 -1
  577. package/dist/task-queue/task-queue.d.ts +6 -0
  578. package/dist/task-queue/task-queue.js +29 -0
  579. package/dist/task-queue/worker-status.d.ts +35 -9
  580. package/dist/task-queue/worker-status.js +84 -17
  581. package/dist/types.d.ts +90 -9
  582. package/dist/url-collision.js +3 -3
  583. package/dist/url-exclusivity-fixtures.d.ts +8 -1
  584. package/dist/url-exclusivity-fixtures.js +11 -1
  585. package/dist/user.js +2 -1
  586. package/dist/utils/atomic-write.js +2 -1
  587. package/dist/utils/base-branch.d.ts +13 -0
  588. package/dist/utils/base-branch.js +23 -0
  589. package/dist/utils/content-serialize.d.ts +15 -7
  590. package/dist/utils/content-serialize.js +353 -51
  591. package/dist/utils/content-write-lock.d.ts +6 -1
  592. package/dist/utils/content-write-lock.js +21 -6
  593. package/dist/utils/debug.d.ts +1 -1
  594. package/dist/utils/debug.js +5 -3
  595. package/dist/utils/error.js +13 -2
  596. package/dist/utils/escape-text.d.ts +7 -0
  597. package/dist/utils/escape-text.js +27 -0
  598. package/dist/utils/fs.d.ts +12 -4
  599. package/dist/utils/fs.js +18 -5
  600. package/dist/utils/git.d.ts +21 -1
  601. package/dist/utils/git.js +63 -8
  602. package/dist/utils/json-source-splice.d.ts +14 -0
  603. package/dist/utils/json-source-splice.js +120 -0
  604. package/dist/utils/markdown-body-splice.d.ts +39 -0
  605. package/dist/utils/markdown-body-splice.js +421 -0
  606. package/dist/utils/occ-json-write.js +18 -12
  607. package/dist/utils/provision-log.d.ts +25 -0
  608. package/dist/utils/provision-log.js +49 -0
  609. package/dist/utils/provisioning-lock.d.ts +32 -1
  610. package/dist/utils/provisioning-lock.js +78 -32
  611. package/dist/utils/sanitize-href.d.ts +9 -23
  612. package/dist/utils/sanitize-href.js +14 -28
  613. package/dist/utils/title-field.d.ts +10 -0
  614. package/dist/utils/title-field.js +47 -0
  615. package/dist/utils/yaml-source-splice.d.ts +58 -0
  616. package/dist/utils/yaml-source-splice.js +515 -0
  617. package/dist/validation/block-structural-keys.d.ts +1 -1
  618. package/dist/validation/block-structural-keys.js +1 -1
  619. package/dist/validation/deletion-checker.d.ts +21 -29
  620. package/dist/validation/deletion-checker.js +41 -117
  621. package/dist/validation/entry-validator.d.ts +6 -10
  622. package/dist/validation/entry-validator.js +19 -26
  623. package/dist/validation/field-traversal.d.ts +22 -0
  624. package/dist/validation/field-traversal.js +39 -8
  625. package/dist/validation/markdown-safety.d.ts +68 -0
  626. package/dist/validation/markdown-safety.js +596 -0
  627. package/dist/validation/mdx-allowlist.d.ts +55 -0
  628. package/dist/validation/mdx-allowlist.js +183 -0
  629. package/dist/validation/reference-validator.d.ts +2 -0
  630. package/dist/validation/reference-validator.js +3 -0
  631. package/dist/version.d.ts +1 -1
  632. package/dist/version.js +1 -1
  633. package/dist/worker/branch-policy.d.ts +124 -0
  634. package/dist/worker/branch-policy.js +243 -0
  635. package/dist/worker/bundle-header.d.ts +117 -0
  636. package/dist/worker/bundle-header.js +253 -0
  637. package/dist/worker/cms-worker.d.ts +236 -142
  638. package/dist/worker/cms-worker.js +772 -285
  639. package/dist/worker/gateway-egress.d.ts +43 -0
  640. package/dist/worker/gateway-egress.js +209 -0
  641. package/dist/worker/gateway-instance.d.ts +42 -0
  642. package/dist/worker/gateway-instance.js +129 -0
  643. package/dist/worker/git-process.d.ts +81 -0
  644. package/dist/worker/git-process.js +234 -0
  645. package/dist/worker/git-sync.d.ts +34 -17
  646. package/dist/worker/git-sync.js +394 -131
  647. package/dist/worker/github-auth.d.ts +9 -9
  648. package/dist/worker/github-auth.js +13 -15
  649. package/dist/worker/github-gateway-http.d.ts +62 -0
  650. package/dist/worker/github-gateway-http.js +977 -0
  651. package/dist/worker/github-gateway-wire.d.ts +590 -0
  652. package/dist/worker/github-gateway-wire.js +488 -0
  653. package/dist/worker/github-gateway.d.ts +238 -0
  654. package/dist/worker/github-gateway.js +726 -0
  655. package/dist/worker/github-mirror-upkeep.d.ts +75 -0
  656. package/dist/worker/github-mirror-upkeep.js +266 -0
  657. package/dist/worker/github-mirror.d.ts +364 -0
  658. package/dist/worker/github-mirror.js +1030 -0
  659. package/dist/worker/github-transfer.d.ts +56 -0
  660. package/dist/worker/github-transfer.js +444 -0
  661. package/dist/worker/history-rewrite.d.ts +4 -5
  662. package/dist/worker/history-rewrite.js +12 -17
  663. package/dist/worker/provisioned-workspace.d.ts +5 -5
  664. package/dist/worker/provisioned-workspace.js +5 -5
  665. package/dist/worker/rebase.d.ts +1 -1
  666. package/dist/worker/rebase.js +58 -54
  667. package/dist/worker/remote-git-maintenance.d.ts +3 -0
  668. package/dist/worker/remote-git-maintenance.js +12 -0
  669. package/dist/worker/schema-gate.d.ts +49 -0
  670. package/dist/worker/schema-gate.js +226 -0
  671. package/dist/worker/self-checks.d.ts +107 -0
  672. package/dist/worker/self-checks.js +358 -0
  673. package/dist/worker/shared-repo-git.d.ts +96 -0
  674. package/dist/worker/shared-repo-git.js +459 -0
  675. package/dist/worker/sparse-cone.d.ts +20 -0
  676. package/dist/worker/sparse-cone.js +136 -0
  677. package/dist/worker/stale-mirror.d.ts +82 -0
  678. package/dist/worker/stale-mirror.js +130 -0
  679. package/dist/worker/task-runner.d.ts +5 -3
  680. package/dist/worker/task-runner.js +287 -186
  681. package/dist/worker/worker-context.d.ts +36 -37
  682. package/package.json +35 -7
  683. package/dist/editor/EditorPanes.d.ts +0 -10
  684. package/dist/editor/EditorPanes.js +0 -98
  685. package/dist/editor/components/EditorFooter.d.ts +0 -11
  686. package/dist/editor/components/EditorFooter.js +0 -9
  687. package/dist/editor/components/EditorSidebar.d.ts +0 -67
  688. package/dist/editor/components/EditorSidebar.js +0 -22
@@ -4,8 +4,8 @@ import { ASSET_PREFIXES } from '../assets/keys.js';
4
4
  import { ALLOWED_UPLOAD_CONTENT_TYPES } from '../assets/pipeline.js';
5
5
  import { finalizeStagedUpload } from '../assets/finalize.js';
6
6
  import { assetSrc } from '../assets/asset-src.js';
7
- import { formatDirectives, parseTransformPath } from '../assets/transform-directives.js';
8
- import { applyTransform } from '../assets/transform.js';
7
+ import { canonicalizeTransformPath } from '../assets/transform-directives.js';
8
+ import { storeTransform, TRANSFORM_CACHE_CONTROL } from '../assets/materialize.js';
9
9
  import { isAdmin } from '../authorization/helpers.js';
10
10
  function toAssetRecord(meta) {
11
11
  return { ...meta, src: assetSrc(meta) };
@@ -194,9 +194,9 @@ const listAssetsHandler = async (ctx, _req, params) => {
194
194
  * `uploadedBy` records the FIRST uploader only: finalizeAsset dedups on content hash, so a second
195
195
  * person uploading an identical file gains no delete rights over it — benign (a 403 where they
196
196
  * expected success, never the reverse; see
197
- * .claude/future-tasks/asset-listing-cross-branch-exposure.md).
197
+ * safeinsights/canopycms#572).
198
198
  *
199
- * Coupled to the blob-GC follow-up in asset-review-followups.md: this permission is safe because
199
+ * Coupled to the blob-GC follow-up in safeinsights/canopycms#575: this permission is safe because
200
200
  * delete is a de-list, not a destroy — nothing another branch references breaks. If GC ever makes
201
201
  * delete destroy the blob, this needs a reference check too.
202
202
  */
@@ -212,63 +212,41 @@ const deleteAssetHandler = async (ctx, req, params) => {
212
212
  await ctx.assetStore.deleteMeta(params.key);
213
213
  return { ok: true, status: 200, data: { deleted: true } };
214
214
  };
215
- /** Cache-Control applied to every transform output this route writes/serves - matches finalize.ts's PUBLIC_CACHE_CONTROL for static public objects. */
216
- const TRANSFORM_CACHE_CONTROL = 'public, max-age=31536000, immutable';
217
215
  /**
218
- * Lazy dev-mode emulation of the prod transform Lambda (reuses `parseTransformPath`/
219
- * `formatDirectives`/`applyTransform` unchanged): parse, load the original, transform, write the
220
- * result back under its CANONICAL key (so a non-canonically-ordered directive string still
221
- * dedupes with any equivalent request), then serve the bytes just computed.
222
- *
223
- * `key` here already starts with `assets/t/` and already missed `rawAssetHandler`'s cache-hit
224
- * `readPublicObject` check.
216
+ * The largest body this route returns inline. The CMS Lambda's Function URL buffers its response
217
+ * and caps it at 6 MiB after base64 encoding, which inflates by 4/3, so 4 MiB leaves headroom.
218
+ * Same bound and reasoning as the transform Lambda's `INLINE_BODY_LIMIT_BYTES`.
225
219
  */
226
- async function serveLazyTransform(assetStore, key) {
227
- const rest = key.slice(ASSET_PREFIXES.transform.length + 1);
228
- const parsed = parseTransformPath(rest.split('/'));
229
- if (!parsed.ok) {
230
- return { ok: false, status: 400, error: parsed.error };
231
- }
232
- const meta = await assetStore.getMeta(parsed.hash32);
233
- if (!meta) {
234
- return { ok: false, status: 404, error: 'Not found' };
235
- }
236
- if (meta.kind !== 'raster') {
237
- return { ok: false, status: 400, error: 'Not a raster asset - svg/pdf are served statically' };
238
- }
239
- // The slug is decorative in the URL but load-bearing in the stored key, so it must equal the
240
- // asset's real slug — the parser only enforces `[a-z0-9-]+`, and any other string that passes
241
- // it aliases the same image into a new cache key. Mirrors the prod transform Lambda's check
242
- // (assets/asset-url.ts); the two paths must agree, or dev accepts URLs prod 404s.
243
- if (parsed.slug !== meta.slug) {
244
- return { ok: false, status: 404, error: 'Not found' };
245
- }
246
- // When the URL omits an explicit `f=` format, the transform preserves the
247
- // source format, so the URL's `{ext}` must match the source's real ext
248
- // exactly - the parser alone can't check this (it doesn't know the source
249
- // format until this meta lookup).
250
- const requestedFormat = parsed.directives.identity ? undefined : parsed.directives.format;
251
- if (requestedFormat === undefined && parsed.ext !== meta.ext) {
252
- return { ok: false, status: 400, error: 'Extension does not match the source format' };
253
- }
254
- const original = await assetStore.readOriginal(parsed.hash32);
255
- if (!original) {
256
- return { ok: false, status: 404, error: 'Not found' };
257
- }
258
- const transformed = await applyTransform({ data: original.data, ext: original.ext }, parsed.directives);
220
+ const INLINE_BODY_LIMIT_BYTES = 4 * 1024 * 1024;
221
+ /**
222
+ * A 302 to a presigned store URL. `no-store` because the URL expires: a cached redirect would
223
+ * outlive it, and a browser revisiting the page would follow it to a 403.
224
+ */
225
+ function presignedRedirect(url) {
226
+ return {
227
+ kind: 'binary',
228
+ status: 302,
229
+ body: new Uint8Array(),
230
+ headers: { location: url, cacheControl: 'no-store' },
231
+ };
232
+ }
233
+ /**
234
+ * This route's own lazy transform: `storeTransform` computes and stores the output, then this
235
+ * serves the bytes just computed. `parsed` is the canonical parse from `canonicalizeTransformPath`,
236
+ * and `canonicalKey` already missed `rawAssetHandler`'s cache check.
237
+ */
238
+ async function serveLazyTransform(assetStore, parsed, canonicalKey) {
239
+ const transformed = await storeTransform(assetStore, parsed, canonicalKey);
259
240
  if (!transformed.ok) {
260
- // Pass the real status through instead of flattening every rejection to 502:
261
- // `applyTransform` already distinguishes client-input errors (400/413) from a genuine decode
262
- // failure (422), none of which are "this server failed." Mirrors the prod transform Lambda.
263
- return { ok: false, status: transformed.status, error: transformed.error };
241
+ // The real status, not a flat 502: none of these rejections is "this server failed".
242
+ const error = transformed.status === 404 ? 'Not found' : transformed.error;
243
+ return { ok: false, status: transformed.status, error };
244
+ }
245
+ if (transformed.data.byteLength > INLINE_BODY_LIMIT_BYTES && assetStore.presignPublicObjectRead) {
246
+ const url = await assetStore.presignPublicObjectRead(canonicalKey);
247
+ if (url)
248
+ return presignedRedirect(url);
264
249
  }
265
- const canonicalKey = `${ASSET_PREFIXES.transform}/${formatDirectives(parsed.directives)}/${parsed.hash32}/${parsed.slug}.${parsed.ext}`;
266
- await assetStore.putPublicObject({
267
- key: canonicalKey,
268
- data: transformed.data,
269
- contentType: transformed.contentType,
270
- cacheControl: TRANSFORM_CACHE_CONTROL,
271
- });
272
250
  return {
273
251
  kind: 'binary',
274
252
  status: 200,
@@ -276,16 +254,47 @@ async function serveLazyTransform(assetStore, key) {
276
254
  headers: { contentType: transformed.contentType, cacheControl: TRANSFORM_CACHE_CONTROL },
277
255
  };
278
256
  }
257
+ /** A redirect to, or the bytes of, the public object stored at `key`; `null` when there is none. */
258
+ async function serveStoredObject(assetStore, key) {
259
+ if (assetStore.presignPublicObjectRead) {
260
+ const url = await assetStore.presignPublicObjectRead(key);
261
+ return url ? presignedRedirect(url) : null;
262
+ }
263
+ const object = await assetStore.readPublicObject(key);
264
+ if (!object)
265
+ return null;
266
+ return {
267
+ kind: 'binary',
268
+ status: 200,
269
+ body: object.data,
270
+ headers: {
271
+ contentType: object.contentType,
272
+ contentDisposition: object.contentDisposition,
273
+ cacheControl: object.cacheControl,
274
+ },
275
+ };
276
+ }
279
277
  /**
280
278
  * Serve a public asset object (sanitized svg/pdf finalize wrote, or a cached transform output)
281
- * for dev-mode `/assets/*` rewrites. Hand-built (not `defineEndpoint`), not registered in
282
- * `ASSET_ROUTES`/the client generator: this returns raw bytes (`CanopyBinaryResponse`), not a
283
- * JSON envelope, so a generated `response.json()` client method would be wrong. Consumers hit
284
- * this route directly (`<img>`/`<a>` src, or a framework rewrite), never through `client.ts`.
279
+ * to the editor, the live preview, and `withCanopy`'s `/assets/*` rewrite. Hand-built (not
280
+ * `defineEndpoint`), not registered in `ASSET_ROUTES`/the client generator: this returns raw bytes
281
+ * (`CanopyBinaryResponse`), not a JSON envelope, so a generated `response.json()` client method
282
+ * would be wrong. Consumers hit this route directly (`<img>`/`<a>` src, or a framework rewrite),
283
+ * never through `client.ts`.
285
284
  *
286
- * Transform outputs (`assets/t/...`) are cache-checked like any other public object first — only
287
- * a MISS under `assets/t/` falls through to `serveLazyTransform`. Mirrors prod (CloudFront
288
- * origin-group -> S3 -> Lambda on miss).
285
+ * A transform key (`assets/t/...`) is resolved to its canonical key first, and that key is what is
286
+ * cache-checked and, on a miss, computed by `serveLazyTransform`. Mirrors `AssetSupport`'s lazy
287
+ * mode (CloudFront origin-group -> S3 -> Lambda on miss), except that a non-canonical spelling is
288
+ * served the canonical bytes rather than the Lambda's 301: this route is authenticated, and
289
+ * redirecting to `/assets/t/...` would bounce the request onto the public path.
290
+ *
291
+ * A stored object on a store that can presign (S3) is answered with a 302 to a presigned GET, so
292
+ * its bytes never pass through this process: the CMS Lambda is concurrency-capped and uncached,
293
+ * the live preview asks for every image on a page at once, and its Function URL cannot return a
294
+ * body over about 6 MiB. The redirect never targets the public `/assets/...` URL, because whether
295
+ * that URL reaches this route again is topology: `withCanopy` rewrites `/assets/:path*` here, so
296
+ * on any deployment where Next serves `/assets` the redirect would loop. Other stores stream the
297
+ * bytes, as does a fresh transform no larger than `INLINE_BODY_LIMIT_BYTES`.
289
298
  */
290
299
  const rawAssetHandler = async (ctx, _req, params) => {
291
300
  if (!ctx.assetStore)
@@ -300,23 +309,22 @@ const rawAssetHandler = async (ctx, _req, params) => {
300
309
  if (!key.startsWith(publicPrefix) || key.includes('..')) {
301
310
  return { ok: false, status: 404, error: 'Not found' };
302
311
  }
303
- const object = await ctx.assetStore.readPublicObject(key);
304
- if (object) {
305
- return {
306
- kind: 'binary',
307
- status: 200,
308
- body: object.data,
309
- headers: {
310
- contentType: object.contentType,
311
- contentDisposition: object.contentDisposition,
312
- cacheControl: object.cacheControl,
313
- },
314
- };
312
+ let readKey = key;
313
+ let transform;
314
+ if (key.startsWith(transformPrefix)) {
315
+ const canonical = canonicalizeTransformPath(key.slice(transformPrefix.length).split('/'), 'any');
316
+ if (!canonical.ok)
317
+ return { ok: false, status: 400, error: canonical.error };
318
+ readKey = `${transformPrefix}${canonical.canonicalPath}`;
319
+ transform = canonical;
315
320
  }
316
- if (!key.startsWith(transformPrefix)) {
321
+ const stored = await serveStoredObject(ctx.assetStore, readKey);
322
+ if (stored)
323
+ return stored;
324
+ if (!transform) {
317
325
  return { ok: false, status: 404, error: 'Not found' };
318
326
  }
319
- return serveLazyTransform(ctx.assetStore, key);
327
+ return serveLazyTransform(ctx.assetStore, transform, readKey);
320
328
  };
321
329
  // Deliberately no 'writableBranch' guard on any endpoint below: none take a
322
330
  // :branch param -- the asset store is branch-agnostic (a single global store,
@@ -325,6 +333,7 @@ const presignAsset = defineEndpoint({
325
333
  namespace: 'assets',
326
334
  name: 'presign',
327
335
  method: 'POST',
336
+ readOnly: true,
328
337
  path: '/assets/presign',
329
338
  body: presignAssetBodySchema,
330
339
  bodyType: 'PresignAssetBody',
@@ -353,6 +362,7 @@ const finalizeAsset = defineEndpoint({
353
362
  const uploadProxied = defineEndpoint({
354
363
  namespace: 'assets',
355
364
  name: 'uploadProxied',
365
+ clientTimeout: 'long',
356
366
  method: 'POST',
357
367
  path: '/assets/upload',
358
368
  bodyFormat: 'multipart',
@@ -0,0 +1,12 @@
1
+ /**
2
+ * How long a create of a branch name its own creator just made answers with that branch rather
3
+ * than a 409, so a request killed between publishing the branch and responding is safe to retry.
4
+ * Dependency-free: the editor applies the same rule when it settles an unanswered create.
5
+ * @internal Exported for tests.
6
+ */
7
+ export declare const IDEMPOTENT_CREATE_WINDOW_MS: number;
8
+ /** Whether `branch` can be the result of `userId`'s create: theirs, and inside the window. */
9
+ export declare function isCreatorsRecentBranch(branch: {
10
+ createdBy: string;
11
+ createdAt: string;
12
+ }, userId: string | undefined): boolean;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * How long a create of a branch name its own creator just made answers with that branch rather
3
+ * than a 409, so a request killed between publishing the branch and responding is safe to retry.
4
+ * Dependency-free: the editor applies the same rule when it settles an unanswered create.
5
+ * @internal Exported for tests.
6
+ */
7
+ export const IDEMPOTENT_CREATE_WINDOW_MS = 5 * 60_000;
8
+ /** Whether `branch` can be the result of `userId`'s create: theirs, and inside the window. */
9
+ export function isCreatorsRecentBranch(branch, userId) {
10
+ return (userId !== undefined &&
11
+ branch.createdBy === userId &&
12
+ Date.now() - Date.parse(branch.createdAt) < IDEMPOTENT_CREATE_WINDOW_MS);
13
+ }
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import type { ApiResponse } from './types.js';
3
- /** @internal No importer; deletion candidate in knip-no-importer-deletion-candidates.md. */
3
+ /** @internal No importer; deletion candidate in safeinsights/canopycms#725. */
4
4
  export interface MarkAsMergedParams {
5
5
  branch: string;
6
6
  }
@@ -54,7 +54,7 @@ const markAsMergedHandler = async (gc, ctx, _req, params) => {
54
54
  // console.error(`CanopyCMS: Failed to delete remote branch ${branchName}:`, err)
55
55
  // }
56
56
  // }
57
- // Comments.json is already in the branch workspace at .canopycms/comments.json
57
+ // Comments.json is already in the branch workspace at .canopy-meta/comments.json
58
58
  // It will be preserved with the archived branch - no action needed
59
59
  return {
60
60
  ok: true,
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import type { BranchResponse } from './branch.js';
2
+ import { type BranchListItemResponse } from './branch.js';
3
3
  export declare const requestChanges: import("./route-builder.js").RouteDefinition<z.ZodObject<{
4
4
  branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
5
5
  }, "strip", z.ZodTypeAny, {
@@ -10,7 +10,7 @@ export declare const requestChanges: import("./route-builder.js").RouteDefinitio
10
10
  branch: string & {
11
11
  readonly __brand: "BranchName";
12
12
  };
13
- }>, undefined, BranchResponse>;
13
+ }>, undefined, BranchListItemResponse>;
14
14
  export declare const approveBranch: import("./route-builder.js").RouteDefinition<z.ZodObject<{
15
15
  branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
16
16
  }, "strip", z.ZodTypeAny, {
@@ -21,4 +21,4 @@ export declare const approveBranch: import("./route-builder.js").RouteDefinition
21
21
  branch: string & {
22
22
  readonly __brand: "BranchName";
23
23
  };
24
- }>, undefined, BranchResponse>;
24
+ }>, undefined, BranchListItemResponse>;
@@ -1,8 +1,9 @@
1
1
  import { branchParamSchema } from './validators.js';
2
+ import { toBranchListItem } from './branch.js';
2
3
  import { getBranchMetadataFileManager } from '../branch-metadata.js';
3
4
  import { defineEndpoint } from './route-builder.js';
4
5
  import { syncConvertToDraft } from './github-sync.js';
5
- const requestChangesHandler = async (gc, ctx, _req, _params) => {
6
+ const requestChangesHandler = async (gc, ctx, req, _params) => {
6
7
  const { branchContext } = gc;
7
8
  if (branchContext.branch.status !== 'submitted') {
8
9
  return {
@@ -16,10 +17,14 @@ const requestChangesHandler = async (gc, ctx, _req, _params) => {
16
17
  const updated = await meta.save({
17
18
  branch: { name: branchContext.branch.name, status: 'editing' },
18
19
  });
19
- // TODO: Optionally record comment in .canopycms/comments.json when comment system is implemented
20
- return { ok: true, status: 200, data: { branch: updated.branch } };
20
+ // TODO: Optionally record comment in .canopy-meta/comments.json when comment system is implemented
21
+ return {
22
+ ok: true,
23
+ status: 200,
24
+ data: { branch: toBranchListItem(ctx.services.config, updated.branch, req.user) },
25
+ };
21
26
  };
22
- const approveBranchHandler = async (gc, _ctx, _req, _params) => {
27
+ const approveBranchHandler = async (gc, ctx, req, _params) => {
23
28
  const { branchContext } = gc;
24
29
  if (branchContext.branch.status !== 'submitted') {
25
30
  return {
@@ -33,7 +38,11 @@ const approveBranchHandler = async (gc, _ctx, _req, _params) => {
33
38
  branch: { name: branchContext.branch.name, status: 'approved' },
34
39
  });
35
40
  // TODO: Optionally call githubService.approvePullRequest() when GitHub integration is needed
36
- return { ok: true, status: 200, data: { branch: updated.branch } };
41
+ return {
42
+ ok: true,
43
+ status: 200,
44
+ data: { branch: toBranchListItem(ctx.services.config, updated.branch, req.user) },
45
+ };
37
46
  };
38
47
  export const requestChanges = defineEndpoint({
39
48
  namespace: 'workflow',
@@ -43,7 +52,7 @@ export const requestChanges = defineEndpoint({
43
52
  params: branchParamSchema,
44
53
  // body/bodyType removed: comment field was declared but never stored.
45
54
  // Re-add when comment storage is implemented (see TODO in handler).
46
- responseType: 'BranchResponse',
55
+ responseType: 'BranchListItemResponse',
47
56
  response: {},
48
57
  defaultMockData: {
49
58
  branch: {
@@ -64,7 +73,7 @@ export const approveBranch = defineEndpoint({
64
73
  method: 'POST',
65
74
  path: '/:branch/approve',
66
75
  params: branchParamSchema,
67
- responseType: 'BranchResponse',
76
+ responseType: 'BranchListItemResponse',
68
77
  response: {},
69
78
  defaultMockData: {
70
79
  branch: {
@@ -1,6 +1,16 @@
1
1
  import { z } from 'zod';
2
- import type { BranchResponse } from './branch.js';
2
+ import type { ApiResponse } from './types.js';
3
+ import { type BranchListItem, type BranchListItemResponse } from './branch.js';
4
+ import type { StampedEntry } from '../stamp-on-publish.js';
3
5
  export type { BranchMergeResponse } from './branch-merge.js';
6
+ /**
7
+ * A submit's answer: the branch, plus the entries the submit stamped (stamp-on-publish.ts) when
8
+ * there were any, so the editor can carry its open forms and drafts onto their new versions.
9
+ */
10
+ export type SubmitBranchResponse = ApiResponse<{
11
+ branch: BranchListItem;
12
+ stamped?: StampedEntry[];
13
+ }>;
4
14
  export declare const WORKFLOW_ROUTES: {
5
15
  readonly getStatus: import("./route-builder.js").RouteDefinition<z.ZodObject<{
6
16
  branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
@@ -12,7 +22,7 @@ export declare const WORKFLOW_ROUTES: {
12
22
  branch: string & {
13
23
  readonly __brand: "BranchName";
14
24
  };
15
- }>, undefined, BranchResponse>;
25
+ }>, undefined, BranchListItemResponse>;
16
26
  readonly submit: import("./route-builder.js").RouteDefinition<z.ZodObject<{
17
27
  branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
18
28
  }, "strip", z.ZodTypeAny, {
@@ -23,7 +33,7 @@ export declare const WORKFLOW_ROUTES: {
23
33
  branch: string & {
24
34
  readonly __brand: "BranchName";
25
35
  };
26
- }>, undefined, BranchResponse>;
36
+ }>, undefined, SubmitBranchResponse>;
27
37
  readonly withdraw: import("./route-builder.js").RouteDefinition<z.ZodObject<{
28
38
  branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
29
39
  }, "strip", z.ZodTypeAny, {
@@ -34,7 +44,7 @@ export declare const WORKFLOW_ROUTES: {
34
44
  branch: string & {
35
45
  readonly __brand: "BranchName";
36
46
  };
37
- }>, undefined, BranchResponse>;
47
+ }>, undefined, BranchListItemResponse>;
38
48
  readonly requestChanges: import("./route-builder.js").RouteDefinition<z.ZodObject<{
39
49
  branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
40
50
  }, "strip", z.ZodTypeAny, {
@@ -45,7 +55,7 @@ export declare const WORKFLOW_ROUTES: {
45
55
  branch: string & {
46
56
  readonly __brand: "BranchName";
47
57
  };
48
- }>, undefined, BranchResponse>;
58
+ }>, undefined, BranchListItemResponse>;
49
59
  readonly approve: import("./route-builder.js").RouteDefinition<z.ZodObject<{
50
60
  branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
51
61
  }, "strip", z.ZodTypeAny, {
@@ -56,7 +66,7 @@ export declare const WORKFLOW_ROUTES: {
56
66
  branch: string & {
57
67
  readonly __brand: "BranchName";
58
68
  };
59
- }>, undefined, BranchResponse>;
69
+ }>, undefined, BranchListItemResponse>;
60
70
  readonly markMerged: import("./route-builder.js").RouteDefinition<z.ZodObject<{
61
71
  branch: z.ZodType<import("../paths/index.js").BranchName, z.ZodTypeDef, import("../paths/index.js").BranchName>;
62
72
  }, "strip", z.ZodTypeAny, {
@@ -1,4 +1,5 @@
1
1
  import { branchParamSchema } from './validators.js';
2
+ import { toBranchListItem } from './branch.js';
2
3
  import { getBranchMetadataFileManager } from '../branch-metadata.js';
3
4
  import { withdrawBranch } from './branch-withdraw.js';
4
5
  import { requestChanges, approveBranch } from './branch-review.js';
@@ -10,9 +11,15 @@ import { getErrorMessage, redactCredentials, sanitizeErrorMessage } from '../uti
10
11
  import { isNonFastForwardRejection } from '../utils/git.js';
11
12
  import { ContentWriteLockBusyError } from '../utils/content-write-lock.js';
12
13
  import { submissionEditorFromUser } from '../submission-attribution.js';
13
- const getBranchStatusHandler = async (gc, _ctx, _req, _params) => {
14
+ import { NothingToSubmitError } from '../services.js';
15
+ import { baseBranchOf } from '../utils/base-branch.js';
16
+ const getBranchStatusHandler = async (gc, ctx, req, _params) => {
14
17
  const { branchContext } = gc;
15
- return { ok: true, status: 200, data: { branch: branchContext.branch } };
18
+ return {
19
+ ok: true,
20
+ status: 200,
21
+ data: { branch: toBranchListItem(ctx.services.config, branchContext.branch, req.user) },
22
+ };
16
23
  };
17
24
  const submitBranchForMergeHandler = async (gc, ctx, req, _params) => {
18
25
  const { branchContext } = gc;
@@ -28,7 +35,7 @@ const submitBranchForMergeHandler = async (gc, ctx, req, _params) => {
28
35
  return {
29
36
  ok: false,
30
37
  status: 403,
31
- error: 'Only the branch creator or users with explicit branch access can submit this branch',
38
+ error: 'You do not have permission to submit this branch for review.',
32
39
  };
33
40
  }
34
41
  // Only an 'editing' branch may be submitted, mirroring
@@ -37,9 +44,8 @@ const submitBranchForMergeHandler = async (gc, ctx, req, _params) => {
37
44
  // (merged) branch could be re-submitted by its creator, re-stamping it
38
45
  // 'submitted' and overwriting the merged PR's title/body.
39
46
  //
40
- // Fails closed on a missing status (branch.json is read with a bare
41
- // JSON.parse, no schema validation, so a damaged file can yield
42
- // `undefined`) -- same as runWritableBranchGuard's unreadable-status arm.
47
+ // Fails closed on a missing status, same as runWritableBranchGuard's
48
+ // unreadable-status arm.
43
49
  const status = branchContext.branch.status;
44
50
  if (status !== 'editing') {
45
51
  return {
@@ -52,12 +58,22 @@ const submitBranchForMergeHandler = async (gc, ctx, req, _params) => {
52
58
  }
53
59
  // Commit and push changes
54
60
  const submitter = submissionEditorFromUser(req.user);
61
+ const { authPlugin } = ctx;
55
62
  let changedPaths;
63
+ let editors;
64
+ let stamped;
56
65
  try {
57
66
  ;
58
- ({ changedPaths } = await ctx.services.submitBranch({ context: branchContext, submitter }));
67
+ ({ changedPaths, editors, stamped } = await ctx.services.submitBranch({
68
+ context: branchContext,
69
+ submitter,
70
+ lookupEditor: authPlugin ? (id) => authPlugin.getUserMetadata(id) : undefined,
71
+ }));
59
72
  }
60
73
  catch (err) {
74
+ if (err instanceof NothingToSubmitError) {
75
+ return { ok: false, status: 400, error: err.message };
76
+ }
61
77
  // Retriable either way: a resubmit commits nothing new and pushes only
62
78
  // what has not reached the remote.
63
79
  if (err instanceof ContentWriteLockBusyError) {
@@ -106,13 +122,34 @@ const submitBranchForMergeHandler = async (gc, ctx, req, _params) => {
106
122
  };
107
123
  }
108
124
  // Create or update PR (sync via githubService, or async via task queue)
109
- const prResult = await syncSubmitPr(ctx, branchContext, { submitter, changedPaths });
110
- // Update metadata with status and PR info
125
+ const submittedAt = new Date().toISOString();
126
+ const prResult = await syncSubmitPr(ctx, branchContext, {
127
+ submitter,
128
+ editors,
129
+ changedPaths,
130
+ submittedAt,
131
+ });
111
132
  const meta = getBranchMetadataFileManager(branchContext.branchRoot, branchContext.baseRoot);
133
+ const pushed = prResult.pushedToGitHub ? { pushedToGitHubAt: submittedAt } : {};
134
+ // GitHub found nothing between the pushed branch and its base, though the diff above found
135
+ // changes: GitHub's base can already hold them, or the diff could not be computed. The branch
136
+ // stays editable; the stamp records that it is now on GitHub, for delete.
137
+ if (prResult.nothingToSubmit) {
138
+ await meta.save({ branch: { name: branchContext.branch.name, ...pushed } });
139
+ const base = branchContext.branch.baseBranch ?? baseBranchOf(ctx.services.config);
140
+ return {
141
+ ok: false,
142
+ status: 400,
143
+ error: new NothingToSubmitError(branchContext.branch.name, base).message,
144
+ };
145
+ }
112
146
  const updated = await meta.save({
113
147
  branch: {
114
148
  name: branchContext.branch.name,
115
149
  status: 'submitted',
150
+ submittedAt,
151
+ ...pushed,
152
+ syncFailureReason: prResult.syncFailureReason,
116
153
  pullRequestUrl: prResult.prUrl ?? branchContext.branch.pullRequestUrl,
117
154
  pullRequestNumber: prResult.prNumber ?? branchContext.branch.pullRequestNumber,
118
155
  ...(prResult.syncStatus !== undefined ? { syncStatus: prResult.syncStatus } : {}),
@@ -122,7 +159,14 @@ const submitBranchForMergeHandler = async (gc, ctx, req, _params) => {
122
159
  rebaseFailure: undefined,
123
160
  },
124
161
  });
125
- return { ok: true, status: 200, data: { branch: updated.branch } };
162
+ return {
163
+ ok: true,
164
+ status: 200,
165
+ data: {
166
+ branch: toBranchListItem(ctx.services.config, updated.branch, req.user),
167
+ ...(stamped.length > 0 ? { stamped } : {}),
168
+ },
169
+ };
126
170
  };
127
171
  const getBranchStatus = defineEndpoint({
128
172
  namespace: 'workflow',
@@ -130,7 +174,7 @@ const getBranchStatus = defineEndpoint({
130
174
  method: 'GET',
131
175
  path: '/:branch/status',
132
176
  params: branchParamSchema,
133
- responseType: 'BranchResponse',
177
+ responseType: 'BranchListItemResponse',
134
178
  response: {},
135
179
  defaultMockData: {
136
180
  branch: {
@@ -148,10 +192,11 @@ const getBranchStatus = defineEndpoint({
148
192
  const submitBranchForMerge = defineEndpoint({
149
193
  namespace: 'workflow',
150
194
  name: 'submit',
195
+ clientTimeout: 'long',
151
196
  method: 'POST',
152
197
  path: '/:branch/submit',
153
198
  params: branchParamSchema,
154
- responseType: 'BranchResponse',
199
+ responseType: 'SubmitBranchResponse',
155
200
  response: {},
156
201
  defaultMockData: {
157
202
  branch: {
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import type { BranchResponse } from './branch.js';
2
+ import { type BranchListItemResponse } from './branch.js';
3
3
  /**
4
4
  * Withdraw a submitted branch, converting the PR to draft and unlocking editing
5
5
  *
@@ -19,4 +19,4 @@ export declare const withdrawBranch: import("./route-builder.js").RouteDefinitio
19
19
  branch: string & {
20
20
  readonly __brand: "BranchName";
21
21
  };
22
- }>, undefined, BranchResponse>;
22
+ }>, undefined, BranchListItemResponse>;
@@ -1,4 +1,5 @@
1
1
  import { branchParamSchema } from './validators.js';
2
+ import { toBranchListItem } from './branch.js';
2
3
  import { getBranchMetadataFileManager } from '../branch-metadata.js';
3
4
  import { defineEndpoint } from './route-builder.js';
4
5
  import { canPerformWorkflowAction, getBranchProtection } from '../authorization/index.js';
@@ -18,7 +19,7 @@ const withdrawBranchHandler = async (gc, ctx, req, _params) => {
18
19
  return {
19
20
  ok: false,
20
21
  status: 403,
21
- error: 'Only the branch creator or users with explicit branch access can withdraw this branch',
22
+ error: 'You do not have permission to withdraw this branch from review.',
22
23
  };
23
24
  }
24
25
  // 'approved' is accepted alongside 'submitted' because withdraw is its only
@@ -60,7 +61,11 @@ const withdrawBranchHandler = async (gc, ctx, req, _params) => {
60
61
  : {}),
61
62
  },
62
63
  });
63
- return { ok: true, status: 200, data: { branch: updated.branch } };
64
+ return {
65
+ ok: true,
66
+ status: 200,
67
+ data: { branch: toBranchListItem(ctx.services.config, updated.branch, req.user) },
68
+ };
64
69
  };
65
70
  /**
66
71
  * Withdraw a submitted branch, converting the PR to draft and unlocking editing
@@ -77,7 +82,7 @@ export const withdrawBranch = defineEndpoint({
77
82
  method: 'POST',
78
83
  path: '/:branch/withdraw',
79
84
  params: branchParamSchema,
80
- responseType: 'BranchResponse',
85
+ responseType: 'BranchListItemResponse',
81
86
  response: {},
82
87
  defaultMockData: {
83
88
  branch: {