emdash 0.34.0 → 0.36.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 (800) hide show
  1. package/dist/activation-K0kGBWuJ.mjs +382 -0
  2. package/dist/activation-K0kGBWuJ.mjs.map +1 -0
  3. package/dist/{adapters-34EN2eXO.d.mts → adapters-CzL3H752.d.mts} +10 -3
  4. package/dist/adapters-CzL3H752.d.mts.map +1 -0
  5. package/dist/{allowed-origins-DW7sYyqg.mjs → allowed-origins-maylGaHv.mjs} +2 -2
  6. package/dist/{allowed-origins-DW7sYyqg.mjs.map → allowed-origins-maylGaHv.mjs.map} +1 -1
  7. package/dist/api/route-utils.d.mts +3 -3
  8. package/dist/api/route-utils.mjs +22 -20
  9. package/dist/api/route-utils.mjs.map +1 -1
  10. package/dist/api/schemas/index.d.mts +2 -2
  11. package/dist/api/schemas/index.mjs +5 -5
  12. package/dist/api-B0w3uBiG.mjs +2269 -0
  13. package/dist/api-B0w3uBiG.mjs.map +1 -0
  14. package/dist/{api-tokens-CFXu8F_2.mjs → api-tokens-9YrrCoE6.mjs} +2 -2
  15. package/dist/{api-tokens-CFXu8F_2.mjs.map → api-tokens-9YrrCoE6.mjs.map} +1 -1
  16. package/dist/{apply-D5zfeD09.mjs → apply-B_VMpJ2P.mjs} +34 -35
  17. package/dist/{apply-D5zfeD09.mjs.map → apply-B_VMpJ2P.mjs.map} +1 -1
  18. package/dist/astro/index.d.mts +12 -12
  19. package/dist/astro/index.d.mts.map +1 -1
  20. package/dist/astro/index.mjs +92 -25
  21. package/dist/astro/index.mjs.map +1 -1
  22. package/dist/astro/middleware/auth.d.mts +10 -10
  23. package/dist/astro/middleware/auth.d.mts.map +1 -1
  24. package/dist/astro/middleware/auth.mjs +10 -7
  25. package/dist/astro/middleware/auth.mjs.map +1 -1
  26. package/dist/astro/middleware/media-usage-write-fence.mjs +2 -2
  27. package/dist/astro/middleware/redirect.mjs +4 -4
  28. package/dist/astro/middleware/request-context.mjs +3 -3
  29. package/dist/astro/middleware/setup.mjs +1 -1
  30. package/dist/astro/middleware.d.mts +11 -12
  31. package/dist/astro/middleware.d.mts.map +1 -1
  32. package/dist/astro/middleware.mjs +255 -1323
  33. package/dist/astro/middleware.mjs.map +1 -1
  34. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
  35. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
  36. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +3 -3
  37. package/dist/astro/routes/api/admin/api-tokens/index.mjs +4 -4
  38. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  39. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -9
  40. package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -9
  41. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -9
  42. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +15 -15
  43. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +15 -15
  44. package/dist/astro/routes/api/admin/bylines/index.mjs +15 -15
  45. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +12 -12
  46. package/dist/astro/routes/api/admin/comments/_id_.mjs +4 -4
  47. package/dist/astro/routes/api/admin/comments/bulk.mjs +9 -9
  48. package/dist/astro/routes/api/admin/comments/counts.mjs +4 -4
  49. package/dist/astro/routes/api/admin/comments/index.mjs +9 -9
  50. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  51. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  52. package/dist/astro/routes/api/admin/media-usage/activation.d.mts +9 -0
  53. package/dist/astro/routes/api/admin/media-usage/activation.d.mts.map +1 -0
  54. package/dist/astro/routes/api/admin/media-usage/activation.mjs +46 -0
  55. package/dist/astro/routes/api/admin/media-usage/activation.mjs.map +1 -0
  56. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +14 -13
  57. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs.map +1 -1
  58. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +14 -13
  59. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs.map +1 -1
  60. package/dist/astro/routes/api/admin/media-usage/progress.d.mts +9 -0
  61. package/dist/astro/routes/api/admin/media-usage/progress.d.mts.map +1 -0
  62. package/dist/astro/routes/api/admin/media-usage/progress.mjs +44 -0
  63. package/dist/astro/routes/api/admin/media-usage/progress.mjs.map +1 -0
  64. package/dist/astro/routes/api/admin/media-usage/repair.mjs +16 -11
  65. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
  66. package/dist/astro/routes/api/admin/media-usage/work/index.mjs +14 -13
  67. package/dist/astro/routes/api/admin/media-usage/work/index.mjs.map +1 -1
  68. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +14 -13
  69. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs.map +1 -1
  70. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
  71. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
  72. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +54 -48
  73. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  74. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +54 -48
  75. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +52 -46
  77. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +4 -4
  79. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +52 -46
  80. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
  81. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +53 -47
  82. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  83. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +53 -47
  84. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  85. package/dist/astro/routes/api/admin/plugins/index.mjs +52 -46
  86. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  87. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
  88. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +52 -46
  89. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  90. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +53 -47
  91. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  92. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +52 -46
  93. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  94. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +53 -47
  95. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  96. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +54 -48
  97. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  98. package/dist/astro/routes/api/admin/plugins/registry/artifact.d.mts.map +1 -1
  99. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +172 -133
  100. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  101. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +54 -48
  102. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  103. package/dist/astro/routes/api/admin/plugins/updates.mjs +52 -46
  104. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  105. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +52 -46
  106. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  107. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
  108. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +52 -46
  109. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  110. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  111. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
  112. package/dist/astro/routes/api/admin/users/_id_/index.mjs +7 -7
  113. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
  114. package/dist/astro/routes/api/admin/users/index.mjs +6 -6
  115. package/dist/astro/routes/api/auth/dev-bypass.d.mts.map +1 -1
  116. package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -7
  117. package/dist/astro/routes/api/auth/dev-bypass.mjs.map +1 -1
  118. package/dist/astro/routes/api/auth/invite/accept.mjs +1 -1
  119. package/dist/astro/routes/api/auth/invite/complete.mjs +11 -11
  120. package/dist/astro/routes/api/auth/invite/index.mjs +8 -8
  121. package/dist/astro/routes/api/auth/invite/register-options.mjs +10 -10
  122. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  123. package/dist/astro/routes/api/auth/magic-link/send.mjs +10 -10
  124. package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
  125. package/dist/astro/routes/api/auth/me.d.mts.map +1 -1
  126. package/dist/astro/routes/api/auth/me.mjs +19 -9
  127. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  128. package/dist/astro/routes/api/auth/mode.mjs +2 -2
  129. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  130. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  131. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
  132. package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
  133. package/dist/astro/routes/api/auth/passkey/options.mjs +12 -12
  134. package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -10
  135. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -11
  136. package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -11
  137. package/dist/astro/routes/api/auth/signup/complete.mjs +11 -11
  138. package/dist/astro/routes/api/auth/signup/request.mjs +10 -10
  139. package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
  140. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +13 -13
  141. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +12 -12
  142. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
  143. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  144. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  145. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +2 -2
  146. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +11 -11
  147. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +8 -8
  148. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +16 -14
  149. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
  150. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +16 -14
  151. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
  152. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  153. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
  154. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -8
  155. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +13 -13
  156. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
  157. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  158. package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
  159. package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
  160. package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
  161. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
  162. package/dist/astro/routes/api/dashboard.mjs +7 -7
  163. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  164. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  165. package/dist/astro/routes/api/import/probe.mjs +16 -16
  166. package/dist/astro/routes/api/import/wordpress/analyze.mjs +2 -2
  167. package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
  168. package/dist/astro/routes/api/import/wordpress/execute.mjs +12 -12
  169. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
  170. package/dist/astro/routes/api/import/wordpress/media.mjs +11 -11
  171. package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
  172. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
  173. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +12 -10
  174. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
  175. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  176. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +16 -16
  177. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
  178. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +26 -26
  179. package/dist/astro/routes/api/manifest.mjs +4 -4
  180. package/dist/astro/routes/api/mcp.mjs +42 -42
  181. package/dist/astro/routes/api/media/_id_/confirm.mjs +11 -11
  182. package/dist/astro/routes/api/media/_id_/upload.mjs +6 -6
  183. package/dist/astro/routes/api/media/_id_/usage.mjs +18 -13
  184. package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
  185. package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
  186. package/dist/astro/routes/api/media/_id_.mjs +21 -13
  187. package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
  188. package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
  189. package/dist/astro/routes/api/media/folders/_id_.d.mts +10 -0
  190. package/dist/astro/routes/api/media/folders/_id_.d.mts.map +1 -0
  191. package/dist/astro/routes/api/media/folders/_id_.mjs +52 -0
  192. package/dist/astro/routes/api/media/folders/_id_.mjs.map +1 -0
  193. package/dist/astro/routes/api/media/folders/index.d.mts +9 -0
  194. package/dist/astro/routes/api/media/folders/index.d.mts.map +1 -0
  195. package/dist/astro/routes/api/media/folders/index.mjs +41 -0
  196. package/dist/astro/routes/api/media/folders/index.mjs.map +1 -0
  197. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
  198. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
  199. package/dist/astro/routes/api/media/providers/index.mjs +2 -2
  200. package/dist/astro/routes/api/media/upload-url.mjs +9 -9
  201. package/dist/astro/routes/api/media.d.mts.map +1 -1
  202. package/dist/astro/routes/api/media.mjs +28 -19
  203. package/dist/astro/routes/api/media.mjs.map +1 -1
  204. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +8 -8
  205. package/dist/astro/routes/api/menus/_name_/items.mjs +8 -8
  206. package/dist/astro/routes/api/menus/_name_/reorder.mjs +8 -8
  207. package/dist/astro/routes/api/menus/_name_/translations.mjs +8 -8
  208. package/dist/astro/routes/api/menus/_name_.mjs +8 -8
  209. package/dist/astro/routes/api/menus/index.mjs +8 -8
  210. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  211. package/dist/astro/routes/api/oauth/device/authorize.mjs +5 -5
  212. package/dist/astro/routes/api/oauth/device/code.mjs +8 -8
  213. package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
  214. package/dist/astro/routes/api/oauth/register.mjs +2 -2
  215. package/dist/astro/routes/api/oauth/token/refresh.mjs +5 -5
  216. package/dist/astro/routes/api/oauth/token/revoke.mjs +5 -5
  217. package/dist/astro/routes/api/oauth/token.mjs +5 -5
  218. package/dist/astro/routes/api/openapi.json.mjs +153 -6
  219. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  220. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
  221. package/dist/astro/routes/api/redirects/404s/index.mjs +10 -10
  222. package/dist/astro/routes/api/redirects/404s/summary.mjs +10 -10
  223. package/dist/astro/routes/api/redirects/_id_.mjs +11 -11
  224. package/dist/astro/routes/api/redirects/index.mjs +11 -11
  225. package/dist/astro/routes/api/relations/_id_/index.mjs +16 -14
  226. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
  227. package/dist/astro/routes/api/relations/_id_/translations.mjs +11 -9
  228. package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
  229. package/dist/astro/routes/api/relations/index.mjs +16 -14
  230. package/dist/astro/routes/api/relations/index.mjs.map +1 -1
  231. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
  232. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  233. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +52 -46
  234. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  235. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +52 -46
  236. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  237. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +52 -46
  238. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  239. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +52 -46
  240. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  241. package/dist/astro/routes/api/schema/collections/index.mjs +52 -46
  242. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  243. package/dist/astro/routes/api/schema/collections/reorder.mjs +52 -46
  244. package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
  245. package/dist/astro/routes/api/schema/index.mjs +10 -8
  246. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  247. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +52 -46
  248. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  249. package/dist/astro/routes/api/schema/orphans/index.mjs +52 -46
  250. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  251. package/dist/astro/routes/api/search/enable.mjs +10 -10
  252. package/dist/astro/routes/api/search/index.mjs +9 -9
  253. package/dist/astro/routes/api/search/rebuild.mjs +10 -10
  254. package/dist/astro/routes/api/search/stats.mjs +5 -5
  255. package/dist/astro/routes/api/search/suggest.mjs +9 -9
  256. package/dist/astro/routes/api/sections/_slug_.mjs +9 -9
  257. package/dist/astro/routes/api/sections/index.mjs +9 -9
  258. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +6 -6
  259. package/dist/astro/routes/api/settings/backups/archives/index.mjs +5 -5
  260. package/dist/astro/routes/api/settings/backups/export.mjs +6 -6
  261. package/dist/astro/routes/api/settings/backups/index.mjs +7 -7
  262. package/dist/astro/routes/api/settings/email.mjs +4 -4
  263. package/dist/astro/routes/api/settings.mjs +13 -13
  264. package/dist/astro/routes/api/setup/admin-verify.mjs +12 -12
  265. package/dist/astro/routes/api/setup/admin.mjs +11 -11
  266. package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
  267. package/dist/astro/routes/api/setup/dev-bypass.mjs +35 -31
  268. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  269. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  270. package/dist/astro/routes/api/setup/index.d.mts.map +1 -1
  271. package/dist/astro/routes/api/setup/index.mjs +31 -35
  272. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  273. package/dist/astro/routes/api/setup/status.mjs +3 -3
  274. package/dist/astro/routes/api/snapshot.mjs +7 -7
  275. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +13 -13
  276. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +13 -13
  277. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +13 -13
  278. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +13 -13
  279. package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +13 -13
  280. package/dist/astro/routes/api/taxonomies/_name_.mjs +13 -13
  281. package/dist/astro/routes/api/taxonomies/index.mjs +13 -13
  282. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  283. package/dist/astro/routes/api/typegen.mjs +4 -4
  284. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  285. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  286. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  287. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +7 -7
  288. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +10 -10
  289. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +10 -10
  290. package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
  291. package/dist/astro/routes/api/widget-areas/index.mjs +10 -10
  292. package/dist/astro/routes/api/widget-components.mjs +2 -2
  293. package/dist/astro/routes/robots.txt.mjs +6 -6
  294. package/dist/astro/routes/sitemap-_collection_.xml.mjs +9 -9
  295. package/dist/astro/routes/sitemap.xml.mjs +7 -7
  296. package/dist/astro/types.d.mts +19 -14
  297. package/dist/astro/types.d.mts.map +1 -1
  298. package/dist/auth/providers/github.d.mts +1 -1
  299. package/dist/auth/providers/google.d.mts +1 -1
  300. package/dist/{authorize-1iFJzPaZ.mjs → authorize-ByYs-Uxs.mjs} +2 -2
  301. package/dist/{authorize-1iFJzPaZ.mjs.map → authorize-ByYs-Uxs.mjs.map} +1 -1
  302. package/dist/{backup-LHjIAVNP.mjs → backup-ChyHfIoX.mjs} +5 -5
  303. package/dist/{backup-LHjIAVNP.mjs.map → backup-ChyHfIoX.mjs.map} +1 -1
  304. package/dist/{bundle-CBEXrBnb.mjs → bundle-DZFhFurR.mjs} +7 -7
  305. package/dist/{bundle-CBEXrBnb.mjs.map → bundle-DZFhFurR.mjs.map} +1 -1
  306. package/dist/{byline-pEcoDps8.mjs → byline-Cpnwrje1.mjs} +6 -6
  307. package/dist/{byline-pEcoDps8.mjs.map → byline-Cpnwrje1.mjs.map} +1 -1
  308. package/dist/{byline-fields-C5S8wygO.mjs → byline-fields-CAyA_Umd.mjs} +2 -2
  309. package/dist/{byline-fields-C5S8wygO.mjs.map → byline-fields-CAyA_Umd.mjs.map} +1 -1
  310. package/dist/{byline-registry-DDun8sxh.mjs → byline-registry-Bpu9-MVs.mjs} +2 -2
  311. package/dist/{byline-registry-DDun8sxh.mjs.map → byline-registry-Bpu9-MVs.mjs.map} +1 -1
  312. package/dist/{bylines-BWemdX2k.mjs → bylines-BSo9B9RX.mjs} +7 -7
  313. package/dist/{bylines-BWemdX2k.mjs.map → bylines-BSo9B9RX.mjs.map} +1 -1
  314. package/dist/{bylines-DJfSfXCL.mjs → bylines-CHjvehVX.mjs} +3 -3
  315. package/dist/{bylines-DJfSfXCL.mjs.map → bylines-CHjvehVX.mjs.map} +1 -1
  316. package/dist/{cache-CBJaDy_E.mjs → cache-5VirJ2Cn.mjs} +4 -4
  317. package/dist/{cache-CBJaDy_E.mjs.map → cache-5VirJ2Cn.mjs.map} +1 -1
  318. package/dist/capture-triggers-CNW1q3HK.mjs +362 -0
  319. package/dist/capture-triggers-CNW1q3HK.mjs.map +1 -0
  320. package/dist/{challenge-store-CZyFVaad.mjs → challenge-store-D5pSY2S1.mjs} +1 -1
  321. package/dist/{challenge-store-CZyFVaad.mjs.map → challenge-store-D5pSY2S1.mjs.map} +1 -1
  322. package/dist/{chunks-DfPax1I0.mjs → chunks-bNiX6_DR.mjs} +2 -2
  323. package/dist/{chunks-DfPax1I0.mjs.map → chunks-bNiX6_DR.mjs.map} +1 -1
  324. package/dist/cli/index.mjs +568 -84
  325. package/dist/cli/index.mjs.map +1 -1
  326. package/dist/client/cf-access.d.mts +1 -1
  327. package/dist/client/index.d.mts +74 -4
  328. package/dist/client/index.d.mts.map +1 -1
  329. package/dist/client/index.mjs +54 -2
  330. package/dist/client/index.mjs.map +1 -1
  331. package/dist/{collection-deletion-C8BugSGz.mjs → collection-deletion-m_oi2c0N.mjs} +13 -356
  332. package/dist/collection-deletion-m_oi2c0N.mjs.map +1 -0
  333. package/dist/{comment-Dxlep6cE.mjs → comment-DhvLEdhQ.mjs} +1 -1
  334. package/dist/{comment-Dxlep6cE.mjs.map → comment-DhvLEdhQ.mjs.map} +1 -1
  335. package/dist/{comment-reaction-D6qfHKBc.mjs → comment-reaction-CRuItEjt.mjs} +2 -2
  336. package/dist/{comment-reaction-D6qfHKBc.mjs.map → comment-reaction-CRuItEjt.mjs.map} +1 -1
  337. package/dist/{comments-CTOuhTSX.mjs → comments-CLj4bi8I.mjs} +2 -2
  338. package/dist/{comments-CTOuhTSX.mjs.map → comments-CLj4bi8I.mjs.map} +1 -1
  339. package/dist/{components-CQy3lh9x.mjs → components-v4U4DT2y.mjs} +1 -1
  340. package/dist/{components-CQy3lh9x.mjs.map → components-v4U4DT2y.mjs.map} +1 -1
  341. package/dist/config-DrfghTPe.d.mts +34 -0
  342. package/dist/config-DrfghTPe.d.mts.map +1 -0
  343. package/dist/{content-BNfwh7xX.mjs → content-CP-SeXNa.mjs} +5 -4
  344. package/dist/content-CP-SeXNa.mjs.map +1 -0
  345. package/dist/{content-list-query-DhQ84eVV.d.mts → content-list-query-BhejKVqb.d.mts} +1 -1
  346. package/dist/{content-list-query-DhQ84eVV.d.mts.map → content-list-query-BhejKVqb.d.mts.map} +1 -1
  347. package/dist/{content-refresh-DNERP4-f.mjs → content-refresh-DLOfIhYc.mjs} +1094 -93
  348. package/dist/content-refresh-DLOfIhYc.mjs.map +1 -0
  349. package/dist/{media-usage-De3qvwjc.mjs → content-repair-BCxzy0r4.mjs} +5 -196
  350. package/dist/content-repair-BCxzy0r4.mjs.map +1 -0
  351. package/dist/{context-DRgOvSnW.mjs → context-BR5hRWiK.mjs} +13 -13
  352. package/dist/{context-DRgOvSnW.mjs.map → context-BR5hRWiK.mjs.map} +1 -1
  353. package/dist/{cron-Y9aLtgrA.mjs → cron-CWfWrWKG.mjs} +1 -1
  354. package/dist/{cron-Y9aLtgrA.mjs.map → cron-CWfWrWKG.mjs.map} +1 -1
  355. package/dist/{dashboard-CB8J89rJ.mjs → dashboard-CkIV2rnQ.mjs} +5 -5
  356. package/dist/{dashboard-CB8J89rJ.mjs.map → dashboard-CkIV2rnQ.mjs.map} +1 -1
  357. package/dist/db/index.d.mts +4 -4
  358. package/dist/db/index.mjs +32 -9
  359. package/dist/db/index.mjs.map +1 -1
  360. package/dist/db/libsql-migrations.d.mts +11 -0
  361. package/dist/db/libsql-migrations.d.mts.map +1 -0
  362. package/dist/db/libsql-migrations.mjs +57 -0
  363. package/dist/db/libsql-migrations.mjs.map +1 -0
  364. package/dist/db/libsql.d.mts +1 -1
  365. package/dist/db/postgres-migrations.d.mts +10 -0
  366. package/dist/db/postgres-migrations.d.mts.map +1 -0
  367. package/dist/db/postgres-migrations.mjs +47 -0
  368. package/dist/db/postgres-migrations.mjs.map +1 -0
  369. package/dist/db/postgres.d.mts +1 -1
  370. package/dist/db/sqlite-migrations.d.mts +10 -0
  371. package/dist/db/sqlite-migrations.d.mts.map +1 -0
  372. package/dist/db/sqlite-migrations.mjs +28 -0
  373. package/dist/db/sqlite-migrations.mjs.map +1 -0
  374. package/dist/db/sqlite.d.mts +1 -1
  375. package/dist/db/sqlite.mjs +3 -3
  376. package/dist/db/sqlite.mjs.map +1 -1
  377. package/dist/{default-DdDl3fxv.mjs → default-YMEfcQUx.mjs} +1 -1
  378. package/dist/{default-DdDl3fxv.mjs.map → default-YMEfcQUx.mjs.map} +1 -1
  379. package/dist/{deferred-tasks-BMsa9F64.d.mts → deferred-tasks-DfqxH5Xf.d.mts} +1 -1
  380. package/dist/{deferred-tasks-BMsa9F64.d.mts.map → deferred-tasks-DfqxH5Xf.d.mts.map} +1 -1
  381. package/dist/{device-flow-CaioYGK3.mjs → device-flow-CojjSBwb.mjs} +5 -5
  382. package/dist/{device-flow-CaioYGK3.mjs.map → device-flow-CojjSBwb.mjs.map} +1 -1
  383. package/dist/direct-executor-BWeWiRYB.mjs +85 -0
  384. package/dist/direct-executor-BWeWiRYB.mjs.map +1 -0
  385. package/dist/{dist-D_6CKR8z.mjs → dist-89xCrGqi.mjs} +1 -1
  386. package/dist/{dist-D_6CKR8z.mjs.map → dist-89xCrGqi.mjs.map} +1 -1
  387. package/dist/{email-console-BHNjIwiB.mjs → email-console-CEJgR_LB.mjs} +1 -1
  388. package/dist/{email-console-BHNjIwiB.mjs.map → email-console-CEJgR_LB.mjs.map} +1 -1
  389. package/dist/{enrich-_ySMEvEE.mjs → enrich-BX0IDp4z.mjs} +3 -3
  390. package/dist/{enrich-_ySMEvEE.mjs.map → enrich-BX0IDp4z.mjs.map} +1 -1
  391. package/dist/{error-DupyJZE2.mjs → error-D9w94fjV.mjs} +2 -2
  392. package/dist/{error-DupyJZE2.mjs.map → error-D9w94fjV.mjs.map} +1 -1
  393. package/dist/{errors-CLUPm7aD.mjs → errors-n2j49uNf.mjs} +22 -2
  394. package/dist/errors-n2j49uNf.mjs.map +1 -0
  395. package/dist/{escape-C_aLKP3H.mjs → escape-DN4Qmjsl.mjs} +1 -1
  396. package/dist/{escape-C_aLKP3H.mjs.map → escape-DN4Qmjsl.mjs.map} +1 -1
  397. package/dist/{field-defs-cache-hg6QkjV9.mjs → field-defs-cache-Bh4q6oar.mjs} +5 -5
  398. package/dist/{field-defs-cache-hg6QkjV9.mjs.map → field-defs-cache-Bh4q6oar.mjs.map} +1 -1
  399. package/dist/focal-point-BeWFo6Xx.mjs +23 -0
  400. package/dist/focal-point-BeWFo6Xx.mjs.map +1 -0
  401. package/dist/{fts-manager-CZ-5dVwJ.mjs → fts-manager-BhOdW0bi.mjs} +3 -3
  402. package/dist/{fts-manager-CZ-5dVwJ.mjs.map → fts-manager-BhOdW0bi.mjs.map} +1 -1
  403. package/dist/{hash-Cg0TXJPV.mjs → hash-CTcngWvj.mjs} +1 -1
  404. package/dist/{hash-Cg0TXJPV.mjs.map → hash-CTcngWvj.mjs.map} +1 -1
  405. package/dist/{hreflang-D3Uehfv2.d.mts → hreflang-7pTHsLZJ.d.mts} +2 -2
  406. package/dist/{hreflang-D3Uehfv2.d.mts.map → hreflang-7pTHsLZJ.d.mts.map} +1 -1
  407. package/dist/{hreflang-DNswoYsi.mjs → hreflang-Huc6O5wa.mjs} +7 -7
  408. package/dist/{hreflang-DNswoYsi.mjs.map → hreflang-Huc6O5wa.mjs.map} +1 -1
  409. package/dist/identity-DXmb4aVp.mjs +35 -0
  410. package/dist/identity-DXmb4aVp.mjs.map +1 -0
  411. package/dist/{import-B41u8Rot.mjs → import-C4_O3KXU.mjs} +5 -5
  412. package/dist/{import-B41u8Rot.mjs.map → import-C4_O3KXU.mjs.map} +1 -1
  413. package/dist/{index-Cof4-Fab.d.mts → index-6bRee8zb.d.mts} +3 -3
  414. package/dist/{index-Cof4-Fab.d.mts.map → index-6bRee8zb.d.mts.map} +1 -1
  415. package/dist/{index-C3Y-rsyN.d.mts → index-DWbJrsAg.d.mts} +138 -83
  416. package/dist/index-DWbJrsAg.d.mts.map +1 -0
  417. package/dist/index.d.mts +21 -20
  418. package/dist/index.mjs +78 -72
  419. package/dist/{init-lock-6b309ZrF.mjs → init-lock-DJkX6Hto.mjs} +1 -1
  420. package/dist/{init-lock-6b309ZrF.mjs.map → init-lock-DJkX6Hto.mjs.map} +1 -1
  421. package/dist/{load-DmvJrtxS.mjs → load-Bo7fZAbg.mjs} +2 -2
  422. package/dist/{load-DmvJrtxS.mjs.map → load-Bo7fZAbg.mjs.map} +1 -1
  423. package/dist/{loader-DLBqRiGP.mjs → loader-ZsjE4paL.mjs} +2 -2
  424. package/dist/{loader-DLBqRiGP.mjs.map → loader-ZsjE4paL.mjs.map} +1 -1
  425. package/dist/manifest-builder-QXuqvVMP.mjs +189 -0
  426. package/dist/manifest-builder-QXuqvVMP.mjs.map +1 -0
  427. package/dist/{manifest-schema-BXbUAHod.mjs → manifest-schema-CUYPfCEH.mjs} +1 -1
  428. package/dist/{manifest-schema-BXbUAHod.mjs.map → manifest-schema-CUYPfCEH.mjs.map} +1 -1
  429. package/dist/media/image-endpoint.mjs +1 -1
  430. package/dist/media/index.d.mts +1 -1
  431. package/dist/media/index.mjs +13 -3
  432. package/dist/media/index.mjs.map +1 -1
  433. package/dist/media/local-runtime.d.mts +14 -12
  434. package/dist/media/local-runtime.d.mts.map +1 -1
  435. package/dist/media/local-runtime.mjs +11 -5
  436. package/dist/media/local-runtime.mjs.map +1 -1
  437. package/dist/{media-BWWWb8kg.mjs → media-BjwNuLEu.mjs} +100 -6
  438. package/dist/media-BjwNuLEu.mjs.map +1 -0
  439. package/dist/{media-CzhAn2--.mjs → media-D6SeAfBl.mjs} +41 -12
  440. package/dist/media-D6SeAfBl.mjs.map +1 -0
  441. package/dist/{media-allowlist-BD6HhgXA.mjs → media-allowlist-D35v7bmL.mjs} +3 -2
  442. package/dist/media-allowlist-D35v7bmL.mjs.map +1 -0
  443. package/dist/media-folders-D-0CmRZj.mjs +210 -0
  444. package/dist/media-folders-D-0CmRZj.mjs.map +1 -0
  445. package/dist/{media-upload-9yas9zGH.mjs → media-upload-C7bLXGcI.mjs} +9 -9
  446. package/dist/{media-upload-9yas9zGH.mjs.map → media-upload-C7bLXGcI.mjs.map} +1 -1
  447. package/dist/{media-url-BvoX88FA.mjs → media-url-DH7giMx_.mjs} +1 -1
  448. package/dist/{media-url-BvoX88FA.mjs.map → media-url-DH7giMx_.mjs.map} +1 -1
  449. package/dist/media-usage-BTRJvS6V.mjs +1833 -0
  450. package/dist/media-usage-BTRJvS6V.mjs.map +1 -0
  451. package/dist/media-usage-activation-Dx0kPV6b.mjs +78 -0
  452. package/dist/media-usage-activation-Dx0kPV6b.mjs.map +1 -0
  453. package/dist/{media-usage-work-DrCbqBcl.mjs → media-usage-work-C-fhM30c.mjs} +221 -59
  454. package/dist/media-usage-work-C-fhM30c.mjs.map +1 -0
  455. package/dist/{media-usage-work-B9Bksh_b.mjs → media-usage-work-DWb2XPMI.mjs} +4 -4
  456. package/dist/{media-usage-work-B9Bksh_b.mjs.map → media-usage-work-DWb2XPMI.mjs.map} +1 -1
  457. package/dist/{media-usage-write-fence-aHEgN1zB.mjs → media-usage-write-fence-BwoJVRDC.mjs} +2 -2
  458. package/dist/{media-usage-write-fence-aHEgN1zB.mjs.map → media-usage-write-fence-BwoJVRDC.mjs.map} +1 -1
  459. package/dist/{menus-fxvEDYE6.mjs → menus-CD7EFLwz.mjs} +3 -3
  460. package/dist/{menus-fxvEDYE6.mjs.map → menus-CD7EFLwz.mjs.map} +1 -1
  461. package/dist/{menus-CC-yn1F9.mjs → menus-CNg0QlDt.mjs} +42 -26
  462. package/dist/menus-CNg0QlDt.mjs.map +1 -0
  463. package/dist/migrations/index.d.mts +22 -0
  464. package/dist/migrations/index.d.mts.map +1 -0
  465. package/dist/migrations/index.mjs +7 -0
  466. package/dist/{mime-DfIVjNkr.mjs → mime-CUIG6x84.mjs} +1 -1
  467. package/dist/mime-CUIG6x84.mjs.map +1 -0
  468. package/dist/{mode-Bpe-tNNy.mjs → mode-DaUM_MB2.mjs} +1 -1
  469. package/dist/{mode-Bpe-tNNy.mjs.map → mode-DaUM_MB2.mjs.map} +1 -1
  470. package/dist/node-sqlite-compat-Udb4Y-rs.mjs +46 -0
  471. package/dist/node-sqlite-compat-Udb4Y-rs.mjs.map +1 -0
  472. package/dist/{normalize-Bc-s_r-a.mjs → normalize-CSxdKBVa.mjs} +22 -1
  473. package/dist/normalize-CSxdKBVa.mjs.map +1 -0
  474. package/dist/{oauth-authorization-BXO1MKej.mjs → oauth-authorization-Dpsb-fMJ.mjs} +5 -5
  475. package/dist/{oauth-authorization-BXO1MKej.mjs.map → oauth-authorization-Dpsb-fMJ.mjs.map} +1 -1
  476. package/dist/{oauth-clients-C_DNqtRu.mjs → oauth-clients-Bxnoe_67.mjs} +1 -1
  477. package/dist/{oauth-clients-C_DNqtRu.mjs.map → oauth-clients-Bxnoe_67.mjs.map} +1 -1
  478. package/dist/{oauth-state-store-DwAxMtk4.mjs → oauth-state-store-0C4s0K6w.mjs} +1 -1
  479. package/dist/{oauth-state-store-DwAxMtk4.mjs.map → oauth-state-store-0C4s0K6w.mjs.map} +1 -1
  480. package/dist/{oauth-user-lookup-BxfZizdk.mjs → oauth-user-lookup-wlGYSeqX.mjs} +1 -1
  481. package/dist/{oauth-user-lookup-BxfZizdk.mjs.map → oauth-user-lookup-wlGYSeqX.mjs.map} +1 -1
  482. package/dist/object-cache/memory.d.mts +1 -1
  483. package/dist/{options-CBXIHUoT.mjs → options-BHPeIbBd.mjs} +1 -1
  484. package/dist/{options-CBXIHUoT.mjs.map → options-BHPeIbBd.mjs.map} +1 -1
  485. package/dist/{options-DRe1pIBL.d.mts → options-BhZIeFY1.d.mts} +3 -3
  486. package/dist/{options-DRe1pIBL.d.mts.map → options-BhZIeFY1.d.mts.map} +1 -1
  487. package/dist/page/index.d.mts +2 -2
  488. package/dist/{parse-DFbuc-Fh.mjs → parse-BRJg1kz8.mjs} +2 -2
  489. package/dist/{parse-DFbuc-Fh.mjs.map → parse-BRJg1kz8.mjs.map} +1 -1
  490. package/dist/{passkey-config-_2GOwwK8.mjs → passkey-config-B3P0nHRR.mjs} +1 -1
  491. package/dist/{passkey-config-_2GOwwK8.mjs.map → passkey-config-B3P0nHRR.mjs.map} +1 -1
  492. package/dist/{patterns-CiyXeDgr.mjs → patterns-D9iDO3j8.mjs} +1 -1
  493. package/dist/{patterns-CiyXeDgr.mjs.map → patterns-D9iDO3j8.mjs.map} +1 -1
  494. package/dist/{placeholder-CzW7Lshs.d.mts → placeholder-BR4ZhVJl.d.mts} +7 -1
  495. package/dist/placeholder-BR4ZhVJl.d.mts.map +1 -0
  496. package/dist/plugin-types.d.mts +1 -1
  497. package/dist/plugin-utils.d.mts +10 -10
  498. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
  499. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  500. package/dist/policy-BVRganJQ.mjs +45 -0
  501. package/dist/policy-BVRganJQ.mjs.map +1 -0
  502. package/dist/{portable-text-DhKbDAGr.mjs → portable-text-C8Tvp4_g.mjs} +1 -1
  503. package/dist/{portable-text-DhKbDAGr.mjs.map → portable-text-C8Tvp4_g.mjs.map} +1 -1
  504. package/dist/{preview-PW-nOnq4.mjs → preview-dn2dUyM0.mjs} +2 -2
  505. package/dist/{preview-PW-nOnq4.mjs.map → preview-dn2dUyM0.mjs.map} +1 -1
  506. package/dist/protocol-CuBEsqgE.d.mts +52 -0
  507. package/dist/protocol-CuBEsqgE.d.mts.map +1 -0
  508. package/dist/{public-url-CWoFhQT8.mjs → public-url-DRz_AinV.mjs} +1 -1
  509. package/dist/{public-url-CWoFhQT8.mjs.map → public-url-DRz_AinV.mjs.map} +1 -1
  510. package/dist/{query-BgjfiLUQ.mjs → query-D18vTdha.mjs} +14 -14
  511. package/dist/{query-BgjfiLUQ.mjs.map → query-D18vTdha.mjs.map} +1 -1
  512. package/dist/{query-vgZNwIhh.mjs → query-tZDEdtCN.mjs} +4 -4
  513. package/dist/{query-vgZNwIhh.mjs.map → query-tZDEdtCN.mjs.map} +1 -1
  514. package/dist/{rate-limit-L5anykgo.mjs → rate-limit-on7KPo-v.mjs} +2 -2
  515. package/dist/{rate-limit-L5anykgo.mjs.map → rate-limit-on7KPo-v.mjs.map} +1 -1
  516. package/dist/{redirect-COsS6SWJ.mjs → redirect-CZkbozu9.mjs} +2 -2
  517. package/dist/{redirect-COsS6SWJ.mjs.map → redirect-CZkbozu9.mjs.map} +1 -1
  518. package/dist/{redirect-KBNE3UEs.mjs → redirect-DrbEmU9a.mjs} +1 -1
  519. package/dist/{redirect-KBNE3UEs.mjs.map → redirect-DrbEmU9a.mjs.map} +1 -1
  520. package/dist/{redirects-CufRQt0Q.mjs → redirects-BahWiu8Z.mjs} +5 -5
  521. package/dist/{redirects-CufRQt0Q.mjs.map → redirects-BahWiu8Z.mjs.map} +1 -1
  522. package/dist/{redirects-cV9g7V-0.mjs → redirects-CRIM55KH.mjs} +6 -6
  523. package/dist/{redirects-cV9g7V-0.mjs.map → redirects-CRIM55KH.mjs.map} +1 -1
  524. package/dist/{api-DdAsirBe.mjs → registry-BiO5WwPq.mjs} +483 -2462
  525. package/dist/registry-BiO5WwPq.mjs.map +1 -0
  526. package/dist/{registry-C581V4TP.mjs → registry-Cx-jEJOS.mjs} +9 -123
  527. package/dist/registry-Cx-jEJOS.mjs.map +1 -0
  528. package/dist/{relation-DtmAQH7s.mjs → relation-A6wLF_DS.mjs} +2 -2
  529. package/dist/{relation-DtmAQH7s.mjs.map → relation-A6wLF_DS.mjs.map} +1 -1
  530. package/dist/{relations-BBApI4e2.mjs → relations-BTPM2saV.mjs} +2 -2
  531. package/dist/{relations-BBApI4e2.mjs.map → relations-BTPM2saV.mjs.map} +1 -1
  532. package/dist/{relations-DItI3MN9.d.mts → relations-C7B46bcU.d.mts} +189 -30
  533. package/dist/relations-C7B46bcU.d.mts.map +1 -0
  534. package/dist/{relations-DICSyfsh.mjs → relations-Cj5hZ6Zg.mjs} +4 -4
  535. package/dist/{relations-DICSyfsh.mjs.map → relations-Cj5hZ6Zg.mjs.map} +1 -1
  536. package/dist/{request-cache-BSUptuJR.mjs → request-cache-DH9zdCOD.mjs} +1 -1
  537. package/dist/{request-cache-BSUptuJR.mjs.map → request-cache-DH9zdCOD.mjs.map} +1 -1
  538. package/dist/request-context.d.mts +1 -1
  539. package/dist/{request-meta-DgZJh3zG.mjs → request-meta-DRp5ENd_.mjs} +2 -2
  540. package/dist/{request-meta-DgZJh3zG.mjs.map → request-meta-DRp5ENd_.mjs.map} +1 -1
  541. package/dist/{resolve-DOhVEvty.mjs → resolve-DxUpTMmE.mjs} +1 -1
  542. package/dist/{resolve-DOhVEvty.mjs.map → resolve-DxUpTMmE.mjs.map} +1 -1
  543. package/dist/{runner-BiDvq5dv.mjs → runner-77GOZCnH.mjs} +443 -289
  544. package/dist/runner-77GOZCnH.mjs.map +1 -0
  545. package/dist/{runner-CgQbELYC.d.mts → runner-zgqA_CqP.d.mts} +16 -3
  546. package/dist/runner-zgqA_CqP.d.mts.map +1 -0
  547. package/dist/runtime.d.mts +11 -11
  548. package/dist/runtime.mjs +1 -1
  549. package/dist/{schema-BFyTg38T.mjs → schema-BY-oVntF.mjs} +3 -3
  550. package/dist/{schema-BFyTg38T.mjs.map → schema-BY-oVntF.mjs.map} +1 -1
  551. package/dist/{schema-CeQSNwB3.mjs → schema-Cw9kVK0I.mjs} +35 -5
  552. package/dist/{schema-CeQSNwB3.mjs.map → schema-Cw9kVK0I.mjs.map} +1 -1
  553. package/dist/{scopes-BVeuhGBE.mjs → scopes-vMR1uIbt.mjs} +3 -3
  554. package/dist/{scopes-BVeuhGBE.mjs.map → scopes-vMR1uIbt.mjs.map} +1 -1
  555. package/dist/{search-BpuPKZb0.mjs → search-CfT3KxO5.mjs} +4 -4
  556. package/dist/{search-BpuPKZb0.mjs.map → search-CfT3KxO5.mjs.map} +1 -1
  557. package/dist/{secrets-dtYs2hDo.mjs → secrets-BH2Qow1-.mjs} +3 -3
  558. package/dist/{secrets-dtYs2hDo.mjs.map → secrets-BH2Qow1-.mjs.map} +1 -1
  559. package/dist/{sections-COctk6hf.mjs → sections-DTfjyf1z.mjs} +2 -2
  560. package/dist/{sections-COctk6hf.mjs.map → sections-DTfjyf1z.mjs.map} +1 -1
  561. package/dist/seed/index.d.mts +2 -2
  562. package/dist/seed/index.mjs +24 -22
  563. package/dist/seo/index.d.mts +3 -3
  564. package/dist/seo/index.mjs +16 -14
  565. package/dist/seo/index.mjs.map +1 -1
  566. package/dist/{seo-AFpVvTqr.mjs → seo-BSiTIXzd.mjs} +1 -1
  567. package/dist/{seo-AFpVvTqr.mjs.map → seo-BSiTIXzd.mjs.map} +1 -1
  568. package/dist/{seo-CvDxfvFb.mjs → seo-Cm-dtvgk.mjs} +2 -2
  569. package/dist/{seo-CvDxfvFb.mjs.map → seo-Cm-dtvgk.mjs.map} +1 -1
  570. package/dist/{service-xMiMSTsR.mjs → service-DYsDHEKe.mjs} +3 -3
  571. package/dist/{service-xMiMSTsR.mjs.map → service-DYsDHEKe.mjs.map} +1 -1
  572. package/dist/{session-user-BzVemLkw.mjs → session-user-CNAfvsdw.mjs} +1 -1
  573. package/dist/{session-user-BzVemLkw.mjs.map → session-user-CNAfvsdw.mjs.map} +1 -1
  574. package/dist/{settings-B372MV0p.mjs → settings-BgN-0DT-.mjs} +6 -6
  575. package/dist/{settings-B372MV0p.mjs.map → settings-BgN-0DT-.mjs.map} +1 -1
  576. package/dist/{settings-BE9aqn_9.mjs → settings-DjtkUo__.mjs} +3 -3
  577. package/dist/{settings-BE9aqn_9.mjs.map → settings-DjtkUo__.mjs.map} +1 -1
  578. package/dist/{setup-complete-DF7TAfD5.mjs → setup-complete-CcpXaPU7.mjs} +2 -2
  579. package/dist/{setup-complete-DF7TAfD5.mjs.map → setup-complete-CcpXaPU7.mjs.map} +1 -1
  580. package/dist/{setup-nonce-Do-U_cg9.mjs → setup-nonce-Reyw30jg.mjs} +1 -1
  581. package/dist/{setup-nonce-Do-U_cg9.mjs.map → setup-nonce-Reyw30jg.mjs.map} +1 -1
  582. package/dist/{single-flight-cache-B0eLKS6K.mjs → single-flight-cache-BPuGPdcN.mjs} +2 -2
  583. package/dist/{single-flight-cache-B0eLKS6K.mjs.map → single-flight-cache-BPuGPdcN.mjs.map} +1 -1
  584. package/dist/{site-url-Bowsi9T-.mjs → site-url-Cs_V9n3V.mjs} +3 -3
  585. package/dist/{site-url-Bowsi9T-.mjs.map → site-url-Cs_V9n3V.mjs.map} +1 -1
  586. package/dist/{snapshot-B5h6aPMh.mjs → snapshot-DSB8P7Lm.mjs} +1 -1
  587. package/dist/{snapshot-B5h6aPMh.mjs.map → snapshot-DSB8P7Lm.mjs.map} +1 -1
  588. package/dist/{ssrf-Cpvg_n3A.mjs → ssrf-DqebVE34.mjs} +21 -5
  589. package/dist/{ssrf-Cpvg_n3A.mjs.map → ssrf-DqebVE34.mjs.map} +1 -1
  590. package/dist/{state-DYwPue40.mjs → state-BJfU9Uzw.mjs} +12 -4
  591. package/dist/state-BJfU9Uzw.mjs.map +1 -0
  592. package/dist/{status-CWVYpbD8.mjs → status-DDu8HIQl.mjs} +1 -1
  593. package/dist/{status-CWVYpbD8.mjs.map → status-DDu8HIQl.mjs.map} +1 -1
  594. package/dist/storage/local.d.mts +1 -1
  595. package/dist/storage/local.mjs +3 -3
  596. package/dist/storage/s3.d.mts +1 -1
  597. package/dist/storage/s3.mjs +1 -1
  598. package/dist/target-D0o26Azo.mjs +27 -0
  599. package/dist/target-D0o26Azo.mjs.map +1 -0
  600. package/dist/{taxonomies-Dk3MAklL.mjs → taxonomies-CPGxo-bn.mjs} +5 -5
  601. package/dist/{taxonomies-Dk3MAklL.mjs.map → taxonomies-CPGxo-bn.mjs.map} +1 -1
  602. package/dist/{taxonomies-B0o9ExNV.mjs → taxonomies-kmyudNGP.mjs} +7 -7
  603. package/dist/{taxonomies-B0o9ExNV.mjs.map → taxonomies-kmyudNGP.mjs.map} +1 -1
  604. package/dist/{taxonomy-CPeM8d2o.mjs → taxonomy-8X09dLN5.mjs} +4 -4
  605. package/dist/{taxonomy-CPeM8d2o.mjs.map → taxonomy-8X09dLN5.mjs.map} +1 -1
  606. package/dist/{tokens-C39klX8R.mjs → tokens-gTS-IVUp.mjs} +1 -1
  607. package/dist/{tokens-C39klX8R.mjs.map → tokens-gTS-IVUp.mjs.map} +1 -1
  608. package/dist/{transaction-NQj4VJ7Z.mjs → transaction-0KOpOyMu.mjs} +2 -1
  609. package/dist/{transaction-NQj4VJ7Z.mjs.map → transaction-0KOpOyMu.mjs.map} +1 -1
  610. package/dist/{transport-B_rfeU-a.d.mts → transport-_6R1ub7t.d.mts} +1 -1
  611. package/dist/{transport-B_rfeU-a.d.mts.map → transport-_6R1ub7t.d.mts.map} +1 -1
  612. package/dist/{transport--CfOflrp.mjs → transport-fX59xaJz.mjs} +4 -3
  613. package/dist/{transport--CfOflrp.mjs.map → transport-fX59xaJz.mjs.map} +1 -1
  614. package/dist/{trusted-proxy-CEVekpby.mjs → trusted-proxy-fe6nEgR4.mjs} +1 -1
  615. package/dist/{trusted-proxy-CEVekpby.mjs.map → trusted-proxy-fe6nEgR4.mjs.map} +1 -1
  616. package/dist/types-75KIqzL9.mjs.map +1 -1
  617. package/dist/{types-BW1syrlw.mjs → types-BXUmsINL.mjs} +1 -1
  618. package/dist/{types-BW1syrlw.mjs.map → types-BXUmsINL.mjs.map} +1 -1
  619. package/dist/{types-CPx2lIGo.d.mts → types-Blb-MRC3.d.mts} +1 -1
  620. package/dist/{types-CPx2lIGo.d.mts.map → types-Blb-MRC3.d.mts.map} +1 -1
  621. package/dist/{types-C3_AbkDP.d.mts → types-Br-ERaxh.d.mts} +2 -1
  622. package/dist/types-Br-ERaxh.d.mts.map +1 -0
  623. package/dist/{types-BPuloofb.d.mts → types-BzBd3PeX.d.mts} +1 -1
  624. package/dist/{types-BPuloofb.d.mts.map → types-BzBd3PeX.d.mts.map} +1 -1
  625. package/dist/{types-DefV3OiO.d.mts → types-CLqvnd05.d.mts} +1 -1
  626. package/dist/{types-DefV3OiO.d.mts.map → types-CLqvnd05.d.mts.map} +1 -1
  627. package/dist/{types-X0MThr7x.d.mts → types-D5VVLr7J.d.mts} +1 -1
  628. package/dist/{types-X0MThr7x.d.mts.map → types-D5VVLr7J.d.mts.map} +1 -1
  629. package/dist/{types-CAGdePf3.d.mts → types-DHxzBjbD.d.mts} +10 -1
  630. package/dist/types-DHxzBjbD.d.mts.map +1 -0
  631. package/dist/{types-D4_1Ga4L.d.mts → types-DJQxln7q.d.mts} +3 -3
  632. package/dist/{types-D4_1Ga4L.d.mts.map → types-DJQxln7q.d.mts.map} +1 -1
  633. package/dist/{types-CtzytVXn.mjs → types-DM3vioBY.mjs} +1 -1
  634. package/dist/{types-CtzytVXn.mjs.map → types-DM3vioBY.mjs.map} +1 -1
  635. package/dist/{types-CFUfyt1s.d.mts → types-D_DWe7k9.d.mts} +1 -1
  636. package/dist/{types-CFUfyt1s.d.mts.map → types-D_DWe7k9.d.mts.map} +1 -1
  637. package/dist/{types-Cs-TFLNf.d.mts → types-Vg6f5i3s.d.mts} +3 -3
  638. package/dist/{types-Cs-TFLNf.d.mts.map → types-Vg6f5i3s.d.mts.map} +1 -1
  639. package/dist/{upload-attempts-hlne3iA1.mjs → upload-attempts-CNyGSepg.mjs} +1 -1
  640. package/dist/{upload-attempts-hlne3iA1.mjs.map → upload-attempts-CNyGSepg.mjs.map} +1 -1
  641. package/dist/{url-pattern-BHS0V5Ng.mjs → url-pattern-K-KdXcSe.mjs} +1 -1
  642. package/dist/{url-pattern-BHS0V5Ng.mjs.map → url-pattern-K-KdXcSe.mjs.map} +1 -1
  643. package/dist/{user-sHgJ62SP.mjs → user-CtYY0tp2.mjs} +2 -2
  644. package/dist/{user-sHgJ62SP.mjs.map → user-CtYY0tp2.mjs.map} +1 -1
  645. package/dist/{utils-D4vD_Y98.mjs → utils-HztC73hq.mjs} +1 -1
  646. package/dist/{utils-D4vD_Y98.mjs.map → utils-HztC73hq.mjs.map} +1 -1
  647. package/dist/{validate-BkqIG_Uj.mjs → validate-Dd-IUjnZ.mjs} +2 -2
  648. package/dist/{validate-BkqIG_Uj.mjs.map → validate-Dd-IUjnZ.mjs.map} +1 -1
  649. package/dist/{validate-DeGtBN84.d.mts → validate-DlqdOZXB.d.mts} +5 -5
  650. package/dist/{validate-DeGtBN84.d.mts.map → validate-DlqdOZXB.d.mts.map} +1 -1
  651. package/dist/{validation-CiJb_X5o.mjs → validation-D83n7Npi.mjs} +10 -8
  652. package/dist/{validation-CiJb_X5o.mjs.map → validation-D83n7Npi.mjs.map} +1 -1
  653. package/dist/version-ri-sCyAp.mjs +7 -0
  654. package/dist/{version-x0NjTScG.mjs.map → version-ri-sCyAp.mjs.map} +1 -1
  655. package/dist/{widgets-Cwzvww0W.mjs → widgets-CpmZd9Q-.mjs} +4 -4
  656. package/dist/{widgets-Cwzvww0W.mjs.map → widgets-CpmZd9Q-.mjs.map} +1 -1
  657. package/dist/{wxr-taxonomies-CmC0ZsDr.d.mts → wxr-taxonomies-BFd0y72_.d.mts} +1 -1
  658. package/dist/{wxr-taxonomies-CmC0ZsDr.d.mts.map → wxr-taxonomies-BFd0y72_.d.mts.map} +1 -1
  659. package/dist/{wxr-taxonomies-QZjWxjyk.mjs → wxr-taxonomies-Df4vxWcU.mjs} +4 -4
  660. package/dist/{wxr-taxonomies-QZjWxjyk.mjs.map → wxr-taxonomies-Df4vxWcU.mjs.map} +1 -1
  661. package/dist/{zod-generator-D11MhxG4.mjs → zod-generator-DylzusHh.mjs} +24 -18
  662. package/dist/zod-generator-DylzusHh.mjs.map +1 -0
  663. package/package.json +32 -8
  664. package/src/api/errors.ts +20 -0
  665. package/src/api/handlers/index.ts +17 -0
  666. package/src/api/handlers/media-allowlist.ts +1 -0
  667. package/src/api/handlers/media-folders.ts +130 -0
  668. package/src/api/handlers/media-usage-activation.ts +102 -0
  669. package/src/api/handlers/media-usage.ts +108 -0
  670. package/src/api/handlers/media.ts +62 -2
  671. package/src/api/handlers/registry.ts +134 -316
  672. package/src/api/openapi/document.ts +192 -2
  673. package/src/api/schemas/media-usage.ts +77 -0
  674. package/src/api/schemas/media.ts +69 -0
  675. package/src/astro/integration/index.ts +46 -14
  676. package/src/astro/integration/routes.ts +18 -0
  677. package/src/astro/integration/runtime.ts +3 -0
  678. package/src/astro/integration/vite-config.ts +2 -7
  679. package/src/astro/middleware/auth.ts +4 -0
  680. package/src/astro/middleware/scoped-db.ts +21 -0
  681. package/src/astro/middleware.ts +67 -17
  682. package/src/astro/routes/api/admin/media-usage/activation.ts +42 -0
  683. package/src/astro/routes/api/admin/media-usage/progress.ts +37 -0
  684. package/src/astro/routes/api/admin/plugins/registry/artifact.ts +246 -96
  685. package/src/astro/routes/api/auth/dev-bypass.ts +0 -4
  686. package/src/astro/routes/api/auth/me.ts +14 -2
  687. package/src/astro/routes/api/media/[id].ts +3 -0
  688. package/src/astro/routes/api/media/folders/[id].ts +55 -0
  689. package/src/astro/routes/api/media/folders/index.ts +37 -0
  690. package/src/astro/routes/api/media.ts +12 -2
  691. package/src/astro/routes/api/setup/dev-bypass.ts +14 -7
  692. package/src/astro/routes/api/setup/index.ts +5 -13
  693. package/src/astro/types.ts +11 -1
  694. package/src/cli/commands/migrate.ts +593 -0
  695. package/src/cli/index.ts +2 -23
  696. package/src/client/index.ts +140 -2
  697. package/src/client/transport.ts +6 -2
  698. package/src/components/EmDashImage.astro +154 -50
  699. package/src/components/Gallery.astro +7 -0
  700. package/src/components/InlinePortableTextEditor.tsx +130 -1
  701. package/src/components/LiveSearch.astro +23 -0
  702. package/src/components/inline-code-block.tsx +180 -49
  703. package/src/content/converters/gallery.ts +2 -0
  704. package/src/content/converters/types.ts +2 -0
  705. package/src/database/connection.ts +2 -31
  706. package/src/database/index.ts +9 -3
  707. package/src/database/json-recordset.ts +16 -0
  708. package/src/database/migrations/019_i18n.ts +1 -1
  709. package/src/database/migrations/071_restore_content_bylines_table.ts +43 -0
  710. package/src/database/migrations/072_media_folders.ts +72 -0
  711. package/src/database/migrations/073_media_focal_point.ts +57 -0
  712. package/src/database/migrations/policy.ts +70 -0
  713. package/src/database/migrations/runner.ts +57 -5
  714. package/src/database/repositories/content.ts +5 -4
  715. package/src/database/repositories/media-folders.ts +115 -0
  716. package/src/database/repositories/media-usage-work.ts +395 -111
  717. package/src/database/repositories/media-usage.ts +1038 -10
  718. package/src/database/repositories/media.ts +97 -29
  719. package/src/database/transaction.ts +1 -0
  720. package/src/database/types.ts +13 -0
  721. package/src/db/adapters.ts +52 -4
  722. package/src/db/index.ts +3 -1
  723. package/src/db/libsql-migrations.ts +72 -0
  724. package/src/db/node-sqlite-compat.ts +78 -0
  725. package/src/db/postgres-migrations.ts +59 -0
  726. package/src/db/sqlite-migrations.ts +37 -0
  727. package/src/db/sqlite.ts +3 -3
  728. package/src/emdash-runtime.ts +154 -120
  729. package/src/fields/image.ts +7 -1
  730. package/src/fields/types.ts +6 -2
  731. package/src/i18n/normalize.ts +31 -0
  732. package/src/index.ts +12 -1
  733. package/src/media/focal-point.ts +37 -0
  734. package/src/media/local-runtime.ts +8 -0
  735. package/src/media/mime.ts +1 -0
  736. package/src/media/normalize.ts +10 -0
  737. package/src/media/types.ts +10 -0
  738. package/src/media/usage/activation.ts +162 -35
  739. package/src/media/usage/capture-triggers.ts +19 -3
  740. package/src/media/usage/collection-deletion-processor.ts +7 -3
  741. package/src/media/usage/collection-deletion.ts +10 -0
  742. package/src/media/usage/content-refresh.ts +258 -39
  743. package/src/media/usage/content-snapshots.ts +128 -31
  744. package/src/media/usage/extractor.ts +32 -13
  745. package/src/media/usage/maintenance-engine.ts +166 -0
  746. package/src/media/usage/reconciliation-processor.ts +106 -29
  747. package/src/media/usage/reconciliation.ts +90 -7
  748. package/src/media/usage/work-processor.ts +342 -101
  749. package/src/migrations/config-loader.ts +205 -0
  750. package/src/migrations/direct-executor.ts +141 -0
  751. package/src/migrations/identity.ts +46 -0
  752. package/src/migrations/index.ts +19 -0
  753. package/src/migrations/integration-metadata.ts +67 -0
  754. package/src/migrations/manifest-builder.ts +55 -0
  755. package/src/migrations/manifest-writer.ts +63 -0
  756. package/src/migrations/manifest.ts +197 -0
  757. package/src/migrations/protocol.ts +59 -0
  758. package/src/migrations/target.ts +35 -0
  759. package/src/plugins/index.ts +1 -0
  760. package/src/plugins/save-rejection.ts +20 -0
  761. package/src/plugins/scheduler/node.ts +1 -13
  762. package/src/plugins/scheduler/types.ts +0 -2
  763. package/src/plugins/state.ts +13 -3
  764. package/src/registry/artifact-fetch.ts +530 -0
  765. package/src/registry/config.ts +24 -6
  766. package/src/registry/types.ts +12 -15
  767. package/src/schema/types.ts +1 -0
  768. package/src/schema/zod-generator.ts +12 -4
  769. package/src/security/ssrf.ts +21 -2
  770. package/src/seed/apply.ts +4 -4
  771. package/src/ui.ts +2 -0
  772. package/src/virtual-modules.d.ts +8 -0
  773. package/dist/adapters-34EN2eXO.d.mts.map +0 -1
  774. package/dist/api-DdAsirBe.mjs.map +0 -1
  775. package/dist/collection-deletion-C8BugSGz.mjs.map +0 -1
  776. package/dist/content-BNfwh7xX.mjs.map +0 -1
  777. package/dist/content-refresh-DNERP4-f.mjs.map +0 -1
  778. package/dist/errors-CLUPm7aD.mjs.map +0 -1
  779. package/dist/index-C3Y-rsyN.d.mts.map +0 -1
  780. package/dist/media-BWWWb8kg.mjs.map +0 -1
  781. package/dist/media-CzhAn2--.mjs.map +0 -1
  782. package/dist/media-allowlist-BD6HhgXA.mjs.map +0 -1
  783. package/dist/media-usage-De3qvwjc.mjs.map +0 -1
  784. package/dist/media-usage-work-DrCbqBcl.mjs.map +0 -1
  785. package/dist/menus-CC-yn1F9.mjs.map +0 -1
  786. package/dist/mime-DfIVjNkr.mjs.map +0 -1
  787. package/dist/normalize-Bc-s_r-a.mjs.map +0 -1
  788. package/dist/placeholder-CzW7Lshs.d.mts.map +0 -1
  789. package/dist/registry-C581V4TP.mjs.map +0 -1
  790. package/dist/relations-DItI3MN9.d.mts.map +0 -1
  791. package/dist/runner-BiDvq5dv.mjs.map +0 -1
  792. package/dist/runner-CgQbELYC.d.mts.map +0 -1
  793. package/dist/state-DYwPue40.mjs.map +0 -1
  794. package/dist/types-C3_AbkDP.d.mts.map +0 -1
  795. package/dist/types-CAGdePf3.d.mts.map +0 -1
  796. package/dist/version-x0NjTScG.mjs +0 -7
  797. package/dist/zod-generator-D11MhxG4.mjs.map +0 -1
  798. /package/dist/{api-tokens-CyTsLszZ.mjs → api-tokens-B5NHLNUN.mjs} +0 -0
  799. /package/dist/{ssrf-DpXgKsna.mjs → ssrf-BPKzkm6O.mjs} +0 -0
  800. /package/dist/{types-CP1CflWf.mjs → types-D6OcIp2P.mjs} +0 -0
