emdash 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (876) hide show
  1. package/dist/{adapters-DPAkL6QF.d.mts → adapters-C0ONzkUP.d.mts} +10 -1
  2. package/dist/adapters-C0ONzkUP.d.mts.map +1 -0
  3. package/dist/{allowed-origins-FXB-FplB.mjs → allowed-origins-CCEi9bPI.mjs} +2 -2
  4. package/dist/{allowed-origins-FXB-FplB.mjs.map → allowed-origins-CCEi9bPI.mjs.map} +1 -1
  5. package/dist/api/route-utils.d.mts +3 -3
  6. package/dist/api/route-utils.d.mts.map +1 -1
  7. package/dist/api/route-utils.mjs +19 -19
  8. package/dist/api/schemas/index.d.mts +2 -2
  9. package/dist/api/schemas/index.mjs +5 -4
  10. package/dist/{api-DVbHNApo.mjs → api-EibxGP-V.mjs} +260 -160
  11. package/dist/api-EibxGP-V.mjs.map +1 -0
  12. package/dist/api-tokens-Cvmixds7.mjs +3 -0
  13. package/dist/{api-tokens-UYBkl17d.mjs → api-tokens-uPt8UDpx.mjs} +2 -2
  14. package/dist/{api-tokens-UYBkl17d.mjs.map → api-tokens-uPt8UDpx.mjs.map} +1 -1
  15. package/dist/{apply-D23iUV4q.mjs → apply-fGHCAwWN.mjs} +42 -44
  16. package/dist/apply-fGHCAwWN.mjs.map +1 -0
  17. package/dist/astro/index.d.mts +12 -13
  18. package/dist/astro/index.d.mts.map +1 -1
  19. package/dist/astro/index.mjs +90 -11
  20. package/dist/astro/index.mjs.map +1 -1
  21. package/dist/astro/middleware/auth.d.mts +10 -11
  22. package/dist/astro/middleware/auth.d.mts.map +1 -1
  23. package/dist/astro/middleware/auth.mjs +56 -107
  24. package/dist/astro/middleware/auth.mjs.map +1 -1
  25. package/dist/astro/middleware/redirect.mjs +7 -7
  26. package/dist/astro/middleware/request-context.mjs +4 -4
  27. package/dist/astro/middleware/request-context.mjs.map +1 -1
  28. package/dist/astro/middleware/setup.mjs +1 -1
  29. package/dist/astro/middleware.d.mts +47 -3
  30. package/dist/astro/middleware.d.mts.map +1 -1
  31. package/dist/astro/middleware.mjs +410 -123
  32. package/dist/astro/middleware.mjs.map +1 -1
  33. package/dist/astro/routes/PluginRegistry.d.mts +5 -1
  34. package/dist/astro/routes/PluginRegistry.d.mts.map +1 -1
  35. package/dist/astro/routes/PluginRegistry.mjs +2 -1
  36. package/dist/astro/routes/PluginRegistry.mjs.map +1 -1
  37. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +9 -8
  38. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs.map +1 -1
  39. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +9 -8
  40. package/dist/astro/routes/api/admin/allowed-domains/index.mjs.map +1 -1
  41. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +5 -5
  42. package/dist/astro/routes/api/admin/api-tokens/index.mjs +7 -7
  43. package/dist/astro/routes/api/admin/api-tokens/index.mjs.map +1 -1
  44. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +5 -5
  45. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +10 -9
  46. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs.map +1 -1
  47. package/dist/astro/routes/api/admin/byline-fields/index.mjs +10 -9
  48. package/dist/astro/routes/api/admin/byline-fields/index.mjs.map +1 -1
  49. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +10 -9
  50. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs.map +1 -1
  51. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +16 -15
  52. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
  53. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +17 -22
  54. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
  55. package/dist/astro/routes/api/admin/bylines/index.d.mts.map +1 -1
  56. package/dist/astro/routes/api/admin/bylines/index.mjs +20 -18
  57. package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
  58. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +13 -12
  59. package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
  60. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
  61. package/dist/astro/routes/api/admin/comments/bulk.mjs +11 -10
  62. package/dist/astro/routes/api/admin/comments/bulk.mjs.map +1 -1
  63. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
  64. package/dist/astro/routes/api/admin/comments/index.mjs +11 -10
  65. package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
  66. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +5 -5
  67. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +4 -4
  68. package/dist/astro/routes/api/admin/media-usage/repair.mjs +12 -10
  69. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
  70. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +5 -5
  71. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +5 -5
  72. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +41 -40
  73. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  74. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +41 -40
  75. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +46 -40
  77. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/_id_/mcp.d.mts +8 -0
  79. package/dist/astro/routes/api/admin/plugins/_id_/mcp.d.mts.map +1 -0
  80. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +39 -0
  81. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs.map +1 -0
  82. package/dist/astro/routes/api/admin/plugins/_id_/settings.d.mts +9 -0
  83. package/dist/astro/routes/api/admin/plugins/_id_/settings.d.mts.map +1 -0
  84. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +78 -0
  85. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -0
  86. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +40 -39
  87. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  88. package/dist/astro/routes/api/admin/plugins/_id_/update.d.mts.map +1 -1
  89. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +43 -40
  90. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  91. package/dist/astro/routes/api/admin/plugins/index.mjs +46 -40
  92. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  93. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +4 -4
  94. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +40 -39
  95. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  96. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.d.mts.map +1 -1
  97. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +46 -41
  98. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  99. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +40 -39
  100. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  101. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +40 -39
  102. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  103. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.d.mts.map +1 -1
  104. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +44 -41
  105. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  106. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +40 -39
  107. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  108. package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -1
  109. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +45 -42
  110. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  111. package/dist/astro/routes/api/admin/plugins/updates.mjs +41 -40
  112. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  113. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +40 -39
  114. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  115. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +4 -4
  116. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +40 -39
  117. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  118. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +4 -4
  119. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +4 -4
  120. package/dist/astro/routes/api/admin/users/_id_/index.mjs +9 -8
  121. package/dist/astro/routes/api/admin/users/_id_/index.mjs.map +1 -1
  122. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +5 -5
  123. package/dist/astro/routes/api/admin/users/index.mjs +9 -8
  124. package/dist/astro/routes/api/admin/users/index.mjs.map +1 -1
  125. package/dist/astro/routes/api/auth/dev-bypass.mjs +7 -7
  126. package/dist/astro/routes/api/auth/invite/accept.mjs +4 -4
  127. package/dist/astro/routes/api/auth/invite/complete.mjs +13 -12
  128. package/dist/astro/routes/api/auth/invite/complete.mjs.map +1 -1
  129. package/dist/astro/routes/api/auth/invite/index.mjs +10 -9
  130. package/dist/astro/routes/api/auth/invite/index.mjs.map +1 -1
  131. package/dist/astro/routes/api/auth/invite/register-options.mjs +12 -11
  132. package/dist/astro/routes/api/auth/invite/register-options.mjs.map +1 -1
  133. package/dist/astro/routes/api/auth/logout.mjs +4 -4
  134. package/dist/astro/routes/api/auth/magic-link/send.mjs +12 -11
  135. package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -1
  136. package/dist/astro/routes/api/auth/magic-link/verify.mjs +5 -5
  137. package/dist/astro/routes/api/auth/me.mjs +9 -8
  138. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  139. package/dist/astro/routes/api/auth/mode.d.mts.map +1 -1
  140. package/dist/astro/routes/api/auth/mode.mjs +6 -3
  141. package/dist/astro/routes/api/auth/mode.mjs.map +1 -1
  142. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +15 -5
  143. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs.map +1 -1
  144. package/dist/astro/routes/api/auth/oauth/_provider_.d.mts.map +1 -1
  145. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +11 -5
  146. package/dist/astro/routes/api/auth/oauth/_provider_.mjs.map +1 -1
  147. package/dist/astro/routes/api/auth/passkey/_id_.mjs +8 -7
  148. package/dist/astro/routes/api/auth/passkey/_id_.mjs.map +1 -1
  149. package/dist/astro/routes/api/auth/passkey/index.mjs +3 -3
  150. package/dist/astro/routes/api/auth/passkey/options.mjs +13 -12
  151. package/dist/astro/routes/api/auth/passkey/options.mjs.map +1 -1
  152. package/dist/astro/routes/api/auth/passkey/register/options.mjs +11 -10
  153. package/dist/astro/routes/api/auth/passkey/register/options.mjs.map +1 -1
  154. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +12 -11
  155. package/dist/astro/routes/api/auth/passkey/register/verify.mjs.map +1 -1
  156. package/dist/astro/routes/api/auth/passkey/verify.mjs +12 -11
  157. package/dist/astro/routes/api/auth/passkey/verify.mjs.map +1 -1
  158. package/dist/astro/routes/api/auth/signup/complete.mjs +13 -12
  159. package/dist/astro/routes/api/auth/signup/complete.mjs.map +1 -1
  160. package/dist/astro/routes/api/auth/signup/request.mjs +12 -11
  161. package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -1
  162. package/dist/astro/routes/api/auth/signup/verify.mjs +4 -4
  163. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +14 -13
  164. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
  165. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +13 -12
  166. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs.map +1 -1
  167. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +5 -5
  168. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs.map +1 -1
  169. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +5 -4
  170. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -1
  171. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +6 -5
  172. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs.map +1 -1
  173. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +5 -5
  174. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs.map +1 -1
  175. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +14 -13
  176. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs.map +1 -1
  177. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +10 -8
  178. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
  179. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.d.mts.map +1 -1
  180. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +20 -19
  181. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
  182. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +18 -17
  183. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
  184. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +5 -4
  185. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs.map +1 -1
  186. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +5 -5
  187. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs.map +1 -1
  188. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +10 -8
  189. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -1
  190. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.d.mts.map +1 -1
  191. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +19 -18
  192. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
  193. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +5 -5
  194. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs.map +1 -1
  195. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +5 -4
  196. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
  197. package/dist/astro/routes/api/content/_collection_/_id_.d.mts.map +1 -1
  198. package/dist/astro/routes/api/content/_collection_/_id_.mjs +11 -9
  199. package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
  200. package/dist/astro/routes/api/content/_collection_/authors.mjs +5 -5
  201. package/dist/astro/routes/api/content/_collection_/authors.mjs.map +1 -1
  202. package/dist/astro/routes/api/content/_collection_/index.d.mts.map +1 -1
  203. package/dist/astro/routes/api/content/_collection_/index.mjs +12 -10
  204. package/dist/astro/routes/api/content/_collection_/index.mjs.map +1 -1
  205. package/dist/astro/routes/api/content/_collection_/trash.mjs +10 -9
  206. package/dist/astro/routes/api/content/_collection_/trash.mjs.map +1 -1
  207. package/dist/astro/routes/api/dashboard.mjs +8 -8
  208. package/dist/astro/routes/api/dev/emails.mjs +4 -4
  209. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  210. package/dist/astro/routes/api/import/probe.mjs +17 -16
  211. package/dist/astro/routes/api/import/probe.mjs.map +1 -1
  212. package/dist/astro/routes/api/import/wordpress/analyze.mjs +4 -4
  213. package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -12
  214. package/dist/astro/routes/api/import/wordpress/execute.d.mts.map +1 -1
  215. package/dist/astro/routes/api/import/wordpress/execute.mjs +13 -13
  216. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -11
  217. package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
  218. package/dist/astro/routes/api/import/wordpress/media.mjs +11 -10
  219. package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
  220. package/dist/astro/routes/api/import/wordpress/prepare.mjs +11 -10
  221. package/dist/astro/routes/api/import/wordpress/prepare.mjs.map +1 -1
  222. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -12
  223. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts.map +1 -1
  224. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +11 -10
  225. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
  226. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  227. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +17 -16
  228. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -1
  229. package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
  230. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -12
  231. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts.map +1 -1
  232. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +24 -23
  233. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
  234. package/dist/astro/routes/api/manifest.mjs +7 -8
  235. package/dist/astro/routes/api/manifest.mjs.map +1 -1
  236. package/dist/astro/routes/api/mcp.d.mts.map +1 -1
  237. package/dist/astro/routes/api/mcp.mjs +149 -45
  238. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  239. package/dist/astro/routes/api/media/_id_/confirm.mjs +9 -8
  240. package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
  241. package/dist/astro/routes/api/media/_id_/usage.d.mts +8 -0
  242. package/dist/astro/routes/api/media/_id_/usage.d.mts.map +1 -0
  243. package/dist/astro/routes/api/media/_id_/usage.mjs +37 -0
  244. package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -0
  245. package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
  246. package/dist/astro/routes/api/media/_id_.mjs +26 -10
  247. package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
  248. package/dist/astro/routes/api/media/file/_...key_.mjs +4 -3
  249. package/dist/astro/routes/api/media/file/_...key_.mjs.map +1 -1
  250. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +4 -4
  251. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +4 -4
  252. package/dist/astro/routes/api/media/providers/index.mjs +4 -4
  253. package/dist/astro/routes/api/media/upload-url.mjs +10 -9
  254. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
  255. package/dist/astro/routes/api/media.d.mts.map +1 -1
  256. package/dist/astro/routes/api/media.mjs +31 -11
  257. package/dist/astro/routes/api/media.mjs.map +1 -1
  258. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +10 -9
  259. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
  260. package/dist/astro/routes/api/menus/_name_/items.mjs +10 -9
  261. package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
  262. package/dist/astro/routes/api/menus/_name_/reorder.mjs +10 -9
  263. package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
  264. package/dist/astro/routes/api/menus/_name_/translations.mjs +10 -9
  265. package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
  266. package/dist/astro/routes/api/menus/_name_.mjs +10 -9
  267. package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
  268. package/dist/astro/routes/api/menus/index.mjs +10 -9
  269. package/dist/astro/routes/api/menus/index.mjs.map +1 -1
  270. package/dist/astro/routes/api/oauth/authorize.d.mts.map +1 -1
  271. package/dist/astro/routes/api/oauth/authorize.mjs +29 -16
  272. package/dist/astro/routes/api/oauth/authorize.mjs.map +1 -1
  273. package/dist/astro/routes/api/oauth/device/authorize.mjs +7 -7
  274. package/dist/astro/routes/api/oauth/device/code.mjs +10 -10
  275. package/dist/astro/routes/api/oauth/device/token.mjs +9 -9
  276. package/dist/astro/routes/api/oauth/register.mjs +4 -4
  277. package/dist/astro/routes/api/oauth/token/refresh.mjs +7 -7
  278. package/dist/astro/routes/api/oauth/token/revoke.mjs +7 -7
  279. package/dist/astro/routes/api/oauth/token.mjs +7 -7
  280. package/dist/astro/routes/api/openapi.json.mjs +52 -24
  281. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  282. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.d.mts.map +1 -1
  283. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +20 -32
  284. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs.map +1 -1
  285. package/dist/astro/routes/api/redirects/404s/index.mjs +12 -11
  286. package/dist/astro/routes/api/redirects/404s/index.mjs.map +1 -1
  287. package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -11
  288. package/dist/astro/routes/api/redirects/404s/summary.mjs.map +1 -1
  289. package/dist/astro/routes/api/redirects/_id_.mjs +13 -12
  290. package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
  291. package/dist/astro/routes/api/redirects/index.mjs +13 -12
  292. package/dist/astro/routes/api/redirects/index.mjs.map +1 -1
  293. package/dist/astro/routes/api/relations/_id_/index.mjs +16 -15
  294. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
  295. package/dist/astro/routes/api/relations/_id_/translations.mjs +11 -11
  296. package/dist/astro/routes/api/relations/index.mjs +16 -15
  297. package/dist/astro/routes/api/relations/index.mjs.map +1 -1
  298. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +4 -4
  299. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +5 -4
  300. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs.map +1 -1
  301. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +40 -39
  302. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  303. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +40 -39
  304. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  305. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +40 -39
  306. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  307. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +40 -39
  308. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  309. package/dist/astro/routes/api/schema/collections/index.mjs +40 -39
  310. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  311. package/dist/astro/routes/api/schema/index.mjs +9 -9
  312. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +40 -39
  313. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  314. package/dist/astro/routes/api/schema/orphans/index.mjs +40 -39
  315. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  316. package/dist/astro/routes/api/search/enable.mjs +13 -12
  317. package/dist/astro/routes/api/search/enable.mjs.map +1 -1
  318. package/dist/astro/routes/api/search/index.mjs +12 -11
  319. package/dist/astro/routes/api/search/index.mjs.map +1 -1
  320. package/dist/astro/routes/api/search/rebuild.mjs +13 -12
  321. package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
  322. package/dist/astro/routes/api/search/stats.mjs +8 -8
  323. package/dist/astro/routes/api/search/suggest.mjs +12 -11
  324. package/dist/astro/routes/api/search/suggest.mjs.map +1 -1
  325. package/dist/astro/routes/api/sections/_slug_.mjs +12 -11
  326. package/dist/astro/routes/api/sections/_slug_.mjs.map +1 -1
  327. package/dist/astro/routes/api/sections/index.mjs +12 -11
  328. package/dist/astro/routes/api/sections/index.mjs.map +1 -1
  329. package/dist/astro/routes/api/settings/backups/archives/_name_.d.mts +9 -0
  330. package/dist/astro/routes/api/settings/backups/archives/_name_.d.mts.map +1 -0
  331. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +48 -0
  332. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs.map +1 -0
  333. package/dist/astro/routes/api/settings/backups/archives/index.d.mts +8 -0
  334. package/dist/astro/routes/api/settings/backups/archives/index.d.mts.map +1 -0
  335. package/dist/astro/routes/api/settings/backups/archives/index.mjs +23 -0
  336. package/dist/astro/routes/api/settings/backups/archives/index.mjs.map +1 -0
  337. package/dist/astro/routes/api/settings/backups/export.d.mts +8 -0
  338. package/dist/astro/routes/api/settings/backups/export.d.mts.map +1 -0
  339. package/dist/astro/routes/api/settings/backups/export.mjs +35 -0
  340. package/dist/astro/routes/api/settings/backups/export.mjs.map +1 -0
  341. package/dist/astro/routes/api/settings/backups/index.d.mts +9 -0
  342. package/dist/astro/routes/api/settings/backups/index.d.mts.map +1 -0
  343. package/dist/astro/routes/api/settings/backups/index.mjs +52 -0
  344. package/dist/astro/routes/api/settings/backups/index.mjs.map +1 -0
  345. package/dist/astro/routes/api/settings/email.mjs +5 -5
  346. package/dist/astro/routes/api/settings.mjs +14 -13
  347. package/dist/astro/routes/api/settings.mjs.map +1 -1
  348. package/dist/astro/routes/api/setup/admin-verify.mjs +19 -15
  349. package/dist/astro/routes/api/setup/admin-verify.mjs.map +1 -1
  350. package/dist/astro/routes/api/setup/admin.mjs +18 -14
  351. package/dist/astro/routes/api/setup/admin.mjs.map +1 -1
  352. package/dist/astro/routes/api/setup/dev-bypass.mjs +26 -26
  353. package/dist/astro/routes/api/setup/dev-reset.mjs +3 -3
  354. package/dist/astro/routes/api/setup/index.mjs +28 -27
  355. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  356. package/dist/astro/routes/api/setup/status.mjs +5 -5
  357. package/dist/astro/routes/api/snapshot.mjs +8 -215
  358. package/dist/astro/routes/api/snapshot.mjs.map +1 -1
  359. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +15 -14
  360. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
  361. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +15 -14
  362. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
  363. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +15 -14
  364. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
  365. package/dist/astro/routes/api/taxonomies/index.mjs +15 -14
  366. package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
  367. package/dist/astro/routes/api/themes/preview.mjs +6 -6
  368. package/dist/astro/routes/api/typegen.mjs +6 -6
  369. package/dist/astro/routes/api/well-known/auth.mjs +1 -1
  370. package/dist/astro/routes/api/well-known/oauth-authorization-server.d.mts.map +1 -1
  371. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +4 -3
  372. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs.map +1 -1
  373. package/dist/astro/routes/api/well-known/oauth-protected-resource.d.mts.map +1 -1
  374. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +4 -3
  375. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs.map +1 -1
  376. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +9 -8
  377. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
  378. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +12 -11
  379. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
  380. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +12 -11
  381. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
  382. package/dist/astro/routes/api/widget-areas/_name_.mjs +7 -7
  383. package/dist/astro/routes/api/widget-areas/index.mjs +12 -11
  384. package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
  385. package/dist/astro/routes/api/widget-components.mjs +4 -4
  386. package/dist/astro/routes/robots.txt.mjs +7 -7
  387. package/dist/astro/routes/sitemap-_collection_.xml.mjs +11 -11
  388. package/dist/astro/routes/sitemap.xml.mjs +8 -8
  389. package/dist/astro/types.d.mts +38 -17
  390. package/dist/astro/types.d.mts.map +1 -1
  391. package/dist/auth/providers/github.d.mts +1 -1
  392. package/dist/auth/providers/google.d.mts +1 -1
  393. package/dist/{authorize-BmCwQCib.mjs → authorize-6WPZa1xZ.mjs} +7 -4
  394. package/dist/authorize-6WPZa1xZ.mjs.map +1 -0
  395. package/dist/backup-Qr9PaGUo.mjs +241 -0
  396. package/dist/backup-Qr9PaGUo.mjs.map +1 -0
  397. package/dist/{base64-C74Msx8h.mjs → base64-B-PsqheR.mjs} +8 -4
  398. package/dist/base64-B-PsqheR.mjs.map +1 -0
  399. package/dist/{byline-BWb5H-65.mjs → byline-BTOxUlFF.mjs} +6 -6
  400. package/dist/{byline-BWb5H-65.mjs.map → byline-BTOxUlFF.mjs.map} +1 -1
  401. package/dist/{byline-fields-DO6bAYK8.mjs → byline-fields-CdU_LTF1.mjs} +1 -1
  402. package/dist/{byline-fields-DO6bAYK8.mjs.map → byline-fields-CdU_LTF1.mjs.map} +1 -1
  403. package/dist/{bylines-CHojDMpj.mjs → bylines-FyZu6TbJ.mjs} +7 -7
  404. package/dist/{bylines-CHojDMpj.mjs.map → bylines-FyZu6TbJ.mjs.map} +1 -1
  405. package/dist/{bylines-BGuKFaBy.mjs → bylines-oVN0iPwX.mjs} +12 -8
  406. package/dist/bylines-oVN0iPwX.mjs.map +1 -0
  407. package/dist/{cache-BeR7RfN8.mjs → cache-DDwy3JDW.mjs} +2 -2
  408. package/dist/{cache-BeR7RfN8.mjs.map → cache-DDwy3JDW.mjs.map} +1 -1
  409. package/dist/{challenge-store-B426wQ6O.mjs → challenge-store-BFzgFRog.mjs} +1 -1
  410. package/dist/{challenge-store-B426wQ6O.mjs.map → challenge-store-BFzgFRog.mjs.map} +1 -1
  411. package/dist/{chunks-BU5_nrAC.mjs → chunks-Bq-5DPrD.mjs} +2 -2
  412. package/dist/{chunks-BU5_nrAC.mjs.map → chunks-Bq-5DPrD.mjs.map} +1 -1
  413. package/dist/cli/index.mjs +139 -32
  414. package/dist/cli/index.mjs.map +1 -1
  415. package/dist/client/cf-access.d.mts +1 -1
  416. package/dist/client/index.d.mts +54 -3
  417. package/dist/client/index.d.mts.map +1 -1
  418. package/dist/client/index.mjs +16 -4
  419. package/dist/client/index.mjs.map +1 -1
  420. package/dist/{comment-SWh31kS0.mjs → comment-B9cUud8H.mjs} +2 -2
  421. package/dist/{comment-SWh31kS0.mjs.map → comment-B9cUud8H.mjs.map} +1 -1
  422. package/dist/{comment-reaction-DgcpziXI.mjs → comment-reaction-VUXO981F.mjs} +2 -2
  423. package/dist/{comment-reaction-DgcpziXI.mjs.map → comment-reaction-VUXO981F.mjs.map} +1 -1
  424. package/dist/{comments-kZYGppMS.mjs → comments-PXj1E9Si.mjs} +3 -3
  425. package/dist/{comments-kZYGppMS.mjs.map → comments-PXj1E9Si.mjs.map} +1 -1
  426. package/dist/{components-CliOkZAs.mjs → components-DZkL4Fx0.mjs} +1 -1
  427. package/dist/{components-CliOkZAs.mjs.map → components-DZkL4Fx0.mjs.map} +1 -1
  428. package/dist/{config-CVssduLe.mjs → config-B6b_BobR.mjs} +6 -2
  429. package/dist/config-B6b_BobR.mjs.map +1 -0
  430. package/dist/{content-BO61Sx4j.mjs → content-BC43pICM.mjs} +53 -19
  431. package/dist/content-BC43pICM.mjs.map +1 -0
  432. package/dist/{content-refresh-BkkApzeS.mjs → content-refresh-CPMSauD3.mjs} +96 -4
  433. package/dist/content-refresh-CPMSauD3.mjs.map +1 -0
  434. package/dist/{context-DtSFYFuS.mjs → context-DOurRIrb.mjs} +97 -74
  435. package/dist/context-DOurRIrb.mjs.map +1 -0
  436. package/dist/{cron-YFJ7uNVA.mjs → cron-BlKIMD_e.mjs} +1 -1
  437. package/dist/{cron-YFJ7uNVA.mjs.map → cron-BlKIMD_e.mjs.map} +1 -1
  438. package/dist/{dashboard-xo_gPs_D.mjs → dashboard-CBm4zhc3.mjs} +4 -4
  439. package/dist/{dashboard-xo_gPs_D.mjs.map → dashboard-CBm4zhc3.mjs.map} +1 -1
  440. package/dist/database/pg-migration-lock.d.mts.map +1 -1
  441. package/dist/database/pg-migration-lock.mjs +1 -4
  442. package/dist/database/pg-migration-lock.mjs.map +1 -1
  443. package/dist/db/index.d.mts +3 -3
  444. package/dist/db/index.mjs +2 -2
  445. package/dist/db/index.mjs.map +1 -1
  446. package/dist/db/libsql.d.mts +1 -1
  447. package/dist/db/postgres.d.mts +1 -1
  448. package/dist/db/sqlite.d.mts +1 -1
  449. package/dist/{db-errors-BKuXh_fi.mjs → db-errors-CcWLaRiR.mjs} +4 -2
  450. package/dist/db-errors-CcWLaRiR.mjs.map +1 -0
  451. package/dist/{default-DY5ovGUG.mjs → default-C4v9KP56.mjs} +1 -1
  452. package/dist/{default-DY5ovGUG.mjs.map → default-C4v9KP56.mjs.map} +1 -1
  453. package/dist/{device-flow-CPXQ_QfW.mjs → device-flow-BeA6lUS1.mjs} +5 -6
  454. package/dist/device-flow-BeA6lUS1.mjs.map +1 -0
  455. package/dist/{dialect-helpers-Cue7QQOR.mjs → dialect-helpers-BtOwo2us.mjs} +60 -14
  456. package/dist/dialect-helpers-BtOwo2us.mjs.map +1 -0
  457. package/dist/{email-console-CjkQc7oY.mjs → email-console-C-9Ng8DM.mjs} +1 -1
  458. package/dist/{email-console-CjkQc7oY.mjs.map → email-console-C-9Ng8DM.mjs.map} +1 -1
  459. package/dist/error-DJ98TRbQ.mjs +90 -0
  460. package/dist/error-DJ98TRbQ.mjs.map +1 -0
  461. package/dist/{error-CfJMLSkd.mjs → errors-DtEXIQQV.mjs} +15 -84
  462. package/dist/errors-DtEXIQQV.mjs.map +1 -0
  463. package/dist/{escape-r8_GbEU2.mjs → escape-CFedIB1C.mjs} +1 -1
  464. package/dist/{escape-r8_GbEU2.mjs.map → escape-CFedIB1C.mjs.map} +1 -1
  465. package/dist/{field-defs-cache-BVrNCgSj.mjs → field-defs-cache-KfNJ_qLQ.mjs} +2 -2
  466. package/dist/{field-defs-cache-BVrNCgSj.mjs.map → field-defs-cache-KfNJ_qLQ.mjs.map} +1 -1
  467. package/dist/{fts-manager-BD9s77qh.mjs → fts-manager-hyCU9OOp.mjs} +3 -3
  468. package/dist/{fts-manager-BD9s77qh.mjs.map → fts-manager-hyCU9OOp.mjs.map} +1 -1
  469. package/dist/{hreflang-CgIxhH4k.d.mts → hreflang-CIxH_LKz.d.mts} +2 -2
  470. package/dist/{hreflang-CgIxhH4k.d.mts.map → hreflang-CIxH_LKz.d.mts.map} +1 -1
  471. package/dist/{hreflang-pAWUiPd_.mjs → hreflang-CbYeCzl9.mjs} +7 -7
  472. package/dist/{hreflang-pAWUiPd_.mjs.map → hreflang-CbYeCzl9.mjs.map} +1 -1
  473. package/dist/{import-2jHtLzsO.mjs → import-oDYCgNYQ.mjs} +4 -4
  474. package/dist/{import-2jHtLzsO.mjs.map → import-oDYCgNYQ.mjs.map} +1 -1
  475. package/dist/{index-DxjWLKGT.d.mts → index-BEui-cRd.d.mts} +3 -3
  476. package/dist/{index-DxjWLKGT.d.mts.map → index-BEui-cRd.d.mts.map} +1 -1
  477. package/dist/{index-BXOCyLj0.d.mts → index-BcEH_FfS.d.mts} +812 -16
  478. package/dist/index-BcEH_FfS.d.mts.map +1 -0
  479. package/dist/index.d.mts +18 -19
  480. package/dist/index.mjs +61 -60
  481. package/dist/{load-DgoWMdRX.mjs → load-Dg5gtWma.mjs} +2 -2
  482. package/dist/{load-DgoWMdRX.mjs.map → load-Dg5gtWma.mjs.map} +1 -1
  483. package/dist/{loader-Rb8QlGZp.mjs → loader-D4rigvjR.mjs} +170 -6
  484. package/dist/loader-D4rigvjR.mjs.map +1 -0
  485. package/dist/{manifest-schema-DeGNma2O.mjs → manifest-schema-bCq54i7F.mjs} +16 -2
  486. package/dist/manifest-schema-bCq54i7F.mjs.map +1 -0
  487. package/dist/media/image-endpoint.d.mts +30 -2
  488. package/dist/media/image-endpoint.d.mts.map +1 -1
  489. package/dist/media/image-endpoint.mjs +29 -3
  490. package/dist/media/image-endpoint.mjs.map +1 -1
  491. package/dist/media/index.d.mts +1 -1
  492. package/dist/media/local-runtime.d.mts +12 -13
  493. package/dist/media/local-runtime.d.mts.map +1 -1
  494. package/dist/media/local-runtime.mjs +6 -6
  495. package/dist/media-BJ6SRY3g.mjs +247 -0
  496. package/dist/media-BJ6SRY3g.mjs.map +1 -0
  497. package/dist/{media-allowlist-Dc05vKtt.mjs → media-allowlist-jUppPWFy.mjs} +1 -1
  498. package/dist/{media-allowlist-Dc05vKtt.mjs.map → media-allowlist-jUppPWFy.mjs.map} +1 -1
  499. package/dist/{media-D2ZFowpn.mjs → media-its1WLYv.mjs} +2 -2
  500. package/dist/{media-D2ZFowpn.mjs.map → media-its1WLYv.mjs.map} +1 -1
  501. package/dist/media-upload-DNu3QySc.mjs +145 -0
  502. package/dist/media-upload-DNu3QySc.mjs.map +1 -0
  503. package/dist/{media-url-DgIEYZr4.mjs → media-url-BCm5vBn6.mjs} +1 -1
  504. package/dist/{media-url-DgIEYZr4.mjs.map → media-url-BCm5vBn6.mjs.map} +1 -1
  505. package/dist/{media-usage-B__v9NMm.mjs → media-usage-DPQ3K4xB.mjs} +138 -5
  506. package/dist/{media-usage-B__v9NMm.mjs.map → media-usage-DPQ3K4xB.mjs.map} +1 -1
  507. package/dist/{menus-BpfevlJD.mjs → menus-BMGzWyWH.mjs} +15 -8
  508. package/dist/menus-BMGzWyWH.mjs.map +1 -0
  509. package/dist/{menus-BtvH7Z7t.mjs → menus-CPf_SW7u.mjs} +234 -13
  510. package/dist/menus-CPf_SW7u.mjs.map +1 -0
  511. package/dist/{mode-BVnhGZ2o.mjs → mode-fiXRMfeA.mjs} +1 -1
  512. package/dist/{mode-BVnhGZ2o.mjs.map → mode-fiXRMfeA.mjs.map} +1 -1
  513. package/dist/{oauth-authorization-BWqWTHBK.mjs → oauth-authorization-oqfbBuMH.mjs} +5 -6
  514. package/dist/oauth-authorization-oqfbBuMH.mjs.map +1 -0
  515. package/dist/{oauth-clients-43_XADoa.mjs → oauth-clients-BlrUHAsf.mjs} +1 -1
  516. package/dist/{oauth-clients-43_XADoa.mjs.map → oauth-clients-BlrUHAsf.mjs.map} +1 -1
  517. package/dist/{oauth-state-store-BRSUEkmx.mjs → oauth-state-store-DnvjRMy6.mjs} +2 -1
  518. package/dist/{oauth-state-store-BRSUEkmx.mjs.map → oauth-state-store-DnvjRMy6.mjs.map} +1 -1
  519. package/dist/{oauth-user-lookup-Ctgemb0k.mjs → oauth-user-lookup-oUllxvAv.mjs} +1 -1
  520. package/dist/{oauth-user-lookup-Ctgemb0k.mjs.map → oauth-user-lookup-oUllxvAv.mjs.map} +1 -1
  521. package/dist/object-cache/memory.d.mts +1 -1
  522. package/dist/{options-COXGTtqd.d.mts → options-BqMkWfYa.d.mts} +6 -6
  523. package/dist/{options-COXGTtqd.d.mts.map → options-BqMkWfYa.d.mts.map} +1 -1
  524. package/dist/page/index.d.mts +2 -2
  525. package/dist/page/index.d.mts.map +1 -1
  526. package/dist/page/index.mjs.map +1 -1
  527. package/dist/{parse-GLksnn-B.mjs → parse-53zyxlpv.mjs} +4 -12
  528. package/dist/{parse-GLksnn-B.mjs.map → parse-53zyxlpv.mjs.map} +1 -1
  529. package/dist/{passkey-config-CjA1UbDj.mjs → passkey-config-C-SKpc0-.mjs} +1 -1
  530. package/dist/{passkey-config-CjA1UbDj.mjs.map → passkey-config-C-SKpc0-.mjs.map} +1 -1
  531. package/dist/{placeholder-DR-eeZr-.d.mts → placeholder-Dop47JMM.d.mts} +1 -1
  532. package/dist/{placeholder-DR-eeZr-.d.mts.map → placeholder-Dop47JMM.d.mts.map} +1 -1
  533. package/dist/plugin-types.d.mts +21 -2
  534. package/dist/plugin-types.d.mts.map +1 -1
  535. package/dist/plugin-utils.d.mts +18 -17
  536. package/dist/plugin-utils.d.mts.map +1 -1
  537. package/dist/plugin-utils.mjs +8 -6
  538. package/dist/plugin-utils.mjs.map +1 -1
  539. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -11
  540. package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
  541. package/dist/plugins/adapt-sandbox-entry.mjs +15 -3
  542. package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
  543. package/dist/{portable-text-BAkgEKcv.mjs → portable-text-sDj92Kxv.mjs} +1 -1
  544. package/dist/{portable-text-BAkgEKcv.mjs.map → portable-text-sDj92Kxv.mjs.map} +1 -1
  545. package/dist/{preview-Rloa09Wp.mjs → preview-D4Jnbfx7.mjs} +2 -2
  546. package/dist/{preview-Rloa09Wp.mjs.map → preview-D4Jnbfx7.mjs.map} +1 -1
  547. package/dist/{public-url-C0cEvrRu.mjs → public-url-DSGTnJFw.mjs} +5 -3
  548. package/dist/public-url-DSGTnJFw.mjs.map +1 -0
  549. package/dist/{query-ZzvXyxWq.mjs → query-BbJQ-dGg.mjs} +15 -15
  550. package/dist/{query-ZzvXyxWq.mjs.map → query-BbJQ-dGg.mjs.map} +1 -1
  551. package/dist/{query-BxT95opY.mjs → query-DdFGHzQF.mjs} +3 -3
  552. package/dist/{query-BxT95opY.mjs.map → query-DdFGHzQF.mjs.map} +1 -1
  553. package/dist/{rate-limit-Cg4Rvc_-.mjs → rate-limit-D3IEiUi2.mjs} +2 -2
  554. package/dist/{rate-limit-Cg4Rvc_-.mjs.map → rate-limit-D3IEiUi2.mjs.map} +1 -1
  555. package/dist/{redirect-Ch3E98qZ.mjs → redirect-BoUac4NT.mjs} +10 -4
  556. package/dist/redirect-BoUac4NT.mjs.map +1 -0
  557. package/dist/{redirect-Ce_6Yb6z.mjs → redirect-Botom7X6.mjs} +1 -1
  558. package/dist/{redirect-Ce_6Yb6z.mjs.map → redirect-Botom7X6.mjs.map} +1 -1
  559. package/dist/{redirects-CXmVGRNZ.mjs → redirects-ChbpqKiS.mjs} +4 -4
  560. package/dist/{redirects-CXmVGRNZ.mjs.map → redirects-ChbpqKiS.mjs.map} +1 -1
  561. package/dist/{redirects-CeD7K7EX.mjs → redirects-D457qwt0.mjs} +10 -181
  562. package/dist/redirects-D457qwt0.mjs.map +1 -0
  563. package/dist/{registry-CyIXPi4E.mjs → registry-y21kjhfG.mjs} +97 -8
  564. package/dist/registry-y21kjhfG.mjs.map +1 -0
  565. package/dist/{relation-Ba68Tm4Z.mjs → relation-Du0r04E0.mjs} +3 -3
  566. package/dist/{relation-Ba68Tm4Z.mjs.map → relation-Du0r04E0.mjs.map} +1 -1
  567. package/dist/{relations-CbBpoOys.d.mts → relations-BKybBoOX.d.mts} +315 -9
  568. package/dist/relations-BKybBoOX.d.mts.map +1 -0
  569. package/dist/{relations-BmBmX4Eb.mjs → relations-CZMpBqEd.mjs} +3 -3
  570. package/dist/{relations-BmBmX4Eb.mjs.map → relations-CZMpBqEd.mjs.map} +1 -1
  571. package/dist/{relations-C8hVyEy0.mjs → relations-DtX1UMBH.mjs} +13 -7
  572. package/dist/relations-DtX1UMBH.mjs.map +1 -0
  573. package/dist/{request-meta-DCwktC5P.mjs → request-meta-DzXYYI-n.mjs} +2 -2
  574. package/dist/{request-meta-DCwktC5P.mjs.map → request-meta-DzXYYI-n.mjs.map} +1 -1
  575. package/dist/{resolve-Cd9dzclN.mjs → resolve-DFr4kq72.mjs} +2 -2
  576. package/dist/{resolve-Cd9dzclN.mjs.map → resolve-DFr4kq72.mjs.map} +1 -1
  577. package/dist/{runner-jt65bF7F.mjs → runner-BdeLEgad.mjs} +334 -200
  578. package/dist/runner-BdeLEgad.mjs.map +1 -0
  579. package/dist/{runner-CiNdIk3y.d.mts → runner-Dd8m8k9F.d.mts} +2 -2
  580. package/dist/{runner-CiNdIk3y.d.mts.map → runner-Dd8m8k9F.d.mts.map} +1 -1
  581. package/dist/runtime.d.mts +11 -12
  582. package/dist/runtime.d.mts.map +1 -1
  583. package/dist/runtime.mjs +4 -4
  584. package/dist/{schema-DwjufZ1S.mjs → schema-BH8P8vaC.mjs} +3 -3
  585. package/dist/{schema-DwjufZ1S.mjs.map → schema-BH8P8vaC.mjs.map} +1 -1
  586. package/dist/scopes-DTIkIaGA.mjs +27 -0
  587. package/dist/scopes-DTIkIaGA.mjs.map +1 -0
  588. package/dist/{search-Cl875CVE.mjs → search-D-Yhpmvs.mjs} +8 -7
  589. package/dist/search-D-Yhpmvs.mjs.map +1 -0
  590. package/dist/{secrets-CO_bEmII.mjs → secrets-CQ-69kF9.mjs} +25 -19
  591. package/dist/secrets-CQ-69kF9.mjs.map +1 -0
  592. package/dist/{sections-DTKzgoiS.mjs → sections-CzVBAKjw.mjs} +3 -3
  593. package/dist/{sections-DTKzgoiS.mjs.map → sections-CzVBAKjw.mjs.map} +1 -1
  594. package/dist/seed/index.d.mts +2 -2
  595. package/dist/seed/index.mjs +20 -20
  596. package/dist/seo/index.d.mts +14 -3
  597. package/dist/seo/index.d.mts.map +1 -1
  598. package/dist/seo/index.mjs +17 -17
  599. package/dist/seo/index.mjs.map +1 -1
  600. package/dist/{seo-BjrbXAd_.mjs → seo-C3GDfT0V.mjs} +1 -1
  601. package/dist/{seo-BjrbXAd_.mjs.map → seo-C3GDfT0V.mjs.map} +1 -1
  602. package/dist/{seo-BLm6SXzS.mjs → seo-kJzVAmmu.mjs} +2 -2
  603. package/dist/{seo-BLm6SXzS.mjs.map → seo-kJzVAmmu.mjs.map} +1 -1
  604. package/dist/{service-D6zDmKXy.mjs → service-CQnc276a.mjs} +3 -3
  605. package/dist/{service-D6zDmKXy.mjs.map → service-CQnc276a.mjs.map} +1 -1
  606. package/dist/{session-user-DoSs6YM8.mjs → session-user-DbHqKDKe.mjs} +1 -1
  607. package/dist/{session-user-DoSs6YM8.mjs.map → session-user-DbHqKDKe.mjs.map} +1 -1
  608. package/dist/{settings-BuQYvLfy.mjs → settings-C2PaBS49.mjs} +4 -4
  609. package/dist/{settings-BuQYvLfy.mjs.map → settings-C2PaBS49.mjs.map} +1 -1
  610. package/dist/{settings-CmS3TwXf.mjs → settings-Dtc0DU31.mjs} +3 -3
  611. package/dist/{settings-CmS3TwXf.mjs.map → settings-Dtc0DU31.mjs.map} +1 -1
  612. package/dist/{setup-complete-BPNgLHlU.mjs → setup-complete-CdIlqloh.mjs} +1 -1
  613. package/dist/{setup-complete-BPNgLHlU.mjs.map → setup-complete-CdIlqloh.mjs.map} +1 -1
  614. package/dist/{setup-nonce-DEPZX96T.mjs → setup-nonce-DqiIPr-J.mjs} +1 -1
  615. package/dist/{setup-nonce-DEPZX96T.mjs.map → setup-nonce-DqiIPr-J.mjs.map} +1 -1
  616. package/dist/{single-flight-cache-DEtW-OEG.mjs → single-flight-cache-C2exrGAi.mjs} +1 -1
  617. package/dist/{single-flight-cache-DEtW-OEG.mjs.map → single-flight-cache-C2exrGAi.mjs.map} +1 -1
  618. package/dist/{site-url-CcLy5D5s.mjs → site-url-NGJT6NTU.mjs} +1 -1
  619. package/dist/{site-url-CcLy5D5s.mjs.map → site-url-NGJT6NTU.mjs.map} +1 -1
  620. package/dist/snapshot-BdpUJKD-.mjs +216 -0
  621. package/dist/snapshot-BdpUJKD-.mjs.map +1 -0
  622. package/dist/{ssrf-TrKYJAy0.mjs → ssrf-CviKqWmq.mjs} +1 -1
  623. package/dist/{ssrf-TrKYJAy0.mjs.map → ssrf-CviKqWmq.mjs.map} +1 -1
  624. package/dist/state-xxv6ZTMv.mjs +128 -0
  625. package/dist/state-xxv6ZTMv.mjs.map +1 -0
  626. package/dist/{status-D3Gy1Zc6.mjs → status-COfycGIU.mjs} +1 -1
  627. package/dist/{status-D3Gy1Zc6.mjs.map → status-COfycGIU.mjs.map} +1 -1
  628. package/dist/storage/local.d.mts +1 -1
  629. package/dist/storage/local.mjs +1 -1
  630. package/dist/storage/s3.d.mts +1 -1
  631. package/dist/storage/s3.mjs +1 -1
  632. package/dist/{taxonomies-pUkuD6nj.mjs → taxonomies-HI9fnTdp.mjs} +8 -8
  633. package/dist/{taxonomies-pUkuD6nj.mjs.map → taxonomies-HI9fnTdp.mjs.map} +1 -1
  634. package/dist/{taxonomies-ClS4RKaZ.mjs → taxonomies-rMeBzOjX.mjs} +26 -17
  635. package/dist/taxonomies-rMeBzOjX.mjs.map +1 -0
  636. package/dist/{taxonomy-BkxbUMSw.mjs → taxonomy-0mCI25HN.mjs} +51 -13
  637. package/dist/taxonomy-0mCI25HN.mjs.map +1 -0
  638. package/dist/{tokens-BV6fgl5A.mjs → tokens-DVltpO2D.mjs} +2 -2
  639. package/dist/{tokens-BV6fgl5A.mjs.map → tokens-DVltpO2D.mjs.map} +1 -1
  640. package/dist/{transport-DTFhhPOg.mjs → transport-CKrlqSpD.mjs} +1 -1
  641. package/dist/{transport-DTFhhPOg.mjs.map → transport-CKrlqSpD.mjs.map} +1 -1
  642. package/dist/{transport-DmlmzD2U.d.mts → transport-Vd7VdqHv.d.mts} +1 -1
  643. package/dist/{transport-DmlmzD2U.d.mts.map → transport-Vd7VdqHv.d.mts.map} +1 -1
  644. package/dist/{trusted-proxy-CSgZVESw.mjs → trusted-proxy-CwjQj0YG.mjs} +1 -1
  645. package/dist/{trusted-proxy-CSgZVESw.mjs.map → trusted-proxy-CwjQj0YG.mjs.map} +1 -1
  646. package/dist/{types-B6-N9cBB.d.mts → types-B4vm81VH.d.mts} +1 -1
  647. package/dist/{types-B6-N9cBB.d.mts.map → types-B4vm81VH.d.mts.map} +1 -1
  648. package/dist/{types-Cm4Cz-8M.d.mts → types-B97jg3aW.d.mts} +6 -1
  649. package/dist/{types-Cm4Cz-8M.d.mts.map → types-B97jg3aW.d.mts.map} +1 -1
  650. package/dist/{types-BNZsPL9z.mjs → types-BD40g7zz.mjs} +1 -1
  651. package/dist/{types-BNZsPL9z.mjs.map → types-BD40g7zz.mjs.map} +1 -1
  652. package/dist/{types-QnlQIyPE.d.mts → types-BNfPHfay.d.mts} +1 -1
  653. package/dist/{types-QnlQIyPE.d.mts.map → types-BNfPHfay.d.mts.map} +1 -1
  654. package/dist/{types-DLIrNtv5.d.mts → types-BfYUPJdP.d.mts} +2 -2
  655. package/dist/{types-DLIrNtv5.d.mts.map → types-BfYUPJdP.d.mts.map} +1 -1
  656. package/dist/{types-CtDVagM4.d.mts → types-BvB7gDOD.d.mts} +38 -4
  657. package/dist/types-BvB7gDOD.d.mts.map +1 -0
  658. package/dist/{types-B_pmP7Yq.d.mts → types-C7U0HlEr.d.mts} +1 -1
  659. package/dist/{types-B_pmP7Yq.d.mts.map → types-C7U0HlEr.d.mts.map} +1 -1
  660. package/dist/{types-Ch8pFMEm.mjs → types-CrRUFdej.mjs} +3 -3
  661. package/dist/{types-Ch8pFMEm.mjs.map → types-CrRUFdej.mjs.map} +1 -1
  662. package/dist/{types-momGysZk.d.mts → types-DPKhI5ib.d.mts} +9 -1
  663. package/dist/types-DPKhI5ib.d.mts.map +1 -0
  664. package/dist/{types-BCVuUPrL.d.mts → types-Kmaxdarz.d.mts} +1 -1
  665. package/dist/{types-BCVuUPrL.d.mts.map → types-Kmaxdarz.d.mts.map} +1 -1
  666. package/dist/{types-qljd1hsL.d.mts → types-iVeZ5g6N.d.mts} +1 -1
  667. package/dist/{types-qljd1hsL.d.mts.map → types-iVeZ5g6N.d.mts.map} +1 -1
  668. package/dist/{user-DTxX_Fha.mjs → user-aTXqmXBz.mjs} +3 -3
  669. package/dist/{user-DTxX_Fha.mjs.map → user-aTXqmXBz.mjs.map} +1 -1
  670. package/dist/{utils-Cz8PVrl4.mjs → utils-BbUgmeZG.mjs} +1 -1
  671. package/dist/{utils-Cz8PVrl4.mjs.map → utils-BbUgmeZG.mjs.map} +1 -1
  672. package/dist/{validate-C3CpWlNt.mjs → validate-CDT1jl6_.mjs} +11 -6
  673. package/dist/validate-CDT1jl6_.mjs.map +1 -0
  674. package/dist/{validate-9jLuE301.d.mts → validate-DULfgpJP.d.mts} +5 -5
  675. package/dist/{validate-9jLuE301.d.mts.map → validate-DULfgpJP.d.mts.map} +1 -1
  676. package/dist/{validation-DM2K_NJC.mjs → validation-DlD0E35W.mjs} +10 -10
  677. package/dist/{validation-DM2K_NJC.mjs.map → validation-DlD0E35W.mjs.map} +1 -1
  678. package/dist/version-Ci6bw6YK.mjs +7 -0
  679. package/dist/{version-BlIJvNAZ.mjs.map → version-Ci6bw6YK.mjs.map} +1 -1
  680. package/dist/{widgets-B_X6_UXJ.mjs → widgets-5fipNkMT.mjs} +3 -3
  681. package/dist/{widgets-B_X6_UXJ.mjs.map → widgets-5fipNkMT.mjs.map} +1 -1
  682. package/dist/{wxr-taxonomies-tADW4KTa.d.mts → wxr-taxonomies-B4tMuN5T.d.mts} +1 -1
  683. package/dist/{wxr-taxonomies-tADW4KTa.d.mts.map → wxr-taxonomies-B4tMuN5T.d.mts.map} +1 -1
  684. package/dist/{wxr-taxonomies-oNJB2S81.mjs → wxr-taxonomies-BmUnBNgz.mjs} +4 -4
  685. package/dist/{wxr-taxonomies-oNJB2S81.mjs.map → wxr-taxonomies-BmUnBNgz.mjs.map} +1 -1
  686. package/dist/{zod-generator-CrfV79De.mjs → zod-generator-CwkHWz4f.mjs} +2 -2
  687. package/dist/{zod-generator-CrfV79De.mjs.map → zod-generator-CwkHWz4f.mjs.map} +1 -1
  688. package/package.json +8 -8
  689. package/src/api/authorize.ts +10 -1
  690. package/src/api/error.ts +5 -5
  691. package/src/api/errors.ts +14 -0
  692. package/src/api/handlers/backup.ts +312 -0
  693. package/src/api/handlers/bylines.ts +5 -4
  694. package/src/api/handlers/content.ts +61 -9
  695. package/src/api/handlers/device-flow.ts +2 -3
  696. package/src/api/handlers/index.ts +16 -0
  697. package/src/api/handlers/marketplace.ts +38 -0
  698. package/src/api/handlers/media-upload.ts +210 -0
  699. package/src/api/handlers/media-usage.ts +211 -2
  700. package/src/api/handlers/menus.ts +11 -6
  701. package/src/api/handlers/oauth-authorization.ts +2 -6
  702. package/src/api/handlers/plugin-settings.ts +218 -0
  703. package/src/api/handlers/plugins.ts +41 -0
  704. package/src/api/handlers/registry.ts +39 -0
  705. package/src/api/handlers/relations.ts +7 -2
  706. package/src/api/handlers/snapshot.ts +21 -3
  707. package/src/api/handlers/taxonomies.ts +22 -14
  708. package/src/api/openapi/document.ts +48 -6
  709. package/src/api/parse.ts +1 -15
  710. package/src/api/public-url.ts +4 -2
  711. package/src/api/schemas/bylines.ts +4 -4
  712. package/src/api/schemas/common.ts +13 -8
  713. package/src/api/schemas/media-usage.ts +72 -0
  714. package/src/api/schemas/media.ts +32 -1
  715. package/src/api/schemas/menus.ts +2 -1
  716. package/src/api/schemas/relations.ts +3 -1
  717. package/src/api/schemas/taxonomies.ts +4 -2
  718. package/src/astro/integration/index.ts +38 -0
  719. package/src/astro/integration/routes.ts +35 -0
  720. package/src/astro/integration/runtime.ts +6 -0
  721. package/src/astro/integration/virtual-modules.ts +41 -7
  722. package/src/astro/integration/vite-config.ts +17 -1
  723. package/src/astro/middleware/auth.ts +70 -78
  724. package/src/astro/middleware/csp.ts +5 -2
  725. package/src/astro/middleware.ts +86 -31
  726. package/src/astro/public-plugin-api-routes.ts +2 -1
  727. package/src/astro/routes/PluginRegistry.tsx +5 -2
  728. package/src/astro/routes/admin.astro +56 -16
  729. package/src/astro/routes/api/admin/api-tokens/index.ts +2 -2
  730. package/src/astro/routes/api/admin/bylines/[id]/translations.ts +2 -5
  731. package/src/astro/routes/api/admin/bylines/index.ts +5 -8
  732. package/src/astro/routes/api/admin/plugins/[id]/index.ts +6 -0
  733. package/src/astro/routes/api/admin/plugins/[id]/mcp.ts +42 -0
  734. package/src/astro/routes/api/admin/plugins/[id]/settings.ts +80 -0
  735. package/src/astro/routes/api/admin/plugins/[id]/update.ts +2 -0
  736. package/src/astro/routes/api/admin/plugins/index.ts +9 -0
  737. package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +2 -0
  738. package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +2 -0
  739. package/src/astro/routes/api/admin/plugins/registry/install.ts +2 -0
  740. package/src/astro/routes/api/admin/plugins/updates.ts +2 -4
  741. package/src/astro/routes/api/auth/mode.ts +6 -14
  742. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +21 -4
  743. package/src/astro/routes/api/auth/oauth/[provider].ts +24 -6
  744. package/src/astro/routes/api/content/[collection]/[id]/compare.ts +3 -4
  745. package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +2 -3
  746. package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +2 -3
  747. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +3 -4
  748. package/src/astro/routes/api/content/[collection]/[id]/references/[relation]/children.ts +4 -6
  749. package/src/astro/routes/api/content/[collection]/[id]/references/[relation]/parents.ts +2 -3
  750. package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +3 -4
  751. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +4 -6
  752. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +3 -4
  753. package/src/astro/routes/api/content/[collection]/[id].ts +3 -4
  754. package/src/astro/routes/api/content/[collection]/authors.ts +2 -3
  755. package/src/astro/routes/api/content/[collection]/index.ts +6 -8
  756. package/src/astro/routes/api/content/[collection]/trash.ts +2 -3
  757. package/src/astro/routes/api/manifest.ts +2 -9
  758. package/src/astro/routes/api/mcp.ts +2 -1
  759. package/src/astro/routes/api/media/[id]/usage.ts +30 -0
  760. package/src/astro/routes/api/media/[id].ts +17 -6
  761. package/src/astro/routes/api/media/file/[...key].ts +8 -0
  762. package/src/astro/routes/api/media.ts +21 -2
  763. package/src/astro/routes/api/oauth/authorize.ts +43 -15
  764. package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +17 -3
  765. package/src/astro/routes/api/settings/backups/archives/[name].ts +78 -0
  766. package/src/astro/routes/api/settings/backups/archives/index.ts +33 -0
  767. package/src/astro/routes/api/settings/backups/export.ts +43 -0
  768. package/src/astro/routes/api/settings/backups/index.ts +76 -0
  769. package/src/astro/routes/api/setup/admin-verify.ts +8 -4
  770. package/src/astro/routes/api/setup/admin.ts +11 -4
  771. package/src/astro/routes/api/well-known/oauth-authorization-server.ts +4 -1
  772. package/src/astro/routes/api/well-known/oauth-protected-resource.ts +5 -1
  773. package/src/astro/types.ts +48 -2
  774. package/src/auth/api-tokens.ts +1 -0
  775. package/src/auth/oauth-state-store.ts +3 -0
  776. package/src/auth/providers/github-admin.tsx +5 -6
  777. package/src/auth/providers/google-admin.tsx +5 -6
  778. package/src/auth/scopes.ts +3 -9
  779. package/src/cli/client-factory.ts +0 -1
  780. package/src/cli/commands/bundle-utils.ts +50 -1
  781. package/src/cli/commands/bundle.ts +9 -2
  782. package/src/cli/commands/media.ts +127 -0
  783. package/src/client/index.ts +84 -2
  784. package/src/client/transport.ts +1 -1
  785. package/src/components/EmDashHead.astro +4 -8
  786. package/src/components/Gallery.astro +141 -26
  787. package/src/components/InlinePortableTextEditor.tsx +56 -35
  788. package/src/components/marks/Link.astro +5 -1
  789. package/src/config/secrets.ts +25 -19
  790. package/src/content/converters/gallery.ts +61 -0
  791. package/src/content/converters/portable-text-to-prosemirror.ts +19 -0
  792. package/src/content/converters/prosemirror-to-portable-text.ts +18 -0
  793. package/src/content/converters/types.ts +36 -0
  794. package/src/database/dialect-helpers.ts +80 -7
  795. package/src/database/migrations/051_content_taxonomies_denorm.ts +120 -0
  796. package/src/database/migrations/052_media_usage_read_index.ts +26 -0
  797. package/src/database/migrations/053_plugin_mcp_tools.ts +21 -0
  798. package/src/database/migrations/runner.ts +6 -0
  799. package/src/database/pg-migration-lock.ts +1 -4
  800. package/src/database/repositories/audit.ts +2 -1
  801. package/src/database/repositories/content.ts +98 -16
  802. package/src/database/repositories/media-usage.ts +250 -1
  803. package/src/database/repositories/plugin-storage.ts +29 -32
  804. package/src/database/repositories/redirect.ts +14 -2
  805. package/src/database/repositories/taxonomy.ts +57 -8
  806. package/src/database/types.ts +23 -0
  807. package/src/db/adapters.ts +9 -0
  808. package/src/emdash-runtime.ts +365 -83
  809. package/src/i18n/config.ts +8 -0
  810. package/src/i18n/repair-locale-casing.ts +51 -0
  811. package/src/loader.ts +292 -1
  812. package/src/mcp/server.ts +222 -5
  813. package/src/media/image-endpoint.ts +38 -2
  814. package/src/page/metadata.ts +27 -0
  815. package/src/plugin-types.ts +19 -0
  816. package/src/plugin-utils.ts +8 -6
  817. package/src/plugins/adapt-sandbox-entry.ts +30 -1
  818. package/src/plugins/context.ts +7 -0
  819. package/src/plugins/define-plugin.ts +16 -0
  820. package/src/plugins/manifest-schema.ts +28 -1
  821. package/src/plugins/routes.ts +27 -1
  822. package/src/plugins/sandbox/runner-options.ts +16 -0
  823. package/src/plugins/sandbox/types.ts +6 -1
  824. package/src/plugins/state.ts +17 -1
  825. package/src/plugins/storage-query.ts +67 -33
  826. package/src/plugins/types.ts +41 -0
  827. package/src/schema/registry.ts +141 -5
  828. package/src/search/query.ts +3 -2
  829. package/src/seed/apply.ts +37 -36
  830. package/src/seed/validate.ts +15 -4
  831. package/src/seo/index.ts +19 -4
  832. package/src/storage/types.ts +6 -0
  833. package/src/utils/base64.ts +7 -2
  834. package/src/utils/db-errors.ts +3 -1
  835. package/src/virtual-modules.d.ts +13 -0
  836. package/src/visual-editing/toolbar.ts +1 -1
  837. package/dist/adapters-DPAkL6QF.d.mts.map +0 -1
  838. package/dist/api-DVbHNApo.mjs.map +0 -1
  839. package/dist/api-tokens-Dk2GrJMY.mjs +0 -3
  840. package/dist/apply-D23iUV4q.mjs.map +0 -1
  841. package/dist/authorize-BmCwQCib.mjs.map +0 -1
  842. package/dist/base64-C74Msx8h.mjs.map +0 -1
  843. package/dist/bylines-BGuKFaBy.mjs.map +0 -1
  844. package/dist/config-CVssduLe.mjs.map +0 -1
  845. package/dist/content-BO61Sx4j.mjs.map +0 -1
  846. package/dist/content-refresh-BkkApzeS.mjs.map +0 -1
  847. package/dist/context-DtSFYFuS.mjs.map +0 -1
  848. package/dist/db-errors-BKuXh_fi.mjs.map +0 -1
  849. package/dist/device-flow-CPXQ_QfW.mjs.map +0 -1
  850. package/dist/dialect-helpers-Cue7QQOR.mjs.map +0 -1
  851. package/dist/error-CfJMLSkd.mjs.map +0 -1
  852. package/dist/index-BXOCyLj0.d.mts.map +0 -1
  853. package/dist/loader-Rb8QlGZp.mjs.map +0 -1
  854. package/dist/manifest-schema-DeGNma2O.mjs.map +0 -1
  855. package/dist/menus-BpfevlJD.mjs.map +0 -1
  856. package/dist/menus-BtvH7Z7t.mjs.map +0 -1
  857. package/dist/oauth-authorization-BWqWTHBK.mjs.map +0 -1
  858. package/dist/public-url-C0cEvrRu.mjs.map +0 -1
  859. package/dist/redirect-Ch3E98qZ.mjs.map +0 -1
  860. package/dist/redirects-CeD7K7EX.mjs.map +0 -1
  861. package/dist/registry-CyIXPi4E.mjs.map +0 -1
  862. package/dist/relations-C8hVyEy0.mjs.map +0 -1
  863. package/dist/relations-CbBpoOys.d.mts.map +0 -1
  864. package/dist/runner-jt65bF7F.mjs.map +0 -1
  865. package/dist/scheduled-publish-DlJHGcuL.d.mts +0 -11
  866. package/dist/scheduled-publish-DlJHGcuL.d.mts.map +0 -1
  867. package/dist/search-Cl875CVE.mjs.map +0 -1
  868. package/dist/secrets-CO_bEmII.mjs.map +0 -1
  869. package/dist/taxonomies-ClS4RKaZ.mjs.map +0 -1
  870. package/dist/taxonomy-BkxbUMSw.mjs.map +0 -1
  871. package/dist/types-CtDVagM4.d.mts.map +0 -1
  872. package/dist/types-momGysZk.d.mts.map +0 -1
  873. package/dist/validate-C3CpWlNt.mjs.map +0 -1
  874. package/dist/version-BlIJvNAZ.mjs +0 -7
  875. /package/dist/{ssrf-B5DOv5E5.mjs → ssrf-BB__xfNM.mjs} +0 -0
  876. /package/dist/{types-DeCZBZxn.mjs → types-CK0LxPUQ.mjs} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"execute.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/import/wordpress-plugin/execute.ts"],"sourcesContent":["/**\n * WordPress Plugin execute import endpoint\n *\n * POST /_emdash/api/import/wordpress-plugin/execute\n *\n * Imports content from WordPress via EmDash Exporter plugin API.\n */\n\nimport type { APIRoute } from \"astro\";\nimport {\n\tContentRepository,\n\tSchemaRegistry,\n\ttype WxrCategory,\n\ttype WxrPost,\n\ttype WxrTag,\n\ttype WxrTerm,\n} from \"emdash\";\nimport type { z } from \"zod\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { handleTaxonomyCreate } from \"#api/handlers/taxonomies.js\";\nimport { isParseError, parseBody } from \"#api/parse.js\";\nimport { wpPluginExecuteBody } from \"#api/schemas.js\";\nimport { BylineRepository } from \"#db/repositories/byline.js\";\nimport { importCommentsFromPlugin } from \"#import/comments.js\";\nimport { getSource } from \"#import/index.js\";\nimport { importMenusFromPlugin } from \"#import/menus.js\";\nimport { importSiteSettings, parseSiteSettingsFromPlugin } from \"#import/settings.js\";\nimport {\n\tfetchPluginComments,\n\tfetchPluginCommentsPage,\n\tfetchPluginContentPage,\n\tfetchPluginMenus,\n\tfetchPluginOptions,\n\tfetchPluginTaxonomies,\n} from \"#import/sources/wordpress-plugin.js\";\nimport { resolveAndValidateExternalUrl, SsrfError } from \"#import/ssrf.js\";\nimport type { ImportConfig, ImportResult, NormalizedItem } from \"#import/types.js\";\nimport { resolveImportByline, sanitizeFieldSlug } from \"#import/utils.js\";\nimport {\n\tattachPostTaxonomies,\n\tloadTaxonomyPlanFromDb,\n\tpreImportWxrTaxonomies,\n\ttype TaxonomyImportPlan,\n} from \"#import/wxr-taxonomies.js\";\nimport type { FieldType } from \"#schema/types.js\";\nimport type { EmDashHandlers, EmDashManifest } from \"#types\";\nimport { slugify } from \"#utils/slugify.js\";\n\nimport { importMediaWithProgress } from \"../wordpress/media.js\";\n\nexport const prerender = false;\n\nexport interface WpPluginImportConfig extends ImportConfig {\n\t/** Author mappings (WP author login -> EmDash user ID) */\n\tauthorMappings?: Record<string, string | null>;\n\t/** Wizard toggles. Absent means enabled (older admins don't send them). */\n\timportMenus?: boolean;\n\timportSiteTitle?: boolean;\n\timportLogo?: boolean;\n\timportSeo?: boolean;\n}\n\nexport interface WpPluginImportResponse {\n\tsuccess: boolean;\n\tresult?: ImportResult;\n\terror?: { message: string };\n}\n\n// =============================================================================\n// Chunked mode (issue #475)\n//\n// A single-invocation import of a large site exceeds Cloudflare Worker\n// resource limits (CPU time, subrequests). In chunked mode the admin drives\n// the import as a loop of bounded requests: one WP content page per call,\n// then paginated comments, then a small finalize step (menus + site\n// identity). Cross-chunk state — the WP-ID -> EmDash-ID map, translation\n// groups, comment threading roots — is accumulated by the client and sent\n// back with each request, so the server stays stateless and an aborted\n// import simply re-runs (skipExisting rebuilds the map while skipping work).\n// =============================================================================\n\n/** Posts per content chunk. 50 posts ≈ a few hundred D1 ops per invocation. */\nconst CONTENT_CHUNK_SIZE = 50;\n\ninterface ChunkCursor {\n\tpostTypeIndex: number;\n\tpage: number;\n}\n\n/** Per-chunk response payload: partial result + state the client must carry. */\ninterface ChunkResponse {\n\tsuccess: boolean;\n\tresult: ImportResult;\n\tdone: boolean;\n\tcursor?: ChunkCursor;\n\tchunk?: {\n\t\tidMap?: Record<string, { id: string; collection: string }>;\n\t\ttranslationGroups?: Record<string, string>;\n\t\tcommentRoots?: Record<string, string>;\n\t};\n}\n\nfunction emptyImportResult(): ImportResult {\n\treturn { success: true, imported: 0, skipped: 0, errors: [], byCollection: {} };\n}\n\nfunction parseIdMap(idMap: Record<string, { id: string; collection: string }> | undefined): {\n\tcontentIdMap: Map<number, string>;\n\tcollectionByWpId: Map<number, string>;\n} {\n\tconst contentIdMap = new Map<number, string>();\n\tconst collectionByWpId = new Map<number, string>();\n\tfor (const [key, value] of Object.entries(idMap ?? {})) {\n\t\tconst wpId = Number(key);\n\t\tif (!Number.isFinite(wpId)) continue;\n\t\tcontentIdMap.set(wpId, value.id);\n\t\tcollectionByWpId.set(wpId, value.collection);\n\t}\n\treturn { contentIdMap, collectionByWpId };\n}\n\nfunction serializeIdMap(\n\tcontentIdMap: Map<number, string>,\n\tcollectionByWpId: Map<number, string>,\n): Record<string, { id: string; collection: string }> {\n\tconst out: Record<string, { id: string; collection: string }> = {};\n\tfor (const [wpId, id] of contentIdMap) {\n\t\tconst collection = collectionByWpId.get(wpId);\n\t\tif (collection) out[String(wpId)] = { id, collection };\n\t}\n\treturn out;\n}\n\nexport const POST: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst denied = requirePerm(user, \"import:execute\");\n\tif (denied) return denied;\n\n\tif (!emdash?.handleContentCreate) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash not configured\", 500);\n\t}\n\n\ttry {\n\t\tconst emdashManifest = await emdash.getManifest();\n\n\t\tconst body = await parseBody(request, wpPluginExecuteBody);\n\t\tif (isParseError(body)) return body;\n\n\t\t// SSRF: reject internal/private network targets. Uses DNS resolution\n\t\t// to catch hostnames that resolve to private addresses.\n\t\ttry {\n\t\t\tawait resolveAndValidateExternalUrl(body.url);\n\t\t} catch (e) {\n\t\t\tconst msg = e instanceof SsrfError ? e.message : \"Invalid URL\";\n\t\t\treturn apiError(\"SSRF_BLOCKED\", msg, 400);\n\t\t}\n\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Zod schema output narrowed to WpPluginImportConfig\n\t\tconst config = body.config as unknown as WpPluginImportConfig;\n\n\t\t// Get the WordPress plugin source\n\t\tconst source = getSource(\"wordpress-plugin\");\n\t\tif (!source) {\n\t\t\treturn apiError(\"NOT_CONFIGURED\", \"WordPress plugin source not available\", 500);\n\t\t}\n\n\t\t// Build the list of post types to fetch\n\t\tconst postTypes = Object.entries(config.postTypeMappings)\n\t\t\t.filter(([_, mapping]) => mapping.enabled)\n\t\t\t.map(([postType]) => postType);\n\n\t\tif (postTypes.length === 0) {\n\t\t\treturn apiError(\"VALIDATION_ERROR\", \"No post types selected for import\", 400);\n\t\t}\n\n\t\tconsole.log(\"[WP Plugin Import] Starting import for:\", body.url);\n\t\tconsole.log(\"[WP Plugin Import] Post types:\", postTypes);\n\n\t\t// Chunked mode: one bounded unit of work per invocation (issue #475).\n\t\tif (body.phase) {\n\t\t\tconst chunk = await runImportPhase(emdash, body, config, postTypes, emdashManifest);\n\t\t\treturn apiSuccess(chunk);\n\t\t}\n\n\t\t// Pre-create taxonomy defs (for custom CPT taxonomies) and terms so\n\t\t// per-post assignments can attach.\n\t\t// Non-fatal: a site whose /taxonomies call fails still gets content.\n\t\tlet taxonomyPlan: TaxonomyImportPlan | undefined;\n\t\tlet taxonomyDefsCreated: string[] = [];\n\t\ttry {\n\t\t\tconst built = await buildTaxonomyPlan(emdash, body.url, body.token, config);\n\t\t\ttaxonomyPlan = built.plan;\n\t\t\ttaxonomyDefsCreated = built.defsCreated;\n\t\t} catch (e) {\n\t\t\tconsole.warn(\"[WP Plugin Import] Taxonomy pre-import failed:\", e);\n\t\t}\n\n\t\t// Import content (including drafts since we have auth)\n\t\tconst { result, contentIdMap, collectionByWpId } = await importContent(\n\t\t\tsource.fetchContent(\n\t\t\t\t{ type: \"url\", url: body.url, token: body.token },\n\t\t\t\t{ postTypes, includeDrafts: true },\n\t\t\t),\n\t\t\tconfig,\n\t\t\temdash,\n\t\t\temdashManifest,\n\t\t\ttaxonomyPlan,\n\t\t);\n\n\t\t// Import navigation menus, resolving item references through the\n\t\t// WP-post-ID -> EmDash-ID map collected during the content pass.\n\t\tif (config.importMenus !== false) {\n\t\t\tawait importMenusInto(result, emdash, body.url, body.token, contentIdMap);\n\t\t}\n\n\t\t// Import comments into EmDash's native comments table, preserving\n\t\t// authors, dates, threading, and approval status.\n\t\t// Non-fatal: older plugin versions have no /comments endpoint.\n\t\ttry {\n\t\t\tconst comments = await fetchPluginComments(body.url, body.token);\n\t\t\tif (comments.length > 0) {\n\t\t\t\tconst commentsResult = await importCommentsFromPlugin(\n\t\t\t\t\tcomments,\n\t\t\t\t\temdash.db,\n\t\t\t\t\tcontentIdMap,\n\t\t\t\t\tcollectionByWpId,\n\t\t\t\t);\n\t\t\t\tresult.comments = {\n\t\t\t\t\timported: commentsResult.imported,\n\t\t\t\t\tskipped: commentsResult.skipped,\n\t\t\t\t};\n\t\t\t\tfor (const commentError of commentsResult.errors) {\n\t\t\t\t\tresult.errors.push({\n\t\t\t\t\t\ttitle: `Comment: ${commentError.comment}`,\n\t\t\t\t\t\terror: commentError.error,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.warn(\"[WP Plugin Import] Comment import failed:\", e);\n\t\t}\n\n\t\t// Apply site identity (title, tagline, logo, favicon) from the source\n\t\t// site's options, replacing the starter template's seed placeholders.\n\t\t// Non-fatal: content is already imported at this point.\n\t\ttry {\n\t\t\tresult.siteSettings = await applySiteSettings(emdash, body.url, body.token, config);\n\t\t} catch (e) {\n\t\t\tconsole.warn(\"[WP Plugin Import] Site settings import failed:\", e);\n\t\t}\n\n\t\tif (taxonomyDefsCreated.length > 0) {\n\t\t\tresult.taxonomiesCreated = taxonomyDefsCreated;\n\t\t}\n\n\t\tconsole.log(\"[WP Plugin Import] Import result:\", JSON.stringify(result, null, 2));\n\n\t\treturn apiSuccess({\n\t\t\tsuccess: true,\n\t\t\tresult,\n\t\t});\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to import from WordPress\", \"WP_PLUGIN_IMPORT_ERROR\");\n\t}\n};\n\n/**\n * Import navigation menus into `result`, resolving item references through\n * the WP-post-ID -> EmDash-ID map collected during the content pass.\n * Non-fatal: older plugin versions have no /menus endpoint.\n */\nasync function importMenusInto(\n\tresult: ImportResult,\n\temdash: EmDashHandlers,\n\turl: string,\n\ttoken: string,\n\tcontentIdMap: Map<number, string>,\n): Promise<void> {\n\ttry {\n\t\tconst menus = await fetchPluginMenus(url, token);\n\t\tif (menus.length > 0) {\n\t\t\tconst menuResult = await importMenusFromPlugin(menus, emdash.db, contentIdMap);\n\t\t\tresult.menus = {\n\t\t\t\tcreated: menuResult.menusCreated,\n\t\t\t\titems: menuResult.itemsCreated,\n\t\t\t};\n\t\t\tfor (const menuError of menuResult.errors) {\n\t\t\t\tresult.errors.push({ title: `Menu: ${menuError.menu}`, error: menuError.error });\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\tconsole.warn(\"[WP Plugin Import] Menu import failed:\", e);\n\t}\n}\n\ntype ExecuteBody = z.infer<typeof wpPluginExecuteBody>;\n\n/** Dispatch one chunk of work for the requested phase. */\nasync function runImportPhase(\n\temdash: EmDashHandlers,\n\tbody: ExecuteBody,\n\tconfig: WpPluginImportConfig,\n\tpostTypes: string[],\n\tmanifest: EmDashManifest,\n): Promise<ChunkResponse> {\n\tswitch (body.phase) {\n\t\tcase \"content\":\n\t\t\treturn runContentChunk(emdash, body, config, postTypes, manifest);\n\t\tcase \"comments\":\n\t\t\treturn runCommentsChunk(emdash, body);\n\t\tcase \"finalize\":\n\t\t\treturn runFinalizePhase(emdash, body, config);\n\t\tcase undefined:\n\t\t\tthrow new Error(\"runImportPhase called without a phase\");\n\t\tdefault:\n\t\t\tbody.phase satisfies never;\n\t\t\tthrow new Error(\"Unknown import phase\");\n\t}\n}\n\n/**\n * Import one page of one post type. The first chunk additionally runs the\n * taxonomy setup (def + term creation); later chunks only reload the\n * lookup maps from the database.\n */\nasync function runContentChunk(\n\temdash: EmDashHandlers,\n\tbody: ExecuteBody,\n\tconfig: WpPluginImportConfig,\n\tpostTypes: string[],\n\tmanifest: EmDashManifest,\n): Promise<ChunkResponse> {\n\tconst cursor = body.cursor ?? { postTypeIndex: 0, page: 1 };\n\tconst postType = postTypes[cursor.postTypeIndex];\n\tif (!postType) {\n\t\treturn { success: true, result: emptyImportResult(), done: true };\n\t}\n\n\tconst isFirstChunk = cursor.postTypeIndex === 0 && cursor.page === 1;\n\tlet taxonomyPlan: TaxonomyImportPlan | undefined;\n\tlet taxonomyDefsCreated: string[] = [];\n\ttry {\n\t\tif (isFirstChunk) {\n\t\t\tconst built = await buildTaxonomyPlan(emdash, body.url, body.token, config);\n\t\t\ttaxonomyPlan = built.plan;\n\t\t\ttaxonomyDefsCreated = built.defsCreated;\n\t\t} else {\n\t\t\ttaxonomyPlan = await loadTaxonomyPlanFromDb(emdash.db);\n\t\t}\n\t} catch (e) {\n\t\tconsole.warn(\"[WP Plugin Import] Taxonomy pre-import failed:\", e);\n\t}\n\n\tconst page = await fetchPluginContentPage({\n\t\tsiteUrl: body.url,\n\t\ttoken: body.token,\n\t\tpostType,\n\t\tpage: cursor.page,\n\t\tperPage: CONTENT_CHUNK_SIZE,\n\t\tincludeDrafts: true,\n\t});\n\n\t// Seed translation-group state from earlier chunks so a translation in\n\t// this page links to its sibling imported three chunks ago.\n\tconst translationGroupMap = new Map(Object.entries(body.translationGroups ?? {}));\n\n\tconst { result, contentIdMap, collectionByWpId } = await importContent(\n\t\tpage.items,\n\t\tconfig,\n\t\temdash,\n\t\tmanifest,\n\t\ttaxonomyPlan,\n\t\ttranslationGroupMap,\n\t);\n\n\tif (taxonomyDefsCreated.length > 0) {\n\t\tresult.taxonomiesCreated = taxonomyDefsCreated;\n\t}\n\n\t// Advance: next page of this post type, first page of the next one,\n\t// or done. An empty last page (totalPages can shrink while paginating\n\t// a live site) still terminates because page >= totalPages.\n\tlet next: ChunkCursor | undefined;\n\tif (cursor.page < page.totalPages) {\n\t\tnext = { postTypeIndex: cursor.postTypeIndex, page: cursor.page + 1 };\n\t} else if (cursor.postTypeIndex + 1 < postTypes.length) {\n\t\tnext = { postTypeIndex: cursor.postTypeIndex + 1, page: 1 };\n\t}\n\n\treturn {\n\t\tsuccess: true,\n\t\tresult,\n\t\tdone: next === undefined,\n\t\tcursor: next,\n\t\tchunk: {\n\t\t\tidMap: serializeIdMap(contentIdMap, collectionByWpId),\n\t\t\ttranslationGroups: Object.fromEntries(translationGroupMap),\n\t\t},\n\t};\n}\n\n/**\n * Import one page of comments (500 per page, ordered by WP comment ID so\n * parents precede children across pages). Requires the accumulated idMap\n * from the content phase; threading roots accumulate in `commentRoots`.\n */\nasync function runCommentsChunk(emdash: EmDashHandlers, body: ExecuteBody): Promise<ChunkResponse> {\n\tconst page = body.cursor?.page ?? 1;\n\tconst result = emptyImportResult();\n\n\tconst { contentIdMap, collectionByWpId } = parseIdMap(body.idMap);\n\tconst rootIds = new Map<number, string>();\n\tfor (const [key, value] of Object.entries(body.commentRoots ?? {})) {\n\t\tconst wpId = Number(key);\n\t\tif (Number.isFinite(wpId)) rootIds.set(wpId, value);\n\t}\n\n\tconst { items, totalPages } = await fetchPluginCommentsPage(body.url, body.token, page);\n\n\tif (items.length > 0) {\n\t\tconst commentsResult = await importCommentsFromPlugin(\n\t\t\titems,\n\t\t\temdash.db,\n\t\t\tcontentIdMap,\n\t\t\tcollectionByWpId,\n\t\t\trootIds,\n\t\t);\n\t\tresult.comments = {\n\t\t\timported: commentsResult.imported,\n\t\t\tskipped: commentsResult.skipped,\n\t\t};\n\t\tfor (const commentError of commentsResult.errors) {\n\t\t\tresult.errors.push({ title: `Comment: ${commentError.comment}`, error: commentError.error });\n\t\t}\n\t\tresult.success = result.errors.length === 0;\n\t}\n\n\tconst done = page >= totalPages;\n\tconst commentRoots: Record<string, string> = {};\n\tfor (const [wpId, id] of rootIds) commentRoots[String(wpId)] = id;\n\n\treturn {\n\t\tsuccess: true,\n\t\tresult,\n\t\tdone,\n\t\tcursor: done ? undefined : { postTypeIndex: 0, page: page + 1 },\n\t\tchunk: { commentRoots },\n\t};\n}\n\n/** Menus + site identity — small, runs as a single closing chunk. */\nasync function runFinalizePhase(\n\temdash: EmDashHandlers,\n\tbody: ExecuteBody,\n\tconfig: WpPluginImportConfig,\n): Promise<ChunkResponse> {\n\tconst result = emptyImportResult();\n\tconst { contentIdMap } = parseIdMap(body.idMap);\n\n\tif (config.importMenus !== false) {\n\t\tawait importMenusInto(result, emdash, body.url, body.token, contentIdMap);\n\t}\n\n\ttry {\n\t\tresult.siteSettings = await applySiteSettings(emdash, body.url, body.token, config);\n\t} catch (e) {\n\t\tconsole.warn(\"[WP Plugin Import] Site settings import failed:\", e);\n\t}\n\n\tresult.success = result.errors.length === 0;\n\treturn { success: true, result, done: true };\n}\n\n/** Fields that should be auto-created if they don't exist */\nconst IMPORT_FIELDS: Array<{\n\tslug: string;\n\tlabel: string;\n\ttype: FieldType;\n\tcheck: (item: NormalizedItem) => boolean;\n}> = [\n\t{\n\t\tslug: \"title\",\n\t\tlabel: \"Title\",\n\t\ttype: \"string\",\n\t\tcheck: () => true,\n\t},\n\t{\n\t\tslug: \"content\",\n\t\tlabel: \"Content\",\n\t\ttype: \"portableText\",\n\t\tcheck: () => true,\n\t},\n\t{\n\t\tslug: \"excerpt\",\n\t\tlabel: \"Excerpt\",\n\t\ttype: \"text\",\n\t\tcheck: (item) => !!item.excerpt,\n\t},\n\t{\n\t\tslug: \"featured_image\",\n\t\tlabel: \"Featured Image\",\n\t\ttype: \"image\",\n\t\tcheck: (item) => !!item.featuredImage,\n\t},\n\t{\n\t\tslug: \"seo_title\",\n\t\tlabel: \"SEO Title\",\n\t\ttype: \"string\",\n\t\tcheck: (item) => !!extractSeo(item).title,\n\t},\n\t{\n\t\tslug: \"seo_description\",\n\t\tlabel: \"SEO Description\",\n\t\ttype: \"text\",\n\t\tcheck: (item) => !!extractSeo(item).description,\n\t},\n];\n\nconst SEO_FIELD_SLUGS = new Set([\"seo_title\", \"seo_description\"]);\n\n/**\n * Coerce a WordPress meta value to an EmDash field type. WP postmeta is\n * stringly typed and inconsistent across posts (the same key can hold\n * \"5\", 5, \"\", or false). Returns `undefined` when the value can't\n * reasonably represent the target type — the caller drops it.\n */\nexport function coerceToFieldType(value: unknown, fieldType: string): unknown {\n\tswitch (fieldType) {\n\t\tcase \"integer\": {\n\t\t\tconst n = typeof value === \"number\" ? value : Number(value);\n\t\t\treturn Number.isInteger(n) ? n : undefined;\n\t\t}\n\t\tcase \"number\": {\n\t\t\tconst n = typeof value === \"number\" ? value : Number(value);\n\t\t\treturn Number.isFinite(n) ? n : undefined;\n\t\t}\n\t\tcase \"boolean\": {\n\t\t\tif (typeof value === \"boolean\") return value;\n\t\t\tif (value === 1 || value === \"1\" || value === \"true\" || value === \"yes\") return true;\n\t\t\tif (value === 0 || value === \"0\" || value === \"false\" || value === \"no\") return false;\n\t\t\treturn undefined;\n\t\t}\n\t\tcase \"datetime\": {\n\t\t\tif (typeof value !== \"string\" && typeof value !== \"number\") return undefined;\n\t\t\tconst d = new Date(value);\n\t\t\treturn Number.isNaN(d.getTime()) ? undefined : d.toISOString();\n\t\t}\n\t\tcase \"json\":\n\t\t\t// Anything serializes; objects/arrays pass through as-is\n\t\t\treturn value;\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"url\":\n\t\tcase \"select\":\n\t\tcase \"slug\":\n\t\tcase \"reference\":\n\t\t\tif (typeof value === \"string\") return value;\n\t\t\tif (typeof value === \"number\") return String(value);\n\t\t\treturn undefined;\n\t\tdefault:\n\t\t\t// Complex types (image, portableText, repeater, multiSelect, file):\n\t\t\t// raw meta can't be coerced reliably -- pass through only if it\n\t\t\t// already has a non-primitive shape.\n\t\t\treturn typeof value === \"object\" ? value : undefined;\n\t}\n}\n\n/**\n * Pull the per-post SEO title/description out of the Yoast / Rank Math\n * blobs the plugin source stashes in `item.meta`. Empty strings mean \"not\n * overridden for this post\" (the plugin exports the raw meta values) and\n * are treated as absent.\n */\nfunction pickSeoValue(blob: unknown, key: string): string | undefined {\n\tif (typeof blob !== \"object\" || blob === null) return undefined;\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- narrowed to non-null object above\n\tconst value = (blob as Record<string, unknown>)[key];\n\treturn typeof value === \"string\" && value.trim() !== \"\" ? value : undefined;\n}\n\nfunction extractSeo(item: NormalizedItem): { title?: string; description?: string } {\n\tconst yoast = item.meta?._yoast;\n\tconst rankmath = item.meta?._rankmath;\n\treturn {\n\t\ttitle: pickSeoValue(yoast, \"title\") ?? pickSeoValue(rankmath, \"title\"),\n\t\tdescription: pickSeoValue(yoast, \"description\") ?? pickSeoValue(rankmath, \"description\"),\n\t};\n}\n\n/**\n * Fetch the site's taxonomies from the plugin API and pre-create all terms,\n * reusing the WXR taxonomy machinery (same def-lookup, idempotency, and\n * collection-filter semantics).\n */\n/** WP built-in taxonomies that either map to seeded defs or are not content taxonomies. */\nconst BUILTIN_TAXONOMIES = new Set([\"category\", \"post_tag\", \"nav_menu\", \"post_format\"]);\n\n/** Mirrors NAME_PATTERN in the taxonomy handler -- names that fail stay in missingTaxonomies. */\nconst TAXONOMY_NAME_PATTERN = /^[a-z][a-z0-9_]*$/;\n\n/**\n * Create EmDash taxonomy defs for custom WP taxonomies (e.g. CPT taxonomies\n * like `company` or `plattform`) that don't exist yet, scoped to the\n * collections the enabled post-type mappings target. Returns the names of\n * the defs created. Runs before term pre-import so the terms and per-post\n * assignments flow through the existing machinery instead of being dropped\n * as `missingTaxonomies`.\n */\nexport async function ensureCustomTaxonomyDefs(\n\tdb: EmDashHandlers[\"db\"],\n\ttaxonomies: Awaited<ReturnType<typeof fetchPluginTaxonomies>>,\n\tconfig: WpPluginImportConfig,\n): Promise<string[]> {\n\tconst created: string[] = [];\n\tfor (const taxonomy of taxonomies) {\n\t\tif (BUILTIN_TAXONOMIES.has(taxonomy.name)) continue;\n\t\tif (taxonomy.terms.length === 0) continue;\n\t\tif (!TAXONOMY_NAME_PATTERN.test(taxonomy.name)) continue;\n\n\t\t// handleTaxonomyCreate's duplicate guard is locale-scoped; check\n\t\t// name-wide existence ourselves to stay idempotent on re-runs.\n\t\tconst existing = await db\n\t\t\t.selectFrom(\"_emdash_taxonomy_defs\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"name\", \"=\", taxonomy.name)\n\t\t\t.executeTakeFirst();\n\t\tif (existing) continue;\n\n\t\t// Scope the def to the collections the WP post types map onto.\n\t\t// Older plugin versions don't send post_types -> empty list = no\n\t\t// collection filter, which the term machinery treats as \"any\".\n\t\tconst collections = (taxonomy.post_types ?? [])\n\t\t\t.map((postType) => config.postTypeMappings[postType])\n\t\t\t.filter((mapping) => mapping?.enabled)\n\t\t\t.map((mapping) => mapping.collection);\n\n\t\tconst result = await handleTaxonomyCreate(db, {\n\t\t\tname: taxonomy.name,\n\t\t\tlabel: taxonomy.label,\n\t\t\tlabelSingular: taxonomy.label_singular,\n\t\t\thierarchical: taxonomy.hierarchical,\n\t\t\tcollections: [...new Set(collections)],\n\t\t});\n\t\tif (result.success) {\n\t\t\tcreated.push(taxonomy.name);\n\t\t} else {\n\t\t\tconsole.warn(\n\t\t\t\t`[WP Plugin Import] Could not create taxonomy '${taxonomy.name}':`,\n\t\t\t\tresult.error.message,\n\t\t\t);\n\t\t}\n\t}\n\treturn created;\n}\n\nasync function buildTaxonomyPlan(\n\temdash: EmDashHandlers,\n\turl: string,\n\ttoken: string,\n\tconfig: WpPluginImportConfig,\n): Promise<{ plan: TaxonomyImportPlan; defsCreated: string[] }> {\n\tconst taxonomies = await fetchPluginTaxonomies(url, token);\n\n\tconst defsCreated = await ensureCustomTaxonomyDefs(emdash.db, taxonomies, config);\n\n\tconst categories: WxrCategory[] = [];\n\tconst tags: WxrTag[] = [];\n\tconst terms: WxrTerm[] = [];\n\n\tfor (const taxonomy of taxonomies) {\n\t\tfor (const term of taxonomy.terms) {\n\t\t\tif (taxonomy.name === \"category\") {\n\t\t\t\tcategories.push({ nicename: term.slug, name: term.name, description: term.description });\n\t\t\t} else if (taxonomy.name === \"post_tag\") {\n\t\t\t\ttags.push({ slug: term.slug, name: term.name, description: term.description });\n\t\t\t} else if (taxonomy.name !== \"nav_menu\" && taxonomy.name !== \"post_format\") {\n\t\t\t\tterms.push({\n\t\t\t\t\tid: term.id,\n\t\t\t\t\ttaxonomy: taxonomy.name,\n\t\t\t\t\tslug: term.slug,\n\t\t\t\t\tname: term.name,\n\t\t\t\t\tdescription: term.description,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tconst plan = await preImportWxrTaxonomies(emdash.db, [], categories, tags, terms, undefined);\n\treturn { plan, defsCreated };\n}\n\n/**\n * Fetch the source site's options and apply its identity (title, tagline,\n * logo, favicon) as EmDash site settings, overwriting seed placeholders.\n * Logo/favicon files are side-loaded into media storage first; the later\n * full media pass dedupes them by content hash.\n *\n * Returns the list of applied setting keys.\n */\nasync function applySiteSettings(\n\temdash: EmDashHandlers,\n\turl: string,\n\ttoken: string,\n\tconfig: WpPluginImportConfig,\n): Promise<string[]> {\n\tconst wantTitle = config.importSiteTitle !== false;\n\tconst wantLogo = config.importLogo !== false;\n\tif (!wantTitle && !wantLogo) return [];\n\n\tconst options = await fetchPluginOptions(url, token);\n\tconst parsed = parseSiteSettingsFromPlugin(options);\n\tif (!wantTitle) {\n\t\tdelete parsed.title;\n\t\tdelete parsed.tagline;\n\t}\n\tif (!wantLogo) {\n\t\tdelete parsed.logo;\n\t\tdelete parsed.favicon;\n\t}\n\n\tconst media: { logoMediaId?: string; faviconMediaId?: string } = {};\n\tif (emdash.storage && (parsed.logo?.url || parsed.favicon?.url)) {\n\t\tconst attachments = [];\n\t\tif (parsed.logo?.url) {\n\t\t\tattachments.push({ id: parsed.logo.id, url: parsed.logo.url });\n\t\t}\n\t\tif (parsed.favicon?.url && parsed.favicon.url !== parsed.logo?.url) {\n\t\t\tattachments.push({ id: parsed.favicon.id, url: parsed.favicon.url });\n\t\t}\n\t\tconst mediaResult = await importMediaWithProgress(\n\t\t\tattachments,\n\t\t\temdash.db,\n\t\t\temdash.storage,\n\t\t\t() => {},\n\t\t);\n\t\tfor (const item of mediaResult.imported) {\n\t\t\tif (item.originalUrl === parsed.logo?.url) {\n\t\t\t\tmedia.logoMediaId = item.mediaId;\n\t\t\t}\n\t\t\tif (item.originalUrl === parsed.favicon?.url) {\n\t\t\t\tmedia.faviconMediaId = item.mediaId;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst settingsResult = await importSiteSettings(parsed, emdash.db, true, media);\n\tfor (const settingError of settingsResult.errors) {\n\t\tconsole.warn(\n\t\t\t`[WP Plugin Import] Site setting \"${settingError.setting}\" failed:`,\n\t\t\tsettingError.error,\n\t\t);\n\t}\n\treturn settingsResult.applied;\n}\n\n/**\n * Adapt a NormalizedItem's taxonomy assignments to the WxrPost shape the\n * shared attach helper consumes.\n */\nfunction toWxrAssignments(item: NormalizedItem): WxrPost {\n\treturn {\n\t\tcategories: item.categories ?? [],\n\t\ttags: item.tags ?? [],\n\t\tcustomTaxonomies: item.customTaxonomies\n\t\t\t? new Map(Object.entries(item.customTaxonomies))\n\t\t\t: undefined,\n\t\tmeta: new Map(),\n\t};\n}\n\n/** Exported for tests (field auto-creation regression coverage). */\nexport async function importContent(\n\titems: AsyncIterable<NormalizedItem> | Iterable<NormalizedItem>,\n\tconfig: WpPluginImportConfig,\n\temdash: EmDashHandlers,\n\tmanifest: EmDashManifest,\n\ttaxonomyPlan: TaxonomyImportPlan | undefined,\n\tseedTranslationGroups?: Map<string, string>,\n): Promise<{\n\tresult: ImportResult;\n\tcontentIdMap: Map<number, string>;\n\tcollectionByWpId: Map<number, string>;\n}> {\n\tconst result: ImportResult = {\n\t\tsuccess: true,\n\t\timported: 0,\n\t\tskipped: 0,\n\t\terrors: [],\n\t\tbyCollection: {},\n\t};\n\n\t// WP post ID -> EmDash content ID, used to resolve menu item references\n\tconst contentIdMap = new Map<number, string>();\n\n\t// WP post ID -> EmDash collection slug, used by the comment import\n\tconst collectionByWpId = new Map<number, string>();\n\n\t// Create content repository for checking existing items\n\tconst contentRepo = new ContentRepository(emdash.db);\n\tconst bylineRepo = new BylineRepository(emdash.db);\n\tconst bylineCache = new Map<string, string>();\n\tconst schemaRegistry = new SchemaRegistry(emdash.db);\n\n\t// Track which (collection, field) pairs have been ensured. Keyed per\n\t// field, not per collection: a field like seo_title may first be\n\t// needed by the 30th item, and gating on the first item would skip it.\n\tconst ensuredFields = new Set<string>();\n\n\t// Field slug -> type per collection, for mapping custom meta/ACF onto\n\t// real fields (and coercing values to the field's type)\n\tconst fieldTypesByCollection = new Map<string, Map<string, string>>();\n\n\t// Track source translationGroup -> EmDash item ID for translation linking.\n\t// Maps source-side translation group ID to the EmDash ID of the first item\n\t// imported for that group (the default-locale item). The chunked import\n\t// seeds this from earlier chunks so groups can span page boundaries.\n\tconst translationGroupMap = seedTranslationGroups ?? new Map<string, string>();\n\n\tfor await (const item of items) {\n\t\tconsole.log(\"[WP Plugin Import] Processing item:\", {\n\t\t\tsourceId: item.sourceId,\n\t\t\ttitle: item.title,\n\t\t\tpostType: item.postType,\n\t\t\tstatus: item.status,\n\t\t\tcontentBlocks: Array.isArray(item.content) ? item.content.length : 0,\n\t\t\tfeaturedImage: item.featuredImage,\n\t\t\tlocale: item.locale,\n\t\t\ttranslationGroup: item.translationGroup,\n\t\t});\n\n\t\tconst mapping = config.postTypeMappings[item.postType];\n\n\t\t// Skip if not mapped or disabled\n\t\tif (!mapping || !mapping.enabled) {\n\t\t\tresult.skipped++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst collection = mapping.collection;\n\n\t\t// Check if collection exists in manifest\n\t\tif (!manifest?.collections[collection]) {\n\t\t\tresult.errors.push({\n\t\t\t\ttitle: item.title || \"Untitled\",\n\t\t\t\terror: `Collection \"${collection}\" does not exist`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\t// Ensure required fields exist in the collection schema. Checked\n\t\t\t// per field and item (not once per collection): whether a field\n\t\t\t// is needed depends on the item — the first post may have no SEO\n\t\t\t// override while a later one does.\n\t\t\tfor (const field of IMPORT_FIELDS) {\n\t\t\t\tif (config.importSeo === false && SEO_FIELD_SLUGS.has(field.slug)) continue;\n\t\t\t\tconst ensureKey = `${collection}:${field.slug}`;\n\t\t\t\tif (ensuredFields.has(ensureKey) || !field.check(item)) continue;\n\t\t\t\tensuredFields.add(ensureKey);\n\t\t\t\tconst existingField = await schemaRegistry.getField(collection, field.slug);\n\t\t\t\tif (!existingField) {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`[WP Plugin Import] Creating missing field \"${field.slug}\" in collection \"${collection}\"`,\n\t\t\t\t\t);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait schemaRegistry.createField(collection, {\n\t\t\t\t\t\t\tslug: field.slug,\n\t\t\t\t\t\t\tlabel: field.label,\n\t\t\t\t\t\t\ttype: field.type,\n\t\t\t\t\t\t\trequired: false,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tfieldTypesByCollection.get(collection)?.set(field.slug, field.type);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// Field might already exist from concurrent creation\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`[WP Plugin Import] Field \"${field.slug}\" creation skipped:`,\n\t\t\t\t\t\t\te instanceof Error ? e.message : e,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Load the collection's field types once, so custom meta / ACF\n\t\t\t// values can land in matching schema fields (created by the\n\t\t\t// prepare step or already present).\n\t\t\tlet fieldTypes = fieldTypesByCollection.get(collection);\n\t\t\tif (!fieldTypes) {\n\t\t\t\tfieldTypes = new Map<string, string>();\n\t\t\t\tconst collectionDef = await schemaRegistry.getCollection(collection);\n\t\t\t\tif (collectionDef) {\n\t\t\t\t\tfor (const field of await schemaRegistry.listFields(collectionDef.id)) {\n\t\t\t\t\t\tfieldTypes.set(field.slug, field.type);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfieldTypesByCollection.set(collection, fieldTypes);\n\t\t\t}\n\n\t\t\t// Generate slug from item slug or title\n\t\t\tconst slug = item.slug || slugify(item.title || `post-${item.sourceId}`);\n\n\t\t\t// Check if already exists (idempotency) — locale-aware lookup\n\t\t\tif (config.skipExisting) {\n\t\t\t\tconst existing = await contentRepo.findBySlug(collection, slug, item.locale);\n\t\t\t\tif (existing) {\n\t\t\t\t\t// Still track the translation group mapping for later items\n\t\t\t\t\tif (item.translationGroup) {\n\t\t\t\t\t\ttranslationGroupMap.set(item.translationGroup, existing.id);\n\t\t\t\t\t}\n\t\t\t\t\t// Menus and comments may reference this post even when we skip it\n\t\t\t\t\tconst wpId = Number(item.sourceId);\n\t\t\t\t\tif (Number.isFinite(wpId)) {\n\t\t\t\t\t\tcontentIdMap.set(wpId, existing.id);\n\t\t\t\t\t\tcollectionByWpId.set(wpId, collection);\n\t\t\t\t\t}\n\t\t\t\t\tresult.skipped++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Map WordPress status to EmDash status\n\t\t\tconst status = mapStatus(item.status);\n\n\t\t\t// Build data object - add all applicable fields\n\t\t\tconst data: Record<string, unknown> = {};\n\n\t\t\t// Add standard fields\n\t\t\tdata.title = item.title || \"Untitled\";\n\t\t\tdata.content = item.content;\n\n\t\t\tif (item.excerpt) {\n\t\t\t\tdata.excerpt = item.excerpt;\n\t\t\t}\n\t\t\tif (item.featuredImage) {\n\t\t\t\tdata.featured_image = item.featuredImage;\n\t\t\t\tconsole.log(\"[WP Plugin Import] Adding featured_image:\", item.featuredImage);\n\t\t\t}\n\n\t\t\t// Per-post SEO overrides from Yoast / Rank Math\n\t\t\tif (config.importSeo !== false) {\n\t\t\t\tconst seo = extractSeo(item);\n\t\t\t\tif (seo.title) {\n\t\t\t\t\tdata.seo_title = seo.title;\n\t\t\t\t}\n\t\t\t\tif (seo.description) {\n\t\t\t\t\tdata.seo_description = seo.description;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Map ACF values and custom meta onto schema fields with the same\n\t\t\t// (sanitized) slug — the same sanitization the analysis applied\n\t\t\t// when suggesting the fields, so keys like `event-date` land in\n\t\t\t// the `event_date` field the prepare step created. Values without\n\t\t\t// a matching field are dropped -- the user controls the schema,\n\t\t\t// we don't invent fields per meta key. Values are coerced to the\n\t\t\t// field's type (the analysis inferred it from ONE sample; real\n\t\t\t// values vary) and dropped when incoercible, so one odd meta\n\t\t\t// value can't fail the whole item.\n\t\t\tconst assignMetaValue = (key: string, value: unknown) => {\n\t\t\t\tif (value === null || value === \"\") return;\n\t\t\t\tconst fieldSlug = sanitizeFieldSlug(key);\n\t\t\t\tconst fieldType = fieldTypes.get(fieldSlug);\n\t\t\t\tif (!fieldType || fieldSlug in data) return;\n\t\t\t\tconst coerced = coerceToFieldType(value, fieldType);\n\t\t\t\tif (coerced !== undefined) {\n\t\t\t\t\tdata[fieldSlug] = coerced;\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst acf = item.meta?._acf;\n\t\t\tif (typeof acf === \"object\" && acf !== null) {\n\t\t\t\tfor (const [key, value] of Object.entries(acf)) {\n\t\t\t\t\tassignMetaValue(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (item.meta) {\n\t\t\t\tfor (const [key, value] of Object.entries(item.meta)) {\n\t\t\t\t\t// Underscore keys are WP-internal or handled above (_acf/_yoast/_rankmath)\n\t\t\t\t\tif (key.startsWith(\"_\")) continue;\n\t\t\t\t\tassignMetaValue(key, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Resolve author ID from mappings\n\t\t\tlet authorId: string | undefined;\n\t\t\tif (config.authorMappings && item.author) {\n\t\t\t\tconst mappedUserId = config.authorMappings[item.author];\n\t\t\t\tif (mappedUserId !== undefined && mappedUserId !== null) {\n\t\t\t\t\tauthorId = mappedUserId;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst bylineId = await resolveImportByline(\n\t\t\t\titem.author,\n\t\t\t\titem.author, // display name fallback is the login\n\t\t\t\tauthorId,\n\t\t\t\tbylineRepo,\n\t\t\t\tbylineCache,\n\t\t\t);\n\n\t\t\t// Resolve translation link: if this item has a translationGroup and\n\t\t\t// we've already imported another item in the same group, link them.\n\t\t\tlet translationOf: string | undefined;\n\t\t\tif (item.translationGroup) {\n\t\t\t\tconst existingGroupItem = translationGroupMap.get(item.translationGroup);\n\t\t\t\tif (existingGroupItem) {\n\t\t\t\t\ttranslationOf = existingGroupItem;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Preserve original dates from the source\n\t\t\tconst itemDateTime = item.date?.getTime();\n\t\t\tconst createdAt =\n\t\t\t\titemDateTime !== undefined && !Number.isNaN(itemDateTime)\n\t\t\t\t\t? item.date.toISOString()\n\t\t\t\t\t: undefined;\n\t\t\tconst publishedAt = status === \"published\" && createdAt ? createdAt : undefined;\n\n\t\t\t// Create the content item\n\t\t\tconst createResult = await emdash.handleContentCreate(collection, {\n\t\t\t\tdata,\n\t\t\t\tslug,\n\t\t\t\tstatus,\n\t\t\t\tauthorId,\n\t\t\t\tbylines: bylineId ? [{ bylineId }] : undefined,\n\t\t\t\tlocale: item.locale,\n\t\t\t\ttranslationOf,\n\t\t\t\tcreatedAt,\n\t\t\t\tpublishedAt,\n\t\t\t});\n\n\t\t\tif (createResult.success) {\n\t\t\t\tresult.imported++;\n\t\t\t\tresult.byCollection[collection] = (result.byCollection[collection] || 0) + 1;\n\n\t\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- create handler returns { item, _rev }\n\t\t\t\tconst createdData = createResult.data as { item?: { id?: string } } | undefined;\n\t\t\t\tconst createdId = createdData?.item?.id;\n\n\t\t\t\tif (createdId) {\n\t\t\t\t\tconst wpId = Number(item.sourceId);\n\t\t\t\t\tif (Number.isFinite(wpId)) {\n\t\t\t\t\t\tcontentIdMap.set(wpId, createdId);\n\t\t\t\t\t\tcollectionByWpId.set(wpId, collection);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Attach category/tag/custom-taxonomy assignments\n\t\t\t\t\tif (taxonomyPlan) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst attached = await attachPostTaxonomies(\n\t\t\t\t\t\t\t\temdash.db,\n\t\t\t\t\t\t\t\tcollection,\n\t\t\t\t\t\t\t\tcreatedId,\n\t\t\t\t\t\t\t\ttoWxrAssignments(item),\n\t\t\t\t\t\t\t\ttaxonomyPlan,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (attached > 0) {\n\t\t\t\t\t\t\t\tresult.taxonomyAssignments = (result.taxonomyAssignments ?? 0) + attached;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\tconsole.warn(`[WP Plugin Import] Taxonomy attach failed for \"${slug}\":`, e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Track translation group: first item in a group establishes the mapping\n\t\t\t\tif (item.translationGroup && !translationGroupMap.has(item.translationGroup) && createdId) {\n\t\t\t\t\ttranslationGroupMap.set(item.translationGroup, createdId);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresult.errors.push({\n\t\t\t\t\ttitle: item.title || \"Untitled\",\n\t\t\t\t\terror:\n\t\t\t\t\t\ttypeof createResult.error === \"object\" && createResult.error !== null\n\t\t\t\t\t\t\t? (createResult.error as { message?: string }).message || \"Unknown error\"\n\t\t\t\t\t\t\t: String(createResult.error),\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(`Import error for \"${item.title || \"Untitled\"}\":`, error);\n\t\t\tresult.errors.push({\n\t\t\t\ttitle: item.title || \"Untitled\",\n\t\t\t\terror: error instanceof Error && error.message ? error.message : \"Failed to import item\",\n\t\t\t});\n\t\t}\n\t}\n\n\tif (taxonomyPlan && taxonomyPlan.missingTaxonomies.length > 0) {\n\t\tresult.missingTaxonomies = taxonomyPlan.missingTaxonomies;\n\t}\n\n\tresult.success = result.errors.length === 0;\n\treturn { result, contentIdMap, collectionByWpId };\n}\n\nfunction mapStatus(wpStatus: string | undefined): string {\n\tswitch (wpStatus) {\n\t\tcase \"publish\":\n\t\t\treturn \"published\";\n\t\tcase \"draft\":\n\t\t\treturn \"draft\";\n\t\tcase \"pending\":\n\t\t\treturn \"draft\";\n\t\tcase \"private\":\n\t\t\treturn \"draft\";\n\t\tdefault:\n\t\t\treturn \"draft\";\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"execute.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/import/wordpress-plugin/execute.ts"],"sourcesContent":["/**\n * WordPress Plugin execute import endpoint\n *\n * POST /_emdash/api/import/wordpress-plugin/execute\n *\n * Imports content from WordPress via EmDash Exporter plugin API.\n */\n\nimport type { APIRoute } from \"astro\";\nimport {\n\tContentRepository,\n\tSchemaRegistry,\n\ttype WxrCategory,\n\ttype WxrPost,\n\ttype WxrTag,\n\ttype WxrTerm,\n} from \"emdash\";\nimport type { z } from \"zod\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { handleTaxonomyCreate } from \"#api/handlers/taxonomies.js\";\nimport { isParseError, parseBody } from \"#api/parse.js\";\nimport { wpPluginExecuteBody } from \"#api/schemas.js\";\nimport { BylineRepository } from \"#db/repositories/byline.js\";\nimport { importCommentsFromPlugin } from \"#import/comments.js\";\nimport { getSource } from \"#import/index.js\";\nimport { importMenusFromPlugin } from \"#import/menus.js\";\nimport { importSiteSettings, parseSiteSettingsFromPlugin } from \"#import/settings.js\";\nimport {\n\tfetchPluginComments,\n\tfetchPluginCommentsPage,\n\tfetchPluginContentPage,\n\tfetchPluginMenus,\n\tfetchPluginOptions,\n\tfetchPluginTaxonomies,\n} from \"#import/sources/wordpress-plugin.js\";\nimport { resolveAndValidateExternalUrl, SsrfError } from \"#import/ssrf.js\";\nimport type { ImportConfig, ImportResult, NormalizedItem } from \"#import/types.js\";\nimport { resolveImportByline, sanitizeFieldSlug } from \"#import/utils.js\";\nimport {\n\tattachPostTaxonomies,\n\tloadTaxonomyPlanFromDb,\n\tpreImportWxrTaxonomies,\n\ttype TaxonomyImportPlan,\n} from \"#import/wxr-taxonomies.js\";\nimport type { FieldType } from \"#schema/types.js\";\nimport type { EmDashHandlers, EmDashManifest } from \"#types\";\nimport { slugify } from \"#utils/slugify.js\";\n\nimport { importMediaWithProgress } from \"../wordpress/media.js\";\n\nexport const prerender = false;\n\nexport interface WpPluginImportConfig extends ImportConfig {\n\t/** Author mappings (WP author login -> EmDash user ID) */\n\tauthorMappings?: Record<string, string | null>;\n\t/** Wizard toggles. Absent means enabled (older admins don't send them). */\n\timportMenus?: boolean;\n\timportSiteTitle?: boolean;\n\timportLogo?: boolean;\n\timportSeo?: boolean;\n}\n\nexport interface WpPluginImportResponse {\n\tsuccess: boolean;\n\tresult?: ImportResult;\n\terror?: { message: string };\n}\n\n// =============================================================================\n// Chunked mode (issue #475)\n//\n// A single-invocation import of a large site exceeds Cloudflare Worker\n// resource limits (CPU time, subrequests). In chunked mode the admin drives\n// the import as a loop of bounded requests: one WP content page per call,\n// then paginated comments, then a small finalize step (menus + site\n// identity). Cross-chunk state — the WP-ID -> EmDash-ID map, translation\n// groups, comment threading roots — is accumulated by the client and sent\n// back with each request, so the server stays stateless and an aborted\n// import simply re-runs (skipExisting rebuilds the map while skipping work).\n// =============================================================================\n\n/** Posts per content chunk. 50 posts ≈ a few hundred D1 ops per invocation. */\nconst CONTENT_CHUNK_SIZE = 50;\n\ninterface ChunkCursor {\n\tpostTypeIndex: number;\n\tpage: number;\n}\n\n/** Per-chunk response payload: partial result + state the client must carry. */\ninterface ChunkResponse {\n\tsuccess: boolean;\n\tresult: ImportResult;\n\tdone: boolean;\n\tcursor?: ChunkCursor;\n\tchunk?: {\n\t\tidMap?: Record<string, { id: string; collection: string }>;\n\t\ttranslationGroups?: Record<string, string>;\n\t\tcommentRoots?: Record<string, string>;\n\t};\n}\n\nfunction emptyImportResult(): ImportResult {\n\treturn { success: true, imported: 0, skipped: 0, errors: [], byCollection: {} };\n}\n\nfunction parseIdMap(idMap: Record<string, { id: string; collection: string }> | undefined): {\n\tcontentIdMap: Map<number, string>;\n\tcollectionByWpId: Map<number, string>;\n} {\n\tconst contentIdMap = new Map<number, string>();\n\tconst collectionByWpId = new Map<number, string>();\n\tfor (const [key, value] of Object.entries(idMap ?? {})) {\n\t\tconst wpId = Number(key);\n\t\tif (!Number.isFinite(wpId)) continue;\n\t\tcontentIdMap.set(wpId, value.id);\n\t\tcollectionByWpId.set(wpId, value.collection);\n\t}\n\treturn { contentIdMap, collectionByWpId };\n}\n\nfunction serializeIdMap(\n\tcontentIdMap: Map<number, string>,\n\tcollectionByWpId: Map<number, string>,\n): Record<string, { id: string; collection: string }> {\n\tconst out: Record<string, { id: string; collection: string }> = {};\n\tfor (const [wpId, id] of contentIdMap) {\n\t\tconst collection = collectionByWpId.get(wpId);\n\t\tif (collection) out[String(wpId)] = { id, collection };\n\t}\n\treturn out;\n}\n\nexport const POST: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst denied = requirePerm(user, \"import:execute\");\n\tif (denied) return denied;\n\n\tif (!emdash?.handleContentCreate) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash not configured\", 500);\n\t}\n\n\ttry {\n\t\tconst emdashManifest = await emdash.getManifest();\n\n\t\tconst body = await parseBody(request, wpPluginExecuteBody);\n\t\tif (isParseError(body)) return body;\n\n\t\t// SSRF: reject internal/private network targets. Uses DNS resolution\n\t\t// to catch hostnames that resolve to private addresses.\n\t\ttry {\n\t\t\tawait resolveAndValidateExternalUrl(body.url);\n\t\t} catch (e) {\n\t\t\tconst msg = e instanceof SsrfError ? e.message : \"Invalid URL\";\n\t\t\treturn apiError(\"SSRF_BLOCKED\", msg, 400);\n\t\t}\n\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Zod schema output narrowed to WpPluginImportConfig\n\t\tconst config = body.config as unknown as WpPluginImportConfig;\n\n\t\t// Get the WordPress plugin source\n\t\tconst source = getSource(\"wordpress-plugin\");\n\t\tif (!source) {\n\t\t\treturn apiError(\"NOT_CONFIGURED\", \"WordPress plugin source not available\", 500);\n\t\t}\n\n\t\t// Build the list of post types to fetch\n\t\tconst postTypes = Object.entries(config.postTypeMappings)\n\t\t\t.filter(([_, mapping]) => mapping.enabled)\n\t\t\t.map(([postType]) => postType);\n\n\t\tif (postTypes.length === 0) {\n\t\t\treturn apiError(\"VALIDATION_ERROR\", \"No post types selected for import\", 400);\n\t\t}\n\n\t\tconsole.log(\"[WP Plugin Import] Starting import for:\", body.url);\n\t\tconsole.log(\"[WP Plugin Import] Post types:\", postTypes);\n\n\t\t// Chunked mode: one bounded unit of work per invocation (issue #475).\n\t\tif (body.phase) {\n\t\t\tconst chunk = await runImportPhase(emdash, body, config, postTypes, emdashManifest);\n\t\t\treturn apiSuccess(chunk);\n\t\t}\n\n\t\t// Pre-create taxonomy defs (for custom CPT taxonomies) and terms so\n\t\t// per-post assignments can attach.\n\t\t// Non-fatal: a site whose /taxonomies call fails still gets content.\n\t\tlet taxonomyPlan: TaxonomyImportPlan | undefined;\n\t\tlet taxonomyDefsCreated: string[] = [];\n\t\ttry {\n\t\t\tconst built = await buildTaxonomyPlan(emdash, body.url, body.token, config);\n\t\t\ttaxonomyPlan = built.plan;\n\t\t\ttaxonomyDefsCreated = built.defsCreated;\n\t\t} catch (e) {\n\t\t\tconsole.warn(\"[WP Plugin Import] Taxonomy pre-import failed:\", e);\n\t\t}\n\n\t\t// Import content (including drafts since we have auth)\n\t\tconst { result, contentIdMap, collectionByWpId } = await importContent(\n\t\t\tsource.fetchContent(\n\t\t\t\t{ type: \"url\", url: body.url, token: body.token },\n\t\t\t\t{ postTypes, includeDrafts: true },\n\t\t\t),\n\t\t\tconfig,\n\t\t\temdash,\n\t\t\temdashManifest,\n\t\t\ttaxonomyPlan,\n\t\t);\n\n\t\t// Import navigation menus, resolving item references through the\n\t\t// WP-post-ID -> EmDash-ID map collected during the content pass.\n\t\tif (config.importMenus !== false) {\n\t\t\tawait importMenusInto(result, emdash, body.url, body.token, contentIdMap);\n\t\t}\n\n\t\t// Import comments into EmDash's native comments table, preserving\n\t\t// authors, dates, threading, and approval status.\n\t\t// Non-fatal: older plugin versions have no /comments endpoint.\n\t\ttry {\n\t\t\tconst comments = await fetchPluginComments(body.url, body.token);\n\t\t\tif (comments.length > 0) {\n\t\t\t\tconst commentsResult = await importCommentsFromPlugin(\n\t\t\t\t\tcomments,\n\t\t\t\t\temdash.db,\n\t\t\t\t\tcontentIdMap,\n\t\t\t\t\tcollectionByWpId,\n\t\t\t\t);\n\t\t\t\tresult.comments = {\n\t\t\t\t\timported: commentsResult.imported,\n\t\t\t\t\tskipped: commentsResult.skipped,\n\t\t\t\t};\n\t\t\t\tfor (const commentError of commentsResult.errors) {\n\t\t\t\t\tresult.errors.push({\n\t\t\t\t\t\ttitle: `Comment: ${commentError.comment}`,\n\t\t\t\t\t\terror: commentError.error,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.warn(\"[WP Plugin Import] Comment import failed:\", e);\n\t\t}\n\n\t\t// Apply site identity (title, tagline, logo, favicon) from the source\n\t\t// site's options, replacing the starter template's seed placeholders.\n\t\t// Non-fatal: content is already imported at this point.\n\t\ttry {\n\t\t\tresult.siteSettings = await applySiteSettings(emdash, body.url, body.token, config);\n\t\t} catch (e) {\n\t\t\tconsole.warn(\"[WP Plugin Import] Site settings import failed:\", e);\n\t\t}\n\n\t\tif (taxonomyDefsCreated.length > 0) {\n\t\t\tresult.taxonomiesCreated = taxonomyDefsCreated;\n\t\t}\n\n\t\tconsole.log(\"[WP Plugin Import] Import result:\", JSON.stringify(result, null, 2));\n\n\t\treturn apiSuccess({\n\t\t\tsuccess: true,\n\t\t\tresult,\n\t\t});\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to import from WordPress\", \"WP_PLUGIN_IMPORT_ERROR\");\n\t}\n};\n\n/**\n * Import navigation menus into `result`, resolving item references through\n * the WP-post-ID -> EmDash-ID map collected during the content pass.\n * Non-fatal: older plugin versions have no /menus endpoint.\n */\nasync function importMenusInto(\n\tresult: ImportResult,\n\temdash: EmDashHandlers,\n\turl: string,\n\ttoken: string,\n\tcontentIdMap: Map<number, string>,\n): Promise<void> {\n\ttry {\n\t\tconst menus = await fetchPluginMenus(url, token);\n\t\tif (menus.length > 0) {\n\t\t\tconst menuResult = await importMenusFromPlugin(menus, emdash.db, contentIdMap);\n\t\t\tresult.menus = {\n\t\t\t\tcreated: menuResult.menusCreated,\n\t\t\t\titems: menuResult.itemsCreated,\n\t\t\t};\n\t\t\tfor (const menuError of menuResult.errors) {\n\t\t\t\tresult.errors.push({ title: `Menu: ${menuError.menu}`, error: menuError.error });\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\tconsole.warn(\"[WP Plugin Import] Menu import failed:\", e);\n\t}\n}\n\ntype ExecuteBody = z.infer<typeof wpPluginExecuteBody>;\n\n/** Dispatch one chunk of work for the requested phase. */\nasync function runImportPhase(\n\temdash: EmDashHandlers,\n\tbody: ExecuteBody,\n\tconfig: WpPluginImportConfig,\n\tpostTypes: string[],\n\tmanifest: EmDashManifest,\n): Promise<ChunkResponse> {\n\tswitch (body.phase) {\n\t\tcase \"content\":\n\t\t\treturn runContentChunk(emdash, body, config, postTypes, manifest);\n\t\tcase \"comments\":\n\t\t\treturn runCommentsChunk(emdash, body);\n\t\tcase \"finalize\":\n\t\t\treturn runFinalizePhase(emdash, body, config);\n\t\tcase undefined:\n\t\t\tthrow new Error(\"runImportPhase called without a phase\");\n\t\tdefault:\n\t\t\tbody.phase satisfies never;\n\t\t\tthrow new Error(\"Unknown import phase\");\n\t}\n}\n\n/**\n * Import one page of one post type. The first chunk additionally runs the\n * taxonomy setup (def + term creation); later chunks only reload the\n * lookup maps from the database.\n */\nasync function runContentChunk(\n\temdash: EmDashHandlers,\n\tbody: ExecuteBody,\n\tconfig: WpPluginImportConfig,\n\tpostTypes: string[],\n\tmanifest: EmDashManifest,\n): Promise<ChunkResponse> {\n\tconst cursor = body.cursor ?? { postTypeIndex: 0, page: 1 };\n\tconst postType = postTypes[cursor.postTypeIndex];\n\tif (!postType) {\n\t\treturn { success: true, result: emptyImportResult(), done: true };\n\t}\n\n\tconst isFirstChunk = cursor.postTypeIndex === 0 && cursor.page === 1;\n\tlet taxonomyPlan: TaxonomyImportPlan | undefined;\n\tlet taxonomyDefsCreated: string[] = [];\n\ttry {\n\t\tif (isFirstChunk) {\n\t\t\tconst built = await buildTaxonomyPlan(emdash, body.url, body.token, config);\n\t\t\ttaxonomyPlan = built.plan;\n\t\t\ttaxonomyDefsCreated = built.defsCreated;\n\t\t} else {\n\t\t\ttaxonomyPlan = await loadTaxonomyPlanFromDb(emdash.db);\n\t\t}\n\t} catch (e) {\n\t\tconsole.warn(\"[WP Plugin Import] Taxonomy pre-import failed:\", e);\n\t}\n\n\tconst page = await fetchPluginContentPage({\n\t\tsiteUrl: body.url,\n\t\ttoken: body.token,\n\t\tpostType,\n\t\tpage: cursor.page,\n\t\tperPage: CONTENT_CHUNK_SIZE,\n\t\tincludeDrafts: true,\n\t});\n\n\t// Seed translation-group state from earlier chunks so a translation in\n\t// this page links to its sibling imported three chunks ago.\n\tconst translationGroupMap = new Map(Object.entries(body.translationGroups ?? {}));\n\n\tconst { result, contentIdMap, collectionByWpId } = await importContent(\n\t\tpage.items,\n\t\tconfig,\n\t\temdash,\n\t\tmanifest,\n\t\ttaxonomyPlan,\n\t\ttranslationGroupMap,\n\t);\n\n\tif (taxonomyDefsCreated.length > 0) {\n\t\tresult.taxonomiesCreated = taxonomyDefsCreated;\n\t}\n\n\t// Advance: next page of this post type, first page of the next one,\n\t// or done. An empty last page (totalPages can shrink while paginating\n\t// a live site) still terminates because page >= totalPages.\n\tlet next: ChunkCursor | undefined;\n\tif (cursor.page < page.totalPages) {\n\t\tnext = { postTypeIndex: cursor.postTypeIndex, page: cursor.page + 1 };\n\t} else if (cursor.postTypeIndex + 1 < postTypes.length) {\n\t\tnext = { postTypeIndex: cursor.postTypeIndex + 1, page: 1 };\n\t}\n\n\treturn {\n\t\tsuccess: true,\n\t\tresult,\n\t\tdone: next === undefined,\n\t\tcursor: next,\n\t\tchunk: {\n\t\t\tidMap: serializeIdMap(contentIdMap, collectionByWpId),\n\t\t\ttranslationGroups: Object.fromEntries(translationGroupMap),\n\t\t},\n\t};\n}\n\n/**\n * Import one page of comments (500 per page, ordered by WP comment ID so\n * parents precede children across pages). Requires the accumulated idMap\n * from the content phase; threading roots accumulate in `commentRoots`.\n */\nasync function runCommentsChunk(emdash: EmDashHandlers, body: ExecuteBody): Promise<ChunkResponse> {\n\tconst page = body.cursor?.page ?? 1;\n\tconst result = emptyImportResult();\n\n\tconst { contentIdMap, collectionByWpId } = parseIdMap(body.idMap);\n\tconst rootIds = new Map<number, string>();\n\tfor (const [key, value] of Object.entries(body.commentRoots ?? {})) {\n\t\tconst wpId = Number(key);\n\t\tif (Number.isFinite(wpId)) rootIds.set(wpId, value);\n\t}\n\n\tconst { items, totalPages } = await fetchPluginCommentsPage(body.url, body.token, page);\n\n\tif (items.length > 0) {\n\t\tconst commentsResult = await importCommentsFromPlugin(\n\t\t\titems,\n\t\t\temdash.db,\n\t\t\tcontentIdMap,\n\t\t\tcollectionByWpId,\n\t\t\trootIds,\n\t\t);\n\t\tresult.comments = {\n\t\t\timported: commentsResult.imported,\n\t\t\tskipped: commentsResult.skipped,\n\t\t};\n\t\tfor (const commentError of commentsResult.errors) {\n\t\t\tresult.errors.push({ title: `Comment: ${commentError.comment}`, error: commentError.error });\n\t\t}\n\t\tresult.success = result.errors.length === 0;\n\t}\n\n\tconst done = page >= totalPages;\n\tconst commentRoots: Record<string, string> = {};\n\tfor (const [wpId, id] of rootIds) commentRoots[String(wpId)] = id;\n\n\treturn {\n\t\tsuccess: true,\n\t\tresult,\n\t\tdone,\n\t\tcursor: done ? undefined : { postTypeIndex: 0, page: page + 1 },\n\t\tchunk: { commentRoots },\n\t};\n}\n\n/** Menus + site identity — small, runs as a single closing chunk. */\nasync function runFinalizePhase(\n\temdash: EmDashHandlers,\n\tbody: ExecuteBody,\n\tconfig: WpPluginImportConfig,\n): Promise<ChunkResponse> {\n\tconst result = emptyImportResult();\n\tconst { contentIdMap } = parseIdMap(body.idMap);\n\n\tif (config.importMenus !== false) {\n\t\tawait importMenusInto(result, emdash, body.url, body.token, contentIdMap);\n\t}\n\n\ttry {\n\t\tresult.siteSettings = await applySiteSettings(emdash, body.url, body.token, config);\n\t} catch (e) {\n\t\tconsole.warn(\"[WP Plugin Import] Site settings import failed:\", e);\n\t}\n\n\tresult.success = result.errors.length === 0;\n\treturn { success: true, result, done: true };\n}\n\n/** Fields that should be auto-created if they don't exist */\nconst IMPORT_FIELDS: Array<{\n\tslug: string;\n\tlabel: string;\n\ttype: FieldType;\n\tcheck: (item: NormalizedItem) => boolean;\n}> = [\n\t{\n\t\tslug: \"title\",\n\t\tlabel: \"Title\",\n\t\ttype: \"string\",\n\t\tcheck: () => true,\n\t},\n\t{\n\t\tslug: \"content\",\n\t\tlabel: \"Content\",\n\t\ttype: \"portableText\",\n\t\tcheck: () => true,\n\t},\n\t{\n\t\tslug: \"excerpt\",\n\t\tlabel: \"Excerpt\",\n\t\ttype: \"text\",\n\t\tcheck: (item) => !!item.excerpt,\n\t},\n\t{\n\t\tslug: \"featured_image\",\n\t\tlabel: \"Featured Image\",\n\t\ttype: \"image\",\n\t\tcheck: (item) => !!item.featuredImage,\n\t},\n\t{\n\t\tslug: \"seo_title\",\n\t\tlabel: \"SEO Title\",\n\t\ttype: \"string\",\n\t\tcheck: (item) => !!extractSeo(item).title,\n\t},\n\t{\n\t\tslug: \"seo_description\",\n\t\tlabel: \"SEO Description\",\n\t\ttype: \"text\",\n\t\tcheck: (item) => !!extractSeo(item).description,\n\t},\n];\n\nconst SEO_FIELD_SLUGS = new Set([\"seo_title\", \"seo_description\"]);\n\n/**\n * Coerce a WordPress meta value to an EmDash field type. WP postmeta is\n * stringly typed and inconsistent across posts (the same key can hold\n * \"5\", 5, \"\", or false). Returns `undefined` when the value can't\n * reasonably represent the target type — the caller drops it.\n */\nexport function coerceToFieldType(value: unknown, fieldType: string): unknown {\n\tswitch (fieldType) {\n\t\tcase \"integer\": {\n\t\t\tconst n = typeof value === \"number\" ? value : Number(value);\n\t\t\treturn Number.isInteger(n) ? n : undefined;\n\t\t}\n\t\tcase \"number\": {\n\t\t\tconst n = typeof value === \"number\" ? value : Number(value);\n\t\t\treturn Number.isFinite(n) ? n : undefined;\n\t\t}\n\t\tcase \"boolean\": {\n\t\t\tif (typeof value === \"boolean\") return value;\n\t\t\tif (value === 1 || value === \"1\" || value === \"true\" || value === \"yes\") return true;\n\t\t\tif (value === 0 || value === \"0\" || value === \"false\" || value === \"no\") return false;\n\t\t\treturn undefined;\n\t\t}\n\t\tcase \"datetime\": {\n\t\t\tif (typeof value !== \"string\" && typeof value !== \"number\") return undefined;\n\t\t\tconst d = new Date(value);\n\t\t\treturn Number.isNaN(d.getTime()) ? undefined : d.toISOString();\n\t\t}\n\t\tcase \"json\":\n\t\t\t// Anything serializes; objects/arrays pass through as-is\n\t\t\treturn value;\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"url\":\n\t\tcase \"select\":\n\t\tcase \"slug\":\n\t\tcase \"reference\":\n\t\t\tif (typeof value === \"string\") return value;\n\t\t\tif (typeof value === \"number\") return String(value);\n\t\t\treturn undefined;\n\t\tdefault:\n\t\t\t// Complex types (image, portableText, repeater, multiSelect, file):\n\t\t\t// raw meta can't be coerced reliably -- pass through only if it\n\t\t\t// already has a non-primitive shape.\n\t\t\treturn typeof value === \"object\" ? value : undefined;\n\t}\n}\n\n/**\n * Pull the per-post SEO title/description out of the Yoast / Rank Math\n * blobs the plugin source stashes in `item.meta`. Empty strings mean \"not\n * overridden for this post\" (the plugin exports the raw meta values) and\n * are treated as absent.\n */\nfunction pickSeoValue(blob: unknown, key: string): string | undefined {\n\tif (typeof blob !== \"object\" || blob === null) return undefined;\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- narrowed to non-null object above\n\tconst value = (blob as Record<string, unknown>)[key];\n\treturn typeof value === \"string\" && value.trim() !== \"\" ? value : undefined;\n}\n\nfunction extractSeo(item: NormalizedItem): { title?: string; description?: string } {\n\tconst yoast = item.meta?._yoast;\n\tconst rankmath = item.meta?._rankmath;\n\treturn {\n\t\ttitle: pickSeoValue(yoast, \"title\") ?? pickSeoValue(rankmath, \"title\"),\n\t\tdescription: pickSeoValue(yoast, \"description\") ?? pickSeoValue(rankmath, \"description\"),\n\t};\n}\n\n/**\n * Fetch the site's taxonomies from the plugin API and pre-create all terms,\n * reusing the WXR taxonomy machinery (same def-lookup, idempotency, and\n * collection-filter semantics).\n */\n/** WP built-in taxonomies that either map to seeded defs or are not content taxonomies. */\nconst BUILTIN_TAXONOMIES = new Set([\"category\", \"post_tag\", \"nav_menu\", \"post_format\"]);\n\n/** Mirrors NAME_PATTERN in the taxonomy handler -- names that fail stay in missingTaxonomies. */\nconst TAXONOMY_NAME_PATTERN = /^[a-z][a-z0-9_]*$/;\n\n/**\n * Create EmDash taxonomy defs for custom WP taxonomies (e.g. CPT taxonomies\n * like `company` or `plattform`) that don't exist yet, scoped to the\n * collections the enabled post-type mappings target. Returns the names of\n * the defs created. Runs before term pre-import so the terms and per-post\n * assignments flow through the existing machinery instead of being dropped\n * as `missingTaxonomies`.\n */\nexport async function ensureCustomTaxonomyDefs(\n\tdb: EmDashHandlers[\"db\"],\n\ttaxonomies: Awaited<ReturnType<typeof fetchPluginTaxonomies>>,\n\tconfig: WpPluginImportConfig,\n): Promise<string[]> {\n\tconst created: string[] = [];\n\tfor (const taxonomy of taxonomies) {\n\t\tif (BUILTIN_TAXONOMIES.has(taxonomy.name)) continue;\n\t\tif (taxonomy.terms.length === 0) continue;\n\t\tif (!TAXONOMY_NAME_PATTERN.test(taxonomy.name)) continue;\n\n\t\t// handleTaxonomyCreate's duplicate guard is locale-scoped; check\n\t\t// name-wide existence ourselves to stay idempotent on re-runs.\n\t\tconst existing = await db\n\t\t\t.selectFrom(\"_emdash_taxonomy_defs\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"name\", \"=\", taxonomy.name)\n\t\t\t.executeTakeFirst();\n\t\tif (existing) continue;\n\n\t\t// Scope the def to the collections the WP post types map onto.\n\t\t// Older plugin versions don't send post_types -> empty list = no\n\t\t// collection filter, which the term machinery treats as \"any\".\n\t\tconst collections = (taxonomy.post_types ?? [])\n\t\t\t.map((postType) => config.postTypeMappings[postType])\n\t\t\t.filter((mapping) => mapping?.enabled)\n\t\t\t.map((mapping) => mapping.collection);\n\n\t\tconst result = await handleTaxonomyCreate(db, {\n\t\t\tname: taxonomy.name,\n\t\t\tlabel: taxonomy.label,\n\t\t\tlabelSingular: taxonomy.label_singular,\n\t\t\thierarchical: taxonomy.hierarchical,\n\t\t\tcollections: [...new Set(collections)],\n\t\t});\n\t\tif (result.success) {\n\t\t\tcreated.push(taxonomy.name);\n\t\t} else {\n\t\t\tconsole.warn(\n\t\t\t\t`[WP Plugin Import] Could not create taxonomy '${taxonomy.name}':`,\n\t\t\t\tresult.error.message,\n\t\t\t);\n\t\t}\n\t}\n\treturn created;\n}\n\nasync function buildTaxonomyPlan(\n\temdash: EmDashHandlers,\n\turl: string,\n\ttoken: string,\n\tconfig: WpPluginImportConfig,\n): Promise<{ plan: TaxonomyImportPlan; defsCreated: string[] }> {\n\tconst taxonomies = await fetchPluginTaxonomies(url, token);\n\n\tconst defsCreated = await ensureCustomTaxonomyDefs(emdash.db, taxonomies, config);\n\n\tconst categories: WxrCategory[] = [];\n\tconst tags: WxrTag[] = [];\n\tconst terms: WxrTerm[] = [];\n\n\tfor (const taxonomy of taxonomies) {\n\t\tfor (const term of taxonomy.terms) {\n\t\t\tif (taxonomy.name === \"category\") {\n\t\t\t\tcategories.push({ nicename: term.slug, name: term.name, description: term.description });\n\t\t\t} else if (taxonomy.name === \"post_tag\") {\n\t\t\t\ttags.push({ slug: term.slug, name: term.name, description: term.description });\n\t\t\t} else if (taxonomy.name !== \"nav_menu\" && taxonomy.name !== \"post_format\") {\n\t\t\t\tterms.push({\n\t\t\t\t\tid: term.id,\n\t\t\t\t\ttaxonomy: taxonomy.name,\n\t\t\t\t\tslug: term.slug,\n\t\t\t\t\tname: term.name,\n\t\t\t\t\tdescription: term.description,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tconst plan = await preImportWxrTaxonomies(emdash.db, [], categories, tags, terms, undefined);\n\treturn { plan, defsCreated };\n}\n\n/**\n * Fetch the source site's options and apply its identity (title, tagline,\n * logo, favicon) as EmDash site settings, overwriting seed placeholders.\n * Logo/favicon files are side-loaded into media storage first; the later\n * full media pass dedupes them by content hash.\n *\n * Returns the list of applied setting keys.\n */\nasync function applySiteSettings(\n\temdash: EmDashHandlers,\n\turl: string,\n\ttoken: string,\n\tconfig: WpPluginImportConfig,\n): Promise<string[]> {\n\tconst wantTitle = config.importSiteTitle !== false;\n\tconst wantLogo = config.importLogo !== false;\n\tif (!wantTitle && !wantLogo) return [];\n\n\tconst options = await fetchPluginOptions(url, token);\n\tconst parsed = parseSiteSettingsFromPlugin(options);\n\tif (!wantTitle) {\n\t\tdelete parsed.title;\n\t\tdelete parsed.tagline;\n\t}\n\tif (!wantLogo) {\n\t\tdelete parsed.logo;\n\t\tdelete parsed.favicon;\n\t}\n\n\tconst media: { logoMediaId?: string; faviconMediaId?: string } = {};\n\tif (emdash.storage && (parsed.logo?.url || parsed.favicon?.url)) {\n\t\tconst attachments = [];\n\t\tif (parsed.logo?.url) {\n\t\t\tattachments.push({ id: parsed.logo.id, url: parsed.logo.url });\n\t\t}\n\t\tif (parsed.favicon?.url && parsed.favicon.url !== parsed.logo?.url) {\n\t\t\tattachments.push({ id: parsed.favicon.id, url: parsed.favicon.url });\n\t\t}\n\t\tconst mediaResult = await importMediaWithProgress(\n\t\t\tattachments,\n\t\t\temdash.db,\n\t\t\temdash.storage,\n\t\t\t() => {},\n\t\t);\n\t\tfor (const item of mediaResult.imported) {\n\t\t\tif (item.originalUrl === parsed.logo?.url) {\n\t\t\t\tmedia.logoMediaId = item.mediaId;\n\t\t\t}\n\t\t\tif (item.originalUrl === parsed.favicon?.url) {\n\t\t\t\tmedia.faviconMediaId = item.mediaId;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst settingsResult = await importSiteSettings(parsed, emdash.db, true, media);\n\tfor (const settingError of settingsResult.errors) {\n\t\tconsole.warn(\n\t\t\t`[WP Plugin Import] Site setting \"${settingError.setting}\" failed:`,\n\t\t\tsettingError.error,\n\t\t);\n\t}\n\treturn settingsResult.applied;\n}\n\n/**\n * Adapt a NormalizedItem's taxonomy assignments to the WxrPost shape the\n * shared attach helper consumes.\n */\nfunction toWxrAssignments(item: NormalizedItem): WxrPost {\n\treturn {\n\t\tcategories: item.categories ?? [],\n\t\ttags: item.tags ?? [],\n\t\tcustomTaxonomies: item.customTaxonomies\n\t\t\t? new Map(Object.entries(item.customTaxonomies))\n\t\t\t: undefined,\n\t\tmeta: new Map(),\n\t};\n}\n\n/** Exported for tests (field auto-creation regression coverage). */\nexport async function importContent(\n\titems: AsyncIterable<NormalizedItem> | Iterable<NormalizedItem>,\n\tconfig: WpPluginImportConfig,\n\temdash: EmDashHandlers,\n\tmanifest: EmDashManifest,\n\ttaxonomyPlan: TaxonomyImportPlan | undefined,\n\tseedTranslationGroups?: Map<string, string>,\n): Promise<{\n\tresult: ImportResult;\n\tcontentIdMap: Map<number, string>;\n\tcollectionByWpId: Map<number, string>;\n}> {\n\tconst result: ImportResult = {\n\t\tsuccess: true,\n\t\timported: 0,\n\t\tskipped: 0,\n\t\terrors: [],\n\t\tbyCollection: {},\n\t};\n\n\t// WP post ID -> EmDash content ID, used to resolve menu item references\n\tconst contentIdMap = new Map<number, string>();\n\n\t// WP post ID -> EmDash collection slug, used by the comment import\n\tconst collectionByWpId = new Map<number, string>();\n\n\t// Create content repository for checking existing items\n\tconst contentRepo = new ContentRepository(emdash.db);\n\tconst bylineRepo = new BylineRepository(emdash.db);\n\tconst bylineCache = new Map<string, string>();\n\tconst schemaRegistry = new SchemaRegistry(emdash.db);\n\n\t// Track which (collection, field) pairs have been ensured. Keyed per\n\t// field, not per collection: a field like seo_title may first be\n\t// needed by the 30th item, and gating on the first item would skip it.\n\tconst ensuredFields = new Set<string>();\n\n\t// Field slug -> type per collection, for mapping custom meta/ACF onto\n\t// real fields (and coercing values to the field's type)\n\tconst fieldTypesByCollection = new Map<string, Map<string, string>>();\n\n\t// Track source translationGroup -> EmDash item ID for translation linking.\n\t// Maps source-side translation group ID to the EmDash ID of the first item\n\t// imported for that group (the default-locale item). The chunked import\n\t// seeds this from earlier chunks so groups can span page boundaries.\n\tconst translationGroupMap = seedTranslationGroups ?? new Map<string, string>();\n\n\tfor await (const item of items) {\n\t\tconsole.log(\"[WP Plugin Import] Processing item:\", {\n\t\t\tsourceId: item.sourceId,\n\t\t\ttitle: item.title,\n\t\t\tpostType: item.postType,\n\t\t\tstatus: item.status,\n\t\t\tcontentBlocks: Array.isArray(item.content) ? item.content.length : 0,\n\t\t\tfeaturedImage: item.featuredImage,\n\t\t\tlocale: item.locale,\n\t\t\ttranslationGroup: item.translationGroup,\n\t\t});\n\n\t\tconst mapping = config.postTypeMappings[item.postType];\n\n\t\t// Skip if not mapped or disabled\n\t\tif (!mapping || !mapping.enabled) {\n\t\t\tresult.skipped++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst collection = mapping.collection;\n\n\t\t// Check if collection exists in manifest\n\t\tif (!manifest?.collections[collection]) {\n\t\t\tresult.errors.push({\n\t\t\t\ttitle: item.title || \"Untitled\",\n\t\t\t\terror: `Collection \"${collection}\" does not exist`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\t// Ensure required fields exist in the collection schema. Checked\n\t\t\t// per field and item (not once per collection): whether a field\n\t\t\t// is needed depends on the item — the first post may have no SEO\n\t\t\t// override while a later one does.\n\t\t\tfor (const field of IMPORT_FIELDS) {\n\t\t\t\tif (config.importSeo === false && SEO_FIELD_SLUGS.has(field.slug)) continue;\n\t\t\t\tconst ensureKey = `${collection}:${field.slug}`;\n\t\t\t\tif (ensuredFields.has(ensureKey) || !field.check(item)) continue;\n\t\t\t\tensuredFields.add(ensureKey);\n\t\t\t\tconst existingField = await schemaRegistry.getField(collection, field.slug);\n\t\t\t\tif (!existingField) {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`[WP Plugin Import] Creating missing field \"${field.slug}\" in collection \"${collection}\"`,\n\t\t\t\t\t);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait schemaRegistry.createField(collection, {\n\t\t\t\t\t\t\tslug: field.slug,\n\t\t\t\t\t\t\tlabel: field.label,\n\t\t\t\t\t\t\ttype: field.type,\n\t\t\t\t\t\t\trequired: false,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tfieldTypesByCollection.get(collection)?.set(field.slug, field.type);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// Field might already exist from concurrent creation\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`[WP Plugin Import] Field \"${field.slug}\" creation skipped:`,\n\t\t\t\t\t\t\te instanceof Error ? e.message : e,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Load the collection's field types once, so custom meta / ACF\n\t\t\t// values can land in matching schema fields (created by the\n\t\t\t// prepare step or already present).\n\t\t\tlet fieldTypes = fieldTypesByCollection.get(collection);\n\t\t\tif (!fieldTypes) {\n\t\t\t\tfieldTypes = new Map<string, string>();\n\t\t\t\tconst collectionDef = await schemaRegistry.getCollection(collection);\n\t\t\t\tif (collectionDef) {\n\t\t\t\t\tfor (const field of await schemaRegistry.listFields(collectionDef.id)) {\n\t\t\t\t\t\tfieldTypes.set(field.slug, field.type);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfieldTypesByCollection.set(collection, fieldTypes);\n\t\t\t}\n\n\t\t\t// Generate slug from item slug or title\n\t\t\tconst slug = item.slug || slugify(item.title || `post-${item.sourceId}`);\n\n\t\t\t// Check if already exists (idempotency) — locale-aware lookup\n\t\t\tif (config.skipExisting) {\n\t\t\t\tconst existing = await contentRepo.findBySlug(collection, slug, item.locale);\n\t\t\t\tif (existing) {\n\t\t\t\t\t// Still track the translation group mapping for later items\n\t\t\t\t\tif (item.translationGroup) {\n\t\t\t\t\t\ttranslationGroupMap.set(item.translationGroup, existing.id);\n\t\t\t\t\t}\n\t\t\t\t\t// Menus and comments may reference this post even when we skip it\n\t\t\t\t\tconst wpId = Number(item.sourceId);\n\t\t\t\t\tif (Number.isFinite(wpId)) {\n\t\t\t\t\t\tcontentIdMap.set(wpId, existing.id);\n\t\t\t\t\t\tcollectionByWpId.set(wpId, collection);\n\t\t\t\t\t}\n\t\t\t\t\tresult.skipped++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Map WordPress status to EmDash status\n\t\t\tconst status = mapStatus(item.status);\n\n\t\t\t// Build data object - add all applicable fields\n\t\t\tconst data: Record<string, unknown> = {};\n\n\t\t\t// Add standard fields\n\t\t\tdata.title = item.title || \"Untitled\";\n\t\t\tdata.content = item.content;\n\n\t\t\tif (item.excerpt) {\n\t\t\t\tdata.excerpt = item.excerpt;\n\t\t\t}\n\t\t\tif (item.featuredImage) {\n\t\t\t\tdata.featured_image = item.featuredImage;\n\t\t\t\tconsole.log(\"[WP Plugin Import] Adding featured_image:\", item.featuredImage);\n\t\t\t}\n\n\t\t\t// Per-post SEO overrides from Yoast / Rank Math\n\t\t\tif (config.importSeo !== false) {\n\t\t\t\tconst seo = extractSeo(item);\n\t\t\t\tif (seo.title) {\n\t\t\t\t\tdata.seo_title = seo.title;\n\t\t\t\t}\n\t\t\t\tif (seo.description) {\n\t\t\t\t\tdata.seo_description = seo.description;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Map ACF values and custom meta onto schema fields with the same\n\t\t\t// (sanitized) slug — the same sanitization the analysis applied\n\t\t\t// when suggesting the fields, so keys like `event-date` land in\n\t\t\t// the `event_date` field the prepare step created. Values without\n\t\t\t// a matching field are dropped -- the user controls the schema,\n\t\t\t// we don't invent fields per meta key. Values are coerced to the\n\t\t\t// field's type (the analysis inferred it from ONE sample; real\n\t\t\t// values vary) and dropped when incoercible, so one odd meta\n\t\t\t// value can't fail the whole item.\n\t\t\tconst assignMetaValue = (key: string, value: unknown) => {\n\t\t\t\tif (value === null || value === \"\") return;\n\t\t\t\tconst fieldSlug = sanitizeFieldSlug(key);\n\t\t\t\tconst fieldType = fieldTypes.get(fieldSlug);\n\t\t\t\tif (!fieldType || fieldSlug in data) return;\n\t\t\t\tconst coerced = coerceToFieldType(value, fieldType);\n\t\t\t\tif (coerced !== undefined) {\n\t\t\t\t\tdata[fieldSlug] = coerced;\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst acf = item.meta?._acf;\n\t\t\tif (typeof acf === \"object\" && acf !== null) {\n\t\t\t\tfor (const [key, value] of Object.entries(acf)) {\n\t\t\t\t\tassignMetaValue(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (item.meta) {\n\t\t\t\tfor (const [key, value] of Object.entries(item.meta)) {\n\t\t\t\t\t// Underscore keys are WP-internal or handled above (_acf/_yoast/_rankmath)\n\t\t\t\t\tif (key.startsWith(\"_\")) continue;\n\t\t\t\t\tassignMetaValue(key, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Resolve author ID from mappings\n\t\t\tlet authorId: string | undefined;\n\t\t\tif (config.authorMappings && item.author) {\n\t\t\t\tconst mappedUserId = config.authorMappings[item.author];\n\t\t\t\tif (mappedUserId !== undefined && mappedUserId !== null) {\n\t\t\t\t\tauthorId = mappedUserId;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst bylineId = await resolveImportByline(\n\t\t\t\titem.author,\n\t\t\t\titem.author, // display name fallback is the login\n\t\t\t\tauthorId,\n\t\t\t\tbylineRepo,\n\t\t\t\tbylineCache,\n\t\t\t);\n\n\t\t\t// Resolve translation link: if this item has a translationGroup and\n\t\t\t// we've already imported another item in the same group, link them.\n\t\t\tlet translationOf: string | undefined;\n\t\t\tif (item.translationGroup) {\n\t\t\t\tconst existingGroupItem = translationGroupMap.get(item.translationGroup);\n\t\t\t\tif (existingGroupItem) {\n\t\t\t\t\ttranslationOf = existingGroupItem;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Preserve original dates from the source\n\t\t\tconst itemDateTime = item.date?.getTime();\n\t\t\tconst createdAt =\n\t\t\t\titemDateTime !== undefined && !Number.isNaN(itemDateTime)\n\t\t\t\t\t? item.date.toISOString()\n\t\t\t\t\t: undefined;\n\t\t\tconst publishedAt = status === \"published\" && createdAt ? createdAt : undefined;\n\n\t\t\t// Create the content item\n\t\t\tconst createResult = await emdash.handleContentCreate(collection, {\n\t\t\t\tdata,\n\t\t\t\tslug,\n\t\t\t\tstatus,\n\t\t\t\tauthorId,\n\t\t\t\tbylines: bylineId ? [{ bylineId }] : undefined,\n\t\t\t\tlocale: item.locale,\n\t\t\t\ttranslationOf,\n\t\t\t\tcreatedAt,\n\t\t\t\tpublishedAt,\n\t\t\t});\n\n\t\t\tif (createResult.success) {\n\t\t\t\tresult.imported++;\n\t\t\t\tresult.byCollection[collection] = (result.byCollection[collection] || 0) + 1;\n\n\t\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- create handler returns { item, _rev }\n\t\t\t\tconst createdData = createResult.data as { item?: { id?: string } } | undefined;\n\t\t\t\tconst createdId = createdData?.item?.id;\n\n\t\t\t\tif (createdId) {\n\t\t\t\t\tconst wpId = Number(item.sourceId);\n\t\t\t\t\tif (Number.isFinite(wpId)) {\n\t\t\t\t\t\tcontentIdMap.set(wpId, createdId);\n\t\t\t\t\t\tcollectionByWpId.set(wpId, collection);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Attach category/tag/custom-taxonomy assignments\n\t\t\t\t\tif (taxonomyPlan) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst attached = await attachPostTaxonomies(\n\t\t\t\t\t\t\t\temdash.db,\n\t\t\t\t\t\t\t\tcollection,\n\t\t\t\t\t\t\t\tcreatedId,\n\t\t\t\t\t\t\t\ttoWxrAssignments(item),\n\t\t\t\t\t\t\t\ttaxonomyPlan,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (attached > 0) {\n\t\t\t\t\t\t\t\tresult.taxonomyAssignments = (result.taxonomyAssignments ?? 0) + attached;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\tconsole.warn(`[WP Plugin Import] Taxonomy attach failed for \"${slug}\":`, e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Track translation group: first item in a group establishes the mapping\n\t\t\t\tif (item.translationGroup && !translationGroupMap.has(item.translationGroup) && createdId) {\n\t\t\t\t\ttranslationGroupMap.set(item.translationGroup, createdId);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresult.errors.push({\n\t\t\t\t\ttitle: item.title || \"Untitled\",\n\t\t\t\t\terror:\n\t\t\t\t\t\ttypeof createResult.error === \"object\" && createResult.error !== null\n\t\t\t\t\t\t\t? (createResult.error as { message?: string }).message || \"Unknown error\"\n\t\t\t\t\t\t\t: String(createResult.error),\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(`Import error for \"${item.title || \"Untitled\"}\":`, error);\n\t\t\tresult.errors.push({\n\t\t\t\ttitle: item.title || \"Untitled\",\n\t\t\t\terror: error instanceof Error && error.message ? error.message : \"Failed to import item\",\n\t\t\t});\n\t\t}\n\t}\n\n\tif (taxonomyPlan && taxonomyPlan.missingTaxonomies.length > 0) {\n\t\tresult.missingTaxonomies = taxonomyPlan.missingTaxonomies;\n\t}\n\n\tresult.success = result.errors.length === 0;\n\treturn { result, contentIdMap, collectionByWpId };\n}\n\nfunction mapStatus(wpStatus: string | undefined): string {\n\tswitch (wpStatus) {\n\t\tcase \"publish\":\n\t\t\treturn \"published\";\n\t\tcase \"draft\":\n\t\t\treturn \"draft\";\n\t\tcase \"pending\":\n\t\t\treturn \"draft\";\n\t\tcase \"private\":\n\t\t\treturn \"draft\";\n\t\tdefault:\n\t\t\treturn \"draft\";\n\t}\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
- import "../../../base64-C74Msx8h.mjs";
2
- import "../../../types-Ch8pFMEm.mjs";
1
+ import "../../../base64-B-PsqheR.mjs";
2
+ import "../../../types-CrRUFdej.mjs";
3
3
  import { t as OptionsRepository } from "../../../options-BlmBHTvX.mjs";
4
- import { r as handleError } from "../../../error-CfJMLSkd.mjs";
5
- import { n as VERSION, t as COMMIT } from "../../../version-BlIJvNAZ.mjs";
6
- import { t as getAuthMode } from "../../../mode-BVnhGZ2o.mjs";
4
+ import { n as apiSuccess, r as handleError } from "../../../error-DJ98TRbQ.mjs";
5
+ import { n as VERSION, t as COMMIT } from "../../../version-Ci6bw6YK.mjs";
6
+ import { t as getAuthMode } from "../../../mode-fiXRMfeA.mjs";
7
7
 
8
8
  //#region src/astro/routes/api/manifest.ts
9
9
  const prerender = false;
@@ -29,7 +29,7 @@ const GET = async ({ locals }) => {
29
29
  `.execute(emdash.db);
30
30
  signupEnabled = Number(result.rows[0]?.cnt ?? 0) > 0;
31
31
  } catch {}
32
- const manifest = emdashManifest ? {
32
+ return apiSuccess(emdashManifest ? {
33
33
  ...emdashManifest,
34
34
  authMode: authMode.type === "external" ? authMode.providerType : "passkey",
35
35
  signupEnabled,
@@ -44,8 +44,7 @@ const GET = async ({ locals }) => {
44
44
  authMode: "passkey",
45
45
  signupEnabled,
46
46
  admin: adminBranding
47
- };
48
- return Response.json({ data: manifest }, { headers: { "Cache-Control": "private, no-store" } });
47
+ });
49
48
  } catch (error) {
50
49
  return handleError(error, "Failed to build manifest", "MANIFEST_BUILD_ERROR");
51
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.mjs","names":[],"sources":["../../../../src/astro/routes/api/manifest.ts"],"sourcesContent":["/**\n * Admin manifest endpoint - injected by EmDash integration\n *\n * GET /_emdash/api/manifest\n *\n * Returns the admin manifest with collection definitions and plugin info.\n * The manifest is generated from the user's live.config.ts at runtime.\n */\n\nimport type { APIRoute } from \"astro\";\n\nimport { handleError } from \"#api/error.js\";\nimport { getAuthMode } from \"#auth/mode.js\";\nimport { OptionsRepository } from \"#db/repositories/options.js\";\n\nimport { COMMIT, VERSION } from \"../../../version.js\";\nimport type { EmDashManifest } from \"../../types.js\";\n\nexport const prerender = false;\n\nexport const GET: APIRoute = async ({ locals }) => {\n\tconst { emdash } = locals;\n\n\ttry {\n\t\t// Manifest is built fresh from the live database per admin request.\n\t\t// `requestCached` inside `getManifest` dedupes if multiple consumers\n\t\t// share the request. Wrapped in try/catch so any future DB-touching\n\t\t// additions to `getManifest()` (plugin manifest loading, marketplace\n\t\t// lookup, etc.) return the standard error envelope rather than an\n\t\t// unstructured 500 — matches the pattern used by the WP execute\n\t\t// routes.\n\t\tconst emdashManifest = emdash ? await emdash.getManifest() : null;\n\n\t\t// Determine auth mode from config\n\t\tconst authMode = getAuthMode(emdash?.config);\n\n\t\t// Read admin branding from the per-request config plumbed through middleware\n\t\t// (same source admin.astro reads from). Reading from a build-time global\n\t\t// here was unreliable -- the virtual config module exports the config but\n\t\t// doesn't assign it to globalThis, so getStoredConfig() always returned\n\t\t// null and the React SPA never received custom logo/siteName/favicon.\n\t\t// See issue #835.\n\t\tlet adminBranding = emdash?.config?.admin;\n\n\t\t// When no build-time `admin.siteName` is configured, brand the admin with\n\t\t// the site's own title so multi-site operators can tell backends apart\n\t\t// (WordPress-style: wp-admin always shows the site name). Precedence:\n\t\t// explicit `admin.siteName` → Site Title (Settings → General) → the title\n\t\t// captured by the setup wizard → the bundled \"EmDash\" default in the SPA.\n\t\tif (!adminBranding?.siteName && emdash?.db) {\n\t\t\ttry {\n\t\t\t\tconst options = new OptionsRepository(emdash.db);\n\t\t\t\tconst titles = await options.getMany<string>([\"site:title\", \"emdash:site_title\"]);\n\t\t\t\tconst siteTitle = titles.get(\"site:title\") || titles.get(\"emdash:site_title\");\n\t\t\t\tif (siteTitle) {\n\t\t\t\t\tadminBranding = { ...adminBranding, siteName: siteTitle };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// options table may not exist yet (pre-setup) — keep the default.\n\t\t\t}\n\t\t}\n\n\t\t// Check if self-signup is enabled (any allowed domain with enabled = 1)\n\t\t// Only relevant for passkey auth — external auth providers handle their own signup\n\t\tlet signupEnabled = false;\n\t\tif (emdash?.db && authMode.type === \"passkey\") {\n\t\t\ttry {\n\t\t\t\tconst { sql } = await import(\"kysely\");\n\t\t\t\tconst result = await sql<{ cnt: unknown }>`\n\t\t\t\t\tSELECT COUNT(*) as cnt FROM allowed_domains WHERE enabled = 1\n\t\t\t\t`.execute(emdash.db);\n\t\t\t\tsignupEnabled = Number(result.rows[0]?.cnt ?? 0) > 0;\n\t\t\t} catch {\n\t\t\t\t// Table may not exist yet, that's fine\n\t\t\t}\n\t\t}\n\n\t\tconst manifest: EmDashManifest = emdashManifest\n\t\t\t? {\n\t\t\t\t\t...emdashManifest,\n\t\t\t\t\tauthMode: authMode.type === \"external\" ? authMode.providerType : \"passkey\",\n\t\t\t\t\tsignupEnabled,\n\t\t\t\t\tadmin: adminBranding,\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tversion: VERSION,\n\t\t\t\t\tcommit: COMMIT,\n\t\t\t\t\thash: \"default\",\n\t\t\t\t\tcollections: {},\n\t\t\t\t\tplugins: {},\n\t\t\t\t\ttaxonomies: [],\n\t\t\t\t\tauthMode: \"passkey\",\n\t\t\t\t\tsignupEnabled,\n\t\t\t\t\tadmin: adminBranding,\n\t\t\t\t};\n\n\t\treturn Response.json(\n\t\t\t{ data: manifest },\n\t\t\t{\n\t\t\t\theaders: {\n\t\t\t\t\t\"Cache-Control\": \"private, no-store\",\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to build manifest\", \"MANIFEST_BUILD_ERROR\");\n\t}\n};\n"],"mappings":";;;;;;;;AAkBA,MAAa,YAAY;AAEzB,MAAa,MAAgB,OAAO,EAAE,aAAa;CAClD,MAAM,EAAE,WAAW;AAEnB,KAAI;EAQH,MAAM,iBAAiB,SAAS,MAAM,OAAO,aAAa,GAAG;EAG7D,MAAM,WAAW,YAAY,QAAQ,OAAO;EAQ5C,IAAI,gBAAgB,QAAQ,QAAQ;AAOpC,MAAI,CAAC,eAAe,YAAY,QAAQ,GACvC,KAAI;GAEH,MAAM,SAAS,MADC,IAAI,kBAAkB,OAAO,GAAG,CACnB,QAAgB,CAAC,cAAc,oBAAoB,CAAC;GACjF,MAAM,YAAY,OAAO,IAAI,aAAa,IAAI,OAAO,IAAI,oBAAoB;AAC7E,OAAI,UACH,iBAAgB;IAAE,GAAG;IAAe,UAAU;IAAW;UAEnD;EAOT,IAAI,gBAAgB;AACpB,MAAI,QAAQ,MAAM,SAAS,SAAS,UACnC,KAAI;GACH,MAAM,EAAE,QAAQ,MAAM,OAAO;GAC7B,MAAM,SAAS,MAAM,GAAqB;;MAExC,QAAQ,OAAO,GAAG;AACpB,mBAAgB,OAAO,OAAO,KAAK,IAAI,OAAO,EAAE,GAAG;UAC5C;EAKT,MAAM,WAA2B,iBAC9B;GACA,GAAG;GACH,UAAU,SAAS,SAAS,aAAa,SAAS,eAAe;GACjE;GACA,OAAO;GACP,GACA;GACA,SAAS;GACT,QAAQ;GACR,MAAM;GACN,aAAa,EAAE;GACf,SAAS,EAAE;GACX,YAAY,EAAE;GACd,UAAU;GACV;GACA,OAAO;GACP;AAEH,SAAO,SAAS,KACf,EAAE,MAAM,UAAU,EAClB,EACC,SAAS,EACR,iBAAiB,qBACjB,EACD,CACD;UACO,OAAO;AACf,SAAO,YAAY,OAAO,4BAA4B,uBAAuB"}
1
+ {"version":3,"file":"manifest.mjs","names":[],"sources":["../../../../src/astro/routes/api/manifest.ts"],"sourcesContent":["/**\n * Admin manifest endpoint - injected by EmDash integration\n *\n * GET /_emdash/api/manifest\n *\n * Returns the admin manifest with collection definitions and plugin info.\n * The manifest is generated from the user's live.config.ts at runtime.\n */\n\nimport type { APIRoute } from \"astro\";\n\nimport { apiSuccess, handleError } from \"#api/error.js\";\nimport { getAuthMode } from \"#auth/mode.js\";\nimport { OptionsRepository } from \"#db/repositories/options.js\";\n\nimport { COMMIT, VERSION } from \"../../../version.js\";\nimport type { EmDashManifest } from \"../../types.js\";\n\nexport const prerender = false;\n\nexport const GET: APIRoute = async ({ locals }) => {\n\tconst { emdash } = locals;\n\n\ttry {\n\t\t// Manifest is built fresh from the live database per admin request.\n\t\t// `requestCached` inside `getManifest` dedupes if multiple consumers\n\t\t// share the request. Wrapped in try/catch so any future DB-touching\n\t\t// additions to `getManifest()` (plugin manifest loading, marketplace\n\t\t// lookup, etc.) return the standard error envelope rather than an\n\t\t// unstructured 500 — matches the pattern used by the WP execute\n\t\t// routes.\n\t\tconst emdashManifest = emdash ? await emdash.getManifest() : null;\n\n\t\t// Determine auth mode from config\n\t\tconst authMode = getAuthMode(emdash?.config);\n\n\t\t// Read admin branding from the per-request config plumbed through middleware\n\t\t// (same source admin.astro reads from). Reading from a build-time global\n\t\t// here was unreliable -- the virtual config module exports the config but\n\t\t// doesn't assign it to globalThis, so getStoredConfig() always returned\n\t\t// null and the React SPA never received custom logo/siteName/favicon.\n\t\t// See issue #835.\n\t\tlet adminBranding = emdash?.config?.admin;\n\n\t\t// When no build-time `admin.siteName` is configured, brand the admin with\n\t\t// the site's own title so multi-site operators can tell backends apart\n\t\t// (WordPress-style: wp-admin always shows the site name). Precedence:\n\t\t// explicit `admin.siteName` → Site Title (Settings → General) → the title\n\t\t// captured by the setup wizard → the bundled \"EmDash\" default in the SPA.\n\t\tif (!adminBranding?.siteName && emdash?.db) {\n\t\t\ttry {\n\t\t\t\tconst options = new OptionsRepository(emdash.db);\n\t\t\t\tconst titles = await options.getMany<string>([\"site:title\", \"emdash:site_title\"]);\n\t\t\t\tconst siteTitle = titles.get(\"site:title\") || titles.get(\"emdash:site_title\");\n\t\t\t\tif (siteTitle) {\n\t\t\t\t\tadminBranding = { ...adminBranding, siteName: siteTitle };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// options table may not exist yet (pre-setup) — keep the default.\n\t\t\t}\n\t\t}\n\n\t\t// Check if self-signup is enabled (any allowed domain with enabled = 1)\n\t\t// Only relevant for passkey auth — external auth providers handle their own signup\n\t\tlet signupEnabled = false;\n\t\tif (emdash?.db && authMode.type === \"passkey\") {\n\t\t\ttry {\n\t\t\t\tconst { sql } = await import(\"kysely\");\n\t\t\t\tconst result = await sql<{ cnt: unknown }>`\n\t\t\t\t\tSELECT COUNT(*) as cnt FROM allowed_domains WHERE enabled = 1\n\t\t\t\t`.execute(emdash.db);\n\t\t\t\tsignupEnabled = Number(result.rows[0]?.cnt ?? 0) > 0;\n\t\t\t} catch {\n\t\t\t\t// Table may not exist yet, that's fine\n\t\t\t}\n\t\t}\n\n\t\tconst manifest: EmDashManifest = emdashManifest\n\t\t\t? {\n\t\t\t\t\t...emdashManifest,\n\t\t\t\t\tauthMode: authMode.type === \"external\" ? authMode.providerType : \"passkey\",\n\t\t\t\t\tsignupEnabled,\n\t\t\t\t\tadmin: adminBranding,\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tversion: VERSION,\n\t\t\t\t\tcommit: COMMIT,\n\t\t\t\t\thash: \"default\",\n\t\t\t\t\tcollections: {},\n\t\t\t\t\tplugins: {},\n\t\t\t\t\ttaxonomies: [],\n\t\t\t\t\tauthMode: \"passkey\",\n\t\t\t\t\tsignupEnabled,\n\t\t\t\t\tadmin: adminBranding,\n\t\t\t\t};\n\n\t\treturn apiSuccess(manifest);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to build manifest\", \"MANIFEST_BUILD_ERROR\");\n\t}\n};\n"],"mappings":";;;;;;;;AAkBA,MAAa,YAAY;AAEzB,MAAa,MAAgB,OAAO,EAAE,aAAa;CAClD,MAAM,EAAE,WAAW;AAEnB,KAAI;EAQH,MAAM,iBAAiB,SAAS,MAAM,OAAO,aAAa,GAAG;EAG7D,MAAM,WAAW,YAAY,QAAQ,OAAO;EAQ5C,IAAI,gBAAgB,QAAQ,QAAQ;AAOpC,MAAI,CAAC,eAAe,YAAY,QAAQ,GACvC,KAAI;GAEH,MAAM,SAAS,MADC,IAAI,kBAAkB,OAAO,GAAG,CACnB,QAAgB,CAAC,cAAc,oBAAoB,CAAC;GACjF,MAAM,YAAY,OAAO,IAAI,aAAa,IAAI,OAAO,IAAI,oBAAoB;AAC7E,OAAI,UACH,iBAAgB;IAAE,GAAG;IAAe,UAAU;IAAW;UAEnD;EAOT,IAAI,gBAAgB;AACpB,MAAI,QAAQ,MAAM,SAAS,SAAS,UACnC,KAAI;GACH,MAAM,EAAE,QAAQ,MAAM,OAAO;GAC7B,MAAM,SAAS,MAAM,GAAqB;;MAExC,QAAQ,OAAO,GAAG;AACpB,mBAAgB,OAAO,OAAO,KAAK,IAAI,OAAO,EAAE,GAAG;UAC5C;AAwBT,SAAO,WAnB0B,iBAC9B;GACA,GAAG;GACH,UAAU,SAAS,SAAS,aAAa,SAAS,eAAe;GACjE;GACA,OAAO;GACP,GACA;GACA,SAAS;GACT,QAAQ;GACR,MAAM;GACN,aAAa,EAAE;GACf,SAAS,EAAE;GACX,YAAY,EAAE;GACd,UAAU;GACV;GACA,OAAO;GACP,CAEwB;UACnB,OAAO;AACf,SAAO,YAAY,OAAO,4BAA4B,uBAAuB"}
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.mts","names":[],"sources":["../../../../src/astro/routes/api/mcp.ts"],"mappings":";;;cAmBa,SAAA;AAAA,cAEA,IAAA,EAAM,QAAA;;;;cA6DN,GAAA,EAAK,QAAA;;;;cAuBL,MAAA,EAAQ,QAAA"}
1
+ {"version":3,"file":"mcp.d.mts","names":[],"sources":["../../../../src/astro/routes/api/mcp.ts"],"mappings":";;;cAmBa,SAAA;AAAA,cAEA,IAAA,EAAM,QAAA;;;;cA8DN,GAAA,EAAK,QAAA;;;;cAuBL,MAAA,EAAQ,QAAA"}