canopycms 0.0.67-int.90 → 0.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (539) hide show
  1. package/README.md +30 -3
  2. package/dist/ai/generate.js +1 -27
  3. package/dist/ai/handler.d.ts +0 -4
  4. package/dist/ai/handler.js +0 -11
  5. package/dist/ai/index.d.ts +1 -8
  6. package/dist/ai/index.js +1 -8
  7. package/dist/ai/json-to-markdown.js +15 -58
  8. package/dist/ai/resolve-branch.d.ts +2 -7
  9. package/dist/ai/resolve-branch.js +2 -8
  10. package/dist/ai/strip-mdx.d.ts +5 -13
  11. package/dist/ai/strip-mdx.js +5 -17
  12. package/dist/ai/to-plain-text.d.ts +10 -28
  13. package/dist/ai/to-plain-text.js +10 -28
  14. package/dist/ai/transform-components.d.ts +2 -4
  15. package/dist/ai/transform-components.js +8 -19
  16. package/dist/ai/types.d.ts +5 -10
  17. package/dist/api/admin-branch-health.d.ts +4 -12
  18. package/dist/api/admin-branch-health.js +26 -66
  19. package/dist/api/admin.d.ts +4 -12
  20. package/dist/api/admin.js +10 -26
  21. package/dist/api/assets.d.ts +0 -25
  22. package/dist/api/assets.js +50 -91
  23. package/dist/api/branch-merge.d.ts +1 -1
  24. package/dist/api/branch-merge.js +0 -2
  25. package/dist/api/branch-review.d.ts +0 -8
  26. package/dist/api/branch-review.js +0 -13
  27. package/dist/api/branch-status.d.ts +0 -3
  28. package/dist/api/branch-status.js +8 -33
  29. package/dist/api/branch-withdraw.d.ts +0 -1
  30. package/dist/api/branch-withdraw.js +6 -9
  31. package/dist/api/branch.d.ts +7 -12
  32. package/dist/api/branch.js +7 -44
  33. package/dist/api/client.d.ts +71 -224
  34. package/dist/api/client.js +82 -233
  35. package/dist/api/comments.d.ts +2 -4
  36. package/dist/api/comments.js +0 -17
  37. package/dist/api/content.d.ts +0 -6
  38. package/dist/api/content.js +66 -142
  39. package/dist/api/entries-constants.d.ts +5 -9
  40. package/dist/api/entries-constants.js +5 -9
  41. package/dist/api/entries.d.ts +8 -13
  42. package/dist/api/entries.js +20 -51
  43. package/dist/api/github-sync.d.ts +2 -4
  44. package/dist/api/github-sync.js +4 -8
  45. package/dist/api/groups.d.ts +3 -0
  46. package/dist/api/groups.js +8 -21
  47. package/dist/api/guards.d.ts +4 -16
  48. package/dist/api/guards.js +3 -22
  49. package/dist/api/index.d.ts +4 -4
  50. package/dist/api/permissions.d.ts +2 -0
  51. package/dist/api/permissions.js +18 -40
  52. package/dist/api/reference-options.d.ts +0 -1
  53. package/dist/api/reference-options.js +3 -11
  54. package/dist/api/request-body-hash.d.ts +12 -23
  55. package/dist/api/request-body-hash.js +12 -23
  56. package/dist/api/resolve-references.d.ts +0 -1
  57. package/dist/api/resolve-references.js +13 -28
  58. package/dist/api/route-builder.d.ts +3 -7
  59. package/dist/api/route-builder.js +1 -1
  60. package/dist/api/routes.d.ts +12 -0
  61. package/dist/api/routes.js +47 -0
  62. package/dist/api/schema.d.ts +23 -33
  63. package/dist/api/schema.js +20 -74
  64. package/dist/api/settings-helpers.js +4 -5
  65. package/dist/api/types.d.ts +0 -5
  66. package/dist/api/user.js +0 -5
  67. package/dist/api/validators.d.ts +14 -52
  68. package/dist/api/validators.js +14 -52
  69. package/dist/assets/asset-prefixes.d.ts +4 -8
  70. package/dist/assets/asset-prefixes.js +4 -8
  71. package/dist/assets/asset-src.d.ts +5 -22
  72. package/dist/assets/asset-src.js +5 -21
  73. package/dist/assets/factory.d.ts +1 -2
  74. package/dist/assets/factory.js +1 -2
  75. package/dist/assets/finalize.d.ts +3 -1
  76. package/dist/assets/finalize.js +3 -1
  77. package/dist/assets/index.d.ts +3 -3
  78. package/dist/assets/index.js +1 -1
  79. package/dist/assets/keys.d.ts +5 -3
  80. package/dist/assets/keys.js +6 -3
  81. package/dist/assets/pipeline.d.ts +3 -2
  82. package/dist/assets/pipeline.js +22 -56
  83. package/dist/assets/sharp-loader.d.ts +2 -10
  84. package/dist/assets/sharp-loader.js +2 -10
  85. package/dist/assets/store-local.d.ts +1 -2
  86. package/dist/assets/store-local.js +1 -2
  87. package/dist/assets/store-s3.d.ts +3 -4
  88. package/dist/assets/svg-sanitizer.d.ts +6 -14
  89. package/dist/assets/svg-sanitizer.js +6 -14
  90. package/dist/assets/transform-directives.d.ts +3 -4
  91. package/dist/assets/transform-directives.js +1 -1
  92. package/dist/assets/transform.d.ts +19 -30
  93. package/dist/assets/transform.js +18 -31
  94. package/dist/auth/cache.d.ts +2 -3
  95. package/dist/auth/cache.js +2 -3
  96. package/dist/auth/caching-auth-plugin.d.ts +11 -22
  97. package/dist/auth/caching-auth-plugin.js +12 -14
  98. package/dist/auth/context-helpers.d.ts +6 -20
  99. package/dist/auth/context-helpers.js +5 -17
  100. package/dist/auth/file-based-auth-cache.d.ts +10 -21
  101. package/dist/auth/file-based-auth-cache.js +23 -51
  102. package/dist/auth/plugin.d.ts +29 -61
  103. package/dist/auth/plugin.js +9 -13
  104. package/dist/auth/types.d.ts +11 -20
  105. package/dist/authorization/branch.d.ts +30 -49
  106. package/dist/authorization/branch.js +16 -42
  107. package/dist/authorization/content.d.ts +11 -40
  108. package/dist/authorization/content.js +13 -45
  109. package/dist/authorization/groups/index.d.ts +0 -3
  110. package/dist/authorization/groups/index.js +0 -3
  111. package/dist/authorization/groups/loader.d.ts +14 -28
  112. package/dist/authorization/groups/loader.js +14 -38
  113. package/dist/authorization/groups/schema.d.ts +0 -6
  114. package/dist/authorization/groups/schema.js +4 -7
  115. package/dist/authorization/helpers.d.ts +9 -28
  116. package/dist/authorization/helpers.js +9 -28
  117. package/dist/authorization/index.d.ts +3 -25
  118. package/dist/authorization/index.js +3 -35
  119. package/dist/authorization/path.d.ts +6 -13
  120. package/dist/authorization/path.js +8 -25
  121. package/dist/authorization/permissions/index.d.ts +0 -3
  122. package/dist/authorization/permissions/index.js +0 -3
  123. package/dist/authorization/permissions/loader.d.ts +14 -27
  124. package/dist/authorization/permissions/loader.js +14 -32
  125. package/dist/authorization/permissions/schema.d.ts +1 -6
  126. package/dist/authorization/permissions/schema.js +6 -13
  127. package/dist/authorization/protected-branch.d.ts +36 -65
  128. package/dist/authorization/protected-branch.js +29 -41
  129. package/dist/authorization/settings-file-store.d.ts +38 -81
  130. package/dist/authorization/settings-file-store.js +38 -79
  131. package/dist/authorization/types.d.ts +4 -26
  132. package/dist/authorization/types.js +0 -6
  133. package/dist/authorization/validation.d.ts +1 -23
  134. package/dist/authorization/validation.js +1 -24
  135. package/dist/branch-health.d.ts +39 -67
  136. package/dist/branch-health.js +27 -43
  137. package/dist/branch-metadata-file.d.ts +16 -32
  138. package/dist/branch-metadata-file.js +13 -26
  139. package/dist/branch-metadata.d.ts +49 -80
  140. package/dist/branch-metadata.js +57 -87
  141. package/dist/branch-registry.d.ts +26 -64
  142. package/dist/branch-registry.js +44 -93
  143. package/dist/branch-schema-cache.d.ts +34 -124
  144. package/dist/branch-schema-cache.js +49 -155
  145. package/dist/branch-workspace.d.ts +4 -4
  146. package/dist/branch-workspace.js +15 -19
  147. package/dist/build/generate-ai-content.d.ts +1 -0
  148. package/dist/build/generate-ai-content.js +12 -25
  149. package/dist/build-canopy.d.ts +17 -27
  150. package/dist/build-canopy.js +16 -23
  151. package/dist/build-mode.d.ts +25 -27
  152. package/dist/build-mode.js +25 -29
  153. package/dist/cli/cli.d.ts +15 -26
  154. package/dist/cli/cli.js +601 -978
  155. package/dist/cli/generate-ai-content.js +351 -628
  156. package/dist/cli/github-app-manifest.d.ts +84 -0
  157. package/dist/cli/github-app-manifest.js +161 -0
  158. package/dist/cli/init-github-app.d.ts +110 -444
  159. package/dist/cli/init-github-app.js +236 -773
  160. package/dist/cli/init.js +39 -80
  161. package/dist/cli/migrate.d.ts +13 -10
  162. package/dist/cli/migrate.js +22 -30
  163. package/dist/cli/project-detect.d.ts +17 -31
  164. package/dist/cli/project-detect.js +31 -42
  165. package/dist/cli/project-root.d.ts +4 -11
  166. package/dist/cli/project-root.js +4 -11
  167. package/dist/cli/prompt.d.ts +10 -0
  168. package/dist/cli/prompt.js +67 -0
  169. package/dist/cli/sync.d.ts +0 -8
  170. package/dist/cli/sync.js +8 -28
  171. package/dist/cli/template-files/middleware.ts.template +15 -29
  172. package/dist/cli/templates.d.ts +2 -12
  173. package/dist/cli/templates.js +4 -4
  174. package/dist/client.d.ts +1 -1
  175. package/dist/comment-store.d.ts +32 -53
  176. package/dist/comment-store.js +32 -52
  177. package/dist/config/flatten.d.ts +0 -3
  178. package/dist/config/flatten.js +2 -12
  179. package/dist/config/helpers.js +2 -2
  180. package/dist/config/index.d.ts +2 -2
  181. package/dist/config/index.js +1 -4
  182. package/dist/config/schemas/collection.d.ts +0 -8
  183. package/dist/config/schemas/collection.js +0 -10
  184. package/dist/config/schemas/config.d.ts +6 -78
  185. package/dist/config/schemas/config.js +38 -54
  186. package/dist/config/schemas/field.d.ts +2 -242
  187. package/dist/config/schemas/field.js +9 -20
  188. package/dist/config/schemas/media.js +1 -1
  189. package/dist/config/schemas/permissions.js +0 -2
  190. package/dist/config/schemas/url.d.ts +5 -6
  191. package/dist/config/schemas/url.js +5 -6
  192. package/dist/config/types.d.ts +45 -33
  193. package/dist/config/types.js +0 -1
  194. package/dist/config.d.ts +1 -8
  195. package/dist/config.js +1 -9
  196. package/dist/content-id-index.d.ts +76 -188
  197. package/dist/content-id-index.js +79 -230
  198. package/dist/content-index-generation.d.ts +27 -48
  199. package/dist/content-index-generation.js +35 -84
  200. package/dist/content-index-registry.d.ts +15 -24
  201. package/dist/content-index-registry.js +5 -9
  202. package/dist/content-listing.d.ts +64 -107
  203. package/dist/content-listing.js +68 -118
  204. package/dist/content-reader.d.ts +20 -29
  205. package/dist/content-reader.js +10 -18
  206. package/dist/content-store.d.ts +210 -336
  207. package/dist/content-store.js +413 -657
  208. package/dist/content-tree.d.ts +44 -86
  209. package/dist/content-tree.js +26 -54
  210. package/dist/context.d.ts +67 -94
  211. package/dist/context.js +53 -72
  212. package/dist/dev-content-watcher.d.ts +18 -18
  213. package/dist/dev-content-watcher.js +41 -44
  214. package/dist/editor/BranchManager.d.ts +3 -6
  215. package/dist/editor/BranchManager.js +24 -44
  216. package/dist/editor/CanopyEditor.js +5 -2
  217. package/dist/editor/CommentsPanel.js +0 -2
  218. package/dist/editor/Editor.js +19 -76
  219. package/dist/editor/EditorAuthGate.d.ts +27 -0
  220. package/dist/editor/EditorAuthGate.js +140 -0
  221. package/dist/editor/EntryNavigator.js +0 -20
  222. package/dist/editor/FormRenderer.d.ts +1 -2
  223. package/dist/editor/FormRenderer.js +8 -23
  224. package/dist/editor/admin/SystemHealthPanel.js +20 -37
  225. package/dist/editor/admin/useSystemHealth.d.ts +2 -3
  226. package/dist/editor/admin/useSystemHealth.js +10 -12
  227. package/dist/editor/client-reference-resolver.d.ts +2 -5
  228. package/dist/editor/client-reference-resolver.js +2 -19
  229. package/dist/editor/comments/BranchComments.js +0 -4
  230. package/dist/editor/comments/EntryComments.js +0 -4
  231. package/dist/editor/comments/FieldWrapper.js +0 -4
  232. package/dist/editor/comments/InlineCommentThread.js +0 -2
  233. package/dist/editor/comments/ThreadCarousel.d.ts +0 -58
  234. package/dist/editor/comments/ThreadCarousel.js +0 -12
  235. package/dist/editor/components/EditorFooter.d.ts +0 -8
  236. package/dist/editor/components/EditorFooter.js +0 -5
  237. package/dist/editor/components/EditorHeader.d.ts +0 -31
  238. package/dist/editor/components/EditorHeader.js +21 -71
  239. package/dist/editor/components/EditorSidebar.d.ts +0 -19
  240. package/dist/editor/components/EditorSidebar.js +0 -16
  241. package/dist/editor/components/EntryCreateModal.d.ts +4 -4
  242. package/dist/editor/components/EntryCreateModal.js +7 -22
  243. package/dist/editor/components/RenameEntryModal.js +0 -4
  244. package/dist/editor/components/UserBadge.js +0 -10
  245. package/dist/editor/components/index.js +0 -2
  246. package/dist/editor/context/ApiClientContext.d.ts +8 -7
  247. package/dist/editor/context/ApiClientContext.js +39 -11
  248. package/dist/editor/context/AssetContext.d.ts +7 -17
  249. package/dist/editor/context/AssetContext.js +7 -17
  250. package/dist/editor/context/EditorIdentityContext.d.ts +13 -0
  251. package/dist/editor/context/EditorIdentityContext.js +7 -0
  252. package/dist/editor/context/EditorStateContext.d.ts +10 -41
  253. package/dist/editor/context/EditorStateContext.js +7 -23
  254. package/dist/editor/context/SWRProvider.d.ts +3 -6
  255. package/dist/editor/context/index.d.ts +1 -20
  256. package/dist/editor/context/index.js +1 -20
  257. package/dist/editor/editor-utils.d.ts +8 -47
  258. package/dist/editor/editor-utils.js +19 -72
  259. package/dist/editor/fields/BlockField.d.ts +2 -2
  260. package/dist/editor/fields/BlockField.js +1 -2
  261. package/dist/editor/fields/CodeField.d.ts +0 -1
  262. package/dist/editor/fields/CodeField.js +0 -1
  263. package/dist/editor/fields/DateTimeField.d.ts +15 -22
  264. package/dist/editor/fields/DateTimeField.js +17 -26
  265. package/dist/editor/fields/ImageField.d.ts +2 -2
  266. package/dist/editor/fields/ImageField.js +8 -14
  267. package/dist/editor/fields/InlineGroupField.d.ts +0 -1
  268. package/dist/editor/fields/InlineGroupField.js +0 -1
  269. package/dist/editor/fields/MarkdownField.d.ts +0 -1
  270. package/dist/editor/fields/MarkdownField.js +6 -11
  271. package/dist/editor/fields/MdxImageDialog.d.ts +1 -1
  272. package/dist/editor/fields/MdxImageDialog.js +0 -1
  273. package/dist/editor/fields/NumberField.d.ts +9 -19
  274. package/dist/editor/fields/NumberField.js +11 -22
  275. package/dist/editor/fields/NumberListField.d.ts +7 -10
  276. package/dist/editor/fields/NumberListField.js +7 -10
  277. package/dist/editor/fields/ObjectField.d.ts +5 -8
  278. package/dist/editor/fields/ObjectField.js +0 -1
  279. package/dist/editor/fields/ReferenceField.d.ts +1 -1
  280. package/dist/editor/fields/ReferenceField.js +11 -16
  281. package/dist/editor/fields/SelectField.d.ts +2 -2
  282. package/dist/editor/fields/SelectField.js +0 -1
  283. package/dist/editor/fields/StringListField.d.ts +0 -4
  284. package/dist/editor/fields/StringListField.js +4 -9
  285. package/dist/editor/fields/TextField.d.ts +0 -1
  286. package/dist/editor/fields/TextField.js +0 -1
  287. package/dist/editor/fields/ToggleField.d.ts +0 -1
  288. package/dist/editor/fields/ToggleField.js +0 -1
  289. package/dist/editor/fields/entry-link/EntryLinkContext.js +2 -4
  290. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -3
  291. package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -4
  292. package/dist/editor/group-manager/ExternalGroupsTab.d.ts +0 -3
  293. package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
  294. package/dist/editor/group-manager/GroupCard.d.ts +0 -3
  295. package/dist/editor/group-manager/GroupForm.d.ts +0 -3
  296. package/dist/editor/group-manager/InternalGroupsTab.d.ts +0 -3
  297. package/dist/editor/group-manager/InternalGroupsTab.js +0 -2
  298. package/dist/editor/group-manager/MemberList.d.ts +0 -3
  299. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +0 -3
  300. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -3
  301. package/dist/editor/group-manager/hooks/useGroupState.d.ts +0 -3
  302. package/dist/editor/group-manager/hooks/useGroupState.js +0 -5
  303. package/dist/editor/group-manager/hooks/useUserSearch.d.ts +0 -3
  304. package/dist/editor/group-manager/hooks/useUserSearch.js +0 -3
  305. package/dist/editor/group-manager/index.d.ts +0 -6
  306. package/dist/editor/group-manager/index.js +0 -10
  307. package/dist/editor/group-manager/types.d.ts +2 -5
  308. package/dist/editor/group-manager/types.js +0 -3
  309. package/dist/editor/hooks/index.d.ts +6 -11
  310. package/dist/editor/hooks/index.js +6 -12
  311. package/dist/editor/hooks/useBranchActions.d.ts +0 -14
  312. package/dist/editor/hooks/useBranchActions.js +0 -22
  313. package/dist/editor/hooks/useBranchManager.d.ts +2 -48
  314. package/dist/editor/hooks/useBranchManager.js +4 -64
  315. package/dist/editor/hooks/useBranchesData.d.ts +7 -6
  316. package/dist/editor/hooks/useBranchesData.js +7 -6
  317. package/dist/editor/hooks/useCommentSystem.d.ts +0 -49
  318. package/dist/editor/hooks/useCommentSystem.js +1 -36
  319. package/dist/editor/hooks/useDraftManager.d.ts +2 -35
  320. package/dist/editor/hooks/useDraftManager.js +23 -64
  321. package/dist/editor/hooks/useEditorLayout.d.ts +0 -20
  322. package/dist/editor/hooks/useEditorLayout.js +0 -23
  323. package/dist/editor/hooks/useEntriesData.d.ts +1 -0
  324. package/dist/editor/hooks/useEntriesData.js +2 -3
  325. package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -2
  326. package/dist/editor/hooks/useEntryLinkResolution.js +2 -2
  327. package/dist/editor/hooks/useEntryManager.d.ts +2 -27
  328. package/dist/editor/hooks/useEntryManager.js +29 -78
  329. package/dist/editor/hooks/useGroupManager.d.ts +0 -21
  330. package/dist/editor/hooks/useGroupManager.js +0 -21
  331. package/dist/editor/hooks/usePermissionManager.d.ts +0 -20
  332. package/dist/editor/hooks/usePermissionManager.js +0 -20
  333. package/dist/editor/hooks/useReferenceResolution.d.ts +2 -33
  334. package/dist/editor/hooks/useReferenceResolution.js +1 -48
  335. package/dist/editor/hooks/useSchemaManager.d.ts +8 -27
  336. package/dist/editor/hooks/useSchemaManager.js +0 -19
  337. package/dist/editor/hooks/useUserContext.d.ts +2 -9
  338. package/dist/editor/hooks/useUserContext.js +10 -10
  339. package/dist/editor/hooks/useUserMetadata.d.ts +0 -2
  340. package/dist/editor/hooks/useUserMetadata.js +0 -3
  341. package/dist/editor/media/AssetCard.d.ts +0 -1
  342. package/dist/editor/media/AssetCard.js +1 -2
  343. package/dist/editor/media/CropStep.d.ts +0 -1
  344. package/dist/editor/media/CropStep.js +0 -1
  345. package/dist/editor/media/MediaLibrary.d.ts +1 -2
  346. package/dist/editor/media/MediaLibrary.js +1 -2
  347. package/dist/editor/media/MediaLibraryBody.d.ts +0 -1
  348. package/dist/editor/media/MediaLibraryBody.js +0 -1
  349. package/dist/editor/media/crop-math.d.ts +1 -1
  350. package/dist/editor/media/crop-math.js +1 -1
  351. package/dist/editor/permission-manager/GroupSelector.d.ts +0 -3
  352. package/dist/editor/permission-manager/PermissionEditor.d.ts +0 -4
  353. package/dist/editor/permission-manager/PermissionEditor.js +0 -1
  354. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +0 -7
  355. package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -4
  356. package/dist/editor/permission-manager/PermissionTree.d.ts +0 -4
  357. package/dist/editor/permission-manager/UserSelector.d.ts +0 -3
  358. package/dist/editor/permission-manager/constants.d.ts +0 -3
  359. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +0 -3
  360. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -10
  361. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +0 -14
  362. package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -11
  363. package/dist/editor/permission-manager/index.d.ts +0 -7
  364. package/dist/editor/permission-manager/index.js +0 -16
  365. package/dist/editor/permission-manager/types.d.ts +2 -12
  366. package/dist/editor/permission-manager/types.js +0 -3
  367. package/dist/editor/permission-manager/utils.d.ts +0 -28
  368. package/dist/editor/permission-manager/utils.js +9 -23
  369. package/dist/editor/preview-bridge.d.ts +5 -1
  370. package/dist/editor/preview-bridge.js +4 -4
  371. package/dist/editor/schema-editor/CollectionEditor.d.ts +0 -5
  372. package/dist/editor/schema-editor/CollectionEditor.js +9 -31
  373. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +0 -6
  374. package/dist/editor/schema-editor/EntryTypeEditor.js +3 -24
  375. package/dist/editor/schema-editor/index.d.ts +0 -7
  376. package/dist/editor/schema-editor/index.js +0 -7
  377. package/dist/editor/utils/env.d.ts +3 -18
  378. package/dist/editor/utils/env.js +6 -25
  379. package/dist/entry-link-resolver.d.ts +13 -35
  380. package/dist/entry-link-resolver.js +20 -53
  381. package/dist/entry-schema-registry.d.ts +20 -60
  382. package/dist/entry-schema-registry.js +20 -69
  383. package/dist/entry-schema.d.ts +118 -223
  384. package/dist/entry-schema.js +46 -91
  385. package/dist/git-manager.d.ts +101 -200
  386. package/dist/git-manager.js +211 -384
  387. package/dist/github-service.d.ts +34 -75
  388. package/dist/github-service.js +37 -91
  389. package/dist/http/handler.d.ts +6 -37
  390. package/dist/http/handler.js +65 -120
  391. package/dist/http/router.d.ts +15 -32
  392. package/dist/http/router.js +34 -99
  393. package/dist/http/types.d.ts +15 -40
  394. package/dist/http/types.js +3 -7
  395. package/dist/id.d.ts +4 -10
  396. package/dist/id.js +4 -10
  397. package/dist/index.js +6 -9
  398. package/dist/operating-mode/client-safe-strategy.d.ts +7 -14
  399. package/dist/operating-mode/client-safe-strategy.js +8 -28
  400. package/dist/operating-mode/client-unsafe-strategy.d.ts +6 -16
  401. package/dist/operating-mode/client-unsafe-strategy.js +9 -34
  402. package/dist/operating-mode/client.d.ts +3 -10
  403. package/dist/operating-mode/client.js +3 -8
  404. package/dist/operating-mode/deployment-name-fixtures.d.ts +14 -18
  405. package/dist/operating-mode/deployment-name-fixtures.js +14 -18
  406. package/dist/operating-mode/deployment-name.d.ts +19 -29
  407. package/dist/operating-mode/deployment-name.js +29 -47
  408. package/dist/operating-mode/index.d.ts +4 -25
  409. package/dist/operating-mode/index.js +9 -31
  410. package/dist/operating-mode/mode-env.d.ts +32 -54
  411. package/dist/operating-mode/mode-env.js +36 -58
  412. package/dist/operating-mode/types.d.ts +24 -72
  413. package/dist/operating-mode/types.js +1 -7
  414. package/dist/paths/branch-name.d.ts +30 -40
  415. package/dist/paths/branch-name.js +30 -40
  416. package/dist/paths/branch.d.ts +4 -18
  417. package/dist/paths/branch.js +6 -22
  418. package/dist/paths/index.d.ts +2 -13
  419. package/dist/paths/index.js +3 -23
  420. package/dist/paths/normalize-server.d.ts +3 -15
  421. package/dist/paths/normalize-server.js +3 -15
  422. package/dist/paths/normalize.d.ts +13 -33
  423. package/dist/paths/normalize.js +13 -33
  424. package/dist/paths/resolve.d.ts +4 -17
  425. package/dist/paths/resolve.js +4 -22
  426. package/dist/paths/types.d.ts +12 -34
  427. package/dist/paths/types.js +5 -7
  428. package/dist/paths/validation.d.ts +22 -119
  429. package/dist/paths/validation.js +30 -148
  430. package/dist/reference-resolver.d.ts +0 -13
  431. package/dist/reference-resolver.js +0 -18
  432. package/dist/resolve-canopy-user.d.ts +16 -18
  433. package/dist/resolve-canopy-user.js +17 -26
  434. package/dist/resource-generation.d.ts +16 -25
  435. package/dist/resource-generation.js +34 -97
  436. package/dist/schema/meta-loader.d.ts +2 -14
  437. package/dist/schema/meta-loader.js +15 -64
  438. package/dist/schema/resolver.d.ts +1 -4
  439. package/dist/schema/resolver.js +1 -6
  440. package/dist/schema/schema-store-types.d.ts +0 -12
  441. package/dist/schema/schema-store.d.ts +61 -138
  442. package/dist/schema/schema-store.js +85 -282
  443. package/dist/schema/types.d.ts +0 -4
  444. package/dist/server.d.ts +62 -93
  445. package/dist/server.js +63 -94
  446. package/dist/services.d.ts +23 -47
  447. package/dist/services.js +55 -98
  448. package/dist/settings-workspace.d.ts +17 -30
  449. package/dist/settings-workspace.js +51 -81
  450. package/dist/static/index.d.ts +26 -52
  451. package/dist/static/index.js +33 -67
  452. package/dist/{worker/task-queue.d.ts → task-queue/cms-task-queue.d.ts} +7 -9
  453. package/dist/task-queue/cms-task-queue.js +13 -0
  454. package/dist/task-queue/task-queue-config.d.ts +3 -0
  455. package/dist/{worker → task-queue}/task-queue-config.js +1 -6
  456. package/dist/task-queue/task-queue.d.ts +16 -22
  457. package/dist/task-queue/task-queue.js +20 -44
  458. package/dist/task-queue/types.d.ts +0 -1
  459. package/dist/task-queue/worker-status.d.ts +35 -0
  460. package/dist/task-queue/worker-status.js +40 -0
  461. package/dist/types.d.ts +40 -51
  462. package/dist/url-collision.d.ts +26 -40
  463. package/dist/url-collision.js +47 -63
  464. package/dist/url-exclusivity-fixtures.d.ts +15 -20
  465. package/dist/url-exclusivity-fixtures.js +14 -19
  466. package/dist/url-path-resolver.d.ts +8 -16
  467. package/dist/url-path-resolver.js +19 -29
  468. package/dist/user.d.ts +8 -21
  469. package/dist/user.js +10 -19
  470. package/dist/utils/async-mutex.js +4 -6
  471. package/dist/utils/atomic-write.d.ts +2 -11
  472. package/dist/utils/atomic-write.js +2 -11
  473. package/dist/utils/body-field.d.ts +8 -15
  474. package/dist/utils/body-field.js +8 -15
  475. package/dist/utils/content-serialize.d.ts +18 -24
  476. package/dist/utils/content-serialize.js +109 -160
  477. package/dist/utils/content-write-lock.d.ts +51 -106
  478. package/dist/utils/content-write-lock.js +66 -111
  479. package/dist/utils/debug.d.ts +7 -23
  480. package/dist/utils/debug.js +7 -23
  481. package/dist/utils/entry-url.d.ts +11 -23
  482. package/dist/utils/entry-url.js +11 -26
  483. package/dist/utils/error.d.ts +25 -90
  484. package/dist/utils/error.js +60 -147
  485. package/dist/utils/flatten-group-fields.d.ts +5 -7
  486. package/dist/utils/flatten-group-fields.js +5 -7
  487. package/dist/utils/format.d.ts +1 -5
  488. package/dist/utils/format.js +1 -5
  489. package/dist/utils/fs.d.ts +3 -4
  490. package/dist/utils/fs.js +3 -4
  491. package/dist/utils/git.d.ts +52 -83
  492. package/dist/utils/git.js +79 -125
  493. package/dist/utils/logger.d.ts +35 -50
  494. package/dist/utils/logger.js +35 -52
  495. package/dist/utils/provisioning-lock.d.ts +23 -35
  496. package/dist/utils/provisioning-lock.js +49 -69
  497. package/dist/utils/sanitize-href.d.ts +5 -10
  498. package/dist/utils/sanitize-href.js +8 -18
  499. package/dist/utils/title-field.d.ts +17 -20
  500. package/dist/utils/title-field.js +18 -27
  501. package/dist/utils/typed-filename.d.ts +20 -48
  502. package/dist/utils/typed-filename.js +25 -61
  503. package/dist/utils/url-prefix.d.ts +3 -3
  504. package/dist/utils/url-prefix.js +3 -3
  505. package/dist/validation/block-structural-keys.d.ts +16 -31
  506. package/dist/validation/block-structural-keys.js +19 -29
  507. package/dist/validation/deletion-checker.d.ts +0 -34
  508. package/dist/validation/deletion-checker.js +1 -43
  509. package/dist/validation/entry-link-validator.d.ts +6 -13
  510. package/dist/validation/entry-link-validator.js +4 -13
  511. package/dist/validation/entry-type-reference-validator.d.ts +13 -18
  512. package/dist/validation/entry-type-reference-validator.js +13 -18
  513. package/dist/validation/entry-validator.d.ts +13 -25
  514. package/dist/validation/entry-validator.js +15 -32
  515. package/dist/validation/field-traversal.d.ts +3 -30
  516. package/dist/validation/field-traversal.js +1 -32
  517. package/dist/validation/reference-validator.d.ts +5 -24
  518. package/dist/validation/reference-validator.js +5 -30
  519. package/dist/worker/cms-worker.d.ts +172 -232
  520. package/dist/worker/cms-worker.js +241 -333
  521. package/dist/worker/git-sync.d.ts +32 -133
  522. package/dist/worker/git-sync.js +129 -191
  523. package/dist/worker/github-auth.d.ts +13 -19
  524. package/dist/worker/github-auth.js +13 -19
  525. package/dist/worker/history-rewrite.d.ts +53 -66
  526. package/dist/worker/history-rewrite.js +54 -60
  527. package/dist/worker/log.d.ts +24 -41
  528. package/dist/worker/log.js +25 -42
  529. package/dist/worker/rebase.d.ts +20 -110
  530. package/dist/worker/rebase.js +279 -354
  531. package/dist/worker/task-runner.d.ts +32 -66
  532. package/dist/worker/task-runner.js +126 -162
  533. package/dist/worker/worker-context.d.ts +41 -81
  534. package/package.json +4 -4
  535. package/dist/cli/template-files/middleware-clerk.ts.template +0 -37
  536. package/dist/worker/task-queue-config.d.ts +0 -8
  537. package/dist/worker/task-queue.js +0 -19
  538. package/dist/worker/worker-status.d.ts +0 -47
  539. package/dist/worker/worker-status.js +0 -52
