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
@@ -0,0 +1,421 @@
1
+ /**
2
+ * Source-preserving save for md/mdx bodies.
3
+ *
4
+ * The editor sends a body re-serialised from its own document model, so every block comes back
5
+ * in the serialiser's style (bullet and emphasis markers, rule style, escapes, no blank line
6
+ * after the frontmatter) whether or not the user touched it. This writes the on-disk text of
7
+ * every block whose MEANING the edit left alone, and the editor's text only where it changed:
8
+ *
9
+ * - Top-level blocks are aligned by a position-free canonical form of their mdast, so a block
10
+ * matches only when it parses to the same tree. A matched block is written from disk.
11
+ * - A changed block paired with an original of the same kind is spliced by its children when it
12
+ * is a list (by item) or a list item (by child block), at any depth, so editing one item leaves
13
+ * its siblings alone. New items take the original list's marker and numbering.
14
+ * - Whitespace between two output blocks that stand for adjacent on-disk blocks (an edited block
15
+ * stands for the original it is paired with) is the disk's; elsewhere it is the editor's,
16
+ * re-indented into the original's nesting and written in its line endings.
17
+ *
18
+ * The result is kept only if it parses to exactly the tree the editor's body parses to. A splice
19
+ * that fails that check is retried without descending into lists, and failing that the editor's
20
+ * body is written as sent: the worst case is the editor's own serialisation, never different
21
+ * content.
22
+ */
23
+ import { fromMarkdown } from 'mdast-util-from-markdown';
24
+ import { gfmFromMarkdown } from 'mdast-util-gfm';
25
+ import { mdxFromMarkdown } from 'mdast-util-mdx';
26
+ import { gfm } from 'micromark-extension-gfm';
27
+ import { mdxjs } from 'micromark-extension-mdxjs';
28
+ import { createDebugLogger } from './debug.js';
29
+ import { getErrorMessage } from './error.js';
30
+ import { withSourceLineEndings } from './yaml-source-splice.js';
31
+ const log = createDebugLogger({ prefix: 'MarkdownBodySplice' });
32
+ /** Above this many LCS cells the middle of a sequence is treated as one changed run. */
33
+ const MAX_LCS_CELLS = 4_000_000;
34
+ function parse(text, format) {
35
+ try {
36
+ return format === 'mdx'
37
+ ? fromMarkdown(text, {
38
+ extensions: [gfm(), mdxjs()],
39
+ mdastExtensions: [gfmFromMarkdown(), mdxFromMarkdown()],
40
+ })
41
+ : fromMarkdown(text, { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] });
42
+ }
43
+ catch (err) {
44
+ log.debug('markdown-body-splice', 'body does not parse', { error: getErrorMessage(err) });
45
+ return undefined;
46
+ }
47
+ }
48
+ /**
49
+ * A node's meaning as a string: its tree without source positions. `data` goes too, because
50
+ * the parser fills it only with derived information (an MDX expression's estree, which carries
51
+ * its own offsets) and never with content. Line endings inside values are normalised, so a CRLF
52
+ * file's blocks match the editor's LF ones. A bare URL and `[url](url)` share a tree, which keeps
53
+ * a bare URL on disk when the editor (whose `linkPlugin()` autolinks, in `MarkdownField.tsx`) sends it back as a link.
54
+ */
55
+ function canonical(node) {
56
+ return JSON.stringify(node, (key, value) => {
57
+ if (key === 'position' || key === 'data')
58
+ return undefined;
59
+ if (typeof value === 'string')
60
+ return value.replace(/\r\n?/g, '\n');
61
+ return value;
62
+ });
63
+ }
64
+ /**
65
+ * `text`'s top-level blocks, each as its source text and its `canonical` form, or undefined when
66
+ * `text` does not parse: two bodies mean the same to the splice exactly when their `meaning`
67
+ * lists are equal.
68
+ *
69
+ * @internal Exported for the round-trip corpus test.
70
+ */
71
+ export function markdownBlocks(text, format) {
72
+ return parse(text, format)?.children?.map((node) => ({
73
+ text: sliceOf(text, node) ?? '',
74
+ meaning: canonical(node),
75
+ }));
76
+ }
77
+ /** What a changed block must share with an original to be paired with it. */
78
+ function kindOf(node) {
79
+ return `${node.type}:${String(node.ordered ?? '')}:${String(node.checked ?? '')}`;
80
+ }
81
+ function startOf(node) {
82
+ return node?.position?.start.offset;
83
+ }
84
+ function endOf(node) {
85
+ return node?.position?.end.offset;
86
+ }
87
+ function sliceOf(text, node) {
88
+ const start = startOf(node);
89
+ const end = endOf(node);
90
+ return start === undefined || end === undefined ? undefined : text.slice(start, end);
91
+ }
92
+ /**
93
+ * Move continuation lines from one nesting to another: a line starting with `from` gets `to`
94
+ * instead. The first line is never touched (it continues whatever precedes it), and neither is
95
+ * an empty line, which would otherwise gain trailing spaces.
96
+ */
97
+ function reindent(text, from, to) {
98
+ if (from === to)
99
+ return text;
100
+ return text
101
+ .split('\n')
102
+ .map((line, i) => i > 0 && line !== '' && line.startsWith(from) ? to + line.slice(from.length) : line)
103
+ .join('\n');
104
+ }
105
+ /**
106
+ * Index pairs of a longest common subsequence of `a` and `b`, in order. Common prefix and suffix
107
+ * are matched first, which is the whole answer for the usual one-region edit.
108
+ */
109
+ function matchSequences(keysA, keysB) {
110
+ // Compared as small integers: the DP compares O(n * m) pairs, and keys are whole serialised trees.
111
+ const ids = new Map();
112
+ const intern = (key) => {
113
+ const id = ids.get(key) ?? ids.size;
114
+ ids.set(key, id);
115
+ return id;
116
+ };
117
+ const a = keysA.map(intern);
118
+ const b = keysB.map(intern);
119
+ const pairs = [];
120
+ let head = 0;
121
+ while (head < a.length && head < b.length && a[head] === b[head]) {
122
+ pairs.push([head, head]);
123
+ head++;
124
+ }
125
+ let endA = a.length;
126
+ let endB = b.length;
127
+ while (endA > head && endB > head && a[endA - 1] === b[endB - 1]) {
128
+ endA--;
129
+ endB--;
130
+ }
131
+ const n = endA - head;
132
+ const m = endB - head;
133
+ if (n > 0 && m > 0 && n * m <= MAX_LCS_CELLS) {
134
+ const width = m + 1;
135
+ // lengths[i * width + j]: LCS length of a[head + i, endA) and b[head + j, endB).
136
+ const lengths = new Uint32Array((n + 1) * width);
137
+ const at = (i, j) => lengths[i * width + j] ?? 0;
138
+ for (let i = n - 1; i >= 0; i--) {
139
+ for (let j = m - 1; j >= 0; j--) {
140
+ lengths[i * width + j] =
141
+ a[head + i] === b[head + j] ? at(i + 1, j + 1) + 1 : Math.max(at(i + 1, j), at(i, j + 1));
142
+ }
143
+ }
144
+ let i = 0;
145
+ let j = 0;
146
+ while (i < n && j < m) {
147
+ if (a[head + i] === b[head + j]) {
148
+ pairs.push([head + i, head + j]);
149
+ i++;
150
+ j++;
151
+ }
152
+ else if (at(i + 1, j) >= at(i, j + 1)) {
153
+ i++;
154
+ }
155
+ else {
156
+ j++;
157
+ }
158
+ }
159
+ }
160
+ for (let k = 0; k < a.length - endA; k++)
161
+ pairs.push([endA + k, endB + k]);
162
+ return pairs;
163
+ }
164
+ /**
165
+ * The text a line has before `offset`, provided it is pure indentation plus list markers: the
166
+ * width continuation lines of a block starting at `offset` are indented by. Undefined for a tab
167
+ * or any other character, whose width this module does not reason about.
168
+ */
169
+ function leadWidth(text, offset) {
170
+ const lineStart = text.lastIndexOf('\n', offset - 1) + 1;
171
+ const lead = text.slice(lineStart, offset);
172
+ return /^(?: |[-*+](?= )|\d{1,9}[.)](?= )|\[[ xX]\](?= ))*$/.test(lead) ? lead.length : undefined;
173
+ }
174
+ class Splicer {
175
+ constructor(original, updated,
176
+ /** Whether a changed list or list item may be spliced by its children. */
177
+ deep) {
178
+ this.original = original;
179
+ this.updated = updated;
180
+ this.deep = deep;
181
+ this.eol = withSourceLineEndings('\n', original);
182
+ }
183
+ /** The whole body, or undefined when either side has nothing to align. */
184
+ body(before, after) {
185
+ const oNodes = before.children ?? [];
186
+ const nNodes = after.children ?? [];
187
+ const first = startOf(oNodes[0]);
188
+ const last = endOf(oNodes[oNodes.length - 1]);
189
+ if (first === undefined || last === undefined || nNodes.length === 0)
190
+ return undefined;
191
+ const o = { text: this.original, indent: '' };
192
+ const n = { text: this.updated, indent: '' };
193
+ const pieces = this.siblings(o, oNodes, n, nNodes);
194
+ if (pieces === undefined)
195
+ return undefined;
196
+ const run = this.join(pieces, o, oNodes, n, nNodes);
197
+ return run === undefined
198
+ ? undefined
199
+ : this.original.slice(0, first) + run + this.original.slice(last);
200
+ }
201
+ /** Output blocks for one sibling sequence, in the editor's order. */
202
+ siblings(o, oNodes, n, nNodes) {
203
+ const oKeys = oNodes.map(canonical);
204
+ const nKeys = nNodes.map(canonical);
205
+ const pairs = matchSequences(oKeys, nKeys);
206
+ // Originals the alignment left unmatched, by meaning: an in-order alignment cannot match a
207
+ // block that MOVED, so a new block equal to one of these is written from its source here.
208
+ const unmatched = new Map();
209
+ const matched = new Set(pairs.map(([oi]) => oi));
210
+ oKeys.forEach((key, index) => {
211
+ if (matched.has(index))
212
+ return;
213
+ const bucket = unmatched.get(key);
214
+ if (bucket)
215
+ bucket.push(index);
216
+ else
217
+ unmatched.set(key, [index]);
218
+ });
219
+ const pieces = [];
220
+ let oNext = 0;
221
+ let nNext = 0;
222
+ for (const [oi, ni] of [...pairs, [oNodes.length, nNodes.length]]) {
223
+ // The changed run between two matches. Its blocks pair by kind, in order: an in-place
224
+ // edit, possibly beside insertions and deletions. A paired block stands in its original's
225
+ // place for whitespace, and may be spliced by its children.
226
+ const kinds = matchSequences(oNodes.slice(oNext, oi).map(kindOf), nNodes.slice(nNext, ni).map(kindOf));
227
+ const partner = new Map(kinds.map(([a, b]) => [nNext + b, oNext + a]));
228
+ for (let k = nNext; k < ni; k++) {
229
+ const nNode = nNodes[k];
230
+ if (nNode === undefined)
231
+ return undefined;
232
+ const moved = unmatched.get(nKeys[k] ?? '')?.shift();
233
+ const movedNode = moved === undefined ? undefined : oNodes[moved];
234
+ if (movedNode !== undefined) {
235
+ const text = sliceOf(o.text, movedNode);
236
+ if (text === undefined)
237
+ return undefined;
238
+ pieces.push({ text, orig: moved });
239
+ continue;
240
+ }
241
+ const oIndex = partner.get(k);
242
+ const oNode = oIndex === undefined ? undefined : oNodes[oIndex];
243
+ const text = (oNode && this.deep ? this.changed(o, oNode, n, nNode) : undefined) ??
244
+ this.fresh(n, nNode, o);
245
+ if (text === undefined)
246
+ return undefined;
247
+ pieces.push({ text, orig: oIndex });
248
+ }
249
+ const oNode = oNodes[oi];
250
+ if (oNode !== undefined && ni < nNodes.length) {
251
+ const text = sliceOf(o.text, oNode);
252
+ if (text === undefined)
253
+ return undefined;
254
+ pieces.push({ text, orig: oi });
255
+ }
256
+ oNext = oi + 1;
257
+ nNext = ni + 1;
258
+ }
259
+ return pieces;
260
+ }
261
+ /** The editor's text for a node, moved into the original's nesting and line endings. */
262
+ fresh(n, node, o) {
263
+ const text = sliceOf(n.text, node);
264
+ return text === undefined ? undefined : this.ownEndings(reindent(text, n.indent, o.indent));
265
+ }
266
+ /** Whitespace from the editor's body, moved into the original's nesting and line endings. */
267
+ editorGap(gap, n, o) {
268
+ return gap === undefined ? undefined : this.ownEndings(reindent(gap, n.indent, o.indent));
269
+ }
270
+ ownEndings(text) {
271
+ const lf = text.replace(/\r\n?/g, '\n');
272
+ return this.eol === '\n' ? lf : lf.replace(/\n/g, this.eol);
273
+ }
274
+ /** A changed node spliced by its children, or undefined to write the editor's text. */
275
+ changed(o, oNode, n, nNode) {
276
+ if (oNode.type === 'list' && nNode.type === 'list' && oNode.ordered === nNode.ordered) {
277
+ return this.list(o, oNode, n, nNode);
278
+ }
279
+ if (oNode.type === 'listItem' &&
280
+ nNode.type === 'listItem' &&
281
+ (oNode.checked ?? null) === (nNode.checked ?? null)) {
282
+ return this.item(o, oNode, n, nNode);
283
+ }
284
+ return undefined;
285
+ }
286
+ list(o, oList, n, nList) {
287
+ const oItems = oList.children ?? [];
288
+ const nItems = nList.children ?? [];
289
+ const markers = listMarkers(o.text, oItems, Boolean(oList.ordered));
290
+ if (markers === undefined)
291
+ return undefined;
292
+ const pieces = this.siblings(o, oItems, n, nItems);
293
+ if (pieces === undefined)
294
+ return undefined;
295
+ for (const [index, piece] of pieces.entries()) {
296
+ const text = markers(piece.text, index);
297
+ if (text === undefined)
298
+ return undefined;
299
+ piece.text = text;
300
+ }
301
+ return this.join(pieces, o, oItems, n, nItems);
302
+ }
303
+ item(o, oItem, n, nItem) {
304
+ const oChildren = oItem.children ?? [];
305
+ const nChildren = nItem.children ?? [];
306
+ const oStart = startOf(oItem);
307
+ const oContent = startOf(oChildren[0]);
308
+ const nContent = startOf(nChildren[0]);
309
+ if (oStart === undefined || oContent === undefined || nContent === undefined)
310
+ return undefined;
311
+ const prefix = o.text.slice(oStart, oContent);
312
+ if (prefix.includes('\n'))
313
+ return undefined;
314
+ const oWidth = leadWidth(o.text, oContent);
315
+ const nWidth = leadWidth(n.text, nContent);
316
+ if (oWidth === undefined || nWidth === undefined)
317
+ return undefined;
318
+ const oInner = { text: o.text, indent: ' '.repeat(oWidth) };
319
+ const nInner = { text: n.text, indent: ' '.repeat(nWidth) };
320
+ const pieces = this.siblings(oInner, oChildren, nInner, nChildren);
321
+ if (pieces === undefined)
322
+ return undefined;
323
+ const run = this.join(pieces, oInner, oChildren, nInner, nChildren);
324
+ return run === undefined ? undefined : prefix + run;
325
+ }
326
+ /**
327
+ * Join output blocks. Between two blocks that were adjacent on disk the disk's whitespace is
328
+ * kept; otherwise the editor's (every output block pair is adjacent in the editor's body).
329
+ */
330
+ join(pieces, o, oNodes, n, nNodes) {
331
+ let out = '';
332
+ for (const [k, piece] of pieces.entries()) {
333
+ if (k > 0) {
334
+ const prev = pieces[k - 1]?.orig;
335
+ const gap = prev !== undefined && piece.orig === prev + 1
336
+ ? gapBetween(o.text, oNodes[prev], oNodes[piece.orig])
337
+ : this.editorGap(gapBetween(n.text, nNodes[k - 1], nNodes[k]), n, o);
338
+ if (gap === undefined)
339
+ return undefined;
340
+ out += gap;
341
+ }
342
+ out += piece.text;
343
+ }
344
+ return out;
345
+ }
346
+ }
347
+ function gapBetween(text, a, b) {
348
+ const end = endOf(a);
349
+ const start = startOf(b);
350
+ return end === undefined || start === undefined ? undefined : text.slice(end, start);
351
+ }
352
+ /**
353
+ * Rewrites an output item's marker to the original list's: its bullet character, or for an
354
+ * ordered list its delimiter and numbering scheme (all one number when the original's first two
355
+ * items share one, else counting up from its start). Undefined when the original's markers
356
+ * cannot be read, or when renumbering would change the width of a multi-line item, whose
357
+ * continuation lines are aligned to the old width.
358
+ */
359
+ function listMarkers(text, items, ordered) {
360
+ const firstStart = startOf(items[0]);
361
+ if (firstStart === undefined)
362
+ return undefined;
363
+ if (!ordered) {
364
+ const bullet = text[firstStart];
365
+ if (bullet !== '-' && bullet !== '*' && bullet !== '+')
366
+ return undefined;
367
+ return (itemText) => (/^[-*+]/.test(itemText) ? bullet + itemText.slice(1) : undefined);
368
+ }
369
+ const numberAt = (offset) => offset === undefined ? null : /^(\d{1,9})([.)])/.exec(text.slice(offset, offset + 10));
370
+ const first = numberAt(firstStart);
371
+ if (first === null)
372
+ return undefined;
373
+ const start = Number(first[1]);
374
+ const delimiter = first[2] ?? '.';
375
+ // `01.`, `02.`: zero-padded to the first marker's width.
376
+ const padTo = first[1]?.startsWith('0') ? first[1].length : 0;
377
+ const second = items.length > 1 ? numberAt(startOf(items[1])) : null;
378
+ const step = second !== null && Number(second[1]) === start ? 0 : 1;
379
+ return (itemText, index) => {
380
+ const current = /^\d{1,9}[.)]/.exec(itemText);
381
+ if (current === null)
382
+ return undefined;
383
+ const marker = `${String(start + step * index).padStart(padTo, '0')}${delimiter}`;
384
+ if (marker.length !== current[0].length && itemText.includes('\n'))
385
+ return undefined;
386
+ return marker + itemText.slice(current[0].length);
387
+ };
388
+ }
389
+ /**
390
+ * The body to write for an edit from `original` (on disk) to `updated` (the editor's body):
391
+ * `updated`'s content, in `original`'s text wherever the two agree. See the module header.
392
+ */
393
+ export function preserveMarkdownSource(original, updated, format) {
394
+ if (original === updated)
395
+ return original;
396
+ try {
397
+ return splice(original, updated, format) ?? updated;
398
+ }
399
+ catch (err) {
400
+ // A deep enough body overflows the stack in parsing or serialising; a save must not fail.
401
+ log.debug('markdown-body-splice', 'splice failed', { error: getErrorMessage(err) });
402
+ return updated;
403
+ }
404
+ }
405
+ function splice(original, updated, format) {
406
+ const before = parse(original, format);
407
+ const after = parse(updated, format);
408
+ if (before === undefined || after === undefined)
409
+ return undefined;
410
+ const target = canonical(after);
411
+ for (const deep of [true, false]) {
412
+ const candidate = new Splicer(original, updated, deep).body(before, after);
413
+ if (candidate === undefined)
414
+ continue;
415
+ const reparsed = parse(candidate, format);
416
+ if (reparsed !== undefined && canonical(reparsed) === target)
417
+ return candidate;
418
+ log.debug('markdown-body-splice', 'splice does not read back as the edit', { deep });
419
+ }
420
+ return undefined;
421
+ }
@@ -4,6 +4,8 @@ import { randomUUID } from 'node:crypto';
4
4
  import lockfile from 'proper-lockfile';
