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,12 +1,11 @@
1
1
  /**
2
- * Protected base branch predicate.
2
+ * Protected base branch predicate: the single source of truth other modules
3
+ * key off of — do not re-derive the comparison elsewhere.
3
4
  *
4
- * The base branch (the PR base — usually `main`) can never be submitted for
5
- * review (both modes: submitting it would push straight to itself, bypassing
6
- * review) and is read-only in the editor in prod (dev needs the base branch
7
- * editable since the developer always lands there — see ARCHITECTURE.md
8
- * "Protected Base Branch"). This is the single source of truth other modules
9
- * key off of; do not re-derive the comparison elsewhere.
5
+ * The base branch (the PR base, usually `main`) can never be submitted for
6
+ * review in either mode, since submitting it would push straight to itself and
7
+ * bypass review, and is read-only in the editor in prod only — dev needs it
8
+ * editable (see ARCHITECTURE.md "Protected Base Branch").
10
9
  */
11
10
  import type { CanopyConfig } from '../config/index.js';
12
11
  import type { BranchStatus } from '../types.js';
@@ -28,7 +27,7 @@ export interface BranchWriteProtection extends BranchProtection {
28
27
  /**
29
28
  * True when content writes must be rejected, for ANY of three reasons: the
30
29
  * branch is the read-only protected base branch, its workflow status has
31
- * moved past `'editing'` (locked while a reviewer looks at its PR), or its
30
+ * moved past `'editing'` (locked while its PR is under review), or its
32
31
  * status could not be read at all.
33
32
  */
34
33
  writeBlocked: boolean;
@@ -40,74 +39,46 @@ export interface BranchWriteProtection extends BranchProtection {
40
39
  * `writeBlocked` does: `status !== 'editing'` is true when `status` is
41
40
  * `undefined`).
42
41
  *
43
- * DELIBERATELY NOT named `submitBlocked` on this type, even though it
44
- * replaces the naive re-derivation of the same conjunction client-side.
45
- * `BranchProtection.submitBlocked` (the field this interface inherits) means
46
- * ONLY "this is the base branch" -- `api/guards.ts`'s `submittableBranch`
47
- * guard reads exactly that, narrow, meaning, and must keep reading it: the
48
- * guard's whole point is to refuse the base branch regardless of status. If
49
- * this wider, compound answer had reused the same field name on the
50
- * subtype, the two meanings would be one property access apart and
51
- * indistinguishable at every call site -- a future edit anywhere near the
52
- * guard could silently start reading the wide answer where the narrow one
53
- * is required (or vice versa) and no type error would catch it, because
54
- * both are `boolean`. The verbose name is the guard against that: nobody
55
- * writes `submitBlockedIncludingStatus` by accident.
42
+ * NOT named `submitBlocked`: `BranchProtection.submitBlocked`, the field
43
+ * this type inherits, means ONLY "this is the base branch", and
44
+ * `api/guards.ts`'s `submittableBranch` guard depends on that narrow
45
+ * meaning, so the compound answer gets a distinct name.
56
46
  *
57
- * Also worth noting the asymmetry with `writeBlocked` above:
58
- * `writeBlocked` is built from `readOnly` (protected base branch, PROD
59
- * ONLY) plus the status clause, while this is built from `isProtected` /
60
- * `submitBlocked` (protected base branch, BOTH MODES) plus the same status
61
- * clause. They are not two spellings of one rule -- in dev, the base branch
62
- * is writable (`readOnly` is false there, so `writeBlocked` can be false)
63
- * but still never submittable (`isProtected` is true regardless of mode, so
64
- * this stays true). A branch can be `writeBlocked: false,
65
- * submitBlockedIncludingStatus: true` in dev's base branch specifically;
66
- * collapsing the two fields into one would lose that state.
47
+ * It also differs from `writeBlocked`: in dev the base branch is writable
48
+ * but never submittable, so the two fields disagree there.
67
49
  */
68
50
  submitBlockedIncludingStatus: boolean;
69
51
  }
