emdash 0.32.0 → 0.33.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 (681) hide show
  1. package/dist/{adapters-C0ONzkUP.d.mts → adapters-BMyNSSZi.d.mts} +9 -1
  2. package/dist/adapters-BMyNSSZi.d.mts.map +1 -0
  3. package/dist/after-CBU2vkoO.mjs +96 -0
  4. package/dist/after-CBU2vkoO.mjs.map +1 -0
  5. package/dist/{allowed-origins-CCEi9bPI.mjs → allowed-origins-CG1pACtS.mjs} +2 -2
  6. package/dist/{allowed-origins-CCEi9bPI.mjs.map → allowed-origins-CG1pACtS.mjs.map} +1 -1
  7. package/dist/api/route-utils.d.mts +3 -4
  8. package/dist/api/route-utils.d.mts.map +1 -1
  9. package/dist/api/route-utils.mjs +22 -22
  10. package/dist/api/schemas/index.d.mts +2 -2
  11. package/dist/api/schemas/index.mjs +5 -5
  12. package/dist/{api-DgPbMlng.mjs → api-D2hunekQ.mjs} +247 -45
  13. package/dist/api-D2hunekQ.mjs.map +1 -0
  14. package/dist/{api-tokens-p-iMzvXR.mjs → api-tokens-CSND0i1z.mjs} +2 -2
  15. package/dist/{api-tokens-p-iMzvXR.mjs.map → api-tokens-CSND0i1z.mjs.map} +1 -1
  16. package/dist/{apply-1_6ra7NP.mjs → apply-kwNEJW2l.mjs} +35 -22
  17. package/dist/apply-kwNEJW2l.mjs.map +1 -0
  18. package/dist/astro/index.d.mts +12 -12
  19. package/dist/astro/index.mjs +13 -3
  20. package/dist/astro/index.mjs.map +1 -1
  21. package/dist/astro/middleware/auth.d.mts +10 -10
  22. package/dist/astro/middleware/auth.mjs +8 -8
  23. package/dist/astro/middleware/redirect.d.mts.map +1 -1
  24. package/dist/astro/middleware/redirect.mjs +8 -8
  25. package/dist/astro/middleware/redirect.mjs.map +1 -1
  26. package/dist/astro/middleware/request-context.mjs +4 -4
  27. package/dist/astro/middleware/setup.mjs +1 -1
  28. package/dist/astro/middleware.d.mts +10 -10
  29. package/dist/astro/middleware.d.mts.map +1 -1
  30. package/dist/astro/middleware.mjs +344 -173
  31. package/dist/astro/middleware.mjs.map +1 -1
  32. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -7
  33. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -7
  34. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
  35. package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
  36. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +5 -5
  37. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +10 -10
  38. package/dist/astro/routes/api/admin/byline-fields/index.mjs +10 -10
  39. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +10 -10
  40. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +18 -18
  41. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +18 -18
  42. package/dist/astro/routes/api/admin/bylines/index.mjs +18 -18
  43. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +16 -16
  44. package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
  45. package/dist/astro/routes/api/admin/comments/_id_.mjs +7 -7
  46. package/dist/astro/routes/api/admin/comments/bulk.mjs +12 -12
  47. package/dist/astro/routes/api/admin/comments/counts.mjs +7 -7
  48. package/dist/astro/routes/api/admin/comments/index.mjs +12 -12
  49. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +5 -5
  50. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +4 -4
  51. package/dist/astro/routes/api/admin/media-usage/repair.mjs +12 -12
  52. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
  53. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
  54. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +46 -45
  55. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  56. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +46 -45
  57. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  58. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +45 -44
  59. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  60. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +5 -5
  61. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +45 -44
  62. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
  63. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +45 -44
  64. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  65. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +45 -44
  66. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  67. package/dist/astro/routes/api/admin/plugins/index.mjs +45 -44
  68. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  69. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  70. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +45 -44
  71. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  72. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +45 -44
  73. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  74. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +45 -44
  75. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +45 -44
  77. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +46 -45
  79. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +45 -44
  81. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +46 -45
  83. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  84. package/dist/astro/routes/api/admin/plugins/updates.mjs +45 -44
  85. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  86. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +45 -44
  87. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  88. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  89. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +45 -44
  90. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  91. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
  92. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  93. package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -8
  94. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +5 -5
  95. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs.map +1 -1
  96. package/dist/astro/routes/api/admin/users/index.mjs +7 -7
  97. package/dist/astro/routes/api/auth/dev-bypass.mjs +6 -6
  98. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  99. package/dist/astro/routes/api/auth/invite/complete.mjs +12 -12
  100. package/dist/astro/routes/api/auth/invite/index.mjs +10 -10
  101. package/dist/astro/routes/api/auth/invite/index.mjs.map +1 -1
  102. package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -11
  103. package/dist/astro/routes/api/auth/logout.mjs +3 -3
  104. package/dist/astro/routes/api/auth/magic-link/send.mjs +12 -12
  105. package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -1
  106. package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
  107. package/dist/astro/routes/api/auth/me.mjs +8 -8
  108. package/dist/astro/routes/api/auth/mode.mjs +3 -3
  109. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  110. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  111. package/dist/astro/routes/api/auth/passkey/_id_.mjs +7 -7
  112. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  113. package/dist/astro/routes/api/auth/passkey/options.mjs +13 -13
  114. package/dist/astro/routes/api/auth/passkey/register/options.mjs +11 -11
  115. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +12 -12
  116. package/dist/astro/routes/api/auth/passkey/verify.mjs +12 -12
  117. package/dist/astro/routes/api/auth/signup/complete.mjs +12 -12
  118. package/dist/astro/routes/api/auth/signup/request.mjs +12 -12
  119. package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -1
  120. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  121. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +18 -18
  122. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
  123. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +15 -15
  124. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  125. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +4 -4
  126. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +4 -4
  127. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  128. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +13 -13
  129. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +9 -9
  130. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +18 -18
  131. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +18 -18
  132. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +4 -4
  133. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  134. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +9 -9
  135. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +16 -16
  136. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  137. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +4 -4
  138. package/dist/astro/routes/api/content/_collection_/_id_.mjs +9 -9
  139. package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
  140. package/dist/astro/routes/api/content/_collection_/index.mjs +9 -9
  141. package/dist/astro/routes/api/content/_collection_/trash.mjs +8 -8
  142. package/dist/astro/routes/api/dashboard.mjs +9 -9
  143. package/dist/astro/routes/api/dev/emails.mjs +3 -3
  144. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  145. package/dist/astro/routes/api/import/probe.mjs +19 -19
  146. package/dist/astro/routes/api/import/wordpress/analyze.mjs +4 -4
  147. package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
  148. package/dist/astro/routes/api/import/wordpress/execute.mjs +15 -15
  149. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
  150. package/dist/astro/routes/api/import/wordpress/media.mjs +10 -10
  151. package/dist/astro/routes/api/import/wordpress/prepare.mjs +11 -11
  152. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
  153. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +12 -12
  154. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  155. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +19 -19
  156. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
  157. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +27 -27
  158. package/dist/astro/routes/api/manifest.mjs +5 -5
  159. package/dist/astro/routes/api/mcp.mjs +42 -42
  160. package/dist/astro/routes/api/media/_id_/confirm.mjs +10 -10
  161. package/dist/astro/routes/api/media/_id_/upload.mjs +6 -6
  162. package/dist/astro/routes/api/media/_id_/usage.mjs +14 -14
  163. package/dist/astro/routes/api/media/_id_.mjs +13 -13
  164. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  165. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  166. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  167. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  168. package/dist/astro/routes/api/media/upload-url.mjs +9 -9
  169. package/dist/astro/routes/api/media.mjs +13 -13
  170. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +11 -11
  171. package/dist/astro/routes/api/menus/_name_/items.mjs +11 -11
  172. package/dist/astro/routes/api/menus/_name_/reorder.mjs +11 -11
  173. package/dist/astro/routes/api/menus/_name_/translations.mjs +11 -11
  174. package/dist/astro/routes/api/menus/_name_.mjs +11 -11
  175. package/dist/astro/routes/api/menus/index.mjs +11 -11
  176. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  177. package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
  178. package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
  179. package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
  180. package/dist/astro/routes/api/oauth/register.mjs +3 -3
  181. package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
  182. package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
  183. package/dist/astro/routes/api/oauth/token.mjs +6 -6
  184. package/dist/astro/routes/api/openapi.json.mjs +36 -5
  185. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  186. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +5 -5
  187. package/dist/astro/routes/api/redirects/404s/index.mjs +12 -12
  188. package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -12
  189. package/dist/astro/routes/api/redirects/_id_.mjs +14 -13
  190. package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
  191. package/dist/astro/routes/api/redirects/index.mjs +14 -13
  192. package/dist/astro/routes/api/redirects/index.mjs.map +1 -1
  193. package/dist/astro/routes/api/relations/_id_/index.mjs +18 -18
  194. package/dist/astro/routes/api/relations/_id_/translations.mjs +13 -13
  195. package/dist/astro/routes/api/relations/index.mjs +18 -18
  196. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  197. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +4 -4
  198. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +45 -44
  199. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  200. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +45 -44
  201. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  202. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +45 -44
  203. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  204. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +45 -44
  205. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  206. package/dist/astro/routes/api/schema/collections/index.mjs +45 -44
  207. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  208. package/dist/astro/routes/api/schema/collections/reorder.d.mts +8 -0
  209. package/dist/astro/routes/api/schema/collections/reorder.d.mts.map +1 -0
  210. package/dist/astro/routes/api/schema/collections/reorder.mjs +66 -0
  211. package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -0
  212. package/dist/astro/routes/api/schema/index.mjs +11 -9
  213. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  214. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +45 -44
  215. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  216. package/dist/astro/routes/api/schema/orphans/index.mjs +45 -44
  217. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  218. package/dist/astro/routes/api/search/enable.d.mts +1 -0
  219. package/dist/astro/routes/api/search/enable.d.mts.map +1 -1
  220. package/dist/astro/routes/api/search/enable.mjs +17 -13
  221. package/dist/astro/routes/api/search/enable.mjs.map +1 -1
  222. package/dist/astro/routes/api/search/index.mjs +11 -11
  223. package/dist/astro/routes/api/search/rebuild.mjs +13 -13
  224. package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
  225. package/dist/astro/routes/api/search/stats.mjs +7 -7
  226. package/dist/astro/routes/api/search/suggest.mjs +11 -11
  227. package/dist/astro/routes/api/sections/_slug_.mjs +11 -11
  228. package/dist/astro/routes/api/sections/index.mjs +11 -11
  229. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +7 -7
  230. package/dist/astro/routes/api/settings/backups/archives/index.mjs +6 -6
  231. package/dist/astro/routes/api/settings/backups/export.mjs +7 -7
  232. package/dist/astro/routes/api/settings/backups/index.mjs +8 -8
  233. package/dist/astro/routes/api/settings/email.mjs +5 -5
  234. package/dist/astro/routes/api/settings.mjs +16 -16
  235. package/dist/astro/routes/api/setup/admin-verify.mjs +13 -13
  236. package/dist/astro/routes/api/setup/admin.mjs +12 -12
  237. package/dist/astro/routes/api/setup/dev-bypass.mjs +30 -30
  238. package/dist/astro/routes/api/setup/dev-reset.mjs +3 -3
  239. package/dist/astro/routes/api/setup/index.mjs +32 -32
  240. package/dist/astro/routes/api/setup/status.mjs +4 -4
  241. package/dist/astro/routes/api/snapshot.mjs +9 -9
  242. package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts +8 -0
  243. package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts.map +1 -0
  244. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +43 -0
  245. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -0
  246. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +16 -16
  247. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +16 -16
  248. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +16 -16
  249. package/dist/astro/routes/api/taxonomies/index.mjs +16 -16
  250. package/dist/astro/routes/api/themes/preview.mjs +7 -7
  251. package/dist/astro/routes/api/typegen.mjs +5 -5
  252. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  253. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  254. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  255. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +8 -8
  256. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +11 -11
  257. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +11 -11
  258. package/dist/astro/routes/api/widget-areas/_name_.mjs +6 -6
  259. package/dist/astro/routes/api/widget-areas/index.mjs +11 -11
  260. package/dist/astro/routes/api/widget-components.mjs +3 -3
  261. package/dist/astro/routes/robots.txt.mjs +9 -9
  262. package/dist/astro/routes/sitemap-_collection_.xml.mjs +12 -12
  263. package/dist/astro/routes/sitemap.xml.mjs +10 -10
  264. package/dist/astro/types.d.mts +19 -13
  265. package/dist/astro/types.d.mts.map +1 -1
  266. package/dist/auth/providers/github.d.mts +1 -1
  267. package/dist/auth/providers/google.d.mts +1 -1
  268. package/dist/{authorize-R5iYYrpa.mjs → authorize-qRhWQnel.mjs} +2 -2
  269. package/dist/{authorize-R5iYYrpa.mjs.map → authorize-qRhWQnel.mjs.map} +1 -1
  270. package/dist/{backup-LpQ1V_sR.mjs → backup-CtA6LDNw.mjs} +5 -5
  271. package/dist/{backup-LpQ1V_sR.mjs.map → backup-CtA6LDNw.mjs.map} +1 -1
  272. package/dist/{byline-C5TAqs8N.mjs → byline-Cp6csL84.mjs} +9 -9
  273. package/dist/{byline-C5TAqs8N.mjs.map → byline-Cp6csL84.mjs.map} +1 -1
  274. package/dist/{byline-fields-T9ZLZMxJ.mjs → byline-fields-CNc0Yq5i.mjs} +2 -2
  275. package/dist/{byline-fields-T9ZLZMxJ.mjs.map → byline-fields-CNc0Yq5i.mjs.map} +1 -1
  276. package/dist/{byline-registry-BCuOp4UF.mjs → byline-registry-ZkPKqEwC.mjs} +4 -4
  277. package/dist/{byline-registry-BCuOp4UF.mjs.map → byline-registry-ZkPKqEwC.mjs.map} +1 -1
  278. package/dist/{bylines-B8-WGdja.mjs → bylines-CEj5CdT4.mjs} +7 -7
  279. package/dist/{bylines-B8-WGdja.mjs.map → bylines-CEj5CdT4.mjs.map} +1 -1
  280. package/dist/{bylines-DGuqBLjV.mjs → bylines-cf-C9lmF.mjs} +4 -4
  281. package/dist/{bylines-DGuqBLjV.mjs.map → bylines-cf-C9lmF.mjs.map} +1 -1
  282. package/dist/cache-C9FptTFR.mjs +108 -0
  283. package/dist/cache-C9FptTFR.mjs.map +1 -0
  284. package/dist/{challenge-store-BFzgFRog.mjs → challenge-store-D9VNKELo.mjs} +1 -1
  285. package/dist/{challenge-store-BFzgFRog.mjs.map → challenge-store-D9VNKELo.mjs.map} +1 -1
  286. package/dist/{chunks-D5dlPeRb.mjs → chunks-DbYzib_k.mjs} +2 -2
  287. package/dist/{chunks-D5dlPeRb.mjs.map → chunks-DbYzib_k.mjs.map} +1 -1
  288. package/dist/cli/index.mjs +40 -32
  289. package/dist/cli/index.mjs.map +1 -1
  290. package/dist/client/cf-access.d.mts +1 -1
  291. package/dist/client/index.d.mts +1 -1
  292. package/dist/client/index.mjs +2 -2
  293. package/dist/{comment-DnTxxVHv.mjs → comment-BIkJ6Fil.mjs} +3 -3
  294. package/dist/{comment-DnTxxVHv.mjs.map → comment-BIkJ6Fil.mjs.map} +1 -1
  295. package/dist/{comment-reaction-BUlUz2bh.mjs → comment-reaction-D-laEceb.mjs} +2 -2
  296. package/dist/{comment-reaction-BUlUz2bh.mjs.map → comment-reaction-D-laEceb.mjs.map} +1 -1
  297. package/dist/{comments-QPKgK3nk.mjs → comments-0_w6KChA.mjs} +3 -3
  298. package/dist/{comments-QPKgK3nk.mjs.map → comments-0_w6KChA.mjs.map} +1 -1
  299. package/dist/{components-DZkL4Fx0.mjs → components-BPxrRjpE.mjs} +1 -1
  300. package/dist/{components-DZkL4Fx0.mjs.map → components-BPxrRjpE.mjs.map} +1 -1
  301. package/dist/{content-CpfKV9QE.mjs → content-CJ94NEx5.mjs} +165 -112
  302. package/dist/content-CJ94NEx5.mjs.map +1 -0
  303. package/dist/{content-refresh-DgqUBeTv.mjs → content-refresh-Xw7aKmou.mjs} +6 -6
  304. package/dist/{content-refresh-DgqUBeTv.mjs.map → content-refresh-Xw7aKmou.mjs.map} +1 -1
  305. package/dist/{context-kO4YZB03.mjs → context-C_1nrqnW.mjs} +14 -14
  306. package/dist/{context-kO4YZB03.mjs.map → context-C_1nrqnW.mjs.map} +1 -1
  307. package/dist/{cron-BlKIMD_e.mjs → cron-DRV-UC4K.mjs} +1 -1
  308. package/dist/{cron-BlKIMD_e.mjs.map → cron-DRV-UC4K.mjs.map} +1 -1
  309. package/dist/{dashboard-DoBwPnON.mjs → dashboard-DINnNVVU.mjs} +5 -5
  310. package/dist/{dashboard-DoBwPnON.mjs.map → dashboard-DINnNVVU.mjs.map} +1 -1
  311. package/dist/db/index.d.mts +3 -3
  312. package/dist/db/index.mjs +2 -2
  313. package/dist/db/index.mjs.map +1 -1
  314. package/dist/db/libsql.d.mts +1 -1
  315. package/dist/db/postgres.d.mts +1 -1
  316. package/dist/db/sqlite.d.mts +1 -1
  317. package/dist/{default-C4v9KP56.mjs → default-BD2FG0xV.mjs} +1 -1
  318. package/dist/{default-C4v9KP56.mjs.map → default-BD2FG0xV.mjs.map} +1 -1
  319. package/dist/deferred-tasks-C-WRCcKo.d.mts +9 -0
  320. package/dist/deferred-tasks-C-WRCcKo.d.mts.map +1 -0
  321. package/dist/{device-flow-DhUsyJU6.mjs → device-flow-CTqd8mud.mjs} +5 -5
  322. package/dist/{device-flow-DhUsyJU6.mjs.map → device-flow-CTqd8mud.mjs.map} +1 -1
  323. package/dist/{dialect-helpers-DFwyWjTM.mjs → dialect-helpers-BSbfaO3F.mjs} +24 -16
  324. package/dist/dialect-helpers-BSbfaO3F.mjs.map +1 -0
  325. package/dist/{email-console-C-9Ng8DM.mjs → email-console-C6WdcWbL.mjs} +1 -1
  326. package/dist/{email-console-C-9Ng8DM.mjs.map → email-console-C6WdcWbL.mjs.map} +1 -1
  327. package/dist/{error-DmmN74gW.mjs → error-oh_9i6bR.mjs} +3 -3
  328. package/dist/{error-DmmN74gW.mjs.map → error-oh_9i6bR.mjs.map} +1 -1
  329. package/dist/{errors-DtEXIQQV.mjs → errors-BucQ8VYz.mjs} +2 -1
  330. package/dist/errors-BucQ8VYz.mjs.map +1 -0
  331. package/dist/{escape-CdW31WsG.mjs → escape-CXps5F_v.mjs} +1 -1
  332. package/dist/{escape-CdW31WsG.mjs.map → escape-CXps5F_v.mjs.map} +1 -1
  333. package/dist/{field-defs-cache-QMVnzTH6.mjs → field-defs-cache-WD82dMQq.mjs} +4 -4
  334. package/dist/{field-defs-cache-QMVnzTH6.mjs.map → field-defs-cache-WD82dMQq.mjs.map} +1 -1
  335. package/dist/{fts-manager-vncsZIJm.mjs → fts-manager-BITakW_R.mjs} +37 -13
  336. package/dist/fts-manager-BITakW_R.mjs.map +1 -0
  337. package/dist/{hash-Cb7U1b5M.mjs → hash-Cg0TXJPV.mjs} +1 -1
  338. package/dist/{hash-Cb7U1b5M.mjs.map → hash-Cg0TXJPV.mjs.map} +1 -1
  339. package/dist/{hreflang-CZbSx6Bg.d.mts → hreflang-BaLCNcDx.d.mts} +2 -2
  340. package/dist/{hreflang-CZbSx6Bg.d.mts.map → hreflang-BaLCNcDx.d.mts.map} +1 -1
  341. package/dist/{hreflang-Bg-zMpkr.mjs → hreflang-BdZuVBhL.mjs} +6 -6
  342. package/dist/{hreflang-Bg-zMpkr.mjs.map → hreflang-BdZuVBhL.mjs.map} +1 -1
  343. package/dist/{import-PKhLeXvn.mjs → import-C-9uqyT3.mjs} +6 -6
  344. package/dist/{import-PKhLeXvn.mjs.map → import-C-9uqyT3.mjs.map} +1 -1
  345. package/dist/{index-j9rTh3cp.d.mts → index-M37iz29I.d.mts} +75 -35
  346. package/dist/index-M37iz29I.d.mts.map +1 -0
  347. package/dist/{index-BeNto_Vs.d.mts → index-MGw47uWq.d.mts} +3 -3
  348. package/dist/{index-BeNto_Vs.d.mts.map → index-MGw47uWq.d.mts.map} +1 -1
  349. package/dist/index.d.mts +19 -19
  350. package/dist/index.mjs +68 -66
  351. package/dist/{load-BwTdWE8B.mjs → load-DV1DZuUJ.mjs} +2 -2
  352. package/dist/{load-BwTdWE8B.mjs.map → load-DV1DZuUJ.mjs.map} +1 -1
  353. package/dist/{loader-C1XOLV5b.mjs → loader-CGugY7Zl.mjs} +29 -15
  354. package/dist/loader-CGugY7Zl.mjs.map +1 -0
  355. package/dist/{manifest-schema-bCq54i7F.mjs → manifest-schema-Da7kj23y.mjs} +1 -1
  356. package/dist/{manifest-schema-bCq54i7F.mjs.map → manifest-schema-Da7kj23y.mjs.map} +1 -1
  357. package/dist/media/index.d.mts +1 -1
  358. package/dist/media/local-runtime.d.mts +12 -12
  359. package/dist/media/local-runtime.mjs +8 -8
  360. package/dist/{media-tQDZEdu7.mjs → media-BP7S-nHT.mjs} +2 -2
  361. package/dist/{media-tQDZEdu7.mjs.map → media-BP7S-nHT.mjs.map} +1 -1
  362. package/dist/{media-DWIzoVFL.mjs → media-OhYL-7Io.mjs} +1 -1
  363. package/dist/{media-DWIzoVFL.mjs.map → media-OhYL-7Io.mjs.map} +1 -1
  364. package/dist/{media-allowlist-B7TPD6aq.mjs → media-allowlist-D-peH1H5.mjs} +1 -1
  365. package/dist/{media-allowlist-B7TPD6aq.mjs.map → media-allowlist-D-peH1H5.mjs.map} +1 -1
  366. package/dist/{media-upload-Bc9Nc5Zf.mjs → media-upload-DtrwU2B0.mjs} +7 -7
  367. package/dist/{media-upload-Bc9Nc5Zf.mjs.map → media-upload-DtrwU2B0.mjs.map} +1 -1
  368. package/dist/{media-url-BCm5vBn6.mjs → media-url-DYs_YLOx.mjs} +1 -1
  369. package/dist/{media-url-BCm5vBn6.mjs.map → media-url-DYs_YLOx.mjs.map} +1 -1
  370. package/dist/{media-usage-DPJcNaAq.mjs → media-usage-BlxfTkN0.mjs} +8 -8
  371. package/dist/{media-usage-DPJcNaAq.mjs.map → media-usage-BlxfTkN0.mjs.map} +1 -1
  372. package/dist/{menus-BxPLFfIc.mjs → menus-BcQWxeXu.mjs} +4 -4
  373. package/dist/{menus-BxPLFfIc.mjs.map → menus-BcQWxeXu.mjs.map} +1 -1
  374. package/dist/{menus-C6qhqP4X.mjs → menus-Y54RDPIN.mjs} +215 -38
  375. package/dist/menus-Y54RDPIN.mjs.map +1 -0
  376. package/dist/{mode-fiXRMfeA.mjs → mode-BWyTBe3I.mjs} +1 -1
  377. package/dist/{mode-fiXRMfeA.mjs.map → mode-BWyTBe3I.mjs.map} +1 -1
  378. package/dist/{oauth-authorization-C9tFinly.mjs → oauth-authorization-ClvktmrL.mjs} +5 -5
  379. package/dist/{oauth-authorization-C9tFinly.mjs.map → oauth-authorization-ClvktmrL.mjs.map} +1 -1
  380. package/dist/{oauth-clients-BwTxE1aE.mjs → oauth-clients-8ao-U66l.mjs} +1 -1
  381. package/dist/{oauth-clients-BwTxE1aE.mjs.map → oauth-clients-8ao-U66l.mjs.map} +1 -1
  382. package/dist/{oauth-state-store-DaRUwPWO.mjs → oauth-state-store-B1ofdTiQ.mjs} +1 -1
  383. package/dist/{oauth-state-store-DaRUwPWO.mjs.map → oauth-state-store-B1ofdTiQ.mjs.map} +1 -1
  384. package/dist/{oauth-user-lookup-CzuzOSlF.mjs → oauth-user-lookup-CscEuIiR.mjs} +1 -1
  385. package/dist/{oauth-user-lookup-CzuzOSlF.mjs.map → oauth-user-lookup-CscEuIiR.mjs.map} +1 -1
  386. package/dist/object-cache/memory.d.mts +1 -1
  387. package/dist/{object-cache-Bok5j2ae.mjs → object-cache-71tPfV4A.mjs} +106 -5
  388. package/dist/object-cache-71tPfV4A.mjs.map +1 -0
  389. package/dist/{options-aeIs8WFu.d.mts → options-CA4eVX6P.d.mts} +3 -3
  390. package/dist/{options-aeIs8WFu.d.mts.map → options-CA4eVX6P.d.mts.map} +1 -1
  391. package/dist/{options-BlmBHTvX.mjs → options-CBXIHUoT.mjs} +1 -1
  392. package/dist/{options-BlmBHTvX.mjs.map → options-CBXIHUoT.mjs.map} +1 -1
  393. package/dist/page/index.d.mts +2 -2
  394. package/dist/{parse-BL49yb9D.mjs → parse-CJKNuewM.mjs} +2 -2
  395. package/dist/{parse-BL49yb9D.mjs.map → parse-CJKNuewM.mjs.map} +1 -1
  396. package/dist/{passkey-config-KMhgFjdQ.mjs → passkey-config-D-zrzMJI.mjs} +1 -1
  397. package/dist/{passkey-config-KMhgFjdQ.mjs.map → passkey-config-D-zrzMJI.mjs.map} +1 -1
  398. package/dist/{placeholder-Dxo5Zt58.d.mts → placeholder-DaeNS-VG.d.mts} +1 -1
  399. package/dist/{placeholder-Dxo5Zt58.d.mts.map → placeholder-DaeNS-VG.d.mts.map} +1 -1
  400. package/dist/plugin-types.d.mts +1 -1
  401. package/dist/plugin-utils.d.mts +10 -10
  402. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
  403. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  404. package/dist/{portable-text-CNahFfj6.mjs → portable-text-B_kqBmXB.mjs} +1 -1
  405. package/dist/{portable-text-CNahFfj6.mjs.map → portable-text-B_kqBmXB.mjs.map} +1 -1
  406. package/dist/{preview-D4Jnbfx7.mjs → preview-BU6PaLpM.mjs} +2 -2
  407. package/dist/{preview-D4Jnbfx7.mjs.map → preview-BU6PaLpM.mjs.map} +1 -1
  408. package/dist/{public-url-DSGTnJFw.mjs → public-url-Ox9oOlgp.mjs} +14 -3
  409. package/dist/public-url-Ox9oOlgp.mjs.map +1 -0
  410. package/dist/{query-DCiXI7OZ.mjs → query-C5w7PsRQ.mjs} +20 -41
  411. package/dist/query-C5w7PsRQ.mjs.map +1 -0
  412. package/dist/{query-1xOcEy18.mjs → query-rC9HKv1N.mjs} +4 -4
  413. package/dist/{query-1xOcEy18.mjs.map → query-rC9HKv1N.mjs.map} +1 -1
  414. package/dist/{rate-limit-Vkp9gQ1Y.mjs → rate-limit-kug6q99V.mjs} +2 -2
  415. package/dist/{rate-limit-Vkp9gQ1Y.mjs.map → rate-limit-kug6q99V.mjs.map} +1 -1
  416. package/dist/{redirect-BJ6d1yHh.mjs → redirect-DIJxXKOG.mjs} +3 -3
  417. package/dist/{redirect-BJ6d1yHh.mjs.map → redirect-DIJxXKOG.mjs.map} +1 -1
  418. package/dist/{redirect-CDgfv9vh.mjs → redirect-Egf2R9yP.mjs} +1 -1
  419. package/dist/{redirect-CDgfv9vh.mjs.map → redirect-Egf2R9yP.mjs.map} +1 -1
  420. package/dist/{redirects-Cwkq2H-G.mjs → redirects-Bzm812bY.mjs} +5 -5
  421. package/dist/{redirects-Cwkq2H-G.mjs.map → redirects-Bzm812bY.mjs.map} +1 -1
  422. package/dist/{redirects-BOn2DIUs.mjs → redirects-G8wAhWAf.mjs} +20 -6
  423. package/dist/redirects-G8wAhWAf.mjs.map +1 -0
  424. package/dist/{registry-BP1JK2xh.mjs → registry-Dbodb4Uo.mjs} +59 -14
  425. package/dist/registry-Dbodb4Uo.mjs.map +1 -0
  426. package/dist/{relation-DJ5DZh7B.mjs → relation-Cbe3Sd3m.mjs} +3 -3
  427. package/dist/{relation-DJ5DZh7B.mjs.map → relation-Cbe3Sd3m.mjs.map} +1 -1
  428. package/dist/{relations-C4duJnwI.mjs → relations-Bw_E1TXo.mjs} +3 -3
  429. package/dist/{relations-C4duJnwI.mjs.map → relations-Bw_E1TXo.mjs.map} +1 -1
  430. package/dist/{relations-DHFQGX0h.d.mts → relations-G2ta7GIl.d.mts} +36 -9
  431. package/dist/relations-G2ta7GIl.d.mts.map +1 -0
  432. package/dist/{relations-DpTjftCl.mjs → relations-qHwpUe66.mjs} +5 -5
  433. package/dist/{relations-DpTjftCl.mjs.map → relations-qHwpUe66.mjs.map} +1 -1
  434. package/dist/request-context.d.mts +3 -0
  435. package/dist/request-context.d.mts.map +1 -1
  436. package/dist/request-context.mjs.map +1 -1
  437. package/dist/{request-meta-DzXYYI-n.mjs → request-meta-Df-jFbAd.mjs} +2 -2
  438. package/dist/{request-meta-DzXYYI-n.mjs.map → request-meta-Df-jFbAd.mjs.map} +1 -1
  439. package/dist/{resolve-BUvFE0Lr.mjs → resolve-DZzG4HqM.mjs} +1 -1
  440. package/dist/{resolve-BUvFE0Lr.mjs.map → resolve-DZzG4HqM.mjs.map} +1 -1
  441. package/dist/{runner-BsI18UgP.mjs → runner-BFTrkYhu.mjs} +482 -216
  442. package/dist/runner-BFTrkYhu.mjs.map +1 -0
  443. package/dist/{runner-CHdkERbt.d.mts → runner-BaADli8l.d.mts} +2 -2
  444. package/dist/{runner-CHdkERbt.d.mts.map → runner-BaADli8l.d.mts.map} +1 -1
  445. package/dist/runtime.d.mts +11 -11
  446. package/dist/runtime.mjs +3 -3
  447. package/dist/{schema-eqZhkR0S.mjs → schema-CiQXr6_F.mjs} +3 -3
  448. package/dist/{schema-eqZhkR0S.mjs.map → schema-CiQXr6_F.mjs.map} +1 -1
  449. package/dist/{scopes-Be2d8bSb.mjs → scopes-CNJVT5i5.mjs} +3 -3
  450. package/dist/{scopes-Be2d8bSb.mjs.map → scopes-CNJVT5i5.mjs.map} +1 -1
  451. package/dist/{search-CeO678cp.mjs → search-FZhx5dDU.mjs} +6 -6
  452. package/dist/{search-CeO678cp.mjs.map → search-FZhx5dDU.mjs.map} +1 -1
  453. package/dist/{secrets-870d-7yA.mjs → secrets-DLd8RXee.mjs} +4 -4
  454. package/dist/{secrets-870d-7yA.mjs.map → secrets-DLd8RXee.mjs.map} +1 -1
  455. package/dist/{sections-DSuEfirn.mjs → sections-BrIrMUCn.mjs} +3 -3
  456. package/dist/{sections-DSuEfirn.mjs.map → sections-BrIrMUCn.mjs.map} +1 -1
  457. package/dist/seed/index.d.mts +2 -2
  458. package/dist/seed/index.mjs +24 -24
  459. package/dist/seo/index.d.mts +3 -3
  460. package/dist/seo/index.mjs +17 -17
  461. package/dist/{seo-C3GDfT0V.mjs → seo-CobNfOvb.mjs} +2 -2
  462. package/dist/{seo-C3GDfT0V.mjs.map → seo-CobNfOvb.mjs.map} +1 -1
  463. package/dist/{seo-BAPKiED8.mjs → seo-DTHRntNC.mjs} +3 -3
  464. package/dist/{seo-BAPKiED8.mjs.map → seo-DTHRntNC.mjs.map} +1 -1
  465. package/dist/{service-DyFT-k-R.mjs → service-B-pYGPgR.mjs} +4 -4
  466. package/dist/{service-DyFT-k-R.mjs.map → service-B-pYGPgR.mjs.map} +1 -1
  467. package/dist/{session-user-BrK2zz7S.mjs → session-user-BvM9-zNl.mjs} +2 -2
  468. package/dist/{session-user-BrK2zz7S.mjs.map → session-user-BvM9-zNl.mjs.map} +1 -1
  469. package/dist/{settings-CREKyj8I.mjs → settings-DPHau-WH.mjs} +3 -3
  470. package/dist/{settings-CREKyj8I.mjs.map → settings-DPHau-WH.mjs.map} +1 -1
  471. package/dist/{settings-DgKouY2S.mjs → settings-kJhGiQ4z.mjs} +7 -7
  472. package/dist/{settings-DgKouY2S.mjs.map → settings-kJhGiQ4z.mjs.map} +1 -1
  473. package/dist/{setup-complete-L7bMK2VI.mjs → setup-complete-u50Qm8rY.mjs} +2 -2
  474. package/dist/{setup-complete-L7bMK2VI.mjs.map → setup-complete-u50Qm8rY.mjs.map} +1 -1
  475. package/dist/{setup-nonce-Ct5tjWcq.mjs → setup-nonce-DCjiiOD2.mjs} +1 -1
  476. package/dist/{setup-nonce-Ct5tjWcq.mjs.map → setup-nonce-DCjiiOD2.mjs.map} +1 -1
  477. package/dist/{single-flight-cache-C2exrGAi.mjs → single-flight-cache-DW3w11Pu.mjs} +1 -1
  478. package/dist/{single-flight-cache-C2exrGAi.mjs.map → single-flight-cache-DW3w11Pu.mjs.map} +1 -1
  479. package/dist/site-url-I-lJ5kma.mjs +16 -0
  480. package/dist/site-url-I-lJ5kma.mjs.map +1 -0
  481. package/dist/{snapshot-BdpUJKD-.mjs → snapshot-I_fjLDaD.mjs} +2 -2
  482. package/dist/{snapshot-BdpUJKD-.mjs.map → snapshot-I_fjLDaD.mjs.map} +1 -1
  483. package/dist/{ssrf-CviKqWmq.mjs → ssrf-CTa6v21i.mjs} +1 -1
  484. package/dist/{ssrf-CviKqWmq.mjs.map → ssrf-CTa6v21i.mjs.map} +1 -1
  485. package/dist/{state-xxv6ZTMv.mjs → state-CQvoefYP.mjs} +1 -1
  486. package/dist/{state-xxv6ZTMv.mjs.map → state-CQvoefYP.mjs.map} +1 -1
  487. package/dist/{status-COfycGIU.mjs → status-CIsmlC1E.mjs} +1 -1
  488. package/dist/{status-COfycGIU.mjs.map → status-CIsmlC1E.mjs.map} +1 -1
  489. package/dist/storage/local.d.mts +1 -1
  490. package/dist/storage/local.mjs +1 -1
  491. package/dist/storage/s3.d.mts +1 -1
  492. package/dist/storage/s3.mjs +1 -1
  493. package/dist/{taxonomies-Dbdki3Un.mjs → taxonomies-CKuaZER5.mjs} +85 -5
  494. package/dist/taxonomies-CKuaZER5.mjs.map +1 -0
  495. package/dist/{taxonomies-B61CRSha.mjs → taxonomies-CLN1N92E.mjs} +16 -17
  496. package/dist/taxonomies-CLN1N92E.mjs.map +1 -0
  497. package/dist/{taxonomy-DvwWAPvA.mjs → taxonomy-pOrB2kZe.mjs} +142 -15
  498. package/dist/taxonomy-pOrB2kZe.mjs.map +1 -0
  499. package/dist/{tokens-DVltpO2D.mjs → tokens-DagaJGK8.mjs} +1 -1
  500. package/dist/{tokens-DVltpO2D.mjs.map → tokens-DagaJGK8.mjs.map} +1 -1
  501. package/dist/{transaction-D0FOsb3X.mjs → transaction-NQj4VJ7Z.mjs} +1 -1
  502. package/dist/{transaction-D0FOsb3X.mjs.map → transaction-NQj4VJ7Z.mjs.map} +1 -1
  503. package/dist/{transport-DokJOScS.d.mts → transport-CAwTaTp_.d.mts} +1 -1
  504. package/dist/{transport-DokJOScS.d.mts.map → transport-CAwTaTp_.d.mts.map} +1 -1
  505. package/dist/{transport-BWQo0Foy.mjs → transport-DVsa5YYo.mjs} +1 -1
  506. package/dist/{transport-BWQo0Foy.mjs.map → transport-DVsa5YYo.mjs.map} +1 -1
  507. package/dist/{trusted-proxy-CwjQj0YG.mjs → trusted-proxy-D20_7fyX.mjs} +1 -1
  508. package/dist/{trusted-proxy-CwjQj0YG.mjs.map → trusted-proxy-D20_7fyX.mjs.map} +1 -1
  509. package/dist/{types-DqZVPILm.d.mts → types-BGWPMiBO.d.mts} +2 -2
  510. package/dist/{types-DqZVPILm.d.mts.map → types-BGWPMiBO.d.mts.map} +1 -1
  511. package/dist/{types-DsAMyD_o.d.mts → types-BK6U01Ro.d.mts} +1 -1
  512. package/dist/{types-DsAMyD_o.d.mts.map → types-BK6U01Ro.d.mts.map} +1 -1
  513. package/dist/{types-BD40g7zz.mjs → types-BjxTKxrZ.mjs} +1 -1
  514. package/dist/{types-BD40g7zz.mjs.map → types-BjxTKxrZ.mjs.map} +1 -1
  515. package/dist/{types-jISEalTX.d.mts → types-BnFP1M3J.d.mts} +22 -1
  516. package/dist/types-BnFP1M3J.d.mts.map +1 -0
  517. package/dist/{types-Dr2GLlwu.d.mts → types-C8oz_ayJ.d.mts} +1 -1
  518. package/dist/{types-Dr2GLlwu.d.mts.map → types-C8oz_ayJ.d.mts.map} +1 -1
  519. package/dist/{types-C9P3ysxq.d.mts → types-CbUcuxxc.d.mts} +1 -1
  520. package/dist/{types-C9P3ysxq.d.mts.map → types-CbUcuxxc.d.mts.map} +1 -1
  521. package/dist/{types-XrQQ-Aex.mjs → types-Cd8iuS1J.mjs} +10 -3
  522. package/dist/{types-XrQQ-Aex.mjs.map → types-Cd8iuS1J.mjs.map} +1 -1
  523. package/dist/types-CtzytVXn.mjs +18 -0
  524. package/dist/types-CtzytVXn.mjs.map +1 -0
  525. package/dist/{types-BQY7VYw6.d.mts → types-Cz8hNch2.d.mts} +5 -5
  526. package/dist/{types-BQY7VYw6.d.mts.map → types-Cz8hNch2.d.mts.map} +1 -1
  527. package/dist/{types-CcSZCcF1.d.mts → types-DLG4IFEQ.d.mts} +1 -1
  528. package/dist/{types-CcSZCcF1.d.mts.map → types-DLG4IFEQ.d.mts.map} +1 -1
  529. package/dist/{types-84Bque3-.d.mts → types-Dc4ntQjQ.d.mts} +10 -1
  530. package/dist/types-Dc4ntQjQ.d.mts.map +1 -0
  531. package/dist/{types-DQLZ58GV.d.mts → types-LoagAGzH.d.mts} +1 -1
  532. package/dist/{types-DQLZ58GV.d.mts.map → types-LoagAGzH.d.mts.map} +1 -1
  533. package/dist/{types-o7xo7VgH.mjs → types-NBI3rF-_.mjs} +3 -2
  534. package/dist/types-NBI3rF-_.mjs.map +1 -0
  535. package/dist/{upload-attempts-C5yd6Gae.mjs → upload-attempts-D8QAFLUJ.mjs} +1 -1
  536. package/dist/{upload-attempts-C5yd6Gae.mjs.map → upload-attempts-D8QAFLUJ.mjs.map} +1 -1
  537. package/dist/{user-BAumEmpA.mjs → user-DBrq4UVq.mjs} +3 -3
  538. package/dist/{user-BAumEmpA.mjs.map → user-DBrq4UVq.mjs.map} +1 -1
  539. package/dist/{utils-BbUgmeZG.mjs → utils-hLzi_2Fn.mjs} +2 -2
  540. package/dist/{utils-BbUgmeZG.mjs.map → utils-hLzi_2Fn.mjs.map} +1 -1
  541. package/dist/{validate-VPnKoIzW.mjs → validate-BpwoDOgt.mjs} +25 -2
  542. package/dist/validate-BpwoDOgt.mjs.map +1 -0
  543. package/dist/{validate-CdYcY-Ln.d.mts → validate-C3oSmA7F.d.mts} +15 -5
  544. package/dist/validate-C3oSmA7F.d.mts.map +1 -0
  545. package/dist/{validate-Bs_wT2ul.mjs → validate-mKo3RxBM.mjs} +3 -3
  546. package/dist/{validate-Bs_wT2ul.mjs.map → validate-mKo3RxBM.mjs.map} +1 -1
  547. package/dist/{validation-BngR0nit.mjs → validation-BgpucsvF.mjs} +10 -10
  548. package/dist/{validation-BngR0nit.mjs.map → validation-BgpucsvF.mjs.map} +1 -1
  549. package/dist/version-C4PnpmoV.mjs +7 -0
  550. package/dist/{version-Bdfb34kk.mjs.map → version-C4PnpmoV.mjs.map} +1 -1
  551. package/dist/{widgets-L-VndTKD.mjs → widgets-CJJtkiKh.mjs} +3 -3
  552. package/dist/{widgets-L-VndTKD.mjs.map → widgets-CJJtkiKh.mjs.map} +1 -1
  553. package/dist/{wxr-taxonomies-BexjtNid.mjs → wxr-taxonomies-9PPSy8X6.mjs} +4 -4
  554. package/dist/{wxr-taxonomies-BexjtNid.mjs.map → wxr-taxonomies-9PPSy8X6.mjs.map} +1 -1
  555. package/dist/{wxr-taxonomies-DueaLFd9.d.mts → wxr-taxonomies-Bkpp_2tK.d.mts} +1 -1
  556. package/dist/{wxr-taxonomies-DueaLFd9.d.mts.map → wxr-taxonomies-Bkpp_2tK.d.mts.map} +1 -1
  557. package/dist/{zod-generator-DMzfga3g.mjs → zod-generator-CG64Hnbh.mjs} +3 -3
  558. package/dist/{zod-generator-DMzfga3g.mjs.map → zod-generator-CG64Hnbh.mjs.map} +1 -1
  559. package/package.json +11 -8
  560. package/src/after.ts +6 -1
  561. package/src/api/errors.ts +1 -0
  562. package/src/api/handlers/content.ts +45 -4
  563. package/src/api/handlers/marketplace.ts +14 -0
  564. package/src/api/handlers/registry.ts +14 -0
  565. package/src/api/handlers/revision.ts +19 -5
  566. package/src/api/handlers/schema.ts +37 -0
  567. package/src/api/handlers/snapshot.ts +1 -1
  568. package/src/api/handlers/taxonomies.ts +101 -1
  569. package/src/api/openapi/document.ts +48 -0
  570. package/src/api/public-url.ts +14 -2
  571. package/src/api/schemas/schema.ts +13 -0
  572. package/src/api/schemas/search.ts +2 -0
  573. package/src/api/schemas/taxonomies.ts +18 -0
  574. package/src/api/site-url.ts +16 -6
  575. package/src/astro/integration/routes.ts +14 -0
  576. package/src/astro/integration/runtime.ts +13 -0
  577. package/src/astro/integration/virtual-modules.ts +2 -0
  578. package/src/astro/middleware/redirect.ts +2 -10
  579. package/src/astro/middleware/scoped-db.ts +35 -4
  580. package/src/astro/middleware.ts +56 -22
  581. package/src/astro/routes/api/admin/comments/[id]/status.ts +1 -1
  582. package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +2 -2
  583. package/src/astro/routes/api/auth/invite/index.ts +2 -2
  584. package/src/astro/routes/api/auth/magic-link/send.ts +2 -2
  585. package/src/astro/routes/api/auth/signup/request.ts +2 -2
  586. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +1 -1
  587. package/src/astro/routes/api/schema/collections/reorder.ts +31 -0
  588. package/src/astro/routes/api/schema/index.ts +2 -0
  589. package/src/astro/routes/api/search/enable.ts +5 -1
  590. package/src/astro/routes/api/search/rebuild.ts +6 -1
  591. package/src/astro/routes/api/taxonomies/[name]/reorder.ts +41 -0
  592. package/src/astro/types.ts +6 -0
  593. package/src/cleanup.ts +31 -26
  594. package/src/cli/commands/dev.ts +13 -3
  595. package/src/cli/commands/export-seed.ts +2 -0
  596. package/src/cli/index.ts +1 -1
  597. package/src/components/CommentForm.astro +1 -1
  598. package/src/components/Comments.astro +1 -1
  599. package/src/components/InlinePortableTextEditor.tsx +171 -31
  600. package/src/components/OrderedList.astro +14 -0
  601. package/src/components/PortableText.astro +26 -4
  602. package/src/components/index.ts +12 -1
  603. package/src/components/ordered-list.ts +549 -0
  604. package/src/content/converters/numbered-list.ts +148 -0
  605. package/src/content/converters/portable-text-to-prosemirror.ts +59 -9
  606. package/src/content/converters/prosemirror-to-portable-text.ts +23 -12
  607. package/src/content/converters/types.ts +2 -0
  608. package/src/content/portable-text-lists.ts +205 -0
  609. package/src/database/dialect-helpers.ts +3 -22
  610. package/src/database/migrations/055_content_translation_group_locale_index.ts +67 -0
  611. package/src/database/migrations/056_taxonomy_term_sort_order.ts +189 -0
  612. package/src/database/migrations/057_collection_hidden.ts +24 -0
  613. package/src/database/migrations/058_collection_sort_order.ts +20 -0
  614. package/src/database/migrations/059_revision_prune_queue.ts +36 -0
  615. package/src/database/migrations/runner.ts +10 -0
  616. package/src/database/repositories/content.ts +191 -178
  617. package/src/database/repositories/revision.ts +82 -17
  618. package/src/database/repositories/taxonomy.ts +202 -7
  619. package/src/database/repositories/types.ts +7 -0
  620. package/src/database/types.ts +13 -1
  621. package/src/database/validate.ts +39 -0
  622. package/src/db/adapters.ts +8 -0
  623. package/src/deferred-tasks.ts +71 -0
  624. package/src/emdash-runtime.ts +268 -92
  625. package/src/index.ts +2 -0
  626. package/src/loader.ts +46 -24
  627. package/src/media/usage/gc.ts +50 -0
  628. package/src/object-cache/index.ts +160 -3
  629. package/src/plugins/routes.ts +34 -0
  630. package/src/plugins/storage-indexes.ts +50 -11
  631. package/src/query.ts +21 -42
  632. package/src/redirects/cache.ts +79 -19
  633. package/src/request-context.ts +3 -0
  634. package/src/scheduled-publish.ts +6 -1
  635. package/src/schema/registry.ts +95 -3
  636. package/src/schema/types.ts +24 -0
  637. package/src/search/fts-manager.ts +50 -10
  638. package/src/search/index.ts +2 -0
  639. package/src/search/types.ts +6 -0
  640. package/src/seed/apply.ts +22 -2
  641. package/src/seed/types.ts +10 -0
  642. package/src/taxonomies/index.ts +11 -7
  643. package/src/taxonomies/term-counts.ts +2 -4
  644. package/src/ui-comments.ts +19 -0
  645. package/src/ui.ts +11 -3
  646. package/src/virtual-modules.d.ts +8 -0
  647. package/dist/adapters-C0ONzkUP.d.mts.map +0 -1
  648. package/dist/after-B1IIdH3Y.mjs +0 -29
  649. package/dist/after-B1IIdH3Y.mjs.map +0 -1
  650. package/dist/api-DgPbMlng.mjs.map +0 -1
  651. package/dist/apply-1_6ra7NP.mjs.map +0 -1
  652. package/dist/cache-B26cufFd.mjs +0 -65
  653. package/dist/cache-B26cufFd.mjs.map +0 -1
  654. package/dist/content-CpfKV9QE.mjs.map +0 -1
  655. package/dist/dialect-helpers-DFwyWjTM.mjs.map +0 -1
  656. package/dist/errors-DtEXIQQV.mjs.map +0 -1
  657. package/dist/fts-manager-vncsZIJm.mjs.map +0 -1
  658. package/dist/index-j9rTh3cp.d.mts.map +0 -1
  659. package/dist/loader-C1XOLV5b.mjs.map +0 -1
  660. package/dist/menus-C6qhqP4X.mjs.map +0 -1
  661. package/dist/object-cache-Bok5j2ae.mjs.map +0 -1
  662. package/dist/public-url-DSGTnJFw.mjs.map +0 -1
  663. package/dist/query-DCiXI7OZ.mjs.map +0 -1
  664. package/dist/redirects-BOn2DIUs.mjs.map +0 -1
  665. package/dist/registry-BP1JK2xh.mjs.map +0 -1
  666. package/dist/relations-DHFQGX0h.d.mts.map +0 -1
  667. package/dist/runner-BsI18UgP.mjs.map +0 -1
  668. package/dist/site-url-8N3kSyVZ.mjs +0 -13
  669. package/dist/site-url-8N3kSyVZ.mjs.map +0 -1
  670. package/dist/taxonomies-B61CRSha.mjs.map +0 -1
  671. package/dist/taxonomies-Dbdki3Un.mjs.map +0 -1
  672. package/dist/taxonomy-DvwWAPvA.mjs.map +0 -1
  673. package/dist/types-84Bque3-.d.mts.map +0 -1
  674. package/dist/types-jISEalTX.d.mts.map +0 -1
  675. package/dist/types-o7xo7VgH.mjs.map +0 -1
  676. package/dist/validate-CdYcY-Ln.d.mts.map +0 -1
  677. package/dist/validate-VPnKoIzW.mjs.map +0 -1
  678. package/dist/version-Bdfb34kk.mjs +0 -7
  679. /package/dist/{api-tokens-CEsW_jCg.mjs → api-tokens-Dai9dND0.mjs} +0 -0
  680. /package/dist/{ssrf-BB__xfNM.mjs → ssrf-Cj568Dgs.mjs} +0 -0
  681. /package/dist/{types-CK0LxPUQ.mjs → types-DKuWPYV5.mjs} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"zod-generator-DMzfga3g.mjs","names":[],"sources":["../src/schema/zod-generator.ts"],"sourcesContent":["import { z, type ZodTypeAny } from \"zod\";\n\nimport { hashString } from \"../utils/hash.js\";\nimport type { Field, FieldType, CollectionWithFields } from \"./types.js\";\n\n/** Pattern to split on underscores, hyphens, and spaces for PascalCase conversion */\nconst PASCAL_CASE_SPLIT_PATTERN = /[_\\-\\s]+/;\n\n/**\n * Generate a Zod schema from a collection's field definitions\n *\n * This allows runtime validation of content based on dynamically\n * defined schemas stored in D1.\n */\nexport function generateZodSchema(\n\tcollection: CollectionWithFields,\n): z.ZodObject<Record<string, ZodTypeAny>> {\n\tconst shape: Record<string, ZodTypeAny> = {};\n\n\tfor (const field of collection.fields) {\n\t\tshape[field.slug] = generateFieldSchema(field);\n\t}\n\n\treturn z.object(shape);\n}\n\n/**\n * Generate Zod schema for a single field\n */\nexport function generateFieldSchema(field: Field): ZodTypeAny {\n\tlet schema = getBaseSchema(field.type, field);\n\n\t// Apply validation rules\n\tif (field.validation) {\n\t\tschema = applyValidation(schema, field);\n\t}\n\n\t// Apply required/optional. Non-required fields use `.nullish()` rather\n\t// than `.optional()` because the underlying SQLite columns are nullable\n\t// (see `SchemaRegistry.addFieldColumn` -- non-required fields are added\n\t// without `NOT NULL`). The admin re-sends what it loaded from the\n\t// server on autosave, so any field that's actually `null` in the DB\n\t// must round-trip cleanly through the validator. `.optional()` only\n\t// accepts `undefined`; `.nullish()` accepts both `undefined` and\n\t// `null`. (#867 — autosave failures on seeded entries.)\n\tif (!field.required) {\n\t\tschema = schema.nullish();\n\t}\n\n\t// Apply default value\n\tif (field.defaultValue !== undefined) {\n\t\tschema = schema.default(field.defaultValue);\n\t}\n\n\treturn schema;\n}\n\n/**\n * Get base Zod schema for a field type\n */\nfunction getBaseSchema(type: FieldType, field: Field): ZodTypeAny {\n\tswitch (type) {\n\t\tcase \"url\":\n\t\t\treturn z.string().url();\n\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"slug\":\n\t\t\treturn z.string();\n\n\t\tcase \"number\":\n\t\t\treturn z.number();\n\n\t\tcase \"integer\":\n\t\t\treturn z.number().int();\n\n\t\tcase \"boolean\":\n\t\t\t// Boolean fields map to `INTEGER` columns (`FIELD_TYPE_TO_COLUMN`\n\t\t\t// in `schema/types.ts`) and `serializeValue` in\n\t\t\t// `database/repositories/content.ts` writes booleans as 0/1.\n\t\t\t// `deserializeValue` never converts them back, so reads return\n\t\t\t// numbers. Coerce the stored 0/1 shape here so a GET → POST\n\t\t\t// round-trip on a boolean field passes validation. Other inputs\n\t\t\t// (strings, other numbers) fall through to `z.boolean()` and\n\t\t\t// produce its standard rejection.\n\t\t\treturn z.preprocess((v) => (v === 0 || v === 1 ? Boolean(v) : v), z.boolean());\n\n\t\tcase \"datetime\":\n\t\t\t// Accept every value that legitimately round-trips through the admin\n\t\t\t// and seeds: ISO with `Z`, ISO with a timezone offset, a naive\n\t\t\t// datetime (`YYYY-MM-DDTHH:mm[:ss]` -- what `<input type=\"datetime-local\">`\n\t\t\t// and many seeds produce), and a date-only value. The admin re-sends\n\t\t\t// every loaded field on autosave, so a stored naive datetime must\n\t\t\t// validate or the entry becomes unsavable through its own editor\n\t\t\t// (#1368; same class as #867). `z.iso.*` retains semantic validation,\n\t\t\t// so impossible dates are still rejected.\n\t\t\treturn z.iso.datetime({ offset: true, local: true }).or(z.iso.date());\n\n\t\tcase \"select\": {\n\t\t\tconst options = field.validation?.options;\n\t\t\tif (options && options.length > 0) {\n\t\t\t\tconst [first, ...rest] = options;\n\t\t\t\treturn z.enum([first, ...rest]);\n\t\t\t}\n\t\t\treturn z.string();\n\t\t}\n\n\t\tcase \"multiSelect\": {\n\t\t\tconst multiOptions = field.validation?.options;\n\t\t\tif (multiOptions && multiOptions.length > 0) {\n\t\t\t\tconst [first, ...rest] = multiOptions;\n\t\t\t\treturn z.array(z.enum([first, ...rest]));\n\t\t\t}\n\t\t\treturn z.array(z.string());\n\t\t}\n\n\t\tcase \"portableText\":\n\t\t\t// Portable Text is an array of blocks. We require `_type` because\n\t\t\t// renderers dispatch on it, but `_key` is intentionally optional:\n\t\t\t// it's a UI-layer concern that the editor regenerates on every\n\t\t\t// change (see `PortableTextEditor`), and the rest of this schema\n\t\t\t// uses `.passthrough()` for everything below the top level. Making\n\t\t\t// `_key` strictly required here was an accidentally tight invariant\n\t\t\t// that rejected any seed/import data not authored against the\n\t\t\t// editor (#867 — autosave failures on seeded template content).\n\t\t\treturn z.array(\n\t\t\t\tz\n\t\t\t\t\t.object({\n\t\t\t\t\t\t_type: z.string(),\n\t\t\t\t\t\t_key: z.string().optional(),\n\t\t\t\t\t})\n\t\t\t\t\t.passthrough(),\n\t\t\t);\n\n\t\tcase \"image\":\n\t\t\treturn z.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tsrc: z.string().optional(),\n\t\t\t\talt: z.string().optional(),\n\t\t\t\twidth: z.number().optional(),\n\t\t\t\theight: z.number().optional(),\n\t\t\t\t/** Provider ID (e.g. \"local\", \"cloudflare-images\") */\n\t\t\t\tprovider: z.string().optional(),\n\t\t\t\t/** Admin-side preview URL for external providers (not persisted by plugins) */\n\t\t\t\tpreviewUrl: z.string().optional(),\n\t\t\t\t/** Provider-specific metadata; for local media this carries storageKey */\n\t\t\t\tmeta: z.record(z.string(), z.unknown()).optional(),\n\t\t\t});\n\n\t\tcase \"file\":\n\t\t\treturn z.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tsrc: z.string().optional(),\n\t\t\t\tfilename: z.string().optional(),\n\t\t\t\tmimeType: z.string().optional(),\n\t\t\t\tsize: z.number().optional(),\n\t\t\t\t/** Provider ID (e.g. \"local\", \"s3\") */\n\t\t\t\tprovider: z.string().optional(),\n\t\t\t\t/** Provider-specific metadata; for local media this carries storageKey */\n\t\t\t\tmeta: z.record(z.string(), z.unknown()).optional(),\n\t\t\t});\n\n\t\tcase \"reference\":\n\t\t\treturn z.string(); // Reference ID\n\n\t\tcase \"json\":\n\t\t\treturn z.unknown();\n\n\t\tdefault:\n\t\t\treturn z.unknown();\n\t}\n}\n\n/**\n * Apply validation rules to a schema\n */\nfunction applyValidation(schema: ZodTypeAny, field: Field): ZodTypeAny {\n\tconst validation = field.validation;\n\tif (!validation) return schema;\n\n\t// String validations\n\tif (schema instanceof z.ZodString) {\n\t\tlet strSchema = schema;\n\t\tif (validation.minLength !== undefined) {\n\t\t\tstrSchema = strSchema.min(validation.minLength);\n\t\t}\n\t\tif (validation.maxLength !== undefined) {\n\t\t\tstrSchema = strSchema.max(validation.maxLength);\n\t\t}\n\t\tif (validation.pattern) {\n\t\t\tstrSchema = strSchema.regex(new RegExp(validation.pattern));\n\t\t}\n\t\treturn strSchema;\n\t}\n\n\t// Number validations\n\tif (schema instanceof z.ZodNumber) {\n\t\tlet numSchema = schema;\n\t\tif (validation.min !== undefined) {\n\t\t\tnumSchema = numSchema.min(validation.min);\n\t\t}\n\t\tif (validation.max !== undefined) {\n\t\t\tnumSchema = numSchema.max(validation.max);\n\t\t}\n\t\treturn numSchema;\n\t}\n\n\treturn schema;\n}\n\n/**\n * Schema cache to avoid regenerating schemas on every request\n */\nconst schemaCache = new Map<string, { schema: z.ZodObject<any>; version: string }>();\n\n/**\n * Get or generate a cached schema for a collection\n */\nexport function getCachedSchema(\n\tcollection: CollectionWithFields,\n\tversion?: string,\n): z.ZodObject<any> {\n\tconst cacheKey = collection.slug;\n\tconst cached = schemaCache.get(cacheKey);\n\n\t// If version matches, return cached schema\n\tif (cached && (!version || cached.version === version)) {\n\t\treturn cached.schema;\n\t}\n\n\t// Generate new schema\n\tconst schema = generateZodSchema(collection);\n\n\t// Cache it\n\tschemaCache.set(cacheKey, {\n\t\tschema,\n\t\tversion: version || collection.updatedAt,\n\t});\n\n\treturn schema;\n}\n\n/**\n * Invalidate cached schema for a collection\n */\nexport function invalidateSchemaCache(slug: string): void {\n\tschemaCache.delete(slug);\n}\n\n/**\n * Clear all cached schemas\n */\nexport function clearSchemaCache(): void {\n\tschemaCache.clear();\n}\n\n/**\n * Validate data against a collection's schema\n */\nexport function validateContent(\n\tcollection: CollectionWithFields,\n\tdata: unknown,\n): { success: true; data: unknown } | { success: false; errors: z.ZodError } {\n\tconst schema = getCachedSchema(collection);\n\n\tconst result = schema.safeParse(data);\n\n\tif (result.success) {\n\t\treturn { success: true, data: result.data };\n\t}\n\n\treturn { success: false, errors: result.error };\n}\n\n/**\n * Generate TypeScript interface from field definitions\n * Used by CLI `emdash types` to generate types\n */\nexport function generateTypeScript(\n\tcollection: CollectionWithFields,\n\tinterfaceName: string = getInterfaceName(collection),\n): string {\n\tconst lines: string[] = [];\n\n\tlines.push(`export interface ${interfaceName} {`);\n\tlines.push(` id: string;`);\n\tlines.push(` slug: string | null;`);\n\tlines.push(` status: string;`);\n\n\tfor (const field of collection.fields) {\n\t\tconst tsType = fieldTypeToTypeScript(field);\n\t\tconst optional = field.required ? \"\" : \"?\";\n\t\tlines.push(` ${field.slug}${optional}: ${tsType};`);\n\t}\n\n\tlines.push(` createdAt: Date;`);\n\tlines.push(` updatedAt: Date;`);\n\tlines.push(` publishedAt: Date | null;`);\n\t// Bylines are eagerly loaded by getEmDashCollection/getEmDashEntry\n\tlines.push(` bylines?: ContentBylineCredit[];`);\n\t// Taxonomy terms are eagerly loaded by getEmDashCollection/getEmDashEntry,\n\t// keyed by taxonomy name (e.g. data.terms?.tag)\n\tlines.push(` terms?: Record<string, TaxonomyTerm[]>;`);\n\tlines.push(`}`);\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Generate a complete types file with module augmentation\n * This produces emdash-env.d.ts content that provides typed query functions\n */\nexport function generateTypesFile(collections: CollectionWithFields[]): string {\n\tconst lines: string[] = [];\n\n\t// Header\n\tlines.push(`// Generated by EmDash on dev server start`);\n\tlines.push(`// Do not edit manually`);\n\tlines.push(``);\n\tlines.push(`/// <reference types=\"emdash/locals\" />`);\n\tlines.push(``);\n\n\t// Check if we need PortableTextBlock import\n\tconst needsPortableText = collections.some((c) =>\n\t\tc.fields.some((f) => f.type === \"portableText\"),\n\t);\n\n\t// Build imports - ContentBylineCredit and TaxonomyTerm are always needed\n\t// for the hydrated bylines/terms fields\n\tconst imports = [\"ContentBylineCredit\", \"TaxonomyTerm\"];\n\tif (needsPortableText) {\n\t\timports.push(\"PortableTextBlock\");\n\t}\n\tlines.push(`import type { ${imports.join(\", \")} } from \"emdash\";`);\n\tlines.push(``);\n\n\t// Singularizing the slug can map two distinct slugs to the same name\n\t// (e.g. `book` and `books` both -> `Book`), so resolve collisions up front\n\t// to keep every interface identifier unique within the file.\n\tconst interfaceNames = uniqueInterfaceNames(collections);\n\n\t// Generate individual interfaces\n\tfor (const collection of collections) {\n\t\tlines.push(generateTypeScript(collection, interfaceNames.get(collection.slug)));\n\t\tlines.push(``);\n\t}\n\n\t// Generate the Collections interface for module augmentation\n\tlines.push(`declare module \"emdash\" {`);\n\tlines.push(` interface EmDashCollections {`);\n\tfor (const collection of collections) {\n\t\tlines.push(` ${collection.slug}: ${interfaceNames.get(collection.slug)};`);\n\t}\n\tlines.push(` }`);\n\tlines.push(`}`);\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Generate schema hash for cache invalidation\n */\nexport async function generateSchemaHash(collections: CollectionWithFields[]): Promise<string> {\n\tconst str = JSON.stringify(\n\t\tcollections.map((c) => ({\n\t\t\tslug: c.slug,\n\t\t\tfields: c.fields.map((f) => ({\n\t\t\t\tslug: f.slug,\n\t\t\t\ttype: f.type,\n\t\t\t\trequired: f.required,\n\t\t\t\tvalidation: f.validation,\n\t\t\t})),\n\t\t})),\n\t);\n\treturn hashString(str);\n}\n\n/**\n * Map field type to TypeScript type\n */\nfunction fieldTypeToTypeScript(field: Field): string {\n\tswitch (field.type) {\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"slug\":\n\t\tcase \"url\":\n\t\tcase \"datetime\":\n\t\t\treturn \"string\";\n\n\t\tcase \"number\":\n\t\tcase \"integer\":\n\t\t\treturn \"number\";\n\n\t\tcase \"boolean\":\n\t\t\treturn \"boolean\";\n\n\t\tcase \"select\":\n\t\t\tconst options = field.validation?.options;\n\t\t\tif (options && options.length > 0) {\n\t\t\t\treturn options.map((o) => `\"${o}\"`).join(\" | \");\n\t\t\t}\n\t\t\treturn \"string\";\n\n\t\tcase \"multiSelect\":\n\t\t\tconst multiOptions = field.validation?.options;\n\t\t\tif (multiOptions && multiOptions.length > 0) {\n\t\t\t\treturn `(${multiOptions.map((o) => `\"${o}\"`).join(\" | \")})[]`;\n\t\t\t}\n\t\t\treturn \"string[]\";\n\n\t\tcase \"portableText\":\n\t\t\treturn \"PortableTextBlock[]\";\n\n\t\tcase \"image\":\n\t\t\treturn \"{ id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record<string, unknown> }\";\n\n\t\tcase \"file\":\n\t\t\treturn \"{ id: string; src?: string; filename?: string; mimeType?: string; size?: number; provider?: string; meta?: Record<string, unknown> }\";\n\n\t\tcase \"reference\":\n\t\t\t// Could be enhanced to include the referenced collection type\n\t\t\treturn \"string\";\n\n\t\tcase \"json\":\n\t\t\treturn \"unknown\";\n\n\t\tdefault:\n\t\t\treturn \"unknown\";\n\t}\n}\n\n/**\n * Convert string to PascalCase (handles slugs, spaces, etc.)\n */\nfunction pascalCase(str: string): string {\n\treturn str\n\t\t.split(PASCAL_CASE_SPLIT_PATTERN)\n\t\t.filter(Boolean)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n\t\t.join(\"\");\n}\n\n/**\n * Naive singularization for slug-derived interface names. Handles the common\n * English plural endings; intentionally simple, not a full inflector.\n */\nfunction singularize(str: string): string {\n\tif (str.endsWith(\"ies\")) {\n\t\treturn str.slice(0, -3) + \"y\";\n\t}\n\tif (\n\t\tstr.endsWith(\"es\") &&\n\t\t(str.endsWith(\"sses\") || str.endsWith(\"xes\") || str.endsWith(\"ches\") || str.endsWith(\"shes\"))\n\t) {\n\t\treturn str.slice(0, -2);\n\t}\n\tif (str.endsWith(\"s\") && !str.endsWith(\"ss\")) {\n\t\treturn str.slice(0, -1);\n\t}\n\treturn str;\n}\n\n/**\n * Get the interface name for a collection.\n *\n * Derived from the slug, not the human label. Slugs are constrained to\n * `/^[a-z][a-z0-9_]*$/`, so PascalCasing one always yields a valid TS\n * identifier; labels are arbitrary and user-controlled (punctuation, spaces,\n * duplicates across collections), which produced syntactically invalid or\n * duplicate interface names. The slug is singularized first because the\n * interface describes a single entry, not the collection (`posts` -> `Post`).\n *\n * Singularization can map two distinct slugs onto the same name, so callers\n * generating more than one interface must dedupe -- see `uniqueInterfaceNames`.\n */\nfunction getInterfaceName(collection: CollectionWithFields): string {\n\treturn pascalCase(singularize(collection.slug));\n}\n\n/**\n * Resolve interface names for a set of collections, guaranteeing each is\n * unique within the file. Collisions (from singularization or PascalCasing\n * collapsing distinct slugs) get a numeric suffix in collection order, so the\n * generated `.d.ts` never declares two interfaces with the same identifier.\n *\n * The suffix is chosen against the set of names already emitted, not a\n * per-base counter, so a generated name can't collide with another slug's\n * base name (e.g. slugs `book`, `books`, `book2`: `books` -> `Book2` would\n * clash with `book2`, so it advances to `Book3`).\n */\nexport function uniqueInterfaceNames(collections: CollectionWithFields[]): Map<string, string> {\n\tconst used = new Set<string>();\n\tconst names = new Map<string, string>();\n\tfor (const collection of collections) {\n\t\tconst base = getInterfaceName(collection);\n\t\tlet name = base;\n\t\tlet suffix = 2;\n\t\twhile (used.has(name)) {\n\t\t\tname = `${base}${suffix}`;\n\t\t\tsuffix++;\n\t\t}\n\t\tused.add(name);\n\t\tnames.set(collection.slug, name);\n\t}\n\treturn names;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"zod-generator-CG64Hnbh.mjs","names":[],"sources":["../src/schema/zod-generator.ts"],"sourcesContent":["import { z, type ZodTypeAny } from \"zod\";\n\nimport { hashString } from \"../utils/hash.js\";\nimport type { Field, FieldType, CollectionWithFields } from \"./types.js\";\n\n/** Pattern to split on underscores, hyphens, and spaces for PascalCase conversion */\nconst PASCAL_CASE_SPLIT_PATTERN = /[_\\-\\s]+/;\n\n/**\n * Generate a Zod schema from a collection's field definitions\n *\n * This allows runtime validation of content based on dynamically\n * defined schemas stored in D1.\n */\nexport function generateZodSchema(\n\tcollection: CollectionWithFields,\n): z.ZodObject<Record<string, ZodTypeAny>> {\n\tconst shape: Record<string, ZodTypeAny> = {};\n\n\tfor (const field of collection.fields) {\n\t\tshape[field.slug] = generateFieldSchema(field);\n\t}\n\n\treturn z.object(shape);\n}\n\n/**\n * Generate Zod schema for a single field\n */\nexport function generateFieldSchema(field: Field): ZodTypeAny {\n\tlet schema = getBaseSchema(field.type, field);\n\n\t// Apply validation rules\n\tif (field.validation) {\n\t\tschema = applyValidation(schema, field);\n\t}\n\n\t// Apply required/optional. Non-required fields use `.nullish()` rather\n\t// than `.optional()` because the underlying SQLite columns are nullable\n\t// (see `SchemaRegistry.addFieldColumn` -- non-required fields are added\n\t// without `NOT NULL`). The admin re-sends what it loaded from the\n\t// server on autosave, so any field that's actually `null` in the DB\n\t// must round-trip cleanly through the validator. `.optional()` only\n\t// accepts `undefined`; `.nullish()` accepts both `undefined` and\n\t// `null`. (#867 — autosave failures on seeded entries.)\n\tif (!field.required) {\n\t\tschema = schema.nullish();\n\t}\n\n\t// Apply default value\n\tif (field.defaultValue !== undefined) {\n\t\tschema = schema.default(field.defaultValue);\n\t}\n\n\treturn schema;\n}\n\n/**\n * Get base Zod schema for a field type\n */\nfunction getBaseSchema(type: FieldType, field: Field): ZodTypeAny {\n\tswitch (type) {\n\t\tcase \"url\":\n\t\t\treturn z.string().url();\n\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"slug\":\n\t\t\treturn z.string();\n\n\t\tcase \"number\":\n\t\t\treturn z.number();\n\n\t\tcase \"integer\":\n\t\t\treturn z.number().int();\n\n\t\tcase \"boolean\":\n\t\t\t// Boolean fields map to `INTEGER` columns (`FIELD_TYPE_TO_COLUMN`\n\t\t\t// in `schema/types.ts`) and `serializeValue` in\n\t\t\t// `database/repositories/content.ts` writes booleans as 0/1.\n\t\t\t// `deserializeValue` never converts them back, so reads return\n\t\t\t// numbers. Coerce the stored 0/1 shape here so a GET → POST\n\t\t\t// round-trip on a boolean field passes validation. Other inputs\n\t\t\t// (strings, other numbers) fall through to `z.boolean()` and\n\t\t\t// produce its standard rejection.\n\t\t\treturn z.preprocess((v) => (v === 0 || v === 1 ? Boolean(v) : v), z.boolean());\n\n\t\tcase \"datetime\":\n\t\t\t// Accept every value that legitimately round-trips through the admin\n\t\t\t// and seeds: ISO with `Z`, ISO with a timezone offset, a naive\n\t\t\t// datetime (`YYYY-MM-DDTHH:mm[:ss]` -- what `<input type=\"datetime-local\">`\n\t\t\t// and many seeds produce), and a date-only value. The admin re-sends\n\t\t\t// every loaded field on autosave, so a stored naive datetime must\n\t\t\t// validate or the entry becomes unsavable through its own editor\n\t\t\t// (#1368; same class as #867). `z.iso.*` retains semantic validation,\n\t\t\t// so impossible dates are still rejected.\n\t\t\treturn z.iso.datetime({ offset: true, local: true }).or(z.iso.date());\n\n\t\tcase \"select\": {\n\t\t\tconst options = field.validation?.options;\n\t\t\tif (options && options.length > 0) {\n\t\t\t\tconst [first, ...rest] = options;\n\t\t\t\treturn z.enum([first, ...rest]);\n\t\t\t}\n\t\t\treturn z.string();\n\t\t}\n\n\t\tcase \"multiSelect\": {\n\t\t\tconst multiOptions = field.validation?.options;\n\t\t\tif (multiOptions && multiOptions.length > 0) {\n\t\t\t\tconst [first, ...rest] = multiOptions;\n\t\t\t\treturn z.array(z.enum([first, ...rest]));\n\t\t\t}\n\t\t\treturn z.array(z.string());\n\t\t}\n\n\t\tcase \"portableText\":\n\t\t\t// Portable Text is an array of blocks. We require `_type` because\n\t\t\t// renderers dispatch on it, but `_key` is intentionally optional:\n\t\t\t// it's a UI-layer concern that the editor regenerates on every\n\t\t\t// change (see `PortableTextEditor`), and the rest of this schema\n\t\t\t// uses `.passthrough()` for everything below the top level. Making\n\t\t\t// `_key` strictly required here was an accidentally tight invariant\n\t\t\t// that rejected any seed/import data not authored against the\n\t\t\t// editor (#867 — autosave failures on seeded template content).\n\t\t\treturn z.array(\n\t\t\t\tz\n\t\t\t\t\t.object({\n\t\t\t\t\t\t_type: z.string(),\n\t\t\t\t\t\t_key: z.string().optional(),\n\t\t\t\t\t})\n\t\t\t\t\t.passthrough(),\n\t\t\t);\n\n\t\tcase \"image\":\n\t\t\treturn z.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tsrc: z.string().optional(),\n\t\t\t\talt: z.string().optional(),\n\t\t\t\twidth: z.number().optional(),\n\t\t\t\theight: z.number().optional(),\n\t\t\t\t/** Provider ID (e.g. \"local\", \"cloudflare-images\") */\n\t\t\t\tprovider: z.string().optional(),\n\t\t\t\t/** Admin-side preview URL for external providers (not persisted by plugins) */\n\t\t\t\tpreviewUrl: z.string().optional(),\n\t\t\t\t/** Provider-specific metadata; for local media this carries storageKey */\n\t\t\t\tmeta: z.record(z.string(), z.unknown()).optional(),\n\t\t\t});\n\n\t\tcase \"file\":\n\t\t\treturn z.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tsrc: z.string().optional(),\n\t\t\t\tfilename: z.string().optional(),\n\t\t\t\tmimeType: z.string().optional(),\n\t\t\t\tsize: z.number().optional(),\n\t\t\t\t/** Provider ID (e.g. \"local\", \"s3\") */\n\t\t\t\tprovider: z.string().optional(),\n\t\t\t\t/** Provider-specific metadata; for local media this carries storageKey */\n\t\t\t\tmeta: z.record(z.string(), z.unknown()).optional(),\n\t\t\t});\n\n\t\tcase \"reference\":\n\t\t\treturn z.string(); // Reference ID\n\n\t\tcase \"json\":\n\t\t\treturn z.unknown();\n\n\t\tdefault:\n\t\t\treturn z.unknown();\n\t}\n}\n\n/**\n * Apply validation rules to a schema\n */\nfunction applyValidation(schema: ZodTypeAny, field: Field): ZodTypeAny {\n\tconst validation = field.validation;\n\tif (!validation) return schema;\n\n\t// String validations\n\tif (schema instanceof z.ZodString) {\n\t\tlet strSchema = schema;\n\t\tif (validation.minLength !== undefined) {\n\t\t\tstrSchema = strSchema.min(validation.minLength);\n\t\t}\n\t\tif (validation.maxLength !== undefined) {\n\t\t\tstrSchema = strSchema.max(validation.maxLength);\n\t\t}\n\t\tif (validation.pattern) {\n\t\t\tstrSchema = strSchema.regex(new RegExp(validation.pattern));\n\t\t}\n\t\treturn strSchema;\n\t}\n\n\t// Number validations\n\tif (schema instanceof z.ZodNumber) {\n\t\tlet numSchema = schema;\n\t\tif (validation.min !== undefined) {\n\t\t\tnumSchema = numSchema.min(validation.min);\n\t\t}\n\t\tif (validation.max !== undefined) {\n\t\t\tnumSchema = numSchema.max(validation.max);\n\t\t}\n\t\treturn numSchema;\n\t}\n\n\treturn schema;\n}\n\n/**\n * Schema cache to avoid regenerating schemas on every request\n */\nconst schemaCache = new Map<string, { schema: z.ZodObject<any>; version: string }>();\n\n/**\n * Get or generate a cached schema for a collection\n */\nexport function getCachedSchema(\n\tcollection: CollectionWithFields,\n\tversion?: string,\n): z.ZodObject<any> {\n\tconst cacheKey = collection.slug;\n\tconst cached = schemaCache.get(cacheKey);\n\n\t// If version matches, return cached schema\n\tif (cached && (!version || cached.version === version)) {\n\t\treturn cached.schema;\n\t}\n\n\t// Generate new schema\n\tconst schema = generateZodSchema(collection);\n\n\t// Cache it\n\tschemaCache.set(cacheKey, {\n\t\tschema,\n\t\tversion: version || collection.updatedAt,\n\t});\n\n\treturn schema;\n}\n\n/**\n * Invalidate cached schema for a collection\n */\nexport function invalidateSchemaCache(slug: string): void {\n\tschemaCache.delete(slug);\n}\n\n/**\n * Clear all cached schemas\n */\nexport function clearSchemaCache(): void {\n\tschemaCache.clear();\n}\n\n/**\n * Validate data against a collection's schema\n */\nexport function validateContent(\n\tcollection: CollectionWithFields,\n\tdata: unknown,\n): { success: true; data: unknown } | { success: false; errors: z.ZodError } {\n\tconst schema = getCachedSchema(collection);\n\n\tconst result = schema.safeParse(data);\n\n\tif (result.success) {\n\t\treturn { success: true, data: result.data };\n\t}\n\n\treturn { success: false, errors: result.error };\n}\n\n/**\n * Generate TypeScript interface from field definitions\n * Used by CLI `emdash types` to generate types\n */\nexport function generateTypeScript(\n\tcollection: CollectionWithFields,\n\tinterfaceName: string = getInterfaceName(collection),\n): string {\n\tconst lines: string[] = [];\n\n\tlines.push(`export interface ${interfaceName} {`);\n\tlines.push(` id: string;`);\n\tlines.push(` slug: string | null;`);\n\tlines.push(` status: string;`);\n\n\tfor (const field of collection.fields) {\n\t\tconst tsType = fieldTypeToTypeScript(field);\n\t\tconst optional = field.required ? \"\" : \"?\";\n\t\tlines.push(` ${field.slug}${optional}: ${tsType};`);\n\t}\n\n\tlines.push(` createdAt: Date;`);\n\tlines.push(` updatedAt: Date;`);\n\tlines.push(` publishedAt: Date | null;`);\n\t// Bylines are eagerly loaded by getEmDashCollection/getEmDashEntry\n\tlines.push(` bylines?: ContentBylineCredit[];`);\n\t// Taxonomy terms are eagerly loaded by getEmDashCollection/getEmDashEntry,\n\t// keyed by taxonomy name (e.g. data.terms?.tag)\n\tlines.push(` terms?: Record<string, TaxonomyTerm[]>;`);\n\tlines.push(`}`);\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Generate a complete types file with module augmentation\n * This produces emdash-env.d.ts content that provides typed query functions\n */\nexport function generateTypesFile(collections: CollectionWithFields[]): string {\n\tconst lines: string[] = [];\n\n\t// Header\n\tlines.push(`// Generated by EmDash on dev server start`);\n\tlines.push(`// Do not edit manually`);\n\tlines.push(``);\n\tlines.push(`/// <reference types=\"emdash/locals\" />`);\n\tlines.push(``);\n\n\t// Check if we need PortableTextBlock import\n\tconst needsPortableText = collections.some((c) =>\n\t\tc.fields.some((f) => f.type === \"portableText\"),\n\t);\n\n\t// Build imports - ContentBylineCredit and TaxonomyTerm are always needed\n\t// for the hydrated bylines/terms fields\n\tconst imports = [\"ContentBylineCredit\", \"TaxonomyTerm\"];\n\tif (needsPortableText) {\n\t\timports.push(\"PortableTextBlock\");\n\t}\n\tlines.push(`import type { ${imports.join(\", \")} } from \"emdash\";`);\n\tlines.push(``);\n\n\t// Singularizing the slug can map two distinct slugs to the same name\n\t// (e.g. `book` and `books` both -> `Book`), so resolve collisions up front\n\t// to keep every interface identifier unique within the file.\n\tconst interfaceNames = uniqueInterfaceNames(collections);\n\n\t// Generate individual interfaces\n\tfor (const collection of collections) {\n\t\tlines.push(generateTypeScript(collection, interfaceNames.get(collection.slug)));\n\t\tlines.push(``);\n\t}\n\n\t// Generate the Collections interface for module augmentation\n\tlines.push(`declare module \"emdash\" {`);\n\tlines.push(` interface EmDashCollections {`);\n\tfor (const collection of collections) {\n\t\tlines.push(` ${collection.slug}: ${interfaceNames.get(collection.slug)};`);\n\t}\n\tlines.push(` }`);\n\tlines.push(`}`);\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Generate schema hash for cache invalidation\n */\nexport async function generateSchemaHash(collections: CollectionWithFields[]): Promise<string> {\n\tconst str = JSON.stringify(\n\t\tcollections.map((c) => ({\n\t\t\tslug: c.slug,\n\t\t\tfields: c.fields.map((f) => ({\n\t\t\t\tslug: f.slug,\n\t\t\t\ttype: f.type,\n\t\t\t\trequired: f.required,\n\t\t\t\tvalidation: f.validation,\n\t\t\t})),\n\t\t})),\n\t);\n\treturn hashString(str);\n}\n\n/**\n * Map field type to TypeScript type\n */\nfunction fieldTypeToTypeScript(field: Field): string {\n\tswitch (field.type) {\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"slug\":\n\t\tcase \"url\":\n\t\tcase \"datetime\":\n\t\t\treturn \"string\";\n\n\t\tcase \"number\":\n\t\tcase \"integer\":\n\t\t\treturn \"number\";\n\n\t\tcase \"boolean\":\n\t\t\treturn \"boolean\";\n\n\t\tcase \"select\":\n\t\t\tconst options = field.validation?.options;\n\t\t\tif (options && options.length > 0) {\n\t\t\t\treturn options.map((o) => `\"${o}\"`).join(\" | \");\n\t\t\t}\n\t\t\treturn \"string\";\n\n\t\tcase \"multiSelect\":\n\t\t\tconst multiOptions = field.validation?.options;\n\t\t\tif (multiOptions && multiOptions.length > 0) {\n\t\t\t\treturn `(${multiOptions.map((o) => `\"${o}\"`).join(\" | \")})[]`;\n\t\t\t}\n\t\t\treturn \"string[]\";\n\n\t\tcase \"portableText\":\n\t\t\treturn \"PortableTextBlock[]\";\n\n\t\tcase \"image\":\n\t\t\treturn \"{ id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record<string, unknown> }\";\n\n\t\tcase \"file\":\n\t\t\treturn \"{ id: string; src?: string; filename?: string; mimeType?: string; size?: number; provider?: string; meta?: Record<string, unknown> }\";\n\n\t\tcase \"reference\":\n\t\t\t// Could be enhanced to include the referenced collection type\n\t\t\treturn \"string\";\n\n\t\tcase \"json\":\n\t\t\treturn \"unknown\";\n\n\t\tdefault:\n\t\t\treturn \"unknown\";\n\t}\n}\n\n/**\n * Convert string to PascalCase (handles slugs, spaces, etc.)\n */\nfunction pascalCase(str: string): string {\n\treturn str\n\t\t.split(PASCAL_CASE_SPLIT_PATTERN)\n\t\t.filter(Boolean)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n\t\t.join(\"\");\n}\n\n/**\n * Naive singularization for slug-derived interface names. Handles the common\n * English plural endings; intentionally simple, not a full inflector.\n */\nfunction singularize(str: string): string {\n\tif (str.endsWith(\"ies\")) {\n\t\treturn str.slice(0, -3) + \"y\";\n\t}\n\tif (\n\t\tstr.endsWith(\"es\") &&\n\t\t(str.endsWith(\"sses\") || str.endsWith(\"xes\") || str.endsWith(\"ches\") || str.endsWith(\"shes\"))\n\t) {\n\t\treturn str.slice(0, -2);\n\t}\n\tif (str.endsWith(\"s\") && !str.endsWith(\"ss\")) {\n\t\treturn str.slice(0, -1);\n\t}\n\treturn str;\n}\n\n/**\n * Get the interface name for a collection.\n *\n * Derived from the slug, not the human label. Slugs are constrained to\n * `/^[a-z][a-z0-9_]*$/`, so PascalCasing one always yields a valid TS\n * identifier; labels are arbitrary and user-controlled (punctuation, spaces,\n * duplicates across collections), which produced syntactically invalid or\n * duplicate interface names. The slug is singularized first because the\n * interface describes a single entry, not the collection (`posts` -> `Post`).\n *\n * Singularization can map two distinct slugs onto the same name, so callers\n * generating more than one interface must dedupe -- see `uniqueInterfaceNames`.\n */\nfunction getInterfaceName(collection: CollectionWithFields): string {\n\treturn pascalCase(singularize(collection.slug));\n}\n\n/**\n * Resolve interface names for a set of collections, guaranteeing each is\n * unique within the file. Collisions (from singularization or PascalCasing\n * collapsing distinct slugs) get a numeric suffix in collection order, so the\n * generated `.d.ts` never declares two interfaces with the same identifier.\n *\n * The suffix is chosen against the set of names already emitted, not a\n * per-base counter, so a generated name can't collide with another slug's\n * base name (e.g. slugs `book`, `books`, `book2`: `books` -> `Book2` would\n * clash with `book2`, so it advances to `Book3`).\n */\nexport function uniqueInterfaceNames(collections: CollectionWithFields[]): Map<string, string> {\n\tconst used = new Set<string>();\n\tconst names = new Map<string, string>();\n\tfor (const collection of collections) {\n\t\tconst base = getInterfaceName(collection);\n\t\tlet name = base;\n\t\tlet suffix = 2;\n\t\twhile (used.has(name)) {\n\t\t\tname = `${base}${suffix}`;\n\t\t\tsuffix++;\n\t\t}\n\t\tused.add(name);\n\t\tnames.set(collection.slug, name);\n\t}\n\treturn names;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emdash",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "Astro-native CMS with WordPress migration support",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -42,6 +42,7 @@
42
42
  "default": "./dist/astro/middleware/redirect.mjs"
43
43
  },
