emdash 0.35.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (971) hide show
  1. package/dist/activation-K0kGBWuJ.mjs +382 -0
  2. package/dist/activation-K0kGBWuJ.mjs.map +1 -0
  3. package/dist/{adapters-Bb1vG41D.d.mts → adapters-BDQwg236.d.mts} +6 -4
  4. package/dist/{adapters-Bb1vG41D.d.mts.map → adapters-BDQwg236.d.mts.map} +1 -1
  5. package/dist/{allowed-origins-DW7sYyqg.mjs → allowed-origins-BGglilcs.mjs} +2 -2
  6. package/dist/{allowed-origins-DW7sYyqg.mjs.map → allowed-origins-BGglilcs.mjs.map} +1 -1
  7. package/dist/api/route-utils.d.mts +3 -3
  8. package/dist/api/route-utils.mjs +23 -21
  9. package/dist/api/route-utils.mjs.map +1 -1
  10. package/dist/api/schemas/index.d.mts +2 -2
  11. package/dist/api/schemas/index.mjs +5 -5
  12. package/dist/api-Dz6rgoa3.mjs +10040 -0
  13. package/dist/api-Dz6rgoa3.mjs.map +1 -0
  14. package/dist/{api-tokens-KO24G1Fd.mjs → api-tokens-BSsQACNe.mjs} +2 -2
  15. package/dist/{api-tokens-KO24G1Fd.mjs.map → api-tokens-BSsQACNe.mjs.map} +1 -1
  16. package/dist/{apply-kUDt80-G.mjs → apply-B4SIOxR1.mjs} +34 -35
  17. package/dist/{apply-kUDt80-G.mjs.map → apply-B4SIOxR1.mjs.map} +1 -1
  18. package/dist/astro/image-endpoint.mjs +2 -2
  19. package/dist/astro/image-endpoint.mjs.map +1 -1
  20. package/dist/astro/index.d.mts +12 -12
  21. package/dist/astro/index.d.mts.map +1 -1
  22. package/dist/astro/index.mjs +128 -44
  23. package/dist/astro/index.mjs.map +1 -1
  24. package/dist/astro/middleware/auth.d.mts +10 -10
  25. package/dist/astro/middleware/auth.d.mts.map +1 -1
  26. package/dist/astro/middleware/auth.mjs +10 -7
  27. package/dist/astro/middleware/auth.mjs.map +1 -1
  28. package/dist/astro/middleware/media-usage-write-fence.mjs +2 -2
  29. package/dist/astro/middleware/redirect.mjs +4 -4
  30. package/dist/astro/middleware/request-context.mjs +3 -3
  31. package/dist/astro/middleware/setup.mjs +1 -1
  32. package/dist/astro/middleware.d.mts +11 -12
  33. package/dist/astro/middleware.d.mts.map +1 -1
  34. package/dist/astro/middleware.mjs +237 -1412
  35. package/dist/astro/middleware.mjs.map +1 -1
  36. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
  37. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
  38. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +3 -3
  39. package/dist/astro/routes/api/admin/api-tokens/index.d.mts.map +1 -1
  40. package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
  41. package/dist/astro/routes/api/admin/api-tokens/index.mjs.map +1 -1
  42. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  43. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -9
  44. package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -9
  45. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -9
  46. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +16 -16
  47. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +16 -16
  48. package/dist/astro/routes/api/admin/bylines/index.mjs +16 -16
  49. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +13 -13
  50. package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
  51. package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
  52. package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
  53. package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
  54. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  55. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  56. package/dist/astro/routes/api/admin/media-usage/activation.d.mts +9 -0
  57. package/dist/astro/routes/api/admin/media-usage/activation.d.mts.map +1 -0
  58. package/dist/astro/routes/api/admin/media-usage/activation.mjs +46 -0
  59. package/dist/astro/routes/api/admin/media-usage/activation.mjs.map +1 -0
  60. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +14 -13
  61. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs.map +1 -1
  62. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +14 -13
  63. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs.map +1 -1
  64. package/dist/astro/routes/api/admin/media-usage/progress.d.mts +9 -0
  65. package/dist/astro/routes/api/admin/media-usage/progress.d.mts.map +1 -0
  66. package/dist/astro/routes/api/admin/media-usage/progress.mjs +44 -0
  67. package/dist/astro/routes/api/admin/media-usage/progress.mjs.map +1 -0
  68. package/dist/astro/routes/api/admin/media-usage/repair.mjs +16 -11
  69. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
  70. package/dist/astro/routes/api/admin/media-usage/work/index.mjs +14 -13
  71. package/dist/astro/routes/api/admin/media-usage/work/index.mjs.map +1 -1
  72. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +14 -13
  73. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs.map +1 -1
  74. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
  75. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
  77. package/dist/astro/routes/api/admin/oauth-clients/index.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +55 -49
  79. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +55 -49
  81. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +53 -47
  83. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  84. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +4 -4
  85. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +53 -47
  86. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
  87. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -48
  88. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  89. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +54 -48
  90. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  91. package/dist/astro/routes/api/admin/plugins/index.mjs +53 -47
  92. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  93. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
  94. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +53 -47
  95. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  96. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +54 -48
  97. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  98. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +53 -47
  99. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  100. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +54 -48
  101. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  102. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.d.mts.map +1 -1
  103. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +60 -50
  104. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  105. package/dist/astro/routes/api/admin/plugins/registry/artifact.d.mts.map +1 -1
  106. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +179 -139
  107. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  108. package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -1
  109. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +61 -51
  110. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  111. package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts +8 -0
  112. package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts.map +1 -0
  113. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +93 -0
  114. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs.map +1 -0
  115. package/dist/astro/routes/api/admin/plugins/updates.mjs +53 -47
  116. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  117. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +53 -47
  118. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  119. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
  120. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +53 -47
  121. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  122. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  123. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
  124. package/dist/astro/routes/api/admin/users/_id_/index.mjs +7 -7
  125. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
  126. package/dist/astro/routes/api/admin/users/index.mjs +6 -6
  127. package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
  128. package/dist/astro/routes/api/auth/invite/accept.mjs +1 -1
  129. package/dist/astro/routes/api/auth/invite/complete.mjs +11 -11
  130. package/dist/astro/routes/api/auth/invite/index.mjs +8 -8
  131. package/dist/astro/routes/api/auth/invite/register-options.mjs +10 -10
  132. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  133. package/dist/astro/routes/api/auth/magic-link/send.mjs +10 -10
  134. package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
  135. package/dist/astro/routes/api/auth/me.d.mts.map +1 -1
  136. package/dist/astro/routes/api/auth/me.mjs +19 -9
  137. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  138. package/dist/astro/routes/api/auth/mode.mjs +2 -2
  139. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  140. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  141. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
  142. package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
  143. package/dist/astro/routes/api/auth/passkey/options.mjs +12 -12
  144. package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -10
  145. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -11
  146. package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -11
  147. package/dist/astro/routes/api/auth/signup/complete.mjs +11 -11
  148. package/dist/astro/routes/api/auth/signup/request.mjs +10 -10
  149. package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
  150. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +14 -14
  151. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +13 -13
  152. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
  153. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.d.mts.map +1 -1
  154. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +14 -5
  155. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -1
  156. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  157. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +2 -2
  158. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +11 -11
  159. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +13 -10
  160. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
  161. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +17 -15
  162. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
  163. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +17 -15
  164. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
  165. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  166. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
  167. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -8
  168. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +14 -14
  169. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
  170. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.d.mts.map +1 -1
  171. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +14 -5
  172. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
  173. package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
  174. package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
  175. package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
  176. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
  177. package/dist/astro/routes/api/dashboard.mjs +8 -8
  178. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  179. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  180. package/dist/astro/routes/api/import/probe.mjs +17 -17
  181. package/dist/astro/routes/api/import/wordpress/analyze.mjs +2 -2
  182. package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
  183. package/dist/astro/routes/api/import/wordpress/execute.mjs +13 -13
  184. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
  185. package/dist/astro/routes/api/import/wordpress/media.mjs +11 -11
  186. package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
  187. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
  188. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +12 -10
  189. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
  190. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  191. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +17 -17
  192. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
  193. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +27 -27
  194. package/dist/astro/routes/api/manifest.mjs +4 -4
  195. package/dist/astro/routes/api/manifest.mjs.map +1 -1
  196. package/dist/astro/routes/api/mcp.mjs +188 -65
  197. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  198. package/dist/astro/routes/api/media/_id_/confirm.mjs +11 -11
  199. package/dist/astro/routes/api/media/_id_/replace.d.mts +8 -0
  200. package/dist/astro/routes/api/media/_id_/replace.d.mts.map +1 -0
  201. package/dist/astro/routes/api/media/_id_/replace.mjs +91 -0
  202. package/dist/astro/routes/api/media/_id_/replace.mjs.map +1 -0
  203. package/dist/astro/routes/api/media/_id_/upload.mjs +6 -6
  204. package/dist/astro/routes/api/media/_id_/usage.mjs +18 -13
  205. package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
  206. package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
  207. package/dist/astro/routes/api/media/_id_.mjs +21 -13
  208. package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
  209. package/dist/astro/routes/api/media/file/_...key_.d.mts.map +1 -1
  210. package/dist/astro/routes/api/media/file/_...key_.mjs +3 -2
  211. package/dist/astro/routes/api/media/file/_...key_.mjs.map +1 -1
  212. package/dist/astro/routes/api/media/folders/_id_.d.mts +10 -0
  213. package/dist/astro/routes/api/media/folders/_id_.d.mts.map +1 -0
  214. package/dist/astro/routes/api/media/folders/_id_.mjs +52 -0
  215. package/dist/astro/routes/api/media/folders/_id_.mjs.map +1 -0
  216. package/dist/astro/routes/api/media/folders/index.d.mts +9 -0
  217. package/dist/astro/routes/api/media/folders/index.d.mts.map +1 -0
  218. package/dist/astro/routes/api/media/folders/index.mjs +41 -0
  219. package/dist/astro/routes/api/media/folders/index.mjs.map +1 -0
  220. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
  221. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
  222. package/dist/astro/routes/api/media/providers/index.mjs +2 -2
  223. package/dist/astro/routes/api/media/upload-url.mjs +16 -14
  224. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
  225. package/dist/astro/routes/api/media.d.mts.map +1 -1
  226. package/dist/astro/routes/api/media.mjs +59 -27
  227. package/dist/astro/routes/api/media.mjs.map +1 -1
  228. package/dist/astro/routes/api/menus/_name_/items/_id_.d.mts.map +1 -1
  229. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +20 -13
  230. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
  231. package/dist/astro/routes/api/menus/_name_/items.d.mts.map +1 -1
  232. package/dist/astro/routes/api/menus/_name_/items.mjs +15 -11
  233. package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
  234. package/dist/astro/routes/api/menus/_name_/reorder.d.mts.map +1 -1
  235. package/dist/astro/routes/api/menus/_name_/reorder.mjs +15 -11
  236. package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
  237. package/dist/astro/routes/api/menus/_name_/translations.d.mts.map +1 -1
  238. package/dist/astro/routes/api/menus/_name_/translations.mjs +16 -12
  239. package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
  240. package/dist/astro/routes/api/menus/_name_.d.mts.map +1 -1
  241. package/dist/astro/routes/api/menus/_name_.mjs +21 -14
  242. package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
  243. package/dist/astro/routes/api/menus/index.d.mts.map +1 -1
  244. package/dist/astro/routes/api/menus/index.mjs +15 -11
  245. package/dist/astro/routes/api/menus/index.mjs.map +1 -1
  246. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  247. package/dist/astro/routes/api/oauth/device/authorize.mjs +5 -5
  248. package/dist/astro/routes/api/oauth/device/code.mjs +8 -8
  249. package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
  250. package/dist/astro/routes/api/oauth/register.mjs +2 -2
  251. package/dist/astro/routes/api/oauth/token/refresh.mjs +5 -5
  252. package/dist/astro/routes/api/oauth/token/revoke.mjs +5 -5
  253. package/dist/astro/routes/api/oauth/token.mjs +5 -5
  254. package/dist/astro/routes/api/openapi.json.mjs +253 -40
  255. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  256. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
  257. package/dist/astro/routes/api/redirects/404s/index.mjs +10 -10
  258. package/dist/astro/routes/api/redirects/404s/summary.mjs +10 -10
  259. package/dist/astro/routes/api/redirects/_id_.mjs +11 -11
  260. package/dist/astro/routes/api/redirects/index.mjs +11 -11
  261. package/dist/astro/routes/api/relations/_id_/index.mjs +17 -15
  262. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
  263. package/dist/astro/routes/api/relations/_id_/translations.mjs +12 -10
  264. package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
  265. package/dist/astro/routes/api/relations/index.mjs +17 -15
  266. package/dist/astro/routes/api/relations/index.mjs.map +1 -1
  267. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
  268. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  269. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +53 -47
  270. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  271. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +53 -47
  272. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  273. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +53 -47
  274. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  275. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +53 -47
  276. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  277. package/dist/astro/routes/api/schema/collections/index.mjs +53 -47
  278. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  279. package/dist/astro/routes/api/schema/collections/reorder.mjs +53 -47
  280. package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
  281. package/dist/astro/routes/api/schema/index.mjs +10 -8
  282. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  283. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +53 -47
  284. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  285. package/dist/astro/routes/api/schema/orphans/index.mjs +53 -47
  286. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  287. package/dist/astro/routes/api/search/enable.mjs +10 -10
  288. package/dist/astro/routes/api/search/index.mjs +9 -9
  289. package/dist/astro/routes/api/search/rebuild.mjs +10 -10
  290. package/dist/astro/routes/api/search/stats.mjs +5 -5
  291. package/dist/astro/routes/api/search/suggest.mjs +9 -9
  292. package/dist/astro/routes/api/sections/_slug_.mjs +9 -9
  293. package/dist/astro/routes/api/sections/index.mjs +9 -9
  294. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +6 -6
  295. package/dist/astro/routes/api/settings/backups/archives/index.mjs +5 -5
  296. package/dist/astro/routes/api/settings/backups/export.mjs +6 -6
  297. package/dist/astro/routes/api/settings/backups/index.mjs +7 -7
  298. package/dist/astro/routes/api/settings/email.mjs +5 -5
  299. package/dist/astro/routes/api/settings/email.mjs.map +1 -1
  300. package/dist/astro/routes/api/settings.d.mts.map +1 -1
  301. package/dist/astro/routes/api/settings.mjs +20 -16
  302. package/dist/astro/routes/api/settings.mjs.map +1 -1
  303. package/dist/astro/routes/api/setup/admin-verify.mjs +12 -12
  304. package/dist/astro/routes/api/setup/admin.mjs +11 -11
  305. package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
  306. package/dist/astro/routes/api/setup/dev-bypass.mjs +35 -28
  307. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  308. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  309. package/dist/astro/routes/api/setup/index.mjs +32 -30
  310. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  311. package/dist/astro/routes/api/setup/status.mjs +3 -3
  312. package/dist/astro/routes/api/snapshot.mjs +7 -7
  313. package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts.map +1 -1
  314. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +20 -16
  315. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -1
  316. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.d.mts.map +1 -1
  317. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +21 -17
  318. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
  319. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.d.mts.map +1 -1
  320. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +25 -18
  321. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
  322. package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -1
  323. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +20 -16
  324. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
  325. package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +14 -14
  326. package/dist/astro/routes/api/taxonomies/_name_.d.mts.map +1 -1
  327. package/dist/astro/routes/api/taxonomies/_name_.mjs +26 -19
  328. package/dist/astro/routes/api/taxonomies/_name_.mjs.map +1 -1
  329. package/dist/astro/routes/api/taxonomies/index.d.mts.map +1 -1
  330. package/dist/astro/routes/api/taxonomies/index.mjs +20 -16
  331. package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
  332. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  333. package/dist/astro/routes/api/typegen.mjs +4 -4
  334. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  335. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  336. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  337. package/dist/astro/routes/api/widget-areas/_name_/reorder.d.mts.map +1 -1
  338. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +10 -8
  339. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
  340. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.d.mts.map +1 -1
  341. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +17 -13
  342. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
  343. package/dist/astro/routes/api/widget-areas/_name_/widgets.d.mts.map +1 -1
  344. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +15 -12
  345. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
  346. package/dist/astro/routes/api/widget-areas/_name_.d.mts.map +1 -1
  347. package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -6
  348. package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
  349. package/dist/astro/routes/api/widget-areas/index.d.mts.map +1 -1
  350. package/dist/astro/routes/api/widget-areas/index.mjs +15 -12
  351. package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
  352. package/dist/astro/routes/api/widget-components.mjs +2 -2
  353. package/dist/astro/routes/robots.txt.mjs +7 -7
  354. package/dist/astro/routes/sitemap-_collection_.xml.mjs +10 -10
  355. package/dist/astro/routes/sitemap.xml.mjs +8 -8
  356. package/dist/astro/types.d.mts +37 -17
  357. package/dist/astro/types.d.mts.map +1 -1
  358. package/dist/auth/providers/github.d.mts +1 -1
  359. package/dist/auth/providers/google.d.mts +1 -1
  360. package/dist/{authorize-DUElxgOQ.mjs → authorize-Bkf_UHhD.mjs} +2 -2
  361. package/dist/{authorize-DUElxgOQ.mjs.map → authorize-Bkf_UHhD.mjs.map} +1 -1
  362. package/dist/{backup-DWYpJlLj.mjs → backup-rwVAl9NI.mjs} +5 -5
  363. package/dist/{backup-DWYpJlLj.mjs.map → backup-rwVAl9NI.mjs.map} +1 -1
  364. package/dist/{bundle-vn56sLos.mjs → bundle-C8S2Zl6U.mjs} +4 -4
  365. package/dist/{bundle-vn56sLos.mjs.map → bundle-C8S2Zl6U.mjs.map} +1 -1
  366. package/dist/{byline-yjvWq4ip.mjs → byline-DuhF7NWl.mjs} +27 -14
  367. package/dist/byline-DuhF7NWl.mjs.map +1 -0
  368. package/dist/{byline-fields-txaLGoME.mjs → byline-fields-BovY4KYM.mjs} +2 -2
  369. package/dist/{byline-fields-txaLGoME.mjs.map → byline-fields-BovY4KYM.mjs.map} +1 -1
  370. package/dist/{byline-registry-DDun8sxh.mjs → byline-registry-Bpu9-MVs.mjs} +2 -2
  371. package/dist/{byline-registry-DDun8sxh.mjs.map → byline-registry-Bpu9-MVs.mjs.map} +1 -1
  372. package/dist/{bylines-DjXS5p0P.mjs → bylines-CJ5nd6Lw.mjs} +7 -7
  373. package/dist/{bylines-DjXS5p0P.mjs.map → bylines-CJ5nd6Lw.mjs.map} +1 -1
  374. package/dist/{bylines-Bu7pchQ4.mjs → bylines-RwBAeOF1.mjs} +3 -3
  375. package/dist/{bylines-Bu7pchQ4.mjs.map → bylines-RwBAeOF1.mjs.map} +1 -1
  376. package/dist/{cache-C46-3Irl.mjs → cache-BF5Kvn59.mjs} +4 -4
  377. package/dist/{cache-C46-3Irl.mjs.map → cache-BF5Kvn59.mjs.map} +1 -1
  378. package/dist/capture-triggers-CNW1q3HK.mjs +362 -0
  379. package/dist/capture-triggers-CNW1q3HK.mjs.map +1 -0
  380. package/dist/{challenge-store-SIL5DsF2.mjs → challenge-store-DQN_Yrck.mjs} +5 -3
  381. package/dist/challenge-store-DQN_Yrck.mjs.map +1 -0
  382. package/dist/chrome-tags-D8fiZG94.mjs +24 -0
  383. package/dist/chrome-tags-D8fiZG94.mjs.map +1 -0
  384. package/dist/{chunks-fiZ8G9RJ.mjs → chunks-C4szrD8o.mjs} +2 -2
  385. package/dist/{chunks-fiZ8G9RJ.mjs.map → chunks-C4szrD8o.mjs.map} +1 -1
  386. package/dist/cli/index.mjs +37 -53
  387. package/dist/cli/index.mjs.map +1 -1
  388. package/dist/client/cf-access.d.mts +1 -1
  389. package/dist/client/index.d.mts +74 -4
  390. package/dist/client/index.d.mts.map +1 -1
  391. package/dist/client/index.mjs +54 -2
  392. package/dist/client/index.mjs.map +1 -1
  393. package/dist/{collection-deletion-DQPvMD8R.mjs → collection-deletion-Bj07NHbj.mjs} +13 -356
  394. package/dist/collection-deletion-Bj07NHbj.mjs.map +1 -0
  395. package/dist/{comment-reaction-D-2J84UE.mjs → comment-reaction-BhrvSHml.mjs} +2 -2
  396. package/dist/{comment-reaction-D-2J84UE.mjs.map → comment-reaction-BhrvSHml.mjs.map} +1 -1
  397. package/dist/{comment-Dxlep6cE.mjs → comment-vxOzWbZN.mjs} +2 -2
  398. package/dist/{comment-Dxlep6cE.mjs.map → comment-vxOzWbZN.mjs.map} +1 -1
  399. package/dist/{comments-CTfZtLmM.mjs → comments-Ct4ensnf.mjs} +2 -2
  400. package/dist/{comments-CTfZtLmM.mjs.map → comments-Ct4ensnf.mjs.map} +1 -1
  401. package/dist/{components-CQy3lh9x.mjs → components-DJbo1z0S.mjs} +1 -1
  402. package/dist/{components-CQy3lh9x.mjs.map → components-DJbo1z0S.mjs.map} +1 -1
  403. package/dist/{config-COQzTe5Q.d.mts → config-B9eZ340f.d.mts} +1 -1
  404. package/dist/{config-COQzTe5Q.d.mts.map → config-B9eZ340f.d.mts.map} +1 -1
  405. package/dist/config-it29yDLA.mjs +1163 -0
  406. package/dist/config-it29yDLA.mjs.map +1 -0
  407. package/dist/{content-ZHCc1ngN.mjs → content-DjvM0T92.mjs} +86 -45
  408. package/dist/content-DjvM0T92.mjs.map +1 -0
  409. package/dist/{content-list-query-wJ2WN8Vp.d.mts → content-list-query-Btqd4uV2.d.mts} +1 -1
  410. package/dist/{content-list-query-wJ2WN8Vp.d.mts.map → content-list-query-Btqd4uV2.d.mts.map} +1 -1
  411. package/dist/{content-refresh-B_8SJzuU.mjs → content-refresh-Bwp-8yGy.mjs} +1139 -111
  412. package/dist/content-refresh-Bwp-8yGy.mjs.map +1 -0
  413. package/dist/{media-usage-BZCydfvu.mjs → content-repair-Czdp7jz5.mjs} +5 -196
  414. package/dist/content-repair-Czdp7jz5.mjs.map +1 -0
  415. package/dist/{context-ZldbZcle.mjs → context-CX6gxOLW.mjs} +55 -20
  416. package/dist/context-CX6gxOLW.mjs.map +1 -0
  417. package/dist/{cron-Y9aLtgrA.mjs → cron-C6o7VppJ.mjs} +1 -1
  418. package/dist/{cron-Y9aLtgrA.mjs.map → cron-C6o7VppJ.mjs.map} +1 -1
  419. package/dist/{dashboard-DvHbk_cv.mjs → dashboard-C2kTeeRI.mjs} +5 -5
  420. package/dist/{dashboard-DvHbk_cv.mjs.map → dashboard-C2kTeeRI.mjs.map} +1 -1
  421. package/dist/db/index.d.mts +3 -3
  422. package/dist/db/index.mjs +3 -3
  423. package/dist/db/index.mjs.map +1 -1
  424. package/dist/db/libsql-migrations.d.mts +1 -1
  425. package/dist/db/libsql-migrations.mjs +4 -4
  426. package/dist/db/libsql.d.mts +1 -1
  427. package/dist/db/postgres-migrations.d.mts +1 -1
  428. package/dist/db/postgres-migrations.mjs +4 -4
  429. package/dist/db/postgres.d.mts +1 -1
  430. package/dist/db/postgres.d.mts.map +1 -1
  431. package/dist/db/postgres.mjs +19 -3
  432. package/dist/db/postgres.mjs.map +1 -1
  433. package/dist/db/sqlite-migrations.d.mts +1 -1
  434. package/dist/db/sqlite-migrations.mjs +4 -4
  435. package/dist/db/sqlite.d.mts +1 -1
  436. package/dist/db/sqlite.mjs +3 -3
  437. package/dist/db/sqlite.mjs.map +1 -1
  438. package/dist/{default-DdDl3fxv.mjs → default-Clra32mw.mjs} +1 -1
  439. package/dist/{default-DdDl3fxv.mjs.map → default-Clra32mw.mjs.map} +1 -1
  440. package/dist/{deferred-tasks-SDWxrgzJ.d.mts → deferred-tasks-CvDETTJO.d.mts} +1 -1
  441. package/dist/{deferred-tasks-SDWxrgzJ.d.mts.map → deferred-tasks-CvDETTJO.d.mts.map} +1 -1
  442. package/dist/dev-typegen-DzOPfsNj.mjs +29 -0
  443. package/dist/dev-typegen-DzOPfsNj.mjs.map +1 -0
  444. package/dist/{device-flow-C0GdD_z8.mjs → device-flow-D2oCWvLJ.mjs} +5 -5
  445. package/dist/{device-flow-C0GdD_z8.mjs.map → device-flow-D2oCWvLJ.mjs.map} +1 -1
  446. package/dist/{direct-executor-CDXeJCSs.mjs → direct-executor-Bfa5_ToE.mjs} +3 -3
  447. package/dist/{direct-executor-CDXeJCSs.mjs.map → direct-executor-Bfa5_ToE.mjs.map} +1 -1
  448. package/dist/{dist-D_6CKR8z.mjs → dist-89xCrGqi.mjs} +1 -1
  449. package/dist/{dist-D_6CKR8z.mjs.map → dist-89xCrGqi.mjs.map} +1 -1
  450. package/dist/{email-console-BHNjIwiB.mjs → email-console-Dzb6uGYa.mjs} +1 -1
  451. package/dist/{email-console-BHNjIwiB.mjs.map → email-console-Dzb6uGYa.mjs.map} +1 -1
  452. package/dist/{enrich-_ySMEvEE.mjs → enrich-BX0IDp4z.mjs} +3 -3
  453. package/dist/{enrich-_ySMEvEE.mjs.map → enrich-BX0IDp4z.mjs.map} +1 -1
  454. package/dist/{error-DupyJZE2.mjs → error-DFrQjppn.mjs} +2 -2
  455. package/dist/{error-DupyJZE2.mjs.map → error-DFrQjppn.mjs.map} +1 -1
  456. package/dist/{errors-CLUPm7aD.mjs → errors-DsVoDUjJ.mjs} +24 -2
  457. package/dist/errors-DsVoDUjJ.mjs.map +1 -0
  458. package/dist/{escape-DUY2r2Zs.mjs → escape-HmHTC1Is.mjs} +1 -1
  459. package/dist/{escape-DUY2r2Zs.mjs.map → escape-HmHTC1Is.mjs.map} +1 -1
  460. package/dist/{field-defs-cache-BzwBlJrR.mjs → field-defs-cache-BM4SIRqH.mjs} +5 -5
  461. package/dist/{field-defs-cache-BzwBlJrR.mjs.map → field-defs-cache-BM4SIRqH.mjs.map} +1 -1
  462. package/dist/focal-point-BeWFo6Xx.mjs +23 -0
  463. package/dist/focal-point-BeWFo6Xx.mjs.map +1 -0
  464. package/dist/{fts-manager-DyZEohZC.mjs → fts-manager-Do1BlW2r.mjs} +3 -3
  465. package/dist/{fts-manager-DyZEohZC.mjs.map → fts-manager-Do1BlW2r.mjs.map} +1 -1
  466. package/dist/{hash-Cg0TXJPV.mjs → hash-3JJPlHF2.mjs} +1 -1
  467. package/dist/{hash-Cg0TXJPV.mjs.map → hash-3JJPlHF2.mjs.map} +1 -1
  468. package/dist/hook-result-DCh2WUpe.d.mts +31 -0
  469. package/dist/hook-result-DCh2WUpe.d.mts.map +1 -0
  470. package/dist/{hreflang-DwT2E0Sa.d.mts → hreflang-B6hbAuuO.d.mts} +2 -2
  471. package/dist/{hreflang-DwT2E0Sa.d.mts.map → hreflang-B6hbAuuO.d.mts.map} +1 -1
  472. package/dist/{hreflang-BBe83kKa.mjs → hreflang-DQbPZliA.mjs} +7 -7
  473. package/dist/{hreflang-BBe83kKa.mjs.map → hreflang-DQbPZliA.mjs.map} +1 -1
  474. package/dist/{identity-Ys6Dlc9C.mjs → identity-DksvEazf.mjs} +3 -3
  475. package/dist/{identity-Ys6Dlc9C.mjs.map → identity-DksvEazf.mjs.map} +1 -1
  476. package/dist/{import-DPkLmeXO.mjs → import-CJpaBBBT.mjs} +6 -6
  477. package/dist/{import-DPkLmeXO.mjs.map → import-CJpaBBBT.mjs.map} +1 -1
  478. package/dist/{index-BVBZj4-7.d.mts → index-BJNMCWmu.d.mts} +267 -82
  479. package/dist/index-BJNMCWmu.d.mts.map +1 -0
  480. package/dist/{index-C9KuQyO-.d.mts → index-Du8K7bWa.d.mts} +3 -3
  481. package/dist/{index-C9KuQyO-.d.mts.map → index-Du8K7bWa.d.mts.map} +1 -1
  482. package/dist/index.d.mts +22 -21
  483. package/dist/index.mjs +79 -73
  484. package/dist/{init-lock-6b309ZrF.mjs → init-lock-DJkX6Hto.mjs} +1 -1
  485. package/dist/{init-lock-6b309ZrF.mjs.map → init-lock-DJkX6Hto.mjs.map} +1 -1
  486. package/dist/{load-YJ2xl3ra.mjs → load-CawHjTOH.mjs} +2 -2
  487. package/dist/{load-YJ2xl3ra.mjs.map → load-CawHjTOH.mjs.map} +1 -1
  488. package/dist/{loader-CdG_Ci3H.mjs → loader-Z8vl7zTq.mjs} +48 -13
  489. package/dist/loader-Z8vl7zTq.mjs.map +1 -0
  490. package/dist/{manifest-builder-B252yQUf.mjs → manifest-builder-COtzuca2.mjs} +2 -2
  491. package/dist/{manifest-builder-B252yQUf.mjs.map → manifest-builder-COtzuca2.mjs.map} +1 -1
  492. package/dist/{manifest-schema-BXbUAHod.mjs → manifest-schema-BNWwR8Vd.mjs} +5 -5
  493. package/dist/manifest-schema-BNWwR8Vd.mjs.map +1 -0
  494. package/dist/media/image-endpoint.d.mts +4 -2
  495. package/dist/media/image-endpoint.d.mts.map +1 -1
  496. package/dist/media/image-endpoint.mjs +6 -4
  497. package/dist/media/image-endpoint.mjs.map +1 -1
  498. package/dist/media/index.d.mts +1 -1
  499. package/dist/media/index.mjs +13 -3
  500. package/dist/media/index.mjs.map +1 -1
  501. package/dist/media/local-runtime.d.mts +14 -12
  502. package/dist/media/local-runtime.d.mts.map +1 -1
  503. package/dist/media/local-runtime.mjs +12 -6
  504. package/dist/media/local-runtime.mjs.map +1 -1
  505. package/dist/{media-CzhAn2--.mjs → media-BBm8Wg3f.mjs} +69 -12
  506. package/dist/media-BBm8Wg3f.mjs.map +1 -0
  507. package/dist/{media-BWWWb8kg.mjs → media-BHjus86Y.mjs} +133 -9
  508. package/dist/media-BHjus86Y.mjs.map +1 -0
  509. package/dist/{media-allowlist-D6x-Us02.mjs → media-allowlist-CoTBWoDp.mjs} +3 -2
  510. package/dist/media-allowlist-CoTBWoDp.mjs.map +1 -0
  511. package/dist/media-folders-BGkATs8n.mjs +210 -0
  512. package/dist/media-folders-BGkATs8n.mjs.map +1 -0
  513. package/dist/{media-upload-CHBVn6m-.mjs → media-upload-BCidR-EY.mjs} +9 -9
  514. package/dist/{media-upload-CHBVn6m-.mjs.map → media-upload-BCidR-EY.mjs.map} +1 -1
  515. package/dist/{media-url-BvoX88FA.mjs → media-url-DHoPr0hq.mjs} +1 -1
  516. package/dist/{media-url-BvoX88FA.mjs.map → media-url-DHoPr0hq.mjs.map} +1 -1
  517. package/dist/media-usage-CdY5jhSR.mjs +1833 -0
  518. package/dist/media-usage-CdY5jhSR.mjs.map +1 -0
  519. package/dist/media-usage-activation-BgeMtK5C.mjs +78 -0
  520. package/dist/media-usage-activation-BgeMtK5C.mjs.map +1 -0
  521. package/dist/{media-usage-work-CLo_6tct.mjs → media-usage-work-CnGCdHxx.mjs} +4 -4
  522. package/dist/{media-usage-work-CLo_6tct.mjs.map → media-usage-work-CnGCdHxx.mjs.map} +1 -1
  523. package/dist/{media-usage-work-DrCbqBcl.mjs → media-usage-work-D6D9sMCA.mjs} +221 -59
  524. package/dist/media-usage-work-D6D9sMCA.mjs.map +1 -0
  525. package/dist/{media-usage-write-fence-aHEgN1zB.mjs → media-usage-write-fence-hjeD4u7s.mjs} +2 -2
  526. package/dist/{media-usage-write-fence-aHEgN1zB.mjs.map → media-usage-write-fence-hjeD4u7s.mjs.map} +1 -1
  527. package/dist/{menus-DSo5QeW0.mjs → menus-6qSwTNxG.mjs} +375 -107
  528. package/dist/menus-6qSwTNxG.mjs.map +1 -0
  529. package/dist/{menus-TCNSd2vp.mjs → menus-uvPLXMZf.mjs} +4 -4
  530. package/dist/{menus-TCNSd2vp.mjs.map → menus-uvPLXMZf.mjs.map} +1 -1
  531. package/dist/migrations/index.d.mts +1 -1
  532. package/dist/migrations/index.mjs +3 -3
  533. package/dist/{mime-DfIVjNkr.mjs → mime-CUIG6x84.mjs} +1 -1
  534. package/dist/mime-CUIG6x84.mjs.map +1 -0
  535. package/dist/{mode-BDoH_WcF.mjs → mode-DN_WneRx.mjs} +1 -1
  536. package/dist/{mode-BDoH_WcF.mjs.map → mode-DN_WneRx.mjs.map} +1 -1
  537. package/dist/node-sqlite-compat-BuJXIeMz.mjs +46 -0
  538. package/dist/node-sqlite-compat-BuJXIeMz.mjs.map +1 -0
  539. package/dist/{normalize-Bc-s_r-a.mjs → normalize-C2xNjTSG.mjs} +36 -5
  540. package/dist/normalize-C2xNjTSG.mjs.map +1 -0
  541. package/dist/{oauth-authorization-iOikmrJd.mjs → oauth-authorization-Dj6Kwc21.mjs} +5 -5
  542. package/dist/{oauth-authorization-iOikmrJd.mjs.map → oauth-authorization-Dj6Kwc21.mjs.map} +1 -1
  543. package/dist/{oauth-clients-CvI0nHMJ.mjs → oauth-clients-FD7I0J8m.mjs} +1 -1
  544. package/dist/{oauth-clients-CvI0nHMJ.mjs.map → oauth-clients-FD7I0J8m.mjs.map} +1 -1
  545. package/dist/{oauth-state-store-Ba8KZlwW.mjs → oauth-state-store-DkmDOmHs.mjs} +1 -1
  546. package/dist/{oauth-state-store-Ba8KZlwW.mjs.map → oauth-state-store-DkmDOmHs.mjs.map} +1 -1
  547. package/dist/{oauth-user-lookup-C5SQwGYC.mjs → oauth-user-lookup-CvIwGpYd.mjs} +1 -1
  548. package/dist/{oauth-user-lookup-C5SQwGYC.mjs.map → oauth-user-lookup-CvIwGpYd.mjs.map} +1 -1
  549. package/dist/object-cache/memory.d.mts +1 -1
  550. package/dist/{object-cache-71tPfV4A.mjs → object-cache-DVKnyOfy.mjs} +19 -3
  551. package/dist/object-cache-DVKnyOfy.mjs.map +1 -0
  552. package/dist/{options-CBXIHUoT.mjs → options-BHPeIbBd.mjs} +1 -1
  553. package/dist/{options-CBXIHUoT.mjs.map → options-BHPeIbBd.mjs.map} +1 -1
  554. package/dist/{options--hhz-jZ1.d.mts → options-CYb4y7hM.d.mts} +24 -11
  555. package/dist/options-CYb4y7hM.d.mts.map +1 -0
  556. package/dist/page/index.d.mts +2 -2
  557. package/dist/{parse-DFbuc-Fh.mjs → parse-Ckpg-Qfp.mjs} +2 -2
  558. package/dist/{parse-DFbuc-Fh.mjs.map → parse-Ckpg-Qfp.mjs.map} +1 -1
  559. package/dist/{passkey-config-DFAmUyuE.mjs → passkey-config-O_lj3y0k.mjs} +1 -1
  560. package/dist/{passkey-config-DFAmUyuE.mjs.map → passkey-config-O_lj3y0k.mjs.map} +1 -1
  561. package/dist/{patterns-CiyXeDgr.mjs → patterns-D9iDO3j8.mjs} +1 -1
  562. package/dist/{patterns-CiyXeDgr.mjs.map → patterns-D9iDO3j8.mjs.map} +1 -1
  563. package/dist/{placeholder-Dp0K3RkD.d.mts → placeholder-4X9BrRrp.d.mts} +7 -1
  564. package/dist/placeholder-4X9BrRrp.d.mts.map +1 -0
  565. package/dist/plugin-types.d.mts +8 -3
  566. package/dist/plugin-types.d.mts.map +1 -1
  567. package/dist/plugin-utils.d.mts +10 -10
  568. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
  569. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  570. package/dist/{policy-TluUIxuu.mjs → policy-Dhtwntrt.mjs} +2 -2
  571. package/dist/{policy-TluUIxuu.mjs.map → policy-Dhtwntrt.mjs.map} +1 -1
  572. package/dist/{portable-text-imBW5cHK.mjs → portable-text-U85hOafW.mjs} +1 -1
  573. package/dist/{portable-text-imBW5cHK.mjs.map → portable-text-U85hOafW.mjs.map} +1 -1
  574. package/dist/{preview-PW-nOnq4.mjs → preview-B_vJLvf8.mjs} +2 -2
  575. package/dist/{preview-PW-nOnq4.mjs.map → preview-B_vJLvf8.mjs.map} +1 -1
  576. package/dist/{protocol-DjsJHTlK.d.mts → protocol-JdD2YhY9.d.mts} +2 -2
  577. package/dist/{protocol-DjsJHTlK.d.mts.map → protocol-JdD2YhY9.d.mts.map} +1 -1
  578. package/dist/{public-url-CWoFhQT8.mjs → public-url-CarhZJgf.mjs} +1 -1
  579. package/dist/{public-url-CWoFhQT8.mjs.map → public-url-CarhZJgf.mjs.map} +1 -1
  580. package/dist/{query-zTVAR1Sl.mjs → query-BZKZcG5b.mjs} +15 -15
  581. package/dist/{query-zTVAR1Sl.mjs.map → query-BZKZcG5b.mjs.map} +1 -1
  582. package/dist/{query-BWs7O5Il.mjs → query-BndaFxxp.mjs} +5 -5
  583. package/dist/{query-BWs7O5Il.mjs.map → query-BndaFxxp.mjs.map} +1 -1
  584. package/dist/{rate-limit-Bx4g9m3p.mjs → rate-limit-BZmATC6W.mjs} +2 -2
  585. package/dist/{rate-limit-Bx4g9m3p.mjs.map → rate-limit-BZmATC6W.mjs.map} +1 -1
  586. package/dist/{redirect-COsS6SWJ.mjs → redirect-CZkbozu9.mjs} +2 -2
  587. package/dist/{redirect-COsS6SWJ.mjs.map → redirect-CZkbozu9.mjs.map} +1 -1
  588. package/dist/{redirect-BgAOGHDq.mjs → redirect-CzqnLxK-.mjs} +1 -1
  589. package/dist/{redirect-BgAOGHDq.mjs.map → redirect-CzqnLxK-.mjs.map} +1 -1
  590. package/dist/{redirects-DTDHWH31.mjs → redirects-BLNOWQTn.mjs} +64 -29
  591. package/dist/redirects-BLNOWQTn.mjs.map +1 -0
  592. package/dist/{redirects-DyChwDkG.mjs → redirects-CqzNM0KM.mjs} +5 -5
  593. package/dist/{redirects-DyChwDkG.mjs.map → redirects-CqzNM0KM.mjs.map} +1 -1
  594. package/dist/{registry-Cqa2I749.mjs → registry-AZAsRsor.mjs} +30 -126
  595. package/dist/registry-AZAsRsor.mjs.map +1 -0
  596. package/dist/{relation-BnHB0T4J.mjs → relation-04DfKRTO.mjs} +2 -2
  597. package/dist/{relation-BnHB0T4J.mjs.map → relation-04DfKRTO.mjs.map} +1 -1
  598. package/dist/{relations-BBApI4e2.mjs → relations-C4htk5sb.mjs} +7 -7
  599. package/dist/relations-C4htk5sb.mjs.map +1 -0
  600. package/dist/{relations-D5sM5fW0.mjs → relations-CcFwDJdJ.mjs} +4 -4
  601. package/dist/{relations-D5sM5fW0.mjs.map → relations-CcFwDJdJ.mjs.map} +1 -1
  602. package/dist/{relations-BHo5A8Zf.d.mts → relations-DnRK4YIT.d.mts} +347 -54
  603. package/dist/relations-DnRK4YIT.d.mts.map +1 -0
  604. package/dist/{request-cache-BSUptuJR.mjs → request-cache-DH9zdCOD.mjs} +1 -1
  605. package/dist/{request-cache-BSUptuJR.mjs.map → request-cache-DH9zdCOD.mjs.map} +1 -1
  606. package/dist/request-context.d.mts +1 -1
  607. package/dist/{request-meta-DgZJh3zG.mjs → request-meta-DYxLlnmz.mjs} +2 -2
  608. package/dist/{request-meta-DgZJh3zG.mjs.map → request-meta-DYxLlnmz.mjs.map} +1 -1
  609. package/dist/{resolve-DOhVEvty.mjs → resolve-prZ3mP7l.mjs} +1 -1
  610. package/dist/{resolve-DOhVEvty.mjs.map → resolve-prZ3mP7l.mjs.map} +1 -1
  611. package/dist/{runner-C1T11CfC.mjs → runner-BLIDKpmA.mjs} +471 -290
  612. package/dist/runner-BLIDKpmA.mjs.map +1 -0
  613. package/dist/{runner-BYHZn3Jy.d.mts → runner-YKfIMnaF.d.mts} +3 -2
  614. package/dist/runner-YKfIMnaF.d.mts.map +1 -0
  615. package/dist/runtime.d.mts +11 -11
  616. package/dist/runtime.mjs +1 -1
  617. package/dist/{schema-DZ5YVVZj.mjs → schema-CGnBeLiW.mjs} +3 -3
  618. package/dist/{schema-DZ5YVVZj.mjs.map → schema-CGnBeLiW.mjs.map} +1 -1
  619. package/dist/{schema-2AOsSn2v.mjs → schema-DYr-Vd-L.mjs} +36 -6
  620. package/dist/{schema-2AOsSn2v.mjs.map → schema-DYr-Vd-L.mjs.map} +1 -1
  621. package/dist/{scopes-8ClPF2bC.mjs → scopes-BhWvBavj.mjs} +3 -3
  622. package/dist/{scopes-8ClPF2bC.mjs.map → scopes-BhWvBavj.mjs.map} +1 -1
  623. package/dist/{search-u1QYdOCG.mjs → search-C-qDk4kx.mjs} +4 -4
  624. package/dist/{search-u1QYdOCG.mjs.map → search-C-qDk4kx.mjs.map} +1 -1
  625. package/dist/{secrets-BEgsAQEU.mjs → secrets-rEtHt-6z.mjs} +3 -3
  626. package/dist/{secrets-BEgsAQEU.mjs.map → secrets-rEtHt-6z.mjs.map} +1 -1
  627. package/dist/{sections-DmHviGyM.mjs → sections-B_nYvlwn.mjs} +2 -2
  628. package/dist/{sections-DmHviGyM.mjs.map → sections-B_nYvlwn.mjs.map} +1 -1
  629. package/dist/seed/index.d.mts +2 -2
  630. package/dist/seed/index.mjs +25 -23
  631. package/dist/seo/index.d.mts +3 -3
  632. package/dist/seo/index.mjs +17 -15
  633. package/dist/seo/index.mjs.map +1 -1
  634. package/dist/{seo-Dq4G-RrO.mjs → seo-926NRIgt.mjs} +3 -3
  635. package/dist/{seo-Dq4G-RrO.mjs.map → seo-926NRIgt.mjs.map} +1 -1
  636. package/dist/{seo-AFpVvTqr.mjs → seo-CVWkdv6x.mjs} +1 -1
  637. package/dist/{seo-AFpVvTqr.mjs.map → seo-CVWkdv6x.mjs.map} +1 -1
  638. package/dist/{service-BpArayPr.mjs → service-CJl3ZAYs.mjs} +3 -3
  639. package/dist/{service-BpArayPr.mjs.map → service-CJl3ZAYs.mjs.map} +1 -1
  640. package/dist/{session-user-BEorKVT-.mjs → session-user-BlMgqCHS.mjs} +1 -1
  641. package/dist/{session-user-BEorKVT-.mjs.map → session-user-BlMgqCHS.mjs.map} +1 -1
  642. package/dist/{settings-xRdoUZr0.mjs → settings--wobrkkC.mjs} +21 -8
  643. package/dist/settings--wobrkkC.mjs.map +1 -0
  644. package/dist/{settings-DE9xtzqd.mjs → settings-Cwm6o2jY.mjs} +3 -3
  645. package/dist/{settings-DE9xtzqd.mjs.map → settings-Cwm6o2jY.mjs.map} +1 -1
  646. package/dist/{setup-complete-BnT8pq_C.mjs → setup-complete-DQbClIN2.mjs} +2 -2
  647. package/dist/{setup-complete-BnT8pq_C.mjs.map → setup-complete-DQbClIN2.mjs.map} +1 -1
  648. package/dist/{setup-nonce-CBitf_oQ.mjs → setup-nonce-DZCFS2sn.mjs} +1 -1
  649. package/dist/{setup-nonce-CBitf_oQ.mjs.map → setup-nonce-DZCFS2sn.mjs.map} +1 -1
  650. package/dist/{single-flight-cache-B0eLKS6K.mjs → single-flight-cache-BPuGPdcN.mjs} +2 -2
  651. package/dist/{single-flight-cache-B0eLKS6K.mjs.map → single-flight-cache-BPuGPdcN.mjs.map} +1 -1
  652. package/dist/{site-url-DjpIXMXJ.mjs → site-url-BrxA_E9H.mjs} +3 -3
  653. package/dist/{site-url-DjpIXMXJ.mjs.map → site-url-BrxA_E9H.mjs.map} +1 -1
  654. package/dist/{snapshot-BBlGkb2q.mjs → snapshot--OCDTqdb.mjs} +1 -1
  655. package/dist/{snapshot-BBlGkb2q.mjs.map → snapshot--OCDTqdb.mjs.map} +1 -1
  656. package/dist/{ssrf-Cpvg_n3A.mjs → ssrf-5sIM5jzl.mjs} +23 -6
  657. package/dist/{ssrf-Cpvg_n3A.mjs.map → ssrf-5sIM5jzl.mjs.map} +1 -1
  658. package/dist/{state-DYwPue40.mjs → state-BpFVsJZB.mjs} +12 -4
  659. package/dist/state-BpFVsJZB.mjs.map +1 -0
  660. package/dist/{status-CWVYpbD8.mjs → status-B777-dXz.mjs} +1 -1
  661. package/dist/{status-CWVYpbD8.mjs.map → status-B777-dXz.mjs.map} +1 -1
  662. package/dist/storage/local.d.mts +2 -1
  663. package/dist/storage/local.d.mts.map +1 -1
  664. package/dist/storage/local.mjs +1 -1
  665. package/dist/storage/local.mjs.map +1 -1
  666. package/dist/storage/s3.d.mts +2 -1
  667. package/dist/storage/s3.d.mts.map +1 -1
  668. package/dist/storage/s3.mjs +3 -2
  669. package/dist/storage/s3.mjs.map +1 -1
  670. package/dist/{target-DixqqV5i.mjs → target-BmR0iTwz.mjs} +1 -1
  671. package/dist/{target-DixqqV5i.mjs.map → target-BmR0iTwz.mjs.map} +1 -1
  672. package/dist/{taxonomies-OHUIiIpI.mjs → taxonomies-DkAkMupr.mjs} +17 -14
  673. package/dist/taxonomies-DkAkMupr.mjs.map +1 -0
  674. package/dist/{taxonomies-BlrncHi9.mjs → taxonomies-Wfy_Lg12.mjs} +43 -17
  675. package/dist/taxonomies-Wfy_Lg12.mjs.map +1 -0
  676. package/dist/{taxonomy-BqCZ1TdU.mjs → taxonomy-tJepplLL.mjs} +5 -5
  677. package/dist/{taxonomy-BqCZ1TdU.mjs.map → taxonomy-tJepplLL.mjs.map} +1 -1
  678. package/dist/{tokens-C39klX8R.mjs → tokens-BCO1oruF.mjs} +1 -1
  679. package/dist/{tokens-C39klX8R.mjs.map → tokens-BCO1oruF.mjs.map} +1 -1
  680. package/dist/{transaction-NQj4VJ7Z.mjs → transaction-0KOpOyMu.mjs} +2 -1
  681. package/dist/{transaction-NQj4VJ7Z.mjs.map → transaction-0KOpOyMu.mjs.map} +1 -1
  682. package/dist/{transport-DBJwa-Si.mjs → transport-Dr3qtwXC.mjs} +4 -3
  683. package/dist/{transport-DBJwa-Si.mjs.map → transport-Dr3qtwXC.mjs.map} +1 -1
  684. package/dist/{transport-RQE36APG.d.mts → transport-xh6YKW2G.d.mts} +1 -1
  685. package/dist/{transport-RQE36APG.d.mts.map → transport-xh6YKW2G.d.mts.map} +1 -1
  686. package/dist/{trusted-proxy-CEVekpby.mjs → trusted-proxy-BR5F1Otp.mjs} +1 -1
  687. package/dist/{trusted-proxy-CEVekpby.mjs.map → trusted-proxy-BR5F1Otp.mjs.map} +1 -1
  688. package/dist/{types-BW1syrlw.mjs → types-3qNFPlci.mjs} +1 -1
  689. package/dist/{types-BW1syrlw.mjs.map → types-3qNFPlci.mjs.map} +1 -1
  690. package/dist/types-75KIqzL9.mjs.map +1 -1
  691. package/dist/{types-DrJrVCT1.d.mts → types-B6GrPic4.d.mts} +9 -3
  692. package/dist/{types-DrJrVCT1.d.mts.map → types-B6GrPic4.d.mts.map} +1 -1
  693. package/dist/{types-DN64_zzZ.d.mts → types-BuHW4xDE.d.mts} +11 -10
  694. package/dist/types-BuHW4xDE.d.mts.map +1 -0
  695. package/dist/{types-DWRhk9xZ.d.mts → types-C00oyCZp.d.mts} +1 -1
  696. package/dist/{types-DWRhk9xZ.d.mts.map → types-C00oyCZp.d.mts.map} +1 -1
  697. package/dist/{types-BntycPTF.d.mts → types-CDtkNSFQ.d.mts} +1 -1
  698. package/dist/{types-BntycPTF.d.mts.map → types-CDtkNSFQ.d.mts.map} +1 -1
  699. package/dist/{types-Dfk8KI5R.d.mts → types-CPeXo1wF.d.mts} +1 -1
  700. package/dist/{types-Dfk8KI5R.d.mts.map → types-CPeXo1wF.d.mts.map} +1 -1
  701. package/dist/types-CQPWhPxE.mjs.map +1 -1
  702. package/dist/{types-BcqdcT-r.d.mts → types-CXFkwEGC.d.mts} +3 -2
  703. package/dist/types-CXFkwEGC.d.mts.map +1 -0
  704. package/dist/{types-CtzytVXn.mjs → types-DM3vioBY.mjs} +1 -1
  705. package/dist/{types-CtzytVXn.mjs.map → types-DM3vioBY.mjs.map} +1 -1
  706. package/dist/{types-CXnOzDe1.d.mts → types-DczELMop.d.mts} +1 -1
  707. package/dist/{types-CXnOzDe1.d.mts.map → types-DczELMop.d.mts.map} +1 -1
  708. package/dist/types-DfuTR6dL.mjs +3 -0
  709. package/dist/{types-DQIDRKF2.d.mts → types-FV23hEoW.d.mts} +10 -1
  710. package/dist/types-FV23hEoW.d.mts.map +1 -0
  711. package/dist/{types-C2pMvO62.d.mts → types-xDMtUFnO.d.mts} +2 -1
  712. package/dist/types-xDMtUFnO.d.mts.map +1 -0
  713. package/dist/{upload-attempts-D4FS8LT6.mjs → upload-attempts-BsqoQ1fn.mjs} +1 -1
  714. package/dist/{upload-attempts-D4FS8LT6.mjs.map → upload-attempts-BsqoQ1fn.mjs.map} +1 -1
  715. package/dist/{url-pattern-BHS0V5Ng.mjs → url-pattern-D4c2czpJ.mjs} +1 -1
  716. package/dist/{url-pattern-BHS0V5Ng.mjs.map → url-pattern-D4c2czpJ.mjs.map} +1 -1
  717. package/dist/{user-DJZfUJVE.mjs → user-BTZ40aq6.mjs} +2 -2
  718. package/dist/{user-DJZfUJVE.mjs.map → user-BTZ40aq6.mjs.map} +1 -1
  719. package/dist/{utils-D4vD_Y98.mjs → utils-BKvNjjhY.mjs} +1 -1
  720. package/dist/{utils-D4vD_Y98.mjs.map → utils-BKvNjjhY.mjs.map} +1 -1
  721. package/dist/{validate-BMLBvxOC.mjs → validate-DE_LRwo9.mjs} +2 -2
  722. package/dist/{validate-BMLBvxOC.mjs.map → validate-DE_LRwo9.mjs.map} +1 -1
  723. package/dist/{validate-CTqt5nuE.d.mts → validate-DUP27OYD.d.mts} +5 -5
  724. package/dist/{validate-CTqt5nuE.d.mts.map → validate-DUP27OYD.d.mts.map} +1 -1
  725. package/dist/{validation-npvENUCB.mjs → validation-BMImFJ11.mjs} +10 -8
  726. package/dist/{validation-npvENUCB.mjs.map → validation-BMImFJ11.mjs.map} +1 -1
  727. package/dist/version-Bii3suaO.mjs +7 -0
  728. package/dist/{version-DB_fqCSI.mjs.map → version-Bii3suaO.mjs.map} +1 -1
  729. package/dist/{widgets-BD3ho7Rq.mjs → widgets-D3gJ10xU.mjs} +18 -5
  730. package/dist/widgets-D3gJ10xU.mjs.map +1 -0
  731. package/dist/{wxr-taxonomies-Ca68766D.d.mts → wxr-taxonomies-B2Og_6rU.d.mts} +1 -1
  732. package/dist/{wxr-taxonomies-Ca68766D.d.mts.map → wxr-taxonomies-B2Og_6rU.d.mts.map} +1 -1
  733. package/dist/{wxr-taxonomies-B1R3vBoD.mjs → wxr-taxonomies-Cxngsjwo.mjs} +4 -4
  734. package/dist/{wxr-taxonomies-B1R3vBoD.mjs.map → wxr-taxonomies-Cxngsjwo.mjs.map} +1 -1
  735. package/dist/{zod-generator-LC-on_Vb.mjs → zod-generator-BuYSQJB7.mjs} +32 -22
  736. package/dist/zod-generator-BuYSQJB7.mjs.map +1 -0
  737. package/package.json +20 -9
  738. package/src/api/errors.ts +22 -0
  739. package/src/api/handlers/content.ts +49 -11
  740. package/src/api/handlers/index.ts +18 -0
  741. package/src/api/handlers/manifest.ts +161 -12
  742. package/src/api/handlers/media-allowlist.ts +1 -0
  743. package/src/api/handlers/media-folders.ts +130 -0
  744. package/src/api/handlers/media-usage-activation.ts +102 -0
  745. package/src/api/handlers/media-usage.ts +108 -0
  746. package/src/api/handlers/media.ts +92 -2
  747. package/src/api/handlers/registry.ts +483 -561
  748. package/src/api/handlers/taxonomies.ts +16 -9
  749. package/src/api/openapi/document.ts +277 -7
  750. package/src/api/parse.ts +1 -1
  751. package/src/api/schemas/auth.ts +4 -4
  752. package/src/api/schemas/bylines.ts +3 -3
  753. package/src/api/schemas/comments.ts +1 -1
  754. package/src/api/schemas/common.ts +3 -2
  755. package/src/api/schemas/content.ts +78 -22
  756. package/src/api/schemas/media-usage.ts +77 -0
  757. package/src/api/schemas/media.ts +121 -0
  758. package/src/api/schemas/redirects.ts +6 -1
  759. package/src/api/schemas/schema.ts +4 -0
  760. package/src/api/schemas/setup.ts +1 -1
  761. package/src/api/schemas/taxonomies.ts +2 -2
  762. package/src/api/schemas/users.ts +1 -1
  763. package/src/api/types.ts +23 -10
  764. package/src/astro/dev-typegen.ts +35 -0
  765. package/src/astro/image-endpoint.ts +2 -2
  766. package/src/astro/integration/dev-typegen.ts +91 -0
  767. package/src/astro/integration/index.ts +9 -44
  768. package/src/astro/integration/routes.ts +23 -0
  769. package/src/astro/integration/vite-config.ts +23 -9
  770. package/src/astro/middleware/auth.ts +4 -0
  771. package/src/astro/middleware.ts +5 -7
  772. package/src/astro/routes/api/admin/api-tokens/index.ts +4 -1
  773. package/src/astro/routes/api/admin/media-usage/activation.ts +42 -0
  774. package/src/astro/routes/api/admin/media-usage/progress.ts +37 -0
  775. package/src/astro/routes/api/admin/oauth-clients/[id].ts +1 -1
  776. package/src/astro/routes/api/admin/oauth-clients/index.ts +1 -1
  777. package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +4 -0
  778. package/src/astro/routes/api/admin/plugins/registry/artifact.ts +247 -101
  779. package/src/astro/routes/api/admin/plugins/registry/install.ts +4 -0
  780. package/src/astro/routes/api/admin/plugins/registry/verify.ts +60 -0
  781. package/src/astro/routes/api/auth/me.ts +14 -2
  782. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +8 -2
  783. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +2 -1
  784. package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +6 -2
  785. package/src/astro/routes/api/manifest.ts +1 -1
  786. package/src/astro/routes/api/media/[id]/replace.ts +114 -0
  787. package/src/astro/routes/api/media/[id].ts +3 -0
  788. package/src/astro/routes/api/media/file/[...key].ts +4 -1
  789. package/src/astro/routes/api/media/folders/[id].ts +55 -0
  790. package/src/astro/routes/api/media/folders/index.ts +37 -0
  791. package/src/astro/routes/api/media/upload-url.ts +8 -4
  792. package/src/astro/routes/api/media.ts +66 -10
  793. package/src/astro/routes/api/menus/[name]/items/[id].ts +7 -2
  794. package/src/astro/routes/api/menus/[name]/items.ts +4 -1
  795. package/src/astro/routes/api/menus/[name]/reorder.ts +4 -1
  796. package/src/astro/routes/api/menus/[name]/translations.ts +4 -1
  797. package/src/astro/routes/api/menus/[name].ts +7 -2
  798. package/src/astro/routes/api/menus/index.ts +4 -1
  799. package/src/astro/routes/api/settings/email.ts +1 -1
  800. package/src/astro/routes/api/settings.ts +4 -1
  801. package/src/astro/routes/api/setup/dev-bypass.ts +12 -0
  802. package/src/astro/routes/api/taxonomies/[name]/reorder.ts +4 -1
  803. package/src/astro/routes/api/taxonomies/[name]/terms/[slug]/translations.ts +4 -1
  804. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +7 -2
  805. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +4 -1
  806. package/src/astro/routes/api/taxonomies/[name].ts +7 -2
  807. package/src/astro/routes/api/taxonomies/index.ts +4 -1
  808. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +3 -1
  809. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +5 -2
  810. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +3 -1
  811. package/src/astro/routes/api/widget-areas/[name].ts +3 -1
  812. package/src/astro/routes/api/widget-areas/index.ts +3 -1
  813. package/src/astro/types.ts +34 -6
  814. package/src/auth/challenge-store.ts +3 -1
  815. package/src/aws-sdk.d.ts +1 -0
  816. package/src/cache/chrome-tags.ts +24 -0
  817. package/src/client/index.ts +140 -2
  818. package/src/client/transport.ts +6 -2
  819. package/src/components/EmDashImage.astro +156 -51
  820. package/src/components/EmDashMedia.astro +2 -1
  821. package/src/components/Gallery.astro +7 -0
  822. package/src/components/InlinePortableTextEditor.tsx +136 -2
  823. package/src/components/LiveSearch.astro +23 -0
  824. package/src/components/inline-code-block.tsx +180 -49
  825. package/src/components/widgets/Archives.astro +2 -37
  826. package/src/content/converters/gallery.ts +2 -0
  827. package/src/content/converters/index.ts +2 -0
  828. package/src/content/converters/mark-safety.ts +1 -0
  829. package/src/content/converters/portable-text-identity.ts +139 -0
  830. package/src/content/converters/portable-text-to-prosemirror.ts +202 -69
  831. package/src/content/converters/prosemirror-to-portable-text.ts +96 -25
  832. package/src/content/converters/types.ts +3 -0
  833. package/src/database/connection.ts +2 -31
  834. package/src/database/index.ts +1 -1
  835. package/src/database/json-recordset.ts +16 -0
  836. package/src/database/migrations/017_authorization_codes.ts +14 -4
  837. package/src/database/migrations/019_i18n.ts +1 -1
  838. package/src/database/migrations/071_restore_content_bylines_table.ts +43 -0
  839. package/src/database/migrations/072_media_folders.ts +72 -0
  840. package/src/database/migrations/073_media_focal_point.ts +57 -0
  841. package/src/database/migrations/074_content_deleted_scheduled_index.ts +55 -0
  842. package/src/database/migrations/runner.ts +11 -3
  843. package/src/database/repositories/byline.ts +32 -16
  844. package/src/database/repositories/content.ts +119 -51
  845. package/src/database/repositories/media-folders.ts +115 -0
  846. package/src/database/repositories/media-usage-work.ts +395 -111
  847. package/src/database/repositories/media-usage.ts +1104 -47
  848. package/src/database/repositories/media.ts +156 -29
  849. package/src/database/repositories/plugin-storage.ts +74 -9
  850. package/src/database/repositories/types.ts +8 -7
  851. package/src/database/transaction.ts +1 -0
  852. package/src/database/types.ts +13 -0
  853. package/src/db/adapters.ts +10 -3
  854. package/src/db/node-sqlite-compat.ts +78 -0
  855. package/src/db/postgres.ts +23 -0
  856. package/src/db/sqlite.ts +3 -3
  857. package/src/emdash-runtime.ts +186 -251
  858. package/src/fields/boolean.ts +1 -1
  859. package/src/fields/datetime.ts +1 -1
  860. package/src/fields/file.ts +1 -1
  861. package/src/fields/image.ts +7 -1
  862. package/src/fields/integer.ts +1 -1
  863. package/src/fields/json.ts +1 -1
  864. package/src/fields/multiselect.ts +1 -1
  865. package/src/fields/number.ts +1 -1
  866. package/src/fields/portable-text.ts +1 -1
  867. package/src/fields/richtext.ts +1 -1
  868. package/src/fields/select.ts +1 -1
  869. package/src/fields/slug.ts +1 -1
  870. package/src/fields/text.ts +1 -1
  871. package/src/fields/textarea.ts +1 -1
  872. package/src/fields/types.ts +8 -4
  873. package/src/index.ts +30 -3
  874. package/src/loader.ts +63 -10
  875. package/src/mcp/server.ts +228 -24
  876. package/src/media/focal-point.ts +37 -0
  877. package/src/media/image-endpoint.ts +5 -2
  878. package/src/media/local-runtime.ts +8 -0
  879. package/src/media/mime.ts +1 -0
  880. package/src/media/normalize.ts +25 -4
  881. package/src/media/types.ts +10 -0
  882. package/src/media/usage/activation.ts +162 -35
  883. package/src/media/usage/capture-triggers.ts +19 -3
  884. package/src/media/usage/cleanup.ts +9 -8
  885. package/src/media/usage/collection-deletion-processor.ts +7 -3
  886. package/src/media/usage/collection-deletion.ts +10 -0
  887. package/src/media/usage/content-refresh.ts +258 -39
  888. package/src/media/usage/content-snapshots.ts +128 -31
  889. package/src/media/usage/extractor.ts +32 -13
  890. package/src/media/usage/maintenance-engine.ts +166 -0
  891. package/src/media/usage/reconciliation-processor.ts +106 -29
  892. package/src/media/usage/reconciliation.ts +90 -7
  893. package/src/media/usage/work-processor.ts +342 -101
  894. package/src/menus/index.ts +16 -0
  895. package/src/object-cache/index.ts +44 -2
  896. package/src/plugin-types.ts +18 -1
  897. package/src/plugins/hooks.ts +4 -1
  898. package/src/plugins/index.ts +7 -0
  899. package/src/plugins/manager.ts +2 -1
  900. package/src/plugins/manifest-schema.ts +1 -1
  901. package/src/plugins/routes.ts +3 -1
  902. package/src/plugins/sandbox/hook-result.ts +52 -0
  903. package/src/plugins/sandbox/index.ts +10 -0
  904. package/src/plugins/save-rejection.ts +20 -0
  905. package/src/plugins/scheduler/node.ts +1 -13
  906. package/src/plugins/scheduler/types.ts +0 -2
  907. package/src/plugins/state.ts +13 -3
  908. package/src/plugins/types.ts +6 -0
  909. package/src/registry/artifact-checksum.ts +15 -0
  910. package/src/registry/artifact-fetch.ts +530 -0
  911. package/src/registry/artifact-verification.ts +85 -0
  912. package/src/registry/authoritative-records.ts +180 -0
  913. package/src/registry/config.ts +24 -6
  914. package/src/registry/record-labels.ts +28 -0
  915. package/src/registry/types.ts +12 -15
  916. package/src/schema/registry.ts +26 -3
  917. package/src/schema/types.ts +1 -0
  918. package/src/schema/zod-generator.ts +28 -17
  919. package/src/security/ssrf.ts +26 -4
  920. package/src/seed/apply.ts +4 -4
  921. package/src/settings/index.ts +16 -0
  922. package/src/storage/local.ts +1 -0
  923. package/src/storage/s3.ts +2 -0
  924. package/src/storage/types.ts +2 -0
  925. package/src/taxonomies/index.ts +40 -20
  926. package/src/ui.ts +2 -0
  927. package/src/widgets/archives.ts +67 -0
  928. package/src/widgets/index.ts +16 -0
  929. package/dist/api-BeBglVxU.mjs +0 -4695
  930. package/dist/api-BeBglVxU.mjs.map +0 -1
  931. package/dist/byline-yjvWq4ip.mjs.map +0 -1
  932. package/dist/challenge-store-SIL5DsF2.mjs.map +0 -1
  933. package/dist/collection-deletion-DQPvMD8R.mjs.map +0 -1
  934. package/dist/content-ZHCc1ngN.mjs.map +0 -1
  935. package/dist/content-refresh-B_8SJzuU.mjs.map +0 -1
  936. package/dist/context-ZldbZcle.mjs.map +0 -1
  937. package/dist/errors-CLUPm7aD.mjs.map +0 -1
  938. package/dist/index-BVBZj4-7.d.mts.map +0 -1
  939. package/dist/loader-CdG_Ci3H.mjs.map +0 -1
  940. package/dist/manifest-schema-BXbUAHod.mjs.map +0 -1
  941. package/dist/media-BWWWb8kg.mjs.map +0 -1
  942. package/dist/media-CzhAn2--.mjs.map +0 -1
  943. package/dist/media-allowlist-D6x-Us02.mjs.map +0 -1
  944. package/dist/media-usage-BZCydfvu.mjs.map +0 -1
  945. package/dist/media-usage-work-DrCbqBcl.mjs.map +0 -1
  946. package/dist/menus-DSo5QeW0.mjs.map +0 -1
  947. package/dist/mime-DfIVjNkr.mjs.map +0 -1
  948. package/dist/normalize-Bc-s_r-a.mjs.map +0 -1
  949. package/dist/object-cache-71tPfV4A.mjs.map +0 -1
  950. package/dist/options--hhz-jZ1.d.mts.map +0 -1
  951. package/dist/placeholder-Dp0K3RkD.d.mts.map +0 -1
  952. package/dist/redirects-DTDHWH31.mjs.map +0 -1
  953. package/dist/registry-Cqa2I749.mjs.map +0 -1
  954. package/dist/relations-BBApI4e2.mjs.map +0 -1
  955. package/dist/relations-BHo5A8Zf.d.mts.map +0 -1
  956. package/dist/runner-BYHZn3Jy.d.mts.map +0 -1
  957. package/dist/runner-C1T11CfC.mjs.map +0 -1
  958. package/dist/settings-xRdoUZr0.mjs.map +0 -1
  959. package/dist/state-DYwPue40.mjs.map +0 -1
  960. package/dist/taxonomies-BlrncHi9.mjs.map +0 -1
  961. package/dist/taxonomies-OHUIiIpI.mjs.map +0 -1
  962. package/dist/types-BcqdcT-r.d.mts.map +0 -1
  963. package/dist/types-C2pMvO62.d.mts.map +0 -1
  964. package/dist/types-CP1CflWf.mjs +0 -3
  965. package/dist/types-DN64_zzZ.d.mts.map +0 -1
  966. package/dist/types-DQIDRKF2.d.mts.map +0 -1
  967. package/dist/version-DB_fqCSI.mjs +0 -7
  968. package/dist/widgets-BD3ho7Rq.mjs.map +0 -1
  969. package/dist/zod-generator-LC-on_Vb.mjs.map +0 -1
  970. /package/dist/{api-tokens-rIXC6LdE.mjs → api-tokens-BfYts-sq.mjs} +0 -0
  971. /package/dist/{ssrf-DpXgKsna.mjs → ssrf-Dg3cIT-b.mjs} +0 -0