@@ -1,60 +1,32 @@
1
1
  /**
2
- * [SYNC-C1] Cross-host mutual exclusion between the worker's rebase loop and
3
- * content writes against the same branch working tree.
4
- *
5
- * Production runs a Lambda (API) and an EC2 worker over one EFS filesystem.
6
- * `ContentStore`'s write/delete/rename critical sections were guarded only by
7
- * the in-process mutex (`utils/async-mutex`), which does not cross that
8
- * boundary, while the worker's rebase loop (`rebaseOneBranch` in
9
- * worker/rebase.ts) rewrites the very same
10
- * working tree. A write landing after `git rebase` started is destroyed two
11
- * ways -- `git checkout --theirs <file>` overwrites it with the branch's
12
- * committed version (and the rebase then SUCCEEDS, logging nothing), and
13
- * `git rebase --abort` hard-resets the tree. Either way the editor already got
14
- * its 200. This lock is what serializes the two.
15
- *
16
- * **Asymmetric by design.** The worker retries every branch on its next sync
17
- * cycle (~5 minutes) while the editor is a person waiting on a save, so the
18
- * worker yields and the writer waits:
19
- *
20
- * - Worker: {@link tryAcquireContentWriteLock} -- zero retries. On contention
21
- * it skips that branch this cycle (an extension of the existing
22
- * skip-dirty-branches behavior) and picks it up next cycle.
23
- * - Writers: {@link withContentWriteLock} -- a short bounded wait, then a
24
- * retriable failure the API surfaces as a 409.
25
- *
26
- * **Reads never take this lock.** An extra EFS round-trip on every read is not
27
- * an acceptable price, and a read racing a rebase gets a consistent-enough
28
- * older or newer file, never a destroyed one.
29
- *
30
- * ## Why the lock marker lives under `{branchRoot}/.canopy-meta`
31
- *
32
- * proper-lockfile keys its module-level `locks{}` bookkeeping (refresh timer,
33
- * release function) by the **target path** passed to `lock()`, NOT by
34
- * `lockfilePath`. `provisioning-lock.ts` now anchors every lock on its own
35
- * marker path, so the registry key equals the on-disk lock identity and two
36
- * live locks can no longer share a key -- aliasing is structurally impossible
37
- * rather than merely avoided by convention. Keeping the content lock under
38
- * `{branchRoot}/.canopy-meta` still buys:
39
- *
40
- * - a per-branch marker, so one branch's content lock is independent of every
41
- * other branch's and of that branch's provisioning lock; and
42
- * - no deadlock potential: the two locks are never both required, and the only
43
- * possible acquisition order (provision, then write) is consistent.
44
- *
45
- * `.canopy-meta/` is git-excluded in every branch clone (`ensureGitExclude`),
46
- * so the lock directory can never dirty the working tree or be swept into
47
- * `git add .` at publish time.
48
- *
49
- * ## Honest caveat (do not oversell this)
50
- *
51
- * `proper-lockfile` decides a holder is dead by reading the lock directory's
52
- * mtime with `fs.stat`, which on EFS is served through the NFS attribute
53
- * cache. A live holder refreshes every `stale/2`, but a waiter can read a
54
- * cached mtime and conclude the lock is stale when it is not, taking it over.
55
- * The failure mode of a bad takeover is exactly today's behavior (two
56
- * unsynchronized writers), so this is a strict improvement -- not a proof of
57
- * mutual exclusion. See docs/concurrency.md.
2
+ * [SYNC-C1] Cross-host mutual exclusion between the worker's rebase loop and content writes
3
+ * against the same branch working tree. docs/concurrency.md has the layering.
4
+ *
5
+ * Production runs a Lambda (API) and an EC2 worker over one EFS filesystem, so `ContentStore`'s
6
+ * in-process mutex (`utils/async-mutex`) does not cover the worker's rebase (`rebaseOneBranch` in
7
+ * worker/rebase.ts) rewriting the same tree. A write landing after `git rebase` started is
8
+ * destroyed either way -- `git checkout --theirs` overwrites it and the rebase then SUCCEEDS
9
+ * silently, or `git rebase --abort` hard-resets the tree -- and the editor already got its 200.
10
+ *
11
+ * **Asymmetric by design.** The worker retries every branch on its next sync cycle (~5 minutes)
12
+ * while the editor is a person waiting on a save, so the worker yields and the writer waits:
13
+ *
14
+ * - Worker: {@link tryAcquireContentWriteLock} -- zero retries, skips the branch this cycle.
15
+ * - Writers: {@link withContentWriteLock} -- a short bounded wait, then
16
+ * {@link ContentWriteLockBusyError}, which `ContentStore` maps to `BranchSyncingError` and the
17
+ * API to a 409.
18
+ *
19
+ * **Reads never take this lock.** An extra EFS round-trip on every read is not an acceptable
20
+ * price, and a read racing a rebase gets an older or newer file, never a destroyed one.
21
+ *
22
+ * The marker lives under `{branchRoot}/.canopy-meta` and the lock anchors on that marker path like
23
+ * every other lock (see provisioning-lock.ts), so it can never alias the branch's provisioning
24
+ * lock; the two are never both required and the only possible order (provision, then write) is
25
+ * consistent, so they cannot deadlock. `.canopy-meta/` is git-excluded in every branch clone
26
+ * (`ensureGitExclude`), so the lock directory cannot dirty the tree or be swept into `git add .`.
27
+ *
28
+ * Mutual exclusion is not proven: on EFS a stale cached mtime lets a waiter take over a live lock,
29
+ * leaving two unsynchronized writers -- the unlocked behaviour, so still a strict improvement.
58
30
  */
