emdash 0.0.0-b → 0.0.2

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 (661) hide show
  1. package/README.md +87 -43
  2. package/dist/adapters-BLMa4JGD.d.mts +106 -0
  3. package/dist/adapters-BLMa4JGD.d.mts.map +1 -0
  4. package/dist/apply-Bjfq_b4-.mjs +1293 -0
  5. package/dist/apply-Bjfq_b4-.mjs.map +1 -0
  6. package/dist/astro/index.d.mts +51 -0
  7. package/dist/astro/index.d.mts.map +1 -0
  8. package/dist/astro/index.mjs +1336 -0
  9. package/dist/astro/index.mjs.map +1 -0
  10. package/dist/astro/middleware/auth.d.mts +31 -0
  11. package/dist/astro/middleware/auth.d.mts.map +1 -0
  12. package/dist/astro/middleware/auth.mjs +654 -0
  13. package/dist/astro/middleware/auth.mjs.map +1 -0
  14. package/dist/astro/middleware/redirect.d.mts +22 -0
  15. package/dist/astro/middleware/redirect.d.mts.map +1 -0
  16. package/dist/astro/middleware/redirect.mjs +63 -0
  17. package/dist/astro/middleware/redirect.mjs.map +1 -0
  18. package/dist/astro/middleware/request-context.d.mts +18 -0
  19. package/dist/astro/middleware/request-context.d.mts.map +1 -0
  20. package/dist/astro/middleware/request-context.mjs +1310 -0
  21. package/dist/astro/middleware/request-context.mjs.map +1 -0
  22. package/dist/astro/middleware/setup.d.mts +20 -0
  23. package/dist/astro/middleware/setup.d.mts.map +1 -0
  24. package/dist/astro/middleware/setup.mjs +47 -0
  25. package/dist/astro/middleware/setup.mjs.map +1 -0
  26. package/dist/astro/middleware.d.mts +13 -0
  27. package/dist/astro/middleware.d.mts.map +1 -0
  28. package/dist/astro/middleware.mjs +1613 -0
  29. package/dist/astro/middleware.mjs.map +1 -0
  30. package/dist/astro/types.d.mts +250 -0
  31. package/dist/astro/types.d.mts.map +1 -0
  32. package/dist/astro/types.mjs +1 -0
  33. package/dist/base64-MBPo9ozB.mjs +59 -0
  34. package/dist/base64-MBPo9ozB.mjs.map +1 -0
  35. package/dist/byline-CL847F26.mjs +213 -0
  36. package/dist/byline-CL847F26.mjs.map +1 -0
  37. package/dist/bylines-C2a-2TGt.mjs +136 -0
  38. package/dist/bylines-C2a-2TGt.mjs.map +1 -0
  39. package/dist/chunk-ClPoSABd.mjs +21 -0
  40. package/dist/cli/index.d.mts +1 -0
  41. package/dist/cli/index.mjs +3909 -0
  42. package/dist/cli/index.mjs.map +1 -0
  43. package/dist/client/cf-access.d.mts +60 -0
  44. package/dist/client/cf-access.d.mts.map +1 -0
  45. package/dist/client/cf-access.mjs +179 -0
  46. package/dist/client/cf-access.mjs.map +1 -0
  47. package/dist/client/index.d.mts +398 -0
  48. package/dist/client/index.d.mts.map +1 -0
  49. package/dist/client/index.mjs +346 -0
  50. package/dist/client/index.mjs.map +1 -0
  51. package/dist/config-CKE8p9xM.mjs +55 -0
  52. package/dist/config-CKE8p9xM.mjs.map +1 -0
  53. package/dist/connection-B4zVnQIa.mjs +40 -0
  54. package/dist/connection-B4zVnQIa.mjs.map +1 -0
  55. package/dist/content-D6C2WsZC.mjs +824 -0
  56. package/dist/content-D6C2WsZC.mjs.map +1 -0
  57. package/dist/db/index.d.mts +4 -0
  58. package/dist/db/index.mjs +62 -0
  59. package/dist/db/index.mjs.map +1 -0
  60. package/dist/db/libsql.d.mts +11 -0
  61. package/dist/db/libsql.d.mts.map +1 -0
  62. package/dist/db/libsql.mjs +17 -0
  63. package/dist/db/libsql.mjs.map +1 -0
  64. package/dist/db/postgres.d.mts +11 -0
  65. package/dist/db/postgres.d.mts.map +1 -0
  66. package/dist/db/postgres.mjs +30 -0
  67. package/dist/db/postgres.mjs.map +1 -0
  68. package/dist/db/sqlite.d.mts +11 -0
  69. package/dist/db/sqlite.d.mts.map +1 -0
  70. package/dist/db/sqlite.mjs +16 -0
  71. package/dist/db/sqlite.mjs.map +1 -0
  72. package/dist/default-Cyi4aAxu.mjs +81 -0
  73. package/dist/default-Cyi4aAxu.mjs.map +1 -0
  74. package/dist/dialect-helpers-B9uSp2GJ.mjs +90 -0
  75. package/dist/dialect-helpers-B9uSp2GJ.mjs.map +1 -0
  76. package/dist/error-Cxz0tQeO.mjs +27 -0
  77. package/dist/error-Cxz0tQeO.mjs.map +1 -0
  78. package/dist/index-C1xF3OGh.d.mts +4527 -0
  79. package/dist/index-C1xF3OGh.d.mts.map +1 -0
  80. package/dist/index.d.mts +16 -0
  81. package/dist/index.mjs +30 -0
  82. package/dist/load-yOOlckBj.mjs +28 -0
  83. package/dist/load-yOOlckBj.mjs.map +1 -0
  84. package/dist/loader-fz8Q_3EO.mjs +447 -0
  85. package/dist/loader-fz8Q_3EO.mjs.map +1 -0
  86. package/dist/manifest-schema-Dcl0R6nM.mjs +184 -0
  87. package/dist/manifest-schema-Dcl0R6nM.mjs.map +1 -0
  88. package/dist/media/index.d.mts +26 -0
  89. package/dist/media/index.d.mts.map +1 -0
  90. package/dist/media/index.mjs +55 -0
  91. package/dist/media/index.mjs.map +1 -0
  92. package/dist/media/local-runtime.d.mts +39 -0
  93. package/dist/media/local-runtime.d.mts.map +1 -0
  94. package/dist/media/local-runtime.mjs +133 -0
  95. package/dist/media/local-runtime.mjs.map +1 -0
  96. package/dist/media-DqHVh136.mjs +200 -0
  97. package/dist/media-DqHVh136.mjs.map +1 -0
  98. package/dist/mode-C2EzN1uE.mjs +23 -0
  99. package/dist/mode-C2EzN1uE.mjs.map +1 -0
  100. package/dist/page/index.d.mts +140 -0
  101. package/dist/page/index.d.mts.map +1 -0
  102. package/dist/page/index.mjs +416 -0
  103. package/dist/page/index.mjs.map +1 -0
  104. package/dist/placeholder-CmGAmqeO.d.mts +276 -0
  105. package/dist/placeholder-CmGAmqeO.d.mts.map +1 -0
  106. package/dist/placeholder-SmpOx-_v.mjs +243 -0
  107. package/dist/placeholder-SmpOx-_v.mjs.map +1 -0
  108. package/dist/plugin-utils.d.mts +58 -0
  109. package/dist/plugin-utils.d.mts.map +1 -0
  110. package/dist/plugin-utils.mjs +78 -0
  111. package/dist/plugin-utils.mjs.map +1 -0
  112. package/dist/plugins/adapt-sandbox-entry.d.mts +22 -0
  113. package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -0
  114. package/dist/plugins/adapt-sandbox-entry.mjs +113 -0
  115. package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -0
  116. package/dist/query-CS_iSj34.mjs +460 -0
  117. package/dist/query-CS_iSj34.mjs.map +1 -0
  118. package/dist/redirect-DIfIni3r.mjs +329 -0
  119. package/dist/redirect-DIfIni3r.mjs.map +1 -0
  120. package/dist/registry-D_w5HW4G.mjs +863 -0
  121. package/dist/registry-D_w5HW4G.mjs.map +1 -0
  122. package/dist/request-context.d.mts +49 -0
  123. package/dist/request-context.d.mts.map +1 -0
  124. package/dist/request-context.mjs +43 -0
  125. package/dist/request-context.mjs.map +1 -0
  126. package/dist/runner-C0hCbYnD.mjs +1412 -0
  127. package/dist/runner-C0hCbYnD.mjs.map +1 -0
  128. package/dist/runner-EAtf0ZIe.d.mts +27 -0
  129. package/dist/runner-EAtf0ZIe.d.mts.map +1 -0
  130. package/dist/runtime.d.mts +26 -0
  131. package/dist/runtime.d.mts.map +1 -0
  132. package/dist/runtime.mjs +42 -0
  133. package/dist/runtime.mjs.map +1 -0
  134. package/dist/search-DG603UrT.mjs +9211 -0
  135. package/dist/search-DG603UrT.mjs.map +1 -0
  136. package/dist/seed/index.d.mts +3 -0
  137. package/dist/seed/index.mjs +15 -0
  138. package/dist/seo/index.d.mts +70 -0
  139. package/dist/seo/index.d.mts.map +1 -0
  140. package/dist/seo/index.mjs +70 -0
  141. package/dist/seo/index.mjs.map +1 -0
  142. package/dist/storage/local.d.mts +39 -0
  143. package/dist/storage/local.d.mts.map +1 -0
  144. package/dist/storage/local.mjs +166 -0
  145. package/dist/storage/local.mjs.map +1 -0
  146. package/dist/storage/s3.d.mts +32 -0
  147. package/dist/storage/s3.d.mts.map +1 -0
  148. package/dist/storage/s3.mjs +175 -0
  149. package/dist/storage/s3.mjs.map +1 -0
  150. package/dist/tokens-DpgrkrXK.mjs +171 -0
  151. package/dist/tokens-DpgrkrXK.mjs.map +1 -0
  152. package/dist/transport-BFGblqwG.d.mts +42 -0
  153. package/dist/transport-BFGblqwG.d.mts.map +1 -0
  154. package/dist/transport-yxiQsi8I.mjs +418 -0
  155. package/dist/transport-yxiQsi8I.mjs.map +1 -0
  156. package/dist/types-BRuPJGdV.d.mts +102 -0
  157. package/dist/types-BRuPJGdV.d.mts.map +1 -0
  158. package/dist/types-C4-fAxN3.d.mts +182 -0
  159. package/dist/types-C4-fAxN3.d.mts.map +1 -0
  160. package/dist/types-CMMN0pNg.mjs +31 -0
  161. package/dist/types-CMMN0pNg.mjs.map +1 -0
  162. package/dist/types-CUBbjgmP.mjs +16 -0
  163. package/dist/types-CUBbjgmP.mjs.map +1 -0
  164. package/dist/types-DRjfYOEv.d.mts +426 -0
  165. package/dist/types-DRjfYOEv.d.mts.map +1 -0
  166. package/dist/types-DY5zk5HN.mjs +73 -0
  167. package/dist/types-DY5zk5HN.mjs.map +1 -0
  168. package/dist/types-DaNLHo_T.d.mts +184 -0
  169. package/dist/types-DaNLHo_T.d.mts.map +1 -0
  170. package/dist/types-DvhsUmSJ.d.mts +1111 -0
  171. package/dist/types-DvhsUmSJ.d.mts.map +1 -0
  172. package/dist/validate-CpBtVMsD.d.mts +378 -0
  173. package/dist/validate-CpBtVMsD.d.mts.map +1 -0
  174. package/dist/validate-CqRJb_xU.mjs +97 -0
  175. package/dist/validate-CqRJb_xU.mjs.map +1 -0
  176. package/dist/validate-O7PWmlnq.mjs +328 -0
  177. package/dist/validate-O7PWmlnq.mjs.map +1 -0
  178. package/locals.d.ts +46 -0
  179. package/package.json +233 -19
  180. package/src/api/authorize.ts +63 -0
  181. package/src/api/csrf.ts +48 -0
  182. package/src/api/error.ts +99 -0
  183. package/src/api/errors.ts +445 -0
  184. package/src/api/escape.ts +9 -0
  185. package/src/api/handlers/api-tokens.ts +240 -0
  186. package/src/api/handlers/comments.ts +314 -0
  187. package/src/api/handlers/content.ts +1315 -0
  188. package/src/api/handlers/dashboard.ts +205 -0
  189. package/src/api/handlers/device-flow.ts +684 -0
  190. package/src/api/handlers/index.ts +163 -0
  191. package/src/api/handlers/manifest.ts +158 -0
  192. package/src/api/handlers/marketplace.ts +930 -0
  193. package/src/api/handlers/media.ts +207 -0
  194. package/src/api/handlers/menus.ts +493 -0
  195. package/src/api/handlers/oauth-authorization.ts +429 -0
  196. package/src/api/handlers/oauth-clients.ts +349 -0
  197. package/src/api/handlers/oauth-user-lookup.ts +39 -0
  198. package/src/api/handlers/plugins.ts +254 -0
  199. package/src/api/handlers/redirects.ts +360 -0
  200. package/src/api/handlers/revision.ts +145 -0
  201. package/src/api/handlers/schema.ts +534 -0
  202. package/src/api/handlers/sections.ts +289 -0
  203. package/src/api/handlers/seo.ts +115 -0
  204. package/src/api/handlers/settings.ts +49 -0
  205. package/src/api/handlers/snapshot.ts +350 -0
  206. package/src/api/handlers/taxonomies.ts +523 -0
  207. package/src/api/index.ts +6 -0
  208. package/src/api/openapi/document.ts +2368 -0
  209. package/src/api/openapi/index.ts +1 -0
  210. package/src/api/parse.ts +139 -0
  211. package/src/api/redirect.ts +14 -0
  212. package/src/api/rev.ts +67 -0
  213. package/src/api/schemas/auth.ts +112 -0
  214. package/src/api/schemas/bylines.ts +85 -0
  215. package/src/api/schemas/comments.ts +117 -0
  216. package/src/api/schemas/common.ts +89 -0
  217. package/src/api/schemas/content.ts +191 -0
  218. package/src/api/schemas/import.ts +52 -0
  219. package/src/api/schemas/index.ts +17 -0
  220. package/src/api/schemas/media.ts +116 -0
  221. package/src/api/schemas/menus.ts +111 -0
  222. package/src/api/schemas/redirects.ts +155 -0
  223. package/src/api/schemas/schema.ts +203 -0
  224. package/src/api/schemas/search.ts +63 -0
  225. package/src/api/schemas/sections.ts +67 -0
  226. package/src/api/schemas/settings.ts +63 -0
  227. package/src/api/schemas/setup.ts +37 -0
  228. package/src/api/schemas/taxonomies.ts +113 -0
  229. package/src/api/schemas/users.ts +96 -0
  230. package/src/api/schemas/widgets.ts +80 -0
  231. package/src/api/site-url.ts +25 -0
  232. package/src/api/types.ts +82 -0
  233. package/src/astro/index.ts +27 -0
  234. package/src/astro/integration/index.ts +303 -0
  235. package/src/astro/integration/routes.ts +834 -0
  236. package/src/astro/integration/runtime.ts +338 -0
  237. package/src/astro/integration/virtual-modules.ts +469 -0
  238. package/src/astro/integration/vite-config.ts +335 -0
  239. package/src/astro/middleware/auth.ts +743 -0
  240. package/src/astro/middleware/redirect.ts +89 -0
  241. package/src/astro/middleware/request-context.ts +129 -0
  242. package/src/astro/middleware/setup.ts +89 -0
  243. package/src/astro/middleware.ts +398 -0
  244. package/src/astro/routes/PluginRegistry.tsx +15 -0
  245. package/src/astro/routes/admin.astro +81 -0
  246. package/src/astro/routes/api/admin/allowed-domains/[domain].ts +112 -0
  247. package/src/astro/routes/api/admin/allowed-domains/index.ts +108 -0
  248. package/src/astro/routes/api/admin/api-tokens/[id].ts +40 -0
  249. package/src/astro/routes/api/admin/api-tokens/index.ts +68 -0
  250. package/src/astro/routes/api/admin/bylines/[id]/index.ts +87 -0
  251. package/src/astro/routes/api/admin/bylines/index.ts +72 -0
  252. package/src/astro/routes/api/admin/comments/[id]/status.ts +116 -0
  253. package/src/astro/routes/api/admin/comments/[id].ts +64 -0
  254. package/src/astro/routes/api/admin/comments/bulk.ts +42 -0
  255. package/src/astro/routes/api/admin/comments/counts.ts +30 -0
  256. package/src/astro/routes/api/admin/comments/index.ts +46 -0
  257. package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +91 -0
  258. package/src/astro/routes/api/admin/hooks/exclusive/index.ts +51 -0
  259. package/src/astro/routes/api/admin/oauth-clients/[id].ts +110 -0
  260. package/src/astro/routes/api/admin/oauth-clients/index.ts +71 -0
  261. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +39 -0
  262. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +39 -0
  263. package/src/astro/routes/api/admin/plugins/[id]/index.ts +38 -0
  264. package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +48 -0
  265. package/src/astro/routes/api/admin/plugins/[id]/update.ts +59 -0
  266. package/src/astro/routes/api/admin/plugins/index.ts +32 -0
  267. package/src/astro/routes/api/admin/plugins/marketplace/[id]/icon.ts +61 -0
  268. package/src/astro/routes/api/admin/plugins/marketplace/[id]/index.ts +33 -0
  269. package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +62 -0
  270. package/src/astro/routes/api/admin/plugins/marketplace/index.ts +38 -0
  271. package/src/astro/routes/api/admin/plugins/updates.ts +28 -0
  272. package/src/astro/routes/api/admin/themes/marketplace/[id]/index.ts +33 -0
  273. package/src/astro/routes/api/admin/themes/marketplace/[id]/thumbnail.ts +61 -0
  274. package/src/astro/routes/api/admin/themes/marketplace/index.ts +45 -0
  275. package/src/astro/routes/api/admin/users/[id]/disable.ts +69 -0
  276. package/src/astro/routes/api/admin/users/[id]/enable.ts +48 -0
  277. package/src/astro/routes/api/admin/users/[id]/index.ts +146 -0
  278. package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +72 -0
  279. package/src/astro/routes/api/admin/users/index.ts +66 -0
  280. package/src/astro/routes/api/auth/dev-bypass.ts +139 -0
  281. package/src/astro/routes/api/auth/invite/accept.ts +52 -0
  282. package/src/astro/routes/api/auth/invite/complete.ts +84 -0
  283. package/src/astro/routes/api/auth/invite/index.ts +99 -0
  284. package/src/astro/routes/api/auth/logout.ts +40 -0
  285. package/src/astro/routes/api/auth/magic-link/send.ts +89 -0
  286. package/src/astro/routes/api/auth/magic-link/verify.ts +71 -0
  287. package/src/astro/routes/api/auth/me.ts +60 -0
  288. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +219 -0
  289. package/src/astro/routes/api/auth/oauth/[provider].ts +119 -0
  290. package/src/astro/routes/api/auth/passkey/[id].ts +124 -0
  291. package/src/astro/routes/api/auth/passkey/index.ts +54 -0
  292. package/src/astro/routes/api/auth/passkey/options.ts +82 -0
  293. package/src/astro/routes/api/auth/passkey/register/options.ts +86 -0
  294. package/src/astro/routes/api/auth/passkey/register/verify.ts +115 -0
  295. package/src/astro/routes/api/auth/passkey/verify.ts +66 -0
  296. package/src/astro/routes/api/auth/signup/complete.ts +85 -0
  297. package/src/astro/routes/api/auth/signup/request.ts +77 -0
  298. package/src/astro/routes/api/auth/signup/verify.ts +53 -0
  299. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +312 -0
  300. package/src/astro/routes/api/content/[collection]/[id]/compare.ts +28 -0
  301. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +54 -0
  302. package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +61 -0
  303. package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +33 -0
  304. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +107 -0
  305. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +56 -0
  306. package/src/astro/routes/api/content/[collection]/[id]/restore.ts +54 -0
  307. package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +31 -0
  308. package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +101 -0
  309. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +140 -0
  310. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +30 -0
  311. package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +56 -0
  312. package/src/astro/routes/api/content/[collection]/[id].ts +137 -0
  313. package/src/astro/routes/api/content/[collection]/index.ts +59 -0
  314. package/src/astro/routes/api/content/[collection]/trash.ts +33 -0
  315. package/src/astro/routes/api/dashboard.ts +32 -0
  316. package/src/astro/routes/api/dev/emails.ts +36 -0
  317. package/src/astro/routes/api/import/probe.ts +47 -0
  318. package/src/astro/routes/api/import/wordpress/analyze.ts +510 -0
  319. package/src/astro/routes/api/import/wordpress/execute.ts +283 -0
  320. package/src/astro/routes/api/import/wordpress/media.ts +338 -0
  321. package/src/astro/routes/api/import/wordpress/prepare.ts +181 -0
  322. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +393 -0
  323. package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +111 -0
  324. package/src/astro/routes/api/import/wordpress-plugin/callback.ts +58 -0
  325. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +347 -0
  326. package/src/astro/routes/api/manifest.ts +62 -0
  327. package/src/astro/routes/api/mcp.ts +124 -0
  328. package/src/astro/routes/api/media/[id]/confirm.ts +93 -0
  329. package/src/astro/routes/api/media/[id].ts +145 -0
  330. package/src/astro/routes/api/media/file/[key].ts +79 -0
  331. package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +86 -0
  332. package/src/astro/routes/api/media/providers/[providerId]/index.ts +111 -0
  333. package/src/astro/routes/api/media/providers/index.ts +30 -0
  334. package/src/astro/routes/api/media/upload-url.ts +137 -0
  335. package/src/astro/routes/api/media.ts +190 -0
  336. package/src/astro/routes/api/menus/[name]/items.ts +87 -0
  337. package/src/astro/routes/api/menus/[name]/reorder.ts +33 -0
  338. package/src/astro/routes/api/menus/[name].ts +65 -0
  339. package/src/astro/routes/api/menus/index.ts +47 -0
  340. package/src/astro/routes/api/oauth/authorize.ts +412 -0
  341. package/src/astro/routes/api/oauth/device/authorize.ts +45 -0
  342. package/src/astro/routes/api/oauth/device/code.ts +51 -0
  343. package/src/astro/routes/api/oauth/device/token.ts +69 -0
  344. package/src/astro/routes/api/oauth/token/refresh.ts +38 -0
  345. package/src/astro/routes/api/oauth/token/revoke.ts +38 -0
  346. package/src/astro/routes/api/oauth/token.ts +184 -0
  347. package/src/astro/routes/api/openapi.json.ts +32 -0
  348. package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +92 -0
  349. package/src/astro/routes/api/redirects/404s/index.ts +72 -0
  350. package/src/astro/routes/api/redirects/404s/summary.ts +33 -0
  351. package/src/astro/routes/api/redirects/[id].ts +84 -0
  352. package/src/astro/routes/api/redirects/index.ts +52 -0
  353. package/src/astro/routes/api/revisions/[revisionId]/index.ts +29 -0
  354. package/src/astro/routes/api/revisions/[revisionId]/restore.ts +58 -0
  355. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +76 -0
  356. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +52 -0
  357. package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +32 -0
  358. package/src/astro/routes/api/schema/collections/[slug]/index.ts +80 -0
  359. package/src/astro/routes/api/schema/collections/index.ts +47 -0
  360. package/src/astro/routes/api/schema/index.ts +109 -0
  361. package/src/astro/routes/api/schema/orphans/[slug].ts +36 -0
  362. package/src/astro/routes/api/schema/orphans/index.ts +26 -0
  363. package/src/astro/routes/api/search/enable.ts +64 -0
  364. package/src/astro/routes/api/search/index.ts +55 -0
  365. package/src/astro/routes/api/search/rebuild.ts +72 -0
  366. package/src/astro/routes/api/search/stats.ts +35 -0
  367. package/src/astro/routes/api/search/suggest.ts +53 -0
  368. package/src/astro/routes/api/sections/[slug].ts +84 -0
  369. package/src/astro/routes/api/sections/index.ts +52 -0
  370. package/src/astro/routes/api/settings/email.ts +150 -0
  371. package/src/astro/routes/api/settings.ts +67 -0
  372. package/src/astro/routes/api/setup/admin-verify.ts +100 -0
  373. package/src/astro/routes/api/setup/admin.ts +94 -0
  374. package/src/astro/routes/api/setup/dev-bypass.ts +199 -0
  375. package/src/astro/routes/api/setup/dev-reset.ts +40 -0
  376. package/src/astro/routes/api/setup/index.ts +126 -0
  377. package/src/astro/routes/api/setup/status.ts +122 -0
  378. package/src/astro/routes/api/snapshot.ts +75 -0
  379. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +95 -0
  380. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +69 -0
  381. package/src/astro/routes/api/taxonomies/index.ts +59 -0
  382. package/src/astro/routes/api/themes/preview.ts +77 -0
  383. package/src/astro/routes/api/typegen.ts +114 -0
  384. package/src/astro/routes/api/well-known/auth.ts +68 -0
  385. package/src/astro/routes/api/well-known/oauth-authorization-server.ts +44 -0
  386. package/src/astro/routes/api/well-known/oauth-protected-resource.ts +37 -0
  387. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +68 -0
  388. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +127 -0
  389. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +80 -0
  390. package/src/astro/routes/api/widget-areas/[name].ts +87 -0
  391. package/src/astro/routes/api/widget-areas/index.ts +99 -0
  392. package/src/astro/routes/api/widget-components.ts +22 -0
  393. package/src/astro/routes/robots.txt.ts +77 -0
  394. package/src/astro/routes/sitemap.xml.ts +97 -0
  395. package/src/astro/storage/adapters.ts +74 -0
  396. package/src/astro/storage/index.ts +19 -0
  397. package/src/astro/storage/types.ts +60 -0
  398. package/src/astro/types.ts +346 -0
  399. package/src/auth/api-tokens.ts +25 -0
  400. package/src/auth/challenge-store.ts +80 -0
  401. package/src/auth/mode.ts +96 -0
  402. package/src/auth/oauth-state-store.ts +96 -0
  403. package/src/auth/passkey-config.ts +27 -0
  404. package/src/auth/rate-limit.ts +158 -0
  405. package/src/auth/scopes.ts +33 -0
  406. package/src/auth/types.ts +104 -0
  407. package/src/aws-sdk.d.ts +100 -0
  408. package/src/bylines/index.ts +237 -0
  409. package/src/cleanup.ts +153 -0
  410. package/src/cli/client-factory.ts +100 -0
  411. package/src/cli/commands/auth.ts +46 -0
  412. package/src/cli/commands/bundle-utils.ts +247 -0
  413. package/src/cli/commands/bundle.ts +609 -0
  414. package/src/cli/commands/content.ts +442 -0
  415. package/src/cli/commands/dev.ts +191 -0
  416. package/src/cli/commands/doctor.ts +211 -0
  417. package/src/cli/commands/export-seed.ts +630 -0
  418. package/src/cli/commands/import/wordpress.ts +1056 -0
  419. package/src/cli/commands/init.ts +192 -0
  420. package/src/cli/commands/login.ts +547 -0
  421. package/src/cli/commands/media.ts +165 -0
  422. package/src/cli/commands/menu.ts +67 -0
  423. package/src/cli/commands/plugin-init.ts +291 -0
  424. package/src/cli/commands/plugin-validate.ts +31 -0
  425. package/src/cli/commands/plugin.ts +33 -0
  426. package/src/cli/commands/publish.ts +697 -0
  427. package/src/cli/commands/schema.ts +233 -0
  428. package/src/cli/commands/search-cmd.ts +54 -0
  429. package/src/cli/commands/seed.ts +286 -0
  430. package/src/cli/commands/taxonomy.ts +128 -0
  431. package/src/cli/commands/types.ts +68 -0
  432. package/src/cli/credentials.ts +236 -0
  433. package/src/cli/index.ts +70 -0
  434. package/src/cli/output.ts +75 -0
  435. package/src/cli/wxr/parser.ts +969 -0
  436. package/src/client/cf-access.ts +193 -0
  437. package/src/client/index.ts +854 -0
  438. package/src/client/portable-text.ts +413 -0
  439. package/src/client/transport.ts +200 -0
  440. package/src/comments/moderator.ts +46 -0
  441. package/src/comments/notifications.ts +144 -0
  442. package/src/comments/query.ts +105 -0
  443. package/src/comments/service.ts +213 -0
  444. package/src/components/Break.astro +45 -0
  445. package/src/components/Button.astro +71 -0
  446. package/src/components/Buttons.astro +49 -0
  447. package/src/components/Code.astro +59 -0
  448. package/src/components/Columns.astro +59 -0
  449. package/src/components/CommentForm.astro +315 -0
  450. package/src/components/Comments.astro +232 -0
  451. package/src/components/Cover.astro +128 -0
  452. package/src/components/EmDashBodyEnd.astro +32 -0
  453. package/src/components/EmDashBodyStart.astro +32 -0
  454. package/src/components/EmDashHead.astro +53 -0
  455. package/src/components/EmDashImage.astro +178 -0
  456. package/src/components/EmDashMedia.astro +167 -0
  457. package/src/components/Embed.astro +128 -0
  458. package/src/components/File.astro +122 -0
  459. package/src/components/Gallery.astro +93 -0
  460. package/src/components/HtmlBlock.astro +33 -0
  461. package/src/components/Image.astro +178 -0
  462. package/src/components/InlineEditor.astro +27 -0
  463. package/src/components/InlinePortableTextEditor.tsx +1905 -0
  464. package/src/components/LiveSearch.astro +614 -0
  465. package/src/components/PortableText.astro +51 -0
  466. package/src/components/Pullquote.astro +51 -0
  467. package/src/components/Table.astro +108 -0
  468. package/src/components/WidgetArea.astro +22 -0
  469. package/src/components/WidgetRenderer.astro +72 -0
  470. package/src/components/index.ts +116 -0
  471. package/src/components/marks/Link.astro +31 -0
  472. package/src/components/marks/StrikeThrough.astro +7 -0
  473. package/src/components/marks/Subscript.astro +7 -0
  474. package/src/components/marks/Superscript.astro +7 -0
  475. package/src/components/marks/Underline.astro +7 -0
  476. package/src/components/widgets/Archives.astro +65 -0
  477. package/src/components/widgets/Categories.astro +35 -0
  478. package/src/components/widgets/RecentPosts.astro +51 -0
  479. package/src/components/widgets/Search.astro +18 -0
  480. package/src/components/widgets/Tags.astro +38 -0
  481. package/src/content/converters/index.ts +9 -0
  482. package/src/content/converters/portable-text-to-prosemirror.ts +385 -0
  483. package/src/content/converters/prosemirror-to-portable-text.ts +413 -0
  484. package/src/content/converters/types.ts +120 -0
  485. package/src/content/index.ts +5 -0
  486. package/src/database/connection.ts +67 -0
  487. package/src/database/dialect-helpers.ts +138 -0
  488. package/src/database/index.ts +5 -0
  489. package/src/database/migrations/001_initial.ts +170 -0
  490. package/src/database/migrations/002_media_status.ts +26 -0
  491. package/src/database/migrations/003_schema_registry.ts +79 -0
  492. package/src/database/migrations/004_plugins.ts +62 -0
  493. package/src/database/migrations/005_menus.ts +67 -0
  494. package/src/database/migrations/006_taxonomy_defs.ts +51 -0
  495. package/src/database/migrations/007_widgets.ts +42 -0
  496. package/src/database/migrations/008_auth.ts +194 -0
  497. package/src/database/migrations/009_user_disabled.ts +27 -0
  498. package/src/database/migrations/011_sections.ts +65 -0
  499. package/src/database/migrations/012_search.ts +25 -0
  500. package/src/database/migrations/013_scheduled_publishing.ts +51 -0
  501. package/src/database/migrations/014_draft_revisions.ts +72 -0
  502. package/src/database/migrations/015_indexes.ts +82 -0
  503. package/src/database/migrations/016_api_tokens.ts +89 -0
  504. package/src/database/migrations/017_authorization_codes.ts +45 -0
  505. package/src/database/migrations/018_seo.ts +56 -0
  506. package/src/database/migrations/019_i18n.ts +618 -0
  507. package/src/database/migrations/020_collection_url_pattern.ts +23 -0
  508. package/src/database/migrations/021_remove_section_categories.ts +43 -0
  509. package/src/database/migrations/022_marketplace_plugin_state.ts +46 -0
  510. package/src/database/migrations/023_plugin_metadata.ts +33 -0
  511. package/src/database/migrations/024_media_placeholders.ts +32 -0
  512. package/src/database/migrations/025_oauth_clients.ts +28 -0
  513. package/src/database/migrations/026_cron_tasks.ts +49 -0
  514. package/src/database/migrations/027_comments.ts +87 -0
  515. package/src/database/migrations/028_drop_author_url.ts +9 -0
  516. package/src/database/migrations/029_redirects.ts +67 -0
  517. package/src/database/migrations/030_widen_scheduled_index.ts +48 -0
  518. package/src/database/migrations/031_bylines.ts +90 -0
  519. package/src/database/migrations/032_rate_limits.ts +39 -0
  520. package/src/database/migrations/runner.ts +170 -0
  521. package/src/database/repositories/audit.ts +294 -0
  522. package/src/database/repositories/byline.ts +387 -0
  523. package/src/database/repositories/comment.ts +458 -0
  524. package/src/database/repositories/content.ts +1144 -0
  525. package/src/database/repositories/index.ts +30 -0
  526. package/src/database/repositories/media.ts +347 -0
  527. package/src/database/repositories/options.ts +150 -0
  528. package/src/database/repositories/plugin-storage.ts +373 -0
  529. package/src/database/repositories/redirect.ts +480 -0
  530. package/src/database/repositories/revision.ts +200 -0
  531. package/src/database/repositories/seo.ts +176 -0
  532. package/src/database/repositories/taxonomy.ts +294 -0
  533. package/src/database/repositories/types.ts +132 -0
  534. package/src/database/repositories/user.ts +258 -0
  535. package/src/database/transaction.ts +54 -0
  536. package/src/database/types.ts +501 -0
  537. package/src/database/validate.ts +138 -0
  538. package/src/db/adapters.ts +125 -0
  539. package/src/db/index.ts +37 -0
  540. package/src/db/libsql.ts +23 -0
  541. package/src/db/postgres.ts +30 -0
  542. package/src/db/sqlite.ts +27 -0
  543. package/src/emdash-runtime.ts +2096 -0
  544. package/src/fields/boolean.ts +34 -0
  545. package/src/fields/datetime.ts +44 -0
  546. package/src/fields/file.ts +41 -0
  547. package/src/fields/image.ts +34 -0
  548. package/src/fields/index.ts +42 -0
  549. package/src/fields/integer.ts +50 -0
  550. package/src/fields/json.ts +37 -0
  551. package/src/fields/multiselect.ts +48 -0
  552. package/src/fields/number.ts +52 -0
  553. package/src/fields/portable-text.ts +33 -0
  554. package/src/fields/reference.ts +29 -0
  555. package/src/fields/richtext.ts +31 -0
  556. package/src/fields/select.ts +46 -0
  557. package/src/fields/slug.ts +38 -0
  558. package/src/fields/text.ts +55 -0
  559. package/src/fields/textarea.ts +52 -0
  560. package/src/fields/types.ts +64 -0
  561. package/src/i18n/config.ts +68 -0
  562. package/src/import/index.ts +90 -0
  563. package/src/import/menus.ts +436 -0
  564. package/src/import/registry.ts +111 -0
  565. package/src/import/sections.ts +103 -0
  566. package/src/import/settings.ts +281 -0
  567. package/src/import/sources/wordpress-plugin.ts +641 -0
  568. package/src/import/sources/wordpress-rest.ts +191 -0
  569. package/src/import/sources/wxr.ts +330 -0
  570. package/src/import/ssrf.ts +260 -0
  571. package/src/import/types.ts +418 -0
  572. package/src/import/utils.ts +412 -0
  573. package/src/index.ts +481 -0
  574. package/src/loader.ts +770 -0
  575. package/src/mcp/server.ts +1463 -0
  576. package/src/media/index.ts +32 -0
  577. package/src/media/local-runtime.ts +213 -0
  578. package/src/media/local.ts +46 -0
  579. package/src/media/normalize.ts +190 -0
  580. package/src/media/placeholder.ts +150 -0
  581. package/src/media/provider-loader.ts +78 -0
  582. package/src/media/types.ts +279 -0
  583. package/src/menus/index.ts +324 -0
  584. package/src/menus/types.ts +112 -0
  585. package/src/page/context.ts +93 -0
  586. package/src/page/fragments.ts +89 -0
  587. package/src/page/index.ts +58 -0
  588. package/src/page/jsonld.ts +94 -0
  589. package/src/page/metadata.ts +185 -0
  590. package/src/page/seo-contributions.ts +136 -0
  591. package/src/plugin-utils.ts +80 -0
  592. package/src/plugins/adapt-sandbox-entry.ts +207 -0
  593. package/src/plugins/context.ts +833 -0
  594. package/src/plugins/cron.ts +361 -0
  595. package/src/plugins/define-plugin.ts +259 -0
  596. package/src/plugins/email-console.ts +73 -0
  597. package/src/plugins/email.ts +209 -0
  598. package/src/plugins/hooks.ts +1273 -0
  599. package/src/plugins/index.ts +193 -0
  600. package/src/plugins/manager.ts +595 -0
  601. package/src/plugins/manifest-schema.ts +230 -0
  602. package/src/plugins/marketplace.ts +460 -0
  603. package/src/plugins/request-meta.ts +139 -0
  604. package/src/plugins/routes.ts +302 -0
  605. package/src/plugins/sandbox/index.ts +18 -0
  606. package/src/plugins/sandbox/noop.ts +76 -0
  607. package/src/plugins/sandbox/types.ts +173 -0
  608. package/src/plugins/scheduler/node.ts +122 -0
  609. package/src/plugins/scheduler/piggyback.ts +71 -0
  610. package/src/plugins/scheduler/types.ts +27 -0
  611. package/src/plugins/state.ts +208 -0
  612. package/src/plugins/storage-indexes.ts +326 -0
  613. package/src/plugins/storage-query.ts +240 -0
  614. package/src/plugins/types.ts +1284 -0
  615. package/src/preview/helpers.ts +27 -0
  616. package/src/preview/index.ts +40 -0
  617. package/src/preview/tokens.ts +279 -0
  618. package/src/preview/urls.ts +118 -0
  619. package/src/query.ts +674 -0
  620. package/src/redirects/patterns.ts +224 -0
  621. package/src/request-context.ts +67 -0
  622. package/src/runtime.ts +21 -0
  623. package/src/schema/index.ts +29 -0
  624. package/src/schema/query.ts +44 -0
  625. package/src/schema/registry.ts +965 -0
  626. package/src/schema/types.ts +276 -0
  627. package/src/schema/zod-generator.ts +413 -0
  628. package/src/search/fts-manager.ts +452 -0
  629. package/src/search/index.ts +26 -0
  630. package/src/search/query.ts +396 -0
  631. package/src/search/text-extraction.ts +162 -0
  632. package/src/search/types.ts +114 -0
  633. package/src/sections/index.ts +226 -0
  634. package/src/sections/types.ts +86 -0
  635. package/src/seed/apply.ts +1141 -0
  636. package/src/seed/default.ts +86 -0
  637. package/src/seed/index.ts +28 -0
  638. package/src/seed/load.ts +35 -0
  639. package/src/seed/types.ts +341 -0
  640. package/src/seed/validate.ts +642 -0
  641. package/src/seo/index.ts +179 -0
  642. package/src/settings/index.ts +203 -0
  643. package/src/settings/types.ts +58 -0
  644. package/src/storage/index.ts +28 -0
  645. package/src/storage/local.ts +249 -0
  646. package/src/storage/s3.ts +263 -0
  647. package/src/storage/types.ts +204 -0
  648. package/src/taxonomies/index.ts +309 -0
  649. package/src/taxonomies/types.ts +61 -0
  650. package/src/ui.ts +75 -0
  651. package/src/utils/base64.ts +73 -0
  652. package/src/utils/hash.ts +36 -0
  653. package/src/utils/sanitize.ts +20 -0
  654. package/src/utils/slugify.ts +29 -0
  655. package/src/utils/url.ts +48 -0
  656. package/src/virtual-modules.d.ts +111 -0
  657. package/src/visual-editing/editable.ts +108 -0
  658. package/src/visual-editing/toolbar.ts +1229 -0
  659. package/src/widgets/components.ts +105 -0
  660. package/src/widgets/index.ts +131 -0
  661. package/src/widgets/types.ts +81 -0
