emdash 0.28.0 → 0.29.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 (687) hide show
  1. package/dist/{adapters-hUy8vOB-.d.mts → adapters-DPAkL6QF.d.mts} +1 -1
  2. package/dist/{adapters-hUy8vOB-.d.mts.map → adapters-DPAkL6QF.d.mts.map} +1 -1
  3. package/dist/{allowed-origins-CRsJ6BPM.mjs → allowed-origins-FXB-FplB.mjs} +2 -2
  4. package/dist/{allowed-origins-CRsJ6BPM.mjs.map → allowed-origins-FXB-FplB.mjs.map} +1 -1
  5. package/dist/api/route-utils.d.mts +3 -3
  6. package/dist/api/route-utils.mjs +22 -21
  7. package/dist/api/route-utils.mjs.map +1 -1
  8. package/dist/api/schemas/index.d.mts +2 -2
  9. package/dist/api/schemas/index.mjs +4 -4
  10. package/dist/{api-BFURZn1i.mjs → api-DVbHNApo.mjs} +75 -23
  11. package/dist/api-DVbHNApo.mjs.map +1 -0
  12. package/dist/{api-tokens-DvqeZmVq.mjs → api-tokens-UYBkl17d.mjs} +2 -2
  13. package/dist/{api-tokens-DvqeZmVq.mjs.map → api-tokens-UYBkl17d.mjs.map} +1 -1
  14. package/dist/{apply-Bu-8nYHL.mjs → apply-D23iUV4q.mjs} +20 -20
  15. package/dist/{apply-Bu-8nYHL.mjs.map → apply-D23iUV4q.mjs.map} +1 -1
  16. package/dist/astro/index.d.mts +13 -12
  17. package/dist/astro/index.d.mts.map +1 -1
  18. package/dist/astro/index.mjs +10 -5
  19. package/dist/astro/index.mjs.map +1 -1
  20. package/dist/astro/middleware/auth.d.mts +11 -10
  21. package/dist/astro/middleware/auth.d.mts.map +1 -1
  22. package/dist/astro/middleware/auth.mjs +8 -8
  23. package/dist/astro/middleware/redirect.mjs +7 -7
  24. package/dist/astro/middleware/request-context.d.mts +3 -0
  25. package/dist/astro/middleware/request-context.d.mts.map +1 -1
  26. package/dist/astro/middleware/request-context.mjs +257 -30
  27. package/dist/astro/middleware/request-context.mjs.map +1 -1
  28. package/dist/astro/middleware/setup.mjs +1 -1
  29. package/dist/astro/middleware.d.mts +2 -2
  30. package/dist/astro/middleware.d.mts.map +1 -1
  31. package/dist/astro/middleware.mjs +125 -79
  32. package/dist/astro/middleware.mjs.map +1 -1
  33. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -7
  34. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -7
  35. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +5 -5
  36. package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
  37. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +6 -6
  38. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +10 -10
  39. package/dist/astro/routes/api/admin/byline-fields/index.mjs +10 -10
  40. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +10 -10
  41. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +17 -17
  42. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +17 -17
  43. package/dist/astro/routes/api/admin/bylines/index.mjs +17 -17
  44. package/dist/astro/routes/api/admin/comments/_id_/status.d.mts.map +1 -1
  45. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +13 -13
  46. package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
  47. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
  48. package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
  49. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
  50. package/dist/astro/routes/api/admin/comments/index.d.mts.map +1 -1
  51. package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
  52. package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
  53. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +6 -6
  54. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +5 -5
  55. package/dist/astro/routes/api/admin/media-usage/repair.d.mts +8 -0
  56. package/dist/astro/routes/api/admin/media-usage/repair.d.mts.map +1 -0
  57. package/dist/astro/routes/api/admin/media-usage/repair.mjs +28 -0
  58. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -0
  59. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +5 -5
  60. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +5 -5
  61. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +43 -41
  62. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  63. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +43 -41
  64. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  65. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +42 -40
  66. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  67. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +42 -40
  68. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  69. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +42 -40
  70. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  71. package/dist/astro/routes/api/admin/plugins/index.mjs +42 -40
  72. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  73. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +4 -4
  74. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +42 -40
  75. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +42 -40
  77. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +42 -40
  79. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +42 -40
  81. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +43 -41
  83. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  84. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +42 -40
  85. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  86. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +43 -41
  87. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  88. package/dist/astro/routes/api/admin/plugins/updates.mjs +42 -40
  89. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  90. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +42 -40
  91. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  92. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +4 -4
  93. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +42 -40
  94. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  95. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +4 -4
  96. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +3 -3
  97. package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -8
  98. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +5 -5
  99. package/dist/astro/routes/api/admin/users/index.mjs +7 -7
  100. package/dist/astro/routes/api/auth/dev-bypass.mjs +7 -7
  101. package/dist/astro/routes/api/auth/invite/accept.mjs +3 -3
  102. package/dist/astro/routes/api/auth/invite/complete.mjs +12 -12
  103. package/dist/astro/routes/api/auth/invite/index.mjs +9 -9
  104. package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -11
  105. package/dist/astro/routes/api/auth/logout.mjs +4 -4
  106. package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -11
  107. package/dist/astro/routes/api/auth/magic-link/verify.mjs +4 -4
  108. package/dist/astro/routes/api/auth/me.mjs +8 -8
  109. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  110. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  111. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs.map +1 -1
  112. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  113. package/dist/astro/routes/api/auth/oauth/_provider_.mjs.map +1 -1
  114. package/dist/astro/routes/api/auth/passkey/_id_.mjs +7 -7
  115. package/dist/astro/routes/api/auth/passkey/index.mjs +3 -3
  116. package/dist/astro/routes/api/auth/passkey/options.mjs +13 -13
  117. package/dist/astro/routes/api/auth/passkey/register/options.mjs +11 -11
  118. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +12 -12
  119. package/dist/astro/routes/api/auth/passkey/verify.mjs +12 -12
  120. package/dist/astro/routes/api/auth/signup/complete.mjs +12 -12
  121. package/dist/astro/routes/api/auth/signup/request.mjs +11 -11
  122. package/dist/astro/routes/api/auth/signup/verify.mjs +3 -3
  123. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.d.mts.map +1 -1
  124. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +69 -14
  125. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
  126. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +13 -13
  127. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +4 -4
  128. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +4 -4
  129. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +4 -4
  130. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +4 -4
  131. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +12 -12
  132. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +8 -8
  133. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.d.mts +9 -0
  134. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.d.mts.map +1 -0
  135. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +71 -0
  136. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -0
  137. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.d.mts +8 -0
  138. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.d.mts.map +1 -0
  139. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +50 -0
  140. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -0
  141. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +4 -4
  142. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +4 -4
  143. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -8
  144. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +15 -15
  145. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +4 -4
  146. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +4 -4
  147. package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
  148. package/dist/astro/routes/api/content/_collection_/authors.mjs +4 -4
  149. package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
  150. package/dist/astro/routes/api/content/_collection_/trash.mjs +8 -8
  151. package/dist/astro/routes/api/dashboard.mjs +8 -8
  152. package/dist/astro/routes/api/dev/emails.mjs +4 -4
  153. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  154. package/dist/astro/routes/api/import/probe.mjs +18 -18
  155. package/dist/astro/routes/api/import/wordpress/analyze.mjs +5 -5
  156. package/dist/astro/routes/api/import/wordpress/execute.d.mts +12 -11
  157. package/dist/astro/routes/api/import/wordpress/execute.d.mts.map +1 -1
  158. package/dist/astro/routes/api/import/wordpress/execute.mjs +16 -16
  159. package/dist/astro/routes/api/import/wordpress/media.d.mts +11 -10
  160. package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
  161. package/dist/astro/routes/api/import/wordpress/media.mjs +11 -11
  162. package/dist/astro/routes/api/import/wordpress/prepare.mjs +11 -11
  163. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +12 -16
  164. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts.map +1 -1
  165. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +11 -11
  166. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  167. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +18 -18
  168. package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
  169. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +12 -11
  170. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts.map +1 -1
  171. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +28 -28
  172. package/dist/astro/routes/api/manifest.mjs +6 -6
  173. package/dist/astro/routes/api/mcp.mjs +56 -47
  174. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  175. package/dist/astro/routes/api/media/_id_/confirm.mjs +9 -9
  176. package/dist/astro/routes/api/media/_id_.mjs +8 -8
  177. package/dist/astro/routes/api/media/file/_...key_.mjs +3 -3
  178. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +4 -4
  179. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +4 -4
  180. package/dist/astro/routes/api/media/providers/index.mjs +4 -4
  181. package/dist/astro/routes/api/media/upload-url.mjs +10 -10
  182. package/dist/astro/routes/api/media.mjs +13 -13
  183. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +9 -9
  184. package/dist/astro/routes/api/menus/_name_/items.mjs +9 -9
  185. package/dist/astro/routes/api/menus/_name_/reorder.mjs +9 -9
  186. package/dist/astro/routes/api/menus/_name_/translations.mjs +9 -9
  187. package/dist/astro/routes/api/menus/_name_.mjs +9 -9
  188. package/dist/astro/routes/api/menus/index.mjs +9 -9
  189. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  190. package/dist/astro/routes/api/oauth/device/authorize.mjs +7 -7
  191. package/dist/astro/routes/api/oauth/device/code.mjs +10 -10
  192. package/dist/astro/routes/api/oauth/device/token.mjs +9 -9
  193. package/dist/astro/routes/api/oauth/register.mjs +4 -4
  194. package/dist/astro/routes/api/oauth/token/refresh.mjs +7 -7
  195. package/dist/astro/routes/api/oauth/token/revoke.mjs +7 -7
  196. package/dist/astro/routes/api/oauth/token.mjs +7 -7
  197. package/dist/astro/routes/api/openapi.json.mjs +24 -5
  198. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  199. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.d.mts.map +1 -1
  200. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +7 -15
  201. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs.map +1 -1
  202. package/dist/astro/routes/api/redirects/404s/index.mjs +12 -12
  203. package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -12
  204. package/dist/astro/routes/api/redirects/_id_.mjs +13 -13
  205. package/dist/astro/routes/api/redirects/index.mjs +13 -13
  206. package/dist/astro/routes/api/relations/_id_/index.d.mts +10 -0
  207. package/dist/astro/routes/api/relations/_id_/index.d.mts.map +1 -0
  208. package/dist/astro/routes/api/relations/_id_/index.mjs +69 -0
  209. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -0
  210. package/dist/astro/routes/api/relations/_id_/translations.d.mts +8 -0
  211. package/dist/astro/routes/api/relations/_id_/translations.d.mts.map +1 -0
  212. package/dist/astro/routes/api/relations/_id_/translations.mjs +34 -0
  213. package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -0
  214. package/dist/astro/routes/api/relations/index.d.mts +9 -0
  215. package/dist/astro/routes/api/relations/index.d.mts.map +1 -0
  216. package/dist/astro/routes/api/relations/index.mjs +53 -0
  217. package/dist/astro/routes/api/relations/index.mjs.map +1 -0
  218. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +4 -4
  219. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +4 -4
  220. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.d.mts.map +1 -1
  221. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +42 -40
  222. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  223. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.d.mts.map +1 -1
  224. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +42 -40
  225. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  226. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +42 -40
  227. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  228. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +42 -40
  229. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  230. package/dist/astro/routes/api/schema/collections/index.mjs +42 -40
  231. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  232. package/dist/astro/routes/api/schema/index.mjs +9 -9
  233. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +42 -40
  234. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  235. package/dist/astro/routes/api/schema/orphans/index.mjs +42 -40
  236. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  237. package/dist/astro/routes/api/search/enable.mjs +12 -12
  238. package/dist/astro/routes/api/search/index.mjs +13 -12
  239. package/dist/astro/routes/api/search/index.mjs.map +1 -1
  240. package/dist/astro/routes/api/search/rebuild.mjs +12 -12
  241. package/dist/astro/routes/api/search/stats.mjs +8 -8
  242. package/dist/astro/routes/api/search/suggest.mjs +11 -11
  243. package/dist/astro/routes/api/sections/_slug_.mjs +11 -11
  244. package/dist/astro/routes/api/sections/index.mjs +11 -11
  245. package/dist/astro/routes/api/settings/email.mjs +6 -6
  246. package/dist/astro/routes/api/settings.mjs +15 -15
  247. package/dist/astro/routes/api/setup/admin-verify.mjs +13 -13
  248. package/dist/astro/routes/api/setup/admin.mjs +12 -12
  249. package/dist/astro/routes/api/setup/dev-bypass.mjs +29 -29
  250. package/dist/astro/routes/api/setup/dev-reset.mjs +4 -4
  251. package/dist/astro/routes/api/setup/index.mjs +30 -30
  252. package/dist/astro/routes/api/setup/status.mjs +5 -5
  253. package/dist/astro/routes/api/snapshot.mjs +8 -8
  254. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +15 -15
  255. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +15 -15
  256. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +15 -15
  257. package/dist/astro/routes/api/taxonomies/index.mjs +15 -15
  258. package/dist/astro/routes/api/themes/preview.mjs +7 -7
  259. package/dist/astro/routes/api/typegen.mjs +6 -6
  260. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  261. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  262. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  263. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +8 -8
  264. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +12 -12
  265. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +12 -12
  266. package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -8
  267. package/dist/astro/routes/api/widget-areas/index.mjs +12 -12
  268. package/dist/astro/routes/api/widget-components.mjs +4 -4
  269. package/dist/astro/routes/robots.txt.mjs +9 -9
  270. package/dist/astro/routes/sitemap-_collection_.xml.mjs +12 -12
  271. package/dist/astro/routes/sitemap.xml.mjs +10 -10
  272. package/dist/astro/types.d.mts +16 -13
  273. package/dist/astro/types.d.mts.map +1 -1
  274. package/dist/auth/providers/github.d.mts +1 -1
  275. package/dist/auth/providers/google.d.mts +1 -1
  276. package/dist/{authorize-Dz88F_Fw.mjs → authorize-BmCwQCib.mjs} +2 -2
  277. package/dist/{authorize-Dz88F_Fw.mjs.map → authorize-BmCwQCib.mjs.map} +1 -1
  278. package/dist/{base64-CmWvODNW.mjs → base64-C74Msx8h.mjs} +1 -1
  279. package/dist/{base64-CmWvODNW.mjs.map → base64-C74Msx8h.mjs.map} +1 -1
  280. package/dist/{byline-Bm8ZgUP9.mjs → byline-BWb5H-65.mjs} +8 -8
  281. package/dist/{byline-Bm8ZgUP9.mjs.map → byline-BWb5H-65.mjs.map} +1 -1
  282. package/dist/{byline-fields-CIx_GuN4.mjs → byline-fields-DO6bAYK8.mjs} +2 -2
  283. package/dist/{byline-fields-CIx_GuN4.mjs.map → byline-fields-DO6bAYK8.mjs.map} +1 -1
  284. package/dist/{byline-registry-BOjqDOim.mjs → byline-registry-BCuOp4UF.mjs} +3 -3
  285. package/dist/{byline-registry-BOjqDOim.mjs.map → byline-registry-BCuOp4UF.mjs.map} +1 -1
  286. package/dist/{bylines-6tm9mgQb.mjs → bylines-BGuKFaBy.mjs} +4 -4
  287. package/dist/{bylines-6tm9mgQb.mjs.map → bylines-BGuKFaBy.mjs.map} +1 -1
  288. package/dist/{bylines-Woq87129.mjs → bylines-CHojDMpj.mjs} +8 -8
  289. package/dist/{bylines-Woq87129.mjs.map → bylines-CHojDMpj.mjs.map} +1 -1
  290. package/dist/{cache-CEBAZjir.mjs → cache-BeR7RfN8.mjs} +3 -3
  291. package/dist/{cache-CEBAZjir.mjs.map → cache-BeR7RfN8.mjs.map} +1 -1
  292. package/dist/{challenge-store-anhwVER0.mjs → challenge-store-B426wQ6O.mjs} +1 -1
  293. package/dist/{challenge-store-anhwVER0.mjs.map → challenge-store-B426wQ6O.mjs.map} +1 -1
  294. package/dist/{chunks-B2isWQhr.mjs → chunks-BU5_nrAC.mjs} +2 -2
  295. package/dist/{chunks-B2isWQhr.mjs.map → chunks-BU5_nrAC.mjs.map} +1 -1
  296. package/dist/cli/index.mjs +28 -28
  297. package/dist/cli/index.mjs.map +1 -1
  298. package/dist/client/cf-access.d.mts +1 -1
  299. package/dist/client/index.d.mts +34 -2
  300. package/dist/client/index.d.mts.map +1 -1
  301. package/dist/client/index.mjs +6 -2
  302. package/dist/client/index.mjs.map +1 -1
  303. package/dist/{comment-CzeatwBV.mjs → comment-SWh31kS0.mjs} +2 -2
  304. package/dist/{comment-CzeatwBV.mjs.map → comment-SWh31kS0.mjs.map} +1 -1
  305. package/dist/{comment-reaction-Cj9QJY7I.mjs → comment-reaction-DgcpziXI.mjs} +2 -2
  306. package/dist/{comment-reaction-Cj9QJY7I.mjs.map → comment-reaction-DgcpziXI.mjs.map} +1 -1
  307. package/dist/{comments-BLE-jv5k.mjs → comments-kZYGppMS.mjs} +3 -3
  308. package/dist/{comments-BLE-jv5k.mjs.map → comments-kZYGppMS.mjs.map} +1 -1
  309. package/dist/{components-B_sATNUc.mjs → components-CliOkZAs.mjs} +1 -1
  310. package/dist/{components-B_sATNUc.mjs.map → components-CliOkZAs.mjs.map} +1 -1
  311. package/dist/{content-DhgEth4k.mjs → content-BO61Sx4j.mjs} +179 -20
  312. package/dist/content-BO61Sx4j.mjs.map +1 -0
  313. package/dist/{content-refresh-CA4uKwzH.mjs → content-refresh-BkkApzeS.mjs} +20 -14
  314. package/dist/content-refresh-BkkApzeS.mjs.map +1 -0
  315. package/dist/{context-CUsR9dXL.mjs → context-DtSFYFuS.mjs} +71 -14
  316. package/dist/context-DtSFYFuS.mjs.map +1 -0
  317. package/dist/{cron-v4FTEFR_.mjs → cron-YFJ7uNVA.mjs} +1 -1
  318. package/dist/{cron-v4FTEFR_.mjs.map → cron-YFJ7uNVA.mjs.map} +1 -1
  319. package/dist/{dashboard-EvEKF5en.mjs → dashboard-xo_gPs_D.mjs} +6 -5
  320. package/dist/dashboard-xo_gPs_D.mjs.map +1 -0
  321. package/dist/database/pg-migration-lock.d.mts +21 -0
  322. package/dist/database/pg-migration-lock.d.mts.map +1 -0
  323. package/dist/database/pg-migration-lock.mjs +45 -0
  324. package/dist/database/pg-migration-lock.mjs.map +1 -0
  325. package/dist/db/index.d.mts +3 -3
  326. package/dist/db/index.mjs +2 -2
  327. package/dist/db/libsql.d.mts +1 -1
  328. package/dist/db/postgres.d.mts +1 -1
  329. package/dist/db/postgres.d.mts.map +1 -1
  330. package/dist/db/postgres.mjs +2 -8
  331. package/dist/db/postgres.mjs.map +1 -1
  332. package/dist/db/sqlite.d.mts +1 -1
  333. package/dist/{db-errors-CK46D-ly.mjs → db-errors-BKuXh_fi.mjs} +1 -1
  334. package/dist/{db-errors-CK46D-ly.mjs.map → db-errors-BKuXh_fi.mjs.map} +1 -1
  335. package/dist/{default-CM_D83Mg.mjs → default-DY5ovGUG.mjs} +1 -1
  336. package/dist/{default-CM_D83Mg.mjs.map → default-DY5ovGUG.mjs.map} +1 -1
  337. package/dist/{device-flow-CfGP18Uw.mjs → device-flow-CPXQ_QfW.mjs} +5 -5
  338. package/dist/{device-flow-CfGP18Uw.mjs.map → device-flow-CPXQ_QfW.mjs.map} +1 -1
  339. package/dist/{dialect-helpers-DRI5pyY3.mjs → dialect-helpers-Cue7QQOR.mjs} +21 -2
  340. package/dist/dialect-helpers-Cue7QQOR.mjs.map +1 -0
  341. package/dist/{email-console-CIf4Lfvv.mjs → email-console-CjkQc7oY.mjs} +1 -1
  342. package/dist/{email-console-CIf4Lfvv.mjs.map → email-console-CjkQc7oY.mjs.map} +1 -1
  343. package/dist/{enrich-BQ0mxJRs.mjs → enrich-CFJJgxs_.mjs} +2 -2
  344. package/dist/{enrich-BQ0mxJRs.mjs.map → enrich-CFJJgxs_.mjs.map} +1 -1
  345. package/dist/{error-CkAseSqZ.mjs → error-CfJMLSkd.mjs} +12 -3
  346. package/dist/error-CfJMLSkd.mjs.map +1 -0
  347. package/dist/{escape-DqD0aCKu.mjs → escape-r8_GbEU2.mjs} +1 -1
  348. package/dist/{escape-DqD0aCKu.mjs.map → escape-r8_GbEU2.mjs.map} +1 -1
  349. package/dist/{field-defs-cache-WUI88LX5.mjs → field-defs-cache-BVrNCgSj.mjs} +5 -5
  350. package/dist/{field-defs-cache-WUI88LX5.mjs.map → field-defs-cache-BVrNCgSj.mjs.map} +1 -1
  351. package/dist/{fts-manager-BbFaF_V8.mjs → fts-manager-BD9s77qh.mjs} +27 -3
  352. package/dist/{fts-manager-BbFaF_V8.mjs.map → fts-manager-BD9s77qh.mjs.map} +1 -1
  353. package/dist/{hash-DrvzIXcz.mjs → hash-DFFrkivP.mjs} +1 -1
  354. package/dist/{hash-DrvzIXcz.mjs.map → hash-DFFrkivP.mjs.map} +1 -1
  355. package/dist/hreflang-CgIxhH4k.d.mts +48 -0
  356. package/dist/hreflang-CgIxhH4k.d.mts.map +1 -0
  357. package/dist/hreflang-pAWUiPd_.mjs +102 -0
  358. package/dist/hreflang-pAWUiPd_.mjs.map +1 -0
  359. package/dist/{import-fYLf14Co.mjs → import-2jHtLzsO.mjs} +6 -6
  360. package/dist/{import-fYLf14Co.mjs.map → import-2jHtLzsO.mjs.map} +1 -1
  361. package/dist/{index-B9sIxwe4.d.mts → index-BXOCyLj0.d.mts} +115 -21
  362. package/dist/index-BXOCyLj0.d.mts.map +1 -0
  363. package/dist/{index-Bm-Gy8Mh.d.mts → index-DxjWLKGT.d.mts} +3 -3
  364. package/dist/{index-Bm-Gy8Mh.d.mts.map → index-DxjWLKGT.d.mts.map} +1 -1
  365. package/dist/index.d.mts +20 -19
  366. package/dist/index.mjs +68 -64
  367. package/dist/{init-lock-6b309ZrF.mjs → init-lock-DJkX6Hto.mjs} +1 -1
  368. package/dist/{init-lock-6b309ZrF.mjs.map → init-lock-DJkX6Hto.mjs.map} +1 -1
  369. package/dist/{load-Djn3HWQS.mjs → load-DgoWMdRX.mjs} +2 -2
  370. package/dist/{load-Djn3HWQS.mjs.map → load-DgoWMdRX.mjs.map} +1 -1
  371. package/dist/{loader-CB6YHeul.mjs → loader-Rb8QlGZp.mjs} +5 -20
  372. package/dist/loader-Rb8QlGZp.mjs.map +1 -0
  373. package/dist/{manifest-schema-BHlgZM1W.mjs → manifest-schema-DeGNma2O.mjs} +3 -1
  374. package/dist/manifest-schema-DeGNma2O.mjs.map +1 -0
  375. package/dist/media/image-endpoint.mjs +1 -1
  376. package/dist/media/index.d.mts +1 -1
  377. package/dist/media/index.mjs +2 -2
  378. package/dist/media/local-runtime.d.mts +13 -12
  379. package/dist/media/local-runtime.d.mts.map +1 -1
  380. package/dist/media/local-runtime.mjs +8 -8
  381. package/dist/{media-BrXIaMFN.mjs → media-D2ZFowpn.mjs} +2 -2
  382. package/dist/media-D2ZFowpn.mjs.map +1 -0
  383. package/dist/{media-allowlist-CRHEeJ_i.mjs → media-allowlist-Dc05vKtt.mjs} +2 -2
  384. package/dist/{media-allowlist-CRHEeJ_i.mjs.map → media-allowlist-Dc05vKtt.mjs.map} +1 -1
  385. package/dist/{media-url-CKi5XSw4.mjs → media-url-DgIEYZr4.mjs} +1 -1
  386. package/dist/{media-url-CKi5XSw4.mjs.map → media-url-DgIEYZr4.mjs.map} +1 -1
  387. package/dist/media-usage-B__v9NMm.mjs +421 -0
  388. package/dist/media-usage-B__v9NMm.mjs.map +1 -0
  389. package/dist/{menus-Cfivc2Ad.mjs → menus-BpfevlJD.mjs} +3 -3
  390. package/dist/{menus-Cfivc2Ad.mjs.map → menus-BpfevlJD.mjs.map} +1 -1
  391. package/dist/{menus-CsZ2ZEK2.mjs → menus-BtvH7Z7t.mjs} +31 -12
  392. package/dist/menus-BtvH7Z7t.mjs.map +1 -0
  393. package/dist/{mime-CCEzze7W.mjs → mime-DfIVjNkr.mjs} +1 -1
  394. package/dist/{mime-CCEzze7W.mjs.map → mime-DfIVjNkr.mjs.map} +1 -1
  395. package/dist/{mode-BM9QgEmt.mjs → mode-BVnhGZ2o.mjs} +1 -1
  396. package/dist/{mode-BM9QgEmt.mjs.map → mode-BVnhGZ2o.mjs.map} +1 -1
  397. package/dist/{normalize-CeglrBT9.mjs → normalize-C-SHXmra.mjs} +1 -1
  398. package/dist/{normalize-CeglrBT9.mjs.map → normalize-C-SHXmra.mjs.map} +1 -1
  399. package/dist/{oauth-authorization-CXWkIGT4.mjs → oauth-authorization-BWqWTHBK.mjs} +5 -5
  400. package/dist/{oauth-authorization-CXWkIGT4.mjs.map → oauth-authorization-BWqWTHBK.mjs.map} +1 -1
  401. package/dist/{oauth-clients-BmTI5rsl.mjs → oauth-clients-43_XADoa.mjs} +1 -1
  402. package/dist/{oauth-clients-BmTI5rsl.mjs.map → oauth-clients-43_XADoa.mjs.map} +1 -1
  403. package/dist/{oauth-state-store-YLFLcH5F.mjs → oauth-state-store-BRSUEkmx.mjs} +1 -1
  404. package/dist/{oauth-state-store-YLFLcH5F.mjs.map → oauth-state-store-BRSUEkmx.mjs.map} +1 -1
  405. package/dist/{oauth-user-lookup-Bsc_GsX-.mjs → oauth-user-lookup-Ctgemb0k.mjs} +1 -1
  406. package/dist/{oauth-user-lookup-Bsc_GsX-.mjs.map → oauth-user-lookup-Ctgemb0k.mjs.map} +1 -1
  407. package/dist/object-cache/memory.d.mts +1 -1
  408. package/dist/{options-DTTML-Tx.mjs → options-BlmBHTvX.mjs} +1 -1
  409. package/dist/{options-DTTML-Tx.mjs.map → options-BlmBHTvX.mjs.map} +1 -1
  410. package/dist/{options-iMpRRxIe.d.mts → options-COXGTtqd.d.mts} +3 -3
  411. package/dist/{options-iMpRRxIe.d.mts.map → options-COXGTtqd.d.mts.map} +1 -1
  412. package/dist/page/index.d.mts +2 -2
  413. package/dist/{parse-GmjecGct.mjs → parse-GLksnn-B.mjs} +2 -2
  414. package/dist/{parse-GmjecGct.mjs.map → parse-GLksnn-B.mjs.map} +1 -1
  415. package/dist/{passkey-config-De0ahnRg.mjs → passkey-config-CjA1UbDj.mjs} +1 -1
  416. package/dist/{passkey-config-De0ahnRg.mjs.map → passkey-config-CjA1UbDj.mjs.map} +1 -1
  417. package/dist/{patterns-BKmjvM7K.mjs → patterns-CiyXeDgr.mjs} +1 -1
  418. package/dist/{patterns-BKmjvM7K.mjs.map → patterns-CiyXeDgr.mjs.map} +1 -1
  419. package/dist/{placeholder-BSKwzRe4.d.mts → placeholder-DR-eeZr-.d.mts} +1 -1
  420. package/dist/{placeholder-BSKwzRe4.d.mts.map → placeholder-DR-eeZr-.d.mts.map} +1 -1
  421. package/dist/plugin-types.d.mts +1 -1
  422. package/dist/plugin-utils.d.mts +11 -10
  423. package/dist/plugin-utils.d.mts.map +1 -1
  424. package/dist/plugins/adapt-sandbox-entry.d.mts +11 -10
  425. package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
  426. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  427. package/dist/{portable-text-cCa0cGV6.mjs → portable-text-BAkgEKcv.mjs} +1 -1
  428. package/dist/{portable-text-cCa0cGV6.mjs.map → portable-text-BAkgEKcv.mjs.map} +1 -1
  429. package/dist/{preview-SSXNVPAC.mjs → preview-Rloa09Wp.mjs} +2 -2
  430. package/dist/{preview-SSXNVPAC.mjs.map → preview-Rloa09Wp.mjs.map} +1 -1
  431. package/dist/{public-url-Danv47cS.mjs → public-url-C0cEvrRu.mjs} +1 -1
  432. package/dist/{public-url-Danv47cS.mjs.map → public-url-C0cEvrRu.mjs.map} +1 -1
  433. package/dist/{schema-Blhoxp3P.mjs → query-BxT95opY.mjs} +5 -10
  434. package/dist/query-BxT95opY.mjs.map +1 -0
  435. package/dist/{query-ByGq6ver.mjs → query-ZzvXyxWq.mjs} +15 -15
  436. package/dist/query-ZzvXyxWq.mjs.map +1 -0
  437. package/dist/{rate-limit-C_Lf63Dd.mjs → rate-limit-Cg4Rvc_-.mjs} +2 -2
  438. package/dist/{rate-limit-C_Lf63Dd.mjs.map → rate-limit-Cg4Rvc_-.mjs.map} +1 -1
  439. package/dist/{redirect-CoW3G-0U.mjs → redirect-Ce_6Yb6z.mjs} +1 -1
  440. package/dist/{redirect-CoW3G-0U.mjs.map → redirect-Ce_6Yb6z.mjs.map} +1 -1
  441. package/dist/{redirect-IyfNirgS.mjs → redirect-Ch3E98qZ.mjs} +4 -4
  442. package/dist/{redirect-IyfNirgS.mjs.map → redirect-Ch3E98qZ.mjs.map} +1 -1
  443. package/dist/{redirects-Bs1g42Ri.mjs → redirects-CXmVGRNZ.mjs} +6 -6
  444. package/dist/{redirects-Bs1g42Ri.mjs.map → redirects-CXmVGRNZ.mjs.map} +1 -1
  445. package/dist/{redirects-DXEXBAII.mjs → redirects-CeD7K7EX.mjs} +43 -5
  446. package/dist/redirects-CeD7K7EX.mjs.map +1 -0
  447. package/dist/{registry-B65mUtPa.mjs → registry-CyIXPi4E.mjs} +8 -8
  448. package/dist/{registry-B65mUtPa.mjs.map → registry-CyIXPi4E.mjs.map} +1 -1
  449. package/dist/relation-Ba68Tm4Z.mjs +334 -0
  450. package/dist/relation-Ba68Tm4Z.mjs.map +1 -0
  451. package/dist/{byline-fields-2Lp5Hbgf.mjs → relations-BmBmX4Eb.mjs} +60 -4
  452. package/dist/relations-BmBmX4Eb.mjs.map +1 -0
  453. package/dist/relations-C8hVyEy0.mjs +397 -0
  454. package/dist/relations-C8hVyEy0.mjs.map +1 -0
  455. package/dist/{byline-fields-Cgrt9-aL.d.mts → relations-CbBpoOys.d.mts} +180 -27
  456. package/dist/relations-CbBpoOys.d.mts.map +1 -0
  457. package/dist/{request-cache-KCNHp_RJ.mjs → request-cache-BSUptuJR.mjs} +1 -1
  458. package/dist/{request-cache-KCNHp_RJ.mjs.map → request-cache-BSUptuJR.mjs.map} +1 -1
  459. package/dist/{request-meta-DbvXaAvU.mjs → request-meta-DCwktC5P.mjs} +2 -2
  460. package/dist/{request-meta-DbvXaAvU.mjs.map → request-meta-DCwktC5P.mjs.map} +1 -1
  461. package/dist/{resolve-DR3wqMTy.mjs → resolve-Cd9dzclN.mjs} +1 -1
  462. package/dist/{resolve-DR3wqMTy.mjs.map → resolve-Cd9dzclN.mjs.map} +1 -1
  463. package/dist/{runner-DvFWoz8-.d.mts → runner-CiNdIk3y.d.mts} +8 -2
  464. package/dist/runner-CiNdIk3y.d.mts.map +1 -0
  465. package/dist/{runner-K8XK101B.mjs → runner-jt65bF7F.mjs} +24 -7
  466. package/dist/{runner-K8XK101B.mjs.map → runner-jt65bF7F.mjs.map} +1 -1
  467. package/dist/runtime.d.mts +12 -11
  468. package/dist/runtime.d.mts.map +1 -1
  469. package/dist/runtime.mjs +4 -4
  470. package/dist/{scheduled-publish-Drffrf2a.d.mts → scheduled-publish-DlJHGcuL.d.mts} +1 -1
  471. package/dist/scheduled-publish-DlJHGcuL.d.mts.map +1 -0
  472. package/dist/schema-DwjufZ1S.mjs +9 -0
  473. package/dist/schema-DwjufZ1S.mjs.map +1 -0
  474. package/dist/{search-DZN2HZ-V.mjs → search-Cl875CVE.mjs} +62 -12
  475. package/dist/search-Cl875CVE.mjs.map +1 -0
  476. package/dist/{secrets-kp760LlD.mjs → secrets-CO_bEmII.mjs} +4 -4
  477. package/dist/{secrets-kp760LlD.mjs.map → secrets-CO_bEmII.mjs.map} +1 -1
  478. package/dist/{sections-CpF5SaGr.mjs → sections-DTKzgoiS.mjs} +3 -3
  479. package/dist/{sections-CpF5SaGr.mjs.map → sections-DTKzgoiS.mjs.map} +1 -1
  480. package/dist/seed/index.d.mts +2 -2
  481. package/dist/seed/index.mjs +23 -23
  482. package/dist/seo/index.d.mts +4 -2
  483. package/dist/seo/index.d.mts.map +1 -1
  484. package/dist/seo/index.mjs +19 -2
  485. package/dist/seo/index.mjs.map +1 -1
  486. package/dist/{seo-DXu2UQnC.mjs → seo-BLm6SXzS.mjs} +2 -2
  487. package/dist/{seo-DXu2UQnC.mjs.map → seo-BLm6SXzS.mjs.map} +1 -1
  488. package/dist/{seo-89-53DDs.mjs → seo-BjrbXAd_.mjs} +1 -1
  489. package/dist/{seo-89-53DDs.mjs.map → seo-BjrbXAd_.mjs.map} +1 -1
  490. package/dist/{service-DQmiBMzt.mjs → service-D6zDmKXy.mjs} +3 -3
  491. package/dist/{service-DQmiBMzt.mjs.map → service-D6zDmKXy.mjs.map} +1 -1
  492. package/dist/{session-user-Cascbax7.mjs → session-user-DoSs6YM8.mjs} +1 -1
  493. package/dist/{session-user-Cascbax7.mjs.map → session-user-DoSs6YM8.mjs.map} +1 -1
  494. package/dist/{settings-zhmdKyJQ.mjs → settings-BuQYvLfy.mjs} +6 -6
  495. package/dist/{settings-zhmdKyJQ.mjs.map → settings-BuQYvLfy.mjs.map} +1 -1
  496. package/dist/{settings-rZyHEcNT.mjs → settings-CmS3TwXf.mjs} +3 -3
  497. package/dist/{settings-rZyHEcNT.mjs.map → settings-CmS3TwXf.mjs.map} +1 -1
  498. package/dist/{setup-complete-Cb3LRyzy.mjs → setup-complete-BPNgLHlU.mjs} +2 -2
  499. package/dist/{setup-complete-Cb3LRyzy.mjs.map → setup-complete-BPNgLHlU.mjs.map} +1 -1
  500. package/dist/{setup-nonce-Z8x0tHf8.mjs → setup-nonce-DEPZX96T.mjs} +1 -1
  501. package/dist/{setup-nonce-Z8x0tHf8.mjs.map → setup-nonce-DEPZX96T.mjs.map} +1 -1
  502. package/dist/{single-flight-cache-BG3B_GhN.mjs → single-flight-cache-DEtW-OEG.mjs} +2 -2
  503. package/dist/{single-flight-cache-BG3B_GhN.mjs.map → single-flight-cache-DEtW-OEG.mjs.map} +1 -1
  504. package/dist/{site-url-BBfDTgZB.mjs → site-url-CcLy5D5s.mjs} +2 -2
  505. package/dist/{site-url-BBfDTgZB.mjs.map → site-url-CcLy5D5s.mjs.map} +1 -1
  506. package/dist/{slugify-Cce3dTdg.mjs → slugify-C_tqlU4G.mjs} +1 -1
  507. package/dist/{slugify-Cce3dTdg.mjs.map → slugify-C_tqlU4G.mjs.map} +1 -1
  508. package/dist/{ssrf-1g7usGBg.mjs → ssrf-TrKYJAy0.mjs} +1 -1
  509. package/dist/{ssrf-1g7usGBg.mjs.map → ssrf-TrKYJAy0.mjs.map} +1 -1
  510. package/dist/{status-CqJQX1QN.mjs → status-D3Gy1Zc6.mjs} +1 -1
  511. package/dist/{status-CqJQX1QN.mjs.map → status-D3Gy1Zc6.mjs.map} +1 -1
  512. package/dist/storage/local.d.mts +1 -1
  513. package/dist/storage/local.mjs +1 -1
  514. package/dist/storage/s3.d.mts +1 -1
  515. package/dist/storage/s3.mjs +1 -1
  516. package/dist/{taxonomies-BGBd16Eu.mjs → taxonomies-ClS4RKaZ.mjs} +13 -9
  517. package/dist/taxonomies-ClS4RKaZ.mjs.map +1 -0
  518. package/dist/{taxonomies-Cnb4F-Pb.mjs → taxonomies-pUkuD6nj.mjs} +115 -36
  519. package/dist/taxonomies-pUkuD6nj.mjs.map +1 -0
  520. package/dist/{taxonomy-BxGxZZdu.mjs → taxonomy-BkxbUMSw.mjs} +12 -3
  521. package/dist/{taxonomy-BxGxZZdu.mjs.map → taxonomy-BkxbUMSw.mjs.map} +1 -1
  522. package/dist/{tokens-BGT0TUUo.mjs → tokens-BV6fgl5A.mjs} +2 -2
  523. package/dist/{tokens-BGT0TUUo.mjs.map → tokens-BV6fgl5A.mjs.map} +1 -1
  524. package/dist/{transaction-qfqpPVpu.mjs → transaction-D0FOsb3X.mjs} +1 -1
  525. package/dist/{transaction-qfqpPVpu.mjs.map → transaction-D0FOsb3X.mjs.map} +1 -1
  526. package/dist/{transport-DPefPMcz.mjs → transport-DTFhhPOg.mjs} +1 -1
  527. package/dist/{transport-DPefPMcz.mjs.map → transport-DTFhhPOg.mjs.map} +1 -1
  528. package/dist/{transport-BMDWHSgZ.d.mts → transport-DmlmzD2U.d.mts} +1 -1
  529. package/dist/{transport-BMDWHSgZ.d.mts.map → transport-DmlmzD2U.d.mts.map} +1 -1
  530. package/dist/{trusted-proxy-VTPRiX2s.mjs → trusted-proxy-CSgZVESw.mjs} +1 -1
  531. package/dist/{trusted-proxy-VTPRiX2s.mjs.map → trusted-proxy-CSgZVESw.mjs.map} +1 -1
  532. package/dist/{types-CgvhU-hF.d.mts → types-B6-N9cBB.d.mts} +1 -1
  533. package/dist/{types-CgvhU-hF.d.mts.map → types-B6-N9cBB.d.mts.map} +1 -1
  534. package/dist/{types-g_aFIdW0.d.mts → types-BCVuUPrL.d.mts} +1 -1
  535. package/dist/{types-g_aFIdW0.d.mts.map → types-BCVuUPrL.d.mts.map} +1 -1
  536. package/dist/{types-Jo3UYd6b.mjs → types-BNZsPL9z.mjs} +1 -1
  537. package/dist/{types-Jo3UYd6b.mjs.map → types-BNZsPL9z.mjs.map} +1 -1
  538. package/dist/{types-vgEWF18A.d.mts → types-B_pmP7Yq.d.mts} +1 -1
  539. package/dist/{types-vgEWF18A.d.mts.map → types-B_pmP7Yq.d.mts.map} +1 -1
  540. package/dist/{types-CuW0zWDA.mjs → types-Ch8pFMEm.mjs} +3 -3
  541. package/dist/{types-CuW0zWDA.mjs.map → types-Ch8pFMEm.mjs.map} +1 -1
  542. package/dist/{types-ea9sAPck.d.mts → types-Cm4Cz-8M.d.mts} +1 -1
  543. package/dist/{types-ea9sAPck.d.mts.map → types-Cm4Cz-8M.d.mts.map} +1 -1
  544. package/dist/{types-CGbYowGQ.d.mts → types-CtDVagM4.d.mts} +55 -3
  545. package/dist/types-CtDVagM4.d.mts.map +1 -0
  546. package/dist/{types-BcLUUbbR.d.mts → types-DLIrNtv5.d.mts} +11 -3
  547. package/dist/{types-BcLUUbbR.d.mts.map → types-DLIrNtv5.d.mts.map} +1 -1
  548. package/dist/{types-DmHk7vAt.d.mts → types-QnlQIyPE.d.mts} +1 -1
  549. package/dist/{types-DmHk7vAt.d.mts.map → types-QnlQIyPE.d.mts.map} +1 -1
  550. package/dist/{types-_HFLRzTn.d.mts → types-momGysZk.d.mts} +1 -1
  551. package/dist/{types-_HFLRzTn.d.mts.map → types-momGysZk.d.mts.map} +1 -1
  552. package/dist/{types-tM44hEcf.mjs → types-o7xo7VgH.mjs} +1 -1
  553. package/dist/{types-tM44hEcf.mjs.map → types-o7xo7VgH.mjs.map} +1 -1
  554. package/dist/{types-ClUkdbWb.d.mts → types-qljd1hsL.d.mts} +1 -1
  555. package/dist/{types-ClUkdbWb.d.mts.map → types-qljd1hsL.d.mts.map} +1 -1
  556. package/dist/{user-BjAOvU3Z.mjs → user-DTxX_Fha.mjs} +3 -3
  557. package/dist/{user-BjAOvU3Z.mjs.map → user-DTxX_Fha.mjs.map} +1 -1
  558. package/dist/{utils-BJXElxdw.mjs → utils-Cz8PVrl4.mjs} +3 -3
  559. package/dist/{utils-BJXElxdw.mjs.map → utils-Cz8PVrl4.mjs.map} +1 -1
  560. package/dist/{validate-Z1b86_3B.d.mts → validate-9jLuE301.d.mts} +5 -5
  561. package/dist/{validate-Z1b86_3B.d.mts.map → validate-9jLuE301.d.mts.map} +1 -1
  562. package/dist/{validate-CWSnKVIJ.mjs → validate-C3CpWlNt.mjs} +3 -3
  563. package/dist/{validate-CWSnKVIJ.mjs.map → validate-C3CpWlNt.mjs.map} +1 -1
  564. package/dist/{validation-c8WjhH8z.mjs → validation-DM2K_NJC.mjs} +10 -10
  565. package/dist/{validation-c8WjhH8z.mjs.map → validation-DM2K_NJC.mjs.map} +1 -1
  566. package/dist/version-BlIJvNAZ.mjs +7 -0
  567. package/dist/{version-CaccU7DM.mjs.map → version-BlIJvNAZ.mjs.map} +1 -1
  568. package/dist/{widgets-hWcIicX8.mjs → widgets-B_X6_UXJ.mjs} +4 -4
  569. package/dist/{widgets-hWcIicX8.mjs.map → widgets-B_X6_UXJ.mjs.map} +1 -1
  570. package/dist/{wxr-taxonomies-BC3OYpvx.mjs → wxr-taxonomies-oNJB2S81.mjs} +4 -4
  571. package/dist/{wxr-taxonomies-BC3OYpvx.mjs.map → wxr-taxonomies-oNJB2S81.mjs.map} +1 -1
  572. package/dist/{wxr-taxonomies-0_uxSrmf.d.mts → wxr-taxonomies-tADW4KTa.d.mts} +1 -1
  573. package/dist/{wxr-taxonomies-0_uxSrmf.d.mts.map → wxr-taxonomies-tADW4KTa.d.mts.map} +1 -1
  574. package/dist/{zod-generator-CacrR-i8.mjs → zod-generator-CrfV79De.mjs} +3 -3
  575. package/dist/{zod-generator-CacrR-i8.mjs.map → zod-generator-CrfV79De.mjs.map} +1 -1
  576. package/package.json +11 -7
  577. package/src/api/errors.ts +11 -0
  578. package/src/api/handlers/content.ts +111 -1
  579. package/src/api/handlers/dashboard.ts +2 -0
  580. package/src/api/handlers/index.ts +6 -0
  581. package/src/api/handlers/media-usage.ts +80 -0
  582. package/src/api/handlers/relations.ts +474 -0
  583. package/src/api/handlers/taxonomies.ts +26 -4
  584. package/src/api/handlers/validate-media-fields.ts +1 -1
  585. package/src/api/openapi/document.ts +25 -0
  586. package/src/api/parse.ts +1 -1
  587. package/src/api/schemas/comments.ts +6 -0
  588. package/src/api/schemas/content.ts +8 -0
  589. package/src/api/schemas/index.ts +2 -0
  590. package/src/api/schemas/media-usage.ts +48 -0
  591. package/src/api/schemas/relations.ts +109 -0
  592. package/src/api/schemas/search.ts +1 -0
  593. package/src/astro/integration/index.ts +1 -0
  594. package/src/astro/integration/routes.ts +5 -0
  595. package/src/astro/integration/runtime.ts +24 -0
  596. package/src/astro/middleware/request-context.ts +162 -20
  597. package/src/astro/middleware.ts +22 -9
  598. package/src/astro/routes/api/admin/comments/[id]/status.ts +1 -2
  599. package/src/astro/routes/api/admin/comments/index.ts +1 -2
  600. package/src/astro/routes/api/admin/media-usage/repair.ts +24 -0
  601. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +1 -2
  602. package/src/astro/routes/api/auth/oauth/[provider].ts +1 -2
  603. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +13 -0
  604. package/src/astro/routes/api/content/[collection]/[id]/references/[relation]/children.ts +97 -0
  605. package/src/astro/routes/api/content/[collection]/[id]/references/[relation]/parents.ts +48 -0
  606. package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +17 -15
  607. package/src/astro/routes/api/relations/[id]/index.ts +84 -0
  608. package/src/astro/routes/api/relations/[id]/translations.ts +33 -0
  609. package/src/astro/routes/api/relations/index.ts +56 -0
  610. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +1 -7
  611. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +1 -2
  612. package/src/astro/routes/api/search/index.ts +2 -0
  613. package/src/astro/types.ts +2 -0
  614. package/src/cli/commands/export-seed.ts +1 -10
  615. package/src/client/index.ts +39 -0
  616. package/src/comments/service.ts +1 -1
  617. package/src/comments/turnstile.ts +64 -0
  618. package/src/components/BlockquoteGroup.astro +34 -0
  619. package/src/components/CommentForm.astro +12 -5
  620. package/src/components/EmDashHead.astro +27 -1
  621. package/src/components/InlinePortableTextEditor.tsx +34 -0
  622. package/src/components/PortableText.astro +7 -1
  623. package/src/components/index.ts +2 -0
  624. package/src/components/portable-text-blockquote-group.ts +68 -0
  625. package/src/content/converters/portable-text-to-prosemirror.ts +32 -1
  626. package/src/database/dialect-helpers.ts +34 -0
  627. package/src/database/migrations/039_fix_fts5_triggers.ts +1 -1
  628. package/src/database/migrations/runner.ts +46 -6
  629. package/src/database/pg-migration-lock.ts +72 -0
  630. package/src/database/repositories/comment.ts +1 -1
  631. package/src/database/repositories/content.ts +172 -21
  632. package/src/database/repositories/media-usage.ts +25 -3
  633. package/src/database/repositories/media.ts +1 -1
  634. package/src/database/repositories/relation.ts +130 -5
  635. package/src/database/repositories/taxonomy.ts +9 -0
  636. package/src/database/repositories/types.ts +8 -0
  637. package/src/db/postgres.ts +5 -2
  638. package/src/emdash-runtime.ts +72 -4
  639. package/src/index.ts +6 -2
  640. package/src/loader.ts +1 -31
  641. package/src/mcp/server.ts +29 -3
  642. package/src/media/usage/content-repair.ts +206 -22
  643. package/src/media/usage/content-snapshots.ts +4 -0
  644. package/src/plugins/context.ts +85 -0
  645. package/src/plugins/define-plugin.ts +1 -0
  646. package/src/plugins/index.ts +4 -0
  647. package/src/plugins/manifest-schema.ts +2 -0
  648. package/src/plugins/types.ts +59 -0
  649. package/src/schema/byline-registry.ts +1 -1
  650. package/src/search/fts-manager.ts +34 -0
  651. package/src/search/match.ts +50 -0
  652. package/src/search/query.ts +94 -9
  653. package/src/seo/hreflang.ts +192 -0
  654. package/src/seo/index.ts +3 -0
  655. package/src/taxonomies/index.ts +62 -45
  656. package/src/taxonomies/term-counts.ts +117 -0
  657. package/src/virtual-modules.d.ts +1 -0
  658. package/src/visual-editing/editable.ts +41 -35
  659. package/src/visual-editing/toolbar-bootstrap.ts +120 -0
  660. package/src/visual-editing/toolbar.ts +46 -0
  661. package/dist/api-BFURZn1i.mjs.map +0 -1
  662. package/dist/byline-fields-2Lp5Hbgf.mjs.map +0 -1
  663. package/dist/byline-fields-Cgrt9-aL.d.mts.map +0 -1
  664. package/dist/content-DhgEth4k.mjs.map +0 -1
  665. package/dist/content-refresh-CA4uKwzH.mjs.map +0 -1
  666. package/dist/context-CUsR9dXL.mjs.map +0 -1
  667. package/dist/dashboard-EvEKF5en.mjs.map +0 -1
  668. package/dist/dialect-helpers-DRI5pyY3.mjs.map +0 -1
  669. package/dist/error-CkAseSqZ.mjs.map +0 -1
  670. package/dist/index-B9sIxwe4.d.mts.map +0 -1
  671. package/dist/loader-CB6YHeul.mjs.map +0 -1
  672. package/dist/manifest-schema-BHlgZM1W.mjs.map +0 -1
  673. package/dist/media-BrXIaMFN.mjs.map +0 -1
  674. package/dist/menus-CsZ2ZEK2.mjs.map +0 -1
  675. package/dist/query-ByGq6ver.mjs.map +0 -1
  676. package/dist/redirects-DXEXBAII.mjs.map +0 -1
  677. package/dist/runner-DvFWoz8-.d.mts.map +0 -1
  678. package/dist/scheduled-publish-Drffrf2a.d.mts.map +0 -1
  679. package/dist/schema-Blhoxp3P.mjs.map +0 -1
  680. package/dist/search-DZN2HZ-V.mjs.map +0 -1
  681. package/dist/taxonomies-BGBd16Eu.mjs.map +0 -1
  682. package/dist/taxonomies-Cnb4F-Pb.mjs.map +0 -1
  683. package/dist/types-CGbYowGQ.d.mts.map +0 -1
  684. package/dist/version-CaccU7DM.mjs +0 -7
  685. /package/dist/{api-tokens-DsvvCtdh.mjs → api-tokens-Dk2GrJMY.mjs} +0 -0
  686. /package/dist/{ssrf-CmuGgCq3.mjs → ssrf-B5DOv5E5.mjs} +0 -0
  687. /package/dist/{types-DROWRxYV.mjs → types-DeCZBZxn.mjs} +0 -0
