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
@@ -92,12 +92,12 @@ export function extractSeoFields(entryData, opts = {}) {
92
92
  * THE `noindex` predicate. Every surface that suppresses an entry must route through this one
93
93
  * function rather than re-deriving the flag:
94
94
  *
95
- * - the page's own `robots: { index: false }` (canopycms-next's `entryToMetadata`), and
96
- * - exclusion from the sitemap (canopycms-next's `generateContentSitemap`).
95
+ * - the page's own `robots: { index: false }` (canopycms-next's `entryToMetadata`),
96
+ * - exclusion from the sitemap (canopycms-next's `generateContentSitemap`), and
97
+ * - exclusion from AI content (`ai/generate.ts`, unless `exclude.noindex: false`).
97
98
  *
98
- * Those two shipping from one predicate is the point of shipping them together. Where they were
99
- * derived separately, a `noindex` entry stayed advertised in one surface while correctly absent
100
- * from the other, and nothing warned.
99
+ * Those surfaces shipping from one predicate is the point. Derived separately, a `noindex` entry
100
+ * stays advertised in one surface while correctly absent from another, and nothing warns.
101
101
  *
102
102
  * NOT applied to enumeration: `noindex` means "don't advertise this", not "don't build this".
103
103
  * `collectRoutableEntries` and `generateStaticParams` must still emit the entry, so its URL
@@ -1,3 +1,4 @@
1
+ import type { UserSearchResult } from './auth/types.js';
1
2
  import type { CanopyUser } from './user.js';
2
3
  /**
3
4
  * Who submitted (and edited) a branch, recorded in the submit commit's trailers
@@ -38,6 +39,13 @@ export declare function sanitizeUserId(raw: string | undefined): string | undefi
38
39
  export declare function sanitizeEmail(raw: string | undefined): string | undefined;
39
40
  /** The submitter as a SubmissionEditor, or undefined for an anonymous user. */
40
41
  export declare function submissionEditorFromUser(user: CanopyUser): SubmissionEditor | undefined;
42
+ /** Looks a user up by id, as `AuthPlugin.getUserMetadata` does. */
43
+ export type EditorLookup = (userId: string) => Promise<UserSearchResult | null>;
44
+ /**
45
+ * Recorded editor ids as SubmissionEditors, with the email and usable name (see personName) the
46
+ * auth provider gives now. A user the lookup cannot find, or a lookup that throws, keeps just the id.
47
+ */
48
+ export declare function describeEditors(userIds: readonly string[], lookup: EditorLookup | undefined): Promise<SubmissionEditor[]>;
41
49
  export interface CommitTrailerOptions {
42
50
  /** One `Edited-by: Name (id)` per editor. */
43
51
  editedBy: boolean;
@@ -1,3 +1,5 @@
1
+ import { personName } from './auth/person-name.js';
2
+ import { getErrorMessage } from './utils/error.js';
1
3
  import { canopyLogWarn } from './utils/logger.js';
2
4
  const MAX_NAME_LENGTH = 80;
3
5
  const MAX_ID_LENGTH = 128;
@@ -87,6 +89,26 @@ export function submissionEditorFromUser(user) {
87
89
  return undefined;
88
90
  return { userId: user.userId, name: user.name, email: user.email };
89
91
  }
92
+ /**
93
+ * Recorded editor ids as SubmissionEditors, with the email and usable name (see personName) the
94
+ * auth provider gives now. A user the lookup cannot find, or a lookup that throws, keeps just the id.
95
+ */
96
+ export async function describeEditors(userIds, lookup) {
97
+ return Promise.all(userIds.map(async (userId) => {
98
+ if (!lookup)
99
+ return { userId };
100
+ try {
101
+ const found = await lookup(userId);
102
+ return found
103
+ ? { userId, name: personName(userId, found.name), email: found.email }
104
+ : { userId };
105
+ }
106
+ catch (err) {
107
+ canopyLogWarn('CanopyCMS: Could not look up an editor; crediting them by id:', getErrorMessage(err));
108
+ return { userId };
109
+ }
110
+ }));
111
+ }
90
112
  function cleanEditors(editors) {
91
113
  const seen = new Set();
92
114
  const result = [];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The user ids CanopyCMS itself acts as, never a person. Dependency-free, so the editor bundle can
3
+ * import it.
4
+ */
5
+ /** Owner of a branch CanopyCMS provisions itself (http/handler.ts); see authorization/branch.ts. */
6
+ export declare const SYSTEM_USER_ID = "canopycms-system";
7
+ /** Owner recorded by a content read outside any request (content-reader.ts). */
8
+ export declare const CONTENT_READER_USER_ID = "canopycms-content-reader";
9
+ /** Owner recorded for the active branch a server context provisions (context.ts). */
10
+ export declare const CONTEXT_USER_ID = "canopycms-context";
11
+ /** True for a user id CanopyCMS acts as; the editor shows these as "CanopyCMS bot". */
12
+ export declare const isSystemUserId: (userId: string) => boolean;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The user ids CanopyCMS itself acts as, never a person. Dependency-free, so the editor bundle can
3
+ * import it.
4
+ */
5
+ /** Owner of a branch CanopyCMS provisions itself (http/handler.ts); see authorization/branch.ts. */
6
+ export const SYSTEM_USER_ID = 'canopycms-system';
7
+ /** Owner recorded by a content read outside any request (content-reader.ts). */
8
+ export const CONTENT_READER_USER_ID = 'canopycms-content-reader';
9
+ /** Owner recorded for the active branch a server context provisions (context.ts). */
10
+ export const CONTEXT_USER_ID = 'canopycms-context';
11
+ /** True for a user id CanopyCMS acts as; the editor shows these as "CanopyCMS bot". */
12
+ export const isSystemUserId = (userId) => userId === SYSTEM_USER_ID || userId === CONTENT_READER_USER_ID || userId === CONTEXT_USER_ID;
@@ -11,5 +11,5 @@ export type WorkerTask = Task & {
11
11
  action: TaskAction;
12
12
  };
13
13
  export declare const cmsTaskQueueLogger: TaskQueueLogger;
14
- export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
14
+ export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, releaseTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
15
15
  export type { Task, TaskStatus, QueueStats, TaskQueueLogger, CorruptTaskFile } from './types.js';
@@ -10,4 +10,4 @@ export const cmsTaskQueueLogger = {
10
10
  debugLogger.debug('task', message, data);
11
11
  },
12
12
  };
13
- export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
13
+ export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, releaseTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
@@ -1,2 +1,2 @@
1
1
  export type { Task, TaskStatus, QueueStats, TaskQueueLogger, CorruptTaskFile } from './types.js';
2
- export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
2
+ export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, releaseTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
@@ -1 +1 @@
1
- export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
1
+ export { enqueueTask, dequeueTask, completeTask, failTask, retryTask, releaseTask, requeueFailedTask, recoverOrphanedTasks, cleanupOldTasks, getTask, listTasks, getQueueStats, listCorruptTaskFiles, } from './task-queue.js';
@@ -49,6 +49,12 @@ export declare function failTask(taskDir: string, taskId: string, error: string,
49
49
  * and a retryAfter timestamp. Backoff: 5s → 10s → 20s → 40s → 60s cap.
50
50
  */
51
51
  export declare function retryTask(taskDir: string, taskId: string, error: string, logger?: TaskQueueLogger): Promise<void>;
52
+ /**
53
+ * Hand an in-flight task back to pending/ unchanged, with no retry spent and no
54
+ * backoff, for a consumer abandoning it while shutting down. Safe only for an
55
+ * idempotent action: the abandoned attempt may have done part of its work.
56
+ */
57
+ export declare function releaseTask(taskDir: string, taskId: string, logger?: TaskQueueLogger): Promise<void>;
52
58
  /**
53
59
  * Requeue a permanently-failed task: reads failed/{taskId}.json and creates a
54
60
  * brand-new pending task with a fresh `id`, never the original — `dequeueTask`
@@ -214,6 +214,35 @@ export async function retryTask(taskDir, taskId, error, logger = nullLogger) {
214
214
  await fs.unlink(processingPath).catch(() => { });
215
215
  logger.debug('Retrying task', { id: taskId, retryCount, backoffMs });
216
216
  }
217
+ /**
218
+ * Hand an in-flight task back to pending/ unchanged, with no retry spent and no
219
+ * backoff, for a consumer abandoning it while shutting down. Safe only for an
220
+ * idempotent action: the abandoned attempt may have done part of its work.
221
+ */
222
+ export async function releaseTask(taskDir, taskId, logger = nullLogger) {
223
+ const processingPath = path.join(taskDir, 'processing', `${taskId}.json`);
224
+ const pendingPath = path.join(taskDir, 'pending', `${taskId}.json`);
225
+ let task;
226
+ try {
227
+ const content = await fs.readFile(processingPath, 'utf-8');
228
+ const parsed = parseTaskJson(content);
229
+ if (!parsed) {
230
+ logger.debug('Corrupt task file in processing, removing', { id: taskId });
231
+ await fs.unlink(processingPath).catch(() => { });
232
+ return;
233
+ }
234
+ task = parsed;
235
+ }
236
+ catch (err) {
237
+ if (isNotFoundError(err))
238
+ return;
239
+ throw err;
240
+ }
241
+ task.status = 'pending';
242
+ await atomicWriteFile(pendingPath, JSON.stringify(task, null, 2));
243
+ await fs.unlink(processingPath).catch(() => { });
244
+ logger.debug('Released task', { id: taskId });
245
+ }
217
246
  /**
218
247
  * Requeue a permanently-failed task: reads failed/{taskId}.json and creates a
219
248
  * brand-new pending task with a fresh `id`, never the original — `dequeueTask`
@@ -11,7 +11,7 @@
11
11
  * (utils/atomic-write.ts), never a read-modify-write, so whichever write lands
12
12
  * last is one writer's complete, self-consistent snapshot. That covers the one
13
13
  * window where two holders overlap -- after a lock compromise the old holder's
14
- * `stop()` drains for up to `taskTimeoutMs` while a new holder is already
14
+ * `stop()` aborts its in-flight work while a new holder may already be
15
15
  * running against the same workspace.
16
16
  *
17
17
  * Readers are stale-tolerant by design: this is a liveness signal, not
@@ -21,6 +21,40 @@
21
21
  */
22
22
  import type { WorkerStatusReport } from '../types.js';
23
23
  export declare const WORKER_STATUS_FILE = "worker-status.json";
24
+ /**
25
+ * What a new worker carries from the previous status file into its first snapshot:
26
+ * `lastFatalError`, so a crash loop keeps its alert, and how the last worker that ran stopped.
27
+ * A `lastShutdown` written by an earlier worker than the file's own means the last one stopped
28
+ * without draining, unless the file's own worker recorded its failed start: it never ran, so the
29
+ * carried record holds. Tolerant like every reader: a missing or unreadable file yields neither.
30
+ */
31
+ export declare function readCarriedOverStatus(taskDir: string): Promise<Pick<WorkerStatusReport, 'lastFatalError' | 'lastShutdown'>>;
32
+ /**
33
+ * `startedAt` of the worker that wrote the status file, or `undefined` when there is no file or
34
+ * it names no worker. Throws on any other read error: a caller deciding whether the file is its
35
+ * own must not take an unreadable one for an absent one.
36
+ */
37
+ export declare function readWorkerStatusStartedAt(taskDir: string): Promise<string | undefined>;
38
+ /** A startup failure the worker recorded; see {@link readWorkerStartupFailure}. */
39
+ export interface WorkerStartupFailure {
40
+ /** Already redacted by the worker. */
41
+ message: string;
42
+ at: string;
43
+ /**
44
+ * The attempt that wrote the file is the one that failed. False while a newer worker carries
45
+ * an older failure forward and is starting again.
46
+ */
47
+ current: boolean;
48
+ }
49
+ /**
50
+ * The startup failure in `{taskDir}/worker-status.json`, for a request that found no remote to
51
+ * clone. A failure is current when the worker that recorded it is the one the snapshot
52
+ * describes: a worker that starts again writes its own `startedAt` and carries the previous
53
+ * failure forward, so a retry in progress (a first clone can take minutes) is not reported as a
54
+ * dead worker. Compared by identity, not by clock, since the two workers can run on different
55
+ * hosts. Tolerant like every reader: a missing or unreadable file yields none.
56
+ */
57
+ export declare function readWorkerStartupFailure(taskDir: string): Promise<WorkerStartupFailure | undefined>;
24
58
  /**
25
59
  * Write the worker's status report to `{taskDir}/worker-status.json`.
26
60
  *
@@ -32,12 +66,4 @@ export declare const WORKER_STATUS_FILE = "worker-status.json";
32
66
  * `processTaskQueue`, `start()`); any that don't fall back on scheduleLoop's
33
67
  * per-cycle catch, which logs and continues.
34
68
  */
35
- /**
36
- * The `lastFatalError` in the current status file, or undefined when the file is
37
- * missing, unreadable or has none. Tolerant for the same reason readers are.
38
- * Its one caller carries the value into a full snapshot right after taking the
39
- * worker lock; a write racing in between only keeps or drops one crash alert
40
- * until the next write.
41
- */
42
- export declare function readLastFatalError(taskDir: string): Promise<WorkerStatusReport['lastFatalError']>;
43
69
  export declare function writeWorkerStatus(taskDir: string, report: WorkerStatusReport): Promise<void>;
@@ -11,7 +11,7 @@
11
11
  * (utils/atomic-write.ts), never a read-modify-write, so whichever write lands
12
12
  * last is one writer's complete, self-consistent snapshot. That covers the one
13
13
  * window where two holders overlap -- after a lock compromise the old holder's
14
- * `stop()` drains for up to `taskTimeoutMs` while a new holder is already
14
+ * `stop()` aborts its in-flight work while a new holder may already be
15
15
  * running against the same workspace.
16
16
  *
17
17
  * Readers are stale-tolerant by design: this is a liveness signal, not
@@ -22,7 +22,90 @@
22
22
  import fs from 'node:fs/promises';
23
23
  import path from 'node:path';
24
24
  import { atomicWriteFile } from '../utils/atomic-write.js';
25
+ import { isNotFoundError } from '../utils/error.js';
25
26
  export const WORKER_STATUS_FILE = 'worker-status.json';
27
+ /**
28
+ * What a new worker carries from the previous status file into its first snapshot:
29
+ * `lastFatalError`, so a crash loop keeps its alert, and how the last worker that ran stopped.
30
+ * A `lastShutdown` written by an earlier worker than the file's own means the last one stopped
31
+ * without draining, unless the file's own worker recorded its failed start: it never ran, so the
32
+ * carried record holds. Tolerant like every reader: a missing or unreadable file yields neither.
33
+ */
34
+ export async function readCarriedOverStatus(taskDir) {
35
+ let previous;
36
+ try {
37
+ const content = await fs.readFile(path.join(taskDir, WORKER_STATUS_FILE), 'utf-8');
38
+ previous = JSON.parse(content);
39
+ }
40
+ catch {
41
+ return {};
42
+ }
43
+ const { lastFatalError, startedAt, updatedAt } = previous;
44
+ let { lastShutdown } = previous;
45
+ const failedStart = lastFatalError?.phase === 'startup' && lastFatalError.workerStartedAt === startedAt;
46
+ if (startedAt && updatedAt && !failedStart && lastShutdown?.workerStartedAt !== startedAt) {
47
+ lastShutdown = {
48
+ reason: 'stopped without draining',
49
+ at: updatedAt,
50
+ workerStartedAt: startedAt,
51
+ outcome: 'not-drained',
52
+ };
53
+ }
54
+ return { lastFatalError, lastShutdown };
55
+ }
56
+ /**
57
+ * `startedAt` of the worker that wrote the status file, or `undefined` when there is no file or
58
+ * it names no worker. Throws on any other read error: a caller deciding whether the file is its
59
+ * own must not take an unreadable one for an absent one.
60
+ */
61
+ export async function readWorkerStatusStartedAt(taskDir) {
62
+ let content;
63
+ try {
64
+ content = await fs.readFile(path.join(taskDir, WORKER_STATUS_FILE), 'utf-8');
65
+ }
66
+ catch (err) {
67
+ if (isNotFoundError(err))
68
+ return undefined;
69
+ throw err;
70
+ }
71
+ try {
72
+ const report = JSON.parse(content);
73
+ return typeof report.startedAt === 'string' ? report.startedAt : undefined;
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ }
79
+ /**
80
+ * The startup failure in `{taskDir}/worker-status.json`, for a request that found no remote to
81
+ * clone. A failure is current when the worker that recorded it is the one the snapshot
82
+ * describes: a worker that starts again writes its own `startedAt` and carries the previous
83
+ * failure forward, so a retry in progress (a first clone can take minutes) is not reported as a
84
+ * dead worker. Compared by identity, not by clock, since the two workers can run on different
85
+ * hosts. Tolerant like every reader: a missing or unreadable file yields none.
86
+ */
87
+ export async function readWorkerStartupFailure(taskDir) {
88
+ let report;
89
+ try {
90
+ report = JSON.parse(await fs.readFile(path.join(taskDir, WORKER_STATUS_FILE), 'utf-8'));
91
+ }
92
+ catch {
93
+ return undefined;
94
+ }
95
+ const fatal = report.lastFatalError;
96
+ if (fatal?.phase !== 'startup' || typeof fatal.message !== 'string')
97
+ return undefined;
98
+ let current;
99
+ if (fatal.workerStartedAt !== undefined) {
100
+ current = fatal.workerStartedAt === report.startedAt;
101
+ }
102
+ else {
103
+ // A file from a worker that does not link the two: compare clocks instead.
104
+ const startedAt = report.startedAt ? Date.parse(report.startedAt) : NaN;
105
+ current = Number.isNaN(startedAt) || !(Date.parse(fatal.at) < startedAt);
106
+ }
107
+ return { message: fatal.message, at: fatal.at, current };
108
+ }
26
109
  /**
27
110
  * Write the worker's status report to `{taskDir}/worker-status.json`.
28
111
  *
@@ -34,22 +117,6 @@ export const WORKER_STATUS_FILE = 'worker-status.json';
34
117
  * `processTaskQueue`, `start()`); any that don't fall back on scheduleLoop's
35
118
  * per-cycle catch, which logs and continues.
36
119
  */
37
- /**
38
- * The `lastFatalError` in the current status file, or undefined when the file is
39
- * missing, unreadable or has none. Tolerant for the same reason readers are.
40
- * Its one caller carries the value into a full snapshot right after taking the
41
- * worker lock; a write racing in between only keeps or drops one crash alert
42
- * until the next write.
43
- */
44
- export async function readLastFatalError(taskDir) {
45
- try {
46
- const content = await fs.readFile(path.join(taskDir, WORKER_STATUS_FILE), 'utf-8');
47
- return JSON.parse(content).lastFatalError;
48
- }
49
- catch {
50
- return undefined;
51
- }
52
- }
53
120
  export async function writeWorkerStatus(taskDir, report) {
54
121
  const filePath = path.join(taskDir, WORKER_STATUS_FILE);
55
122
  const stamped = { ...report, updatedAt: new Date().toISOString() };
package/dist/types.d.ts CHANGED
@@ -33,11 +33,23 @@ export interface BranchMetadata {
33
33
  baseBranch?: string;
34
34
  pullRequestUrl?: string;
35
35
  pullRequestNumber?: number;
36
+ /**
37
+ * ISO timestamp of the branch's latest submit. A queued PR task carries it, so the worker's
38
+ * failure handling acts only on the submit that queued it.
39
+ */
40
+ submittedAt?: string;
41
+ /**
42
+ * ISO timestamp of the CMS's latest push of this branch to GitHub: a worker push GitHub
43
+ * accepted, or a direct-path submit. Branch delete takes it as
44
+ * proof that a same-named GitHub branch is this branch's, since a submit whose PR GitHub refused
45
+ * leaves no PR number. Absent on branches the CMS never pushed there.
46
+ */
47
+ pushedToGitHubAt?: string;
36
48
  /** Sync status for async GitHub operations (used when Lambda has no internet) */
37
49
  syncStatus?: SyncStatus;
38
- /** Whether this branch has unresolved merge conflicts with the base branch */
50
+ /** Whether any rebase of this branch kept its version of an entry base also changed */
39
51
  conflictStatus?: ConflictStatus;
40
- /** ContentIds of entries where --theirs was applied during rebase; cleared on clean rebase */
52
+ /** ContentIds of the entries where a rebase kept the branch's version; cleared when it merges */
41
53
  conflictFiles?: ContentId[];
42
54
  /**
43
55
  * Lifecycle state of the branch's PR as last observed by the worker's merge-poll
@@ -78,14 +90,21 @@ export interface BranchMetadata {
78
90
  */
79
91
  historyRewrittenFrom?: string;
80
92
  /**
81
- * Short, sanitized reason the worker's last GitHub sync task failed permanently,
82
- * set alongside `syncStatus: 'sync-failed'` by
83
- * CmsWorker.updateBranchMetadataOnFailure -- e.g. a non-fast-forward push
84
- * rejection naming the branch. Absent until a task has failed permanently, and
85
- * reset to undefined by the next successful sync task, so a stale reason never
86
- * survives a later successful push.
93
+ * Short, sanitized reason the last GitHub sync failed, set alongside
94
+ * `syncStatus: 'sync-failed'` by the worker's updateBranchMetadataOnFailure or a
95
+ * direct-path submit -- e.g. a non-fast-forward push rejection naming the branch.
96
+ * Shown on the branch's row in the editor. Reset to undefined by the next submit
97
+ * and the next successful sync task, so a stale reason never outlives it.
87
98
  */
88
99
  syncFailureReason?: string;
100
+ /**
101
+ * Every user whose successful change to the branch's working tree is recorded, in first-save
102
+ * order (recordBranchEditor in branch-metadata.ts). Never reset, because each submit rebuilds
103
+ * the whole PR body and credits them all. User ids only: names are looked up at submit.
104
+ */
105
+ editors?: CanopyUserId[];
106
+ /** The `editors` who saved since the last submit commit; the next one's trailers name them. */
107
+ uncommittedEditors?: CanopyUserId[];
89
108
  }
90
109
  export interface BranchPaths {
91
110
  /** Root where all branches live (e.g., /mnt/efs/site, ~/.canopycms/branches) */
@@ -127,6 +146,29 @@ export interface BuildIdentity {
127
146
  /** Adopter source revision the deployed image was built from (CANOPY_SOURCE_SHA); absent when unset. */
128
147
  sourceRevision?: string;
129
148
  }
149
+ /**
150
+ * The worker holding the base branch at its current tip because the incoming tip references
151
+ * entry schemas the serving editor does not define (worker/schema-gate.ts).
152
+ */
153
+ export interface BaseSchemaHold {
154
+ /** The earliest of `firstSeen`. */
155
+ since: string;
156
+ /** When each missing schema was first seen missing; the earliest bounds the hold. */
157
+ firstSeen: Record<string, string>;
158
+ /** GitHub's base tip the worker is not advancing to. */
159
+ incomingSha: string;
160
+ /** Schema names the incoming tip references and the serving editor's registry lacks. */
161
+ missingSchemas: string[];
162
+ /** `.collection.json` files referencing them, repo-relative, at most 10. */
163
+ files: string[];
164
+ /** How many files reference them, `files` being the first 10. */
165
+ fileCount: number;
166
+ /** The build that recorded the registry the gate checked against. */
167
+ editorBuild: BuildIdentity;
168
+ editorRecordedAt: string;
169
+ /** Set on the cycle the hold outlived its bound and the worker advanced anyway. */
170
+ expired?: true;
171
+ }
130
172
  /**
131
173
  * Wire shape of the worker's self-reported status file (worker-status.json, under
132
174
  * the task queue dir), written by the CmsWorker daemon. Read-only here: GET
@@ -164,7 +206,8 @@ export interface WorkerStatusReport {
164
206
  baseRefresh?: BaseRefreshReport;
165
207
  /**
166
208
  * Outcome of reconciling remote.git's `refs/heads/*` against GitHub's fetched
167
- * tips, non-destructively (worker/git-sync.ts's `reconcileTrackedBranches`).
209
+ * tips, never moving a head the worker finds editor work in (worker/git-sync.ts's
210
+ * `reconcileTrackedBranches`, worker/stale-mirror.ts).
168
211
  * Optional: a status file written by a worker without that reconcile has no
169
212
  * such key, and readers must tolerate its absence rather than assume every
170
213
  * status file has it.
@@ -180,11 +223,49 @@ export interface WorkerStatusReport {
180
223
  * queued -- expected and self-resolving, unlike `diverged`.
181
224
  */
182
225
  rewritten?: string[];
226
+ /**
227
+ * Optional for the same reason. Heads that diverged while holding no editor
228
+ * work, moved to GitHub's tip, and heads of branches GitHub no longer has,
229
+ * deleted (worker/stale-mirror.ts).
230
+ */
231
+ repointed?: string[];
232
+ pruned?: string[];
183
233
  };
184
234
  };
185
235
  lastFatalError?: {
186
236
  message: string;
187
237
  at: string;
188
238
  phase: 'startup' | 'run';
239
+ /**
240
+ * `startedAt` of the worker that recorded it. Equal to the snapshot's own `startedAt` until a
241
+ * newer worker carries the failure forward. Optional: older workers do not write it.
242
+ */
243
+ workerStartedAt?: string;
189
244
  };
245
+ /**
246
+ * Present while the schema gate holds the base branch, and on the cycle a hold expires. Carried
247
+ * into a restarted worker's first snapshot, so the bound survives restarts.
248
+ */
249
+ baseHold?: BaseSchemaHold;
250
+ /**
251
+ * How the last worker that ran stopped, carried into each new worker's snapshot
252
+ * (task-queue/worker-status.ts's `readCarriedOverStatus`). A worker that fails at startup
253
+ * leaves it alone and records the failure as `lastFatalError`.
254
+ */
255
+ lastShutdown?: WorkerShutdownRecord;
256
+ }
257
+ export interface WorkerShutdownRecord {
258
+ /** Why the worker stopped, as its entrypoint described it (e.g. 'SIGTERM'). */
259
+ reason: string;
260
+ at: string;
261
+ /** The stopped worker's `startedAt`, which ties the record to that worker. */
262
+ workerStartedAt: string;
263
+ /**
264
+ * `deadline`: in-flight work was still running at the drain deadline, and
265
+ * `abandoned` names it. `not-drained`: the worker stopped without recording
266
+ * a shutdown (a crash or a forced kill); `at` is its last status write.
267
+ */
268
+ outcome: 'drained' | 'deadline' | 'not-drained';
269
+ drainMs?: number;
270
+ abandoned?: string[];
190
271
  }
@@ -18,7 +18,7 @@ import { parseTypedFilename } from './utils/typed-filename.js';
18
18
  * `doc.index.{id}.json` inside an md-only collection claims no URL yet still blocks a sibling
19
19
  * write. Closing it means threading each collection's configured formats into a deliberately
20
20
  * schema-free module; tracked in
21
- * .claude/future-tasks/url-collision-guard-superset-of-listing.md.
21
+ * safeinsights/canopycms#878.
22
22
  *
23
23
  * A literal rather than derived: deriving it needs the `ContentFormat` union this module has no
24
24
  * reason to import. `url-collision.test.ts` carries the drift tripwire — a compile-time
@@ -65,7 +65,7 @@ async function findChildCollectionDir(dir, name) {
65
65
  *
66
66
  * The claimant set is nonetheless a strict superset of the listing's in three ways, all
67
67
  * over-blocking and all needing the same fix — the schema this module deliberately does not have
68
- * — so they are one task (.claude/future-tasks/url-collision-guard-superset-of-listing.md):
68
+ * — so they are one task (safeinsights/canopycms#878):
69
69
  *
70
70
  * 1. extension — see `CONTENT_EXTENSIONS` above;
71
71
  * 2. entry TYPE — parsing is structural (no `entryTypes` list), so a file whose type is not in
@@ -155,7 +155,7 @@ export async function findUrlPathClaimant(opts) {
155
155
  // KNOWN DEPENDENCY: this resolves ONE child collection by name, and the index direction above
156
156
  // likewise looks only for a parent ENTRY. Neither considers a second collection sharing the same
157
157
  // name, because nothing stops one being created -- `createCollectionInner` does not check
158
- // sibling names (tracked in collection-sibling-name-uniqueness.md). With two `guides.{id}`
158
+ // sibling names (tracked in safeinsights/canopycms#624). With two `guides.{id}`
159
159
  // directories side by side, an index entry can be written into each: both writes pass this
160
160
  // guard, two entries claim one URL, and only the build guard catches it. Fixing sibling-name
161
161
  // uniqueness closes this without changing anything here, so that task is a prerequisite for
@@ -15,7 +15,7 @@
15
15
  * Scope of the invariant it checks: entries written in the `{type}.{slug}.{id}.{ext}` grammar
16
16
  * with a type their collection declares -- exactly the set `listEntries` can see. A legacy
17
17
  * untyped file (`overview.json`) is invisible to enumeration and is deliberately NOT probed here;
18
- * see `.claude/future-tasks/legacy-untyped-files-url-addressable.md`.
18
+ * see safeinsights/canopycms#728.
19
19
  */
20
20
  import { type RootCollectionConfig } from './config/index.js';
21
21
  import type { ListEntriesItem } from './content-listing.js';
@@ -54,6 +54,13 @@ export interface UrlExclusivityReport {
54
54
  expectedEntryId?: string;
55
55
  actualEntryId?: string;
56
56
  }>;
57
+ /** Published URLs whose read reports a `meta.slug` or `meta.urlPath` other than the listing's. */
58
+ misreported: Array<{
59
+ urlPath: string;
60
+ slug: string;
61
+ readSlug: string;
62
+ readUrlPath: string;
63
+ }>;
57
64
  /** Every adjacent URL probed, sorted. */
58
65
  probes: string[];
59
66
  /** Probes that resolved despite not being published -- the phantom URLs. Must be empty. */
@@ -15,7 +15,7 @@
15
15
  * Scope of the invariant it checks: entries written in the `{type}.{slug}.{id}.{ext}` grammar
16
16
  * with a type their collection declares -- exactly the set `listEntries` can see. A legacy
17
17
  * untyped file (`overview.json`) is invisible to enumeration and is deliberately NOT probed here;
18
- * see `.claude/future-tasks/legacy-untyped-files-url-addressable.md`.
18
+ * see safeinsights/canopycms#728.
19
19
  */
20
20
  import { flattenSchema } from './config/index.js';
21
21
  import { findDuplicateUrlPaths } from './static/index.js';
@@ -80,12 +80,21 @@ export const collectUrlExclusivityReport = async (ctx, schema, contentRoot = 'co
80
80
  const items = await ctx.listEntries();
81
81
  const unresolved = [];
82
82
  const mismatched = [];
83
+ const misreported = [];
83
84
  for (const item of items) {
84
85
  const result = await ctx.readByUrlPath(item.urlPath);
85
86
  if (!result) {
86
87
  unresolved.push(item.urlPath);
87
88
  continue;
88
89
  }
90
+ if (result.meta.slug !== item.slug || result.meta.urlPath !== item.urlPath) {
91
+ misreported.push({
92
+ urlPath: item.urlPath,
93
+ slug: item.slug,
94
+ readSlug: result.meta.slug,
95
+ readUrlPath: result.meta.urlPath,
96
+ });
97
+ }
89
98
  if (result.meta.entryId !== item.entryId) {
90
99
  mismatched.push({
91
100
  urlPath: item.urlPath,
@@ -108,6 +117,7 @@ export const collectUrlExclusivityReport = async (ctx, schema, contentRoot = 'co
108
117
  duplicates: findDuplicateUrlPaths(items),
109
118
  unresolved: unresolved.sort(),
110
119
  mismatched,
120
+ misreported,
111
121
  probes: [...probes].sort(),
112
122
  phantoms: phantoms.sort(),
113
123
  };
package/dist/user.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // Import from client-safe subpaths to avoid pulling in server-only loader code
2
2
  import { RESERVED_GROUPS, stripReservedGroups } from './authorization/helpers.js';
3
+ import { personName } from './auth/person-name.js';
3
4
  /** Pass this constant explicitly wherever anonymous access is intended. */
4
5
  export const ANONYMOUS_USER = Object.freeze({
5
6
  type: 'anonymous',
@@ -37,7 +38,7 @@ export function authResultToCanopyUser(authResult, bootstrapAdminIds, internalGr
37
38
  type: 'authenticated',
38
39
  userId: authResult.user.userId,
39
40
  email: authResult.user.email,
40
- name: authResult.user.name,
41
+ name: personName(authResult.user.userId, authResult.user.name),
41
42
  avatarUrl: authResult.user.avatarUrl,
42
43
  groups,
43
44
  };
@@ -8,8 +8,9 @@ export async function atomicWriteFile(filePath, content) {
8
8
  const dir = path.dirname(filePath);
9
9
  await fs.mkdir(dir, { recursive: true });
10
10
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
11
- await fs.writeFile(tempPath, content, typeof content === 'string' ? 'utf-8' : undefined);
11
+ // A temp file a failed write leaves behind would sit in the working tree for a commit to pick up.
12
12
  try {
13
+ await fs.writeFile(tempPath, content, typeof content === 'string' ? 'utf-8' : undefined);
13
14
  await fs.rename(tempPath, filePath);
14
15
  }
15
16
  catch (err) {
@@ -0,0 +1,13 @@
1
+ import type { CanopyConfig } from '../config/index.js';
2
+ /** The base branch is unknown, and prod never assumes one. */
3
+ export declare class BaseBranchUnresolvedError extends Error {
4
+ constructor(detail: string);
5
+ }
6
+ /**
7
+ * `config`'s resolved base branch. Prod resolves an unset one from the remote's HEAD
8
+ * (createCanopyServices, then resolvePendingBaseBranch per request), so an unset value in prod
9
+ * means that has not happened yet, or the path skipped it, and it throws rather than guessing
10
+ * 'main'. In the other modes a config that never went through
11
+ * services keeps the 'main' default; services itself resolves dev's from git HEAD.
12
+ */
13
+ export declare function baseBranchOf(config: Pick<CanopyConfig, 'mode' | 'defaultBaseBranch'>): string;