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
@@ -1,52 +1,153 @@
1
1
  /**
2
2
  * Reading `branch.json` — the file format, nothing else.
3
3
  *
4
- * Deliberately a LEAF, importing only node built-ins, a type, and the error
5
- * helper. That is what keeps `branch-registry.ts` (which reads every branch's
4
+ * Deliberately a LEAF, importing only node built-ins, zod, types, and the error
5
+ * modules. That is what keeps `branch-registry.ts` (which reads every branch's
6
6
  * `branch.json`) out of a runtime import cycle with `branch-metadata.ts` (which
7
7
  * writes it and then invalidates the registry cache).
8
8
  */
9
9
  import fs from 'node:fs/promises';
10
10
  import path from 'node:path';
11
- import { isNotFoundError } from './utils/error.js';
11
+ import { z } from 'zod';
12
+ import { getErrorMessage, isNotFoundError } from './utils/error.js';
13
+ import { BranchMetadataCorruptError } from './branch-metadata-error.js';
14
+ export { BranchMetadataCorruptError };
12
15
  export const BRANCH_META_DIR = '.canopy-meta';
13
16
  export const BRANCH_META_FILE = 'branch.json';
17
+ /** Absolute path to a branch workspace's `branch.json`. */
18
+ const branchMetadataFilePath = (branchRoot) => path.join(path.resolve(branchRoot), BRANCH_META_DIR, BRANCH_META_FILE);
19
+ /** The value a timestamp missing from a hand-written file reads as: honestly unknown, and stable. */
20
+ const UNKNOWN_TIME = new Date(0).toISOString();
21
+ /** A zod enum of every member of `T`: the mapped type refuses to compile if one is left out. */
22
+ const enumOf = (values) => z.nativeEnum(values);
14
23
  /**
15
- * branch.json exists but is not valid JSON. Distinguished from provisioning and
16
- * IO failures so callers can degrade instead of failing hard: the registry scan
17
- * quarantines the branch, and the request handler keeps serving (with empty
18
- * internal groups) when the BASE branch is the corrupt one — otherwise the
19
- * admin recovery surface is unreachable exactly when it is needed.
24
+ * `branch.json`'s shape, checked on every read.
25
+ *
26
+ * Strict where a guard or an access check reads the field: `status` (writes and
27
+ * submits are gated on it), `name` (the protected-base-branch test compares it)
28
+ * and `access` (defaulted to `{}`, a missing one would read as "no ACL" and
29
+ * widen access). Lenient,
30
+ * with defaults, where a hand-written file can omit bookkeeping: the envelope's
31
+ * `version` (0, as occ-json-write reads it) and `schemaVersion`, and
32
+ * `createdBy` ('unknown', which matches no user, so the creator grant fails
33
+ * closed) and the timestamps. Every optional field is type-checked.
34
+ *
35
+ * Unknown keys pass through, so a field a newer deployment writes survives an
36
+ * older one's save. Code reads only the typed {@link BranchMetadataFile}.
20
37
  */