@@ -1,52 +1,27 @@
1
1
  /**
2
- * Registry plugin install handler.
3
- *
4
- * Installs a plugin published to the experimental decentralized plugin
5
- * registry described in RFC 0001. The install flow:
6
- *
7
- * 1. Resolve `(handle, slug)` to a publisher DID via the configured
8
- * aggregator's `resolvePackage` XRPC.
9
- * 2. Look up the requested release (or the policy-filtered latest one)
10
- * via `getLatestRelease` / `listReleases`.
11
- * 3. Reject the install if the aggregator surfaces a `security:yanked`
12
- * hard-enforcement label or the release is below the configured
13
- * minimum release age.
14
- * 4. Fetch the bundle artifact, walking aggregator mirrors first and
15
- * falling back to the publisher-declared URL.
16
- * 5. Verify the artifact's multibase checksum against the signed
17
- * release record's `artifacts.package.checksum`.
18
- * 6. Extract `manifest.json` + `backend.js` + optional `admin.js` from
19
- * the gzipped tar bundle.
20
- * 7. Store the extracted files in site-local R2 under the
21
- * `registry/<plugin-id>/<version>/` prefix.
22
- * 8. Write a `plugin_states` row with `source = "registry"` and the
23
- * `(publisher_did, slug)` pair so updates can be resolved later.
24
- * 9. Sync the runtime so the plugin becomes active immediately.
25
- *
26
- * Known gaps (tracked separately):
27
- *
28
- * - The aggregator-supplied records are not yet cryptographically
29
- * verified against the publisher's MST signature. The signed bytes
30
- * and CIDs are passed through verbatim per the lexicon, but full
31
- * PDS-direct verification with proof traversal is follow-up work.
32
- * The artifact checksum is verified end-to-end against the value
33
- * in the (aggregator-relayed) release record, which is the actual
34
- * trust boundary for the bytes that end up in the sandbox.
35
- * - `acceptLabelers` is forwarded as-is to the aggregator; this
36
- * handler does not independently re-fetch and verify labels from
37
- * each labeller's DID. Aggregator label envelope tampering is
38
- * mitigated by the artifact checksum but not detected.
2
+ * Registry installs use aggregator results only for discovery and moderation
3
+ * metadata. The handler reads profile and release records from the publisher's
4
+ * PDS, verifies their repository proofs, then validates the bundle checksum,
5
+ * manifest, policy, and consent before storing or activating executable code.
6
+ * Applicable labels can block installation but cannot supply any verified
7
+ * record, checksum, permission, or executable byte.
39
8
  */
