emdash 0.0.0-a → 0.0.1

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 -1
  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 +1333 -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-B-u2F2b6.mjs +1412 -0
  127. package/dist/runner-B-u2F2b6.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 +687 -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 +353 -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 +328 -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 +120 -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 +117 -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 +105 -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 +62 -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 +72 -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 +699 -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 +288 -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 +136 -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 +42 -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 +253 -0
  646. package/src/storage/s3.ts +271 -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,1284 @@
1
+ /**
2
+ * Plugin System Types v2
3
+ *
4
+ * New plugin API with:
5
+ * - Single unified context shape for all hooks and routes
6
+ * - Paginated storage queries (no async iterators)
7
+ * - Unified KV API (replaces settings + options)
8
+ * - Explicit ctx.http and ctx.log
9
+ *
10
+ */
11
+
12
+ import type { Element } from "@emdash-cms/blocks";
13
+ import type { JSX } from "astro/jsx-runtime";
14
+ import type { z } from "astro/zod";
15
+
16
+ import type { FieldType } from "../schema/types.js";
17
+
18
+ // =============================================================================
19
+ // Core Types
20
+ // =============================================================================
21
+
22
+ /**
23
+ * Plugin capabilities determine what APIs are available in context
24
+ */
25
+ export type PluginCapability =
26
+ | "network:fetch" // ctx.http is available (host-restricted via allowedHosts)
27
+ | "network:fetch:any" // ctx.http is available (unrestricted outbound — use for user-configured URLs)
28
+ | "read:content" // ctx.content.get/list available
29
+ | "write:content" // ctx.content.create/update/delete available
30
+ | "read:media" // ctx.media.get/list available
31
+ | "write:media" // ctx.media.getUploadUrl/delete available
32
+ | "read:users" // ctx.users is available
33
+ | "email:send" // ctx.email is available (when a provider is configured)
34
+ | "email:provide" // can register email:deliver exclusive hook (transport provider)
35
+ | "email:intercept" // can register email:beforeSend / email:afterSend hooks
36
+ | "page:inject"; // can register page:fragments hook (inject scripts/styles into pages)
37
+
38
+ // =============================================================================
39
+ // Storage Types
40
+ // =============================================================================
41
+
42
+ /**
43
+ * Storage collection declaration in plugin definition
44
+ */
45
+ export interface StorageCollectionConfig {
46
+ /**
47
+ * Fields to index for querying.
48
+ * Each entry can be a single field name or an array for composite indexes.
49
+ */
50
+ indexes: Array<string | string[]>;
51
+ /**
52
+ * Fields with unique constraints.
53
+ * Each entry can be a single field name or an array for composite unique indexes.
54
+ * Unique indexes are also queryable (no need to duplicate in `indexes`).
55
+ */
56
+ uniqueIndexes?: Array<string | string[]>;
57
+ }
58
+
59
+ /**
60
+ * Plugin storage configuration
61
+ */
62
+ export type PluginStorageConfig = Record<string, StorageCollectionConfig>;
63
+
64
+ /**
65
+ * Query filter operators
66
+ */
67
+ export interface RangeFilter {
68
+ gt?: number | string;
69
+ gte?: number | string;
70
+ lt?: number | string;
71
+ lte?: number | string;
72
+ }
73
+
74
+ export interface InFilter {
75
+ in: Array<string | number>;
76
+ }
77
+
78
+ export interface StartsWithFilter {
79
+ startsWith: string;
80
+ }
81
+
82
+ /**
83
+ * Where clause value types
84
+ */
85
+ export type WhereValue =
86
+ | string
87
+ | number
88
+ | boolean
89
+ | null
90
+ | RangeFilter
91
+ | InFilter
92
+ | StartsWithFilter;
93
+
94
+ /**
95
+ * Where clause for storage queries
96
+ */
97
+ export type WhereClause = Record<string, WhereValue>;
98
+
99
+ /**
100
+ * Query options for storage.query()
101
+ */
102
+ export interface QueryOptions {
103
+ where?: WhereClause;
104
+ orderBy?: Record<string, "asc" | "desc">;
105
+ limit?: number; // Default 50, max 1000
106
+ cursor?: string;
107
+ }
108
+
109
+ /**
110
+ * Paginated result (used by storage.query, content.list, media.list)
111
+ */
112
+ export interface PaginatedResult<T> {
113
+ items: T[];
114
+ cursor?: string;
115
+ hasMore: boolean;
116
+ }
117
+
118
+ /**
119
+ * Storage collection interface - the API exposed to plugins
120
+ * No async iterators - all operations return promises with pagination
121
+ */
122
+ export interface StorageCollection<T = unknown> {
123
+ // Basic CRUD
124
+ get(id: string): Promise<T | null>;
125
+ put(id: string, data: T): Promise<void>;
126
+ delete(id: string): Promise<boolean>;
127
+ exists(id: string): Promise<boolean>;
128
+
129
+ // Batch operations
130
+ getMany(ids: string[]): Promise<Map<string, T>>;
131
+ putMany(items: Array<{ id: string; data: T }>): Promise<void>;
132
+ deleteMany(ids: string[]): Promise<number>;
133
+
134
+ // Query - always paginated
135
+ query(options?: QueryOptions): Promise<PaginatedResult<{ id: string; data: T }>>;
136
+ count(where?: WhereClause): Promise<number>;
137
+ }
138
+
139
+ /**
140
+ * Plugin storage context - typed based on declared collections
141
+ */
142
+ export type PluginStorage<T extends PluginStorageConfig> = {
143
+ [K in keyof T]: StorageCollection;
144
+ };
145
+
146
+ // =============================================================================
147
+ // Context APIs
148
+ // =============================================================================
149
+
150
+ /**
151
+ * KV store interface - unified replacement for settings + options
152
+ *
153
+ * Convention:
154
+ * - `settings:*` - User-configurable preferences (shown in admin UI)
155
+ * - `state:*` - Internal plugin state (not shown to users)
156
+ */
157
+ export interface KVAccess {
158
+ get<T>(key: string): Promise<T | null>;
159
+ set(key: string, value: unknown): Promise<void>;
160
+ delete(key: string): Promise<boolean>;
161
+ list(prefix?: string): Promise<Array<{ key: string; value: unknown }>>;
162
+ }
163
+
164
+ /**
165
+ * Content item returned from content API
166
+ */
167
+ export interface ContentItem {
168
+ id: string;
169
+ type: string;
170
+ data: Record<string, unknown>;
171
+ createdAt: string;
172
+ updatedAt: string;
173
+ }
174
+
175
+ /**
176
+ * Content list options
177
+ */
178
+ export interface ContentListOptions {
179
+ limit?: number;
180
+ cursor?: string;
181
+ orderBy?: Record<string, "asc" | "desc">;
182
+ }
183
+
184
+ /**
185
+ * Content access interface - capability-gated
186
+ */
187
+ export interface ContentAccess {
188
+ // Read operations (requires read:content)
189
+ get(collection: string, id: string): Promise<ContentItem | null>;
190
+ list(collection: string, options?: ContentListOptions): Promise<PaginatedResult<ContentItem>>;
191
+
192
+ // Write operations (requires write:content) - optional on interface
193
+ create?(collection: string, data: Record<string, unknown>): Promise<ContentItem>;
194
+ update?(collection: string, id: string, data: Record<string, unknown>): Promise<ContentItem>;
195
+ delete?(collection: string, id: string): Promise<boolean>;
196
+ }
197
+
198
+ /**
199
+ * Full content access with write operations
200
+ */
201
+ export interface ContentAccessWithWrite extends ContentAccess {
202
+ create(collection: string, data: Record<string, unknown>): Promise<ContentItem>;
203
+ update(collection: string, id: string, data: Record<string, unknown>): Promise<ContentItem>;
204
+ delete(collection: string, id: string): Promise<boolean>;
205
+ }
206
+
207
+ /**
208
+ * Media item returned from media API
209
+ */
210
+ export interface MediaItem {
211
+ id: string;
212
+ filename: string;
213
+ mimeType: string;
214
+ size: number | null;
215
+ url: string;
216
+ createdAt: string;
217
+ }
218
+
219
+ /**
220
+ * Media list options
221
+ */
222
+ export interface MediaListOptions {
223
+ limit?: number;
224
+ cursor?: string;
225
+ mimeType?: string; // Filter by mime type prefix, e.g., "image/"
226
+ }
227
+
228
+ /**
229
+ * Media access interface - capability-gated
230
+ */
231
+ export interface MediaAccess {
232
+ // Read operations (requires read:media)
233
+ get(id: string): Promise<MediaItem | null>;
234
+ list(options?: MediaListOptions): Promise<PaginatedResult<MediaItem>>;
235
+
236
+ // Write operations (requires write:media) - optional on interface
237
+ getUploadUrl?(
238
+ filename: string,
239
+ contentType: string,
240
+ ): Promise<{ uploadUrl: string; mediaId: string }>;
241
+ /**
242
+ * Upload media bytes directly. Preferred in sandboxed mode where
243
+ * plugins cannot make external requests to a presigned URL.
244
+ * Returns the created media item.
245
+ */
246
+ upload?(
247
+ filename: string,
248
+ contentType: string,
249
+ bytes: ArrayBuffer,
250
+ ): Promise<{ mediaId: string; storageKey: string; url: string }>;
251
+ delete?(id: string): Promise<boolean>;
252
+ }
253
+
254
+ /**
255
+ * Full media access with write operations
256
+ */
257
+ export interface MediaAccessWithWrite extends MediaAccess {
258
+ getUploadUrl(
259
+ filename: string,
260
+ contentType: string,
261
+ ): Promise<{ uploadUrl: string; mediaId: string }>;
262
+ upload(
263
+ filename: string,
264
+ contentType: string,
265
+ bytes: ArrayBuffer,
266
+ ): Promise<{ mediaId: string; storageKey: string; url: string }>;
267
+ delete(id: string): Promise<boolean>;
268
+ }
269
+
270
+ /**
271
+ * HTTP client interface - requires network:fetch capability
272
+ */
273
+ export interface HttpAccess {
274
+ fetch(url: string, init?: RequestInit): Promise<Response>;
275
+ }
276
+
277
+ /**
278
+ * Logger interface - always available
279
+ */
280
+ export interface LogAccess {
281
+ debug(message: string, data?: unknown): void;
282
+ info(message: string, data?: unknown): void;
283
+ warn(message: string, data?: unknown): void;
284
+ error(message: string, data?: unknown): void;
285
+ }
286
+
287
+ // =============================================================================
288
+ // Site & User Access
289
+ // =============================================================================
290
+
291
+ /**
292
+ * Site information available to all plugins
293
+ */
294
+ export interface SiteInfo {
295
+ /** Site name (from settings) */
296
+ name: string;
297
+ /** Site URL (from settings or request) */
298
+ url: string;
299
+ /** Site locale (from settings, defaults to "en") */
300
+ locale: string;
301
+ }
302
+
303
+ /**
304
+ * Read-only user information exposed to plugins.
305
+ * Sensitive fields (password hashes, sessions, passkeys) are excluded.
306
+ */
307
+ export interface UserInfo {
308
+ id: string;
309
+ email: string;
310
+ name: string | null;
311
+ role: number;
312
+ createdAt: string;
313
+ }
314
+
315
+ /**
316
+ * User access interface - requires read:users capability
317
+ */
318
+ export interface UserAccess {
319
+ /** Get a user by ID */
320
+ get(id: string): Promise<UserInfo | null>;
321
+ /** Get a user by email */
322
+ getByEmail(email: string): Promise<UserInfo | null>;
323
+ /** List users with optional filters */
324
+ list(opts?: { role?: number; limit?: number; cursor?: string }): Promise<{
325
+ items: UserInfo[];
326
+ nextCursor?: string;
327
+ }>;
328
+ }
329
+
330
+ // =============================================================================
331
+ // Plugin Context
332
+ // =============================================================================
333
+
334
+ /**
335
+ * The unified plugin context - same shape for all hooks and routes
336
+ */
337
+ export interface PluginContext<TStorage extends PluginStorageConfig = PluginStorageConfig> {
338
+ /** Plugin metadata */
339
+ plugin: {
340
+ id: string;
341
+ version: string;
342
+ };
343
+
344
+ /** Storage collections - only if plugin declares storage */
345
+ storage: PluginStorage<TStorage>;
346
+
347
+ /** Key-value store for config and state */
348
+ kv: KVAccess;
349
+
350
+ /** Content access - only if read:content or write:content capability */
351
+ content?: ContentAccess | ContentAccessWithWrite;
352
+
353
+ /** Media access - only if read:media or write:media capability */
354
+ media?: MediaAccess | MediaAccessWithWrite;
355
+
356
+ /** HTTP client - only if network:fetch capability */
357
+ http?: HttpAccess;
358
+
359
+ /** Logger - always available */
360
+ log: LogAccess;
361
+
362
+ /** Site information - always available */
363
+ site: SiteInfo;
364
+
365
+ /** URL helper - generates absolute URLs from paths. Always available. */
366
+ url(path: string): string;
367
+
368
+ /** User access - only if read:users capability */
369
+ users?: UserAccess;
370
+
371
+ /** Cron task scheduling - always available, scoped to plugin */
372
+ cron?: CronAccess;
373
+
374
+ /** Email access - only if email:send capability and a provider is configured */
375
+ email?: EmailAccess;
376
+ }
377
+
378
+ // =============================================================================
379
+ // Cron Types
380
+ // =============================================================================
381
+
382
+ /**
383
+ * Cron access interface �� always available on plugin context, scoped to plugin.
384
+ */
385
+ export interface CronAccess {
386
+ /** Schedule a recurring or one-shot task */
387
+ schedule(name: string, opts: { schedule: string; data?: Record<string, unknown> }): Promise<void>;
388
+ /** Cancel a scheduled task */
389
+ cancel(name: string): Promise<void>;
390
+ /** List this plugin's scheduled tasks */
391
+ list(): Promise<CronTaskInfo[]>;
392
+ }
393
+
394
+ /**
395
+ * Task info returned from CronAccess.list()
396
+ */
397
+ export interface CronTaskInfo {
398
+ name: string;
399
+ schedule: string;
400
+ nextRunAt: string;
401
+ lastRunAt: string | null;
402
+ }
403
+
404
+ /**
405
+ * Event passed to the `cron` hook handler
406
+ */
407
+ export interface CronEvent {
408
+ name: string;
409
+ data?: Record<string, unknown>;
410
+ scheduledAt: string;
411
+ }
412
+
413
+ /**
414
+ * Cron hook handler type
415
+ */
416
+ export type CronHandler = (event: CronEvent, ctx: PluginContext) => Promise<void>;
417
+
418
+ // =============================================================================
419
+ // Email Types
420
+ // =============================================================================
421
+
422
+ /**
423
+ * Email access interface — requires `email:send` capability.
424
+ * Undefined when no `email:deliver` provider is configured.
425
+ *
426
+ * Related capabilities:
427
+ * - `email:send` — grants ctx.email (this interface)
428
+ * - `email:provide` — allows registering the `email:deliver` exclusive hook
429
+ * - `email:intercept` — allows registering `email:beforeSend` / `email:afterSend` hooks
430
+ */
431
+ export interface EmailAccess {
432
+ send(message: EmailMessage): Promise<void>;
433
+ }
434
+
435
+ /**
436
+ * Email message shape
437
+ */
438
+ export interface EmailMessage {
439
+ to: string;
440
+ subject: string;
441
+ text: string;
442
+ html?: string;
443
+ }
444
+
445
+ /**
446
+ * Event passed to email:beforeSend hooks (middleware — transform, validate, cancel)
447
+ */
448
+ export interface EmailBeforeSendEvent {
449
+ message: EmailMessage;
450
+ /** Where the email originated — "system" for auth emails, plugin ID for plugin emails */
451
+ source: string;
452
+ }
453
+
454
+ /**
455
+ * Event passed to email:deliver hook (exclusive — exactly one provider delivers)
456
+ */
457
+ export interface EmailDeliverEvent {
458
+ message: EmailMessage;
459
+ source: string;
460
+ }
461
+
462
+ /**
463
+ * Event passed to email:afterSend hooks (logging, analytics, fire-and-forget)
464
+ */
465
+ export interface EmailAfterSendEvent {
466
+ message: EmailMessage;
467
+ source: string;
468
+ }
469
+
470
+ /**
471
+ * Handler type for email:beforeSend hooks.
472
+ * Returns modified message, or false to cancel delivery.
473
+ */
474
+ export type EmailBeforeSendHandler = (
475
+ event: EmailBeforeSendEvent,
476
+ ctx: PluginContext,
477
+ ) => Promise<EmailMessage | false>;
478
+
479
+ /**
480
+ * Handler type for email:deliver hooks (exclusive provider).
481
+ */
482
+ export type EmailDeliverHandler = (event: EmailDeliverEvent, ctx: PluginContext) => Promise<void>;
483
+
484
+ /**
485
+ * Handler type for email:afterSend hooks (fire-and-forget).
486
+ */
487
+ export type EmailAfterSendHandler = (
488
+ event: EmailAfterSendEvent,
489
+ ctx: PluginContext,
490
+ ) => Promise<void>;
491
+
492
+ // =============================================================================
493
+ // Comment Types
494
+ // =============================================================================
495
+
496
+ /**
497
+ * Collection comment settings (read from _emdash_collections)
498
+ */
499
+ export interface CollectionCommentSettings {
500
+ commentsEnabled: boolean;
501
+ commentsModeration: "all" | "first_time" | "none";
502
+ commentsClosedAfterDays: number;
503
+ commentsAutoApproveUsers: boolean;
504
+ }
505
+
506
+ /**
507
+ * Event passed to comment:beforeCreate hooks (middleware — transform, enrich, reject)
508
+ */
509
+ export interface CommentBeforeCreateEvent {
510
+ comment: {
511
+ collection: string;
512
+ contentId: string;
513
+ parentId: string | null;
514
+ authorName: string;
515
+ authorEmail: string;
516
+ authorUserId: string | null;
517
+ body: string;
518
+ ipHash: string | null;
519
+ userAgent: string | null;
520
+ };
521
+ /** Metadata bag — plugins can attach signals for the moderator */
522
+ metadata: Record<string, unknown>;
523
+ }
524
+
525
+ /**
526
+ * Event passed to comment:moderate hook (exclusive — decides initial status)
527
+ */
528
+ export interface CommentModerateEvent {
529
+ comment: CommentBeforeCreateEvent["comment"];
530
+ metadata: Record<string, unknown>;
531
+ collectionSettings: CollectionCommentSettings;
532
+ /** Number of prior approved comments from this email address */
533
+ priorApprovedCount: number;
534
+ }
535
+
536
+ /**
537
+ * Moderation decision returned by the comment:moderate handler
538
+ */
539
+ export interface ModerationDecision {
540
+ status: "approved" | "pending" | "spam";
541
+ /** Optional reason for admin visibility */
542
+ reason?: string;
543
+ }
544
+
545
+ /**
546
+ * Stored comment shape (full record with id, status, timestamps)
547
+ */
548
+ export interface StoredComment {
549
+ id: string;
550
+ collection: string;
551
+ contentId: string;
552
+ parentId: string | null;
553
+ authorName: string;
554
+ authorEmail: string;
555
+ authorUserId: string | null;
556
+ body: string;
557
+ status: string;
558
+ moderationMetadata: Record<string, unknown> | null;
559
+ createdAt: string;
560
+ updatedAt: string;
561
+ }
562
+
563
+ /**
564
+ * Event passed to comment:afterCreate hooks (fire-and-forget)
565
+ */
566
+ export interface CommentAfterCreateEvent {
567
+ comment: StoredComment;
568
+ metadata: Record<string, unknown>;
569
+ /** The content item the comment is on */
570
+ content: { id: string; collection: string; slug: string; title?: string };
571
+ /** The content author (for notifications) */
572
+ contentAuthor?: { id: string; name: string | null; email: string };
573
+ }
574
+
575
+ /**
576
+ * Event passed to comment:afterModerate hooks (fire-and-forget, admin status change)
577
+ */
578
+ export interface CommentAfterModerateEvent {
579
+ comment: StoredComment;
580
+ previousStatus: string;
581
+ newStatus: string;
582
+ /** The admin who moderated */
583
+ moderator: { id: string; name: string | null };
584
+ }
585
+
586
+ /**
587
+ * Handler type for comment:beforeCreate hooks.
588
+ * Returns modified event, or false to reject the comment.
589
+ */
590
+ export type CommentBeforeCreateHandler = (
591
+ event: CommentBeforeCreateEvent,
592
+ ctx: PluginContext,
593
+ ) => Promise<CommentBeforeCreateEvent | false | void>;
594
+
595
+ /**
596
+ * Handler type for comment:moderate hook (exclusive provider).
597
+ */
598
+ export type CommentModerateHandler = (
599
+ event: CommentModerateEvent,
600
+ ctx: PluginContext,
601
+ ) => Promise<ModerationDecision>;
602
+
603
+ /**
604
+ * Handler type for comment:afterCreate hooks (fire-and-forget).
605
+ */
606
+ export type CommentAfterCreateHandler = (
607
+ event: CommentAfterCreateEvent,
608
+ ctx: PluginContext,
609
+ ) => Promise<void>;
610
+
611
+ /**
612
+ * Handler type for comment:afterModerate hooks (fire-and-forget).
613
+ */
614
+ export type CommentAfterModerateHandler = (
615
+ event: CommentAfterModerateEvent,
616
+ ctx: PluginContext,
617
+ ) => Promise<void>;
618
+
619
+ // =============================================================================
620
+ // Hook Types
621
+ // =============================================================================
622
+
623
+ /**
624
+ * Hook configuration
625
+ */
626
+ export interface HookConfig<THandler> {
627
+ /** Explicit ordering - lower numbers run first (default: 100) */
628
+ priority?: number;
629
+ /** Max execution time in ms (default: 5000) */
630
+ timeout?: number;
631
+ /** Run after these plugins */
632
+ dependencies?: string[];
633
+ /** Error handling policy */
634
+ errorPolicy?: "continue" | "abort";
635
+ /**
636
+ * Mark this hook as exclusive — only one plugin can be the active provider.
637
+ * Exclusive hooks skip the priority pipeline and dispatch only to the
638
+ * admin-selected provider. Used for email:deliver, search, image optimization, etc.
639
+ */
640
+ exclusive?: boolean;
641
+ /** The hook handler */
642
+ handler: THandler;
643
+ }
644
+
645
+ /**
646
+ * Content hook event
647
+ */
648
+ export interface ContentHookEvent {
649
+ content: Record<string, unknown>;
650
+ collection: string;
651
+ isNew: boolean;
652
+ }
653
+
654
+ /**
655
+ * Content delete hook event
656
+ */
657
+ export interface ContentDeleteEvent {
658
+ id: string;
659
+ collection: string;
660
+ }
661
+
662
+ /**
663
+ * Media hook event
664
+ */
665
+ export interface MediaUploadEvent {
666
+ file: { name: string; type: string; size: number };
667
+ }
668
+
669
+ /**
670
+ * Media after upload event
671
+ */
672
+ export interface MediaAfterUploadEvent {
673
+ media: MediaItem;
674
+ }
675
+
676
+ /**
677
+ * Lifecycle hook event
678
+ */
679
+ export interface LifecycleEvent {
680
+ // Empty for install/activate/deactivate
681
+ }
682
+
683
+ /**
684
+ * Uninstall hook event
685
+ */
686
+ export interface UninstallEvent {
687
+ deleteData: boolean;
688
+ }
689
+
690
+ // Hook handler types - all receive (event, ctx) with unified context
691
+ export type ContentBeforeSaveHandler = (
692
+ event: ContentHookEvent,
693
+ ctx: PluginContext,
694
+ ) => Promise<Record<string, unknown> | void>;
695
+
696
+ export type ContentAfterSaveHandler = (
697
+ event: ContentHookEvent,
698
+ ctx: PluginContext,
699
+ ) => Promise<void>;
700
+
701
+ export type ContentBeforeDeleteHandler = (
702
+ event: ContentDeleteEvent,
703
+ ctx: PluginContext,
704
+ ) => Promise<boolean | void>;
705
+
706
+ export type ContentAfterDeleteHandler = (
707
+ event: ContentDeleteEvent,
708
+ ctx: PluginContext,
709
+ ) => Promise<void>;
710
+
711
+ export type MediaBeforeUploadHandler = (
712
+ event: MediaUploadEvent,
713
+ ctx: PluginContext,
714
+ ) => Promise<{ name: string; type: string; size: number } | void>;
715
+
716
+ export type MediaAfterUploadHandler = (
717
+ event: MediaAfterUploadEvent,
718
+ ctx: PluginContext,
719
+ ) => Promise<void>;
720
+
721
+ export type LifecycleHandler = (event: LifecycleEvent, ctx: PluginContext) => Promise<void>;
722
+
723
+ export type UninstallHandler = (event: UninstallEvent, ctx: PluginContext) => Promise<void>;
724
+
725
+ // =============================================================================
726
+ // Public Page Contribution Types
727
+ // =============================================================================
728
+
729
+ /** Placement targets for page fragment contributions */
730
+ export type PagePlacement = "head" | "body:start" | "body:end";
731
+
732
+ /**
733
+ * Describes the page being rendered. Passed to page hooks so plugins
734
+ * can decide what to contribute without fetching content themselves.
735
+ */
736
+ export interface PublicPageContext {
737
+ url: string;
738
+ path: string;
739
+ locale: string | null;
740
+ kind: "content" | "custom";
741
+ pageType: string;
742
+ title: string | null;
743
+ description: string | null;
744
+ canonical: string | null;
745
+ image: string | null;
746
+ content?: {
747
+ collection: string;
748
+ id: string;
749
+ slug: string | null;
750
+ };
751
+ /** SEO meta for base metadata generation in EmDashHead */
752
+ seo?: {
753
+ ogTitle?: string | null;
754
+ ogDescription?: string | null;
755
+ ogImage?: string | null;
756
+ robots?: string | null;
757
+ };
758
+ /** Article metadata for Open Graph article: tags */
759
+ articleMeta?: {
760
+ publishedTime?: string | null;
761
+ modifiedTime?: string | null;
762
+ author?: string | null;
763
+ };
764
+ /** Site name for structured data and og:site_name */
765
+ siteName?: string;
766
+ }
767
+
768
+ // ── page:metadata ───────────────────────────────────────────────
769
+
770
+ export interface PageMetadataEvent {
771
+ page: PublicPageContext;
772
+ }
773
+
774
+ /**
775
+ * Allowed rel values for link contributions.
776
+ * This is a security-critical allowlist -- sandboxed plugins can only inject
777
+ * link tags with these rel values. Adding "stylesheet", "prefetch", "prerender"
778
+ * etc. would allow sandboxed plugins to inject external resources.
779
+ */
780
+ export type PageMetadataLinkRel =
781
+ | "canonical"
782
+ | "alternate"
783
+ | "author"
784
+ | "license"
785
+ | "site.standard.document";
786
+
787
+ export type PageMetadataContribution =
788
+ | { kind: "meta"; name: string; content: string; key?: string }
789
+ | { kind: "property"; property: string; content: string; key?: string }
790
+ | { kind: "link"; rel: PageMetadataLinkRel; href: string; hreflang?: string; key?: string }
791
+ | {
792
+ kind: "jsonld";
793
+ id?: string;
794
+ graph: Record<string, unknown> | Array<Record<string, unknown>>;
795
+ };
796
+
797
+ export type PageMetadataHandler = (
798
+ event: PageMetadataEvent,
799
+ ctx: PluginContext,
800
+ ) =>
801
+ | PageMetadataContribution
802
+ | PageMetadataContribution[]
803
+ | null
804
+ | Promise<PageMetadataContribution | PageMetadataContribution[] | null>;
805
+
806
+ // ── page:fragments (trusted-only) ──────────────────────────────
807
+
808
+ export interface PageFragmentEvent {
809
+ page: PublicPageContext;
810
+ }
811
+
812
+ export type PageFragmentContribution =
813
+ | {
814
+ kind: "external-script";
815
+ placement: PagePlacement;
816
+ src: string;
817
+ async?: boolean;
818
+ defer?: boolean;
819
+ attributes?: Record<string, string>;
820
+ key?: string;
821
+ }
822
+ | {
823
+ kind: "inline-script";
824
+ placement: PagePlacement;
825
+ code: string;
826
+ attributes?: Record<string, string>;
827
+ key?: string;
828
+ }
829
+ | {
830
+ kind: "html";
831
+ placement: PagePlacement;
832
+ html: string;
833
+ key?: string;
834
+ };
835
+
836
+ export type PageFragmentHandler = (
837
+ event: PageFragmentEvent,
838
+ ctx: PluginContext,
839
+ ) =>
840
+ | PageFragmentContribution
841
+ | PageFragmentContribution[]
842
+ | null
843
+ | Promise<PageFragmentContribution | PageFragmentContribution[] | null>;
844
+
845
+ /**
846
+ * Plugin hooks definition
847
+ */
848
+ export interface PluginHooks {
849
+ // Lifecycle hooks
850
+ "plugin:install"?: HookConfig<LifecycleHandler> | LifecycleHandler;
851
+ "plugin:activate"?: HookConfig<LifecycleHandler> | LifecycleHandler;
852
+ "plugin:deactivate"?: HookConfig<LifecycleHandler> | LifecycleHandler;
853
+ "plugin:uninstall"?: HookConfig<UninstallHandler> | UninstallHandler;
854
+
855
+ // Content hooks
856
+ "content:beforeSave"?: HookConfig<ContentBeforeSaveHandler> | ContentBeforeSaveHandler;
857
+ "content:afterSave"?: HookConfig<ContentAfterSaveHandler> | ContentAfterSaveHandler;
858
+ "content:beforeDelete"?: HookConfig<ContentBeforeDeleteHandler> | ContentBeforeDeleteHandler;
859
+ "content:afterDelete"?: HookConfig<ContentAfterDeleteHandler> | ContentAfterDeleteHandler;
860
+
861
+ // Media hooks
862
+ "media:beforeUpload"?: HookConfig<MediaBeforeUploadHandler> | MediaBeforeUploadHandler;
863
+ "media:afterUpload"?: HookConfig<MediaAfterUploadHandler> | MediaAfterUploadHandler;
864
+
865
+ // Cron hook
866
+ cron?: HookConfig<CronHandler> | CronHandler;
867
+
868
+ // Email hooks
869
+ "email:beforeSend"?: HookConfig<EmailBeforeSendHandler> | EmailBeforeSendHandler;
870
+ "email:deliver"?: HookConfig<EmailDeliverHandler> | EmailDeliverHandler;
871
+ "email:afterSend"?: HookConfig<EmailAfterSendHandler> | EmailAfterSendHandler;
872
+
873
+ // Comment hooks
874
+ "comment:beforeCreate"?: HookConfig<CommentBeforeCreateHandler> | CommentBeforeCreateHandler;
875
+ "comment:moderate"?: HookConfig<CommentModerateHandler> | CommentModerateHandler;
876
+ "comment:afterCreate"?: HookConfig<CommentAfterCreateHandler> | CommentAfterCreateHandler;
877
+ "comment:afterModerate"?: HookConfig<CommentAfterModerateHandler> | CommentAfterModerateHandler;
878
+
879
+ // Public page hooks
880
+ "page:metadata"?: HookConfig<PageMetadataHandler> | PageMetadataHandler;
881
+ "page:fragments"?: HookConfig<PageFragmentHandler> | PageFragmentHandler;
882
+ }
883
+
884
+ /**
885
+ * Hook names
886
+ */
887
+ export type HookName = keyof PluginHooks;
888
+
889
+ /**
890
+ * Hook metadata entry in a plugin manifest.
891
+ * Replaces the plain hook name string with structured metadata.
892
+ */
893
+ export interface ManifestHookEntry {
894
+ name: string;
895
+ exclusive?: boolean;
896
+ priority?: number;
897
+ timeout?: number;
898
+ }
899
+
900
+ /**
901
+ * Route metadata entry in a plugin manifest.
902
+ * Replaces the plain route name string with structured metadata.
903
+ */
904
+ export interface ManifestRouteEntry {
905
+ name: string;
906
+ public?: boolean;
907
+ }
908
+
909
+ /**
910
+ * Resolved hook with normalized config
911
+ */
912
+ export interface ResolvedHook<THandler> {
913
+ priority: number;
914
+ timeout: number;
915
+ dependencies: string[];
916
+ errorPolicy: "continue" | "abort";
917
+ /** Whether this hook is exclusive (provider pattern) */
918
+ exclusive: boolean;
919
+ handler: THandler;
920
+ pluginId: string;
921
+ }
922
+
923
+ // =============================================================================
924
+ // Request Metadata Types
925
+ // =============================================================================
926
+
927
+ /**
928
+ * Geographic location information derived from the request.
929
+ * Available when running on Cloudflare Workers (via the `cf` object).
930
+ */
931
+ export interface GeoInfo {
932
+ country: string | null;
933
+ region: string | null;
934
+ city: string | null;
935
+ }
936
+
937
+ /**
938
+ * Normalized request metadata available to plugin route handlers.
939
+ * Extracted from request headers and platform-specific properties.
940
+ */
941
+ export interface RequestMeta {
942
+ ip: string | null;
943
+ userAgent: string | null;
944
+ referer: string | null;
945
+ geo: GeoInfo | null;
946
+ }
947
+
948
+ // =============================================================================
949
+ // Route Types
950
+ // =============================================================================
951
+
952
+ /**
953
+ * Route handler context extends plugin context with request-specific data
954
+ */
955
+ export interface RouteContext<TInput = unknown> extends PluginContext {
956
+ /** Validated input from request body */
957
+ input: TInput;
958
+ /** Original request */
959
+ request: Request;
960
+ /** Normalized request metadata (IP, user agent, geo) */
961
+ requestMeta: RequestMeta;
962
+ }
963
+
964
+ /**
965
+ * Route definition
966
+ */
967
+ export interface PluginRoute<TInput = unknown> {
968
+ /** Zod schema for input validation */
969
+ input?: z.ZodType<TInput>;
970
+ /**
971
+ * Mark this route as publicly accessible (no authentication required).
972
+ * Public routes skip session/token auth and CSRF checks.
973
+ */
974
+ public?: boolean;
975
+ /** Route handler */
976
+ handler: (ctx: RouteContext<TInput>) => Promise<unknown>;
977
+ }
978
+
979
+ // =============================================================================
980
+ // Plugin Definition
981
+ // =============================================================================
982
+
983
+ /**
984
+ * Admin page definition
985
+ */
986
+ export interface PluginAdminPage {
987
+ path: string;
988
+ label: string;
989
+ icon?: string;
990
+ }
991
+
992
+ /**
993
+ * Dashboard widget definition
994
+ */
995
+ export interface PluginDashboardWidget {
996
+ id: string;
997
+ size?: "full" | "half" | "third";
998
+ title?: string;
999
+ }
1000
+
1001
+ /**
1002
+ * Settings field types (for admin UI generation)
1003
+ */
1004
+ export type SettingFieldType = "string" | "number" | "boolean" | "select" | "secret";
1005
+
1006
+ export interface BaseSettingField {
1007
+ type: SettingFieldType;
1008
+ label: string;
1009
+ description?: string;
1010
+ }
1011
+
1012
+ export interface StringSettingField extends BaseSettingField {
1013
+ type: "string";
1014
+ default?: string;
1015
+ multiline?: boolean;
1016
+ }
1017
+
1018
+ export interface NumberSettingField extends BaseSettingField {
1019
+ type: "number";
1020
+ default?: number;
1021
+ min?: number;
1022
+ max?: number;
1023
+ }
1024
+
1025
+ export interface BooleanSettingField extends BaseSettingField {
1026
+ type: "boolean";
1027
+ default?: boolean;
1028
+ }
1029
+
1030
+ export interface SelectSettingField extends BaseSettingField {
1031
+ type: "select";
1032
+ options: Array<{ value: string; label: string }>;
1033
+ default?: string;
1034
+ }
1035
+
1036
+ export interface SecretSettingField extends BaseSettingField {
1037
+ type: "secret";
1038
+ }
1039
+
1040
+ export type SettingField =
1041
+ | StringSettingField
1042
+ | NumberSettingField
1043
+ | BooleanSettingField
1044
+ | SelectSettingField
1045
+ | SecretSettingField;
1046
+
1047
+ /**
1048
+ * Block Kit element for block editing fields.
1049
+ * This is the `Element` discriminated union from `@emdash-cms/blocks`.
1050
+ * Plugin authors should use `@emdash-cms/blocks` builder functions to create these.
1051
+ */
1052
+ export type PortableTextBlockField = Element;
1053
+
1054
+ /**
1055
+ * Configuration for a Portable Text block type contributed by a plugin
1056
+ */
1057
+ export interface PortableTextBlockConfig {
1058
+ /** Block type name (must match the `_type` in Portable Text) */
1059
+ type: string;
1060
+ /** Human-readable label shown in slash commands and modals */
1061
+ label: string;
1062
+ /** Icon key (e.g., "video", "code", "link", "link-external") */
1063
+ icon?: string;
1064
+ /** Description shown in slash command menu */
1065
+ description?: string;
1066
+ /** Placeholder text for the URL input */
1067
+ placeholder?: string;
1068
+ /** Block Kit form fields for the editing UI. If declared, replaces the simple URL input. */
1069
+ fields?: PortableTextBlockField[];
1070
+ }
1071
+
1072
+ /**
1073
+ * Configuration for a field widget type contributed by a plugin.
1074
+ * A field widget provides a custom editing UI for a schema field.
1075
+ * The field references the widget via `widget: "pluginId:widgetName"`.
1076
+ */
1077
+ export interface FieldWidgetConfig {
1078
+ /** Widget name (without plugin ID prefix) */
1079
+ name: string;
1080
+ /** Human-readable label for the admin UI */
1081
+ label: string;
1082
+ /** Which field types this widget can edit (e.g., ["json", "string"]) */
1083
+ fieldTypes: FieldType[];
1084
+ /** Block Kit elements for sandboxed rendering. Omit for trusted plugins using React. */
1085
+ elements?: Element[];
1086
+ }
1087
+
1088
+ /**
1089
+ * Admin configuration
1090
+ */
1091
+ export interface PluginAdminConfig {
1092
+ /** Module specifier for admin UI exports (e.g., "@emdash-cms/plugin-audit-log/admin") */
1093
+ entry?: string;
1094
+ /** Settings schema for auto-generated UI */
1095
+ settingsSchema?: Record<string, SettingField>;
1096
+ /** Admin pages */
1097
+ pages?: PluginAdminPage[];
1098
+ /** Dashboard widgets */
1099
+ widgets?: PluginDashboardWidget[];
1100
+ /** Portable Text block types this plugin provides */
1101
+ portableTextBlocks?: PortableTextBlockConfig[];
1102
+ /** Field widget types this plugin provides */
1103
+ fieldWidgets?: FieldWidgetConfig[];
1104
+ }
1105
+
1106
+ /**
1107
+ * Plugin definition - input to definePlugin()
1108
+ */
1109
+ export interface PluginDefinition<TStorage extends PluginStorageConfig = PluginStorageConfig> {
1110
+ /** Unique plugin identifier */
1111
+ id: string;
1112
+ /** Plugin version (semver) */
1113
+ version: string;
1114
+
1115
+ /** Declared capabilities */
1116
+ capabilities?: PluginCapability[];
1117
+
1118
+ /** Allowed hosts for network:fetch (wildcards supported: *.example.com) */
1119
+ allowedHosts?: string[];
1120
+
1121
+ /** Storage collections with indexes */
1122
+ storage?: TStorage;
1123
+
1124
+ /** Hooks */
1125
+ hooks?: PluginHooks;
1126
+
1127
+ /** API routes */
1128
+ routes?: Record<string, PluginRoute>;
1129
+
1130
+ /** Admin UI configuration */
1131
+ admin?: PluginAdminConfig;
1132
+ }
1133
+
1134
+ /**
1135
+ * Resolved plugin - after definePlugin() processing
1136
+ */
1137
+ export interface ResolvedPlugin<TStorage extends PluginStorageConfig = PluginStorageConfig> {
1138
+ id: string;
1139
+ version: string;
1140
+ capabilities: PluginCapability[];
1141
+ allowedHosts: string[];
1142
+ storage: TStorage;
1143
+ hooks: ResolvedPluginHooks;
1144
+ routes: Record<string, PluginRoute>;
1145
+ admin: PluginAdminConfig;
1146
+ }
1147
+
1148
+ /**
1149
+ * Resolved hooks with normalized config
1150
+ */
1151
+ export interface ResolvedPluginHooks {
1152
+ "plugin:install"?: ResolvedHook<LifecycleHandler>;
1153
+ "plugin:activate"?: ResolvedHook<LifecycleHandler>;
1154
+ "plugin:deactivate"?: ResolvedHook<LifecycleHandler>;
1155
+ "plugin:uninstall"?: ResolvedHook<UninstallHandler>;
1156
+ "content:beforeSave"?: ResolvedHook<ContentBeforeSaveHandler>;
1157
+ "content:afterSave"?: ResolvedHook<ContentAfterSaveHandler>;
1158
+ "content:beforeDelete"?: ResolvedHook<ContentBeforeDeleteHandler>;
1159
+ "content:afterDelete"?: ResolvedHook<ContentAfterDeleteHandler>;
1160
+ "media:beforeUpload"?: ResolvedHook<MediaBeforeUploadHandler>;
1161
+ "media:afterUpload"?: ResolvedHook<MediaAfterUploadHandler>;
1162
+ cron?: ResolvedHook<CronHandler>;
1163
+ "email:beforeSend"?: ResolvedHook<EmailBeforeSendHandler>;
1164
+ "email:deliver"?: ResolvedHook<EmailDeliverHandler>;
1165
+ "email:afterSend"?: ResolvedHook<EmailAfterSendHandler>;
1166
+ "comment:beforeCreate"?: ResolvedHook<CommentBeforeCreateHandler>;
1167
+ "comment:moderate"?: ResolvedHook<CommentModerateHandler>;
1168
+ "comment:afterCreate"?: ResolvedHook<CommentAfterCreateHandler>;
1169
+ "comment:afterModerate"?: ResolvedHook<CommentAfterModerateHandler>;
1170
+ "page:metadata"?: ResolvedHook<PageMetadataHandler>;
1171
+ "page:fragments"?: ResolvedHook<PageFragmentHandler>;
1172
+ }
1173
+
1174
+ // =============================================================================
1175
+ // Standard Plugin Format (Unified Plugin Format)
1176
+ // =============================================================================
1177
+
1178
+ /**
1179
+ * Standard plugin hook handler -- same as sandbox entry format.
1180
+ * Receives the event as the first argument and a PluginContext as the second.
1181
+ *
1182
+ * Plugin authors annotate their event parameters with specific types for IDE
1183
+ * support. At the type level, we accept any function with compatible arity.
1184
+ */
1185
+ // eslint-disable-next-line typescript-eslint/no-explicit-any -- must accept handlers with specific event types
1186
+ export type StandardHookHandler = (...args: any[]) => Promise<any>;
1187
+
1188
+ /**
1189
+ * Standard plugin hook entry -- either a bare handler or a config object.
1190
+ */
1191
+ export type StandardHookEntry =
1192
+ | StandardHookHandler
1193
+ | {
1194
+ handler: StandardHookHandler;
1195
+ priority?: number;
1196
+ timeout?: number;
1197
+ dependencies?: string[];
1198
+ errorPolicy?: "continue" | "abort";
1199
+ exclusive?: boolean;
1200
+ };
1201
+
1202
+ /**
1203
+ * Standard plugin route handler -- takes (routeCtx, pluginCtx) like sandbox entries.
1204
+ * The routeCtx contains input and request info; pluginCtx is the full plugin context.
1205
+ *
1206
+ * Uses `any` for routeCtx to allow plugins to access properties like
1207
+ * `routeCtx.request.url` without needing exact type matches across
1208
+ * trusted (Request object) and sandboxed (plain object) modes.
1209
+ */
1210
+ // eslint-disable-next-line typescript-eslint/no-explicit-any -- see above
1211
+ export type StandardRouteHandler = (routeCtx: any, ctx: PluginContext) => Promise<unknown>;
1212
+
1213
+ /**
1214
+ * Standard plugin route entry -- either a config object with handler, or just a handler.
1215
+ */
1216
+ export interface StandardRouteEntry {
1217
+ handler: StandardRouteHandler;
1218
+ input?: unknown;
1219
+ public?: boolean;
1220
+ }
1221
+
1222
+ /**
1223
+ * Standard plugin definition -- the sandbox entry format.
1224
+ * Used by standard plugins that work in both trusted and sandboxed modes.
1225
+ * No id/version/capabilities -- those come from the descriptor.
1226
+ *
1227
+ * This is the input to definePlugin() for standard-format plugins.
1228
+ *
1229
+ * The hooks and routes use permissive types (Record<string, any>) so that
1230
+ * plugin authors can annotate their handlers with specific event types
1231
+ * without type errors from strictFunctionTypes contravariance.
1232
+ */
1233
+ export interface StandardPluginDefinition {
1234
+ // eslint-disable-next-line typescript-eslint/no-explicit-any -- must accept handlers with specific event/route types
1235
+ hooks?: Record<string, any>;
1236
+ // eslint-disable-next-line typescript-eslint/no-explicit-any -- must accept handlers with specific event/route types
1237
+ routes?: Record<string, any>;
1238
+ }
1239
+
1240
+ /**
1241
+ * Check if a value is a StandardPluginDefinition (has hooks/routes but no id/version).
1242
+ */
1243
+ export function isStandardPluginDefinition(value: unknown): value is StandardPluginDefinition {
1244
+ if (typeof value !== "object" || value === null) return false;
1245
+ // Standard format: has hooks or routes, but NOT id+version (which are on PluginDefinition)
1246
+ const hasPluginShape = "hooks" in value || "routes" in value;
1247
+ const hasNativeShape = "id" in value && "version" in value;
1248
+ return hasPluginShape && !hasNativeShape;
1249
+ }
1250
+
1251
+ // =============================================================================
1252
+ // Plugin Admin Exports
1253
+ // =============================================================================
1254
+
1255
+ /**
1256
+ * What a plugin exports from its /admin entrypoint
1257
+ * Uses generic component type to avoid React dependency
1258
+ */
1259
+ export interface PluginAdminExports {
1260
+ widgets?: Record<string, JSX.Element>;
1261
+ pages?: Record<string, JSX.Element>;
1262
+ fields?: Record<string, JSX.Element>;
1263
+ }
1264
+
1265
+ // =============================================================================
1266
+ // Sandbox Types
1267
+ // =============================================================================
1268
+
1269
+ /**
1270
+ * Plugin manifest - the metadata portion of a plugin bundle
1271
+ * Used for sandboxed plugins loaded from marketplace
1272
+ */
1273
+ export interface PluginManifest {
1274
+ id: string;
1275
+ version: string;
1276
+ capabilities: PluginCapability[];
1277
+ allowedHosts: string[];
1278
+ storage: PluginStorageConfig;
1279
+ /** Hook declarations — either plain name strings or structured objects */
1280
+ hooks: Array<ManifestHookEntry | HookName>;
1281
+ /** Route declarations — either plain name strings or structured objects */
1282
+ routes: Array<ManifestRouteEntry | string>;
1283
+ admin: PluginAdminConfig;
1284
+ }