59
31
  import path from 'node:path';
60
32
  import { getErrorMessage, isNodeError } from './error.js';
@@ -63,42 +35,33 @@ import { tryAcquireProvisioningLock } from './provisioning-lock.js';
63
35
  /** Directory the lock marker lives in, relative to the branch root. */
64
36
  const META_DIR = '.canopy-meta';
65
37
  /** On-disk name of the lock marker (a directory, created by mkdir). */
66
- export const CONTENT_WRITE_LOCK_NAME = 'content-write.lock';
38
+ const CONTENT_WRITE_LOCK_NAME = 'content-write.lock';
67
39
  /**
68
40
  * Default bounded wait for a content write.
69
41
  *
70
- * Deliberately short. A rebase holds this lock for a fetch plus a replay plus
71
- * N conflict rounds of git subprocesses on EFS -- far longer than any wait an
72
- * interactive save can absorb, and longer than the API's own latency budget.
73
- * So the wait is not sized to outlast a rebase (nothing reasonable could); it
74
- * is sized to absorb lock handoff and the short holds of other writers, and to
75
- * turn everything longer into a fast, explicit "retry" the editor can act on
76
- * rather than a request that hangs toward the Lambda timeout. The worker
77
- * revisits the branch next cycle, so the blocked state is transient.
78
- *
79
- * This is ALSO the writer-vs-writer budget, not only the rebase one. The lock
80
- * is per-branch-root, so every write to one branch now serializes behind it
81
- * where in-process serialization used to be per-entry -- bounded on Lambda
82
- * (one invocation per container), but real under `next dev` and build-time
83
- * provisioning across worker processes. The case to watch is a write whose
84
- * in-lock path triggers a full `idIndex()` rescan of a large tree over EFS:
85
- * that can plausibly exceed this budget and start 409ing unrelated saves.
86
- * Making it configurable for prod tuning is tracked in
42
+ * Deliberately short. A rebase holds this lock for a fetch, a replay and N conflict rounds of git
43
+ * subprocesses on EFS -- longer than an interactive save can absorb -- so the wait is not sized to
44
+ * outlast one. It absorbs lock handoff and other writers' short holds, and turns everything longer
45
+ * into a fast, explicit "retry" rather than a request hanging toward the Lambda timeout; the
46
+ * worker revisits the branch next cycle, so the blocked state is transient.
47
+ *
48
+ * It is ALSO the writer-vs-writer budget: the lock is per-branch-root, so every write to one
49
+ * branch serializes behind it -- bounded on Lambda (one invocation per container), but real under
50
+ * `next dev` and build-time provisioning across worker processes. The case to watch is a write
51
+ * whose in-lock path triggers a full `idIndex()` rescan of a large tree over EFS, which can exceed
52
+ * this budget and start 409ing unrelated saves. Making it configurable is tracked in
87
53
  * .claude/future-tasks/content-write-lock-tuning-and-granularity.md.
88
54
  */