40
9
 
41
10
  import { ClientResponseError, ClientValidationError } from "@atcute/client";
42
11
  import type { Did } from "@atcute/lexicons";
12
+ import { canonicalizeDeclaredAccess } from "@emdash-cms/plugin-types";
13
+ import type { CanonicalDeclaredAccess } from "@emdash-cms/plugin-types";
43
14
  import { checkEnvCompatibility, findSkippedEnvConstraints } from "@emdash-cms/registry-client/env";
44
15
  import type { HostEnv } from "@emdash-cms/registry-client/env";
16
+ import { evaluateRegistryReleaseWithdrawal } from "@emdash-cms/registry-client/withdrawal";
17
+ import { NSID } from "@emdash-cms/registry-lexicons";
18
+ import {
19
+ fetchReleaseArtifact,
20
+ type ReleaseArtifactReference,
21
+ } from "@emdash-cms/registry-verification/artifact";
45
22
  import type { Kysely } from "kysely";
46
23
 
47
24
  import type { Database } from "../../database/types.js";
48
- import { extractBundle } from "../../plugins/marketplace.js";
49
- import type { PluginBundle } from "../../plugins/marketplace.js";
50
25
  import type { SandboxRunner } from "../../plugins/sandbox/types.js";
51
26
  import { PluginStateRepository } from "../../plugins/state.js";