5
5
  import { getErrorMessage, isFileExistsError, isNodeError } from './error.js';
6
6
  import { createDebugLogger } from './debug.js';
7
+ import { canopyLogWarn } from './logger.js';
8
+ import { guardOnCompromised } from './provisioning-lock.js';
7
9
  const log = createDebugLogger({ prefix: 'OccJsonWrite' });
8
10
  /**
9
11
  * Shared optimistic-concurrency-control (OCC) JSON write machinery, extracted
@@ -158,8 +160,16 @@ export async function withOccRetry(operation, options) {
158
160
  * legitimately take many seconds.
159
161
  */
160
162
  export async function withOccFileLock(filePath, fn) {
161
- const dir = path.dirname(filePath);
162
- await fs.mkdir(dir, { recursive: true });
163
+ // The lock's own directory only, never its ancestors: a waiter must not recreate a branch root
164
+ // that a delete removed under it (the same rule as the ENOENT case in the loop below).
165
+ try {
166
+ await fs.mkdir(path.dirname(filePath));
167
+ }
168
+ catch (err) {
169
+ if (!isNodeError(err) || err.code !== 'EEXIST') {
170
+ throw new OccWriteConflictError(`Could not acquire file lock: ${getErrorMessage(err)}`);
171
+ }
172
+ }
163
173
  // Acquisition retries are OUR loop, not proper-lockfile's built-in
164
174
  // `retries`: the built-in loop retries blindly on ANY error, so a waiter
165
175
  // whose target directory was deleted mid-poll (deleteBranch's rm removing
@@ -191,16 +201,12 @@ export async function withOccFileLock(filePath, fn) {
191
201
  realpath: false,
192
202
  stale: 10_000,
193
203
  retries: 0,
194
- // A compromised lock (the lock dir vanished or refresh failed
195
- // mid-hold, e.g. the branch directory containing it was deleted)
196
- // must not crash the process, which is proper-lockfile's default.
197
- // Our critical sections are short and idempotent-on-conflict (OCC
198
- // verify inside); log and let the section finish.
199
- onCompromised: (err) => {
200
- log.warn('lock', `Lock compromised mid-hold for ${filePath}`, {
201
- error: getErrorMessage(err),
202
- });
203
- },
204
+ // A compromised lock (the lock dir vanished or refresh failed mid-hold, e.g. the branch
205
+ // directory containing it was deleted, or an EFS waiter misread a cached mtime and took
206
+ // it over) must not crash the process, which is proper-lockfile's default. Our critical
207
+ // sections are short and idempotent-on-conflict (OCC verify inside); log and let the
208
+ // section finish.
209
+ onCompromised: guardOnCompromised((err) => canopyLogWarn(`[canopy] OCC file lock compromised mid-hold for ${filePath}:`, getErrorMessage(err)), filePath),
204
210
  });