@@ -0,0 +1,930 @@
1
+ /**
2
+ * Marketplace plugin handlers
3
+ *
4
+ * Business logic for installing, updating, uninstalling, and checking
5
+ * updates for marketplace plugins. Routes are thin wrappers around these.
6
+ */
7
+
8
+ import type { Kysely } from "kysely";
9
+
10
+ import type { Database } from "../../database/types.js";
11
+ import { validatePluginIdentifier } from "../../database/validate.js";
12
+ import { pluginManifestSchema } from "../../plugins/manifest-schema.js";
13
+ import { normalizeManifestRoute } from "../../plugins/manifest-schema.js";
14
+ import {
15
+ createMarketplaceClient,
16
+ MarketplaceError,
17
+ MarketplaceUnavailableError,
18
+ type MarketplaceClient,
19
+ type MarketplacePluginDetail,
20
+ type MarketplaceSearchOpts,
21
+ type MarketplaceThemeSearchOpts,
22
+ type MarketplaceVersionSummary,
23
+ type PluginBundle,
24
+ } from "../../plugins/marketplace.js";
25
+ import type { SandboxRunner } from "../../plugins/sandbox/types.js";
26
+ import { PluginStateRepository } from "../../plugins/state.js";
27
+ import type { PluginManifest } from "../../plugins/types.js";
28
+ import { EmDashStorageError } from "../../storage/types.js";
29
+ import type { Storage } from "../../storage/types.js";
30
+ import type { ApiResult } from "../types.js";
31
+
32
+ // ── Types ──────────────────────────────────────────────────────────
33
+
34
+ export interface MarketplaceInstallResult {
35
+ pluginId: string;
36
+ version: string;
37
+ capabilities: string[];
38
+ }
39
+
40
+ export interface MarketplaceUpdateResult {
41
+ pluginId: string;
42
+ oldVersion: string;
43
+ newVersion: string;
44
+ capabilityChanges: {
45
+ added: string[];
46
+ removed: string[];
47
+ };
48
+ routeVisibilityChanges?: {
49
+ newlyPublic: string[];
50
+ };
51
+ }
52
+
53
+ export interface MarketplaceUpdateCheck {
54
+ pluginId: string;
55
+ installed: string;
56
+ latest: string;
57
+ hasUpdate: boolean;
58
+ hasCapabilityChanges: boolean;
59
+ capabilityChanges?: {
60
+ added: string[];
61
+ removed: string[];
62
+ };
63
+ hasRouteVisibilityChanges: boolean;
64
+ routeVisibilityChanges?: {
65
+ newlyPublic: string[];
66
+ };
67
+ }
68
+
69
+ export interface MarketplaceUninstallResult {
70
+ pluginId: string;
71
+ dataDeleted: boolean;
72
+ }
73
+
74
+ // ── Helpers ────────────────────────────────────────────────────────
75
+
76
+ /** Semver-like pattern: digits, dots, hyphens, plus signs (e.g. 1.0.0, 1.0.0-beta.1) */
77
+ const VERSION_PATTERN = /^[a-z0-9][a-z0-9._+-]*$/i;
78
+
79
+ function validateVersion(version: string): void {
80
+ if (version.includes("..")) throw new Error("Invalid version format");
81
+ if (!VERSION_PATTERN.test(version)) {
82
+ throw new Error("Invalid version format");
83
+ }
84
+ }
85
+
86
+ function getClient(marketplaceUrl: string | undefined): MarketplaceClient | null {
87
+ if (!marketplaceUrl) return null;
88
+ return createMarketplaceClient(marketplaceUrl);
89
+ }
90
+
91
+ function diffCapabilities(
92
+ oldCaps: string[],
93
+ newCaps: string[],
94
+ ): { added: string[]; removed: string[] } {
95
+ const oldSet = new Set(oldCaps);
96
+ const newSet = new Set(newCaps);
97
+ return {
98
+ added: newCaps.filter((c) => !oldSet.has(c)),
99
+ removed: oldCaps.filter((c) => !newSet.has(c)),
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Diff route visibility between two manifests.
105
+ * Returns routes that changed from private to public (newly exposed).
106
+ */
107
+ function diffRouteVisibility(
108
+ oldManifest: PluginManifest | undefined,
109
+ newManifest: PluginManifest,
110
+ ): { newlyPublic: string[] } {
111
+ const oldPublicRoutes = new Set<string>();
112
+ if (oldManifest) {
113
+ for (const entry of oldManifest.routes) {
114
+ const normalized = normalizeManifestRoute(entry);
115
+ if (normalized.public === true) {
116
+ oldPublicRoutes.add(normalized.name);
117
+ }
118
+ }
119
+ }
120
+
121
+ const newlyPublic: string[] = [];
122
+ for (const entry of newManifest.routes) {
123
+ const normalized = normalizeManifestRoute(entry);
124
+ if (normalized.public === true && !oldPublicRoutes.has(normalized.name)) {
125
+ newlyPublic.push(normalized.name);
126
+ }
127
+ }
128
+
129
+ return { newlyPublic };
130
+ }
131
+
132
+ async function resolveVersionMetadata(
133
+ client: MarketplaceClient,
134
+ pluginId: string,
135
+ pluginDetail: MarketplacePluginDetail,
136
+ version: string,
137
+ ): Promise<MarketplaceVersionSummary | null> {
138
+ if (pluginDetail.latestVersion?.version === version) {
139
+ return {
140
+ version: pluginDetail.latestVersion.version,
141
+ minEmDashVersion: pluginDetail.latestVersion.minEmDashVersion,
142
+ bundleSize: pluginDetail.latestVersion.bundleSize,
143
+ checksum: pluginDetail.latestVersion.checksum,
144
+ changelog: pluginDetail.latestVersion.changelog,
145
+ capabilities: pluginDetail.latestVersion.capabilities,
146
+ status: pluginDetail.latestVersion.status,
147
+ auditVerdict: pluginDetail.latestVersion.audit?.verdict ?? null,
148
+ imageAuditVerdict: pluginDetail.latestVersion.imageAudit?.verdict ?? null,
149
+ publishedAt: pluginDetail.latestVersion.publishedAt,
150
+ };
151
+ }
152
+
153
+ const versions = await client.getVersions(pluginId);
154
+ return versions.find((v) => v.version === version) ?? null;
155
+ }
156
+
157
+ function validateBundleIdentity(
158
+ bundle: PluginBundle,
159
+ pluginId: string,
160
+ version: string,
161
+ ): ApiResult<never> | null {
162
+ if (bundle.manifest.id !== pluginId) {
163
+ return {
164
+ success: false,
165
+ error: {
166
+ code: "MANIFEST_MISMATCH",
167
+ message: `Bundle manifest ID (${bundle.manifest.id}) does not match requested plugin (${pluginId})`,
168
+ },
169
+ };
170
+ }
171
+
172
+ if (bundle.manifest.version !== version) {
173
+ return {
174
+ success: false,
175
+ error: {
176
+ code: "MANIFEST_VERSION_MISMATCH",
177
+ message: `Bundle manifest version (${bundle.manifest.version}) does not match requested version (${version})`,
178
+ },
179
+ };
180
+ }
181
+
182
+ return null;
183
+ }
184
+
185
+ /** Store a plugin bundle's files in site-local R2 storage */
186
+ async function storeBundleInR2(
187
+ storage: Storage,
188
+ pluginId: string,
189
+ version: string,
190
+ bundle: PluginBundle,
191
+ ): Promise<void> {
192
+ validatePluginIdentifier(pluginId, "plugin ID");
193
+ validateVersion(version);
194
+ const prefix = `marketplace/${pluginId}/${version}`;
195
+
196
+ // Store manifest
197
+ await storage.upload({
198
+ key: `${prefix}/manifest.json`,
199
+ body: new TextEncoder().encode(JSON.stringify(bundle.manifest)),
200
+ contentType: "application/json",
201
+ });
202
+
203
+ // Store backend code
204
+ await storage.upload({
205
+ key: `${prefix}/backend.js`,
206
+ body: new TextEncoder().encode(bundle.backendCode),
207
+ contentType: "application/javascript",
208
+ });
209
+
210
+ // Store admin code if present
211
+ if (bundle.adminCode) {
212
+ await storage.upload({
213
+ key: `${prefix}/admin.js`,
214
+ body: new TextEncoder().encode(bundle.adminCode),
215
+ contentType: "application/javascript",
216
+ });
217
+ }
218
+ }
219
+
220
+ /** Read a ReadableStream to string */
221
+ async function streamToText(stream: ReadableStream<Uint8Array>): Promise<string> {
222
+ return new Response(stream).text();
223
+ }
224
+
225
+ /** Load a plugin bundle from site-local R2 storage */
226
+ export async function loadBundleFromR2(
227
+ storage: Storage,
228
+ pluginId: string,
229
+ version: string,
230
+ ): Promise<{ manifest: PluginManifest; backendCode: string; adminCode?: string } | null> {
231
+ validatePluginIdentifier(pluginId, "plugin ID");
232
+ validateVersion(version);
233
+ const prefix = `marketplace/${pluginId}/${version}`;
234
+
235
+ try {
236
+ const manifestResult = await storage.download(`${prefix}/manifest.json`);
237
+ const backendResult = await storage.download(`${prefix}/backend.js`);
238
+
239
+ const manifestText = await streamToText(manifestResult.body);
240
+ const backendCode = await streamToText(backendResult.body);
241
+ const parsed: unknown = JSON.parse(manifestText);
242
+ const result = pluginManifestSchema.safeParse(parsed);
243
+ if (!result.success) return null;
244
+ // Elements are validated as unknown[] by Zod; cast to PluginManifest
245
+ // for the Element[] type (Block Kit validation happens at render time).
246
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Zod types elements as unknown[]; Element type validated at render time
247
+ const manifest = result.data as unknown as PluginManifest;
248
+
249
+ // Try to load admin code (optional)
250
+ let adminCode: string | undefined;
251
+ try {
252
+ const adminResult = await storage.download(`${prefix}/admin.js`);
253
+ adminCode = await streamToText(adminResult.body);
254
+ } catch {
255
+ // admin.js is optional
256
+ }
257
+
258
+ return { manifest, backendCode, adminCode };
259
+ } catch {
260
+ return null;
261
+ }
262
+ }
263
+
264
+ /** Delete a plugin bundle from site-local R2 storage */
265
+ async function deleteBundleFromR2(
266
+ storage: Storage,
267
+ pluginId: string,
268
+ version: string,
269
+ ): Promise<void> {
270
+ validatePluginIdentifier(pluginId, "plugin ID");
271
+ validateVersion(version);
272
+ const prefix = `marketplace/${pluginId}/${version}`;
273
+ const files = ["manifest.json", "backend.js", "admin.js"];
274
+
275
+ for (const file of files) {
276
+ try {
277
+ await storage.delete(`${prefix}/${file}`);
278
+ } catch {
279
+ // Ignore missing files
280
+ }
281
+ }
282
+ }
283
+
284
+ // ── Install ────────────────────────────────────────────────────────
285
+
286
+ export async function handleMarketplaceInstall(
287
+ db: Kysely<Database>,
288
+ storage: Storage | null,
289
+ sandboxRunner: SandboxRunner | null,
290
+ marketplaceUrl: string | undefined,
291
+ pluginId: string,
292
+ opts?: { version?: string; configuredPluginIds?: Set<string> },
293
+ ): Promise<ApiResult<MarketplaceInstallResult>> {
294
+ const client = getClient(marketplaceUrl);
295
+ if (!client) {
296
+ return {
297
+ success: false,
298
+ error: {
299
+ code: "MARKETPLACE_NOT_CONFIGURED",
300
+ message: "Marketplace is not configured",
301
+ },
302
+ };
303
+ }
304
+
305
+ if (!storage) {
306
+ return {
307
+ success: false,
308
+ error: {
309
+ code: "STORAGE_NOT_CONFIGURED",
310
+ message: "Storage is required for marketplace plugin installation",
311
+ },
312
+ };
313
+ }
314
+
315
+ if (!sandboxRunner || !sandboxRunner.isAvailable()) {
316
+ return {
317
+ success: false,
318
+ error: {
319
+ code: "SANDBOX_NOT_AVAILABLE",
320
+ message: "Sandbox runner is required for marketplace plugins",
321
+ },
322
+ };
323
+ }
324
+
325
+ try {
326
+ // Check if already installed
327
+ const stateRepo = new PluginStateRepository(db);
328
+ const existing = await stateRepo.get(pluginId);
329
+ if (existing && existing.source === "marketplace") {
330
+ return {
331
+ success: false,
332
+ error: {
333
+ code: "ALREADY_INSTALLED",
334
+ message: `Plugin ${pluginId} is already installed`,
335
+ },
336
+ };
337
+ }
338
+
339
+ // Block installation if a configured (trusted) plugin with the same ID exists.
340
+ // Without this check, the sandboxed plugin could shadow the trusted plugin's
341
+ // route handlers while auth decisions are made against the trusted plugin's metadata.
342
+ if (opts?.configuredPluginIds?.has(pluginId)) {
343
+ return {
344
+ success: false,
345
+ error: {
346
+ code: "PLUGIN_ID_CONFLICT",
347
+ message: `Cannot install marketplace plugin "${pluginId}" — a configured plugin with the same ID already exists`,
348
+ },
349
+ };
350
+ }
351
+
352
+ // Fetch plugin detail from marketplace
353
+ const pluginDetail = await client.getPlugin(pluginId);
354
+ const version = opts?.version ?? pluginDetail.latestVersion?.version;
355
+ if (!version) {
356
+ return {
357
+ success: false,
358
+ error: {
359
+ code: "NO_VERSION",
360
+ message: `No published versions found for plugin ${pluginId}`,
361
+ },
362
+ };
363
+ }
364
+
365
+ const versionMetadata = await resolveVersionMetadata(client, pluginId, pluginDetail, version);
366
+ if (!versionMetadata) {
367
+ return {
368
+ success: false,
369
+ error: {
370
+ code: "NO_VERSION",
371
+ message: `Version ${version} was not found for plugin ${pluginId}`,
372
+ },
373
+ };
374
+ }
375
+
376
+ // Block installation of plugins that haven't passed audit.
377
+ // Both "fail" (explicitly malicious) and "warn" (audit error or
378
+ // inconclusive) are non-installable — only "pass" or null (no audit
379
+ // ran) are allowed through.
380
+ if (versionMetadata.auditVerdict === "fail" || versionMetadata.auditVerdict === "warn") {
381
+ return {
382
+ success: false,
383
+ error: {
384
+ code: "AUDIT_FAILED",
385
+ message:
386
+ versionMetadata.auditVerdict === "fail"
387
+ ? "Plugin failed security audit and cannot be installed"
388
+ : "Plugin audit was inconclusive and cannot be installed until reviewed",
389
+ },
390
+ };
391
+ }
392
+
393
+ // Download and extract bundle
394
+ const bundle = await client.downloadBundle(pluginId, version);
395
+
396
+ // Verify checksum matches marketplace-published checksum
397
+ if (versionMetadata.checksum && bundle.checksum !== versionMetadata.checksum) {
398
+ return {
399
+ success: false,
400
+ error: {
401
+ code: "CHECKSUM_MISMATCH",
402
+ message: "Bundle checksum does not match marketplace record. Download may be corrupted.",
403
+ },
404
+ };
405
+ }
406
+
407
+ const bundleIdentityError = validateBundleIdentity(bundle, pluginId, version);
408
+ if (bundleIdentityError) return bundleIdentityError;
409
+
410
+ // Store bundle in site-local R2
411
+ await storeBundleInR2(storage, pluginId, version, bundle);
412
+
413
+ // Write plugin state
414
+ await stateRepo.upsert(pluginId, version, "active", {
415
+ source: "marketplace",
416
+ marketplaceVersion: version,
417
+ displayName: pluginDetail.name,
418
+ description: pluginDetail.description ?? undefined,
419
+ });
420
+
421
+ // Fire-and-forget install stat
422
+ client.reportInstall(pluginId, version).catch(() => {
423
+ // Intentional: never fails the install
424
+ });
425
+ return {
426
+ success: true,
427
+ data: {
428
+ pluginId,
429
+ version,
430
+ capabilities: bundle.manifest.capabilities,
431
+ },
432
+ };
433
+ } catch (err) {
434
+ if (err instanceof MarketplaceUnavailableError) {
435
+ return {
436
+ success: false,
437
+ error: {
438
+ code: "MARKETPLACE_UNAVAILABLE",
439
+ message: "Plugin marketplace is currently unavailable",
440
+ },
441
+ };
442
+ }
443
+ if (err instanceof MarketplaceError) {
444
+ return {
445
+ success: false,
446
+ error: {
447
+ code: err.code ?? "MARKETPLACE_ERROR",
448
+ message: err.message,
449
+ },
450
+ };
451
+ }
452
+ if (err instanceof EmDashStorageError) {
453
+ return {
454
+ success: false,
455
+ error: {
456
+ code: err.code ?? "STORAGE_ERROR",
457
+ message: "Storage error while installing plugin",
458
+ },
459
+ };
460
+ }
461
+ if (err && typeof err === "object" && "code" in err) {
462
+ const code = (err as { code?: unknown }).code;
463
+ if (typeof code === "string" && code.trim()) {
464
+ return {
465
+ success: false,
466
+ error: {
467
+ code,
468
+ message: "Failed to install plugin from marketplace",
469
+ },
470
+ };
471
+ }
472
+ }
473
+ console.error("Failed to install marketplace plugin:", err);
474
+ return {
475
+ success: false,
476
+ error: {
477
+ code: "INSTALL_FAILED",
478
+ message: "Failed to install plugin from marketplace",
479
+ },
480
+ };
481
+ }
482
+ }
483
+
484
+ // ── Update ─────────────────────────────────────────────────────────
485
+
486
+ export async function handleMarketplaceUpdate(
487
+ db: Kysely<Database>,
488
+ storage: Storage | null,
489
+ sandboxRunner: SandboxRunner | null,
490
+ marketplaceUrl: string | undefined,
491
+ pluginId: string,
492
+ opts?: {
493
+ version?: string;
494
+ confirmCapabilityChanges?: boolean;
495
+ confirmRouteVisibilityChanges?: boolean;
496
+ },
497
+ ): Promise<ApiResult<MarketplaceUpdateResult>> {
498
+ const client = getClient(marketplaceUrl);
499
+ if (!client) {
500
+ return {
501
+ success: false,
502
+ error: { code: "MARKETPLACE_NOT_CONFIGURED", message: "Marketplace is not configured" },
503
+ };
504
+ }
505
+ if (!storage) {
506
+ return {
507
+ success: false,
508
+ error: { code: "STORAGE_NOT_CONFIGURED", message: "Storage is required" },
509
+ };
510
+ }
511
+ if (!sandboxRunner || !sandboxRunner.isAvailable()) {
512
+ return {
513
+ success: false,
514
+ error: { code: "SANDBOX_NOT_AVAILABLE", message: "Sandbox runner is required" },
515
+ };
516
+ }
517
+
518
+ try {
519
+ const stateRepo = new PluginStateRepository(db);
520
+ const existing = await stateRepo.get(pluginId);
521
+ if (!existing || existing.source !== "marketplace") {
522
+ return {
523
+ success: false,
524
+ error: {
525
+ code: "NOT_FOUND",
526
+ message: `No marketplace plugin found: ${pluginId}`,
527
+ },
528
+ };
529
+ }
530
+
531
+ const oldVersion = existing.marketplaceVersion ?? existing.version;
532
+
533
+ // Get target version
534
+ const pluginDetail = await client.getPlugin(pluginId);
535
+ const newVersion = opts?.version ?? pluginDetail.latestVersion?.version;
536
+ if (!newVersion) {
537
+ return {
538
+ success: false,
539
+ error: { code: "NO_VERSION", message: "No newer version available" },
540
+ };
541
+ }
542
+
543
+ if (newVersion === oldVersion) {
544
+ return {
545
+ success: false,
546
+ error: { code: "ALREADY_UP_TO_DATE", message: "Plugin is already up to date" },
547
+ };
548
+ }
549
+
550
+ const versionMetadata = await resolveVersionMetadata(
551
+ client,
552
+ pluginId,
553
+ pluginDetail,
554
+ newVersion,
555
+ );
556
+ if (!versionMetadata) {
557
+ return {
558
+ success: false,
559
+ error: {
560
+ code: "NO_VERSION",
561
+ message: `Version ${newVersion} was not found for plugin ${pluginId}`,
562
+ },
563
+ };
564
+ }
565
+
566
+ // Download new bundle
567
+ const bundle = await client.downloadBundle(pluginId, newVersion);
568
+
569
+ // Verify checksum matches marketplace-published checksum for this version
570
+ if (versionMetadata.checksum && bundle.checksum !== versionMetadata.checksum) {
571
+ return {
572
+ success: false,
573
+ error: {
574
+ code: "CHECKSUM_MISMATCH",
575
+ message: "Bundle checksum does not match marketplace record. Download may be corrupted.",
576
+ },
577
+ };
578
+ }
579
+
580
+ const bundleIdentityError = validateBundleIdentity(bundle, pluginId, newVersion);
581
+ if (bundleIdentityError) return bundleIdentityError;
582
+
583
+ // Diff capabilities and route visibility against old version
584
+ const oldBundle = await loadBundleFromR2(storage, pluginId, oldVersion);
585
+ const oldCaps = oldBundle?.manifest.capabilities ?? [];
586
+ const capabilityChanges = diffCapabilities(oldCaps, bundle.manifest.capabilities);
587
+ const hasEscalation = capabilityChanges.added.length > 0;
588
+
589
+ // If capabilities escalated, require explicit confirmation
590
+ if (hasEscalation && !opts?.confirmCapabilityChanges) {
591
+ return {
592
+ success: false,
593
+ error: {
594
+ code: "CAPABILITY_ESCALATION",
595
+ message: "Plugin update requires new capabilities",
596
+ details: { capabilityChanges },
597
+ },
598
+ };
599
+ }
600
+
601
+ // Diff route visibility — routes going from private to public are a
602
+ // security-sensitive change that exposes unauthenticated endpoints.
603
+ const routeVisibilityChanges = diffRouteVisibility(oldBundle?.manifest, bundle.manifest);
604
+ const hasNewPublicRoutes = routeVisibilityChanges.newlyPublic.length > 0;
605
+
606
+ if (hasNewPublicRoutes && !opts?.confirmRouteVisibilityChanges) {
607
+ return {
608
+ success: false,
609
+ error: {
610
+ code: "ROUTE_VISIBILITY_ESCALATION",
611
+ message: "Plugin update exposes new public (unauthenticated) routes",
612
+ details: { routeVisibilityChanges, capabilityChanges },
613
+ },
614
+ };
615
+ }
616
+
617
+ // Store new bundle
618
+ await storeBundleInR2(storage, pluginId, newVersion, bundle);
619
+
620
+ // Update state
621
+ await stateRepo.upsert(pluginId, newVersion, "active", {
622
+ source: "marketplace",
623
+ marketplaceVersion: newVersion,
624
+ displayName: pluginDetail.name,
625
+ description: pluginDetail.description ?? undefined,
626
+ });
627
+
628
+ // Clean up old bundle from R2 (best-effort)
629
+ deleteBundleFromR2(storage, pluginId, oldVersion).catch(() => {});
630
+
631
+ return {
632
+ success: true,
633
+ data: {
634
+ pluginId,
635
+ oldVersion,
636
+ newVersion,
637
+ capabilityChanges,
638
+ routeVisibilityChanges: hasNewPublicRoutes ? routeVisibilityChanges : undefined,
639
+ },
640
+ };
641
+ } catch (err) {
642
+ if (err instanceof MarketplaceUnavailableError) {
643
+ return {
644
+ success: false,
645
+ error: { code: "MARKETPLACE_UNAVAILABLE", message: "Marketplace is unavailable" },
646
+ };
647
+ }
648
+ if (err instanceof MarketplaceError) {
649
+ return {
650
+ success: false,
651
+ error: { code: err.code ?? "MARKETPLACE_ERROR", message: err.message },
652
+ };
653
+ }
654
+ console.error("Failed to update marketplace plugin:", err);
655
+ return {
656
+ success: false,
657
+ error: { code: "UPDATE_FAILED", message: "Failed to update plugin" },
658
+ };
659
+ }
660
+ }
661
+
662
+ // ── Uninstall ──────────────────────────────────────────────────────
663
+
664
+ export async function handleMarketplaceUninstall(
665
+ db: Kysely<Database>,
666
+ storage: Storage | null,
667
+ pluginId: string,
668
+ opts?: { deleteData?: boolean },
669
+ ): Promise<ApiResult<MarketplaceUninstallResult>> {
670
+ try {
671
+ const stateRepo = new PluginStateRepository(db);
672
+ const existing = await stateRepo.get(pluginId);
673
+ if (!existing || existing.source !== "marketplace") {
674
+ return {
675
+ success: false,
676
+ error: {
677
+ code: "NOT_FOUND",
678
+ message: `No marketplace plugin found: ${pluginId}`,
679
+ },
680
+ };
681
+ }
682
+
683
+ const version = existing.marketplaceVersion ?? existing.version;
684
+
685
+ // Delete bundle from site R2
686
+ if (storage) {
687
+ await deleteBundleFromR2(storage, pluginId, version);
688
+ }
689
+
690
+ // Optionally delete plugin storage data
691
+ let dataDeleted = false;
692
+ if (opts?.deleteData) {
693
+ try {
694
+ await db.deleteFrom("_plugin_storage").where("plugin_id", "=", pluginId).execute();
695
+ dataDeleted = true;
696
+ } catch {
697
+ // Plugin storage table may not have data for this plugin
698
+ }
699
+ }
700
+
701
+ // Delete state row
702
+ await stateRepo.delete(pluginId);
703
+
704
+ return {
705
+ success: true,
706
+ data: { pluginId, dataDeleted },
707
+ };
708
+ } catch (err) {
709
+ console.error("Failed to uninstall marketplace plugin:", err);
710
+ return {
711
+ success: false,
712
+ error: {
713
+ code: "UNINSTALL_FAILED",
714
+ message: "Failed to uninstall plugin",
715
+ },
716
+ };
717
+ }
718
+ }
719
+
720
+ // ── Update check ───────────────────────────────────────────────────
721
+
722
+ export async function handleMarketplaceUpdateCheck(
723
+ db: Kysely<Database>,
724
+ marketplaceUrl: string | undefined,
725
+ ): Promise<ApiResult<{ items: MarketplaceUpdateCheck[] }>> {
726
+ const client = getClient(marketplaceUrl);
727
+ if (!client) {
728
+ return {
729
+ success: false,
730
+ error: { code: "MARKETPLACE_NOT_CONFIGURED", message: "Marketplace is not configured" },
731
+ };
732
+ }
733
+
734
+ try {
735
+ const stateRepo = new PluginStateRepository(db);
736
+ const marketplacePlugins = await stateRepo.getMarketplacePlugins();
737
+
738
+ const items: MarketplaceUpdateCheck[] = [];
739
+
740
+ for (const plugin of marketplacePlugins) {
741
+ try {
742
+ const detail = await client.getPlugin(plugin.pluginId);
743
+ const latest = detail.latestVersion?.version;
744
+ const installed = plugin.marketplaceVersion ?? plugin.version;
745
+
746
+ if (!latest) continue;
747
+
748
+ const hasUpdate = latest !== installed;
749
+ let capabilityChanges: { added: string[]; removed: string[] } | undefined;
750
+ let hasCapabilityChanges = false;
751
+
752
+ if (hasUpdate && detail.latestVersion) {
753
+ const oldCaps = detail.capabilities ?? [];
754
+ const newCaps = detail.latestVersion.capabilities ?? [];
755
+ capabilityChanges = diffCapabilities(oldCaps, newCaps);
756
+ hasCapabilityChanges =
757
+ capabilityChanges.added.length > 0 || capabilityChanges.removed.length > 0;
758
+ }
759
+
760
+ items.push({
761
+ pluginId: plugin.pluginId,
762
+ installed,
763
+ latest: latest ?? installed,
764
+ hasUpdate,
765
+ hasCapabilityChanges,
766
+ capabilityChanges: hasCapabilityChanges ? capabilityChanges : undefined,
767
+ // Route visibility changes require downloading both bundles to compare
768
+ // manifests, which is too expensive for a preview check. The actual
769
+ // enforcement happens at update time in handleMarketplaceUpdate.
770
+ hasRouteVisibilityChanges: false,
771
+ });
772
+ } catch (err) {
773
+ // Skip plugins that can't be checked (marketplace down, plugin delisted)
774
+ console.warn(`Failed to check updates for ${plugin.pluginId}:`, err);
775
+ }
776
+ }
777
+
778
+ return { success: true, data: { items } };
779
+ } catch (err) {
780
+ if (err instanceof MarketplaceUnavailableError) {
781
+ return {
782
+ success: false,
783
+ error: { code: "MARKETPLACE_UNAVAILABLE", message: "Marketplace is unavailable" },
784
+ };
785
+ }
786
+ console.error("Failed to check marketplace updates:", err);
787
+ return {
788
+ success: false,
789
+ error: { code: "UPDATE_CHECK_FAILED", message: "Failed to check for updates" },
790
+ };
791
+ }
792
+ }
793
+
794
+ // ── Proxy ──────────────────────────────────────────────────────────
795
+
796
+ export async function handleMarketplaceSearch(
797
+ marketplaceUrl: string | undefined,
798
+ query?: string,
799
+ opts?: MarketplaceSearchOpts,
800
+ ): Promise<ApiResult<unknown>> {
801
+ const client = getClient(marketplaceUrl);
802
+ if (!client) {
803
+ return {
804
+ success: false,
805
+ error: { code: "MARKETPLACE_NOT_CONFIGURED", message: "Marketplace is not configured" },
806
+ };
807
+ }
808
+
809
+ try {
810
+ const result = await client.search(query, opts);
811
+ return { success: true, data: result };
812
+ } catch (err) {
813
+ if (err instanceof MarketplaceUnavailableError) {
814
+ return {
815
+ success: false,
816
+ error: { code: "MARKETPLACE_UNAVAILABLE", message: "Marketplace is unavailable" },
817
+ };
818
+ }
819
+ console.error("Failed to search marketplace:", err);
820
+ return {
821
+ success: false,
822
+ error: { code: "SEARCH_FAILED", message: "Failed to search marketplace" },
823
+ };
824
+ }
825
+ }
826
+
827
+ export async function handleMarketplaceGetPlugin(
828
+ marketplaceUrl: string | undefined,
829
+ pluginId: string,
830
+ ): Promise<ApiResult<unknown>> {
831
+ const client = getClient(marketplaceUrl);
832
+ if (!client) {
833
+ return {
834
+ success: false,
835
+ error: { code: "MARKETPLACE_NOT_CONFIGURED", message: "Marketplace is not configured" },
836
+ };
837
+ }
838
+
839
+ try {
840
+ const result = await client.getPlugin(pluginId);
841
+ return { success: true, data: result };
842
+ } catch (err) {
843
+ if (err instanceof MarketplaceError && err.status === 404) {
844
+ return {
845
+ success: false,
846
+ error: { code: "NOT_FOUND", message: `Plugin not found: ${pluginId}` },
847
+ };
848
+ }
849
+ if (err instanceof MarketplaceUnavailableError) {
850
+ return {
851
+ success: false,
852
+ error: { code: "MARKETPLACE_UNAVAILABLE", message: "Marketplace is unavailable" },
853
+ };
854
+ }
855
+ console.error("Failed to get marketplace plugin:", err);
856
+ return {
857
+ success: false,
858
+ error: { code: "GET_PLUGIN_FAILED", message: "Failed to get plugin details" },
859
+ };
860
+ }
861
+ }
862
+
863
+ // ── Theme proxy handlers ──────────────────────────────────────────
864
+
865
+ export async function handleThemeSearch(
866
+ marketplaceUrl: string | undefined,
867
+ query?: string,
868
+ opts?: MarketplaceThemeSearchOpts,
869
+ ): Promise<ApiResult<unknown>> {
870
+ const client = getClient(marketplaceUrl);
871
+ if (!client) {
872
+ return {
873
+ success: false,
874
+ error: { code: "MARKETPLACE_NOT_CONFIGURED", message: "Marketplace is not configured" },
875
+ };
876
+ }
877
+
878
+ try {
879
+ const result = await client.searchThemes(query, opts);
880
+ return { success: true, data: result };
881
+ } catch (err) {
882
+ if (err instanceof MarketplaceUnavailableError) {
883
+ return {
884
+ success: false,
885
+ error: { code: "MARKETPLACE_UNAVAILABLE", message: "Marketplace is unavailable" },
886
+ };
887
+ }
888
+ console.error("Failed to search themes:", err);
889
+ return {
890
+ success: false,
891
+ error: { code: "THEME_SEARCH_FAILED", message: "Failed to search themes" },
892
+ };
893
+ }
894
+ }
895
+
896
+ export async function handleThemeGetDetail(
897
+ marketplaceUrl: string | undefined,
898
+ themeId: string,
899
+ ): Promise<ApiResult<unknown>> {
900
+ const client = getClient(marketplaceUrl);
901
+ if (!client) {
902
+ return {
903
+ success: false,
904
+ error: { code: "MARKETPLACE_NOT_CONFIGURED", message: "Marketplace is not configured" },
905
+ };
906
+ }
907
+
908
+ try {
909
+ const result = await client.getTheme(themeId);
910
+ return { success: true, data: result };
911
+ } catch (err) {
912
+ if (err instanceof MarketplaceError && err.status === 404) {
913
+ return {
914
+ success: false,
915
+ error: { code: "NOT_FOUND", message: `Theme not found: ${themeId}` },
916
+ };
917
+ }
918
+ if (err instanceof MarketplaceUnavailableError) {
919
+ return {
920
+ success: false,
921
+ error: { code: "MARKETPLACE_UNAVAILABLE", message: "Marketplace is unavailable" },
922
+ };
923
+ }
924
+ console.error("Failed to get marketplace theme:", err);
925
+ return {
926
+ success: false,
927
+ error: { code: "GET_THEME_FAILED", message: "Failed to get theme details" },
928
+ };
929
+ }
930
+ }