emdash 0.39.0 → 0.40.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 (1650) hide show
  1. package/dist/action-token-DYwe4Pk0.mjs +26 -0
  2. package/dist/action-token-DYwe4Pk0.mjs.map +1 -0
  3. package/dist/activation-LOkDM5WT.mjs +382 -0
  4. package/dist/activation-LOkDM5WT.mjs.map +1 -0
  5. package/dist/adapters-B64LbYcB.d.mts +171 -0
  6. package/dist/adapters-B64LbYcB.d.mts.map +1 -0
  7. package/dist/allowed-origins-CHB8oZNM.mjs +116 -0
  8. package/dist/allowed-origins-CHB8oZNM.mjs.map +1 -0
  9. package/dist/api/route-utils.d.mts +4 -4
  10. package/dist/api/route-utils.mjs +49 -40
  11. package/dist/api/route-utils.mjs.map +1 -1
  12. package/dist/api/schemas/index.d.mts +2 -2
  13. package/dist/api/schemas/index.mjs +11 -7
  14. package/dist/api-cFFZpyS0.mjs +4718 -0
  15. package/dist/api-cFFZpyS0.mjs.map +1 -0
  16. package/dist/api-tokens-CE1ECIg0.mjs +3 -0
  17. package/dist/api-tokens-s4i4aUp_.mjs +166 -0
  18. package/dist/api-tokens-s4i4aUp_.mjs.map +1 -0
  19. package/dist/apply-CoT603H3.mjs +1163 -0
  20. package/dist/apply-CoT603H3.mjs.map +1 -0
  21. package/dist/approvals-CqMfNxak.mjs +460 -0
  22. package/dist/approvals-CqMfNxak.mjs.map +1 -0
  23. package/dist/artifact-fetch-DZMOGZ3-.mjs +818 -0
  24. package/dist/artifact-fetch-DZMOGZ3-.mjs.map +1 -0
  25. package/dist/astro/index.d.mts +14 -14
  26. package/dist/astro/index.mjs +62 -12
  27. package/dist/astro/index.mjs.map +1 -1
  28. package/dist/astro/middleware/auth.d.mts +14 -12
  29. package/dist/astro/middleware/auth.d.mts.map +1 -1
  30. package/dist/astro/middleware/auth.mjs +26 -11
  31. package/dist/astro/middleware/auth.mjs.map +1 -1
  32. package/dist/astro/middleware/media-usage-write-fence.d.mts +9 -1
  33. package/dist/astro/middleware/media-usage-write-fence.d.mts.map +1 -1
  34. package/dist/astro/middleware/media-usage-write-fence.mjs +95 -13
  35. package/dist/astro/middleware/media-usage-write-fence.mjs.map +1 -1
  36. package/dist/astro/middleware/redirect.mjs +8 -8
  37. package/dist/astro/middleware/request-context.d.mts.map +1 -1
  38. package/dist/astro/middleware/request-context.mjs +145 -47
  39. package/dist/astro/middleware/request-context.mjs.map +1 -1
  40. package/dist/astro/middleware/setup.mjs +1 -1
  41. package/dist/astro/middleware.d.mts +12 -12
  42. package/dist/astro/middleware.d.mts.map +1 -1
  43. package/dist/astro/middleware.mjs +129 -122
  44. package/dist/astro/middleware.mjs.map +1 -1
  45. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +14 -8
  46. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs.map +1 -1
  47. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +14 -8
  48. package/dist/astro/routes/api/admin/allowed-domains/index.mjs.map +1 -1
  49. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +5 -3
  50. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs.map +1 -1
  51. package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -4
  52. package/dist/astro/routes/api/admin/api-tokens/index.mjs.map +1 -1
  53. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +6 -4
  54. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs.map +1 -1
  55. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +17 -11
  56. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs.map +1 -1
  57. package/dist/astro/routes/api/admin/byline-fields/index.mjs +17 -11
  58. package/dist/astro/routes/api/admin/byline-fields/index.mjs.map +1 -1
  59. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +17 -11
  60. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs.map +1 -1
  61. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +25 -19
  62. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
  63. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +25 -19
  64. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
  65. package/dist/astro/routes/api/admin/bylines/index.mjs +25 -19
  66. package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
  67. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +19 -13
  68. package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
  69. package/dist/astro/routes/api/admin/comments/_id_.mjs +7 -5
  70. package/dist/astro/routes/api/admin/comments/_id_.mjs.map +1 -1
  71. package/dist/astro/routes/api/admin/comments/bulk.mjs +18 -12
  72. package/dist/astro/routes/api/admin/comments/bulk.mjs.map +1 -1
  73. package/dist/astro/routes/api/admin/comments/counts.mjs +7 -5
  74. package/dist/astro/routes/api/admin/comments/counts.mjs.map +1 -1
  75. package/dist/astro/routes/api/admin/comments/index.mjs +18 -12
  76. package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
  77. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +6 -4
  78. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs.map +1 -1
  79. package/dist/astro/routes/api/admin/hooks/exclusive/index.d.mts.map +1 -1
  80. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +5 -6
  81. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/media-usage/activation.mjs +21 -15
  83. package/dist/astro/routes/api/admin/media-usage/activation.mjs.map +1 -1
  84. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +23 -17
  85. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs.map +1 -1
  86. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +23 -17
  87. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs.map +1 -1
  88. package/dist/astro/routes/api/admin/media-usage/progress.mjs +26 -15
  89. package/dist/astro/routes/api/admin/media-usage/progress.mjs.map +1 -1
  90. package/dist/astro/routes/api/admin/media-usage/repair.mjs +32 -19
  91. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
  92. package/dist/astro/routes/api/admin/media-usage/work/index.mjs +23 -17
  93. package/dist/astro/routes/api/admin/media-usage/work/index.mjs.map +1 -1
  94. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +23 -17
  95. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs.map +1 -1
  96. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +5 -3
  97. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs.map +1 -1
  98. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +5 -3
  99. package/dist/astro/routes/api/admin/oauth-clients/index.mjs.map +1 -1
  100. package/dist/astro/routes/api/admin/plugins/_id_/disable.d.mts.map +1 -1
  101. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +10 -8
  102. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  103. package/dist/astro/routes/api/admin/plugins/_id_/enable.d.mts.map +1 -1
  104. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +10 -8
  105. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  106. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +72 -61
  107. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  108. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +6 -4
  109. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs.map +1 -1
  110. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +71 -60
  111. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
  112. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.d.mts.map +1 -1
  113. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +74 -63
  114. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  115. package/dist/astro/routes/api/admin/plugins/_id_/update.d.mts.map +1 -1
  116. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +84 -73
  117. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  118. package/dist/astro/routes/api/admin/plugins/index.mjs +72 -61
  119. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  120. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +4 -2
  121. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs.map +1 -1
  122. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +71 -60
  123. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  124. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.d.mts.map +1 -1
  125. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +83 -72
  126. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  127. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +71 -60
  128. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  129. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.d.mts.map +1 -1
  130. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +74 -63
  131. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  132. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +85 -74
  133. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  134. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +8 -6
  135. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  136. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +76 -65
  137. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  138. package/dist/astro/routes/api/admin/plugins/registry/publisher-handle.d.mts +8 -0
  139. package/dist/astro/routes/api/admin/plugins/registry/publisher-handle.d.mts.map +1 -0
  140. package/dist/astro/routes/api/admin/plugins/registry/publisher-handle.mjs +221 -0
  141. package/dist/astro/routes/api/admin/plugins/registry/publisher-handle.mjs.map +1 -0
  142. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +72 -61
  143. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs.map +1 -1
  144. package/dist/astro/routes/api/admin/plugins/updates.mjs +71 -60
  145. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  146. package/dist/astro/routes/api/admin/scheduled-policy-rejections/_collection_/_id_.mjs +6 -4
  147. package/dist/astro/routes/api/admin/scheduled-policy-rejections/_collection_/_id_.mjs.map +1 -1
  148. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +71 -60
  149. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  150. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +4 -2
  151. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs.map +1 -1
  152. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +71 -60
  153. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  154. package/dist/astro/routes/api/admin/transfer/_...path_.d.mts +12 -0
  155. package/dist/astro/routes/api/admin/transfer/_...path_.d.mts.map +1 -0
  156. package/dist/astro/routes/api/admin/transfer/_...path_.mjs +144 -0
  157. package/dist/astro/routes/api/admin/transfer/_...path_.mjs.map +1 -0
  158. package/dist/astro/routes/api/admin/transfer/approvals/_id_/approve.d.mts +8 -0
  159. package/dist/astro/routes/api/admin/transfer/approvals/_id_/approve.d.mts.map +1 -0
  160. package/dist/astro/routes/api/admin/transfer/approvals/_id_/approve.mjs +68 -0
  161. package/dist/astro/routes/api/admin/transfer/approvals/_id_/approve.mjs.map +1 -0
  162. package/dist/astro/routes/api/admin/transfer/approvals/_id_/deny.d.mts +8 -0
  163. package/dist/astro/routes/api/admin/transfer/approvals/_id_/deny.d.mts.map +1 -0
  164. package/dist/astro/routes/api/admin/transfer/approvals/_id_/deny.mjs +68 -0
  165. package/dist/astro/routes/api/admin/transfer/approvals/_id_/deny.mjs.map +1 -0
  166. package/dist/astro/routes/api/admin/transfer/approvals/index.d.mts +8 -0
  167. package/dist/astro/routes/api/admin/transfer/approvals/index.d.mts.map +1 -0
  168. package/dist/astro/routes/api/admin/transfer/approvals/index.mjs +67 -0
  169. package/dist/astro/routes/api/admin/transfer/approvals/index.mjs.map +1 -0
  170. package/dist/astro/routes/api/admin/transfer/capabilities.d.mts +8 -0
  171. package/dist/astro/routes/api/admin/transfer/capabilities.d.mts.map +1 -0
  172. package/dist/astro/routes/api/admin/transfer/capabilities.mjs +70 -0
  173. package/dist/astro/routes/api/admin/transfer/capabilities.mjs.map +1 -0
  174. package/dist/astro/routes/api/admin/transfer/exports/_id_/advance.d.mts +8 -0
  175. package/dist/astro/routes/api/admin/transfer/exports/_id_/advance.d.mts.map +1 -0
  176. package/dist/astro/routes/api/admin/transfer/exports/_id_/advance.mjs +67 -0
  177. package/dist/astro/routes/api/admin/transfer/exports/_id_/advance.mjs.map +1 -0
  178. package/dist/astro/routes/api/admin/transfer/exports/_id_/archive.d.mts +8 -0
  179. package/dist/astro/routes/api/admin/transfer/exports/_id_/archive.d.mts.map +1 -0
  180. package/dist/astro/routes/api/admin/transfer/exports/_id_/archive.mjs +70 -0
  181. package/dist/astro/routes/api/admin/transfer/exports/_id_/archive.mjs.map +1 -0
  182. package/dist/astro/routes/api/admin/transfer/exports/_id_/files/_...path_.d.mts +8 -0
  183. package/dist/astro/routes/api/admin/transfer/exports/_id_/files/_...path_.d.mts.map +1 -0
  184. package/dist/astro/routes/api/admin/transfer/exports/_id_/files/_...path_.mjs +69 -0
  185. package/dist/astro/routes/api/admin/transfer/exports/_id_/files/_...path_.mjs.map +1 -0
  186. package/dist/astro/routes/api/admin/transfer/exports/_id_/index.d.mts +8 -0
  187. package/dist/astro/routes/api/admin/transfer/exports/_id_/index.d.mts.map +1 -0
  188. package/dist/astro/routes/api/admin/transfer/exports/_id_/index.mjs +66 -0
  189. package/dist/astro/routes/api/admin/transfer/exports/_id_/index.mjs.map +1 -0
  190. package/dist/astro/routes/api/admin/transfer/exports/_id_/manifest.d.mts +8 -0
  191. package/dist/astro/routes/api/admin/transfer/exports/_id_/manifest.d.mts.map +1 -0
  192. package/dist/astro/routes/api/admin/transfer/exports/_id_/manifest.mjs +69 -0
  193. package/dist/astro/routes/api/admin/transfer/exports/_id_/manifest.mjs.map +1 -0
  194. package/dist/astro/routes/api/admin/transfer/exports/index.d.mts +9 -0
  195. package/dist/astro/routes/api/admin/transfer/exports/index.d.mts.map +1 -0
  196. package/dist/astro/routes/api/admin/transfer/exports/index.mjs +91 -0
  197. package/dist/astro/routes/api/admin/transfer/exports/index.mjs.map +1 -0
  198. package/dist/astro/routes/api/admin/transfer/imports/_id_/abandon.d.mts +8 -0
  199. package/dist/astro/routes/api/admin/transfer/imports/_id_/abandon.d.mts.map +1 -0
  200. package/dist/astro/routes/api/admin/transfer/imports/_id_/abandon.mjs +66 -0
  201. package/dist/astro/routes/api/admin/transfer/imports/_id_/abandon.mjs.map +1 -0
  202. package/dist/astro/routes/api/admin/transfer/imports/_id_/advance.d.mts +8 -0
  203. package/dist/astro/routes/api/admin/transfer/imports/_id_/advance.d.mts.map +1 -0
  204. package/dist/astro/routes/api/admin/transfer/imports/_id_/advance.mjs +70 -0
  205. package/dist/astro/routes/api/admin/transfer/imports/_id_/advance.mjs.map +1 -0
  206. package/dist/astro/routes/api/admin/transfer/imports/_id_/analyze.d.mts +8 -0
  207. package/dist/astro/routes/api/admin/transfer/imports/_id_/analyze.d.mts.map +1 -0
  208. package/dist/astro/routes/api/admin/transfer/imports/_id_/analyze.mjs +78 -0
  209. package/dist/astro/routes/api/admin/transfer/imports/_id_/analyze.mjs.map +1 -0
  210. package/dist/astro/routes/api/admin/transfer/imports/_id_/cancel.d.mts +8 -0
  211. package/dist/astro/routes/api/admin/transfer/imports/_id_/cancel.d.mts.map +1 -0
  212. package/dist/astro/routes/api/admin/transfer/imports/_id_/cancel.mjs +66 -0
  213. package/dist/astro/routes/api/admin/transfer/imports/_id_/cancel.mjs.map +1 -0
  214. package/dist/astro/routes/api/admin/transfer/imports/_id_/execute.d.mts +8 -0
  215. package/dist/astro/routes/api/admin/transfer/imports/_id_/execute.d.mts.map +1 -0
  216. package/dist/astro/routes/api/admin/transfer/imports/_id_/execute.mjs +77 -0
  217. package/dist/astro/routes/api/admin/transfer/imports/_id_/execute.mjs.map +1 -0
  218. package/dist/astro/routes/api/admin/transfer/imports/_id_/files/_...path_.d.mts +8 -0
  219. package/dist/astro/routes/api/admin/transfer/imports/_id_/files/_...path_.d.mts.map +1 -0
  220. package/dist/astro/routes/api/admin/transfer/imports/_id_/files/_...path_.mjs +77 -0
  221. package/dist/astro/routes/api/admin/transfer/imports/_id_/files/_...path_.mjs.map +1 -0
  222. package/dist/astro/routes/api/admin/transfer/imports/_id_/index.d.mts +8 -0
  223. package/dist/astro/routes/api/admin/transfer/imports/_id_/index.d.mts.map +1 -0
  224. package/dist/astro/routes/api/admin/transfer/imports/_id_/index.mjs +66 -0
  225. package/dist/astro/routes/api/admin/transfer/imports/_id_/index.mjs.map +1 -0
  226. package/dist/astro/routes/api/admin/transfer/imports/_id_/missing.d.mts +8 -0
  227. package/dist/astro/routes/api/admin/transfer/imports/_id_/missing.d.mts.map +1 -0
  228. package/dist/astro/routes/api/admin/transfer/imports/_id_/missing.mjs +70 -0
  229. package/dist/astro/routes/api/admin/transfer/imports/_id_/missing.mjs.map +1 -0
  230. package/dist/astro/routes/api/admin/transfer/imports/_id_/plan.d.mts +8 -0
  231. package/dist/astro/routes/api/admin/transfer/imports/_id_/plan.d.mts.map +1 -0
  232. package/dist/astro/routes/api/admin/transfer/imports/_id_/plan.mjs +67 -0
  233. package/dist/astro/routes/api/admin/transfer/imports/_id_/plan.mjs.map +1 -0
  234. package/dist/astro/routes/api/admin/transfer/imports/_id_/receipt.d.mts +8 -0
  235. package/dist/astro/routes/api/admin/transfer/imports/_id_/receipt.d.mts.map +1 -0
  236. package/dist/astro/routes/api/admin/transfer/imports/_id_/receipt.mjs +66 -0
  237. package/dist/astro/routes/api/admin/transfer/imports/_id_/receipt.mjs.map +1 -0
  238. package/dist/astro/routes/api/admin/transfer/imports/index.d.mts +9 -0
  239. package/dist/astro/routes/api/admin/transfer/imports/index.d.mts.map +1 -0
  240. package/dist/astro/routes/api/admin/transfer/imports/index.mjs +99 -0
  241. package/dist/astro/routes/api/admin/transfer/imports/index.mjs.map +1 -0
  242. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -1
  243. package/dist/astro/routes/api/admin/users/_id_/disable.mjs.map +1 -1
  244. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +3 -1
  245. package/dist/astro/routes/api/admin/users/_id_/enable.mjs.map +1 -1
  246. package/dist/astro/routes/api/admin/users/_id_/index.mjs +14 -8
  247. package/dist/astro/routes/api/admin/users/_id_/index.mjs.map +1 -1
  248. package/dist/astro/routes/api/admin/users/_id_/send-recovery.d.mts.map +1 -1
  249. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +16 -6
  250. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs.map +1 -1
  251. package/dist/astro/routes/api/admin/users/index.mjs +14 -8
  252. package/dist/astro/routes/api/admin/users/index.mjs.map +1 -1
  253. package/dist/astro/routes/api/auth/dev-bypass.mjs +5 -3
  254. package/dist/astro/routes/api/auth/dev-bypass.mjs.map +1 -1
  255. package/dist/astro/routes/api/auth/invite/accept.mjs +3 -1
  256. package/dist/astro/routes/api/auth/invite/accept.mjs.map +1 -1
  257. package/dist/astro/routes/api/auth/invite/complete.mjs +19 -13
  258. package/dist/astro/routes/api/auth/invite/complete.mjs.map +1 -1
  259. package/dist/astro/routes/api/auth/invite/index.d.mts.map +1 -1
  260. package/dist/astro/routes/api/auth/invite/index.mjs +24 -12
  261. package/dist/astro/routes/api/auth/invite/index.mjs.map +1 -1
  262. package/dist/astro/routes/api/auth/invite/register-options.mjs +18 -12
  263. package/dist/astro/routes/api/auth/invite/register-options.mjs.map +1 -1
  264. package/dist/astro/routes/api/auth/logout.mjs +4 -2
  265. package/dist/astro/routes/api/auth/logout.mjs.map +1 -1
  266. package/dist/astro/routes/api/auth/magic-link/send.d.mts.map +1 -1
  267. package/dist/astro/routes/api/auth/magic-link/send.mjs +29 -15
  268. package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -1
  269. package/dist/astro/routes/api/auth/magic-link/verify.d.mts +2 -1
  270. package/dist/astro/routes/api/auth/magic-link/verify.d.mts.map +1 -1
  271. package/dist/astro/routes/api/auth/magic-link/verify.mjs +37 -17
  272. package/dist/astro/routes/api/auth/magic-link/verify.mjs.map +1 -1
  273. package/dist/astro/routes/api/auth/me.mjs +15 -9
  274. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  275. package/dist/astro/routes/api/auth/mode.mjs +4 -2
  276. package/dist/astro/routes/api/auth/mode.mjs.map +1 -1
  277. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  278. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  279. package/dist/astro/routes/api/auth/passkey/_id_.mjs +14 -8
  280. package/dist/astro/routes/api/auth/passkey/_id_.mjs.map +1 -1
  281. package/dist/astro/routes/api/auth/passkey/index.mjs +3 -1
  282. package/dist/astro/routes/api/auth/passkey/index.mjs.map +1 -1
  283. package/dist/astro/routes/api/auth/passkey/options.d.mts.map +1 -1
  284. package/dist/astro/routes/api/auth/passkey/options.mjs +21 -24
  285. package/dist/astro/routes/api/auth/passkey/options.mjs.map +1 -1
  286. package/dist/astro/routes/api/auth/passkey/register/options.mjs +18 -12
  287. package/dist/astro/routes/api/auth/passkey/register/options.mjs.map +1 -1
  288. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +19 -13
  289. package/dist/astro/routes/api/auth/passkey/register/verify.mjs.map +1 -1
  290. package/dist/astro/routes/api/auth/passkey/verify.mjs +19 -13
  291. package/dist/astro/routes/api/auth/passkey/verify.mjs.map +1 -1
  292. package/dist/astro/routes/api/auth/signup/complete.mjs +19 -13
  293. package/dist/astro/routes/api/auth/signup/complete.mjs.map +1 -1
  294. package/dist/astro/routes/api/auth/signup/request.mjs +18 -12
  295. package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -1
  296. package/dist/astro/routes/api/auth/signup/verify.mjs +3 -1
  297. package/dist/astro/routes/api/auth/signup/verify.mjs.map +1 -1
  298. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +14 -11
  299. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
  300. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +21 -15
  301. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs.map +1 -1
  302. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +4 -2
  303. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs.map +1 -1
  304. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +18 -13
  305. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -1
  306. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +4 -3
  307. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs.map +1 -1
  308. package/dist/astro/routes/api/content/_collection_/_id_/lock.mjs +18 -13
  309. package/dist/astro/routes/api/content/_collection_/_id_/lock.mjs.map +1 -1
  310. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +4 -2
  311. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs.map +1 -1
  312. package/dist/astro/routes/api/content/_collection_/_id_/plugin-extensions/_pluginId_/_kind_/_extensionId_.mjs +10 -8
  313. package/dist/astro/routes/api/content/_collection_/_id_/plugin-extensions/_pluginId_/_kind_/_extensionId_.mjs.map +1 -1
  314. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.d.mts.map +1 -1
  315. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +68 -16
  316. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs.map +1 -1
  317. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +18 -13
  318. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
  319. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +31 -22
  320. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
  321. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +31 -22
  322. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
  323. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +4 -3
  324. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs.map +1 -1
  325. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +4 -2
  326. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs.map +1 -1
  327. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +18 -13
  328. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -1
  329. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.d.mts.map +1 -1
  330. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +38 -19
  331. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
  332. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +4 -2
  333. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs.map +1 -1
  334. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +18 -13
  335. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
  336. package/dist/astro/routes/api/content/_collection_/_id_.mjs +18 -13
  337. package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
  338. package/dist/astro/routes/api/content/_collection_/authors.mjs +4 -2
  339. package/dist/astro/routes/api/content/_collection_/authors.mjs.map +1 -1
  340. package/dist/astro/routes/api/content/_collection_/index.mjs +15 -10
  341. package/dist/astro/routes/api/content/_collection_/index.mjs.map +1 -1
  342. package/dist/astro/routes/api/content/_collection_/trash.mjs +15 -9
  343. package/dist/astro/routes/api/content/_collection_/trash.mjs.map +1 -1
  344. package/dist/astro/routes/api/dashboard.mjs +11 -9
  345. package/dist/astro/routes/api/dashboard.mjs.map +1 -1
  346. package/dist/astro/routes/api/dev/emails.mjs +4 -2
  347. package/dist/astro/routes/api/dev/emails.mjs.map +1 -1
  348. package/dist/astro/routes/api/health.mjs +5 -3
  349. package/dist/astro/routes/api/health.mjs.map +1 -1
  350. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  351. package/dist/astro/routes/api/import/probe.mjs +27 -21
  352. package/dist/astro/routes/api/import/probe.mjs.map +1 -1
  353. package/dist/astro/routes/api/import/wordpress/analyze.mjs +5 -3
  354. package/dist/astro/routes/api/import/wordpress/analyze.mjs.map +1 -1
  355. package/dist/astro/routes/api/import/wordpress/execute.d.mts +13 -13
  356. package/dist/astro/routes/api/import/wordpress/execute.mjs +18 -16
  357. package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
  358. package/dist/astro/routes/api/import/wordpress/media.d.mts +12 -12
  359. package/dist/astro/routes/api/import/wordpress/media.mjs +19 -13
  360. package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
  361. package/dist/astro/routes/api/import/wordpress/prepare.mjs +18 -12
  362. package/dist/astro/routes/api/import/wordpress/prepare.mjs.map +1 -1
  363. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.d.mts +5 -1
  364. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.d.mts.map +1 -1
  365. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.mjs +4 -2
  366. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.mjs.map +1 -1
  367. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +13 -13
  368. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +29 -15
  369. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
  370. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  371. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +27 -21
  372. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -1
  373. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +13 -13
  374. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +38 -32
  375. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
  376. package/dist/astro/routes/api/manifest.mjs +6 -4
  377. package/dist/astro/routes/api/manifest.mjs.map +1 -1
  378. package/dist/astro/routes/api/mcp.d.mts.map +1 -1
  379. package/dist/astro/routes/api/mcp.mjs +717 -67
  380. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  381. package/dist/astro/routes/api/media/_id_/confirm.mjs +19 -13
  382. package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
  383. package/dist/astro/routes/api/media/_id_/replace.mjs +16 -10
  384. package/dist/astro/routes/api/media/_id_/replace.mjs.map +1 -1
  385. package/dist/astro/routes/api/media/_id_/upload.mjs +8 -6
  386. package/dist/astro/routes/api/media/_id_/upload.mjs.map +1 -1
  387. package/dist/astro/routes/api/media/_id_/usage.mjs +34 -21
  388. package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
  389. package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
  390. package/dist/astro/routes/api/media/_id_.mjs +37 -26
  391. package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
  392. package/dist/astro/routes/api/media/asset/_id_/_filename_.mjs +5 -3
  393. package/dist/astro/routes/api/media/asset/_id_/_filename_.mjs.map +1 -1
  394. package/dist/astro/routes/api/media/file/_...key_.d.mts.map +1 -1
  395. package/dist/astro/routes/api/media/file/_...key_.mjs +31 -2
  396. package/dist/astro/routes/api/media/file/_...key_.mjs.map +1 -1
  397. package/dist/astro/routes/api/media/folders/_id_.mjs +16 -10
  398. package/dist/astro/routes/api/media/folders/_id_.mjs.map +1 -1
  399. package/dist/astro/routes/api/media/folders/index.mjs +16 -10
  400. package/dist/astro/routes/api/media/folders/index.mjs.map +1 -1
  401. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +4 -2
  402. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs.map +1 -1
  403. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +4 -2
  404. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs.map +1 -1
  405. package/dist/astro/routes/api/media/providers/index.mjs +4 -2
  406. package/dist/astro/routes/api/media/providers/index.mjs.map +1 -1
  407. package/dist/astro/routes/api/media/upload-url.mjs +18 -12
  408. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
  409. package/dist/astro/routes/api/media.mjs +36 -24
  410. package/dist/astro/routes/api/media.mjs.map +1 -1
  411. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +18 -12
  412. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
  413. package/dist/astro/routes/api/menus/_name_/items.mjs +18 -12
  414. package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
  415. package/dist/astro/routes/api/menus/_name_/reorder.mjs +18 -12
  416. package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
  417. package/dist/astro/routes/api/menus/_name_/translations.mjs +18 -12
  418. package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
  419. package/dist/astro/routes/api/menus/_name_.mjs +18 -12
  420. package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
  421. package/dist/astro/routes/api/menus/index.mjs +18 -12
  422. package/dist/astro/routes/api/menus/index.mjs.map +1 -1
  423. package/dist/astro/routes/api/oauth/authorize.d.mts.map +1 -1
  424. package/dist/astro/routes/api/oauth/authorize.mjs +9 -6
  425. package/dist/astro/routes/api/oauth/authorize.mjs.map +1 -1
  426. package/dist/astro/routes/api/oauth/device/authorize.mjs +7 -5
  427. package/dist/astro/routes/api/oauth/device/authorize.mjs.map +1 -1
  428. package/dist/astro/routes/api/oauth/device/code.mjs +10 -8
  429. package/dist/astro/routes/api/oauth/device/code.mjs.map +1 -1
  430. package/dist/astro/routes/api/oauth/device/token.mjs +9 -7
  431. package/dist/astro/routes/api/oauth/device/token.mjs.map +1 -1
  432. package/dist/astro/routes/api/oauth/register.mjs +4 -2
  433. package/dist/astro/routes/api/oauth/register.mjs.map +1 -1
  434. package/dist/astro/routes/api/oauth/token/refresh.mjs +7 -5
  435. package/dist/astro/routes/api/oauth/token/refresh.mjs.map +1 -1
  436. package/dist/astro/routes/api/oauth/token/revoke.mjs +7 -5
  437. package/dist/astro/routes/api/oauth/token/revoke.mjs.map +1 -1
  438. package/dist/astro/routes/api/oauth/token.mjs +7 -5
  439. package/dist/astro/routes/api/oauth/token.mjs.map +1 -1
  440. package/dist/astro/routes/api/openapi.json.mjs +497 -12
  441. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  442. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +10 -8
  443. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs.map +1 -1
  444. package/dist/astro/routes/api/redirects/404s/index.mjs +21 -15
  445. package/dist/astro/routes/api/redirects/404s/index.mjs.map +1 -1
  446. package/dist/astro/routes/api/redirects/404s/summary.mjs +21 -15
  447. package/dist/astro/routes/api/redirects/404s/summary.mjs.map +1 -1
  448. package/dist/astro/routes/api/redirects/_id_.mjs +21 -15
  449. package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
  450. package/dist/astro/routes/api/redirects/index.mjs +21 -15
  451. package/dist/astro/routes/api/redirects/index.mjs.map +1 -1
  452. package/dist/astro/routes/api/relations/_id_/index.mjs +31 -22
  453. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
  454. package/dist/astro/routes/api/relations/_id_/translations.mjs +21 -16
  455. package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
  456. package/dist/astro/routes/api/relations/index.mjs +31 -22
  457. package/dist/astro/routes/api/relations/index.mjs.map +1 -1
  458. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +4 -2
  459. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs.map +1 -1
  460. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +4 -3
  461. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs.map +1 -1
  462. package/dist/astro/routes/api/schema/block-types/_slug_/index.d.mts +9 -0
  463. package/dist/astro/routes/api/schema/block-types/_slug_/index.d.mts.map +1 -0
  464. package/dist/astro/routes/api/schema/block-types/_slug_/index.mjs +102 -0
  465. package/dist/astro/routes/api/schema/block-types/_slug_/index.mjs.map +1 -0
  466. package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.d.mts +8 -0
  467. package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.d.mts.map +1 -0
  468. package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.mjs +96 -0
  469. package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.mjs.map +1 -0
  470. package/dist/astro/routes/api/schema/block-types/index.d.mts +9 -0
  471. package/dist/astro/routes/api/schema/block-types/index.d.mts.map +1 -0
  472. package/dist/astro/routes/api/schema/block-types/index.mjs +102 -0
  473. package/dist/astro/routes/api/schema/block-types/index.mjs.map +1 -0
  474. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +71 -60
  475. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  476. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +71 -60
  477. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  478. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +71 -60
  479. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  480. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +71 -60
  481. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  482. package/dist/astro/routes/api/schema/collections/index.mjs +71 -60
  483. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  484. package/dist/astro/routes/api/schema/collections/reorder.mjs +71 -60
  485. package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
  486. package/dist/astro/routes/api/schema/index.mjs +17 -14
  487. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  488. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +71 -60
  489. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  490. package/dist/astro/routes/api/schema/orphans/index.mjs +71 -60
  491. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  492. package/dist/astro/routes/api/search/enable.mjs +20 -14
  493. package/dist/astro/routes/api/search/enable.mjs.map +1 -1
  494. package/dist/astro/routes/api/search/index.mjs +19 -13
  495. package/dist/astro/routes/api/search/index.mjs.map +1 -1
  496. package/dist/astro/routes/api/search/rebuild.mjs +20 -14
  497. package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
  498. package/dist/astro/routes/api/search/stats.mjs +10 -8
  499. package/dist/astro/routes/api/search/stats.mjs.map +1 -1
  500. package/dist/astro/routes/api/search/suggest.mjs +19 -13
  501. package/dist/astro/routes/api/search/suggest.mjs.map +1 -1
  502. package/dist/astro/routes/api/sections/_slug_.mjs +19 -13
  503. package/dist/astro/routes/api/sections/_slug_.mjs.map +1 -1
  504. package/dist/astro/routes/api/sections/index.mjs +19 -13
  505. package/dist/astro/routes/api/sections/index.mjs.map +1 -1
  506. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +8 -7
  507. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs.map +1 -1
  508. package/dist/astro/routes/api/settings/backups/archives/index.mjs +8 -6
  509. package/dist/astro/routes/api/settings/backups/archives/index.mjs.map +1 -1
  510. package/dist/astro/routes/api/settings/backups/export.mjs +8 -7
  511. package/dist/astro/routes/api/settings/backups/export.mjs.map +1 -1
  512. package/dist/astro/routes/api/settings/backups/index.mjs +9 -8
  513. package/dist/astro/routes/api/settings/backups/index.mjs.map +1 -1
  514. package/dist/astro/routes/api/settings/email.mjs +6 -4
  515. package/dist/astro/routes/api/settings/email.mjs.map +1 -1
  516. package/dist/astro/routes/api/settings.mjs +25 -19
  517. package/dist/astro/routes/api/settings.mjs.map +1 -1
  518. package/dist/astro/routes/api/setup/admin-verify.mjs +20 -14
  519. package/dist/astro/routes/api/setup/admin-verify.mjs.map +1 -1
  520. package/dist/astro/routes/api/setup/admin.mjs +19 -13
  521. package/dist/astro/routes/api/setup/admin.mjs.map +1 -1
  522. package/dist/astro/routes/api/setup/dev-bypass.mjs +43 -37
  523. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  524. package/dist/astro/routes/api/setup/dev-reset.mjs +4 -2
  525. package/dist/astro/routes/api/setup/dev-reset.mjs.map +1 -1
  526. package/dist/astro/routes/api/setup/index.mjs +48 -38
  527. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  528. package/dist/astro/routes/api/setup/status.mjs +5 -3
  529. package/dist/astro/routes/api/setup/status.mjs.map +1 -1
  530. package/dist/astro/routes/api/snapshot.mjs +10 -8
  531. package/dist/astro/routes/api/snapshot.mjs.map +1 -1
  532. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +25 -19
  533. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -1
  534. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +25 -19
  535. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
  536. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +25 -19
  537. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
  538. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +25 -19
  539. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
  540. package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +24 -18
  541. package/dist/astro/routes/api/taxonomies/_name_/translations.mjs.map +1 -1
  542. package/dist/astro/routes/api/taxonomies/_name_.mjs +25 -19
  543. package/dist/astro/routes/api/taxonomies/_name_.mjs.map +1 -1
  544. package/dist/astro/routes/api/taxonomies/bulk-tag.d.mts +8 -0
  545. package/dist/astro/routes/api/taxonomies/bulk-tag.d.mts.map +1 -0
  546. package/dist/astro/routes/api/taxonomies/bulk-tag.mjs +295 -0
  547. package/dist/astro/routes/api/taxonomies/bulk-tag.mjs.map +1 -0
  548. package/dist/astro/routes/api/taxonomies/index.mjs +25 -19
  549. package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
  550. package/dist/astro/routes/api/themes/preview.mjs +7 -5
  551. package/dist/astro/routes/api/themes/preview.mjs.map +1 -1
  552. package/dist/astro/routes/api/typegen.d.mts.map +1 -1
  553. package/dist/astro/routes/api/typegen.mjs +12 -9
  554. package/dist/astro/routes/api/typegen.mjs.map +1 -1
  555. package/dist/astro/routes/api/visual-editing/action-token.mjs +7 -5
  556. package/dist/astro/routes/api/visual-editing/action-token.mjs.map +1 -1
  557. package/dist/astro/routes/api/visual-editing/content/_collection_/_id_/publish.mjs +22 -16
  558. package/dist/astro/routes/api/visual-editing/content/_collection_/_id_/publish.mjs.map +1 -1
  559. package/dist/astro/routes/api/visual-editing/toolbar-labels.mjs +3 -1
  560. package/dist/astro/routes/api/visual-editing/toolbar-labels.mjs.map +1 -1
  561. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  562. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  563. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  564. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +16 -10
  565. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
  566. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +20 -14
  567. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
  568. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +20 -14
  569. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
  570. package/dist/astro/routes/api/widget-areas/_name_.mjs +10 -8
  571. package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
  572. package/dist/astro/routes/api/widget-areas/index.mjs +20 -14
  573. package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
  574. package/dist/astro/routes/api/widget-components.mjs +4 -2
  575. package/dist/astro/routes/api/widget-components.mjs.map +1 -1
  576. package/dist/astro/routes/robots.txt.mjs +10 -10
  577. package/dist/astro/routes/sitemap-_collection_.xml.mjs +13 -13
  578. package/dist/astro/routes/sitemap.xml.mjs +11 -11
  579. package/dist/astro/types.d.mts +21 -17
  580. package/dist/astro/types.d.mts.map +1 -1
  581. package/dist/audit-RfCugiSB.mjs +127 -0
  582. package/dist/audit-RfCugiSB.mjs.map +1 -0
  583. package/dist/auth/providers/github.d.mts +1 -1
  584. package/dist/auth/providers/google.d.mts +1 -1
  585. package/dist/authoritative-records-CoJvBXAD.mjs +5172 -0
  586. package/dist/authoritative-records-CoJvBXAD.mjs.map +1 -0
  587. package/dist/authorize-seCifOMJ.mjs +49 -0
  588. package/dist/authorize-seCifOMJ.mjs.map +1 -0
  589. package/dist/backup-CBu0LrXf.mjs +246 -0
  590. package/dist/backup-CBu0LrXf.mjs.map +1 -0
  591. package/dist/block-type-registry-DUI_3KxT.mjs +6709 -0
  592. package/dist/block-type-registry-DUI_3KxT.mjs.map +1 -0
  593. package/dist/block-types-BmZmLmAD.mjs +132 -0
  594. package/dist/block-types-BmZmLmAD.mjs.map +1 -0
  595. package/dist/block-values-C_VIPr5t.mjs +175 -0
  596. package/dist/block-values-C_VIPr5t.mjs.map +1 -0
  597. package/dist/byline-DYmkXqvo.mjs +909 -0
  598. package/dist/byline-DYmkXqvo.mjs.map +1 -0
  599. package/dist/byline-fields-D28AT-Fh.mjs +123 -0
  600. package/dist/byline-fields-D28AT-Fh.mjs.map +1 -0
  601. package/dist/byline-registry-gnKscbQX.mjs +406 -0
  602. package/dist/byline-registry-gnKscbQX.mjs.map +1 -0
  603. package/dist/bylines-BPwJqoYH.mjs +232 -0
  604. package/dist/bylines-BPwJqoYH.mjs.map +1 -0
  605. package/dist/bylines-ukLaYpVZ.mjs +214 -0
  606. package/dist/bylines-ukLaYpVZ.mjs.map +1 -0
  607. package/dist/cache-m7UEkRFV.mjs +108 -0
  608. package/dist/cache-m7UEkRFV.mjs.map +1 -0
  609. package/dist/canonical-YhEQezjg.mjs +174 -0
  610. package/dist/canonical-YhEQezjg.mjs.map +1 -0
  611. package/dist/capture-triggers-kJyl_yyM.mjs +362 -0
  612. package/dist/capture-triggers-kJyl_yyM.mjs.map +1 -0
  613. package/dist/challenge-store-ppU6Uv13.mjs +51 -0
  614. package/dist/challenge-store-ppU6Uv13.mjs.map +1 -0
  615. package/dist/checksum-B2IINIlN.mjs +162 -0
  616. package/dist/checksum-B2IINIlN.mjs.map +1 -0
  617. package/dist/chrome-tags-DApCQSIk.mjs +24 -0
  618. package/dist/chrome-tags-DApCQSIk.mjs.map +1 -0
  619. package/dist/chunks-Dms7Uxbt.mjs +25 -0
  620. package/dist/chunks-Dms7Uxbt.mjs.map +1 -0
  621. package/dist/cli/index.mjs +1471 -56
  622. package/dist/cli/index.mjs.map +1 -1
  623. package/dist/client/cf-access.d.mts +1 -1
  624. package/dist/client/index.d.mts +1050 -4
  625. package/dist/client/index.d.mts.map +1 -1
  626. package/dist/client/index.mjs +193 -11
  627. package/dist/client/index.mjs.map +1 -1
  628. package/dist/collection-deletion-BIm28mo3.mjs +436 -0
  629. package/dist/collection-deletion-BIm28mo3.mjs.map +1 -0
  630. package/dist/collection-deletion-processor-Br6rxs3u.mjs +217 -0
  631. package/dist/collection-deletion-processor-Br6rxs3u.mjs.map +1 -0
  632. package/dist/collection-slugs-cache-CjGk__zp.mjs +85 -0
  633. package/dist/collection-slugs-cache-CjGk__zp.mjs.map +1 -0
  634. package/dist/comment-CrBl22OF.mjs +313 -0
  635. package/dist/comment-CrBl22OF.mjs.map +1 -0
  636. package/dist/comment-reaction-JEQ2Fpd8.mjs +85 -0
  637. package/dist/comment-reaction-JEQ2Fpd8.mjs.map +1 -0
  638. package/dist/comments-2ah9du9y.mjs +195 -0
  639. package/dist/comments-2ah9du9y.mjs.map +1 -0
  640. package/dist/comments-fHZ8ON9O.mjs +99 -0
  641. package/dist/comments-fHZ8ON9O.mjs.map +1 -0
  642. package/dist/components-CXbq7CSM.mjs +108 -0
  643. package/dist/components-CXbq7CSM.mjs.map +1 -0
  644. package/dist/config-5ii2haYu.d.mts +6528 -0
  645. package/dist/config-5ii2haYu.d.mts.map +1 -0
  646. package/dist/config-CvmjJcVw.mjs +73 -0
  647. package/dist/config-CvmjJcVw.mjs.map +1 -0
  648. package/dist/config-IhybvkIQ.mjs +242 -0
  649. package/dist/config-IhybvkIQ.mjs.map +1 -0
  650. package/dist/config-Py43JK9r.d.mts +39 -0
  651. package/dist/config-Py43JK9r.d.mts.map +1 -0
  652. package/dist/content-DFaIEDj9.mjs +2281 -0
  653. package/dist/content-DFaIEDj9.mjs.map +1 -0
  654. package/dist/content-list-query-Dra4Dalc.d.mts +26 -0
  655. package/dist/content-list-query-Dra4Dalc.d.mts.map +1 -0
  656. package/dist/content-policy-DFNw91-d.mjs +46 -0
  657. package/dist/content-policy-DFNw91-d.mjs.map +1 -0
  658. package/dist/content-repair-Q8IUrpLL.mjs +442 -0
  659. package/dist/content-repair-Q8IUrpLL.mjs.map +1 -0
  660. package/dist/context-BeGccdUX.mjs +1921 -0
  661. package/dist/context-BeGccdUX.mjs.map +1 -0
  662. package/dist/cron-7DCJwPXq.mjs +284 -0
  663. package/dist/cron-7DCJwPXq.mjs.map +1 -0
  664. package/dist/dashboard-DjrB-xxZ.mjs +151 -0
  665. package/dist/dashboard-DjrB-xxZ.mjs.map +1 -0
  666. package/dist/db/index.d.mts +3 -3
  667. package/dist/db/index.mjs +3 -3
  668. package/dist/db/libsql-migrations.d.mts +1 -1
  669. package/dist/db/libsql-migrations.mjs +6 -6
  670. package/dist/db/libsql.d.mts +1 -1
  671. package/dist/db/postgres-migrations.d.mts +1 -1
  672. package/dist/db/postgres-migrations.mjs +6 -6
  673. package/dist/db/postgres.d.mts +1 -1
  674. package/dist/db/sqlite-migrations.d.mts +1 -1
  675. package/dist/db/sqlite-migrations.mjs +6 -6
  676. package/dist/db/sqlite.d.mts +1 -1
  677. package/dist/db/sqlite.mjs +2 -2
  678. package/dist/db/sqlite.mjs.map +1 -1
  679. package/dist/default-DQ6vsviu.mjs +81 -0
  680. package/dist/default-DQ6vsviu.mjs.map +1 -0
  681. package/dist/deferred-tasks-BPxXUB4x.d.mts +9 -0
  682. package/dist/deferred-tasks-BPxXUB4x.d.mts.map +1 -0
  683. package/dist/device-flow-CRd6DXmW.mjs +466 -0
  684. package/dist/device-flow-CRd6DXmW.mjs.map +1 -0
  685. package/dist/dialect-helpers-7_IqPdh9.mjs +307 -0
  686. package/dist/dialect-helpers-7_IqPdh9.mjs.map +1 -0
  687. package/dist/digest-BarSh-pN.mjs +194 -0
  688. package/dist/digest-BarSh-pN.mjs.map +1 -0
  689. package/dist/direct-executor-BmuFJJZV.mjs +104 -0
  690. package/dist/direct-executor-BmuFJJZV.mjs.map +1 -0
  691. package/dist/dist-BNwANFJR.mjs +810 -0
  692. package/dist/dist-BNwANFJR.mjs.map +1 -0
  693. package/dist/email-console-bJn3ctqA.mjs +50 -0
  694. package/dist/email-console-bJn3ctqA.mjs.map +1 -0
  695. package/dist/email-locale-CN6WAMx0.mjs +25 -0
  696. package/dist/email-locale-CN6WAMx0.mjs.map +1 -0
  697. package/dist/emdash-runtime-DAjghyEG.mjs +4186 -0
  698. package/dist/emdash-runtime-DAjghyEG.mjs.map +1 -0
  699. package/dist/enrich-Dl3kZ-lP.mjs +194 -0
  700. package/dist/enrich-Dl3kZ-lP.mjs.map +1 -0
  701. package/dist/entry-lock-BJYdBfXb.mjs +1097 -0
  702. package/dist/entry-lock-BJYdBfXb.mjs.map +1 -0
  703. package/dist/entry-lock-DHQOxN7R.mjs +163 -0
  704. package/dist/entry-lock-DHQOxN7R.mjs.map +1 -0
  705. package/dist/entry-lock-DVhCt49A.d.mts +4368 -0
  706. package/dist/entry-lock-DVhCt49A.d.mts.map +1 -0
  707. package/dist/entry-locks-D0foLdjQ.mjs +167 -0
  708. package/dist/entry-locks-D0foLdjQ.mjs.map +1 -0
  709. package/dist/error-CfvOHP0M.mjs +90 -0
  710. package/dist/error-CfvOHP0M.mjs.map +1 -0
  711. package/dist/errors-BF0_LFhW.mjs +460 -0
  712. package/dist/errors-BF0_LFhW.mjs.map +1 -0
  713. package/dist/errors-DgE1zJGG.mjs +120 -0
  714. package/dist/errors-DgE1zJGG.mjs.map +1 -0
  715. package/dist/escape-CuaZhZvL.mjs +9 -0
  716. package/dist/escape-CuaZhZvL.mjs.map +1 -0
  717. package/dist/fence-CMDXJYc_.mjs +162 -0
  718. package/dist/fence-CMDXJYc_.mjs.map +1 -0
  719. package/dist/field-defs-cache-HIgBbqZZ.mjs +77 -0
  720. package/dist/field-defs-cache-HIgBbqZZ.mjs.map +1 -0
  721. package/dist/fts-manager-S-K0laoS.mjs +443 -0
  722. package/dist/fts-manager-S-K0laoS.mjs.map +1 -0
  723. package/dist/gc-DjKY0Ay1.mjs +75 -0
  724. package/dist/gc-DjKY0Ay1.mjs.map +1 -0
  725. package/dist/hash-SDAW6zC-.mjs +37 -0
  726. package/dist/hash-SDAW6zC-.mjs.map +1 -0
  727. package/dist/hook-result-B5bXDgS2.d.mts +31 -0
  728. package/dist/hook-result-B5bXDgS2.d.mts.map +1 -0
  729. package/dist/hreflang-D5uNnsGJ.d.mts +50 -0
  730. package/dist/hreflang-D5uNnsGJ.d.mts.map +1 -0
  731. package/dist/hreflang-DMFBL2eb.mjs +105 -0
  732. package/dist/hreflang-DMFBL2eb.mjs.map +1 -0
  733. package/dist/http-route-dispatch-BitqzwMy.mjs +339 -0
  734. package/dist/http-route-dispatch-BitqzwMy.mjs.map +1 -0
  735. package/dist/identity-IyNWwXr6.mjs +35 -0
  736. package/dist/identity-IyNWwXr6.mjs.map +1 -0
  737. package/dist/import-C3JZplWq.mjs +2020 -0
  738. package/dist/import-C3JZplWq.mjs.map +1 -0
  739. package/dist/index-D-hdtLe8.d.mts +623 -0
  740. package/dist/index-D-hdtLe8.d.mts.map +1 -0
  741. package/dist/index.d.mts +24 -24
  742. package/dist/index.mjs +105 -93
  743. package/dist/install-finalization-DfM1fj5x.mjs +58 -0
  744. package/dist/install-finalization-DfM1fj5x.mjs.map +1 -0
  745. package/dist/keys-yxLm5l35.mjs +61 -0
  746. package/dist/keys-yxLm5l35.mjs.map +1 -0
  747. package/dist/kinds-CRu_T5T5.mjs +961 -0
  748. package/dist/kinds-CRu_T5T5.mjs.map +1 -0
  749. package/dist/lifecycle-BzeGPeQ-.mjs +25 -0
  750. package/dist/lifecycle-BzeGPeQ-.mjs.map +1 -0
  751. package/dist/load-XgJiE6_3.mjs +28 -0
  752. package/dist/load-XgJiE6_3.mjs.map +1 -0
  753. package/dist/loader-BzqeR1ZN.mjs +1086 -0
  754. package/dist/loader-BzqeR1ZN.mjs.map +1 -0
  755. package/dist/loops-BQSzv5Zn.mjs +203 -0
  756. package/dist/loops-BQSzv5Zn.mjs.map +1 -0
  757. package/dist/manifest-builder-B8zR-73J.mjs +189 -0
  758. package/dist/manifest-builder-B8zR-73J.mjs.map +1 -0
  759. package/dist/manifest-schema-CY3zng_M.mjs +492 -0
  760. package/dist/manifest-schema-CY3zng_M.mjs.map +1 -0
  761. package/dist/media/image-endpoint.mjs +1 -1
  762. package/dist/media/index.d.mts +1 -1
  763. package/dist/media/index.mjs +3 -3
  764. package/dist/media/local-runtime.d.mts +14 -14
  765. package/dist/media/local-runtime.d.mts.map +1 -1
  766. package/dist/media/local-runtime.mjs +15 -16
  767. package/dist/media/local-runtime.mjs.map +1 -1
  768. package/dist/media-Pu1L6xir.mjs +408 -0
  769. package/dist/media-Pu1L6xir.mjs.map +1 -0
  770. package/dist/media-Qp5S3eH3.mjs +355 -0
  771. package/dist/media-Qp5S3eH3.mjs.map +1 -0
  772. package/dist/media-allowlist-BSoa9Vvm.mjs +44 -0
  773. package/dist/media-allowlist-BSoa9Vvm.mjs.map +1 -0
  774. package/dist/media-folders-BE0oo2eX.mjs +210 -0
  775. package/dist/media-folders-BE0oo2eX.mjs.map +1 -0
  776. package/dist/media-h7tOiDLj.mjs +221 -0
  777. package/dist/media-h7tOiDLj.mjs.map +1 -0
  778. package/dist/media-url-jArBpzIG.mjs +43 -0
  779. package/dist/media-url-jArBpzIG.mjs.map +1 -0
  780. package/dist/media-usage-My-Vi-ex.mjs +1629 -0
  781. package/dist/media-usage-My-Vi-ex.mjs.map +1 -0
  782. package/dist/media-usage-activation-HKp7zk7W.mjs +78 -0
  783. package/dist/media-usage-activation-HKp7zk7W.mjs.map +1 -0
  784. package/dist/media-usage-work-C2OZLCAd.mjs +586 -0
  785. package/dist/media-usage-work-C2OZLCAd.mjs.map +1 -0
  786. package/dist/media-usage-work-DwUNbgvS.mjs +152 -0
  787. package/dist/media-usage-work-DwUNbgvS.mjs.map +1 -0
  788. package/dist/menus-BM7BPLOb.mjs +139 -0
  789. package/dist/menus-BM7BPLOb.mjs.map +1 -0
  790. package/dist/menus-ITeAbcIF.mjs +753 -0
  791. package/dist/menus-ITeAbcIF.mjs.map +1 -0
  792. package/dist/menus-TsSGmSR4.mjs +3608 -0
  793. package/dist/menus-TsSGmSR4.mjs.map +1 -0
  794. package/dist/migrations/index.d.mts +1 -1
  795. package/dist/migrations/index.mjs +5 -5
  796. package/dist/mime-ChLqYwDk.mjs +36 -0
  797. package/dist/mime-ChLqYwDk.mjs.map +1 -0
  798. package/dist/mode-CCuKXUci.mjs +23 -0
  799. package/dist/mode-CCuKXUci.mjs.map +1 -0
  800. package/dist/node-sqlite-compat-CONMsl-x.mjs +46 -0
  801. package/dist/node-sqlite-compat-CONMsl-x.mjs.map +1 -0
  802. package/dist/normalize-CO2P8-iE.mjs +191 -0
  803. package/dist/normalize-CO2P8-iE.mjs.map +1 -0
  804. package/dist/oauth-authorization-DlMIL5_h.mjs +274 -0
  805. package/dist/oauth-authorization-DlMIL5_h.mjs.map +1 -0
  806. package/dist/oauth-clients-Djq9r_KP.mjs +266 -0
  807. package/dist/oauth-clients-Djq9r_KP.mjs.map +1 -0
  808. package/dist/oauth-state-store-BbjnZXNC.mjs +50 -0
  809. package/dist/oauth-state-store-BbjnZXNC.mjs.map +1 -0
  810. package/dist/oauth-user-lookup-vue9a64A.mjs +26 -0
  811. package/dist/oauth-user-lookup-vue9a64A.mjs.map +1 -0
  812. package/dist/object-cache/memory.d.mts +1 -1
  813. package/dist/object-cache-CqWNY5uP.mjs +582 -0
  814. package/dist/object-cache-CqWNY5uP.mjs.map +1 -0
  815. package/dist/options-DhrI7eMv.mjs +208 -0
  816. package/dist/options-DhrI7eMv.mjs.map +1 -0
  817. package/dist/options-SQCHQCPH.d.mts +81 -0
  818. package/dist/options-SQCHQCPH.d.mts.map +1 -0
  819. package/dist/page/index.d.mts +2 -2
  820. package/dist/page/index.mjs +9 -9
  821. package/dist/parse-CVgO-Qai.d.mts +168 -0
  822. package/dist/parse-CVgO-Qai.d.mts.map +1 -0
  823. package/dist/parse-DY6OD1j_.mjs +81 -0
  824. package/dist/parse-DY6OD1j_.mjs.map +1 -0
  825. package/dist/passkey-config-X_H8128a.mjs +46 -0
  826. package/dist/passkey-config-X_H8128a.mjs.map +1 -0
  827. package/dist/paths-JxokKIbr.mjs +77 -0
  828. package/dist/paths-JxokKIbr.mjs.map +1 -0
  829. package/dist/patterns-BhAvqO0S.mjs +145 -0
  830. package/dist/patterns-BhAvqO0S.mjs.map +1 -0
  831. package/dist/placeholder-MSEd8gty.d.mts +306 -0
  832. package/dist/placeholder-MSEd8gty.d.mts.map +1 -0
  833. package/dist/plan-xjcWrAah.mjs +135 -0
  834. package/dist/plan-xjcWrAah.mjs.map +1 -0
  835. package/dist/plugin-test-runtime.d.mts +38 -15
  836. package/dist/plugin-test-runtime.d.mts.map +1 -1
  837. package/dist/plugin-test-runtime.mjs +112 -99
  838. package/dist/plugin-types.d.mts +2 -2
  839. package/dist/plugin-utils.d.mts +12 -12
  840. package/dist/plugins/adapt-sandbox-entry.d.mts +12 -12
  841. package/dist/plugins/adapt-sandbox-entry.mjs +3 -3
  842. package/dist/plugins/host.d.mts +5 -5
  843. package/dist/plugins/host.mjs +45 -37
  844. package/dist/plugins-DUXUt8B9.mjs +277 -0
  845. package/dist/plugins-DUXUt8B9.mjs.map +1 -0
  846. package/dist/policy-CUvwAARc.mjs +45 -0
  847. package/dist/policy-CUvwAARc.mjs.map +1 -0
  848. package/dist/portable-text-5O_ZGUjB.mjs +307 -0
  849. package/dist/portable-text-5O_ZGUjB.mjs.map +1 -0
  850. package/dist/preview-CIUcPYr6.mjs +107 -0
  851. package/dist/preview-CIUcPYr6.mjs.map +1 -0
  852. package/dist/protocol-B97DZaFr.d.mts +61 -0
  853. package/dist/protocol-B97DZaFr.d.mts.map +1 -0
  854. package/dist/public-submission-BFBUrqst.mjs +250 -0
  855. package/dist/public-submission-BFBUrqst.mjs.map +1 -0
  856. package/dist/public-url-Bn7U0D7-.mjs +101 -0
  857. package/dist/public-url-Bn7U0D7-.mjs.map +1 -0
  858. package/dist/query-B48peoRT.mjs +890 -0
  859. package/dist/query-B48peoRT.mjs.map +1 -0
  860. package/dist/query-C4IH0uhR.mjs +41 -0
  861. package/dist/query-C4IH0uhR.mjs.map +1 -0
  862. package/dist/rate-limit-BZ6nybgc.mjs +120 -0
  863. package/dist/rate-limit-BZ6nybgc.mjs.map +1 -0
  864. package/dist/receipt-B4wJj5Jq.mjs +43 -0
  865. package/dist/receipt-B4wJj5Jq.mjs.map +1 -0
  866. package/dist/redirect-Cd1E7VUt.mjs +17 -0
  867. package/dist/redirect-Cd1E7VUt.mjs.map +1 -0
  868. package/dist/redirect-iJf5fltD.mjs +415 -0
  869. package/dist/redirect-iJf5fltD.mjs.map +1 -0
  870. package/dist/redirects-CYu1u1aK.mjs +446 -0
  871. package/dist/redirects-CYu1u1aK.mjs.map +1 -0
  872. package/dist/redirects-Cv9apQKj.mjs +148 -0
  873. package/dist/redirects-Cv9apQKj.mjs.map +1 -0
  874. package/dist/registry/testing.mjs +3 -3
  875. package/dist/relation-M4NUCb0b.mjs +334 -0
  876. package/dist/relation-M4NUCb0b.mjs.map +1 -0
  877. package/dist/relations-CCif47Oq.mjs +403 -0
  878. package/dist/relations-CCif47Oq.mjs.map +1 -0
  879. package/dist/relations-CK9CYUyr.mjs +310 -0
  880. package/dist/relations-CK9CYUyr.mjs.map +1 -0
  881. package/dist/request-cache-DzLYKm_x.mjs +104 -0
  882. package/dist/request-cache-DzLYKm_x.mjs.map +1 -0
  883. package/dist/request-context.d.mts +1 -1
  884. package/dist/request-meta-sZXt3sRo.mjs +141 -0
  885. package/dist/request-meta-sZXt3sRo.mjs.map +1 -0
  886. package/dist/resolve-2WbQsCvT.mjs +199 -0
  887. package/dist/resolve-2WbQsCvT.mjs.map +1 -0
  888. package/dist/route-wire--1hLuACz.mjs +197 -0
  889. package/dist/route-wire--1hLuACz.mjs.map +1 -0
  890. package/dist/routes-CUsM40Cc.mjs +272 -0
  891. package/dist/routes-CUsM40Cc.mjs.map +1 -0
  892. package/dist/runner-1y4J9fJB.mjs +5742 -0
  893. package/dist/runner-1y4J9fJB.mjs.map +1 -0
  894. package/dist/runner-D5_LzKXI.d.mts +68 -0
  895. package/dist/runner-D5_LzKXI.d.mts.map +1 -0
  896. package/dist/runtime.d.mts +13 -13
  897. package/dist/runtime.mjs +4 -4
  898. package/dist/schema-BABiRdsH.mjs +13 -0
  899. package/dist/schema-BABiRdsH.mjs.map +1 -0
  900. package/dist/schema-Du3MzCG0.mjs +446 -0
  901. package/dist/schema-Du3MzCG0.mjs.map +1 -0
  902. package/dist/scopes-Dbk-ElhT.mjs +34 -0
  903. package/dist/scopes-Dbk-ElhT.mjs.map +1 -0
  904. package/dist/search-Bt1PL1jf.mjs +448 -0
  905. package/dist/search-Bt1PL1jf.mjs.map +1 -0
  906. package/dist/secrets-CIi0nFiR.mjs +341 -0
  907. package/dist/secrets-CIi0nFiR.mjs.map +1 -0
  908. package/dist/sections-Ddptx_o9.mjs +346 -0
  909. package/dist/sections-Ddptx_o9.mjs.map +1 -0
  910. package/dist/seed/index.d.mts +2 -2
  911. package/dist/seed/index.mjs +35 -30
  912. package/dist/seo/index.d.mts +3 -3
  913. package/dist/seo/index.mjs +25 -21
  914. package/dist/seo/index.mjs.map +1 -1
  915. package/dist/seo-CZT2oLtT.mjs +114 -0
  916. package/dist/seo-CZT2oLtT.mjs.map +1 -0
  917. package/dist/seo-D5w3-Azy.mjs +133 -0
  918. package/dist/seo-D5w3-Azy.mjs.map +1 -0
  919. package/dist/service-DuykXPyW.mjs +150 -0
  920. package/dist/service-DuykXPyW.mjs.map +1 -0
  921. package/dist/session-user-BhaVp8w0.mjs +51 -0
  922. package/dist/session-user-BhaVp8w0.mjs.map +1 -0
  923. package/dist/settings-2xNYr90o.mjs +51 -0
  924. package/dist/settings-2xNYr90o.mjs.map +1 -0
  925. package/dist/settings-CgjRWGW9.mjs +439 -0
  926. package/dist/settings-CgjRWGW9.mjs.map +1 -0
  927. package/dist/settings-DYidIB26.mjs +61 -0
  928. package/dist/settings-DYidIB26.mjs.map +1 -0
  929. package/dist/setup-complete-bOogcHWG.mjs +26 -0
  930. package/dist/setup-complete-bOogcHWG.mjs.map +1 -0
  931. package/dist/setup-nonce-0EGuKM4s.mjs +25 -0
  932. package/dist/setup-nonce-0EGuKM4s.mjs.map +1 -0
  933. package/dist/site-settings-DOgRUoFX.mjs +50 -0
  934. package/dist/site-settings-DOgRUoFX.mjs.map +1 -0
  935. package/dist/site-url-BU_bbxwj.mjs +16 -0
  936. package/dist/site-url-BU_bbxwj.mjs.map +1 -0
  937. package/dist/snapshot-CugwUqKQ.mjs +237 -0
  938. package/dist/snapshot-CugwUqKQ.mjs.map +1 -0
  939. package/dist/ssrf-CJUnJymJ.mjs +406 -0
  940. package/dist/ssrf-CJUnJymJ.mjs.map +1 -0
  941. package/dist/stage-DgrBz_du.mjs +662 -0
  942. package/dist/stage-DgrBz_du.mjs.map +1 -0
  943. package/dist/state-DDiPySSG.mjs +153 -0
  944. package/dist/state-DDiPySSG.mjs.map +1 -0
  945. package/dist/status-DZKIG4a9.mjs +30 -0
  946. package/dist/status-DZKIG4a9.mjs.map +1 -0
  947. package/dist/storage/local.d.mts +1 -1
  948. package/dist/storage/local.mjs +1 -1
  949. package/dist/storage/s3.d.mts +1 -1
  950. package/dist/storage/s3.mjs +1 -1
  951. package/dist/storage-query-Bdv7A62k.d.mts +1576 -0
  952. package/dist/storage-query-Bdv7A62k.d.mts.map +1 -0
  953. package/dist/tar-_1AgTYFu.mjs +449 -0
  954. package/dist/tar-_1AgTYFu.mjs.map +1 -0
  955. package/dist/target-_7pn2Gbg.mjs +27 -0
  956. package/dist/target-_7pn2Gbg.mjs.map +1 -0
  957. package/dist/taxonomies-C1IwNe8Q.mjs +697 -0
  958. package/dist/taxonomies-C1IwNe8Q.mjs.map +1 -0
  959. package/dist/taxonomies-C_XMoRcX.mjs +939 -0
  960. package/dist/taxonomies-C_XMoRcX.mjs.map +1 -0
  961. package/dist/taxonomy-C3Fu1YeH.mjs +525 -0
  962. package/dist/taxonomy-C3Fu1YeH.mjs.map +1 -0
  963. package/dist/tokens-u78jETkX.mjs +171 -0
  964. package/dist/tokens-u78jETkX.mjs.map +1 -0
  965. package/dist/transfer-BhUCrgDc.mjs +8675 -0
  966. package/dist/transfer-BhUCrgDc.mjs.map +1 -0
  967. package/dist/transfer-DIGvR9Af.mjs +166 -0
  968. package/dist/transfer-DIGvR9Af.mjs.map +1 -0
  969. package/dist/transfer-download-Pml7RMio.mjs +33 -0
  970. package/dist/transfer-download-Pml7RMio.mjs.map +1 -0
  971. package/dist/transformations-9gfpwY65.mjs +196 -0
  972. package/dist/transformations-9gfpwY65.mjs.map +1 -0
  973. package/dist/transport-BIfH7_hx.d.mts +42 -0
  974. package/dist/transport-BIfH7_hx.d.mts.map +1 -0
  975. package/dist/transport-DKWIq53q.mjs +136 -0
  976. package/dist/transport-DKWIq53q.mjs.map +1 -0
  977. package/dist/trusted-proxy-tKiVtyTD.mjs +51 -0
  978. package/dist/trusted-proxy-tKiVtyTD.mjs.map +1 -0
  979. package/dist/turnstile-DpsoyOfg.mjs +53 -0
  980. package/dist/turnstile-DpsoyOfg.mjs.map +1 -0
  981. package/dist/types-B4sUC2p1.d.mts +198 -0
  982. package/dist/types-B4sUC2p1.d.mts.map +1 -0
  983. package/dist/types-B7Lmsh1E.d.mts +887 -0
  984. package/dist/types-B7Lmsh1E.d.mts.map +1 -0
  985. package/dist/types-BZakh7Ym.mjs +22 -0
  986. package/dist/types-BZakh7Ym.mjs.map +1 -0
  987. package/dist/types-B_CppEmv.d.mts +264 -0
  988. package/dist/types-B_CppEmv.d.mts.map +1 -0
  989. package/dist/types-BcrBrE6-.mjs +159 -0
  990. package/dist/types-BcrBrE6-.mjs.map +1 -0
  991. package/dist/types-CAZgq9fO.d.mts +124 -0
  992. package/dist/types-CAZgq9fO.d.mts.map +1 -0
  993. package/dist/types-Cd-O03VG.d.mts +363 -0
  994. package/dist/types-Cd-O03VG.d.mts.map +1 -0
  995. package/dist/types-Cr-Kg-3D.d.mts +1641 -0
  996. package/dist/types-Cr-Kg-3D.d.mts.map +1 -0
  997. package/dist/types-D2fvWd1i.d.mts +359 -0
  998. package/dist/types-D2fvWd1i.d.mts.map +1 -0
  999. package/dist/types-D7HUsguf.mjs +77 -0
  1000. package/dist/types-D7HUsguf.mjs.map +1 -0
  1001. package/dist/types-DA1Te3Oy.d.mts +198 -0
  1002. package/dist/types-DA1Te3Oy.d.mts.map +1 -0
  1003. package/dist/types-DhV6Q5bP.mjs +18 -0
  1004. package/dist/types-DhV6Q5bP.mjs.map +1 -0
  1005. package/dist/types-DnnVSl6Y.d.mts +85 -0
  1006. package/dist/types-DnnVSl6Y.d.mts.map +1 -0
  1007. package/dist/types-VT4-BugW.mjs +16 -0
  1008. package/dist/types-VT4-BugW.mjs.map +1 -0
  1009. package/dist/upload-attempts-B68ZGNu9.mjs +32 -0
  1010. package/dist/upload-attempts-B68ZGNu9.mjs.map +1 -0
  1011. package/dist/url-pattern-CCeZMGOh.mjs +33 -0
  1012. package/dist/url-pattern-CCeZMGOh.mjs.map +1 -0
  1013. package/dist/user-DDSL-sGw.mjs +171 -0
  1014. package/dist/user-DDSL-sGw.mjs.map +1 -0
  1015. package/dist/utils-DdhmO9w9.mjs +439 -0
  1016. package/dist/utils-DdhmO9w9.mjs.map +1 -0
  1017. package/dist/validate-CHosPEhs.mjs +453 -0
  1018. package/dist/validate-CHosPEhs.mjs.map +1 -0
  1019. package/dist/validate-CM79QXYE.d.mts +432 -0
  1020. package/dist/validate-CM79QXYE.d.mts.map +1 -0
  1021. package/dist/validation-BFecQczG.mjs +218 -0
  1022. package/dist/validation-BFecQczG.mjs.map +1 -0
  1023. package/dist/version-BRZXVifl.mjs +9 -0
  1024. package/dist/version-BRZXVifl.mjs.map +1 -0
  1025. package/dist/version-DUQDHgrf.mjs +7 -0
  1026. package/dist/version-DUQDHgrf.mjs.map +1 -0
  1027. package/dist/widgets-WdV72brA.mjs +126 -0
  1028. package/dist/widgets-WdV72brA.mjs.map +1 -0
  1029. package/dist/wxr-taxonomies-BHTdqkmZ.d.mts +34 -0
  1030. package/dist/wxr-taxonomies-BHTdqkmZ.d.mts.map +1 -0
  1031. package/dist/wxr-taxonomies-DP6ESgMN.mjs +372 -0
  1032. package/dist/wxr-taxonomies-DP6ESgMN.mjs.map +1 -0
  1033. package/dist/zod-generator-D6uE4WiS.mjs +389 -0
  1034. package/dist/zod-generator-D6uE4WiS.mjs.map +1 -0
  1035. package/package.json +6 -5
  1036. package/src/api/authorize.ts +17 -0
  1037. package/src/api/email-locale.ts +24 -0
  1038. package/src/api/errors.ts +16 -0
  1039. package/src/api/handlers/api-tokens.ts +18 -3
  1040. package/src/api/handlers/block-types.ts +129 -0
  1041. package/src/api/handlers/bulk-tag.ts +309 -0
  1042. package/src/api/handlers/comments.ts +0 -28
  1043. package/src/api/handlers/index.ts +10 -0
  1044. package/src/api/handlers/manifest.ts +8 -1
  1045. package/src/api/handlers/marketplace.ts +22 -16
  1046. package/src/api/handlers/media-usage.ts +31 -12
  1047. package/src/api/handlers/media.ts +19 -14
  1048. package/src/api/handlers/schema.ts +33 -8
  1049. package/src/api/handlers/settings.ts +2 -2
  1050. package/src/api/handlers/snapshot.ts +4 -0
  1051. package/src/api/handlers/taxonomies.ts +132 -83
  1052. package/src/api/handlers/transfer.ts +1347 -0
  1053. package/src/api/handlers/validate-media-fields.ts +124 -12
  1054. package/src/api/media-usage-write-fence.ts +5 -26
  1055. package/src/api/openapi/document.ts +614 -5
  1056. package/src/api/schemas/auth.ts +7 -1
  1057. package/src/api/schemas/common.ts +5 -0
  1058. package/src/api/schemas/content.ts +20 -1
  1059. package/src/api/schemas/media-usage.ts +11 -0
  1060. package/src/api/schemas/menus.ts +1 -1
  1061. package/src/api/schemas/schema.ts +117 -0
  1062. package/src/api/schemas/settings.ts +3 -3
  1063. package/src/api/schemas/taxonomies.ts +18 -0
  1064. package/src/api/schemas/transfer.ts +249 -0
  1065. package/src/api/transfer-download.ts +36 -0
  1066. package/src/api/types.ts +3 -0
  1067. package/src/astro/integration/routes.ts +31 -0
  1068. package/src/astro/integration/vite-config.ts +26 -0
  1069. package/src/astro/middleware/auth.ts +30 -7
  1070. package/src/astro/middleware/media-usage-write-fence.ts +106 -11
  1071. package/src/astro/middleware/request-context.ts +16 -2
  1072. package/src/astro/middleware.ts +38 -18
  1073. package/src/astro/routes/admin.astro +3 -1
  1074. package/src/astro/routes/api/admin/hooks/exclusive/index.ts +1 -5
  1075. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +4 -3
  1076. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +4 -3
  1077. package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +4 -3
  1078. package/src/astro/routes/api/admin/plugins/[id]/update.ts +4 -3
  1079. package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +4 -3
  1080. package/src/astro/routes/api/admin/plugins/registry/[id]/uninstall.ts +4 -3
  1081. package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +3 -3
  1082. package/src/astro/routes/api/admin/plugins/registry/install.ts +3 -3
  1083. package/src/astro/routes/api/admin/plugins/registry/publisher-handle.ts +59 -0
  1084. package/src/astro/routes/api/admin/transfer/[...path].ts +143 -0
  1085. package/src/astro/routes/api/admin/transfer/approvals/[id]/approve.ts +35 -0
  1086. package/src/astro/routes/api/admin/transfer/approvals/[id]/deny.ts +35 -0
  1087. package/src/astro/routes/api/admin/transfer/approvals/index.ts +33 -0
  1088. package/src/astro/routes/api/admin/transfer/capabilities.ts +36 -0
  1089. package/src/astro/routes/api/admin/transfer/exports/[id]/advance.ts +34 -0
  1090. package/src/astro/routes/api/admin/transfer/exports/[id]/archive.ts +35 -0
  1091. package/src/astro/routes/api/admin/transfer/exports/[id]/files/[...path].ts +40 -0
  1092. package/src/astro/routes/api/admin/transfer/exports/[id]/index.ts +25 -0
  1093. package/src/astro/routes/api/admin/transfer/exports/[id]/manifest.ts +38 -0
  1094. package/src/astro/routes/api/admin/transfer/exports/index.ts +64 -0
  1095. package/src/astro/routes/api/admin/transfer/imports/[id]/abandon.ts +28 -0
  1096. package/src/astro/routes/api/admin/transfer/imports/[id]/advance.ts +37 -0
  1097. package/src/astro/routes/api/admin/transfer/imports/[id]/analyze.ts +51 -0
  1098. package/src/astro/routes/api/admin/transfer/imports/[id]/cancel.ts +27 -0
  1099. package/src/astro/routes/api/admin/transfer/imports/[id]/execute.ts +48 -0
  1100. package/src/astro/routes/api/admin/transfer/imports/[id]/files/[...path].ts +54 -0
  1101. package/src/astro/routes/api/admin/transfer/imports/[id]/index.ts +25 -0
  1102. package/src/astro/routes/api/admin/transfer/imports/[id]/missing.ts +34 -0
  1103. package/src/astro/routes/api/admin/transfer/imports/[id]/plan.ts +29 -0
  1104. package/src/astro/routes/api/admin/transfer/imports/[id]/receipt.ts +25 -0
  1105. package/src/astro/routes/api/admin/transfer/imports/index.ts +85 -0
  1106. package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +9 -1
  1107. package/src/astro/routes/api/auth/invite/index.ts +15 -1
  1108. package/src/astro/routes/api/auth/magic-link/send.ts +9 -1
  1109. package/src/astro/routes/api/auth/magic-link/verify.ts +37 -39
  1110. package/src/astro/routes/api/auth/passkey/options.ts +2 -21
  1111. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +64 -13
  1112. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +16 -1
  1113. package/src/astro/routes/api/import/wordpress/rewrite-url-helpers.ts +11 -4
  1114. package/src/astro/routes/api/mcp.ts +1 -0
  1115. package/src/astro/routes/api/media/[id].ts +5 -15
  1116. package/src/astro/routes/api/media/file/[...key].ts +30 -5
  1117. package/src/astro/routes/api/oauth/authorize.ts +4 -0
  1118. package/src/astro/routes/api/schema/block-types/[slug]/index.ts +29 -0
  1119. package/src/astro/routes/api/schema/block-types/[slug]/versions/[version]/activate.ts +31 -0
  1120. package/src/astro/routes/api/schema/block-types/index.ts +29 -0
  1121. package/src/astro/routes/api/taxonomies/bulk-tag.ts +29 -0
  1122. package/src/astro/routes/api/typegen.ts +8 -4
  1123. package/src/astro/types.ts +4 -0
  1124. package/src/auth/api-tokens.ts +1 -0
  1125. package/src/auth/rate-limit.ts +2 -1
  1126. package/src/auth/scopes.ts +11 -0
  1127. package/src/cleanup.ts +14 -0
  1128. package/src/cli/client-factory.ts +6 -1
  1129. package/src/cli/commands/export-seed.ts +41 -15
  1130. package/src/cli/commands/seed.ts +6 -2
  1131. package/src/cli/commands/site.ts +556 -0
  1132. package/src/cli/index.ts +2 -0
  1133. package/src/cli/site/archive.ts +273 -0
  1134. package/src/cli/site/export.ts +311 -0
  1135. package/src/cli/site/import.ts +815 -0
  1136. package/src/cli/site/shared.ts +241 -0
  1137. package/src/client/index.ts +414 -11
  1138. package/src/client/portable-text.ts +41 -0
  1139. package/src/comments/public-submission.ts +19 -11
  1140. package/src/components/Blocks.astro +46 -0
  1141. package/src/components/Gallery.astro +2 -1
  1142. package/src/components/Image.astro +51 -6
  1143. package/src/components/InlinePortableTextEditor.tsx +28 -18
  1144. package/src/components/blocks.ts +44 -0
  1145. package/src/components/index.ts +14 -4
  1146. package/src/content/converters/gallery.ts +54 -16
  1147. package/src/content/converters/image-link.ts +27 -0
  1148. package/src/content/converters/index.ts +1 -0
  1149. package/src/content/converters/portable-text-to-prosemirror.ts +10 -7
  1150. package/src/content/converters/prosemirror-to-portable-text.ts +14 -0
  1151. package/src/content/converters/types.ts +17 -0
  1152. package/src/database/content-datetime.ts +19 -2
  1153. package/src/database/migrations/035_bounded_404_log.ts +28 -39
  1154. package/src/database/migrations/081_redirect_write_guards.ts +7 -4
  1155. package/src/database/migrations/083_block_types.ts +58 -0
  1156. package/src/database/migrations/084_site_transfer.ts +264 -0
  1157. package/src/database/migrations/085_taxonomy_def_groups.ts +155 -0
  1158. package/src/database/migrations/runner.ts +6 -0
  1159. package/src/database/repositories/audit.ts +10 -1
  1160. package/src/database/repositories/byline.ts +4 -1
  1161. package/src/database/repositories/content.ts +83 -11
  1162. package/src/database/repositories/media-usage.ts +43 -13
  1163. package/src/database/repositories/media.ts +32 -1
  1164. package/src/database/repositories/options.ts +12 -0
  1165. package/src/database/repositories/revision.ts +6 -3
  1166. package/src/database/repositories/taxonomy-def.ts +126 -0
  1167. package/src/database/repositories/taxonomy.ts +65 -26
  1168. package/src/database/types.ts +133 -1
  1169. package/src/db/sqlite.ts +1 -1
  1170. package/src/emdash-runtime.ts +287 -51
  1171. package/src/import/utils.ts +8 -1
  1172. package/src/import/wxr-taxonomies.ts +11 -67
  1173. package/src/index.ts +26 -1
  1174. package/src/loader.ts +9 -5
  1175. package/src/mcp/server.ts +521 -29
  1176. package/src/mcp/transfer.ts +569 -0
  1177. package/src/media/local-runtime.ts +5 -18
  1178. package/src/media/upload-attempts.ts +5 -0
  1179. package/src/media/url.ts +15 -0
  1180. package/src/media/usage/content-fields.ts +69 -5
  1181. package/src/media/usage/content-refresh.ts +13 -66
  1182. package/src/media/usage/content-repair.ts +10 -2
  1183. package/src/media/usage/extractor.ts +74 -3
  1184. package/src/media/usage/projection-fingerprint.ts +14 -0
  1185. package/src/media/usage/schema-invalidation.ts +72 -0
  1186. package/src/media/usage/site-settings.ts +52 -0
  1187. package/src/media/usage/types.ts +2 -0
  1188. package/src/object-cache/index.ts +69 -4
  1189. package/src/plugin-test-runtime.ts +1 -0
  1190. package/src/plugins/context.ts +104 -56
  1191. package/src/plugins/cron.ts +6 -4
  1192. package/src/plugins/define-plugin.ts +11 -1
  1193. package/src/plugins/email-console.ts +2 -0
  1194. package/src/plugins/email.ts +16 -0
  1195. package/src/plugins/hooks.ts +33 -9
  1196. package/src/plugins/manifest-schema.ts +1 -0
  1197. package/src/plugins/route-error.ts +57 -0
  1198. package/src/plugins/routes.ts +5 -60
  1199. package/src/plugins/sandbox/types.ts +22 -2
  1200. package/src/plugins/types.ts +4 -0
  1201. package/src/registry/artifact-fetch.ts +17 -4
  1202. package/src/registry/publisher-handle.ts +273 -0
  1203. package/src/schema/block-type-contract.ts +478 -0
  1204. package/src/schema/block-type-registry.ts +689 -0
  1205. package/src/schema/block-types.ts +110 -0
  1206. package/src/schema/block-values.ts +293 -0
  1207. package/src/schema/byline-registry.ts +1 -1
  1208. package/src/schema/index.ts +28 -0
  1209. package/src/schema/registry.ts +462 -12
  1210. package/src/schema/types.ts +12 -1
  1211. package/src/schema/zod-generator.ts +92 -9
  1212. package/src/search/fts-manager.ts +40 -0
  1213. package/src/seed/apply.ts +173 -49
  1214. package/src/seed/types.ts +23 -3
  1215. package/src/seed/validate.ts +131 -7
  1216. package/src/settings/index.ts +32 -9
  1217. package/src/settings/types.ts +15 -0
  1218. package/src/taxonomies/index.ts +72 -83
  1219. package/src/transfer/analyze/index.ts +45 -0
  1220. package/src/transfer/analyze/issues.ts +55 -0
  1221. package/src/transfer/analyze/plan.ts +327 -0
  1222. package/src/transfer/analyze/read.ts +134 -0
  1223. package/src/transfer/analyze/step.ts +551 -0
  1224. package/src/transfer/analyze/target.ts +269 -0
  1225. package/src/transfer/analyze/unique-keys.ts +228 -0
  1226. package/src/transfer/analyze/validate.ts +1412 -0
  1227. package/src/transfer/analyze/values.ts +227 -0
  1228. package/src/transfer/analyze/write-rules.ts +196 -0
  1229. package/src/transfer/audit.ts +38 -0
  1230. package/src/transfer/auth.ts +25 -0
  1231. package/src/transfer/container/tar.ts +242 -0
  1232. package/src/transfer/domain.ts +248 -0
  1233. package/src/transfer/errors.ts +134 -0
  1234. package/src/transfer/export/exporter.ts +1067 -0
  1235. package/src/transfer/export/fence.ts +84 -0
  1236. package/src/transfer/export/media.ts +165 -0
  1237. package/src/transfer/export/readers.ts +1164 -0
  1238. package/src/transfer/export/verify.ts +677 -0
  1239. package/src/transfer/fence.ts +193 -0
  1240. package/src/transfer/format/canonical.ts +151 -0
  1241. package/src/transfer/format/column-codec.ts +275 -0
  1242. package/src/transfer/format/columns.ts +707 -0
  1243. package/src/transfer/format/digest.ts +287 -0
  1244. package/src/transfer/format/features.ts +91 -0
  1245. package/src/transfer/format/issues.ts +39 -0
  1246. package/src/transfer/format/kinds.ts +936 -0
  1247. package/src/transfer/format/limits.ts +87 -0
  1248. package/src/transfer/format/manifest.ts +297 -0
  1249. package/src/transfer/format/media-refs.ts +716 -0
  1250. package/src/transfer/format/paths.ts +73 -0
  1251. package/src/transfer/format/plan.ts +162 -0
  1252. package/src/transfer/format/receipt.ts +43 -0
  1253. package/src/transfer/format/records.ts +127 -0
  1254. package/src/transfer/format/settings.ts +94 -0
  1255. package/src/transfer/format/transformations.ts +260 -0
  1256. package/src/transfer/format/version.ts +9 -0
  1257. package/src/transfer/gc.ts +103 -0
  1258. package/src/transfer/import/advance.ts +522 -0
  1259. package/src/transfer/import/context.ts +281 -0
  1260. package/src/transfer/import/entries.ts +186 -0
  1261. package/src/transfer/import/ids.ts +40 -0
  1262. package/src/transfer/import/index.ts +15 -0
  1263. package/src/transfer/import/media.ts +141 -0
  1264. package/src/transfer/import/rebuild.ts +300 -0
  1265. package/src/transfer/import/rows.ts +114 -0
  1266. package/src/transfer/import/scaffold.ts +221 -0
  1267. package/src/transfer/import/sql.ts +134 -0
  1268. package/src/transfer/import/stages.ts +329 -0
  1269. package/src/transfer/import/writers.ts +370 -0
  1270. package/src/transfer/index.ts +472 -0
  1271. package/src/transfer/ops/approvals.ts +359 -0
  1272. package/src/transfer/ops/budget.ts +82 -0
  1273. package/src/transfer/ops/collation.ts +21 -0
  1274. package/src/transfer/ops/identity-map.ts +142 -0
  1275. package/src/transfer/ops/media-blobs.ts +135 -0
  1276. package/src/transfer/ops/operations.ts +842 -0
  1277. package/src/transfer/ops/package-index.ts +159 -0
  1278. package/src/transfer/ops/staged-files.ts +231 -0
  1279. package/src/transfer/ops/states.ts +265 -0
  1280. package/src/transfer/ops/time.ts +54 -0
  1281. package/src/transfer/schema-importer.ts +34 -0
  1282. package/src/transfer/site-id.ts +30 -0
  1283. package/src/transfer/staging/assembler.ts +217 -0
  1284. package/src/transfer/staging/keys.ts +94 -0
  1285. package/src/transfer/staging/package.ts +229 -0
  1286. package/src/transfer/staging/stage.ts +231 -0
  1287. package/src/ui.ts +19 -0
  1288. package/src/visual-editing/toolbar.ts +125 -37
  1289. package/dist/action-token-On1sot_c.mjs +0 -26
  1290. package/dist/action-token-On1sot_c.mjs.map +0 -1
  1291. package/dist/activation-CaMfdBt1.mjs +0 -382
  1292. package/dist/activation-CaMfdBt1.mjs.map +0 -1
  1293. package/dist/adapters-Cq10K4_c.d.mts +0 -171
  1294. package/dist/adapters-Cq10K4_c.d.mts.map +0 -1
  1295. package/dist/allowed-origins-1Sa-2wZK.mjs +0 -116
  1296. package/dist/allowed-origins-1Sa-2wZK.mjs.map +0 -1
  1297. package/dist/api-DmjKxPCY.mjs +0 -4831
  1298. package/dist/api-DmjKxPCY.mjs.map +0 -1
  1299. package/dist/api-tokens-BIXItFKV.mjs +0 -163
  1300. package/dist/api-tokens-BIXItFKV.mjs.map +0 -1
  1301. package/dist/api-tokens-DS1VzN7_.mjs +0 -3
  1302. package/dist/apply-drpfmqrR.mjs +0 -1005
  1303. package/dist/apply-drpfmqrR.mjs.map +0 -1
  1304. package/dist/artifact-fetch-COiMhVgk.mjs +0 -813
  1305. package/dist/artifact-fetch-COiMhVgk.mjs.map +0 -1
  1306. package/dist/authoritative-records-DbqshfKD.mjs +0 -5172
  1307. package/dist/authoritative-records-DbqshfKD.mjs.map +0 -1
  1308. package/dist/authorize-BXpfMpp0.mjs +0 -40
  1309. package/dist/authorize-BXpfMpp0.mjs.map +0 -1
  1310. package/dist/backup-BsPQdwnS.mjs +0 -246
  1311. package/dist/backup-BsPQdwnS.mjs.map +0 -1
  1312. package/dist/byline-Doh-wt99.mjs +0 -906
  1313. package/dist/byline-Doh-wt99.mjs.map +0 -1
  1314. package/dist/byline-fields-CsrdvaLV.mjs +0 -123
  1315. package/dist/byline-fields-CsrdvaLV.mjs.map +0 -1
  1316. package/dist/byline-registry-D6lWzXk6.mjs +0 -406
  1317. package/dist/byline-registry-D6lWzXk6.mjs.map +0 -1
  1318. package/dist/bylines-Cop0hkoy.mjs +0 -214
  1319. package/dist/bylines-Cop0hkoy.mjs.map +0 -1
  1320. package/dist/bylines-kpBzHPr8.mjs +0 -232
  1321. package/dist/bylines-kpBzHPr8.mjs.map +0 -1
  1322. package/dist/cache-B0pOlXdM.mjs +0 -108
  1323. package/dist/cache-B0pOlXdM.mjs.map +0 -1
  1324. package/dist/capture-triggers-gxQxnxrJ.mjs +0 -362
  1325. package/dist/capture-triggers-gxQxnxrJ.mjs.map +0 -1
  1326. package/dist/challenge-store-CkRdSdd8.mjs +0 -51
  1327. package/dist/challenge-store-CkRdSdd8.mjs.map +0 -1
  1328. package/dist/checksum-Bsi2D9M1.mjs +0 -162
  1329. package/dist/checksum-Bsi2D9M1.mjs.map +0 -1
  1330. package/dist/chrome-tags-D7LGB91U.mjs +0 -24
  1331. package/dist/chrome-tags-D7LGB91U.mjs.map +0 -1
  1332. package/dist/chunks-BmWyou79.mjs +0 -25
  1333. package/dist/chunks-BmWyou79.mjs.map +0 -1
  1334. package/dist/collection-deletion-C3Xixmkx.mjs +0 -436
  1335. package/dist/collection-deletion-C3Xixmkx.mjs.map +0 -1
  1336. package/dist/collection-slugs-cache-D9auiKmI.mjs +0 -85
  1337. package/dist/collection-slugs-cache-D9auiKmI.mjs.map +0 -1
  1338. package/dist/comment-DcT8qdPK.mjs +0 -313
  1339. package/dist/comment-DcT8qdPK.mjs.map +0 -1
  1340. package/dist/comment-reaction-DY5-y8Y5.mjs +0 -85
  1341. package/dist/comment-reaction-DY5-y8Y5.mjs.map +0 -1
  1342. package/dist/comments-B67VRNGw.mjs +0 -204
  1343. package/dist/comments-B67VRNGw.mjs.map +0 -1
  1344. package/dist/comments-BVzzt6x5.mjs +0 -99
  1345. package/dist/comments-BVzzt6x5.mjs.map +0 -1
  1346. package/dist/components-DIl9Qzh6.mjs +0 -108
  1347. package/dist/components-DIl9Qzh6.mjs.map +0 -1
  1348. package/dist/config-B2acLeC5.mjs +0 -242
  1349. package/dist/config-B2acLeC5.mjs.map +0 -1
  1350. package/dist/config-CGVeT8Hc.mjs +0 -73
  1351. package/dist/config-CGVeT8Hc.mjs.map +0 -1
  1352. package/dist/config-D3HhLON3.d.mts +0 -39
  1353. package/dist/config-D3HhLON3.d.mts.map +0 -1
  1354. package/dist/config-DS7e4EZ9.d.mts +0 -6387
  1355. package/dist/config-DS7e4EZ9.d.mts.map +0 -1
  1356. package/dist/content-DZ3B5qtA.mjs +0 -2216
  1357. package/dist/content-DZ3B5qtA.mjs.map +0 -1
  1358. package/dist/content-list-query-CbPdzsna.d.mts +0 -26
  1359. package/dist/content-list-query-CbPdzsna.d.mts.map +0 -1
  1360. package/dist/content-policy-CM4skrNi.mjs +0 -46
  1361. package/dist/content-policy-CM4skrNi.mjs.map +0 -1
  1362. package/dist/content-refresh-BHLHJgyt.mjs +0 -3599
  1363. package/dist/content-refresh-BHLHJgyt.mjs.map +0 -1
  1364. package/dist/content-repair-BVll5WNl.mjs +0 -440
  1365. package/dist/content-repair-BVll5WNl.mjs.map +0 -1
  1366. package/dist/context-B195kjqZ.mjs +0 -1835
  1367. package/dist/context-B195kjqZ.mjs.map +0 -1
  1368. package/dist/cron-BcR3RN4K.mjs +0 -282
  1369. package/dist/cron-BcR3RN4K.mjs.map +0 -1
  1370. package/dist/dashboard-CZD4G7k_.mjs +0 -151
  1371. package/dist/dashboard-CZD4G7k_.mjs.map +0 -1
  1372. package/dist/default-BtlxWgh9.mjs +0 -81
  1373. package/dist/default-BtlxWgh9.mjs.map +0 -1
  1374. package/dist/deferred-tasks-Ba9fphlE.d.mts +0 -9
  1375. package/dist/deferred-tasks-Ba9fphlE.d.mts.map +0 -1
  1376. package/dist/device-flow-3E5l0fpH.mjs +0 -466
  1377. package/dist/device-flow-3E5l0fpH.mjs.map +0 -1
  1378. package/dist/dialect-helpers-CtYdsX4W.mjs +0 -307
  1379. package/dist/dialect-helpers-CtYdsX4W.mjs.map +0 -1
  1380. package/dist/direct-executor-ZI8E01Yt.mjs +0 -104
  1381. package/dist/direct-executor-ZI8E01Yt.mjs.map +0 -1
  1382. package/dist/dist-2hDLsLeq.mjs +0 -810
  1383. package/dist/dist-2hDLsLeq.mjs.map +0 -1
  1384. package/dist/email-console-gc-E3Eqj.mjs +0 -50
  1385. package/dist/email-console-gc-E3Eqj.mjs.map +0 -1
  1386. package/dist/emdash-runtime-DZGinqTL.mjs +0 -4041
  1387. package/dist/emdash-runtime-DZGinqTL.mjs.map +0 -1
  1388. package/dist/enrich-C2Mk0lwE.mjs +0 -194
  1389. package/dist/enrich-C2Mk0lwE.mjs.map +0 -1
  1390. package/dist/entry-lock-D3C99MXS.mjs +0 -1520
  1391. package/dist/entry-lock-D3C99MXS.mjs.map +0 -1
  1392. package/dist/entry-lock-D9rnj2Lu.mjs +0 -163
  1393. package/dist/entry-lock-D9rnj2Lu.mjs.map +0 -1
  1394. package/dist/entry-lock-DX7QkqEg.d.mts +0 -3503
  1395. package/dist/entry-lock-DX7QkqEg.d.mts.map +0 -1
  1396. package/dist/entry-locks-CO_KaoU5.mjs +0 -167
  1397. package/dist/entry-locks-CO_KaoU5.mjs.map +0 -1
  1398. package/dist/error-CCTCskie.mjs +0 -90
  1399. package/dist/error-CCTCskie.mjs.map +0 -1
  1400. package/dist/errors-W3PA8HqB.mjs +0 -447
  1401. package/dist/errors-W3PA8HqB.mjs.map +0 -1
  1402. package/dist/escape-DN4Qmjsl.mjs +0 -9
  1403. package/dist/escape-DN4Qmjsl.mjs.map +0 -1
  1404. package/dist/field-defs-cache-B337z8UJ.mjs +0 -77
  1405. package/dist/field-defs-cache-B337z8UJ.mjs.map +0 -1
  1406. package/dist/fts-manager-DMb0sbIQ.mjs +0 -413
  1407. package/dist/fts-manager-DMb0sbIQ.mjs.map +0 -1
  1408. package/dist/hash-CLVFzWvx.mjs +0 -37
  1409. package/dist/hash-CLVFzWvx.mjs.map +0 -1
  1410. package/dist/hook-result-KY1jq21o.d.mts +0 -31
  1411. package/dist/hook-result-KY1jq21o.d.mts.map +0 -1
  1412. package/dist/hreflang-BgS2FqhH.d.mts +0 -50
  1413. package/dist/hreflang-BgS2FqhH.d.mts.map +0 -1
  1414. package/dist/hreflang-CrJwqoeb.mjs +0 -105
  1415. package/dist/hreflang-CrJwqoeb.mjs.map +0 -1
  1416. package/dist/http-route-dispatch-DAKlanXb.mjs +0 -339
  1417. package/dist/http-route-dispatch-DAKlanXb.mjs.map +0 -1
  1418. package/dist/identity-DRGSHEEr.mjs +0 -35
  1419. package/dist/identity-DRGSHEEr.mjs.map +0 -1
  1420. package/dist/import-DFr5B_hC.mjs +0 -2020
  1421. package/dist/import-DFr5B_hC.mjs.map +0 -1
  1422. package/dist/index-CDwKDkTq.d.mts +0 -623
  1423. package/dist/index-CDwKDkTq.d.mts.map +0 -1
  1424. package/dist/install-finalization-5Os-QZ-2.mjs +0 -58
  1425. package/dist/install-finalization-5Os-QZ-2.mjs.map +0 -1
  1426. package/dist/lifecycle-7e-Lt_kX.mjs +0 -25
  1427. package/dist/lifecycle-7e-Lt_kX.mjs.map +0 -1
  1428. package/dist/load-Cu5bvCZh.mjs +0 -28
  1429. package/dist/load-Cu5bvCZh.mjs.map +0 -1
  1430. package/dist/loader-t4TxaFiQ.mjs +0 -1004
  1431. package/dist/loader-t4TxaFiQ.mjs.map +0 -1
  1432. package/dist/manifest-builder-D31UoJCu.mjs +0 -189
  1433. package/dist/manifest-builder-D31UoJCu.mjs.map +0 -1
  1434. package/dist/manifest-schema-Bkh8SeHd.mjs +0 -491
  1435. package/dist/manifest-schema-Bkh8SeHd.mjs.map +0 -1
  1436. package/dist/media-TBlKCwo7.mjs +0 -334
  1437. package/dist/media-TBlKCwo7.mjs.map +0 -1
  1438. package/dist/media-allowlist-s60eAIf6.mjs +0 -44
  1439. package/dist/media-allowlist-s60eAIf6.mjs.map +0 -1
  1440. package/dist/media-folders-ClIRLUQv.mjs +0 -210
  1441. package/dist/media-folders-ClIRLUQv.mjs.map +0 -1
  1442. package/dist/media-url-DY_ijO3B.mjs +0 -43
  1443. package/dist/media-url-DY_ijO3B.mjs.map +0 -1
  1444. package/dist/media-usage-C4NolTpQ.mjs +0 -1833
  1445. package/dist/media-usage-C4NolTpQ.mjs.map +0 -1
  1446. package/dist/media-usage-activation-CAhmb14O.mjs +0 -78
  1447. package/dist/media-usage-activation-CAhmb14O.mjs.map +0 -1
  1448. package/dist/media-usage-work-Cu5DqewW.mjs +0 -586
  1449. package/dist/media-usage-work-Cu5DqewW.mjs.map +0 -1
  1450. package/dist/media-usage-work-DJr3rOEN.mjs +0 -152
  1451. package/dist/media-usage-work-DJr3rOEN.mjs.map +0 -1
  1452. package/dist/media-usage-write-fence-DW5oHT7q.mjs +0 -42
  1453. package/dist/media-usage-write-fence-DW5oHT7q.mjs.map +0 -1
  1454. package/dist/menus-DbgU9z5Q.mjs +0 -3732
  1455. package/dist/menus-DbgU9z5Q.mjs.map +0 -1
  1456. package/dist/menus-Wy8m5NcH.mjs +0 -753
  1457. package/dist/menus-Wy8m5NcH.mjs.map +0 -1
  1458. package/dist/mime-CCYRC4r1.mjs +0 -36
  1459. package/dist/mime-CCYRC4r1.mjs.map +0 -1
  1460. package/dist/mode-CkmOhFqj.mjs +0 -23
  1461. package/dist/mode-CkmOhFqj.mjs.map +0 -1
  1462. package/dist/node-sqlite-compat-vpVaILnb.mjs +0 -46
  1463. package/dist/node-sqlite-compat-vpVaILnb.mjs.map +0 -1
  1464. package/dist/normalize-dChXQjwS.mjs +0 -191
  1465. package/dist/normalize-dChXQjwS.mjs.map +0 -1
  1466. package/dist/oauth-authorization-DZ-R-hmx.mjs +0 -274
  1467. package/dist/oauth-authorization-DZ-R-hmx.mjs.map +0 -1
  1468. package/dist/oauth-clients-BMPA8vjl.mjs +0 -266
  1469. package/dist/oauth-clients-BMPA8vjl.mjs.map +0 -1
  1470. package/dist/oauth-state-store-0gIdb48-.mjs +0 -50
  1471. package/dist/oauth-state-store-0gIdb48-.mjs.map +0 -1
  1472. package/dist/oauth-user-lookup-ChVW4UWu.mjs +0 -26
  1473. package/dist/oauth-user-lookup-ChVW4UWu.mjs.map +0 -1
  1474. package/dist/object-cache-DlSVbn5Y.mjs +0 -534
  1475. package/dist/object-cache-DlSVbn5Y.mjs.map +0 -1
  1476. package/dist/options-D5WcorD5.mjs +0 -200
  1477. package/dist/options-D5WcorD5.mjs.map +0 -1
  1478. package/dist/options-DAknChnw.d.mts +0 -77
  1479. package/dist/options-DAknChnw.d.mts.map +0 -1
  1480. package/dist/parse-Cr379Av9.mjs +0 -81
  1481. package/dist/parse-Cr379Av9.mjs.map +0 -1
  1482. package/dist/parse-tj9XLNO_.d.mts +0 -165
  1483. package/dist/parse-tj9XLNO_.d.mts.map +0 -1
  1484. package/dist/passkey-config-BD-wMaCf.mjs +0 -46
  1485. package/dist/passkey-config-BD-wMaCf.mjs.map +0 -1
  1486. package/dist/patterns-Do3iqi7N.mjs +0 -145
  1487. package/dist/patterns-Do3iqi7N.mjs.map +0 -1
  1488. package/dist/placeholder-CubPFn_L.d.mts +0 -306
  1489. package/dist/placeholder-CubPFn_L.d.mts.map +0 -1
  1490. package/dist/plugins-DHurtdXQ.mjs +0 -277
  1491. package/dist/plugins-DHurtdXQ.mjs.map +0 -1
  1492. package/dist/policy-lyZnjpwz.mjs +0 -45
  1493. package/dist/policy-lyZnjpwz.mjs.map +0 -1
  1494. package/dist/portable-text-oAwodbPS.mjs +0 -286
  1495. package/dist/portable-text-oAwodbPS.mjs.map +0 -1
  1496. package/dist/preview-Cup2bj8c.mjs +0 -107
  1497. package/dist/preview-Cup2bj8c.mjs.map +0 -1
  1498. package/dist/protocol-C1CNiKX4.d.mts +0 -61
  1499. package/dist/protocol-C1CNiKX4.d.mts.map +0 -1
  1500. package/dist/public-submission-Cs-pFMQK.mjs +0 -245
  1501. package/dist/public-submission-Cs-pFMQK.mjs.map +0 -1
  1502. package/dist/public-url-BWVHsfQv.mjs +0 -101
  1503. package/dist/public-url-BWVHsfQv.mjs.map +0 -1
  1504. package/dist/query-D-lxiOgn.mjs +0 -890
  1505. package/dist/query-D-lxiOgn.mjs.map +0 -1
  1506. package/dist/query-D7kHKOws.mjs +0 -41
  1507. package/dist/query-D7kHKOws.mjs.map +0 -1
  1508. package/dist/rate-limit-CqJn-w4h.mjs +0 -120
  1509. package/dist/rate-limit-CqJn-w4h.mjs.map +0 -1
  1510. package/dist/redirect-BrChFPIu.mjs +0 -17
  1511. package/dist/redirect-BrChFPIu.mjs.map +0 -1
  1512. package/dist/redirect-D2Yo-b6S.mjs +0 -415
  1513. package/dist/redirect-D2Yo-b6S.mjs.map +0 -1
  1514. package/dist/redirects-BCiTaIQ-.mjs +0 -146
  1515. package/dist/redirects-BCiTaIQ-.mjs.map +0 -1
  1516. package/dist/redirects-ByItliuu.mjs +0 -644
  1517. package/dist/redirects-ByItliuu.mjs.map +0 -1
  1518. package/dist/registry-CkuPoZs7.mjs +0 -1222
  1519. package/dist/registry-CkuPoZs7.mjs.map +0 -1
  1520. package/dist/relation-XTAtOpJO.mjs +0 -334
  1521. package/dist/relation-XTAtOpJO.mjs.map +0 -1
  1522. package/dist/relations-B4w-G5U9.mjs +0 -309
  1523. package/dist/relations-B4w-G5U9.mjs.map +0 -1
  1524. package/dist/relations-O63JWBRj.mjs +0 -403
  1525. package/dist/relations-O63JWBRj.mjs.map +0 -1
  1526. package/dist/request-cache-DaMhSQdx.mjs +0 -104
  1527. package/dist/request-cache-DaMhSQdx.mjs.map +0 -1
  1528. package/dist/request-meta-C8yl_sIs.mjs +0 -141
  1529. package/dist/request-meta-C8yl_sIs.mjs.map +0 -1
  1530. package/dist/resolve-BJIS8wKr.mjs +0 -199
  1531. package/dist/resolve-BJIS8wKr.mjs.map +0 -1
  1532. package/dist/route-wire-D66L2oaZ.mjs +0 -197
  1533. package/dist/route-wire-D66L2oaZ.mjs.map +0 -1
  1534. package/dist/routes-DYsTe-oR.mjs +0 -321
  1535. package/dist/routes-DYsTe-oR.mjs.map +0 -1
  1536. package/dist/runner-3gfxRsJG.d.mts +0 -68
  1537. package/dist/runner-3gfxRsJG.d.mts.map +0 -1
  1538. package/dist/runner-ClrleR8A.mjs +0 -5502
  1539. package/dist/runner-ClrleR8A.mjs.map +0 -1
  1540. package/dist/schema-CPhPM9Do.mjs +0 -420
  1541. package/dist/schema-CPhPM9Do.mjs.map +0 -1
  1542. package/dist/schema-DEERRrKS.mjs +0 -9
  1543. package/dist/schema-DEERRrKS.mjs.map +0 -1
  1544. package/dist/scopes-Bn8_rCSt.mjs +0 -27
  1545. package/dist/scopes-Bn8_rCSt.mjs.map +0 -1
  1546. package/dist/search-lami_Ota.mjs +0 -448
  1547. package/dist/search-lami_Ota.mjs.map +0 -1
  1548. package/dist/secrets-DNgVgz_e.mjs +0 -341
  1549. package/dist/secrets-DNgVgz_e.mjs.map +0 -1
  1550. package/dist/sections-BIpd4Cxf.mjs +0 -346
  1551. package/dist/sections-BIpd4Cxf.mjs.map +0 -1
  1552. package/dist/seo-BtU70b7l.mjs +0 -114
  1553. package/dist/seo-BtU70b7l.mjs.map +0 -1
  1554. package/dist/seo-_4qmazxz.mjs +0 -133
  1555. package/dist/seo-_4qmazxz.mjs.map +0 -1
  1556. package/dist/service-Bpy5iTX0.mjs +0 -150
  1557. package/dist/service-Bpy5iTX0.mjs.map +0 -1
  1558. package/dist/session-user-BQeS2Mrb.mjs +0 -51
  1559. package/dist/session-user-BQeS2Mrb.mjs.map +0 -1
  1560. package/dist/settings-DmroR53o.mjs +0 -420
  1561. package/dist/settings-DmroR53o.mjs.map +0 -1
  1562. package/dist/settings-g2F2a3Ub.mjs +0 -51
  1563. package/dist/settings-g2F2a3Ub.mjs.map +0 -1
  1564. package/dist/setup-complete-BrZaXpVV.mjs +0 -26
  1565. package/dist/setup-complete-BrZaXpVV.mjs.map +0 -1
  1566. package/dist/setup-nonce-Cb9-884O.mjs +0 -25
  1567. package/dist/setup-nonce-Cb9-884O.mjs.map +0 -1
  1568. package/dist/site-url-DjhYCaep.mjs +0 -16
  1569. package/dist/site-url-DjhYCaep.mjs.map +0 -1
  1570. package/dist/snapshot-C32Xuv3o.mjs +0 -233
  1571. package/dist/snapshot-C32Xuv3o.mjs.map +0 -1
  1572. package/dist/ssrf-Co1GGFZz.mjs +0 -406
  1573. package/dist/ssrf-Co1GGFZz.mjs.map +0 -1
  1574. package/dist/state-C7gCoiHr.mjs +0 -153
  1575. package/dist/state-C7gCoiHr.mjs.map +0 -1
  1576. package/dist/status-DLoBmcDz.mjs +0 -30
  1577. package/dist/status-DLoBmcDz.mjs.map +0 -1
  1578. package/dist/storage-query-CPctdMKX.d.mts +0 -1519
  1579. package/dist/storage-query-CPctdMKX.d.mts.map +0 -1
  1580. package/dist/target-CUKlGrXb.mjs +0 -27
  1581. package/dist/target-CUKlGrXb.mjs.map +0 -1
  1582. package/dist/taxonomies-BpqpXJUE.mjs +0 -707
  1583. package/dist/taxonomies-BpqpXJUE.mjs.map +0 -1
  1584. package/dist/taxonomies-ClCo4IGO.mjs +0 -899
  1585. package/dist/taxonomies-ClCo4IGO.mjs.map +0 -1
  1586. package/dist/taxonomy-Dj1vpw5E.mjs +0 -508
  1587. package/dist/taxonomy-Dj1vpw5E.mjs.map +0 -1
  1588. package/dist/tokens-Ns02GAma.mjs +0 -171
  1589. package/dist/tokens-Ns02GAma.mjs.map +0 -1
  1590. package/dist/transport-B8P1xsqk.mjs +0 -136
  1591. package/dist/transport-B8P1xsqk.mjs.map +0 -1
  1592. package/dist/transport-BLHZZcD_.d.mts +0 -42
  1593. package/dist/transport-BLHZZcD_.d.mts.map +0 -1
  1594. package/dist/trusted-proxy-Cg7SuD15.mjs +0 -51
  1595. package/dist/trusted-proxy-Cg7SuD15.mjs.map +0 -1
  1596. package/dist/turnstile-DWCSh_Qc.mjs +0 -53
  1597. package/dist/turnstile-DWCSh_Qc.mjs.map +0 -1
  1598. package/dist/types-B3RkqvDW.mjs +0 -156
  1599. package/dist/types-B3RkqvDW.mjs.map +0 -1
  1600. package/dist/types-BtuppOTV.mjs +0 -16
  1601. package/dist/types-BtuppOTV.mjs.map +0 -1
  1602. package/dist/types-CCR3ivXE.d.mts +0 -363
  1603. package/dist/types-CCR3ivXE.d.mts.map +0 -1
  1604. package/dist/types-CSHgU_-_.mjs +0 -18
  1605. package/dist/types-CSHgU_-_.mjs.map +0 -1
  1606. package/dist/types-CbwqGOXm.d.mts +0 -766
  1607. package/dist/types-CbwqGOXm.d.mts.map +0 -1
  1608. package/dist/types-D4RGIc2I.d.mts +0 -198
  1609. package/dist/types-D4RGIc2I.d.mts.map +0 -1
  1610. package/dist/types-DEHidMEk.d.mts +0 -1637
  1611. package/dist/types-DEHidMEk.d.mts.map +0 -1
  1612. package/dist/types-DQeU2SR7.d.mts +0 -279
  1613. package/dist/types-DQeU2SR7.d.mts.map +0 -1
  1614. package/dist/types-DxQWoQLA.d.mts +0 -264
  1615. package/dist/types-DxQWoQLA.d.mts.map +0 -1
  1616. package/dist/types-FBBnGZ2Y.d.mts +0 -198
  1617. package/dist/types-FBBnGZ2Y.d.mts.map +0 -1
  1618. package/dist/types-ZCnlropY.mjs +0 -69
  1619. package/dist/types-ZCnlropY.mjs.map +0 -1
  1620. package/dist/types-_Qtyawiy.d.mts +0 -124
  1621. package/dist/types-_Qtyawiy.d.mts.map +0 -1
  1622. package/dist/types-gC6FAoa_.d.mts +0 -75
  1623. package/dist/types-gC6FAoa_.d.mts.map +0 -1
  1624. package/dist/types-k--LneIF.mjs +0 -22
  1625. package/dist/types-k--LneIF.mjs.map +0 -1
  1626. package/dist/upload-attempts-2xdnoKGe.mjs +0 -27
  1627. package/dist/upload-attempts-2xdnoKGe.mjs.map +0 -1
  1628. package/dist/url-pattern-3m3reMzr.mjs +0 -33
  1629. package/dist/url-pattern-3m3reMzr.mjs.map +0 -1
  1630. package/dist/user-DroRntkx.mjs +0 -171
  1631. package/dist/user-DroRntkx.mjs.map +0 -1
  1632. package/dist/utils-CC2oNGV8.mjs +0 -435
  1633. package/dist/utils-CC2oNGV8.mjs.map +0 -1
  1634. package/dist/validate-BCP9HNDn.mjs +0 -384
  1635. package/dist/validate-BCP9HNDn.mjs.map +0 -1
  1636. package/dist/validate-CyQ7I3xF.d.mts +0 -410
  1637. package/dist/validate-CyQ7I3xF.d.mts.map +0 -1
  1638. package/dist/validation-DBy6WF94.mjs +0 -216
  1639. package/dist/validation-DBy6WF94.mjs.map +0 -1
  1640. package/dist/version-B57E4Uo7.mjs +0 -7
  1641. package/dist/version-B57E4Uo7.mjs.map +0 -1
  1642. package/dist/widgets-wQ7Xcgq8.mjs +0 -126
  1643. package/dist/widgets-wQ7Xcgq8.mjs.map +0 -1
  1644. package/dist/wxr-taxonomies-DIHfvEDt.mjs +0 -406
  1645. package/dist/wxr-taxonomies-DIHfvEDt.mjs.map +0 -1
  1646. package/dist/wxr-taxonomies-DU2ZSiGc.d.mts +0 -34
  1647. package/dist/wxr-taxonomies-DU2ZSiGc.d.mts.map +0 -1
  1648. package/dist/zod-generator-D_yHIWfs.mjs +0 -329
  1649. package/dist/zod-generator-D_yHIWfs.mjs.map +0 -1
  1650. /package/dist/{ssrf-BRpTDMV6.mjs → ssrf-DpBH7aOt.mjs} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-cFFZpyS0.mjs","names":["pluginManifestSchema","reconcileManifestAccess","normalizeManifestRoute","pluginManifestSchema","reconcileManifestAccess","pluginManifestSchema","reconcileManifestAccess"],"sources":["../src/api/rev.ts","../src/api/handlers/validate-media-fields.ts","../src/api/handlers/content.ts","../src/api/handlers/manifest.ts","../src/api/handlers/revision.ts","../src/api/handlers/plugin-settings.ts","../src/plugins/marketplace.ts","../src/plugins/storage-indexes.ts","../src/api/handlers/marketplace.ts","../../registry-verification/dist/bundle.js","../src/registry/artifact-verification.ts","../src/registry/plugin-id.ts","../src/registry/record-labels.ts","../src/api/handlers/registry.ts"],"sourcesContent":["/**\n * Opaque _rev token generation and validation.\n *\n * Format: base64(\"version:updated_at\")\n * Stateless — server decodes and checks both components.\n *\n * Rules:\n * - No _rev sent → blind write (backwards-compatible)\n * - _rev matches → write proceeds, new _rev returned\n * - _rev mismatch → 409 Conflict\n */\n\nimport type { ContentItem } from \"../database/repositories/types.js\";\nimport { encodeBase64, decodeBase64 } from \"../utils/base64.js\";\n\n/**\n * Generate a _rev token from a content item's version and updatedAt.\n */\nexport function encodeRev(item: ContentItem): string {\n\treturn encodeBase64(`${item.version}:${item.updatedAt}`);\n}\n\n/**\n * Decode a _rev token into its components.\n * Returns null if the token is malformed.\n */\nexport function decodeRev(rev: string): { version: number; updatedAt: string } | null {\n\ttry {\n\t\tconst decoded = decodeBase64(rev);\n\t\tconst colonIdx = decoded.indexOf(\":\");\n\t\tif (colonIdx === -1) return null;\n\n\t\tconst version = parseInt(decoded.slice(0, colonIdx), 10);\n\t\tconst updatedAt = decoded.slice(colonIdx + 1);\n\n\t\tif (isNaN(version) || !updatedAt) return null;\n\t\treturn { version, updatedAt };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Validate a _rev token against a content item.\n * Returns null if valid (or if no _rev provided), or an error message if invalid.\n */\nexport function validateRev(\n\trev: string | undefined,\n\titem: ContentItem,\n): { valid: true } | { valid: false; message: string } {\n\t// No _rev = blind write (backwards-compatible)\n\tif (!rev) return { valid: true };\n\n\tconst decoded = decodeRev(rev);\n\tif (!decoded) {\n\t\treturn { valid: false, message: \"Malformed _rev token\" };\n\t}\n\n\tif (decoded.version !== item.version || decoded.updatedAt !== item.updatedAt) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tmessage: \"Content has been modified since last read (version conflict)\",\n\t\t};\n\t}\n\n\treturn { valid: true };\n}\n","import type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../../database/types.js\";\nimport { matchesMimeAllowlist, parseAllowedMimeTypes } from \"../../media/mime.js\";\nimport { requestCached } from \"../../request-cache.js\";\nimport { BlockTypeRegistry } from \"../../schema/block-type-registry.js\";\nimport type { BlockType } from \"../../schema/block-types.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport type { ApiResult } from \"../types.js\";\n\ninterface FieldRow {\n\tslug: string;\n\ttype: string;\n\tallowedMimeTypes: string[];\n\tblockTypes?: BlockType[];\n}\n\ninterface MediaTarget {\n\tpath: string;\n\tvalue: unknown;\n\tallowedMimeTypes: string[];\n}\n\ninterface MediaRefValue {\n\tid?: unknown;\n\tprovider?: unknown;\n\tmimeType?: unknown;\n\tdarkVariant?: unknown;\n}\n\nfunction asMediaRef(value: unknown): MediaRefValue | null {\n\tif (value === null || value === undefined) return null;\n\tif (typeof value !== \"object\" || Array.isArray(value)) return null;\n\treturn value;\n}\n\nfunction fail(message: string): ApiResult<never> {\n\treturn { success: false, error: { code: \"INVALID_MIME_FOR_FIELD\", message } };\n}\n\nfunction addTarget(\n\ttargets: MediaTarget[],\n\tpath: string,\n\tvalue: unknown,\n\tallowedMimeTypes: readonly string[] | undefined,\n\tincludeDarkVariant: boolean,\n): void {\n\tif (!allowedMimeTypes?.length || value == null) return;\n\ttargets.push({ path, value, allowedMimeTypes: [...allowedMimeTypes] });\n\tconst ref = includeDarkVariant ? asMediaRef(value) : null;\n\tif (ref && \"darkVariant\" in ref && ref.darkVariant != null) {\n\t\ttargets.push({\n\t\t\tpath: `${path}.darkVariant`,\n\t\t\tvalue: ref.darkVariant,\n\t\t\tallowedMimeTypes: [...allowedMimeTypes],\n\t\t});\n\t}\n}\n\nfunction collectBlockTargets(\n\ttargets: MediaTarget[],\n\tfield: FieldRow,\n\tvalue: unknown,\n): ApiResult<true> {\n\tif (!Array.isArray(value)) return { success: true, data: true };\n\tconst types = new Map((field.blockTypes ?? []).map((type) => [type.slug, type]));\n\tfor (const [index, block] of value.entries()) {\n\t\tif (!block || typeof block !== \"object\" || Array.isArray(block)) continue;\n\t\tconst typeSlug = \"_type\" in block && typeof block._type === \"string\" ? block._type : undefined;\n\t\tconst versionNumber =\n\t\t\t\"_version\" in block && typeof block._version === \"number\" ? block._version : undefined;\n\t\tconst key = \"_key\" in block && typeof block._key === \"string\" ? block._key : String(index);\n\t\tconst type = typeSlug ? types.get(typeSlug) : undefined;\n\t\tconst version = type?.versions.find((candidate) => candidate.version === versionNumber);\n\t\tif (!type || !version || version.unsupportedTypes?.length) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"UNSUPPORTED_FIELD_TYPE\",\n\t\t\t\t\tmessage: `Field '${field.slug}' block '${key}' has no retained definition`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tfor (const nestedField of version.fields) {\n\t\t\tconst nestedValue = nestedField.slug in block ? block[nestedField.slug] : undefined;\n\t\t\tconst nestedPath = `${field.slug}.${key}.${nestedField.slug}`;\n\t\t\tif (nestedField.type !== \"image\" && nestedField.type !== \"file\") continue;\n\t\t\taddTarget(\n\t\t\t\ttargets,\n\t\t\t\tnestedPath,\n\t\t\t\tnestedValue,\n\t\t\t\tnestedField.validation?.allowedMimeTypes,\n\t\t\t\tnestedField.type === \"image\",\n\t\t\t);\n\t\t}\n\t}\n\treturn { success: true, data: true };\n}\n\nasync function loadMediaFieldsForCollection(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n): Promise<FieldRow[]> {\n\tconst rows = await db\n\t\t.selectFrom(\"_emdash_fields\")\n\t\t.innerJoin(\"_emdash_collections\", \"_emdash_collections.id\", \"_emdash_fields.collection_id\")\n\t\t.select([\"_emdash_fields.slug\", \"_emdash_fields.type\", \"_emdash_fields.validation\"])\n\t\t.where(\"_emdash_collections.slug\", \"=\", collectionSlug)\n\t\t.where(\"_emdash_fields.type\", \"in\", [\"file\", \"image\", \"blocks\"])\n\t\t.execute();\n\n\tconst out: FieldRow[] = [];\n\tconst allBlockTypes = rows.some((row) => row.type === \"blocks\")\n\t\t? await new BlockTypeRegistry(db).listBlockTypes()\n\t\t: [];\n\tconst blockTypeBySlug = new Map(allBlockTypes.map((type) => [type.slug, type]));\n\tfor (const row of rows) {\n\t\tif (row.type === \"blocks\") {\n\t\t\tlet validation: unknown;\n\t\t\ttry {\n\t\t\t\tvalidation = row.validation ? JSON.parse(row.validation) : {};\n\t\t\t} catch {\n\t\t\t\tvalidation = {};\n\t\t\t}\n\t\t\tlet configured: unknown[] = [];\n\t\t\tif (validation && typeof validation === \"object\" && !Array.isArray(validation)) {\n\t\t\t\tconst allowed =\n\t\t\t\t\t\"allowedTypes\" in validation && Array.isArray(validation.allowedTypes)\n\t\t\t\t\t\t? validation.allowedTypes\n\t\t\t\t\t\t: [];\n\t\t\t\tconst retired =\n\t\t\t\t\t\"retiredTypes\" in validation && Array.isArray(validation.retiredTypes)\n\t\t\t\t\t\t? validation.retiredTypes\n\t\t\t\t\t\t: [];\n\t\t\t\tconfigured = [...allowed, ...retired];\n\t\t\t}\n\t\t\tout.push({\n\t\t\t\tslug: row.slug,\n\t\t\t\ttype: row.type,\n\t\t\t\tallowedMimeTypes: [],\n\t\t\t\tblockTypes: configured.flatMap((slug) => {\n\t\t\t\t\tconst type = typeof slug === \"string\" ? blockTypeBySlug.get(slug) : undefined;\n\t\t\t\t\treturn type ? [type] : [];\n\t\t\t\t}),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tconst list = parseAllowedMimeTypes(row.validation);\n\t\tif (!list) continue;\n\t\tout.push({ slug: row.slug, type: row.type, allowedMimeTypes: list });\n\t}\n\treturn out;\n}\n\nexport async function validateMediaFields(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tdata: Record<string, unknown>,\n): Promise<ApiResult<true>> {\n\t// Cache is keyed on slug only. If a handler creates/modifies a field and\n\t// then writes content in the same request (e.g. bulk import), the cached\n\t// list will be stale for that request. This is an edge case in normal use.\n\tconst fields = await requestCached(`mediaFields:${collectionSlug}`, () =>\n\t\tloadMediaFieldsForCollection(db, collectionSlug),\n\t);\n\tif (fields.length === 0) return { success: true, data: true };\n\tconst targets: MediaTarget[] = [];\n\tfor (const field of fields) {\n\t\tconst value = data[field.slug];\n\t\tif (field.type === \"blocks\") {\n\t\t\tconst collected = collectBlockTargets(targets, field, value);\n\t\t\tif (!collected.success) return collected;\n\t\t} else {\n\t\t\taddTarget(targets, field.slug, value, field.allowedMimeTypes, field.type === \"image\");\n\t\t}\n\t}\n\tif (targets.length === 0) return { success: true, data: true };\n\n\t// Collect local media ids that need a MIME lookup\n\tconst localIds = new Set<string>();\n\tfor (const target of targets) {\n\t\tconst ref = asMediaRef(target.value);\n\t\tif (!ref) continue;\n\t\tconst provider = typeof ref.provider === \"string\" ? ref.provider : \"local\";\n\t\tif (provider === \"local\" && typeof ref.id === \"string\") {\n\t\t\tlocalIds.add(ref.id);\n\t\t}\n\t}\n\n\t// Batch-load local media MIMEs\n\tconst idList = [...localIds];\n\tconst mimeById = new Map<string, string>();\n\tif (idList.length > 0) {\n\t\tfor (const batch of chunks(idList, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await db\n\t\t\t\t.selectFrom(\"media\")\n\t\t\t\t.select([\"id\", \"mime_type\"])\n\t\t\t\t.where(\"id\", \"in\", batch)\n\t\t\t\t.execute();\n\t\t\tfor (const r of rows) mimeById.set(r.id, r.mime_type);\n\t\t}\n\t}\n\n\tfor (const target of targets) {\n\t\tconst ref = asMediaRef(target.value);\n\t\tif (!ref) continue;\n\n\t\tconst provider = typeof ref.provider === \"string\" ? ref.provider : \"local\";\n\n\t\t// External providers carry mimeType in the ref; trust it as-is.\n\t\t// Local media: look up the stored mimeType by id.\n\t\tlet mime: string | undefined;\n\t\tif (provider === \"local\") {\n\t\t\tif (typeof ref.id !== \"string\") {\n\t\t\t\treturn fail(`Field '${target.path}' references media with an invalid id`);\n\t\t\t}\n\t\t\tmime = mimeById.get(ref.id);\n\t\t\tif (!mime) {\n\t\t\t\treturn fail(`Field '${target.path}' references media with unknown MIME type`);\n\t\t\t}\n\t\t} else {\n\t\t\tif (typeof ref.mimeType !== \"string\") {\n\t\t\t\treturn fail(`Field '${target.path}' requires a mimeType declaration for non-local media`);\n\t\t\t}\n\t\t\t// TODO: long-term, consider a server-side HEAD probe or provider-vouched\n\t\t\t// MIMEs for non-local refs; for now the constraint is only as strong as\n\t\t\t// the client that constructed the ref.\n\t\t\tmime = ref.mimeType;\n\t\t}\n\n\t\tif (!matchesMimeAllowlist(mime, target.allowedMimeTypes)) {\n\t\t\treturn fail(`Field '${target.path}' does not accept ${mime}`);\n\t\t}\n\t}\n\n\treturn { success: true, data: true };\n}\n","/**\n * Content CRUD handlers\n */\n\nimport type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport type { ContentFieldFilters } from \"../../content-list-query.js\";\nimport { isSqlite } from \"../../database/dialect-helpers.js\";\nimport { BylineRepository } from \"../../database/repositories/byline.js\";\nimport type { ContentBylineInput } from \"../../database/repositories/byline.js\";\nimport { CommentRepository } from \"../../database/repositories/comment.js\";\nimport {\n\tContentRepository,\n\tisSystemOrderField,\n\ttype ContentRevisionPrecondition,\n} from \"../../database/repositories/content.js\";\nimport { EntryLockRepository } from \"../../database/repositories/entry-locks.js\";\nimport { OptionsRepository } from \"../../database/repositories/options.js\";\nimport { RedirectRepository } from \"../../database/repositories/redirect.js\";\nimport { RevisionRepository } from \"../../database/repositories/revision.js\";\nimport { SeoRepository } from \"../../database/repositories/seo.js\";\nimport { TaxonomyRepository } from \"../../database/repositories/taxonomy.js\";\nimport {\n\tContentCollectionNotFoundError,\n\tContentMutationConflictError,\n\tEmDashValidationError,\n\tScheduledNotDueError,\n\tInvalidCursorError,\n\ttype BylineSummary,\n\ttype ContentBylineCredit,\n\ttype ContentBylineFilter,\n\ttype ContentDateField,\n\ttype ContentItem,\n\ttype ContentSeo,\n\ttype ContentSeoInput,\n\ttype FindManyOptions,\n} from \"../../database/repositories/types.js\";\nimport { UserRepository } from \"../../database/repositories/user.js\";\nimport { withTransaction } from \"../../database/transaction.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { validateIdentifier } from \"../../database/validate.js\";\nimport { getI18nConfig, isI18nEnabled, resolveConfiguredLocale } from \"../../i18n/config.js\";\nimport {\n\tscheduledPolicyRejectionKey,\n\ttype ScheduledPolicyRejection,\n} from \"../../plugins/content-policy.js\";\nimport { invalidateRedirectCache } from \"../../redirects/cache.js\";\nimport { FTSManager } from \"../../search/fts-manager.js\";\nimport { invalidateTermCache } from \"../../taxonomies/index.js\";\nimport { isMissingColumnError, isMissingTableError } from \"../../utils/db-errors.js\";\nimport { decodeRev, encodeRev, validateRev } from \"../rev.js\";\nimport type { ApiResult, ContentListResponse, ContentResponse } from \"../types.js\";\nimport { validateMediaFields } from \"./validate-media-fields.js\";\n\n/**\n * Narrow a caught error to one carrying a structured `apiError` discriminant.\n * Used by transaction callbacks that want to surface a specific error code\n * through the standard Error throwing path.\n */\nfunction hasApiError(error: unknown): error is Error & { apiError: { code: string } } {\n\tif (!(error instanceof Error) || !(\"apiError\" in error)) return false;\n\tconst { apiError } = error;\n\treturn (\n\t\ttypeof apiError === \"object\" &&\n\t\tapiError !== null &&\n\t\t\"code\" in apiError &&\n\t\ttypeof apiError.code === \"string\"\n\t);\n}\n\nfunction isTranslationLocaleConflict(error: unknown, collection: string): boolean {\n\tif (!(error instanceof Error)) return false;\n\tconst message = error.message.toLowerCase();\n\tconst storedIndexName = `uidx_ec_${collection}_active_tg_locale`.slice(0, 63).toLowerCase();\n\treturn (\n\t\tmessage.includes(storedIndexName) ||\n\t\t(message.includes(\"unique constraint failed\") &&\n\t\t\tmessage.includes(\"translation_group\") &&\n\t\t\tmessage.includes(\"locale\"))\n\t);\n}\n\nfunction decodeRevisionPrecondition(\n\trev: string | undefined,\n): ContentRevisionPrecondition | undefined {\n\tif (rev === undefined) return undefined;\n\tconst decoded = decodeRev(rev);\n\tif (!decoded) throw new ContentMutationConflictError(\"Revision precondition did not match\");\n\treturn decoded;\n}\n\n/**\n * Extract a slug source (title or name) from content data.\n * Returns null if no suitable string field is found.\n */\nfunction getSlugSource(data: Record<string, unknown>): string | null {\n\tif (typeof data.title === \"string\" && data.title.length > 0) return data.title;\n\tif (typeof data.name === \"string\" && data.name.length > 0) return data.name;\n\treturn null;\n}\n\n/** Default SEO values for content without an explicit SEO row */\nconst SEO_DEFAULTS: ContentSeo = {\n\ttitle: null,\n\tdescription: null,\n\timage: null,\n\tcanonical: null,\n\tnoIndex: false,\n};\n\n/**\n * Check if a collection has SEO enabled.\n */\nasync function collectionHasSeo(db: Kysely<Database>, collection: string): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select(\"has_seo\")\n\t\t.where(\"slug\", \"=\", collection)\n\t\t.executeTakeFirst();\n\treturn row?.has_seo === 1;\n}\n\nasync function getCollectionPublishConfig(\n\tdb: Kysely<Database>,\n\tcollection: string,\n): Promise<{ supportsRevisions: boolean; routable: boolean }> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select([\"supports\", \"routable\"])\n\t\t.where(\"slug\", \"=\", collection)\n\t\t.executeTakeFirst();\n\tconst supports: unknown = row?.supports ? JSON.parse(row.supports) : [];\n\treturn {\n\t\tsupportsRevisions: Array.isArray(supports) && supports.includes(\"revisions\"),\n\t\troutable: row?.routable !== 0,\n\t};\n}\n\nfunction requireRoutablePublishSlug(routable: boolean, slug: string | null | undefined): void {\n\tif (routable && !slug?.trim()) {\n\t\tthrow new EmDashValidationError(\"Cannot publish routable content without a slug\");\n\t}\n}\n\n/**\n * Hydrate SEO data on a single content item if the collection has SEO enabled.\n */\nasync function hydrateSeo(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\titem: ContentItem,\n\thasSeo: boolean,\n): Promise<void> {\n\tif (!hasSeo) return;\n\tconst seoRepo = new SeoRepository(db);\n\titem.seo = await seoRepo.get(collection, item.id);\n}\n\n/**\n * Hydrate SEO data on multiple content items using a single batch query.\n */\nasync function hydrateSeoMany(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\titems: ContentItem[],\n\thasSeo: boolean,\n): Promise<void> {\n\tif (!hasSeo || items.length === 0) return;\n\tconst seoRepo = new SeoRepository(db);\n\tconst seoMap = await seoRepo.getMany(\n\t\tcollection,\n\t\titems.map((i) => i.id),\n\t);\n\tfor (const item of items) {\n\t\titem.seo = seoMap.get(item.id) ?? { ...SEO_DEFAULTS };\n\t}\n}\n\nasync function hydrateBylines(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\titem: ContentItem,\n): Promise<void> {\n\tconst bylineRepo = new BylineRepository(db);\n\t// Strict per-locale (migration 040): a credit at locale X renders iff a\n\t// byline row exists at locale X in the credited translation_group. The\n\t// junction itself spans translations; rendering does not fall back.\n\tconst localeOpt = item.locale ? { locale: item.locale } : undefined;\n\tconst bylines = await bylineRepo.getContentBylines(collection, item.id, localeOpt);\n\n\tif (bylines.length > 0) {\n\t\titem.bylines = bylines.map((c) => ({ ...c, source: \"explicit\" as const }));\n\t\titem.byline = bylines[0]?.byline ?? null;\n\t\treturn;\n\t}\n\n\t// `primaryBylineId` is set iff junction rows exist; non-null\n\t// suppresses author fallback even when the credit doesn't resolve\n\t// at this locale.\n\tif (item.primaryBylineId) {\n\t\titem.bylines = [];\n\t\titem.byline = null;\n\t\treturn;\n\t}\n\n\tif (item.authorId) {\n\t\t// Same strict-locale rule as explicit credits: a user-linked byline\n\t\t// renders on the entry only when a sibling exists at the entry's\n\t\t// locale. Without this we'd silently surface the default-locale\n\t\t// row, which contradicts the per-locale model.\n\t\tconst fallback = await bylineRepo.findByUserId(item.authorId, localeOpt);\n\t\tif (fallback) {\n\t\t\titem.bylines = [{ byline: fallback, sortOrder: 0, roleLabel: null, source: \"inferred\" }];\n\t\t\titem.byline = fallback;\n\t\t\treturn;\n\t\t}\n\t}\n\n\titem.bylines = [];\n\titem.byline = null;\n}\n\n/**\n * Batch-hydrate bylines for multiple items using two bulk queries instead of N+1.\n *\n * Items may live at different locales (e.g. a list endpoint returning the\n * translations of an entry). Group by `item.locale` and call the strict\n * per-locale repo method once per group so each item resolves against its\n * own locale's byline rows.\n */\nasync function hydrateBylinesMany(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\titems: ContentItem[],\n): Promise<void> {\n\tif (items.length === 0) return;\n\n\tconst bylineRepo = new BylineRepository(db);\n\n\t// 1. Bucket items by locale so we can call the strict-locale repo\n\t// once per bucket. Items with a null/undefined locale (pre-i18n\n\t// rows on a single-locale install) share an \"unscoped\" bucket.\n\tconst localeBuckets = new Map<string | null, ContentItem[]>();\n\tfor (const item of items) {\n\t\tconst key = item.locale ?? null;\n\t\tconst bucket = localeBuckets.get(key);\n\t\tif (bucket) bucket.push(item);\n\t\telse localeBuckets.set(key, [item]);\n\t}\n\n\t// 2. Per-locale: fetch explicit credits. Items whose credits don't\n\t// resolve at this locale go through a locale-agnostic \"has any\n\t// junction\" check before being considered for author inference —\n\t// explicit editorial intent at any locale beats inferred fallback.\n\tconst bylinesByItem = new Map<string, ContentBylineCredit[]>();\n\tconst itemsNeedingAuthorCheck: ContentItem[] = [];\n\tfor (const [locale, bucket] of localeBuckets) {\n\t\tconst localeOpt = locale ? { locale } : undefined;\n\t\tconst ids = bucket.map((i) => i.id);\n\t\tconst credits = await bylineRepo.getContentBylinesMany(collection, ids, localeOpt);\n\t\tfor (const [id, list] of credits) bylinesByItem.set(id, list);\n\n\t\tfor (const item of bucket) {\n\t\t\tif (credits.has(item.id) && credits.get(item.id)!.length > 0) continue;\n\t\t\tif (item.authorId) itemsNeedingAuthorCheck.push(item);\n\t\t}\n\t}\n\n\t// 3. Author fallback applies only when no explicit credit exists\n\t// (primaryBylineId null).\n\tconst fallbackByItem = new Map<string, BylineSummary>();\n\tif (itemsNeedingAuthorCheck.length > 0) {\n\t\tconst authorBuckets = new Map<string | null, ContentItem[]>();\n\t\tfor (const item of itemsNeedingAuthorCheck) {\n\t\t\tif (item.primaryBylineId) continue;\n\t\t\tconst key = item.locale ?? null;\n\t\t\tconst bucket = authorBuckets.get(key);\n\t\t\tif (bucket) bucket.push(item);\n\t\t\telse authorBuckets.set(key, [item]);\n\t\t}\n\n\t\tfor (const [locale, bucket] of authorBuckets) {\n\t\t\tconst localeOpt = locale ? { locale } : undefined;\n\t\t\tconst authorIds = bucket.map((i) => i.authorId).filter((id): id is string => id !== null);\n\t\t\tconst uniqueAuthorIds = [...new Set(authorIds)];\n\t\t\tif (uniqueAuthorIds.length === 0) continue;\n\t\t\tconst authorMap = await bylineRepo.findByUserIds(uniqueAuthorIds, localeOpt);\n\t\t\tfor (const item of bucket) {\n\t\t\t\tif (!item.authorId) continue;\n\t\t\t\tconst f = authorMap.get(item.authorId);\n\t\t\t\tif (f) fallbackByItem.set(item.id, f);\n\t\t\t}\n\t\t}\n\t}\n\n\t// 4. Assign to each item.\n\tfor (const item of items) {\n\t\tconst explicit = bylinesByItem.get(item.id);\n\t\tif (explicit && explicit.length > 0) {\n\t\t\titem.bylines = explicit.map((c) => ({ ...c, source: \"explicit\" as const }));\n\t\t\titem.byline = explicit[0]?.byline ?? null;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst fallback = fallbackByItem.get(item.id);\n\t\tif (fallback) {\n\t\t\titem.bylines = [{ byline: fallback, sortOrder: 0, roleLabel: null, source: \"inferred\" }];\n\t\t\titem.byline = fallback;\n\t\t\tcontinue;\n\t\t}\n\n\t\titem.bylines = [];\n\t\titem.byline = null;\n\t}\n}\n\n/**\n * Resolve an identifier (ID or slug) to a real content ID.\n * Returns the ID if found, null if not found.\n * When locale is provided, slug lookups are scoped to that locale.\n */\nasync function resolveId(\n\trepo: ContentRepository,\n\tcollection: string,\n\tidentifier: string,\n\tlocale?: string,\n): Promise<string | null> {\n\tconst item = await repo.findByIdOrSlug(\n\t\tcollection,\n\t\tidentifier,\n\t\tlocale ? resolveConfiguredLocale(locale) : undefined,\n\t);\n\treturn item?.id ?? null;\n}\n\n/**\n * Resolve an identifier (ID or slug) to a real content ID,\n * including trashed (soft-deleted) items.\n */\nasync function resolveIdIncludingTrashed(\n\trepo: ContentRepository,\n\tcollection: string,\n\tidentifier: string,\n\tlocale?: string,\n): Promise<string | null> {\n\tconst item = await repo.findByIdOrSlugIncludingTrashed(\n\t\tcollection,\n\t\tidentifier,\n\t\tlocale ? resolveConfiguredLocale(locale) : undefined,\n\t);\n\treturn item?.id ?? null;\n}\n\n/**\n * Trashed content item with deletion timestamp\n */\nexport interface TrashedContentItem {\n\tid: string;\n\ttype: string;\n\tslug: string | null;\n\tstatus: string;\n\tlocale: string | null;\n\ttranslationGroup: string | null;\n\tdata: Record<string, unknown>;\n\tauthorId: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tpublishedAt: string | null;\n\tdeletedAt: string;\n}\n\n/**\n * Resolve the columns a content-list search should match against. Always\n * includes `slug` (a standard column), adds the configured `titleField` plus\n * the `title`/`name` display fields when the collection actually defines them,\n * mirroring the admin's item-title resolution (titleField -> title -> name ->\n * slug), and includes every field explicitly marked searchable. Returning only\n * schema-backed columns avoids \"no such column\" errors.\n */\nasync function resolveSearchColumns(db: Kysely<Database>, collection: string): Promise<string[]> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select([\"id\", \"title_field\"])\n\t\t.where(\"slug\", \"=\", collection)\n\t\t.executeTakeFirst();\n\tif (!row) return [\"slug\"];\n\n\tconst fields = await db\n\t\t.selectFrom(\"_emdash_fields\")\n\t\t.select([\"slug\", \"searchable\"])\n\t\t.where(\"collection_id\", \"=\", row.id)\n\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t.execute();\n\tconst columns = new Set([\"slug\"]);\n\tconst fieldSlugs = new Set(fields.map((f) => f.slug));\n\n\t// A configured titleField takes precedence, then the conventional\n\t// title/name fields. A null title_field falls through to those defaults.\n\tif (row.title_field && fieldSlugs.has(row.title_field)) columns.add(row.title_field);\n\tfor (const candidate of [\"title\", \"name\"]) {\n\t\tif (fieldSlugs.has(candidate)) columns.add(candidate);\n\t}\n\tfor (const field of fields) {\n\t\tif (field.searchable === 1) columns.add(field.slug);\n\t}\n\treturn [...columns];\n}\n\n/**\n * Decide whether the content-list `q` filter can be served from the\n * collection's FTS5 index instead of a full-scan substring LIKE (#1517).\n *\n * Requires SQLite (FTS5 is SQLite-only), search enabled on the collection,\n * every non-slug display column present in the searchable-field set (or the\n * index would miss matches the LIKE finds), and the index table actually\n * existing.\n */\nasync function canUseFtsForListFilter(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tsearchColumns: string[],\n): Promise<boolean> {\n\tif (!isSqlite(db)) return false;\n\tconst ftsManager = new FTSManager(db);\n\tconst config = await ftsManager.getSearchConfig(collection);\n\tif (!config?.enabled) return false;\n\tconst searchable = new Set(await ftsManager.getSearchableFields(collection));\n\tconst covered = searchColumns.every((col) => col === \"slug\" || searchable.has(col));\n\tif (!covered) return false;\n\treturn ftsManager.ftsTableExists(collection);\n}\n\n/**\n * Create a 301 auto-redirect from an entry's old URL to its new one after a\n * slug change, using the collection's URL pattern. Shared by\n * handleContentUpdate (direct slug edits) and handleContentPublish (slug edits\n * staged as `_slug` in a draft revision, which only land on publish).\n */\nasync function createSlugChangeRedirect(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\toldSlug: string,\n\tnewSlug: string,\n\tcontentId: string,\n\toldPublishedAt: string | null,\n\tnewPublishedAt: string | null,\n): Promise<void> {\n\t// A URL pattern has no locale token, so every locale variant of an entry\n\t// generates the same URL, and slugs are unique per (slug, locale) — a\n\t// translation may still hold the old slug. Redirecting away from a URL\n\t// another row still answers on would take that page down: the redirect\n\t// middleware runs `order: \"pre\"`, so routing never gets a chance.\n\t// Any surviving row counts, published or not: a draft that publishes later\n\t// would otherwise be shadowed by the redirect.\n\tif (await slugStillTaken(db, collection, oldSlug, contentId)) return;\n\n\tconst collectionRow = await db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select(\"url_pattern\")\n\t\t.where(\"slug\", \"=\", collection)\n\t\t.executeTakeFirst();\n\n\tconst redirectRepo = new RedirectRepository(db);\n\tawait redirectRepo.createAutoRedirect(\n\t\tcollection,\n\t\toldSlug,\n\t\tnewSlug,\n\t\tcontentId,\n\t\tcollectionRow?.url_pattern ?? null,\n\t\toldPublishedAt,\n\t\tnewPublishedAt,\n\t);\n\tinvalidateRedirectCache();\n}\n\n/** Whether a row other than `contentId` still holds `slug` in this collection. */\nasync function slugStillTaken(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tslug: string,\n\tcontentId: string,\n): Promise<boolean> {\n\tvalidateIdentifier(collection, \"collection slug\");\n\tconst result = await sql<{ id: string }>`\n\t\tSELECT id FROM ${sql.ref(`ec_${collection}`)}\n\t\tWHERE slug = ${slug}\n\t\tAND id != ${contentId}\n\t\tAND deleted_at IS NULL\n\t\tLIMIT 1\n\t`.execute(db);\n\treturn result.rows.length > 0;\n}\n\n/** Matches a date-only `YYYY-MM-DD` bound (no time component). */\nconst DATE_ONLY_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/**\n * Normalize a date-range bound to an ISO datetime for lexicographic comparison\n * against stored ISO 8601 timestamps. A bare `YYYY-MM-DD` is widened to the\n * appropriate UTC day boundary so the range stays inclusive: a `start` bound\n * becomes the start of the day and an `end` bound the end of the day.\n * Otherwise a date-only upper bound would exclude every same-day row (since\n * `2024-06-01T12:00:00Z` sorts after `2024-06-01`). Full datetimes pass\n * through unchanged.\n */\nfunction normalizeDateBound(value: string | undefined, edge: \"start\" | \"end\"): string | undefined {\n\tif (!value) return undefined;\n\tif (!DATE_ONLY_RE.test(value)) return new Date(value).toISOString();\n\treturn edge === \"start\" ? `${value}T00:00:00.000Z` : `${value}T23:59:59.999Z`;\n}\n\n/**\n * Build the repository's byline filter from the wire params.\n *\n * `locale` is the locale the list is scoped to, which is the locale an\n * inferred credit has to resolve at — the admin list is always scoped to the\n * locale picked in its switcher.\n */\nfunction resolveBylineFilter(\n\tparams: { bylines?: string[]; bylinesNone?: boolean; includeInferredBylines?: boolean },\n\tlocale: string | undefined,\n): ContentBylineFilter | undefined {\n\tconst includeInferred = params.includeInferredBylines === true;\n\n\tif (params.bylinesNone) return { mode: \"none\", includeInferred, locale };\n\n\tconst bylineIds = params.bylines ?? [];\n\tif (bylineIds.length === 0) return undefined;\n\n\treturn { mode: \"any\", bylineIds, includeInferred, locale };\n}\n\n/**\n * Create content list handler\n */\nexport async function handleContentList(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tparams: {\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t\tstatus?: string;\n\t\torderBy?: string;\n\t\torder?: \"asc\" | \"desc\";\n\t\tlocale?: string;\n\t\tq?: string;\n\t\tauthorId?: string;\n\t\tdateField?: ContentDateField;\n\t\tdateFrom?: string;\n\t\tdateTo?: string;\n\t\tbylines?: string[];\n\t\tbylinesNone?: boolean;\n\t\tincludeInferredBylines?: boolean;\n\t\tfieldFilters?: ContentFieldFilters;\n\t},\n): Promise<ApiResult<ContentListResponse>> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst where: FindManyOptions[\"where\"] = {};\n\t\tif (params.status) where.status = params.status;\n\t\tconst locale = params.locale ? resolveConfiguredLocale(params.locale) : undefined;\n\t\tif (locale) where.locale = locale;\n\t\tif (params.authorId) where.authorId = params.authorId;\n\t\tif (params.fieldFilters && Object.keys(params.fieldFilters).length > 0) {\n\t\t\twhere.fieldFilters = params.fieldFilters;\n\t\t}\n\n\t\tconst bylineFilter = resolveBylineFilter(params, locale);\n\t\tif (bylineFilter) where.bylineFilter = bylineFilter;\n\n\t\t// A date range requires a target column; ignore stray from/to without\n\t\t// a field so a half-specified filter doesn't silently drop all rows.\n\t\tif (params.dateField && (params.dateFrom || params.dateTo)) {\n\t\t\twhere.dateFilter = {\n\t\t\t\tfield: params.dateField,\n\t\t\t\tfrom: normalizeDateBound(params.dateFrom, \"start\"),\n\t\t\t\tto: normalizeDateBound(params.dateTo, \"end\"),\n\t\t\t};\n\t\t}\n\n\t\tconst q = params.q?.trim();\n\t\tif (q) {\n\t\t\twhere.q = q;\n\t\t\twhere.searchColumns = await resolveSearchColumns(db, collection);\n\t\t\twhere.useFts = await canUseFtsForListFilter(db, collection, where.searchColumns);\n\t\t}\n\n\t\t// Sorting by a non-system field (a collection's titleField/dateField)\n\t\t// needs the collection's *actual* sort fields resolved server-side,\n\t\t// so the orderBy set stays closed. Only query when it's not a system field.\n\t\tlet sortableExtras: string[] | undefined;\n\t\tif (params.orderBy && !isSystemOrderField(params.orderBy)) {\n\t\t\tconst coll = await db\n\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t.select([\"title_field\", \"date_field\"])\n\t\t\t\t.where(\"slug\", \"=\", collection)\n\t\t\t\t.executeTakeFirst();\n\t\t\tsortableExtras = [coll?.title_field, coll?.date_field].filter(\n\t\t\t\t(slug): slug is string => !!slug,\n\t\t\t);\n\t\t}\n\n\t\tconst result = await repo.findMany(collection, {\n\t\t\tcursor: params.cursor,\n\t\t\tlimit: params.limit || 50,\n\t\t\twhere: Object.keys(where).length > 0 ? where : undefined,\n\t\t\torderBy: params.orderBy\n\t\t\t\t? { field: params.orderBy, direction: params.order || \"desc\" }\n\t\t\t\t: undefined,\n\t\t\tsortableExtras,\n\t\t});\n\n\t\t// Hydrate SEO data if the collection has SEO enabled\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\t\tawait hydrateSeoMany(db, collection, result.items, hasSeo);\n\t\tawait hydrateBylinesMany(db, collection, result.items);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\titems: result.items,\n\t\t\t\tnextCursor: result.nextCursor,\n\t\t\t\ttotal: result.total,\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof InvalidCursorError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"INVALID_CURSOR\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tif (error instanceof ContentCollectionNotFoundError || isMissingTableError(error)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"COLLECTION_NOT_FOUND\",\n\t\t\t\t\tmessage: `Collection '${collection}' not found`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (isMissingColumnError(error, \"deleted_at\")) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"COLLECTION_SCHEMA_MISMATCH\",\n\t\t\t\t\tmessage: `Collection '${collection}' backing table is missing the 'deleted_at' column`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashValidationError) {\n\t\t\t// e.g. invalid orderBy field\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"VALIDATION_ERROR\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content list error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_LIST_ERROR\",\n\t\t\t\tmessage: \"Failed to list content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/** A content author option for the admin author filter. */\nexport interface ContentAuthor {\n\tid: string;\n\tname: string | null;\n\temail: string;\n\tavatarUrl: string | null;\n}\n\n/**\n * List the distinct authors of a collection's live content.\n *\n * Backs the admin content-list author filter. Unlike `/admin/users` (ADMIN\n * only), this is gated on `content:read`, so any editor can filter by author.\n * Returns only users who have authored at least one non-trashed entry, sorted\n * by display name then email for a stable dropdown order.\n */\nexport async function handleContentAuthors(\n\tdb: Kysely<Database>,\n\tcollection: string,\n): Promise<ApiResult<{ items: ContentAuthor[] }>> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst authorIds = await repo.findDistinctAuthorIds(collection);\n\t\tif (authorIds.length === 0) {\n\t\t\treturn { success: true, data: { items: [] } };\n\t\t}\n\n\t\tconst userRepo = new UserRepository(db);\n\t\tconst users = await userRepo.findByIds(authorIds);\n\n\t\tconst items: ContentAuthor[] = users\n\t\t\t.map((u) => ({ id: u.id, name: u.name, email: u.email, avatarUrl: u.avatarUrl }))\n\t\t\t.toSorted((a, b) => (a.name ?? a.email).localeCompare(b.name ?? b.email));\n\n\t\treturn { success: true, data: { items } };\n\t} catch (error) {\n\t\tif (isMissingTableError(error)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"COLLECTION_NOT_FOUND\",\n\t\t\t\t\tmessage: `Collection '${collection}' not found`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content authors error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_AUTHORS_ERROR\",\n\t\t\t\tmessage: \"Failed to list content authors\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Get single content item\n */\nexport async function handleContentGet(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\tlocale?: string,\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst item = await repo.findByIdOrSlug(\n\t\t\tcollection,\n\t\t\tid,\n\t\t\tlocale ? resolveConfiguredLocale(locale) : undefined,\n\t\t);\n\n\t\tif (!item) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Content item not found: ${id}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Hydrate SEO data if the collection has SEO enabled\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\t\tawait hydrateSeo(db, collection, item, hasSeo);\n\t\tawait hydrateBylines(db, collection, item);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"Content get error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_GET_ERROR\",\n\t\t\t\tmessage: \"Failed to get content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Get a content item by id, including trashed items.\n * Used by restore endpoint for ownership checks on soft-deleted items.\n */\nexport async function handleContentGetIncludingTrashed(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\tlocale?: string,\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst item = await repo.findByIdOrSlugIncludingTrashed(\n\t\t\tcollection,\n\t\t\tid,\n\t\t\tlocale ? resolveConfiguredLocale(locale) : undefined,\n\t\t);\n\n\t\tif (!item) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Content item not found: ${id}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Hydrate SEO data if the collection has SEO enabled\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\t\tawait hydrateSeo(db, collection, item, hasSeo);\n\t\tawait hydrateBylines(db, collection, item);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"Content get error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_GET_ERROR\",\n\t\t\t\tmessage: \"Failed to get content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Create content item.\n *\n * Content + SEO writes are wrapped in a transaction so either both succeed\n * or neither does. If `body.seo` is provided for a non-SEO collection, the\n * API returns a validation error rather than silently dropping it.\n */\nexport async function handleContentCreate(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tbody: {\n\t\tdata: Record<string, unknown>;\n\t\tslug?: string | null;\n\t\tstatus?: string;\n\t\tauthorId?: string;\n\t\tbylines?: ContentBylineInput[];\n\t\tlocale?: string;\n\t\ttranslationOf?: string;\n\t\tseo?: ContentSeoInput;\n\t\ttaxonomies?: Record<string, string[]>;\n\t\tcreatedAt?: string | null;\n\t\tpublishedAt?: string | null;\n\t},\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\n\t\t// Reject SEO input for non-SEO collections\n\t\tif (body.seo && !hasSeo) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\t\t\tmessage: `Collection \"${collection}\" does not have SEO enabled. Remove the seo field or enable SEO on this collection.`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst mimeCheck = await validateMediaFields(db, collection, body.data);\n\t\tif (!mimeCheck.success) return mimeCheck;\n\n\t\t// Wrap content + SEO writes in a transaction for atomicity\n\t\tconst item = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst bylineRepo = new BylineRepository(trx);\n\t\t\tconst inheritedFields = body.translationOf\n\t\t\t\t? (\n\t\t\t\t\t\tawait trx\n\t\t\t\t\t\t\t.selectFrom(\"_emdash_fields as field\")\n\t\t\t\t\t\t\t.innerJoin(\n\t\t\t\t\t\t\t\t\"_emdash_collections as collection\",\n\t\t\t\t\t\t\t\t\"collection.id\",\n\t\t\t\t\t\t\t\t\"field.collection_id\",\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.select(\"field.slug\")\n\t\t\t\t\t\t\t.where(\"collection.slug\", \"=\", collection)\n\t\t\t\t\t\t\t.where(\"field.translatable\", \"=\", 0)\n\t\t\t\t\t\t\t.execute()\n\t\t\t\t\t).map((field) => field.slug)\n\t\t\t\t: [];\n\n\t\t\t// Default to the configured site locale rather than the repo's\n\t\t\t// hard-coded \"en\" — otherwise non-English default-locale sites\n\t\t\t// silently create entries in a locale the editor never chose.\n\t\t\tconst effectiveLocale = body.locale\n\t\t\t\t? resolveConfiguredLocale(body.locale)\n\t\t\t\t: getI18nConfig()?.defaultLocale;\n\n\t\t\tlet slug: string | null | undefined = body.slug;\n\t\t\tif (!slug) {\n\t\t\t\tconst slugSource = getSlugSource(body.data);\n\t\t\t\tif (slugSource) {\n\t\t\t\t\tslug = await repo.generateUniqueSlug(collection, slugSource, effectiveLocale);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (body.status === \"published\") {\n\t\t\t\tconst publishConfig = await getCollectionPublishConfig(trx, collection);\n\t\t\t\trequireRoutablePublishSlug(publishConfig.routable, slug);\n\t\t\t}\n\n\t\t\tconst created = await repo.create({\n\t\t\t\ttype: collection,\n\t\t\t\tslug,\n\t\t\t\tdata: body.data,\n\t\t\t\tstatus: body.status || \"draft\",\n\t\t\t\tauthorId: body.authorId,\n\t\t\t\tlocale: effectiveLocale,\n\t\t\t\ttranslationOf: body.translationOf,\n\t\t\t\tinheritFields: inheritedFields,\n\t\t\t\tcreatedAt: body.createdAt,\n\t\t\t\tpublishedAt: body.publishedAt,\n\t\t\t});\n\n\t\t\tif (body.bylines !== undefined) {\n\t\t\t\tconst credits = await bylineRepo.setContentBylines(collection, created.id, body.bylines);\n\t\t\t\t// `setContentBylines` translates wire row ids to their\n\t\t\t\t// `translation_group` before writing. The response-shape\n\t\t\t\t// `primaryBylineId` must match what's now in the DB, so read\n\t\t\t\t// it from the returned credit (whose `byline` came from a\n\t\t\t\t// hydration round-trip).\n\t\t\t\tcreated.primaryBylineId = credits[0]?.byline.translationGroup ?? null;\n\t\t\t}\n\n\t\t\t// Taxonomy assignments already belong to the content translation\n\t\t\t// group. Byline credits remain per content row and need copying.\n\t\t\t// Explicit `body.bylines` wins — `copyContentBylines` no-ops\n\t\t\t// when the target already has credits, but the cleaner guard\n\t\t\t// is to skip the call entirely.\n\t\t\tif (body.translationOf) {\n\t\t\t\tif (body.bylines === undefined) {\n\t\t\t\t\tawait bylineRepo.copyContentBylines(collection, body.translationOf, created.id);\n\t\t\t\t\t// `copyContentBylines` writes the source's primary\n\t\t\t\t\t// pointer onto the new row; reflect it in-memory so the\n\t\t\t\t\t// response includes it before hydrateBylines runs.\n\t\t\t\t\tconst source = await repo.findById(collection, body.translationOf);\n\t\t\t\t\tif (source) created.primaryBylineId = source.primaryBylineId;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait hydrateBylines(trx, collection, created);\n\n\t\t\t// Side-write SEO data if provided\n\t\t\tif (body.seo && hasSeo) {\n\t\t\t\tconst seoRepo = new SeoRepository(trx);\n\t\t\t\tcreated.seo = await seoRepo.upsert(collection, created.id, body.seo);\n\t\t\t} else if (hasSeo) {\n\t\t\t\t// Assign defaults in-memory — no DB round-trip needed\n\t\t\t\tcreated.seo = { ...SEO_DEFAULTS };\n\t\t\t}\n\n\t\t\t// Attach taxonomy terms in the same transaction. The MCP tool\n\t\t\t// (and the REST create body) previously accepted a `taxonomies`\n\t\t\t// field on `content_create` without doing anything with it, so\n\t\t\t// agents publishing a categorized/tagged entry had to make N\n\t\t\t// follow-up REST calls per taxonomy. This resolves each slug in\n\t\t\t// the entry's locale and pipes it through the same\n\t\t\t// `setTermsForEntry` path the `.../terms/{taxonomy}` REST route\n\t\t\t// uses, so the two entry points can't drift.\n\t\t\tif (body.taxonomies) {\n\t\t\t\tawait assignTaxonomies(trx, collection, created.id, effectiveLocale, body.taxonomies);\n\t\t\t}\n\n\t\t\treturn created;\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tif (isMissingTableError(error)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"COLLECTION_NOT_FOUND\",\n\t\t\t\t\tmessage: `Collection '${collection}' not found`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashValidationError) {\n\t\t\tif (error.message === \"Translation source content not found\") {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: { code: \"NOT_FOUND\", message: error.message },\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"VALIDATION_ERROR\", message: error.message },\n\t\t\t};\n\t\t}\n\t\t// SQLite UNIQUE constraint OR Postgres unique_violation — slug\n\t\t// collisions and any other unique violations land here. Match\n\t\t// specifically on \"unique constraint failed\" / \"duplicate key\" so we\n\t\t// don't false-positive on NOT NULL or CHECK violations whose\n\t\t// messages also contain \"constraint failed\".\n\t\tconst message = error instanceof Error ? error.message.toLowerCase() : \"\";\n\t\tif (message.includes(\"unique constraint failed\") || message.includes(\"duplicate key\")) {\n\t\t\tif (\n\t\t\t\tmessage.includes(\"active_tg_locale\") ||\n\t\t\t\t(message.includes(\"translation_group\") && message.includes(\"locale\"))\n\t\t\t) {\n\t\t\t\tconst locale = body.locale ?? getI18nConfig()?.defaultLocale ?? \"en\";\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"CONFLICT\",\n\t\t\t\t\t\tmessage: `Translation already exists in locale \"${locale}\" for this content item`,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\t// Detect slug-specific collisions by message fingerprint\n\t\t\tif (message.includes(\"slug\")) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"SLUG_CONFLICT\",\n\t\t\t\t\t\tmessage: `Slug '${body.slug ?? \"(auto-generated)\"}' already exists in collection '${collection}'`,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CONFLICT\",\n\t\t\t\t\tmessage: \"Unique constraint violation\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content create error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_CREATE_ERROR\",\n\t\t\t\tmessage: \"Failed to create content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Update content item.\n * If `_rev` is provided, validates it against the current version before writing.\n * No `_rev` = blind write (backwards-compatible for admin UI).\n *\n * Content + SEO writes are wrapped in a transaction for atomicity.\n */\nexport async function handleContentUpdate(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\tbody: {\n\t\tdata?: Record<string, unknown>;\n\t\tslug?: string | null;\n\t\tstatus?: string;\n\t\tauthorId?: string | null;\n\t\tbylines?: ContentBylineInput[];\n\t\tlocale?: string;\n\t\t_rev?: string;\n\t\tseo?: ContentSeoInput;\n\t\ttaxonomies?: Record<string, string[]>;\n\t\tpublishedAt?: string | null;\n\t},\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\n\t\t// Reject SEO input for non-SEO collections\n\t\tif (body.seo && !hasSeo) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\t\t\tmessage: `Collection \"${collection}\" does not have SEO enabled. Remove the seo field or enable SEO on this collection.`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (body.data) {\n\t\t\tconst mimeCheck = await validateMediaFields(db, collection, body.data);\n\t\t\tif (!mimeCheck.success) return mimeCheck;\n\t\t}\n\n\t\tconst repo = new ContentRepository(db);\n\n\t\t// Resolve slug → ID if needed\n\t\tconst resolvedId = (await resolveId(repo, collection, id, body.locale)) ?? id;\n\n\t\t// Wrap content + SEO writes in a transaction for atomicity.\n\t\t// The _rev check is inside the transaction so the read-then-write\n\t\t// is atomic -- no concurrent write can slip between the check and update.\n\t\tconst item = await withTransaction(db, async (trx) => {\n\t\t\tconst trxRepo = new ContentRepository(trx);\n\t\t\tconst bylineRepo = new BylineRepository(trx);\n\n\t\t\t// Read existing item once for both _rev check and old slug capture\n\t\t\tconst existing =\n\t\t\t\tbody._rev || body.slug !== undefined || body.status === \"published\"\n\t\t\t\t\t? await trxRepo.findById(collection, resolvedId)\n\t\t\t\t\t: null;\n\n\t\t\t// Validate _rev if provided (optimistic concurrency)\n\t\t\tif (body._rev) {\n\t\t\t\tif (!existing) {\n\t\t\t\t\tthrow Object.assign(new Error(`Content item not found: ${id}`), {\n\t\t\t\t\t\tapiError: { code: \"NOT_FOUND\" as const },\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst revCheck = validateRev(body._rev, existing);\n\t\t\t\tif (!revCheck.valid) {\n\t\t\t\t\tthrow Object.assign(new Error(revCheck.message), {\n\t\t\t\t\t\tapiError: { code: \"CONFLICT\" as const },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Capture old slug before update for auto-redirect\n\t\t\tlet oldSlug: string | undefined;\n\t\t\tif (body.slug && existing?.slug && existing.slug !== body.slug) {\n\t\t\t\toldSlug = existing.slug;\n\t\t\t}\n\n\t\t\tconst resultingStatus = body.status ?? existing?.status;\n\t\t\tif (resultingStatus === \"published\") {\n\t\t\t\tif (!existing) {\n\t\t\t\t\tthrow Object.assign(new Error(`Content item not found: ${id}`), {\n\t\t\t\t\t\tapiError: { code: \"NOT_FOUND\" as const },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst publishConfig = await getCollectionPublishConfig(trx, collection);\n\t\t\t\tconst intendedSlug = body.slug !== undefined ? body.slug : existing.slug;\n\t\t\t\trequireRoutablePublishSlug(publishConfig.routable, intendedSlug);\n\t\t\t}\n\n\t\t\tconst updated = await trxRepo.update(collection, resolvedId, {\n\t\t\t\tdata: body.data,\n\t\t\t\tslug: body.slug,\n\t\t\t\tstatus: body.status,\n\t\t\t\tauthorId: body.authorId,\n\t\t\t\tpublishedAt: body.publishedAt,\n\t\t\t});\n\n\t\t\tif (body.bylines !== undefined) {\n\t\t\t\tconst credits = await bylineRepo.setContentBylines(collection, resolvedId, body.bylines);\n\t\t\t\t// `setContentBylines` translates wire row ids to their\n\t\t\t\t// `translation_group` before writing. Read the in-memory\n\t\t\t\t// pointer from the persisted credit so the response shape\n\t\t\t\t// matches the DB. See the matching block in handleContentCreate.\n\t\t\t\tupdated.primaryBylineId = credits[0]?.byline.translationGroup ?? null;\n\t\t\t}\n\n\t\t\t// Create auto-redirect when slug changes. Date tokens in the URL\n\t\t\t// pattern resolve from the publish date, so the old URL uses the\n\t\t\t// pre-update date (the URL that was actually live) and the new URL\n\t\t\t// the post-update one.\n\t\t\tif (oldSlug && body.slug) {\n\t\t\t\tawait createSlugChangeRedirect(\n\t\t\t\t\ttrx,\n\t\t\t\t\tcollection,\n\t\t\t\t\toldSlug,\n\t\t\t\t\tbody.slug,\n\t\t\t\t\tresolvedId,\n\t\t\t\t\texisting?.publishedAt ?? null,\n\t\t\t\t\tupdated.publishedAt ?? null,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Sync non-translatable fields to sibling locales in the same\n\t\t\t// translation group. Only runs when i18n is enabled, data was updated,\n\t\t\t// and the item belongs to a translation group with siblings.\n\t\t\tif (isI18nEnabled() && body.data && updated.translationGroup) {\n\t\t\t\tawait trxRepo.syncNonTranslatableFields(\n\t\t\t\t\tcollection,\n\t\t\t\t\tupdated.id,\n\t\t\t\t\tupdated.translationGroup,\n\t\t\t\t\tbody.data,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Side-write SEO data if provided, always hydrate for SEO-enabled collections\n\t\t\tif (body.seo && hasSeo) {\n\t\t\t\tconst seoRepo = new SeoRepository(trx);\n\t\t\t\tupdated.seo = await seoRepo.upsert(collection, resolvedId, body.seo);\n\t\t\t} else if (hasSeo) {\n\t\t\t\tconst seoRepo = new SeoRepository(trx);\n\t\t\t\tupdated.seo = await seoRepo.get(collection, resolvedId);\n\t\t\t}\n\n\t\t\tawait hydrateBylines(trx, collection, updated);\n\n\t\t\t// Replace taxonomy assignments in the same transaction. Uses the\n\t\t\t// entry's own locale (post-update) to resolve slugs so an update\n\t\t\t// that also changes locale still lands on the correct term\n\t\t\t// variants. See handleContentCreate for rationale.\n\t\t\tif (body.taxonomies) {\n\t\t\t\tawait assignTaxonomies(\n\t\t\t\t\ttrx,\n\t\t\t\t\tcollection,\n\t\t\t\t\tresolvedId,\n\t\t\t\t\tupdated.locale ?? body.locale,\n\t\t\t\t\tbody.taxonomies,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn updated;\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\t// Handle structured errors thrown from inside the transaction\n\t\t// (rev check failures, not-found)\n\t\tif (hasApiError(error)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: error.apiError.code, message: error.message },\n\t\t\t};\n\t\t}\n\t\tif (isMissingTableError(error)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"COLLECTION_NOT_FOUND\",\n\t\t\t\t\tmessage: `Collection '${collection}' not found`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"VALIDATION_ERROR\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tconst message = error instanceof Error ? error.message.toLowerCase() : \"\";\n\t\tif (message.includes(\"unique constraint failed\") || message.includes(\"duplicate key\")) {\n\t\t\tif (message.includes(\"slug\")) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"SLUG_CONFLICT\",\n\t\t\t\t\t\tmessage: `Slug '${body.slug ?? id}' already exists in collection '${collection}'`,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CONFLICT\",\n\t\t\t\t\tmessage: \"Unique constraint violation\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content update error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_UPDATE_ERROR\",\n\t\t\t\tmessage: \"Failed to update content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Duplicate content item.\n *\n * Only copies SEO data if the collection has SEO enabled.\n * Always returns consistent `seo` shape for SEO-enabled collections.\n */\nexport async function handleContentDuplicate(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\tauthorId?: string,\n): Promise<ApiResult<{ item: ContentItem }>> {\n\ttry {\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\n\t\t// Wrap duplicate + SEO copy in a transaction for atomicity\n\t\tconst duplicate = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst bylineRepo = new BylineRepository(trx);\n\t\t\tconst resolvedId = (await resolveId(repo, collection, id)) ?? id;\n\t\t\tconst dup = await repo.duplicate(collection, resolvedId, authorId);\n\n\t\t\tconst existingBylines = await bylineRepo.getContentBylines(collection, resolvedId);\n\t\t\tif (existingBylines.length > 0) {\n\t\t\t\tawait bylineRepo.setContentBylines(\n\t\t\t\t\tcollection,\n\t\t\t\t\tdup.id,\n\t\t\t\t\texistingBylines.map((entry) => ({\n\t\t\t\t\t\tbylineId: entry.byline.id,\n\t\t\t\t\t\troleLabel: entry.roleLabel,\n\t\t\t\t\t})),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (hasSeo) {\n\t\t\t\t// Copy SEO data from the original (clears canonical)\n\t\t\t\tconst seoRepo = new SeoRepository(trx);\n\t\t\t\tawait seoRepo.copyForDuplicate(collection, resolvedId, dup.id);\n\t\t\t\t// Always hydrate SEO for consistent response shape\n\t\t\t\tdup.seo = await seoRepo.get(collection, dup.id);\n\t\t\t}\n\n\t\t\tawait hydrateBylines(trx, collection, dup);\n\n\t\t\treturn dup;\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item: duplicate },\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof EmDashValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: err.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content duplicate error:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_DUPLICATE_ERROR\",\n\t\t\t\tmessage: \"Failed to duplicate content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Delete content item (soft delete - moves to trash)\n */\nexport async function handleContentDelete(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n): Promise<ApiResult<{ deleted: true; id: string }>> {\n\ttry {\n\t\tconst result = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst resolvedId = (await resolveId(repo, collection, id)) ?? id;\n\t\t\tconst deleted = await repo.delete(collection, resolvedId);\n\t\t\tif (deleted) {\n\t\t\t\tawait new EntryLockRepository(trx).releaseEntry(collection, resolvedId);\n\t\t\t}\n\t\t\treturn { id: resolvedId, deleted };\n\t\t});\n\n\t\tif (!result.deleted) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Content item not found: ${id}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { deleted: true, id: result.id },\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"Content delete error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_DELETE_ERROR\",\n\t\t\t\tmessage: \"Failed to delete content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Restore content item from trash\n */\nexport async function handleContentRestore(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\toptions: { _rev?: string } = {},\n): Promise<ApiResult<{ restored: true; item: ContentItem; _rev: string }>> {\n\ttry {\n\t\tconst expectedRevision = decodeRevisionPrecondition(options._rev);\n\t\tconst item = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst resolvedId = (await resolveIdIncludingTrashed(repo, collection, id)) ?? id;\n\t\t\treturn repo.restore(collection, resolvedId, expectedRevision);\n\t\t});\n\n\t\tif (!item) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Trashed content item not found: ${id}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { restored: true, item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof ContentMutationConflictError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"CONFLICT\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tif (isTranslationLocaleConflict(error, collection)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CONFLICT\",\n\t\t\t\t\tmessage: \"An active translation already exists in this locale\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content restore error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_RESTORE_ERROR\",\n\t\t\t\tmessage: \"Failed to restore content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Permanently delete content item (cannot be undone).\n * Also cleans up associated SEO data.\n */\nexport async function handleContentPermanentDelete(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n): Promise<ApiResult<{ deleted: true; id: string }>> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst resolvedId = (await resolveIdIncludingTrashed(repo, collection, id)) ?? id;\n\n\t\t// Wrap content delete + SEO/comment cleanup in a transaction\n\t\tconst deleted = await withTransaction(db, async (trx) => {\n\t\t\tconst trxRepo = new ContentRepository(trx);\n\t\t\tconst item = await trxRepo.findByIdIncludingTrashed(collection, resolvedId);\n\t\t\tconst wasDeleted = await trxRepo.permanentDelete(collection, resolvedId);\n\n\t\t\tif (wasDeleted) {\n\t\t\t\t// Clean up SEO data for permanently deleted content\n\t\t\t\tconst seoRepo = new SeoRepository(trx);\n\t\t\t\tawait seoRepo.delete(collection, resolvedId);\n\t\t\t\t// Clean up comments for permanently deleted content\n\t\t\t\tconst commentRepo = new CommentRepository(trx);\n\t\t\t\tawait commentRepo.deleteByContent(collection, resolvedId);\n\t\t\t\t// Clean up revisions for permanently deleted content\n\t\t\t\tconst revisionRepo = new RevisionRepository(trx);\n\t\t\t\tawait revisionRepo.deleteByEntry(collection, resolvedId);\n\t\t\t\tawait new EntryLockRepository(trx).releaseEntry(collection, resolvedId);\n\t\t\t\tawait new BylineRepository(trx).deleteContentBylines(collection, resolvedId);\n\t\t\t\t// Term assignments are keyed by translation_group, so they belong to the\n\t\t\t\t// group rather than to this row. They go only once no row of the group is\n\t\t\t\t// left, trashed ones included, since a trashed row can still be restored.\n\t\t\t\tif (item?.translationGroup) {\n\t\t\t\t\tconst groupSurvives = await trxRepo.hasTranslationsIncludingTrashed(\n\t\t\t\t\t\tcollection,\n\t\t\t\t\t\titem.translationGroup,\n\t\t\t\t\t);\n\t\t\t\t\tif (!groupSurvives) {\n\t\t\t\t\t\tawait new TaxonomyRepository(trx).clearEntryGroupTerms(\n\t\t\t\t\t\t\tcollection,\n\t\t\t\t\t\t\titem.translationGroup,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn wasDeleted;\n\t\t});\n\n\t\tif (!deleted) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Content item not found: ${id}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { deleted: true, id: resolvedId },\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"Content permanent delete error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_DELETE_ERROR\",\n\t\t\t\tmessage: \"Failed to permanently delete content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * List trashed content items\n */\nexport async function handleContentListTrashed(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\toptions: { limit?: number; cursor?: string; locale?: string } = {},\n): Promise<ApiResult<{ items: TrashedContentItem[]; nextCursor?: string }>> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst result = await repo.findTrashed(collection, {\n\t\t\tlimit: options.limit,\n\t\t\tcursor: options.cursor,\n\t\t\twhere: { locale: options.locale },\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\titems: result.items.map((item) => ({\n\t\t\t\t\tid: item.id,\n\t\t\t\t\ttype: item.type,\n\t\t\t\t\tslug: item.slug,\n\t\t\t\t\tstatus: item.status,\n\t\t\t\t\tlocale: item.locale,\n\t\t\t\t\ttranslationGroup: item.translationGroup,\n\t\t\t\t\tdata: item.data,\n\t\t\t\t\tauthorId: item.authorId,\n\t\t\t\t\tcreatedAt: item.createdAt,\n\t\t\t\t\tupdatedAt: item.updatedAt,\n\t\t\t\t\tpublishedAt: item.publishedAt,\n\t\t\t\t\tdeletedAt: item.deletedAt,\n\t\t\t\t})),\n\t\t\t\tnextCursor: result.nextCursor,\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof InvalidCursorError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"INVALID_CURSOR\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content list trashed error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_LIST_ERROR\",\n\t\t\t\tmessage: \"Failed to list trashed content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Count trashed content items\n */\nexport async function handleContentCountTrashed(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\toptions: { locale?: string } = {},\n): Promise<ApiResult<{ count: number }>> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst count = await repo.countTrashed(collection, { locale: options.locale });\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { count },\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"Content count trashed error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_COUNT_ERROR\",\n\t\t\t\tmessage: \"Failed to count trashed content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Schedule content for future publishing\n */\nexport async function handleContentSchedule(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\tscheduledAt: string,\n\tcurrentTime: Date = new Date(),\n\t_rev?: string,\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst expectedRevision = decodeRevisionPrecondition(_rev);\n\t\tconst item = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst existing = await repo.findByIdOrSlug(collection, id);\n\t\t\tconst resolvedId = existing?.id ?? id;\n\t\t\tif (existing) {\n\t\t\t\tconst publishConfig = await getCollectionPublishConfig(trx, collection);\n\t\t\t\trequireRoutablePublishSlug(publishConfig.routable, existing.slug);\n\t\t\t}\n\t\t\treturn repo.schedule(collection, resolvedId, scheduledAt, currentTime, expectedRevision);\n\t\t});\n\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\t\tawait hydrateSeo(db, collection, item, hasSeo);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof ContentMutationConflictError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"CONFLICT\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content schedule error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_SCHEDULE_ERROR\",\n\t\t\t\tmessage: \"Failed to schedule content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Unschedule content (revert to draft)\n */\nexport async function handleContentUnschedule(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\toptions: { _rev?: string } = {},\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst expectedRevision = decodeRevisionPrecondition(options._rev);\n\t\tconst item = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst resolvedId = (await resolveId(repo, collection, id)) ?? id;\n\t\t\treturn repo.unschedule(collection, resolvedId, expectedRevision);\n\t\t});\n\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\t\tawait hydrateSeo(db, collection, item, hasSeo);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof ContentMutationConflictError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"CONFLICT\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content unschedule error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_UNSCHEDULE_ERROR\",\n\t\t\t\tmessage: \"Failed to unschedule content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Persist a permanent policy rejection and unschedule the publication.\n * Databases with transactions commit both writes together. D1 persists the\n * reason first so a failed option write cannot silently remove the entry from\n * future sweeps; if unscheduling then fails, the due entry and its dashboard\n * notice remain available for retry and operator action.\n */\nexport async function handleScheduledPolicyRejection(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\toptions: { _rev: string; rejection: ScheduledPolicyRejection },\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst expectedRevision = decodeRevisionPrecondition(options._rev);\n\t\tconst item = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst resolvedId = (await resolveId(repo, collection, id)) ?? id;\n\t\t\tconst optionsRepo = new OptionsRepository(trx);\n\t\t\tconst rejectionKey = scheduledPolicyRejectionKey(collection, resolvedId);\n\t\t\tconst rejectionRevision = await optionsRepo.setVersioned(rejectionKey, {\n\t\t\t\t...options.rejection,\n\t\t\t\tid: resolvedId,\n\t\t\t});\n\t\t\ttry {\n\t\t\t\treturn await repo.unschedule(collection, resolvedId, expectedRevision);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ContentMutationConflictError) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait optionsRepo.compareAndDelete(rejectionKey, rejectionRevision);\n\t\t\t\t\t} catch (cleanupError) {\n\t\t\t\t\t\tconsole.error(\"Failed to clear stale scheduled policy rejection:\", cleanupError);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t});\n\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\t\tawait hydrateSeo(db, collection, item, hasSeo);\n\t\treturn { success: true, data: { item, _rev: encodeRev(item) } };\n\t} catch (error) {\n\t\tif (error instanceof ContentMutationConflictError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"CONFLICT\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"VALIDATION_ERROR\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Scheduled policy rejection error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_UNSCHEDULE_ERROR\",\n\t\t\t\tmessage: \"Failed to record scheduled publication rejection\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Publish content immediately.\n *\n * Publication is one atomic content-row statement. On databases that support\n * transactions, the slug redirect and the locale sync stay grouped with it.\n */\nexport async function handleContentPublish(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\toptions: {\n\t\tpublishedAt?: string;\n\t\trequireScheduledDue?: boolean;\n\t\texpectedScheduledAt?: string;\n\t\t_rev?: string;\n\t\tcurrentTime?: Date;\n\t} = {},\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst expectedRevision = decodeRevisionPrecondition(options._rev);\n\t\tconst item = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst resolvedId = (await resolveId(repo, collection, id)) ?? id;\n\t\t\tconst publishConfig = await getCollectionPublishConfig(trx, collection);\n\n\t\t\t// Capture the pre-publish state. For revision-supporting collections a\n\t\t\t// slug edit is staged as `_slug` in the draft revision and only lands\n\t\t\t// on the live `slug` column here, inside `repo.publish()` — it never\n\t\t\t// passes through handleContentUpdate, where slug-change auto-redirects\n\t\t\t// are normally created.\n\t\t\tconst existing = await repo.findById(collection, resolvedId);\n\n\t\t\tconst published = await repo.publish(\n\t\t\t\tcollection,\n\t\t\t\tresolvedId,\n\t\t\t\toptions.publishedAt,\n\t\t\t\toptions.requireScheduledDue,\n\t\t\t\toptions.expectedScheduledAt,\n\t\t\t\tpublishConfig.supportsRevisions,\n\t\t\t\tpublishConfig.routable,\n\t\t\t\texpectedRevision,\n\t\t\t\toptions.currentTime,\n\t\t\t);\n\n\t\t\tif (\n\t\t\t\texisting &&\n\t\t\t\tisI18nEnabled() &&\n\t\t\t\tpublishConfig.supportsRevisions &&\n\t\t\t\tpublished.translationGroup\n\t\t\t) {\n\t\t\t\tawait repo.syncNonTranslatableFields(\n\t\t\t\t\tcollection,\n\t\t\t\t\tpublished.id,\n\t\t\t\t\tpublished.translationGroup,\n\t\t\t\t\tpublished.data,\n\t\t\t\t\t{ previous: existing.data },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Leave a 301 behind when publishing changed the slug of an entry that\n\t\t\t// was already published — its old URL was live and may be indexed or\n\t\t\t// linked. A first publish is excluded: a draft's URL was never public.\n\t\t\tif (\n\t\t\t\texisting?.status === \"published\" &&\n\t\t\t\texisting.slug &&\n\t\t\t\tpublished.slug &&\n\t\t\t\texisting.slug !== published.slug\n\t\t\t) {\n\t\t\t\tawait createSlugChangeRedirect(\n\t\t\t\t\ttrx,\n\t\t\t\t\tcollection,\n\t\t\t\t\texisting.slug,\n\t\t\t\t\tpublished.slug,\n\t\t\t\t\tresolvedId,\n\t\t\t\t\texisting.publishedAt ?? null,\n\t\t\t\t\tpublished.publishedAt ?? null,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn published;\n\t\t});\n\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\t\tawait hydrateSeo(db, collection, item, hasSeo);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof ContentMutationConflictError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CONFLICT\",\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\t// The scheduled sweep gates publish on the row still being due; a row\n\t\t// unscheduled in the meantime is a silent skip, not a failure.\n\t\tif (error instanceof ScheduledNotDueError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_DUE\",\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashValidationError) {\n\t\t\t// The staged-slug pre-check tags its error so it maps to the same\n\t\t\t// 409 SLUG_CONFLICT as direct slug edits in create/update.\n\t\t\tconst details: unknown = error.details;\n\t\t\tconst isSlugConflict =\n\t\t\t\ttypeof details === \"object\" &&\n\t\t\t\tdetails !== null &&\n\t\t\t\t\"code\" in details &&\n\t\t\t\tdetails.code === \"SLUG_CONFLICT\";\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: isSlugConflict ? \"SLUG_CONFLICT\" : \"VALIDATION_ERROR\",\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\t// Backstop for the pre-check inside repo.publish(): a concurrent write\n\t\t// can still take the slug between the check and the UPDATE, in which\n\t\t// case the `(slug, locale)` unique constraint fires. Same fingerprint\n\t\t// mapping as create/update — never a raw SQLite error to the client.\n\t\tconst message = error instanceof Error ? error.message.toLowerCase() : \"\";\n\t\tif (\n\t\t\t(message.includes(\"unique constraint failed\") || message.includes(\"duplicate key\")) &&\n\t\t\tmessage.includes(\"slug\")\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"SLUG_CONFLICT\",\n\t\t\t\t\tmessage: `The staged slug is already used by another entry in collection '${collection}'`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content publish error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_PUBLISH_ERROR\",\n\t\t\t\tmessage: \"Failed to publish content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Unpublish content (revert to draft).\n *\n * Wrapped in a transaction — unpublish may create a draft revision\n * from the live version then update the status, which is multi-step.\n */\nexport async function handleContentUnpublish(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\toptions: { _rev?: string } = {},\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst expectedRevision = decodeRevisionPrecondition(options._rev);\n\t\tconst item = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst resolvedId = (await resolveId(repo, collection, id)) ?? id;\n\t\t\treturn repo.unpublish(collection, resolvedId, expectedRevision);\n\t\t});\n\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\t\tawait hydrateSeo(db, collection, item, hasSeo);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof ContentMutationConflictError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"CONFLICT\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content unpublish error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_UNPUBLISH_ERROR\",\n\t\t\t\tmessage: \"Failed to unpublish content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Count scheduled content items\n */\nexport async function handleContentCountScheduled(\n\tdb: Kysely<Database>,\n\tcollection: string,\n): Promise<ApiResult<{ count: number }>> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst count = await repo.countScheduled(collection);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { count },\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"Content count scheduled error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_COUNT_ERROR\",\n\t\t\t\tmessage: \"Failed to count scheduled content\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Discard draft changes (revert to live version)\n */\nexport async function handleContentDiscardDraft(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n\toptions: { _rev?: string } = {},\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst expectedRevision = decodeRevisionPrecondition(options._rev);\n\t\tconst item = await withTransaction(db, async (trx) => {\n\t\t\tconst repo = new ContentRepository(trx);\n\t\t\tconst resolvedId = (await resolveId(repo, collection, id)) ?? id;\n\t\t\treturn repo.discardDraft(collection, resolvedId, expectedRevision);\n\t\t});\n\n\t\tconst hasSeo = await collectionHasSeo(db, collection);\n\t\tawait hydrateSeo(db, collection, item, hasSeo);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof ContentMutationConflictError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"CONFLICT\", message: error.message },\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Content discard draft error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_DISCARD_DRAFT_ERROR\",\n\t\t\t\tmessage: \"Failed to discard draft\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Compare live and draft revisions\n */\nexport async function handleContentCompare(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n): Promise<\n\tApiResult<{\n\t\thasChanges: boolean;\n\t\tlive: Record<string, unknown> | null;\n\t\tdraft: Record<string, unknown> | null;\n\t}>\n> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst entry = await repo.findByIdOrSlug(collection, id);\n\n\t\tif (!entry) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Content item not found: ${id}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst revisionRepo = new RevisionRepository(db);\n\n\t\tconst live = entry.liveRevisionId ? await revisionRepo.findById(entry.liveRevisionId) : null;\n\t\tconst draft = entry.draftRevisionId ? await revisionRepo.findById(entry.draftRevisionId) : null;\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\thasChanges:\n\t\t\t\t\tentry.draftRevisionId !== null && entry.draftRevisionId !== entry.liveRevisionId,\n\t\t\t\tlive: live?.data ?? null,\n\t\t\t\tdraft: draft?.data ?? null,\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"Content compare error:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_COMPARE_ERROR\",\n\t\t\t\tmessage: \"Failed to compare revisions\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Get all translations for a content item.\n * Returns the item's translation group members with locale and status info.\n */\nexport async function handleContentTranslations(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tid: string,\n): Promise<\n\tApiResult<{\n\t\ttranslationGroup: string;\n\t\ttranslations: Array<{\n\t\t\tid: string;\n\t\t\tlocale: string | null;\n\t\t\tslug: string | null;\n\t\t\tstatus: string;\n\t\t\tupdatedAt: string;\n\t\t}>;\n\t}>\n> {\n\ttry {\n\t\tconst repo = new ContentRepository(db);\n\t\tconst item = await repo.findByIdOrSlug(collection, id);\n\n\t\tif (!item) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Content item not found: ${id}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (!item.translationGroup) {\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: {\n\t\t\t\t\ttranslationGroup: item.id,\n\t\t\t\t\ttranslations: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: item.id,\n\t\t\t\t\t\t\tlocale: item.locale,\n\t\t\t\t\t\t\tslug: item.slug,\n\t\t\t\t\t\t\tstatus: item.status,\n\t\t\t\t\t\t\tupdatedAt: item.updatedAt,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst translations = await repo.findTranslations(collection, item.translationGroup);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\ttranslationGroup: item.translationGroup,\n\t\t\t\ttranslations: translations.map((t) => ({\n\t\t\t\t\tid: t.id,\n\t\t\t\t\tlocale: t.locale,\n\t\t\t\t\tslug: t.slug,\n\t\t\t\t\tstatus: t.status,\n\t\t\t\t\tupdatedAt: t.updatedAt,\n\t\t\t\t})),\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof Error) {\n\t\t\tconsole.error(\"Content translations error:\", error);\n\t\t}\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"CONTENT_TRANSLATIONS_ERROR\",\n\t\t\t\tmessage: \"Failed to get translations\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Resolve a `{ taxonomyName: [slug, ...] }` map to term IDs and replace the\n * entry's assignments for each named taxonomy.\n *\n * Shared by handleContentCreate and handleContentUpdate so both MCP entry\n * points behave identically. Slug resolution is scoped to `locale`; passing\n * `undefined` lets `findBySlug` fall back to its default (lowest locale code)\n * so callers on single-locale sites don't need to know the site's default.\n *\n * Throws EmDashValidationError on unknown slug or wrong shape; the calling\n * handler translates that into a VALIDATION_ERROR response.\n */\nasync function assignTaxonomies(\n\ttrx: Kysely<Database>,\n\tcollection: string,\n\tentryId: string,\n\tlocale: string | undefined,\n\ttaxonomies: Record<string, string[]>,\n): Promise<void> {\n\tconst taxRepo = new TaxonomyRepository(trx);\n\tlet anyChange = false;\n\n\tfor (const [taxonomyName, slugs] of Object.entries(taxonomies)) {\n\t\tif (!Array.isArray(slugs)) {\n\t\t\tthrow new EmDashValidationError(`taxonomies.${taxonomyName} must be an array of term slugs`);\n\t\t}\n\n\t\tconst termIds: string[] = [];\n\t\tfor (const slug of slugs) {\n\t\t\tif (typeof slug !== \"string\" || slug.length === 0) {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`taxonomies.${taxonomyName} contains a non-string or empty slug`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst term = await taxRepo.findBySlug(taxonomyName, slug, locale);\n\t\t\tif (!term) {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Unknown taxonomy term: ${taxonomyName}='${slug}'${\n\t\t\t\t\t\tlocale ? ` (locale '${locale}')` : \"\"\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\ttermIds.push(term.id);\n\t\t}\n\n\t\tawait taxRepo.setTermsForEntry(collection, entryId, taxonomyName, termIds);\n\t\tanyChange = true;\n\t}\n\n\t// Match the REST route's behaviour: taxonomy term assignments changed,\n\t// so invalidate the taxonomy object cache used during hydration.\n\tif (anyChange) invalidateTermCache();\n}\n","/**\n * Manifest generation handlers\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../../database/types.js\";\nimport { expandCollectionBlockFields } from \"../../schema/block-values.js\";\nimport { SchemaRegistry } from \"../../schema/registry.js\";\nimport { MAX_COLLECTION_LIST_COLUMNS } from \"../../schema/types.js\";\nimport type { Field, FieldType } from \"../../schema/types.js\";\nimport { hashString } from \"../../utils/hash.js\";\nimport type {\n\tFieldDescriptor,\n\tManifestCollectionMap,\n\tManifestFieldDescriptor,\n\tManifestResponse,\n} from \"../types.js\";\n\n/** Pattern to add spaces before capital letters */\nconst CAMEL_CASE_PATTERN = /([A-Z])/g;\nconst FIRST_CHAR_PATTERN = /^./;\n\n/**\n * Map schema field types to editor field kinds.\n */\n/** Field types that can be surfaced as list columns in the admin. */\nconst LIST_COLUMN_FIELD_TYPES: ReadonlySet<FieldType> = new Set([\n\t\"string\",\n\t\"number\",\n\t\"integer\",\n\t\"boolean\",\n\t\"datetime\",\n\t\"select\",\n\t\"multiSelect\",\n]);\n\nconst FIELD_TYPE_TO_KIND: Record<FieldType, string> = {\n\tstring: \"string\",\n\tslug: \"string\",\n\turl: \"url\",\n\ttext: \"richText\",\n\tnumber: \"number\",\n\tinteger: \"number\",\n\tboolean: \"boolean\",\n\tdatetime: \"datetime\",\n\tselect: \"select\",\n\tmultiSelect: \"multiSelect\",\n\tportableText: \"portableText\",\n\timage: \"image\",\n\tfile: \"file\",\n\treference: \"reference\",\n\tjson: \"json\",\n\trepeater: \"repeater\",\n\tblocks: \"blocks\",\n};\n\n// Collection definition shape for manifest generation\ninterface CollectionDefinition {\n\tschema: {\n\t\t_def?: { shape?: () => Record<string, unknown> };\n\t\tshape?: Record<string, unknown>;\n\t};\n\tadmin: {\n\t\tlabel: string;\n\t\tlabelSingular?: string;\n\t\tsupports?: string[];\n\t\troutable?: boolean;\n\t};\n}\ntype CollectionMap = Record<string, CollectionDefinition>;\n\ninterface GenerateManifestOptions {\n\tdb?: Kysely<Database> | null;\n}\n\n/**\n * Generate admin manifest from collections\n */\nexport async function generateManifest(\n\tcollections: CollectionMap,\n\tplugins: Record<\n\t\tstring,\n\t\t{\n\t\t\tadminPages?: Array<{ path: string; component: string }>;\n\t\t\twidgets?: string[];\n\t\t}\n\t> = {},\n\toptions: GenerateManifestOptions = {},\n): Promise<ManifestResponse> {\n\tconst manifestCollections = await buildManifestCollections(collections, options.db);\n\n\t// Generate hash from collections (for cache invalidation)\n\tconst hash = await hashString(JSON.stringify(manifestCollections));\n\n\treturn {\n\t\tversion: \"0.1.0\",\n\t\thash,\n\t\tcollections: manifestCollections,\n\t\tplugins,\n\t};\n}\n\n/**\n * Build collection descriptors from build-time config plus live database rows.\n *\n * Config collections are added first and win on slug conflicts. Runtime/manual\n * collections have no Zod schema to inspect, so their field descriptors are\n * synthesized from `_emdash_fields`.\n */\nexport async function buildManifestCollections(\n\tcollections: CollectionMap,\n\tdb?: Kysely<Database> | null,\n): Promise<ManifestCollectionMap> {\n\tconst manifestCollections: ManifestCollectionMap = {};\n\n\tfor (const [name, definition] of Object.entries(collections)) {\n\t\t// Extract field descriptors from Zod schema\n\t\tconst fields = extractFieldDescriptors(definition.schema);\n\n\t\tmanifestCollections[name] = {\n\t\t\tlabel: definition.admin.label,\n\t\t\tlabelSingular: definition.admin.labelSingular || definition.admin.label,\n\t\t\tsupports: definition.admin.supports || [],\n\t\t\thasSeo: (definition.admin.supports || []).includes(\"seo\"),\n\t\t\troutable: definition.admin.routable ?? true,\n\t\t\tfields,\n\t\t};\n\t}\n\n\tif (!db) return manifestCollections;\n\n\ttry {\n\t\tconst registry = new SchemaRegistry(db);\n\t\tconst storedCollections = await registry.listCollectionsWithFields();\n\t\tconst dbCollections = await Promise.all(\n\t\t\tstoredCollections.map((collection) => expandCollectionBlockFields(db, collection)),\n\t\t);\n\t\tfor (const collection of dbCollections) {\n\t\t\tif (manifestCollections[collection.slug]) continue;\n\n\t\t\tconst fields: Record<string, ManifestFieldDescriptor> = {};\n\t\t\tfor (const field of collection.fields) {\n\t\t\t\tfields[field.slug] = dbFieldDescriptor(field);\n\t\t\t}\n\n\t\t\tconst configuredListColumns = collection.admin?.listColumns ?? [];\n\t\t\tconst fieldTypes = new Map(collection.fields.map((field) => [field.slug, field.type]));\n\t\t\tconst listColumns: string[] = [];\n\t\t\tfor (const slug of configuredListColumns) {\n\t\t\t\tif (listColumns.includes(slug)) continue;\n\t\t\t\tconst fieldType = fieldTypes.get(slug);\n\t\t\t\tif (!fieldType || !LIST_COLUMN_FIELD_TYPES.has(fieldType)) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`EmDash: Ignoring unsupported or unknown list column \"${slug}\" in collection \"${collection.slug}\".`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (listColumns.length >= MAX_COLLECTION_LIST_COLUMNS) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`EmDash: Collection \"${collection.slug}\" declares more than ${MAX_COLLECTION_LIST_COLUMNS} list columns; extra columns are ignored.`,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlistColumns.push(slug);\n\t\t\t}\n\n\t\t\tmanifestCollections[collection.slug] = {\n\t\t\t\tlabel: collection.label,\n\t\t\t\tlabelSingular: collection.labelSingular || collection.label,\n\t\t\t\tsupports: collection.supports || [],\n\t\t\t\thasSeo: collection.hasSeo,\n\t\t\t\turlPattern: collection.urlPattern,\n\t\t\t\troutable: collection.routable !== false,\n\t\t\t\ttitleField: collection.titleField,\n\t\t\t\tdateField: collection.dateField,\n\t\t\t\t...(collection.hidden ? { hidden: true } : {}),\n\t\t\t\t...(collection.group ? { group: collection.group } : {}),\n\t\t\t\tlistColumns: listColumns.length > 0 ? listColumns : undefined,\n\t\t\t\tfields,\n\t\t\t};\n\t\t}\n\t} catch (error) {\n\t\tconsole.debug(\"EmDash: Could not load database collections for manifest:\", error);\n\t}\n\n\treturn manifestCollections;\n}\n\n/**\n * Extract field descriptors from Zod schema\n * Note: This is a simplified implementation that handles common types\n */\nfunction extractFieldDescriptors(schema: {\n\t_def?: { shape?: () => Record<string, unknown> };\n\tshape?: Record<string, unknown>;\n}): Record<string, ManifestFieldDescriptor> {\n\tconst fields: Record<string, ManifestFieldDescriptor> = {};\n\n\t// Handle Zod object schema\n\tconst shape = typeof schema._def?.shape === \"function\" ? schema._def.shape() : schema.shape || {};\n\n\tfor (const [name, fieldSchema] of Object.entries(shape)) {\n\t\tfields[name] = extractFieldType(name, fieldSchema);\n\t}\n\n\treturn fields;\n}\n\n/**\n * Extract field type from Zod schema\n */\n/** Type guard: check if a value is a non-null object */\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction extractFieldType(name: string, schema: unknown): FieldDescriptor {\n\tif (!isObject(schema)) {\n\t\treturn { kind: \"string\", label: formatLabel(name) };\n\t}\n\n\t// Check for custom field markers\n\tif (schema.isPortableText) {\n\t\treturn { kind: \"portableText\", label: formatLabel(name) };\n\t}\n\tif (schema.isImage) {\n\t\treturn { kind: \"image\", label: formatLabel(name) };\n\t}\n\tif (schema.isReference) {\n\t\treturn { kind: \"reference\", label: formatLabel(name) };\n\t}\n\n\t// Handle standard Zod types\n\tconst def = isObject(schema._def) ? schema._def : undefined;\n\tconst typeName = typeof def?.typeName === \"string\" ? def.typeName : undefined;\n\n\tswitch (typeName) {\n\t\tcase \"ZodString\":\n\t\t\treturn { kind: \"string\", label: formatLabel(name) };\n\t\tcase \"ZodNumber\":\n\t\t\treturn { kind: \"number\", label: formatLabel(name) };\n\t\tcase \"ZodBoolean\":\n\t\t\treturn { kind: \"boolean\", label: formatLabel(name) };\n\t\tcase \"ZodDate\":\n\t\t\treturn { kind: \"datetime\", label: formatLabel(name) };\n\t\tcase \"ZodEnum\": {\n\t\t\tconst values = Array.isArray(def?.values) ? def.values : [];\n\t\t\treturn {\n\t\t\t\tkind: \"select\",\n\t\t\t\tlabel: formatLabel(name),\n\t\t\t\toptions: values\n\t\t\t\t\t.filter((v): v is string => typeof v === \"string\")\n\t\t\t\t\t.map((v) => ({\n\t\t\t\t\t\tvalue: v,\n\t\t\t\t\t\tlabel: v.charAt(0).toUpperCase() + v.slice(1),\n\t\t\t\t\t})),\n\t\t\t};\n\t\t}\n\t\tcase \"ZodArray\":\n\t\t\treturn { kind: \"array\", label: formatLabel(name) };\n\t\tcase \"ZodObject\":\n\t\t\treturn { kind: \"object\", label: formatLabel(name) };\n\t\tcase \"ZodOptional\":\n\t\tcase \"ZodDefault\":\n\t\t\t// Unwrap optional/default types\n\t\t\tif (def?.innerType) {\n\t\t\t\treturn extractFieldType(name, def.innerType);\n\t\t\t}\n\t\t\treturn { kind: \"string\", label: formatLabel(name) };\n\t\tdefault:\n\t\t\treturn { kind: \"string\", label: formatLabel(name) };\n\t}\n}\n\nfunction dbFieldDescriptor(field: Field): ManifestFieldDescriptor {\n\tconst entry: ManifestFieldDescriptor = {\n\t\tkind: field.unsupportedType ? \"unsupported\" : FIELD_TYPE_TO_KIND[field.type],\n\t\tlabel: field.label,\n\t\trequired: field.required,\n\t\ttranslatable: field.translatable,\n\t\tid: field.id,\n\t};\n\tif (field.unsupportedType) entry.unsupportedType = field.unsupportedType;\n\tif (field.blockTypes) entry.blockTypes = field.blockTypes;\n\tif (field.blockTypeFingerprint) entry.blockTypeFingerprint = field.blockTypeFingerprint;\n\n\tif (field.widget) entry.widget = field.widget;\n\tif (field.options) entry.options = field.options;\n\n\t// Legacy: select/multiSelect enum options live on `field.validation.options`.\n\t// They win over widget options to preserve existing select behavior.\n\tif (field.validation?.options) {\n\t\tentry.options = field.validation.options.map((value) => ({\n\t\t\tvalue,\n\t\t\tlabel: value.charAt(0).toUpperCase() + value.slice(1),\n\t\t}));\n\t}\n\n\tif (field.validation) {\n\t\tentry.validation = { ...field.validation };\n\t}\n\n\treturn entry;\n}\n\n/**\n * Format field name as label\n */\nfunction formatLabel(name: string): string {\n\treturn name\n\t\t.replace(CAMEL_CASE_PATTERN, \" $1\")\n\t\t.replace(FIRST_CHAR_PATTERN, (str) => str.toUpperCase())\n\t\t.trim();\n}\n","/**\n * Revision history handlers\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport { after } from \"../../after.js\";\nimport { ContentRepository } from \"../../database/repositories/content.js\";\nimport { RevisionRepository, type Revision } from \"../../database/repositories/revision.js\";\nimport { ContentMutationConflictError } from \"../../database/repositories/types.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { encodeRev } from \"../rev.js\";\nimport type { ApiResult, ContentResponse } from \"../types.js\";\n\nexport interface RevisionListResponse {\n\titems: Revision[];\n\ttotal: number;\n}\n\nexport interface RevisionResponse {\n\titem: Revision;\n}\n\n/**\n * List revisions for a content entry\n */\nexport async function handleRevisionList(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tentryId: string,\n\tparams: { limit?: number } = {},\n): Promise<ApiResult<RevisionListResponse>> {\n\ttry {\n\t\tconst repo = new RevisionRepository(db);\n\t\tconst [items, total] = await Promise.all([\n\t\t\trepo.findByEntry(collection, entryId, { limit: Math.min(params.limit || 50, 100) }),\n\t\t\trepo.countByEntry(collection, entryId),\n\t\t]);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { items, total },\n\t\t};\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"REVISION_LIST_ERROR\",\n\t\t\t\tmessage: \"Failed to list revisions\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Get a specific revision\n */\nexport async function handleRevisionGet(\n\tdb: Kysely<Database>,\n\trevisionId: string,\n): Promise<ApiResult<RevisionResponse>> {\n\ttry {\n\t\tconst repo = new RevisionRepository(db);\n\t\tconst item = await repo.findById(revisionId);\n\n\t\tif (!item) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Revision not found: ${revisionId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item },\n\t\t};\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"REVISION_GET_ERROR\",\n\t\t\t\tmessage: \"Failed to get revision\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Restore a revision (updates content to this revision's data and creates new revision)\n */\nexport async function handleRevisionRestore(\n\tdb: Kysely<Database>,\n\trevisionId: string,\n\tcallerUserId: string,\n): Promise<ApiResult<ContentResponse>> {\n\ttry {\n\t\tconst revisionRepo = new RevisionRepository(db);\n\n\t\t// Get the revision\n\t\tconst revision = await revisionRepo.findById(revisionId);\n\t\tif (!revision) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Revision not found: ${revisionId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst { item, revisionId: queuedRevisionId } = await new ContentRepository(db).restoreRevision(\n\t\t\trevision.collection,\n\t\t\trevision.entryId,\n\t\t\trevision.data,\n\t\t\tcallerUserId,\n\t\t);\n\n\t\tconst pruneRepo = new RevisionRepository(db);\n\t\tafter(async () => {\n\t\t\ttry {\n\t\t\t\tawait pruneRepo.pruneQueuedEntry(\n\t\t\t\t\trevision.collection,\n\t\t\t\t\trevision.entryId,\n\t\t\t\t\tqueuedRevisionId,\n\t\t\t\t\t50,\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[revisions] Failed to prune revisions for ${revision.collection}/${revision.entryId}:`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { item, _rev: encodeRev(item) },\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof ContentMutationConflictError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"CONFLICT\", message: error.message },\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"REVISION_RESTORE_ERROR\",\n\t\t\t\tmessage: \"Failed to restore revision\",\n\t\t\t},\n\t\t};\n\t}\n}\n","/**\n * Plugin settings handlers\n *\n * Auto-generated settings UI backend for plugins that declare\n * `admin.settingsSchema`. Values are stored in the options table under\n * `plugin:{pluginId}:settings:{key}` — the same keys the plugin itself\n * reads via `ctx.kv.get(\"settings:{key}\")`.\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport { EmDashSecretsError } from \"../../config/secrets.js\";\nimport { OptionsRepository } from \"../../database/repositories/options.js\";\nimport { withTransaction } from \"../../database/transaction.js\";\nimport type { Database } from \"../../database/types.js\";\nimport type { SandboxedPluginEntry } from \"../../emdash-runtime.js\";\nimport { PluginSettingEncryptionError, encodePluginSettingValue } from \"../../plugins/settings.js\";\nimport type { ResolvedPlugin, SettingField } from \"../../plugins/types.js\";\nimport { ErrorCode } from \"../errors.js\";\nimport type { ApiResult } from \"../types.js\";\n\nexport interface PluginSettingsResponse {\n\t/** The plugin's declared settings schema, keyed by setting name */\n\tschema: Record<string, SettingField>;\n\t/**\n\t * Current values keyed by setting name. Secret fields are never\n\t * included here — check `secretsSet` instead.\n\t */\n\tvalues: Record<string, unknown>;\n\t/** For secret fields: whether a value is currently stored */\n\tsecretsSet: Record<string, boolean>;\n}\n\nfunction settingsKey(pluginId: string, key: string): string {\n\treturn `plugin:${pluginId}:settings:${key}`;\n}\n\n/**\n * Resolve a plugin's settings schema from either the configured\n * (in-process) plugin list or the statically-sandboxed entries.\n * Returns null when the plugin doesn't exist, undefined-equivalent\n * empty object when it declares no schema.\n */\nexport function getPluginSettingsSchema(\n\tconfiguredPlugins: ResolvedPlugin[],\n\tsandboxedPluginEntries: SandboxedPluginEntry[],\n\tpluginId: string,\n): Record<string, SettingField> | null {\n\tconst plugin = configuredPlugins.find((p) => p.id === pluginId);\n\tif (plugin) return plugin.admin.settingsSchema ?? {};\n\n\tconst sandboxed = sandboxedPluginEntries.find((e) => e.id === pluginId);\n\tif (sandboxed) return sandboxed.settingsSchema ?? {};\n\n\treturn null;\n}\n\n/**\n * Validate a single value against its schema field.\n * Returns an error message, or null when valid.\n */\nfunction validateValue(key: string, field: SettingField, value: unknown): string | null {\n\tswitch (field.type) {\n\t\tcase \"string\":\n\t\tcase \"secret\":\n\t\tcase \"url\":\n\t\tcase \"email\":\n\t\t\tif (typeof value !== \"string\") return `Setting \"${key}\" must be a string`;\n\t\t\tif (field.type === \"url\" && value !== \"\" && !URL.canParse(value)) {\n\t\t\t\treturn `Setting \"${key}\" must be a valid URL`;\n\t\t\t}\n\t\t\tif (field.type === \"email\" && value !== \"\" && !value.includes(\"@\")) {\n\t\t\t\treturn `Setting \"${key}\" must be a valid email address`;\n\t\t\t}\n\t\t\treturn null;\n\t\tcase \"number\": {\n\t\t\tif (typeof value !== \"number\" || Number.isNaN(value)) {\n\t\t\t\treturn `Setting \"${key}\" must be a number`;\n\t\t\t}\n\t\t\tif (field.min !== undefined && value < field.min) {\n\t\t\t\treturn `Setting \"${key}\" must be at least ${field.min}`;\n\t\t\t}\n\t\t\tif (field.max !== undefined && value > field.max) {\n\t\t\t\treturn `Setting \"${key}\" must be at most ${field.max}`;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tcase \"boolean\":\n\t\t\treturn typeof value === \"boolean\" ? null : `Setting \"${key}\" must be a boolean`;\n\t\tcase \"select\":\n\t\t\tif (typeof value !== \"string\" || !field.options.some((o) => o.value === value)) {\n\t\t\t\treturn `Setting \"${key}\" must be one of the defined options`;\n\t\t\t}\n\t\t\treturn null;\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = field;\n\t\t\treturn `Setting \"${key}\" has an unknown field type`;\n\t\t}\n\t}\n}\n\nasync function buildSettingsResponse(\n\toptionsRepo: OptionsRepository,\n\tpluginId: string,\n\tschema: Record<string, SettingField>,\n): Promise<PluginSettingsResponse> {\n\tconst keys = Object.keys(schema);\n\tconst stored = await optionsRepo.getMany(keys.map((key) => settingsKey(pluginId, key)));\n\n\tconst values: Record<string, unknown> = {};\n\tconst secretsSet: Record<string, boolean> = {};\n\n\tfor (const key of keys) {\n\t\tconst field = schema[key];\n\t\tif (!field) continue;\n\t\tconst storedValue = stored.get(settingsKey(pluginId, key));\n\n\t\tif (field.type === \"secret\") {\n\t\t\tsecretsSet[key] = storedValue !== undefined && storedValue !== null && storedValue !== \"\";\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (storedValue !== undefined && storedValue !== null) {\n\t\t\tvalues[key] = storedValue;\n\t\t} else if (\"default\" in field && field.default !== undefined) {\n\t\t\tvalues[key] = field.default;\n\t\t} else {\n\t\t\tvalues[key] = null;\n\t\t}\n\t}\n\n\treturn { schema, values, secretsSet };\n}\n\n/**\n * Get a plugin's settings (schema + current values, secrets masked)\n */\nexport async function handlePluginSettingsGet(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tschema: Record<string, SettingField>,\n): Promise<ApiResult<PluginSettingsResponse>> {\n\ttry {\n\t\tconst optionsRepo = new OptionsRepository(db);\n\t\treturn { success: true, data: await buildSettingsResponse(optionsRepo, pluginId, schema) };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.PLUGIN_SETTINGS_READ_ERROR,\n\t\t\t\tmessage: \"Failed to read plugin settings\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n/**\n * Update a plugin's settings.\n *\n * Only keys present in `updates` are written. A `null` value deletes the\n * stored value (reverting to the schema default). Secret fields are\n * write-only: they accept a new string value or `null` to clear, and the\n * response never echoes them back.\n */\nexport async function handlePluginSettingsUpdate(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tschema: Record<string, SettingField>,\n\tupdates: Record<string, unknown>,\n): Promise<ApiResult<PluginSettingsResponse>> {\n\ttry {\n\t\t// Validate everything before writing anything.\n\t\tfor (const [key, value] of Object.entries(updates)) {\n\t\t\tconst field = schema[key];\n\t\t\tif (!field) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: ErrorCode.VALIDATION_ERROR,\n\t\t\t\t\t\tmessage: `Unknown setting \"${key}\" for plugin \"${pluginId}\"`,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (value === null) continue;\n\t\t\tconst error = validateValue(key, field, value);\n\t\t\tif (error) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: { code: ErrorCode.VALIDATION_ERROR, message: error },\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconst encodedUpdates = new Map<string, unknown>();\n\t\tfor (const [key, value] of Object.entries(updates)) {\n\t\t\tencodedUpdates.set(\n\t\t\t\tkey,\n\t\t\t\tvalue === null ? null : await encodePluginSettingValue(pluginId, key, value, schema),\n\t\t\t);\n\t\t}\n\n\t\t// Wrap the writes + read-back in a transaction so a partial failure\n\t\t// can't leave some settings updated and others not. On D1\n\t\t// withTransaction degrades to running the callback directly — D1 is\n\t\t// single-writer, so per-statement atomicity still holds.\n\t\tconst data = await withTransaction(db, async (trx) => {\n\t\t\tconst txRepo = new OptionsRepository(trx);\n\t\t\tfor (const [key, value] of encodedUpdates) {\n\t\t\t\tif (value === null) {\n\t\t\t\t\tawait txRepo.delete(settingsKey(pluginId, key));\n\t\t\t\t} else {\n\t\t\t\t\tawait txRepo.set(settingsKey(pluginId, key), value);\n\t\t\t\t}\n\t\t\t\tawait trx\n\t\t\t\t\t.deleteFrom(\"_plugin_storage\")\n\t\t\t\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t\t\t\t.where(\"collection\", \"=\", \"__kv\")\n\t\t\t\t\t.where(\"id\", \"=\", `settings:${key}`)\n\t\t\t\t\t.execute();\n\t\t\t}\n\t\t\treturn buildSettingsResponse(txRepo, pluginId, schema);\n\t\t});\n\n\t\treturn { success: true, data };\n\t} catch (error) {\n\t\tif (error instanceof PluginSettingEncryptionError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: error.code, message: error.message },\n\t\t\t};\n\t\t}\n\t\tif (error instanceof EmDashSecretsError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: ErrorCode.PLUGIN_SETTING_ENCRYPTION_KEY_INVALID,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"EMDASH_ENCRYPTION_KEY is malformed. Restore or correct the configured key from its secret backup. Generate a new key only if no stored plugin setting depends on the lost key.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.PLUGIN_SETTINGS_UPDATE_ERROR,\n\t\t\t\tmessage: \"Failed to update plugin settings\",\n\t\t\t},\n\t\t};\n\t}\n}\n","/**\n * MarketplaceClient — HTTP client for the EmDash Plugin Marketplace\n *\n * Used by the install/update/proxy endpoints in EmDash core to communicate\n * with the marketplace Worker. The marketplace is a distribution channel,\n * not a runtime dependency — bundles are copied to site-local R2 at install time.\n */\n\nimport { createGzipDecoder, unpackTar } from \"modern-tar\";\n\nimport { pluginManifestSchema, reconcileManifestAccess } from \"./manifest-schema.js\";\nimport type { PluginManifest } from \"./types.js\";\n\n// ── Module-level regex patterns ───────────────────────────────────\n\nconst TRAILING_SLASHES = /\\/+$/;\nconst LEADING_DOT_SLASH = /^\\.\\//;\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface MarketplacePluginSummary {\n\tid: string;\n\tname: string;\n\tdescription: string | null;\n\tauthor: {\n\t\tname: string;\n\t\tverified: boolean;\n\t\tavatarUrl: string | null;\n\t};\n\tcapabilities: string[];\n\tkeywords: string[];\n\tinstallCount: number;\n\thasIcon: boolean;\n\ticonUrl: string;\n\tlatestVersion?: {\n\t\tversion: string;\n\t\taudit?: {\n\t\t\tverdict: string;\n\t\t\triskScore: number;\n\t\t};\n\t\timageAudit?: {\n\t\t\tverdict: string;\n\t\t};\n\t};\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface MarketplaceVersionSummary {\n\tversion: string;\n\tminEmDashVersion: string | null;\n\tbundleSize: number;\n\tchecksum: string;\n\tchangelog: string | null;\n\tcapabilities: string[];\n\tstatus: string;\n\tauditVerdict: string | null;\n\timageAuditVerdict: string | null;\n\tpublishedAt: string;\n}\n\nexport interface MarketplacePluginDetail extends MarketplacePluginSummary {\n\trepositoryUrl: string | null;\n\thomepageUrl: string | null;\n\tlicense: string | null;\n\tlatestVersion?: {\n\t\tversion: string;\n\t\tminEmDashVersion: string | null;\n\t\tbundleSize: number;\n\t\tchecksum: string;\n\t\tchangelog: string | null;\n\t\treadme: string | null;\n\t\thasIcon: boolean;\n\t\tscreenshotCount: number;\n\t\tscreenshotUrls: string[];\n\t\tcapabilities: string[];\n\t\tstatus: string;\n\t\taudit?: {\n\t\t\tverdict: string;\n\t\t\triskScore: number;\n\t\t};\n\t\timageAudit?: {\n\t\t\tverdict: string;\n\t\t};\n\t\tpublishedAt: string;\n\t};\n}\n\nexport interface MarketplaceSearchOpts {\n\tcategory?: string;\n\tcapability?: string;\n\tsort?: \"installs\" | \"updated\" | \"created\" | \"name\";\n\tcursor?: string;\n\tlimit?: number;\n}\n\nexport interface MarketplaceSearchResult {\n\titems: MarketplacePluginSummary[];\n\tnextCursor?: string;\n}\n\n// ── Theme types ───────────────────────────────────────────────────\n\nexport interface MarketplaceThemeSummary {\n\tid: string;\n\tname: string;\n\tdescription: string | null;\n\tauthor: {\n\t\tname: string;\n\t\tverified: boolean;\n\t\tavatarUrl: string | null;\n\t};\n\tkeywords: string[];\n\tpreviewUrl: string;\n\tdemoUrl: string | null;\n\thasThumbnail: boolean;\n\tthumbnailUrl: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface MarketplaceThemeDetail extends MarketplaceThemeSummary {\n\tauthor: {\n\t\tid: string;\n\t\tname: string;\n\t\tverified: boolean;\n\t\tavatarUrl: string | null;\n\t};\n\trepositoryUrl: string | null;\n\thomepageUrl: string | null;\n\tlicense: string | null;\n\tscreenshotCount: number;\n\tscreenshotUrls: string[];\n}\n\nexport interface MarketplaceThemeSearchOpts {\n\tkeyword?: string;\n\tsort?: \"name\" | \"created\" | \"updated\";\n\tcursor?: string;\n\tlimit?: number;\n}\n\nexport interface MarketplaceThemeSearchResult {\n\titems: MarketplaceThemeSummary[];\n\tnextCursor?: string;\n}\n\nexport interface PluginBundle {\n\tmanifest: PluginManifest;\n\tbackendCode: string;\n\tadminCode?: string;\n\tchecksum: string;\n}\n\n// ── Interface ──────────────────────────────────────────────────────\n\nexport interface MarketplaceClient {\n\t/** Search the marketplace catalog */\n\tsearch(query?: string, opts?: MarketplaceSearchOpts): Promise<MarketplaceSearchResult>;\n\n\t/** Get full plugin detail */\n\tgetPlugin(id: string): Promise<MarketplacePluginDetail>;\n\n\t/** Get version history for a plugin */\n\tgetVersions(id: string): Promise<MarketplaceVersionSummary[]>;\n\n\t/** Download and extract a plugin bundle */\n\tdownloadBundle(id: string, version: string): Promise<PluginBundle>;\n\n\t/** Fire-and-forget install stat (never throws) */\n\treportInstall(id: string, version: string): Promise<void>;\n\n\t/** Search theme listings */\n\tsearchThemes(\n\t\tquery?: string,\n\t\topts?: MarketplaceThemeSearchOpts,\n\t): Promise<MarketplaceThemeSearchResult>;\n\n\t/** Get full theme detail */\n\tgetTheme(id: string): Promise<MarketplaceThemeDetail>;\n}\n\n// ── Errors ─────────────────────────────────────────────────────────\n\nexport class MarketplaceError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly status?: number,\n\t\tpublic readonly code?: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"MarketplaceError\";\n\t}\n}\n\nexport class MarketplaceUnavailableError extends MarketplaceError {\n\tconstructor(cause?: unknown) {\n\t\tsuper(\"Plugin marketplace is unavailable\", undefined, \"MARKETPLACE_UNAVAILABLE\");\n\t\tif (cause) this.cause = cause;\n\t}\n}\n\n// ── Implementation ─────────────────────────────────────────────────\n\nclass MarketplaceClientImpl implements MarketplaceClient {\n\tprivate readonly baseUrl: string;\n\tprivate readonly siteOrigin: string | undefined;\n\n\tconstructor(baseUrl: string, siteOrigin?: string) {\n\t\t// Strip trailing slash\n\t\tthis.baseUrl = baseUrl.replace(TRAILING_SLASHES, \"\");\n\t\tthis.siteOrigin = siteOrigin;\n\t}\n\n\tasync search(query?: string, opts?: MarketplaceSearchOpts): Promise<MarketplaceSearchResult> {\n\t\tconst params = new URLSearchParams();\n\t\tif (query) params.set(\"q\", query);\n\t\tif (opts?.category) params.set(\"category\", opts.category);\n\t\tif (opts?.capability) params.set(\"capability\", opts.capability);\n\t\tif (opts?.sort) params.set(\"sort\", opts.sort);\n\t\tif (opts?.cursor) params.set(\"cursor\", opts.cursor);\n\t\tif (opts?.limit) params.set(\"limit\", String(opts.limit));\n\n\t\tconst qs = params.toString();\n\t\tconst url = `${this.baseUrl}/api/v1/plugins${qs ? `?${qs}` : \"\"}`;\n\t\tconst data = await this.fetchJson<MarketplaceSearchResult>(url);\n\t\treturn data;\n\t}\n\n\tasync getPlugin(id: string): Promise<MarketplacePluginDetail> {\n\t\tconst url = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}`;\n\t\treturn this.fetchJson<MarketplacePluginDetail>(url);\n\t}\n\n\tasync getVersions(id: string): Promise<MarketplaceVersionSummary[]> {\n\t\tconst url = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}/versions`;\n\t\tconst data = await this.fetchJson<{ items: MarketplaceVersionSummary[] }>(url);\n\t\treturn data.items;\n\t}\n\n\tasync downloadBundle(id: string, version: string): Promise<PluginBundle> {\n\t\tconst bundleUrl = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}/versions/${encodeURIComponent(version)}/bundle`;\n\n\t\tconst marketplaceOrigin = new URL(this.baseUrl).origin;\n\t\tconst MAX_REDIRECTS = 5;\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tlet currentUrl = bundleUrl;\n\t\t\tresponse = await fetch(currentUrl, { redirect: \"manual\" });\n\n\t\t\t// Follow redirects manually, validating each target stays on the marketplace host\n\t\t\tfor (let i = 0; i < MAX_REDIRECTS; i++) {\n\t\t\t\tif (response.status < 300 || response.status >= 400) break;\n\n\t\t\t\tconst location = response.headers.get(\"location\");\n\t\t\t\tif (!location) break;\n\n\t\t\t\tconst target = new URL(location, currentUrl);\n\t\t\t\tif (target.origin !== marketplaceOrigin) {\n\t\t\t\t\tthrow new MarketplaceError(\n\t\t\t\t\t\t`Bundle download redirected to untrusted host: ${target.origin}`,\n\t\t\t\t\t\tresponse.status,\n\t\t\t\t\t\t\"BUNDLE_REDIRECT_UNTRUSTED\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcurrentUrl = target.href;\n\t\t\t\tresponse = await fetch(currentUrl, { redirect: \"manual\" });\n\t\t\t}\n\n\t\t\t// If still a redirect after MAX_REDIRECTS, fail explicitly\n\t\t\tif (response.status >= 300 && response.status < 400) {\n\t\t\t\tthrow new MarketplaceError(\n\t\t\t\t\t`Bundle download exceeded maximum redirects (${MAX_REDIRECTS})`,\n\t\t\t\t\tresponse.status,\n\t\t\t\t\t\"BUNDLE_TOO_MANY_REDIRECTS\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (err instanceof MarketplaceError) throw err;\n\t\t\tthrow new MarketplaceUnavailableError(err);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tthrow new MarketplaceError(\n\t\t\t\t`Failed to download bundle: ${response.status} ${response.statusText}`,\n\t\t\t\tresponse.status,\n\t\t\t\t\"BUNDLE_DOWNLOAD_FAILED\",\n\t\t\t);\n\t\t}\n\n\t\tconst tarballBytes = new Uint8Array(await response.arrayBuffer());\n\t\ttry {\n\t\t\treturn await extractBundle(tarballBytes);\n\t\t} catch (err) {\n\t\t\tif (err instanceof MarketplaceError) throw err;\n\t\t\tthrow new MarketplaceError(\n\t\t\t\t\"Failed to extract plugin bundle\",\n\t\t\t\tundefined,\n\t\t\t\t\"BUNDLE_EXTRACT_FAILED\",\n\t\t\t);\n\t\t}\n\t}\n\n\tasync reportInstall(id: string, version: string): Promise<void> {\n\t\t// Generate a stable site hash from the site origin (best-effort, non-identifying)\n\t\tconst siteHash = await generateSiteHash(this.siteOrigin);\n\t\tconst url = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}/installs`;\n\n\t\ttry {\n\t\t\tawait fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ siteHash, version }),\n\t\t\t});\n\t\t} catch {\n\t\t\t// Fire-and-forget — never throw\n\t\t}\n\t}\n\n\tasync searchThemes(\n\t\tquery?: string,\n\t\topts?: MarketplaceThemeSearchOpts,\n\t): Promise<MarketplaceThemeSearchResult> {\n\t\tconst params = new URLSearchParams();\n\t\tif (query) params.set(\"q\", query);\n\t\tif (opts?.keyword) params.set(\"keyword\", opts.keyword);\n\t\tif (opts?.sort) params.set(\"sort\", opts.sort);\n\t\tif (opts?.cursor) params.set(\"cursor\", opts.cursor);\n\t\tif (opts?.limit) params.set(\"limit\", String(opts.limit));\n\n\t\tconst qs = params.toString();\n\t\tconst url = `${this.baseUrl}/api/v1/themes${qs ? `?${qs}` : \"\"}`;\n\t\treturn this.fetchJson<MarketplaceThemeSearchResult>(url);\n\t}\n\n\tasync getTheme(id: string): Promise<MarketplaceThemeDetail> {\n\t\tconst url = `${this.baseUrl}/api/v1/themes/${encodeURIComponent(id)}`;\n\t\treturn this.fetchJson<MarketplaceThemeDetail>(url);\n\t}\n\n\tprivate async fetchJson<T>(url: string): Promise<T> {\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\theaders: { Accept: \"application/json\" },\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tthrow new MarketplaceUnavailableError(err);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tlet errorMessage = `Marketplace request failed: ${response.status}`;\n\t\t\ttry {\n\t\t\t\tconst body: { error?: string } = await response.json();\n\t\t\t\tif (body.error) errorMessage = body.error;\n\t\t\t} catch {\n\t\t\t\t// use default message\n\t\t\t}\n\t\t\tthrow new MarketplaceError(errorMessage, response.status);\n\t\t}\n\n\t\tconst data: T = await response.json();\n\t\treturn data;\n\t}\n}\n\n// ── Bundle extraction ──────────────────────────────────────────────\n\n/**\n * Extract manifest + code files from a tarball.\n *\n * The tarball is a gzipped tar archive containing:\n * - manifest.json\n * - backend.js\n * - admin.js (optional)\n *\n * We use a minimal tar parser since we only need to read a few small files.\n */\n/**\n * Exported so the experimental registry install handler can reuse the\n * same parse / validate / hash primitive. Despite the file name, this\n * function predates the marketplace-vs-registry split and is generic\n * over plugin bundle tarballs regardless of distribution channel.\n */\n// Aligns with RFC 0001 §\"Bundle size limits\" (256 KiB decompressed,\n// 20 files). Matches `MAX_BUNDLE_SIZE` in cli/commands/bundle-utils.ts\n// (the publish-side cap). We don't import that constant to keep this\n// runtime module independent of the CLI; the two values are\n// load-bearing identical and must stay in sync.\n//\n// Tar adds per-file headers (~512 bytes each) plus directory entries,\n// so the entry count cap is set comfortably above RFC's 20-file limit.\n// Going over either is a strong signal the bundle isn't a legitimate\n// sandboxed plugin.\nconst MAX_DECOMPRESSED_BUNDLE_BYTES = 256 * 1024;\nconst MAX_BUNDLE_TAR_ENTRIES = 32;\n\nexport async function extractBundle(tarballBytes: Uint8Array): Promise<PluginBundle> {\n\t// Decompress fully into memory first, then parse the tar.\n\t// Passing a pipeThrough() stream directly to unpackTar causes a backpressure\n\t// deadlock in workerd: the tar decoder's body-stream pull() needs more\n\t// decompressed data, but the upstream pipe is stalled waiting for the\n\t// decoder's writable side to drain — a circular dependency.\n\tconst decompressedStream = new ReadableStream<Uint8Array>({\n\t\tstart(controller) {\n\t\t\tcontroller.enqueue(tarballBytes);\n\t\t\tcontroller.close();\n\t\t},\n\t}).pipeThrough(createGzipDecoder());\n\n\t// Collect decompressed bytes with a hard cap. A gzip-bomb -- a small\n\t// tarball that decompresses to gigabytes -- otherwise exhausts\n\t// worker / Node memory before we know to reject it. The cap matches\n\t// RFC 0001's publish-time bundle size limit (MAX_DECOMPRESSED_BUNDLE_BYTES);\n\t// anything past that isn't a legitimate sandboxed plugin.\n\tconst reader = decompressedStream.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet total = 0;\n\twhile (true) {\n\t\tconst { done, value } = await reader.read();\n\t\tif (done) break;\n\t\tif (!value) continue;\n\t\ttotal += value.byteLength;\n\t\tif (total > MAX_DECOMPRESSED_BUNDLE_BYTES) {\n\t\t\ttry {\n\t\t\t\tawait reader.cancel();\n\t\t\t} catch {\n\t\t\t\t// nothing to do\n\t\t\t}\n\t\t\tthrow new MarketplaceError(\n\t\t\t\t`Bundle decompressed size exceeds limit (${MAX_DECOMPRESSED_BUNDLE_BYTES} bytes)`,\n\t\t\t\tundefined,\n\t\t\t\t\"INVALID_BUNDLE\",\n\t\t\t);\n\t\t}\n\t\tchunks.push(value);\n\t}\n\tconst decompressedBytes = new Uint8Array(total);\n\t{\n\t\tlet offset = 0;\n\t\tfor (const chunk of chunks) {\n\t\t\tdecompressedBytes.set(chunk, offset);\n\t\t\toffset += chunk.byteLength;\n\t\t}\n\t}\n\n\tconst decompressed = new ReadableStream<Uint8Array>({\n\t\tstart(controller) {\n\t\t\tcontroller.enqueue(decompressedBytes);\n\t\t\tcontroller.close();\n\t\t},\n\t});\n\n\tconst entries = await unpackTar(decompressed);\n\tif (entries.length > MAX_BUNDLE_TAR_ENTRIES) {\n\t\tthrow new MarketplaceError(\n\t\t\t`Bundle has too many tar entries (${entries.length} > ${MAX_BUNDLE_TAR_ENTRIES})`,\n\t\t\tundefined,\n\t\t\t\"INVALID_BUNDLE\",\n\t\t);\n\t}\n\n\tconst decoder = new TextDecoder();\n\tconst files = new Map<string, string>();\n\tfor (const entry of entries) {\n\t\tif (entry.data && entry.header.type === \"file\") {\n\t\t\t// Strip leading ./ prefix that tar tools commonly add\n\t\t\tconst name = entry.header.name.replace(LEADING_DOT_SLASH, \"\");\n\t\t\tfiles.set(name, decoder.decode(entry.data));\n\t\t}\n\t}\n\n\tconst manifestJson = files.get(\"manifest.json\");\n\tconst backendCode = files.get(\"backend.js\");\n\n\tif (!manifestJson) {\n\t\tthrow new MarketplaceError(\n\t\t\t\"Invalid bundle: missing manifest.json\",\n\t\t\tundefined,\n\t\t\t\"INVALID_BUNDLE\",\n\t\t);\n\t}\n\tif (!backendCode) {\n\t\tthrow new MarketplaceError(\"Invalid bundle: missing backend.js\", undefined, \"INVALID_BUNDLE\");\n\t}\n\n\tlet manifest: PluginManifest;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(manifestJson);\n\t\tconst result = pluginManifestSchema.safeParse(parsed);\n\t\tif (!result.success) {\n\t\t\tthrow new MarketplaceError(\n\t\t\t\t\"Invalid bundle: manifest.json failed validation\",\n\t\t\t\tundefined,\n\t\t\t\t\"INVALID_BUNDLE\",\n\t\t\t);\n\t\t}\n\t\tmanifest = reconcileManifestAccess(result.data);\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceError) throw err;\n\t\tthrow new MarketplaceError(\n\t\t\t\"Invalid bundle: malformed manifest.json\",\n\t\t\tundefined,\n\t\t\t\"INVALID_BUNDLE\",\n\t\t);\n\t}\n\n\t// Compute SHA-256 checksum of the tarball for verification\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Uint8Array is a valid BufferSource at runtime; TS lib mismatch\n\tconst hashBuffer = await crypto.subtle.digest(\"SHA-256\", tarballBytes as unknown as BufferSource);\n\tconst hashArray = new Uint8Array(hashBuffer);\n\tconst checksum = Array.from(hashArray, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n\n\treturn {\n\t\tmanifest,\n\t\tbackendCode,\n\t\tadminCode: files.get(\"admin.js\"),\n\t\tchecksum,\n\t};\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\n/**\n * Generate a stable non-identifying site hash from the site origin.\n * The same origin always produces the same hash, so the marketplace\n * installs table deduplicates correctly per (plugin_id, site_hash).\n */\nasync function generateSiteHash(siteOrigin?: string): Promise<string> {\n\tconst seed = siteOrigin ? `emdash-site:${siteOrigin}` : `emdash-anonymous`;\n\ttry {\n\t\tconst hash = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(seed));\n\t\tconst arr = new Uint8Array(hash);\n\t\treturn Array.from(arr.slice(0, 8), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n\t} catch {\n\t\t// Fallback for environments without crypto.subtle: FNV-1a hash encoded as hex.\n\t\t// Deterministic, uniform distribution, no origin leakage.\n\t\tlet h = 0x811c9dc5;\n\t\tfor (let i = 0; i < seed.length; i++) {\n\t\t\th ^= seed.charCodeAt(i);\n\t\t\th = Math.imul(h, 0x01000193);\n\t\t}\n\t\tconst h2 = h ^ (h >>> 16);\n\t\treturn (h >>> 0).toString(16).padStart(8, \"0\") + (h2 >>> 0).toString(16).padStart(8, \"0\");\n\t}\n}\n\n// ── Factory ────────────────────────────────────────────────────────\n\n/**\n * Create a MarketplaceClient for the given marketplace URL.\n *\n * @param baseUrl - The marketplace API base URL (e.g. \"https://marketplace.emdashcms.com\")\n * @param siteOrigin - The origin of the EmDash site (e.g. \"https://myblog.example.com\").\n * Used to generate a stable, non-identifying site hash for install deduplication.\n */\nexport function createMarketplaceClient(baseUrl: string, siteOrigin?: string): MarketplaceClient {\n\treturn new MarketplaceClientImpl(baseUrl, siteOrigin);\n}\n","/**\n * Plugin Storage Index Management\n *\n * Manages expression indexes on the _plugin_storage table for efficient queries.\n *\n * @see PLUGIN-SYSTEM.md § Plugin Storage > Index Management\n */\n\nimport type { Kysely, RawBuilder } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { jsonExtractExpr, isPostgres } from \"../database/dialect-helpers.js\";\nimport type { Database } from \"../database/types.js\";\nimport {\n\tvalidateJsonFieldName,\n\tvalidatePluginIdentifier,\n\tvalidateStorageCollectionName,\n} from \"../database/validate.js\";\n\n/**\n * Generate a deterministic index name.\n * Unique indexes use a `uidx_` prefix to avoid collisions with regular indexes on the same fields.\n */\nexport function generateIndexName(\n\tpluginId: string,\n\tcollection: string,\n\tfields: string[],\n\toptions?: { unique?: boolean },\n): string {\n\tconst prefix = options?.unique ? \"uidx\" : \"idx\";\n\tconst fieldPart = fields.join(\"_\");\n\t// SQLite index names have no length limit, but keep it reasonable\n\treturn `${prefix}_plugin_${pluginId}_${collection}_${fieldPart}`.substring(0, 128);\n}\n\n/**\n * Generate a Kysely sql expression for creating an expression index.\n *\n * Validates all inputs before interpolation. The collection uses the\n * permissive manifest-key rules rather than SQL-identifier rules — it is\n * stored as opaque text and only reaches SQL inside the generated index\n * name — so kebab-case collections index like any other.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function generateCreateIndexSql(\n\tdb: Kysely<any>,\n\tpluginId: string,\n\tcollection: string,\n\tfields: string[],\n\toptions?: { unique?: boolean },\n): RawBuilder<unknown> {\n\tvalidatePluginIdentifier(pluginId, \"plugin ID\");\n\tvalidateStorageCollectionName(collection, \"collection name\");\n\tfor (const field of fields) {\n\t\tvalidateJsonFieldName(field, \"index field name\");\n\t}\n\n\tconst indexName = generateIndexName(pluginId, collection, fields, options);\n\n\t// Build the indexed expressions\n\t// Fields are validated above, safe to interpolate into json path\n\tconst expressions = fields\n\t\t.map((field) => {\n\t\t\tif (isPostgres(db)) {\n\t\t\t\t// Postgres expression indexes need parens around the expression\n\t\t\t\treturn `(${jsonExtractExpr(db, \"data\", field)})`;\n\t\t\t}\n\t\t\treturn jsonExtractExpr(db, \"data\", field);\n\t\t})\n\t\t.join(\", \");\n\n\t// Composite non-partial index: the leading (plugin_id, collection) columns\n\t// scope it per plugin/collection — including unique-index semantics — and\n\t// let it serve the repository's bound-parameter WHERE plus the JSON\n\t// expression ORDER BY. A partial index (WHERE plugin_id = 'x' AND\n\t// collection = 'y') is never chosen by SQLite under bound parameters\n\t// unless ANALYZE has run, and D1 never runs ANALYZE.\n\tconst createKeyword = options?.unique ? \"CREATE UNIQUE INDEX\" : \"CREATE INDEX\";\n\treturn sql`${sql.raw(createKeyword)} IF NOT EXISTS ${sql.ref(indexName)}\n\t\tON _plugin_storage(plugin_id, collection, ${sql.raw(expressions)})\n\t`;\n}\n\n/**\n * Generate a Kysely sql expression for dropping an index.\n *\n * Uses sql.ref() for safe identifier quoting.\n */\nexport function generateDropIndexSql(indexName: string): RawBuilder<unknown> {\n\treturn sql`DROP INDEX IF EXISTS ${sql.ref(indexName)}`;\n}\n\n/**\n * Normalize index declarations to arrays of field arrays\n */\nexport function normalizeIndexes(indexes: Array<string | string[]>): string[][] {\n\treturn indexes.map((index) => (Array.isArray(index) ? index : [index]));\n}\n\n/**\n * Create all declared indexes for a plugin collection\n */\nexport async function createStorageIndexes(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tcollection: string,\n\tindexes: Array<string | string[]>,\n\toptions?: { uniqueIndexes?: Array<string | string[]> },\n): Promise<{\n\tcreated: string[];\n\terrors: Array<{ index: string; error: string }>;\n}> {\n\tconst normalized = normalizeIndexes(indexes);\n\tconst uniqueNormalized = options?.uniqueIndexes ? normalizeIndexes(options.uniqueIndexes) : [];\n\tconst uniqueSet = new Set(uniqueNormalized.map((f) => f.join(\",\")));\n\n\t// Deduplicate: if fields appear in both indexes and uniqueIndexes, only create the unique version\n\tconst deduped = normalized.filter((f) => !uniqueSet.has(f.join(\",\")));\n\tconst allEntries: Array<{ fields: string[]; unique: boolean }> = [\n\t\t...deduped.map((fields) => ({ fields, unique: false })),\n\t\t...uniqueNormalized.map((fields) => ({ fields, unique: true })),\n\t];\n\n\tconst created: string[] = [];\n\tconst errors: Array<{ index: string; error: string }> = [];\n\n\tfor (const entry of allEntries) {\n\t\tconst { fields } = entry;\n\t\tconst indexName = generateIndexName(pluginId, collection, fields, { unique: entry.unique });\n\n\t\ttry {\n\t\t\t// Create the index\n\t\t\tconst createSql = generateCreateIndexSql(db, pluginId, collection, fields, {\n\t\t\t\tunique: entry.unique,\n\t\t\t});\n\t\t\tawait createSql.execute(db);\n\n\t\t\t// Track in _plugin_indexes table\n\t\t\tawait db\n\t\t\t\t.insertInto(\"_plugin_indexes\")\n\t\t\t\t.values({\n\t\t\t\t\tplugin_id: pluginId,\n\t\t\t\t\tcollection,\n\t\t\t\t\tindex_name: indexName,\n\t\t\t\t\tfields: JSON.stringify(fields),\n\t\t\t\t})\n\t\t\t\t.onConflict((oc) =>\n\t\t\t\t\toc\n\t\t\t\t\t\t.columns([\"plugin_id\", \"collection\", \"index_name\"])\n\t\t\t\t\t\t.doUpdateSet({ fields: JSON.stringify(fields) }),\n\t\t\t\t)\n\t\t\t\t.execute();\n\n\t\t\tcreated.push(indexName);\n\t\t} catch (error) {\n\t\t\terrors.push({\n\t\t\t\tindex: indexName,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { created, errors };\n}\n\n/**\n * Remove indexes that are no longer declared\n */\nexport async function removeOrphanedIndexes(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tcollection: string,\n\tcurrentIndexes: Array<string | string[]>,\n\toptions?: { uniqueIndexes?: Array<string | string[]> },\n): Promise<{\n\tremoved: string[];\n\terrors: Array<{ index: string; error: string }>;\n}> {\n\tconst normalized = normalizeIndexes(currentIndexes);\n\tconst uniqueNormalized = options?.uniqueIndexes ? normalizeIndexes(options.uniqueIndexes) : [];\n\tconst uniqueSet = new Set(uniqueNormalized.map((f) => f.join(\",\")));\n\n\t// Build the set of expected index names (regular + unique with correct prefix)\n\tconst currentIndexNames = new Set<string>();\n\tfor (const fields of normalized) {\n\t\t// If this field set is in both, only the unique version exists (deduplication in create)\n\t\tif (!uniqueSet.has(fields.join(\",\"))) {\n\t\t\tcurrentIndexNames.add(generateIndexName(pluginId, collection, fields));\n\t\t}\n\t}\n\tfor (const fields of uniqueNormalized) {\n\t\tcurrentIndexNames.add(generateIndexName(pluginId, collection, fields, { unique: true }));\n\t}\n\n\t// Get existing indexes from tracking table\n\tconst existingIndexes = await db\n\t\t.selectFrom(\"_plugin_indexes\")\n\t\t.select([\"index_name\"])\n\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t.where(\"collection\", \"=\", collection)\n\t\t.execute();\n\n\tconst removed: string[] = [];\n\tconst errors: Array<{ index: string; error: string }> = [];\n\n\tfor (const { index_name } of existingIndexes) {\n\t\tif (!currentIndexNames.has(index_name)) {\n\t\t\ttry {\n\t\t\t\t// Drop the index\n\t\t\t\tawait generateDropIndexSql(index_name).execute(db);\n\n\t\t\t\t// Remove from tracking table\n\t\t\t\tawait db\n\t\t\t\t\t.deleteFrom(\"_plugin_indexes\")\n\t\t\t\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t\t\t.where(\"index_name\", \"=\", index_name)\n\t\t\t\t\t.execute();\n\n\t\t\t\tremoved.push(index_name);\n\t\t\t} catch (error) {\n\t\t\t\terrors.push({\n\t\t\t\t\tindex: index_name,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { removed, errors };\n}\n\n/**\n * Sync indexes for a plugin collection (create new, remove old)\n */\nexport async function syncStorageIndexes(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tcollection: string,\n\tindexes: Array<string | string[]>,\n\toptions?: { uniqueIndexes?: Array<string | string[]> },\n): Promise<{\n\tcreated: string[];\n\tremoved: string[];\n\terrors: Array<{ index: string; error: string }>;\n}> {\n\tconst [createResult, removeResult] = await Promise.all([\n\t\tcreateStorageIndexes(db, pluginId, collection, indexes, options),\n\t\tremoveOrphanedIndexes(db, pluginId, collection, indexes, options),\n\t]);\n\n\treturn {\n\t\tcreated: createResult.created,\n\t\tremoved: removeResult.removed,\n\t\terrors: [...createResult.errors, ...removeResult.errors],\n\t};\n}\n\n/**\n * Materialize the storage indexes a set of plugins declare in their\n * manifests. Failures are logged per collection and never thrown — a missing\n * index affects query performance, not correctness, so it must not fail an\n * install or a scheduler tick.\n */\nexport async function syncDeclaredStorageIndexes(\n\tdb: Kysely<Database>,\n\tplugins: Array<{\n\t\tid: string;\n\t\tstorage?: Record<\n\t\t\tstring,\n\t\t\t{ indexes: Array<string | string[]>; uniqueIndexes?: Array<string | string[]> }\n\t\t>;\n\t}>,\n): Promise<void> {\n\tfor (const plugin of plugins) {\n\t\tfor (const [collection, config] of Object.entries(plugin.storage ?? {})) {\n\t\t\ttry {\n\t\t\t\tconst result = await syncStorageIndexes(db, plugin.id, collection, config.indexes, {\n\t\t\t\t\tuniqueIndexes: config.uniqueIndexes,\n\t\t\t\t});\n\t\t\t\tfor (const failure of result.errors) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`[plugins] Failed to sync storage index ${failure.index} for ${plugin.id}/${collection}: ${failure.error}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[plugins] Failed to sync storage indexes for ${plugin.id}/${collection}:`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Remove all indexes for a plugin\n */\nexport async function removeAllPluginIndexes(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n): Promise<{\n\tremoved: string[];\n\terrors: Array<{ index: string; error: string }>;\n}> {\n\tconst existingIndexes = await db\n\t\t.selectFrom(\"_plugin_indexes\")\n\t\t.select([\"index_name\", \"collection\"])\n\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t.execute();\n\n\tconst removed: string[] = [];\n\tconst errors: Array<{ index: string; error: string }> = [];\n\n\tfor (const { index_name } of existingIndexes) {\n\t\ttry {\n\t\t\tawait generateDropIndexSql(index_name).execute(db);\n\t\t\tremoved.push(index_name);\n\t\t} catch (error) {\n\t\t\terrors.push({\n\t\t\t\tindex: index_name,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t}\n\t}\n\n\t// Clean up tracking table\n\tawait db.deleteFrom(\"_plugin_indexes\").where(\"plugin_id\", \"=\", pluginId).execute();\n\n\treturn { removed, errors };\n}\n\n/**\n * Get current index status for a plugin\n */\nexport async function getPluginIndexStatus(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n): Promise<\n\tArray<{\n\t\tcollection: string;\n\t\tindexName: string;\n\t\tfields: string[];\n\t\tcreatedAt: string;\n\t}>\n> {\n\tconst rows = await db\n\t\t.selectFrom(\"_plugin_indexes\")\n\t\t.select([\"collection\", \"index_name\", \"fields\", \"created_at\"])\n\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t.execute();\n\n\treturn rows.map((row) => {\n\t\tconst parsed: unknown = JSON.parse(row.fields);\n\t\tconst fields = Array.isArray(parsed)\n\t\t\t? parsed.filter((f): f is string => typeof f === \"string\")\n\t\t\t: [];\n\t\treturn {\n\t\t\tcollection: row.collection,\n\t\t\tindexName: row.index_name,\n\t\t\tfields,\n\t\t\tcreatedAt: row.created_at,\n\t\t};\n\t});\n}\n","/**\n * Marketplace plugin handlers\n *\n * Business logic for installing, updating, uninstalling, and checking\n * updates for marketplace plugins. Routes are thin wrappers around these.\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../../database/types.js\";\nimport { validatePluginIdentifier } from \"../../database/validate.js\";\nimport { pluginManifestSchema, reconcileManifestAccess } from \"../../plugins/manifest-schema.js\";\nimport { normalizeManifestRoute } from \"../../plugins/manifest-schema.js\";\nimport {\n\tcreateMarketplaceClient,\n\tMarketplaceError,\n\tMarketplaceUnavailableError,\n\ttype MarketplaceClient,\n\ttype MarketplacePluginDetail,\n\ttype MarketplaceSearchOpts,\n\ttype MarketplaceThemeSearchOpts,\n\ttype MarketplaceVersionSummary,\n\ttype PluginBundle,\n} from \"../../plugins/marketplace.js\";\nimport { withUnavailableReason } from \"../../plugins/sandbox/types.js\";\nimport type { SandboxRunner } from \"../../plugins/sandbox/types.js\";\nimport { PluginStateRepository } from \"../../plugins/state.js\";\nimport type { PluginState } from \"../../plugins/state.js\";\nimport {\n\tremoveAllPluginIndexes,\n\tsyncDeclaredStorageIndexes,\n} from \"../../plugins/storage-indexes.js\";\nimport { normalizeCapabilities } from \"../../plugins/types.js\";\nimport type { PluginManifest } from \"../../plugins/types.js\";\nimport { EmDashStorageError } from \"../../storage/types.js\";\nimport type { Storage } from \"../../storage/types.js\";\nimport type { ApiResult } from \"../types.js\";\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface MarketplaceInstallResult {\n\tpluginId: string;\n\tversion: string;\n\tcapabilities: string[];\n}\n\nexport interface MarketplaceUpdateResult {\n\tpluginId: string;\n\toldVersion: string;\n\tnewVersion: string;\n\tcapabilityChanges: {\n\t\tadded: string[];\n\t\tremoved: string[];\n\t};\n\trouteVisibilityChanges?: {\n\t\tnewlyPublic: string[];\n\t};\n}\n\nexport interface PluginUpdateRollbackResult {\n\tpluginId: string;\n\tversion: string;\n}\n\nexport interface MarketplaceUpdateCheck {\n\tpluginId: string;\n\tinstalled: string;\n\tlatest: string;\n\thasUpdate: boolean;\n\thasCapabilityChanges: boolean;\n\tcapabilityChanges?: {\n\t\tadded: string[];\n\t\tremoved: string[];\n\t};\n\thasRouteVisibilityChanges: boolean;\n\trouteVisibilityChanges?: {\n\t\tnewlyPublic: string[];\n\t};\n}\n\nexport interface MarketplaceUninstallResult {\n\tpluginId: string;\n\tdataDeleted: boolean;\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\n/** Semver-like pattern: digits, dots, hyphens, plus signs (e.g. 1.0.0, 1.0.0-beta.1) */\nconst VERSION_PATTERN = /^[a-z0-9][a-z0-9._+-]*$/i;\n\nfunction validateVersion(version: string): void {\n\tif (version.includes(\"..\")) throw new Error(\"Invalid version format\");\n\tif (!VERSION_PATTERN.test(version)) {\n\t\tthrow new Error(\"Invalid version format\");\n\t}\n}\n\nfunction getClient(\n\tmarketplaceUrl: string | undefined,\n\tsiteOrigin?: string,\n): MarketplaceClient | null {\n\tif (!marketplaceUrl) return null;\n\treturn createMarketplaceClient(marketplaceUrl, siteOrigin);\n}\n\nexport function diffCapabilities(\n\toldCaps: string[],\n\tnewCaps: string[],\n): { added: string[]; removed: string[] } {\n\t// Normalize both sides before diffing so that an installed v1 manifest\n\t// declaring `read:content` and an upgrade v2 manifest declaring\n\t// `content:read` produces an empty diff — users should not see a\n\t// spurious \"capability changed\" prompt for a pure rename.\n\tconst oldNorm = normalizeCapabilities(oldCaps);\n\tconst newNorm = normalizeCapabilities(newCaps);\n\tconst oldSet = new Set(oldNorm);\n\tconst newSet = new Set(newNorm);\n\treturn {\n\t\tadded: newNorm.filter((c) => !oldSet.has(c)),\n\t\tremoved: oldNorm.filter((c) => !newSet.has(c)),\n\t};\n}\n\n/**\n * Diff route visibility between two manifests.\n * Returns routes that changed from private to public (newly exposed).\n */\nexport function diffRouteVisibility(\n\toldManifest: PluginManifest | undefined,\n\tnewManifest: PluginManifest,\n): { newlyPublic: string[] } {\n\tconst oldPublicRoutes = new Set<string>();\n\tif (oldManifest) {\n\t\tfor (const entry of oldManifest.routes) {\n\t\t\tconst normalized = normalizeManifestRoute(entry);\n\t\t\tif (normalized.public === true) {\n\t\t\t\toldPublicRoutes.add(normalized.name);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst newlyPublic: string[] = [];\n\tfor (const entry of newManifest.routes) {\n\t\tconst normalized = normalizeManifestRoute(entry);\n\t\tif (normalized.public === true && !oldPublicRoutes.has(normalized.name)) {\n\t\t\tnewlyPublic.push(normalized.name);\n\t\t}\n\t}\n\n\treturn { newlyPublic };\n}\n\nasync function resolveVersionMetadata(\n\tclient: MarketplaceClient,\n\tpluginId: string,\n\tpluginDetail: MarketplacePluginDetail,\n\tversion: string,\n): Promise<MarketplaceVersionSummary | null> {\n\tif (pluginDetail.latestVersion?.version === version) {\n\t\treturn {\n\t\t\tversion: pluginDetail.latestVersion.version,\n\t\t\tminEmDashVersion: pluginDetail.latestVersion.minEmDashVersion,\n\t\t\tbundleSize: pluginDetail.latestVersion.bundleSize,\n\t\t\tchecksum: pluginDetail.latestVersion.checksum,\n\t\t\tchangelog: pluginDetail.latestVersion.changelog,\n\t\t\tcapabilities: pluginDetail.latestVersion.capabilities,\n\t\t\tstatus: pluginDetail.latestVersion.status,\n\t\t\tauditVerdict: pluginDetail.latestVersion.audit?.verdict ?? null,\n\t\t\timageAuditVerdict: pluginDetail.latestVersion.imageAudit?.verdict ?? null,\n\t\t\tpublishedAt: pluginDetail.latestVersion.publishedAt,\n\t\t};\n\t}\n\n\tconst versions = await client.getVersions(pluginId);\n\treturn versions.find((v) => v.version === version) ?? null;\n}\n\n/** A null verdict means no audit ran, which is allowed; \"fail\" and \"warn\" are not. */\nfunction checkAuditVerdict(versionMetadata: MarketplaceVersionSummary): ApiResult<never> | null {\n\tif (versionMetadata.auditVerdict !== \"fail\" && versionMetadata.auditVerdict !== \"warn\") {\n\t\treturn null;\n\t}\n\treturn {\n\t\tsuccess: false,\n\t\terror: {\n\t\t\tcode: \"AUDIT_FAILED\",\n\t\t\tmessage:\n\t\t\t\tversionMetadata.auditVerdict === \"fail\"\n\t\t\t\t\t? \"Plugin failed security audit and cannot be installed or updated\"\n\t\t\t\t\t: \"Plugin audit was inconclusive and cannot be installed or updated until reviewed\",\n\t\t},\n\t};\n}\n\nfunction validateBundleIdentity(\n\tbundle: PluginBundle,\n\tpluginId: string,\n\tversion: string,\n): ApiResult<never> | null {\n\tif (bundle.manifest.id !== pluginId) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"MANIFEST_MISMATCH\",\n\t\t\t\tmessage: `Bundle manifest ID (${bundle.manifest.id}) does not match requested plugin (${pluginId})`,\n\t\t\t},\n\t\t};\n\t}\n\n\tif (bundle.manifest.version !== version) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"MANIFEST_VERSION_MISMATCH\",\n\t\t\t\tmessage: `Bundle manifest version (${bundle.manifest.version}) does not match requested version (${version})`,\n\t\t\t},\n\t\t};\n\t}\n\n\treturn null;\n}\n\n/** Store a plugin bundle's files in site-local R2 storage */\n/**\n * Storage source for an installed plugin bundle. Determines the R2\n * key prefix and is used to keep marketplace and registry installs\n * cleanly separated in object listings.\n */\nexport type PluginBundleSource = \"marketplace\" | \"registry\";\n\nfunction bundlePrefix(source: PluginBundleSource, pluginId: string, version: string): string {\n\treturn `${source}/${pluginId}/${version}`;\n}\n\nexport async function storeBundleInR2(\n\tstorage: Storage,\n\tpluginId: string,\n\tversion: string,\n\tbundle: PluginBundle,\n\tsource: PluginBundleSource = \"marketplace\",\n): Promise<void> {\n\tvalidatePluginIdentifier(pluginId, \"plugin ID\");\n\tvalidateVersion(version);\n\tconst prefix = bundlePrefix(source, pluginId, version);\n\n\t// Store manifest\n\tawait storage.upload({\n\t\tkey: `${prefix}/manifest.json`,\n\t\tbody: new TextEncoder().encode(JSON.stringify(bundle.manifest)),\n\t\tcontentType: \"application/json\",\n\t});\n\n\t// Store backend code\n\tawait storage.upload({\n\t\tkey: `${prefix}/backend.js`,\n\t\tbody: new TextEncoder().encode(bundle.backendCode),\n\t\tcontentType: \"application/javascript\",\n\t});\n\n\t// Store admin code if present\n\tif (bundle.adminCode) {\n\t\tawait storage.upload({\n\t\t\tkey: `${prefix}/admin.js`,\n\t\t\tbody: new TextEncoder().encode(bundle.adminCode),\n\t\t\tcontentType: \"application/javascript\",\n\t\t});\n\t}\n}\n\n/** Read a ReadableStream to string */\nasync function streamToText(stream: ReadableStream<Uint8Array>): Promise<string> {\n\treturn new Response(stream).text();\n}\n\n/**\n * Load a plugin bundle from site-local R2 storage.\n *\n * `source` selects the R2 key prefix: marketplace plugins are stored\n * under `marketplace/<id>/<version>/`, registry plugins under\n * `registry/<id>/<version>/`. Defaults to `\"marketplace\"` for\n * backwards compatibility with pre-registry call sites.\n */\nexport async function loadBundleFromR2(\n\tstorage: Storage,\n\tpluginId: string,\n\tversion: string,\n\tsource: PluginBundleSource = \"marketplace\",\n): Promise<{ manifest: PluginManifest; backendCode: string; adminCode?: string } | null> {\n\tvalidatePluginIdentifier(pluginId, \"plugin ID\");\n\tvalidateVersion(version);\n\tconst prefix = bundlePrefix(source, pluginId, version);\n\n\ttry {\n\t\tconst manifestResult = await storage.download(`${prefix}/manifest.json`);\n\t\tconst backendResult = await storage.download(`${prefix}/backend.js`);\n\n\t\tconst manifestText = await streamToText(manifestResult.body);\n\t\tconst backendCode = await streamToText(backendResult.body);\n\t\tconst parsed: unknown = JSON.parse(manifestText);\n\t\tconst result = pluginManifestSchema.safeParse(parsed);\n\t\tif (!result.success) return null;\n\t\tconst manifest = reconcileManifestAccess(result.data);\n\n\t\t// Try to load admin code (optional)\n\t\tlet adminCode: string | undefined;\n\t\ttry {\n\t\t\tconst adminResult = await storage.download(`${prefix}/admin.js`);\n\t\t\tadminCode = await streamToText(adminResult.body);\n\t\t} catch {\n\t\t\t// admin.js is optional\n\t\t}\n\n\t\treturn { manifest, backendCode, adminCode };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Delete a plugin bundle from site-local R2 storage */\nexport async function deleteBundleFromR2(\n\tstorage: Storage,\n\tpluginId: string,\n\tversion: string,\n\tsource: PluginBundleSource = \"marketplace\",\n): Promise<void> {\n\tvalidatePluginIdentifier(pluginId, \"plugin ID\");\n\tvalidateVersion(version);\n\tconst prefix = bundlePrefix(source, pluginId, version);\n\tconst files = [\"manifest.json\", \"backend.js\", \"admin.js\"];\n\n\tfor (const file of files) {\n\t\ttry {\n\t\t\tawait storage.delete(`${prefix}/${file}`);\n\t\t} catch {\n\t\t\t// Ignore missing files\n\t\t}\n\t}\n}\n\nexport async function rollbackPluginUpdate(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tpreviousState: PluginState,\n\tfailedVersion: string,\n\tsource: PluginBundleSource,\n): Promise<ApiResult<PluginUpdateRollbackResult>> {\n\tif (!storage) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"STORAGE_NOT_CONFIGURED\", message: \"Storage is required\" },\n\t\t};\n\t}\n\tif (previousState.source !== source) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"UPDATE_ROLLBACK_CONFLICT\", message: \"Plugin source changed during update\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst restored = await new PluginStateRepository(db).restoreIfVersion(\n\t\t\tfailedVersion,\n\t\t\tpreviousState,\n\t\t);\n\t\tif (!restored) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"UPDATE_ROLLBACK_CONFLICT\",\n\t\t\t\t\tmessage: \"Plugin state changed before the failed update could be rolled back\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tawait deleteBundleFromR2(storage, previousState.pluginId, failedVersion, source);\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { pluginId: previousState.pluginId, version: previousState.version },\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"Failed to roll back plugin update:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"UPDATE_ROLLBACK_FAILED\", message: \"Failed to roll back plugin update\" },\n\t\t};\n\t}\n}\n\n// ── Install ────────────────────────────────────────────────────────\n\nexport async function handleMarketplaceInstall(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tsandboxRunner: SandboxRunner | null,\n\tmarketplaceUrl: string | undefined,\n\tpluginId: string,\n\topts?: {\n\t\tversion?: string;\n\t\tconfiguredPluginIds?: Set<string>;\n\t\tsiteOrigin?: string;\n\t\t/**\n\t\t * When true, sandbox: false bypass mode is active. The sandbox runner\n\t\t * is the noop runner (isAvailable() === false) but the runtime will\n\t\t * load the marketplace plugin in-process via syncMarketplacePlugins().\n\t\t * Skip the SANDBOX_NOT_AVAILABLE gate so the install can proceed.\n\t\t */\n\t\tsandboxBypassed?: boolean;\n\t\tconfirmMcpTools?: boolean;\n\t\tacknowledgedPublicRoutes?: string[];\n\t},\n): Promise<ApiResult<MarketplaceInstallResult>> {\n\tconst client = getClient(marketplaceUrl, opts?.siteOrigin);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"MARKETPLACE_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Marketplace is not configured\",\n\t\t\t},\n\t\t};\n\t}\n\n\tif (!storage) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"STORAGE_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Storage is required for marketplace plugin installation\",\n\t\t\t},\n\t\t};\n\t}\n\n\t// Sandbox availability check: skip when sandbox: false bypass is active.\n\t// The runtime's syncMarketplacePlugins() will load the plugin in-process.\n\tif (!opts?.sandboxBypassed && (!sandboxRunner || !sandboxRunner.isAvailable())) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"SANDBOX_NOT_AVAILABLE\",\n\t\t\t\tmessage: withUnavailableReason(\n\t\t\t\t\t\"Sandbox runner is required for marketplace plugins\",\n\t\t\t\t\tsandboxRunner,\n\t\t\t\t),\n\t\t\t},\n\t\t};\n\t}\n\n\ttry {\n\t\t// Check if already installed\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (existing && existing.source === \"marketplace\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ALREADY_INSTALLED\",\n\t\t\t\t\tmessage: `Plugin ${pluginId} is already installed`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Block installation if a configured (trusted) plugin with the same ID exists.\n\t\t// Without this check, the sandboxed plugin could shadow the trusted plugin's\n\t\t// route handlers while auth decisions are made against the trusted plugin's metadata.\n\t\tif (opts?.configuredPluginIds?.has(pluginId)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"PLUGIN_ID_CONFLICT\",\n\t\t\t\t\tmessage: `Cannot install marketplace plugin \"${pluginId}\" — a configured plugin with the same ID already exists`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fetch plugin detail from marketplace\n\t\tconst pluginDetail = await client.getPlugin(pluginId);\n\t\tconst version = opts?.version ?? pluginDetail.latestVersion?.version;\n\t\tif (!version) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_VERSION\",\n\t\t\t\t\tmessage: `No published versions found for plugin ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst versionMetadata = await resolveVersionMetadata(client, pluginId, pluginDetail, version);\n\t\tif (!versionMetadata) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_VERSION\",\n\t\t\t\t\tmessage: `Version ${version} was not found for plugin ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst auditError = checkAuditVerdict(versionMetadata);\n\t\tif (auditError) return auditError;\n\n\t\t// Download and extract bundle\n\t\tconst bundle = await client.downloadBundle(pluginId, version);\n\n\t\t// Verify checksum matches marketplace-published checksum\n\t\tif (versionMetadata.checksum && bundle.checksum !== versionMetadata.checksum) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CHECKSUM_MISMATCH\",\n\t\t\t\t\tmessage: \"Bundle checksum does not match marketplace record. Download may be corrupted.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst bundleIdentityError = validateBundleIdentity(bundle, pluginId, version);\n\t\tif (bundleIdentityError) return bundleIdentityError;\n\n\t\tconst routeVisibilityChanges = diffRouteVisibility(undefined, bundle.manifest);\n\t\tconst hasPublicRoutes = routeVisibilityChanges.newlyPublic.length > 0;\n\t\tconst publicRoutes = routeVisibilityChanges.newlyPublic.toSorted();\n\t\tconst acknowledgedPublicRoutes = (opts?.acknowledgedPublicRoutes ?? []).toSorted();\n\t\tconst mcpTools = bundle.manifest.mcp?.tools.map(\n\t\t\t({ inputSchema: _, outputSchema: __, ...tool }) => tool,\n\t\t);\n\t\tconst consentDetails = {\n\t\t\trouteVisibilityChanges: hasPublicRoutes ? { newlyPublic: publicRoutes } : undefined,\n\t\t\tmcpTools,\n\t\t};\n\t\tif (\n\t\t\thasPublicRoutes &&\n\t\t\tJSON.stringify(acknowledgedPublicRoutes) !== JSON.stringify(publicRoutes)\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ROUTE_VISIBILITY_ESCALATION\",\n\t\t\t\t\tmessage: \"Plugin install exposes public (unauthenticated) routes\",\n\t\t\t\t\tdetails: consentDetails,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif ((mcpTools?.length ?? 0) > 0 && !opts?.confirmMcpTools) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MCP_TOOL_CONSENT_REQUIRED\",\n\t\t\t\t\tmessage: \"Plugin MCP tools require explicit consent\",\n\t\t\t\t\tdetails: consentDetails,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Store bundle in site-local R2\n\t\tawait storeBundleInR2(storage, pluginId, version, bundle);\n\n\t\t// Write plugin state\n\t\tawait stateRepo.upsert(pluginId, version, \"active\", {\n\t\t\tsource: \"marketplace\",\n\t\t\tmarketplaceVersion: version,\n\t\t\tdisplayName: pluginDetail.name,\n\t\t\tdescription: pluginDetail.description ?? undefined,\n\t\t});\n\n\t\tawait syncDeclaredStorageIndexes(db, [bundle.manifest]);\n\n\t\t// Fire-and-forget install stat\n\t\tclient.reportInstall(pluginId, version).catch(() => {\n\t\t\t// Intentional: never fails the install\n\t\t});\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\tpluginId,\n\t\t\t\tversion,\n\t\t\t\tcapabilities: bundle.manifest.capabilities,\n\t\t\t},\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MARKETPLACE_UNAVAILABLE\",\n\t\t\t\t\tmessage: \"Plugin marketplace is currently unavailable\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof MarketplaceError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.code ?? \"MARKETPLACE_ERROR\",\n\t\t\t\t\tmessage: err.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof EmDashStorageError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.code ?? \"STORAGE_ERROR\",\n\t\t\t\t\tmessage: \"Storage error while installing plugin\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err && typeof err === \"object\" && \"code\" in err) {\n\t\t\tconst code = (err as { code?: unknown }).code;\n\t\t\tif (typeof code === \"string\" && code.trim()) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode,\n\t\t\t\t\t\tmessage: \"Failed to install plugin from marketplace\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tconsole.error(\"Failed to install marketplace plugin:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"INSTALL_FAILED\",\n\t\t\t\tmessage: \"Failed to install plugin from marketplace\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Update ─────────────────────────────────────────────────────────\n\nexport async function handleMarketplaceUpdate(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tsandboxRunner: SandboxRunner | null,\n\tmarketplaceUrl: string | undefined,\n\tpluginId: string,\n\topts?: {\n\t\tversion?: string;\n\t\tconfirmCapabilityChanges?: boolean;\n\t\tacknowledgedPublicRoutes?: string[];\n\t\tconfirmMcpTools?: boolean;\n\t\t/**\n\t\t * When true, sandbox: false bypass mode is active. The sandbox runner\n\t\t * is the noop runner (isAvailable() === false) but the runtime will\n\t\t * load the marketplace plugin in-process via syncMarketplacePlugins().\n\t\t * Skip the SANDBOX_NOT_AVAILABLE gate so the update can proceed.\n\t\t */\n\t\tsandboxBypassed?: boolean;\n\t},\n): Promise<ApiResult<MarketplaceUpdateResult>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\tif (!storage) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"STORAGE_NOT_CONFIGURED\", message: \"Storage is required\" },\n\t\t};\n\t}\n\t// Sandbox availability check: skip when sandbox: false bypass is active.\n\t// The runtime's syncMarketplacePlugins() will load the plugin in-process.\n\tif (!opts?.sandboxBypassed && (!sandboxRunner || !sandboxRunner.isAvailable())) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"SANDBOX_NOT_AVAILABLE\",\n\t\t\t\tmessage: withUnavailableReason(\"Sandbox runner is required\", sandboxRunner),\n\t\t\t},\n\t\t};\n\t}\n\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (!existing || existing.source !== \"marketplace\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `No marketplace plugin found: ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst oldVersion = existing.marketplaceVersion ?? existing.version;\n\n\t\t// Get target version\n\t\tconst pluginDetail = await client.getPlugin(pluginId);\n\t\tconst newVersion = opts?.version ?? pluginDetail.latestVersion?.version;\n\t\tif (!newVersion) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NO_VERSION\", message: \"No newer version available\" },\n\t\t\t};\n\t\t}\n\n\t\tif (newVersion === oldVersion) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"ALREADY_UP_TO_DATE\", message: \"Plugin is already up to date\" },\n\t\t\t};\n\t\t}\n\n\t\tconst versionMetadata = await resolveVersionMetadata(\n\t\t\tclient,\n\t\t\tpluginId,\n\t\t\tpluginDetail,\n\t\t\tnewVersion,\n\t\t);\n\t\tif (!versionMetadata) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_VERSION\",\n\t\t\t\t\tmessage: `Version ${newVersion} was not found for plugin ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst auditError = checkAuditVerdict(versionMetadata);\n\t\tif (auditError) return auditError;\n\n\t\t// Download new bundle\n\t\tconst bundle = await client.downloadBundle(pluginId, newVersion);\n\n\t\t// Verify checksum matches marketplace-published checksum for this version\n\t\tif (versionMetadata.checksum && bundle.checksum !== versionMetadata.checksum) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CHECKSUM_MISMATCH\",\n\t\t\t\t\tmessage: \"Bundle checksum does not match marketplace record. Download may be corrupted.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst bundleIdentityError = validateBundleIdentity(bundle, pluginId, newVersion);\n\t\tif (bundleIdentityError) return bundleIdentityError;\n\n\t\t// Diff capabilities and route visibility against old version\n\t\tconst oldBundle = await loadBundleFromR2(storage, pluginId, oldVersion);\n\t\tconst oldCaps = oldBundle?.manifest.capabilities ?? [];\n\t\tconst capabilityChanges = diffCapabilities(oldCaps, bundle.manifest.capabilities);\n\t\tconst hasEscalation = capabilityChanges.added.length > 0;\n\t\tconst routeVisibilityChanges = diffRouteVisibility(oldBundle?.manifest, bundle.manifest);\n\t\tconst hasNewPublicRoutes = routeVisibilityChanges.newlyPublic.length > 0;\n\t\tconst newlyPublicRoutes = routeVisibilityChanges.newlyPublic.toSorted();\n\t\tconst acknowledgedPublicRoutes = (opts?.acknowledgedPublicRoutes ?? []).toSorted();\n\t\tconst oldMcpTools = [...(oldBundle?.manifest.mcp?.tools ?? [])].toSorted((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t);\n\t\tconst newMcpTools = [...(bundle.manifest.mcp?.tools ?? [])].toSorted((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t);\n\t\tconst hasMcpChanges = JSON.stringify(oldMcpTools) !== JSON.stringify(newMcpTools);\n\t\tconst mcpTools = newMcpTools.map(({ inputSchema: _, outputSchema: __, ...tool }) => tool);\n\t\tconst consentDetails = {\n\t\t\tcapabilityChanges,\n\t\t\trouteVisibilityChanges: hasNewPublicRoutes ? { newlyPublic: newlyPublicRoutes } : undefined,\n\t\t\tmcpTools: hasMcpChanges ? mcpTools : undefined,\n\t\t};\n\n\t\t// If capabilities escalated, require explicit confirmation\n\t\tif (hasEscalation && !opts?.confirmCapabilityChanges) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CAPABILITY_ESCALATION\",\n\t\t\t\t\tmessage: \"Plugin update requires new capabilities\",\n\t\t\t\t\tdetails: consentDetails,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Diff route visibility — routes going from private to public are a\n\t\t// security-sensitive change that exposes unauthenticated endpoints.\n\t\tif (\n\t\t\thasNewPublicRoutes &&\n\t\t\tJSON.stringify(acknowledgedPublicRoutes) !== JSON.stringify(newlyPublicRoutes)\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ROUTE_VISIBILITY_ESCALATION\",\n\t\t\t\t\tmessage: \"Plugin update exposes new public (unauthenticated) routes\",\n\t\t\t\t\tdetails: consentDetails,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (hasMcpChanges && !opts?.confirmMcpTools) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MCP_TOOL_CONSENT_REQUIRED\",\n\t\t\t\t\tmessage: \"Plugin update changes its MCP tools\",\n\t\t\t\t\tdetails: consentDetails,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Store new bundle\n\t\tawait storeBundleInR2(storage, pluginId, newVersion, bundle);\n\n\t\t// Update state\n\t\tawait stateRepo.upsert(pluginId, newVersion, \"active\", {\n\t\t\tsource: \"marketplace\",\n\t\t\tmarketplaceVersion: newVersion,\n\t\t\tdisplayName: pluginDetail.name,\n\t\t\tdescription: pluginDetail.description ?? undefined,\n\t\t\tmcpToolsEnabled: false,\n\t\t\tmcpToolsConsent: null,\n\t\t});\n\n\t\tawait syncDeclaredStorageIndexes(db, [bundle.manifest]);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\tpluginId,\n\t\t\t\toldVersion,\n\t\t\t\tnewVersion,\n\t\t\t\tcapabilityChanges,\n\t\t\t\trouteVisibilityChanges: hasNewPublicRoutes ? routeVisibilityChanges : undefined,\n\t\t\t},\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tif (err instanceof MarketplaceError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: err.code ?? \"MARKETPLACE_ERROR\", message: err.message },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to update marketplace plugin:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"UPDATE_FAILED\", message: \"Failed to update plugin\" },\n\t\t};\n\t}\n}\n\n// ── Uninstall ──────────────────────────────────────────────────────\n\nexport async function handleMarketplaceUninstall(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tpluginId: string,\n\topts?: { deleteData?: boolean; beforeDelete?: () => Promise<void> },\n): Promise<ApiResult<MarketplaceUninstallResult>> {\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (!existing || existing.source !== \"marketplace\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `No marketplace plugin found: ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst version = existing.marketplaceVersion ?? existing.version;\n\t\tawait opts?.beforeDelete?.();\n\n\t\t// Optionally delete plugin storage data\n\t\tlet dataDeleted = false;\n\t\tif (opts?.deleteData) {\n\t\t\ttry {\n\t\t\t\tawait db.deleteFrom(\"_plugin_storage\").where(\"plugin_id\", \"=\", pluginId).execute();\n\t\t\t\tdataDeleted = true;\n\t\t\t} catch {\n\t\t\t\t// Plugin storage table may not have data for this plugin\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tawait removeAllPluginIndexes(db, pluginId);\n\t\t} catch {\n\t\t\t// Nothing to drop, or tracking table predates the feature\n\t\t}\n\n\t\t// Delete state row\n\t\tawait stateRepo.delete(pluginId);\n\n\t\t// Delete the bundle after database state. A failed external cleanup can\n\t\t// leave an inert orphan, but never an active row pointing at missing bytes.\n\t\tif (storage) {\n\t\t\tawait deleteBundleFromR2(storage, pluginId, version);\n\t\t}\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { pluginId, dataDeleted },\n\t\t};\n\t} catch (err) {\n\t\tconsole.error(\"Failed to uninstall marketplace plugin:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"UNINSTALL_FAILED\",\n\t\t\t\tmessage: \"Failed to uninstall plugin\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Update check ───────────────────────────────────────────────────\n\nexport async function handleMarketplaceUpdateCheck(\n\tdb: Kysely<Database>,\n\tmarketplaceUrl: string | undefined,\n): Promise<ApiResult<{ items: MarketplaceUpdateCheck[] }>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst marketplacePlugins = await stateRepo.getMarketplacePlugins();\n\n\t\tconst items: MarketplaceUpdateCheck[] = [];\n\n\t\tfor (const plugin of marketplacePlugins) {\n\t\t\ttry {\n\t\t\t\tconst detail = await client.getPlugin(plugin.pluginId);\n\t\t\t\tconst latest = detail.latestVersion?.version;\n\t\t\t\tconst installed = plugin.marketplaceVersion ?? plugin.version;\n\n\t\t\t\tif (!latest) continue;\n\n\t\t\t\tconst hasUpdate = latest !== installed;\n\t\t\t\tlet capabilityChanges: { added: string[]; removed: string[] } | undefined;\n\t\t\t\tlet hasCapabilityChanges = false;\n\n\t\t\t\tif (hasUpdate && detail.latestVersion) {\n\t\t\t\t\tconst oldCaps = detail.capabilities ?? [];\n\t\t\t\t\tconst newCaps = detail.latestVersion.capabilities ?? [];\n\t\t\t\t\tcapabilityChanges = diffCapabilities(oldCaps, newCaps);\n\t\t\t\t\thasCapabilityChanges =\n\t\t\t\t\t\tcapabilityChanges.added.length > 0 || capabilityChanges.removed.length > 0;\n\t\t\t\t}\n\n\t\t\t\titems.push({\n\t\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\t\tinstalled,\n\t\t\t\t\tlatest: latest ?? installed,\n\t\t\t\t\thasUpdate,\n\t\t\t\t\thasCapabilityChanges,\n\t\t\t\t\tcapabilityChanges: hasCapabilityChanges ? capabilityChanges : undefined,\n\t\t\t\t\t// Route visibility changes require downloading both bundles to compare\n\t\t\t\t\t// manifests, which is too expensive for a preview check. The actual\n\t\t\t\t\t// enforcement happens at update time in handleMarketplaceUpdate.\n\t\t\t\t\thasRouteVisibilityChanges: false,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\t// Skip plugins that can't be checked (marketplace down, plugin delisted)\n\t\t\t\tconsole.warn(`Failed to check updates for ${plugin.pluginId}:`, err);\n\t\t\t}\n\t\t}\n\n\t\treturn { success: true, data: { items } };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to check marketplace updates:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"UPDATE_CHECK_FAILED\", message: \"Failed to check for updates\" },\n\t\t};\n\t}\n}\n\n// ── Proxy ──────────────────────────────────────────────────────────\n\nexport async function handleMarketplaceSearch(\n\tmarketplaceUrl: string | undefined,\n\tquery?: string,\n\topts?: MarketplaceSearchOpts,\n): Promise<ApiResult<unknown>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst result = await client.search(query, opts);\n\t\treturn { success: true, data: result };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to search marketplace:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"SEARCH_FAILED\", message: \"Failed to search marketplace\" },\n\t\t};\n\t}\n}\n\nexport async function handleMarketplaceGetPlugin(\n\tmarketplaceUrl: string | undefined,\n\tpluginId: string,\n): Promise<ApiResult<unknown>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst result = await client.getPlugin(pluginId);\n\t\treturn { success: true, data: result };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceError && err.status === 404) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: `Plugin not found: ${pluginId}` },\n\t\t\t};\n\t\t}\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to get marketplace plugin:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"GET_PLUGIN_FAILED\", message: \"Failed to get plugin details\" },\n\t\t};\n\t}\n}\n\n// ── Theme proxy handlers ──────────────────────────────────────────\n\nexport async function handleThemeSearch(\n\tmarketplaceUrl: string | undefined,\n\tquery?: string,\n\topts?: MarketplaceThemeSearchOpts,\n): Promise<ApiResult<unknown>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst result = await client.searchThemes(query, opts);\n\t\treturn { success: true, data: result };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to search themes:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"THEME_SEARCH_FAILED\", message: \"Failed to search themes\" },\n\t\t};\n\t}\n}\n\nexport async function handleThemeGetDetail(\n\tmarketplaceUrl: string | undefined,\n\tthemeId: string,\n): Promise<ApiResult<unknown>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst result = await client.getTheme(themeId);\n\t\treturn { success: true, data: result };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceError && err.status === 404) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: `Theme not found: ${themeId}` },\n\t\t\t};\n\t\t}\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to get marketplace theme:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"GET_THEME_FAILED\", message: \"Failed to get theme details\" },\n\t\t};\n\t}\n}\n","import { n as verificationError } from \"./errors-D3_zxvwe.js\";\nimport { pluginManifestSchema, reconcileManifestAccess } from \"@emdash-cms/plugin-types\";\nimport { createGzipDecoder } from \"modern-tar\";\n\n//#region src/bundle-limits.ts\n/** Maximum accepted gzip payload size. */\nconst MAX_BUNDLE_COMPRESSED_BYTES = 384 * 1024;\n/** Maximum aggregate size of regular-file contents. */\nconst MAX_BUNDLE_SIZE = 256 * 1024;\n/** Maximum size of one regular file. */\nconst MAX_BUNDLE_FILE_BYTES = 128 * 1024;\n/** Maximum number of regular files. */\nconst MAX_BUNDLE_FILE_COUNT = 20;\n/** Maximum total tar entries, including harmless directory entries. */\nconst MAX_BUNDLE_TAR_ENTRY_COUNT = 32;\n/**\n* Maximum tar stream size after decompression. This includes USTAR headers,\n* file padding, and the end marker in addition to the regular-file contents.\n*/\nconst MAX_BUNDLE_DECOMPRESSED_BYTES = MAX_BUNDLE_SIZE + MAX_BUNDLE_TAR_ENTRY_COUNT * 512 + MAX_BUNDLE_FILE_COUNT * 511 + 2 * 512;\n\n//#endregion\n//#region src/bundle.ts\nconst TAR_BLOCK_BYTES = 512;\nconst TAR_END_BYTES = TAR_BLOCK_BYTES * 2;\nconst OCTAL_PATTERN = /^[0-7]+$/;\nconst WINDOWS_DRIVE_PATTERN = /^[a-zA-Z]:/;\nconst decoder = new TextDecoder(\"utf-8\", {\n\tfatal: true,\n\tignoreBOM: false\n});\nasync function validatePluginBundle(compressed, options = {}) {\n\tif (compressed.byteLength > MAX_BUNDLE_COMPRESSED_BYTES) return verificationError(\"BUNDLE_COMPRESSED_SIZE_EXCEEDED\", \"The compressed plugin bundle exceeds the size limit.\");\n\tconst decompressed = await decompressBundle(compressed);\n\tif (!decompressed.success) return decompressed;\n\tconst files = parseTar(decompressed.value);\n\tif (!files.success) return files;\n\tconst manifestFile = files.value.get(\"manifest.json\");\n\tif (!manifestFile) return verificationError(\"BUNDLE_MISSING_MANIFEST\", \"The plugin bundle is missing manifest.json.\");\n\tconst backend = files.value.get(\"backend.js\");\n\tif (!backend) return verificationError(\"BUNDLE_MISSING_BACKEND\", \"The plugin bundle is missing backend.js.\");\n\tlet parsed;\n\ttry {\n\t\tparsed = JSON.parse(decoder.decode(manifestFile.data));\n\t} catch {\n\t\treturn verificationError(\"BUNDLE_INVALID_MANIFEST\", \"The plugin bundle manifest is not valid JSON.\");\n\t}\n\tconst validated = pluginManifestSchema.safeParse(parsed);\n\tif (!validated.success) return verificationError(\"BUNDLE_INVALID_MANIFEST\", \"The plugin bundle manifest failed schema validation.\");\n\tconst manifest = reconcileManifestAccess(validated.data);\n\tif (options.expectedSlug !== void 0 && manifest.id !== options.expectedSlug) return verificationError(\"BUNDLE_ID_MISMATCH\", \"The plugin bundle manifest id does not match the expected plugin.\");\n\tif (options.expectedVersion !== void 0 && manifest.version !== options.expectedVersion) return verificationError(\"BUNDLE_VERSION_MISMATCH\", \"The plugin bundle manifest version does not match the expected version.\");\n\tconst result = {\n\t\tmanifest,\n\t\tdeclaredAccess: manifest.declaredAccess ?? {},\n\t\tbackend: backend.data\n\t};\n\tconst admin = files.value.get(\"admin.js\");\n\tif (admin) result.admin = admin.data;\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: result\n\t};\n}\nasync function decompressBundle(bytes) {\n\tconst source = new ReadableStream({ start(controller) {\n\t\tcontroller.enqueue(bytes);\n\t\tcontroller.close();\n\t} });\n\tlet reader;\n\ttry {\n\t\treader = source.pipeThrough(createGzipDecoder()).getReader();\n\t} catch {\n\t\treturn verificationError(\"BUNDLE_INVALID_ARCHIVE\", \"The plugin bundle is not valid gzip data.\");\n\t}\n\tconst chunks = [];\n\tlet total = 0;\n\ttry {\n\t\twhile (true) {\n\t\t\tconst { done, value } = await reader.read();\n\t\t\tif (done) break;\n\t\t\tif (!value) continue;\n\t\t\ttotal += value.byteLength;\n\t\t\tif (total > MAX_BUNDLE_DECOMPRESSED_BYTES) {\n\t\t\t\tawait reader.cancel().catch(() => void 0);\n\t\t\t\treturn verificationError(\"BUNDLE_DECOMPRESSED_SIZE_EXCEEDED\", \"The decompressed plugin bundle exceeds the size limit.\");\n\t\t\t}\n\t\t\tchunks.push(value);\n\t\t}\n\t} catch {\n\t\treturn verificationError(\"BUNDLE_INVALID_ARCHIVE\", \"The plugin bundle is not valid gzip data.\");\n\t}\n\tconst output = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\toutput.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: output\n\t};\n}\nfunction parseTar(bytes) {\n\tif (bytes.byteLength < TAR_END_BYTES || bytes.byteLength % TAR_BLOCK_BYTES !== 0) return invalidArchive();\n\tconst files = /* @__PURE__ */ new Map();\n\tconst rawPaths = /* @__PURE__ */ new Set();\n\tconst normalizedPaths = /* @__PURE__ */ new Set();\n\tlet offset = 0;\n\tlet fileCount = 0;\n\tlet entryCount = 0;\n\tlet totalFileBytes = 0;\n\tlet ended = false;\n\twhile (offset + TAR_BLOCK_BYTES <= bytes.byteLength) {\n\t\tconst header = bytes.subarray(offset, offset + TAR_BLOCK_BYTES);\n\t\tif (isZeroBlock(header)) {\n\t\t\tif (!isZeroBlock(bytes.subarray(offset + TAR_BLOCK_BYTES, offset + TAR_END_BYTES))) return invalidArchive();\n\t\t\tif (bytes.subarray(offset + TAR_END_BYTES).some((byte) => byte !== 0)) return invalidArchive();\n\t\t\tended = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (!validChecksum(header)) return invalidArchive();\n\t\tentryCount += 1;\n\t\tif (entryCount > MAX_BUNDLE_TAR_ENTRY_COUNT) return verificationError(\"BUNDLE_FILE_COUNT_EXCEEDED\", \"The plugin bundle contains too many archive entries.\");\n\t\tconst rawName = readTarPath(header);\n\t\tif (!rawName.success) return rawName;\n\t\tconst typeFlag = header[156];\n\t\tconst type = typeFlag === 0 ? \"file\" : String.fromCharCode(typeFlag ?? 0);\n\t\tconst isDirectory = type === \"5\";\n\t\tconst normalized = normalizePath(rawName.value, isDirectory);\n\t\tif (!normalized.success) return normalized;\n\t\tif (rawPaths.has(rawName.value) || normalizedPaths.has(normalized.value)) return verificationError(\"BUNDLE_PATH_COLLISION\", \"The plugin bundle contains duplicate or ambiguous paths.\");\n\t\trawPaths.add(rawName.value);\n\t\tnormalizedPaths.add(normalized.value);\n\t\tconst size = readOctal(header.subarray(124, 136));\n\t\tif (size === null || !Number.isSafeInteger(size)) return invalidArchive();\n\t\tconst bodyStart = offset + TAR_BLOCK_BYTES;\n\t\tconst nextOffset = bodyStart + Math.ceil(size / TAR_BLOCK_BYTES) * TAR_BLOCK_BYTES;\n\t\tif (nextOffset > bytes.byteLength) return invalidArchive();\n\t\tif (isDirectory) {\n\t\t\tif (size !== 0) return invalidArchive();\n\t\t} else if (type === \"file\" || type === \"0\") {\n\t\t\tfileCount += 1;\n\t\t\tif (fileCount > MAX_BUNDLE_FILE_COUNT) return verificationError(\"BUNDLE_FILE_COUNT_EXCEEDED\", \"The plugin bundle contains too many files.\");\n\t\t\tif (size > MAX_BUNDLE_FILE_BYTES) return verificationError(\"BUNDLE_FILE_SIZE_EXCEEDED\", \"A file in the plugin bundle exceeds the per-file size limit.\");\n\t\t\ttotalFileBytes += size;\n\t\t\tif (totalFileBytes > MAX_BUNDLE_SIZE) return verificationError(\"BUNDLE_DECOMPRESSED_SIZE_EXCEEDED\", \"The plugin bundle file contents exceed the size limit.\");\n\t\t\tfiles.set(normalized.value, {\n\t\t\t\tname: normalized.value,\n\t\t\t\tdata: bytes.slice(bodyStart, bodyStart + size)\n\t\t\t});\n\t\t} else return verificationError(\"BUNDLE_UNSUPPORTED_ENTRY\", \"The plugin bundle contains an unsupported archive entry type.\");\n\t\toffset = nextOffset;\n\t}\n\tif (!ended) return invalidArchive();\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: files\n\t};\n}\nfunction readTarPath(header) {\n\tconst name = readTarString(header.subarray(0, 100));\n\tconst prefix = readTarString(header.subarray(345, 500));\n\tif (name === null || prefix === null || name.length === 0) return verificationError(\"BUNDLE_INVALID_PATH\", \"The plugin bundle contains a malformed path.\");\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: prefix ? `${prefix}/${name}` : name\n\t};\n}\nfunction readTarString(field) {\n\tconst terminator = field.indexOf(0);\n\tconst end = terminator === -1 ? field.length : terminator;\n\tif (terminator !== -1 && field.subarray(terminator + 1).some((byte) => byte !== 0)) return null;\n\ttry {\n\t\treturn decoder.decode(field.subarray(0, end));\n\t} catch {\n\t\treturn null;\n\t}\n}\nfunction normalizePath(raw, isDirectory) {\n\tif (raw.includes(\"\\\\\") || raw.startsWith(\"/\") || WINDOWS_DRIVE_PATTERN.test(raw) || hasControlCharacter(raw) || raw.endsWith(\"/\") !== isDirectory) return verificationError(\"BUNDLE_INVALID_PATH\", \"The plugin bundle contains an unsafe path.\");\n\tconst parts = raw.split(\"/\");\n\tif (isDirectory) parts.pop();\n\tconst normalized = [];\n\tfor (const part of parts) {\n\t\tif (part === \".\") continue;\n\t\tif (part === \"\" || part === \"..\") return verificationError(\"BUNDLE_INVALID_PATH\", \"The plugin bundle contains an unsafe path.\");\n\t\tnormalized.push(part.normalize(\"NFC\"));\n\t}\n\tif (normalized.length === 0) return verificationError(\"BUNDLE_INVALID_PATH\", \"The plugin bundle contains a malformed path.\");\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: normalized.join(\"/\")\n\t};\n}\nfunction hasControlCharacter(value) {\n\tfor (const character of value) {\n\t\tconst codePoint = character.codePointAt(0);\n\t\tif (codePoint !== void 0 && (codePoint <= 31 || codePoint === 127)) return true;\n\t}\n\treturn false;\n}\nfunction readOctal(field) {\n\tconst text = new TextDecoder().decode(field).replaceAll(\"\\0\", \"\").trim();\n\tif (!OCTAL_PATTERN.test(text)) return null;\n\tconst value = Number.parseInt(text, 8);\n\treturn Number.isSafeInteger(value) ? value : null;\n}\nfunction validChecksum(header) {\n\tconst expected = readOctal(header.subarray(148, 156));\n\tif (expected === null) return false;\n\tlet actual = 0;\n\tfor (let index = 0; index < header.length; index += 1) actual += index >= 148 && index < 156 ? 32 : header[index] ?? 0;\n\treturn actual === expected;\n}\nfunction isZeroBlock(block) {\n\treturn block.byteLength === TAR_BLOCK_BYTES && block.every((byte) => byte === 0);\n}\nfunction invalidArchive() {\n\treturn verificationError(\"BUNDLE_INVALID_ARCHIVE\", \"The plugin bundle is not a valid tar archive.\");\n}\n\n//#endregion\nexport { validatePluginBundle };\n//# sourceMappingURL=bundle.js.map","import type { VerificationErrorCode } from \"@emdash-cms/registry-verification\";\nimport { validatePluginBundle } from \"@emdash-cms/registry-verification/bundle\";\nimport {\n\tcomputeArtifactDigestCandidates,\n\tverifyMultihash,\n} from \"@emdash-cms/registry-verification/checksum\";\n\nimport { pluginManifestSchema, reconcileManifestAccess } from \"../plugins/manifest-schema.js\";\nimport type { PluginBundle } from \"../plugins/marketplace.js\";\n\nconst bundleDecoder = new TextDecoder(\"utf-8\", { fatal: true });\n\nexport type RegistryArtifactVerificationCode =\n\t| VerificationErrorCode\n\t| \"BUNDLE_INVALID_CODE_ENCODING\"\n\t| \"BUNDLE_RUNTIME_UNSUPPORTED\";\n\nexport type RegistryArtifactVerificationResult =\n\t| {\n\t\t\tsuccess: true;\n\t\t\tvalue: {\n\t\t\t\tbundle: PluginBundle;\n\t\t\t\tartifactDigest: Uint8Array;\n\t\t\t\tartifactDigests: readonly Uint8Array[];\n\t\t\t};\n\t }\n\t| {\n\t\t\tsuccess: false;\n\t\t\terror: { code: RegistryArtifactVerificationCode; message: string };\n\t };\n\nexport async function validateRegistryArtifact(\n\tbytes: Uint8Array,\n\tchecksum: string,\n\tslug: string,\n\tversion: string,\n): Promise<RegistryArtifactVerificationResult> {\n\tconst checksumReport = await verifyMultihash(bytes, checksum);\n\tif (!checksumReport.success) return checksumReport;\n\tconst [artifactDigest, ...artifactDigests] = await computeArtifactDigestCandidates(bytes);\n\n\tconst bundleReport = await validatePluginBundle(bytes, {\n\t\texpectedSlug: slug,\n\t\texpectedVersion: version,\n\t});\n\tif (!bundleReport.success) return bundleReport;\n\n\tconst manifest = pluginManifestSchema.safeParse(bundleReport.value.manifest);\n\tif (!manifest.success) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"BUNDLE_RUNTIME_UNSUPPORTED\",\n\t\t\t\tmessage: \"The plugin bundle uses manifest features unsupported by this EmDash version.\",\n\t\t\t},\n\t\t};\n\t}\n\n\ttry {\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue: {\n\t\t\t\tbundle: {\n\t\t\t\t\tmanifest: reconcileManifestAccess(manifest.data),\n\t\t\t\t\tbackendCode: bundleDecoder.decode(bundleReport.value.backend),\n\t\t\t\t\tadminCode:\n\t\t\t\t\t\tbundleReport.value.admin === undefined\n\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t: bundleDecoder.decode(bundleReport.value.admin),\n\t\t\t\t\tchecksum,\n\t\t\t\t},\n\t\t\t\tartifactDigest,\n\t\t\t\tartifactDigests,\n\t\t\t},\n\t\t};\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"BUNDLE_INVALID_CODE_ENCODING\",\n\t\t\t\tmessage: \"Plugin bundle code must be valid UTF-8.\",\n\t\t\t},\n\t\t};\n\t}\n}\n","/**\n * Plugin identifier helpers for the experimental decentralized plugin\n * registry.\n *\n * Registry plugins are addressed by `(publisher_did, slug)`, but the\n * EmDash runtime threads a single `pluginId: string` through every\n * install primitive (R2 storage keys, `PluginStateRepository`,\n * `syncMarketplacePlugins`, sandbox cache keys). Rather than refactor\n * everything to carry a composite identifier, we normalize the registry\n * tuple to an opaque content-addressed id that satisfies the existing\n * `validatePluginIdentifier` shape (`/^[a-z][a-z0-9_-]*$/`).\n *\n * The normalized id is:\n *\n * `r_` + base32-encoded SHA-256(publisher_did + \"\\n\" + slug), truncated.\n *\n * Properties:\n *\n * - Deterministic. The same `(publisher, slug)` always produces the\n * same id, so re-resolving an installed plugin's metadata against\n * the aggregator is a straightforward lookup keyed by the columns\n * stored alongside `plugin_id` in `plugin_states`.\n * - Collision-resistant. 80 bits of truncated hash; a 50% birthday\n * collision happens around 2^40 distinct plugins, well beyond what\n * this registry will ever index.\n * - R2-safe. Lowercase alphanumerics + underscore (no hyphens), no\n * `:` or `/`. Existing sandbox cache keys (`${pluginId}:${version}`)\n * keep working because the id contains no `:`.\n * - Syntactically distinct from typical marketplace plugin ids: the\n * `r_` prefix plus exactly 16 base32 characters is unlikely to be\n * chosen as a marketplace id. Not formally guaranteed by the\n * validator -- marketplace ids may begin with `r_` and contain\n * hyphens -- so the install handler also performs an explicit\n * pre-existing-row check at the derived id and rejects any cross-\n * source collision (`PLUGIN_ID_COLLISION`).\n *\n * Reverse lookup (id → publisher + slug) requires the `plugin_states`\n * row -- the hash is one-way. That's intentional: any code path that\n * needs the human-meaningful pair already has the state row in hand.\n */\n\n/** Length (in base32 characters) of the truncated hash portion of the id. */\nconst HASH_LENGTH = 16;\n\n/** Total expected length of a registry plugin id. */\nexport const REGISTRY_PLUGIN_ID_LENGTH = 2 /* \"r_\" */ + HASH_LENGTH;\n\n/**\n * Regex matching a well-formed registry plugin id. Used by call sites\n * that need to distinguish registry installs from marketplace installs\n * without consulting the `source` column on `plugin_states`.\n *\n * The base32 alphabet here uses RFC 4648 lowercase without padding,\n * matching {@link base32Encode}'s output.\n */\nexport const REGISTRY_PLUGIN_ID_PATTERN = /^r_[a-z2-7]{16}$/;\n\nconst BASE32_ALPHABET = \"abcdefghijklmnopqrstuvwxyz234567\";\n\n/**\n * RFC 4648 base32 encoding without padding, lowercase. Implemented inline\n * rather than depending on a multibase library because (a) we only need\n * lowercase base32 here, (b) we need it to run identically in workerd,\n * Node, and the browser, and (c) the implementation is fewer lines than\n * the import statement would be.\n */\nfunction base32Encode(bytes: Uint8Array): string {\n\tlet bits = 0;\n\tlet value = 0;\n\tlet out = \"\";\n\tfor (const byte of bytes) {\n\t\tvalue = (value << 8) | byte;\n\t\tbits += 8;\n\t\twhile (bits >= 5) {\n\t\t\tbits -= 5;\n\t\t\tout += BASE32_ALPHABET[(value >>> bits) & 0x1f];\n\t\t}\n\t}\n\tif (bits > 0) {\n\t\tout += BASE32_ALPHABET[(value << (5 - bits)) & 0x1f];\n\t}\n\treturn out;\n}\n\n/**\n * Derive the normalized plugin id for a registry-published plugin.\n *\n * Throws if either input is empty or whitespace-only -- a missing DID\n * or slug is always a programming error in the install path, not a\n * recoverable runtime condition.\n */\nexport async function makeRegistryPluginId(publisherDid: string, slug: string): Promise<string> {\n\tconst did = publisherDid.trim();\n\tconst s = slug.trim();\n\tif (!did) throw new Error(\"makeRegistryPluginId: publisherDid is required\");\n\tif (!s) throw new Error(\"makeRegistryPluginId: slug is required\");\n\n\t// `\\n` separator avoids ambiguity: no canonical did:plc / did:web form\n\t// contains a literal newline, so `(\"a\", \"b\\nc\")` cannot hash to the\n\t// same bytes as `(\"a\\nb\", \"c\")`.\n\tconst input = `${did}\\n${s}`;\n\tconst hashBuffer = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(input));\n\tconst encoded = base32Encode(new Uint8Array(hashBuffer));\n\treturn `r_${encoded.slice(0, HASH_LENGTH)}`;\n}\n\n/**\n * Return whether `pluginId` is a well-formed registry plugin id.\n *\n * This is a syntactic check, not a database lookup -- it answers\n * \"could this id have come from `makeRegistryPluginId`?\", not \"is this\n * plugin installed?\".\n */\nexport function isRegistryPluginId(pluginId: string): boolean {\n\treturn REGISTRY_PLUGIN_ID_PATTERN.test(pluginId);\n}\n","interface VersionedRecordLabel {\n\tval?: string;\n\turi?: string;\n\tcid?: string;\n\tneg?: boolean;\n\texp?: string;\n}\n\nexport function hasCurrentRecordLabel(\n\tlabels: readonly VersionedRecordLabel[],\n\tvalue: string,\n\trecord: { uri: string; cid: string },\n\tnow = Date.now(),\n): boolean {\n\treturn labels.some((label) => {\n\t\tif (\n\t\t\tlabel.val !== value ||\n\t\t\tlabel.uri !== record.uri ||\n\t\t\tlabel.cid !== record.cid ||\n\t\t\tlabel.neg === true\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\tif (label.exp === undefined) return true;\n\t\tconst expiresAt = Date.parse(label.exp);\n\t\treturn Number.isFinite(expiresAt) && expiresAt > now;\n\t});\n}\n","/**\n * Registry installs use aggregator results only for discovery and moderation\n * metadata. The handler reads profile and release records from the publisher's\n * PDS, verifies their repository proofs, then validates the bundle checksum,\n * manifest, policy, and consent before storing or activating executable code.\n * Applicable labels can block installation but cannot supply any verified\n * record, checksum, permission, or executable byte.\n */\n\nimport { ClientResponseError, ClientValidationError } from \"@atcute/client\";\nimport type { Did } from \"@atcute/lexicons\";\nimport { canonicalizeDeclaredAccess } from \"@emdash-cms/plugin-types\";\nimport type { CanonicalDeclaredAccess } from \"@emdash-cms/plugin-types\";\nimport { checkEnvCompatibility, findSkippedEnvConstraints } from \"@emdash-cms/registry-client/env\";\nimport type { HostEnv } from \"@emdash-cms/registry-client/env\";\nimport { isProvenFirstRelease } from \"@emdash-cms/registry-client/listing-policy\";\nimport { evaluateRegistryReleaseWithdrawal } from \"@emdash-cms/registry-client/withdrawal\";\nimport { NSID } from \"@emdash-cms/registry-lexicons\";\nimport {\n\tfetchReleaseArtifact,\n\ttype ReleaseArtifactReference,\n} from \"@emdash-cms/registry-verification/artifact\";\nimport type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../../database/types.js\";\nimport { withUnavailableReason } from \"../../plugins/sandbox/types.js\";\nimport type { SandboxRunner } from \"../../plugins/sandbox/types.js\";\nimport { PluginStateRepository } from \"../../plugins/state.js\";\nimport {\n\tremoveAllPluginIndexes,\n\tsyncDeclaredStorageIndexes,\n} from \"../../plugins/storage-indexes.js\";\nimport { declaredAccessToCapabilities } from \"../../plugins/types.js\";\nimport type { DeclaredAccess } from \"../../plugins/types.js\";\nimport { fetchRegistryArtifactUrl } from \"../../registry/artifact-fetch.js\";\nimport {\n\tvalidateRegistryArtifact,\n\ttype RegistryArtifactVerificationCode,\n} from \"../../registry/artifact-verification.js\";\nimport {\n\treadAuthoritativePackageRelease,\n\tverifyAuthoritativePackageRelease,\n\ttype AuthoritativeRecordErrorCode,\n\ttype AuthoritativeRecordReader,\n\ttype AuthoritativeRecordReadOptions,\n\ttype VerifiedAuthoritativeReleaseReport,\n\ttype VerifiedAuthoritativeRecords,\n} from \"../../registry/authoritative-records.js\";\nimport {\n\tcanonicalCapabilitiesForDriftCheck,\n\tcoerceRegistryConfig,\n\tparseDurationSeconds,\n\treleaseExemptFromMinimumAge,\n\tvalidateAggregatorUrl,\n} from \"../../registry/config.js\";\nimport { makeRegistryPluginId } from \"../../registry/plugin-id.js\";\nimport { hasCurrentRecordLabel } from \"../../registry/record-labels.js\";\nimport type { RegistryConfigInput } from \"../../registry/types.js\";\nimport { resolveAndValidateExternalUrlTarget } from \"../../security/ssrf.js\";\nimport { EmDashStorageError } from \"../../storage/types.js\";\nimport type { Storage } from \"../../storage/types.js\";\nimport type { ApiResult } from \"../types.js\";\nimport {\n\tdeleteBundleFromR2,\n\tdiffCapabilities,\n\tdiffRouteVisibility,\n\tloadBundleFromR2,\n\tstoreBundleInR2,\n} from \"./marketplace.js\";\n\nexport { assertSafeArtifactUrl } from \"../../registry/artifact-fetch.js\";\n\n/**\n * Whether two `declaredAccess` blocks grant exactly the same enforced access --\n * the same capabilities AND the same host allow-list. Both are lowered through\n * the canonical converter so that constraint content (`allowedHosts`), not just\n * the capability set, is part of the comparison. The capability-set consent\n * gate is blind to host scope; this is what keeps a bundle from being installed\n * with a wider (or simply different) host allow-list than its published record\n * advertised and the user consented to.\n */\nexport function enforcedAccessEqual(a: DeclaredAccess, b: DeclaredAccess): boolean {\n\tconst aa = declaredAccessToCapabilities(a);\n\tconst bb = declaredAccessToCapabilities(b);\n\treturn (\n\t\tJSON.stringify(aa.capabilities.toSorted()) === JSON.stringify(bb.capabilities.toSorted()) &&\n\t\tJSON.stringify(aa.allowedHosts.toSorted()) === JSON.stringify(bb.allowedHosts.toSorted())\n\t);\n}\n\nfunction verifiedAccessEqual(a: CanonicalDeclaredAccess, b: DeclaredAccess): boolean {\n\treturn JSON.stringify(a) === JSON.stringify(canonicalizeDeclaredAccess(b));\n}\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface RegistryInstallInput {\n\t/**\n\t * Publisher DID. Required. The browser is expected to resolve\n\t * `(handle, slug) → (did, slug)` via the aggregator's\n\t * `resolvePackage` XRPC before posting -- the server then skips that\n\t * round-trip and looks up the package directly.\n\t *\n\t * Passing DID rather than handle here means installs work for\n\t * publishers whose handle the aggregator couldn't resolve at view\n\t * time (handle is \"best-effort\" per the lexicon -- absent for any\n\t * publisher whose DID document didn't resolve cleanly at ingest).\n\t */\n\tdid: string;\n\t/** Package slug (rkey of the publisher's profile record). */\n\tslug: string;\n\t/** Optional explicit version. When omitted, the aggregator's latest. */\n\tversion?: string;\n\t/**\n\t * Capabilities the admin acknowledged in the consent dialog, lifted\n\t * from the release record's `declaredAccess` block. Compared against\n\t * the bundle's `manifest.declaredAccess` to detect drift between\n\t * what the admin agreed to and what the bundle actually requests.\n\t *\n\t * When omitted, drift detection is skipped -- callers that don't\n\t * surface a consent UI before posting (e.g. CI scripts) opt out.\n\t */\n\tacknowledgedDeclaredAccess?: unknown;\n\tacknowledgedMcpTools?: unknown;\n\tacknowledgedPublicRoutes?: unknown;\n\tacknowledgedProfileCid?: string;\n\tacknowledgedReleaseCid?: string;\n}\n\nexport interface RegistryInstallResult {\n\t/** Hashed, opaque plugin id used everywhere in the runtime. */\n\tpluginId: string;\n\t/** Publisher DID resolved from the handle. */\n\tpublisherDid: string;\n\t/** Publisher slug (== the registry slug). */\n\tslug: string;\n\t/** Installed version. */\n\tversion: string;\n\t/** Capabilities surfaced from the bundle's manifest. */\n\tcapabilities: string[];\n\tdeclaredAccess: DeclaredAccess;\n\tmcpTools: RegistryMcpConsentTool[];\n\tpublicRoutes: string[];\n\tverification: RegistryRecordVerificationSummary;\n}\n\nexport interface RegistryMcpConsentTool {\n\tname: string;\n\tdescription: string;\n\troute: string;\n\tpermission: string;\n\tdestructive: boolean;\n}\n\nexport interface RegistryRecordVerificationSummary {\n\tprofileCid: string;\n\treleaseCid: string;\n\tprovenance: \"verified\" | \"absent-optional\";\n\tpolicy: {\n\t\trequireProvenance: boolean;\n\t\tconfirmation: \"escalation-only\" | \"always\";\n\t\tapprovers: string[];\n\t};\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\nexport function registryArtifactError(\n\tcode: RegistryArtifactVerificationCode,\n\tmessage: string,\n\toperation: \"install\" | \"update\",\n): ApiResult<never> {\n\tlet apiCode: string;\n\tswitch (code) {\n\t\tcase \"BUNDLE_ID_MISMATCH\":\n\t\t\tapiCode = operation === \"install\" ? \"MANIFEST_ID_MISMATCH\" : \"BUNDLE_IDENTITY_MISMATCH\";\n\t\t\tbreak;\n\t\tcase \"BUNDLE_VERSION_MISMATCH\":\n\t\t\tapiCode = operation === \"install\" ? \"MANIFEST_VERSION_MISMATCH\" : code;\n\t\t\tbreak;\n\t\tcase \"CHECKSUM_MISMATCH\":\n\t\tcase \"INVALID_MULTIHASH\":\n\t\tcase \"UNSUPPORTED_MULTIHASH\":\n\t\t\tapiCode = \"CHECKSUM_MISMATCH\";\n\t\t\tbreak;\n\t\tcase \"FETCH_FAILED\":\n\t\tcase \"HOST_REJECTED\":\n\t\tcase \"REDIRECT_LIMIT_EXCEEDED\":\n\t\tcase \"REDIRECT_LOCATION_MISSING\":\n\t\tcase \"RESOURCE_STATUS_ERROR\":\n\t\tcase \"RESOURCE_TIMEOUT\":\n\t\t\tapiCode = operation === \"install\" ? \"INSTALL_FAILED\" : \"UPDATE_FAILED\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tapiCode = \"INVALID_BUNDLE\";\n\t}\n\treturn {\n\t\tsuccess: false,\n\t\terror: {\n\t\t\tcode: apiCode,\n\t\t\tmessage,\n\t\t\tdetails: { verificationCode: code },\n\t\t},\n\t};\n}\n\nfunction registryRecordError(\n\tcode: AuthoritativeRecordErrorCode,\n\tmessage: string,\n): ApiResult<never> {\n\treturn {\n\t\tsuccess: false,\n\t\terror: {\n\t\t\tcode: \"RECORD_VERIFICATION_FAILED\",\n\t\t\tmessage,\n\t\t\tdetails: { verificationCode: code },\n\t\t},\n\t};\n}\n\nfunction recordConsentError(\n\tinput: { profileCid?: string; releaseCid?: string },\n\trecords: VerifiedAuthoritativeRecords,\n): ApiResult<never> | null {\n\tif (!input.profileCid || !input.releaseCid) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"RECORD_CONSENT_REQUIRED\",\n\t\t\t\tmessage: \"Verify the signed package records before confirming this action.\",\n\t\t\t},\n\t\t};\n\t}\n\tif (input.profileCid !== records.profile.cid || input.releaseCid !== records.release.cid) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"RECORD_VERIFICATION_DRIFT\",\n\t\t\t\tmessage: \"The signed package records changed after review. Verify them again.\",\n\t\t\t},\n\t\t};\n\t}\n\treturn null;\n}\n\nfunction recordVerificationSummary(\n\trecords: VerifiedAuthoritativeRecords,\n\treport: VerifiedAuthoritativeReleaseReport,\n): RegistryRecordVerificationSummary {\n\treturn {\n\t\tprofileCid: records.profile.cid,\n\t\treleaseCid: records.release.cid,\n\t\tprovenance: report.provenance.status,\n\t\tpolicy: report.value.policy,\n\t};\n}\n\n/**\n * Bytes-per-artifact cap on the gzipped tarball we'll download before\n * decompression. RFC 0001 caps a sandboxed plugin bundle at 256 KiB\n * decompressed (see `MAX_BUNDLE_SIZE` in cli/commands/bundle-utils.ts);\n * gzip on a mix of JSON manifest + JS code typically gives 0.3-0.6\n * ratio, so compressed bundles are well under 200 KiB in practice.\n * 512 KiB leaves margin for unusual file mixes that compress poorly\n * while still rejecting anything that's obviously not a legitimate\n * plugin bundle.\n */\nconst MAX_ARTIFACT_BYTES = 512 * 1024;\n\n/**\n * Maximum number of HTTP redirects followed during artifact download.\n * Each hop is independently URL-validated, so a malicious server cannot\n * redirect through a series of allowed-looking origins to reach a\n * forbidden one.\n */\nconst MAX_REDIRECTS = 5;\n\n/**\n * Wall-clock cap on any single artifact fetch attempt (per URL).\n * Defends against slow-loris artifact sources that accept the connection but\n * never finish sending headers or body.\n */\nconst ARTIFACT_FETCH_TIMEOUT_MS = 15_000;\n\n/**\n * Total wall-clock budget for the artifact-download phase across all\n * advertised caches, the publisher PDS, and the declared URL. Even with the\n * per-URL timeout, unavailable sources could otherwise tie up the install request for\n * minutes; this caps total time at a budget interactive admins can\n * tolerate. Tuned so a fast happy path takes <1s of budget per\n * attempt and a worst case still completes in under a minute.\n */\nconst ARTIFACT_TOTAL_BUDGET_MS = 45_000;\n\n/**\n * Per-request timeout applied to every aggregator XRPC call\n * (`resolvePackage`, `getLatestRelease`, `listReleases`). Matches the\n * per-URL artifact-fetch cap. Without this, a slow-loris aggregator\n * can stall the install before the artifact phase even starts.\n */\nconst AGGREGATOR_REQUEST_TIMEOUT_MS = 15_000;\n\n/**\n * Total wall-clock budget for the aggregator-discovery phase\n * (resolve + selected-release lookup). Mirrors the artifact-download\n * budget. Worst case with the pinned-version path's 20-page cap is\n * 20 + 1 calls; capping the total ensures any one stalled call\n * still bounds the whole phase.\n */\nconst AGGREGATOR_TOTAL_BUDGET_MS = 30_000;\n\nclass RegistryArtifactFetchError extends Error {\n\tconstructor(\n\t\treadonly code: RegistryArtifactVerificationCode,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"RegistryArtifactFetchError\";\n\t}\n}\n\n/** Build a fetch function that enforces a per-request and per-budget timeout. */\nfunction timedFetch(totalDeadline: number): typeof fetch {\n\treturn (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => {\n\t\tconst now = Date.now();\n\t\tconst remaining = Math.max(0, totalDeadline - now);\n\t\tif (remaining === 0) {\n\t\t\treturn Promise.reject(new Error(\"Aggregator request budget exhausted\"));\n\t\t}\n\t\tconst timeout = Math.min(AGGREGATOR_REQUEST_TIMEOUT_MS, remaining);\n\t\tconst controller = new AbortController();\n\t\tconst timer = setTimeout(() => controller.abort(), timeout);\n\t\tconst callerSignal = init?.signal;\n\t\tif (callerSignal) {\n\t\t\tif (callerSignal.aborted) controller.abort(callerSignal.reason);\n\t\t\telse callerSignal.addEventListener(\"abort\", () => controller.abort(callerSignal.reason));\n\t\t}\n\t\treturn fetch(input, { ...init, signal: controller.signal }).finally(() => {\n\t\t\tclearTimeout(timer);\n\t\t});\n\t};\n}\n\nasync function fetchArtifact(\n\tartifactCaches: readonly unknown[],\n\tartifact: ReleaseArtifactReference,\n\trecord: {\n\t\tdid: string;\n\t\tcollection: string;\n\t\trkey: string;\n\t\tcid: string;\n\t},\n\tauth?: unknown,\n): Promise<Uint8Array> {\n\tconst result = await fetchReleaseArtifact(\n\t\t{\n\t\t\tartifact,\n\t\t\trecord,\n\t\t\tartifactCaches,\n\t\t\t...(auth === undefined ? {} : { auth }),\n\t\t},\n\t\t{\n\t\t\tfetch: async (url, init) => {\n\t\t\t\tif (!init.signal) throw new Error(\"Registry artifact fetch requires an abort signal\");\n\t\t\t\treturn fetchRegistryArtifactUrl(url.href, {\n\t\t\t\t\tsignal: init.signal,\n\t\t\t\t\tmaxResponseBytes: MAX_ARTIFACT_BYTES,\n\t\t\t\t});\n\t\t\t},\n\t\t\tresolveHostname: async (hostname) =>\n\t\t\t\t(await resolveAndValidateExternalUrlTarget(`https://${hostname}`)).addresses,\n\t\t\tallowHttpLocalhost: import.meta.env.DEV,\n\t\t\theaderTimeoutMs: ARTIFACT_FETCH_TIMEOUT_MS,\n\t\t\ttotalTimeoutMs: ARTIFACT_TOTAL_BUDGET_MS,\n\t\t\tmaxBytes: MAX_ARTIFACT_BYTES,\n\t\t\tmaxRedirects: MAX_REDIRECTS,\n\t\t},\n\t);\n\tif (!result.success) {\n\t\tthrow new RegistryArtifactFetchError(result.error.code, result.error.message);\n\t}\n\treturn result.value.bytes;\n}\n\n/**\n * The shape of a single env-compatibility failure returned to the admin in\n * the `ENV_INCOMPATIBLE` error's `details`.\n */\ninterface EnvIncompatibleError {\n\tcode: \"ENV_INCOMPATIBLE\";\n\tmessage: string;\n\tdetails: { requires: Record<string, string>; host: HostEnv };\n}\n\n/**\n * Gate a release's `requires` constraints against the running host\n * environment. `requires` is the lexicon-`unknown` value off the signed\n * release record — never trust its shape; `checkEnvCompatibility` guards it.\n *\n * Returns `null` when every advertised constraint is satisfied (or there are\n * none), or a structured `ENV_INCOMPATIBLE` error naming the unsatisfied\n * constraints and the host versions. The error carries the guarded `requires`\n * and `host` maps so the admin can render the same mismatch the UI gate shows.\n */\nexport function assertEnvCompatible(\n\trequires: unknown,\n\thostEnv: HostEnv,\n): EnvIncompatibleError | null {\n\t// A constraint the host can't evaluate (unknown or unparseable host\n\t// version) downgrades the gate to a no-op for that env. Log it so a\n\t// silent bypass is observable rather than invisible.\n\tfor (const skipped of findSkippedEnvConstraints(requires, hostEnv)) {\n\t\tconsole.warn(\n\t\t\t`[registry] env compatibility constraint skipped: ${skipped.key} requires ${skipped.required} but host version is ${skipped.reason}`,\n\t\t);\n\t}\n\tconst mismatches = checkEnvCompatibility(requires, hostEnv);\n\tif (mismatches.length === 0) return null;\n\tconst guarded: Record<string, string> = {};\n\tfor (const m of mismatches) guarded[m.key] = m.required;\n\tconst summary = mismatches\n\t\t.map((m) => `${m.key} requires ${m.required} but host is ${m.host}`)\n\t\t.join(\"; \");\n\treturn {\n\t\tcode: \"ENV_INCOMPATIBLE\",\n\t\tmessage: `This release is not compatible with the current environment: ${summary}.`,\n\t\tdetails: { requires: guarded, host: hostEnv },\n\t};\n}\n\n// ── Install ────────────────────────────────────────────────────────\n\nexport async function handleRegistryInstall(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tsandboxRunner: SandboxRunner | null,\n\tregistryConfigInput: RegistryConfigInput | undefined,\n\tinput: RegistryInstallInput,\n\topts?: {\n\t\tconfiguredPluginIds?: Set<string>;\n\t\thostEnv?: HostEnv;\n\t\tauthoritativeRecords?: AuthoritativeRecordReadOptions;\n\t\treadAuthoritativeRecords?: AuthoritativeRecordReader;\n\t\tverifyOnly?: boolean;\n\t},\n): Promise<ApiResult<RegistryInstallResult>> {\n\t// Accept either the bare-string shorthand or the full\n\t// `RegistryConfig` object (see `RegistryConfigInput`).\n\tconst registryConfig = coerceRegistryConfig(registryConfigInput);\n\tif (!registryConfig) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"REGISTRY_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Registry is not configured\",\n\t\t\t},\n\t\t};\n\t}\n\n\tif (!storage) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"STORAGE_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Storage is required for registry plugin installation\",\n\t\t\t},\n\t\t};\n\t}\n\n\tif (!sandboxRunner || !sandboxRunner.isAvailable()) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"SANDBOX_NOT_AVAILABLE\",\n\t\t\t\tmessage: withUnavailableReason(\n\t\t\t\t\t\"Sandbox runner is required for registry plugins\",\n\t\t\t\t\tsandboxRunner,\n\t\t\t\t),\n\t\t\t},\n\t\t};\n\t}\n\n\t// Defense in depth: validate the aggregator URL even though the same\n\t// check runs at config-normalize time. Keeps every entrypoint into\n\t// `handleRegistryInstall` safe regardless of how the caller obtained\n\t// the config.\n\ttry {\n\t\tvalidateAggregatorUrl(registryConfig.aggregatorUrl);\n\t} catch (err) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"REGISTRY_NOT_CONFIGURED\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Invalid aggregator URL\",\n\t\t\t},\n\t\t};\n\t}\n\n\tconst { did, slug, version: requestedVersion } = input;\n\n\t// Lazy-load the discovery client. Avoids pulling @atcute/client into\n\t// every code path that imports core/api/handlers.\n\tconst { DiscoveryClient, registryLabelerPolicy } =\n\t\tawait import(\"@emdash-cms/registry-client/discovery\");\n\n\t// Every aggregator XRPC call passes through `timedFetch`, which\n\t// enforces a per-request timeout and shares a single total-budget\n\t// deadline. Defends against a slow-loris aggregator stalling the\n\t// install before the artifact phase begins.\n\tconst aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;\n\tconst discovery = new DiscoveryClient({\n\t\taggregatorUrl: registryConfig.aggregatorUrl,\n\t\tacceptLabelers: registryConfig.acceptLabelers,\n\t\tlabelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),\n\t\tfetch: timedFetch(aggregatorDeadline),\n\t});\n\n\t// Basic shape check on the DID. The browser is expected to send a\n\t// DID resolved via the aggregator's `resolvePackage`; reject obvious\n\t// malformations here rather than letting the XRPC call fail\n\t// opaquely. The lexicon's `did:${string}:${string}` template is the\n\t// authoritative check.\n\tif (!did.startsWith(\"did:\") || did.split(\":\").length < 3) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"INVALID_DID\",\n\t\t\t\tmessage: \"DID must be a valid atproto DID (e.g. did:plc:abc123)\",\n\t\t\t},\n\t\t};\n\t}\n\n\ttry {\n\t\t// Step 1: look up the package by DID + slug. The browser already\n\t\t// resolved any handle to a DID via `resolvePackage`; we skip that\n\t\t// round-trip and go straight to `getPackage`.\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- validated above\n\t\tconst publisherDid = did as Did;\n\t\tconst packageView = await discovery.getPackage({\n\t\t\tdid: publisherDid,\n\t\t\tslug,\n\t\t});\n\n\t\t// Step 2: select the target release.\n\t\t// For an explicit version, page through listReleases until we find\n\t\t// the matching record; the aggregator returns releases ordered by\n\t\t// semver descending. For \"latest\", use the dedicated convenience\n\t\t// endpoint which applies the aggregator's policy filter (yanked\n\t\t// exclusion etc.) server-side.\n\t\t//\n\t\t// Pagination is bounded both by total pages and by repeated-cursor\n\t\t// detection: a buggy or compromised aggregator could otherwise\n\t\t// return endless distinct cursors that never include the\n\t\t// requested version, hanging the install for the platform's\n\t\t// request-time budget.\n\t\tconst MAX_LIST_PAGES = 20; // 20 * 50 limit = 1000 releases worth\n\t\tconst latestRelease = await (async () => {\n\t\t\tif (!requestedVersion) {\n\t\t\t\treturn discovery.getLatestRelease({\n\t\t\t\t\tdid: publisherDid,\n\t\t\t\t\tpackage: slug,\n\t\t\t\t});\n\t\t\t}\n\t\t\tlet cursor: string | undefined;\n\t\t\tconst seenCursors = new Set<string>();\n\t\t\tfor (let page = 0; page < MAX_LIST_PAGES; page++) {\n\t\t\t\tif (cursor !== undefined) {\n\t\t\t\t\tif (seenCursors.has(cursor)) break;\n\t\t\t\t\tseenCursors.add(cursor);\n\t\t\t\t}\n\t\t\t\tconst result = await discovery.listReleases({\n\t\t\t\t\tdid: publisherDid,\n\t\t\t\t\tpackage: slug,\n\t\t\t\t\tcursor,\n\t\t\t\t\tlimit: 50,\n\t\t\t\t});\n\t\t\t\tfor (const r of result.releases) {\n\t\t\t\t\tif (r.version === requestedVersion) return r;\n\t\t\t\t}\n\t\t\t\tif (!result.cursor) break;\n\t\t\t\tcursor = result.cursor;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t})();\n\t\tconst releaseView = latestRelease;\n\n\t\tif (!releaseView) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_RELEASE\",\n\t\t\t\t\tmessage: requestedVersion\n\t\t\t\t\t\t? `Version ${requestedVersion} not found for ${publisherDid}/${slug}`\n\t\t\t\t\t\t: `No installable release found for ${publisherDid}/${slug}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// The aggregator selects the package/version and supplies mirrors and\n\t\t// moderation metadata. Its copies of the signed records are not\n\t\t// verification inputs.\n\t\tif (packageView.did !== publisherDid || packageView.slug !== slug) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_IDENTITY_MISMATCH\",\n\t\t\t\t\tmessage: \"Aggregator returned a package view for a different publisher or slug.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (\n\t\t\treleaseView.did !== publisherDid ||\n\t\t\treleaseView.package !== slug ||\n\t\t\t(requestedVersion !== undefined && releaseView.version !== requestedVersion)\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_IDENTITY_MISMATCH\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Aggregator returned a release view that does not match the requested package or version.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst version = releaseView.version;\n\t\tif (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"RELEASE_YANKED\",\n\t\t\t\t\tmessage: \"This release has been withdrawn\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst authoritative = await (opts?.readAuthoritativeRecords ?? readAuthoritativePackageRelease)(\n\t\t\tpublisherDid,\n\t\t\tslug,\n\t\t\tversion,\n\t\t\topts?.authoritativeRecords,\n\t\t);\n\t\tif (!authoritative.success) {\n\t\t\treturn registryRecordError(authoritative.error.code, authoritative.error.message);\n\t\t}\n\t\tconst records = authoritative.value;\n\t\tconst { profile, release } = records.inspection.value;\n\t\tif (\n\t\t\tpackageView.uri !== records.profile.uri ||\n\t\t\tpackageView.cid !== records.profile.cid ||\n\t\t\treleaseView.uri !== records.release.uri ||\n\t\t\treleaseView.cid !== records.release.cid\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_RECORD_MISMATCH\",\n\t\t\t\t\tmessage: \"Aggregator metadata does not match the publisher's signed records.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (!opts?.verifyOnly) {\n\t\t\tconst consentError = recordConsentError(\n\t\t\t\t{\n\t\t\t\t\tprofileCid: input.acknowledgedProfileCid,\n\t\t\t\t\treleaseCid: input.acknowledgedReleaseCid,\n\t\t\t\t},\n\t\t\t\trecords,\n\t\t\t);\n\t\t\tif (consentError) return consentError;\n\t\t}\n\n\t\tconst packageYanked =\n\t\t\thasCurrentRecordLabel(packageView.labels ?? [], \"security:yanked\", records.profile) ||\n\t\t\thasCurrentRecordLabel(packageView.labels ?? [], \"security-yanked\", records.profile);\n\t\tif (packageYanked) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"RELEASE_YANKED\",\n\t\t\t\t\tmessage: \"This release has been withdrawn\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Environment compatibility remains an install-safety gate. Listing\n\t\t// approval says only that displayed metadata passed moderation. A release\n\t\t// may carry a `requires` block (`env:emdash`, `env:astro`, ...). Refuse\n\t\t// the install if the running host doesn't satisfy a constraint, so a\n\t\t// stale browser tab or non-UI caller can't bypass the admin's\n\t\t// disabled Install button. `requires` is lexicon-`unknown`; the\n\t\t// helper guards its shape.\n\t\tif (opts?.hostEnv) {\n\t\t\tconst envError = assertEnvCompatible(release.requires, opts.hostEnv);\n\t\t\tif (envError) return { success: false, error: envError };\n\t\t}\n\n\t\t// Step 3a: enforce the configured minimum release age. The browser\n\t\t// applies the same check up front for UX, but the gate lives here\n\t\t// -- a stale browser tab, a deep link, or a non-admin-UI caller\n\t\t// must still hit the holdback. The `minimumReleaseAgeExclude`\n\t\t// allowlist short-circuits the check for trusted publisher DIDs.\n\t\t//\n\t\t// `releaseView.indexedAt` is aggregator operational data, not part\n\t\t// of the signed release. The release schema has no publication\n\t\t// timestamp, so minimum age remains a local discovery holdback\n\t\t// rather than a cryptographic property. A missing or malformed\n\t\t// timestamp fails closed.\n\t\t// `registryConfig` is the user-supplied integration option, not\n\t\t// the normalized manifest shape, so the duration parse runs once\n\t\t// per install. Catch a malformed value here -- normally caught at\n\t\t// `normalizeRegistryConfig` time, but a future config-mutation\n\t\t// path could re-enter with a bad value -- and surface it as a\n\t\t// structured error rather than letting it bubble out as a generic\n\t\t// 500.\n\t\tconst minimumReleaseAge = registryConfig.policy?.minimumReleaseAge;\n\t\tlet minimumReleaseAgeSeconds = 0;\n\t\tif (minimumReleaseAge !== undefined) {\n\t\t\ttry {\n\t\t\t\tminimumReleaseAgeSeconds = parseDurationSeconds(minimumReleaseAge);\n\t\t\t} catch (err) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"REGISTRY_POLICY_INVALID\",\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\terr instanceof Error\n\t\t\t\t\t\t\t\t? err.message\n\t\t\t\t\t\t\t\t: \"Invalid minimumReleaseAge value in registry config\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tif (minimumReleaseAgeSeconds > 0) {\n\t\t\tconst exclude = registryConfig.policy?.minimumReleaseAgeExclude?.map((e) =>\n\t\t\t\te.trim().toLowerCase(),\n\t\t\t);\n\t\t\tconst exempt =\n\t\t\t\treleaseExemptFromMinimumAge(exclude, publisherDid, slug) ||\n\t\t\t\tisProvenFirstRelease(packageView);\n\t\t\tif (!exempt) {\n\t\t\t\tconst indexedAt = Date.parse(releaseView.indexedAt);\n\t\t\t\tif (!Number.isFinite(indexedAt)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tcode: \"RELEASE_TIMESTAMP_INVALID\",\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"Release record is missing a valid indexed-at timestamp; cannot evaluate minimum release age policy.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tconst ageSeconds = (Date.now() - indexedAt) / 1000;\n\t\t\t\tif (ageSeconds < minimumReleaseAgeSeconds) {\n\t\t\t\t\tconst remaining = Math.ceil(minimumReleaseAgeSeconds - ageSeconds);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tcode: \"RELEASE_TOO_NEW\",\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`This release does not meet the configured minimum release age of ` +\n\t\t\t\t\t\t\t\t`${minimumReleaseAgeSeconds}s. It will be installable in ~${remaining}s.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Derive the normalized opaque plugin id we'll use as the\n\t\t// runtime-wide identifier from here on. The publisher_did + slug\n\t\t// stay in the state row for update resolution and admin display.\n\t\tconst pluginId = await makeRegistryPluginId(publisherDid, slug);\n\n\t\t// Block installation if a configured (trusted) plugin shares this\n\t\t// id. Mirrors the marketplace install's PLUGIN_ID_CONFLICT check.\n\t\tif (opts?.configuredPluginIds?.has(pluginId)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"PLUGIN_ID_CONFLICT\",\n\t\t\t\t\tmessage: \"A configured plugin with the same derived id already exists\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Check for an existing install (any source) under the derived id.\n\t\t// We reject all pre-existing rows -- if the row is from a registry\n\t\t// install of this same package, the caller should go through the\n\t\t// (future) update flow; if it's from any other source, the\n\t\t// pluginId collision means installing would silently mutate an\n\t\t// unrelated plugin's lifecycle row.\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (existing) {\n\t\t\tif (existing.source === \"registry\") {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"ALREADY_INSTALLED\",\n\t\t\t\t\t\tmessage: `Plugin ${publisherDid}/${slug} is already installed`,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"PLUGIN_ID_COLLISION\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`A non-registry plugin already exists at the derived id ${pluginId}. ` +\n\t\t\t\t\t\t\"Uninstall it before installing this registry plugin.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Step 6: fetch bytes through an aggregator cache, the publisher PDS,\n\t\t// or the URL in the authoritative signed release. Every source remains untrusted.\n\t\tconst packageArtifact = release.artifacts.package;\n\t\tconst declaredChecksum = release.artifacts.package.checksum;\n\n\t\tif (!packageArtifact || !declaredChecksum) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"INVALID_RELEASE\",\n\t\t\t\t\tmessage: \"Release record is missing its package artifact or checksum\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tlet artifactBytes: Uint8Array;\n\t\ttry {\n\t\t\tartifactBytes = await fetchArtifact(\n\t\t\t\treleaseView.artifactCaches ?? [],\n\t\t\t\tpackageArtifact,\n\t\t\t\t{\n\t\t\t\t\tdid: publisherDid,\n\t\t\t\t\tcollection: NSID.packageRelease,\n\t\t\t\t\trkey: `${slug}:${version}`,\n\t\t\t\t\tcid: releaseView.cid,\n\t\t\t\t},\n\t\t\t\trelease.auth,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (error instanceof RegistryArtifactFetchError) {\n\t\t\t\treturn registryArtifactError(error.code, error.message, \"install\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Steps 6-7: verify the signed checksum, archive, manifest, and\n\t\t// expected package identity with the runtime-neutral verifier used\n\t\t// by the release service.\n\t\tconst artifactReport = await validateRegistryArtifact(\n\t\t\tartifactBytes,\n\t\t\tdeclaredChecksum,\n\t\t\tslug,\n\t\t\tversion,\n\t\t);\n\t\tif (!artifactReport.success) {\n\t\t\treturn registryArtifactError(\n\t\t\t\tartifactReport.error.code,\n\t\t\t\tartifactReport.error.message,\n\t\t\t\t\"install\",\n\t\t\t);\n\t\t}\n\t\tconst { bundle, artifactDigest, artifactDigests } = artifactReport.value;\n\t\tconst recordReport = await verifyAuthoritativePackageRelease(records, artifactDigest, {\n\t\t\t...opts?.authoritativeRecords,\n\t\t\tartifactDigests,\n\t\t});\n\t\tif (!recordReport.success) {\n\t\t\treturn registryRecordError(\n\t\t\t\trecordReport.code,\n\t\t\t\trecordReport.reasons[0]?.message ?? \"The release provenance is invalid.\",\n\t\t\t);\n\t\t}\n\t\tconst verification = recordVerificationSummary(records, recordReport);\n\n\t\t// Rewrite the manifest's id to the derived opaque pluginId before\n\t\t// it reaches R2 storage or the sandbox loader. The sandbox uses\n\t\t// `manifest.id` as its identity for per-plugin storage and bridge\n\t\t// calls; addressing it by the same pluginId we use in the runtime\n\t\t// cache, R2 prefix, and `_plugin_state` row keeps every layer\n\t\t// in sync and prevents registry installs from colliding with\n\t\t// marketplace plugins that happen to share the publisher's slug.\n\t\tbundle.manifest = { ...bundle.manifest, id: pluginId };\n\n\t\t// Integrity: the bundle that will run MUST declare exactly the access\n\t\t// the signed release record advertises. The consent dialog is driven\n\t\t// from the record's `declaredAccess`, so a bundle enforcing something\n\t\t// different -- a wider host allow-list, an extra capability -- would run\n\t\t// outside what the user reviewed. The capability-set consent gate below\n\t\t// is blind to constraint content (host scope), so compare the full\n\t\t// enforced access of record vs bundle here and refuse on any difference.\n\t\tif (\n\t\t\t!verifiedAccessEqual(recordReport.value.declaredAccess, bundle.manifest.declaredAccess ?? {})\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"DECLARED_ACCESS_DRIFT\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"The plugin bundle declares different permissions than its published record. Installation refused.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Capability consent gate: the admin MUST acknowledge the\n\t\t// capabilities the bundle's manifest actually declares before we\n\t\t// install it. The bundle manifest is the runtime enforcement\n\t\t// currency; the exact-equality check above binds it to the\n\t\t// independently verified signed release.\n\t\t//\n\t\t// Two outcomes after normalization (filter to strings, dedupe,\n\t\t// sort):\n\t\t//\n\t\t// 1. The bundle declares no capabilities: install is allowed\n\t\t// without any acknowledgement (nothing to consent to).\n\t\t// 2. The bundle declares capabilities: install requires the\n\t\t// caller to send `acknowledgedDeclaredAccess`, and the\n\t\t// sorted lists must match exactly.\n\t\t//\n\t\t// We compare against the bundle's *capabilities* (the legacy\n\t\t// shape) for v1 because EmDash's existing sandbox enforces\n\t\t// capabilities, not the RFC's structured `declaredAccess`. Once\n\t\t// the runtime starts enforcing `declaredAccess` natively, this\n\t\t// comparison switches to that shape.\n\t\tconst actualCapabilities = canonicalCapabilitiesForDriftCheck(bundle.manifest.capabilities);\n\t\tif (!opts?.verifyOnly && actualCapabilities.length > 0) {\n\t\t\tif (input.acknowledgedDeclaredAccess === undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"DECLARED_ACCESS_REQUIRED\",\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"This plugin declares capabilities that require consent. Re-open the install dialog to review and acknowledge them.\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst acknowledged = canonicalCapabilitiesForDriftCheck(input.acknowledgedDeclaredAccess);\n\t\t\tif (\n\t\t\t\tacknowledged.length !== actualCapabilities.length ||\n\t\t\t\tacknowledged.some((cap, i) => cap !== actualCapabilities[i])\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"DECLARED_ACCESS_DRIFT\",\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"Plugin manifest has changed since you consented. Re-open the install dialog to review the new permissions.\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconst actualMcpTools = (bundle.manifest.mcp?.tools ?? []).map(\n\t\t\t({ inputSchema: _, outputSchema: __, ...tool }) => tool,\n\t\t);\n\t\tconst publicRoutes = diffRouteVisibility(undefined, bundle.manifest).newlyPublic.toSorted();\n\t\tif (!opts?.verifyOnly && publicRoutes.length > 0) {\n\t\t\tconst acknowledgedPublicRoutes = Array.isArray(input.acknowledgedPublicRoutes)\n\t\t\t\t? input.acknowledgedPublicRoutes\n\t\t\t\t\t\t.filter((route): route is string => typeof route === \"string\")\n\t\t\t\t\t\t.toSorted()\n\t\t\t\t: [];\n\t\t\tif (JSON.stringify(acknowledgedPublicRoutes) !== JSON.stringify(publicRoutes)) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"ROUTE_VISIBILITY_ESCALATION\",\n\t\t\t\t\t\tmessage: \"Plugin install exposes public (unauthenticated) routes\",\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\trouteVisibilityChanges: { newlyPublic: publicRoutes },\n\t\t\t\t\t\t\tmcpTools: actualMcpTools,\n\t\t\t\t\t\t\tverification,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tif (!opts?.verifyOnly && actualMcpTools.length > 0) {\n\t\t\tif (JSON.stringify(input.acknowledgedMcpTools) !== JSON.stringify(actualMcpTools)) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"MCP_TOOL_CONSENT_REQUIRED\",\n\t\t\t\t\t\tmessage: \"Plugin MCP tools require explicit consent\",\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\tmcpTools: actualMcpTools,\n\t\t\t\t\t\t\trouteVisibilityChanges:\n\t\t\t\t\t\t\t\tpublicRoutes.length > 0 ? { newlyPublic: publicRoutes } : undefined,\n\t\t\t\t\t\t\tverification,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconst result: RegistryInstallResult = {\n\t\t\tpluginId,\n\t\t\tpublisherDid,\n\t\t\tslug,\n\t\t\tversion,\n\t\t\tcapabilities: bundle.manifest.capabilities,\n\t\t\tdeclaredAccess: recordReport.value.releaseExtension.declaredAccess,\n\t\t\tmcpTools: actualMcpTools,\n\t\t\tpublicRoutes,\n\t\t\tverification,\n\t\t};\n\t\tif (opts?.verifyOnly) return { success: true, data: result };\n\n\t\t// Step 7: store in R2 under the registry prefix.\n\t\tawait storeBundleInR2(storage, pluginId, version, bundle, \"registry\");\n\n\t\t// Step 8: write plugin state.\n\t\t// Display name and description come from the *package profile*\n\t\t// (the signed record from the publisher's repo), not from the\n\t\t// bundle manifest -- the manifest carries the trust contract,\n\t\t// the profile carries the marketing copy.\n\t\t//\n\t\t// On failure, we may need to clean up the R2 bundle we just\n\t\t// wrote. But two parallel installs of the same (did, slug,\n\t\t// version) both pass the earlier `existing` check at line 822\n\t\t// (the read is not transactional with the insert), both upload\n\t\t// to the same deterministic R2 prefix (overwrites are\n\t\t// content-identical because R2 keys include the version and\n\t\t// the bundle is checksum-verified upstream), and then one wins\n\t\t// the insert while the other fails with a PK constraint\n\t\t// violation.\n\t\t//\n\t\t// If we blindly clean up R2 on every state-write failure, the\n\t\t// loser of that race would delete the winner's bundle and the\n\t\t// runtime would fail to load the plugin on the next sync.\n\t\t//\n\t\t// Instead: on state-write failure, re-query the state row. If\n\t\t// a row now exists for this pluginId, we lost the race -- the\n\t\t// winner owns the R2 bundle and we must not touch it. If the\n\t\t// row doesn't exist, the failure was a real DB error and the\n\t\t// R2 bytes are orphans; clean them up.\n\t\t//\n\t\t// Cleanup is best-effort; if it also fails, the row failure\n\t\t// still surfaces to the caller and the orphan R2 bundle costs\n\t\t// only the storage of a single checksum-verified zip.\n\t\ttry {\n\t\t\tawait stateRepo.upsert(pluginId, version, \"active\", {\n\t\t\t\tsource: \"registry\",\n\t\t\t\tdisplayName: profile.name ?? slug,\n\t\t\t\tdescription: profile.description ?? undefined,\n\t\t\t\tregistryPublisherDid: publisherDid,\n\t\t\t\tregistrySlug: slug,\n\t\t\t});\n\t\t} catch (stateErr) {\n\t\t\tlet lostRace = false;\n\t\t\ttry {\n\t\t\t\tconst winner = await stateRepo.get(pluginId);\n\t\t\t\tlostRace = winner !== undefined && winner !== null;\n\t\t\t} catch (probeErr) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[registry-install] Failed to probe state row for ${pluginId} after state-write failure; treating as orphan:`,\n\t\t\t\t\tprobeErr,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!lostRace) {\n\t\t\t\ttry {\n\t\t\t\t\tawait deleteBundleFromR2(storage, pluginId, version, \"registry\");\n\t\t\t\t} catch (cleanupErr) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[registry-install] Failed to clean up R2 bundle for ${pluginId}@${version} after state-row write failure:`,\n\t\t\t\t\t\tcleanupErr,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow stateErr;\n\t\t}\n\n\t\tawait syncDeclaredStorageIndexes(db, [bundle.manifest]);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: result,\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof ClientValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_RESPONSE_INVALID\",\n\t\t\t\t\tmessage: `Aggregator returned a response that does not conform to its lexicon (${err.target})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof ClientResponseError) {\n\t\t\tif (err.error === \"ListingUnavailable\") {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"LISTING_UNAVAILABLE\",\n\t\t\t\t\t\tmessage: \"This plugin is unavailable under the active registry policy\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.status === 404 ? \"AGGREGATOR_NOT_FOUND\" : \"AGGREGATOR_HTTP_ERROR\",\n\t\t\t\t\tmessage: `Aggregator returned ${err.status}: ${err.error}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof EmDashStorageError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.code ?? \"STORAGE_ERROR\",\n\t\t\t\t\tmessage: \"Storage error while installing plugin\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[registry-install] Failed:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"INSTALL_FAILED\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Failed to install plugin from registry\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Uninstall ──────────────────────────────────────────────────────\n\nexport interface RegistryUninstallResult {\n\tpluginId: string;\n\t/** True when `_plugin_storage` rows were also deleted (opts.deleteData). */\n\tdataDeleted: boolean;\n}\n\n/**\n * Uninstall a registry-source plugin. Deletes the R2 bundle under\n * `registry/<pluginId>/<version>/`, optionally drops the plugin's\n * `_plugin_storage` rows, and removes the `_plugin_state` row. The\n * sandbox runtime is reconciled by the route's `syncRegistryPlugins`\n * call after this returns.\n *\n * Refuses to uninstall plugins whose `source` is not `\"registry\"` to\n * avoid trashing a marketplace/config plugin that happens to share the\n * pluginId namespace.\n */\nexport async function handleRegistryUninstall(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tpluginId: string,\n\topts?: { deleteData?: boolean; beforeDelete?: () => Promise<void> },\n): Promise<ApiResult<RegistryUninstallResult>> {\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (!existing || existing.source !== \"registry\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `No registry plugin found: ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// `_plugin_state.version` carries the installed version directly for\n\t\t// registry-source rows (there's no shadow column like marketplace's\n\t\t// `marketplaceVersion`). Use it verbatim for the R2 prefix.\n\t\tconst version = existing.version;\n\t\tawait opts?.beforeDelete?.();\n\n\t\t// Lifecycle cleanup runs before every destructive step so the plugin can\n\t\t// inspect its stored state. The database cleanup then runs before the\n\t\t// idempotent bundle delete, leaving the state row and bundle intact if a\n\t\t// transient database error makes the uninstall retryable.\n\t\tlet dataDeleted = false;\n\t\tif (opts?.deleteData) {\n\t\t\tawait db.deleteFrom(\"_plugin_storage\").where(\"plugin_id\", \"=\", pluginId).execute();\n\t\t\tdataDeleted = true;\n\t\t}\n\n\t\ttry {\n\t\t\tawait removeAllPluginIndexes(db, pluginId);\n\t\t} catch {\n\t\t\t// Nothing to drop, or tracking table predates the feature\n\t\t}\n\n\t\tawait stateRepo.delete(pluginId);\n\n\t\tif (storage) {\n\t\t\tawait deleteBundleFromR2(storage, pluginId, version, \"registry\");\n\t\t}\n\n\t\treturn { success: true, data: { pluginId, dataDeleted } };\n\t} catch (err) {\n\t\tconsole.error(\"[registry-uninstall] Failed:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"UNINSTALL_FAILED\",\n\t\t\t\tmessage: \"Failed to uninstall plugin\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Update ─────────────────────────────────────────────────────────\n\nexport interface RegistryUpdateResult {\n\tpluginId: string;\n\toldVersion: string;\n\tnewVersion: string;\n\tcapabilityChanges: { added: string[]; removed: string[] };\n\t/** Set only when `newlyPublic` is non-empty, mirroring marketplace. */\n\trouteVisibilityChanges?: { newlyPublic: string[] };\n\tverification: RegistryRecordVerificationSummary;\n}\n\n/**\n * Update a registry-source plugin to a newer release. Mirrors\n * `handleMarketplaceUpdate`: resolves the target version via the aggregator,\n * re-runs the artifact fetch / checksum / extract pipeline, diffs capabilities\n * and route visibility against the currently installed bundle, and gates\n * escalations behind `confirmCapabilityChanges` and an exact\n * `acknowledgedPublicRoutes` match so the admin re-consents to widened permissions.\n *\n * Refuses non-registry sources. Refuses when the stored state row is missing\n * the `(publisherDid, slug)` it needs to resolve against the aggregator.\n */\nexport async function handleRegistryUpdate(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tsandboxRunner: SandboxRunner | null,\n\tregistryConfigInput: RegistryConfigInput | undefined,\n\tpluginId: string,\n\topts?: {\n\t\tversion?: string;\n\t\tconfirmCapabilityChanges?: boolean;\n\t\tacknowledgedPublicRoutes?: string[];\n\t\tconfirmMcpTools?: boolean;\n\t\tacknowledgedProfileCid?: string;\n\t\tacknowledgedReleaseCid?: string;\n\t\thostEnv?: HostEnv;\n\t\tauthoritativeRecords?: AuthoritativeRecordReadOptions;\n\t\treadAuthoritativeRecords?: AuthoritativeRecordReader;\n\t},\n): Promise<ApiResult<RegistryUpdateResult>> {\n\tconst registryConfig = coerceRegistryConfig(registryConfigInput);\n\tif (!registryConfig) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"REGISTRY_NOT_CONFIGURED\", message: \"Registry is not configured\" },\n\t\t};\n\t}\n\tif (!storage) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"STORAGE_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Storage is required for registry plugin updates\",\n\t\t\t},\n\t\t};\n\t}\n\tif (!sandboxRunner || !sandboxRunner.isAvailable()) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"SANDBOX_NOT_AVAILABLE\",\n\t\t\t\tmessage: withUnavailableReason(\"Sandbox runner is required\", sandboxRunner),\n\t\t\t},\n\t\t};\n\t}\n\ttry {\n\t\tvalidateAggregatorUrl(registryConfig.aggregatorUrl);\n\t} catch (err) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"REGISTRY_NOT_CONFIGURED\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Invalid aggregator URL\",\n\t\t\t},\n\t\t};\n\t}\n\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (!existing || existing.source !== \"registry\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: `No registry plugin found: ${pluginId}` },\n\t\t\t};\n\t\t}\n\t\tif (!existing.registryPublisherDid || !existing.registrySlug) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"INVALID_STATE\",\n\t\t\t\t\tmessage: `Registry plugin ${pluginId} is missing publisher DID or slug in state`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst oldVersion = existing.version;\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- existing.registryPublisherDid is a DID string written by the install handler\n\t\tconst publisherDid = existing.registryPublisherDid as Did;\n\t\tconst slug = existing.registrySlug;\n\n\t\tconst { DiscoveryClient, registryLabelerPolicy } =\n\t\t\tawait import(\"@emdash-cms/registry-client/discovery\");\n\t\tconst aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;\n\t\tconst discovery = new DiscoveryClient({\n\t\t\taggregatorUrl: registryConfig.aggregatorUrl,\n\t\t\tacceptLabelers: registryConfig.acceptLabelers,\n\t\t\tlabelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),\n\t\t\tfetch: timedFetch(aggregatorDeadline),\n\t\t});\n\n\t\t// Resolve target release. Explicit version → paginate listReleases;\n\t\t// otherwise getLatestRelease (aggregator applies its own filters).\n\t\tconst MAX_LIST_PAGES = 20;\n\t\tconst releaseView = await (async () => {\n\t\t\tif (!opts?.version) {\n\t\t\t\treturn discovery.getLatestRelease({ did: publisherDid, package: slug });\n\t\t\t}\n\t\t\tlet cursor: string | undefined;\n\t\t\tconst seenCursors = new Set<string>();\n\t\t\tfor (let page = 0; page < MAX_LIST_PAGES; page++) {\n\t\t\t\tif (cursor !== undefined) {\n\t\t\t\t\tif (seenCursors.has(cursor)) break;\n\t\t\t\t\tseenCursors.add(cursor);\n\t\t\t\t}\n\t\t\t\tconst result = await discovery.listReleases({\n\t\t\t\t\tdid: publisherDid,\n\t\t\t\t\tpackage: slug,\n\t\t\t\t\tcursor,\n\t\t\t\t\tlimit: 50,\n\t\t\t\t});\n\t\t\t\tfor (const r of result.releases) {\n\t\t\t\t\tif (r.version === opts.version) return r;\n\t\t\t\t}\n\t\t\t\tif (!result.cursor) break;\n\t\t\t\tcursor = result.cursor;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t})();\n\n\t\tif (!releaseView) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_VERSION\",\n\t\t\t\t\tmessage: opts?.version\n\t\t\t\t\t\t? `Version ${opts.version} not found for ${publisherDid}/${slug}`\n\t\t\t\t\t\t: `No installable release found for ${publisherDid}/${slug}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// The aggregator selects the target version and supplies mirrors and\n\t\t// moderation metadata. Its release-record copy is not trusted.\n\t\tif (\n\t\t\treleaseView.did !== publisherDid ||\n\t\t\treleaseView.package !== slug ||\n\t\t\t(opts?.version !== undefined && releaseView.version !== opts.version)\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_IDENTITY_MISMATCH\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Aggregator returned a release view that does not match the requested package or version.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst packageView = await discovery.getPackage({ did: publisherDid, slug });\n\t\tif (!packageView || packageView.did !== publisherDid || packageView.slug !== slug) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_IDENTITY_MISMATCH\",\n\t\t\t\t\tmessage: \"Aggregator returned a package view that does not match the installed plugin.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst newVersion = releaseView.version;\n\t\tif (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"YANKED\", message: \"Release has been withdrawn\" },\n\t\t\t};\n\t\t}\n\t\tif (newVersion === oldVersion) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ALREADY_UP_TO_DATE\",\n\t\t\t\t\tmessage: \"Plugin is already at the requested version\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst authoritative = await (opts?.readAuthoritativeRecords ?? readAuthoritativePackageRelease)(\n\t\t\tpublisherDid,\n\t\t\tslug,\n\t\t\tnewVersion,\n\t\t\topts?.authoritativeRecords,\n\t\t);\n\t\tif (!authoritative.success) {\n\t\t\treturn registryRecordError(authoritative.error.code, authoritative.error.message);\n\t\t}\n\t\tconst records = authoritative.value;\n\t\tconst { profile, release } = records.inspection.value;\n\t\tif (\n\t\t\tpackageView.uri !== records.profile.uri ||\n\t\t\tpackageView.cid !== records.profile.cid ||\n\t\t\treleaseView.uri !== records.release.uri ||\n\t\t\treleaseView.cid !== records.release.cid\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_RECORD_MISMATCH\",\n\t\t\t\t\tmessage: \"Aggregator metadata does not match the publisher's signed records.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst packageYanked =\n\t\t\thasCurrentRecordLabel(packageView.labels ?? [], \"security:yanked\", records.profile) ||\n\t\t\thasCurrentRecordLabel(packageView.labels ?? [], \"security-yanked\", records.profile);\n\t\tif (packageYanked) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"YANKED\", message: \"Package has been withdrawn\" },\n\t\t\t};\n\t\t}\n\t\tif (\n\t\t\topts?.confirmCapabilityChanges ||\n\t\t\t(opts?.acknowledgedPublicRoutes?.length ?? 0) > 0 ||\n\t\t\topts?.confirmMcpTools\n\t\t) {\n\t\t\tconst consentError = recordConsentError(\n\t\t\t\t{\n\t\t\t\t\tprofileCid: opts?.acknowledgedProfileCid,\n\t\t\t\t\treleaseCid: opts?.acknowledgedReleaseCid,\n\t\t\t\t},\n\t\t\t\trecords,\n\t\t\t);\n\t\t\tif (consentError) return consentError;\n\t\t}\n\t\t// Environment compatibility remains independent from listing approval.\n\t\t// An ungated update could otherwise\n\t\t// land a version whose `requires` the host doesn't satisfy. Same\n\t\t// guard as install; `requires` is lexicon-`unknown`.\n\t\tif (opts?.hostEnv) {\n\t\t\tconst envError = assertEnvCompatible(release.requires, opts.hostEnv);\n\t\t\tif (envError) return { success: false, error: envError };\n\t\t}\n\n\t\tconst packageArtifact = release.artifacts.package;\n\t\tconst declaredChecksum = packageArtifact?.checksum;\n\t\tif (!packageArtifact || !declaredChecksum) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"INVALID_RELEASE\",\n\t\t\t\t\tmessage: \"Release record is missing its package artifact or checksum\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tlet artifactBytes: Uint8Array;\n\t\ttry {\n\t\t\tartifactBytes = await fetchArtifact(\n\t\t\t\treleaseView.artifactCaches ?? [],\n\t\t\t\tpackageArtifact,\n\t\t\t\t{\n\t\t\t\t\tdid: publisherDid,\n\t\t\t\t\tcollection: NSID.packageRelease,\n\t\t\t\t\trkey: `${slug}:${newVersion}`,\n\t\t\t\t\tcid: releaseView.cid,\n\t\t\t\t},\n\t\t\t\trelease.auth,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (error instanceof RegistryArtifactFetchError) {\n\t\t\t\treturn registryArtifactError(error.code, error.message, \"update\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tconst artifactReport = await validateRegistryArtifact(\n\t\t\tartifactBytes,\n\t\t\tdeclaredChecksum,\n\t\t\tslug,\n\t\t\tnewVersion,\n\t\t);\n\t\tif (!artifactReport.success) {\n\t\t\treturn registryArtifactError(\n\t\t\t\tartifactReport.error.code,\n\t\t\t\tartifactReport.error.message,\n\t\t\t\t\"update\",\n\t\t\t);\n\t\t}\n\t\tconst { bundle, artifactDigest, artifactDigests } = artifactReport.value;\n\t\tconst recordReport = await verifyAuthoritativePackageRelease(records, artifactDigest, {\n\t\t\t...opts?.authoritativeRecords,\n\t\t\tartifactDigests,\n\t\t});\n\t\tif (!recordReport.success) {\n\t\t\treturn registryRecordError(\n\t\t\t\trecordReport.code,\n\t\t\t\trecordReport.reasons[0]?.message ?? \"The release provenance is invalid.\",\n\t\t\t);\n\t\t}\n\t\tconst verification = recordVerificationSummary(records, recordReport);\n\n\t\t// Rewrite manifest.id to the opaque pluginId so the sandbox loader\n\t\t// and R2 layout stay in sync across install and update.\n\t\tbundle.manifest = { ...bundle.manifest, id: pluginId };\n\n\t\t// Integrity: same gate as install. The new bundle must declare exactly\n\t\t// the access its signed release record advertises. Without it, an update\n\t\t// that changes only the host scope (e.g. api.good.com -> evil.com) keeps\n\t\t// the capability set identical, sails through the escalation diff below,\n\t\t// and installs a bundle enforcing a scope the record never showed.\n\t\tif (\n\t\t\t!verifiedAccessEqual(recordReport.value.declaredAccess, bundle.manifest.declaredAccess ?? {})\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"DECLARED_ACCESS_DRIFT\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"The plugin bundle declares different permissions than its published record. Update refused.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Diff capabilities + route visibility against the currently\n\t\t// installed bundle. Loading from R2 keeps us honest: the diff is\n\t\t// against the bytes the sandbox is actually running, not whatever\n\t\t// the state row claims.\n\t\tconst oldBundle = await loadBundleFromR2(storage, pluginId, oldVersion, \"registry\");\n\t\tconst oldCaps = oldBundle?.manifest.capabilities ?? [];\n\t\tconst capabilityChanges = diffCapabilities(oldCaps, bundle.manifest.capabilities);\n\t\tconst hasEscalation = capabilityChanges.added.length > 0;\n\t\tif (hasEscalation && !opts?.confirmCapabilityChanges) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CAPABILITY_ESCALATION\",\n\t\t\t\t\tmessage: \"Plugin update requires new capabilities\",\n\t\t\t\t\tdetails: { capabilityChanges, verification },\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst routeVisibilityChanges = diffRouteVisibility(oldBundle?.manifest, bundle.manifest);\n\t\tconst newlyPublicRoutes = routeVisibilityChanges.newlyPublic.toSorted();\n\t\tconst acknowledgedPublicRoutes = (opts?.acknowledgedPublicRoutes ?? [])\n\t\t\t.filter((route): route is string => typeof route === \"string\")\n\t\t\t.toSorted();\n\t\tif (\n\t\t\tnewlyPublicRoutes.length > 0 &&\n\t\t\tJSON.stringify(acknowledgedPublicRoutes) !== JSON.stringify(newlyPublicRoutes)\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ROUTE_VISIBILITY_ESCALATION\",\n\t\t\t\t\tmessage: \"Plugin update exposes new public (unauthenticated) routes\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\trouteVisibilityChanges: { newlyPublic: newlyPublicRoutes },\n\t\t\t\t\t\tcapabilityChanges,\n\t\t\t\t\t\tverification,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst oldMcpTools = [...(oldBundle?.manifest.mcp?.tools ?? [])].toSorted((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t);\n\t\tconst newMcpTools = [...(bundle.manifest.mcp?.tools ?? [])].toSorted((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t);\n\t\tif (JSON.stringify(oldMcpTools) !== JSON.stringify(newMcpTools) && !opts?.confirmMcpTools) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MCP_TOOL_CONSENT_REQUIRED\",\n\t\t\t\t\tmessage: \"Plugin update changes its MCP tools\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tmcpTools: newMcpTools.map(({ inputSchema: _, outputSchema: __, ...tool }) => tool),\n\t\t\t\t\t\tverification,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Store new bundle. R2 prefix is deterministic per (pluginId, version),\n\t\t// so a retry of the same update is idempotent.\n\t\tawait storeBundleInR2(storage, pluginId, newVersion, bundle, \"registry\");\n\n\t\t// Refresh display metadata from the same signed profile used for\n\t\t// release-policy verification.\n\t\tawait stateRepo.upsert(pluginId, newVersion, \"active\", {\n\t\t\tsource: \"registry\",\n\t\t\tregistryPublisherDid: publisherDid,\n\t\t\tregistrySlug: slug,\n\t\t\tdisplayName: profile.name ?? slug,\n\t\t\tdescription: profile.description ?? undefined,\n\t\t\tmcpToolsEnabled: false,\n\t\t\tmcpToolsConsent: null,\n\t\t});\n\n\t\tawait syncDeclaredStorageIndexes(db, [bundle.manifest]);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\tpluginId,\n\t\t\t\toldVersion,\n\t\t\t\tnewVersion,\n\t\t\t\tcapabilityChanges,\n\t\t\t\trouteVisibilityChanges:\n\t\t\t\t\tnewlyPublicRoutes.length > 0 ? { newlyPublic: newlyPublicRoutes } : undefined,\n\t\t\t\tverification,\n\t\t\t},\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof ClientValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_RESPONSE_INVALID\",\n\t\t\t\t\tmessage: `Aggregator returned a response that does not conform to its lexicon (${err.target})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof ClientResponseError) {\n\t\t\tif (err.error === \"ListingUnavailable\") {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"LISTING_UNAVAILABLE\",\n\t\t\t\t\t\tmessage: \"This plugin is unavailable under the active registry policy\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.status === 404 ? \"AGGREGATOR_NOT_FOUND\" : \"AGGREGATOR_HTTP_ERROR\",\n\t\t\t\t\tmessage: `Aggregator returned ${err.status}: ${err.error}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof EmDashStorageError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.code ?? \"STORAGE_ERROR\",\n\t\t\t\t\tmessage: \"Storage error while updating plugin\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[registry-update] Failed:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"UPDATE_FAILED\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Failed to update plugin\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Update check ───────────────────────────────────────────────────\n\nexport interface RegistryUpdateCheck {\n\tpluginId: string;\n\tinstalled: string;\n\tlatest: string;\n\thasUpdate: boolean;\n\t/**\n\t * Both diff fields are `false` here by design: computing them at\n\t * update-check time would require downloading both bundles (or\n\t * extracting from the signed release extension and the installed\n\t * R2 bundle), which is too expensive for a bulk preview. The actual\n\t * escalation gate runs at update time in `handleRegistryUpdate`.\n\t * Mirrors marketplace's `hasRouteVisibilityChanges: false`.\n\t */\n\thasCapabilityChanges: boolean;\n\thasRouteVisibilityChanges: boolean;\n}\n\n/**\n * Bulk update check across every installed registry plugin. Queries the\n * aggregator for each plugin's latest release and reports `hasUpdate`\n * based on the version comparison. Plugins whose aggregator lookup fails\n * (unreachable, delisted, malformed) are skipped silently — one bad\n * publisher must not blank the whole admin Updates list.\n */\nexport async function handleRegistryUpdateCheck(\n\tdb: Kysely<Database>,\n\tregistryConfigInput: RegistryConfigInput | undefined,\n): Promise<ApiResult<{ items: RegistryUpdateCheck[] }>> {\n\tconst registryConfig = coerceRegistryConfig(registryConfigInput);\n\tif (!registryConfig) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"REGISTRY_NOT_CONFIGURED\", message: \"Registry is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst registryPlugins = await stateRepo.getRegistryPlugins();\n\t\tif (registryPlugins.length === 0) {\n\t\t\treturn { success: true, data: { items: [] } };\n\t\t}\n\n\t\tconst { DiscoveryClient, registryLabelerPolicy } =\n\t\t\tawait import(\"@emdash-cms/registry-client/discovery\");\n\t\tconst aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;\n\t\tconst discovery = new DiscoveryClient({\n\t\t\taggregatorUrl: registryConfig.aggregatorUrl,\n\t\t\tacceptLabelers: registryConfig.acceptLabelers,\n\t\t\tlabelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),\n\t\t\tfetch: timedFetch(aggregatorDeadline),\n\t\t});\n\n\t\tconst items: RegistryUpdateCheck[] = [];\n\t\tfor (const plugin of registryPlugins) {\n\t\t\tif (!plugin.registryPublisherDid || !plugin.registrySlug) continue;\n\t\t\ttry {\n\t\t\t\tconst releaseView = await discovery.getLatestRelease({\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- DID string was validated by the install handler\n\t\t\t\t\tdid: plugin.registryPublisherDid as Did,\n\t\t\t\t\tpackage: plugin.registrySlug,\n\t\t\t\t});\n\t\t\t\tif (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst latest = releaseView.version;\n\t\t\t\tif (!latest) continue;\n\t\t\t\tconst installed = plugin.version;\n\t\t\t\titems.push({\n\t\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\t\tinstalled,\n\t\t\t\t\tlatest,\n\t\t\t\t\thasUpdate: latest !== installed,\n\t\t\t\t\thasCapabilityChanges: false,\n\t\t\t\t\thasRouteVisibilityChanges: false,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\t// Skip plugins that can't be checked. Don't fail the whole\n\t\t\t\t// list because one aggregator query went wrong.\n\t\t\t\tconsole.warn(`[registry-update-check] Skipped ${plugin.pluginId}:`, err);\n\t\t\t}\n\t\t}\n\n\t\treturn { success: true, data: { items } };\n\t} catch (err) {\n\t\tif (err instanceof ClientValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_RESPONSE_INVALID\",\n\t\t\t\t\tmessage: `Aggregator returned a response that does not conform to its lexicon (${err.target})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof ClientResponseError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.status === 404 ? \"AGGREGATOR_NOT_FOUND\" : \"AGGREGATOR_HTTP_ERROR\",\n\t\t\t\t\tmessage: `Aggregator returned ${err.status}: ${err.error}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[registry-update-check] Failed:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"UPDATE_CHECK_FAILED\", message: \"Failed to check for registry updates\" },\n\t\t};\n\t}\n}\n"],"mappings":"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"}