205
211
  }
206
212
  catch (err) {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Replace where step lines go; no argument restores `canopyLog`. The test
3
+ * setup silences them, since nearly every suite provisions a workspace.
4
+ * @internal Exported for tests.
5
+ */
6
+ export declare function setProvisionLogSink(next?: (line: string) => void): void;
7
+ /**
8
+ * Per-step timing for one workspace provisioning, printed unconditionally
9
+ * (never behind `CANOPYCMS_DEBUG`). A request killed mid-step, such as a Lambda
10
+ * at its timeout, never reaches its own error handling, so each step prints a
11
+ * `start` line and a `done` line: the last line in the log names the step that
12
+ * was running.
13
+ *
14
+ * Lines read `[canopy] provision id=<id> dir=<dir> step=<step> start|done ms=<n>|failed ms=<n>`,
15
+ * then one `outcome=<outcome> total=<ms> <step>=<ms> …` summary.
16
+ */
17
+ export declare class ProvisionLog {
18
+ readonly id: string;
19
+ private readonly startedAt;
20
+ private readonly durations;
21
+ private readonly prefix;
22
+ constructor(dir: string);
23
+ step<T>(name: string, run: () => Promise<T>): Promise<T>;
24
+ finish(outcome: string): void;
25
+ }
@@ -0,0 +1,49 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { canopyLog } from './logger.js';
3
+ const defaultSink = (line) => canopyLog(line);
4
+ let sink = defaultSink;
5
+ /**
6
+ * Replace where step lines go; no argument restores `canopyLog`. The test
7
+ * setup silences them, since nearly every suite provisions a workspace.
8
+ * @internal Exported for tests.
9
+ */
10
+ export function setProvisionLogSink(next) {
11
+ sink = next ?? defaultSink;
12
+ }
13
+ /**
14
+ * Per-step timing for one workspace provisioning, printed unconditionally
15
+ * (never behind `CANOPYCMS_DEBUG`). A request killed mid-step, such as a Lambda
16
+ * at its timeout, never reaches its own error handling, so each step prints a
17
+ * `start` line and a `done` line: the last line in the log names the step that
18
+ * was running.
19
+ *
20
+ * Lines read `[canopy] provision id=<id> dir=<dir> step=<step> start|done ms=<n>|failed ms=<n>`,
21
+ * then one `outcome=<outcome> total=<ms> <step>=<ms> …` summary.
22
+ */
23
+ export class ProvisionLog {
24
+ constructor(dir) {
25
+ this.id = randomBytes(3).toString('hex');
26
+ this.startedAt = Date.now();
27
+ this.durations = [];
28
+ this.prefix = `[canopy] provision id=${this.id} dir=${dir}`;
29
+ }
30
+ async step(name, run) {
31
+ sink(`${this.prefix} step=${name} start`);
32
+ const stepStartedAt = Date.now();
33
+ let state = 'failed';
34
+ try {
35
+ const result = await run();
36
+ state = 'done';
37
+ return result;
38
+ }
39
+ finally {
40
+ const ms = Date.now() - stepStartedAt;
41
+ this.durations.push(`${name}=${ms}`);
42
+ sink(`${this.prefix} step=${name} ${state} ms=${ms}`);
43
+ }
44
+ }
45
+ finish(outcome) {
46
+ const total = Date.now() - this.startedAt;
47
+ sink(`${this.prefix} outcome=${outcome} total=${total} ${this.durations.join(' ')}`.trim());
48
+ }
49
+ }
@@ -9,6 +9,23 @@
9
9
  * inside the refresh timer, so a throw is an uncaught exception that kills the process.
10
10
  */
11
11
  export type OnLockCompromised = (err: Error) => void;
12
+ /**
13
+ * Wrap a compromise handler for proper-lockfile's `onCompromised`, which it invokes from inside
14
+ * its refresh timer: ANY throw escaping there is an uncaught exception that kills the process.
15
+ * Call sites are told not to throw (see {@link OnLockCompromised}); this makes it structural
16
+ * rather than a convention, and also covers the LOGGER throwing -- under `CI=true`, vitest's
17
+ * `onConsoleLog` turns a console write into a throw.
18
+ *
19
+ * Handlers report through an always-on logger (`canopyLogWarn`), never the debug logger: "two
20
+ * holders may now be live" must be visible without CANOPYCMS_DEBUG.
21
+ */
22
+ export declare function guardOnCompromised(handler: OnLockCompromised, lockPath: string): OnLockCompromised;
23
+ /**
24
+ * When an acquirer may take a provisioning marker over. Staleness is read from the marker's mtime,
25
+ * which EFS serves from the NFS attribute cache for up to 60s, so a live holder's 15s refreshes
26
+ * can look 60s late; one threshold above that for every acquirer means none reaps a live hold.
27
+ */
28
+ export declare const PROVISIONING_LOCK_STALE_MS = 90000;
12
29
  /**
13
30
  * The provisioning lock's name for the branch workspace directory `dirName`, held in that
14
31
  * directory's parent. Every party to a branch workspace's provisioning names it through this:
@@ -44,5 +61,19 @@ export declare function acquireProvisioningLock(lockTargetDir: string, lockName:
44
61
  *
45
62
  * @param staleMs how old the marker must look before this caller takes it over; defaults to
46
63
  * {@link PROVISIONING_LOCK_STALE_MS}
64
+ * @param createParents whether a missing parent of `lockTargetDir` is created. A lock inside a
65
+ * branch passes false: a waiter must never recreate a branch root a delete removed, so a missing
66
+ * parent fails with ENOENT instead.
67
+ */
68
+ export declare function tryAcquireProvisioningLock(lockTargetDir: string, lockName: string, onCompromised?: OnLockCompromised, staleMs?: number, createParents?: boolean): Promise<() => Promise<void>>;
69
+ /**
70
+ * {@link tryAcquireProvisioningLock} retried for at most `waitMs`, for a caller whose own hold is
71
+ * milliseconds long (publishing a staged branch workspace). A dead holder's marker stays live for
72
+ * {@link PROVISIONING_LOCK_STALE_MS}, so waiting it out from a request would spend the request's
73
+ * whole timeout; this gives up instead.
74
+ *
75
+ * Retries only on `ELOCKED`, like the content-write lock's bounded wait.
76
+ *
77
+ * @throws the last `ELOCKED` error once the budget is spent
47
78
  */
48
- export declare function tryAcquireProvisioningLock(lockTargetDir: string, lockName: string, onCompromised?: OnLockCompromised, staleMs?: number): Promise<() => Promise<void>>;
79
+ export declare function acquireProvisioningLockWithin(lockTargetDir: string, lockName: string, waitMs: number, onCompromised?: OnLockCompromised): Promise<() => Promise<void>>;