44
44
  "./ui": "./src/ui.ts",
45
+ "./ui/comments": "./src/ui-comments.ts",
45
46
  "./ui/search": "./src/components/LiveSearch.astro",
46
47
  "./cli": {
47
48
  "types": "./dist/cli/index.d.mts",
@@ -207,10 +208,12 @@
207
208
  "@tiptap/extension-focus": "^3.20.0",
208
209
  "@tiptap/extension-image": "^3.20.0",
209
210
  "@tiptap/extension-link": "^3.20.0",
211
+ "@tiptap/extension-list": "^3.20.0",
210
212
  "@tiptap/extension-placeholder": "^3.20.0",
211
213
  "@tiptap/extension-text-align": "^3.20.0",
212
214
  "@tiptap/extension-typography": "^3.20.0",
213
215
  "@tiptap/extension-underline": "^3.20.0",
216
+ "@tiptap/pm": "^3.20.0",
214
217
  "@tiptap/react": "^3.20.0",
215
218
  "@tiptap/starter-kit": "^3.20.0",
216
219
  "@tiptap/suggestion": "^3.20.0",
@@ -219,7 +222,7 @@
219
222
  "astro-portabletext": "^0.11.0",
220
223
  "better-sqlite3": "^12.8.0",
221
224
  "blurhash": "^2.0.5",
222
- "citty": "^0.1.6",
225
+ "citty": "^0.2.2",
223
226
  "consola": "^3.4.2",
224
227
  "croner": "^10.0.1",
225
228
  "image-size": "^2.0.2",
@@ -235,11 +238,11 @@
235
238
  "upng-js": "^2.1.0",
236
239
  "zod": "^4.4.1",
237
240
  "@atcute/client": "^5.0.0",
238
- "@emdash-cms/admin": "0.32.0",
239
- "@emdash-cms/auth": "0.32.0",
240
- "@emdash-cms/gutenberg-to-portable-text": "0.32.0",
241
- "@emdash-cms/registry-client": "0.3.4",
242
- "@emdash-cms/plugin-types": "0.3.0"
241
+ "@emdash-cms/gutenberg-to-portable-text": "0.33.0",
242
+ "@emdash-cms/plugin-types": "0.3.0",
243
+ "@emdash-cms/auth": "0.33.0",
244
+ "@emdash-cms/admin": "0.33.0",
245
+ "@emdash-cms/registry-client": "0.3.4"
243
246
  },