70
52
  /**
71
- * Determine whether `branchName` is the protected base branch for `config`.
53
+ * Whether `branchName` is the protected base branch for `config`.
72
54
  *
73
- * Comparison is sanitization-aware: branch metadata names are sanitized
74
- * (`sanitizeBranchName`) but `config.defaultBaseBranch` holds the raw git
75
- * name, so both sides are sanitized before comparing.
55
+ * The comparison is sanitization-aware: branch metadata names are sanitized but
56
+ * `config.defaultBaseBranch` holds the raw git name, so both sides go through
57
+ * `sanitizeBranchName` first.
76
58
  *
77
- * `recordedBaseBranch` (a branch's own `baseBranch` field, i.e. its recorded
78
- * fork point) is an additional, independent protection clause: a branch whose
79
- * fork point equals its own name IS a base workspace, regardless of what
80
- * `config.defaultBaseBranch` says right now. This matters because in dev
81
- * mode, `config.defaultBaseBranch` tracks live git HEAD (`refreshActiveBranch`)
82
- * and can drift to a different branch after the base workspace was created --
83
- * without this clause, that drift would silently un-protect the branch the
84
- * base workspace was actually forked from. The clause is purely additive: it
85
- * only ever adds protection the config clause didn't already grant, so a
86
- * normal editing branch (`baseBranch !== name`) is never falsely protected.
59
+ * `recordedBaseBranch` (a branch's own recorded fork point) is a second, purely
60
+ * additive clause: a branch whose fork point equals its own name IS a base
61
+ * workspace whatever `config.defaultBaseBranch` says now. In dev that config
62
+ * value tracks live git HEAD (`refreshActiveBranch`) and can drift, and without
63
+ * this clause the drift would silently un-protect the branch the base workspace
64
+ * was forked from; a normal branch (`baseBranch !== name`) is never falsely
65
+ * protected.
87
66
  *
88
- * This answers base-branch questions only (submit/delete/ACL rails). To
89
- * authorize a content write or render a lock, use
90
- * {@link getBranchWriteProtection}, which also accounts for workflow status.
67
+ * Answers base-branch questions only (submit/delete/ACL rails). To authorize a
68
+ * content write or render a lock, use {@link getBranchWriteProtection}, which
69
+ * also accounts for workflow status.
91
70
  */
92
71
  export declare function getBranchProtection(config: Pick<CanopyConfig, 'mode' | 'defaultBaseBranch'>, branchName: string, recordedBaseBranch?: string): BranchProtection;
93
72
  /**
94
- * {@link getBranchProtection} plus the write decision: writes are blocked on the
95
- * read-only base branch, and on any branch whose status has left `'editing'`.
96
- * This is the single expression of the "which statuses lock editing" rule --
97
- * the API guard, the branches-list wire flag, and the editor all read it here.
73
+ * {@link getBranchProtection} plus the write decision: writes are blocked on
74
+ * the read-only base branch and on any branch whose status has left
75
+ * `'editing'` -- the single expression of that rule, read by the API guard, the
76
+ * branches-list wire flag and the editor.
98
77
  *
99
- * `status` is REQUIRED, and deliberately typed to admit `undefined`, because a
100
- * missing status must FAIL CLOSED. `branch.json` is read with a bare
101
- * `JSON.parse(...) as BranchMetadataFile` (branch-metadata.ts) with no schema
102
- * validation, so a hand-repaired or partially-written file can yield
103
- * `status: undefined` at runtime even though the type says otherwise -- and
104
- * malformed branch metadata is a real, handled condition here (see the
105
- * corrupt-metadata quarantine in branch-registry/branch-health). A branch whose
106
- * review state cannot be determined must not be writable.
107
- *
108
- * Requiring the parameter is the point: an optional one would make "caller
109
- * omitted it" and "the file had no status" indistinguishable, and the safe
110
- * answer differs between them. Callers that genuinely don't care about status
111
- * call {@link getBranchProtection} instead and get no `writeBlocked` at all.
78
+ * `status` is REQUIRED and admits `undefined` because a missing status must
79
+ * FAIL CLOSED: `branch.json` is read with a bare `JSON.parse(...) as
80
+ * BranchMetadataFile` (branch-metadata.ts), so it can be absent at runtime.
81
+ * Required, not optional, so "argument omitted" and "file had no status" stay
82
+ * distinguishable -- the safe answer differs.
112
83
  */
113
84
  export declare function getBranchWriteProtection(config: Pick<CanopyConfig, 'mode' | 'defaultBaseBranch'>, branchName: string, recordedBaseBranch: string | undefined, status: BranchStatus | undefined): BranchWriteProtection;
@@ -1,38 +1,34 @@
1
1
  /**
2
- * Protected base branch predicate.
2
+ * Protected base branch predicate: the single source of truth other modules
3
+ * key off of — do not re-derive the comparison elsewhere.
3
4
  *
4
- * The base branch (the PR base — usually `main`) can never be submitted for
5
- * review (both modes: submitting it would push straight to itself, bypassing
6
- * review) and is read-only in the editor in prod (dev needs the base branch
7
- * editable since the developer always lands there — see ARCHITECTURE.md
8
- * "Protected Base Branch"). This is the single source of truth other modules
9
- * key off of; do not re-derive the comparison elsewhere.
5
+ * The base branch (the PR base, usually `main`) can never be submitted for
6
+ * review in either mode, since submitting it would push straight to itself and
7
+ * bypass review, and is read-only in the editor in prod only — dev needs it
8
+ * editable (see ARCHITECTURE.md "Protected Base Branch").
10
9
  */