89
55
  export const DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS = 2000;
90
56
  /**
91
- * Thrown when the bounded wait expires with the branch's content lock still
92
- * held. Retriable -- callers translate this into a 409 with a message that
93
- * says so.
94
- *
95
- * The message says "syncing OR another save" because BOTH produce it. The
96
- * lock is taken by `write`/`delete`/`renameEntry` and by the admin
97
- * repair-content-duplicates action as well as by the worker's rebase, so a
98
- * message naming only the rebase was a confident claim about something that
99
- * may not be happening. `api/content.ts` routes this error ahead of the
100
- * generic conflict specifically so the editor sees this wording, which makes
101
- * the wording load-bearing rather than cosmetic.
57
+ * Thrown when the bounded wait expires with the branch's content lock still held. Retriable --
58
+ * callers translate it into a 409 with a message that says so.
59
+ *
60
+ * The message says "syncing OR another save" because BOTH produce it: the lock is taken by
61
+ * `write`/`delete`/`renameEntry` and the admin repair-content-duplicates action as well as by the
62
+ * worker's rebase, so naming only the rebase would claim more than is known. `api/content.ts`
63
+ * routes this error ahead of the generic conflict so the editor sees this wording, making it
64
+ * load-bearing rather than cosmetic.
102
65
  */