21
- export class BranchMetadataCorruptError extends Error {
22
- constructor(branchRoot, cause) {
23
- super(`Corrupt branch metadata in '${branchRoot}': ${cause}`);
24
- this.name = 'BranchMetadataCorruptError';
25
- this.branchRoot = branchRoot;
26
- this.parseCause = cause;
38
+ const branchMetadataShape = {
39
+ name: z.string().min(1),
40
+ title: z.string().optional(),
41
+ description: z.string().optional(),
42
+ status: enumOf({
43
+ editing: 'editing',
44
+ submitted: 'submitted',
45
+ approved: 'approved',
46
+ archived: 'archived',
47
+ }),
48
+ access: z
49
+ .object({
50
+ allowedUsers: z.array(z.string()).optional(),
51
+ allowedGroups: z.array(z.string()).optional(),
52
+ managerOrAdminAllowed: z.boolean().optional(),
53
+ })
54
+ .passthrough(),
55
+ createdBy: z.string().default('unknown'),
56
+ createdAt: z.string().default(UNKNOWN_TIME),
57
+ updatedAt: z.string().default(UNKNOWN_TIME),
58
+ baseBranch: z.string().optional(),
59
+ pullRequestUrl: z.string().optional(),
60
+ pullRequestNumber: z.number().int().optional(),
61
+ submittedAt: z.string().optional(),
62
+ pushedToGitHubAt: z.string().optional(),
63
+ syncStatus: enumOf({
64
+ synced: 'synced',
65
+ 'pending-sync': 'pending-sync',
66
+ 'sync-failed': 'sync-failed',
67
+ }).optional(),
68
+ conflictStatus: enumOf({
69
+ clean: 'clean',
70
+ 'conflicts-detected': 'conflicts-detected',
71
+ }).optional(),
72
+ conflictFiles: z.array(z.custom((v) => typeof v === 'string')).optional(),
73
+ pullRequestState: enumOf({
74
+ open: 'open',
75
+ closed: 'closed',
76
+ merged: 'merged',
77
+ }).optional(),
78
+ mergedAt: z.string().optional(),
79
+ rebaseFailure: z
80
+ .object({ message: z.string(), firstAt: z.string(), lastAt: z.string() })
81
+ .optional(),
82
+ historyRewrittenFrom: z.string().optional(),
83
+ syncFailureReason: z.string().optional(),
84
+ editors: z.array(z.string()).optional(),
85
+ uncommittedEditors: z.array(z.string()).optional(),
86
+ };
87
+ /** The `branch` object, also embedded in the registry snapshot (branch-registry.ts). */
88
+ export const branchMetadataSchema = z.object(branchMetadataShape).passthrough();
89
+ const branchMetadataFileSchema = z
90
+ .object({
91
+ schemaVersion: z.number().int().default(1),
92
+ version: z.number().int().nonnegative().default(0),
93
+ writeId: z.string().optional(),
94
+ branch: branchMetadataSchema,
95
+ })
96
+ .passthrough();
97
+ /** The schema failures, by field and without values, so no file content reaches a client. */
98
+ function describeSchemaIssues(issues) {
99
+ const missing = new Set();
100
+ const invalid = new Set();
101
+ for (const issue of issues) {
102
+ const field = issue.path.length > 0 ? issue.path.join('.') : 'the file';
103
+ const isMissing = issue.code === z.ZodIssueCode.invalid_type && issue.received === z.ZodParsedType.undefined;
104
+ (isMissing ? missing : invalid).add(field);
27
105
  }
106
+ const parts = [];
107
+ if (missing.size > 0)
108
+ parts.push(`Missing: ${[...missing].join(', ')}`);
109
+ if (invalid.size > 0)
110
+ parts.push(`Invalid: ${[...invalid].join(', ')}`);
111
+ return `Not branch metadata. ${parts.join('. ')}`;
28
112
  }
29
- /** Absolute path to a branch workspace's `branch.json`. */
30
- const branchMetadataFilePath = (branchRoot) => path.join(path.resolve(branchRoot), BRANCH_META_DIR, BRANCH_META_FILE);
31
113
  /**
32
- * Read and parse `branch.json`, with no locking, no OCC and no side effects.
33
- * Callers distinguish three outcomes: `null` when the file does not exist (an
34
- * un-provisioned or non-branch directory), `BranchMetadataCorruptError` on
35
- * malformed JSON, and every other IO failure rethrown unchanged.
114
+ * Read, parse and validate `branch.json`, with no locking, no OCC and no side
115
+ * effects. Callers distinguish three outcomes: `null` when the file does not
116
+ * exist (an un-provisioned or non-branch directory),
117
+ * `BranchMetadataCorruptError` on malformed JSON or a failed schema check, and
118
+ * every other IO failure rethrown unchanged.
36
119
  */
37
120
  export async function readBranchMetadataFile(branchRoot) {
38
121
  const resolvedRoot = path.resolve(branchRoot);
122
+ let raw;
39
123
  try {
40
- const raw = await fs.readFile(branchMetadataFilePath(resolvedRoot), 'utf8');
41
- return JSON.parse(raw);
124
+ raw = await fs.readFile(branchMetadataFilePath(resolvedRoot), 'utf8');
42
125
  }
43
126
  catch (err) {
44
127
  if (isNotFoundError(err)) {
45
128
  return null;
46
129
  }
47
- if (err instanceof SyntaxError) {
48
- throw new BranchMetadataCorruptError(resolvedRoot, err.message);
49
- }
50
130
  throw err;
51
131
  }
132
+ return parseBranchMetadataFile(resolvedRoot, raw);
133
+ }
134
+ /**
135
+ * Parse and validate `branch.json` text already read from `branchRoot`.
136
+ *
137
+ * @throws BranchMetadataCorruptError when it is not JSON or fails the schema
138
+ */
139
+ function parseBranchMetadataFile(branchRoot, raw) {
140
+ let json;
141
+ try {
142
+ json = JSON.parse(raw);
143
+ }
144
+ catch (err) {
145
+ throw new BranchMetadataCorruptError(branchRoot, `Not valid JSON: ${getErrorMessage(err)}`);
146
+ }
147
+ const result = branchMetadataFileSchema.safeParse(json);
148
+ if (!result.success) {
149
+ throw new BranchMetadataCorruptError(branchRoot, describeSchemaIssues(result.error.issues));
150
+ }
151
+ const file = result.data;
152
+ return file;
52
153
  }
@@ -1,4 +1,5 @@
1
- import type { BranchContext, BranchMetadata } from './types.js';
1
+ import type { BranchContext, BranchMetadata, BranchPaths, CanopyUserId } from './types.js';
2
+ import type { CanopyUser } from './user.js';
2
3
  import { BranchMetadataCorruptError, type BranchMetadataFile } from './branch-metadata-file.js';
3
4
  import { type OperatingMode } from './operating-mode/index.js';
4
5
  export { BranchMetadataCorruptError, type BranchMetadataFile };
@@ -37,6 +38,12 @@ export declare class BranchMetadataFileManager {
37
38
  static get(branchRoot: string, baseRoot: string, options?: {
38
39
  settleMs?: number;
39
40
  }): BranchMetadataFileManager;
41
+ /**
42
+ * The file the save cycle merges over. A corrupt one throws
43
+ * {@link BranchMetadataCorruptError} rather than reading as absent, so a save
44
+ * never lays defaults (status 'editing', no ACL) over a branch whose real
45
+ * state is unknown; repair-metadata archives the file before it saves.
46
+ */
40
47
  private load;
41
48
  /**
42
49
  * Write branch.json via the shared OCC helper, applying the schemaVersion
@@ -63,16 +70,48 @@ export declare class BranchMetadataFileManager {
63
70
  * entry with no clone behind it. It runs BEFORE the lock stack so a doomed
64
71
  * save fails fast instead of paying for a lock.
65
72
  *
66
- * Accepted residual window: a save that passes the check can still race a
67
- * `rm` that starts moments later and is mid-flight when the write lands.
68
- * Closing that needs a tombstone OUTSIDE the tree being removed, and the
69
- * lockfile taken next lives INSIDE `branchRoot`, so it can promise no more
70
- * than "the directory existed a moment ago".
73
+ * A save that passes the check just before a delete still cannot write: the
74
+ * delete renames the tree away under the same lockfile, and that lockfile
75
+ * creates only its own directory (`withOccFileLock`), so the save fails there.
71
76
  */
72
77
  save(incoming: BranchMetadataUpdate): Promise<BranchMetadataFile>;
78
+ /**
79
+ * {@link save}, applied only when `onlyIf` accepts the metadata on disk; resolves null when it
80
+ * does not, having written nothing. `onlyIf` runs inside the lock stack against the version
81
+ * the write is checked against, so the decision and the write are one compare-and-set.
82
+ */
83
+ saveIf(incoming: BranchMetadataUpdate, onlyIf: (existing: BranchMetadataFile | null) => boolean): Promise<BranchMetadataFile | null>;
84
+ /**
85
+ * A save whose update `compute` derives from the metadata on disk, inside the lock stack, so
86
+ * an update built from the existing value (adding to a list) cannot lose a concurrent one.
87
+ * `compute` returning null writes nothing and resolves null. `keepUpdatedAt` leaves
88
+ * `updatedAt` as it was, for bookkeeping writes that are not a change to the branch.
89
+ */
90
+ update(compute: (existing: BranchMetadataFile | null) => BranchMetadataUpdate | null, options?: {
91
+ keepUpdatedAt?: boolean;
92
+ }): Promise<BranchMetadataFile | null>;
93
+ /**
94
+ * Adds `userId` to `editors` and `uncommittedEditors`. A lock-free read skips the write when
95
+ * both already hold it, so only a user's first save since the last submit commit pays for the lock.
96
+ * Never creates branch.json.
97
+ */
98
+ recordEditor(userId: CanopyUserId): Promise<void>;
99
+ /**
100
+ * Removes `userIds` from `uncommittedEditors` once a submit commit's trailers name them. Ids
101
+ * recorded after the commit read them stay, so the next commit names them.
102
+ */
103
+ markEditorsCommitted(userIds: readonly CanopyUserId[]): Promise<void>;
73
104
  /** Invalidate the registry cache so the next list() regenerates. */
74
105
  private invalidateRegistry;
75
106
  }
107
+ /**
108
+ * Records `user` as an editor of the branch, for the submit's trailers and PR body. Called once a
109
+ * change to the branch's working tree has succeeded, so a failure here is logged and never fails
110
+ * that change. Anonymous users are not recorded.
111
+ */
112
+ export declare function recordBranchEditor(context: BranchPaths, user: CanopyUser): Promise<void>;
113
+ /** The branch.json a brand-new branch starts with: {@link mergeBranchMetadata} over nothing. */
114
+ export declare function buildInitialBranchMetadata(incoming: BranchMetadataUpdate, now?: string): BranchMetadataFile;
76
115
  /**
77
116
  * Fields save() accepts. `createdBy` applies on creation only and is ignored
78
117
  * once metadata exists; createdAt/updatedAt are managed here, not by callers.
@@ -3,7 +3,8 @@ import path from 'node:path';
3
3
  import { BranchRegistry } from './branch-registry.js';
4
4
  import { BRANCH_META_DIR, BRANCH_META_FILE, BranchMetadataCorruptError, readBranchMetadataFile, } from './branch-metadata-file.js';
5
5
  import { resolveBranchPath } from './paths/index.js';
6
- import { isNotFoundError } from './utils/error.js';
6
+ import { getErrorMessage, isNotFoundError } from './utils/error.js';
7
+ import { canopyLogWarn } from './utils/logger.js';
7
8
  import { withLock } from './utils/async-mutex.js';
8
9
  import { writeOccJsonFile, withOccRetry, withOccFileLock, OccWriteConflictError, } from './utils/occ-json-write.js';
9
10
  // The file format lives in the leaf so branch-registry.ts can read branch.json
@@ -52,19 +53,15 @@ export class BranchMetadataFileManager {
52
53
  static get(branchRoot, baseRoot, options) {
53
54
  return new BranchMetadataFileManager(branchRoot, baseRoot, options);
54
55
  }
56
+ /**
57
+ * The file the save cycle merges over. A corrupt one throws
58
+ * {@link BranchMetadataCorruptError} rather than reading as absent, so a save
59
+ * never lays defaults (status 'editing', no ACL) over a branch whose real
60
+ * state is unknown; repair-metadata archives the file before it saves.
61
+ */
55
62
  async load() {
56
- try {
57
- const raw = await fs.readFile(this.filePath, 'utf8');
58
- const parsed = JSON.parse(raw);
59
- const version = parsed.version ?? 0;
60
- return { meta: parsed, version };
61
- }
62
- catch (err) {
63
- if (isNotFoundError(err)) {
64
- return { meta: null, version: null };
65
- }
66
- throw err;
67
- }
63
+ const meta = await readBranchMetadataFile(this.branchRoot);
64
+ return { meta, version: meta?.version ?? null };
68
65
  }
69
66
  /**
70
67
  * Write branch.json via the shared OCC helper, applying the schemaVersion
@@ -101,13 +98,31 @@ export class BranchMetadataFileManager {
101
98
  * entry with no clone behind it. It runs BEFORE the lock stack so a doomed
102
99
  * save fails fast instead of paying for a lock.
103
100
  *
104
- * Accepted residual window: a save that passes the check can still race a
105
- * `rm` that starts moments later and is mid-flight when the write lands.
106
- * Closing that needs a tombstone OUTSIDE the tree being removed, and the
107
- * lockfile taken next lives INSIDE `branchRoot`, so it can promise no more
108
- * than "the directory existed a moment ago".
101
+ * A save that passes the check just before a delete still cannot write: the
102
+ * delete renames the tree away under the same lockfile, and that lockfile
103
+ * creates only its own directory (`withOccFileLock`), so the save fails there.
109
104
  */
110
105
  async save(incoming) {
106
+ const saved = await this.saveIf(incoming, () => true);
107
+ if (saved === null)
108
+ throw new Error('unreachable: an unconditional save was skipped');
109
+ return saved;
110
+ }
111
+ /**
112
+ * {@link save}, applied only when `onlyIf` accepts the metadata on disk; resolves null when it
113
+ * does not, having written nothing. `onlyIf` runs inside the lock stack against the version
114
+ * the write is checked against, so the decision and the write are one compare-and-set.
115
+ */
116
+ async saveIf(incoming, onlyIf) {
117
+ return this.update((existing) => (onlyIf(existing) ? incoming : null));
118
+ }
119
+ /**
120
+ * A save whose update `compute` derives from the metadata on disk, inside the lock stack, so
121
+ * an update built from the existing value (adding to a list) cannot lose a concurrent one.
122
+ * `compute` returning null writes nothing and resolves null. `keepUpdatedAt` leaves
123
+ * `updatedAt` as it was, for bookkeeping writes that are not a change to the branch.
124
+ */
125
+ async update(compute, options = {}) {
111
126
  try {
112
127
  await fs.stat(this.branchRoot);
113
128
  }
@@ -121,37 +136,12 @@ export class BranchMetadataFileManager {
121
136
  try {
122
137
  saved = await withLock(this.filePath, () => withOccFileLock(this.filePath, () => withOccRetry(async () => {
123
138
  const { meta: existing, version } = await this.load();
124
- const now = new Date().toISOString();
125
- const defaults = {
126
- name: 'unknown',
127
- status: 'editing',
128
- access: {},
129
- createdBy: 'unknown',
130
- createdAt: now,
131
- updatedAt: now,
132
- };
133
- const merged = {
134
- schemaVersion: CURRENT_SCHEMA_VERSION,
135
- version: version ?? 0,
136
- branch: {
137
- ...defaults,
138
- ...existing?.branch,
139
- ...incoming.branch,
140
- access: {
141
- ...existing?.branch?.access,
142
- ...incoming.branch?.access,
143
- },
144
- // Immutable after creation
145
- createdBy: existing?.branch.createdBy ?? incoming.branch?.createdBy ?? defaults.createdBy,
146
- createdAt: existing?.branch.createdAt ?? defaults.createdAt,
147
- // Fork point is recorded once at creation; later saves must not move it
148
- baseBranch: existing?.branch.baseBranch ?? incoming.branch?.baseBranch,
149
- // Always stamped fresh; the spreads above would otherwise let
150
- // the creation-time value win forever, freezing the timestamp
151
- // the editor's Branches panel sorts and displays by
152
- updatedAt: now,
153
- },
154
- };
139
+ const incoming = compute(existing);
140
+ if (incoming === null)
141
+ return null;
142
+ const merged = mergeBranchMetadata(existing, version, incoming);
143
+ if (options.keepUpdatedAt && existing)
144
+ merged.branch.updatedAt = existing.branch.updatedAt;
155
145
  const written = await this.write(merged, version);
156
146
  merged.version = written.version;
157
147
  merged.writeId = written.writeId;
@@ -169,15 +159,114 @@ export class BranchMetadataFileManager {
169
159
  // does, and the registry's eager regeneration is O(branch count) fs reads
170
160
  // on EFS — holding the lock through it would stretch every save's critical
171
161
  // section for no correctness gain.
172
- await this.invalidateRegistry();
162
+ if (saved !== null)
163
+ await this.invalidateRegistry();
173
164
  return saved;
174
165
  }
166
+ /**
167
+ * Adds `userId` to `editors` and `uncommittedEditors`. A lock-free read skips the write when
168
+ * both already hold it, so only a user's first save since the last submit commit pays for the lock.
169
+ * Never creates branch.json.
170
+ */
171
+ async recordEditor(userId) {
172
+ const current = await readBranchMetadataFile(this.branchRoot);
173
+ if (!current || isRecordedEditor(current.branch, userId))
174
+ return;
175
+ await this.update((existing) => existing && !isRecordedEditor(existing.branch, userId)
176
+ ? {
177
+ branch: {
178
+ editors: withMember(existing.branch.editors, userId),
179
+ uncommittedEditors: withMember(existing.branch.uncommittedEditors, userId),
180
+ },
181
+ }
182
+ : null, { keepUpdatedAt: true });
183
+ }
184
+ /**
185
+ * Removes `userIds` from `uncommittedEditors` once a submit commit's trailers name them. Ids
186
+ * recorded after the commit read them stay, so the next commit names them.
187
+ */
188
+ async markEditorsCommitted(userIds) {
189
+ const committed = new Set(userIds);
190
+ if (committed.size === 0)
191
+ return;
192
+ await this.update((existing) => {
193
+ const pending = existing?.branch.uncommittedEditors ?? [];
194
+ const remaining = pending.filter((id) => !committed.has(id));
195
+ if (remaining.length === pending.length)
196
+ return null;
197
+ return { branch: { uncommittedEditors: remaining.length > 0 ? remaining : undefined } };
198
+ }, { keepUpdatedAt: true });
199
+ }
175
200
  /** Invalidate the registry cache so the next list() regenerates. */
176
201
  async invalidateRegistry() {
177
202
  const registry = new BranchRegistry(this.baseRoot);
178
203
  await registry.invalidate();
179
204
  }
180
205
  }
206
+ function isRecordedEditor(branch, userId) {
207
+ return ((branch.editors?.includes(userId) ?? false) &&
208
+ (branch.uncommittedEditors?.includes(userId) ?? false));
209
+ }
210
+ function withMember(list, id) {
211
+ return list?.includes(id) ? [...list] : [...(list ?? []), id];
212
+ }
213
+ /**
214
+ * Records `user` as an editor of the branch, for the submit's trailers and PR body. Called once a
215
+ * change to the branch's working tree has succeeded, so a failure here is logged and never fails
216
+ * that change. Anonymous users are not recorded.
217
+ */
218
+ export async function recordBranchEditor(context, user) {
219
+ if (user.type !== 'authenticated')
220
+ return;
221
+ try {
222
+ await getBranchMetadataFileManager(context.branchRoot, context.baseRoot).recordEditor(user.userId);
223
+ }
224
+ catch (err) {
225
+ canopyLogWarn(`CanopyCMS: Could not record an editor of ${path.basename(context.branchRoot)}; the next submit will not credit them:`, getErrorMessage(err));
226
+ }
227
+ }
228
+ /**
229
+ * The branch.json that {@link BranchMetadataFileManager.save} writes for `incoming` over
230
+ * `existing`. Pure, so a staged workspace's first branch.json (branch-provisioning.ts) is built by
231
+ * the same rules as a save and the two cannot drift.
232
+ */
233
+ function mergeBranchMetadata(existing, version, incoming, now = new Date().toISOString()) {
234
+ const defaults = {
235
+ name: 'unknown',
236
+ status: 'editing',
237
+ access: {},
238
+ createdBy: 'unknown',
239
+ createdAt: now,
240
+ updatedAt: now,
241
+ };
242
+ return {
243
+ // Envelope keys this code does not know survive, as unknown branch keys do below.
244
+ ...existing,
245
+ schemaVersion: CURRENT_SCHEMA_VERSION,
246
+ version: version ?? 0,
247
+ branch: {
248
+ ...defaults,
249
+ ...existing?.branch,
250
+ ...incoming.branch,
251
+ access: {
252
+ ...existing?.branch?.access,
253
+ ...incoming.branch?.access,
254
+ },
255
+ // Immutable after creation
256
+ createdBy: existing?.branch.createdBy ?? incoming.branch?.createdBy ?? defaults.createdBy,
257
+ createdAt: existing?.branch.createdAt ?? defaults.createdAt,
258
+ // Fork point is recorded once at creation; later saves must not move it
259
+ baseBranch: existing?.branch.baseBranch ?? incoming.branch?.baseBranch,
260
+ // Always stamped fresh; the spreads above would otherwise let the creation-time value win
261
+ // forever, freezing the timestamp the editor's Branches panel sorts and displays by
262
+ updatedAt: now,
263
+ },
264
+ };
265
+ }
266
+ /** The branch.json a brand-new branch starts with: {@link mergeBranchMetadata} over nothing. */
267
+ export function buildInitialBranchMetadata(incoming, now) {
268
+ return mergeBranchMetadata(null, null, incoming, now);
269
+ }
181
270
  /**
182
271
  * The metadata update for archiving a branch whose PR merged. Shared by the
183
272
  * worker's merge-poll (CmsWorker.pollMergeState) and the manual markAsMerged
@@ -193,6 +282,9 @@ export function buildMergedBranchUpdate(branchName, now = new Date()) {
193
282
  status: 'archived',
194
283
  pullRequestState: 'merged',
195
284
  mergedAt: now.toISOString(),
285
+ // The merge settled every conflict the record named.
286
+ conflictStatus: 'clean',
287
+ conflictFiles: [],
196
288
  };
197
289
  }
198
290
  /** {@link BranchMetadataFileManager.get} as a function. */
@@ -0,0 +1,167 @@
1
+ import { type BranchMetadataFile } from './branch-metadata-file.js';
2
+ import { type CloneRepoOptions } from './git-manager.js';
3
+ import type { ProvisionLog } from './utils/provision-log.js';
4
+ /** How long residue must have been untouched before a create request moves it aside. */
5
+ export declare const CREATE_RESIDUE_MIN_QUIET_MS = 60000;
6
+ /**
7
+ * Retriable: another process is setting this branch up, or what is at its path is too recently
8
+ * touched (or under an admin repair) to judge. Callers answer 503.
9
+ */
10
+ export declare class BranchProvisioningBusyError extends Error {
11
+ readonly dirName: string;
12
+ constructor(dirName: string, message?: string);
13
+ }
14
+ /** The branch's path holds a git repository this deployment did not create (prod only). */
15
+ export declare class BranchDirOccupiedError extends Error {
16
+ readonly dirName: string;
17
+ constructor(dirName: string);
18
+ }
19
+ /** `YYYYMMDDTHHMMSSZ` in UTC, with no colons. */
20
+ export declare function formatDirStamp(date: Date): string;
21
+ /**
22
+ * The date in a name ending `-{STAMP}`, or null. Leftovers are aged by this alone, never by
23
+ * mtime: `rename()` keeps a directory's mtime, so a months-old residue would look old the
24
+ * moment it was moved.
25
+ */
26
+ export declare function parseDirStamp(name: string): Date | null;
27
+ /**
28
+ * A branch directory name cut to a bounded, still-unique form: branch names run to 250 chars.
29
+ * @internal Exported for tests.
30
+ */
31
+ export declare function shortDirName(dirName: string): string;
32
+ /** Where a branch directory goes the moment it is deleted, before its `rm`. */
33
+ export declare const deletingDirName: (dirName: string, now?: Date) => string;
34
+ /** The settings workspace's staging and trash siblings, under the workspace root. */
35
+ export declare const settingsStagingDirName: (now?: Date) => string;
36
+ export declare const settingsTrashDirName: (now?: Date) => string;
37
+ interface ResidueSignature {
38
+ hasGitDir: boolean;
39
+ configLock: boolean;
40
+ hasIndex: boolean;
41
+ }
42
+ /**
43
+ * What occupies a branch's final path.
44
+ *
45
+ * - `live` / `corrupt`: branch.json is there. Never touched by provisioning.
46
+ * - `protected`: an admin repair-metadata is mid-way (`branch.json.corrupt-*` with no
47
+ * branch.json), or branch.json vanished between the listing and the read. Left alone.
48
+ * - `residue`: nothing this deployment could still be using — no `.git`, or a `.git` whose config
49
+ * is missing or unreadable (an interrupted clone), or one whose remote is this deployment's own
50
+ * (an interrupted clone or delete).
51
+ * - `foreign`: any other repository, such as a dev CLI-sync workspace, which has no remote.
52
+ */
53
+ export type FinalDirState = {
54
+ kind: 'vacant';
55
+ } | {
56
+ kind: 'live';
57
+ } | {
58
+ kind: 'corrupt';
59
+ } | {
60
+ kind: 'protected';
61
+ } | {
62
+ kind: 'foreign';
63
+ } | {
64
+ kind: 'residue';
65
+ signature: ResidueSignature;
66
+ };
67
+ /** Classify what is at `dirPath`; see {@link FinalDirState}. */
68
+ export declare function classifyFinalDir(dirPath: string, expectedRemoteUrl: string): Promise<FinalDirState>;
69
+ /**
70
+ * Milliseconds since anything was last created or removed in the directory, its `.git` or its
71
+ * `.canopy-meta`. A clone killed inside `git config` touched `.git` when it made `config.lock`.
72
+ */
73
+ export declare function quietAgeMs(dirPath: string, now?: number): Promise<number>;
74
+ type KeptReason = 'live' | 'corrupt' | 'protected' | 'foreign' | 'too-young' | 'replaced';
75
+ export type QuarantineResult = {
76
+ kind: 'quarantined';
77
+ trashName: string;
78
+ } | {
79
+ kind: 'vacant';
80
+ } | {
81
+ kind: 'kept';
82
+ reason: KeptReason;
83
+ };
84
+ export interface QuarantineOptions {
85
+ minQuietMs: number;
86
+ expectedRemoteUrl: string;
87
+ now?: number;
88
+ }
89
+ /**
90
+ * Move residue at `baseRoot/dirName` to `.trash-*`. The caller MUST hold the branch's
91
+ * provisioning lock: admin repair-metadata holds it while it legitimately leaves a directory
92
+ * with real edits and no branch.json.
93
+ *
94
+ * Move-then-verify: the name is renamed aside first and only what arrived at the new name is
95
+ * judged — the same inode the old name showed, still residue, quiet for `minQuietMs` — because
96
+ * a look through the old name can come from a stale NFS dentry for a directory another host has
97
+ * since replaced with a live clone. Anything that fails a check, or cannot be checked, is
98
+ * renamed straight back: the sweep would trash a `.repair-*` without branch.json.
99
+ */
100
+ export declare function quarantineResidueAt(baseRoot: string, dirName: string, options: QuarantineOptions): Promise<QuarantineResult>;
101
+ export interface StageBranchOptions {
102
+ baseRoot: string;
103
+ dirName: string;
104
+ branchName: string;
105
+ baseBranch: string;
106
+ remoteUrl: string;
107
+ clone: Pick<CloneRepoOptions, 'remoteName' | 'config'>;
108
+ gitExcludePattern: string;
109
+ metadata: BranchMetadataFile;
110
+ provisionLog: ProvisionLog;
111
+ /** The sparse-checkout cone (branch-sparse.ts), set before the checkout; none for a full clone. */
112
+ sparseCone?: readonly string[] | null;
113
+ }
114
+ export type StageResult = {
115
+ kind: 'staged';
116
+ stagingPath: string;
117
+ meta: BranchMetadataFile;
118
+ } | {
119
+ kind: 'exists';
120
+ };
121
+ /** Remove a staging or deleted directory, leaving it to the worker's sweep on failure. */
122
+ export declare function removeLeftoverDir(dirPath: string): Promise<void>;
123
+ /**
124
+ * Build a complete branch workspace, branch.json included, in a staging sibling of the final
125
+ * path. Takes no lock. Returns `exists` without checking out when a competitor published first.
126
+ * A failure removes the staging directory; a kill leaves it for the worker.
127
+ */
128
+ export declare function stageBranchWorkspace(options: StageBranchOptions): Promise<StageResult>;
129
+ /**
130
+ * The branch's provisioning lock with a bounded wait; contention is
131
+ * {@link BranchProvisioningBusyError}.
132
+ */
133
+ export declare function lockBranchName(baseRoot: string, dirName: string): Promise<() => Promise<void>>;
134
+ /** What blocks a refused publish rename, as {@link publishStaging} sees it. */
135
+ export type BlockingKind = 'live' | 'protected' | 'residue';
136
+ export interface PublishOptions {
137
+ baseRoot: string;
138
+ dirName: string;
139
+ stagingPath: string;
140
+ /** branch.json's `writeId` in the staged workspace: proof the rename was ours. */
141
+ writeId: string | undefined;
142
+ expectedRemoteUrl: string;
143
+ /** Test seam replacing the look at what blocks a refused rename. */
144
+ inspectBlocked?: (finalPath: string) => Promise<BlockingKind>;
145
+ }
146
+ /**
147
+ * Publish a staged workspace at its final path, under the branch's provisioning lock.
148
+ *
149
+ * A refused rename is inspected: branch.json there means another request won (`exists`);
150
+ * residue is quarantined and the rename retried once. ENOENT can be an NFS retransmission of a
151
+ * rename that already landed, so it counts as published when the final branch.json carries our
152
+ * `writeId`. The caller removes the staging directory on any outcome but `published`.
153
+ */
154
+ export declare function publishStaging(options: PublishOptions): Promise<'published' | 'exists'>;
155
+ export interface LeftoverAction {
156
+ name: string;
157
+ action: 'removed' | 'restored' | 'trashed' | 'failed';
158
+ detail?: string;
159
+ }
160
+ /**
161
+ * Clear what killed processes left beside the branch directories (`baseRoot`) and the settings
162
+ * workspace (`workspaceRoot`): stale `.prov-*` builds, `.deleting-*` directories whose `rm` never
163
+ * finished, `.repair-*` from an interrupted quarantine, and expired settings trash. Ages come
164
+ * from the stamp in each name.
165
+ */
166
+ export declare function sweepProvisioningLeftovers(baseRoot: string, workspaceRoot: string, now?: number): Promise<LeftoverAction[]>;
167
+ export {};