@@ -9,11 +9,21 @@ import {
9
9
  } from "kysely";
10
10
  import { ulid } from "ulidx";
11
11
 
12
+ import {
13
+ MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION,
14
+ MediaUsageActivationVersionMismatchError,
15
+ } from "../../media/usage/activation.js";
12
16
  import { isMediaUsageProjectionFingerprint } from "../../media/usage/projection-fingerprint.js";
13
17
  import type { MediaUsageContentSourceVariant } from "../../media/usage/source-key.js";
14
- import type { MediaKind, MediaUsageReferenceType } from "../../media/usage/types.js";
18
+ import {
19
+ CONTENT_SOURCE_SCHEMA_VERSION,
20
+ type MediaKind,
21
+ type MediaUsageReferenceType,
22
+ } from "../../media/usage/types.js";
23
+ import { getRequestContext } from "../../request-context.js";
15
24
  import { chunks, SQL_BATCH_SIZE } from "../../utils/chunks.js";
16
25
  import { isPostgres } from "../dialect-helpers.js";
26
+ import { jsonTextValues } from "../json-recordset.js";
17
27
  import { withTransaction } from "../transaction.js";
18
28
  import type {
19
29
  Database,
@@ -33,11 +43,30 @@ type MediaUsageSourceNullableStringColumn =
33
43
  | "updated_at"
34
44
  | "last_attempted_at"
35
45
  | "last_error_code";
46
+ type BatchSourceExpectedColumn =
47
+ | "source.collection_id"
48
+ | "source.source_fingerprint"
49
+ | "source.source_updated_at"
50
+ | "source.source_version"
51
+ | "source.identity_version"
52
+ | "source.revision_id"
53
+ | "source.last_attempted_at"
54
+ | "source.last_error_code";
55
+ type BatchInputExpectedColumn =
56
+ | "input.expected_collection_id"
57
+ | "input.expected_source_fingerprint"
58
+ | "input.expected_source_updated_at"
59
+ | "input.expected_source_version"
60
+ | "input.expected_identity_version"
61
+ | "input.expected_revision_id"
62
+ | "input.expected_last_attempted_at"
63
+ | "input.expected_last_error_code";
36
64
  const OCCURRENCE_BIND_COLUMNS = 13;
65
+ const D1_MAX_BOUND_PARAMETERS = 100;
37
66
  export const MEDIA_USAGE_GENERATION_WRITE_LEASE_MS = 60 * 60 * 1000;
38
67
  const OCCURRENCE_INSERT_BATCH_SIZE = Math.max(
39
68
  1,
40
- Math.floor(SQL_BATCH_SIZE / OCCURRENCE_BIND_COLUMNS),
69
+ Math.floor(D1_MAX_BOUND_PARAMETERS / OCCURRENCE_BIND_COLUMNS),
41
70
  );
42
71
 
43
72
  function cleanupDeleteBatchSize(cleanupLease: MediaUsageCleanupLease | undefined): number {
@@ -48,6 +77,84 @@ function canIssueCleanupStatement(canIssueStatement: (() => boolean) | undefined
48
77
  return canIssueStatement?.() ?? true;
49
78
  }
50
79
 
80
+ function nullableRefMatch(
81
+ left: "content.live_revision_id" | "content.draft_revision_id",
82
+ right: "input.revision_id",
83
+ ): RawBuilder<boolean> {
84
+ const leftRef = sql.ref(left);
85
+ const rightRef = sql.ref(right);
86
+ return sql<boolean>`(
87
+ (${leftRef} IS NULL AND ${rightRef} IS NULL)
88
+ OR ${leftRef} = ${rightRef}
89
+ )`;
90
+ }
91
+
92
+ const MAX_JSON_BIND_BYTES = 1_900_000;
93
+ const MEDIA_USAGE_EVENT_QUERY_CEILING = 900;
94
+ const MEDIA_USAGE_QUERY_RESERVE = 150;
95
+
96
+ function chunkJsonRows<T>(rows: readonly T[]): T[][] {
97
+ const batches: T[][] = [];
98
+ let batch: T[] = [];
99
+ let batchBytes = 2;
100
+ for (const row of rows) {
101
+ const rowBytes =
102
+ new TextEncoder().encode(JSON.stringify(row)).byteLength + (batch.length > 0 ? 1 : 0);
103
+ if (batch.length > 0 && batchBytes + rowBytes > MAX_JSON_BIND_BYTES) {
104
+ batches.push(batch);
105
+ batch = [];
106
+ batchBytes = 2;
107
+ }
108
+ batch.push(row);
109
+ batchBytes += rowBytes;
110
+ }
111
+ if (batch.length > 0) batches.push(batch);
112
+ return batches;
113
+ }
114
+
115
+ function mediaUsageQueryBudgetAllows(queries: number): boolean {
116
+ const metrics = getRequestContext()?.metrics;
117
+ return (
118
+ !metrics ||
119
+ metrics.dbCount + queries + MEDIA_USAGE_QUERY_RESERVE <= MEDIA_USAGE_EVENT_QUERY_CEILING
120
+ );
121
+ }
122
+
123
+ function mergeInto<T>(target: Set<T>, source: ReadonlySet<T>): void {
124
+ for (const value of source) target.add(value);
125
+ }
126
+
127
+ function batchOccurrenceRows(
128
+ prepared: readonly {
129
+ projection: { occurrences: readonly MediaUsageOccurrenceInput[] };
130
+ generation: string;
131
+ leaseToken: string;
132
+ row: { source_key: string };
133
+ }[],
134
+ now: string,
135
+ ) {
136
+ return prepared.flatMap((item) =>
137
+ item.projection.occurrences.map((occurrence) => ({
138
+ id: ulid(),
139
+ source_key: item.row.source_key,
140
+ generation: item.generation,
141
+ field_slug: occurrence.fieldSlug,
142
+ field_path: occurrence.fieldPath,
143
+ occurrence_index: occurrence.occurrenceIndex ?? 0,
144
+ reference_type: occurrence.referenceType,
145
+ media_id: occurrence.mediaId,
146
+ provider: occurrence.provider,
147
+ provider_asset_id: occurrence.providerAssetId,
148
+ media_kind: occurrence.mediaKind ?? null,
149
+ mime_type: occurrence.mimeType ?? null,
150
+ created_at: now,
151
+ lease_token: item.leaseToken,
152
+ })),
153
+ );
154
+ }
155
+
156
+ type BatchOccurrenceRow = ReturnType<typeof batchOccurrenceRows>[number];
157
+
51
158
  function cleanupDurationSeconds(value: number): number {
52
159
  if (!Number.isSafeInteger(value) || value < 0) {
53
160
  throw new Error("Media usage cleanup duration must be a non-negative whole number of seconds");
@@ -168,6 +275,15 @@ export interface MediaUsageGuardedReplaceResult {
168
275
  source: MediaUsageSource | null;
169
276
  }
170
277
 
278
+ export interface MediaUsageNewSourceProjection {
279
+ source: MediaUsageSourceInput;
280
+ occurrences: readonly MediaUsageOccurrenceInput[];
281
+ }
282
+
283
+ export interface MediaUsageExistingSourceProjection extends MediaUsageNewSourceProjection {
284
+ expectedSource: MediaUsageSource;
285
+ }
286
+
171
287
  export interface MediaUsageGuardedDeleteResult {
172
288
  deleted: boolean;
173
289
  source: MediaUsageSource | null;
@@ -340,6 +456,12 @@ export interface MediaUsageCollectionIndexStatusScope {
340
456
  reconciliationRequired: boolean;
341
457
  }
342
458
 
459
+ export interface MediaUsageCollectionProgress {
460
+ status: "indexing" | "ready" | "needs_attention";
461
+ readyCollections: number;
462
+ totalCollections: number;
463
+ }
464
+
343
465
  export interface MediaUsageEntrySource {
344
466
  source: MediaUsageSource;
345
467
  occurrences: MediaUsageOccurrence[];
@@ -537,14 +659,15 @@ export class MediaUsageRepository {
537
659
  const uniqueSourceKeys = [...new Set(sourceKeys)];
538
660
  const sources = new Map<string, MediaUsageSource>();
539
661
  if (uniqueSourceKeys.length === 0) return sources;
540
-
541
- for (const sourceKeyBatch of chunks(uniqueSourceKeys, SQL_BATCH_SIZE)) {
542
- const rows = await this.db
543
- .selectFrom("_emdash_media_usage_sources")
544
- .selectAll()
545
- .where("source_key", "in", sourceKeyBatch)
546
- .execute();
547
- for (const row of rows) {
662
+ for (const sourceKeyBatch of chunkJsonRows(uniqueSourceKeys)) {
663
+ const input = jsonTextValues(this.db, sourceKeyBatch);
664
+ const result = await sql<Selectable<MediaUsageSourceTable>>`
665
+ WITH requested AS (${input})
666
+ SELECT source.*
667
+ FROM _emdash_media_usage_sources AS source
668
+ INNER JOIN requested ON requested.value = source.source_key
669
+ `.execute(this.db);
670
+ for (const row of result.rows) {
548
671
  const source = rowToSource(row);
549
672
  sources.set(source.sourceKey, source);
550
673
  }
@@ -619,6 +742,441 @@ export class MediaUsageRepository {
619
742
  };
620
743
  }
621
744
 
745
+ async replaceNewSourcesBatch(
746
+ projections: readonly MediaUsageNewSourceProjection[],
747
+ ): Promise<Set<string>> {
748
+ const unique = [
749
+ ...new Map(
750
+ projections.map((projection) => [projection.source.sourceKey, projection]),
751
+ ).values(),
752
+ ];
753
+ if (unique.length === 0) return new Set();
754
+ const collectionSlug = unique[0]?.source.collectionSlug;
755
+ const collectionId = unique[0]?.source.collectionId;
756
+ if (!collectionSlug || !collectionId) {
757
+ throw new Error("Canonical media usage batch requires collection identity");
758
+ }
759
+ validateIdentifier(collectionSlug, "collection slug");
760
+ if (
761
+ unique.some(
762
+ (projection) =>
763
+ projection.source.collectionSlug !== collectionSlug ||
764
+ projection.source.collectionId !== collectionId,
765
+ )
766
+ ) {
767
+ throw new Error("Canonical media usage batch must contain one collection");
768
+ }
769
+
770
+ const now = new Date().toISOString();
771
+ const expiresAt = new Date(Date.now() + MEDIA_USAGE_GENERATION_WRITE_LEASE_MS).toISOString();
772
+ const prepared = unique.map((projection) => {
773
+ const generation = ulid();
774
+ return {
775
+ projection,
776
+ generation,
777
+ leaseToken: ulid(),
778
+ row: this.buildSourceRow(projection.source, generation, now),
779
+ };
780
+ });
781
+ const sourceRows = prepared.map((item) => ({
782
+ ...item.row,
783
+ lease_token: item.leaseToken,
784
+ }));
785
+ const occurrenceRows = batchOccurrenceRows(prepared, now);
786
+ const estimatedQueries =
787
+ 2 + chunkJsonRows(occurrenceRows).length + chunkJsonRows(sourceRows).length;
788
+ if (!mediaUsageQueryBudgetAllows(estimatedQueries)) {
789
+ if (unique.length === 1) return new Set();
790
+ const midpoint = Math.ceil(unique.length / 2);
791
+ const inserted = new Set<string>();
792
+ mergeInto(inserted, await this.replaceNewSourcesBatch(unique.slice(0, midpoint)));
793
+ mergeInto(inserted, await this.replaceNewSourcesBatch(unique.slice(midpoint)));
794
+ return inserted;
795
+ }
796
+
797
+ const leasesPayload = JSON.stringify(
798
+ prepared.map((item) => ({
799
+ source_key: item.row.source_key,
800
+ collection_id: item.row.collection_id,
801
+ collection_slug: item.row.collection_slug,
802
+ generation: item.generation,
803
+ lease_token: item.leaseToken,
804
+ expires_at: expiresAt,
805
+ created_at: now,
806
+ })),
807
+ );
808
+ const leases = this.generationWriteBatchInput(leasesPayload);
809
+ await sql`
810
+ WITH input AS (${leases})
811
+ INSERT INTO _emdash_media_usage_generation_writes (
812
+ source_key, generation, lease_token, expires_at, created_at
813
+ )
814
+ SELECT source_key, generation, lease_token, expires_at, created_at
815
+ FROM input
816
+ WHERE EXISTS (
817
+ SELECT 1
818
+ FROM _emdash_collections AS collection
819
+ INNER JOIN _emdash_media_usage_index_status AS status
820
+ ON status.collection_id = collection.id
821
+ AND status.scope_key = collection.slug
822
+ WHERE collection.id = input.collection_id
823
+ AND collection.slug = input.collection_slug
824
+ AND status.adapter_id = 'content-media'
825
+ AND status.scope_type = 'collection'
826
+ AND status.capture_state = 'active'
827
+ AND NOT EXISTS (
828
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
829
+ WHERE deletion.collection_id = input.collection_id
830
+ )
831
+ )
832
+ `.execute(this.db);
833
+
834
+ try {
835
+ await this.insertBatchOccurrences(occurrenceRows);
836
+ const tableName = `ec_${collectionSlug}`;
837
+ validateIdentifier(tableName, "content table");
838
+ const inserted = new Set<string>();
839
+ for (const sourceRowBatch of chunkJsonRows(sourceRows)) {
840
+ const input = this.sourceBatchInput(JSON.stringify(sourceRowBatch));
841
+ const result = await sql<{ source_key: string }>`
842
+ WITH input AS (${input})
843
+ INSERT INTO _emdash_media_usage_sources (
844
+ source_key, source_type, collection_id, collection_slug, content_id,
845
+ source_variant, locale, translation_group, content_slug, content_title,
846
+ content_status, content_scheduled_at, content_deleted_at, revision_id,
847
+ current_generation, schema_version, source_updated_at, source_version,
848
+ source_fingerprint, identity_version, source_completeness,
849
+ last_attempted_at, last_error_code, indexed_at, updated_at
850
+ )
851
+ SELECT
852
+ input.source_key, input.source_type, input.collection_id, input.collection_slug,
853
+ input.content_id, input.source_variant, input.locale, input.translation_group,
854
+ input.content_slug, input.content_title, input.content_status,
855
+ input.content_scheduled_at, input.content_deleted_at, input.revision_id,
856
+ input.current_generation, input.schema_version, input.source_updated_at,
857
+ input.source_version, input.source_fingerprint, input.identity_version,
858
+ input.source_completeness, input.last_attempted_at, input.last_error_code,
859
+ input.indexed_at, input.updated_at
860
+ FROM input
861
+ WHERE EXISTS (
862
+ SELECT 1
863
+ FROM _emdash_media_usage_generation_writes AS writer
864
+ WHERE writer.source_key = input.source_key
865
+ AND writer.generation = input.current_generation
866
+ AND writer.lease_token = input.lease_token
867
+ AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
868
+ )
869
+ AND EXISTS (
870
+ SELECT 1
871
+ FROM _emdash_collections AS collection
872
+ INNER JOIN _emdash_media_usage_index_status AS status
873
+ ON status.collection_id = collection.id
874
+ AND status.scope_key = collection.slug
875
+ WHERE collection.id = input.collection_id
876
+ AND collection.slug = input.collection_slug
877
+ AND status.adapter_id = 'content-media'
878
+ AND status.scope_type = 'collection'
879
+ AND status.capture_state = 'active'
880
+ AND NOT EXISTS (
881
+ SELECT 1
882
+ FROM _emdash_media_usage_collection_deletions AS deletion
883
+ WHERE deletion.collection_id = input.collection_id
884
+ )
885
+ )
886
+ AND EXISTS (
887
+ SELECT 1
888
+ FROM ${sql.ref(tableName)} AS content
889
+ WHERE content.id = input.content_id
890
+ AND content.version = input.source_version
891
+ AND content.updated_at = input.source_updated_at
892
+ AND (
893
+ (input.source_variant = 'columns' AND ${nullableRefMatch("content.live_revision_id", "input.revision_id")})
894
+ OR
895
+ (input.source_variant = 'draft_overlay' AND ${nullableRefMatch("content.draft_revision_id", "input.revision_id")})
896
+ )
897
+ )
898
+ ON CONFLICT (source_key) DO NOTHING
899
+ RETURNING source_key
900
+ `.execute(this.db);
901
+ for (const row of result.rows) inserted.add(row.source_key);
902
+ }
903
+ return inserted;
904
+ } finally {
905
+ await sql`
906
+ WITH input AS (${leases})
907
+ DELETE FROM _emdash_media_usage_generation_writes AS writer
908
+ WHERE EXISTS (
909
+ SELECT 1
910
+ FROM input
911
+ WHERE input.source_key = writer.source_key
912
+ AND input.generation = writer.generation
913
+ AND input.lease_token = writer.lease_token
914
+ )
915
+ `.execute(this.db);
916
+ }
917
+ }
918
+
919
+ async replaceExistingSourcesBatch(
920
+ projections: readonly MediaUsageExistingSourceProjection[],
921
+ ): Promise<Set<string>> {
922
+ const unique = [
923
+ ...new Map(
924
+ projections.map((projection) => [projection.source.sourceKey, projection]),
925
+ ).values(),
926
+ ];
927
+ if (unique.length === 0) return new Set();
928
+ const collectionSlug = unique[0]?.source.collectionSlug;
929
+ const collectionId = unique[0]?.source.collectionId;
930
+ if (!collectionSlug || !collectionId) {
931
+ throw new Error("Canonical media usage batch requires collection identity");
932
+ }
933
+ validateIdentifier(collectionSlug, "collection slug");
934
+ if (
935
+ unique.some(
936
+ (projection) =>
937
+ projection.source.collectionSlug !== collectionSlug ||
938
+ projection.source.collectionId !== collectionId,
939
+ )
940
+ ) {
941
+ throw new Error("Canonical media usage batch must contain one collection");
942
+ }
943
+
944
+ const now = new Date().toISOString();
945
+ const expiresAt = new Date(Date.now() + MEDIA_USAGE_GENERATION_WRITE_LEASE_MS).toISOString();
946
+ const prepared = unique.map((projection) => {
947
+ const generation = ulid();
948
+ return {
949
+ projection,
950
+ generation,
951
+ leaseToken: ulid(),
952
+ row: this.buildSourceRow(projection.source, generation, now),
953
+ };
954
+ });
955
+ const sourceRows = prepared.map((item) => ({
956
+ ...item.row,
957
+ lease_token: item.leaseToken,
958
+ expected_generation: item.projection.expectedSource.currentGeneration,
959
+ expected_collection_id: item.projection.expectedSource.collectionId,
960
+ expected_updated_at: item.projection.expectedSource.updatedAt,
961
+ expected_source_fingerprint: item.projection.expectedSource.sourceFingerprint,
962
+ expected_source_updated_at: item.projection.expectedSource.sourceUpdatedAt,
963
+ expected_source_version: item.projection.expectedSource.sourceVersion,
964
+ expected_identity_version: item.projection.expectedSource.identityVersion,
965
+ expected_revision_id: item.projection.expectedSource.revisionId,
966
+ expected_source_completeness: item.projection.expectedSource.sourceCompleteness,
967
+ expected_last_attempted_at: item.projection.expectedSource.lastAttemptedAt,
968
+ expected_last_error_code: item.projection.expectedSource.lastErrorCode,
969
+ }));
970
+ const occurrenceRows = batchOccurrenceRows(prepared, now);
971
+ const estimatedQueries =
972
+ 2 + chunkJsonRows(occurrenceRows).length + chunkJsonRows(sourceRows).length;
973
+ if (!mediaUsageQueryBudgetAllows(estimatedQueries)) {
974
+ if (unique.length === 1) return new Set();
975
+ const midpoint = Math.ceil(unique.length / 2);
976
+ const replaced = new Set<string>();
977
+ mergeInto(replaced, await this.replaceExistingSourcesBatch(unique.slice(0, midpoint)));
978
+ mergeInto(replaced, await this.replaceExistingSourcesBatch(unique.slice(midpoint)));
979
+ return replaced;
980
+ }
981
+ const leasesPayload = JSON.stringify(
982
+ prepared.map((item) => ({
983
+ source_key: item.row.source_key,
984
+ collection_id: item.row.collection_id,
985
+ collection_slug: item.row.collection_slug,
986
+ generation: item.generation,
987
+ lease_token: item.leaseToken,
988
+ expires_at: expiresAt,
989
+ created_at: now,
990
+ })),
991
+ );
992
+ const leases = this.generationWriteBatchInput(leasesPayload);
993
+ await sql`
994
+ WITH input AS (${leases})
995
+ INSERT INTO _emdash_media_usage_generation_writes (
996
+ source_key, generation, lease_token, expires_at, created_at
997
+ )
998
+ SELECT source_key, generation, lease_token, expires_at, created_at
999
+ FROM input
1000
+ WHERE EXISTS (
1001
+ SELECT 1
1002
+ FROM _emdash_collections AS collection
1003
+ INNER JOIN _emdash_media_usage_index_status AS status
1004
+ ON status.collection_id = collection.id
1005
+ AND status.scope_key = collection.slug
1006
+ WHERE collection.id = input.collection_id
1007
+ AND collection.slug = input.collection_slug
1008
+ AND status.adapter_id = 'content-media'
1009
+ AND status.scope_type = 'collection'
1010
+ AND status.capture_state = 'active'
1011
+ AND NOT EXISTS (
1012
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
1013
+ WHERE deletion.collection_id = input.collection_id
1014
+ )
1015
+ )
1016
+ `.execute(this.db);
1017
+
1018
+ try {
1019
+ await this.insertBatchOccurrences(occurrenceRows);
1020
+ const tableName = `ec_${collectionSlug}`;
1021
+ validateIdentifier(tableName, "content table");
1022
+ const replaced = new Set<string>();
1023
+ for (const sourceRowBatch of chunkJsonRows(sourceRows)) {
1024
+ const input = this.sourceBatchInput(JSON.stringify(sourceRowBatch));
1025
+ const result = await sql<{ source_key: string }>`
1026
+ WITH input AS (${input})
1027
+ UPDATE _emdash_media_usage_sources AS source
1028
+ SET source_type = input.source_type,
1029
+ collection_id = input.collection_id,
1030
+ collection_slug = input.collection_slug,
1031
+ content_id = input.content_id,
1032
+ source_variant = input.source_variant,
1033
+ locale = input.locale,
1034
+ translation_group = input.translation_group,
1035
+ content_slug = input.content_slug,
1036
+ content_title = input.content_title,
1037
+ content_status = input.content_status,
1038
+ content_scheduled_at = input.content_scheduled_at,
1039
+ content_deleted_at = input.content_deleted_at,
1040
+ revision_id = input.revision_id,
1041
+ current_generation = input.current_generation,
1042
+ schema_version = input.schema_version,
1043
+ source_updated_at = input.source_updated_at,
1044
+ source_version = input.source_version,
1045
+ source_fingerprint = input.source_fingerprint,
1046
+ identity_version = input.identity_version,
1047
+ source_completeness = input.source_completeness,
1048
+ last_attempted_at = input.last_attempted_at,
1049
+ last_error_code = input.last_error_code,
1050
+ indexed_at = input.indexed_at,
1051
+ updated_at = input.updated_at
1052
+ FROM input
1053
+ WHERE source.source_key = input.source_key
1054
+ AND source.current_generation = input.expected_generation
1055
+ AND ${this.batchRefsMatch("source.collection_id", "input.expected_collection_id")}
1056
+ AND source.updated_at = input.expected_updated_at
1057
+ AND ${this.batchRefsMatch("source.source_fingerprint", "input.expected_source_fingerprint")}
1058
+ AND ${this.batchRefsMatch("source.source_updated_at", "input.expected_source_updated_at")}
1059
+ AND ${this.batchRefsMatch("source.source_version", "input.expected_source_version")}
1060
+ AND ${this.batchRefsMatch("source.identity_version", "input.expected_identity_version")}
1061
+ AND ${this.batchRefsMatch("source.revision_id", "input.expected_revision_id")}
1062
+ AND source.source_completeness = input.expected_source_completeness
1063
+ AND ${this.batchRefsMatch("source.last_attempted_at", "input.expected_last_attempted_at")}
1064
+ AND ${this.batchRefsMatch("source.last_error_code", "input.expected_last_error_code")}
1065
+ AND EXISTS (
1066
+ SELECT 1
1067
+ FROM _emdash_media_usage_generation_writes AS writer
1068
+ WHERE writer.source_key = input.source_key
1069
+ AND writer.generation = input.current_generation
1070
+ AND writer.lease_token = input.lease_token
1071
+ AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
1072
+ )
1073
+ AND EXISTS (
1074
+ SELECT 1
1075
+ FROM _emdash_collections AS collection
1076
+ INNER JOIN _emdash_media_usage_index_status AS status
1077
+ ON status.collection_id = collection.id
1078
+ AND status.scope_key = collection.slug
1079
+ WHERE collection.id = input.collection_id
1080
+ AND collection.slug = input.collection_slug
1081
+ AND status.adapter_id = 'content-media'
1082
+ AND status.scope_type = 'collection'
1083
+ AND status.capture_state = 'active'
1084
+ AND NOT EXISTS (
1085
+ SELECT 1
1086
+ FROM _emdash_media_usage_collection_deletions AS deletion
1087
+ WHERE deletion.collection_id = input.collection_id
1088
+ )
1089
+ )
1090
+ AND EXISTS (
1091
+ SELECT 1
1092
+ FROM ${sql.ref(tableName)} AS content
1093
+ WHERE content.id = input.content_id
1094
+ AND content.version = input.source_version
1095
+ AND content.updated_at = input.source_updated_at
1096
+ AND (
1097
+ (input.source_variant = 'columns' AND ${nullableRefMatch("content.live_revision_id", "input.revision_id")})
1098
+ OR
1099
+ (input.source_variant = 'draft_overlay' AND ${nullableRefMatch("content.draft_revision_id", "input.revision_id")})
1100
+ )
1101
+ )
1102
+ RETURNING ${isPostgres(this.db) ? sql`source.source_key` : sql`source_key`} AS source_key
1103
+ `.execute(this.db);
1104
+ for (const row of result.rows) replaced.add(row.source_key);
1105
+ }
1106
+ return replaced;
1107
+ } finally {
1108
+ await sql`
1109
+ WITH input AS (${leases})
1110
+ DELETE FROM _emdash_media_usage_generation_writes AS writer
1111
+ WHERE EXISTS (
1112
+ SELECT 1
1113
+ FROM input
1114
+ WHERE input.source_key = writer.source_key
1115
+ AND input.generation = writer.generation
1116
+ AND input.lease_token = writer.lease_token
1117
+ )
1118
+ `.execute(this.db);
1119
+ }
1120
+ }
1121
+
1122
+ async matchingExistingSourcesBatch(
1123
+ projections: readonly MediaUsageExistingSourceProjection[],
1124
+ ): Promise<Set<string>> {
1125
+ if (projections.length === 0) return new Set();
1126
+ const now = new Date().toISOString();
1127
+ const rows = projections.map((projection) => ({
1128
+ ...this.buildSourceRow(projection.source, projection.expectedSource.currentGeneration, now),
1129
+ expected_generation: projection.expectedSource.currentGeneration,
1130
+ expected_collection_id: projection.expectedSource.collectionId,
1131
+ expected_updated_at: projection.expectedSource.updatedAt,
1132
+ expected_source_fingerprint: projection.expectedSource.sourceFingerprint,
1133
+ expected_source_updated_at: projection.expectedSource.sourceUpdatedAt,
1134
+ expected_source_version: projection.expectedSource.sourceVersion,
1135
+ expected_identity_version: projection.expectedSource.identityVersion,
1136
+ expected_revision_id: projection.expectedSource.revisionId,
1137
+ expected_source_completeness: projection.expectedSource.sourceCompleteness,
1138
+ expected_last_attempted_at: projection.expectedSource.lastAttemptedAt,
1139
+ expected_last_error_code: projection.expectedSource.lastErrorCode,
1140
+ }));
1141
+ if (!mediaUsageQueryBudgetAllows(chunkJsonRows(rows).length)) {
1142
+ if (projections.length === 1) return new Set();
1143
+ const midpoint = Math.ceil(projections.length / 2);
1144
+ const matched = new Set<string>();
1145
+ mergeInto(matched, await this.matchingExistingSourcesBatch(projections.slice(0, midpoint)));
1146
+ mergeInto(matched, await this.matchingExistingSourcesBatch(projections.slice(midpoint)));
1147
+ return matched;
1148
+ }
1149
+ const matched = new Set<string>();
1150
+ for (const batch of chunkJsonRows(rows)) {
1151
+ const input = this.sourceBatchInput(JSON.stringify(batch));
1152
+ const result = await sql<{ source_key: string }>`
1153
+ WITH input AS (${input})
1154
+ SELECT source.source_key
1155
+ FROM _emdash_media_usage_sources AS source
1156
+ INNER JOIN input ON input.source_key = source.source_key
1157
+ WHERE source.current_generation = input.expected_generation
1158
+ AND ${this.batchRefsMatch("source.collection_id", "input.expected_collection_id")}
1159
+ AND source.updated_at = input.expected_updated_at
1160
+ AND ${this.batchRefsMatch("source.source_fingerprint", "input.expected_source_fingerprint")}
1161
+ AND ${this.batchRefsMatch("source.source_updated_at", "input.expected_source_updated_at")}
1162
+ AND ${this.batchRefsMatch("source.source_version", "input.expected_source_version")}
1163
+ AND ${this.batchRefsMatch("source.identity_version", "input.expected_identity_version")}
1164
+ AND ${this.batchRefsMatch("source.revision_id", "input.expected_revision_id")}
1165
+ AND source.source_completeness = input.expected_source_completeness
1166
+ AND ${this.batchRefsMatch("source.last_attempted_at", "input.expected_last_attempted_at")}
1167
+ AND ${this.batchRefsMatch("source.last_error_code", "input.expected_last_error_code")}
1168
+ AND EXISTS (
1169
+ SELECT 1
1170
+ FROM _emdash_collections AS collection
1171
+ WHERE collection.id = source.collection_id
1172
+ AND collection.slug = source.collection_slug
1173
+ )
1174
+ `.execute(this.db);
1175
+ for (const row of result.rows) matched.add(row.source_key);
1176
+ }
1177
+ return matched;
1178
+ }
1179
+
622
1180
  async markSourceAttempted(source: MediaUsageSourceInput): Promise<MediaUsageSource> {
623
1181
  if (source.collectionId !== undefined && source.collectionId !== null) {
624
1182
  const expectedSource = await this.findSource(source.sourceKey);
@@ -762,6 +1320,129 @@ export class MediaUsageRepository {
762
1320
  }));
763
1321
  }
764
1322
 
1323
+ async findCollectionProgress(): Promise<MediaUsageCollectionProgress | null> {
1324
+ const result = await sql<{
1325
+ activation_active: boolean | number;
1326
+ activation_generation: number | string | null;
1327
+ needs_attention: boolean | number;
1328
+ ready_collections: number | string;
1329
+ total_collections: number | string;
1330
+ cleanup_pending: boolean | number;
1331
+ }>`
1332
+ WITH collection_progress AS (
1333
+ SELECT
1334
+ CASE WHEN status.status = 'complete'
1335
+ AND status.schema_version = ${CONTENT_SOURCE_SCHEMA_VERSION}
1336
+ AND status.reconciliation_required = 0
1337
+ AND status.capture_state = 'active'
1338
+ AND NOT EXISTS (
1339
+ SELECT 1 FROM _emdash_media_usage_work AS work
1340
+ WHERE work.collection_id = collection.id
1341
+ AND work.collection_slug = collection.slug
1342
+ )
1343
+ THEN 1 ELSE 0 END AS is_ready,
1344
+ CASE WHEN status.collection_id IS NULL
1345
+ OR COALESCE(status.capture_state, '') <> 'active'
1346
+ OR status.status NOT IN ('complete', 'never', 'running', 'partial', 'failed', 'stale')
1347
+ OR status.status = 'failed'
1348
+ OR (
1349
+ status.reconciliation_required = 0
1350
+ AND (
1351
+ status.status <> 'complete'
1352
+ OR COALESCE(status.schema_version, -1) <> ${CONTENT_SOURCE_SCHEMA_VERSION}
1353
+ )
1354
+ AND NOT EXISTS (
1355
+ SELECT 1 FROM _emdash_media_usage_work AS work
1356
+ WHERE work.collection_id = collection.id
1357
+ AND work.collection_slug = collection.slug
1358
+ )
1359
+ )
1360
+ OR EXISTS (
1361
+ SELECT 1 FROM _emdash_media_usage_work AS work
1362
+ WHERE work.collection_id = collection.id
1363
+ AND work.collection_slug = collection.slug
1364
+ AND work.state = 'failed'
1365
+ )
1366
+ OR EXISTS (
1367
+ SELECT 1 FROM _emdash_media_usage_reconciliations AS reconciliation
1368
+ WHERE reconciliation.collection_id = collection.id
1369
+ AND reconciliation.collection_slug = collection.slug
1370
+ AND reconciliation.state = 'failed'
1371
+ AND status.reconciliation_required = 1
1372
+ AND (
1373
+ reconciliation.target_epoch IS NULL
1374
+ OR reconciliation.target_epoch >= status.change_epoch
1375
+ )
1376
+ )
1377
+ THEN 1 ELSE 0 END AS needs_attention
1378
+ FROM _emdash_collections AS collection
1379
+ LEFT JOIN _emdash_media_usage_index_status AS status
1380
+ ON status.adapter_id = 'content-media'
1381
+ AND status.scope_type = 'collection'
1382
+ AND status.collection_id = collection.id
1383
+ AND status.scope_key = collection.slug
1384
+ WHERE NOT EXISTS (
1385
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
1386
+ WHERE deletion.collection_id = collection.id
1387
+ AND deletion.collection_slug = collection.slug
1388
+ )
1389
+ )
1390
+ SELECT
1391
+ (
1392
+ SELECT activation.runtime_generation
1393
+ FROM _emdash_media_usage_activation AS activation
1394
+ WHERE activation.task_key = 'incremental_capture'
1395
+ ) AS activation_generation,
1396
+ EXISTS (
1397
+ SELECT 1 FROM _emdash_media_usage_activation AS activation
1398
+ WHERE activation.task_key = 'incremental_capture'
1399
+ AND activation.state = 'active'
1400
+ ) AS activation_active,
1401
+ COUNT(*) AS total_collections,
1402
+ COALESCE(SUM(is_ready), 0) AS ready_collections,
1403
+ EXISTS (
1404
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
1405
+ WHERE deletion.state IN ('pending', 'retry', 'leased')
1406
+ ) AS cleanup_pending,
1407
+ CASE WHEN COALESCE(MAX(needs_attention), 0) = 1 OR EXISTS (
1408
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
1409
+ WHERE deletion.state = 'failed'
1410
+ ) THEN 1 ELSE 0 END AS needs_attention
1411
+ FROM collection_progress
1412
+ `.execute(this.db);
1413
+ const row = result.rows[0];
1414
+ if (!row) throw new Error("Media usage progress query returned no result");
1415
+ if (!row.activation_active) return null;
1416
+ if (Number(row.activation_generation) !== MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION) {
1417
+ throw new MediaUsageActivationVersionMismatchError(
1418
+ "Media usage activation runtime generation is incompatible",
1419
+ );
1420
+ }
1421
+ const readyCollections = Number(row.ready_collections);
1422
+ const totalCollections = Number(row.total_collections);
1423
+ const cleanupPending = Number(row.cleanup_pending);
1424
+ const needsAttention = Number(row.needs_attention);
1425
+ if (
1426
+ !Number.isSafeInteger(readyCollections) ||
1427
+ !Number.isSafeInteger(totalCollections) ||
1428
+ readyCollections < 0 ||
1429
+ totalCollections < readyCollections ||
1430
+ (cleanupPending !== 0 && cleanupPending !== 1) ||
1431
+ (needsAttention !== 0 && needsAttention !== 1)
1432
+ ) {
1433
+ throw new Error("Media usage progress query returned invalid counts");
1434
+ }
1435
+ return {
1436
+ status: needsAttention
1437
+ ? "needs_attention"
1438
+ : readyCollections === totalCollections && cleanupPending === 0
1439
+ ? "ready"
1440
+ : "indexing",
1441
+ readyCollections,
1442
+ totalCollections,
1443
+ };
1444
+ }
1445
+
765
1446
  async findCurrentEntryUsagePageByMediaId(
766
1447
  mediaId: string,
767
1448
  options: FindMediaUsageOptions = {},
@@ -1694,6 +2375,7 @@ export class MediaUsageRepository {
1694
2375
  last_error_code: sql<
1695
2376
  string | null
1696
2377
  >`CASE WHEN ${canComplete} THEN NULL ELSE last_error_code END`,
2378
+ cursor: sql<string | null>`CASE WHEN ${canComplete} THEN NULL ELSE cursor END`,
1697
2379
  last_incremental_success_at: now,
1698
2380
  updated_at: now,
1699
2381
  })
@@ -1715,6 +2397,94 @@ export class MediaUsageRepository {
1715
2397
  return Number(result.numUpdatedRows ?? 0) > 0;
1716
2398
  }
1717
2399
 
2400
+ async prepareIncrementalFinalization(
2401
+ input: MediaUsageIncrementalStatusIdentity,
2402
+ ): Promise<
2403
+ { outcome: "marked"; marker: string } | { outcome: "not_required" } | { outcome: "lost" }
2404
+ > {
2405
+ const observed = await this.db
2406
+ .selectFrom("_emdash_media_usage_index_status")
2407
+ .select(["change_epoch", "reconciliation_required"])
2408
+ .where("adapter_id", "=", "content-media")
2409
+ .where("scope_type", "=", "collection")
2410
+ .where("scope_key", "=", input.collectionSlug)
2411
+ .where("collection_id", "=", input.collectionId)
2412
+ .where("capture_state", "=", "active")
2413
+ .executeTakeFirst();
2414
+ if (!observed) return { outcome: "lost" };
2415
+ if (observed.reconciliation_required !== 0) return { outcome: "not_required" };
2416
+ const marker = `incremental-finalize:${String(observed.change_epoch)}:${ulid()}`;
2417
+ const result = await this.db
2418
+ .updateTable("_emdash_media_usage_index_status")
2419
+ .set({ cursor: marker, updated_at: this.sortableUtcTimestamp() })
2420
+ .where("adapter_id", "=", "content-media")
2421
+ .where("scope_type", "=", "collection")
2422
+ .where("scope_key", "=", input.collectionSlug)
2423
+ .where("collection_id", "=", input.collectionId)
2424
+ .where("capture_state", "=", "active")
2425
+ .where("reconciliation_required", "=", 0)
2426
+ .where("change_epoch", "=", observed.change_epoch)
2427
+ .where(
2428
+ sql<boolean>`EXISTS (
2429
+ SELECT 1
2430
+ FROM _emdash_collections AS collection
2431
+ WHERE collection.id = ${input.collectionId}
2432
+ AND collection.slug = ${input.collectionSlug}
2433
+ )`,
2434
+ )
2435
+ .where(
2436
+ sql<boolean>`NOT EXISTS (
2437
+ SELECT 1
2438
+ FROM _emdash_media_usage_collection_deletions AS deletion
2439
+ WHERE deletion.collection_id = ${input.collectionId}
2440
+ )`,
2441
+ )
2442
+ .executeTakeFirst();
2443
+ return Number(result.numUpdatedRows ?? 0) > 0
2444
+ ? { outcome: "marked", marker }
2445
+ : { outcome: "lost" };
2446
+ }
2447
+
2448
+ async recoverIncrementalFinalizations(): Promise<number> {
2449
+ const markerMatchesEpoch = isPostgres(this.db)
2450
+ ? sql<boolean>`status.cursor LIKE ('incremental-finalize:' || status.change_epoch::text || ':%')`
2451
+ : sql<boolean>`status.cursor LIKE ('incremental-finalize:' || CAST(status.change_epoch AS text) || ':%')`;
2452
+ const now = new Date().toISOString();
2453
+ const result = await sql<{ collection_id: string }>`
2454
+ UPDATE _emdash_media_usage_index_status AS status
2455
+ SET status = 'complete',
2456
+ completed_at = ${now},
2457
+ cursor = NULL,
2458
+ last_error_code = NULL,
2459
+ last_incremental_success_at = ${now},
2460
+ updated_at = ${now}
2461
+ WHERE status.adapter_id = 'content-media'
2462
+ AND status.scope_type = 'collection'
2463
+ AND status.capture_state = 'active'
2464
+ AND status.reconciliation_required = 0
2465
+ AND status.cursor IS NOT NULL
2466
+ AND ${markerMatchesEpoch}
2467
+ AND EXISTS (
2468
+ SELECT 1
2469
+ FROM _emdash_collections AS collection
2470
+ WHERE collection.id = status.collection_id
2471
+ AND collection.slug = status.scope_key
2472
+ )
2473
+ AND NOT EXISTS (
2474
+ SELECT 1
2475
+ FROM _emdash_media_usage_collection_deletions AS deletion
2476
+ WHERE deletion.collection_id = status.collection_id
2477
+ )
2478
+ AND NOT EXISTS (
2479
+ SELECT 1
2480
+ FROM _emdash_media_usage_work AS work
2481
+ WHERE work.collection_id = status.collection_id
2482
+ )
2483
+ RETURNING collection_id
2484
+ `.execute(this.db);
2485
+ return result.rows.length;
2486
+ }
2487
+
1718
2488
  async recordIncrementalFailure(
1719
2489
  input: MediaUsageIncrementalStatusIdentity & {
1720
2490
  contentId: string;
@@ -1778,6 +2548,71 @@ export class MediaUsageRepository {
1778
2548
  return Number(result.numUpdatedRows ?? 0) > 0;
1779
2549
  }
1780
2550
 
2551
+ async recordIncrementalFailuresByCollection(input: {
2552
+ collectionIds: readonly string[];
2553
+ errorCode: string;
2554
+ }): Promise<Set<string>> {
2555
+ const collectionIds = [...new Set(input.collectionIds)];
2556
+ if (collectionIds.length === 0) return new Set();
2557
+ const collections = jsonTextValues(this.db, collectionIds);
2558
+ const now = new Date().toISOString();
2559
+ const result = await sql<{ collection_id: string }>`
2560
+ WITH failed_collections AS (${collections})
2561
+ UPDATE _emdash_media_usage_index_status AS status
2562
+ SET status = CASE
2563
+ WHEN EXISTS (
2564
+ SELECT 1
2565
+ FROM _emdash_media_usage_reconciliations AS reconciliation
2566
+ WHERE reconciliation.collection_id = status.collection_id
2567
+ AND reconciliation.run_token = status.cursor
2568
+ ) THEN status.status
2569
+ WHEN status.reconciliation_required = 0 THEN 'partial'
2570
+ WHEN status.status = 'running' THEN 'stale'
2571
+ ELSE status.status
2572
+ END,
2573
+ completed_at = CASE
2574
+ WHEN EXISTS (
2575
+ SELECT 1
2576
+ FROM _emdash_media_usage_reconciliations AS reconciliation
2577
+ WHERE reconciliation.collection_id = status.collection_id
2578
+ AND reconciliation.run_token = status.cursor
2579
+ ) THEN status.completed_at
2580
+ WHEN status.reconciliation_required = 0 OR status.status = 'running' THEN NULL
2581
+ ELSE status.completed_at
2582
+ END,
2583
+ cursor = CASE
2584
+ WHEN EXISTS (
2585
+ SELECT 1
2586
+ FROM _emdash_media_usage_reconciliations AS reconciliation
2587
+ WHERE reconciliation.collection_id = status.collection_id
2588
+ AND reconciliation.run_token = status.cursor
2589
+ ) THEN status.cursor
2590
+ WHEN status.status = 'running' THEN NULL
2591
+ ELSE status.cursor
2592
+ END,
2593
+ last_error_code = ${input.errorCode},
2594
+ updated_at = ${now}
2595
+ WHERE status.adapter_id = 'content-media'
2596
+ AND status.scope_type = 'collection'
2597
+ AND status.collection_id IN (SELECT value FROM failed_collections)
2598
+ AND EXISTS (
2599
+ SELECT 1
2600
+ FROM _emdash_media_usage_work AS work
2601
+ WHERE work.collection_id = status.collection_id
2602
+ AND work.state = 'failed'
2603
+ AND work.last_error_code = ${input.errorCode}
2604
+ )
2605
+ AND EXISTS (
2606
+ SELECT 1
2607
+ FROM _emdash_collections AS collection
2608
+ WHERE collection.id = status.collection_id
2609
+ AND collection.slug = status.scope_key
2610
+ )
2611
+ RETURNING collection_id
2612
+ `.execute(this.db);
2613
+ return new Set(result.rows.map((row) => row.collection_id));
2614
+ }
2615
+
1781
2616
  async findIndexStatus(
1782
2617
  identity: MediaUsageIndexStatusIdentity,
1783
2618
  ): Promise<MediaUsageIndexStatus | null> {
@@ -2204,6 +3039,199 @@ export class MediaUsageRepository {
2204
3039
  }
2205
3040
  }
2206
3041
 
3042
+ private async insertBatchOccurrences(rows: readonly BatchOccurrenceRow[]): Promise<void> {
3043
+ for (const rowBatch of chunkJsonRows(rows)) {
3044
+ const input = this.occurrenceBatchInput(JSON.stringify(rowBatch));
3045
+ await sql`
3046
+ WITH input AS (${input})
3047
+ INSERT INTO _emdash_media_usage (
3048
+ id, source_key, generation, field_slug, field_path, occurrence_index,
3049
+ reference_type, media_id, provider, provider_asset_id, media_kind,
3050
+ mime_type, created_at
3051
+ )
3052
+ SELECT
3053
+ id, source_key, generation, field_slug, field_path, occurrence_index,
3054
+ reference_type, media_id, provider, provider_asset_id, media_kind,
3055
+ mime_type, created_at
3056
+ FROM input
3057
+ WHERE EXISTS (
3058
+ SELECT 1
3059
+ FROM _emdash_media_usage_generation_writes AS writer
3060
+ WHERE writer.source_key = input.source_key
3061
+ AND writer.generation = input.generation
3062
+ AND writer.lease_token = input.lease_token
3063
+ AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
3064
+ )
3065
+ `.execute(this.db);
3066
+ }
3067
+ }
3068
+
3069
+ private occurrenceBatchInput(payload: string): RawBuilder<unknown> {
3070
+ if (isPostgres(this.db)) {
3071
+ return sql`
3072
+ SELECT
3073
+ entry.value ->> 'id' AS id,
3074
+ entry.value ->> 'source_key' AS source_key,
3075
+ entry.value ->> 'generation' AS generation,
3076
+ entry.value ->> 'field_slug' AS field_slug,
3077
+ entry.value ->> 'field_path' AS field_path,
3078
+ CAST(entry.value ->> 'occurrence_index' AS integer) AS occurrence_index,
3079
+ entry.value ->> 'reference_type' AS reference_type,
3080
+ entry.value ->> 'media_id' AS media_id,
3081
+ entry.value ->> 'provider' AS provider,
3082
+ entry.value ->> 'provider_asset_id' AS provider_asset_id,
3083
+ entry.value ->> 'media_kind' AS media_kind,
3084
+ entry.value ->> 'mime_type' AS mime_type,
3085
+ entry.value ->> 'created_at' AS created_at,
3086
+ entry.value ->> 'lease_token' AS lease_token
3087
+ FROM jsonb_array_elements(${payload}::jsonb) AS entry(value)
3088
+ `;
3089
+ }
3090
+ return sql`
3091
+ SELECT
3092
+ json_extract(entry.value, '$.id') AS id,
3093
+ json_extract(entry.value, '$.source_key') AS source_key,
3094
+ json_extract(entry.value, '$.generation') AS generation,
3095
+ json_extract(entry.value, '$.field_slug') AS field_slug,
3096
+ json_extract(entry.value, '$.field_path') AS field_path,
3097
+ CAST(json_extract(entry.value, '$.occurrence_index') AS integer) AS occurrence_index,
3098
+ json_extract(entry.value, '$.reference_type') AS reference_type,
3099
+ json_extract(entry.value, '$.media_id') AS media_id,
3100
+ json_extract(entry.value, '$.provider') AS provider,
3101
+ json_extract(entry.value, '$.provider_asset_id') AS provider_asset_id,
3102
+ json_extract(entry.value, '$.media_kind') AS media_kind,
3103
+ json_extract(entry.value, '$.mime_type') AS mime_type,
3104
+ json_extract(entry.value, '$.created_at') AS created_at,
3105
+ json_extract(entry.value, '$.lease_token') AS lease_token
3106
+ FROM json_each(${payload}) AS entry
3107
+ `;
3108
+ }
3109
+
3110
+ private sourceBatchInput(payload: string): RawBuilder<unknown> {
3111
+ if (isPostgres(this.db)) {
3112
+ return sql`
3113
+ SELECT
3114
+ entry.value ->> 'source_key' AS source_key,
3115
+ entry.value ->> 'source_type' AS source_type,
3116
+ entry.value ->> 'collection_id' AS collection_id,
3117
+ entry.value ->> 'collection_slug' AS collection_slug,
3118
+ entry.value ->> 'content_id' AS content_id,
3119
+ entry.value ->> 'source_variant' AS source_variant,
3120
+ entry.value ->> 'locale' AS locale,
3121
+ entry.value ->> 'translation_group' AS translation_group,
3122
+ entry.value ->> 'content_slug' AS content_slug,
3123
+ entry.value ->> 'content_title' AS content_title,
3124
+ entry.value ->> 'content_status' AS content_status,
3125
+ entry.value ->> 'content_scheduled_at' AS content_scheduled_at,
3126
+ entry.value ->> 'content_deleted_at' AS content_deleted_at,
3127
+ entry.value ->> 'revision_id' AS revision_id,
3128
+ entry.value ->> 'current_generation' AS current_generation,
3129
+ CAST(entry.value ->> 'schema_version' AS integer) AS schema_version,
3130
+ entry.value ->> 'source_updated_at' AS source_updated_at,
3131
+ CAST(entry.value ->> 'source_version' AS bigint) AS source_version,
3132
+ entry.value ->> 'source_fingerprint' AS source_fingerprint,
3133
+ CAST(entry.value ->> 'identity_version' AS integer) AS identity_version,
3134
+ entry.value ->> 'source_completeness' AS source_completeness,
3135
+ entry.value ->> 'last_attempted_at' AS last_attempted_at,
3136
+ entry.value ->> 'last_error_code' AS last_error_code,
3137
+ entry.value ->> 'indexed_at' AS indexed_at,
3138
+ entry.value ->> 'updated_at' AS updated_at,
3139
+ entry.value ->> 'lease_token' AS lease_token,
3140
+ entry.value ->> 'expected_generation' AS expected_generation,
3141
+ entry.value ->> 'expected_collection_id' AS expected_collection_id,
3142
+ entry.value ->> 'expected_updated_at' AS expected_updated_at,
3143
+ entry.value ->> 'expected_source_fingerprint' AS expected_source_fingerprint,
3144
+ entry.value ->> 'expected_source_updated_at' AS expected_source_updated_at,
3145
+ CAST(entry.value ->> 'expected_source_version' AS bigint) AS expected_source_version,
3146
+ CAST(entry.value ->> 'expected_identity_version' AS integer) AS expected_identity_version,
3147
+ entry.value ->> 'expected_revision_id' AS expected_revision_id,
3148
+ entry.value ->> 'expected_source_completeness' AS expected_source_completeness,
3149
+ entry.value ->> 'expected_last_attempted_at' AS expected_last_attempted_at,
3150
+ entry.value ->> 'expected_last_error_code' AS expected_last_error_code
3151
+ FROM jsonb_array_elements(${payload}::jsonb) AS entry(value)
3152
+ `;
3153
+ }
3154
+ return sql`
3155
+ SELECT
3156
+ json_extract(entry.value, '$.source_key') AS source_key,
3157
+ json_extract(entry.value, '$.source_type') AS source_type,
3158
+ json_extract(entry.value, '$.collection_id') AS collection_id,
3159
+ json_extract(entry.value, '$.collection_slug') AS collection_slug,
3160
+ json_extract(entry.value, '$.content_id') AS content_id,
3161
+ json_extract(entry.value, '$.source_variant') AS source_variant,
3162
+ json_extract(entry.value, '$.locale') AS locale,
3163
+ json_extract(entry.value, '$.translation_group') AS translation_group,
3164
+ json_extract(entry.value, '$.content_slug') AS content_slug,
3165
+ json_extract(entry.value, '$.content_title') AS content_title,
3166
+ json_extract(entry.value, '$.content_status') AS content_status,
3167
+ json_extract(entry.value, '$.content_scheduled_at') AS content_scheduled_at,
3168
+ json_extract(entry.value, '$.content_deleted_at') AS content_deleted_at,
3169
+ json_extract(entry.value, '$.revision_id') AS revision_id,
3170
+ json_extract(entry.value, '$.current_generation') AS current_generation,
3171
+ CAST(json_extract(entry.value, '$.schema_version') AS integer) AS schema_version,
3172
+ json_extract(entry.value, '$.source_updated_at') AS source_updated_at,
3173
+ CAST(json_extract(entry.value, '$.source_version') AS integer) AS source_version,
3174
+ json_extract(entry.value, '$.source_fingerprint') AS source_fingerprint,
3175
+ CAST(json_extract(entry.value, '$.identity_version') AS integer) AS identity_version,
3176
+ json_extract(entry.value, '$.source_completeness') AS source_completeness,
3177
+ json_extract(entry.value, '$.last_attempted_at') AS last_attempted_at,
3178
+ json_extract(entry.value, '$.last_error_code') AS last_error_code,
3179
+ json_extract(entry.value, '$.indexed_at') AS indexed_at,
3180
+ json_extract(entry.value, '$.updated_at') AS updated_at,
3181
+ json_extract(entry.value, '$.lease_token') AS lease_token,
3182
+ json_extract(entry.value, '$.expected_generation') AS expected_generation,
3183
+ json_extract(entry.value, '$.expected_collection_id') AS expected_collection_id,
3184
+ json_extract(entry.value, '$.expected_updated_at') AS expected_updated_at,
3185
+ json_extract(entry.value, '$.expected_source_fingerprint') AS expected_source_fingerprint,
3186
+ json_extract(entry.value, '$.expected_source_updated_at') AS expected_source_updated_at,
3187
+ CAST(json_extract(entry.value, '$.expected_source_version') AS integer) AS expected_source_version,
3188
+ CAST(json_extract(entry.value, '$.expected_identity_version') AS integer) AS expected_identity_version,
3189
+ json_extract(entry.value, '$.expected_revision_id') AS expected_revision_id,
3190
+ json_extract(entry.value, '$.expected_source_completeness') AS expected_source_completeness,
3191
+ json_extract(entry.value, '$.expected_last_attempted_at') AS expected_last_attempted_at,
3192
+ json_extract(entry.value, '$.expected_last_error_code') AS expected_last_error_code
3193
+ FROM json_each(${payload}) AS entry
3194
+ `;
3195
+ }
3196
+
3197
+ private generationWriteBatchInput(payload: string): RawBuilder<unknown> {
3198
+ if (isPostgres(this.db)) {
3199
+ return sql`
3200
+ SELECT
3201
+ entry.value ->> 'source_key' AS source_key,
3202
+ entry.value ->> 'collection_id' AS collection_id,
3203
+ entry.value ->> 'collection_slug' AS collection_slug,
3204
+ entry.value ->> 'generation' AS generation,
3205
+ entry.value ->> 'lease_token' AS lease_token,
3206
+ entry.value ->> 'expires_at' AS expires_at,
3207
+ entry.value ->> 'created_at' AS created_at
3208
+ FROM jsonb_array_elements(${payload}::jsonb) AS entry(value)
3209
+ `;
3210
+ }
3211
+ return sql`
3212
+ SELECT
3213
+ json_extract(entry.value, '$.source_key') AS source_key,
3214
+ json_extract(entry.value, '$.collection_id') AS collection_id,
3215
+ json_extract(entry.value, '$.collection_slug') AS collection_slug,
3216
+ json_extract(entry.value, '$.generation') AS generation,
3217
+ json_extract(entry.value, '$.lease_token') AS lease_token,
3218
+ json_extract(entry.value, '$.expires_at') AS expires_at,
3219
+ json_extract(entry.value, '$.created_at') AS created_at
3220
+ FROM json_each(${payload}) AS entry
3221
+ `;
3222
+ }
3223
+
3224
+ private batchRefsMatch(
3225
+ left: BatchSourceExpectedColumn,
3226
+ right: BatchInputExpectedColumn,
3227
+ ): RawBuilder<boolean> {
3228
+ const leftRef = sql.ref(left);
3229
+ const rightRef = sql.ref(right);
3230
+ return isPostgres(this.db)
3231
+ ? sql<boolean>`${leftRef} IS NOT DISTINCT FROM ${rightRef}`
3232
+ : sql<boolean>`${leftRef} IS ${rightRef}`;
3233
+ }
3234
+
2207
3235
  private async lockCanonicalSourceCollection(
2208
3236
  db: DatabaseExecutor,
2209
3237
  source: MediaUsageSourceInput,