103
66
  export class ContentWriteLockBusyError extends Error {
104
67
  constructor(message = 'This branch is busy (syncing with the base branch, or another save is in flight); the change was not saved. Try again in a moment.') {
@@ -115,11 +78,8 @@ const sleep = (ms) => new Promise((resolve) => {
115
78
  setTimeout(resolve, ms);
116
79
  });
117
80
  /**
118
- * Acquire the branch's content-write lock WITHOUT waiting.
119
- *
120
- * Throws an error with `code === 'ELOCKED'` when a live holder has it. Used by
121
- * the worker's rebase loop (which skips the branch and retries next cycle) and
122
- * by tests standing in for a writer.
81
+ * Acquire the branch's content-write lock WITHOUT waiting, for the worker's rebase loop, which
82
+ * skips the branch and retries next cycle. Throws with `code === 'ELOCKED'` on a live holder.
123
83
  */
124
84
  export function tryAcquireContentWriteLock(branchRoot, onCompromised) {
125
85
  return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME, onCompromised);
@@ -127,14 +87,13 @@ export function tryAcquireContentWriteLock(branchRoot, onCompromised) {
127
87
  /**
128
88
  * Acquire the branch's content-write lock with a short bounded wait.
129
89
  *
130
- * Retries ONLY on genuine contention (`ELOCKED`), the same discipline
131
- * `withOccFileLock` uses: proper-lockfile's own retry loop retries blindly on
132
- * any error, which would burn the whole budget re-hitting e.g. ENOENT after
133
- * the branch directory was deleted out from under the caller.
90
+ * Retries ONLY on genuine contention (`ELOCKED`), the same discipline `withOccFileLock` uses:
91
+ * proper-lockfile's own retry loop retries blindly on any error, burning the whole budget
92
+ * re-hitting e.g. ENOENT after the branch directory was deleted under the caller.
134
93
  *
135
94
  * @throws ContentWriteLockBusyError when the budget expires under contention.
136
95
  */
137
- export async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, onCompromised) {
96
+ async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, onCompromised) {
138
97
  const startedAt = Date.now();
139
98
  for (let attempt = 0;; attempt++) {
140
99
  try {
@@ -146,23 +105,20 @@ export async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTE
146
105
  const remaining = waitMs - (Date.now() - startedAt);
147
106
  if (remaining <= 0)
148
107
  throw new ContentWriteLockBusyError();
149
- // Exponential backoff with jitter, capped so a long budget still polls
150
- // often enough to pick the lock up promptly when the holder releases.
108
+ // Exponential backoff with jitter, capped so a long budget still polls often enough to
109
+ // pick the lock up promptly when the holder releases.
151
110
  const base = Math.min(40 * 2 ** attempt, 250);
152
111
  await sleep(Math.min(base * (0.5 + Math.random()), remaining));
153
112
  }
154
113
  }
155
114
  }
156
- /**
157
- * Run `fn` holding the branch's content-write lock, releasing it in a
158
- * `finally` so a throw can never strand it.
159
- */
115
+ /** Run `fn` holding the branch's content-write lock, releasing in a `finally` so a throw cannot
116
+ * strand it. */
160
117
  export async function withContentWriteLock(branchRoot, fn, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS) {
161
- // A compromise means this write may no longer have been exclusive -- the
162
- // worker's rebase (or another writer) could have been running against the
163
- // same tree. Reporting success would silently accept a possibly-raced write,
164
- // so surface the same retriable "branch busy" error contention already maps
165
- // to (409) and let the caller re-save against the settled tree.
118
+ // A compromise means this write may not have been exclusive -- the worker's rebase, or another
119
+ // writer, could have been running against the same tree. Reporting success would silently
120
+ // accept a possibly-raced write, so surface the same retriable "branch busy" error (409) that
121
+ // contention maps to and let the caller re-save against the settled tree.
166
122
  let compromised = false;
167
123
  const release = await acquireContentWriteLock(branchRoot, waitMs, (err) => {
168
124
  compromised = true;
@@ -176,11 +132,10 @@ export async function withContentWriteLock(branchRoot, fn, waitMs = DEFAULT_CONT
176
132
  await release();
177
133
  }
178
134
  if (compromised) {
179
- // Deliberately NOT the default "was not saved" message: `fn()` completed,
180
- // so the write is on disk. What we lost is the proof it was exclusive, so
181
- // the honest instruction is "reload, then decide" rather than "retry",
182
- // which would resend a now-stale expectedVersion and bounce off the
183
- // caller's own landed write as a phantom editor collision.
135
+ // Deliberately NOT the default "was not saved" message: `fn()` completed, so the write is on
136
+ // disk and only the proof of exclusivity is lost. "Reload, then decide" rather than "retry",
137
+ // which would resend a now-stale expectedVersion and bounce off the caller's own landed write
138
+ // as a phantom editor collision.
184
139
  throw new ContentWriteLockBusyError('This branch was being synced while your change was written, so the change may or may not have been recorded. Reload the entry to see the current state before saving again.');
185
140
  }
186
141
  return result;
@@ -15,40 +15,24 @@ export declare class DebugLogger {
15
15
  constructor(options?: DebugOptions);
16
16
  private shouldLog;
17
17
  /**
18
- * The ISO-8601 timestamp leads the line BARE - not wrapped in brackets as it
19
- * once was. `CANOPYCMS_DEBUG=true` can be set on the EC2 worker, whose stdout
20
- * appends to /var/log/canopy-worker/worker.log; the CloudWatch agent's
21
- * `multi_line_start_pattern` matches the timestamp at the START of a line
22
- * (see worker/log.ts's INVARIANT and cms-service.ts's agent config), so a
23
- * leading `[` meant every debug line was silently folded into the previous
24
- * event instead of starting its own. The bracketed `[prefix:category]` and
25
- * `[level]` fields still follow, so the human-readable shape is unchanged
26
- * apart from those two characters.
18
+ * The ISO-8601 timestamp leads the line BARE, never bracketed. `CANOPYCMS_DEBUG=true` can
19
+ * be set on the EC2 worker, whose stdout appends to /var/log/canopy-worker/worker.log, and
20
+ * the CloudWatch agent's `multi_line_start_pattern` matches a timestamp at the START of a
21
+ * line (see worker/log.ts's INVARIANT and cms-service.ts's agent config): any leading
22
+ * character folds the line into the previous event instead of starting its own.
27
23
  */
28
24
  private formatMessage;
29
25
  debug(category: string, message: string, data?: unknown): void;
30
26
  info(category: string, message: string, data?: unknown): void;
31
27
  warn(category: string, message: string, data?: unknown): void;
32
28
  error(category: string, message: string, data?: unknown): void;
33
- /**
34
- * Start timing an operation
35
- */
36
29
  time(label: string): void;
37
- /**
38
- * End timing an operation and log the duration
39
- */
40
30
  timeEnd(category: string, label: string): number | undefined;
41
- /**
42
- * Wrap an async function with automatic timing
43
- */
44
31
  timed<T>(category: string, label: string, fn: () => Promise<T>): Promise<T>;
45
32
  }
46
- /**
47
- * Create a debug logger instance
48
- */
49
33
  export declare function createDebugLogger(options?: DebugOptions): DebugLogger;
50
34
  /**
51
- * Default logger for test infrastructure (E2E tests)
52
- * Enabled via E2E_DEBUG=true
35
+ * Default logger for E2E test infrastructure.
36
+ * @internal No importer; deletion candidate in knip-no-importer-deletion-candidates.md.
53
37
  */
54
38
  export declare const testLogger: DebugLogger;
@@ -18,15 +18,11 @@ export class DebugLogger {
18
18
  return LOG_LEVELS[level] >= LOG_LEVELS[minLevel];
19
19
  }
20
20
  /**
21
- * The ISO-8601 timestamp leads the line BARE - not wrapped in brackets as it
22
- * once was. `CANOPYCMS_DEBUG=true` can be set on the EC2 worker, whose stdout
23
- * appends to /var/log/canopy-worker/worker.log; the CloudWatch agent's
24
- * `multi_line_start_pattern` matches the timestamp at the START of a line
25
- * (see worker/log.ts's INVARIANT and cms-service.ts's agent config), so a
26
- * leading `[` meant every debug line was silently folded into the previous
27
- * event instead of starting its own. The bracketed `[prefix:category]` and
28
- * `[level]` fields still follow, so the human-readable shape is unchanged
29
- * apart from those two characters.
21
+ * The ISO-8601 timestamp leads the line BARE, never bracketed. `CANOPYCMS_DEBUG=true` can
22
+ * be set on the EC2 worker, whose stdout appends to /var/log/canopy-worker/worker.log, and
23
+ * the CloudWatch agent's `multi_line_start_pattern` matches a timestamp at the START of a
24
+ * line (see worker/log.ts's INVARIANT and cms-service.ts's agent config): any leading
25
+ * character folds the line into the previous event instead of starting its own.
30
26
  */
31
27
  formatMessage(level, category, message) {
32
28
  const timestamp = new Date().toISOString();
@@ -59,15 +55,9 @@ export class DebugLogger {
59
55
  throw new Error(errorMsg);
60
56
  }
61
57
  }
62
- /**
63
- * Start timing an operation
64
- */
65
58
  time(label) {
66
59
  this.timers.set(label, Date.now());
67
60
  }
68
- /**
69
- * End timing an operation and log the duration
70
- */
71
61
  timeEnd(category, label) {
72
62
  const start = this.timers.get(label);
73
63
  if (start === undefined) {
@@ -79,9 +69,6 @@ export class DebugLogger {
79
69
  this.debug(category, `${label} completed`, { durationMs: duration });
80
70
  return duration;
81
71
  }
82
- /**
83
- * Wrap an async function with automatic timing
84
- */
85
72
  async timed(category, label, fn) {
86
73
  this.time(label);
87
74
  try {
@@ -92,15 +79,12 @@ export class DebugLogger {
92
79
  }
93
80
  }
94
81
  }
95
- /**
96
- * Create a debug logger instance
97
- */
98
82
  export function createDebugLogger(options) {
99
83
  return new DebugLogger(options);
100
84
  }
101
85
  /**
102
- * Default logger for test infrastructure (E2E tests)
103
- * Enabled via E2E_DEBUG=true
86
+ * Default logger for E2E test infrastructure.
87
+ * @internal No importer; deletion candidate in knip-no-importer-deletion-candidates.md.
104
88
  */
105
89
  export const testLogger = createDebugLogger({
106
90
  enabled: process.env.E2E_DEBUG === 'true',
@@ -1,33 +1,21 @@
1
1
  /**
2
- * Shared entry URL computation — used by both server and client code.
3
- *
4
- * This module has NO server-only dependencies (no node:fs, etc.)
5
- * so it can be safely imported into browser bundles.
2
+ * Shared entry URL computation, used by server and client code alike. No server-only
3
+ * dependencies, so it is safe to import into browser bundles.
6
4
  */
7
5
  /**
8
- * Is this slug the collection-index slug?
6
+ * Is this slug the collection-index slug? The shared home for that decision — every forward and
7
+ * reverse URL rule routes through it (see utils/AGENTS.md), and it is re-exported from
8
+ * `canopycms/server` for adopters.
9
9
  *
10
- * The shared home for that decision. Six sites route through here: `computeEntryUrl` below,
11
- * `content-tree.ts`'s `defaultBuildPath`, `content-reader.ts`'s `buildEntryPath` and the
12
- * editor's `buildPreviewSrc` (the four forward-rule implementations), the reverse
13
- * `resolveUrlPathCandidates`, and `canopycms-next`'s `collectStaticParams`, which skips index
14
- * entries for a single-segment route. Re-exported from `canopycms/server` for adopters. (`content-tree.ts`'s `indexEntry` lookup still does its own
15
- * `slug === 'index'`; it is fed slugs already lowercased by the listing, so it is safe rather
16
- * than shared.) Slug matching is case-insensitive throughout
17
- * CanopyCMS (`parseSlug` lowercases, and `ContentStore` resolves slugs by a lowercased
18
- * directory scan), so this compares lowercased — a bare `slug === 'index'` is correct only
19
- * for callers whose input was already normalized, and silently wrong for the ones handling
20
- * raw URL segments or on-disk names. `resolveUrlPathCandidates` was exactly that case: its
21
- * strict compare left `/x/Index` resolving the index entry after `/x/index` stopped.
10
+ * The compare is lowercased because slug matching is case-insensitive throughout CanopyCMS
11
+ * (`parseSlug` lowercases; `ContentStore` resolves slugs by a lowercased directory scan): a bare
12
+ * `slug === 'index'` is correct only where the input is already normalized, and silently wrong
13
+ * for callers handling raw URL segments or on-disk names.
22
14
  */
23
15
  export declare function isIndexSlug(slug: string | undefined): boolean;
24
16
  /**
25
- * Compute a URL path from an entry's collection path and slug.
26
- *
27
- * Logic:
28
- * - Strip the contentRoot prefix (e.g., "content/") from the collection path
29
- * - Append the slug (unless it's "index", which collapses to the parent path)
30
- * - Always returns a path starting with "/"
17
+ * Compute a URL path from an entry's collection path and slug: strip the `contentRoot` prefix,
18
+ * append the slug unless it is an index slug, lowercase. Always starts with `/`.
31
19
  *
32
20
  * Examples:
33
21
  * ("content/posts", "hello-world", "content") => "/posts/hello-world"
@@ -1,36 +1,24 @@
1
1
  /**
2
- * Shared entry URL computation — used by both server and client code.
3
- *
4
- * This module has NO server-only dependencies (no node:fs, etc.)
5
- * so it can be safely imported into browser bundles.
2
+ * Shared entry URL computation, used by server and client code alike. No server-only
3
+ * dependencies, so it is safe to import into browser bundles.
6
4
  */
7
5
  import { trimSlashes } from '../paths/normalize.js';
8
6
  /**
9
- * Is this slug the collection-index slug?
7
+ * Is this slug the collection-index slug? The shared home for that decision — every forward and
8
+ * reverse URL rule routes through it (see utils/AGENTS.md), and it is re-exported from
9
+ * `canopycms/server` for adopters.
10
10
  *
11
- * The shared home for that decision. Six sites route through here: `computeEntryUrl` below,
12
- * `content-tree.ts`'s `defaultBuildPath`, `content-reader.ts`'s `buildEntryPath` and the
13
- * editor's `buildPreviewSrc` (the four forward-rule implementations), the reverse
14
- * `resolveUrlPathCandidates`, and `canopycms-next`'s `collectStaticParams`, which skips index
15
- * entries for a single-segment route. Re-exported from `canopycms/server` for adopters. (`content-tree.ts`'s `indexEntry` lookup still does its own
16
- * `slug === 'index'`; it is fed slugs already lowercased by the listing, so it is safe rather
17
- * than shared.) Slug matching is case-insensitive throughout
18
- * CanopyCMS (`parseSlug` lowercases, and `ContentStore` resolves slugs by a lowercased
19
- * directory scan), so this compares lowercased — a bare `slug === 'index'` is correct only
20
- * for callers whose input was already normalized, and silently wrong for the ones handling
21
- * raw URL segments or on-disk names. `resolveUrlPathCandidates` was exactly that case: its
22
- * strict compare left `/x/Index` resolving the index entry after `/x/index` stopped.
11
+ * The compare is lowercased because slug matching is case-insensitive throughout CanopyCMS
12
+ * (`parseSlug` lowercases; `ContentStore` resolves slugs by a lowercased directory scan): a bare
13
+ * `slug === 'index'` is correct only where the input is already normalized, and silently wrong
14
+ * for callers handling raw URL segments or on-disk names.
23
15
  */
24
16
  export function isIndexSlug(slug) {
25
17
  return slug?.toLowerCase() === 'index';
26
18
  }
27
19
  /**
28
- * Compute a URL path from an entry's collection path and slug.
29
- *
30
- * Logic:
31
- * - Strip the contentRoot prefix (e.g., "content/") from the collection path
32
- * - Append the slug (unless it's "index", which collapses to the parent path)
33
- * - Always returns a path starting with "/"
20
+ * Compute a URL path from an entry's collection path and slug: strip the `contentRoot` prefix,
21
+ * append the slug unless it is an index slug, lowercase. Always starts with `/`.
34
22
  *
35
23
  * Examples:
36
24
  * ("content/posts", "hello-world", "content") => "/posts/hello-world"
@@ -39,7 +27,6 @@ export function isIndexSlug(slug) {
39
27
  */
40
28
  export function computeEntryUrl(collection, slug, contentRoot) {
41
29
  const root = trimSlashes(contentRoot);
42
- // Strip contentRoot prefix
43
30
  let stripped = collection;
44
31
  if (root && collection.startsWith(`${root}/`)) {
45
32
  stripped = collection.slice(root.length + 1);
@@ -47,9 +34,7 @@ export function computeEntryUrl(collection, slug, contentRoot) {
47
34
  else if (collection === root) {
48
35
  stripped = '';
49
36
  }
50
- // Build URL segments
51
37
  const segments = stripped.split('/').filter(Boolean);
52
- // Append slug unless it's an index slug (index entries collapse to parent)
53
38
  if (slug && !isIndexSlug(slug)) {
54
39
  segments.push(slug);
55
40
  }
@@ -1,103 +1,38 @@
1
- /**
2
- * Error handling utilities for type-safe error handling.
3
- *
4
- * These utilities help convert `catch (err: unknown)` to usable error information
5
- * without using `any` types.
6
- */
7
- /**
8
- * Extract a message string from an unknown error value.
9
- *
10
- * @param err - The caught error (unknown type)
11
- * @returns A string message suitable for logging or user display
12
- *
13
- * @example
14
- * ```ts
15
- * try {
16
- * await riskyOperation()
17
- * } catch (err: unknown) {
18
- * canopyLogError('Operation failed:', getErrorMessage(err))
19
- * }
20
- * ```
21
- */
1
+ /** Reading a `catch (err: unknown)` value as usable error information, without `any`. */
2
+ /** Message string for an unknown thrown value. */
22
3
  export declare function getErrorMessage(err: unknown): string;
23
4
  /**
24
- * Redact sensitive material from an error message before sending it to API
25
- * clients. Log the ORIGINAL message server-side; send the sanitized one.
5
+ * Redact sensitive material from an error message before it reaches an API client. Log the
6
+ * ORIGINAL message server-side; send the sanitized one.
26
7
  *
27
- * Git/filesystem errors are unbounded (stderr varies by git version, locale,
28
- * and hooks can print anything), so enumerating safe messages is not
29
- * feasible. Instead, redact the known-sensitive SHAPES that can appear in
30
- * any of them:
31
- * - credentials embedded in URLs (`https://x-access-token:tok@github.com/…`)
32
- * - absolute filesystem paths (workspace roots, EFS mounts, home directories)
8
+ * Git/filesystem error text is unbounded (stderr varies by git version and locale, and hooks
9
+ * print anything), so this redacts the known-sensitive SHAPES rather than enumerating safe
10
+ * messages: credentials embedded in URLs (`https://x-access-token:tok@github.com/…`) and
11
+ * absolute filesystem paths (workspace roots, EFS mounts, home directories). Paths under the
12
+ * current working directory are shortened to relative form (CMS-internal layout like
13
+ * `.canopy-dev/remote.git` helps debugging and is not sensitive); absolute paths outside it
14
+ * become `<path>`.
33
15
  *
34
- * Paths under the current working directory are shortened to relative form
35
- * (CMS-internal layout like `.canopy-dev/remote.git` is useful for debugging
36
- * and not sensitive); absolute paths outside it are replaced with `<path>`.
37
- */
38
- /**
39
- * TAG: `[REDACT]`
40
- *
41
- * Grep `[REDACT]` to find every site that persists or surfaces raw error text on
42
- * a path that reaches a browser — worker task errors folded into
43
- * `worker-status.json`, `branch.json` parse failures served by the admin
44
- * branch-health endpoint, rebase-failure messages, and their tests.
45
- *
46
- * Those sites carried the tag `[HIGH-1]`/`[MEDIUM-2]` until 2026-08-23, IDs from
47
- * a review pass whose findings list was never committed — 13 occurrences across
48
- * 6 files resolving to nothing. Renamed rather than deleted because the
49
- * underlying rule is real and genuinely cross-file: a Node error embeds absolute
50
- * paths, and a git remote URL can embed a token, so anything on that path is
51
- * redacted through the function below.
52
- *
53
- * The tag is a grep aid, not the rule. The rule is stated at each site.
16
+ * TAG: `[REDACT]` — grep it for every site that persists or surfaces raw error text on a path
17
+ * reaching a browser (worker task errors folded into `worker-status.json`, `branch.json` parse
18
+ * failures served by the admin branch-health endpoint, rebase-failure messages, and their
19
+ * tests). A Node error embeds absolute paths and a git remote URL can embed a token, so
20
+ * everything on such a path goes through this function. The tag is a grep aid; the rule itself
21
+ * is stated at each site.
54
22
  */
55
23
  export declare function sanitizeErrorMessage(message: string): string;
56
24
  /**
57
- * Redact only credential material from a message, leaving filesystem paths
58
- * intact. For server-side log lines that deliberately keep full path detail
59
- * (server logs only, useful for debugging) but must never persist a live
60
- * token — client-facing messages go through sanitizeErrorMessage instead,
61
- * which calls this and then also redacts paths.
25
+ * Redact only credential material, leaving filesystem paths intact — for server-side log lines
26
+ * that deliberately keep full path detail for debugging but must never persist a live token.
27
+ * Client-facing messages go through `sanitizeErrorMessage` instead, which calls this and then
28
+ * also redacts paths.
62
29
  */
63
30
  export declare function redactCredentials(message: string): string;
64
- /**
65
- * Type guard to check if an error is a Node.js system error with a code property.
66
- *
67
- * @param err - The caught error (unknown type)
68
- * @returns True if the error has a `code` property (like ENOENT, EACCES, etc.)
69
- *
70
- * @example
71
- * ```ts
72
- * try {
73
- * await fs.readFile(path)
74
- * } catch (err: unknown) {
75
- * if (isNodeError(err) && err.code === 'ENOENT') {
76
- * return null // File not found is expected
77
- * }
78
- * throw err
79
- * }
80
- * ```
81
- */
31
+ /** True for a Node system error — an `Error` carrying a `code` (ENOENT, EACCES, …). */
82
32
  export declare function isNodeError(err: unknown): err is NodeJS.ErrnoException;
83
- /**
84
- * Check if an error indicates a "file not found" condition.
85
- *
86
- * @param err - The caught error (unknown type)
87
- * @returns True if the error is ENOENT (file/directory not found)
88
- */
33
+ /** True for ENOENT. */
89
34
  export declare function isNotFoundError(err: unknown): boolean;
90
- /**
91
- * Check if an error indicates a "permission denied" condition.
92
- *
93
- * @param err - The caught error (unknown type)
94
- * @returns True if the error is EACCES (permission denied)
95
- */
35
+ /** True for EACCES. */
96
36
  export declare function isPermissionError(err: unknown): boolean;
97
- /**
98
- * Check if an error indicates a "file already exists" condition.
99
- *
100
- * @param err - The caught error (unknown type)
101
- * @returns True if the error is EEXIST (file already exists)
102
- */
37
+ /** True for EEXIST. */
103
38
  export declare function isFileExistsError(err: unknown): boolean;