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,181 @@
1
+ /**
2
+ * WordPress import prepare endpoint
3
+ *
4
+ * POST /_emdash/api/import/wordpress/prepare
5
+ *
6
+ * Creates collections and fields needed for import.
7
+ * This is called after analyze, before execute.
8
+ */
9
+
10
+ import type { APIRoute } from "astro";
11
+
12
+ import { requirePerm } from "#api/authorize.js";
13
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
14
+ import { isParseError, parseBody } from "#api/parse.js";
15
+ import { wpPrepareBody } from "#api/schemas.js";
16
+ import { FIELD_TYPES, type FieldType } from "#schema/types.js";
17
+ import type { EmDashHandlers } from "#types";
18
+
19
+ import { capitalize, singularize, type ImportFieldDef } from "./analyze.js";
20
+
21
+ /** Validate that a string is a known FieldType, returning undefined if not */
22
+ function asFieldType(value: string): FieldType | undefined {
23
+ // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- validated by includes check
24
+ return (FIELD_TYPES as readonly string[]).includes(value) ? (value as FieldType) : undefined;
25
+ }
26
+
27
+ export const prerender = false;
28
+
29
+ interface PrepareRequest {
30
+ postTypes: Array<{
31
+ name: string;
32
+ collection: string;
33
+ fields: ImportFieldDef[];
34
+ }>;
35
+ }
36
+
37
+ export interface PrepareResult {
38
+ success: boolean;
39
+ collectionsCreated: string[];
40
+ fieldsCreated: Array<{ collection: string; field: string }>;
41
+ errors: Array<{ collection: string; error: string }>;
42
+ }
43
+
44
+ export const POST: APIRoute = async ({ request, locals }) => {
45
+ const { emdash, user } = locals;
46
+
47
+ if (!emdash?.db) {
48
+ return apiError("NOT_CONFIGURED", "EmDash not configured", 500);
49
+ }
50
+
51
+ const denied = requirePerm(user, "import:execute");
52
+ if (denied) return denied;
53
+
54
+ try {
55
+ const body = await parseBody(request, wpPrepareBody);
56
+ if (isParseError(body)) return body;
57
+
58
+ // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Zod schema output narrowed to PrepareRequest
59
+ const result = await prepareImport(emdash.db, body as PrepareRequest);
60
+
61
+ return apiSuccess(result, result.success ? 200 : 400);
62
+ } catch (error) {
63
+ return handleError(error, "Failed to prepare import", "WXR_PREPARE_ERROR");
64
+ }
65
+ };
66
+
67
+ async function prepareImport(
68
+ db: NonNullable<EmDashHandlers["db"]>,
69
+ request: PrepareRequest,
70
+ ): Promise<PrepareResult> {
71
+ const { SchemaRegistry } = await import("#schema/registry.js");
72
+ const registry = new SchemaRegistry(db);
73
+
74
+ const result: PrepareResult = {
75
+ success: true,
76
+ collectionsCreated: [],
77
+ fieldsCreated: [],
78
+ errors: [],
79
+ };
80
+
81
+ for (const postType of request.postTypes) {
82
+ const collectionSlug = postType.collection;
83
+
84
+ try {
85
+ // Check if collection exists
86
+ let collection = await registry.getCollection(collectionSlug);
87
+
88
+ if (!collection) {
89
+ // Create the collection
90
+ const label = capitalize(collectionSlug);
91
+ const labelSingular = capitalize(singularize(collectionSlug));
92
+
93
+ // Enable search by default for posts and pages
94
+ const isSearchable = ["posts", "pages", "post", "page"].includes(collectionSlug);
95
+ const supports: ("revisions" | "drafts" | "search")[] = ["revisions", "drafts"];
96
+ if (isSearchable) {
97
+ supports.push("search");
98
+ }
99
+
100
+ // Default URL patterns for known post types
101
+ const urlPattern =
102
+ collectionSlug === "pages"
103
+ ? "/{slug}"
104
+ : collectionSlug === "posts"
105
+ ? "/blog/{slug}"
106
+ : undefined;
107
+
108
+ collection = await registry.createCollection({
109
+ slug: collectionSlug,
110
+ label,
111
+ labelSingular,
112
+ description: `Imported from WordPress post type: ${postType.name}`,
113
+ supports,
114
+ urlPattern,
115
+ });
116
+
117
+ result.collectionsCreated.push(collectionSlug);
118
+ }
119
+
120
+ // Create missing fields
121
+ const existingFields = await registry.listFields(collection.id);
122
+ const existingFieldSlugs = new Set(existingFields.map((f) => f.slug));
123
+
124
+ for (const field of postType.fields) {
125
+ if (existingFieldSlugs.has(field.slug)) {
126
+ // Field already exists - skip
127
+ continue;
128
+ }
129
+
130
+ const fieldType = asFieldType(field.type);
131
+ if (!fieldType) {
132
+ result.errors.push({
133
+ collection: collectionSlug,
134
+ error: `Unknown field type "${field.type}" for field "${field.slug}"`,
135
+ });
136
+ continue;
137
+ }
138
+
139
+ await registry.createField(collectionSlug, {
140
+ slug: field.slug,
141
+ label: field.label,
142
+ type: fieldType,
143
+ required: field.required,
144
+ unique: false,
145
+ searchable: field.searchable ?? false,
146
+ sortOrder: existingFields.length + result.fieldsCreated.length,
147
+ });
148
+
149
+ result.fieldsCreated.push({
150
+ collection: collectionSlug,
151
+ field: field.slug,
152
+ });
153
+ }
154
+
155
+ // Enable search if collection supports it and has searchable fields
156
+ const isSearchable = ["posts", "pages", "post", "page"].includes(collectionSlug);
157
+ if (isSearchable) {
158
+ const { FTSManager } = await import("#search/fts-manager.js");
159
+ const ftsManager = new FTSManager(db);
160
+
161
+ const searchableFields = await ftsManager.getSearchableFields(collectionSlug);
162
+ if (searchableFields.length > 0) {
163
+ try {
164
+ await ftsManager.enableSearch(collectionSlug);
165
+ } catch {
166
+ // Ignore - search can be enabled manually later
167
+ }
168
+ }
169
+ }
170
+ } catch (error) {
171
+ console.error(`Prepare error for collection "${collectionSlug}":`, error);
172
+ result.success = false;
173
+ result.errors.push({
174
+ collection: collectionSlug,
175
+ error: "Failed to prepare collection",
176
+ });
177
+ }
178
+ }
179
+
180
+ return result;
181
+ }
@@ -0,0 +1,393 @@
1
+ /**
2
+ * WordPress URL rewrite endpoint
3
+ *
4
+ * POST /_emdash/api/import/wordpress/rewrite-urls
5
+ *
6
+ * Rewrites old WordPress media URLs in Portable Text content
7
+ * to point to newly imported EmDash media URLs.
8
+ *
9
+ * Handles URL variants (e.g., image.jpg vs image.jpg?w=200) by matching
10
+ * on the base URL path without query parameters.
11
+ */
12
+
13
+ import type { APIRoute } from "astro";
14
+ import { sql } from "kysely";
15
+
16
+ import { requirePerm } from "#api/authorize.js";
17
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
18
+ import { isParseError, parseBody } from "#api/parse.js";
19
+ import { wpRewriteUrlsBody } from "#api/schemas.js";
20
+ import { normalizeMediaValue } from "#media/normalize.js";
21
+ import type { MediaProvider } from "#media/types.js";
22
+ import type { EmDashHandlers } from "#types";
23
+
24
+ export const prerender = false;
25
+
26
+ const REGEX_SPECIAL_CHARS = /[.*+?^${}()|[\]\\]/g;
27
+
28
+ export interface RewriteUrlsResult {
29
+ /** Total items updated */
30
+ updated: number;
31
+ /** Updates by collection */
32
+ byCollection: Record<string, number>;
33
+ /** URLs that were rewritten */
34
+ urlsRewritten: number;
35
+ /** Any errors encountered */
36
+ errors: Array<{ collection: string; id: string; error: string }>;
37
+ }
38
+
39
+ export const POST: APIRoute = async ({ request, locals }) => {
40
+ const { emdash, user } = locals;
41
+
42
+ if (!emdash?.db) {
43
+ return apiError("NO_DB", "Database not initialized", 500);
44
+ }
45
+
46
+ const denied = requirePerm(user, "import:execute");
47
+ if (denied) return denied;
48
+
49
+ try {
50
+ const body = await parseBody(request, wpRewriteUrlsBody);
51
+ if (isParseError(body)) return body;
52
+
53
+ const urlEntries = Object.entries(body.urlMap);
54
+ if (urlEntries.length === 0) {
55
+ return apiSuccess({
56
+ updated: 0,
57
+ byCollection: {},
58
+ urlsRewritten: 0,
59
+ errors: [],
60
+ });
61
+ }
62
+
63
+ const getProvider = (id: string) => emdash.getMediaProvider(id);
64
+ const result = await rewriteUrls(emdash.db, body.urlMap, getProvider, body.collections);
65
+
66
+ return apiSuccess(result);
67
+ } catch (error) {
68
+ return handleError(error, "Failed to rewrite URLs", "REWRITE_ERROR");
69
+ }
70
+ };
71
+
72
+ /**
73
+ * Strip query parameters from a URL for base matching
74
+ */
75
+ function getBaseUrl(url: string): string {
76
+ try {
77
+ const parsed = new URL(url);
78
+ return `${parsed.origin}${parsed.pathname}`;
79
+ } catch {
80
+ // If URL parsing fails, try simple string split
81
+ return url.split("?")[0] || url;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Build a map of base URLs to new URLs for flexible matching
87
+ */
88
+ function buildBaseUrlMap(urlMap: Record<string, string>): Map<string, string> {
89
+ const baseMap = new Map<string, string>();
90
+ for (const [oldUrl, newUrl] of Object.entries(urlMap)) {
91
+ const baseUrl = getBaseUrl(oldUrl);
92
+ baseMap.set(baseUrl, newUrl);
93
+ }
94
+ return baseMap;
95
+ }
96
+
97
+ /**
98
+ * Find matching new URL for a given URL, checking both exact and base matches
99
+ */
100
+ function findMatchingUrl(
101
+ url: string,
102
+ exactMap: Record<string, string>,
103
+ baseMap: Map<string, string>,
104
+ ): string | null {
105
+ // Try exact match first
106
+ if (exactMap[url]) {
107
+ return exactMap[url];
108
+ }
109
+
110
+ // Try base URL match (ignoring query params)
111
+ const baseUrl = getBaseUrl(url);
112
+ const baseMatch = baseMap.get(baseUrl);
113
+ if (baseMatch) {
114
+ return baseMatch;
115
+ }
116
+
117
+ return null;
118
+ }
119
+
120
+ /**
121
+ * Portable Text block type (simplified for URL rewriting)
122
+ */
123
+ interface PortableTextBlock {
124
+ _type: string;
125
+ _key?: string;
126
+ asset?: {
127
+ _type?: string;
128
+ _ref?: string;
129
+ url?: string;
130
+ };
131
+ link?: string;
132
+ // For nested content like galleries
133
+ images?: PortableTextBlock[];
134
+ columns?: Array<{ content?: PortableTextBlock[] }>;
135
+ [key: string]: unknown;
136
+ }
137
+
138
+ /**
139
+ * Rewrite URLs in a Portable Text array, returning whether any changes were made
140
+ */
141
+ function rewritePortableTextUrls(
142
+ blocks: PortableTextBlock[],
143
+ exactMap: Record<string, string>,
144
+ baseMap: Map<string, string>,
145
+ ): { changed: boolean; urlsRewritten: number } {
146
+ let changed = false;
147
+ let urlsRewritten = 0;
148
+
149
+ for (const block of blocks) {
150
+ // Handle image blocks
151
+ if (block._type === "image" && block.asset?.url) {
152
+ const newUrl = findMatchingUrl(block.asset.url, exactMap, baseMap);
153
+ if (newUrl) {
154
+ block.asset.url = newUrl;
155
+ block.asset._ref = newUrl; // Also update the reference
156
+ changed = true;
157
+ urlsRewritten++;
158
+ }
159
+ }
160
+
161
+ // Handle image link URLs (for linked images)
162
+ if (block._type === "image" && block.link) {
163
+ const newUrl = findMatchingUrl(block.link, exactMap, baseMap);
164
+ if (newUrl) {
165
+ block.link = newUrl;
166
+ changed = true;
167
+ urlsRewritten++;
168
+ }
169
+ }
170
+
171
+ // Handle gallery blocks with nested images
172
+ if (block._type === "gallery" && Array.isArray(block.images)) {
173
+ const result = rewritePortableTextUrls(block.images, exactMap, baseMap);
174
+ if (result.changed) {
175
+ changed = true;
176
+ urlsRewritten += result.urlsRewritten;
177
+ }
178
+ }
179
+
180
+ // Handle columns blocks with nested content
181
+ if (block._type === "columns" && Array.isArray(block.columns)) {
182
+ for (const column of block.columns) {
183
+ if (Array.isArray(column.content)) {
184
+ const result = rewritePortableTextUrls(column.content, exactMap, baseMap);
185
+ if (result.changed) {
186
+ changed = true;
187
+ urlsRewritten += result.urlsRewritten;
188
+ }
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ return { changed, urlsRewritten };
195
+ }
196
+
197
+ /**
198
+ * Rewrite URLs in a string field using simple string replacement
199
+ */
200
+ function rewriteStringUrls(
201
+ value: string,
202
+ exactMap: Record<string, string>,
203
+ baseMap: Map<string, string>,
204
+ ): { newValue: string; changed: boolean; urlsRewritten: number } {
205
+ let newValue = value;
206
+ let changed = false;
207
+ let urlsRewritten = 0;
208
+
209
+ // Try exact matches first
210
+ for (const [oldUrl, newUrl] of Object.entries(exactMap)) {
211
+ if (newValue.includes(oldUrl)) {
212
+ newValue = newValue.split(oldUrl).join(newUrl);
213
+ changed = true;
214
+ urlsRewritten++;
215
+ }
216
+ }
217
+
218
+ // For base URL matching in strings, we need to be more careful
219
+ // Only match if we find a URL that starts with the base
220
+ for (const [baseUrl, newUrl] of baseMap.entries()) {
221
+ // Look for the base URL followed by optional query string or end
222
+ const regex = new RegExp(escapeRegExp(baseUrl) + "(\\?[^\"'\\s]*)?", "g");
223
+ const matches = newValue.match(regex);
224
+ if (matches) {
225
+ for (const match of matches) {
226
+ // Don't replace if we already have an exact match in the map
227
+ if (!exactMap[match]) {
228
+ newValue = newValue.split(match).join(newUrl);
229
+ changed = true;
230
+ urlsRewritten++;
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ return { newValue, changed, urlsRewritten };
237
+ }
238
+
239
+ /**
240
+ * Escape special regex characters in a string
241
+ */
242
+ function escapeRegExp(string: string): string {
243
+ return string.replace(REGEX_SPECIAL_CHARS, "\\$&");
244
+ }
245
+
246
+ async function rewriteUrls(
247
+ db: NonNullable<EmDashHandlers["db"]>,
248
+ urlMap: Record<string, string>,
249
+ getProvider: (id: string) => MediaProvider | undefined,
250
+ collections?: string[],
251
+ ): Promise<RewriteUrlsResult> {
252
+ const { SchemaRegistry } = await import("#schema/registry.js");
253
+ const registry = new SchemaRegistry(db);
254
+
255
+ const result: RewriteUrlsResult = {
256
+ updated: 0,
257
+ byCollection: {},
258
+ urlsRewritten: 0,
259
+ errors: [],
260
+ };
261
+
262
+ // Build base URL map for flexible matching
263
+ const baseMap = buildBaseUrlMap(urlMap);
264
+
265
+ // Get all collections or filter to specified ones
266
+ const allCollections = await registry.listCollections();
267
+ const targetCollections = collections?.length
268
+ ? allCollections.filter((c) => collections.includes(c.slug))
269
+ : allCollections;
270
+
271
+ for (const collection of targetCollections) {
272
+ // Get fields that might contain URLs
273
+ const fields = await registry.listFields(collection.id);
274
+ const portableTextFields = fields.filter((f) => f.type === "portableText");
275
+ const stringFields = fields.filter((f) => ["text", "string"].includes(f.type));
276
+ // Image and file fields store URLs directly as TEXT
277
+ const mediaFields = fields.filter((f) => ["image", "file"].includes(f.type));
278
+
279
+ if (portableTextFields.length === 0 && stringFields.length === 0 && mediaFields.length === 0)
280
+ continue;
281
+
282
+ // Get table name
283
+ const tableName = `ec_${collection.slug}`;
284
+
285
+ try {
286
+ // Query all rows
287
+ const rows = await sql<{ id: string; [key: string]: unknown }>`
288
+ SELECT * FROM ${sql.ref(tableName)}
289
+ WHERE deleted_at IS NULL
290
+ `.execute(db);
291
+
292
+ for (const row of rows.rows) {
293
+ let rowUpdated = false;
294
+ const updates: Record<string, unknown> = {};
295
+ let rowUrlsRewritten = 0;
296
+
297
+ // Handle Portable Text fields - parse JSON and rewrite URLs in blocks
298
+ for (const field of portableTextFields) {
299
+ const value = row[field.slug];
300
+ if (!value || typeof value !== "string") continue;
301
+
302
+ try {
303
+ // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- JSON.parse returns unknown; validated by Array.isArray below
304
+ const blocks = JSON.parse(value) as PortableTextBlock[];
305
+ if (!Array.isArray(blocks)) continue;
306
+
307
+ const rewriteResult = rewritePortableTextUrls(blocks, urlMap, baseMap);
308
+
309
+ if (rewriteResult.changed) {
310
+ updates[field.slug] = JSON.stringify(blocks);
311
+ rowUpdated = true;
312
+ rowUrlsRewritten += rewriteResult.urlsRewritten;
313
+ }
314
+ } catch {
315
+ // Not valid JSON, try string replacement as fallback
316
+ const stringResult = rewriteStringUrls(value, urlMap, baseMap);
317
+ if (stringResult.changed) {
318
+ updates[field.slug] = stringResult.newValue;
319
+ rowUpdated = true;
320
+ rowUrlsRewritten += stringResult.urlsRewritten;
321
+ }
322
+ }
323
+ }
324
+
325
+ // Handle string/text fields - simple string replacement
326
+ for (const field of stringFields) {
327
+ const value = row[field.slug];
328
+ if (!value || typeof value !== "string") continue;
329
+
330
+ const stringResult = rewriteStringUrls(value, urlMap, baseMap);
331
+ if (stringResult.changed) {
332
+ updates[field.slug] = stringResult.newValue;
333
+ rowUpdated = true;
334
+ rowUrlsRewritten += stringResult.urlsRewritten;
335
+ }
336
+ }
337
+
338
+ // Handle image/file fields - normalize to MediaValue objects
339
+ for (const field of mediaFields) {
340
+ const value = row[field.slug];
341
+ if (!value || typeof value !== "string") continue;
342
+
343
+ // Try to find a matching rewritten URL
344
+ const newUrl = findMatchingUrl(value, urlMap, baseMap);
345
+ if (newUrl) {
346
+ // Normalize into a proper MediaValue instead of storing a bare URL
347
+ try {
348
+ const normalized = await normalizeMediaValue(newUrl, getProvider);
349
+ updates[field.slug] = normalized ? JSON.stringify(normalized) : newUrl;
350
+ } catch {
351
+ updates[field.slug] = newUrl;
352
+ }
353
+ rowUpdated = true;
354
+ rowUrlsRewritten++;
355
+ }
356
+ }
357
+
358
+ if (rowUpdated) {
359
+ try {
360
+ // Build update query dynamically
361
+ // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Kysely dynamic table requires type assertion
362
+ let query = db.updateTable(tableName as any).where("id", "=", row.id);
363
+
364
+ for (const [key, value] of Object.entries(updates)) {
365
+ // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Kysely dynamic column update requires type assertion
366
+ query = query.set({ [key]: value } as any);
367
+ }
368
+
369
+ await query.execute();
370
+
371
+ result.updated++;
372
+ result.urlsRewritten += rowUrlsRewritten;
373
+ result.byCollection[collection.slug] = (result.byCollection[collection.slug] || 0) + 1;
374
+ } catch (updateError) {
375
+ result.errors.push({
376
+ collection: collection.slug,
377
+ id: row.id,
378
+ error: updateError instanceof Error ? updateError.message : "Update failed",
379
+ });
380
+ }
381
+ }
382
+ }
383
+ } catch (queryError) {
384
+ result.errors.push({
385
+ collection: collection.slug,
386
+ id: "*",
387
+ error: queryError instanceof Error ? queryError.message : "Query failed for collection",
388
+ });
389
+ }
390
+ }
391
+
392
+ return result;
393
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * WordPress Plugin analyze endpoint
3
+ *
4
+ * POST /_emdash/api/import/wordpress-plugin/analyze
5
+ *
6
+ * Analyzes a WordPress site with EmDash Exporter plugin installed.
7
+ * Returns content counts, schema compatibility, etc.
8
+ */
9
+
10
+ import type { APIRoute } from "astro";
11
+ import { SchemaRegistry } from "emdash";
12
+
13
+ import { requirePerm } from "#api/authorize.js";
14
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
15
+ import { isParseError, parseBody } from "#api/parse.js";
16
+ import { wpPluginAnalyzeBody } from "#api/schemas.js";
17
+ import { getSource } from "#import/index.js";
18
+ import { validateExternalUrl, SsrfError } from "#import/ssrf.js";
19
+ import type { ImportAnalysis } from "#import/types.js";
20
+ import type { EmDashHandlers } from "#types";
21
+
22
+ export const prerender = false;
23
+
24
+ export interface WpPluginAnalyzeResponse {
25
+ success: boolean;
26
+ analysis?: ImportAnalysis;
27
+ error?: { message: string };
28
+ }
29
+
30
+ export const POST: APIRoute = async ({ request, locals }) => {
31
+ const { emdash, user } = locals;
32
+
33
+ const denied = requirePerm(user, "import:execute");
34
+ if (denied) return denied;
35
+
36
+ try {
37
+ const body = await parseBody(request, wpPluginAnalyzeBody);
38
+ if (isParseError(body)) return body;
39
+
40
+ // SSRF: reject internal/private network targets
41
+ try {
42
+ validateExternalUrl(body.url);
43
+ } catch (e) {
44
+ const msg = e instanceof SsrfError ? e.message : "Invalid URL";
45
+ return apiError("SSRF_BLOCKED", msg, 400);
46
+ }
47
+
48
+ // Get the WordPress plugin source
49
+ const source = getSource("wordpress-plugin");
50
+ if (!source) {
51
+ return apiError("NOT_CONFIGURED", "WordPress plugin source not available", 500);
52
+ }
53
+
54
+ // Build context with existing collections info
55
+ const existingCollections = await fetchExistingCollections(emdash?.db);
56
+
57
+ // Analyze the site
58
+ const analysis = await source.analyze(
59
+ { type: "url", url: body.url, token: body.token },
60
+ {
61
+ db: emdash?.db,
62
+ getExistingCollections: async () => existingCollections,
63
+ },
64
+ );
65
+
66
+ return apiSuccess({
67
+ success: true,
68
+ analysis,
69
+ });
70
+ } catch (error) {
71
+ return handleError(error, "Failed to analyze WordPress site", "WP_PLUGIN_ANALYZE_ERROR");
72
+ }
73
+ };
74
+
75
+ /** Existing collection info from schema registry */
76
+ interface ExistingCollection {
77
+ slug: string;
78
+ fields: Map<string, { type: string }>;
79
+ }
80
+
81
+ /** Fetch collections and their fields from schema registry */
82
+ async function fetchExistingCollections(
83
+ db: EmDashHandlers["db"] | undefined,
84
+ ): Promise<Map<string, ExistingCollection>> {
85
+ const result = new Map<string, ExistingCollection>();
86
+
87
+ if (!db) return result;
88
+
89
+ try {
90
+ const registry = new SchemaRegistry(db);
91
+ const collections = await registry.listCollections();
92
+
93
+ for (const collection of collections) {
94
+ const fields = await registry.listFields(collection.id);
95
+ const fieldMap = new Map<string, { type: string }>();
96
+
97
+ for (const field of fields) {
98
+ fieldMap.set(field.slug, { type: field.type });
99
+ }
100
+
101
+ result.set(collection.slug, {
102
+ slug: collection.slug,
103
+ fields: fieldMap,
104
+ });
105
+ }
106
+ } catch (error) {
107
+ console.warn("Could not fetch schema registry:", error);
108
+ }
109
+
110
+ return result;
111
+ }