@@ -2,6 +2,9 @@ import { sql, type Kysely } from "kysely";
2
2
  import { ulid } from "ulidx";
3
3
 
4
4
  import { invalidateCollectionCache } from "../../object-cache/index.js";
5
+ import { buildFtsPrefixMatch, buildSlugGlobPrefix } from "../../search/match.js";
6
+ import { chunks, SQL_BATCH_SIZE } from "../../utils/chunks.js";
7
+ import { isMissingTableError } from "../../utils/db-errors.js";
5
8
  import { slugify } from "../../utils/slugify.js";
6
9
  import type { Database } from "../types.js";
7
10
  import { validateIdentifier } from "../validate.js";
@@ -402,16 +405,26 @@ export class ContentRepository {
402
405
  ? (t: string, s: string) => this.findBySlugIncludingTrashed(t, s, locale)
403
406
  : (t: string, s: string) => this.findBySlug(t, s, locale);
404
407
 
405
- if (looksLikeUlid) {
406
- // Try ID first, fall back to slug
407
- const byId = await findById(type, identifier);
408
- if (byId) return byId;
409
- return findBySlug(type, identifier);
408
+ try {
409
+ if (looksLikeUlid) {
410
+ // Try ID first, fall back to slug
411
+ const byId = await findById(type, identifier);
412
+ if (byId) return byId;
413
+ return await findBySlug(type, identifier);
414
+ }
415
+ // Try slug first, fall back to ID
416
+ const bySlug = await findBySlug(type, identifier);
417
+ if (bySlug) return bySlug;
418
+ return await findById(type, identifier);
419
+ } catch (error) {
420
+ // A collection dropped out from under a still-referencing caller (e.g. a
421
+ // relation whose collection was deleted without cascading) leaves the
422
+ // ec_* table missing. Treat that as "not found", matching
423
+ // findManyByIdOrSlug and findTranslationsForGroups, so callers surface a
424
+ // structured NOT_FOUND instead of a 500.
425
+ if (isMissingTableError(error)) return null;
426
+ throw error;
410
427
  }
411
- // Try slug first, fall back to ID
412
- const bySlug = await findBySlug(type, identifier);
413
- if (bySlug) return bySlug;
414
- return findById(type, identifier);
415
428
  }
416
429
 
417
430
  /**
@@ -513,7 +526,7 @@ export class ContentRepository {
513
526
  query = query.where("locale" as any, "=", options.where.locale);
514
527
  }
515
528
 
516
- query = this.applySearchFilter(query, options.where);
529
+ query = this.applySearchFilter(query, options.where, type);
517
530
  query = this.applyDateFilter(query, options.where);
518
531
 
519
532
  // Handle cursor pagination — decodeCursor throws InvalidCursorError
@@ -792,19 +805,46 @@ export class ContentRepository {
792
805
  }
793
806
 
794
807
  /**
795
- * Apply the optional case-insensitive `q` substring filter across the
796
- * handler-resolved `searchColumns` (OR'd). User input is treated literally
797
- * (LIKE wildcards escaped) and `lower()` is applied on both sides for
798
- * SQLite/Postgres case-insensitive parity.
808
+ * Apply the optional `q` filter.
809
+ *
810
+ * When the handler sets `useFts` (collection has a healthy FTS5 index
811
+ * covering the display columns; SQLite only), the filter is served from
812
+ * the index: a token-prefix MATCH against `_emdash_fts_<slug>` OR'd with
813
+ * an index-served `slug GLOB 'term*'` prefix (the slug is not in the FTS
814
+ * index). Both sides are index-backed, so SQLite's OR optimization avoids
815
+ * the full-table scan the LIKE fallback needs (#1517). The trade-off is
816
+ * search semantics: token-prefix matching instead of arbitrary substring.
817
+ *
818
+ * Fallback (Postgres, search disabled, or no usable terms): case-
819
+ * insensitive substring LIKE across the handler-resolved `searchColumns`
820
+ * (OR'd). User input is treated literally (LIKE wildcards escaped) and
821
+ * `lower()` is applied on both sides for SQLite/Postgres parity.
799
822
  */
800
823
  private applySearchFilter<QB extends { where: (cb: (eb: any) => unknown) => QB }>(
801
824
  query: QB,
802
- where?: { q?: string; searchColumns?: string[] },
825
+ where: { q?: string; searchColumns?: string[]; useFts?: boolean } | undefined,
826
+ type: string,
803
827
  ): QB {
804
828
  const term = where?.q?.trim();
805
829
  const columns = where?.searchColumns;
806
830
  if (!term || !columns || columns.length === 0) return query;
807
831
 
832
+ if (where.useFts) {
833
+ const match = buildFtsPrefixMatch(term);
834
+ if (match) {
835
+ validateIdentifier(type, "collection slug");
836
+ const ftsTable = `_emdash_fts_${type}`;
837
+ const slugPrefix = buildSlugGlobPrefix(term);
838
+ return query.where((eb) =>
839
+ eb.or([
840
+ sql<boolean>`id IN (SELECT id FROM ${sql.ref(ftsTable)} WHERE ${sql.ref(ftsTable)} MATCH ${match})`,
841
+ sql<boolean>`slug GLOB ${slugPrefix}`,
842
+ ]),
843
+ );
844
+ }
845
+ // No usable terms (e.g. quotes only) — fall through to LIKE.
846
+ }
847
+
808
848
  const escaped = term.replace(LIKE_WILDCARD_RE, (c) => `\\${c}`);
809
849
  const pattern = `%${escaped}%`;
810
850
 
@@ -866,7 +906,7 @@ export class ContentRepository {
866
906
  query = query.where("locale" as any, "=", where.locale);
867
907
  }
868
908
 
869
- query = this.applySearchFilter(query, where);
909
+ query = this.applySearchFilter(query, where, type);
870
910
  query = this.applyDateFilter(query, where);
871
911
 
872
912
  const result = await query.executeTakeFirst();
@@ -890,13 +930,13 @@ export class ContentRepository {
890
930
  .where("author_id" as never, "is not", null)
891
931
  .execute();
892
932
 
893
- return rows
894
- .map((row) => (row as { author_id: string | null }).author_id)
895
- .filter((id): id is string => id !== null);
933
+ return rows.map((row) => row.author_id).filter((id): id is string => id !== null);
896
934
  }
897
935
 
898
- // get overall statistics (total, published, draft) for a content type in a single query
899
- async getStats(type: string): Promise<{ total: number; published: number; draft: number }> {
936
+ // get overall statistics for a content type in a single query
937
+ async getStats(
938
+ type: string,
939
+ ): Promise<{ total: number; published: number; draft: number; scheduled: number }> {
900
940
  const tableName = getTableName(type);
901
941
 
902
942
  const result = await this.db
@@ -905,6 +945,7 @@ export class ContentRepository {
905
945
  eb.fn.count("id").as("total"),
906
946
  eb.fn.sum(eb.case().when("status", "=", "published").then(1).else(0).end()).as("published"),
907
947
  eb.fn.sum(eb.case().when("status", "=", "draft").then(1).else(0).end()).as("draft"),
948
+ sql<number>`SUM(CASE WHEN scheduled_at IS NOT NULL THEN 1 ELSE 0 END)`.as("scheduled"),
908
949
  ])
909
950
  .where("deleted_at" as never, "is", null)
910
951
  .executeTakeFirst();
@@ -913,6 +954,7 @@ export class ContentRepository {
913
954
  total: Number(result?.total || 0),
914
955
  published: Number(result?.published || 0),
915
956
  draft: Number(result?.draft || 0),
957
+ scheduled: Number(result?.scheduled || 0),
916
958
  };
917
959
  }
918
960
 
@@ -1054,6 +1096,115 @@ export class ContentRepository {
1054
1096
  return result.rows.map((row) => this.mapRow(type, row));
1055
1097
  }
1056
1098
 
1099
+ /**
1100
+ * Batch variant of {@link findTranslations}: every (non-deleted) locale
1101
+ * variant for any of `translationGroups`, in one `WHERE translation_group IN
1102
+ * (...)` query chunked at `SQL_BATCH_SIZE` for D1's bind-parameter limit.
1103
+ * Lets callers resolve many edge groups without an N+1 per group. The caller
1104
+ * groups the flat result by `translationGroup` itself.
1105
+ *
1106
+ * `publishedOnly` restricts the result to `status = 'published'` — reference
1107
+ * reads pass this for callers without `content:read_drafts` so draft/scheduled
1108
+ * entries never leak through an edge traversal.
1109
+ *
1110
+ * A reference edge stores only a collection slug (no SQL FK), so the table may
1111
+ * have been dropped since the edge was written. That is a tolerated dangling
1112
+ * state, not an error: a missing table resolves to no rows, mirroring how the
1113
+ * content read handlers treat `isMissingTableError`.
1114
+ */
1115
+ async findTranslationsForGroups(
1116
+ type: string,
1117
+ translationGroups: string[],
1118
+ options: { publishedOnly?: boolean } = {},
1119
+ ): Promise<ContentItem[]> {
1120
+ if (translationGroups.length === 0) return [];
1121
+ const tableName = getTableName(type);
1122
+ const publishedFilter = options.publishedOnly ? sql`AND status = 'published'` : sql``;
1123
+
1124
+ const items: ContentItem[] = [];
1125
+ try {
1126
+ for (const chunk of chunks(translationGroups, SQL_BATCH_SIZE)) {
1127
+ const result = await sql<Record<string, unknown>>`
1128
+ SELECT * FROM ${sql.ref(tableName)}
1129
+ WHERE translation_group IN (${sql.join(chunk)})
1130
+ AND deleted_at IS NULL
1131
+ ${publishedFilter}
1132
+ ORDER BY locale ASC
1133
+ `.execute(this.db);
1134
+ for (const row of result.rows) items.push(this.mapRow(type, row));
1135
+ }
1136
+ } catch (error) {
1137
+ if (isMissingTableError(error)) return [];
1138
+ throw error;
1139
+ }
1140
+ return items;
1141
+ }
1142
+
1143
+ /**
1144
+ * Batch variant of {@link findByIdOrSlug}: resolve many identifiers (each an
1145
+ * id OR a slug) within `type` in a constant number of queries — one `WHERE id
1146
+ * IN (...)` and one `WHERE slug IN (...)`, each chunked at `SQL_BATCH_SIZE`.
1147
+ * Returns a map from the input identifier to its resolved item; identifiers
1148
+ * that match nothing are absent. Used on write paths that accept a list of
1149
+ * references, so a single request doesn't fan out to an N+1 of point lookups.
1150
+ *
1151
+ * Resolution mirrors {@link findByIdOrSlug}: a ULID-shaped identifier prefers
1152
+ * the id match and falls back to slug; anything else prefers the slug match
1153
+ * and falls back to id. Slug matches collapse to the lowest-locale variant
1154
+ * (`ORDER BY locale ASC`), matching the slug-without-locale lookup.
1155
+ */
1156
+ async findManyByIdOrSlug(type: string, identifiers: string[]): Promise<Map<string, ContentItem>> {
1157
+ const resolved = new Map<string, ContentItem>();
1158
+ const unique = [...new Set(identifiers)];
1159
+ if (unique.length === 0) return resolved;
1160
+
1161
+ const tableName = getTableName(type);
1162
+ const byId = new Map<string, ContentItem>();
1163
+ const bySlug = new Map<string, ContentItem>();
1164
+
1165
+ try {
1166
+ for (const chunk of chunks(unique, SQL_BATCH_SIZE)) {
1167
+ const idRows = await sql<Record<string, unknown>>`
1168
+ SELECT * FROM ${sql.ref(tableName)}
1169
+ WHERE id IN (${sql.join(chunk)})
1170
+ AND deleted_at IS NULL
1171
+ `.execute(this.db);
1172
+ for (const row of idRows.rows) {
1173
+ const item = this.mapRow(type, row);
1174
+ byId.set(item.id, item);
1175
+ }
1176
+
1177
+ const slugRows = await sql<Record<string, unknown>>`
1178
+ SELECT * FROM ${sql.ref(tableName)}
1179
+ WHERE slug IN (${sql.join(chunk)})
1180
+ AND deleted_at IS NULL
1181
+ ORDER BY locale ASC
1182
+ `.execute(this.db);
1183
+ for (const row of slugRows.rows) {
1184
+ const item = this.mapRow(type, row);
1185
+ // First write wins → lowest locale, matching findBySlug without a locale.
1186
+ if (item.slug != null && !bySlug.has(item.slug)) bySlug.set(item.slug, item);
1187
+ }
1188
+ }
1189
+ } catch (error) {
1190
+ // A collection dropped after a relation was created leaves the relation
1191
+ // pointing at a missing table. Treat it like an empty collection (no
1192
+ // matches) so callers surface a structured NOT_FOUND, not a 500 —
1193
+ // mirroring findTranslationsForGroups.
1194
+ if (isMissingTableError(error)) return resolved;
1195
+ throw error;
1196
+ }
1197
+
1198
+ for (const identifier of unique) {
1199
+ const looksLikeUlid = ULID_PATTERN.test(identifier);
1200
+ const item = looksLikeUlid
1201
+ ? (byId.get(identifier) ?? bySlug.get(identifier))
1202
+ : (bySlug.get(identifier) ?? byId.get(identifier));
1203
+ if (item) resolved.set(identifier, item);
1204
+ }
1205
+ return resolved;
1206
+ }
1207
+
1057
1208
  /**
1058
1209
  * Publish the current draft
1059
1210
  *
@@ -496,7 +496,7 @@ export class MediaUsageRepository {
496
496
  .executeTakeFirst();
497
497
  deleted = Number(result.numDeletedRows ?? 0) > 0;
498
498
  if (!deleted) return;
499
- await trx.deleteFrom("_emdash_media_usage").where("source_key", "=", sourceKey).execute();
499
+ await this.deleteSourceGenerationOccurrences(trx, sourceKey, expectedCurrentGeneration);
500
500
  });
501
501
 
502
502
  return {
@@ -518,7 +518,11 @@ export class MediaUsageRepository {
518
518
  .executeTakeFirst();
519
519
  deleted = Number(result.numDeletedRows ?? 0) > 0;
520
520
  if (!deleted) return;
521
- await trx.deleteFrom("_emdash_media_usage").where("source_key", "=", sourceKey).execute();
521
+ await this.deleteSourceGenerationOccurrences(
522
+ trx,
523
+ sourceKey,
524
+ expectedSource.currentGeneration,
525
+ );
522
526
  });
523
527
 
524
528
  return {
@@ -547,7 +551,11 @@ export class MediaUsageRepository {
547
551
  .executeTakeFirst();
548
552
  deleted = Number(result.numDeletedRows ?? 0) > 0;
549
553
  if (!deleted) return;
550
- await trx.deleteFrom("_emdash_media_usage").where("source_key", "=", sourceKey).execute();
554
+ await this.deleteSourceGenerationOccurrences(
555
+ trx,
556
+ sourceKey,
557
+ expectedSource.currentGeneration,
558
+ );
551
559
  });
552
560
  const contentPresent = deleted ? false : await this.contentRowExists(tableName, contentId);
553
561
 
@@ -903,6 +911,20 @@ export class MediaUsageRepository {
903
911
  });
904
912
  }
905
913
 
914
+ private async deleteSourceGenerationOccurrences(
915
+ db: DatabaseExecutor,
916
+ sourceKey: string,
917
+ generation: string,
918
+ ): Promise<void> {
919
+ // Guarded source deletes remove only the generation that won the source CAS;
920
+ // unmatched generations become invisible orphans reclaimed by age-gated cleanup.
921
+ await db
922
+ .deleteFrom("_emdash_media_usage")
923
+ .where("source_key", "=", sourceKey)
924
+ .where("generation", "=", generation)
925
+ .execute();
926
+ }
927
+
906
928
  private async insertOccurrences(
907
929
  db: DatabaseExecutor,
908
930
  sourceKey: string,
@@ -118,7 +118,7 @@ export class MediaRepository {
118
118
 
119
119
  await this.db.insertInto("media").values(row).execute();
120
120
 
121
- return this.rowToItem(row as MediaRow);
121
+ return this.rowToItem(row);
122
122
  }
123
123
 
124
124
  /**
@@ -3,6 +3,7 @@ import { ulid } from "ulidx";
3
3
 
4
4
  import { chunks, SQL_BATCH_SIZE } from "../../utils/chunks.js";
5
5
  import type { Database, RelationTable, ContentReferenceTable } from "../types.js";
6
+ import { decodeCursor, encodeCursor, InvalidCursorError, type FindManyResult } from "./types.js";
6
7
 
7
8
  export interface Relation {
8
9
  id: string;
@@ -17,8 +18,12 @@ export interface Relation {
17
18
 
18
19
  export interface CreateRelationInput {
19
20
  name: string;
20
- parentCollection: string;
21
- childCollection: string;
21
+ /** Required for a base relation; ignored (inherited from the source) when
22
+ * `translationOf` is set. */
23
+ parentCollection?: string;
24
+ /** Required for a base relation; ignored (inherited from the source) when
25
+ * `translationOf` is set. */
26
+ childCollection?: string;
22
27
  parentLabel: string;
23
28
  childLabel: string;
24
29
  /** Omit to let the DB default (current value: 'en') apply. Higher layers
@@ -73,9 +78,9 @@ export class RelationRepository {
73
78
  const now = new Date().toISOString();
74
79
 
75
80
  let translationGroup = id;
76
- let name = input.name;
77
- let parentCollection = input.parentCollection;
78
- let childCollection = input.childCollection;
81
+ let name: string;
82
+ let parentCollection: string;
83
+ let childCollection: string;
79
84
 
80
85
  if (input.translationOf) {
81
86
  const source = await this.findById(input.translationOf);
@@ -87,6 +92,18 @@ export class RelationRepository {
87
92
  name = source.name;
88
93
  parentCollection = source.parentCollection;
89
94
  childCollection = source.childCollection;
95
+ } else {
96
+ // A base relation carries its own structural fields. The API layer's Zod
97
+ // schema enforces this; guard here too since the repo trusts its inputs
98
+ // and the columns are NOT NULL.
99
+ if (input.parentCollection === undefined || input.childCollection === undefined) {
100
+ throw new Error(
101
+ "parentCollection and childCollection are required unless translationOf is set",
102
+ );
103
+ }
104
+ name = input.name;
105
+ parentCollection = input.parentCollection;
106
+ childCollection = input.childCollection;
90
107
  }
91
108
 
92
109
  await this.db
@@ -319,6 +336,61 @@ export class RelationRepository {
319
336
  return rows.map((row) => this.rowToReference(row));
320
337
  }
321
338
 
339
+ /**
340
+ * Forward traversal, paginated: one page of a parent's children for a
341
+ * relation, ordered by `(sort_order, id)`. Use this on request paths — a
342
+ * parent's children are capped but still up to 1000, and an unbounded read
343
+ * scales poorly. Returns `{ items, nextCursor? }`; the cursor's order value is
344
+ * the row's `sort_order`. Default limit 50, max 100.
345
+ */
346
+ async getChildrenPage(
347
+ relation: string,
348
+ parentGroup: string,
349
+ options: { limit?: number; cursor?: string } = {},
350
+ ): Promise<FindManyResult<ContentReference>> {
351
+ const relationGroup = await this.resolveRelationGroup(relation);
352
+ if (!relationGroup) return { items: [] };
353
+
354
+ const limit = Math.min(options.limit || 50, 100);
355
+
356
+ let query = this.db
357
+ .selectFrom("_emdash_content_references")
358
+ .selectAll()
359
+ .where("relation_group", "=", relationGroup)
360
+ .where("parent_group", "=", parentGroup);
361
+
362
+ if (options.cursor) {
363
+ const decoded = decodeCursor(options.cursor);
364
+ const sortOrder = Number(decoded.orderValue);
365
+ // `decodeCursor` only guarantees `orderValue` is a string; a hand-crafted
366
+ // cursor with a non-numeric order value would coerce to NaN and blow up at
367
+ // the driver bind as a 500. A bad cursor is a client error — surface it as
368
+ // INVALID_CURSOR (400). Server-issued cursors are always numeric here.
369
+ if (!Number.isFinite(sortOrder)) throw new InvalidCursorError(options.cursor);
370
+ query = query.where((eb) =>
371
+ eb.or([
372
+ eb("sort_order", ">", sortOrder),
373
+ eb.and([eb("sort_order", "=", sortOrder), eb("id", ">", decoded.id)]),
374
+ ]),
375
+ );
376
+ }
377
+
378
+ const rows = await query
379
+ .orderBy("sort_order", "asc")
380
+ .orderBy("id", "asc")
381
+ .limit(limit + 1)
382
+ .execute();
383
+
384
+ const hasMore = rows.length > limit;
385
+ const items = rows.slice(0, limit).map((row) => this.rowToReference(row));
386
+ const result: FindManyResult<ContentReference> = { items };
387
+ const last = items.at(-1);
388
+ if (hasMore && last) {
389
+ result.nextCursor = encodeCursor(String(last.sortOrder), last.id);
390
+ }
391
+ return result;
392
+ }
393
+
322
394
  /** Backlink traversal: the parents that reference a child for a relation. */
323
395
  async getParents(relation: string, childGroup: string): Promise<ContentReference[]> {
324
396
  const relationGroup = await this.resolveRelationGroup(relation);
@@ -346,6 +418,15 @@ export class RelationRepository {
346
418
  * relying on the insert's onConflict to silently drop them. Not wrapped in a
347
419
  * transaction: a crash between the delete and insert leaves the parent with
348
420
  * no children — acceptable for a replace-all, since a retry restores state.
421
+ *
422
+ * Concurrency: two simultaneous replace-all calls for the same (relation,
423
+ * parent) can interleave their deletes and inserts and merge into the union of
424
+ * both sets (a lost update — neither "replace" wins). This is non-corrupting —
425
+ * keyset pagination stays totally ordered via the `(sort_order, id)` tiebreak
426
+ * even with duplicate sort_orders — and a single client editing one parent's
427
+ * children serially never hits it. A D1-portable fix isn't available (no
428
+ * multi-statement transactions), so concurrent replace-all on one parent is
429
+ * unsupported by design rather than guarded here.
349
430
  */
350
431
  async setChildren(relation: string, parentGroup: string, childGroups: string[]): Promise<void> {
351
432
  const relationGroup = await this.resolveRelationGroup(relation);
@@ -381,6 +462,50 @@ export class RelationRepository {
381
462
  .execute();
382
463
  }
383
464
 
465
+ /**
466
+ * Backlink traversal, paginated: one page of the parents that reference a
467
+ * child for a relation, ordered by `id`. Unlike a parent's children, a
468
+ * child's backlinks are *unbounded* — one popular entry can be referenced by
469
+ * arbitrarily many parents — so this read must paginate. Returns
470
+ * `{ items, nextCursor? }`; the cursor's order value is the row `id`. Default
471
+ * limit 50, max 100.
472
+ */
473
+ async getParentsPage(
474
+ relation: string,
475
+ childGroup: string,
476
+ options: { limit?: number; cursor?: string } = {},
477
+ ): Promise<FindManyResult<ContentReference>> {
478
+ const relationGroup = await this.resolveRelationGroup(relation);
479
+ if (!relationGroup) return { items: [] };
480
+
481
+ const limit = Math.min(options.limit || 50, 100);
482
+
483
+ let query = this.db
484
+ .selectFrom("_emdash_content_references")
485
+ .selectAll()
486
+ .where("relation_group", "=", relationGroup)
487
+ .where("child_group", "=", childGroup);
488
+
489
+ if (options.cursor) {
490
+ const decoded = decodeCursor(options.cursor);
491
+ query = query.where("id", ">", decoded.id);
492
+ }
493
+
494
+ const rows = await query
495
+ .orderBy("id", "asc")
496
+ .limit(limit + 1)
497
+ .execute();
498
+
499
+ const hasMore = rows.length > limit;
500
+ const items = rows.slice(0, limit).map((row) => this.rowToReference(row));
501
+ const result: FindManyResult<ContentReference> = { items };
502
+ const last = items.at(-1);
503
+ if (hasMore && last) {
504
+ result.nextCursor = encodeCursor(last.id, last.id);
505
+ }
506
+ return result;
507
+ }
508
+
384
509
  /**
385
510
  * Remove every edge where `group` is the parent OR the child — i.e. ensure no
386
511
  * orphaned reference edges survive when a content entry is deleted. The
@@ -404,6 +404,12 @@ export class TaxonomyRepository {
404
404
  /**
405
405
  * Count content entries that use any translation of this term. Accepts
406
406
  * either a term id or a translation_group — we normalise to the group.
407
+ *
408
+ * Counts raw pivot rows regardless of the entry's status or deletion —
409
+ * drafts and trashed entries are included. User-facing counts (admin term
410
+ * list/get, public widget and term pages) use `fetchVisibleTermCounts`
411
+ * from `taxonomies/term-counts.ts` instead, which counts only publicly
412
+ * visible entries.
407
413
  */
408
414
  async countEntriesWithTerm(termIdOrGroup: string): Promise<number> {
409
415
  const group = await this.resolveTranslationGroup(termIdOrGroup);
@@ -453,6 +459,9 @@ export class TaxonomyRepository {
453
459
  *
454
460
  * Pass translation_groups (not term ids) — `content_taxonomies.taxonomy_id`
455
461
  * stores the translation_group so a single assignment spans every locale.
462
+ *
463
+ * Like `countEntriesWithTerm`, this counts raw pivot rows regardless of
464
+ * status/deletion; user-facing counts go through `fetchVisibleTermCounts`.
456
465
  */
457
466
  async countEntriesForTerms(translationGroups: string[]): Promise<Map<string, number>> {
458
467
  if (translationGroups.length === 0) return new Map();
@@ -158,6 +158,14 @@ export interface FindManyOptions {
158
158
  * repository stays generic. Each name is validated as a SQL identifier.
159
159
  */
160
160
  searchColumns?: string[];
161
+ /**
162
+ * Serve `q` from the collection's FTS5 index (`_emdash_fts_<slug>`)
163
+ * instead of an unindexable substring LIKE. Set by the handler only
164
+ * when the collection has search enabled and the index exists
165
+ * (SQLite only). The repository combines a token-prefix MATCH with
166
+ * an index-served slug prefix so slug lookups keep working.
167
+ */
168
+ useFts?: boolean;
161
169
  /** Inclusive date range over a whitelisted timestamp column. */
162
170
  dateFilter?: ContentDateFilter;
163
171
  };
@@ -5,9 +5,10 @@
5
5
  * Loaded at runtime via virtual module.
6
6
  */
7
7
 
8
- import { PostgresDialect } from "kysely";
8
+ import type { PostgresDialect } from "kysely";
9
9
  import { Pool } from "pg";
10
10
 
11
+ import { FailFastPostgresDialect } from "../database/pg-migration-lock.js";
11
12
  import type { PostgresConfig } from "./adapters.js";
12
13
 
13
14
  /**
@@ -26,5 +27,7 @@ export function createDialect(config: PostgresConfig): PostgresDialect {
26
27
  max: config.pool?.max ?? 10,
27
28
  });
28
29
 
29
- return new PostgresDialect({ pool });
30
+ // Fail-fast migration locking instead of Kysely's blocking advisory
31
+ // lock — see pg-migration-lock.ts (#1744).
32
+ return new FailFastPostgresDialect({ pool });
30
33
  }