11
10
  // branch-name, NOT branch: this module is client-reachable (editor bundle →
12
11
  // api/guards.ts → here), and paths/branch.ts drags node:fs into the graph,
13
12
  // which breaks adopters' production `next build`.
14
13
  import { sanitizeBranchName } from '../paths/branch-name.js';
15
14
  /**
16
- * Determine whether `branchName` is the protected base branch for `config`.
15
+ * Whether `branchName` is the protected base branch for `config`.
17
16
  *
18
- * Comparison is sanitization-aware: branch metadata names are sanitized
19
- * (`sanitizeBranchName`) but `config.defaultBaseBranch` holds the raw git
20
- * name, so both sides are sanitized before comparing.
17
+ * The comparison is sanitization-aware: branch metadata names are sanitized but
18
+ * `config.defaultBaseBranch` holds the raw git name, so both sides go through
19
+ * `sanitizeBranchName` first.
21
20
  *
22
- * `recordedBaseBranch` (a branch's own `baseBranch` field, i.e. its recorded
23
- * fork point) is an additional, independent protection clause: a branch whose
24
- * fork point equals its own name IS a base workspace, regardless of what
25
- * `config.defaultBaseBranch` says right now. This matters because in dev
26
- * mode, `config.defaultBaseBranch` tracks live git HEAD (`refreshActiveBranch`)
27
- * and can drift to a different branch after the base workspace was created --
28
- * without this clause, that drift would silently un-protect the branch the
29
- * base workspace was actually forked from. The clause is purely additive: it
30
- * only ever adds protection the config clause didn't already grant, so a
31
- * normal editing branch (`baseBranch !== name`) is never falsely protected.
21
+ * `recordedBaseBranch` (a branch's own recorded fork point) is a second, purely
22
+ * additive clause: a branch whose fork point equals its own name IS a base
23
+ * workspace whatever `config.defaultBaseBranch` says now. In dev that config
24
+ * value tracks live git HEAD (`refreshActiveBranch`) and can drift, and without
25
+ * this clause the drift would silently un-protect the branch the base workspace
26
+ * was forked from; a normal branch (`baseBranch !== name`) is never falsely
27
+ * protected.
32
28
  *
33
- * This answers base-branch questions only (submit/delete/ACL rails). To
34
- * authorize a content write or render a lock, use
35
- * {@link getBranchWriteProtection}, which also accounts for workflow status.
29
+ * Answers base-branch questions only (submit/delete/ACL rails). To authorize a
30
+ * content write or render a lock, use {@link getBranchWriteProtection}, which
31
+ * also accounts for workflow status.
36
32
  */
37
33
  export function getBranchProtection(config, branchName, recordedBaseBranch) {
38
34
  const sanitizedName = sanitizeBranchName(branchName);
@@ -45,24 +41,16 @@ export function getBranchProtection(config, branchName, recordedBaseBranch) {
45
41
  };
46
42
  }
47
43
  /**
48
- * {@link getBranchProtection} plus the write decision: writes are blocked on the
49
- * read-only base branch, and on any branch whose status has left `'editing'`.
50
- * This is the single expression of the "which statuses lock editing" rule --
51
- * the API guard, the branches-list wire flag, and the editor all read it here.
44
+ * {@link getBranchProtection} plus the write decision: writes are blocked on
45
+ * the read-only base branch and on any branch whose status has left
46
+ * `'editing'` -- the single expression of that rule, read by the API guard, the
47
+ * branches-list wire flag and the editor.
52
48
  *
53
- * `status` is REQUIRED, and deliberately typed to admit `undefined`, because a
54
- * missing status must FAIL CLOSED. `branch.json` is read with a bare
55
- * `JSON.parse(...) as BranchMetadataFile` (branch-metadata.ts) with no schema
56
- * validation, so a hand-repaired or partially-written file can yield
57
- * `status: undefined` at runtime even though the type says otherwise -- and
58
- * malformed branch metadata is a real, handled condition here (see the
59
- * corrupt-metadata quarantine in branch-registry/branch-health). A branch whose
60
- * review state cannot be determined must not be writable.
61
- *
62
- * Requiring the parameter is the point: an optional one would make "caller
63
- * omitted it" and "the file had no status" indistinguishable, and the safe
64
- * answer differs between them. Callers that genuinely don't care about status
65
- * call {@link getBranchProtection} instead and get no `writeBlocked` at all.
49
+ * `status` is REQUIRED and admits `undefined` because a missing status must
50
+ * FAIL CLOSED: `branch.json` is read with a bare `JSON.parse(...) as
51
+ * BranchMetadataFile` (branch-metadata.ts), so it can be absent at runtime.
52
+ * Required, not optional, so "argument omitted" and "file had no status" stay
53
+ * distinguishable -- the safe answer differs.
66
54
  */