244
247
  "optionalDependencies": {
245
248
  "@libsql/kysely-libsql": "^0.4.0",
@@ -272,7 +275,7 @@
272
275
  "vite": "^6.0.0",
273
276
  "vitest": "^4.1.5",
274
277
  "zod-openapi": "^5.4.6",
275
- "@emdash-cms/blocks": "0.32.0"
278
+ "@emdash-cms/blocks": "0.33.0"
276
279
  },
277
280
  "repository": {
278
281
  "type": "git",
package/src/after.ts CHANGED
@@ -16,6 +16,9 @@
16
16
  * `after()`, which they don't during type-checking.
17
17
  */
18
18
 
19
+ import { trackDeferredTask } from "./deferred-tasks.js";
20
+ import { getRequestContext } from "./request-context.js";
21
+
19
22
  export type WaitUntilFn = (promise: Promise<unknown>) => void;
20
23
 
21
24
  // Resolves to the host's waitUntil if the adapter provided one, or
@@ -45,11 +48,13 @@ waitUntilReady.catch(() => {});
45
48
  * that care about errors should handle them inside `fn`.
46
49
  */
47
50
  export function after(fn: () => void | Promise<void>): void {
48
- const promise = Promise.resolve()
51
+ const task = Promise.resolve()
49
52
  .then(fn)
50
53
  .catch((error) => {
51
54
  console.error("[emdash] deferred task failed:", error);
52
55
  });
56
+ const requestTask = getRequestContext()?.deferredTasks?.track(task) ?? task;
57
+ const promise = trackDeferredTask(requestTask);
53
58
 
54
59
  // Defer the lifetime-extender handoff to the microtask that resolves
55
60
  // waitUntilReady. On workerd this is effectively instant (the virtual
package/src/api/errors.ts CHANGED
@@ -246,6 +246,7 @@ export const ErrorCode = {
246
246
  TERM_GET_ERROR: "TERM_GET_ERROR",
247
247
  TERM_UPDATE_ERROR: "TERM_UPDATE_ERROR",
248
248
  TERM_DELETE_ERROR: "TERM_DELETE_ERROR",
249
+ TERM_REORDER_ERROR: "TERM_REORDER_ERROR",
249
250
  TERMS_GET_ERROR: "TERMS_GET_ERROR",
250
251
  TERMS_SET_ERROR: "TERMS_SET_ERROR",
251
252
 
@@ -15,6 +15,7 @@ import { RevisionRepository } from "../../database/repositories/revision.js";
15
15
  import { SeoRepository } from "../../database/repositories/seo.js";
16
16
  import { TaxonomyRepository } from "../../database/repositories/taxonomy.js";
17
17
  import {
18
+ ContentMutationConflictError,
18
19
  EmDashValidationError,
19
20
  ScheduledNotDueError,
20
21
  InvalidCursorError,
@@ -376,6 +377,15 @@ async function createSlugChangeRedirect(
376
377
  newSlug: string,
377
378
  contentId: string,
378
379
  ): Promise<void> {
380
+ // A URL pattern has no locale token, so every locale variant of an entry
381
+ // generates the same URL, and slugs are unique per (slug, locale) — a
382
+ // translation may still hold the old slug. Redirecting away from a URL
383
+ // another row still answers on would take that page down: the redirect
384
+ // middleware runs `order: "pre"`, so routing never gets a chance.
385
+ // Any surviving row counts, published or not: a draft that publishes later
386
+ // would otherwise be shadowed by the redirect.
387
+ if (await slugStillTaken(db, collection, oldSlug, contentId)) return;
388
+
379
389
  const collectionRow = await db
380
390
  .selectFrom("_emdash_collections")
381
391
  .select("url_pattern")
@@ -393,6 +403,24 @@ async function createSlugChangeRedirect(
393
403
  invalidateRedirectCache();
394
404
  }
395
405
 
406
+ /** Whether a row other than `contentId` still holds `slug` in this collection. */
407
+ async function slugStillTaken(
408
+ db: Kysely<Database>,
409
+ collection: string,
410
+ slug: string,
411
+ contentId: string,
412
+ ): Promise<boolean> {
413
+ validateIdentifier(collection, "collection slug");
414
+ const result = await sql<{ id: string }>`
415
+ SELECT id FROM ${sql.ref(`ec_${collection}`)}
416
+ WHERE slug = ${slug}
417
+ AND id != ${contentId}
418
+ AND deleted_at IS NULL
419
+ LIMIT 1
420
+ `.execute(db);
421
+ return result.rows.length > 0;
422
+ }
423
+
396
424
  /** Matches a date-only `YYYY-MM-DD` bound (no time component). */
397
425
  const DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
398
426
 
@@ -1450,15 +1478,18 @@ export async function handleContentUnschedule(
1450
1478
  /**
1451
1479
  * Publish content immediately.
1452
1480
  *
1453
- * Wrapped in a transaction because publish performs multiple writes
1454
- * (syncDataColumns, slug sync, status/revision update) that must
1455
- * be atomic to prevent FTS shadow table corruption on crash.
1481
+ * Publication is one atomic content-row statement. On databases that support
1482
+ * transactions, the existing slug-redirect side write remains grouped with it.
1456
1483
  */
1457
1484
  export async function handleContentPublish(
1458
1485
  db: Kysely<Database>,
1459
1486
  collection: string,
1460
1487
  id: string,
1461
- options: { publishedAt?: string; requireScheduledDue?: boolean } = {},
1488
+ options: {
1489
+ publishedAt?: string;
1490
+ requireScheduledDue?: boolean;
1491
+ expectedScheduledAt?: string;
1492
+ } = {},
1462
1493
  ): Promise<ApiResult<ContentResponse>> {
1463
1494
  try {
1464
1495
  const item = await withTransaction(db, async (trx) => {
@@ -1477,6 +1508,7 @@ export async function handleContentPublish(
1477
1508
  resolvedId,
1478
1509
  options.publishedAt,
1479
1510
  options.requireScheduledDue,
1511
+ options.expectedScheduledAt,
1480
1512
  );
1481
1513
 
1482
1514
  // Leave a 301 behind when publishing changed the slug of an entry that
@@ -1502,6 +1534,15 @@ export async function handleContentPublish(
1502
1534
  data: { item },
1503
1535
  };
1504
1536
  } catch (error) {
1537
+ if (error instanceof ContentMutationConflictError) {
1538
+ return {
1539
+ success: false,
1540
+ error: {
1541
+ code: "CONFLICT",
1542
+ message: error.message,
1543
+ },
1544
+ };
1545
+ }
1505
1546
  // The scheduled sweep gates publish on the row still being due; a row
1506
1547
  // unscheduled in the meantime is a silent skip, not a failure.
1507
1548
  if (error instanceof ScheduledNotDueError) {
@@ -24,6 +24,10 @@ import {
24
24
  } from "../../plugins/marketplace.js";
25
25
  import type { SandboxRunner } from "../../plugins/sandbox/types.js";
26
26
  import { PluginStateRepository } from "../../plugins/state.js";
27
+ import {
28
+ removeAllPluginIndexes,
29
+ syncDeclaredStorageIndexes,
30
+ } from "../../plugins/storage-indexes.js";
27
31
  import { normalizeCapabilities } from "../../plugins/types.js";
28
32
  import type { PluginManifest } from "../../plugins/types.js";
29
33
  import { EmDashStorageError } from "../../storage/types.js";
@@ -475,6 +479,8 @@ export async function handleMarketplaceInstall(
475
479
  description: pluginDetail.description ?? undefined,
476
480
  });
477
481
 
482
+ await syncDeclaredStorageIndexes(db, [bundle.manifest]);
483
+
478
484
  // Fire-and-forget install stat
479
485
  client.reportInstall(pluginId, version).catch(() => {
480
486
  // Intentional: never fails the install
@@ -713,6 +719,8 @@ export async function handleMarketplaceUpdate(
713
719
  mcpToolsConsent: null,
714
720
  });
715
721
 
722
+ await syncDeclaredStorageIndexes(db, [bundle.manifest]);
723
+
716
724
  // Clean up old bundle from R2 (best-effort)
717
725
  deleteBundleFromR2(storage, pluginId, oldVersion).catch(() => {});
718
726
 
@@ -786,6 +794,12 @@ export async function handleMarketplaceUninstall(
786
794
  }
787
795
  }
788
796
 
797
+ try {
798
+ await removeAllPluginIndexes(db, pluginId);
799
+ } catch {
800
+ // Nothing to drop, or tracking table predates the feature
801
+ }
802
+
789
803
  // Delete state row
790
804
  await stateRepo.delete(pluginId);
791
805
 
@@ -49,6 +49,10 @@ import { extractBundle } from "../../plugins/marketplace.js";
49
49
  import type { PluginBundle } from "../../plugins/marketplace.js";
50
50
  import type { SandboxRunner } from "../../plugins/sandbox/types.js";
51
51
  import { PluginStateRepository } from "../../plugins/state.js";
52
+ import {
53
+ removeAllPluginIndexes,
54
+ syncDeclaredStorageIndexes,
55
+ } from "../../plugins/storage-indexes.js";
52
56
  import { declaredAccessToCapabilities } from "../../plugins/types.js";
53
57
  import type { DeclaredAccess } from "../../plugins/types.js";
54
58
  import {
@@ -1181,6 +1185,8 @@ export async function handleRegistryInstall(
1181
1185
  throw stateErr;
1182
1186
  }
1183
1187
 
1188
+ await syncDeclaredStorageIndexes(db, [bundle.manifest]);
1189
+
1184
1190
  return {
1185
1191
  success: true,
1186
1192
  data: {
@@ -1288,6 +1294,12 @@ export async function handleRegistryUninstall(
1288
1294
  await deleteBundleFromR2(storage, pluginId, version, "registry");
1289
1295
  }
1290
1296
 
1297
+ try {
1298
+ await removeAllPluginIndexes(db, pluginId);
1299
+ } catch {
1300
+ // Nothing to drop, or tracking table predates the feature
1301
+ }
1302
+
1291
1303
  await stateRepo.delete(pluginId);
1292
1304
 
1293
1305
  return { success: true, data: { pluginId, dataDeleted } };
@@ -1646,6 +1658,8 @@ export async function handleRegistryUpdate(
1646
1658
  mcpToolsConsent: null,
1647
1659
  });
1648
1660
 
1661
+ await syncDeclaredStorageIndexes(db, [bundle.manifest]);
1662
+
1649
1663
  // Best-effort cleanup of the old bundle. Failures here don't roll
1650
1664
  // back the upgrade (the new bundle is already stored and committed
1651
1665
  // in the state row); the orphan is just storage we'll pay for.
@@ -4,6 +4,7 @@
4
4
 
5
5
  import type { Kysely } from "kysely";
6
6
 
7
+ import { after } from "../../after.js";
7
8
  import { ContentRepository } from "../../database/repositories/content.js";
8
9
  import { RevisionRepository, type Revision } from "../../database/repositories/revision.js";
9
10
  import { withTransaction } from "../../database/transaction.js";
@@ -115,7 +116,7 @@ export async function handleRevisionRestore(
115
116
  // Atomically update content and create a new revision to record the restore.
116
117
  // If either operation fails, neither is committed (on engines that support
117
118
  // transactions; on D1, withTransaction falls back to sequential execution).
118
- const item = await withTransaction(db, async (trx) => {
119
+ const { item, queuedRevisionId } = await withTransaction(db, async (trx) => {
119
120
  const trxContentRepo = new ContentRepository(trx);
120
121
  const trxRevisionRepo = new RevisionRepository(trx);
121
122
 
@@ -124,19 +125,32 @@ export async function handleRevisionRestore(
124
125
  slug: typeof _slug === "string" ? _slug : undefined,
125
126
  });
126
127
 
127
- await trxRevisionRepo.create({
128
+ const queuedRevision = await trxRevisionRepo.create({
128
129
  collection: revision.collection,
129
130
  entryId: revision.entryId,
130
131
  data: revision.data,
131
132
  authorId: callerUserId,
132
133
  });
133
134
 
134
- return updated;
135
+ return { item: updated, queuedRevisionId: queuedRevision.id };
135
136
  });
136
137
 
137
- // Fire-and-forget: prune old revisions to prevent unbounded growth
138
138
  const pruneRepo = new RevisionRepository(db);
139
- void pruneRepo.pruneOldRevisions(revision.collection, revision.entryId, 50).catch(() => {});
139
+ after(async () => {
140
+ try {
141
+ await pruneRepo.pruneQueuedEntry(
142
+ revision.collection,
143
+ revision.entryId,
144
+ queuedRevisionId,
145
+ 50,
146
+ );
147
+ } catch (error) {
148
+ console.error(
149
+ `[revisions] Failed to prune revisions for ${revision.collection}/${revision.entryId}:`,
150
+ error,
151
+ );
152
+ }
153
+ });
140
154
 
141
155
  return {
142
156
  success: true,
@@ -423,6 +423,43 @@ export async function handleSchemaFieldDelete(
423
423
  }
424
424
  }
425
425
 
426
+ /**
427
+ * Reorder collections in the admin sidebar
428
+ */
429
+ export async function handleSchemaCollectionReorder(
430
+ db: Kysely<Database>,
431
+ slugs: string[],
432
+ ): Promise<ApiResult<{ success: boolean }>> {
433
+ try {
434
+ const registry = new SchemaRegistry(db);
435
+ await registry.reorderCollections(slugs);
436
+
437
+ return {
438
+ success: true,
439
+ data: { success: true },
440
+ };
441
+ } catch (error) {
442
+ if (error instanceof SchemaError) {
443
+ return {
444
+ success: false,
445
+ error: {
446
+ code: error.code,
447
+ message: error.message,
448
+ details: error.details,
449
+ },
450
+ };
451
+ }
452
+ console.error("[emdash] Failed to reorder collections:", error);
453
+ return {
454
+ success: false,
455
+ error: {
456
+ code: "SCHEMA_COLLECTION_REORDER_ERROR",
457
+ message: "Failed to reorder collections",
458
+ },
459
+ };
460
+ }
461
+ }
462
+
426
463
  /**
427
464
  * Reorder fields
428
465
  */
@@ -312,7 +312,7 @@ export async function generateSnapshot(
312
312
  await sql<Record<string, unknown>>`
313
313
  SELECT * FROM ${sql.raw(`"${tableName}"`)}
314
314
  WHERE deleted_at IS NULL
315
- AND (status = 'published' OR (status = 'scheduled' AND scheduled_at <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now')))
315
+ AND status = 'published'
316
316
  `.execute(db)
317
317
  ).rows;
318
318
  }
@@ -10,7 +10,7 @@
10
10
  import type { Kysely, Selectable } from "kysely";
11
11
  import { ulid } from "ulidx";
12
12
 
13
- import { TaxonomyRepository } from "../../database/repositories/taxonomy.js";
13
+ import { TaxonomyRepository, type SiblingPosition } from "../../database/repositories/taxonomy.js";
14
14
  import type { Database, TaxonomyDefTable } from "../../database/types.js";
15
15
  import { resolveConfiguredLocale } from "../../i18n/config.js";
16
16
  import { invalidateTaxonomyDefsCache, invalidateTermCache } from "../../taxonomies/index.js";
@@ -59,6 +59,10 @@ export interface TermListResponse {
59
59
  terms: TermWithCount[];
60
60
  }
61
61
 
62
+ export interface TermReorderResponse {
63
+ reordered: true;
64
+ }
65
+
62
66
  export interface TermResponse {
63
67
  term: TermData;
64
68
  }
@@ -432,6 +436,102 @@ export async function handleTermList(
432
436
  }
433
437
  }
434
438
 
439
+ /**
440
+ * Set the manual order of one sibling group.
441
+ *
442
+ * `ids` names the terms to move, in the desired order; each is a row id or a
443
+ * translation_group and resolves to the latter, because a position belongs to a
444
+ * term rather than to one of its locales. Reordering never reparents — moving a
445
+ * term under a different parent is a term update.
446
+ *
447
+ * The group is `(taxonomy, parentId)` and has no locale: `parentId` is the
448
+ * parent's translation_group (a row id is accepted and resolved), and `null`
449
+ * selects the top level — which for a flat taxonomy is every term.
450
+ *
451
+ * `ids` may be a *subset* of the group, and the listed terms are permuted
452
+ * within the positions they already occupy. A locale only renders the terms
453
+ * translated into it, so a caller often cannot name every member; a member left
454
+ * out keeps its place, which is also what makes a stale list harmless.
455
+ */
456
+ export async function handleTermReorder(
457
+ db: Kysely<Database>,
458
+ taxonomyName: string,
459
+ input: { parentId?: string | null; ids: string[] },
460
+ ): Promise<ApiResult<TermReorderResponse>> {
461
+ try {
462
+ const lookup = await requireTaxonomyDef(db, taxonomyName);
463
+ if (!lookup.success) return lookup;
464
+
465
+ const repo = new TaxonomyRepository(db);
466
+
467
+ let parentId = input.parentId ?? null;
468
+ if (parentId !== null) {
469
+ // Children store the parent's translation_group, so a caller passing a
470
+ // row id still addresses the right group. A parent whose anchor row is
471
+ // gone keeps the value as given (see TaxonomyRepository.resolveParentRef).
472
+ const parent = await repo.findById(parentId);
473
+ if (parent) parentId = parent.translationGroup ?? parent.id;
474
+ }
475
+
476
+ // Every locale's rows: membership is locale-agnostic, and a term the
477
+ // caller can't see still holds its slot.
478
+ // The order carries into how ties are resolved, so it must be stable
479
+ // across calls; with locales interleaved a tie falls to whichever
480
+ // locale's label sorts first, not to the order the caller rendered.
481
+ const members = await repo.findByName(taxonomyName, { parentId });
482
+ const siblings: SiblingPosition[] = [];
483
+ const seen = new Set<string>();
484
+ const resolve = new Map<string, string>();
485
+ for (const term of members) {
486
+ const group = term.translationGroup ?? term.id;
487
+ if (!seen.has(group)) {
488
+ seen.add(group);
489
+ siblings.push({ group, position: term.sortOrder });
490
+ }
491
+ // Both spellings resolve to the group: a row id, and the group itself for
492
+ // a caller that already holds one — including when its anchor row is gone
493
+ // and no member's id matches it.
494
+ resolve.set(group, group);
495
+ resolve.set(term.id, group);
496
+ }
497
+
498
+ const groups: string[] = [];
499
+ for (const id of input.ids) {
500
+ const group = resolve.get(id);
501
+ if (!group) {
502
+ return {
503
+ success: false,
504
+ error: {
505
+ code: "REORDER_MISMATCH",
506
+ message: `Term "${id}" is not in the sibling group being reordered`,
507
+ },
508
+ };
509
+ }
510
+ if (groups.includes(group)) {
511
+ return {
512
+ success: false,
513
+ error: {
514
+ code: "REORDER_MISMATCH",
515
+ message: `Term "${id}" is listed more than once`,
516
+ },
517
+ };
518
+ }
519
+ groups.push(group);
520
+ }
521
+
522
+ await repo.reorder(groups, siblings);
523
+ invalidateTermCache();
524
+
525
+ return { success: true, data: { reordered: true } };
526
+ } catch (error) {
527
+ console.error("[taxonomies] term reorder failed:", error);
528
+ return {
529
+ success: false,
530
+ error: { code: "TERM_REORDER_ERROR", message: "Failed to reorder terms" },
531
+ };
532
+ }
533
+ }
534
+
435
535
  /**
436
536
  * Validate a parent term reference for create/update.
437
537
  *