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,11 +1,7 @@
1
1
  import type { AuthPlugin } from './plugin.js';
2
2
  import type { UserSearchResult, GroupMetadata, AuthenticationResult } from './types.js';
3
3
  import type { CanopyUserId, CanopyGroupId } from '../types.js';
4
- /**
5
- * Generic cache provider interface for auth metadata.
6
- * Any auth system can implement its own cache backend
7
- * (file-based, Redis, in-memory, etc.)
8
- */
4
+ /** Auth-metadata cache backend: file-based, Redis, in-memory, whatever. */
9
5
  export interface AuthCacheProvider {
10
6
  getUser(userId: CanopyUserId): Promise<UserSearchResult | null>;
11
7
  getGroup(groupId: CanopyGroupId): Promise<GroupMetadata | null>;
@@ -13,28 +9,21 @@ export interface AuthCacheProvider {
13
9
  getAllGroups(): Promise<GroupMetadata[]>;
14
10
  getUserExternalGroups(userId: CanopyUserId): Promise<CanopyGroupId[]>;
15
11
  }
16
- /**
17
- * Token verifier function type.
18
- * Given a request context, extracts and verifies the auth token,
19
- * returning the user ID on success.
20
- */
12
+ /** Extracts and verifies the request's auth token, yielding its user ID. */
21
13
  export type TokenVerifier = (context: unknown) => Promise<{
22
14
  userId: CanopyUserId;
23
15
  } | null>;
24
16
  /**
25
- * Auth plugin that wraps a token verifier with cached metadata lookups.
26
- *
27
- * Used in environments where the auth provider API is not reachable
28
- * (e.g., Lambda with no internet). JWT verification is done locally,
29
- * and user/group metadata comes from a cache populated externally
30
- * (e.g., by an EC2 worker).
31
- *
32
- * In dev mode, an optional `lazyRefresher` can be provided to auto-populate
33
- * the cache on first request, eliminating the need to run `worker run-once` manually.
17
+ * Wraps a token verifier with cached metadata lookups, for environments where
18
+ * the auth provider's API is unreachable (a Lambda with no internet): JWT
19
+ * verification happens locally, and user/group metadata comes from a cache
20
+ * populated externally by the EC2 worker. In dev an optional `lazyRefresher`
21
+ * populates that cache on first request instead of `worker run-once`.
34
22
  *
35
- * This wrapper only forwards the inner plugin's `verifiesCredentials` affirmation via the
36
- * `options` constructor param — it cannot launder an insecure plugin, because
37
- * createNextCanopyContext asserts the INNER plugin (before wrapping) in context-wrapper.ts.
23
+ * The wrapper only FORWARDS the inner plugin's `verifiesCredentials`
24
+ * affirmation, via the `options` param. It cannot launder an insecure plugin,
25
+ * because createNextCanopyContext asserts the INNER plugin before wrapping
26
+ * (context-wrapper.ts).
38
27
  */
39
28
  export declare class CachingAuthPlugin implements AuthPlugin {
40
29
  private readonly verifyToken;
@@ -1,19 +1,16 @@
1
1
  import { createDebugLogger } from '../utils/debug.js';
2
2
  const log = createDebugLogger({ prefix: 'CachingAuthPlugin' });
3
3
  /**
4
- * Auth plugin that wraps a token verifier with cached metadata lookups.
4
+ * Wraps a token verifier with cached metadata lookups, for environments where
5
+ * the auth provider's API is unreachable (a Lambda with no internet): JWT
6
+ * verification happens locally, and user/group metadata comes from a cache
7
+ * populated externally by the EC2 worker. In dev an optional `lazyRefresher`
8
+ * populates that cache on first request instead of `worker run-once`.
5
9
  *
6
- * Used in environments where the auth provider API is not reachable
7
- * (e.g., Lambda with no internet). JWT verification is done locally,
8
- * and user/group metadata comes from a cache populated externally
9
- * (e.g., by an EC2 worker).
10
- *
11
- * In dev mode, an optional `lazyRefresher` can be provided to auto-populate
12
- * the cache on first request, eliminating the need to run `worker run-once` manually.
13
- *
14
- * This wrapper only forwards the inner plugin's `verifiesCredentials` affirmation via the
15
- * `options` constructor param — it cannot launder an insecure plugin, because
16
- * createNextCanopyContext asserts the INNER plugin (before wrapping) in context-wrapper.ts.
10
+ * The wrapper only FORWARDS the inner plugin's `verifiesCredentials`
11
+ * affirmation, via the `options` param. It cannot launder an insecure plugin,
12
+ * because createNextCanopyContext asserts the INNER plugin before wrapping
13
+ * (context-wrapper.ts).
17
14
  */
18
15
  export class CachingAuthPlugin {
19
16
  constructor(verifyToken, cache, lazyRefresher, options) {
@@ -26,7 +23,7 @@ export class CachingAuthPlugin {
26
23
  async ensureCachePopulated() {
27
24
  if (!this.lazyRefresher)
28
25
  return;
29
- // Use a shared promise so concurrent callers coalesce into a single refresh
26
+ // A shared promise, so concurrent callers coalesce into one refresh.
30
27
  this.refreshPromise ??= this.lazyRefresher()
31
28
  .then(() => log.debug('auth', 'Lazy cache refresh completed'))
32
29
  .catch((err) => {
@@ -56,7 +53,8 @@ export class CachingAuthPlugin {
56
53
  };
57
54
  }
58
55
  catch {
59
- // Cache error — still return authenticated with minimal info
56
+ // A cache failure must not reject an already-verified token: degrade to
57
+ // the bare identity, with no external groups.
60
58
  log.debug('auth', 'Cache lookup failed, returning minimal user', {
61
59
  userId: identity.userId,
62
60
  });
@@ -1,30 +1,16 @@
1
1
  import type { CanopyRequest } from '../http/types.js';
2
- /**
3
- * Headers-like interface for auth context.
4
- * Framework-agnostic - matches Web Headers, Next.js Headers, and any similar interface.
5
- */
2
+ /** The shape Web Headers, Next.js Headers and anything similar share. */
6
3
  export interface HeadersLike {
7
4
  get(name: string): string | null;
8
5
  }
9
- /**
10
- * Type guard to check if context is a CanopyRequest.
11
- * CanopyRequest has both 'header' method and 'method' property.
12
- */
6
+ /** A CanopyRequest is recognized by its `header` method plus `method`. */
13
7
  export declare function isCanopyRequest(context: unknown): context is CanopyRequest;
14
- /**
15
- * Type guard to check if context is a headers-like object.
16
- * Headers have a 'get' method for retrieving header values.
17
- */
8
+ /** Headers-like objects are recognized by their `get` method. */
18
9
  export declare function isHeadersLike(context: unknown): context is HeadersLike;
19
10
  /**
20
- * Extract headers from various auth context types.
21
- * Supports CanopyRequest (API routes) and any headers-like object (server components).
22
- *
23
- * @returns HeadersLike object or null if context type is unsupported
11
+ * Headers from either auth context shape -- a CanopyRequest (API routes) or a
12
+ * headers-like object (server components) -- or null for anything else.
24
13
  */
25
14
  export declare function extractHeaders(context: unknown): HeadersLike | null;
26
- /**
27
- * Validate auth context and throw helpful error if unsupported.
28
- * Use this in auth plugins to provide clear error messages.
29
- */
15
+ /** Use this in auth plugins: same as extractHeaders, but throws on null. */
30
16
  export declare function validateAuthContext(context: unknown): HeadersLike;
@@ -1,7 +1,4 @@
1
- /**
2
- * Type guard to check if context is a CanopyRequest.
3
- * CanopyRequest has both 'header' method and 'method' property.
4
- */
1
+ /** A CanopyRequest is recognized by its `header` method plus `method`. */
5
2
  export function isCanopyRequest(context) {
6
3
  return (typeof context === 'object' &&
7
4
  context !== null &&
@@ -9,10 +6,7 @@ export function isCanopyRequest(context) {
9
6
  'method' in context &&
10
7
  typeof context.header === 'function');
11
8
  }
12
- /**
13
- * Type guard to check if context is a headers-like object.
14
- * Headers have a 'get' method for retrieving header values.
15
- */
9
+ /** Headers-like objects are recognized by their `get` method. */
16
10
  export function isHeadersLike(context) {
17
11
  return (typeof context === 'object' &&
18
12
  context !== null &&
@@ -20,14 +14,11 @@ export function isHeadersLike(context) {
20
14
  typeof context.get === 'function');
21
15
  }
22
16
  /**
23
- * Extract headers from various auth context types.
24
- * Supports CanopyRequest (API routes) and any headers-like object (server components).
25
- *
26
- * @returns HeadersLike object or null if context type is unsupported
17
+ * Headers from either auth context shape -- a CanopyRequest (API routes) or a
18
+ * headers-like object (server components) -- or null for anything else.
27
19
  */
28
20
  export function extractHeaders(context) {
29
21
  if (isCanopyRequest(context)) {
30
- // Wrap CanopyRequest.header() as HeadersLike.get()
31
22
  return {
32
23
  get: (name) => context.header(name),
33
24
  };
@@ -37,10 +28,7 @@ export function extractHeaders(context) {
37
28
  }
38
29
  return null;
39
30
  }
40
- /**
41
- * Validate auth context and throw helpful error if unsupported.
42
- * Use this in auth plugins to provide clear error messages.
43
- */
31
+ /** Use this in auth plugins: same as extractHeaders, but throws on null. */
44
32
  export function validateAuthContext(context) {
45
33
  const headers = extractHeaders(context);
46
34
  if (!headers) {
@@ -2,20 +2,12 @@ import type { AuthCacheProvider } from './caching-auth-plugin.js';
2
2
  import type { UserSearchResult, GroupMetadata } from './types.js';
3
3
  import type { CanopyUserId, CanopyGroupId } from '../types.js';
4
4
  /**
5
- * File-based auth cache provider.
6
- * Reads JSON files from a directory that is populated externally
7
- * (e.g., by an EC2 worker running refreshClerkCache).
5
+ * Reads a directory populated externally (the EC2 worker's refreshClerkCache)
6
+ * and holds the result in memory, re-reading whenever a file's mtime changes.
8
7
  *
9
- * Supports two directory layouts:
10
- * - Snapshot layout: {cachePath}/current/ symlink → snapshot-{ts}/ directory
11
- * - Flat layout: files directly in {cachePath}/
12
- *
13
- * Expects:
14
- * - users.json — { users: UserSearchResult[] }
15
- * - orgs.json — { groups: GroupMetadata[] }
16
- * - memberships.json — { memberships: { [userId]: groupId[] } }
17
- *
18
- * Caches in memory and re-reads when file mtime changes.
8
+ * Expects users.json `{ users }`, orgs.json `{ groups }` and memberships.json
9
+ * `{ memberships: { [userId]: groupId[] } }`, in whichever directory
10
+ * `resolveActiveCacheDir` picks.
19
11
  */
20
12
  export declare class FileBasedAuthCache implements AuthCacheProvider {
21
13
  private readonly cachePath;
@@ -33,13 +25,10 @@ export declare class FileBasedAuthCache implements AuthCacheProvider {
33
25
  private emptyCache;
34
26
  }
35
27
  /**
36
- * Write auth cache files atomically using a snapshot directory and symlink swap.
37
- *
38
- * 1. Writes files to a timestamped snapshot directory: {cachePath}/snapshot-{ts}/
39
- * 2. Creates a temporary symlink, then atomically renames it to {cachePath}/current
40
- * 3. Cleans up old snapshot directories (keeps the 2 most recent)
41
- *
42
- * This ensures readers (FileBasedAuthCache) always see a consistent set of files:
43
- * either the old snapshot or the new one, never a mix.
28
+ * Write the cache files into a fresh snapshot directory, then swap the `current`
29
+ * symlink over to it, so a reader always sees one consistent set of files --
30
+ * the old snapshot or the new one, never a mix. Individual files go down by
31
+ * temp-file + rename (utils/atomic-write.ts owns that mechanism). The 2 most
32
+ * recent snapshot directories are kept.
44
33
  */
45
34
  export declare function writeAuthCacheSnapshot(cachePath: string, files: Record<string, unknown>): Promise<string>;
@@ -3,22 +3,16 @@ import path from 'node:path';
3
3
  import { createDebugLogger } from '../utils/debug.js';
4
4
  const log = createDebugLogger({ prefix: 'FileBasedAuthCache' });
5
5
  /**
6
- * Resolve the active cache directory.
7
- *
8
- * Supports two layouts:
9
- * 1. Snapshot layout (preferred): {cachePath}/current → {cachePath}/snapshot-{ts}/
10
- * The `current` symlink points to the active snapshot directory.
11
- * 2. Flat layout (legacy/simple): files directly in {cachePath}/
12
- *
13
- * Returns the directory path where users.json, orgs.json, memberships.json live.
6
+ * The directory holding users.json, orgs.json and memberships.json: the target
7
+ * of the `{cachePath}/current` symlink under the preferred snapshot layout, or
8
+ * `{cachePath}` itself under the flat one.
14
9
  */
15
10
  async function resolveActiveCacheDir(cachePath) {
16
11
  const currentLink = path.join(cachePath, 'current');
17
12
  try {
18
13
  const target = await fs.readlink(currentLink);
19
- // Symlink target may be relative or absolute
20
14
  const resolved = path.isAbsolute(target) ? target : path.resolve(cachePath, target);
21
- // SECURITY: Validate that resolved target stays within the expected cache directory
15
+ // SECURITY: the symlink target must stay inside the cache directory.
22
16
  const normalizedCache = path.resolve(cachePath);
23
17
  const normalizedTarget = path.resolve(resolved);
24
18
  if (!normalizedTarget.startsWith(normalizedCache + path.sep) &&
@@ -37,20 +31,12 @@ async function resolveActiveCacheDir(cachePath) {
37
31
  }
38
32
  }
39
33
  /**
40
- * File-based auth cache provider.
41
- * Reads JSON files from a directory that is populated externally
42
- * (e.g., by an EC2 worker running refreshClerkCache).
43
- *
44
- * Supports two directory layouts:
45
- * - Snapshot layout: {cachePath}/current/ symlink → snapshot-{ts}/ directory
46
- * - Flat layout: files directly in {cachePath}/
34
+ * Reads a directory populated externally (the EC2 worker's refreshClerkCache)
35
+ * and holds the result in memory, re-reading whenever a file's mtime changes.
47
36
  *
48
- * Expects:
49
- * - users.json — { users: UserSearchResult[] }
50
- * - orgs.json — { groups: GroupMetadata[] }
51
- * - memberships.json — { memberships: { [userId]: groupId[] } }
52
- *
53
- * Caches in memory and re-reads when file mtime changes.
37
+ * Expects users.json `{ users }`, orgs.json `{ groups }` and memberships.json
38
+ * `{ memberships: { [userId]: groupId[] } }`, in whichever directory
39
+ * `resolveActiveCacheDir` picks.
54
40
  */
55
41
  export class FileBasedAuthCache {
56
42
  constructor(cachePath) {
@@ -83,7 +69,6 @@ export class FileBasedAuthCache {
83
69
  const usersPath = path.join(activeDir, 'users.json');
84
70
  const orgsPath = path.join(activeDir, 'orgs.json');
85
71
  const membershipsPath = path.join(activeDir, 'memberships.json');
86
- // Check max mtime across all three files for cache freshness
87
72
  let maxMtime = 0;
88
73
  for (const filePath of [usersPath, orgsPath, membershipsPath]) {
89
74
  try {
@@ -95,17 +80,14 @@ export class FileBasedAuthCache {
95
80
  }
96
81
  }
97
82
  if (maxMtime === 0) {
98
- // No cache files exist — return empty cache
99
83
  if (!this.cache) {
100
84
  this.cache = this.emptyCache();
101
85
  }
102
86
  return this.cache;
103
87
  }
104
- // If max mtime hasn't changed and we have a cache, return it
105
88
  if (this.cache && maxMtime === this.lastMtime) {
106
89
  return this.cache;
107
90
  }
108
- // Load fresh data
109
91
  this.cache = await this.loadFromDisk(activeDir);
110
92
  this.lastMtime = maxMtime;
111
93
  return this.cache;
@@ -168,45 +150,36 @@ export class FileBasedAuthCache {
168
150
  }
169
151
  }
170
152
  /**
171
- * Write auth cache files atomically using a snapshot directory and symlink swap.
172
- *
173
- * 1. Writes files to a timestamped snapshot directory: {cachePath}/snapshot-{ts}/
174
- * 2. Creates a temporary symlink, then atomically renames it to {cachePath}/current
175
- * 3. Cleans up old snapshot directories (keeps the 2 most recent)
176
- *
177
- * This ensures readers (FileBasedAuthCache) always see a consistent set of files:
178
- * either the old snapshot or the new one, never a mix.
153
+ * Write the cache files into a fresh snapshot directory, then swap the `current`
154
+ * symlink over to it, so a reader always sees one consistent set of files --
155
+ * the old snapshot or the new one, never a mix. Individual files go down by
156
+ * temp-file + rename (utils/atomic-write.ts owns that mechanism). The 2 most
157
+ * recent snapshot directories are kept.
179
158
  */
180
159
  export async function writeAuthCacheSnapshot(cachePath, files) {
181
160
  await fs.mkdir(cachePath, { recursive: true });
182
161
  const timestamp = Date.now();
183
162
  const snapshotDir = path.join(cachePath, `snapshot-${timestamp}`);
184
163
  await fs.mkdir(snapshotDir, { recursive: true });
185
- // Write all files to the snapshot directory
186
164
  for (const [fileName, data] of Object.entries(files)) {
187
165
  const tmpPath = path.join(snapshotDir, `${fileName}.tmp`);
188
166
  const finalPath = path.join(snapshotDir, fileName);
189
167
  await fs.writeFile(tmpPath, JSON.stringify(data, null, 2), 'utf-8');
190
168
  await fs.rename(tmpPath, finalPath);
191
169
  }
192
- // Atomic symlink swap: create temp symlink, rename over current.
193
- //
194
- // The target MUST be relative (the bare `snapshot-<ts>` basename), because
195
- // writer and reader do not always share a mount namespace. In prod the EC2
196
- // worker mounts the EFS filesystem root and writes through
197
- // CANOPYCMS_WORKSPACE_ROOT=/mnt/efs/workspace (cachePath
198
- // /mnt/efs/workspace/.cache), while the CMS Lambda mounts the /workspace
199
- // access point at /mnt/efs and reads the SAME directory as /mnt/efs/.cache.
200
- // An absolute target recorded by one is a nonexistent path to the other -
201
- // `resolveActiveCacheDir`'s escape guard then correctly rejects it and falls
202
- // back to the flat layout, where the worker never writes, leaving the Lambda
203
- // with a permanently empty cache. A relative target resolves against
204
- // whichever cachePath the reader was given, so it is correct from both.
170
+ // The symlink target MUST be relative (the bare `snapshot-<ts>` basename),
171
+ // because writer and reader do not share a mount namespace: in prod the EC2
172
+ // worker writes through /mnt/efs/workspace/.cache while the CMS Lambda mounts
173
+ // the /workspace access point and reads the SAME directory as /mnt/efs/.cache.
174
+ // An absolute target recorded by one is a nonexistent path to the other, and
175
+ // `resolveActiveCacheDir`'s escape guard then falls back to the flat layout,
176
+ // where the worker never writes — leaving the Lambda a permanently empty
177
+ // cache. A relative target resolves against whichever cachePath the reader
178
+ // was given, so it is correct from both.
205
179
  const currentLink = path.join(cachePath, 'current');
206
180
  const tmpLink = path.join(cachePath, `current-${timestamp}`);
207
181
  await fs.symlink(path.basename(snapshotDir), tmpLink);
208
182
  await fs.rename(tmpLink, currentLink);
209
- // Clean up old snapshots (keep the 2 most recent)
210
183
  await cleanupOldSnapshots(cachePath, 2);
211
184
  return snapshotDir;
212
185
  }
@@ -222,7 +195,6 @@ async function cleanupOldSnapshots(cachePath, keepCount) {
222
195
  .filter((e) => e.startsWith('snapshot-'))
223
196
  .sort()
224
197
  .reverse();
225
- // Skip the most recent `keepCount` snapshots
226
198
  for (const snapshot of snapshots.slice(keepCount)) {
227
199
  try {
228
200
  await fs.rm(path.join(cachePath, snapshot), {
@@ -1,98 +1,66 @@
1
1
  import type { UserSearchResult, GroupMetadata, AuthenticationResult } from './types.js';
2
2
  import type { CanopyUserId, CanopyGroupId } from '../types.js';
3
3
  import type { OperatingMode } from '../operating-mode/types.js';
4
- /**
5
- * Abstract auth provider interface.
6
- * Implement this to integrate different auth systems (Clerk, Auth0, NextAuth, etc.)
7
- */
4
+ /** Implement this to integrate an auth system (Clerk, Auth0, NextAuth, ...). */
8
5
  export interface AuthPlugin {
9
6
  /**
10
- * Affirmative allowlist marker: set to true ONLY on plugins that
11
- * cryptographically verify credentials (e.g. Clerk JWT verification).
12
- * assertAuthPluginAllowedForMode() rejects any plugin without this marker
13
- * whenever the operating mode is 'prod' — absence fails closed, so a
14
- * third-party plugin that forgets the marker is rejected, not accepted.
7
+ * Affirmative allowlist marker: true ONLY on plugins that cryptographically
8
+ * verify credentials. In 'prod', assertAuthPluginAllowedForMode() rejects any
9
+ * plugin without it, so a plugin that forgets the marker fails closed.
15
10
  */
16
11
  readonly verifiesCredentials?: boolean;
17
12
  /**
18
- * Authenticate user from request context.
19
- * Returns user identity (without final groups) - core will apply bootstrap admins.
13
+ * Authenticate from a request context, returning identity without final
14
+ * groups; core applies bootstrap admins.
20
15
  *
21
- * Contract: CREDENTIAL failures (missing/invalid/expired token) resolve to
22
- * `{ success: false }` — they map to 401s. CONFIGURATION errors (e.g. a
23
- * required secret like CLERK_SECRET_KEY is absent) may THROW instead: they
24
- * are operator mistakes, not user mistakes, and should surface as loud 500s
25
- * rather than quiet auth denials. Callers invoking authenticate() directly
26
- * (custom adapters) should be prepared for a rejection on misconfiguration.
27
- *
28
- * @param context - Framework-specific context (CanopyRequest, headers, etc.)
29
- * @returns AuthenticationResult with user identity or error
16
+ * CREDENTIAL failures (missing, invalid or expired token) RESOLVE to
17
+ * `{ success: false }` and map to a 401. CONFIGURATION errors (an absent
18
+ * CLERK_SECRET_KEY, say) may THROW instead: they are operator mistakes, and
19
+ * belong in a loud 500 rather than a quiet auth denial. A custom adapter
20
+ * calling this directly must be ready for that rejection.
30
21
  */
31
22
  authenticate(context: unknown): Promise<AuthenticationResult>;
32
- /**
33
- * Search for users (for permission management UI)
34
- * @param query - Search string (email, name, etc.)
35
- * @param limit - Max results (default 10)
36
- */
23
+ /** For the permission-management UI. */
37
24
  searchUsers(query: string, limit?: number): Promise<UserSearchResult[]>;
38
- /**
39
- * Get detailed user metadata by ID
40
- */
41
25
  getUserMetadata(userId: CanopyUserId): Promise<UserSearchResult | null>;
42
- /**
43
- * Get group/organization metadata by ID
44
- */
45
26
  getGroupMetadata(groupId: CanopyGroupId): Promise<GroupMetadata | null>;
46
- /**
47
- * List all groups (for permission UI dropdowns)
48
- */
27
+ /** For permission UI dropdowns. */
49
28
  listGroups(limit?: number): Promise<GroupMetadata[]>;
50
- /**
51
- * Search for external groups/organizations (for group management UI)
52
- * Optional - only needed if auth provider supports external groups
53
- * @param query - Search string (name, ID, etc.)
54
- */
29
+ /** For the group-management UI; only providers with groups need it. */
55
30
  searchExternalGroups?(query: string): Promise<Array<{
56
31
  id: CanopyGroupId;
57
32
  name: string;
58
33
  }>>;
59
34
  /**
60
- * Optional: lightweight token-only verification (no user metadata lookup, no network).
61
- * When present, createNextCanopyContext automatically wraps this plugin with
62
- * CachingAuthPlugin in prod/dev modes. The cache is populated by the worker daemon.
35
+ * Token-only verification: no metadata lookup, no network. When present,
36
+ * createNextCanopyContext wraps this plugin with CachingAuthPlugin in both
37
+ * modes, over a cache the worker daemon populates.
63
38
  */
64
39
  verifyTokenOnly?(context: unknown): Promise<{
65
40
  userId: CanopyUserId;
66
41
  } | null>;
67
42
  /**
68
- * Optional: create a function that refreshes the auth cache for this plugin.
69
- * Used by the worker daemon and CLI run-once to populate the file-based auth cache.
70
- * Returns undefined if this plugin doesn't support cache refresh (e.g., missing credentials).
43
+ * Builds the refresher the worker daemon and CLI run-once use to populate the
44
+ * file-based auth cache. Undefined when this plugin cannot refresh (no
45
+ * credentials, say).
71
46
  */
72
47
  createCacheRefresher?(cachePath: string): (() => Promise<{
73
48
  userCount: number;
74
49
  groupCount: number;
75
50
  }>) | undefined;
76
51
  }
77
- /**
78
- * Factory function type for creating auth plugins
79
- */
80
52
  export type AuthPluginFactory<TConfig = unknown> = (config: TConfig) => AuthPlugin;
81
53
  /**
82
- * Fail closed: only allow auth plugins that affirmatively declare real
83
- * credential verification when the CMS runs in production.
84
- *
85
- * This is an allowlist, not a denylist: a plugin must set
86
- * `verifiesCredentials: true` to pass this guard in prod. DevAuthPlugin (and
87
- * any third-party plugin that forgets to set the marker) trusts request
88
- * headers/cookies without cryptographic verification, so accepting it with
89
- * mode 'prod' would let any caller impersonate any user — including admins —
90
- * by sending a header like `X-Test-User: admin`. Call this wherever an
91
- * adopter-provided auth plugin meets the operating mode (framework wrappers,
92
- * request handlers) BEFORE the plugin is wrapped or used.
54
+ * Fail closed in prod: an ALLOWLIST, so a plugin passes only by setting
55
+ * `verifiesCredentials: true`. DevAuthPlugin, and any plugin that forgets the
56
+ * marker, trusts request headers without cryptographic verification, so
57
+ * accepting one in 'prod' would let any caller impersonate any user, admins
58
+ * included, with a header like `X-Test-User: admin`. Absence of
59
+ * `verifyTokenOnly` is NOT a substitute for the marker — the dev plugin
60
+ * implements that too.
93
61
  *
94
- * Note: checking for the absence of verifyTokenOnly is NOT a substitute for
95
- * this marker — the dev plugin implements verifyTokenOnly too.
62
+ * Call this wherever an adopter-provided plugin meets the operating mode
63
+ * (framework wrappers, request handlers) BEFORE the plugin is wrapped or used.
96
64
  *
97
65
  * @throws Error when mode is 'prod' and the plugin does not set `verifiesCredentials: true`
98
66
  */
@@ -1,18 +1,14 @@
1
1
  /**
2
- * Fail closed: only allow auth plugins that affirmatively declare real
3
- * credential verification when the CMS runs in production.
2
+ * Fail closed in prod: an ALLOWLIST, so a plugin passes only by setting
3
+ * `verifiesCredentials: true`. DevAuthPlugin, and any plugin that forgets the
4
+ * marker, trusts request headers without cryptographic verification, so
5
+ * accepting one in 'prod' would let any caller impersonate any user, admins
6
+ * included, with a header like `X-Test-User: admin`. Absence of
7
+ * `verifyTokenOnly` is NOT a substitute for the marker — the dev plugin
8
+ * implements that too.
4
9
  *
5
- * This is an allowlist, not a denylist: a plugin must set
6
- * `verifiesCredentials: true` to pass this guard in prod. DevAuthPlugin (and
7
- * any third-party plugin that forgets to set the marker) trusts request
8
- * headers/cookies without cryptographic verification, so accepting it with
9
- * mode 'prod' would let any caller impersonate any user — including admins —
10
- * by sending a header like `X-Test-User: admin`. Call this wherever an
11
- * adopter-provided auth plugin meets the operating mode (framework wrappers,
12
- * request handlers) BEFORE the plugin is wrapped or used.
13
- *
14
- * Note: checking for the absence of verifyTokenOnly is NOT a substitute for
15
- * this marker — the dev plugin implements verifyTokenOnly too.
10
+ * Call this wherever an adopter-provided plugin meets the operating mode
11
+ * (framework wrappers, request handlers) BEFORE the plugin is wrapped or used.
16
12
  *
17
13
  * @throws Error when mode is 'prod' and the plugin does not set `verifiesCredentials: true`
18
14
  */
@@ -1,16 +1,12 @@
1
1
  import type { CanopyUserId, CanopyGroupId } from '../types.js';
2
- /**
3
- * User search result for permission UI
4
- */
2
+ /** A user offered as a permission target. */
5
3
  export interface UserSearchResult {
6
4
  id: CanopyUserId;
7
5
  name: string;
8
6
  email: string;
9
7
  avatarUrl?: string;
10
8
  }
11
- /**
12
- * Group metadata for permission UI
13
- */
9
+ /** Group metadata for the permission UI. */
14
10
  export interface GroupMetadata {
15
11
  id: CanopyGroupId;
16
12
  name: string;
@@ -22,12 +18,11 @@ export interface GroupMetadata {
22
18
  * ('external'), Canopy's own groups.json ('internal'), or BOTH -- the same ID
23
19
  * exists in each universe.
24
20
  *
25
- * 'both' matters for what a grant actually does. The two ID spaces are not
26
- * namespaced against each other, and `checkPathPermission` matches one
27
- * flattened `user.groups` list by ID, so granting a colliding ID reaches the
28
- * internal group's members AND every provider user whose external groups
29
- * include that ID. Labeling such an option merely 'internal' would understate
30
- * its blast radius to the admin making the grant.
21
+ * 'both' changes what a grant does. The two ID spaces are not namespaced against
22
+ * each other and `checkPathPermission` matches one flattened `user.groups` list
23
+ * by ID, so granting a colliding ID reaches the internal group's members AND
24
+ * every provider user carrying that ID externally. Labeling such an option
25
+ * merely 'internal' would understate that blast radius to the granting admin.
31
26
  */
32
27
  export type GroupSource = 'internal' | 'external' | 'both';
33
28
  /**
@@ -35,21 +30,17 @@ export type GroupSource = 'internal' | 'external' | 'both';
35
30
  *
36
31
  * Both universes are valid `allowedGroups` values -- `authResultToCanopyUser`
37
32
  * (user.ts) flattens external and internal groups into one `user.groups` list
38
- * and `checkPathPermission` matches `allowedGroups` against it by ID -- so the
39
- * picker has to offer both. The two ID spaces are not namespaced against each
40
- * other, hence `source` to disambiguate them in the UI.
33
+ * that `checkPathPermission` matches by ID -- so the picker offers both, with
34
+ * `source` disambiguating the two un-namespaced ID spaces in the UI.
41
35
  *
42
36
  * Internal options deliberately carry no `memberCount`: member identities and
43
37
  * counts stay behind the admin-only `groups.getInternal`, while this endpoint
44
- * is `privileged` (admin or reviewer).
38
+ * only needs the `privileged` guard (authorization/helpers.ts's `isPrivileged`).
45
39
  */
46
40
  export interface PermissionGroupOption extends GroupMetadata {
47
41
  source: GroupSource;
48
42
  }
49
- /**
50
- * Authentication result from auth plugins.
51
- * Returns user identity (without final groups) on success.
52
- */
43
+ /** An auth plugin's verdict: on success, user identity without final groups. */
53
44
  export interface AuthenticationResult {
54
45
  success: boolean;
55
46
  user?: {