67
55
  export function getBranchWriteProtection(config, branchName, recordedBaseBranch, status) {
68
56
  const protection = getBranchProtection(config, branchName, recordedBaseBranch);
@@ -1,64 +1,30 @@
1
1
  /**
2
- * Cross-host layered locking for settings JSON files (permissions.json,
3
- * groups.json) in the settings workspace — a single global orphan-git-branch
2
+ * Cross-host layered locking for the settings JSON files (permissions.json,
3
+ * groups.json) in the settings workspace — one global orphan-git-branch
4
4
  * checkout at `{settingsRoot}` shared by every branch (see
5
5
  * `api/settings-helpers.ts`'s `getSettingsBranchContext`).
6
6
  *
7
- * Without this helper, the write path is a classic unprotected TOCTOU: load
8
- * -> compare -> mutate -> write with no lock spanning the cycle, so two warm
9
- * Lambda containers (separate NFS clients on EFS) can each read the same
10
- * pre-mutation file and have the second write silently clobber the first.
11
- * `mutateSettingsJsonFile` closes that with the standard 3-layer recipe from
12
- * docs/concurrency.md, structured identically to {@link CommentStore}'s
13
- * `withMutation` (see comment-store.ts) and `BranchMetadataFileManager.save()`
14
- * (see branch-metadata.ts):
7
+ * `mutateSettingsJsonFile` composes the three layers docs/concurrency.md owns
8
+ * ("The four layers"; the settings-files row of "Who uses what"): `withLock`
9
+ * on the resolved path, then `withOccFileLock`, then `withOccRetry` around
10
+ * `writeOccJsonFile`, which reloads the file on every attempt. Without all
11
+ * three the write path is an unprotected TOCTOU: two warm Lambda containers
12
+ * are separate NFS clients on EFS, so both can read the same pre-mutation file
13
+ * and the second write silently wins.
15
14
  *
16
- * 1. {@link withLock} - an in-process FIFO mutex keyed by the resolved file
17
- * path. Deterministic same-process serialization.
18
- * 2. {@link withOccFileLock} - a server-enforced, cross-process/cross-host
19
- * lock (proper-lockfile, mkdir-based), immune to NFS client dentry/
20
- * attribute caching. This is the actual fix for a lost permission/group
21
- * edit across two warm Lambda containers on EFS.
22
- * 3. {@link withOccRetry} around {@link writeOccJsonFile} - version/writeId
23
- * based optimistic concurrency control, reloading the file fresh on
24
- * EVERY retry attempt. With layers 1-2 in place this is defense-in-depth
25
- * (e.g. a stale process from a rolling deploy writing without the lock),
26
- * not the primary safety mechanism.
15
+ * What that doc and `utils/occ-json-write.ts` do not cover:
27
16
  *
28
- * `writeOccJsonFile`'s managed `version`/`writeId` pair is now THE single
29
- * counter for these files: the old hand-rolled `version: 1` format literal
30
- * and the separate `contentVersion` field are gone. A pre-existing file on
31
- * disk that still says `"version": 1` simply reads as OCC version 1 and
32
- * continues incrementing from there; a leftover `contentVersion` key is
33
- * silently stripped by the (non-strict, extra-keys-stripped) zod parse.
34
- *
35
- * Three caveats specific to these files, on top of the generic guarantee
36
- * documented on `utils/occ-json-write.ts`:
37
- *
38
- * (a) UNLIKE comments.json/branch.json (which never leave the branch
39
- * workspace), permissions.json/groups.json are git-committed on the
40
- * settings orphan branch. `commitSettings()` (api/settings-helpers.ts)
41
- * calls `commitToSettingsBranch`, whose `pullCurrentBranch()` merge runs
42
- * AFTER this helper has released its lock, and that merge can rewrite
43
- * the file's `version` from upstream. So, unlike branch.json, `version`
44
- * here is NOT guaranteed monotonic — it remains a correctness aid (it
45
- * still catches the common same-host and short-window cross-host races)
46
- * but is advisory/defense-in-depth, not a hard guarantee. The LOCKFILE
47
- * (layer 2) is the actual cross-host correctness mechanism.
48
- * (b) The git commit+push deliberately happens OUTSIDE this lock (mirrors
49
- * branch-metadata.ts keeping registry invalidation outside the lock it
50
- * protects): committing/pushing is comparatively slow network/process
51
- * I/O, and holding a mkdir-based lock across it would serialize
52
- * unrelated requests behind that I/O for no correctness gain — only the
53
- * write to the working tree needs the lock.
54
- * (c) Because commit+push is outside the lock, two writers' save-then-commit
55
- * sequences can interleave (A saves+commits+pushes, B's save lands and
56
- * commits before A's push is visible, or similar), and the second
57
- * `git commit` can run against an already-clean tree. Verified benign
58
- * with simple-git 3.36: its task-error detection depends on stderr
59
- * output, and a clean-tree `git commit` exits 1 with "nothing to
60
- * commit" on STDOUT only, so `git.commit()` resolves rather than
61
- * throwing. Re-verify this on any simple-git upgrade.
17
+ * (a) These files are git-committed, so `version` is NOT monotonic here --
18
+ * `commitSettings()` (api/settings-helpers.ts) calls
19
+ * `commitToSettingsBranch`, whose `pullCurrentBranch()` merge runs after
20
+ * this helper releases its lock and can rewrite `version` from upstream.
21
+ * The lockfile (layer 2) is the cross-host guarantee; OCC is defense.
22
+ * (b) The git commit+push deliberately runs OUTSIDE this lock: it is slow
23
+ * network I/O, and only the working-tree write needs the lock.
24
+ * (c) So two writers' save-then-commit sequences can interleave and the second
25
+ * `git commit` can hit an already-clean tree. Benign with simple-git 3.36,
26
+ * whose task-error detection reads stderr while a clean-tree commit exits
27
+ * 1 with "nothing to commit" on stdout only. Re-verify on upgrade.
62
28
  */
63
29
  import { type OccWriteResult } from '../utils/occ-json-write.js';
64
30
  /**
@@ -70,23 +36,19 @@ export declare class SettingsFileConflictError extends Error {
70
36
  constructor(message?: string);
71
37
  }
72
38
  /**
73
- * Thrown by a caller's `mutate` callback when an app-level
74
- * `expectedContentVersion` sent by a client doesn't match the file's current
75
- * `version`. A different concern from {@link SettingsFileConflictError}:
76
- * this is a real edit conflict the user must resolve by reloading, not
77
- * transient lock contention a retry can fix — and indeed it never IS
78
- * retried, since {@link withOccRetry} only recognizes
79
- * {@link OccWriteConflictError} as retryable, so this propagates on the
80
- * very first attempt.
39
+ * Thrown by a caller's `mutate` when a client-supplied
40
+ * `expectedContentVersion` doesn't match the file's current `version`: a real
41
+ * edit conflict the user resolves by reloading, not the transient contention
42
+ * behind {@link SettingsFileConflictError}. It is never retried —
43
+ * {@link withOccRetry} only retries {@link OccWriteConflictError} — so it
44
+ * propagates on the first attempt.
81
45
  */
82
46
  export declare class SettingsVersionConflictError extends Error {
83
47
  constructor(message?: string);
84
48
  }
85
49
  /**
86
- * Structural shape `mutateSettingsJsonFile` needs from a parsed settings
87
- * file: just enough to read the OCC version off it without resorting to
88
- * `any`. Concrete file types (e.g. `PermissionsFile`, `GroupsFile`) satisfy
89
- * this automatically since `version` is optional on both.
50
+ * Just enough of a parsed settings file to read its OCC version without `any`.
51
+ * `PermissionsFile` and `GroupsFile` satisfy it — `version` is optional on both.
90
52
  */
91
53
  interface VersionedSettingsFile {
92
54
  version?: number;
@@ -97,13 +59,11 @@ export interface MutateSettingsFileOptions<TFile extends VersionedSettingsFile>
97
59
  /** JSON.parse + zod-parse the raw file contents. Throws propagate untouched (never retried). */
98
60
  parse: (raw: string) => TFile;
99
61
  /**
100
- * Compute the next payload from the current parsed file (`null` on
101
- * ENOENT) and the version to write it under. Return `null` for a
102
- * deliberate no-op — the write is skipped entirely. Called once per
103
- * retry attempt against freshly reloaded state, so it must be safe to
104
- * call more than once; anything it throws (besides the retried
105
- * `OccWriteConflictError`, which it should never throw itself) propagates
106
- * out of `mutateSettingsJsonFile` untouched.
62
+ * Compute the next payload from the current parsed file (`null` on ENOENT)
63
+ * and the version to write it under; return `null` for a deliberate no-op.
64
+ * Called once per retry attempt against freshly reloaded state, so it must be
65
+ * safe to call more than once. Anything it throws propagates out untouched,
66
+ * and it must never throw `OccWriteConflictError` itself.
107
67
  */
108
68
  mutate: (current: TFile | null, version: number) => Record<string, unknown> | null | Promise<Record<string, unknown> | null>;
109
69
  /** Forwarded to writeOccJsonFile. Pass 0 in tests. */
@@ -112,15 +72,12 @@ export interface MutateSettingsFileOptions<TFile extends VersionedSettingsFile>
112
72
  maxAttempts?: number;
113
73
  }
114
74
  /**
115
- * Run a load -> mutate -> write cycle for a settings JSON file under the
116
- * full lock + OCC-retry stack described in the module doc comment above. A
117
- * conflict that survives every retry surfaces as
75
+ * Run one load -> mutate -> write cycle under the full lock + OCC-retry stack
76
+ * described in the module doc. A conflict surviving every retry surfaces as
118
77
  * {@link SettingsFileConflictError}; everything else — including
119
78
  * {@link SettingsVersionConflictError} thrown by `mutate`, and any
120
- * parse/validation error — propagates untouched.
121
- *
122
- * Returns the `writeOccJsonFile` result, or `null` if `mutate` chose a
123
- * no-op (no write happened).
79
+ * parse/validation error — propagates untouched. Returns the
80
+ * `writeOccJsonFile` result, or `null` if `mutate` chose a no-op.
124
81
  */
125
82
  export declare function mutateSettingsJsonFile<TFile extends VersionedSettingsFile>(opts: MutateSettingsFileOptions<TFile>): Promise<OccWriteResult | null>;
126
83
  export {};
@@ -1,64 +1,30 @@
1
1
  /**
2
- * Cross-host layered locking for settings JSON files (permissions.json,
3
- * groups.json) in the settings workspace — a single global orphan-git-branch
2
+ * Cross-host layered locking for the settings JSON files (permissions.json,
3
+ * groups.json) in the settings workspace — one global orphan-git-branch
4
4
  * checkout at `{settingsRoot}` shared by every branch (see
5
5
  * `api/settings-helpers.ts`'s `getSettingsBranchContext`).
6
6
  *
7
- * Without this helper, the write path is a classic unprotected TOCTOU: load
8
- * -> compare -> mutate -> write with no lock spanning the cycle, so two warm
9
- * Lambda containers (separate NFS clients on EFS) can each read the same
10
- * pre-mutation file and have the second write silently clobber the first.
11
- * `mutateSettingsJsonFile` closes that with the standard 3-layer recipe from
12
- * docs/concurrency.md, structured identically to {@link CommentStore}'s
13
- * `withMutation` (see comment-store.ts) and `BranchMetadataFileManager.save()`
14
- * (see branch-metadata.ts):
7
+ * `mutateSettingsJsonFile` composes the three layers docs/concurrency.md owns
8
+ * ("The four layers"; the settings-files row of "Who uses what"): `withLock`
9
+ * on the resolved path, then `withOccFileLock`, then `withOccRetry` around
10
+ * `writeOccJsonFile`, which reloads the file on every attempt. Without all
11
+ * three the write path is an unprotected TOCTOU: two warm Lambda containers
12
+ * are separate NFS clients on EFS, so both can read the same pre-mutation file
13
+ * and the second write silently wins.
15
14
  *
16
- * 1. {@link withLock} - an in-process FIFO mutex keyed by the resolved file
17
- * path. Deterministic same-process serialization.
18
- * 2. {@link withOccFileLock} - a server-enforced, cross-process/cross-host
19
- * lock (proper-lockfile, mkdir-based), immune to NFS client dentry/
20
- * attribute caching. This is the actual fix for a lost permission/group
21
- * edit across two warm Lambda containers on EFS.
22
- * 3. {@link withOccRetry} around {@link writeOccJsonFile} - version/writeId
23
- * based optimistic concurrency control, reloading the file fresh on
24
- * EVERY retry attempt. With layers 1-2 in place this is defense-in-depth
25
- * (e.g. a stale process from a rolling deploy writing without the lock),
26
- * not the primary safety mechanism.
15
+ * What that doc and `utils/occ-json-write.ts` do not cover:
27
16
  *
28
- * `writeOccJsonFile`'s managed `version`/`writeId` pair is now THE single
29
- * counter for these files: the old hand-rolled `version: 1` format literal
30
- * and the separate `contentVersion` field are gone. A pre-existing file on
31
- * disk that still says `"version": 1` simply reads as OCC version 1 and
32
- * continues incrementing from there; a leftover `contentVersion` key is
33
- * silently stripped by the (non-strict, extra-keys-stripped) zod parse.
34
- *
35
- * Three caveats specific to these files, on top of the generic guarantee
36
- * documented on `utils/occ-json-write.ts`:
37
- *
38
- * (a) UNLIKE comments.json/branch.json (which never leave the branch
39
- * workspace), permissions.json/groups.json are git-committed on the
40
- * settings orphan branch. `commitSettings()` (api/settings-helpers.ts)
41
- * calls `commitToSettingsBranch`, whose `pullCurrentBranch()` merge runs
42
- * AFTER this helper has released its lock, and that merge can rewrite
43
- * the file's `version` from upstream. So, unlike branch.json, `version`
44
- * here is NOT guaranteed monotonic — it remains a correctness aid (it
45
- * still catches the common same-host and short-window cross-host races)
46
- * but is advisory/defense-in-depth, not a hard guarantee. The LOCKFILE
47
- * (layer 2) is the actual cross-host correctness mechanism.
48
- * (b) The git commit+push deliberately happens OUTSIDE this lock (mirrors
49
- * branch-metadata.ts keeping registry invalidation outside the lock it
50
- * protects): committing/pushing is comparatively slow network/process
51
- * I/O, and holding a mkdir-based lock across it would serialize
52
- * unrelated requests behind that I/O for no correctness gain — only the
53
- * write to the working tree needs the lock.
54
- * (c) Because commit+push is outside the lock, two writers' save-then-commit
55
- * sequences can interleave (A saves+commits+pushes, B's save lands and
56
- * commits before A's push is visible, or similar), and the second
57
- * `git commit` can run against an already-clean tree. Verified benign
58
- * with simple-git 3.36: its task-error detection depends on stderr
59
- * output, and a clean-tree `git commit` exits 1 with "nothing to
60
- * commit" on STDOUT only, so `git.commit()` resolves rather than
61
- * throwing. Re-verify this on any simple-git upgrade.
17
+ * (a) These files are git-committed, so `version` is NOT monotonic here --
18
+ * `commitSettings()` (api/settings-helpers.ts) calls
19
+ * `commitToSettingsBranch`, whose `pullCurrentBranch()` merge runs after
20
+ * this helper releases its lock and can rewrite `version` from upstream.
21
+ * The lockfile (layer 2) is the cross-host guarantee; OCC is defense.
22
+ * (b) The git commit+push deliberately runs OUTSIDE this lock: it is slow
23
+ * network I/O, and only the working-tree write needs the lock.
24
+ * (c) So two writers' save-then-commit sequences can interleave and the second
25
+ * `git commit` can hit an already-clean tree. Benign with simple-git 3.36,
26
+ * whose task-error detection reads stderr while a clean-tree commit exits
27
+ * 1 with "nothing to commit" on stdout only. Re-verify on upgrade.
62
28
  */
63
29
  import fs from 'node:fs/promises';
64
30
  import path from 'node:path';
@@ -77,14 +43,12 @@ export class SettingsFileConflictError extends Error {
77
43
  }
78
44
  }