52
27
  import {
@@ -55,6 +30,20 @@ import {
55
30
  } from "../../plugins/storage-indexes.js";
56
31
  import { declaredAccessToCapabilities } from "../../plugins/types.js";
57
32
  import type { DeclaredAccess } from "../../plugins/types.js";
33
+ import { fetchRegistryArtifactUrl } from "../../registry/artifact-fetch.js";
34
+ import {
35
+ validateRegistryArtifact,
36
+ type RegistryArtifactVerificationCode,
37
+ } from "../../registry/artifact-verification.js";
38
+ import {
39
+ readAuthoritativePackageRelease,
40
+ verifyAuthoritativePackageRelease,
41
+ type AuthoritativeRecordErrorCode,
42
+ type AuthoritativeRecordReader,
43
+ type AuthoritativeRecordReadOptions,
44
+ type VerifiedAuthoritativeReleaseReport,
45
+ type VerifiedAuthoritativeRecords,
46
+ } from "../../registry/authoritative-records.js";
58
47
  import {
59
48
  canonicalCapabilitiesForDriftCheck,
60
49
  coerceRegistryConfig,
@@ -63,8 +52,9 @@ import {
63
52
  validateAggregatorUrl,
64
53
  } from "../../registry/config.js";
65
54
  import { makeRegistryPluginId } from "../../registry/plugin-id.js";
55
+ import { hasCurrentRecordLabel } from "../../registry/record-labels.js";
66
56
  import type { RegistryConfigInput } from "../../registry/types.js";
67
- import { resolveAndValidateExternalUrl, SsrfError } from "../../security/ssrf.js";
57
+ import { resolveAndValidateExternalUrlTarget } from "../../security/ssrf.js";
68
58
  import { EmDashStorageError } from "../../storage/types.js";
69
59
  import type { Storage } from "../../storage/types.js";
70
60
  import type { ApiResult } from "../types.js";
@@ -76,7 +66,7 @@ import {
76
66
  storeBundleInR2,
77
67
  } from "./marketplace.js";
78
68
 
79
- const RELEASE_EXTENSION_NSID = "com.emdashcms.experimental.package.releaseExtension";
69
+ export { assertSafeArtifactUrl } from "../../registry/artifact-fetch.js";
80
70
 
81
71
  /**
82
72
  * Whether two `declaredAccess` blocks grant exactly the same enforced access --
@@ -96,6 +86,10 @@ export function enforcedAccessEqual(a: DeclaredAccess, b: DeclaredAccess): boole
96
86
  );
97
87
  }
98
88
 
89
+ function verifiedAccessEqual(a: CanonicalDeclaredAccess, b: DeclaredAccess): boolean {
90
+ return JSON.stringify(a) === JSON.stringify(canonicalizeDeclaredAccess(b));
91
+ }
92
+
99
93
  // ── Types ──────────────────────────────────────────────────────────
100
94
 
101
95
  export interface RegistryInstallInput {
@@ -126,6 +120,8 @@ export interface RegistryInstallInput {
126
120
  */
127
121
  acknowledgedDeclaredAccess?: unknown;
128
122
  acknowledgedMcpTools?: unknown;
123
+ acknowledgedProfileCid?: string;
124
+ acknowledgedReleaseCid?: string;
129
125
  }
130
126
 
131
127
  export interface RegistryInstallResult {
@@ -139,84 +135,120 @@ export interface RegistryInstallResult {
139
135
  version: string;
140
136
  /** Capabilities surfaced from the bundle's manifest. */
141
137
  capabilities: string[];
138
+ declaredAccess: DeclaredAccess;
139
+ mcpTools: RegistryMcpConsentTool[];
140
+ verification: RegistryRecordVerificationSummary;
142
141
  }
143
142
 
144
- // ── Helpers ────────────────────────────────────────────────────────
145
-
146
- /** Matches a bare 64-character lowercase/uppercase hex SHA-256 digest. */
147
- const SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/i;
143
+ export interface RegistryMcpConsentTool {
144
+ name: string;
145
+ description: string;
146
+ route: string;
147
+ permission: string;
148
+ destructive: boolean;
149
+ }
148
150
 
149
- /** Compute the SHA-256 of `bytes` as a lowercase hex string. */
150
- async function sha256Hex(bytes: Uint8Array): Promise<string> {
151
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Uint8Array is a valid BufferSource at runtime
152
- const buf = await crypto.subtle.digest("SHA-256", bytes as unknown as BufferSource);
153
- const arr = new Uint8Array(buf);
154
- return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
151
+ export interface RegistryRecordVerificationSummary {
152
+ profileCid: string;
153
+ releaseCid: string;
154
+ provenance: "verified" | "absent-optional";
155
+ policy: {
156
+ requireProvenance: boolean;
157
+ confirmation: "escalation-only" | "always";
158
+ approvers: string[];
159
+ };
155
160
  }
156
161
 
157
- /** multihash code for sha2-256 (single-byte varint). */
158
- const MULTIHASH_SHA256_CODE = 0x12;
159
- /** sha2-256 digest length in bytes (single-byte varint). */
160
- const MULTIHASH_SHA256_LENGTH = 0x20;
162
+ // ── Helpers ────────────────────────────────────────────────────────
161
163
 
162
- /**
163
- * Compute the multibase-multihash sha2-256 checksum of `bytes`, in the
164
- * same `b<base32>` shape the registry CLI publishes
165
- * (`packages/plugin-cli/src/multihash.ts`). Returns a 56-character
166
- * string starting with `b`.
167
- *
168
- * The trust contract is: if both sides produce the same string for
169
- * the same bytes, the bytes are unchanged. We don't decode the
170
- * publisher-supplied checksum -- we just re-encode our own and compare,
171
- * which is equivalent and avoids needing a base32 decoder.
172
- */
173
- async function sha256MultibaseMultihash(bytes: Uint8Array): Promise<string> {
174
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Uint8Array is a valid BufferSource at runtime
175
- const digestBuf = await crypto.subtle.digest("SHA-256", bytes as unknown as BufferSource);
176
- const digest = new Uint8Array(digestBuf);
177
- const multihash = new Uint8Array(2 + digest.length);
178
- multihash[0] = MULTIHASH_SHA256_CODE;
179
- multihash[1] = MULTIHASH_SHA256_LENGTH;
180
- multihash.set(digest, 2);
181
- const { toBase32 } = await import("@atcute/multibase");
182
- return `b${toBase32(multihash)}`;
164
+ export function registryArtifactError(
165
+ code: RegistryArtifactVerificationCode,
166
+ message: string,
167
+ operation: "install" | "update",
168
+ ): ApiResult<never> {
169
+ let apiCode: string;
170
+ switch (code) {
171
+ case "BUNDLE_ID_MISMATCH":
172
+ apiCode = operation === "install" ? "MANIFEST_ID_MISMATCH" : "BUNDLE_IDENTITY_MISMATCH";
173
+ break;
174
+ case "BUNDLE_VERSION_MISMATCH":
175
+ apiCode = operation === "install" ? "MANIFEST_VERSION_MISMATCH" : code;
176
+ break;
177
+ case "CHECKSUM_MISMATCH":
178
+ case "INVALID_MULTIHASH":
179
+ case "UNSUPPORTED_MULTIHASH":
180
+ apiCode = "CHECKSUM_MISMATCH";
181
+ break;
182
+ case "FETCH_FAILED":
183
+ case "HOST_REJECTED":
184
+ case "REDIRECT_LIMIT_EXCEEDED":
185
+ case "REDIRECT_LOCATION_MISSING":
186
+ case "RESOURCE_STATUS_ERROR":
187
+ case "RESOURCE_TIMEOUT":
188
+ apiCode = operation === "install" ? "INSTALL_FAILED" : "UPDATE_FAILED";
189
+ break;
190
+ default:
191
+ apiCode = "INVALID_BUNDLE";
192
+ }
193
+ return {
194
+ success: false,
195
+ error: {
196
+ code: apiCode,
197
+ message,
198
+ details: { verificationCode: code },
199
+ },
200
+ };
183
201
  }
184
202
 
185
- /**
186
- * Verify that a checksum string from a release record's
187
- * `artifact.checksum` field corresponds to the SHA-256 of the given
188
- * bytes.
189
- *
190
- * Accepts two formats:
191
- *
192
- * - Bare lowercase/uppercase hex SHA-256 (64 chars). Convenience for
193
- * publishers / tools that emit hex rather than multibase.
194
- * - Multibase-multihash with the `b` (base32) prefix and sha2-256.
195
- * This is the format RFC 0001 mandates and the registry CLI emits
196
- * (see `packages/plugin-cli/src/multihash.ts`).
197
- *
198
- * Hash functions other than sha2-256 are out of scope for this
199
- * initial release; the install fails closed.
200
- */
201
- export async function verifyChecksum(bytes: Uint8Array, checksum: string): Promise<boolean> {
202
- if (SHA256_HEX_PATTERN.test(checksum)) {
203
- const actual = await sha256Hex(bytes);
204
- return checksum.toLowerCase() === actual;
205
- }
203
+ function registryRecordError(
204
+ code: AuthoritativeRecordErrorCode,
205
+ message: string,
206
+ ): ApiResult<never> {
207
+ return {
208
+ success: false,
209
+ error: {
210
+ code: "RECORD_VERIFICATION_FAILED",
211
+ message,
212
+ details: { verificationCode: code },
213
+ },
214
+ };
215
+ }
206
216
 
207
- // Multibase-base32 multihash with sha2-256. We re-encode our own
208
- // digest in the same shape and compare strings -- equivalent to
209
- // decoding and comparing bytes, but doesn't need a base32 decoder.
210
- // 56 chars = 'b' + base32(34 bytes) = 'b' + 55 chars.
211
- if (checksum.length === 56 && checksum.startsWith("b")) {
212
- const actual = await sha256MultibaseMultihash(bytes);
213
- // Case-insensitive: multibase 'b' is lowercase by convention but
214
- // some emitters use uppercase. RFC 4648 base32 alphabets are
215
- // case-insensitive.
216
- return actual.toLowerCase() === checksum.toLowerCase();
217
+ function recordConsentError(
218
+ input: { profileCid?: string; releaseCid?: string },
219
+ records: VerifiedAuthoritativeRecords,
220
+ ): ApiResult<never> | null {
221
+ if (!input.profileCid || !input.releaseCid) {
222
+ return {
223
+ success: false,
224
+ error: {
225
+ code: "RECORD_CONSENT_REQUIRED",
226
+ message: "Verify the signed package records before confirming this action.",
227
+ },
228
+ };
229
+ }
230
+ if (input.profileCid !== records.profile.cid || input.releaseCid !== records.release.cid) {
231
+ return {
232
+ success: false,
233
+ error: {
234
+ code: "RECORD_VERIFICATION_DRIFT",
235
+ message: "The signed package records changed after review. Verify them again.",
236
+ },
237
+ };
217
238
  }
239
+ return null;
240
+ }
218
241
 
219
- return false;
242
+ function recordVerificationSummary(
243
+ records: VerifiedAuthoritativeRecords,
244
+ report: VerifiedAuthoritativeReleaseReport,
245
+ ): RegistryRecordVerificationSummary {
246
+ return {
247
+ profileCid: records.profile.cid,
248
+ releaseCid: records.release.cid,
249
+ provenance: report.provenance.status,
250
+ policy: report.value.policy,
251
+ };
220
252
  }
221
253
 
222
254
  /**
@@ -241,30 +273,21 @@ const MAX_REDIRECTS = 5;
241
273
 
242
274
  /**
243
275
  * Wall-clock cap on any single artifact fetch attempt (per URL).
244
- * Defends against slow-loris mirrors that accept the connection but
276
+ * Defends against slow-loris artifact sources that accept the connection but
245
277
  * never finish sending headers or body.
246
278
  */
247
279
  const ARTIFACT_FETCH_TIMEOUT_MS = 15_000;
248
280
 
249
281
  /**
250
282
  * Total wall-clock budget for the artifact-download phase across all
251
- * mirrors and the declared URL. Even with the per-URL timeout, a
252
- * malicious mirror list could otherwise tie up the install request for
283
+ * advertised caches, the publisher PDS, and the declared URL. Even with the
284
+ * per-URL timeout, unavailable sources could otherwise tie up the install request for
253
285
  * minutes; this caps total time at a budget interactive admins can
254
286
  * tolerate. Tuned so a fast happy path takes <1s of budget per
255
287
  * attempt and a worst case still completes in under a minute.
256
288
  */
257
289
  const ARTIFACT_TOTAL_BUDGET_MS = 45_000;
258
290
 
259
- /**
260
- * Cap on the number of mirror URLs we try before falling back to the
261
- * publisher-declared URL. Matches the aggregator lexicon's
262
- * `mirrors` array length cap (16) but enforced here independently so
263
- * a misbehaving aggregator can't slow-loris us through hundreds of
264
- * URLs.
265
- */
266
- const MAX_MIRRORS = 16;
267
-
268
291
  /**
269
292
  * Per-request timeout applied to every aggregator XRPC call
270
293
  * (`resolvePackage`, `getLatestRelease`, `listReleases`). Matches the
@@ -282,6 +305,16 @@ const AGGREGATOR_REQUEST_TIMEOUT_MS = 15_000;
282
305
  */
283
306
  const AGGREGATOR_TOTAL_BUDGET_MS = 30_000;
284
307
 
308
+ class RegistryArtifactFetchError extends Error {
309
+ constructor(
310
+ readonly code: RegistryArtifactVerificationCode,
311
+ message: string,
312
+ ) {
313
+ super(message);
314
+ this.name = "RegistryArtifactFetchError";
315
+ }
316
+ }
317
+
285
318
  /** Build a fetch function that enforces a per-request and per-budget timeout. */
286
319
  function timedFetch(totalDeadline: number): typeof fetch {
287
320
  return (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => {
@@ -304,249 +337,45 @@ function timedFetch(totalDeadline: number): typeof fetch {
304
337
  };
305
338
  }
306
339
 
307
- /**
308
- * Localhost-equivalent hostnames the artifact fetcher rejects in
309
- * production. The full literal-IP / DNS-rebinding blocklist lives in
310
- * `#security/ssrf.js` and is invoked via `resolveAndValidateExternalUrl`
311
- * below; this small set exists only because the artifact handler has
312
- * a dev-mode escape hatch that lets `http://localhost` through.
313
- */
314
- const FORBIDDEN_HOSTNAMES = new Set([
315
- "localhost",
316
- "localhost.localdomain",
317
- "ip6-localhost",
318
- "ip6-loopback",
319
- ]);
320
-
321
- /** Trailing dot on a hostname, stripped before URL host comparisons. */
322
- const TRAILING_DOT = /\.$/;
323
-
324
- /** Hostnames that resolve to the local machine; rejected outright in production. */
325
- function isLocalhostHostname(hostname: string): boolean {
326
- // WHATWG URL preserves brackets on IPv6 hostnames; strip them before
327
- // comparison so `[::1]` is recognised as localhost.
328
- const stripped = hostname.toLowerCase().replace(TRAILING_DOT, "");
329
- const h = stripped.startsWith("[") && stripped.endsWith("]") ? stripped.slice(1, -1) : stripped;
330
- if (FORBIDDEN_HOSTNAMES.has(h)) return true;
331
- if (h === "localhost") return true;
332
- if (h.endsWith(".localhost")) return true;
333
- if (h === "127.0.0.1" || h === "::1") return true;
334
- if (h.startsWith("::ffff:127.") || h.startsWith("::ffff:7f00:")) return true;
335
- return false;
336
- }
337
-
338
- /**
339
- * Validate that `urlString` is a safe outbound target for artifact
340
- * downloads. Rejects non-HTTPS (except localhost in dev), embedded
341
- * credentials, any host that's a loopback / private / link-local
342
- * literal address, and any hostname whose resolved A or AAAA records
343
- * point at one of those addresses (closes the DNS-rebinding gap).
344
- *
345
- * Wraps `resolveAndValidateExternalUrl` from the import-pipeline SSRF
346
- * module so both code paths share one DoH cache, one resolver, one
347
- * blocklist, and one set of regression tests. Layers an
348
- * artifact-specific protocol/dev-localhost policy on top.
349
- *
350
- * `import.meta.env.DEV` is a Vite/Astro compile-time constant, so
351
- * production bundles cannot enable the dev escape hatch at runtime.
352
- */
353
- export async function assertSafeArtifactUrl(urlString: string): Promise<URL> {
354
- let url: URL;
355
- try {
356
- url = new URL(urlString);
357
- } catch {
358
- throw new Error(`Invalid artifact URL: ${urlString}`);
359
- }
360
- if (url.protocol !== "https:" && url.protocol !== "http:") {
361
- throw new Error(`Artifact URL protocol not allowed: ${url.protocol}`);
362
- }
363
- if (url.username || url.password) {
364
- throw new Error("Artifact URL must not contain embedded credentials");
365
- }
366
-
367
- const rawHostname = url.hostname.toLowerCase().replace(TRAILING_DOT, "");
368
- // Strip brackets so the IPv4/IPv6 checks see the canonical form.
369
- const hostname =
370
- rawHostname.startsWith("[") && rawHostname.endsWith("]")
371
- ? rawHostname.slice(1, -1)
372
- : rawHostname;
373
- const localhost = isLocalhostHostname(hostname);
374
-
375
- // In production: reject HTTP entirely and reject localhost over any
376
- // protocol -- a publisher pointing at `https://localhost` is still
377
- // trying to bounce the server through its own loopback interface.
378
- if (!import.meta.env.DEV) {
379
- if (url.protocol === "http:") {
380
- throw new Error("Artifact URL must use https");
381
- }
382
- if (localhost) {
383
- throw new Error(`Artifact URL points to localhost: ${hostname}`);
384
- }
385
- } else if (url.protocol === "http:" && !localhost) {
386
- // Dev mode: http allowed only for localhost.
387
- throw new Error("Artifact URL must use https (http allowed only for localhost in dev)");
388
- }
389
-
390
- if (localhost) {
391
- // Dev-only path; nothing to resolve.
392
- return url;
393
- }
394
-
395
- // Delegate IP-literal + DNS-rebinding validation to the import
396
- // pipeline's SSRF helper. Adapts the SsrfError to the existing
397
- // artifact-URL error vocabulary so callers keep their current
398
- // catch shape.
399
- try {
400
- return await resolveAndValidateExternalUrl(url.href);
401
- } catch (err) {
402
- if (err instanceof SsrfError) {
403
- throw new Error(`Artifact URL rejected: ${err.message}`, { cause: err });
404
- }
405
- throw err;
406
- }
407
- }
408
-
409
- /**
410
- * Fetch one URL with manual redirect handling so every hop is
411
- * URL-validated, a hard byte cap so a malicious response body cannot
412
- * exhaust memory before the checksum check rejects it, and a wall-clock
413
- * timeout that covers connect, headers, and body together. The timeout
414
- * is the minimum of the per-URL cap and the remaining total budget so
415
- * a late-arriving mirror still respects the install's global budget.
416
- */
417
- async function fetchWithLimits(initialUrl: string, totalDeadline: number): Promise<Uint8Array> {
418
- const now = Date.now();
419
- const remaining = Math.max(0, totalDeadline - now);
420
- if (remaining === 0) {
421
- throw new Error("Artifact download budget exhausted");
422
- }
423
- const perUrlTimeout = Math.min(ARTIFACT_FETCH_TIMEOUT_MS, remaining);
424
- const controller = new AbortController();
425
- const timer = setTimeout(() => controller.abort(), perUrlTimeout);
426
- try {
427
- let current = await assertSafeArtifactUrl(initialUrl);
428
- let response: Response;
429
- for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
430
- response = await fetch(current.href, { redirect: "manual", signal: controller.signal });
431
- if (response.status < 300 || response.status >= 400) break;
432
- const location = response.headers.get("location");
433
- if (!location) break;
434
- if (hop === MAX_REDIRECTS) {
435
- throw new Error(`Too many redirects fetching artifact (>${MAX_REDIRECTS})`);
436
- }
437
- const next = new URL(location, current);
438
- current = await assertSafeArtifactUrl(next.href);
439
- }
440
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- response is assigned in the first loop iteration
441
- const finalResponse = response!;
442
- if (!finalResponse.ok) {
443
- throw new Error(`HTTP ${finalResponse.status}`);
444
- }
445
-
446
- // Check Content-Length up front when present. Untrusted servers can
447
- // lie or omit it; the streaming cap below is the real defense.
448
- const lengthHeader = finalResponse.headers.get("content-length");
449
- if (lengthHeader) {
450
- const declared = Number(lengthHeader);
451
- if (Number.isFinite(declared) && declared > MAX_ARTIFACT_BYTES) {
452
- throw new Error(
453
- `Artifact too large (declared ${declared} bytes, limit ${MAX_ARTIFACT_BYTES})`,
454
- );
455
- }
456
- }
457
-
458
- const body = finalResponse.body;
459
- if (!body) {
460
- // Workers can't return a null body for a normal GET; defensive fallback.
461
- const buf = new Uint8Array(await finalResponse.arrayBuffer());
462
- if (buf.byteLength > MAX_ARTIFACT_BYTES) {
463
- throw new Error(`Artifact too large (limit ${MAX_ARTIFACT_BYTES} bytes)`);
464
- }
465
- return buf;
466
- }
467
-
468
- const reader = body.getReader();
469
- const chunks: Uint8Array[] = [];
470
- let total = 0;
471
- while (true) {
472
- const { done, value } = await reader.read();
473
- if (done) break;
474
- if (!value) continue;
475
- total += value.byteLength;
476
- if (total > MAX_ARTIFACT_BYTES) {
477
- try {
478
- await reader.cancel();
479
- } catch {
480
- // nothing to do
481
- }
482
- throw new Error(`Artifact too large (limit ${MAX_ARTIFACT_BYTES} bytes)`);
483
- }
484
- chunks.push(value);
485
- }
486
-
487
- const out = new Uint8Array(total);
488
- let offset = 0;
489
- for (const chunk of chunks) {
490
- out.set(chunk, offset);
491
- offset += chunk.byteLength;
492
- }
493
- return out;
494
- } finally {
495
- clearTimeout(timer);
496
- }
497
- }
498
-
499
- /**
500
- * Strip query string and fragment from a URL for use in
501
- * client-visible error messages. Registry artifacts are often hosted
502
- * on storage backends that include presigned tokens in the query
503
- * string; surfacing the raw URL on a failed install leaks those
504
- * tokens into the admin's HTTP response and any log drain that
505
- * captures the error chain. Origin + pathname is enough to identify
506
- * the host and resource without exposing credentials.
507
- *
508
- * Falls back to a generic placeholder when the URL is malformed.
509
- */
510
- function redactUrlForError(raw: string): string {
511
- try {
512
- const u = new URL(raw);
513
- return `${u.origin}${u.pathname}`;
514
- } catch {
515
- return "<malformed url>";
516
- }
517
- }
518
-
519
- /** Walk artifact source URLs in priority order and return the first that fetches successfully. */
520
- async function fetchArtifact(mirrors: string[], declaredUrl: string): Promise<Uint8Array> {
521
- // Clamp mirrors regardless of what the lexicon type says -- a buggy
522
- // or malicious aggregator could return more than the spec'd limit
523
- // and slow-loris each one. The declared URL is always tried last.
524
- const clampedMirrors = mirrors.slice(0, MAX_MIRRORS);
525
- const urls = [...clampedMirrors, declaredUrl];
526
- // Client-visible errors carry redacted URLs (origin + path only).
527
- // The full URL with any query-string token is logged server-side
528
- // so operators can still debug delivery failures.
529
- const clientErrors: string[] = [];
530
-
531
- const totalDeadline = Date.now() + ARTIFACT_TOTAL_BUDGET_MS;
532
-
533
- for (const url of urls) {
534
- if (Date.now() >= totalDeadline) {
535
- clientErrors.push("(total artifact download budget exhausted)");
536
- break;
537
- }
538
- try {
539
- return await fetchWithLimits(url, totalDeadline);
540
- } catch (err) {
541
- const message = err instanceof Error ? err.message : String(err);
542
- console.warn(`[registry-install] Artifact fetch failed from ${url}:`, message);
543
- clientErrors.push(`${redactUrlForError(url)}: ${message}`);
544
- }
545
- }
546
-
547
- throw new Error(
548
- `Failed to download artifact from any source. Tried:\n ${clientErrors.join("\n ")}`,
340
+ async function fetchArtifact(
341
+ artifactCaches: readonly unknown[],
342
+ artifact: ReleaseArtifactReference,
343
+ record: {
344
+ did: string;
345
+ collection: string;
346
+ rkey: string;
347
+ cid: string;
348
+ },
349
+ auth?: unknown,
350
+ ): Promise<Uint8Array> {
351
+ const result = await fetchReleaseArtifact(
352
+ {
353
+ artifact,
354
+ record,
355
+ artifactCaches,
356
+ ...(auth === undefined ? {} : { auth }),
357
+ },
358
+ {
359
+ fetch: async (url, init) => {
360
+ if (!init.signal) throw new Error("Registry artifact fetch requires an abort signal");
361
+ return fetchRegistryArtifactUrl(url.href, {
362
+ signal: init.signal,
363
+ maxResponseBytes: MAX_ARTIFACT_BYTES,
364
+ });
365
+ },
366
+ resolveHostname: async (hostname) =>
367
+ (await resolveAndValidateExternalUrlTarget(`https://${hostname}`)).addresses,
368
+ allowHttpLocalhost: import.meta.env.DEV,
369
+ headerTimeoutMs: ARTIFACT_FETCH_TIMEOUT_MS,
370
+ totalTimeoutMs: ARTIFACT_TOTAL_BUDGET_MS,
371
+ maxBytes: MAX_ARTIFACT_BYTES,
372
+ maxRedirects: MAX_REDIRECTS,
373
+ },
549
374
  );
375
+ if (!result.success) {
376
+ throw new RegistryArtifactFetchError(result.error.code, result.error.message);
377
+ }
378
+ return result.value.bytes;
550
379
  }
551
380
 
552
381
  /**
@@ -603,7 +432,13 @@ export async function handleRegistryInstall(
603
432
  sandboxRunner: SandboxRunner | null,
604
433
  registryConfigInput: RegistryConfigInput | undefined,
605
434
  input: RegistryInstallInput,
606
- opts?: { configuredPluginIds?: Set<string>; hostEnv?: HostEnv },
435
+ opts?: {
436
+ configuredPluginIds?: Set<string>;
437
+ hostEnv?: HostEnv;
438
+ authoritativeRecords?: AuthoritativeRecordReadOptions;
439
+ readAuthoritativeRecords?: AuthoritativeRecordReader;
440
+ verifyOnly?: boolean;
441
+ },
607
442
  ): Promise<ApiResult<RegistryInstallResult>> {
608
443
  // Accept either the bare-string shorthand or the full
609
444
  // `RegistryConfig` object (see `RegistryConfigInput`).
@@ -658,7 +493,8 @@ export async function handleRegistryInstall(
658
493
 
659
494
  // Lazy-load the discovery client. Avoids pulling @atcute/client into
660
495
  // every code path that imports core/api/handlers.
661
- const { DiscoveryClient } = await import("@emdash-cms/registry-client/discovery");
496
+ const { DiscoveryClient, registryLabelerPolicy } =
497
+ await import("@emdash-cms/registry-client/discovery");
662
498
 
663
499
  // Every aggregator XRPC call passes through `timedFetch`, which
664
500
  // enforces a per-request timeout and shares a single total-budget
@@ -668,6 +504,7 @@ export async function handleRegistryInstall(
668
504
  const discovery = new DiscoveryClient({
669
505
  aggregatorUrl: registryConfig.aggregatorUrl,
670
506
  acceptLabelers: registryConfig.acceptLabelers,
507
+ labelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),
671
508
  fetch: timedFetch(aggregatorDeadline),
672
509
  });
673
510
 
@@ -752,19 +589,9 @@ export async function handleRegistryInstall(
752
589
  };
753
590
  }
754
591
 
755
- // Identity cross-check on every field the aggregator denormalises
756
- // onto the package and release views. A buggy or compromised
757
- // aggregator could otherwise return a release view for a
758
- // different `(did, slug, version)` than we asked for; the
759
- // handler would then fetch + checksum-verify + install bytes
760
- // under the requested package's pluginId but for a different
761
- // publisher's record. Checksum verification only proves the bytes
762
- // match the *returned* record, not that the record belongs to
763
- // the package we requested.
764
- // `releaseView.release` is validated against the release lexicon by
765
- // DiscoveryClient (or `null` if it didn't conform). A `null` here makes
766
- // the identity checks below fail closed, which is the desired outcome.
767
- const signedRelease = releaseView.release;
592
+ // The aggregator selects the package/version and supplies mirrors and
593
+ // moderation metadata. Its copies of the signed records are not
594
+ // verification inputs.
768
595
  if (packageView.did !== publisherDid || packageView.slug !== slug) {
769
596
  return {
770
597
  success: false,
@@ -777,9 +604,7 @@ export async function handleRegistryInstall(
777
604
  if (
778
605
  releaseView.did !== publisherDid ||
779
606
  releaseView.package !== slug ||
780
- signedRelease?.package !== slug ||
781
- (requestedVersion !== undefined && releaseView.version !== requestedVersion) ||
782
- signedRelease?.version !== releaseView.version
607
+ (requestedVersion !== undefined && releaseView.version !== requestedVersion)
783
608
  ) {
784
609
  return {
785
610
  success: false,
@@ -792,34 +617,74 @@ export async function handleRegistryInstall(
792
617
  }
793
618
 
794
619
  const version = releaseView.version;
620
+ if (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {
621
+ return {
622
+ success: false,
623
+ error: {
624
+ code: "RELEASE_YANKED",
625
+ message: "This release has been withdrawn",
626
+ },
627
+ };
628
+ }
795
629
 
796
- // Step 3: takedown label check (hard-enforced via aggregator's
797
- // `atproto-accept-labelers` filtering, but we belt-and-suspenders
798
- // the package-level labels too).
799
- const yanked = (packageView.labels ?? []).some(
800
- (l: { val?: string }) => l.val === "security:yanked",
801
- );
802
- const releaseYanked = (releaseView.labels ?? []).some(
803
- (l: { val?: string }) => l.val === "security:yanked",
630
+ const authoritative = await (opts?.readAuthoritativeRecords ?? readAuthoritativePackageRelease)(
631
+ publisherDid,
632
+ slug,
633
+ version,
634
+ opts?.authoritativeRecords,
804
635
  );
805
- if (yanked || releaseYanked) {
636
+ if (!authoritative.success) {
637
+ return registryRecordError(authoritative.error.code, authoritative.error.message);
638
+ }
639
+ const records = authoritative.value;
640
+ const { profile, release } = records.inspection.value;
641
+ if (
642
+ packageView.uri !== records.profile.uri ||
643
+ packageView.cid !== records.profile.cid ||
644
+ releaseView.uri !== records.release.uri ||
645
+ releaseView.cid !== records.release.cid
646
+ ) {
647
+ return {
648
+ success: false,
649
+ error: {
650
+ code: "AGGREGATOR_RECORD_MISMATCH",
651
+ message: "Aggregator metadata does not match the publisher's signed records.",
652
+ },
653
+ };
654
+ }
655
+ if (!opts?.verifyOnly) {
656
+ const consentError = recordConsentError(
657
+ {
658
+ profileCid: input.acknowledgedProfileCid,
659
+ releaseCid: input.acknowledgedReleaseCid,
660
+ },
661
+ records,
662
+ );
663
+ if (consentError) return consentError;
664
+ }
665
+
666
+ const packageYanked =
667
+ hasCurrentRecordLabel(packageView.labels ?? [], "security:yanked", records.profile) ||
668
+ hasCurrentRecordLabel(packageView.labels ?? [], "security-yanked", records.profile);
669
+ if (packageYanked) {
806
670
  return {
807
671
  success: false,
808
672
  error: {
809
673
  code: "RELEASE_YANKED",
810
- message: "This release has been withdrawn (security:yanked label).",
674
+ message: "This release has been withdrawn",
811
675
  },
812
676
  };
813
677
  }
814
678
 
815
- // Step 3b: environment compatibility. The signed release record may
816
- // carry a `requires` block (`env:emdash`, `env:astro`, ...). Refuse
679
+ // Environment compatibility remains an install-safety gate. Listing
680
+ // approval says only that displayed metadata passed moderation. A release
681
+ // may carry a `requires` block (`env:emdash`, `env:astro`, ...). Refuse
817
682
  // the install if the running host doesn't satisfy a constraint, so a
818
683
  // stale browser tab or non-UI caller can't bypass the admin's
819
684
  // disabled Install button. `requires` is lexicon-`unknown`; the
820
685
  // helper guards its shape.
821
686
  if (opts?.hostEnv) {
822
- const envError = assertEnvCompatible(releaseView.release?.requires, opts.hostEnv);
687
+ const envError = assertEnvCompatible(release.requires, opts.hostEnv);
823
688
  if (envError) return { success: false, error: envError };
824
689
  }
825
690
 
@@ -829,13 +694,11 @@ export async function handleRegistryInstall(
829
694
  // must still hit the holdback. The `minimumReleaseAgeExclude`
830
695
  // allowlist short-circuits the check for trusted publisher DIDs.
831
696
  //
832
- // Caveat: `releaseView.indexedAt` is aggregator-supplied envelope
833
- // data, not a signed timestamp. A compromised aggregator can
834
- // claim an arbitrary indexed-at date and bypass the holdback;
835
- // closing this gap requires fetching the release record's
836
- // signed createdAt from the publisher's PDS (deferred to the
837
- // follow-up that adds full MST verification). If the timestamp
838
- // is missing or malformed, we fail closed and reject the install.
697
+ // `releaseView.indexedAt` is aggregator operational data, not part
698
+ // of the signed release. The release schema has no publication
699
+ // timestamp, so minimum age remains a local discovery holdback
700
+ // rather than a cryptographic property. A missing or malformed
701
+ // timestamp fails closed.
839
702
  // `registryConfig` is the user-supplied integration option, not
840
703
  // the normalized manifest shape, so the duration parse runs once
841
704
  // per install. Catch a malformed value here -- normally caught at
@@ -940,82 +803,69 @@ export async function handleRegistryInstall(
940
803
  };
941
804
  }
942
805
 
943
- // Step 4: fetch the artifact bytes.
944
- // `releaseView.release` is lexicon-validated by DiscoveryClient (or
945
- // `null`); a missing url/checksum (incl. the `null` case) fails closed
946
- // below. Mirrors come from the envelope (aggregator operational data,
947
- // not part of the signed record).
948
- const release = releaseView.release;
949
- const declaredUrl = release?.artifacts?.package?.url;
950
- const declaredChecksum = release?.artifacts?.package?.checksum;
806
+ // Step 6: fetch bytes through an aggregator cache, the publisher PDS,
807
+ // or the URL in the authoritative signed release. Every source remains untrusted.
808
+ const packageArtifact = release.artifacts.package;
809
+ const declaredChecksum = release.artifacts.package.checksum;
951
810
 
952
- if (!declaredUrl || !declaredChecksum) {
811
+ if (!packageArtifact || !declaredChecksum) {
953
812
  return {
954
813
  success: false,
955
814
  error: {
956
815
  code: "INVALID_RELEASE",
957
- message: "Release record is missing artifact url or checksum",
958
- },
959
- };
960
- }
961
-
962
- const mirrors = releaseView.mirrors ?? [];
963
- const artifactBytes = await fetchArtifact(mirrors, declaredUrl);
964
-
965
- // Step 5: verify the bytes against the signed record's checksum.
966
- const checksumOk = await verifyChecksum(artifactBytes, declaredChecksum);
967
- if (!checksumOk) {
968
- return {
969
- success: false,
970
- error: {
971
- code: "CHECKSUM_MISMATCH",
972
- message:
973
- "Artifact bytes do not match the release record's checksum, or the checksum encoding is unsupported.",
816
+ message: "Release record is missing its package artifact or checksum",
974
817
  },
975
818
  };
976
819
  }
977
820
 
978
- // Step 6: extract the bundle.
979
- let bundle: PluginBundle;
821
+ let artifactBytes: Uint8Array;
980
822
  try {
981
- bundle = await extractBundle(artifactBytes);
982
- } catch (err) {
983
- return {
984
- success: false,
985
- error: {
986
- code: "INVALID_BUNDLE",
987
- message: err instanceof Error ? err.message : "Failed to extract plugin bundle",
823
+ artifactBytes = await fetchArtifact(
824
+ releaseView.artifactCaches ?? [],
825
+ packageArtifact,
826
+ {
827
+ did: publisherDid,
828
+ collection: NSID.packageRelease,
829
+ rkey: `${slug}:${version}`,
830
+ cid: releaseView.cid,
988
831
  },
989
- };
832
+ release.auth,
833
+ );
834
+ } catch (error) {
835
+ if (error instanceof RegistryArtifactFetchError) {
836
+ return registryArtifactError(error.code, error.message, "install");
837
+ }
838
+ throw error;
990
839
  }
991
840
 
992
- // Manifest sanity: declared version must match the release's version.
993
- if (bundle.manifest.version !== version) {
994
- return {
995
- success: false,
996
- error: {
997
- code: "MANIFEST_VERSION_MISMATCH",
998
- message: `Bundle manifest version (${bundle.manifest.version}) does not match release version (${version})`,
999
- },
1000
- };
841
+ // Steps 6-7: verify the signed checksum, archive, manifest, and
842
+ // expected package identity with the runtime-neutral verifier used
843
+ // by the release service.
844
+ const artifactReport = await validateRegistryArtifact(
845
+ artifactBytes,
846
+ declaredChecksum,
847
+ slug,
848
+ version,
849
+ );
850
+ if (!artifactReport.success) {
851
+ return registryArtifactError(
852
+ artifactReport.error.code,
853
+ artifactReport.error.message,
854
+ "install",
855
+ );
1001
856
  }
1002
-
1003
- // Manifest identity: the bundle's `manifest.id` is the publisher's
1004
- // natural plugin id (their slug). It MUST equal the slug the
1005
- // install was requested for; otherwise a malicious registry bundle
1006
- // could declare `manifest.id: "audit-log"` and confuse the sandbox
1007
- // bridge, which uses `manifest.id` as the trust key for
1008
- // per-plugin storage, cron schedules, and bridge-scoped
1009
- // operations.
1010
- if (bundle.manifest.id !== slug) {
1011
- return {
1012
- success: false,
1013
- error: {
1014
- code: "MANIFEST_ID_MISMATCH",
1015
- message: `Bundle manifest id (${bundle.manifest.id}) does not match registry slug (${slug})`,
1016
- },
1017
- };
857
+ const { bundle, artifactDigest, artifactDigests } = artifactReport.value;
858
+ const recordReport = await verifyAuthoritativePackageRelease(records, artifactDigest, {
859
+ ...opts?.authoritativeRecords,
860
+ artifactDigests,
861
+ });
862
+ if (!recordReport.success) {
863
+ return registryRecordError(
864
+ recordReport.code,
865
+ recordReport.reasons[0]?.message ?? "The release provenance is invalid.",
866
+ );
1018
867
  }
868
+ const verification = recordVerificationSummary(records, recordReport);
1019
869
 
1020
870
  // Rewrite the manifest's id to the derived opaque pluginId before
1021
871
  // it reaches R2 storage or the sandbox loader. The sandbox uses
@@ -1033,13 +883,8 @@ export async function handleRegistryInstall(
1033
883
  // outside what the user reviewed. The capability-set consent gate below
1034
884
  // is blind to constraint content (host scope), so compare the full
1035
885
  // enforced access of record vs bundle here and refuse on any difference.
1036
- const recordExt =
1037
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- extensions is the lexicon's open `unknown` map; narrow to read our own extension
1038
- (release?.extensions as Record<string, { declaredAccess?: DeclaredAccess }> | undefined)?.[
1039
- RELEASE_EXTENSION_NSID
1040
- ];
1041
886
  if (
1042
- !enforcedAccessEqual(recordExt?.declaredAccess ?? {}, bundle.manifest.declaredAccess ?? {})
887
+ !verifiedAccessEqual(recordReport.value.declaredAccess, bundle.manifest.declaredAccess ?? {})
1043
888
  ) {
1044
889
  return {
1045
890
  success: false,
@@ -1053,13 +898,9 @@ export async function handleRegistryInstall(
1053
898
 
1054
899
  // Capability consent gate: the admin MUST acknowledge the
1055
900
  // capabilities the bundle's manifest actually declares before we
1056
- // install it. The bundle manifest is the only source of truth
1057
- // the runtime sandbox enforces -- the release record's
1058
- // `declaredAccess` extension is an aggregator-supplied
1059
- // assertion that the publisher may or may not have included,
1060
- // and trusting it would let a malicious publisher (or a
1061
- // compromised aggregator) ship a bundle whose manifest
1062
- // requests `content:*` etc. behind an empty consent dialog.
901
+ // install it. The bundle manifest is the runtime enforcement
902
+ // currency; the exact-equality check above binds it to the
903
+ // independently verified signed release.
1063
904
  //
1064
905
  // Two outcomes after normalization (filter to strings, dedupe,
1065
906
  // sort):
@@ -1076,7 +917,7 @@ export async function handleRegistryInstall(
1076
917
  // the runtime starts enforcing `declaredAccess` natively, this
1077
918
  // comparison switches to that shape.
1078
919
  const actualCapabilities = canonicalCapabilitiesForDriftCheck(bundle.manifest.capabilities);
1079
- if (actualCapabilities.length > 0) {
920
+ if (!opts?.verifyOnly && actualCapabilities.length > 0) {
1080
921
  if (input.acknowledgedDeclaredAccess === undefined) {
1081
922
  return {
1082
923
  success: false,
@@ -1106,19 +947,31 @@ export async function handleRegistryInstall(
1106
947
  const actualMcpTools = (bundle.manifest.mcp?.tools ?? []).map(
1107
948
  ({ inputSchema: _, outputSchema: __, ...tool }) => tool,
1108
949
  );
1109
- if (actualMcpTools.length > 0) {
950
+ if (!opts?.verifyOnly && actualMcpTools.length > 0) {
1110
951
  if (JSON.stringify(input.acknowledgedMcpTools) !== JSON.stringify(actualMcpTools)) {
1111
952
  return {
1112
953
  success: false,
1113
954
  error: {
1114
955
  code: "MCP_TOOL_CONSENT_REQUIRED",
1115
956
  message: "Plugin MCP tools require explicit consent",
1116
- details: { mcpTools: actualMcpTools },
957
+ details: { mcpTools: actualMcpTools, verification },
1117
958
  },
1118
959
  };
1119
960
  }
1120
961
  }
1121
962
 
963
+ const result: RegistryInstallResult = {
964
+ pluginId,
965
+ publisherDid,
966
+ slug,
967
+ version,
968
+ capabilities: bundle.manifest.capabilities,
969
+ declaredAccess: recordReport.value.releaseExtension.declaredAccess,
970
+ mcpTools: actualMcpTools,
971
+ verification,
972
+ };
973
+ if (opts?.verifyOnly) return { success: true, data: result };
974
+
1122
975
  // Step 7: store in R2 under the registry prefix.
1123
976
  await storeBundleInR2(storage, pluginId, version, bundle, "registry");
1124
977
 
@@ -1151,13 +1004,11 @@ export async function handleRegistryInstall(
1151
1004
  // Cleanup is best-effort; if it also fails, the row failure
1152
1005
  // still surfaces to the caller and the orphan R2 bundle costs
1153
1006
  // only the storage of a single checksum-verified zip.
1154
- // `packageView.profile` is lexicon-validated by DiscoveryClient (or null).
1155
- const profile = packageView.profile;
1156
1007
  try {
1157
1008
  await stateRepo.upsert(pluginId, version, "active", {
1158
1009
  source: "registry",
1159
- displayName: profile?.name ?? slug,
1160
- description: profile?.description ?? undefined,
1010
+ displayName: profile.name ?? slug,
1011
+ description: profile.description ?? undefined,
1161
1012
  registryPublisherDid: publisherDid,
1162
1013
  registrySlug: slug,
1163
1014
  });
@@ -1189,13 +1040,7 @@ export async function handleRegistryInstall(
1189
1040
 
1190
1041
  return {
1191
1042
  success: true,
1192
- data: {
1193
- pluginId,
1194
- publisherDid,
1195
- slug,
1196
- version,
1197
- capabilities: bundle.manifest.capabilities,
1198
- },
1043
+ data: result,
1199
1044
  };
1200
1045
  } catch (err) {
1201
1046
  if (err instanceof ClientValidationError) {
@@ -1208,6 +1053,15 @@ export async function handleRegistryInstall(
1208
1053
  };
1209
1054
  }
1210
1055
  if (err instanceof ClientResponseError) {
1056
+ if (err.error === "ListingUnavailable") {
1057
+ return {
1058
+ success: false,
1059
+ error: {
1060
+ code: "LISTING_UNAVAILABLE",
1061
+ message: "This plugin is unavailable under the active registry policy",
1062
+ },
1063
+ };
1064
+ }
1211
1065
  return {
1212
1066
  success: false,
1213
1067
  error: {
@@ -1324,6 +1178,7 @@ export interface RegistryUpdateResult {
1324
1178
  capabilityChanges: { added: string[]; removed: string[] };
1325
1179
  /** Set only when `newlyPublic` is non-empty, mirroring marketplace. */
1326
1180
  routeVisibilityChanges?: { newlyPublic: string[] };
1181
+ verification: RegistryRecordVerificationSummary;
1327
1182
  }
1328
1183
 
1329
1184
  /**
@@ -1348,7 +1203,11 @@ export async function handleRegistryUpdate(
1348
1203
  confirmCapabilityChanges?: boolean;
1349
1204
  confirmRouteVisibilityChanges?: boolean;
1350
1205
  confirmMcpTools?: boolean;
1206
+ acknowledgedProfileCid?: string;
1207
+ acknowledgedReleaseCid?: string;
1351
1208
  hostEnv?: HostEnv;
1209
+ authoritativeRecords?: AuthoritativeRecordReadOptions;
1210
+ readAuthoritativeRecords?: AuthoritativeRecordReader;
1352
1211
  },
1353
1212
  ): Promise<ApiResult<RegistryUpdateResult>> {
1354
1213
  const registryConfig = coerceRegistryConfig(registryConfigInput);
@@ -1408,11 +1267,13 @@ export async function handleRegistryUpdate(
1408
1267
  const publisherDid = existing.registryPublisherDid as Did;
1409
1268
  const slug = existing.registrySlug;
1410
1269
 
1411
- const { DiscoveryClient } = await import("@emdash-cms/registry-client/discovery");
1270
+ const { DiscoveryClient, registryLabelerPolicy } =
1271
+ await import("@emdash-cms/registry-client/discovery");
1412
1272
  const aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;
1413
1273
  const discovery = new DiscoveryClient({
1414
1274
  aggregatorUrl: registryConfig.aggregatorUrl,
1415
1275
  acceptLabelers: registryConfig.acceptLabelers,
1276
+ labelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),
1416
1277
  fetch: timedFetch(aggregatorDeadline),
1417
1278
  });
1418
1279
 
@@ -1457,16 +1318,12 @@ export async function handleRegistryUpdate(
1457
1318
  };
1458
1319
  }
1459
1320
 
1460
- // Identity cross-check. A buggy/compromised aggregator must not
1461
- // trick us into installing a record signed for a different
1462
- // (did, slug, version) under this plugin's pluginId.
1463
- const signedRelease = releaseView.release;
1321
+ // The aggregator selects the target version and supplies mirrors and
1322
+ // moderation metadata. Its release-record copy is not trusted.
1464
1323
  if (
1465
1324
  releaseView.did !== publisherDid ||
1466
1325
  releaseView.package !== slug ||
1467
- signedRelease?.package !== slug ||
1468
- (opts?.version !== undefined && releaseView.version !== opts.version) ||
1469
- signedRelease?.version !== releaseView.version
1326
+ (opts?.version !== undefined && releaseView.version !== opts.version)
1470
1327
  ) {
1471
1328
  return {
1472
1329
  success: false,
@@ -1477,90 +1334,147 @@ export async function handleRegistryUpdate(
1477
1334
  },
1478
1335
  };
1479
1336
  }
1480
-
1481
- const newVersion = releaseView.version;
1482
- if (newVersion === oldVersion) {
1337
+ const packageView = await discovery.getPackage({ did: publisherDid, slug });
1338
+ if (!packageView || packageView.did !== publisherDid || packageView.slug !== slug) {
1483
1339
  return {
1484
1340
  success: false,
1485
1341
  error: {
1486
- code: "ALREADY_UP_TO_DATE",
1487
- message: "Plugin is already at the requested version",
1342
+ code: "AGGREGATOR_IDENTITY_MISMATCH",
1343
+ message: "Aggregator returned a package view that does not match the installed plugin.",
1488
1344
  },
1489
1345
  };
1490
1346
  }
1491
1347
 
1492
- // Yanked label check (mirrors install).
1493
- const releaseYanked = (releaseView.labels ?? []).some(
1494
- (l: { val?: string }) => l.val === "security:yanked",
1495
- );
1496
- if (releaseYanked) {
1348
+ const newVersion = releaseView.version;
1349
+ if (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {
1497
1350
  return {
1498
1351
  success: false,
1499
- error: { code: "YANKED", message: "Release has been yanked by a trusted labeller" },
1352
+ error: { code: "YANKED", message: "Release has been withdrawn" },
1500
1353
  };
1501
1354
  }
1502
-
1503
- // Environment compatibility gate. An ungated update could otherwise
1504
- // land a version whose `requires` the host doesn't satisfy. Same
1505
- // guard as install; `requires` is lexicon-`unknown`.
1506
- if (opts?.hostEnv) {
1507
- const envError = assertEnvCompatible(signedRelease.requires, opts.hostEnv);
1508
- if (envError) return { success: false, error: envError };
1509
- }
1510
-
1511
- const declaredUrl = signedRelease.artifacts?.package?.url;
1512
- const declaredChecksum = signedRelease.artifacts?.package?.checksum;
1513
- if (!declaredUrl || !declaredChecksum) {
1355
+ if (newVersion === oldVersion) {
1514
1356
  return {
1515
1357
  success: false,
1516
1358
  error: {
1517
- code: "INVALID_RELEASE",
1518
- message: "Release record is missing artifact url or checksum",
1359
+ code: "ALREADY_UP_TO_DATE",
1360
+ message: "Plugin is already at the requested version",
1519
1361
  },
1520
1362
  };
1521
1363
  }
1522
-
1523
- // SSRF check on declared URL + each mirror.
1524
- await assertSafeArtifactUrl(declaredUrl);
1525
- const rawMirrors = releaseView.mirrors ?? [];
1526
- const mirrors = rawMirrors.slice(0, MAX_MIRRORS);
1527
- for (const mirror of mirrors) {
1528
- await assertSafeArtifactUrl(mirror);
1364
+ const authoritative = await (opts?.readAuthoritativeRecords ?? readAuthoritativePackageRelease)(
1365
+ publisherDid,
1366
+ slug,
1367
+ newVersion,
1368
+ opts?.authoritativeRecords,
1369
+ );
1370
+ if (!authoritative.success) {
1371
+ return registryRecordError(authoritative.error.code, authoritative.error.message);
1529
1372
  }
1530
-
1531
- // `fetchArtifact` derives its own per-call deadline internally.
1532
- const artifactBytes = await fetchArtifact(mirrors, declaredUrl);
1533
- if (!(await verifyChecksum(artifactBytes, declaredChecksum))) {
1373
+ const records = authoritative.value;
1374
+ const { profile, release } = records.inspection.value;
1375
+ if (
1376
+ packageView.uri !== records.profile.uri ||
1377
+ packageView.cid !== records.profile.cid ||
1378
+ releaseView.uri !== records.release.uri ||
1379
+ releaseView.cid !== records.release.cid
1380
+ ) {
1534
1381
  return {
1535
1382
  success: false,
1536
1383
  error: {
1537
- code: "CHECKSUM_MISMATCH",
1538
- message: "Artifact bytes do not match the release's published checksum",
1384
+ code: "AGGREGATOR_RECORD_MISMATCH",
1385
+ message: "Aggregator metadata does not match the publisher's signed records.",
1539
1386
  },
1540
1387
  };
1541
1388
  }
1542
-
1543
- const bundle: PluginBundle = await extractBundle(artifactBytes);
1544
-
1545
- if (bundle.manifest.version !== newVersion) {
1389
+ const packageYanked =
1390
+ hasCurrentRecordLabel(packageView.labels ?? [], "security:yanked", records.profile) ||
1391
+ hasCurrentRecordLabel(packageView.labels ?? [], "security-yanked", records.profile);
1392
+ if (packageYanked) {
1546
1393
  return {
1547
1394
  success: false,
1548
- error: {
1549
- code: "BUNDLE_VERSION_MISMATCH",
1550
- message: `Bundle manifest version (${bundle.manifest.version}) does not match release version (${newVersion})`,
1551
- },
1395
+ error: { code: "YANKED", message: "Package has been withdrawn" },
1552
1396
  };
1553
1397
  }
1554
- if (bundle.manifest.id !== slug) {
1398
+ if (
1399
+ opts?.confirmCapabilityChanges ||
1400
+ opts?.confirmRouteVisibilityChanges ||
1401
+ opts?.confirmMcpTools
1402
+ ) {
1403
+ const consentError = recordConsentError(
1404
+ {
1405
+ profileCid: opts.acknowledgedProfileCid,
1406
+ releaseCid: opts.acknowledgedReleaseCid,
1407
+ },
1408
+ records,
1409
+ );
1410
+ if (consentError) return consentError;
1411
+ }
1412
+ // Environment compatibility remains independent from listing approval.
1413
+ // An ungated update could otherwise
1414
+ // land a version whose `requires` the host doesn't satisfy. Same
1415
+ // guard as install; `requires` is lexicon-`unknown`.
1416
+ if (opts?.hostEnv) {
1417
+ const envError = assertEnvCompatible(release.requires, opts.hostEnv);
1418
+ if (envError) return { success: false, error: envError };
1419
+ }
1420
+
1421
+ const packageArtifact = release.artifacts.package;
1422
+ const declaredChecksum = packageArtifact?.checksum;
1423
+ if (!packageArtifact || !declaredChecksum) {
1555
1424
  return {
1556
1425
  success: false,
1557
1426
  error: {
1558
- code: "BUNDLE_IDENTITY_MISMATCH",
1559
- message: `Bundle manifest id (${bundle.manifest.id}) does not match registry slug (${slug})`,
1427
+ code: "INVALID_RELEASE",
1428
+ message: "Release record is missing its package artifact or checksum",
1560
1429
  },
1561
1430
  };
1562
1431
  }
1563
1432
 
1433
+ let artifactBytes: Uint8Array;
1434
+ try {
1435
+ artifactBytes = await fetchArtifact(
1436
+ releaseView.artifactCaches ?? [],
1437
+ packageArtifact,
1438
+ {
1439
+ did: publisherDid,
1440
+ collection: NSID.packageRelease,
1441
+ rkey: `${slug}:${newVersion}`,
1442
+ cid: releaseView.cid,
1443
+ },
1444
+ release.auth,
1445
+ );
1446
+ } catch (error) {
1447
+ if (error instanceof RegistryArtifactFetchError) {
1448
+ return registryArtifactError(error.code, error.message, "update");
1449
+ }
1450
+ throw error;
1451
+ }
1452
+ const artifactReport = await validateRegistryArtifact(
1453
+ artifactBytes,
1454
+ declaredChecksum,
1455
+ slug,
1456
+ newVersion,
1457
+ );
1458
+ if (!artifactReport.success) {
1459
+ return registryArtifactError(
1460
+ artifactReport.error.code,
1461
+ artifactReport.error.message,
1462
+ "update",
1463
+ );
1464
+ }
1465
+ const { bundle, artifactDigest, artifactDigests } = artifactReport.value;
1466
+ const recordReport = await verifyAuthoritativePackageRelease(records, artifactDigest, {
1467
+ ...opts?.authoritativeRecords,
1468
+ artifactDigests,
1469
+ });
1470
+ if (!recordReport.success) {
1471
+ return registryRecordError(
1472
+ recordReport.code,
1473
+ recordReport.reasons[0]?.message ?? "The release provenance is invalid.",
1474
+ );
1475
+ }
1476
+ const verification = recordVerificationSummary(records, recordReport);
1477
+
1564
1478
  // Rewrite manifest.id to the opaque pluginId so the sandbox loader
1565
1479
  // and R2 layout stay in sync across install and update.
1566
1480
  bundle.manifest = { ...bundle.manifest, id: pluginId };
@@ -1570,13 +1484,8 @@ export async function handleRegistryUpdate(
1570
1484
  // that changes only the host scope (e.g. api.good.com -> evil.com) keeps
1571
1485
  // the capability set identical, sails through the escalation diff below,
1572
1486
  // and installs a bundle enforcing a scope the record never showed.
1573
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- extensions is the lexicon's open `unknown` map; narrow to read our own extension
1574
- const updateRecordExtensions = signedRelease?.extensions as
1575
- | Record<string, { declaredAccess?: DeclaredAccess }>
1576
- | undefined;
1577
- const recordExt = updateRecordExtensions?.[RELEASE_EXTENSION_NSID];
1578
1487
  if (
1579
- !enforcedAccessEqual(recordExt?.declaredAccess ?? {}, bundle.manifest.declaredAccess ?? {})
1488
+ !verifiedAccessEqual(recordReport.value.declaredAccess, bundle.manifest.declaredAccess ?? {})
1580
1489
  ) {
1581
1490
  return {
1582
1491
  success: false,
@@ -1602,7 +1511,7 @@ export async function handleRegistryUpdate(
1602
1511
  error: {
1603
1512
  code: "CAPABILITY_ESCALATION",
1604
1513
  message: "Plugin update requires new capabilities",
1605
- details: { capabilityChanges },
1514
+ details: { capabilityChanges, verification },
1606
1515
  },
1607
1516
  };
1608
1517
  }
@@ -1615,7 +1524,7 @@ export async function handleRegistryUpdate(
1615
1524
  error: {
1616
1525
  code: "ROUTE_VISIBILITY_ESCALATION",
1617
1526
  message: "Plugin update exposes new public (unauthenticated) routes",
1618
- details: { routeVisibilityChanges, capabilityChanges },
1527
+ details: { routeVisibilityChanges, capabilityChanges, verification },
1619
1528
  },
1620
1529
  };
1621
1530
  }
@@ -1634,6 +1543,7 @@ export async function handleRegistryUpdate(
1634
1543
  message: "Plugin update changes its MCP tools",
1635
1544
  details: {
1636
1545
  mcpTools: newMcpTools.map(({ inputSchema: _, outputSchema: __, ...tool }) => tool),
1546
+ verification,
1637
1547
  },
1638
1548
  },
1639
1549
  };
@@ -1643,17 +1553,14 @@ export async function handleRegistryUpdate(
1643
1553
  // so a retry of the same update is idempotent.
1644
1554
  await storeBundleInR2(storage, pluginId, newVersion, bundle, "registry");
1645
1555
 
1646
- // Update state. Preserve publisher/slug; refresh displayName /
1647
- // description from the install handler's seeded values (we don't
1648
- // re-fetch the profile here — that's a separate `getPackage` round
1649
- // trip and the install-time values are still authoritative for
1650
- // the same package identity).
1556
+ // Refresh display metadata from the same signed profile used for
1557
+ // release-policy verification.
1651
1558
  await stateRepo.upsert(pluginId, newVersion, "active", {
1652
1559
  source: "registry",
1653
1560
  registryPublisherDid: publisherDid,
1654
1561
  registrySlug: slug,
1655
- displayName: existing.displayName ?? slug,
1656
- description: existing.description ?? undefined,
1562
+ displayName: profile.name ?? slug,
1563
+ description: profile.description ?? undefined,
1657
1564
  mcpToolsEnabled: false,
1658
1565
  mcpToolsConsent: null,
1659
1566
  });
@@ -1673,6 +1580,7 @@ export async function handleRegistryUpdate(
1673
1580
  newVersion,
1674
1581
  capabilityChanges,
1675
1582
  routeVisibilityChanges: hasNewPublicRoutes ? routeVisibilityChanges : undefined,
1583
+ verification,
1676
1584
  },
1677
1585
  };
1678
1586
  } catch (err) {
@@ -1686,6 +1594,15 @@ export async function handleRegistryUpdate(
1686
1594
  };
1687
1595
  }
1688
1596
  if (err instanceof ClientResponseError) {
1597
+ if (err.error === "ListingUnavailable") {
1598
+ return {
1599
+ success: false,
1600
+ error: {
1601
+ code: "LISTING_UNAVAILABLE",
1602
+ message: "This plugin is unavailable under the active registry policy",
1603
+ },
1604
+ };
1605
+ }
1689
1606
  return {
1690
1607
  success: false,
1691
1608
  error: {
@@ -1759,11 +1676,13 @@ export async function handleRegistryUpdateCheck(
1759
1676
  return { success: true, data: { items: [] } };
1760
1677
  }
1761
1678
 
1762
- const { DiscoveryClient } = await import("@emdash-cms/registry-client/discovery");
1679
+ const { DiscoveryClient, registryLabelerPolicy } =
1680
+ await import("@emdash-cms/registry-client/discovery");
1763
1681
  const aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;
1764
1682
  const discovery = new DiscoveryClient({
1765
1683
  aggregatorUrl: registryConfig.aggregatorUrl,
1766
1684
  acceptLabelers: registryConfig.acceptLabelers,
1685
+ labelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),
1767
1686
  fetch: timedFetch(aggregatorDeadline),
1768
1687
  });
1769
1688
 
@@ -1776,6 +1695,9 @@ export async function handleRegistryUpdateCheck(
1776
1695
  did: plugin.registryPublisherDid as Did,
1777
1696
  package: plugin.registrySlug,
1778
1697
  });
1698
+ if (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {
1699
+ continue;
1700
+ }
1779
1701
  const latest = releaseView.version;
1780
1702
  if (!latest) continue;
1781
1703
  const installed = plugin.version;