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
@@ -1 +0,0 @@
1
- {"version":3,"file":"byline-fields-2Lp5Hbgf.mjs","names":["authenticatorTransport","registrationCredential"],"sources":["../src/api/schemas/auth.ts","../src/api/schemas/import.ts","../src/api/schemas/setup.ts","../src/api/schemas/byline-fields.ts"],"sourcesContent":["import { z } from \"zod\";\n\nimport { roleLevel } from \"./common.js\";\n\n// ---------------------------------------------------------------------------\n// WebAuthn credential schemas (matching @emdash-cms/auth/passkey types)\n// ---------------------------------------------------------------------------\n\nconst authenticatorTransport = z.enum([\"usb\", \"nfc\", \"ble\", \"internal\", \"hybrid\"]);\n\n/** RegistrationResponse — sent by the browser after navigator.credentials.create() */\nconst registrationCredential = z.object({\n\tid: z.string(),\n\trawId: z.string(),\n\ttype: z.literal(\"public-key\"),\n\tresponse: z.object({\n\t\tclientDataJSON: z.string(),\n\t\tattestationObject: z.string(),\n\t\ttransports: z.array(authenticatorTransport).optional(),\n\t}),\n\tauthenticatorAttachment: z.enum([\"platform\", \"cross-platform\"]).optional(),\n});\n\n/** AuthenticationResponse — sent by the browser after navigator.credentials.get() */\nconst authenticationCredential = z.object({\n\tid: z.string(),\n\trawId: z.string(),\n\ttype: z.literal(\"public-key\"),\n\tresponse: z.object({\n\t\tclientDataJSON: z.string(),\n\t\tauthenticatorData: z.string(),\n\t\tsignature: z.string(),\n\t\tuserHandle: z.string().optional(),\n\t}),\n\tauthenticatorAttachment: z.enum([\"platform\", \"cross-platform\"]).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Auth: Input schemas\n// ---------------------------------------------------------------------------\n\nexport const signupRequestBody = z\n\t.object({\n\t\temail: z.string().email(),\n\t})\n\t.meta({ id: \"SignupRequestBody\" });\n\nexport const signupCompleteBody = z\n\t.object({\n\t\ttoken: z.string().min(1),\n\t\tcredential: registrationCredential,\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"SignupCompleteBody\" });\n\nexport const inviteCreateBody = z\n\t.object({\n\t\temail: z.string().email(),\n\t\trole: roleLevel.optional(),\n\t})\n\t.meta({ id: \"InviteCreateBody\" });\n\nexport const inviteRegisterOptionsBody = z\n\t.object({\n\t\ttoken: z.string().min(1),\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"InviteRegisterOptionsBody\" });\n\nexport const inviteCompleteBody = z\n\t.object({\n\t\ttoken: z.string().min(1),\n\t\tcredential: registrationCredential,\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"InviteCompleteBody\" });\n\nexport const magicLinkSendBody = z\n\t.object({\n\t\temail: z.string().email(),\n\t})\n\t.meta({ id: \"MagicLinkSendBody\" });\n\nexport const passkeyOptionsBody = z\n\t.object({\n\t\temail: z.string().email().optional(),\n\t})\n\t.meta({ id: \"PasskeyOptionsBody\" });\n\nexport const passkeyVerifyBody = z\n\t.object({\n\t\tcredential: authenticationCredential,\n\t})\n\t.meta({ id: \"PasskeyVerifyBody\" });\n\nexport const passkeyRegisterOptionsBody = z\n\t.object({\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"PasskeyRegisterOptionsBody\" });\n\nexport const passkeyRegisterVerifyBody = z\n\t.object({\n\t\tcredential: registrationCredential,\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"PasskeyRegisterVerifyBody\" });\n\nexport const passkeyRenameBody = z\n\t.object({\n\t\tname: z.string().min(1),\n\t})\n\t.meta({ id: \"PasskeyRenameBody\" });\n\nexport const authMeActionBody = z\n\t.object({\n\t\taction: z.string().min(1),\n\t})\n\t.meta({ id: \"AuthMeActionBody\" });\n","import { z } from \"zod\";\n\nimport { httpUrl } from \"./common.js\";\n\n// ---------------------------------------------------------------------------\n// Import\n// ---------------------------------------------------------------------------\n\nexport const importProbeBody = z.object({\n\turl: httpUrl,\n});\n\nexport const wpPluginAnalyzeBody = z.object({\n\turl: httpUrl,\n\ttoken: z.string().min(1),\n});\n\nexport const wpPluginExecuteBody = z.object({\n\turl: httpUrl,\n\ttoken: z.string().min(1),\n\tconfig: z.record(z.string(), z.unknown()),\n\t// --- Chunked mode (issue #475). Absent phase = single-shot legacy run. ---\n\t// The admin drives the import in a loop of bounded requests; cross-chunk\n\t// state travels with the client so the server stays stateless.\n\tphase: z.enum([\"content\", \"comments\", \"finalize\"]).optional(),\n\t/** Position within the phase: post-type index + WP page (content), page (comments) */\n\tcursor: z\n\t\t.object({\n\t\t\tpostTypeIndex: z.number().int().min(0).default(0),\n\t\t\tpage: z.number().int().min(1).default(1),\n\t\t})\n\t\t.optional(),\n\t/** WP post ID -> created EmDash item, accumulated across content chunks */\n\tidMap: z\n\t\t.record(z.string(), z.object({ id: z.string().min(1), collection: z.string().min(1) }))\n\t\t.optional(),\n\t/** Source translation group -> EmDash item ID, accumulated across content chunks */\n\ttranslationGroups: z.record(z.string(), z.string().min(1)).optional(),\n\t/** WP comment ID -> EmDash root comment ID, accumulated across comment chunks */\n\tcommentRoots: z.record(z.string(), z.string().min(1)).optional(),\n});\n\nexport const wpPrepareBody = z.object({\n\tpostTypes: z.array(\n\t\tz.object({\n\t\t\tname: z.string().min(1),\n\t\t\tcollection: z.string().min(1),\n\t\t\tfields: z\n\t\t\t\t.array(\n\t\t\t\t\tz.object({\n\t\t\t\t\t\tslug: z.string().min(1),\n\t\t\t\t\t\tlabel: z.string().min(1),\n\t\t\t\t\t\ttype: z.string().min(1),\n\t\t\t\t\t\trequired: z.boolean(),\n\t\t\t\t\t\tsearchable: z.boolean().optional(),\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\t.optional(),\n\t\t}),\n\t),\n});\n\nexport const wpMediaImportBody = z.object({\n\tattachments: z.array(z.record(z.string(), z.unknown())),\n\tstream: z.boolean().optional(),\n});\n\nexport const wpRewriteUrlsBody = z.object({\n\turlMap: z.record(z.string(), z.string()),\n\tcollections: z.array(z.string()).optional(),\n});\n","import { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Setup\n// ---------------------------------------------------------------------------\n\n/** Registration credential — duplicated reference for setup flow.\n * The canonical definition lives in auth.ts but setup needs it independently\n * because setup runs before auth is configured. */\nconst authenticatorTransport = z.enum([\"usb\", \"nfc\", \"ble\", \"internal\", \"hybrid\"]);\n\nconst registrationCredential = z.object({\n\tid: z.string(),\n\trawId: z.string(),\n\ttype: z.literal(\"public-key\"),\n\tresponse: z.object({\n\t\tclientDataJSON: z.string(),\n\t\tattestationObject: z.string(),\n\t\ttransports: z.array(authenticatorTransport).optional(),\n\t}),\n\tauthenticatorAttachment: z.enum([\"platform\", \"cross-platform\"]).optional(),\n});\n\nexport const setupBody = z.object({\n\ttitle: z.string().min(1),\n\ttagline: z.string().optional(),\n\tincludeContent: z.boolean(),\n});\n\nexport const setupAdminBody = z.object({\n\temail: z.string().email(),\n\tname: z.string().optional(),\n});\n\nexport const setupAdminVerifyBody = z.object({\n\tcredential: registrationCredential,\n});\n\nexport const atprotoLoginBody = z.object({\n\thandle: z.string().trim().min(1),\n});\n\nexport const setupAtprotoAdminBody = z.object({\n\thandle: z.string().trim().min(1),\n});\n","/**\n * Zod schemas for the byline-fields admin API (Discussion #1174, Phase 4).\n *\n * Reserved-slug + identifier validation runs at the zod layer so the\n * route returns a clean 400 (`VALIDATION_ERROR` from `parseBody`) rather\n * than bubbling a registry-level `BylineSchemaError` (\"RESERVED_SLUG\" /\n * \"INVALID_SLUG\"). The registry repeats the same checks for non-HTTP\n * callers (seeds, scripts) — see `BylineSchemaRegistry.validateSlug`.\n *\n * Field types are constrained to the v1 subset declared in\n * `BYLINE_FIELD_TYPES`. Adding a type to the union there will require a\n * corresponding update to this enum.\n */\n\nimport { z } from \"zod\";\n\nimport { BYLINE_FIELD_TYPES, RESERVED_BYLINE_FIELD_SLUGS } from \"../../schema/types.js\";\n\n/**\n * Slug pattern for byline field definitions — matches the identifier rule\n * used by `validateIdentifier` (and `slugPattern` in `common.ts`).\n * Lowercase letters, digits, and underscores; must start with a letter.\n */\nconst bylineFieldSlugPattern = /^[a-z][a-z0-9_]*$/;\n\n/** Hard cap on a slug — mirrors `BylineSchemaRegistry.MAX_SLUG_LENGTH`. */\nconst MAX_SLUG_LENGTH = 63;\n/** Hard cap on a label — mirrors `BylineSchemaRegistry.MAX_LABEL_LENGTH`. */\nconst MAX_LABEL_LENGTH = 200;\n/** Hard cap on a select field's `options` list. */\nconst MAX_SELECT_OPTIONS = 200;\n\nconst RESERVED_SET: ReadonlySet<string> = new Set(RESERVED_BYLINE_FIELD_SLUGS);\n\n// Enumerate the v1 byline field types explicitly so zod gets the exact\n// literal union for `z.infer<>`. Mirrors `BYLINE_FIELD_TYPES`; CI's\n// type-checker catches drift via the satisfies/import below.\nconst bylineFieldTypeValues = z.enum([\"string\", \"text\", \"url\", \"boolean\", \"select\"]);\n// Compile-time guard: a drift here trips the satisfies check.\ntype _BylineFieldTypeDriftCheck =\n\t(typeof BYLINE_FIELD_TYPES)[number] extends z.infer<typeof bylineFieldTypeValues>\n\t\t? z.infer<typeof bylineFieldTypeValues> extends (typeof BYLINE_FIELD_TYPES)[number]\n\t\t\t? true\n\t\t\t: never\n\t\t: never;\nconst _bylineFieldTypeDriftCheck: _BylineFieldTypeDriftCheck = true;\nvoid _bylineFieldTypeDriftCheck;\n\n/**\n * Validation payload for a byline custom field. v1 only exposes\n * `options` (used by `select`-type fields). Empty/duplicate options are\n * rejected at the registry layer; the zod layer only enforces shape and\n * caps. Future field types may add keys here.\n */\nconst bylineFieldValidationSchema = z\n\t.object({\n\t\toptions: z\n\t\t\t.array(z.string().min(1))\n\t\t\t.min(1, \"select options must contain at least one entry\")\n\t\t\t.max(MAX_SELECT_OPTIONS, `select options cannot exceed ${MAX_SELECT_OPTIONS} entries`)\n\t\t\t.optional(),\n\t})\n\t.strict()\n\t.nullable();\n\n/**\n * Slug validation chain shared by create + reorder bodies. Centralised so\n * the reserved-slug message and pattern are identical everywhere.\n */\nconst bylineFieldSlug = z\n\t.string()\n\t.min(1, \"Byline field slug is required\")\n\t.max(MAX_SLUG_LENGTH, `Byline field slug must be ${MAX_SLUG_LENGTH} characters or less`)\n\t.regex(\n\t\tbylineFieldSlugPattern,\n\t\t\"Byline field slug must contain only lowercase letters, digits, and underscores, and start with a letter\",\n\t)\n\t.refine((slug) => !RESERVED_SET.has(slug), {\n\t\t// Surface the offending slug in the validation issue path-message\n\t\t// for easier debugging from the admin UI's error toast.\n\t\tmessage: \"Byline field slug is reserved\",\n\t});\n\nconst bylineFieldLabel = z\n\t.string()\n\t.min(1, \"Byline field label is required\")\n\t.max(MAX_LABEL_LENGTH, `Byline field label must be ${MAX_LABEL_LENGTH} characters or less`);\n\n// ---------------------------------------------------------------------------\n// Request bodies\n// ---------------------------------------------------------------------------\n\nexport const bylineFieldCreateBody = z\n\t.object({\n\t\tslug: bylineFieldSlug,\n\t\tlabel: bylineFieldLabel,\n\t\ttype: bylineFieldTypeValues,\n\t\trequired: z.boolean().optional(),\n\t\t/**\n\t\t * Whether values are stored per-locale (translatable, default) or\n\t\t * shared across the translation group. See `BylineFieldDefinition`.\n\t\t */\n\t\ttranslatable: z.boolean().optional(),\n\t\tvalidation: bylineFieldValidationSchema.optional(),\n\t\tsortOrder: z.number().int().min(0).optional(),\n\t})\n\t.strict()\n\t.meta({ id: \"BylineFieldCreateBody\" });\n\n/**\n * Update body. `slug` and `type` are intentionally absent — both are\n * immutable post-create (changing them would invalidate stored values).\n * `translatable` flips are gated at the registry layer when value rows\n * exist (`TRANSLATABLE_LOCKED`).\n */\nexport const bylineFieldUpdateBody = z\n\t.object({\n\t\tlabel: bylineFieldLabel.optional(),\n\t\trequired: z.boolean().optional(),\n\t\ttranslatable: z.boolean().optional(),\n\t\tvalidation: bylineFieldValidationSchema.optional(),\n\t\tsortOrder: z.number().int().min(0).optional(),\n\t})\n\t.strict()\n\t.meta({ id: \"BylineFieldUpdateBody\" });\n\nexport const bylineFieldReorderBody = z\n\t.object({\n\t\t/**\n\t\t * Exact set of currently registered slugs in the desired order.\n\t\t * The registry rejects any drift (`REORDER_MISMATCH`); the zod\n\t\t * layer enforces slug shape only. An empty array is permitted —\n\t\t * `reorderFields([])` is a valid no-op when zero fields are\n\t\t * registered (registry contract). Rejecting empty here would\n\t\t * produce a spurious 400 for an admin UI that submits a reorder\n\t\t * after deleting the last field.\n\t\t */\n\t\tslugs: z.array(bylineFieldSlug),\n\t})\n\t.strict()\n\t.meta({ id: \"BylineFieldReorderBody\" });\n\n// ---------------------------------------------------------------------------\n// Response shapes\n// ---------------------------------------------------------------------------\n\nexport const bylineFieldDefinitionSchema = z\n\t.object({\n\t\tid: z.string(),\n\t\tslug: z.string(),\n\t\tlabel: z.string(),\n\t\ttype: bylineFieldTypeValues,\n\t\trequired: z.boolean(),\n\t\ttranslatable: z.boolean(),\n\t\tvalidation: z\n\t\t\t.object({\n\t\t\t\toptions: z.array(z.string()).optional(),\n\t\t\t})\n\t\t\t.nullable(),\n\t\tsortOrder: z.number().int(),\n\t\tcreatedAt: z.string(),\n\t\tupdatedAt: z.string(),\n\t})\n\t.meta({ id: \"BylineFieldDefinition\" });\n\nexport const bylineFieldListResponseSchema = z\n\t.object({\n\t\titems: z.array(bylineFieldDefinitionSchema),\n\t})\n\t.meta({ id: \"BylineFieldListResponse\" });\n\n/**\n * Response shape for `GET /api/admin/byline-fields/[slug]/usage`.\n *\n * `translatableValueCount` counts rows in `_emdash_byline_field_values`.\n * `groupValueCount` counts rows in `_emdash_byline_field_group_values`.\n * `totalAffectedRows` is the sum — what the destructive-delete confirm\n * dialog surfaces. Both individual counts are exposed for diagnostic\n * value (e.g. inconsistency with the field's current `translatable`\n * flag would show non-zero on the \"wrong\" side).\n */\nexport const bylineFieldUsageResponseSchema = z\n\t.object({\n\t\ttranslatableValueCount: z.number().int().nonnegative(),\n\t\tgroupValueCount: z.number().int().nonnegative(),\n\t\ttotalAffectedRows: z.number().int().nonnegative(),\n\t})\n\t.meta({ id: \"BylineFieldUsageResponse\" });\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"byline-fields-Cgrt9-aL.d.mts","names":[],"sources":["../src/api/schemas/common.ts","../src/api/schemas/content.ts","../src/api/schemas/media.ts","../src/api/schemas/schema.ts","../src/api/schemas/comments.ts","../src/api/schemas/auth.ts","../src/api/schemas/menus.ts","../src/api/schemas/taxonomies.ts","../src/api/schemas/sections.ts","../src/api/schemas/settings.ts","../src/api/schemas/search.ts","../src/api/schemas/import.ts","../src/api/schemas/setup.ts","../src/api/schemas/users.ts","../src/api/schemas/widgets.ts","../src/api/schemas/redirects.ts","../src/api/schemas/bylines.ts","../src/api/schemas/byline-fields.ts"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"content-DhgEth4k.mjs","names":[],"sources":["../src/database/repositories/revision.ts","../src/database/repositories/content.ts"],"sourcesContent":["import type { Kysely } from \"kysely\";\nimport { monotonicFactory } from \"ulidx\";\n\nimport type { Database, RevisionTable } from \"../types.js\";\n\nconst monotonic = monotonicFactory();\n\nexport interface Revision {\n\tid: string;\n\tcollection: string;\n\tentryId: string;\n\tdata: Record<string, unknown>;\n\tauthorId: string | null;\n\tcreatedAt: string;\n}\n\nexport interface CreateRevisionInput {\n\tcollection: string;\n\tentryId: string;\n\tdata: Record<string, unknown>;\n\tauthorId?: string;\n}\n\n/**\n * Revision repository for version history\n *\n * Each revision stores a JSON snapshot of the content at a point in time.\n * Used when collection has `supports: [\"revisions\"]` enabled.\n */\nexport class RevisionRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t/**\n\t * Create a new revision\n\t */\n\tasync create(input: CreateRevisionInput): Promise<Revision> {\n\t\tconst id = monotonic();\n\n\t\tconst row: Omit<RevisionTable, \"created_at\"> = {\n\t\t\tid,\n\t\t\tcollection: input.collection,\n\t\t\tentry_id: input.entryId,\n\t\t\tdata: JSON.stringify(input.data),\n\t\t\tauthor_id: input.authorId ?? null,\n\t\t};\n\n\t\tawait this.db.insertInto(\"revisions\").values(row).execute();\n\n\t\tconst revision = await this.findById(id);\n\t\tif (!revision) {\n\t\t\tthrow new Error(\"Failed to create revision\");\n\t\t}\n\t\treturn revision;\n\t}\n\n\t/**\n\t * Find revision by ID\n\t */\n\tasync findById(id: string): Promise<Revision | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.rowToRevision(row) : null;\n\t}\n\n\t/**\n\t * Get all revisions for an entry (newest first)\n\t *\n\t * Orders by monotonic ULID (descending). The monotonic factory\n\t * guarantees strictly increasing IDs even within the same millisecond.\n\t */\n\tasync findByEntry(\n\t\tcollection: string,\n\t\tentryId: string,\n\t\toptions: { limit?: number } = {},\n\t): Promise<Revision[]> {\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.selectAll()\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.orderBy(\"id\", \"desc\");\n\n\t\tif (options.limit) {\n\t\t\tquery = query.limit(options.limit);\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\treturn rows.map((row) => this.rowToRevision(row));\n\t}\n\n\t/**\n\t * Get the most recent revision for an entry\n\t */\n\tasync findLatest(collection: string, entryId: string): Promise<Revision | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.selectAll()\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.orderBy(\"id\", \"desc\")\n\t\t\t.limit(1)\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.rowToRevision(row) : null;\n\t}\n\n\t/**\n\t * Count revisions for an entry\n\t */\n\tasync countByEntry(collection: string, entryId: string): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.executeTakeFirst();\n\n\t\treturn Number(result?.count || 0);\n\t}\n\n\t/**\n\t * Delete all revisions for an entry (use when entry is deleted)\n\t */\n\tasync deleteByEntry(collection: string, entryId: string): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"revisions\")\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.executeTakeFirst();\n\n\t\treturn Number(result.numDeletedRows ?? 0);\n\t}\n\n\t/**\n\t * Delete old revisions, keeping the most recent N\n\t */\n\tasync pruneOldRevisions(collection: string, entryId: string, keepCount: number): Promise<number> {\n\t\t// Get IDs of revisions to keep\n\t\tconst keep = await this.db\n\t\t\t.selectFrom(\"revisions\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.orderBy(\"created_at\", \"desc\")\n\t\t\t.orderBy(\"id\", \"desc\") // ULID tiebreaker\n\t\t\t.limit(keepCount)\n\t\t\t.execute();\n\n\t\tconst keepIds = keep.map((r) => r.id);\n\n\t\tif (keepIds.length === 0) return 0;\n\n\t\t// Delete everything else for this entry\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"revisions\")\n\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t.where(\"entry_id\", \"=\", entryId)\n\t\t\t.where(\"id\", \"not in\", keepIds)\n\t\t\t.executeTakeFirst();\n\n\t\treturn Number(result.numDeletedRows ?? 0);\n\t}\n\n\t/**\n\t * Update revision data in place\n\t * Used for autosave to avoid creating many small revisions.\n\t */\n\tasync updateData(id: string, data: Record<string, unknown>): Promise<void> {\n\t\tawait this.db\n\t\t\t.updateTable(\"revisions\")\n\t\t\t.set({ data: JSON.stringify(data) })\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.execute();\n\t}\n\n\t/**\n\t * Convert database row to Revision object\n\t */\n\tprivate rowToRevision(row: {\n\t\tid: string;\n\t\tcollection: string;\n\t\tentry_id: string;\n\t\tdata: string;\n\t\tauthor_id: string | null;\n\t\tcreated_at: string;\n\t}): Revision {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tcollection: row.collection,\n\t\t\tentryId: row.entry_id,\n\t\t\tdata: JSON.parse(row.data),\n\t\t\tauthorId: row.author_id,\n\t\t\tcreatedAt: row.created_at,\n\t\t};\n\t}\n}\n","import { sql, type Kysely } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { invalidateCollectionCache } from \"../../object-cache/index.js\";\nimport { slugify } from \"../../utils/slugify.js\";\nimport type { Database } from \"../types.js\";\nimport { validateIdentifier } from \"../validate.js\";\nimport { RevisionRepository } from \"./revision.js\";\nimport type {\n\tCreateContentInput,\n\tUpdateContentInput,\n\tFindManyOptions,\n\tFindManyResult,\n\tContentItem,\n\tContentDateField,\n} from \"./types.js\";\nimport {\n\tEmDashValidationError,\n\tScheduledNotDueError,\n\tencodeCursor,\n\tdecodeCursor,\n} from \"./types.js\";\n\n// Regex pattern for ULID validation\nconst ULID_PATTERN = /^[0-9A-Z]{26}$/;\n\n// LIKE wildcards that must be escaped so user search input is matched literally.\nconst LIKE_WILDCARD_RE = /[\\\\%_]/g;\n\n/**\n * Whitelist mapping a public date-filter field to its physical column. Keeping\n * this separate from `mapOrderField` makes the filterable set explicit and\n * prevents filtering on arbitrary columns.\n */\nconst DATE_FILTER_COLUMNS: Record<ContentDateField, \"created_at\" | \"updated_at\" | \"published_at\"> =\n\t{\n\t\tcreatedAt: \"created_at\",\n\t\tupdatedAt: \"updated_at\",\n\t\tpublishedAt: \"published_at\",\n\t};\n\n/**\n * System columns that exist in every ec_* table\n */\nconst SYSTEM_COLUMNS = new Set([\n\t\"id\",\n\t\"slug\",\n\t\"status\",\n\t\"author_id\",\n\t\"primary_byline_id\",\n\t\"created_at\",\n\t\"updated_at\",\n\t\"published_at\",\n\t\"scheduled_at\",\n\t\"deleted_at\",\n\t\"version\",\n\t\"live_revision_id\",\n\t\"draft_revision_id\",\n\t\"locale\",\n\t\"translation_group\",\n]);\n\n/**\n * Get the table name for a collection type\n */\nfunction getTableName(type: string): string {\n\tvalidateIdentifier(type, \"collection type\");\n\treturn `ec_${type}`;\n}\n\n/**\n * Serialize a value for database storage\n * Objects/arrays are JSON-stringified\n * Booleans are converted to 0/1 for SQLite\n */\nfunction serializeValue(value: unknown): unknown {\n\tif (value === null || value === undefined) {\n\t\treturn null;\n\t}\n\tif (typeof value === \"boolean\") {\n\t\treturn value ? 1 : 0;\n\t}\n\tif (typeof value === \"object\") {\n\t\treturn JSON.stringify(value);\n\t}\n\treturn value;\n}\n\n/**\n * Deserialize a value from database storage\n * Attempts to parse JSON strings that look like objects/arrays\n */\nfunction deserializeValue(value: unknown): unknown {\n\tif (typeof value === \"string\") {\n\t\t// Try to parse if it looks like JSON\n\t\tif (value.startsWith(\"{\") || value.startsWith(\"[\")) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(value);\n\t\t\t} catch {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t}\n\treturn value;\n}\n\n/** Pattern for escaping special regex characters */\nconst REGEX_ESCAPE_PATTERN = /[.*+?^${}()|[\\]\\\\]/g;\n\n/**\n * Escape special regex characters in a string for use in `new RegExp()`\n */\nfunction escapeRegExp(s: string): string {\n\treturn s.replace(REGEX_ESCAPE_PATTERN, \"\\\\$&\");\n}\n\n/**\n * Repository for content CRUD operations\n *\n * Content is stored in per-collection tables (ec_posts, ec_pages, etc.)\n * Each field becomes a real column in the table.\n */\nexport class ContentRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t/**\n\t * Create a new content item\n\t */\n\tasync create(input: CreateContentInput): Promise<ContentItem> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\tconst {\n\t\t\ttype,\n\t\t\tslug,\n\t\t\tdata,\n\t\t\tstatus = \"draft\",\n\t\t\tauthorId,\n\t\t\tprimaryBylineId,\n\t\t\tlocale,\n\t\t\ttranslationOf,\n\t\t\tpublishedAt,\n\t\t\tcreatedAt,\n\t\t} = input;\n\n\t\t// Validate required fields\n\t\tif (!type) {\n\t\t\tthrow new EmDashValidationError(\"Content type is required\");\n\t\t}\n\n\t\tconst tableName = getTableName(type);\n\n\t\t// Resolve translation_group: if translationOf is set, look up the source item's group\n\t\tlet translationGroup: string = id; // default: self-reference\n\t\tif (translationOf) {\n\t\t\tconst source = await this.findById(type, translationOf);\n\t\t\tif (!source) {\n\t\t\t\tthrow new EmDashValidationError(\"Translation source content not found\");\n\t\t\t}\n\t\t\ttranslationGroup = source.translationGroup || source.id;\n\t\t}\n\n\t\t// Build column names and values\n\t\tconst columns: string[] = [\n\t\t\t\"id\",\n\t\t\t\"slug\",\n\t\t\t\"status\",\n\t\t\t\"author_id\",\n\t\t\t\"primary_byline_id\",\n\t\t\t\"created_at\",\n\t\t\t\"updated_at\",\n\t\t\t\"published_at\",\n\t\t\t\"version\",\n\t\t\t\"locale\",\n\t\t\t\"translation_group\",\n\t\t];\n\t\tconst values: unknown[] = [\n\t\t\tid,\n\t\t\tslug || null,\n\t\t\tstatus,\n\t\t\tauthorId || null,\n\t\t\tprimaryBylineId ?? null,\n\t\t\tcreatedAt || now,\n\t\t\tnow,\n\t\t\tpublishedAt || null,\n\t\t\t1,\n\t\t\tlocale || \"en\",\n\t\t\ttranslationGroup,\n\t\t];\n\n\t\t// Add data fields as columns (skip system columns to prevent injection via data)\n\t\tif (data && typeof data === \"object\") {\n\t\t\tfor (const [key, value] of Object.entries(data)) {\n\t\t\t\tif (!SYSTEM_COLUMNS.has(key)) {\n\t\t\t\t\tvalidateIdentifier(key, \"content field name\");\n\t\t\t\t\tcolumns.push(key);\n\t\t\t\t\tvalues.push(serializeValue(value));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Build dynamic INSERT using raw SQL\n\t\tconst columnRefs = columns.map((c) => sql.ref(c));\n\t\tconst valuePlaceholders = values.map((v) => (v === null ? sql`NULL` : sql`${v}`));\n\n\t\tawait sql`\n\t\t\tINSERT INTO ${sql.ref(tableName)} (${sql.join(columnRefs, sql`, `)})\n\t\t\tVALUES (${sql.join(valuePlaceholders, sql`, `)})\n\t\t`.execute(this.db);\n\n\t\tinvalidateCollectionCache(type);\n\n\t\t// Fetch and return the created item\n\t\tconst item = await this.findById(type, id);\n\t\tif (!item) {\n\t\t\tthrow new Error(\"Failed to create content\");\n\t\t}\n\t\treturn item;\n\t}\n\n\t/**\n\t * Generate a unique slug for a content item within a collection.\n\t *\n\t * Checks the collection table for existing slugs that match `baseSlug`\n\t * (optionally scoped to a locale) and appends a numeric suffix (`-1`,\n\t * `-2`, etc.) on collision to guarantee uniqueness.\n\t *\n\t * Returns `null` if `baseSlug` is empty after slugification.\n\t */\n\tasync generateUniqueSlug(type: string, text: string, locale?: string): Promise<string | null> {\n\t\tconst baseSlug = slugify(text);\n\t\tif (!baseSlug) return null;\n\n\t\tconst tableName = getTableName(type);\n\n\t\t// Check if the base slug is available\n\t\tconst existing = locale\n\t\t\t? await sql<{ slug: string }>`\n\t\t\t\t\tSELECT slug FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${baseSlug}\n\t\t\t\t\tAND locale = ${locale}\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(this.db)\n\t\t\t: await sql<{ slug: string }>`\n\t\t\t\t\tSELECT slug FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${baseSlug}\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(this.db);\n\n\t\tif (existing.rows.length === 0) {\n\t\t\treturn baseSlug;\n\t\t}\n\n\t\t// Find all slugs matching the pattern `baseSlug` or `baseSlug-N`\n\t\tconst pattern = `${baseSlug}-%`;\n\t\tconst candidates = locale\n\t\t\t? await sql<{ slug: string }>`\n\t\t\t\t\tSELECT slug FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE (slug = ${baseSlug} OR slug LIKE ${pattern})\n\t\t\t\t\tAND locale = ${locale}\n\t\t\t\t`.execute(this.db)\n\t\t\t: await sql<{ slug: string }>`\n\t\t\t\t\tSELECT slug FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${baseSlug} OR slug LIKE ${pattern}\n\t\t\t\t`.execute(this.db);\n\n\t\t// Find the highest numeric suffix in use\n\t\tlet maxSuffix = 0;\n\t\tconst suffixPattern = new RegExp(`^${escapeRegExp(baseSlug)}-(\\\\d+)$`);\n\t\tfor (const row of candidates.rows) {\n\t\t\tconst match = suffixPattern.exec(row.slug);\n\t\t\tif (match) {\n\t\t\t\tconst n = parseInt(match[1], 10);\n\t\t\t\tif (n > maxSuffix) maxSuffix = n;\n\t\t\t}\n\t\t}\n\n\t\treturn `${baseSlug}-${maxSuffix + 1}`;\n\t}\n\n\t/**\n\t * Duplicate a content item\n\t * Creates a new draft copy with \"(Copy)\" appended to the title.\n\t * A slug is auto-generated from the new title by the handler layer.\n\t */\n\tasync duplicate(type: string, id: string, authorId?: string): Promise<ContentItem> {\n\t\t// Fetch the original item\n\t\tconst original = await this.findById(type, id);\n\t\tif (!original) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\t// Prepare the new data\n\t\tconst newData = { ...original.data };\n\n\t\t// Append \"(Copy)\" to title if present\n\t\tif (typeof newData.title === \"string\") {\n\t\t\tnewData.title = `${newData.title} (Copy)`;\n\t\t} else if (typeof newData.name === \"string\") {\n\t\t\tnewData.name = `${newData.name} (Copy)`;\n\t\t}\n\n\t\t// Auto-generate a unique slug from the new title/name\n\t\tconst slugSource =\n\t\t\ttypeof newData.title === \"string\"\n\t\t\t\t? newData.title\n\t\t\t\t: typeof newData.name === \"string\"\n\t\t\t\t\t? newData.name\n\t\t\t\t\t: null;\n\n\t\tconst slug = slugSource\n\t\t\t? await this.generateUniqueSlug(type, slugSource, original.locale ?? undefined)\n\t\t\t: null;\n\n\t\t// Create the duplicate as a draft — use override authorId if provided (caller owns the copy)\n\t\treturn this.create({\n\t\t\ttype,\n\t\t\tslug,\n\t\t\tdata: newData,\n\t\t\tstatus: \"draft\",\n\t\t\tauthorId: authorId || original.authorId || undefined,\n\t\t});\n\t}\n\n\t/**\n\t * Find content by ID\n\t */\n\tasync findById(type: string, id: string): Promise<ContentItem | null> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql<Record<string, unknown>>`\n\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tconst row = result.rows[0];\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.mapRow(type, row);\n\t}\n\n\t/**\n\t * Find content by id, including trashed (soft-deleted) items.\n\t * Used by restore endpoint for ownership checks.\n\t */\n\tasync findByIdIncludingTrashed(type: string, id: string): Promise<ContentItem | null> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql<Record<string, unknown>>`\n\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\tWHERE id = ${id}\n\t\t`.execute(this.db);\n\n\t\tconst row = result.rows[0];\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.mapRow(type, row);\n\t}\n\n\t/**\n\t * Find content by ID or slug. Tries ID first if it looks like a ULID,\n\t * otherwise tries slug. Falls back to the other if the first lookup misses.\n\t */\n\tasync findByIdOrSlug(\n\t\ttype: string,\n\t\tidentifier: string,\n\t\tlocale?: string,\n\t): Promise<ContentItem | null> {\n\t\treturn this._findByIdOrSlug(type, identifier, false, locale);\n\t}\n\n\t/**\n\t * Find content by ID or slug, including trashed (soft-deleted) items.\n\t * Used by restore/permanent-delete endpoints.\n\t */\n\tasync findByIdOrSlugIncludingTrashed(\n\t\ttype: string,\n\t\tidentifier: string,\n\t\tlocale?: string,\n\t): Promise<ContentItem | null> {\n\t\treturn this._findByIdOrSlug(type, identifier, true, locale);\n\t}\n\n\tprivate async _findByIdOrSlug(\n\t\ttype: string,\n\t\tidentifier: string,\n\t\tincludeTrashed: boolean,\n\t\tlocale?: string,\n\t): Promise<ContentItem | null> {\n\t\t// ULIDs are 26 uppercase alphanumeric chars\n\t\tconst looksLikeUlid = ULID_PATTERN.test(identifier);\n\n\t\tconst findById = includeTrashed\n\t\t\t? (t: string, id: string) => this.findByIdIncludingTrashed(t, id)\n\t\t\t: (t: string, id: string) => this.findById(t, id);\n\t\tconst findBySlug = includeTrashed\n\t\t\t? (t: string, s: string) => this.findBySlugIncludingTrashed(t, s, locale)\n\t\t\t: (t: string, s: string) => this.findBySlug(t, s, locale);\n\n\t\tif (looksLikeUlid) {\n\t\t\t// Try ID first, fall back to slug\n\t\t\tconst byId = await findById(type, identifier);\n\t\t\tif (byId) return byId;\n\t\t\treturn findBySlug(type, identifier);\n\t\t}\n\t\t// Try slug first, fall back to ID\n\t\tconst bySlug = await findBySlug(type, identifier);\n\t\tif (bySlug) return bySlug;\n\t\treturn findById(type, identifier);\n\t}\n\n\t/**\n\t * Find content by slug\n\t */\n\tasync findBySlug(type: string, slug: string, locale?: string): Promise<ContentItem | null> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = locale\n\t\t\t? await sql<Record<string, unknown>>`\n\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${slug}\n\t\t\t\t\tAND locale = ${locale}\n\t\t\t\t\tAND deleted_at IS NULL\n\t\t\t\t`.execute(this.db)\n\t\t\t: await sql<Record<string, unknown>>`\n\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${slug}\n\t\t\t\t\tAND deleted_at IS NULL\n\t\t\t\t\tORDER BY locale ASC\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(this.db);\n\n\t\tconst row = result.rows[0];\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.mapRow(type, row);\n\t}\n\n\t/**\n\t * Find content by slug, including trashed (soft-deleted) items.\n\t * Used by restore/permanent-delete endpoints.\n\t */\n\tasync findBySlugIncludingTrashed(\n\t\ttype: string,\n\t\tslug: string,\n\t\tlocale?: string,\n\t): Promise<ContentItem | null> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = locale\n\t\t\t? await sql<Record<string, unknown>>`\n\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${slug}\n\t\t\t\t\tAND locale = ${locale}\n\t\t\t\t`.execute(this.db)\n\t\t\t: await sql<Record<string, unknown>>`\n\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE slug = ${slug}\n\t\t\t\t\tORDER BY locale ASC\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(this.db);\n\n\t\tconst row = result.rows[0];\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.mapRow(type, row);\n\t}\n\n\t/**\n\t * Find many content items with filtering and pagination\n\t */\n\tasync findMany(\n\t\ttype: string,\n\t\toptions: FindManyOptions = {},\n\t): Promise<FindManyResult<ContentItem>> {\n\t\tconst tableName = getTableName(type);\n\t\tconst limit = Math.min(options.limit || 50, 100);\n\n\t\t// Determine ordering\n\t\tconst orderField = options.orderBy?.field || \"createdAt\";\n\t\tconst orderDirection = options.orderBy?.direction || \"desc\";\n\t\tconst dbField = this.mapOrderField(orderField);\n\n\t\t// Validate order direction to prevent injection\n\t\tconst safeOrderDirection = orderDirection.toLowerCase() === \"asc\" ? \"ASC\" : \"DESC\";\n\n\t\t// Build query with parameterized values (no string interpolation)\n\t\t// Note: Dynamic content tables have deleted_at column, cast needed for Kysely\n\t\tlet query = this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.selectAll()\n\t\t\t.where(\"deleted_at\" as never, \"is\", null);\n\n\t\t// Apply filters with parameterized queries\n\t\tif (options.where?.status) {\n\t\t\tquery = query.where(\"status\", \"=\", options.where.status);\n\t\t}\n\n\t\tif (options.where?.authorId) {\n\t\t\tquery = query.where(\"author_id\", \"=\", options.where.authorId);\n\t\t}\n\n\t\tif (options.where?.locale) {\n\t\t\tquery = query.where(\"locale\" as any, \"=\", options.where.locale);\n\t\t}\n\n\t\tquery = this.applySearchFilter(query, options.where);\n\t\tquery = this.applyDateFilter(query, options.where);\n\n\t\t// Handle cursor pagination — decodeCursor throws InvalidCursorError\n\t\t// on malformed input; let it propagate so handlers surface a\n\t\t// structured INVALID_CURSOR rather than silently returning page 1.\n\t\tif (options.cursor) {\n\t\t\tconst { orderValue, id: cursorId } = decodeCursor(options.cursor);\n\n\t\t\tif (safeOrderDirection === \"DESC\") {\n\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\teb.or([\n\t\t\t\t\t\teb(dbField as any, \"<\", orderValue),\n\t\t\t\t\t\teb.and([eb(dbField as any, \"=\", orderValue), eb(\"id\", \"<\", cursorId)]),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\teb.or([\n\t\t\t\t\t\teb(dbField as any, \">\", orderValue),\n\t\t\t\t\t\teb.and([eb(dbField as any, \"=\", orderValue), eb(\"id\", \">\", cursorId)]),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Apply ordering and limit\n\t\tquery = query\n\t\t\t.orderBy(dbField as any, safeOrderDirection === \"ASC\" ? \"asc\" : \"desc\")\n\t\t\t.orderBy(\"id\", safeOrderDirection === \"ASC\" ? \"asc\" : \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\t// Run the page fetch and the unbounded count together — the UI needs\n\t\t// both to render a stable denominator (kept on every page intentionally),\n\t\t// and issuing them in parallel on SQLite is essentially free.\n\t\tconst [rows, total] = await Promise.all([query.execute(), this.count(type, options.where)]);\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit);\n\n\t\tconst mappedResult: FindManyResult<ContentItem> = {\n\t\t\titems: items.map((row) => this.mapRow(type, row as Record<string, unknown>)),\n\t\t\ttotal,\n\t\t};\n\n\t\tif (hasMore && items.length > 0) {\n\t\t\tconst lastRow = items.at(-1) as Record<string, unknown>;\n\t\t\tconst lastOrderValue = lastRow[dbField];\n\t\t\tconst orderStr =\n\t\t\t\ttypeof lastOrderValue === \"string\" || typeof lastOrderValue === \"number\"\n\t\t\t\t\t? String(lastOrderValue)\n\t\t\t\t\t: \"\";\n\t\t\tmappedResult.nextCursor = encodeCursor(orderStr, String(lastRow.id));\n\t\t}\n\n\t\treturn mappedResult;\n\t}\n\n\t/**\n\t * Update content\n\t */\n\tasync update(type: string, id: string, input: UpdateContentInput): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\t// Build update object with parameterized values\n\t\tconst updates: Record<string, unknown> = {\n\t\t\tupdated_at: now,\n\t\t\tversion: sql`version + 1`,\n\t\t};\n\n\t\tif (input.status !== undefined) {\n\t\t\tupdates.status = input.status;\n\t\t}\n\n\t\tif (input.slug !== undefined) {\n\t\t\tupdates.slug = input.slug;\n\t\t}\n\n\t\tif (input.publishedAt !== undefined) {\n\t\t\tupdates.published_at = input.publishedAt;\n\t\t}\n\n\t\tif (input.scheduledAt !== undefined) {\n\t\t\tupdates.scheduled_at = input.scheduledAt;\n\t\t}\n\n\t\tif (input.authorId !== undefined) {\n\t\t\tupdates.author_id = input.authorId;\n\t\t}\n\n\t\tif (input.primaryBylineId !== undefined) {\n\t\t\tupdates.primary_byline_id = input.primaryBylineId;\n\t\t}\n\n\t\t// Update data fields (skip system columns to prevent injection via data)\n\t\tif (input.data !== undefined && typeof input.data === \"object\") {\n\t\t\tfor (const [key, value] of Object.entries(input.data)) {\n\t\t\t\tif (!SYSTEM_COLUMNS.has(key)) {\n\t\t\t\t\tvalidateIdentifier(key, \"content field name\");\n\t\t\t\t\tupdates[key] = serializeValue(value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tawait this.db\n\t\t\t.updateTable(tableName as keyof Database)\n\t\t\t.set(updates)\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.where(\"deleted_at\" as never, \"is\", null)\n\t\t\t.execute();\n\n\t\tinvalidateCollectionCache(type);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Delete content (soft delete - moves to trash)\n\t */\n\tasync delete(type: string, id: string): Promise<boolean> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst result = await sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET deleted_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tconst changed = (result.numAffectedRows ?? 0n) > 0n;\n\t\tif (changed) invalidateCollectionCache(type);\n\t\treturn changed;\n\t}\n\n\t/**\n\t * Restore content from trash\n\t */\n\tasync restore(type: string, id: string): Promise<ContentItem | null> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql<Record<string, unknown>>`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET deleted_at = NULL\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NOT NULL\n\t\t\tRETURNING *\n\t\t`.execute(this.db);\n\n\t\tconst restored = result.rows[0];\n\t\tif (!restored) return null;\n\n\t\tinvalidateCollectionCache(type);\n\t\treturn this.mapRow(type, restored);\n\t}\n\n\t/**\n\t * Permanently delete content (cannot be undone)\n\t */\n\t/**\n\t * Permanently delete a soft-deleted content row.\n\t *\n\t * Returns `true` only when a soft-deleted (trashed) row was removed.\n\t * Returns `false` when no row exists OR when the row exists but is live —\n\t * the caller is responsible for distinguishing these cases (typically via\n\t * a follow-up `findByIdOrSlugIncludingTrashed` to surface NOT_FOUND vs\n\t * NOT_TRASHED). The `AND deleted_at IS NOT NULL` clause is the safety net\n\t * that prevents permanent delete from bypassing the trash workflow.\n\t */\n\tasync permanentDelete(type: string, id: string): Promise<boolean> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql`\n\t\t\tDELETE FROM ${sql.ref(tableName)}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NOT NULL\n\t\t`.execute(this.db);\n\n\t\tconst changed = (result.numAffectedRows ?? 0n) > 0n;\n\t\tif (changed) invalidateCollectionCache(type);\n\t\treturn changed;\n\t}\n\n\t/**\n\t * Find trashed content items\n\t */\n\tasync findTrashed(\n\t\ttype: string,\n\t\toptions: Omit<FindManyOptions, \"where\"> = {},\n\t): Promise<FindManyResult<ContentItem & { deletedAt: string }>> {\n\t\tconst tableName = getTableName(type);\n\t\tconst limit = Math.min(options.limit || 50, 100);\n\n\t\t// Determine ordering - default to most recently deleted\n\t\tconst orderField = options.orderBy?.field || \"deletedAt\";\n\t\tconst orderDirection = options.orderBy?.direction || \"desc\";\n\t\tconst dbField = this.mapOrderField(orderField);\n\n\t\tconst safeOrderDirection = orderDirection.toLowerCase() === \"asc\" ? \"ASC\" : \"DESC\";\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.selectAll()\n\t\t\t.where(\"deleted_at\" as never, \"is not\", null);\n\n\t\t// Handle cursor pagination — decodeCursor throws on invalid input.\n\t\tif (options.cursor) {\n\t\t\tconst { orderValue, id: cursorId } = decodeCursor(options.cursor);\n\n\t\t\tif (safeOrderDirection === \"DESC\") {\n\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\teb.or([\n\t\t\t\t\t\teb(dbField as any, \"<\", orderValue),\n\t\t\t\t\t\teb.and([eb(dbField as any, \"=\", orderValue), eb(\"id\", \"<\", cursorId)]),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tquery = query.where((eb) =>\n\t\t\t\t\teb.or([\n\t\t\t\t\t\teb(dbField as any, \">\", orderValue),\n\t\t\t\t\t\teb.and([eb(dbField as any, \"=\", orderValue), eb(\"id\", \">\", cursorId)]),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tquery = query\n\t\t\t.orderBy(dbField as any, safeOrderDirection === \"ASC\" ? \"asc\" : \"desc\")\n\t\t\t.orderBy(\"id\", safeOrderDirection === \"ASC\" ? \"asc\" : \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\tconst rows = await query.execute();\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit);\n\n\t\tconst mappedResult: FindManyResult<ContentItem & { deletedAt: string }> = {\n\t\t\titems: items.map((row) => {\n\t\t\t\tconst record = row as Record<string, unknown>;\n\t\t\t\treturn {\n\t\t\t\t\t...this.mapRow(type, record),\n\t\t\t\t\tdeletedAt: typeof record.deleted_at === \"string\" ? record.deleted_at : \"\",\n\t\t\t\t};\n\t\t\t}),\n\t\t};\n\n\t\tif (hasMore && items.length > 0) {\n\t\t\tconst lastRow = items.at(-1) as Record<string, unknown>;\n\t\t\tconst lastOrderValue = lastRow[dbField];\n\t\t\tconst orderStr =\n\t\t\t\ttypeof lastOrderValue === \"string\" || typeof lastOrderValue === \"number\"\n\t\t\t\t\t? String(lastOrderValue)\n\t\t\t\t\t: \"\";\n\t\t\tmappedResult.nextCursor = encodeCursor(orderStr, String(lastRow.id));\n\t\t}\n\n\t\treturn mappedResult;\n\t}\n\n\t/**\n\t * Count trashed content items\n\t */\n\tasync countTrashed(type: string): Promise<number> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"deleted_at\" as never, \"is not\", null)\n\t\t\t.executeTakeFirst();\n\n\t\treturn Number(result?.count || 0);\n\t}\n\n\t/**\n\t * Apply the optional case-insensitive `q` substring filter across the\n\t * handler-resolved `searchColumns` (OR'd). User input is treated literally\n\t * (LIKE wildcards escaped) and `lower()` is applied on both sides for\n\t * SQLite/Postgres case-insensitive parity.\n\t */\n\tprivate applySearchFilter<QB extends { where: (cb: (eb: any) => unknown) => QB }>(\n\t\tquery: QB,\n\t\twhere?: { q?: string; searchColumns?: string[] },\n\t): QB {\n\t\tconst term = where?.q?.trim();\n\t\tconst columns = where?.searchColumns;\n\t\tif (!term || !columns || columns.length === 0) return query;\n\n\t\tconst escaped = term.replace(LIKE_WILDCARD_RE, (c) => `\\\\${c}`);\n\t\tconst pattern = `%${escaped}%`;\n\n\t\treturn query.where((eb) =>\n\t\t\teb.or(\n\t\t\t\tcolumns.map((col) => {\n\t\t\t\t\tvalidateIdentifier(col, \"search column\");\n\t\t\t\t\treturn eb(sql`lower(${sql.ref(col)})`, \"like\", sql`lower(${pattern}) escape '\\\\'`);\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t}\n\n\t/**\n\t * Apply the optional inclusive date-range filter. The field is mapped\n\t * through `DATE_FILTER_COLUMNS` (a closed whitelist), and bounds compare\n\t * lexicographically against the stored ISO 8601 timestamps. A `publishedAt`\n\t * range naturally excludes never-published rows (their column is NULL).\n\t */\n\tprivate applyDateFilter<QB extends { where: (cb: (eb: any) => unknown) => QB }>(\n\t\tquery: QB,\n\t\twhere?: { dateFilter?: { field: string; from?: string; to?: string } },\n\t): QB {\n\t\tconst filter = where?.dateFilter;\n\t\tif (!filter) return query;\n\t\tconst column = DATE_FILTER_COLUMNS[filter.field as ContentDateField];\n\t\tif (!column) {\n\t\t\tthrow new EmDashValidationError(`Invalid date filter field: ${filter.field}`);\n\t\t}\n\t\tconst { from, to } = filter;\n\t\tif (!from && !to) return query;\n\n\t\tlet next = query;\n\t\tif (from) next = next.where((eb) => eb(column as any, \">=\", from));\n\t\tif (to) next = next.where((eb) => eb(column as any, \"<=\", to));\n\t\treturn next;\n\t}\n\n\t/**\n\t * Count content items\n\t */\n\tasync count(type: string, where?: FindManyOptions[\"where\"]): Promise<number> {\n\t\tconst tableName = getTableName(type);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"deleted_at\" as never, \"is\", null);\n\n\t\tif (where?.status) {\n\t\t\tquery = query.where(\"status\", \"=\", where.status);\n\t\t}\n\n\t\tif (where?.authorId) {\n\t\t\tquery = query.where(\"author_id\", \"=\", where.authorId);\n\t\t}\n\n\t\tif (where?.locale) {\n\t\t\tquery = query.where(\"locale\" as any, \"=\", where.locale);\n\t\t}\n\n\t\tquery = this.applySearchFilter(query, where);\n\t\tquery = this.applyDateFilter(query, where);\n\n\t\tconst result = await query.executeTakeFirst();\n\t\treturn Number(result?.count || 0);\n\t}\n\n\t/**\n\t * Distinct, non-null `author_id` values across the collection's live\n\t * (non-trashed) content. Used to populate the admin author filter with\n\t * only the users who have actually authored entries, rather than the\n\t * full user directory (which requires admin privileges to read).\n\t */\n\tasync findDistinctAuthorIds(type: string): Promise<string[]> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.select(\"author_id\")\n\t\t\t.distinct()\n\t\t\t.where(\"deleted_at\" as never, \"is\", null)\n\t\t\t.where(\"author_id\" as never, \"is not\", null)\n\t\t\t.execute();\n\n\t\treturn rows\n\t\t\t.map((row) => (row as { author_id: string | null }).author_id)\n\t\t\t.filter((id): id is string => id !== null);\n\t}\n\n\t// get overall statistics (total, published, draft) for a content type in a single query\n\tasync getStats(type: string): Promise<{ total: number; published: number; draft: number }> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await this.db\n\t\t\t.selectFrom(tableName as keyof Database)\n\t\t\t.select((eb) => [\n\t\t\t\teb.fn.count(\"id\").as(\"total\"),\n\t\t\t\teb.fn.sum(eb.case().when(\"status\", \"=\", \"published\").then(1).else(0).end()).as(\"published\"),\n\t\t\t\teb.fn.sum(eb.case().when(\"status\", \"=\", \"draft\").then(1).else(0).end()).as(\"draft\"),\n\t\t\t])\n\t\t\t.where(\"deleted_at\" as never, \"is\", null)\n\t\t\t.executeTakeFirst();\n\n\t\treturn {\n\t\t\ttotal: Number(result?.total || 0),\n\t\t\tpublished: Number(result?.published || 0),\n\t\t\tdraft: Number(result?.draft || 0),\n\t\t};\n\t}\n\n\t/**\n\t * Schedule content for future publishing\n\t *\n\t * Sets status to 'scheduled' and stores the scheduled publish time.\n\t * The content will be auto-published when the scheduled time is reached.\n\t */\n\tasync schedule(type: string, id: string, scheduledAt: string): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\t// Validate scheduledAt is in the future\n\t\tconst scheduledDate = new Date(scheduledAt);\n\t\tif (isNaN(scheduledDate.getTime())) {\n\t\t\tthrow new EmDashValidationError(\"Invalid scheduled date\");\n\t\t}\n\t\tif (scheduledDate <= new Date()) {\n\t\t\tthrow new EmDashValidationError(\"Scheduled date must be in the future\");\n\t\t}\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\t// Published posts keep their status — the schedule applies to the\n\t\t// pending draft, not the currently-live revision. Unpublished posts\n\t\t// transition to 'scheduled' so they aren't visible before the time.\n\t\tconst newStatus = existing.status === \"published\" ? \"published\" : \"scheduled\";\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET status = ${newStatus},\n\t\t\t\tscheduled_at = ${scheduledAt},\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tinvalidateCollectionCache(type);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Unschedule content\n\t *\n\t * Clears the scheduled time. Published posts stay published;\n\t * draft/scheduled posts revert to 'draft'.\n\t */\n\tasync unschedule(type: string, id: string): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\t// Published posts keep their status — just clear the pending schedule.\n\t\t// Draft/scheduled posts revert to 'draft'.\n\t\tconst newStatus = existing.status === \"published\" ? \"published\" : \"draft\";\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET status = ${newStatus},\n\t\t\t\tscheduled_at = NULL,\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND scheduled_at IS NOT NULL\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tinvalidateCollectionCache(type);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Find content that is ready to be published\n\t *\n\t * Returns all content where scheduled_at <= now, regardless of status.\n\t * This covers both draft-scheduled posts (status='scheduled') and\n\t * published posts with scheduled draft changes (status='published').\n\t *\n\t * `limit` (optional) caps how many due rows are returned, oldest-due first.\n\t * The scheduled-publishing sweep passes a limit so a large backlog can't\n\t * fan out unbounded publish/webhook work in a single tick (and blow a Worker\n\t * invocation's CPU/subrequest budget); the remainder drains on later ticks.\n\t */\n\tasync findReadyToPublish(type: string, limit?: number): Promise<ContentItem[]> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\t// Embed an empty fragment when unbounded so callers that want every due\n\t\t// row (manual flows, tests) keep the original behaviour.\n\t\tconst limitClause =\n\t\t\ttypeof limit === \"number\" && Number.isInteger(limit) && limit > 0\n\t\t\t\t? sql`LIMIT ${limit}`\n\t\t\t\t: sql``;\n\n\t\tconst result = await sql<Record<string, unknown>>`\n\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\tWHERE scheduled_at IS NOT NULL\n\t\t\tAND scheduled_at <= ${now}\n\t\t\tAND deleted_at IS NULL\n\t\t\tORDER BY scheduled_at ASC\n\t\t\t${limitClause}\n\t\t`.execute(this.db);\n\n\t\treturn result.rows.map((row) => this.mapRow(type, row));\n\t}\n\n\t/**\n\t * Find all translations in a translation group\n\t */\n\tasync findTranslations(type: string, translationGroup: string): Promise<ContentItem[]> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql<Record<string, unknown>>`\n\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\tWHERE translation_group = ${translationGroup}\n\t\t\tAND deleted_at IS NULL\n\t\t\tORDER BY locale ASC\n\t\t`.execute(this.db);\n\n\t\treturn result.rows.map((row) => this.mapRow(type, row));\n\t}\n\n\t/**\n\t * Publish the current draft\n\t *\n\t * Promotes draft_revision_id to live_revision_id and clears draft pointer.\n\t * Syncs the draft revision's data into the content table columns so the\n\t * content table always reflects the published version.\n\t * If no draft revision exists, creates one from current data and publishes it.\n\t *\n\t * `publishedAt` (optional) overrides the publication timestamp. If omitted,\n\t * the existing `published_at` is preserved (idempotent re-publish keeps the\n\t * original date) and falls back to the current time on first publish. Pass\n\t * an explicit value to backdate a publish (e.g. when migrating content from\n\t * another CMS).\n\t *\n\t * `requireDue` (optional) gates the publish on the row still being due:\n\t * `scheduled_at` non-null and in the past. Used by the scheduled-publishing\n\t * sweep to avoid publishing content an editor unscheduled or rescheduled\n\t * between selection and publish. It claims the row with a single conditional\n\t * UPDATE (clearing `scheduled_at`) before any other write, so it is atomic\n\t * even on D1 (no multi-statement transactions) and serialises against\n\t * `unschedule()` and concurrent sweeps — no TOCTOU and no double publish.\n\t */\n\tasync publish(\n\t\ttype: string,\n\t\tid: string,\n\t\tpublishedAt?: string,\n\t\trequireDue = false,\n\t): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\t// Scheduled sweep: atomically claim the row before any other write. A\n\t\t// single conditional UPDATE is atomic per-statement on every dialect\n\t\t// (it doesn't depend on a wrapping transaction, which D1 lacks). If the\n\t\t// schedule was cleared or pushed to the future (unschedule/reschedule)\n\t\t// or another sweep already claimed it, this affects 0 rows and we bail\n\t\t// before promoting any revision — so the row can't be double-published.\n\t\tlet claimedScheduledAt: string | null = null;\n\t\tlet claimedUpdatedAt: string | null = null;\n\t\tif (requireDue) {\n\t\t\tconst claim = await sql`\n\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\tSET scheduled_at = NULL,\n\t\t\t\t\tupdated_at = ${now}\n\t\t\t\tWHERE id = ${id}\n\t\t\t\tAND scheduled_at IS NOT NULL\n\t\t\t\tAND scheduled_at <= ${now}\n\t\t\t\tAND deleted_at IS NULL\n\t\t\t`.execute(this.db);\n\t\t\tif ((claim.numAffectedRows ?? 0n) === 0n) {\n\t\t\t\tthrow new ScheduledNotDueError();\n\t\t\t}\n\t\t\t// Remember what we cleared so we can put it back if the publish work\n\t\t\t// below fails on a driver without transactions (see catch). Both\n\t\t\t// values come from the pre-claim snapshot: if a concurrent\n\t\t\t// reschedule-to-a-different-past-time landed between findById and the\n\t\t\t// claim, the restore writes the snapshot value rather than the one the\n\t\t\t// claim actually cleared. That window is tiny and the restore is\n\t\t\t// best-effort retry bookkeeping, so the imprecision is acceptable.\n\t\t\tclaimedScheduledAt = existing.scheduledAt;\n\t\t\tclaimedUpdatedAt = existing.updatedAt;\n\t\t}\n\n\t\t// Track whether the final publish write committed. On D1 the claim above\n\t\t// is already durable (withTransaction is a no-op there), so if a later\n\t\t// step throws we must restore the schedule — otherwise the row is left\n\t\t// `scheduled` with `scheduled_at = NULL` and no sweep ever retries it.\n\t\tlet publishCommitted = false;\n\t\ttry {\n\t\t\tconst revisionRepo = new RevisionRepository(this.db);\n\t\t\tlet revisionToPublish = existing.draftRevisionId || existing.liveRevisionId;\n\n\t\t\tif (!revisionToPublish) {\n\t\t\t\t// No revision exists - create one from current data\n\t\t\t\tconst revision = await revisionRepo.create({\n\t\t\t\t\tcollection: type,\n\t\t\t\t\tentryId: id,\n\t\t\t\t\tdata: existing.data,\n\t\t\t\t});\n\t\t\t\trevisionToPublish = revision.id;\n\t\t\t}\n\n\t\t\t// Sync the revision's data into the content table columns\n\t\t\t// so the content table always holds the published version\n\t\t\tconst revision = await revisionRepo.findById(revisionToPublish);\n\t\t\tif (revision) {\n\t\t\t\tawait this.syncDataColumns(type, id, revision.data);\n\n\t\t\t\t// Sync slug from revision if stored there\n\t\t\t\tif (typeof revision.data._slug === \"string\") {\n\t\t\t\t\tawait sql`\n\t\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\t\tSET slug = ${revision.data._slug}\n\t\t\t\t\t\tWHERE id = ${id}\n\t\t\t\t\t`.execute(this.db);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (publishedAt !== undefined) {\n\t\t\t\t// Caller supplied an explicit timestamp, so we overwrite published_at\n\t\t\t\t// directly (used to backdate a publish, e.g. for content migrations).\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\tSET live_revision_id = ${revisionToPublish},\n\t\t\t\t\t\tdraft_revision_id = NULL,\n\t\t\t\t\t\tstatus = 'published',\n\t\t\t\t\t\tscheduled_at = NULL,\n\t\t\t\t\t\tpublished_at = ${publishedAt},\n\t\t\t\t\t\tupdated_at = ${now}\n\t\t\t\t\tWHERE id = ${id}\n\t\t\t\t\tAND deleted_at IS NULL\n\t\t\t\t`.execute(this.db);\n\t\t\t} else {\n\t\t\t\t// No timestamp supplied — preserve existing published_at on\n\t\t\t\t// idempotent re-publish, fall back to `now` on first publish.\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\tSET live_revision_id = ${revisionToPublish},\n\t\t\t\t\t\tdraft_revision_id = NULL,\n\t\t\t\t\t\tstatus = 'published',\n\t\t\t\t\t\tscheduled_at = NULL,\n\t\t\t\t\t\tpublished_at = COALESCE(published_at, ${now}),\n\t\t\t\t\t\tupdated_at = ${now}\n\t\t\t\t\tWHERE id = ${id}\n\t\t\t\t\tAND deleted_at IS NULL\n\t\t\t\t`.execute(this.db);\n\t\t\t}\n\t\t\tpublishCommitted = true;\n\n\t\t\tconst updated = await this.findById(type, id);\n\t\t\tif (!updated) {\n\t\t\t\tthrow new Error(\"Content not found\");\n\t\t\t}\n\n\t\t\tinvalidateCollectionCache(type);\n\t\t\treturn updated;\n\t\t} catch (error) {\n\t\t\t// Best-effort schedule restore for the no-transaction (D1) case so a\n\t\t\t// failed publish stays retryable. Skipped when the publish actually\n\t\t\t// committed (the failure was afterwards). On SQLite/Postgres the\n\t\t\t// enclosing transaction rolls the claim back, so this restore also\n\t\t\t// rolls back — a harmless no-op. Never mask the original error.\n\t\t\tif (requireDue && claimedScheduledAt && !publishCommitted) {\n\t\t\t\ttry {\n\t\t\t\t\t// Only restore if the row still has pending work: either it's not\n\t\t\t\t\t// published, or it's a published row that still has a draft change\n\t\t\t\t\t// queued. This avoids re-adding a stale schedule (and triggering a\n\t\t\t\t\t// redundant republish) when another actor fully published the row\n\t\t\t\t\t// in the failure window — that publish clears draft_revision_id.\n\t\t\t\t\t// Restore updated_at to its pre-claim value too — the claim bumped\n\t\t\t\t\t// it to `now`, and a failed publish made no real change, so leaving\n\t\t\t\t\t// it advanced would be a phantom modification for \"changed since\"\n\t\t\t\t\t// consumers (sync, ETags, incremental indexers).\n\t\t\t\t\tawait sql`\n\t\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\t\tSET scheduled_at = ${claimedScheduledAt},\n\t\t\t\t\t\t\tupdated_at = ${claimedUpdatedAt ?? now}\n\t\t\t\t\t\tWHERE id = ${id}\n\t\t\t\t\t\tAND scheduled_at IS NULL\n\t\t\t\t\t\tAND deleted_at IS NULL\n\t\t\t\t\t\tAND (status != 'published' OR draft_revision_id IS NOT NULL)\n\t\t\t\t\t`.execute(this.db);\n\t\t\t\t} catch (restoreError) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`[content] Failed to restore schedule for ${type}/${id} after publish failure:`,\n\t\t\t\t\t\trestoreError,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Unpublish content\n\t *\n\t * Removes live pointer but preserves draft. If no draft exists,\n\t * creates one from the live version so the content isn't lost.\n\t */\n\tasync unpublish(type: string, id: string): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\t// If no draft exists, create one from the live version\n\t\tif (!existing.draftRevisionId && existing.liveRevisionId) {\n\t\t\tconst revisionRepo = new RevisionRepository(this.db);\n\t\t\tconst liveRevision = await revisionRepo.findById(existing.liveRevisionId);\n\t\t\tif (liveRevision) {\n\t\t\t\tconst draft = await revisionRepo.create({\n\t\t\t\t\tcollection: type,\n\t\t\t\t\tentryId: id,\n\t\t\t\t\tdata: liveRevision.data,\n\t\t\t\t});\n\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\tSET draft_revision_id = ${draft.id}\n\t\t\t\t\tWHERE id = ${id}\n\t\t\t\t`.execute(this.db);\n\t\t\t}\n\t\t}\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET live_revision_id = NULL,\n\t\t\t\tstatus = 'draft',\n\t\t\t\tpublished_at = NULL,\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tinvalidateCollectionCache(type);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Set the draft revision pointer for a content item.\n\t *\n\t * Used by seed/import paths that stage a new revision's data before\n\t * promoting it to live via `publish()`.\n\t *\n\t * Validates that the content item exists and is not soft-deleted, that\n\t * the revision exists, and that the revision belongs to the same\n\t * collection and entry. Without these checks, a caller could leave the\n\t * content row pointing at a missing or unrelated revision.\n\t */\n\tasync setDraftRevision(type: string, id: string, revisionId: string): Promise<void> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\tconst revisionRepo = new RevisionRepository(this.db);\n\t\tconst revision = await revisionRepo.findById(revisionId);\n\t\tif (!revision) {\n\t\t\tthrow new EmDashValidationError(\"Revision not found\");\n\t\t}\n\n\t\tif (revision.collection !== type || revision.entryId !== id) {\n\t\t\tthrow new EmDashValidationError(\"Revision does not belong to the specified content item\");\n\t\t}\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET draft_revision_id = ${revisionId},\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tinvalidateCollectionCache(type);\n\t}\n\n\t/**\n\t * Discard pending draft changes\n\t *\n\t * Clears draft_revision_id. The content table columns already hold the\n\t * published version, so no data sync is needed.\n\t */\n\tasync discardDraft(type: string, id: string): Promise<ContentItem> {\n\t\tconst tableName = getTableName(type);\n\t\tconst now = new Date().toISOString();\n\n\t\tconst existing = await this.findById(type, id);\n\t\tif (!existing) {\n\t\t\tthrow new EmDashValidationError(\"Content item not found\");\n\t\t}\n\n\t\tif (!existing.draftRevisionId) {\n\t\t\t// No draft to discard\n\t\t\treturn existing;\n\t\t}\n\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET draft_revision_id = NULL,\n\t\t\t\tupdated_at = ${now}\n\t\t\tWHERE id = ${id}\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\tinvalidateCollectionCache(type);\n\n\t\tconst updated = await this.findById(type, id);\n\t\tif (!updated) {\n\t\t\tthrow new Error(\"Content not found\");\n\t\t}\n\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Sync data columns in the content table from a data object.\n\t * Used to promote revision data into the content table on publish.\n\t * Keys starting with _ are revision metadata (e.g. _slug) and are skipped.\n\t */\n\tprivate async syncDataColumns(\n\t\ttype: string,\n\t\tid: string,\n\t\tdata: Record<string, unknown>,\n\t): Promise<void> {\n\t\tconst tableName = getTableName(type);\n\t\tconst updates: Record<string, unknown> = {};\n\n\t\tfor (const [key, value] of Object.entries(data)) {\n\t\t\tif (SYSTEM_COLUMNS.has(key)) continue;\n\t\t\tif (key.startsWith(\"_\")) continue; // revision metadata\n\t\t\tvalidateIdentifier(key, \"content field name\");\n\t\t\tupdates[key] = serializeValue(value);\n\t\t}\n\n\t\tif (Object.keys(updates).length === 0) return;\n\n\t\tawait this.db\n\t\t\t.updateTable(tableName as keyof Database)\n\t\t\t.set(updates)\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.execute();\n\t}\n\n\t/**\n\t * Count content items with a pending schedule.\n\t * Includes both draft-scheduled (status='scheduled') and published\n\t * posts with scheduled draft changes (status='published', scheduled_at set).\n\t */\n\tasync countScheduled(type: string): Promise<number> {\n\t\tconst tableName = getTableName(type);\n\n\t\tconst result = await sql<{ count: number }>`\n\t\t\tSELECT COUNT(id) as count FROM ${sql.ref(tableName)}\n\t\t\tWHERE scheduled_at IS NOT NULL\n\t\t\tAND deleted_at IS NULL\n\t\t`.execute(this.db);\n\n\t\treturn Number(result.rows[0]?.count || 0);\n\t}\n\n\t/**\n\t * Map database row to ContentItem\n\t * Extracts system columns and puts content fields in data\n\t * Excludes null values from data to match input semantics\n\t */\n\tprivate mapRow(type: string, row: Record<string, unknown>): ContentItem {\n\t\tconst data: Record<string, unknown> = {};\n\n\t\tfor (const [key, value] of Object.entries(row)) {\n\t\t\tif (!SYSTEM_COLUMNS.has(key) && value !== null) {\n\t\t\t\tdata[key] = deserializeValue(value);\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tid: row.id as string,\n\t\t\ttype,\n\t\t\tslug: row.slug as string | null,\n\t\t\tstatus: row.status as string,\n\t\t\tdata,\n\t\t\tauthorId: row.author_id as string | null,\n\t\t\tprimaryBylineId: (row.primary_byline_id as string | null) ?? null,\n\t\t\tcreatedAt: row.created_at as string,\n\t\t\tupdatedAt: row.updated_at as string,\n\t\t\tpublishedAt: row.published_at as string | null,\n\t\t\tscheduledAt: row.scheduled_at as string | null,\n\t\t\tliveRevisionId: (row.live_revision_id as string | null) ?? null,\n\t\t\tdraftRevisionId: (row.draft_revision_id as string | null) ?? null,\n\t\t\tversion: typeof row.version === \"number\" ? row.version : 1,\n\t\t\tlocale: (row.locale as string) ?? null,\n\t\t\ttranslationGroup: (row.translation_group as string) ?? null,\n\t\t};\n\t}\n\n\t/**\n\t * Map order field names to database columns.\n\t * Only allows known fields to prevent column enumeration via crafted orderBy values.\n\t */\n\tprivate mapOrderField(field: string): string {\n\t\tconst mapping: Record<string, string> = {\n\t\t\tcreatedAt: \"created_at\",\n\t\t\tupdatedAt: \"updated_at\",\n\t\t\tpublishedAt: \"published_at\",\n\t\t\tscheduledAt: \"scheduled_at\",\n\t\t\tdeletedAt: \"deleted_at\",\n\t\t\ttitle: \"title\",\n\t\t\tname: \"name\",\n\t\t\tslug: \"slug\",\n\t\t\tstatus: \"status\",\n\t\t\tlocale: \"locale\",\n\t\t};\n\n\t\tconst mapped = mapping[field];\n\t\tif (!mapped) {\n\t\t\tthrow new EmDashValidationError(`Invalid order field: ${field}`);\n\t\t}\n\t\treturn mapped;\n\t}\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"content-refresh-CA4uKwzH.mjs","names":["isRecord","isRecord","readString"],"sources":["../src/database/repositories/media-usage.ts","../src/media/usage/content-fields.ts","../src/media/usage/extractor.ts","../src/media/usage/source-key.ts","../src/media/usage/content-snapshots.ts","../src/media/usage/content-refresh.ts"],"sourcesContent":["import {\n\tsql,\n\ttype ExpressionBuilder,\n\ttype Insertable,\n\ttype Kysely,\n\ttype Selectable,\n\ttype Transaction,\n\ttype Updateable,\n} from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport type { MediaUsageContentSourceVariant } from \"../../media/usage/source-key.js\";\nimport type { MediaKind, MediaUsageReferenceType } from \"../../media/usage/types.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport { withTransaction } from \"../transaction.js\";\nimport type {\n\tDatabase,\n\tMediaUsageIndexStatusTable,\n\tMediaUsageSourceTable,\n\tMediaUsageTable,\n} from \"../types.js\";\nimport { validateIdentifier } from \"../validate.js\";\nimport { decodeCursor, encodeCursor, type FindManyResult } from \"./types.js\";\n\ntype DatabaseExecutor = Kysely<Database> | Transaction<Database>;\ntype MediaUsageSourceNullableStringColumn =\n\t| \"source_fingerprint\"\n\t| \"source_updated_at\"\n\t| \"revision_id\"\n\t| \"updated_at\"\n\t| \"last_attempted_at\"\n\t| \"last_error_code\";\n\nconst OCCURRENCE_BIND_COLUMNS = 13;\nconst OCCURRENCE_INSERT_BATCH_SIZE = Math.max(\n\t1,\n\tMath.floor(SQL_BATCH_SIZE / OCCURRENCE_BIND_COLUMNS),\n);\n\nexport interface MediaUsageSourceInput {\n\tsourceKey: string;\n\tsourceType: string;\n\tcollectionSlug?: string | null;\n\tcontentId?: string | null;\n\tsourceVariant: MediaUsageContentSourceVariant;\n\tlocale?: string | null;\n\ttranslationGroup?: string | null;\n\tcontentSlug?: string | null;\n\tcontentTitle?: string | null;\n\tcontentStatus?: string | null;\n\tcontentScheduledAt?: string | null;\n\tcontentDeletedAt?: string | null;\n\trevisionId?: string | null;\n\tschemaVersion?: number;\n\tsourceUpdatedAt?: string | null;\n\tsourceVersion?: number | null;\n\tsourceFingerprint?: string | null;\n\tsourceCompleteness?: MediaUsageSourceCompleteness;\n\tlastAttemptedAt?: string | null;\n\tlastErrorCode?: string | null;\n}\n\nexport interface MediaUsageOccurrenceInput {\n\tfieldSlug: string;\n\tfieldPath: string;\n\toccurrenceIndex?: number;\n\treferenceType: MediaUsageReferenceType;\n\tmediaId: string | null;\n\tprovider: string;\n\tproviderAssetId: string;\n\tmediaKind?: MediaKind | null;\n\tmimeType?: string | null;\n}\n\nexport interface MediaUsageSource {\n\tsourceKey: string;\n\tsourceType: string;\n\tcollectionSlug: string | null;\n\tcontentId: string | null;\n\tsourceVariant: string;\n\tlocale: string | null;\n\ttranslationGroup: string | null;\n\tcontentSlug: string | null;\n\tcontentTitle: string | null;\n\tcontentStatus: string | null;\n\tcontentScheduledAt: string | null;\n\tcontentDeletedAt: string | null;\n\trevisionId: string | null;\n\tcurrentGeneration: string;\n\tschemaVersion: number;\n\tsourceUpdatedAt: string | null;\n\tsourceVersion: number | null;\n\tsourceFingerprint: string | null;\n\tsourceCompleteness: string;\n\tlastAttemptedAt: string | null;\n\tlastErrorCode: string | null;\n\tindexedAt: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface MediaUsageGuardedReplaceResult {\n\treplaced: boolean;\n\t/** Populated only when a guarded replacement did not win the current source row. */\n\tsource: MediaUsageSource | null;\n}\n\nexport interface MediaUsageGuardedDeleteResult {\n\tdeleted: boolean;\n\tsource: MediaUsageSource | null;\n}\n\nexport interface MediaUsageGuardedAbsentDeleteResult extends MediaUsageGuardedDeleteResult {\n\tcontentPresent: boolean;\n}\n\nexport interface MediaUsageGuardedAttemptResult {\n\tattempted: boolean;\n\t/** Populated only when a guarded attempted mark did not win the current source row. */\n\tsource: MediaUsageSource | null;\n}\n\nexport interface MediaUsageIndexStatusRepairInput extends MediaUsageIndexStatusIdentity {\n\trunToken: string;\n\tschemaVersion?: number;\n\tstartedAt: string;\n\tupdatedAt?: string;\n}\n\nexport interface MediaUsageIndexStatusFinalizeInput extends MediaUsageIndexStatusIdentity {\n\trunToken: string;\n\tstatus: Exclude<MediaUsageIndexStatusValue, \"never\" | \"running\" | \"stale\">;\n\tschemaVersion?: number;\n\tcompletedAt: string;\n\tindexedSourceCount?: number;\n\tfailedSourceCount?: number;\n\tlastErrorCode?: string | null;\n\tupdatedAt?: string;\n}\n\nexport interface MediaUsageGuardedIndexStatusResult {\n\tfinalized: boolean;\n\tstatus: MediaUsageIndexStatus | null;\n}\n\nexport type MediaUsageSourceCompleteness =\n\t| \"unknown\"\n\t| \"complete\"\n\t| \"partial\"\n\t| \"failed\"\n\t| \"unsupported\";\n\nexport type MediaUsageIndexStatusValue =\n\t| \"never\"\n\t| \"running\"\n\t| \"complete\"\n\t| \"partial\"\n\t| \"failed\"\n\t| \"stale\";\n\nexport interface MediaUsageIndexStatusIdentity {\n\tadapterId: string;\n\tscopeType: string;\n\tscopeKey: string;\n}\n\nexport interface MediaUsageIndexStatusInput extends MediaUsageIndexStatusIdentity {\n\tstatus: MediaUsageIndexStatusValue;\n\tschemaVersion?: number;\n\tstartedAt?: string | null;\n\tcompletedAt?: string | null;\n\tcursor?: string | null;\n\tindexedSourceCount?: number;\n\tfailedSourceCount?: number;\n\tlastErrorCode?: string | null;\n\tupdatedAt?: string;\n}\n\nexport interface MediaUsageIndexStatus extends MediaUsageIndexStatusIdentity {\n\tstatus: string;\n\tschemaVersion: number;\n\tstartedAt: string | null;\n\tcompletedAt: string | null;\n\tcursor: string | null;\n\tindexedSourceCount: number;\n\tfailedSourceCount: number;\n\tlastErrorCode: string | null;\n\tupdatedAt: string;\n}\n\nexport interface FindMediaUsageOptions {\n\tlimit?: number;\n\tcursor?: string;\n}\n\ninterface MediaUsageSourceRow {\n\tsource_key: string;\n\tsource_type: string;\n\tcollection_slug: string | null;\n\tcontent_id: string | null;\n\tsource_variant: string;\n\tlocale: string | null;\n\ttranslation_group: string | null;\n\tcontent_slug: string | null;\n\tcontent_title: string | null;\n\tcontent_status: string | null;\n\tcontent_scheduled_at: string | null;\n\tcontent_deleted_at: string | null;\n\trevision_id: string | null;\n\tcurrent_generation: string;\n\tschema_version: number;\n\tsource_updated_at: string | null;\n\tsource_version: number | null;\n\tsource_fingerprint: string | null;\n\tsource_completeness: string;\n\tlast_attempted_at: string | null;\n\tlast_error_code: string | null;\n\tindexed_at: string;\n\tcreated_at: string;\n\tupdated_at: string;\n}\n\nexport interface MediaUsageOccurrence {\n\tid: string;\n\tsourceKey: string;\n\tgeneration: string;\n\tfieldSlug: string;\n\tfieldPath: string;\n\toccurrenceIndex: number;\n\treferenceType: string;\n\tmediaId: string | null;\n\tprovider: string;\n\tproviderAssetId: string;\n\tmediaKind: string | null;\n\tmimeType: string | null;\n\tcreatedAt: string;\n}\n\nexport interface MediaUsageRecord {\n\tsource: MediaUsageSource;\n\toccurrence: MediaUsageOccurrence;\n}\n\ninterface JoinedUsageRow {\n\tsource_key: string;\n\tsource_type: string;\n\tcollection_slug: string | null;\n\tcontent_id: string | null;\n\tsource_variant: string;\n\tlocale: string | null;\n\ttranslation_group: string | null;\n\tcontent_slug: string | null;\n\tcontent_title: string | null;\n\tcontent_status: string | null;\n\tcontent_scheduled_at: string | null;\n\tcontent_deleted_at: string | null;\n\trevision_id: string | null;\n\tcurrent_generation: string;\n\tschema_version: number;\n\tsource_updated_at: string | null;\n\tsource_version: number | null;\n\tsource_fingerprint: string | null;\n\tsource_completeness: string;\n\tlast_attempted_at: string | null;\n\tlast_error_code: string | null;\n\tindexed_at: string;\n\tsource_created_at: string;\n\tsource_row_updated_at: string;\n\toccurrence_id: string;\n\tgeneration: string;\n\tfield_slug: string;\n\tfield_path: string;\n\toccurrence_index: number;\n\treference_type: string;\n\tmedia_id: string | null;\n\tprovider: string;\n\tprovider_asset_id: string;\n\tmedia_kind: string | null;\n\tmime_type: string | null;\n\toccurrence_created_at: string;\n}\n\n/** Persistence-only repository for the internal media usage projection tables. */\nexport class MediaUsageRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\tasync replaceSource(\n\t\tsource: MediaUsageSourceInput,\n\t\toccurrences: readonly MediaUsageOccurrenceInput[],\n\t): Promise<MediaUsageSource> {\n\t\tconst generation = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait this.insertOccurrences(trx, source.sourceKey, generation, occurrences, now);\n\t\t\tawait this.upsertSource(trx, source, generation, now);\n\t\t});\n\n\t\tconst replaced = await this.findSource(source.sourceKey);\n\t\tif (!replaced) {\n\t\t\tthrow new Error(`Media usage source ${source.sourceKey} was not persisted`);\n\t\t}\n\t\treturn replaced;\n\t}\n\tasync replaceSourceIfCurrent(\n\t\tsource: MediaUsageSourceInput,\n\t\toccurrences: readonly MediaUsageOccurrenceInput[],\n\t\texpectedCurrentGeneration: string | null,\n\t): Promise<MediaUsageGuardedReplaceResult> {\n\t\tconst generation = ulid();\n\t\tconst now = new Date().toISOString();\n\t\tconst row = this.buildSourceRow(source, generation, now);\n\t\tlet replaced = false;\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait this.insertOccurrences(trx, source.sourceKey, generation, occurrences, now);\n\t\t\tif (expectedCurrentGeneration === null) {\n\t\t\t\treplaced = await this.insertSourceIfAbsent(trx, row);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treplaced = await this.updateSourceIfGeneration(trx, row, expectedCurrentGeneration);\n\t\t});\n\n\t\treturn {\n\t\t\treplaced,\n\t\t\tsource: replaced ? null : await this.findSource(source.sourceKey),\n\t\t};\n\t}\n\n\tasync findSource(sourceKey: string): Promise<MediaUsageSource | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_media_usage_sources\")\n\t\t\t.selectAll()\n\t\t\t.where(\"source_key\", \"=\", sourceKey)\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? rowToSource(row) : null;\n\t}\n\n\tasync findSources(sourceKeys: readonly string[]): Promise<Map<string, MediaUsageSource>> {\n\t\tconst uniqueSourceKeys = [...new Set(sourceKeys)];\n\t\tconst sources = new Map<string, MediaUsageSource>();\n\t\tif (uniqueSourceKeys.length === 0) return sources;\n\n\t\tfor (const sourceKeyBatch of chunks(uniqueSourceKeys, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_media_usage_sources\")\n\t\t\t\t.selectAll()\n\t\t\t\t.where(\"source_key\", \"in\", sourceKeyBatch)\n\t\t\t\t.execute();\n\t\t\tfor (const row of rows) {\n\t\t\t\tconst source = rowToSource(row);\n\t\t\t\tsources.set(source.sourceKey, source);\n\t\t\t}\n\t\t}\n\n\t\treturn sources;\n\t}\n\n\tasync replaceSourceIfMatching(\n\t\tsource: MediaUsageSourceInput,\n\t\toccurrences: readonly MediaUsageOccurrenceInput[],\n\t\texpectedSource: MediaUsageSource | null,\n\t): Promise<MediaUsageGuardedReplaceResult> {\n\t\tconst generation = ulid();\n\t\tconst now = new Date().toISOString();\n\t\tconst row = this.buildSourceRow(source, generation, now);\n\t\tlet replaced = false;\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait this.insertOccurrences(trx, source.sourceKey, generation, occurrences, now);\n\t\t\tif (expectedSource === null) {\n\t\t\t\treplaced = await this.insertSourceIfAbsent(trx, row);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treplaced = await this.updateSourceIfMatching(trx, row, expectedSource);\n\t\t});\n\n\t\treturn {\n\t\t\treplaced,\n\t\t\tsource: replaced ? null : await this.findSource(source.sourceKey),\n\t\t};\n\t}\n\n\tasync markSourceAttempted(source: MediaUsageSourceInput): Promise<MediaUsageSource> {\n\t\tconst now = new Date().toISOString();\n\t\tconst row = this.buildAttemptedSourceRow(source, now);\n\t\tconst updates = this.attemptedSourceUpdateSet(source, row);\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_media_usage_sources\")\n\t\t\t.values(row)\n\t\t\t.onConflict((oc) => oc.column(\"source_key\").doUpdateSet(updates))\n\t\t\t.execute();\n\n\t\tconst attempted = await this.findSource(source.sourceKey);\n\t\tif (!attempted) {\n\t\t\tthrow new Error(`Media usage source ${source.sourceKey} was not persisted`);\n\t\t}\n\t\treturn attempted;\n\t}\n\n\tasync markSourceAttemptedIfMatching(\n\t\tsource: MediaUsageSourceInput,\n\t\texpectedSource: MediaUsageSource | null,\n\t): Promise<MediaUsageGuardedAttemptResult> {\n\t\tconst now = new Date().toISOString();\n\t\tconst row = this.buildAttemptedSourceRow(source, now);\n\t\tlet attempted = false;\n\n\t\tif (expectedSource === null) {\n\t\t\tattempted = await this.insertSourceIfAbsent(this.db, row);\n\t\t} else {\n\t\t\tattempted = await this.updateAttemptedSourceIfMatching(this.db, source, row, expectedSource);\n\t\t}\n\n\t\treturn {\n\t\t\tattempted,\n\t\t\tsource: attempted ? null : await this.findSource(source.sourceKey),\n\t\t};\n\t}\n\n\tasync findCurrentUsageByMediaId(mediaId: string): Promise<MediaUsageRecord[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_media_usage_sources as s\")\n\t\t\t.innerJoin(\"_emdash_media_usage as u\", (join) =>\n\t\t\t\tjoin\n\t\t\t\t\t.onRef(\"u.source_key\", \"=\", \"s.source_key\")\n\t\t\t\t\t.onRef(\"u.generation\", \"=\", \"s.current_generation\"),\n\t\t\t)\n\t\t\t.select(currentUsageSelect)\n\t\t\t.where(\"u.media_id\", \"=\", mediaId)\n\t\t\t.orderBy(\"s.source_key\", \"asc\")\n\t\t\t.orderBy(\"u.field_path\", \"asc\")\n\t\t\t.orderBy(\"u.occurrence_index\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map(rowToUsageRecord);\n\t}\n\n\tasync findCurrentUsageByProviderAsset(\n\t\tprovider: string,\n\t\tproviderAssetId: string,\n\t): Promise<MediaUsageRecord[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_media_usage_sources as s\")\n\t\t\t.innerJoin(\"_emdash_media_usage as u\", (join) =>\n\t\t\t\tjoin\n\t\t\t\t\t.onRef(\"u.source_key\", \"=\", \"s.source_key\")\n\t\t\t\t\t.onRef(\"u.generation\", \"=\", \"s.current_generation\"),\n\t\t\t)\n\t\t\t.select(currentUsageSelect)\n\t\t\t.where(\"u.provider\", \"=\", provider)\n\t\t\t.where(\"u.provider_asset_id\", \"=\", providerAssetId)\n\t\t\t.orderBy(\"s.source_key\", \"asc\")\n\t\t\t.orderBy(\"u.field_path\", \"asc\")\n\t\t\t.orderBy(\"u.occurrence_index\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map(rowToUsageRecord);\n\t}\n\n\tasync findCurrentUsagePageByMediaId(\n\t\tmediaId: string,\n\t\toptions: FindMediaUsageOptions = {},\n\t): Promise<FindManyResult<MediaUsageRecord>> {\n\t\treturn this.findCurrentUsagePage((query) => query.where(\"u.media_id\", \"=\", mediaId), options);\n\t}\n\n\tasync findCurrentUsagePageByProviderAsset(\n\t\tprovider: string,\n\t\tproviderAssetId: string,\n\t\toptions: FindMediaUsageOptions = {},\n\t): Promise<FindManyResult<MediaUsageRecord>> {\n\t\treturn this.findCurrentUsagePage(\n\t\t\t(query) =>\n\t\t\t\tquery.where(\"u.provider\", \"=\", provider).where(\"u.provider_asset_id\", \"=\", providerAssetId),\n\t\t\toptions,\n\t\t);\n\t}\n\n\tasync deleteSource(sourceKey: string): Promise<number> {\n\t\treturn this.deleteSources([sourceKey]);\n\t}\n\n\tasync deleteSourceIfCurrent(\n\t\tsourceKey: string,\n\t\texpectedCurrentGeneration: string,\n\t): Promise<MediaUsageGuardedDeleteResult> {\n\t\tlet deleted = false;\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tconst result = await trx\n\t\t\t\t.deleteFrom(\"_emdash_media_usage_sources\")\n\t\t\t\t.where(\"source_key\", \"=\", sourceKey)\n\t\t\t\t.where(\"current_generation\", \"=\", expectedCurrentGeneration)\n\t\t\t\t.executeTakeFirst();\n\t\t\tdeleted = Number(result.numDeletedRows ?? 0) > 0;\n\t\t\tif (!deleted) return;\n\t\t\tawait trx.deleteFrom(\"_emdash_media_usage\").where(\"source_key\", \"=\", sourceKey).execute();\n\t\t});\n\n\t\treturn {\n\t\t\tdeleted,\n\t\t\tsource: await this.findSource(sourceKey),\n\t\t};\n\t}\n\n\tasync deleteSourceIfMatching(\n\t\tsourceKey: string,\n\t\texpectedSource: MediaUsageSource,\n\t): Promise<MediaUsageGuardedDeleteResult> {\n\t\tlet deleted = false;\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tconst result = await trx\n\t\t\t\t.deleteFrom(\"_emdash_media_usage_sources\")\n\t\t\t\t.where(\"source_key\", \"=\", sourceKey)\n\t\t\t\t.where(this.sourceMatchExpression(expectedSource))\n\t\t\t\t.executeTakeFirst();\n\t\t\tdeleted = Number(result.numDeletedRows ?? 0) > 0;\n\t\t\tif (!deleted) return;\n\t\t\tawait trx.deleteFrom(\"_emdash_media_usage\").where(\"source_key\", \"=\", sourceKey).execute();\n\t\t});\n\n\t\treturn {\n\t\t\tdeleted,\n\t\t\tsource: await this.findSource(sourceKey),\n\t\t};\n\t}\n\n\tasync deleteSourceIfMatchingContentAbsent(\n\t\tsourceKey: string,\n\t\texpectedSource: MediaUsageSource,\n\t\tcollectionSlug: string,\n\t\tcontentId: string,\n\t): Promise<MediaUsageGuardedAbsentDeleteResult> {\n\t\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\t\tconst tableName = `ec_${collectionSlug}`;\n\t\tlet deleted = false;\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tconst result = await trx\n\t\t\t\t.deleteFrom(\"_emdash_media_usage_sources\")\n\t\t\t\t.where(\"source_key\", \"=\", sourceKey)\n\t\t\t\t.where(this.sourceMatchExpression(expectedSource))\n\t\t\t\t.where(\n\t\t\t\t\tsql<boolean>`NOT EXISTS (SELECT 1 FROM ${sql.ref(tableName)} WHERE id = ${contentId})`,\n\t\t\t\t)\n\t\t\t\t.executeTakeFirst();\n\t\t\tdeleted = Number(result.numDeletedRows ?? 0) > 0;\n\t\t\tif (!deleted) return;\n\t\t\tawait trx.deleteFrom(\"_emdash_media_usage\").where(\"source_key\", \"=\", sourceKey).execute();\n\t\t});\n\t\tconst contentPresent = deleted ? false : await this.contentRowExists(tableName, contentId);\n\n\t\treturn {\n\t\t\tdeleted,\n\t\t\tcontentPresent,\n\t\t\tsource: deleted || contentPresent ? null : await this.findSource(sourceKey),\n\t\t};\n\t}\n\n\tasync deleteSources(sourceKeys: readonly string[]): Promise<number> {\n\t\treturn this.deleteSourceKeys(sourceKeys);\n\t}\n\n\tasync deleteContentSources(collectionSlug: string, contentId: string): Promise<number> {\n\t\tconst sourceRows = await this.db\n\t\t\t.selectFrom(\"_emdash_media_usage_sources\")\n\t\t\t.select(\"source_key\")\n\t\t\t.where(\"source_type\", \"=\", \"content\")\n\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"content_id\", \"=\", contentId)\n\t\t\t.execute();\n\t\tconst sourceKeys = sourceRows.map((row) => row.source_key);\n\t\treturn this.deleteSourceKeys(sourceKeys);\n\t}\n\n\tasync deleteCollectionSources(collectionSlug: string): Promise<number> {\n\t\tlet deleted = 0;\n\t\twhile (true) {\n\t\t\tconst sourceRows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_media_usage_sources\")\n\t\t\t\t.select(\"source_key\")\n\t\t\t\t.where(\"source_type\", \"=\", \"content\")\n\t\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t\t.orderBy(\"source_key\", \"asc\")\n\t\t\t\t.limit(SQL_BATCH_SIZE)\n\t\t\t\t.execute();\n\t\t\tif (sourceRows.length === 0) break;\n\n\t\t\tdeleted += await this.deleteSourceKeys(sourceRows.map((row) => row.source_key));\n\t\t}\n\t\treturn deleted;\n\t}\n\n\tasync findCollectionContentSources(collectionSlug: string): Promise<MediaUsageSource[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_media_usage_sources\")\n\t\t\t.selectAll()\n\t\t\t.where(\"source_type\", \"=\", \"content\")\n\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t.orderBy(\"source_key\", \"asc\")\n\t\t\t.execute();\n\t\treturn rows.map((row) => rowToSource(row));\n\t}\n\n\tasync deleteOrphanOccurrencesOlderThan(cutoff: string, limit: number): Promise<number> {\n\t\tconst batchLimit = Math.floor(limit);\n\t\tif (batchLimit <= 0) return 0;\n\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_media_usage as u\")\n\t\t\t.leftJoin(\"_emdash_media_usage_sources as s\", (join) =>\n\t\t\t\tjoin.onRef(\"s.source_key\", \"=\", \"u.source_key\"),\n\t\t\t)\n\t\t\t.select(\"u.id\")\n\t\t\t.where(\"s.source_key\", \"is\", null)\n\t\t\t.where(\"u.created_at\", \"<\", cutoff)\n\t\t\t.orderBy(\"u.created_at\", \"asc\")\n\t\t\t.orderBy(\"u.id\", \"asc\")\n\t\t\t.limit(batchLimit)\n\t\t\t.execute();\n\n\t\tlet deleted = 0;\n\t\tfor (const idBatch of chunks(\n\t\t\trows.map((row) => row.id),\n\t\t\tSQL_BATCH_SIZE,\n\t\t)) {\n\t\t\tconst result = await this.db\n\t\t\t\t.deleteFrom(\"_emdash_media_usage\")\n\t\t\t\t.where(\"id\", \"in\", idBatch)\n\t\t\t\t.where(\"created_at\", \"<\", cutoff)\n\t\t\t\t.where(\n\t\t\t\t\tsql<boolean>`NOT EXISTS (SELECT 1 FROM _emdash_media_usage_sources s WHERE s.source_key = _emdash_media_usage.source_key)`,\n\t\t\t\t)\n\t\t\t\t.executeTakeFirst();\n\t\t\tdeleted += Number(result.numDeletedRows ?? 0);\n\t\t}\n\t\treturn deleted;\n\t}\n\n\tasync deleteStaleGenerationsOlderThan(cutoff: string, limit: number): Promise<number> {\n\t\tconst batchLimit = Math.floor(limit);\n\t\tif (batchLimit <= 0) return 0;\n\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_media_usage as u\")\n\t\t\t.innerJoin(\"_emdash_media_usage_sources as s\", (join) =>\n\t\t\t\tjoin.onRef(\"s.source_key\", \"=\", \"u.source_key\"),\n\t\t\t)\n\t\t\t.select(\"u.id\")\n\t\t\t.where(\"u.created_at\", \"<\", cutoff)\n\t\t\t.whereRef(\"u.generation\", \"!=\", \"s.current_generation\")\n\t\t\t.whereRef(\"u.created_at\", \"<\", \"s.indexed_at\")\n\t\t\t.orderBy(\"u.created_at\", \"asc\")\n\t\t\t.orderBy(\"u.id\", \"asc\")\n\t\t\t.limit(batchLimit)\n\t\t\t.execute();\n\n\t\tconst ids = rows.map((row) => row.id);\n\t\tif (ids.length === 0) return 0;\n\n\t\tlet deleted = 0;\n\t\tfor (const idBatch of chunks(ids, SQL_BATCH_SIZE)) {\n\t\t\tconst result = await this.db\n\t\t\t\t.deleteFrom(\"_emdash_media_usage\")\n\t\t\t\t.where(\"id\", \"in\", idBatch)\n\t\t\t\t.where(\"created_at\", \"<\", cutoff)\n\t\t\t\t.where((eb) =>\n\t\t\t\t\teb.exists(\n\t\t\t\t\t\teb\n\t\t\t\t\t\t\t.selectFrom(\"_emdash_media_usage_sources as s\")\n\t\t\t\t\t\t\t.select(\"s.source_key\")\n\t\t\t\t\t\t\t.whereRef(\"s.source_key\", \"=\", \"_emdash_media_usage.source_key\")\n\t\t\t\t\t\t\t.whereRef(\"s.current_generation\", \"!=\", \"_emdash_media_usage.generation\")\n\t\t\t\t\t\t\t.whereRef(\"_emdash_media_usage.created_at\", \"<\", \"s.indexed_at\"),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.executeTakeFirst();\n\t\t\tdeleted += Number(result.numDeletedRows ?? 0);\n\t\t}\n\t\treturn deleted;\n\t}\n\n\tasync deleteAbandonedGenerationsOlderThan(cutoff: string, limit: number): Promise<number> {\n\t\tconst batchLimit = Math.floor(limit);\n\t\tif (batchLimit <= 0) return 0;\n\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_media_usage as u\")\n\t\t\t.innerJoin(\"_emdash_media_usage_sources as s\", (join) =>\n\t\t\t\tjoin.onRef(\"s.source_key\", \"=\", \"u.source_key\"),\n\t\t\t)\n\t\t\t.select(\"u.id\")\n\t\t\t.where(\"u.created_at\", \"<\", cutoff)\n\t\t\t.whereRef(\"u.generation\", \"!=\", \"s.current_generation\")\n\t\t\t.whereRef(\"u.created_at\", \">=\", \"s.indexed_at\")\n\t\t\t.orderBy(\"u.created_at\", \"asc\")\n\t\t\t.orderBy(\"u.id\", \"asc\")\n\t\t\t.limit(batchLimit)\n\t\t\t.execute();\n\n\t\tlet deleted = 0;\n\t\tfor (const idBatch of chunks(\n\t\t\trows.map((row) => row.id),\n\t\t\tSQL_BATCH_SIZE,\n\t\t)) {\n\t\t\tconst result = await this.db\n\t\t\t\t.deleteFrom(\"_emdash_media_usage\")\n\t\t\t\t.where(\"id\", \"in\", idBatch)\n\t\t\t\t.where(\"created_at\", \"<\", cutoff)\n\t\t\t\t.where((eb) =>\n\t\t\t\t\teb.exists(\n\t\t\t\t\t\teb\n\t\t\t\t\t\t\t.selectFrom(\"_emdash_media_usage_sources as s\")\n\t\t\t\t\t\t\t.select(\"s.source_key\")\n\t\t\t\t\t\t\t.whereRef(\"s.source_key\", \"=\", \"_emdash_media_usage.source_key\")\n\t\t\t\t\t\t\t.whereRef(\"s.current_generation\", \"!=\", \"_emdash_media_usage.generation\")\n\t\t\t\t\t\t\t.whereRef(\"_emdash_media_usage.created_at\", \">=\", \"s.indexed_at\"),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.executeTakeFirst();\n\t\t\tdeleted += Number(result.numDeletedRows ?? 0);\n\t\t}\n\t\treturn deleted;\n\t}\n\n\tasync upsertIndexStatus(input: MediaUsageIndexStatusInput): Promise<MediaUsageIndexStatus> {\n\t\tconst now = input.updatedAt ?? new Date().toISOString();\n\t\tconst row = {\n\t\t\tadapter_id: input.adapterId,\n\t\t\tscope_type: input.scopeType,\n\t\t\tscope_key: input.scopeKey,\n\t\t\tstatus: input.status,\n\t\t\tschema_version: input.schemaVersion ?? 1,\n\t\t\tstarted_at: input.startedAt ?? null,\n\t\t\tcompleted_at: input.completedAt ?? null,\n\t\t\tcursor: input.cursor ?? null,\n\t\t\tindexed_source_count: input.indexedSourceCount ?? 0,\n\t\t\tfailed_source_count: input.failedSourceCount ?? 0,\n\t\t\tlast_error_code: input.lastErrorCode ?? null,\n\t\t\tupdated_at: now,\n\t\t};\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_media_usage_index_status\")\n\t\t\t.values(row)\n\t\t\t.onConflict((oc) =>\n\t\t\t\toc.columns([\"adapter_id\", \"scope_type\", \"scope_key\"]).doUpdateSet({\n\t\t\t\t\tstatus: row.status,\n\t\t\t\t\tschema_version: row.schema_version,\n\t\t\t\t\tstarted_at: row.started_at,\n\t\t\t\t\tcompleted_at: row.completed_at,\n\t\t\t\t\tcursor: row.cursor,\n\t\t\t\t\tindexed_source_count: row.indexed_source_count,\n\t\t\t\t\tfailed_source_count: row.failed_source_count,\n\t\t\t\t\tlast_error_code: row.last_error_code,\n\t\t\t\t\tupdated_at: row.updated_at,\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.execute();\n\n\t\tconst status = await this.findIndexStatus(input);\n\t\tif (!status) {\n\t\t\tthrow new Error(\n\t\t\t\t`Media usage index status ${input.adapterId}:${input.scopeType}:${input.scopeKey} was not persisted`,\n\t\t\t);\n\t\t}\n\t\treturn status;\n\t}\n\n\tasync beginIndexStatusRepair(\n\t\tinput: MediaUsageIndexStatusRepairInput,\n\t): Promise<MediaUsageIndexStatus> {\n\t\treturn this.upsertIndexStatus({\n\t\t\tadapterId: input.adapterId,\n\t\t\tscopeType: input.scopeType,\n\t\t\tscopeKey: input.scopeKey,\n\t\t\tstatus: \"running\",\n\t\t\tschemaVersion: input.schemaVersion,\n\t\t\tstartedAt: input.startedAt,\n\t\t\tcompletedAt: null,\n\t\t\tcursor: input.runToken,\n\t\t\tindexedSourceCount: 0,\n\t\t\tfailedSourceCount: 0,\n\t\t\tlastErrorCode: null,\n\t\t\tupdatedAt: input.updatedAt,\n\t\t});\n\t}\n\n\tasync finalizeIndexStatusRepairIfRunning(\n\t\tinput: MediaUsageIndexStatusFinalizeInput,\n\t): Promise<MediaUsageGuardedIndexStatusResult> {\n\t\tconst updates: Updateable<MediaUsageIndexStatusTable> = {\n\t\t\tstatus: input.status,\n\t\t\tcompleted_at: input.completedAt,\n\t\t\tcursor: null,\n\t\t\tindexed_source_count: input.indexedSourceCount ?? 0,\n\t\t\tfailed_source_count: input.failedSourceCount ?? 0,\n\t\t\tlast_error_code: input.lastErrorCode ?? null,\n\t\t\tupdated_at: input.updatedAt ?? new Date().toISOString(),\n\t\t};\n\t\tif (input.schemaVersion !== undefined) updates.schema_version = input.schemaVersion;\n\n\t\tconst result = await this.db\n\t\t\t.updateTable(\"_emdash_media_usage_index_status\")\n\t\t\t.set(updates)\n\t\t\t.where(\"adapter_id\", \"=\", input.adapterId)\n\t\t\t.where(\"scope_type\", \"=\", input.scopeType)\n\t\t\t.where(\"scope_key\", \"=\", input.scopeKey)\n\t\t\t.where(\"status\", \"=\", \"running\")\n\t\t\t.where(\"cursor\", \"=\", input.runToken)\n\t\t\t.executeTakeFirst();\n\t\tconst finalized = Number(result.numUpdatedRows ?? 0) > 0;\n\n\t\treturn {\n\t\t\tfinalized,\n\t\t\tstatus: await this.findIndexStatus(input),\n\t\t};\n\t}\n\n\tasync findIndexStatus(\n\t\tidentity: MediaUsageIndexStatusIdentity,\n\t): Promise<MediaUsageIndexStatus | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_media_usage_index_status\")\n\t\t\t.selectAll()\n\t\t\t.where(\"adapter_id\", \"=\", identity.adapterId)\n\t\t\t.where(\"scope_type\", \"=\", identity.scopeType)\n\t\t\t.where(\"scope_key\", \"=\", identity.scopeKey)\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? rowToIndexStatus(row) : null;\n\t}\n\n\tasync deleteIndexStatus(identity: MediaUsageIndexStatusIdentity): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_emdash_media_usage_index_status\")\n\t\t\t.where(\"adapter_id\", \"=\", identity.adapterId)\n\t\t\t.where(\"scope_type\", \"=\", identity.scopeType)\n\t\t\t.where(\"scope_key\", \"=\", identity.scopeKey)\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result.numDeletedRows ?? 0);\n\t}\n\n\tprivate async findCurrentUsagePage(\n\t\tapplyFilter: (\n\t\t\tquery: ReturnType<MediaUsageRepository[\"currentUsageBaseQuery\"]>,\n\t\t) => ReturnType<MediaUsageRepository[\"currentUsageBaseQuery\"]>,\n\t\toptions: FindMediaUsageOptions,\n\t): Promise<FindManyResult<MediaUsageRecord>> {\n\t\tconst limit = Math.min(Math.max(1, options.limit ?? 50), 100);\n\t\tlet query = applyFilter(this.currentUsageBaseQuery())\n\t\t\t.orderBy(\"u.id\", \"asc\")\n\t\t\t.limit(limit + 1);\n\n\t\tif (options.cursor) {\n\t\t\tconst { id } = decodeCursor(options.cursor);\n\t\t\tquery = query.where(\"u.id\", \">\", id);\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\tconst items = rows.slice(0, limit).map(rowToUsageRecord);\n\t\tconst result: FindManyResult<MediaUsageRecord> = { items };\n\n\t\tif (rows.length > limit && items.length > 0) {\n\t\t\tconst last = items.at(-1)!;\n\t\t\tresult.nextCursor = encodeCursor(last.occurrence.id, last.occurrence.id);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate currentUsageBaseQuery() {\n\t\treturn this.db\n\t\t\t.selectFrom(\"_emdash_media_usage_sources as s\")\n\t\t\t.innerJoin(\"_emdash_media_usage as u\", (join) =>\n\t\t\t\tjoin\n\t\t\t\t\t.onRef(\"u.source_key\", \"=\", \"s.source_key\")\n\t\t\t\t\t.onRef(\"u.generation\", \"=\", \"s.current_generation\"),\n\t\t\t)\n\t\t\t.select(currentUsageSelect);\n\t}\n\n\tprivate async deleteSourceKeys(sourceKeys: readonly string[]): Promise<number> {\n\t\tconst uniqueSourceKeys = [...new Set(sourceKeys)];\n\t\tif (uniqueSourceKeys.length === 0) return 0;\n\n\t\treturn withTransaction(this.db, async (trx) => {\n\t\t\tlet deleted = 0;\n\t\t\tfor (const sourceKeyBatch of chunks(uniqueSourceKeys, SQL_BATCH_SIZE)) {\n\t\t\t\tconst result = await trx\n\t\t\t\t\t.deleteFrom(\"_emdash_media_usage_sources\")\n\t\t\t\t\t.where(\"source_key\", \"in\", sourceKeyBatch)\n\t\t\t\t\t.executeTakeFirst();\n\t\t\t\tdeleted += Number(result.numDeletedRows ?? 0);\n\n\t\t\t\tawait trx\n\t\t\t\t\t.deleteFrom(\"_emdash_media_usage\")\n\t\t\t\t\t.where(\"source_key\", \"in\", sourceKeyBatch)\n\t\t\t\t\t.execute();\n\t\t\t}\n\t\t\treturn deleted;\n\t\t});\n\t}\n\n\tprivate async insertOccurrences(\n\t\tdb: DatabaseExecutor,\n\t\tsourceKey: string,\n\t\tgeneration: string,\n\t\toccurrences: readonly MediaUsageOccurrenceInput[],\n\t\tnow: string,\n\t): Promise<void> {\n\t\tif (occurrences.length === 0) return;\n\n\t\tconst rows = occurrences.map((occurrence) => ({\n\t\t\tid: ulid(),\n\t\t\tsource_key: sourceKey,\n\t\t\tgeneration,\n\t\t\tfield_slug: occurrence.fieldSlug,\n\t\t\tfield_path: occurrence.fieldPath,\n\t\t\toccurrence_index: occurrence.occurrenceIndex ?? 0,\n\t\t\treference_type: occurrence.referenceType,\n\t\t\tmedia_id: occurrence.mediaId,\n\t\t\tprovider: occurrence.provider,\n\t\t\tprovider_asset_id: occurrence.providerAssetId,\n\t\t\tmedia_kind: occurrence.mediaKind ?? null,\n\t\t\tmime_type: occurrence.mimeType ?? null,\n\t\t\tcreated_at: now,\n\t\t}));\n\n\t\tfor (const rowBatch of chunks(rows, OCCURRENCE_INSERT_BATCH_SIZE)) {\n\t\t\tawait db.insertInto(\"_emdash_media_usage\").values(rowBatch).execute();\n\t\t}\n\t}\n\n\tprivate async upsertSource(\n\t\tdb: DatabaseExecutor,\n\t\tsource: MediaUsageSourceInput,\n\t\tgeneration: string,\n\t\tnow: string,\n\t): Promise<void> {\n\t\tconst row = this.buildSourceRow(source, generation, now);\n\n\t\tawait db\n\t\t\t.insertInto(\"_emdash_media_usage_sources\")\n\t\t\t.values(row)\n\t\t\t.onConflict((oc) => oc.column(\"source_key\").doUpdateSet(this.sourceUpdateSet(row)))\n\t\t\t.execute();\n\t}\n\n\tprivate async insertSourceIfAbsent(\n\t\tdb: DatabaseExecutor,\n\t\trow: Insertable<MediaUsageSourceTable>,\n\t): Promise<boolean> {\n\t\tconst result = await db\n\t\t\t.insertInto(\"_emdash_media_usage_sources\")\n\t\t\t.values(row)\n\t\t\t.onConflict((oc) => oc.column(\"source_key\").doNothing())\n\t\t\t.executeTakeFirst();\n\t\treturn (result.numInsertedOrUpdatedRows ?? 0n) > 0n;\n\t}\n\n\tprivate async updateSourceIfGeneration(\n\t\tdb: DatabaseExecutor,\n\t\trow: ReturnType<MediaUsageRepository[\"buildSourceRow\"]>,\n\t\texpectedCurrentGeneration: string,\n\t): Promise<boolean> {\n\t\tconst result = await db\n\t\t\t.updateTable(\"_emdash_media_usage_sources\")\n\t\t\t.set(this.sourceUpdateSet(row))\n\t\t\t.where(\"source_key\", \"=\", row.source_key)\n\t\t\t.where(\"current_generation\", \"=\", expectedCurrentGeneration)\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result.numUpdatedRows ?? 0) > 0;\n\t}\n\n\tprivate async updateSourceIfMatching(\n\t\tdb: DatabaseExecutor,\n\t\trow: ReturnType<MediaUsageRepository[\"buildSourceRow\"]>,\n\t\texpectedSource: MediaUsageSource,\n\t): Promise<boolean> {\n\t\tconst result = await db\n\t\t\t.updateTable(\"_emdash_media_usage_sources\")\n\t\t\t.set(this.sourceUpdateSet(row))\n\t\t\t.where(\"source_key\", \"=\", row.source_key)\n\t\t\t.where(this.sourceMatchExpression(expectedSource))\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result.numUpdatedRows ?? 0) > 0;\n\t}\n\n\tprivate async updateAttemptedSourceIfMatching(\n\t\tdb: DatabaseExecutor,\n\t\tsource: MediaUsageSourceInput,\n\t\trow: ReturnType<MediaUsageRepository[\"buildAttemptedSourceRow\"]>,\n\t\texpectedSource: MediaUsageSource,\n\t): Promise<boolean> {\n\t\tconst result = await db\n\t\t\t.updateTable(\"_emdash_media_usage_sources\")\n\t\t\t.set(this.attemptedSourceUpdateSet(source, row))\n\t\t\t.where(\"source_key\", \"=\", row.source_key)\n\t\t\t.where(this.sourceMatchExpression(expectedSource))\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result.numUpdatedRows ?? 0) > 0;\n\t}\n\n\tprivate sourceMatchExpression(expectedSource: MediaUsageSource) {\n\t\treturn (eb: ExpressionBuilder<Database, \"_emdash_media_usage_sources\">) =>\n\t\t\teb.and([\n\t\t\t\teb(\"current_generation\", \"=\", expectedSource.currentGeneration),\n\t\t\t\teb(\"source_completeness\", \"=\", expectedSource.sourceCompleteness),\n\t\t\t\tthis.nullableStringExpression(eb, \"updated_at\", expectedSource.updatedAt),\n\t\t\t\tthis.nullableStringExpression(eb, \"source_fingerprint\", expectedSource.sourceFingerprint),\n\t\t\t\tthis.nullableStringExpression(eb, \"source_updated_at\", expectedSource.sourceUpdatedAt),\n\t\t\t\tthis.nullableNumberExpression(eb, \"source_version\", expectedSource.sourceVersion),\n\t\t\t\tthis.nullableStringExpression(eb, \"revision_id\", expectedSource.revisionId),\n\t\t\t\tthis.nullableStringExpression(eb, \"last_attempted_at\", expectedSource.lastAttemptedAt),\n\t\t\t\tthis.nullableStringExpression(eb, \"last_error_code\", expectedSource.lastErrorCode),\n\t\t\t]);\n\t}\n\n\tprivate nullableStringExpression(\n\t\teb: ExpressionBuilder<Database, \"_emdash_media_usage_sources\">,\n\t\tcolumn: MediaUsageSourceNullableStringColumn,\n\t\tvalue: string | null,\n\t) {\n\t\treturn value === null ? eb(column, \"is\", null) : eb(column, \"=\", value);\n\t}\n\n\tprivate nullableNumberExpression(\n\t\teb: ExpressionBuilder<Database, \"_emdash_media_usage_sources\">,\n\t\tcolumn: \"source_version\",\n\t\tvalue: number | null,\n\t) {\n\t\treturn value === null ? eb(column, \"is\", null) : eb(column, \"=\", value);\n\t}\n\n\tprivate async contentRowExists(tableName: string, contentId: string): Promise<boolean> {\n\t\tconst result = await sql<{ id: string }>`\n\t\t\tSELECT id\n\t\t\tFROM ${sql.ref(tableName)}\n\t\t\tWHERE id = ${contentId}\n\t\t\tLIMIT 1\n\t\t`.execute(this.db);\n\t\treturn result.rows.length > 0;\n\t}\n\n\tprivate buildSourceRow(source: MediaUsageSourceInput, generation: string, now: string) {\n\t\treturn {\n\t\t\tsource_key: source.sourceKey,\n\t\t\tsource_type: source.sourceType,\n\t\t\tcollection_slug: source.collectionSlug ?? null,\n\t\t\tcontent_id: source.contentId ?? null,\n\t\t\tsource_variant: source.sourceVariant,\n\t\t\tlocale: source.locale ?? null,\n\t\t\ttranslation_group: source.translationGroup ?? null,\n\t\t\tcontent_slug: source.contentSlug ?? null,\n\t\t\tcontent_title: source.contentTitle ?? null,\n\t\t\tcontent_status: source.contentStatus ?? null,\n\t\t\tcontent_scheduled_at: source.contentScheduledAt ?? null,\n\t\t\tcontent_deleted_at: source.contentDeletedAt ?? null,\n\t\t\trevision_id: source.revisionId ?? null,\n\t\t\tcurrent_generation: generation,\n\t\t\tschema_version: source.schemaVersion ?? 1,\n\t\t\tsource_updated_at: source.sourceUpdatedAt ?? null,\n\t\t\tsource_version: source.sourceVersion ?? null,\n\t\t\tsource_fingerprint: source.sourceFingerprint ?? null,\n\t\t\t// Complete means this source was fully refreshed for the extractor's current\n\t\t\t// schema/version coverage, not that every possible reference shape is known.\n\t\t\tsource_completeness: source.sourceCompleteness ?? \"complete\",\n\t\t\tlast_attempted_at: source.lastAttemptedAt ?? now,\n\t\t\tlast_error_code: null,\n\t\t\tindexed_at: now,\n\t\t\tupdated_at: now,\n\t\t};\n\t}\n\n\tprivate buildAttemptedSourceRow(source: MediaUsageSourceInput, now: string) {\n\t\treturn {\n\t\t\tsource_key: source.sourceKey,\n\t\t\tsource_type: source.sourceType,\n\t\t\tcollection_slug: source.collectionSlug ?? null,\n\t\t\tcontent_id: source.contentId ?? null,\n\t\t\tsource_variant: source.sourceVariant,\n\t\t\tlocale: source.locale ?? null,\n\t\t\ttranslation_group: source.translationGroup ?? null,\n\t\t\tcontent_slug: source.contentSlug ?? null,\n\t\t\tcontent_title: source.contentTitle ?? null,\n\t\t\tcontent_status: source.contentStatus ?? null,\n\t\t\tcontent_scheduled_at: source.contentScheduledAt ?? null,\n\t\t\tcontent_deleted_at: source.contentDeletedAt ?? null,\n\t\t\trevision_id: source.revisionId ?? null,\n\t\t\tcurrent_generation: ulid(),\n\t\t\tschema_version: source.schemaVersion ?? 1,\n\t\t\tsource_updated_at: source.sourceUpdatedAt ?? null,\n\t\t\tsource_version: source.sourceVersion ?? null,\n\t\t\tsource_fingerprint: source.sourceFingerprint ?? null,\n\t\t\tsource_completeness:\n\t\t\t\tsource.sourceCompleteness ?? (source.lastErrorCode ? \"failed\" : \"unknown\"),\n\t\t\tlast_attempted_at: source.lastAttemptedAt ?? now,\n\t\t\tlast_error_code: source.lastErrorCode ?? null,\n\t\t\tindexed_at: now,\n\t\t\tupdated_at: now,\n\t\t};\n\t}\n\n\tprivate attemptedSourceUpdateSet(\n\t\tsource: MediaUsageSourceInput,\n\t\trow: ReturnType<MediaUsageRepository[\"buildAttemptedSourceRow\"]>,\n\t): Updateable<MediaUsageSourceTable> {\n\t\tconst updates: Updateable<MediaUsageSourceTable> = {\n\t\t\tsource_type: row.source_type,\n\t\t\tsource_variant: row.source_variant,\n\t\t\tsource_completeness: row.source_completeness,\n\t\t\tlast_attempted_at: row.last_attempted_at,\n\t\t\tlast_error_code: row.last_error_code,\n\t\t\tupdated_at: row.updated_at,\n\t\t};\n\n\t\tif (source.collectionSlug !== undefined) updates.collection_slug = row.collection_slug;\n\t\tif (source.contentId !== undefined) updates.content_id = row.content_id;\n\t\tif (source.locale !== undefined) updates.locale = row.locale;\n\t\tif (source.translationGroup !== undefined) updates.translation_group = row.translation_group;\n\t\tif (source.contentSlug !== undefined) updates.content_slug = row.content_slug;\n\t\tif (source.contentTitle !== undefined) updates.content_title = row.content_title;\n\t\tif (source.contentStatus !== undefined) updates.content_status = row.content_status;\n\t\tif (source.contentScheduledAt !== undefined) {\n\t\t\tupdates.content_scheduled_at = row.content_scheduled_at;\n\t\t}\n\t\tif (source.contentDeletedAt !== undefined) updates.content_deleted_at = row.content_deleted_at;\n\t\tif (source.revisionId !== undefined) updates.revision_id = row.revision_id;\n\t\tif (source.schemaVersion !== undefined) updates.schema_version = row.schema_version;\n\t\tif (source.sourceUpdatedAt !== undefined) updates.source_updated_at = row.source_updated_at;\n\t\tif (source.sourceVersion !== undefined) updates.source_version = row.source_version;\n\t\tif (source.sourceFingerprint !== undefined) {\n\t\t\tupdates.source_fingerprint = row.source_fingerprint;\n\t\t}\n\n\t\treturn updates;\n\t}\n\n\tprivate sourceUpdateSet(\n\t\trow: ReturnType<MediaUsageRepository[\"buildSourceRow\"]>,\n\t): Updateable<MediaUsageSourceTable> {\n\t\treturn {\n\t\t\tsource_type: row.source_type,\n\t\t\tcollection_slug: row.collection_slug,\n\t\t\tcontent_id: row.content_id,\n\t\t\tsource_variant: row.source_variant,\n\t\t\tlocale: row.locale,\n\t\t\ttranslation_group: row.translation_group,\n\t\t\tcontent_slug: row.content_slug,\n\t\t\tcontent_title: row.content_title,\n\t\t\tcontent_status: row.content_status,\n\t\t\tcontent_scheduled_at: row.content_scheduled_at,\n\t\t\tcontent_deleted_at: row.content_deleted_at,\n\t\t\trevision_id: row.revision_id,\n\t\t\tcurrent_generation: row.current_generation,\n\t\t\tschema_version: row.schema_version,\n\t\t\tsource_updated_at: row.source_updated_at,\n\t\t\tsource_version: row.source_version,\n\t\t\tsource_fingerprint: row.source_fingerprint,\n\t\t\tsource_completeness: row.source_completeness,\n\t\t\tlast_attempted_at: row.last_attempted_at,\n\t\t\tlast_error_code: row.last_error_code,\n\t\t\tindexed_at: row.indexed_at,\n\t\t\tupdated_at: row.updated_at,\n\t\t};\n\t}\n}\n\nconst currentUsageSelect = [\n\t\"s.source_key as source_key\",\n\t\"s.source_type as source_type\",\n\t\"s.collection_slug as collection_slug\",\n\t\"s.content_id as content_id\",\n\t\"s.source_variant as source_variant\",\n\t\"s.locale as locale\",\n\t\"s.translation_group as translation_group\",\n\t\"s.content_slug as content_slug\",\n\t\"s.content_title as content_title\",\n\t\"s.content_status as content_status\",\n\t\"s.content_scheduled_at as content_scheduled_at\",\n\t\"s.content_deleted_at as content_deleted_at\",\n\t\"s.revision_id as revision_id\",\n\t\"s.current_generation as current_generation\",\n\t\"s.schema_version as schema_version\",\n\t\"s.source_updated_at as source_updated_at\",\n\t\"s.source_version as source_version\",\n\t\"s.source_fingerprint as source_fingerprint\",\n\t\"s.source_completeness as source_completeness\",\n\t\"s.last_attempted_at as last_attempted_at\",\n\t\"s.last_error_code as last_error_code\",\n\t\"s.indexed_at as indexed_at\",\n\t\"s.created_at as source_created_at\",\n\t\"s.updated_at as source_row_updated_at\",\n\t\"u.id as occurrence_id\",\n\t\"u.generation as generation\",\n\t\"u.field_slug as field_slug\",\n\t\"u.field_path as field_path\",\n\t\"u.occurrence_index as occurrence_index\",\n\t\"u.reference_type as reference_type\",\n\t\"u.media_id as media_id\",\n\t\"u.provider as provider\",\n\t\"u.provider_asset_id as provider_asset_id\",\n\t\"u.media_kind as media_kind\",\n\t\"u.mime_type as mime_type\",\n\t\"u.created_at as occurrence_created_at\",\n] as const;\n\nfunction rowToSource(row: MediaUsageSourceRow): MediaUsageSource {\n\treturn {\n\t\tsourceKey: row.source_key,\n\t\tsourceType: row.source_type,\n\t\tcollectionSlug: row.collection_slug,\n\t\tcontentId: row.content_id,\n\t\tsourceVariant: row.source_variant,\n\t\tlocale: row.locale,\n\t\ttranslationGroup: row.translation_group,\n\t\tcontentSlug: row.content_slug,\n\t\tcontentTitle: row.content_title,\n\t\tcontentStatus: row.content_status,\n\t\tcontentScheduledAt: row.content_scheduled_at,\n\t\tcontentDeletedAt: row.content_deleted_at,\n\t\trevisionId: row.revision_id,\n\t\tcurrentGeneration: row.current_generation,\n\t\tschemaVersion: Number(row.schema_version),\n\t\tsourceUpdatedAt: row.source_updated_at,\n\t\tsourceVersion: row.source_version === null ? null : Number(row.source_version),\n\t\tsourceFingerprint: row.source_fingerprint,\n\t\tsourceCompleteness: row.source_completeness,\n\t\tlastAttemptedAt: row.last_attempted_at,\n\t\tlastErrorCode: row.last_error_code,\n\t\tindexedAt: row.indexed_at,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t};\n}\n\nfunction rowToOccurrence(row: Selectable<MediaUsageTable>): MediaUsageOccurrence {\n\treturn {\n\t\tid: row.id,\n\t\tsourceKey: row.source_key,\n\t\tgeneration: row.generation,\n\t\tfieldSlug: row.field_slug,\n\t\tfieldPath: row.field_path,\n\t\toccurrenceIndex: Number(row.occurrence_index),\n\t\treferenceType: row.reference_type,\n\t\tmediaId: row.media_id,\n\t\tprovider: row.provider,\n\t\tproviderAssetId: row.provider_asset_id,\n\t\tmediaKind: row.media_kind,\n\t\tmimeType: row.mime_type,\n\t\tcreatedAt: row.created_at,\n\t};\n}\n\nfunction rowToUsageRecord(row: JoinedUsageRow): MediaUsageRecord {\n\treturn {\n\t\tsource: rowToSource({\n\t\t\tsource_key: row.source_key,\n\t\t\tsource_type: row.source_type,\n\t\t\tcollection_slug: row.collection_slug,\n\t\t\tcontent_id: row.content_id,\n\t\t\tsource_variant: row.source_variant,\n\t\t\tlocale: row.locale,\n\t\t\ttranslation_group: row.translation_group,\n\t\t\tcontent_slug: row.content_slug,\n\t\t\tcontent_title: row.content_title,\n\t\t\tcontent_status: row.content_status,\n\t\t\tcontent_scheduled_at: row.content_scheduled_at,\n\t\t\tcontent_deleted_at: row.content_deleted_at,\n\t\t\trevision_id: row.revision_id,\n\t\t\tcurrent_generation: row.current_generation,\n\t\t\tschema_version: row.schema_version,\n\t\t\tsource_updated_at: row.source_updated_at,\n\t\t\tsource_version: row.source_version,\n\t\t\tsource_fingerprint: row.source_fingerprint,\n\t\t\tsource_completeness: row.source_completeness,\n\t\t\tlast_attempted_at: row.last_attempted_at,\n\t\t\tlast_error_code: row.last_error_code,\n\t\t\tindexed_at: row.indexed_at,\n\t\t\tcreated_at: row.source_created_at,\n\t\t\tupdated_at: row.source_row_updated_at,\n\t\t}),\n\t\toccurrence: rowToOccurrence({\n\t\t\tid: row.occurrence_id,\n\t\t\tsource_key: row.source_key,\n\t\t\tgeneration: row.generation,\n\t\t\tfield_slug: row.field_slug,\n\t\t\tfield_path: row.field_path,\n\t\t\toccurrence_index: row.occurrence_index,\n\t\t\treference_type: row.reference_type,\n\t\t\tmedia_id: row.media_id,\n\t\t\tprovider: row.provider,\n\t\t\tprovider_asset_id: row.provider_asset_id,\n\t\t\tmedia_kind: row.media_kind,\n\t\t\tmime_type: row.mime_type,\n\t\t\tcreated_at: row.occurrence_created_at,\n\t\t}),\n\t};\n}\n\nfunction rowToIndexStatus(row: Selectable<MediaUsageIndexStatusTable>): MediaUsageIndexStatus {\n\treturn {\n\t\tadapterId: row.adapter_id,\n\t\tscopeType: row.scope_type,\n\t\tscopeKey: row.scope_key,\n\t\tstatus: row.status,\n\t\tschemaVersion: Number(row.schema_version),\n\t\tstartedAt: row.started_at,\n\t\tcompletedAt: row.completed_at,\n\t\tcursor: row.cursor,\n\t\tindexedSourceCount: Number(row.indexed_source_count),\n\t\tfailedSourceCount: Number(row.failed_source_count),\n\t\tlastErrorCode: row.last_error_code,\n\t\tupdatedAt: row.updated_at,\n\t};\n}\n","import type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../../database/types.js\";\nimport { validateIdentifier } from \"../../database/validate.js\";\nimport type { MediaUsageExtractionField, MediaUsageExtractionSubField } from \"./types.js\";\n\nexport type ContentMediaUsageField = MediaUsageExtractionField;\n\nexport interface ContentMediaUsageFieldDiscovery {\n\textractionFields: ContentMediaUsageField[];\n\tdisplayFieldSlugs: string[];\n}\n\nexport class MediaUsageFieldDiscoveryError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic code: \"INVALID_REPEATER_VALIDATION\",\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"MediaUsageFieldDiscoveryError\";\n\t}\n}\n\ninterface FieldDiscoveryRow {\n\tslug: string;\n\ttype: string;\n\tvalidation: string | null;\n}\n\nconst DISPLAY_FIELD_SLUGS = [\"title\", \"name\"] as const;\nconst SUPPORTED_TOP_LEVEL_TYPES = [\"file\", \"image\", \"portableText\"] as const;\n\ntype SupportedTopLevelType = (typeof SUPPORTED_TOP_LEVEL_TYPES)[number];\n\nexport async function loadContentMediaUsageFields(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n): Promise<ContentMediaUsageFieldDiscovery> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\n\tconst rows = await db\n\t\t.selectFrom(\"_emdash_fields\")\n\t\t.innerJoin(\"_emdash_collections\", \"_emdash_collections.id\", \"_emdash_fields.collection_id\")\n\t\t.select([\"_emdash_fields.slug\", \"_emdash_fields.type\", \"_emdash_fields.validation\"])\n\t\t.where(\"_emdash_collections.slug\", \"=\", collectionSlug)\n\t\t.execute();\n\n\tconst extractionFields: ContentMediaUsageField[] = [];\n\tconst rowBySlug = new Map<string, FieldDiscoveryRow>();\n\n\tfor (const row of rows) {\n\t\trowBySlug.set(row.slug, row);\n\t\tif (isSupportedTopLevelType(row.type)) {\n\t\t\tvalidateIdentifier(row.slug, \"media usage field slug\");\n\t\t\textractionFields.push({ slug: row.slug, type: row.type });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (row.type === \"repeater\") {\n\t\t\tvalidateIdentifier(row.slug, \"media usage field slug\");\n\t\t\tconst subFields = normalizeRepeaterImageSubFields(row.validation);\n\t\t\tif (subFields.length > 0) {\n\t\t\t\textractionFields.push({\n\t\t\t\t\tslug: row.slug,\n\t\t\t\t\ttype: \"repeater\",\n\t\t\t\t\tvalidation: { subFields },\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\textractionFields.sort((a, b) => a.slug.localeCompare(b.slug));\n\n\treturn {\n\t\textractionFields,\n\t\tdisplayFieldSlugs: DISPLAY_FIELD_SLUGS.filter((slug) => {\n\t\t\tif (!rowBySlug.has(slug)) return false;\n\t\t\tvalidateIdentifier(slug, \"media usage display field slug\");\n\t\t\treturn true;\n\t\t}),\n\t};\n}\n\nfunction normalizeRepeaterImageSubFields(\n\trawValidation: string | null,\n): MediaUsageExtractionSubField[] {\n\tconst validation = parseValidation(rawValidation);\n\tif (!isRecord(validation) || !Array.isArray(validation.subFields)) return [];\n\n\tconst subFields: MediaUsageExtractionSubField[] = [];\n\tfor (const subField of validation.subFields) {\n\t\tif (!isRecord(subField) || subField.type !== \"image\") continue;\n\t\tif (typeof subField.slug !== \"string\") continue;\n\t\tvalidateIdentifier(subField.slug, \"media usage repeater sub-field slug\");\n\t\tsubFields.push({ slug: subField.slug, type: \"image\" });\n\t}\n\n\treturn subFields.toSorted((a, b) => a.slug.localeCompare(b.slug));\n}\n\nfunction parseValidation(rawValidation: string | null): unknown {\n\tif (!rawValidation) return null;\n\ttry {\n\t\treturn JSON.parse(rawValidation);\n\t} catch {\n\t\tthrow new MediaUsageFieldDiscoveryError(\n\t\t\t\"Repeater field validation must be valid JSON before media usage can be discovered\",\n\t\t\t\"INVALID_REPEATER_VALIDATION\",\n\t\t);\n\t}\n}\n\nfunction isSupportedTopLevelType(value: string): value is SupportedTopLevelType {\n\treturn (SUPPORTED_TOP_LEVEL_TYPES as readonly string[]).includes(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { normalizeMime } from \"../mime.js\";\nimport { INTERNAL_MEDIA_PREFIX } from \"../normalize.js\";\nimport type {\n\tExtractedMediaUsageOccurrence,\n\tExtractMediaUsageOccurrencesInput,\n\tMediaKind,\n\tMediaUsageExtractionSubField,\n\tMediaUsageReferenceType,\n} from \"./types.js\";\n\nconst URL_LIKE_RE = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i;\n\ninterface MediaRef {\n\tmediaId: string | null;\n\tprovider: string;\n\tproviderAssetId: string;\n\tmediaKind: MediaKind | null;\n\tmimeType: string | null;\n}\n\ninterface AddOccurrenceInput {\n\tfieldSlug: string;\n\tfieldPath: string;\n\treferenceType: MediaUsageReferenceType;\n\tvalue: unknown;\n\tfallbackKind: MediaKind | null;\n}\n\nexport function extractMediaUsageOccurrences({\n\tfields,\n\tdata,\n}: ExtractMediaUsageOccurrencesInput): ExtractedMediaUsageOccurrence[] {\n\tconst occurrences: ExtractedMediaUsageOccurrence[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const field of fields) {\n\t\tconst value = data[field.slug];\n\n\t\tif (field.type === \"image\") {\n\t\t\taddOccurrence(occurrences, seen, {\n\t\t\t\tfieldSlug: field.slug,\n\t\t\t\tfieldPath: field.slug,\n\t\t\t\treferenceType: \"image_field\",\n\t\t\t\tvalue,\n\t\t\t\tfallbackKind: \"image\",\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (field.type === \"file\") {\n\t\t\taddOccurrence(occurrences, seen, {\n\t\t\t\tfieldSlug: field.slug,\n\t\t\t\tfieldPath: field.slug,\n\t\t\t\treferenceType: \"file_field\",\n\t\t\t\tvalue,\n\t\t\t\tfallbackKind: null,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (field.type === \"repeater\") {\n\t\t\textractRepeaterOccurrences(occurrences, seen, field.slug, value, field.validation?.subFields);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (field.type === \"portableText\") {\n\t\t\textractPortableTextOccurrences(occurrences, seen, field.slug, value);\n\t\t}\n\t}\n\n\treturn occurrences;\n}\n\nfunction extractRepeaterOccurrences(\n\toccurrences: ExtractedMediaUsageOccurrence[],\n\tseen: Set<string>,\n\tfieldSlug: string,\n\tvalue: unknown,\n\tsubFields: readonly MediaUsageExtractionSubField[] | undefined,\n): void {\n\tif (!Array.isArray(value) || !Array.isArray(subFields)) return;\n\n\tfor (const [itemIndex, item] of value.entries()) {\n\t\tif (!isRecord(item)) continue;\n\n\t\tfor (const subField of subFields) {\n\t\t\tif (subField.type !== \"image\") continue;\n\n\t\t\taddOccurrence(occurrences, seen, {\n\t\t\t\tfieldSlug,\n\t\t\t\tfieldPath: `${fieldSlug}[${itemIndex}].${subField.slug}`,\n\t\t\t\treferenceType: \"image_field\",\n\t\t\t\tvalue: item[subField.slug],\n\t\t\t\tfallbackKind: \"image\",\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction extractPortableTextOccurrences(\n\toccurrences: ExtractedMediaUsageOccurrence[],\n\tseen: Set<string>,\n\tfieldSlug: string,\n\tvalue: unknown,\n): void {\n\tif (!Array.isArray(value)) return;\n\n\tfor (const [blockIndex, block] of value.entries()) {\n\t\tif (!isRecord(block) || block._type !== \"image\" || !isRecord(block.asset)) continue;\n\n\t\tconst provider = normalizeProvider(block.asset.provider);\n\t\tconst ref = readPortableTextAssetRef(block.asset, provider);\n\t\tif (!ref) continue;\n\n\t\taddRefOccurrence(occurrences, seen, {\n\t\t\tfieldSlug,\n\t\t\tfieldPath: `${fieldSlug}[${blockIndex}].asset.${ref.key}`,\n\t\t\treferenceType: \"portable_text_image\",\n\t\t\tref: buildMediaRef({\n\t\t\t\tid: ref.id,\n\t\t\t\tprovider,\n\t\t\t\tmimeType: normalizeMimeValue(block.asset.mimeType),\n\t\t\t\tfallbackKind: \"image\",\n\t\t\t}),\n\t\t});\n\t}\n}\n\nfunction addOccurrence(\n\toccurrences: ExtractedMediaUsageOccurrence[],\n\tseen: Set<string>,\n\tinput: AddOccurrenceInput,\n): void {\n\tconst ref = readMediaRef(input.value, input.fallbackKind);\n\tif (!ref) return;\n\n\taddRefOccurrence(occurrences, seen, {\n\t\tfieldSlug: input.fieldSlug,\n\t\tfieldPath: input.fieldPath,\n\t\treferenceType: input.referenceType,\n\t\tref,\n\t});\n}\n\nfunction addRefOccurrence(\n\toccurrences: ExtractedMediaUsageOccurrence[],\n\tseen: Set<string>,\n\tinput: {\n\t\tfieldSlug: string;\n\t\tfieldPath: string;\n\t\treferenceType: MediaUsageReferenceType;\n\t\tref: MediaRef | null;\n\t},\n): void {\n\tif (!input.ref) return;\n\n\tconst occurrence: ExtractedMediaUsageOccurrence = {\n\t\tfieldSlug: input.fieldSlug,\n\t\tfieldPath: input.fieldPath,\n\t\toccurrenceIndex: 0,\n\t\treferenceType: input.referenceType,\n\t\tmediaId: input.ref.mediaId,\n\t\tprovider: input.ref.provider,\n\t\tproviderAssetId: input.ref.providerAssetId,\n\t\tmediaKind: input.ref.mediaKind,\n\t\tmimeType: input.ref.mimeType,\n\t};\n\n\tconst key = [\n\t\toccurrence.fieldSlug,\n\t\toccurrence.fieldPath,\n\t\toccurrence.occurrenceIndex,\n\t\toccurrence.referenceType,\n\t\toccurrence.provider,\n\t\toccurrence.providerAssetId,\n\t\toccurrence.mediaId ?? \"\",\n\t].join(\"\\0\");\n\n\tif (seen.has(key)) return;\n\tseen.add(key);\n\toccurrences.push(occurrence);\n}\n\nfunction readMediaRef(value: unknown, fallbackKind: MediaKind | null): MediaRef | null {\n\tif (typeof value === \"string\") {\n\t\tconst id = normalizeLocalMediaId(value);\n\t\treturn id ? buildMediaRef({ id, provider: \"local\", mimeType: null, fallbackKind }) : null;\n\t}\n\n\tif (!isRecord(value)) return null;\n\n\tconst provider = normalizeProvider(value.provider);\n\tconst id = provider === \"local\" ? normalizeLocalMediaId(value.id) : normalizeStableId(value.id);\n\tif (!id) return null;\n\n\treturn buildMediaRef({\n\t\tid,\n\t\tprovider,\n\t\tmimeType: normalizeMimeValue(value.mimeType),\n\t\tfallbackKind,\n\t});\n}\n\nfunction buildMediaRef(input: {\n\tid: string;\n\tprovider: string;\n\tmimeType: string | null;\n\tfallbackKind: MediaKind | null;\n}): MediaRef | null {\n\tconst provider = normalizeProvider(input.provider);\n\tif (provider === \"external\") return null;\n\n\treturn {\n\t\tmediaId: provider === \"local\" ? input.id : null,\n\t\tprovider,\n\t\tproviderAssetId: input.id,\n\t\tmediaKind: mediaKindFromMime(input.mimeType) ?? input.fallbackKind,\n\t\tmimeType: input.mimeType,\n\t};\n}\n\nfunction readPortableTextAssetRef(\n\tasset: Record<string, unknown>,\n\tprovider: string,\n): { key: \"_ref\" | \"id\"; id: string } | null {\n\tconst normalizeId = provider === \"local\" ? normalizeLocalMediaId : normalizeStableId;\n\tconst ref = normalizeId(asset._ref);\n\tif (ref) return { key: \"_ref\", id: ref };\n\n\tconst id = normalizeId(asset.id);\n\tif (id) return { key: \"id\", id };\n\n\treturn null;\n}\n\nfunction normalizeProvider(value: unknown): string {\n\tconst provider = readString(value)?.trim();\n\treturn provider || \"local\";\n}\n\nfunction normalizeLocalMediaId(value: unknown): string | null {\n\tconst id = normalizeStableId(value);\n\tif (!id) return null;\n\treturn id.includes(\"/\") ? null : id;\n}\n\nfunction normalizeStableId(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst trimmed = value.trim();\n\tif (!trimmed) return null;\n\tif (URL_LIKE_RE.test(trimmed)) return null;\n\tif (trimmed.startsWith(INTERNAL_MEDIA_PREFIX)) return null;\n\treturn trimmed;\n}\n\nfunction normalizeMimeValue(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst normalized = normalizeMime(value);\n\treturn normalized.includes(\"/\") ? normalized : null;\n}\n\nfunction mediaKindFromMime(mimeType: string | null): MediaKind | null {\n\tif (!mimeType) return null;\n\tif (mimeType.startsWith(\"image/\")) return \"image\";\n\tif (mimeType.startsWith(\"video/\")) return \"video\";\n\tif (mimeType.startsWith(\"audio/\")) return \"audio\";\n\tif (mimeType.startsWith(\"font/\") || mimeType.startsWith(\"application/font-\")) return \"font\";\n\tif (mimeType.startsWith(\"text/\")) return \"text\";\n\tif (isDocumentMime(mimeType)) return \"document\";\n\tif (isArchiveMime(mimeType)) return \"archive\";\n\treturn \"other\";\n}\n\nfunction isDocumentMime(mimeType: string): boolean {\n\treturn (\n\t\tmimeType === \"application/pdf\" ||\n\t\tmimeType === \"application/msword\" ||\n\t\tmimeType === \"application/rtf\" ||\n\t\tmimeType === \"application/vnd.ms-excel\" ||\n\t\tmimeType === \"application/vnd.ms-powerpoint\" ||\n\t\tmimeType.startsWith(\"application/vnd.openxmlformats-officedocument.\")\n\t);\n}\n\nfunction isArchiveMime(mimeType: string): boolean {\n\treturn (\n\t\tmimeType === \"application/zip\" ||\n\t\tmimeType === \"application/gzip\" ||\n\t\tmimeType === \"application/x-tar\" ||\n\t\tmimeType === \"application/x-7z-compressed\" ||\n\t\tmimeType === \"application/x-rar-compressed\" ||\n\t\tmimeType === \"application/vnd.rar\"\n\t);\n}\n\nfunction readString(value: unknown): string | null {\n\treturn typeof value === \"string\" ? value : null;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","export const MEDIA_USAGE_CONTENT_SOURCE_VARIANTS = [\"columns\", \"draft_overlay\"] as const;\n\nexport type MediaUsageContentSourceVariant = (typeof MEDIA_USAGE_CONTENT_SOURCE_VARIANTS)[number];\n\nexport interface ContentMediaUsageSourceKeyInput {\n\tcollectionSlug: string;\n\tcontentId: string;\n\tsourceVariant: MediaUsageContentSourceVariant;\n}\n\nexport function isMediaUsageContentSourceVariant(\n\tvalue: unknown,\n): value is MediaUsageContentSourceVariant {\n\treturn (\n\t\ttypeof value === \"string\" &&\n\t\t(MEDIA_USAGE_CONTENT_SOURCE_VARIANTS as readonly string[]).includes(value)\n\t);\n}\n\nexport function buildContentMediaUsageSourceKey(input: ContentMediaUsageSourceKeyInput): string {\n\treturn `content:${input.collectionSlug}:${input.contentId}:${input.sourceVariant}`;\n}\n","import { sql, type Kysely } from \"kysely\";\n\nimport type {\n\tMediaUsageOccurrenceInput,\n\tMediaUsageSourceInput,\n} from \"../../database/repositories/media-usage.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { validateIdentifier } from \"../../database/validate.js\";\nimport { hashString } from \"../../utils/hash.js\";\nimport {\n\tloadContentMediaUsageFields,\n\ttype ContentMediaUsageField,\n\ttype ContentMediaUsageFieldDiscovery,\n} from \"./content-fields.js\";\nimport { extractMediaUsageOccurrences } from \"./extractor.js\";\nimport {\n\tbuildContentMediaUsageSourceKey,\n\ttype MediaUsageContentSourceVariant,\n} from \"./source-key.js\";\n\nexport const CONTENT_SOURCE_SCHEMA_VERSION = 1;\n\nconst CONTENT_SYSTEM_COLUMNS = [\n\t\"id\",\n\t\"slug\",\n\t\"status\",\n\t\"created_at\",\n\t\"updated_at\",\n\t\"published_at\",\n\t\"scheduled_at\",\n\t\"deleted_at\",\n\t\"version\",\n\t\"live_revision_id\",\n\t\"draft_revision_id\",\n\t\"locale\",\n\t\"translation_group\",\n] as const;\n\nexport type LoadContentMediaUsageSnapshotsResult =\n\t| { success: true; snapshots: ContentMediaUsageSnapshot[] }\n\t| {\n\t\t\tsuccess: false;\n\t\t\terror:\n\t\t\t\t| \"CONTENT_NOT_FOUND\"\n\t\t\t\t| \"DRAFT_REVISION_NOT_FOUND\"\n\t\t\t\t| \"DRAFT_REVISION_MISMATCH\"\n\t\t\t\t| \"DRAFT_REVISION_INVALID\";\n\t\t\tsource?: MediaUsageSourceInput;\n\t };\n\nexport interface ContentMediaUsageSnapshot {\n\tsource: MediaUsageSourceInput;\n\toccurrences: MediaUsageOccurrenceInput[];\n\tfields: readonly ContentMediaUsageField[];\n}\n\nexport async function loadContentMediaUsageSnapshots(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentId: string,\n\tfieldDiscovery?: ContentMediaUsageFieldDiscovery,\n): Promise<LoadContentMediaUsageSnapshotsResult> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\tconst discovery = fieldDiscovery ?? (await loadContentMediaUsageFields(db, collectionSlug));\n\tconst row = await loadContentRow(db, collectionSlug, contentId, [\n\t\t...discovery.extractionFields.map((field) => field.slug),\n\t\t...discovery.displayFieldSlugs,\n\t]);\n\n\tif (!row) return { success: false, error: \"CONTENT_NOT_FOUND\" };\n\n\tconst columnsData = projectData(\n\t\trow,\n\t\tdiscovery.extractionFields.map((field) => field.slug),\n\t);\n\tconst displayData = projectRawData(row, discovery.displayFieldSlugs);\n\tconst occurrences = extractMediaUsageOccurrences({\n\t\tfields: discovery.extractionFields,\n\t\tdata: columnsData,\n\t});\n\tconst columnsRevisionId = readNullableString(row.live_revision_id);\n\tconst columnsFingerprint = await buildSourceFingerprint({\n\t\tcollectionSlug,\n\t\tsourceVariant: \"columns\",\n\t\trevisionId: columnsRevisionId,\n\t\tfields: discovery.extractionFields,\n\t\tdata: columnsData,\n\t});\n\tconst snapshots: ContentMediaUsageSnapshot[] = [\n\t\t{\n\t\t\tsource: buildContentSource({\n\t\t\t\tcollectionSlug,\n\t\t\t\trow,\n\t\t\t\tdisplayData,\n\t\t\t\tsourceVariant: \"columns\",\n\t\t\t\trevisionId: columnsRevisionId,\n\t\t\t\tsourceFingerprint: columnsFingerprint,\n\t\t\t}),\n\t\t\toccurrences,\n\t\t\tfields: discovery.extractionFields,\n\t\t},\n\t];\n\n\tconst draftRevisionId = readNullableString(row.draft_revision_id);\n\tif (draftRevisionId) {\n\t\tconst attemptedDraftSource = buildContentSource({\n\t\t\tcollectionSlug,\n\t\t\trow,\n\t\t\tdisplayData,\n\t\t\tsourceVariant: \"draft_overlay\",\n\t\t\trevisionId: draftRevisionId,\n\t\t});\n\t\tconst revisionResult = await loadRevisionRow(db, draftRevisionId);\n\t\tif (!revisionResult) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"DRAFT_REVISION_NOT_FOUND\",\n\t\t\t\tsource: attemptedDraftSource,\n\t\t\t};\n\t\t}\n\t\tif (!revisionResult.success) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"DRAFT_REVISION_INVALID\",\n\t\t\t\tsource: attemptedDraftSource,\n\t\t\t};\n\t\t}\n\t\tconst revision = revisionResult.revision;\n\t\tif (revision.collection !== collectionSlug || revision.entryId !== row.id) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"DRAFT_REVISION_MISMATCH\",\n\t\t\t\tsource: attemptedDraftSource,\n\t\t\t};\n\t\t}\n\n\t\tconst revisionData = stripRevisionMetadata(revision.data);\n\t\tconst draftOverlayData = { ...columnsData, ...revisionData };\n\t\tconst draftDisplayData = {\n\t\t\t...displayData,\n\t\t\t...projectPresentData(revisionData, discovery.displayFieldSlugs),\n\t\t};\n\t\tconst draftContentSlug =\n\t\t\treadNullableString(revision.data._slug) ?? readNullableString(row.slug);\n\t\tconst draftFingerprint = await buildSourceFingerprint({\n\t\t\tcollectionSlug,\n\t\t\tsourceVariant: \"draft_overlay\",\n\t\t\trevisionId: draftRevisionId,\n\t\t\tfields: discovery.extractionFields,\n\t\t\tdata: draftOverlayData,\n\t\t});\n\t\tsnapshots.push({\n\t\t\tsource: buildContentSource({\n\t\t\t\tcollectionSlug,\n\t\t\t\trow,\n\t\t\t\tdisplayData: draftDisplayData,\n\t\t\t\tsourceVariant: \"draft_overlay\",\n\t\t\t\trevisionId: draftRevisionId,\n\t\t\t\tcontentSlug: draftContentSlug,\n\t\t\t\tsourceFingerprint: draftFingerprint,\n\t\t\t}),\n\t\t\toccurrences: extractMediaUsageOccurrences({\n\t\t\t\tfields: discovery.extractionFields,\n\t\t\t\tdata: draftOverlayData,\n\t\t\t}),\n\t\t\tfields: discovery.extractionFields,\n\t\t});\n\t}\n\n\treturn {\n\t\tsuccess: true,\n\t\tsnapshots,\n\t};\n}\n\ninterface RevisionSnapshotRow {\n\tid: string;\n\tcollection: string;\n\tentryId: string;\n\tdata: Record<string, unknown>;\n}\n\nasync function loadContentRow(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentId: string,\n\tfieldSlugs: readonly string[],\n): Promise<Record<string, unknown> | null> {\n\tconst tableName = getContentTableName(collectionSlug);\n\tconst columns = uniqueColumns([...CONTENT_SYSTEM_COLUMNS, ...fieldSlugs]);\n\tconst columnRefs = columns.map((column) => sql.ref(column));\n\tconst result = await sql<Record<string, unknown>>`\n\t\tSELECT ${sql.join(columnRefs, sql`, `)}\n\t\tFROM ${sql.ref(tableName)}\n\t\tWHERE id = ${contentId}\n\t\tLIMIT 1\n\t`.execute(db);\n\n\treturn result.rows[0] ?? null;\n}\n\nasync function loadRevisionRow(\n\tdb: Kysely<Database>,\n\trevisionId: string,\n): Promise<{ success: true; revision: RevisionSnapshotRow } | { success: false } | null> {\n\tconst row = await db\n\t\t.selectFrom(\"revisions\")\n\t\t.select([\"id\", \"collection\", \"entry_id\", \"data\"])\n\t\t.where(\"id\", \"=\", revisionId)\n\t\t.executeTakeFirst();\n\tif (!row) return null;\n\tconst data = parseRevisionData(row.data);\n\tif (!data) return { success: false };\n\treturn {\n\t\tsuccess: true,\n\t\trevision: {\n\t\t\tid: row.id,\n\t\t\tcollection: row.collection,\n\t\t\tentryId: row.entry_id,\n\t\t\tdata,\n\t\t},\n\t};\n}\n\nfunction buildContentSource(input: {\n\tcollectionSlug: string;\n\trow: Record<string, unknown>;\n\tdisplayData: Record<string, unknown>;\n\tsourceVariant: MediaUsageContentSourceVariant;\n\trevisionId: string | null;\n\tcontentSlug?: string | null;\n\tsourceFingerprint?: string | null;\n}): MediaUsageSourceInput {\n\tconst { collectionSlug, row, displayData, sourceVariant, revisionId } = input;\n\tconst contentId = readString(row.id) ?? \"\";\n\tconst contentSlug = input.contentSlug ?? readNullableString(row.slug);\n\tconst source: MediaUsageSourceInput = {\n\t\tsourceKey: buildContentMediaUsageSourceKey({\n\t\t\tcollectionSlug,\n\t\t\tcontentId,\n\t\t\tsourceVariant,\n\t\t}),\n\t\tsourceType: \"content\",\n\t\tcollectionSlug,\n\t\tcontentId,\n\t\tsourceVariant,\n\t\tlocale: readNullableString(row.locale),\n\t\ttranslationGroup: readNullableString(row.translation_group),\n\t\tcontentSlug,\n\t\tcontentTitle: deriveContentTitle(displayData, contentSlug, contentId),\n\t\tcontentStatus: readNullableString(row.status),\n\t\tcontentScheduledAt: readNullableString(row.scheduled_at),\n\t\tcontentDeletedAt: readNullableString(row.deleted_at),\n\t\trevisionId,\n\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\tsourceUpdatedAt: readNullableString(row.updated_at),\n\t\tsourceVersion: readNumber(row.version),\n\t};\n\tif (input.sourceFingerprint !== undefined) source.sourceFingerprint = input.sourceFingerprint;\n\treturn source;\n}\n\nasync function buildSourceFingerprint(input: {\n\tcollectionSlug: string;\n\tsourceVariant: MediaUsageContentSourceVariant;\n\trevisionId: string | null;\n\tfields: readonly ContentMediaUsageField[];\n\tdata: Record<string, unknown>;\n}): Promise<string> {\n\treturn hashString(\n\t\tcanonicalJson({\n\t\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\t\tcollectionSlug: input.collectionSlug,\n\t\t\tsourceVariant: input.sourceVariant,\n\t\t\tfields: normalizeFingerprintFields(input.fields),\n\t\t\tvalues: projectFingerprintData(input.data, input.fields),\n\t\t\trevisionId: input.sourceVariant === \"draft_overlay\" ? input.revisionId : null,\n\t\t}),\n\t);\n}\n\nfunction normalizeFingerprintFields(\n\tfields: readonly ContentMediaUsageField[],\n): Record<string, unknown>[] {\n\treturn fields\n\t\t.map((field) => {\n\t\t\tif (field.type !== \"repeater\") return { slug: field.slug, type: field.type };\n\t\t\treturn {\n\t\t\t\tslug: field.slug,\n\t\t\t\ttype: field.type,\n\t\t\t\tsubFields: (field.validation?.subFields ?? [])\n\t\t\t\t\t.map((subField) => ({ slug: subField.slug, type: subField.type }))\n\t\t\t\t\t.toSorted((a, b) => a.slug.localeCompare(b.slug)),\n\t\t\t};\n\t\t})\n\t\t.toSorted((a, b) => String(a.slug).localeCompare(String(b.slug)));\n}\n\nfunction projectFingerprintData(\n\tdata: Record<string, unknown>,\n\tfields: readonly ContentMediaUsageField[],\n): Record<string, unknown> {\n\tconst projected: Record<string, unknown> = {};\n\tfor (const field of fields) {\n\t\tprojected[field.slug] = Object.hasOwn(data, field.slug) ? data[field.slug] : null;\n\t}\n\treturn projected;\n}\n\nfunction canonicalJson(value: unknown): string {\n\treturn JSON.stringify(canonicalize(value));\n}\n\nfunction canonicalize(value: unknown): unknown {\n\tif (value === undefined) return null;\n\tif (typeof value === \"bigint\") return value.toString();\n\tif (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n\tif (Array.isArray(value)) return value.map((item) => canonicalize(item));\n\tif (!isRecord(value)) return value;\n\n\tconst canonical: Record<string, unknown> = {};\n\tfor (const key of Object.keys(value).toSorted()) {\n\t\tcanonical[key] = canonicalize(value[key]);\n\t}\n\treturn canonical;\n}\n\nfunction projectData(\n\trow: Record<string, unknown>,\n\tfieldSlugs: readonly string[],\n): Record<string, unknown> {\n\tconst data: Record<string, unknown> = {};\n\tfor (const fieldSlug of fieldSlugs) {\n\t\tdata[fieldSlug] = deserializeValue(row[fieldSlug] ?? null);\n\t}\n\treturn data;\n}\n\nfunction projectRawData(\n\trow: Record<string, unknown>,\n\tfieldSlugs: readonly string[],\n): Record<string, unknown> {\n\tconst data: Record<string, unknown> = {};\n\tfor (const fieldSlug of fieldSlugs) {\n\t\tdata[fieldSlug] = row[fieldSlug] ?? null;\n\t}\n\treturn data;\n}\n\nfunction projectPresentData(\n\trow: Record<string, unknown>,\n\tfieldSlugs: readonly string[],\n): Record<string, unknown> {\n\tconst data: Record<string, unknown> = {};\n\tfor (const fieldSlug of fieldSlugs) {\n\t\tif (Object.hasOwn(row, fieldSlug)) data[fieldSlug] = row[fieldSlug];\n\t}\n\treturn data;\n}\n\nfunction uniqueColumns(columns: readonly string[]): string[] {\n\tconst unique = [...new Set(columns)];\n\tfor (const column of unique) validateIdentifier(column, \"content media usage column\");\n\treturn unique;\n}\n\nfunction getContentTableName(collectionSlug: string): string {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\treturn `ec_${collectionSlug}`;\n}\n\nfunction deserializeValue(value: unknown): unknown {\n\tif (typeof value === \"string\" && (value.startsWith(\"{\") || value.startsWith(\"[\"))) {\n\t\ttry {\n\t\t\treturn JSON.parse(value);\n\t\t} catch {\n\t\t\treturn value;\n\t\t}\n\t}\n\treturn value;\n}\n\nfunction parseRevisionData(value: unknown): Record<string, unknown> | null {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(value);\n\t\t\treturn isRecord(parsed) ? parsed : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn isRecord(value) ? value : null;\n}\n\nfunction stripRevisionMetadata(data: Record<string, unknown>): Record<string, unknown> {\n\tconst stripped: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (!key.startsWith(\"_\")) stripped[key] = value;\n\t}\n\treturn stripped;\n}\n\nfunction deriveContentTitle(\n\tdisplayData: Record<string, unknown>,\n\tcontentSlug: string | null,\n\tcontentId: string,\n): string | null {\n\tfor (const fieldSlug of [\"title\", \"name\"] as const) {\n\t\tconst value = displayData[fieldSlug];\n\t\tif (typeof value === \"string\" && value.trim()) return value;\n\t}\n\treturn contentSlug ?? contentId;\n}\n\nfunction readString(value: unknown): string | null {\n\treturn typeof value === \"string\" ? value : null;\n}\n\nfunction readNullableString(value: unknown): string | null {\n\treturn value === null || value === undefined ? null : readString(value);\n}\n\nfunction readNumber(value: unknown): number | null {\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\tif (typeof value === \"bigint\") return Number(value);\n\tif (typeof value === \"string\" && value) {\n\t\tconst parsed = Number(value);\n\t\treturn Number.isFinite(parsed) ? parsed : null;\n\t}\n\treturn null;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { sql, type Kysely } from \"kysely\";\n\nimport { MediaUsageRepository } from \"../../database/repositories/media-usage.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { validateIdentifier } from \"../../database/validate.js\";\nimport { isI18nEnabled } from \"../../i18n/config.js\";\nimport { loadContentMediaUsageFields } from \"./content-fields.js\";\nimport {\n\tCONTENT_SOURCE_SCHEMA_VERSION,\n\tloadContentMediaUsageSnapshots,\n} from \"./content-snapshots.js\";\nimport {\n\tbuildContentMediaUsageSourceKey,\n\tMEDIA_USAGE_CONTENT_SOURCE_VARIANTS,\n} from \"./source-key.js\";\n\nexport const CONTENT_MEDIA_USAGE_ADAPTER_ID = \"content-media\";\nexport const CONTENT_MEDIA_USAGE_COLLECTION_SCOPE = \"collection\";\n\nconst CONTENT_USAGE_LOCKS_KEY = Symbol.for(\"emdash.mediaUsage.contentLocks\");\nconst CONTENT_USAGE_COLLECTION_LOCKS_KEY = Symbol.for(\"emdash.mediaUsage.collectionLocks\");\nconst CONTENT_USAGE_REFRESH_MAX_ATTEMPTS = 2;\n\n// These maps only de-dupe usage work inside the current isolate/process. Cross-worker\n// correctness comes from expected-generation guards on repository writes.\n\nexport type ContentMediaUsageRefreshErrorCode =\n\t| \"CONTENT_NOT_FOUND\"\n\t| \"DRAFT_REVISION_NOT_FOUND\"\n\t| \"DRAFT_REVISION_MISMATCH\"\n\t| \"DRAFT_REVISION_INVALID\"\n\t| \"CONTENT_USAGE_REFRESH_ERROR\"\n\t| \"CONTENT_USAGE_DELETE_ERROR\"\n\t| \"CONTENT_USAGE_GENERATION_CONFLICT\"\n\t| \"CONTENT_USAGE_STALE\";\n\nexport interface ContentMediaUsageRefreshResult {\n\tsuccess: boolean;\n\trefreshedSourceCount: number;\n\tdeletedSourceCount: number;\n\tfailedSourceCount: number;\n\terrorCode?: ContentMediaUsageRefreshErrorCode;\n}\n\nconst ZERO_RESULT: ContentMediaUsageRefreshResult = {\n\tsuccess: true,\n\trefreshedSourceCount: 0,\n\tdeletedSourceCount: 0,\n\tfailedSourceCount: 0,\n};\n\nexport async function refreshContentMediaUsage(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentId: string,\n): Promise<ContentMediaUsageRefreshResult> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\treturn withContentUsageCollectionLock(collectionSlug, () =>\n\t\twithContentUsageLock(collectionSlug, contentId, () =>\n\t\t\trefreshContentMediaUsageUnlocked(db, collectionSlug, contentId),\n\t\t),\n\t);\n}\n\nasync function refreshContentMediaUsageUnlocked(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentId: string,\n): Promise<ContentMediaUsageRefreshResult> {\n\ttry {\n\t\tlet conflictResult: ContentMediaUsageRefreshResult | null = null;\n\t\tfor (let attempt = 0; attempt < CONTENT_USAGE_REFRESH_MAX_ATTEMPTS; attempt++) {\n\t\t\tconst result = await refreshContentMediaUsageAttempt(db, collectionSlug, contentId);\n\t\t\tif (result.errorCode !== \"CONTENT_USAGE_GENERATION_CONFLICT\") return result;\n\t\t\tconflictResult = result;\n\t\t}\n\n\t\treturn markGenerationConflict(db, collectionSlug, {\n\t\t\trefreshedSourceCount: conflictResult?.refreshedSourceCount ?? 0,\n\t\t\tdeletedSourceCount: conflictResult?.deletedSourceCount ?? 0,\n\t\t});\n\t} catch (error) {\n\t\tconsole.error(`[media-usage] Failed to refresh ${collectionSlug}/${contentId}:`, error);\n\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\tdb,\n\t\t\tcollectionSlug,\n\t\t\t\"CONTENT_USAGE_REFRESH_ERROR\",\n\t\t);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\trefreshedSourceCount: 0,\n\t\t\tdeletedSourceCount: 0,\n\t\t\tfailedSourceCount: 0,\n\t\t\terrorCode: \"CONTENT_USAGE_REFRESH_ERROR\",\n\t\t};\n\t}\n}\n\nasync function refreshContentMediaUsageAttempt(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentId: string,\n): Promise<ContentMediaUsageRefreshResult> {\n\tconst repo = new MediaUsageRepository(db);\n\tconst observedGenerations = await loadObservedContentSourceGenerations(\n\t\tdb,\n\t\tcollectionSlug,\n\t\tcontentId,\n\t);\n\tconst snapshotsResult = await loadContentMediaUsageSnapshots(db, collectionSlug, contentId);\n\tif (!snapshotsResult.success) {\n\t\treturn markSnapshotFailure(db, collectionSlug, snapshotsResult);\n\t}\n\n\tif (!(await contentCollectionExists(db, collectionSlug))) {\n\t\tconst deletedSourceCount = await repo.deleteContentSources(collectionSlug, contentId);\n\t\treturn { ...ZERO_RESULT, deletedSourceCount };\n\t}\n\n\tlet refreshedSourceCount = 0;\n\tfor (const snapshot of snapshotsResult.snapshots) {\n\t\tconst result = await repo.replaceSourceIfCurrent(\n\t\t\tsnapshot.source,\n\t\t\tsnapshot.occurrences,\n\t\t\tobservedGenerations.get(snapshot.source.sourceKey) ?? null,\n\t\t);\n\t\tif (!result.replaced) {\n\t\t\treturn generationConflictResult({\n\t\t\t\trefreshedSourceCount,\n\t\t\t\tdeletedSourceCount: 0,\n\t\t\t});\n\t\t}\n\t\trefreshedSourceCount++;\n\t}\n\tif (!(await contentCollectionExists(db, collectionSlug))) {\n\t\tconst deletedSourceCount = await repo.deleteContentSources(collectionSlug, contentId);\n\t\treturn { ...ZERO_RESULT, deletedSourceCount };\n\t}\n\n\tconst expectedSourceKeys = new Set(\n\t\tsnapshotsResult.snapshots.map((snapshot) => snapshot.source.sourceKey),\n\t);\n\tconst absentSourceKeys = MEDIA_USAGE_CONTENT_SOURCE_VARIANTS.map((sourceVariant) =>\n\t\tbuildContentMediaUsageSourceKey({ collectionSlug, contentId, sourceVariant }),\n\t).filter((sourceKey) => !expectedSourceKeys.has(sourceKey));\n\tlet deletedSourceCount = 0;\n\tfor (const sourceKey of absentSourceKeys) {\n\t\tconst expectedGeneration = observedGenerations.get(sourceKey) ?? null;\n\t\tif (expectedGeneration === null) continue;\n\n\t\tconst result = await repo.deleteSourceIfCurrent(sourceKey, expectedGeneration);\n\t\tif (result.deleted) {\n\t\t\tdeletedSourceCount++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (result.source) {\n\t\t\treturn generationConflictResult({\n\t\t\t\trefreshedSourceCount,\n\t\t\t\tdeletedSourceCount,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn {\n\t\tsuccess: true,\n\t\trefreshedSourceCount,\n\t\tdeletedSourceCount,\n\t\tfailedSourceCount: 0,\n\t};\n}\n\nasync function loadObservedContentSourceGenerations(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentId: string,\n): Promise<Map<string, string | null>> {\n\tconst generations = new Map<string, string | null>();\n\tconst sourceKeys = MEDIA_USAGE_CONTENT_SOURCE_VARIANTS.map((sourceVariant) =>\n\t\tbuildContentMediaUsageSourceKey({\n\t\t\tcollectionSlug,\n\t\t\tcontentId,\n\t\t\tsourceVariant,\n\t\t}),\n\t);\n\tfor (const sourceKey of sourceKeys) {\n\t\tgenerations.set(sourceKey, null);\n\t}\n\n\tconst rows = await db\n\t\t.selectFrom(\"_emdash_media_usage_sources\")\n\t\t.select([\"source_key\", \"current_generation\"])\n\t\t.where(\"source_key\", \"in\", sourceKeys)\n\t\t.execute();\n\tfor (const row of rows) {\n\t\tgenerations.set(row.source_key, row.current_generation);\n\t}\n\n\treturn generations;\n}\n\nasync function markGenerationConflict(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcounts: Pick<ContentMediaUsageRefreshResult, \"refreshedSourceCount\" | \"deletedSourceCount\">,\n): Promise<ContentMediaUsageRefreshResult> {\n\tawait markContentMediaUsageCollectionStaleSafely(\n\t\tdb,\n\t\tcollectionSlug,\n\t\t\"CONTENT_USAGE_GENERATION_CONFLICT\",\n\t);\n\treturn {\n\t\tsuccess: false,\n\t\trefreshedSourceCount: counts.refreshedSourceCount,\n\t\tdeletedSourceCount: counts.deletedSourceCount,\n\t\tfailedSourceCount: 0,\n\t\terrorCode: \"CONTENT_USAGE_GENERATION_CONFLICT\",\n\t};\n}\n\nfunction generationConflictResult(\n\tcounts: Pick<ContentMediaUsageRefreshResult, \"refreshedSourceCount\" | \"deletedSourceCount\">,\n): ContentMediaUsageRefreshResult {\n\treturn {\n\t\tsuccess: false,\n\t\trefreshedSourceCount: counts.refreshedSourceCount,\n\t\tdeletedSourceCount: counts.deletedSourceCount,\n\t\tfailedSourceCount: 0,\n\t\terrorCode: \"CONTENT_USAGE_GENERATION_CONFLICT\",\n\t};\n}\n\nasync function contentCollectionExists(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select(\"id\")\n\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t.executeTakeFirst();\n\treturn row !== undefined;\n}\n\nexport async function deleteContentMediaUsage(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentId: string,\n): Promise<ContentMediaUsageRefreshResult> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\treturn withContentUsageCollectionLock(collectionSlug, () =>\n\t\twithContentUsageLock(collectionSlug, contentId, () =>\n\t\t\tdeleteContentMediaUsageUnlocked(db, collectionSlug, contentId),\n\t\t),\n\t);\n}\n\nasync function deleteContentMediaUsageUnlocked(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentId: string,\n): Promise<ContentMediaUsageRefreshResult> {\n\ttry {\n\t\tconst deletedSourceCount = await new MediaUsageRepository(db).deleteContentSources(\n\t\t\tcollectionSlug,\n\t\t\tcontentId,\n\t\t);\n\t\treturn { ...ZERO_RESULT, deletedSourceCount };\n\t} catch (error) {\n\t\tconsole.error(\n\t\t\t`[media-usage] Failed to delete usage for ${collectionSlug}/${contentId}:`,\n\t\t\terror,\n\t\t);\n\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\tdb,\n\t\t\tcollectionSlug,\n\t\t\t\"CONTENT_USAGE_DELETE_ERROR\",\n\t\t);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\trefreshedSourceCount: 0,\n\t\t\tdeletedSourceCount: 0,\n\t\t\tfailedSourceCount: 0,\n\t\t\terrorCode: \"CONTENT_USAGE_DELETE_ERROR\",\n\t\t};\n\t}\n}\n\nexport async function deleteContentMediaUsageCollection(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n): Promise<ContentMediaUsageRefreshResult> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\treturn withContentUsageCollectionLock(collectionSlug, () =>\n\t\tdeleteContentMediaUsageCollectionUnlocked(db, collectionSlug),\n\t);\n}\n\nasync function deleteContentMediaUsageCollectionUnlocked(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n): Promise<ContentMediaUsageRefreshResult> {\n\ttry {\n\t\tconst repo = new MediaUsageRepository(db);\n\t\tconst deletedSourceCount = await repo.deleteCollectionSources(collectionSlug);\n\t\tawait repo.deleteIndexStatus({\n\t\t\tadapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,\n\t\t\tscopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n\t\t\tscopeKey: collectionSlug,\n\t\t});\n\t\treturn { ...ZERO_RESULT, deletedSourceCount };\n\t} catch (error) {\n\t\tconsole.error(`[media-usage] Failed to delete usage for collection ${collectionSlug}:`, error);\n\t\ttry {\n\t\t\tawait new MediaUsageRepository(db).deleteIndexStatus({\n\t\t\t\tadapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,\n\t\t\t\tscopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n\t\t\t\tscopeKey: collectionSlug,\n\t\t\t});\n\t\t} catch (statusError) {\n\t\t\tconsole.error(\n\t\t\t\t`[media-usage] Failed to clear usage status for deleted collection ${collectionSlug}:`,\n\t\t\t\tstatusError,\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\trefreshedSourceCount: 0,\n\t\t\tdeletedSourceCount: 0,\n\t\t\tfailedSourceCount: 0,\n\t\t\terrorCode: \"CONTENT_USAGE_DELETE_ERROR\",\n\t\t};\n\t}\n}\n\nexport async function refreshContentMediaUsageAfterWrite(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentId: string,\n): Promise<void> {\n\tconst result = await refreshContentMediaUsage(db, collectionSlug, contentId);\n\tif (!result.success) {\n\t\tconsole.error(\n\t\t\t`[media-usage] Usage refresh for ${collectionSlug}/${contentId} finished with ${result.errorCode}`,\n\t\t);\n\t}\n}\n\nexport async function markContentMediaUsageCollectionStale(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tlastErrorCode: string,\n): Promise<void> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\tconst repo = new MediaUsageRepository(db);\n\tconst identity = {\n\t\tadapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,\n\t\tscopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n\t\tscopeKey: collectionSlug,\n\t};\n\tconst existing = await repo.findIndexStatus(identity);\n\tawait repo.upsertIndexStatus({\n\t\t...identity,\n\t\tstatus: \"stale\",\n\t\tschemaVersion: existing?.schemaVersion ?? CONTENT_SOURCE_SCHEMA_VERSION,\n\t\tstartedAt: existing?.startedAt ?? null,\n\t\tcompletedAt: existing?.completedAt ?? null,\n\t\tcursor: existing?.cursor ?? null,\n\t\tindexedSourceCount: existing?.indexedSourceCount ?? 0,\n\t\tfailedSourceCount: existing?.failedSourceCount ?? 0,\n\t\tlastErrorCode,\n\t});\n}\n\nexport async function findNonTranslatableSiblingContentIds(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tupdatedContentId: string,\n\ttranslationGroup: string | null | undefined,\n\tupdatedData: Record<string, unknown> | undefined,\n): Promise<string[]> {\n\tif (!isI18nEnabled() || !updatedData || !translationGroup) return [];\n\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\tconst collection = await db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select(\"id\")\n\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t.executeTakeFirst();\n\tif (!collection) return [];\n\n\tconst fields = await db\n\t\t.selectFrom(\"_emdash_fields\")\n\t\t.select(\"slug\")\n\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t.where(\"translatable\", \"=\", 0)\n\t\t.execute();\n\n\tconst touchedNonTranslatableSlugs = fields\n\t\t.filter((field) => field.slug in updatedData)\n\t\t.map((field) => field.slug);\n\tif (touchedNonTranslatableSlugs.length === 0) return [];\n\n\tconst usageFields = await loadContentMediaUsageFields(db, collectionSlug);\n\tconst usageRelevantSlugs = new Set([\n\t\t...usageFields.extractionFields.map((field) => field.slug),\n\t\t...usageFields.displayFieldSlugs,\n\t]);\n\tif (!touchedNonTranslatableSlugs.some((slug) => usageRelevantSlugs.has(slug))) return [];\n\n\tconst tableName = `ec_${collectionSlug}`;\n\tconst rows = await sql<{ id: string }>`\n\t\tSELECT id\n\t\tFROM ${sql.ref(tableName)}\n\t\tWHERE translation_group = ${translationGroup}\n\t\tAND id != ${updatedContentId}\n\t\tORDER BY id ASC\n\t`.execute(db);\n\n\treturn rows.rows.map((row) => row.id);\n}\n\nasync function markSnapshotFailure(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tresult: Exclude<Awaited<ReturnType<typeof loadContentMediaUsageSnapshots>>, { success: true }>,\n): Promise<ContentMediaUsageRefreshResult> {\n\tconst repo = new MediaUsageRepository(db);\n\tif (result.source) {\n\t\tawait repo.markSourceAttempted({\n\t\t\t...result.source,\n\t\t\tsourceCompleteness: \"failed\",\n\t\t\tlastErrorCode: result.error,\n\t\t});\n\t}\n\tawait markContentMediaUsageCollectionStale(db, collectionSlug, result.error);\n\treturn {\n\t\tsuccess: false,\n\t\trefreshedSourceCount: 0,\n\t\tdeletedSourceCount: 0,\n\t\tfailedSourceCount: result.source ? 1 : 0,\n\t\terrorCode: result.error,\n\t};\n}\n\nexport async function markContentMediaUsageCollectionStaleSafely(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tlastErrorCode: ContentMediaUsageRefreshErrorCode,\n): Promise<boolean> {\n\ttry {\n\t\tawait markContentMediaUsageCollectionStale(db, collectionSlug, lastErrorCode);\n\t\treturn true;\n\t} catch (error) {\n\t\tconsole.error(`[media-usage] Failed to mark ${collectionSlug} stale:`, error);\n\t\treturn false;\n\t}\n}\n\nasync function withContentUsageLock<T>(\n\tcollectionSlug: string,\n\tcontentId: string,\n\tfn: () => Promise<T>,\n): Promise<T> {\n\tconst locks = getContentUsageLocks();\n\tconst lockKey = `${collectionSlug}\\0${contentId}`;\n\tconst previous = locks.get(lockKey) ?? Promise.resolve();\n\tlet releaseCurrent!: () => void;\n\tconst current = new Promise<void>((resolve) => {\n\t\treleaseCurrent = resolve;\n\t});\n\tconst next = previous.catch(() => {}).then(() => current);\n\tlocks.set(lockKey, next);\n\n\ttry {\n\t\tawait previous.catch(() => {});\n\t\treturn await fn();\n\t} finally {\n\t\treleaseCurrent();\n\t\tif (locks.get(lockKey) === next) locks.delete(lockKey);\n\t}\n}\n\nexport async function withContentUsageCollectionLock<T>(\n\tcollectionSlug: string,\n\tfn: () => Promise<T>,\n): Promise<T> {\n\t// Coarse by design: row refreshes and collection source deletes must not interleave.\n\tconst locks = getContentUsageCollectionLocks();\n\tconst previous = locks.get(collectionSlug) ?? Promise.resolve();\n\tlet releaseCurrent!: () => void;\n\tconst current = new Promise<void>((resolve) => {\n\t\treleaseCurrent = resolve;\n\t});\n\tconst next = previous.catch(() => {}).then(() => current);\n\tlocks.set(collectionSlug, next);\n\n\ttry {\n\t\tawait previous.catch(() => {});\n\t\treturn await fn();\n\t} finally {\n\t\treleaseCurrent();\n\t\tif (locks.get(collectionSlug) === next) locks.delete(collectionSlug);\n\t}\n}\n\nfunction getContentUsageLocks(): Map<string, Promise<void>> {\n\tconst global = globalThis as typeof globalThis & Record<symbol, unknown>;\n\tconst existing = global[CONTENT_USAGE_LOCKS_KEY];\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis symbol slot stores only this map\n\tif (existing instanceof Map) return existing as Map<string, Promise<void>>;\n\tconst locks = new Map<string, Promise<void>>();\n\tglobal[CONTENT_USAGE_LOCKS_KEY] = locks;\n\treturn locks;\n}\n\nfunction getContentUsageCollectionLocks(): Map<string, Promise<void>> {\n\tconst global = globalThis as typeof globalThis & Record<symbol, unknown>;\n\tconst existing = global[CONTENT_USAGE_COLLECTION_LOCKS_KEY];\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis symbol slot stores only this map\n\tif (existing instanceof Map) return existing as Map<string, Promise<void>>;\n\tconst locks = new Map<string, Promise<void>>();\n\tglobal[CONTENT_USAGE_COLLECTION_LOCKS_KEY] = locks;\n\treturn locks;\n}\n"],"mappings":"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"}