79
45
  /**
80
- * Thrown by a caller's `mutate` callback when an app-level
81
- * `expectedContentVersion` sent by a client doesn't match the file's current
82
- * `version`. A different concern from {@link SettingsFileConflictError}:
83
- * this is a real edit conflict the user must resolve by reloading, not
84
- * transient lock contention a retry can fix — and indeed it never IS
85
- * retried, since {@link withOccRetry} only recognizes
86
- * {@link OccWriteConflictError} as retryable, so this propagates on the
87
- * very first attempt.
46
+ * Thrown by a caller's `mutate` when a client-supplied
47
+ * `expectedContentVersion` doesn't match the file's current `version`: a real
48
+ * edit conflict the user resolves by reloading, not the transient contention
49
+ * behind {@link SettingsFileConflictError}. It is never retried —
50
+ * {@link withOccRetry} only retries {@link OccWriteConflictError} — so it
51
+ * propagates on the first attempt.
88
52
  */
89
53
  export class SettingsVersionConflictError extends Error {
90
54
  constructor(message = 'Settings were modified by another user. Please reload and try again.') {
@@ -93,17 +57,15 @@ export class SettingsVersionConflictError extends Error {
93
57
  }
94
58
  }
95
59
  /**
96
- * Reload the file fresh and report the version to feed both `mutate()` and
60
+ * Reload the file fresh and report the version fed to both `mutate()` and
97
61
  * `writeOccJsonFile`'s `expectedVersion`.
98
62
  *
99
- * ENOENT is the ONLY case that maps to a `null` `occExpectedVersion` (the
100
- * create-via-link path in {@link writeOccJsonFile}): an EXISTING file —
101
- * even one hand-written without a `version` field — maps to `0` and takes
102
- * the rename-based update path instead. Conflating the two would make
103
- * `writeOccJsonFile` attempt a `link()` create against a file that already
104
- * exists, spuriously failing with EEXIST instead of doing a normal
105
- * versioned update. (Same contract as `CommentStore`'s `loadWithVersion` in
106
- * comment-store.ts.)
63
+ * ENOENT is the ONLY case mapping to a `null` `occExpectedVersion` (the
64
+ * create-via-link path in {@link writeOccJsonFile}); an existing file, even one
65
+ * hand-written with no `version` field, maps to `0` and takes the rename-based
66
+ * update path. Conflating them makes `writeOccJsonFile` attempt a `link()`
67
+ * create over a file that exists and fail with EEXIST. (Same contract as
68
+ * `CommentStore`'s `loadWithVersion` in comment-store.ts.)
107
69
  */
108
70
  async function loadCurrent(filePath, parse) {
109
71
  let raw;
@@ -120,15 +82,12 @@ async function loadCurrent(filePath, parse) {
120
82
  return { current: parsed, occExpectedVersion: parsed.version ?? 0 };
121
83
  }
122
84
  /**
123
- * Run a load -> mutate -> write cycle for a settings JSON file under the
124
- * full lock + OCC-retry stack described in the module doc comment above. A
125
- * conflict that survives every retry surfaces as
85
+ * Run one load -> mutate -> write cycle under the full lock + OCC-retry stack
86
+ * described in the module doc. A conflict surviving every retry surfaces as
126
87
  * {@link SettingsFileConflictError}; everything else — including
127
88
  * {@link SettingsVersionConflictError} thrown by `mutate`, and any
128
- * parse/validation error — propagates untouched.
129
- *
130
- * Returns the `writeOccJsonFile` result, or `null` if `mutate` chose a
131
- * no-op (no write happened).
89
+ * parse/validation error — propagates untouched. Returns the
90
+ * `writeOccJsonFile` result, or `null` if `mutate` chose a no-op.
132
91
  */
133
92
  export async function mutateSettingsJsonFile(opts) {
134
93
  const resolved = path.resolve(opts.filePath);
@@ -1,54 +1,32 @@
1
1
  /**
2
- * Authorization types for CanopyCMS
3
- *
4
- * This module exports all types related to authorization including
5
- * branch access, path permissions, and content access results.
6
- */
7
- import type { CanopyUserId, CanopyGroupId } from '../types.js';
8
- export type { CanopyUserId, CanopyGroupId };
9
- /**
10
- * A path used in permission rules.
11
- * SECURITY CRITICAL: Always validated to prevent path traversal.
12
- * Example: "content/posts" or "content/settings/config"
2
+ * A path used in permission rules, e.g. "content/posts".
3
+ * SECURITY CRITICAL: always validated to prevent path traversal.
13
4
  */
14
5
  export type PermissionPath = string & {
15
6
  readonly __brand: 'PermissionPath';
16
7
  };
17
- /**
18
- * Result of checking branch-level access
19
- */
20
8
  export interface BranchAccessResult {
21
9
  allowed: boolean;
22
10
  reason: 'privileged' | 'base_branch' | 'creator' | 'allowed_by_acl' | 'denied_by_acl' | 'no_acl';
23
11
  }
24
- /**
25
- * Result of checking path-level permissions
26
- */
27
12
  export interface PathPermissionResult {
28
13
  allowed: boolean;
29
14
  matchedRule?: import('../config/index.js').PathPermission;
30
15
  reason?: string;
31
16
  }
32
- /**
33
- * Combined result of checking both branch and path access
34
- */
35
17
  export interface ContentAccessResult {
36
18
  allowed: boolean;
37
19
  branch: BranchAccessResult;
38
20
  path: PathPermissionResult;
39
21
  }
40
- /**
41
- * Dependencies for content access checking
42
- */
43
22
  export interface ContentAccessDeps {
44
23
  checkBranchAccess: (context: import('../types.js').BranchContext, user: import('../user.js').CanopyUser) => BranchAccessResult;
45
24
  loadPathPermissions: (branchRoot: string, mode: import('../operating-mode/index.js').OperatingMode) => Promise<import('../config/index.js').PathPermission[]>;
46
25
  defaultPathAccess: import('../config/index.js').DefaultPathAccess;
47
26
  mode: import('../operating-mode/index.js').OperatingMode;
48
27
  /**
49
- * Get the settings branch root path for loading centralized permissions.
50
- * Used in modes with a separate settings branch.
51
- * Must throw if settings branch cannot be loaded.
28
+ * Settings branch root for loading centralized permissions, in modes with a
29
+ * separate settings branch. Must throw if it cannot be loaded.
52
30
  */
53
31
  getSettingsBranchRoot?: () => Promise<string>;
54
32
  }
@@ -1,7 +1 @@
1
- /**
2
- * Authorization types for CanopyCMS
3
- *
4
- * This module exports all types related to authorization including
5
- * branch access, path permissions, and content access results.
6
- */
7
1
  export {};