emdash 0.35.0 → 0.37.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 (971) hide show
  1. package/dist/activation-K0kGBWuJ.mjs +382 -0
  2. package/dist/activation-K0kGBWuJ.mjs.map +1 -0
  3. package/dist/{adapters-Bb1vG41D.d.mts → adapters-BDQwg236.d.mts} +6 -4
  4. package/dist/{adapters-Bb1vG41D.d.mts.map → adapters-BDQwg236.d.mts.map} +1 -1
  5. package/dist/{allowed-origins-DW7sYyqg.mjs → allowed-origins-BGglilcs.mjs} +2 -2
  6. package/dist/{allowed-origins-DW7sYyqg.mjs.map → allowed-origins-BGglilcs.mjs.map} +1 -1
  7. package/dist/api/route-utils.d.mts +3 -3
  8. package/dist/api/route-utils.mjs +23 -21
  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-Dz6rgoa3.mjs +10040 -0
  13. package/dist/api-Dz6rgoa3.mjs.map +1 -0
  14. package/dist/{api-tokens-KO24G1Fd.mjs → api-tokens-BSsQACNe.mjs} +2 -2
  15. package/dist/{api-tokens-KO24G1Fd.mjs.map → api-tokens-BSsQACNe.mjs.map} +1 -1
  16. package/dist/{apply-kUDt80-G.mjs → apply-B4SIOxR1.mjs} +34 -35
  17. package/dist/{apply-kUDt80-G.mjs.map → apply-B4SIOxR1.mjs.map} +1 -1
  18. package/dist/astro/image-endpoint.mjs +2 -2
  19. package/dist/astro/image-endpoint.mjs.map +1 -1
  20. package/dist/astro/index.d.mts +12 -12
  21. package/dist/astro/index.d.mts.map +1 -1
  22. package/dist/astro/index.mjs +128 -44
  23. package/dist/astro/index.mjs.map +1 -1
  24. package/dist/astro/middleware/auth.d.mts +10 -10
  25. package/dist/astro/middleware/auth.d.mts.map +1 -1
  26. package/dist/astro/middleware/auth.mjs +10 -7
  27. package/dist/astro/middleware/auth.mjs.map +1 -1
  28. package/dist/astro/middleware/media-usage-write-fence.mjs +2 -2
  29. package/dist/astro/middleware/redirect.mjs +4 -4
  30. package/dist/astro/middleware/request-context.mjs +3 -3
  31. package/dist/astro/middleware/setup.mjs +1 -1
  32. package/dist/astro/middleware.d.mts +11 -12
  33. package/dist/astro/middleware.d.mts.map +1 -1
  34. package/dist/astro/middleware.mjs +237 -1412
  35. package/dist/astro/middleware.mjs.map +1 -1
  36. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
  37. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
  38. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +3 -3
  39. package/dist/astro/routes/api/admin/api-tokens/index.d.mts.map +1 -1
  40. package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
  41. package/dist/astro/routes/api/admin/api-tokens/index.mjs.map +1 -1
  42. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  43. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -9
  44. package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -9
  45. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -9
  46. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +16 -16
  47. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +16 -16
  48. package/dist/astro/routes/api/admin/bylines/index.mjs +16 -16
  49. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +13 -13
  50. package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
  51. package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
  52. package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
  53. package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
  54. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  55. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  56. package/dist/astro/routes/api/admin/media-usage/activation.d.mts +9 -0
  57. package/dist/astro/routes/api/admin/media-usage/activation.d.mts.map +1 -0
  58. package/dist/astro/routes/api/admin/media-usage/activation.mjs +46 -0
  59. package/dist/astro/routes/api/admin/media-usage/activation.mjs.map +1 -0
  60. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +14 -13
  61. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs.map +1 -1
  62. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +14 -13
  63. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs.map +1 -1
  64. package/dist/astro/routes/api/admin/media-usage/progress.d.mts +9 -0
  65. package/dist/astro/routes/api/admin/media-usage/progress.d.mts.map +1 -0
  66. package/dist/astro/routes/api/admin/media-usage/progress.mjs +44 -0
  67. package/dist/astro/routes/api/admin/media-usage/progress.mjs.map +1 -0
  68. package/dist/astro/routes/api/admin/media-usage/repair.mjs +16 -11
  69. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
  70. package/dist/astro/routes/api/admin/media-usage/work/index.mjs +14 -13
  71. package/dist/astro/routes/api/admin/media-usage/work/index.mjs.map +1 -1
  72. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +14 -13
  73. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs.map +1 -1
  74. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
  75. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
  77. package/dist/astro/routes/api/admin/oauth-clients/index.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +55 -49
  79. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +55 -49
  81. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +53 -47
  83. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  84. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +4 -4
  85. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +53 -47
  86. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
  87. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -48
  88. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  89. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +54 -48
  90. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  91. package/dist/astro/routes/api/admin/plugins/index.mjs +53 -47
  92. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  93. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
  94. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +53 -47
  95. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  96. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +54 -48
  97. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  98. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +53 -47
  99. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  100. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +54 -48
  101. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  102. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.d.mts.map +1 -1
  103. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +60 -50
  104. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  105. package/dist/astro/routes/api/admin/plugins/registry/artifact.d.mts.map +1 -1
  106. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +179 -139
  107. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  108. package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -1
  109. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +61 -51
  110. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  111. package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts +8 -0
  112. package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts.map +1 -0
  113. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +93 -0
  114. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs.map +1 -0
  115. package/dist/astro/routes/api/admin/plugins/updates.mjs +53 -47
  116. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  117. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +53 -47
  118. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  119. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
  120. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +53 -47
  121. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  122. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  123. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
  124. package/dist/astro/routes/api/admin/users/_id_/index.mjs +7 -7
  125. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
  126. package/dist/astro/routes/api/admin/users/index.mjs +6 -6
  127. package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
  128. package/dist/astro/routes/api/auth/invite/accept.mjs +1 -1
  129. package/dist/astro/routes/api/auth/invite/complete.mjs +11 -11
  130. package/dist/astro/routes/api/auth/invite/index.mjs +8 -8
  131. package/dist/astro/routes/api/auth/invite/register-options.mjs +10 -10
  132. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  133. package/dist/astro/routes/api/auth/magic-link/send.mjs +10 -10
  134. package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
  135. package/dist/astro/routes/api/auth/me.d.mts.map +1 -1
  136. package/dist/astro/routes/api/auth/me.mjs +19 -9
  137. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  138. package/dist/astro/routes/api/auth/mode.mjs +2 -2
  139. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  140. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  141. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
  142. package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
  143. package/dist/astro/routes/api/auth/passkey/options.mjs +12 -12
  144. package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -10
  145. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -11
  146. package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -11
  147. package/dist/astro/routes/api/auth/signup/complete.mjs +11 -11
  148. package/dist/astro/routes/api/auth/signup/request.mjs +10 -10
  149. package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
  150. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +14 -14
  151. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +13 -13
  152. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
  153. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.d.mts.map +1 -1
  154. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +14 -5
  155. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -1
  156. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  157. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +2 -2
  158. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +11 -11
  159. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +13 -10
  160. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
  161. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +17 -15
  162. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
  163. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +17 -15
  164. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
  165. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  166. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
  167. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -8
  168. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +14 -14
  169. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
  170. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.d.mts.map +1 -1
  171. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +14 -5
  172. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
  173. package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
  174. package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
  175. package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
  176. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
  177. package/dist/astro/routes/api/dashboard.mjs +8 -8
  178. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  179. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  180. package/dist/astro/routes/api/import/probe.mjs +17 -17
  181. package/dist/astro/routes/api/import/wordpress/analyze.mjs +2 -2
  182. package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
  183. package/dist/astro/routes/api/import/wordpress/execute.mjs +13 -13
  184. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
  185. package/dist/astro/routes/api/import/wordpress/media.mjs +11 -11
  186. package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
  187. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
  188. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +12 -10
  189. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
  190. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  191. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +17 -17
  192. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
  193. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +27 -27
  194. package/dist/astro/routes/api/manifest.mjs +4 -4
  195. package/dist/astro/routes/api/manifest.mjs.map +1 -1
  196. package/dist/astro/routes/api/mcp.mjs +188 -65
  197. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  198. package/dist/astro/routes/api/media/_id_/confirm.mjs +11 -11
  199. package/dist/astro/routes/api/media/_id_/replace.d.mts +8 -0
  200. package/dist/astro/routes/api/media/_id_/replace.d.mts.map +1 -0
  201. package/dist/astro/routes/api/media/_id_/replace.mjs +91 -0
  202. package/dist/astro/routes/api/media/_id_/replace.mjs.map +1 -0
  203. package/dist/astro/routes/api/media/_id_/upload.mjs +6 -6
  204. package/dist/astro/routes/api/media/_id_/usage.mjs +18 -13
  205. package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
  206. package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
  207. package/dist/astro/routes/api/media/_id_.mjs +21 -13
  208. package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
  209. package/dist/astro/routes/api/media/file/_...key_.d.mts.map +1 -1
  210. package/dist/astro/routes/api/media/file/_...key_.mjs +3 -2
  211. package/dist/astro/routes/api/media/file/_...key_.mjs.map +1 -1
  212. package/dist/astro/routes/api/media/folders/_id_.d.mts +10 -0
  213. package/dist/astro/routes/api/media/folders/_id_.d.mts.map +1 -0
  214. package/dist/astro/routes/api/media/folders/_id_.mjs +52 -0
  215. package/dist/astro/routes/api/media/folders/_id_.mjs.map +1 -0
  216. package/dist/astro/routes/api/media/folders/index.d.mts +9 -0
  217. package/dist/astro/routes/api/media/folders/index.d.mts.map +1 -0
  218. package/dist/astro/routes/api/media/folders/index.mjs +41 -0
  219. package/dist/astro/routes/api/media/folders/index.mjs.map +1 -0
  220. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
  221. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
  222. package/dist/astro/routes/api/media/providers/index.mjs +2 -2
  223. package/dist/astro/routes/api/media/upload-url.mjs +16 -14
  224. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
  225. package/dist/astro/routes/api/media.d.mts.map +1 -1
  226. package/dist/astro/routes/api/media.mjs +59 -27
  227. package/dist/astro/routes/api/media.mjs.map +1 -1
  228. package/dist/astro/routes/api/menus/_name_/items/_id_.d.mts.map +1 -1
  229. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +20 -13
  230. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
  231. package/dist/astro/routes/api/menus/_name_/items.d.mts.map +1 -1
  232. package/dist/astro/routes/api/menus/_name_/items.mjs +15 -11
  233. package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
  234. package/dist/astro/routes/api/menus/_name_/reorder.d.mts.map +1 -1
  235. package/dist/astro/routes/api/menus/_name_/reorder.mjs +15 -11
  236. package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
  237. package/dist/astro/routes/api/menus/_name_/translations.d.mts.map +1 -1
  238. package/dist/astro/routes/api/menus/_name_/translations.mjs +16 -12
  239. package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
  240. package/dist/astro/routes/api/menus/_name_.d.mts.map +1 -1
  241. package/dist/astro/routes/api/menus/_name_.mjs +21 -14
  242. package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
  243. package/dist/astro/routes/api/menus/index.d.mts.map +1 -1
  244. package/dist/astro/routes/api/menus/index.mjs +15 -11
  245. package/dist/astro/routes/api/menus/index.mjs.map +1 -1
  246. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  247. package/dist/astro/routes/api/oauth/device/authorize.mjs +5 -5
  248. package/dist/astro/routes/api/oauth/device/code.mjs +8 -8
  249. package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
  250. package/dist/astro/routes/api/oauth/register.mjs +2 -2
  251. package/dist/astro/routes/api/oauth/token/refresh.mjs +5 -5
  252. package/dist/astro/routes/api/oauth/token/revoke.mjs +5 -5
  253. package/dist/astro/routes/api/oauth/token.mjs +5 -5
  254. package/dist/astro/routes/api/openapi.json.mjs +253 -40
  255. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  256. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
  257. package/dist/astro/routes/api/redirects/404s/index.mjs +10 -10
  258. package/dist/astro/routes/api/redirects/404s/summary.mjs +10 -10
  259. package/dist/astro/routes/api/redirects/_id_.mjs +11 -11
  260. package/dist/astro/routes/api/redirects/index.mjs +11 -11
  261. package/dist/astro/routes/api/relations/_id_/index.mjs +17 -15
  262. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
  263. package/dist/astro/routes/api/relations/_id_/translations.mjs +12 -10
  264. package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
  265. package/dist/astro/routes/api/relations/index.mjs +17 -15
  266. package/dist/astro/routes/api/relations/index.mjs.map +1 -1
  267. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
  268. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  269. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +53 -47
  270. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  271. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +53 -47
  272. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  273. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +53 -47
  274. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  275. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +53 -47
  276. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  277. package/dist/astro/routes/api/schema/collections/index.mjs +53 -47
  278. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  279. package/dist/astro/routes/api/schema/collections/reorder.mjs +53 -47
  280. package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
  281. package/dist/astro/routes/api/schema/index.mjs +10 -8
  282. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  283. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +53 -47
  284. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  285. package/dist/astro/routes/api/schema/orphans/index.mjs +53 -47
  286. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  287. package/dist/astro/routes/api/search/enable.mjs +10 -10
  288. package/dist/astro/routes/api/search/index.mjs +9 -9
  289. package/dist/astro/routes/api/search/rebuild.mjs +10 -10
  290. package/dist/astro/routes/api/search/stats.mjs +5 -5
  291. package/dist/astro/routes/api/search/suggest.mjs +9 -9
  292. package/dist/astro/routes/api/sections/_slug_.mjs +9 -9
  293. package/dist/astro/routes/api/sections/index.mjs +9 -9
  294. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +6 -6
  295. package/dist/astro/routes/api/settings/backups/archives/index.mjs +5 -5
  296. package/dist/astro/routes/api/settings/backups/export.mjs +6 -6
  297. package/dist/astro/routes/api/settings/backups/index.mjs +7 -7
  298. package/dist/astro/routes/api/settings/email.mjs +5 -5
  299. package/dist/astro/routes/api/settings/email.mjs.map +1 -1
  300. package/dist/astro/routes/api/settings.d.mts.map +1 -1
  301. package/dist/astro/routes/api/settings.mjs +20 -16
  302. package/dist/astro/routes/api/settings.mjs.map +1 -1
  303. package/dist/astro/routes/api/setup/admin-verify.mjs +12 -12
  304. package/dist/astro/routes/api/setup/admin.mjs +11 -11
  305. package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
  306. package/dist/astro/routes/api/setup/dev-bypass.mjs +35 -28
  307. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  308. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  309. package/dist/astro/routes/api/setup/index.mjs +32 -30
  310. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  311. package/dist/astro/routes/api/setup/status.mjs +3 -3
  312. package/dist/astro/routes/api/snapshot.mjs +7 -7
  313. package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts.map +1 -1
  314. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +20 -16
  315. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -1
  316. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.d.mts.map +1 -1
  317. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +21 -17
  318. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
  319. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.d.mts.map +1 -1
  320. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +25 -18
  321. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
  322. package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -1
  323. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +20 -16
  324. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
  325. package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +14 -14
  326. package/dist/astro/routes/api/taxonomies/_name_.d.mts.map +1 -1
  327. package/dist/astro/routes/api/taxonomies/_name_.mjs +26 -19
  328. package/dist/astro/routes/api/taxonomies/_name_.mjs.map +1 -1
  329. package/dist/astro/routes/api/taxonomies/index.d.mts.map +1 -1
  330. package/dist/astro/routes/api/taxonomies/index.mjs +20 -16
  331. package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
  332. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  333. package/dist/astro/routes/api/typegen.mjs +4 -4
  334. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  335. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  336. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  337. package/dist/astro/routes/api/widget-areas/_name_/reorder.d.mts.map +1 -1
  338. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +10 -8
  339. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
  340. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.d.mts.map +1 -1
  341. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +17 -13
  342. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
  343. package/dist/astro/routes/api/widget-areas/_name_/widgets.d.mts.map +1 -1
  344. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +15 -12
  345. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
  346. package/dist/astro/routes/api/widget-areas/_name_.d.mts.map +1 -1
  347. package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -6
  348. package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
  349. package/dist/astro/routes/api/widget-areas/index.d.mts.map +1 -1
  350. package/dist/astro/routes/api/widget-areas/index.mjs +15 -12
  351. package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
  352. package/dist/astro/routes/api/widget-components.mjs +2 -2
  353. package/dist/astro/routes/robots.txt.mjs +7 -7
  354. package/dist/astro/routes/sitemap-_collection_.xml.mjs +10 -10
  355. package/dist/astro/routes/sitemap.xml.mjs +8 -8
  356. package/dist/astro/types.d.mts +37 -17
  357. package/dist/astro/types.d.mts.map +1 -1
  358. package/dist/auth/providers/github.d.mts +1 -1
  359. package/dist/auth/providers/google.d.mts +1 -1
  360. package/dist/{authorize-DUElxgOQ.mjs → authorize-Bkf_UHhD.mjs} +2 -2
  361. package/dist/{authorize-DUElxgOQ.mjs.map → authorize-Bkf_UHhD.mjs.map} +1 -1
  362. package/dist/{backup-DWYpJlLj.mjs → backup-rwVAl9NI.mjs} +5 -5
  363. package/dist/{backup-DWYpJlLj.mjs.map → backup-rwVAl9NI.mjs.map} +1 -1
  364. package/dist/{bundle-vn56sLos.mjs → bundle-C8S2Zl6U.mjs} +4 -4
  365. package/dist/{bundle-vn56sLos.mjs.map → bundle-C8S2Zl6U.mjs.map} +1 -1
  366. package/dist/{byline-yjvWq4ip.mjs → byline-DuhF7NWl.mjs} +27 -14
  367. package/dist/byline-DuhF7NWl.mjs.map +1 -0
  368. package/dist/{byline-fields-txaLGoME.mjs → byline-fields-BovY4KYM.mjs} +2 -2
  369. package/dist/{byline-fields-txaLGoME.mjs.map → byline-fields-BovY4KYM.mjs.map} +1 -1
  370. package/dist/{byline-registry-DDun8sxh.mjs → byline-registry-Bpu9-MVs.mjs} +2 -2
  371. package/dist/{byline-registry-DDun8sxh.mjs.map → byline-registry-Bpu9-MVs.mjs.map} +1 -1
  372. package/dist/{bylines-DjXS5p0P.mjs → bylines-CJ5nd6Lw.mjs} +7 -7
  373. package/dist/{bylines-DjXS5p0P.mjs.map → bylines-CJ5nd6Lw.mjs.map} +1 -1
  374. package/dist/{bylines-Bu7pchQ4.mjs → bylines-RwBAeOF1.mjs} +3 -3
  375. package/dist/{bylines-Bu7pchQ4.mjs.map → bylines-RwBAeOF1.mjs.map} +1 -1
  376. package/dist/{cache-C46-3Irl.mjs → cache-BF5Kvn59.mjs} +4 -4
  377. package/dist/{cache-C46-3Irl.mjs.map → cache-BF5Kvn59.mjs.map} +1 -1
  378. package/dist/capture-triggers-CNW1q3HK.mjs +362 -0
  379. package/dist/capture-triggers-CNW1q3HK.mjs.map +1 -0
  380. package/dist/{challenge-store-SIL5DsF2.mjs → challenge-store-DQN_Yrck.mjs} +5 -3
  381. package/dist/challenge-store-DQN_Yrck.mjs.map +1 -0
  382. package/dist/chrome-tags-D8fiZG94.mjs +24 -0
  383. package/dist/chrome-tags-D8fiZG94.mjs.map +1 -0
  384. package/dist/{chunks-fiZ8G9RJ.mjs → chunks-C4szrD8o.mjs} +2 -2
  385. package/dist/{chunks-fiZ8G9RJ.mjs.map → chunks-C4szrD8o.mjs.map} +1 -1
  386. package/dist/cli/index.mjs +37 -53
  387. package/dist/cli/index.mjs.map +1 -1
  388. package/dist/client/cf-access.d.mts +1 -1
  389. package/dist/client/index.d.mts +74 -4
  390. package/dist/client/index.d.mts.map +1 -1
  391. package/dist/client/index.mjs +54 -2
  392. package/dist/client/index.mjs.map +1 -1
  393. package/dist/{collection-deletion-DQPvMD8R.mjs → collection-deletion-Bj07NHbj.mjs} +13 -356
  394. package/dist/collection-deletion-Bj07NHbj.mjs.map +1 -0
  395. package/dist/{comment-reaction-D-2J84UE.mjs → comment-reaction-BhrvSHml.mjs} +2 -2
  396. package/dist/{comment-reaction-D-2J84UE.mjs.map → comment-reaction-BhrvSHml.mjs.map} +1 -1
  397. package/dist/{comment-Dxlep6cE.mjs → comment-vxOzWbZN.mjs} +2 -2
  398. package/dist/{comment-Dxlep6cE.mjs.map → comment-vxOzWbZN.mjs.map} +1 -1
  399. package/dist/{comments-CTfZtLmM.mjs → comments-Ct4ensnf.mjs} +2 -2
  400. package/dist/{comments-CTfZtLmM.mjs.map → comments-Ct4ensnf.mjs.map} +1 -1
  401. package/dist/{components-CQy3lh9x.mjs → components-DJbo1z0S.mjs} +1 -1
  402. package/dist/{components-CQy3lh9x.mjs.map → components-DJbo1z0S.mjs.map} +1 -1
  403. package/dist/{config-COQzTe5Q.d.mts → config-B9eZ340f.d.mts} +1 -1
  404. package/dist/{config-COQzTe5Q.d.mts.map → config-B9eZ340f.d.mts.map} +1 -1
  405. package/dist/config-it29yDLA.mjs +1163 -0
  406. package/dist/config-it29yDLA.mjs.map +1 -0
  407. package/dist/{content-ZHCc1ngN.mjs → content-DjvM0T92.mjs} +86 -45
  408. package/dist/content-DjvM0T92.mjs.map +1 -0
  409. package/dist/{content-list-query-wJ2WN8Vp.d.mts → content-list-query-Btqd4uV2.d.mts} +1 -1
  410. package/dist/{content-list-query-wJ2WN8Vp.d.mts.map → content-list-query-Btqd4uV2.d.mts.map} +1 -1
  411. package/dist/{content-refresh-B_8SJzuU.mjs → content-refresh-Bwp-8yGy.mjs} +1139 -111
  412. package/dist/content-refresh-Bwp-8yGy.mjs.map +1 -0
  413. package/dist/{media-usage-BZCydfvu.mjs → content-repair-Czdp7jz5.mjs} +5 -196
  414. package/dist/content-repair-Czdp7jz5.mjs.map +1 -0
  415. package/dist/{context-ZldbZcle.mjs → context-CX6gxOLW.mjs} +55 -20
  416. package/dist/context-CX6gxOLW.mjs.map +1 -0
  417. package/dist/{cron-Y9aLtgrA.mjs → cron-C6o7VppJ.mjs} +1 -1
  418. package/dist/{cron-Y9aLtgrA.mjs.map → cron-C6o7VppJ.mjs.map} +1 -1
  419. package/dist/{dashboard-DvHbk_cv.mjs → dashboard-C2kTeeRI.mjs} +5 -5
  420. package/dist/{dashboard-DvHbk_cv.mjs.map → dashboard-C2kTeeRI.mjs.map} +1 -1
  421. package/dist/db/index.d.mts +3 -3
  422. package/dist/db/index.mjs +3 -3
  423. package/dist/db/index.mjs.map +1 -1
  424. package/dist/db/libsql-migrations.d.mts +1 -1
  425. package/dist/db/libsql-migrations.mjs +4 -4
  426. package/dist/db/libsql.d.mts +1 -1
  427. package/dist/db/postgres-migrations.d.mts +1 -1
  428. package/dist/db/postgres-migrations.mjs +4 -4
  429. package/dist/db/postgres.d.mts +1 -1
  430. package/dist/db/postgres.d.mts.map +1 -1
  431. package/dist/db/postgres.mjs +19 -3
  432. package/dist/db/postgres.mjs.map +1 -1
  433. package/dist/db/sqlite-migrations.d.mts +1 -1
  434. package/dist/db/sqlite-migrations.mjs +4 -4
  435. package/dist/db/sqlite.d.mts +1 -1
  436. package/dist/db/sqlite.mjs +3 -3
  437. package/dist/db/sqlite.mjs.map +1 -1
  438. package/dist/{default-DdDl3fxv.mjs → default-Clra32mw.mjs} +1 -1
  439. package/dist/{default-DdDl3fxv.mjs.map → default-Clra32mw.mjs.map} +1 -1
  440. package/dist/{deferred-tasks-SDWxrgzJ.d.mts → deferred-tasks-CvDETTJO.d.mts} +1 -1
  441. package/dist/{deferred-tasks-SDWxrgzJ.d.mts.map → deferred-tasks-CvDETTJO.d.mts.map} +1 -1
  442. package/dist/dev-typegen-DzOPfsNj.mjs +29 -0
  443. package/dist/dev-typegen-DzOPfsNj.mjs.map +1 -0
  444. package/dist/{device-flow-C0GdD_z8.mjs → device-flow-D2oCWvLJ.mjs} +5 -5
  445. package/dist/{device-flow-C0GdD_z8.mjs.map → device-flow-D2oCWvLJ.mjs.map} +1 -1
  446. package/dist/{direct-executor-CDXeJCSs.mjs → direct-executor-Bfa5_ToE.mjs} +3 -3
  447. package/dist/{direct-executor-CDXeJCSs.mjs.map → direct-executor-Bfa5_ToE.mjs.map} +1 -1
  448. package/dist/{dist-D_6CKR8z.mjs → dist-89xCrGqi.mjs} +1 -1
  449. package/dist/{dist-D_6CKR8z.mjs.map → dist-89xCrGqi.mjs.map} +1 -1
  450. package/dist/{email-console-BHNjIwiB.mjs → email-console-Dzb6uGYa.mjs} +1 -1
  451. package/dist/{email-console-BHNjIwiB.mjs.map → email-console-Dzb6uGYa.mjs.map} +1 -1
  452. package/dist/{enrich-_ySMEvEE.mjs → enrich-BX0IDp4z.mjs} +3 -3
  453. package/dist/{enrich-_ySMEvEE.mjs.map → enrich-BX0IDp4z.mjs.map} +1 -1
  454. package/dist/{error-DupyJZE2.mjs → error-DFrQjppn.mjs} +2 -2
  455. package/dist/{error-DupyJZE2.mjs.map → error-DFrQjppn.mjs.map} +1 -1
  456. package/dist/{errors-CLUPm7aD.mjs → errors-DsVoDUjJ.mjs} +24 -2
  457. package/dist/errors-DsVoDUjJ.mjs.map +1 -0
  458. package/dist/{escape-DUY2r2Zs.mjs → escape-HmHTC1Is.mjs} +1 -1
  459. package/dist/{escape-DUY2r2Zs.mjs.map → escape-HmHTC1Is.mjs.map} +1 -1
  460. package/dist/{field-defs-cache-BzwBlJrR.mjs → field-defs-cache-BM4SIRqH.mjs} +5 -5
  461. package/dist/{field-defs-cache-BzwBlJrR.mjs.map → field-defs-cache-BM4SIRqH.mjs.map} +1 -1
  462. package/dist/focal-point-BeWFo6Xx.mjs +23 -0
  463. package/dist/focal-point-BeWFo6Xx.mjs.map +1 -0
  464. package/dist/{fts-manager-DyZEohZC.mjs → fts-manager-Do1BlW2r.mjs} +3 -3
  465. package/dist/{fts-manager-DyZEohZC.mjs.map → fts-manager-Do1BlW2r.mjs.map} +1 -1
  466. package/dist/{hash-Cg0TXJPV.mjs → hash-3JJPlHF2.mjs} +1 -1
  467. package/dist/{hash-Cg0TXJPV.mjs.map → hash-3JJPlHF2.mjs.map} +1 -1
  468. package/dist/hook-result-DCh2WUpe.d.mts +31 -0
  469. package/dist/hook-result-DCh2WUpe.d.mts.map +1 -0
  470. package/dist/{hreflang-DwT2E0Sa.d.mts → hreflang-B6hbAuuO.d.mts} +2 -2
  471. package/dist/{hreflang-DwT2E0Sa.d.mts.map → hreflang-B6hbAuuO.d.mts.map} +1 -1
  472. package/dist/{hreflang-BBe83kKa.mjs → hreflang-DQbPZliA.mjs} +7 -7
  473. package/dist/{hreflang-BBe83kKa.mjs.map → hreflang-DQbPZliA.mjs.map} +1 -1
  474. package/dist/{identity-Ys6Dlc9C.mjs → identity-DksvEazf.mjs} +3 -3
  475. package/dist/{identity-Ys6Dlc9C.mjs.map → identity-DksvEazf.mjs.map} +1 -1
  476. package/dist/{import-DPkLmeXO.mjs → import-CJpaBBBT.mjs} +6 -6
  477. package/dist/{import-DPkLmeXO.mjs.map → import-CJpaBBBT.mjs.map} +1 -1
  478. package/dist/{index-BVBZj4-7.d.mts → index-BJNMCWmu.d.mts} +267 -82
  479. package/dist/index-BJNMCWmu.d.mts.map +1 -0
  480. package/dist/{index-C9KuQyO-.d.mts → index-Du8K7bWa.d.mts} +3 -3
  481. package/dist/{index-C9KuQyO-.d.mts.map → index-Du8K7bWa.d.mts.map} +1 -1
  482. package/dist/index.d.mts +22 -21
  483. package/dist/index.mjs +79 -73
  484. package/dist/{init-lock-6b309ZrF.mjs → init-lock-DJkX6Hto.mjs} +1 -1
  485. package/dist/{init-lock-6b309ZrF.mjs.map → init-lock-DJkX6Hto.mjs.map} +1 -1
  486. package/dist/{load-YJ2xl3ra.mjs → load-CawHjTOH.mjs} +2 -2
  487. package/dist/{load-YJ2xl3ra.mjs.map → load-CawHjTOH.mjs.map} +1 -1
  488. package/dist/{loader-CdG_Ci3H.mjs → loader-Z8vl7zTq.mjs} +48 -13
  489. package/dist/loader-Z8vl7zTq.mjs.map +1 -0
  490. package/dist/{manifest-builder-B252yQUf.mjs → manifest-builder-COtzuca2.mjs} +2 -2
  491. package/dist/{manifest-builder-B252yQUf.mjs.map → manifest-builder-COtzuca2.mjs.map} +1 -1
  492. package/dist/{manifest-schema-BXbUAHod.mjs → manifest-schema-BNWwR8Vd.mjs} +5 -5
  493. package/dist/manifest-schema-BNWwR8Vd.mjs.map +1 -0
  494. package/dist/media/image-endpoint.d.mts +4 -2
  495. package/dist/media/image-endpoint.d.mts.map +1 -1
  496. package/dist/media/image-endpoint.mjs +6 -4
  497. package/dist/media/image-endpoint.mjs.map +1 -1
  498. package/dist/media/index.d.mts +1 -1
  499. package/dist/media/index.mjs +13 -3
  500. package/dist/media/index.mjs.map +1 -1
  501. package/dist/media/local-runtime.d.mts +14 -12
  502. package/dist/media/local-runtime.d.mts.map +1 -1
  503. package/dist/media/local-runtime.mjs +12 -6
  504. package/dist/media/local-runtime.mjs.map +1 -1
  505. package/dist/{media-CzhAn2--.mjs → media-BBm8Wg3f.mjs} +69 -12
  506. package/dist/media-BBm8Wg3f.mjs.map +1 -0
  507. package/dist/{media-BWWWb8kg.mjs → media-BHjus86Y.mjs} +133 -9
  508. package/dist/media-BHjus86Y.mjs.map +1 -0
  509. package/dist/{media-allowlist-D6x-Us02.mjs → media-allowlist-CoTBWoDp.mjs} +3 -2
  510. package/dist/media-allowlist-CoTBWoDp.mjs.map +1 -0
  511. package/dist/media-folders-BGkATs8n.mjs +210 -0
  512. package/dist/media-folders-BGkATs8n.mjs.map +1 -0
  513. package/dist/{media-upload-CHBVn6m-.mjs → media-upload-BCidR-EY.mjs} +9 -9
  514. package/dist/{media-upload-CHBVn6m-.mjs.map → media-upload-BCidR-EY.mjs.map} +1 -1
  515. package/dist/{media-url-BvoX88FA.mjs → media-url-DHoPr0hq.mjs} +1 -1
  516. package/dist/{media-url-BvoX88FA.mjs.map → media-url-DHoPr0hq.mjs.map} +1 -1
  517. package/dist/media-usage-CdY5jhSR.mjs +1833 -0
  518. package/dist/media-usage-CdY5jhSR.mjs.map +1 -0
  519. package/dist/media-usage-activation-BgeMtK5C.mjs +78 -0
  520. package/dist/media-usage-activation-BgeMtK5C.mjs.map +1 -0
  521. package/dist/{media-usage-work-CLo_6tct.mjs → media-usage-work-CnGCdHxx.mjs} +4 -4
  522. package/dist/{media-usage-work-CLo_6tct.mjs.map → media-usage-work-CnGCdHxx.mjs.map} +1 -1
  523. package/dist/{media-usage-work-DrCbqBcl.mjs → media-usage-work-D6D9sMCA.mjs} +221 -59
  524. package/dist/media-usage-work-D6D9sMCA.mjs.map +1 -0
  525. package/dist/{media-usage-write-fence-aHEgN1zB.mjs → media-usage-write-fence-hjeD4u7s.mjs} +2 -2
  526. package/dist/{media-usage-write-fence-aHEgN1zB.mjs.map → media-usage-write-fence-hjeD4u7s.mjs.map} +1 -1
  527. package/dist/{menus-DSo5QeW0.mjs → menus-6qSwTNxG.mjs} +375 -107
  528. package/dist/menus-6qSwTNxG.mjs.map +1 -0
  529. package/dist/{menus-TCNSd2vp.mjs → menus-uvPLXMZf.mjs} +4 -4
  530. package/dist/{menus-TCNSd2vp.mjs.map → menus-uvPLXMZf.mjs.map} +1 -1
  531. package/dist/migrations/index.d.mts +1 -1
  532. package/dist/migrations/index.mjs +3 -3
  533. package/dist/{mime-DfIVjNkr.mjs → mime-CUIG6x84.mjs} +1 -1
  534. package/dist/mime-CUIG6x84.mjs.map +1 -0
  535. package/dist/{mode-BDoH_WcF.mjs → mode-DN_WneRx.mjs} +1 -1
  536. package/dist/{mode-BDoH_WcF.mjs.map → mode-DN_WneRx.mjs.map} +1 -1
  537. package/dist/node-sqlite-compat-BuJXIeMz.mjs +46 -0
  538. package/dist/node-sqlite-compat-BuJXIeMz.mjs.map +1 -0
  539. package/dist/{normalize-Bc-s_r-a.mjs → normalize-C2xNjTSG.mjs} +36 -5
  540. package/dist/normalize-C2xNjTSG.mjs.map +1 -0
  541. package/dist/{oauth-authorization-iOikmrJd.mjs → oauth-authorization-Dj6Kwc21.mjs} +5 -5
  542. package/dist/{oauth-authorization-iOikmrJd.mjs.map → oauth-authorization-Dj6Kwc21.mjs.map} +1 -1
  543. package/dist/{oauth-clients-CvI0nHMJ.mjs → oauth-clients-FD7I0J8m.mjs} +1 -1
  544. package/dist/{oauth-clients-CvI0nHMJ.mjs.map → oauth-clients-FD7I0J8m.mjs.map} +1 -1
  545. package/dist/{oauth-state-store-Ba8KZlwW.mjs → oauth-state-store-DkmDOmHs.mjs} +1 -1
  546. package/dist/{oauth-state-store-Ba8KZlwW.mjs.map → oauth-state-store-DkmDOmHs.mjs.map} +1 -1
  547. package/dist/{oauth-user-lookup-C5SQwGYC.mjs → oauth-user-lookup-CvIwGpYd.mjs} +1 -1
  548. package/dist/{oauth-user-lookup-C5SQwGYC.mjs.map → oauth-user-lookup-CvIwGpYd.mjs.map} +1 -1
  549. package/dist/object-cache/memory.d.mts +1 -1
  550. package/dist/{object-cache-71tPfV4A.mjs → object-cache-DVKnyOfy.mjs} +19 -3
  551. package/dist/object-cache-DVKnyOfy.mjs.map +1 -0
  552. package/dist/{options-CBXIHUoT.mjs → options-BHPeIbBd.mjs} +1 -1
  553. package/dist/{options-CBXIHUoT.mjs.map → options-BHPeIbBd.mjs.map} +1 -1
  554. package/dist/{options--hhz-jZ1.d.mts → options-CYb4y7hM.d.mts} +24 -11
  555. package/dist/options-CYb4y7hM.d.mts.map +1 -0
  556. package/dist/page/index.d.mts +2 -2
  557. package/dist/{parse-DFbuc-Fh.mjs → parse-Ckpg-Qfp.mjs} +2 -2
  558. package/dist/{parse-DFbuc-Fh.mjs.map → parse-Ckpg-Qfp.mjs.map} +1 -1
  559. package/dist/{passkey-config-DFAmUyuE.mjs → passkey-config-O_lj3y0k.mjs} +1 -1
  560. package/dist/{passkey-config-DFAmUyuE.mjs.map → passkey-config-O_lj3y0k.mjs.map} +1 -1
  561. package/dist/{patterns-CiyXeDgr.mjs → patterns-D9iDO3j8.mjs} +1 -1
  562. package/dist/{patterns-CiyXeDgr.mjs.map → patterns-D9iDO3j8.mjs.map} +1 -1
  563. package/dist/{placeholder-Dp0K3RkD.d.mts → placeholder-4X9BrRrp.d.mts} +7 -1
  564. package/dist/placeholder-4X9BrRrp.d.mts.map +1 -0
  565. package/dist/plugin-types.d.mts +8 -3
  566. package/dist/plugin-types.d.mts.map +1 -1
  567. package/dist/plugin-utils.d.mts +10 -10
  568. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
  569. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  570. package/dist/{policy-TluUIxuu.mjs → policy-Dhtwntrt.mjs} +2 -2
  571. package/dist/{policy-TluUIxuu.mjs.map → policy-Dhtwntrt.mjs.map} +1 -1
  572. package/dist/{portable-text-imBW5cHK.mjs → portable-text-U85hOafW.mjs} +1 -1
  573. package/dist/{portable-text-imBW5cHK.mjs.map → portable-text-U85hOafW.mjs.map} +1 -1
  574. package/dist/{preview-PW-nOnq4.mjs → preview-B_vJLvf8.mjs} +2 -2
  575. package/dist/{preview-PW-nOnq4.mjs.map → preview-B_vJLvf8.mjs.map} +1 -1
  576. package/dist/{protocol-DjsJHTlK.d.mts → protocol-JdD2YhY9.d.mts} +2 -2
  577. package/dist/{protocol-DjsJHTlK.d.mts.map → protocol-JdD2YhY9.d.mts.map} +1 -1
  578. package/dist/{public-url-CWoFhQT8.mjs → public-url-CarhZJgf.mjs} +1 -1
  579. package/dist/{public-url-CWoFhQT8.mjs.map → public-url-CarhZJgf.mjs.map} +1 -1
  580. package/dist/{query-zTVAR1Sl.mjs → query-BZKZcG5b.mjs} +15 -15
  581. package/dist/{query-zTVAR1Sl.mjs.map → query-BZKZcG5b.mjs.map} +1 -1
  582. package/dist/{query-BWs7O5Il.mjs → query-BndaFxxp.mjs} +5 -5
  583. package/dist/{query-BWs7O5Il.mjs.map → query-BndaFxxp.mjs.map} +1 -1
  584. package/dist/{rate-limit-Bx4g9m3p.mjs → rate-limit-BZmATC6W.mjs} +2 -2
  585. package/dist/{rate-limit-Bx4g9m3p.mjs.map → rate-limit-BZmATC6W.mjs.map} +1 -1
  586. package/dist/{redirect-COsS6SWJ.mjs → redirect-CZkbozu9.mjs} +2 -2
  587. package/dist/{redirect-COsS6SWJ.mjs.map → redirect-CZkbozu9.mjs.map} +1 -1
  588. package/dist/{redirect-BgAOGHDq.mjs → redirect-CzqnLxK-.mjs} +1 -1
  589. package/dist/{redirect-BgAOGHDq.mjs.map → redirect-CzqnLxK-.mjs.map} +1 -1
  590. package/dist/{redirects-DTDHWH31.mjs → redirects-BLNOWQTn.mjs} +64 -29
  591. package/dist/redirects-BLNOWQTn.mjs.map +1 -0
  592. package/dist/{redirects-DyChwDkG.mjs → redirects-CqzNM0KM.mjs} +5 -5
  593. package/dist/{redirects-DyChwDkG.mjs.map → redirects-CqzNM0KM.mjs.map} +1 -1
  594. package/dist/{registry-Cqa2I749.mjs → registry-AZAsRsor.mjs} +30 -126
  595. package/dist/registry-AZAsRsor.mjs.map +1 -0
  596. package/dist/{relation-BnHB0T4J.mjs → relation-04DfKRTO.mjs} +2 -2
  597. package/dist/{relation-BnHB0T4J.mjs.map → relation-04DfKRTO.mjs.map} +1 -1
  598. package/dist/{relations-BBApI4e2.mjs → relations-C4htk5sb.mjs} +7 -7
  599. package/dist/relations-C4htk5sb.mjs.map +1 -0
  600. package/dist/{relations-D5sM5fW0.mjs → relations-CcFwDJdJ.mjs} +4 -4
  601. package/dist/{relations-D5sM5fW0.mjs.map → relations-CcFwDJdJ.mjs.map} +1 -1
  602. package/dist/{relations-BHo5A8Zf.d.mts → relations-DnRK4YIT.d.mts} +347 -54
  603. package/dist/relations-DnRK4YIT.d.mts.map +1 -0
  604. package/dist/{request-cache-BSUptuJR.mjs → request-cache-DH9zdCOD.mjs} +1 -1
  605. package/dist/{request-cache-BSUptuJR.mjs.map → request-cache-DH9zdCOD.mjs.map} +1 -1
  606. package/dist/request-context.d.mts +1 -1
  607. package/dist/{request-meta-DgZJh3zG.mjs → request-meta-DYxLlnmz.mjs} +2 -2
  608. package/dist/{request-meta-DgZJh3zG.mjs.map → request-meta-DYxLlnmz.mjs.map} +1 -1
  609. package/dist/{resolve-DOhVEvty.mjs → resolve-prZ3mP7l.mjs} +1 -1
  610. package/dist/{resolve-DOhVEvty.mjs.map → resolve-prZ3mP7l.mjs.map} +1 -1
  611. package/dist/{runner-C1T11CfC.mjs → runner-BLIDKpmA.mjs} +471 -290
  612. package/dist/runner-BLIDKpmA.mjs.map +1 -0
  613. package/dist/{runner-BYHZn3Jy.d.mts → runner-YKfIMnaF.d.mts} +3 -2
  614. package/dist/runner-YKfIMnaF.d.mts.map +1 -0
  615. package/dist/runtime.d.mts +11 -11
  616. package/dist/runtime.mjs +1 -1
  617. package/dist/{schema-DZ5YVVZj.mjs → schema-CGnBeLiW.mjs} +3 -3
  618. package/dist/{schema-DZ5YVVZj.mjs.map → schema-CGnBeLiW.mjs.map} +1 -1
  619. package/dist/{schema-2AOsSn2v.mjs → schema-DYr-Vd-L.mjs} +36 -6
  620. package/dist/{schema-2AOsSn2v.mjs.map → schema-DYr-Vd-L.mjs.map} +1 -1
  621. package/dist/{scopes-8ClPF2bC.mjs → scopes-BhWvBavj.mjs} +3 -3
  622. package/dist/{scopes-8ClPF2bC.mjs.map → scopes-BhWvBavj.mjs.map} +1 -1
  623. package/dist/{search-u1QYdOCG.mjs → search-C-qDk4kx.mjs} +4 -4
  624. package/dist/{search-u1QYdOCG.mjs.map → search-C-qDk4kx.mjs.map} +1 -1
  625. package/dist/{secrets-BEgsAQEU.mjs → secrets-rEtHt-6z.mjs} +3 -3
  626. package/dist/{secrets-BEgsAQEU.mjs.map → secrets-rEtHt-6z.mjs.map} +1 -1
  627. package/dist/{sections-DmHviGyM.mjs → sections-B_nYvlwn.mjs} +2 -2
  628. package/dist/{sections-DmHviGyM.mjs.map → sections-B_nYvlwn.mjs.map} +1 -1
  629. package/dist/seed/index.d.mts +2 -2
  630. package/dist/seed/index.mjs +25 -23
  631. package/dist/seo/index.d.mts +3 -3
  632. package/dist/seo/index.mjs +17 -15
  633. package/dist/seo/index.mjs.map +1 -1
  634. package/dist/{seo-Dq4G-RrO.mjs → seo-926NRIgt.mjs} +3 -3
  635. package/dist/{seo-Dq4G-RrO.mjs.map → seo-926NRIgt.mjs.map} +1 -1
  636. package/dist/{seo-AFpVvTqr.mjs → seo-CVWkdv6x.mjs} +1 -1
  637. package/dist/{seo-AFpVvTqr.mjs.map → seo-CVWkdv6x.mjs.map} +1 -1
  638. package/dist/{service-BpArayPr.mjs → service-CJl3ZAYs.mjs} +3 -3
  639. package/dist/{service-BpArayPr.mjs.map → service-CJl3ZAYs.mjs.map} +1 -1
  640. package/dist/{session-user-BEorKVT-.mjs → session-user-BlMgqCHS.mjs} +1 -1
  641. package/dist/{session-user-BEorKVT-.mjs.map → session-user-BlMgqCHS.mjs.map} +1 -1
  642. package/dist/{settings-xRdoUZr0.mjs → settings--wobrkkC.mjs} +21 -8
  643. package/dist/settings--wobrkkC.mjs.map +1 -0
  644. package/dist/{settings-DE9xtzqd.mjs → settings-Cwm6o2jY.mjs} +3 -3
  645. package/dist/{settings-DE9xtzqd.mjs.map → settings-Cwm6o2jY.mjs.map} +1 -1
  646. package/dist/{setup-complete-BnT8pq_C.mjs → setup-complete-DQbClIN2.mjs} +2 -2
  647. package/dist/{setup-complete-BnT8pq_C.mjs.map → setup-complete-DQbClIN2.mjs.map} +1 -1
  648. package/dist/{setup-nonce-CBitf_oQ.mjs → setup-nonce-DZCFS2sn.mjs} +1 -1
  649. package/dist/{setup-nonce-CBitf_oQ.mjs.map → setup-nonce-DZCFS2sn.mjs.map} +1 -1
  650. package/dist/{single-flight-cache-B0eLKS6K.mjs → single-flight-cache-BPuGPdcN.mjs} +2 -2
  651. package/dist/{single-flight-cache-B0eLKS6K.mjs.map → single-flight-cache-BPuGPdcN.mjs.map} +1 -1
  652. package/dist/{site-url-DjpIXMXJ.mjs → site-url-BrxA_E9H.mjs} +3 -3
  653. package/dist/{site-url-DjpIXMXJ.mjs.map → site-url-BrxA_E9H.mjs.map} +1 -1
  654. package/dist/{snapshot-BBlGkb2q.mjs → snapshot--OCDTqdb.mjs} +1 -1
  655. package/dist/{snapshot-BBlGkb2q.mjs.map → snapshot--OCDTqdb.mjs.map} +1 -1
  656. package/dist/{ssrf-Cpvg_n3A.mjs → ssrf-5sIM5jzl.mjs} +23 -6
  657. package/dist/{ssrf-Cpvg_n3A.mjs.map → ssrf-5sIM5jzl.mjs.map} +1 -1
  658. package/dist/{state-DYwPue40.mjs → state-BpFVsJZB.mjs} +12 -4
  659. package/dist/state-BpFVsJZB.mjs.map +1 -0
  660. package/dist/{status-CWVYpbD8.mjs → status-B777-dXz.mjs} +1 -1
  661. package/dist/{status-CWVYpbD8.mjs.map → status-B777-dXz.mjs.map} +1 -1
  662. package/dist/storage/local.d.mts +2 -1
  663. package/dist/storage/local.d.mts.map +1 -1
  664. package/dist/storage/local.mjs +1 -1
  665. package/dist/storage/local.mjs.map +1 -1
  666. package/dist/storage/s3.d.mts +2 -1
  667. package/dist/storage/s3.d.mts.map +1 -1
  668. package/dist/storage/s3.mjs +3 -2
  669. package/dist/storage/s3.mjs.map +1 -1
  670. package/dist/{target-DixqqV5i.mjs → target-BmR0iTwz.mjs} +1 -1
  671. package/dist/{target-DixqqV5i.mjs.map → target-BmR0iTwz.mjs.map} +1 -1
  672. package/dist/{taxonomies-OHUIiIpI.mjs → taxonomies-DkAkMupr.mjs} +17 -14
  673. package/dist/taxonomies-DkAkMupr.mjs.map +1 -0
  674. package/dist/{taxonomies-BlrncHi9.mjs → taxonomies-Wfy_Lg12.mjs} +43 -17
  675. package/dist/taxonomies-Wfy_Lg12.mjs.map +1 -0
  676. package/dist/{taxonomy-BqCZ1TdU.mjs → taxonomy-tJepplLL.mjs} +5 -5
  677. package/dist/{taxonomy-BqCZ1TdU.mjs.map → taxonomy-tJepplLL.mjs.map} +1 -1
  678. package/dist/{tokens-C39klX8R.mjs → tokens-BCO1oruF.mjs} +1 -1
  679. package/dist/{tokens-C39klX8R.mjs.map → tokens-BCO1oruF.mjs.map} +1 -1
  680. package/dist/{transaction-NQj4VJ7Z.mjs → transaction-0KOpOyMu.mjs} +2 -1
  681. package/dist/{transaction-NQj4VJ7Z.mjs.map → transaction-0KOpOyMu.mjs.map} +1 -1
  682. package/dist/{transport-DBJwa-Si.mjs → transport-Dr3qtwXC.mjs} +4 -3
  683. package/dist/{transport-DBJwa-Si.mjs.map → transport-Dr3qtwXC.mjs.map} +1 -1
  684. package/dist/{transport-RQE36APG.d.mts → transport-xh6YKW2G.d.mts} +1 -1
  685. package/dist/{transport-RQE36APG.d.mts.map → transport-xh6YKW2G.d.mts.map} +1 -1
  686. package/dist/{trusted-proxy-CEVekpby.mjs → trusted-proxy-BR5F1Otp.mjs} +1 -1
  687. package/dist/{trusted-proxy-CEVekpby.mjs.map → trusted-proxy-BR5F1Otp.mjs.map} +1 -1
  688. package/dist/{types-BW1syrlw.mjs → types-3qNFPlci.mjs} +1 -1
  689. package/dist/{types-BW1syrlw.mjs.map → types-3qNFPlci.mjs.map} +1 -1
  690. package/dist/types-75KIqzL9.mjs.map +1 -1
  691. package/dist/{types-DrJrVCT1.d.mts → types-B6GrPic4.d.mts} +9 -3
  692. package/dist/{types-DrJrVCT1.d.mts.map → types-B6GrPic4.d.mts.map} +1 -1
  693. package/dist/{types-DN64_zzZ.d.mts → types-BuHW4xDE.d.mts} +11 -10
  694. package/dist/types-BuHW4xDE.d.mts.map +1 -0
  695. package/dist/{types-DWRhk9xZ.d.mts → types-C00oyCZp.d.mts} +1 -1
  696. package/dist/{types-DWRhk9xZ.d.mts.map → types-C00oyCZp.d.mts.map} +1 -1
  697. package/dist/{types-BntycPTF.d.mts → types-CDtkNSFQ.d.mts} +1 -1
  698. package/dist/{types-BntycPTF.d.mts.map → types-CDtkNSFQ.d.mts.map} +1 -1
  699. package/dist/{types-Dfk8KI5R.d.mts → types-CPeXo1wF.d.mts} +1 -1
  700. package/dist/{types-Dfk8KI5R.d.mts.map → types-CPeXo1wF.d.mts.map} +1 -1
  701. package/dist/types-CQPWhPxE.mjs.map +1 -1
  702. package/dist/{types-BcqdcT-r.d.mts → types-CXFkwEGC.d.mts} +3 -2
  703. package/dist/types-CXFkwEGC.d.mts.map +1 -0
  704. package/dist/{types-CtzytVXn.mjs → types-DM3vioBY.mjs} +1 -1
  705. package/dist/{types-CtzytVXn.mjs.map → types-DM3vioBY.mjs.map} +1 -1
  706. package/dist/{types-CXnOzDe1.d.mts → types-DczELMop.d.mts} +1 -1
  707. package/dist/{types-CXnOzDe1.d.mts.map → types-DczELMop.d.mts.map} +1 -1
  708. package/dist/types-DfuTR6dL.mjs +3 -0
  709. package/dist/{types-DQIDRKF2.d.mts → types-FV23hEoW.d.mts} +10 -1
  710. package/dist/types-FV23hEoW.d.mts.map +1 -0
  711. package/dist/{types-C2pMvO62.d.mts → types-xDMtUFnO.d.mts} +2 -1
  712. package/dist/types-xDMtUFnO.d.mts.map +1 -0
  713. package/dist/{upload-attempts-D4FS8LT6.mjs → upload-attempts-BsqoQ1fn.mjs} +1 -1
  714. package/dist/{upload-attempts-D4FS8LT6.mjs.map → upload-attempts-BsqoQ1fn.mjs.map} +1 -1
  715. package/dist/{url-pattern-BHS0V5Ng.mjs → url-pattern-D4c2czpJ.mjs} +1 -1
  716. package/dist/{url-pattern-BHS0V5Ng.mjs.map → url-pattern-D4c2czpJ.mjs.map} +1 -1
  717. package/dist/{user-DJZfUJVE.mjs → user-BTZ40aq6.mjs} +2 -2
  718. package/dist/{user-DJZfUJVE.mjs.map → user-BTZ40aq6.mjs.map} +1 -1
  719. package/dist/{utils-D4vD_Y98.mjs → utils-BKvNjjhY.mjs} +1 -1
  720. package/dist/{utils-D4vD_Y98.mjs.map → utils-BKvNjjhY.mjs.map} +1 -1
  721. package/dist/{validate-BMLBvxOC.mjs → validate-DE_LRwo9.mjs} +2 -2
  722. package/dist/{validate-BMLBvxOC.mjs.map → validate-DE_LRwo9.mjs.map} +1 -1
  723. package/dist/{validate-CTqt5nuE.d.mts → validate-DUP27OYD.d.mts} +5 -5
  724. package/dist/{validate-CTqt5nuE.d.mts.map → validate-DUP27OYD.d.mts.map} +1 -1
  725. package/dist/{validation-npvENUCB.mjs → validation-BMImFJ11.mjs} +10 -8
  726. package/dist/{validation-npvENUCB.mjs.map → validation-BMImFJ11.mjs.map} +1 -1
  727. package/dist/version-Bii3suaO.mjs +7 -0
  728. package/dist/{version-DB_fqCSI.mjs.map → version-Bii3suaO.mjs.map} +1 -1
  729. package/dist/{widgets-BD3ho7Rq.mjs → widgets-D3gJ10xU.mjs} +18 -5
  730. package/dist/widgets-D3gJ10xU.mjs.map +1 -0
  731. package/dist/{wxr-taxonomies-Ca68766D.d.mts → wxr-taxonomies-B2Og_6rU.d.mts} +1 -1
  732. package/dist/{wxr-taxonomies-Ca68766D.d.mts.map → wxr-taxonomies-B2Og_6rU.d.mts.map} +1 -1
  733. package/dist/{wxr-taxonomies-B1R3vBoD.mjs → wxr-taxonomies-Cxngsjwo.mjs} +4 -4
  734. package/dist/{wxr-taxonomies-B1R3vBoD.mjs.map → wxr-taxonomies-Cxngsjwo.mjs.map} +1 -1
  735. package/dist/{zod-generator-LC-on_Vb.mjs → zod-generator-BuYSQJB7.mjs} +32 -22
  736. package/dist/zod-generator-BuYSQJB7.mjs.map +1 -0
  737. package/package.json +20 -9
  738. package/src/api/errors.ts +22 -0
  739. package/src/api/handlers/content.ts +49 -11
  740. package/src/api/handlers/index.ts +18 -0
  741. package/src/api/handlers/manifest.ts +161 -12
  742. package/src/api/handlers/media-allowlist.ts +1 -0
  743. package/src/api/handlers/media-folders.ts +130 -0
  744. package/src/api/handlers/media-usage-activation.ts +102 -0
  745. package/src/api/handlers/media-usage.ts +108 -0
  746. package/src/api/handlers/media.ts +92 -2
  747. package/src/api/handlers/registry.ts +483 -561
  748. package/src/api/handlers/taxonomies.ts +16 -9
  749. package/src/api/openapi/document.ts +277 -7
  750. package/src/api/parse.ts +1 -1
  751. package/src/api/schemas/auth.ts +4 -4
  752. package/src/api/schemas/bylines.ts +3 -3
  753. package/src/api/schemas/comments.ts +1 -1
  754. package/src/api/schemas/common.ts +3 -2
  755. package/src/api/schemas/content.ts +78 -22
  756. package/src/api/schemas/media-usage.ts +77 -0
  757. package/src/api/schemas/media.ts +121 -0
  758. package/src/api/schemas/redirects.ts +6 -1
  759. package/src/api/schemas/schema.ts +4 -0
  760. package/src/api/schemas/setup.ts +1 -1
  761. package/src/api/schemas/taxonomies.ts +2 -2
  762. package/src/api/schemas/users.ts +1 -1
  763. package/src/api/types.ts +23 -10
  764. package/src/astro/dev-typegen.ts +35 -0
  765. package/src/astro/image-endpoint.ts +2 -2
  766. package/src/astro/integration/dev-typegen.ts +91 -0
  767. package/src/astro/integration/index.ts +9 -44
  768. package/src/astro/integration/routes.ts +23 -0
  769. package/src/astro/integration/vite-config.ts +23 -9
  770. package/src/astro/middleware/auth.ts +4 -0
  771. package/src/astro/middleware.ts +5 -7
  772. package/src/astro/routes/api/admin/api-tokens/index.ts +4 -1
  773. package/src/astro/routes/api/admin/media-usage/activation.ts +42 -0
  774. package/src/astro/routes/api/admin/media-usage/progress.ts +37 -0
  775. package/src/astro/routes/api/admin/oauth-clients/[id].ts +1 -1
  776. package/src/astro/routes/api/admin/oauth-clients/index.ts +1 -1
  777. package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +4 -0
  778. package/src/astro/routes/api/admin/plugins/registry/artifact.ts +247 -101
  779. package/src/astro/routes/api/admin/plugins/registry/install.ts +4 -0
  780. package/src/astro/routes/api/admin/plugins/registry/verify.ts +60 -0
  781. package/src/astro/routes/api/auth/me.ts +14 -2
  782. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +8 -2
  783. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +2 -1
  784. package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +6 -2
  785. package/src/astro/routes/api/manifest.ts +1 -1
  786. package/src/astro/routes/api/media/[id]/replace.ts +114 -0
  787. package/src/astro/routes/api/media/[id].ts +3 -0
  788. package/src/astro/routes/api/media/file/[...key].ts +4 -1
  789. package/src/astro/routes/api/media/folders/[id].ts +55 -0
  790. package/src/astro/routes/api/media/folders/index.ts +37 -0
  791. package/src/astro/routes/api/media/upload-url.ts +8 -4
  792. package/src/astro/routes/api/media.ts +66 -10
  793. package/src/astro/routes/api/menus/[name]/items/[id].ts +7 -2
  794. package/src/astro/routes/api/menus/[name]/items.ts +4 -1
  795. package/src/astro/routes/api/menus/[name]/reorder.ts +4 -1
  796. package/src/astro/routes/api/menus/[name]/translations.ts +4 -1
  797. package/src/astro/routes/api/menus/[name].ts +7 -2
  798. package/src/astro/routes/api/menus/index.ts +4 -1
  799. package/src/astro/routes/api/settings/email.ts +1 -1
  800. package/src/astro/routes/api/settings.ts +4 -1
  801. package/src/astro/routes/api/setup/dev-bypass.ts +12 -0
  802. package/src/astro/routes/api/taxonomies/[name]/reorder.ts +4 -1
  803. package/src/astro/routes/api/taxonomies/[name]/terms/[slug]/translations.ts +4 -1
  804. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +7 -2
  805. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +4 -1
  806. package/src/astro/routes/api/taxonomies/[name].ts +7 -2
  807. package/src/astro/routes/api/taxonomies/index.ts +4 -1
  808. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +3 -1
  809. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +5 -2
  810. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +3 -1
  811. package/src/astro/routes/api/widget-areas/[name].ts +3 -1
  812. package/src/astro/routes/api/widget-areas/index.ts +3 -1
  813. package/src/astro/types.ts +34 -6
  814. package/src/auth/challenge-store.ts +3 -1
  815. package/src/aws-sdk.d.ts +1 -0
  816. package/src/cache/chrome-tags.ts +24 -0
  817. package/src/client/index.ts +140 -2
  818. package/src/client/transport.ts +6 -2
  819. package/src/components/EmDashImage.astro +156 -51
  820. package/src/components/EmDashMedia.astro +2 -1
  821. package/src/components/Gallery.astro +7 -0
  822. package/src/components/InlinePortableTextEditor.tsx +136 -2
  823. package/src/components/LiveSearch.astro +23 -0
  824. package/src/components/inline-code-block.tsx +180 -49
  825. package/src/components/widgets/Archives.astro +2 -37
  826. package/src/content/converters/gallery.ts +2 -0
  827. package/src/content/converters/index.ts +2 -0
  828. package/src/content/converters/mark-safety.ts +1 -0
  829. package/src/content/converters/portable-text-identity.ts +139 -0
  830. package/src/content/converters/portable-text-to-prosemirror.ts +202 -69
  831. package/src/content/converters/prosemirror-to-portable-text.ts +96 -25
  832. package/src/content/converters/types.ts +3 -0
  833. package/src/database/connection.ts +2 -31
  834. package/src/database/index.ts +1 -1
  835. package/src/database/json-recordset.ts +16 -0
  836. package/src/database/migrations/017_authorization_codes.ts +14 -4
  837. package/src/database/migrations/019_i18n.ts +1 -1
  838. package/src/database/migrations/071_restore_content_bylines_table.ts +43 -0
  839. package/src/database/migrations/072_media_folders.ts +72 -0
  840. package/src/database/migrations/073_media_focal_point.ts +57 -0
  841. package/src/database/migrations/074_content_deleted_scheduled_index.ts +55 -0
  842. package/src/database/migrations/runner.ts +11 -3
  843. package/src/database/repositories/byline.ts +32 -16
  844. package/src/database/repositories/content.ts +119 -51
  845. package/src/database/repositories/media-folders.ts +115 -0
  846. package/src/database/repositories/media-usage-work.ts +395 -111
  847. package/src/database/repositories/media-usage.ts +1104 -47
  848. package/src/database/repositories/media.ts +156 -29
  849. package/src/database/repositories/plugin-storage.ts +74 -9
  850. package/src/database/repositories/types.ts +8 -7
  851. package/src/database/transaction.ts +1 -0
  852. package/src/database/types.ts +13 -0
  853. package/src/db/adapters.ts +10 -3
  854. package/src/db/node-sqlite-compat.ts +78 -0
  855. package/src/db/postgres.ts +23 -0
  856. package/src/db/sqlite.ts +3 -3
  857. package/src/emdash-runtime.ts +186 -251
  858. package/src/fields/boolean.ts +1 -1
  859. package/src/fields/datetime.ts +1 -1
  860. package/src/fields/file.ts +1 -1
  861. package/src/fields/image.ts +7 -1
  862. package/src/fields/integer.ts +1 -1
  863. package/src/fields/json.ts +1 -1
  864. package/src/fields/multiselect.ts +1 -1
  865. package/src/fields/number.ts +1 -1
  866. package/src/fields/portable-text.ts +1 -1
  867. package/src/fields/richtext.ts +1 -1
  868. package/src/fields/select.ts +1 -1
  869. package/src/fields/slug.ts +1 -1
  870. package/src/fields/text.ts +1 -1
  871. package/src/fields/textarea.ts +1 -1
  872. package/src/fields/types.ts +8 -4
  873. package/src/index.ts +30 -3
  874. package/src/loader.ts +63 -10
  875. package/src/mcp/server.ts +228 -24
  876. package/src/media/focal-point.ts +37 -0
  877. package/src/media/image-endpoint.ts +5 -2
  878. package/src/media/local-runtime.ts +8 -0
  879. package/src/media/mime.ts +1 -0
  880. package/src/media/normalize.ts +25 -4
  881. package/src/media/types.ts +10 -0
  882. package/src/media/usage/activation.ts +162 -35
  883. package/src/media/usage/capture-triggers.ts +19 -3
  884. package/src/media/usage/cleanup.ts +9 -8
  885. package/src/media/usage/collection-deletion-processor.ts +7 -3
  886. package/src/media/usage/collection-deletion.ts +10 -0
  887. package/src/media/usage/content-refresh.ts +258 -39
  888. package/src/media/usage/content-snapshots.ts +128 -31
  889. package/src/media/usage/extractor.ts +32 -13
  890. package/src/media/usage/maintenance-engine.ts +166 -0
  891. package/src/media/usage/reconciliation-processor.ts +106 -29
  892. package/src/media/usage/reconciliation.ts +90 -7
  893. package/src/media/usage/work-processor.ts +342 -101
  894. package/src/menus/index.ts +16 -0
  895. package/src/object-cache/index.ts +44 -2
  896. package/src/plugin-types.ts +18 -1
  897. package/src/plugins/hooks.ts +4 -1
  898. package/src/plugins/index.ts +7 -0
  899. package/src/plugins/manager.ts +2 -1
  900. package/src/plugins/manifest-schema.ts +1 -1
  901. package/src/plugins/routes.ts +3 -1
  902. package/src/plugins/sandbox/hook-result.ts +52 -0
  903. package/src/plugins/sandbox/index.ts +10 -0
  904. package/src/plugins/save-rejection.ts +20 -0
  905. package/src/plugins/scheduler/node.ts +1 -13
  906. package/src/plugins/scheduler/types.ts +0 -2
  907. package/src/plugins/state.ts +13 -3
  908. package/src/plugins/types.ts +6 -0
  909. package/src/registry/artifact-checksum.ts +15 -0
  910. package/src/registry/artifact-fetch.ts +530 -0
  911. package/src/registry/artifact-verification.ts +85 -0
  912. package/src/registry/authoritative-records.ts +180 -0
  913. package/src/registry/config.ts +24 -6
  914. package/src/registry/record-labels.ts +28 -0
  915. package/src/registry/types.ts +12 -15
  916. package/src/schema/registry.ts +26 -3
  917. package/src/schema/types.ts +1 -0
  918. package/src/schema/zod-generator.ts +28 -17
  919. package/src/security/ssrf.ts +26 -4
  920. package/src/seed/apply.ts +4 -4
  921. package/src/settings/index.ts +16 -0
  922. package/src/storage/local.ts +1 -0
  923. package/src/storage/s3.ts +2 -0
  924. package/src/storage/types.ts +2 -0
  925. package/src/taxonomies/index.ts +40 -20
  926. package/src/ui.ts +2 -0
  927. package/src/widgets/archives.ts +67 -0
  928. package/src/widgets/index.ts +16 -0
  929. package/dist/api-BeBglVxU.mjs +0 -4695
  930. package/dist/api-BeBglVxU.mjs.map +0 -1
  931. package/dist/byline-yjvWq4ip.mjs.map +0 -1
  932. package/dist/challenge-store-SIL5DsF2.mjs.map +0 -1
  933. package/dist/collection-deletion-DQPvMD8R.mjs.map +0 -1
  934. package/dist/content-ZHCc1ngN.mjs.map +0 -1
  935. package/dist/content-refresh-B_8SJzuU.mjs.map +0 -1
  936. package/dist/context-ZldbZcle.mjs.map +0 -1
  937. package/dist/errors-CLUPm7aD.mjs.map +0 -1
  938. package/dist/index-BVBZj4-7.d.mts.map +0 -1
  939. package/dist/loader-CdG_Ci3H.mjs.map +0 -1
  940. package/dist/manifest-schema-BXbUAHod.mjs.map +0 -1
  941. package/dist/media-BWWWb8kg.mjs.map +0 -1
  942. package/dist/media-CzhAn2--.mjs.map +0 -1
  943. package/dist/media-allowlist-D6x-Us02.mjs.map +0 -1
  944. package/dist/media-usage-BZCydfvu.mjs.map +0 -1
  945. package/dist/media-usage-work-DrCbqBcl.mjs.map +0 -1
  946. package/dist/menus-DSo5QeW0.mjs.map +0 -1
  947. package/dist/mime-DfIVjNkr.mjs.map +0 -1
  948. package/dist/normalize-Bc-s_r-a.mjs.map +0 -1
  949. package/dist/object-cache-71tPfV4A.mjs.map +0 -1
  950. package/dist/options--hhz-jZ1.d.mts.map +0 -1
  951. package/dist/placeholder-Dp0K3RkD.d.mts.map +0 -1
  952. package/dist/redirects-DTDHWH31.mjs.map +0 -1
  953. package/dist/registry-Cqa2I749.mjs.map +0 -1
  954. package/dist/relations-BBApI4e2.mjs.map +0 -1
  955. package/dist/relations-BHo5A8Zf.d.mts.map +0 -1
  956. package/dist/runner-BYHZn3Jy.d.mts.map +0 -1
  957. package/dist/runner-C1T11CfC.mjs.map +0 -1
  958. package/dist/settings-xRdoUZr0.mjs.map +0 -1
  959. package/dist/state-DYwPue40.mjs.map +0 -1
  960. package/dist/taxonomies-BlrncHi9.mjs.map +0 -1
  961. package/dist/taxonomies-OHUIiIpI.mjs.map +0 -1
  962. package/dist/types-BcqdcT-r.d.mts.map +0 -1
  963. package/dist/types-C2pMvO62.d.mts.map +0 -1
  964. package/dist/types-CP1CflWf.mjs +0 -3
  965. package/dist/types-DN64_zzZ.d.mts.map +0 -1
  966. package/dist/types-DQIDRKF2.d.mts.map +0 -1
  967. package/dist/version-DB_fqCSI.mjs +0 -7
  968. package/dist/widgets-BD3ho7Rq.mjs.map +0 -1
  969. package/dist/zod-generator-LC-on_Vb.mjs.map +0 -1
  970. /package/dist/{api-tokens-rIXC6LdE.mjs → api-tokens-BfYts-sq.mjs} +0 -0
  971. /package/dist/{ssrf-DpXgKsna.mjs → ssrf-Dg3cIT-b.mjs} +0 -0
@@ -1,11 +1,13 @@
1
1
  import { t as validateIdentifier } from "./validate-BpwoDOgt.mjs";
2
2
  import { p as tableExists, s as isPostgres } from "./dialect-helpers-BSbfaO3F.mjs";
3
3
  import { i as isI18nEnabled } from "./config-C3EBstHi.mjs";
4
- import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-fiZ8G9RJ.mjs";
4
+ import { getRequestContext } from "./request-context.mjs";
5
+ import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-C4szrD8o.mjs";
5
6
  import { i as InvalidCursorError, o as decodeCursor, s as encodeCursor } from "./types-CQPWhPxE.mjs";
6
- import { t as withTransaction } from "./transaction-NQj4VJ7Z.mjs";
7
- import { t as INTERNAL_MEDIA_PREFIX } from "./normalize-Bc-s_r-a.mjs";
8
- import { n as normalizeMime } from "./mime-DfIVjNkr.mjs";
7
+ import { t as withTransaction } from "./transaction-0KOpOyMu.mjs";
8
+ import { t as INTERNAL_MEDIA_PREFIX } from "./normalize-C2xNjTSG.mjs";
9
+ import { n as normalizeMime } from "./mime-CUIG6x84.mjs";
10
+ import { n as MediaUsageActivationVersionMismatchError, t as MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION } from "./activation-K0kGBWuJ.mjs";
9
11
  import { sql } from "kysely";
10
12
  import { ulid } from "ulidx";
11
13
 
@@ -100,17 +102,82 @@ function isRecord$3(value) {
100
102
  return typeof value === "object" && value !== null && !Array.isArray(value);
101
103
  }
102
104
 
105
+ //#endregion
106
+ //#region src/media/usage/types.ts
107
+ const CONTENT_SOURCE_SCHEMA_VERSION = 1;
108
+
109
+ //#endregion
110
+ //#region src/database/json-recordset.ts
111
+ function jsonTextValues(db, values) {
112
+ const payload = JSON.stringify([...new Set(values)]);
113
+ return isPostgres(db) ? sql`SELECT value::text AS value FROM jsonb_array_elements_text(${payload}::jsonb) AS value` : sql`SELECT value AS value FROM json_each(${payload})`;
114
+ }
115
+
103
116
  //#endregion
104
117
  //#region src/database/repositories/media-usage.ts
105
118
  const OCCURRENCE_BIND_COLUMNS = 13;
119
+ const D1_MAX_BOUND_PARAMETERS = 100;
106
120
  const MEDIA_USAGE_GENERATION_WRITE_LEASE_MS = 3600 * 1e3;
107
- const OCCURRENCE_INSERT_BATCH_SIZE = Math.max(1, Math.floor(SQL_BATCH_SIZE / OCCURRENCE_BIND_COLUMNS));
121
+ const OCCURRENCE_INSERT_BATCH_SIZE = Math.max(1, Math.floor(D1_MAX_BOUND_PARAMETERS / OCCURRENCE_BIND_COLUMNS));
108
122
  function cleanupDeleteBatchSize(cleanupLease) {
109
123
  return cleanupLease ? SQL_BATCH_SIZE - 3 : SQL_BATCH_SIZE;
110
124
  }
111
125
  function canIssueCleanupStatement(canIssueStatement) {
112
126
  return canIssueStatement?.() ?? true;
113
127
  }
128
+ function nullableRefMatch(left, right) {
129
+ const leftRef = sql.ref(left);
130
+ const rightRef = sql.ref(right);
131
+ return sql`(
132
+ (${leftRef} IS NULL AND ${rightRef} IS NULL)
133
+ OR ${leftRef} = ${rightRef}
134
+ )`;
135
+ }
136
+ const MAX_JSON_BIND_BYTES = 19e5;
137
+ const MEDIA_USAGE_EVENT_QUERY_CEILING = 900;
138
+ const MEDIA_USAGE_QUERY_RESERVE = 150;
139
+ function chunkJsonRows(rows) {
140
+ const batches = [];
141
+ let batch = [];
142
+ let batchBytes = 2;
143
+ for (const row of rows) {
144
+ const rowBytes = new TextEncoder().encode(JSON.stringify(row)).byteLength + (batch.length > 0 ? 1 : 0);
145
+ if (batch.length > 0 && batchBytes + rowBytes > MAX_JSON_BIND_BYTES) {
146
+ batches.push(batch);
147
+ batch = [];
148
+ batchBytes = 2;
149
+ }
150
+ batch.push(row);
151
+ batchBytes += rowBytes;
152
+ }
153
+ if (batch.length > 0) batches.push(batch);
154
+ return batches;
155
+ }
156
+ function mediaUsageQueryBudgetAllows(queries) {
157
+ const metrics = getRequestContext()?.metrics;
158
+ return !metrics || metrics.dbCount + queries + MEDIA_USAGE_QUERY_RESERVE <= MEDIA_USAGE_EVENT_QUERY_CEILING;
159
+ }
160
+ function mergeInto(target, source) {
161
+ for (const value of source) target.add(value);
162
+ }
163
+ function batchOccurrenceRows(prepared, now) {
164
+ return prepared.flatMap((item) => item.projection.occurrences.map((occurrence) => ({
165
+ id: ulid(),
166
+ source_key: item.row.source_key,
167
+ generation: item.generation,
168
+ field_slug: occurrence.fieldSlug,
169
+ field_path: occurrence.fieldPath,
170
+ occurrence_index: occurrence.occurrenceIndex ?? 0,
171
+ reference_type: occurrence.referenceType,
172
+ media_id: occurrence.mediaId,
173
+ provider: occurrence.provider,
174
+ provider_asset_id: occurrence.providerAssetId,
175
+ media_kind: occurrence.mediaKind ?? null,
176
+ mime_type: occurrence.mimeType ?? null,
177
+ created_at: now,
178
+ lease_token: item.leaseToken
179
+ })));
180
+ }
114
181
  function cleanupDurationSeconds(value) {
115
182
  if (!Number.isSafeInteger(value) || value < 0) throw new Error("Media usage cleanup duration must be a non-negative whole number of seconds");
116
183
  return value;
@@ -199,9 +266,14 @@ var MediaUsageRepository = class {
199
266
  const uniqueSourceKeys = [...new Set(sourceKeys)];
200
267
  const sources = /* @__PURE__ */ new Map();
201
268
  if (uniqueSourceKeys.length === 0) return sources;
202
- for (const sourceKeyBatch of chunks(uniqueSourceKeys, SQL_BATCH_SIZE)) {
203
- const rows = await this.db.selectFrom("_emdash_media_usage_sources").selectAll().where("source_key", "in", sourceKeyBatch).execute();
204
- for (const row of rows) {
269
+ for (const sourceKeyBatch of chunkJsonRows(uniqueSourceKeys)) {
270
+ const result = await sql`
271
+ WITH requested AS (${jsonTextValues(this.db, sourceKeyBatch)})
272
+ SELECT source.*
273
+ FROM _emdash_media_usage_sources AS source
274
+ INNER JOIN requested ON requested.value = source.source_key
275
+ `.execute(this.db);
276
+ for (const row of result.rows) {
205
277
  const source = rowToSource(row);
206
278
  sources.set(source.sourceKey, source);
207
279
  }
@@ -250,6 +322,388 @@ var MediaUsageRepository = class {
250
322
  source: replaced ? null : await this.findSource(source.sourceKey)
251
323
  };
252
324
  }
325
+ async replaceNewSourcesBatch(projections) {
326
+ const unique = [...new Map(projections.map((projection) => [projection.source.sourceKey, projection])).values()];
327
+ if (unique.length === 0) return /* @__PURE__ */ new Set();
328
+ const collectionSlug = unique[0]?.source.collectionSlug;
329
+ const collectionId = unique[0]?.source.collectionId;
330
+ if (!collectionSlug || !collectionId) throw new Error("Canonical media usage batch requires collection identity");
331
+ validateIdentifier(collectionSlug, "collection slug");
332
+ if (unique.some((projection) => projection.source.collectionSlug !== collectionSlug || projection.source.collectionId !== collectionId)) throw new Error("Canonical media usage batch must contain one collection");
333
+ const now = (/* @__PURE__ */ new Date()).toISOString();
334
+ const expiresAt = new Date(Date.now() + MEDIA_USAGE_GENERATION_WRITE_LEASE_MS).toISOString();
335
+ const prepared = unique.map((projection) => {
336
+ const generation = ulid();
337
+ return {
338
+ projection,
339
+ generation,
340
+ leaseToken: ulid(),
341
+ row: this.buildSourceRow(projection.source, generation, now)
342
+ };
343
+ });
344
+ const sourceRows = prepared.map((item) => ({
345
+ ...item.row,
346
+ lease_token: item.leaseToken
347
+ }));
348
+ const occurrenceRows = batchOccurrenceRows(prepared, now);
349
+ if (!mediaUsageQueryBudgetAllows(2 + chunkJsonRows(occurrenceRows).length + chunkJsonRows(sourceRows).length)) {
350
+ if (unique.length === 1) return /* @__PURE__ */ new Set();
351
+ const midpoint = Math.ceil(unique.length / 2);
352
+ const inserted = /* @__PURE__ */ new Set();
353
+ mergeInto(inserted, await this.replaceNewSourcesBatch(unique.slice(0, midpoint)));
354
+ mergeInto(inserted, await this.replaceNewSourcesBatch(unique.slice(midpoint)));
355
+ return inserted;
356
+ }
357
+ const leasesPayload = JSON.stringify(prepared.map((item) => ({
358
+ source_key: item.row.source_key,
359
+ collection_id: item.row.collection_id,
360
+ collection_slug: item.row.collection_slug,
361
+ generation: item.generation,
362
+ lease_token: item.leaseToken,
363
+ expires_at: expiresAt,
364
+ created_at: now
365
+ })));
366
+ const leases = this.generationWriteBatchInput(leasesPayload);
367
+ await sql`
368
+ WITH input AS (${leases})
369
+ INSERT INTO _emdash_media_usage_generation_writes (
370
+ source_key, generation, lease_token, expires_at, created_at
371
+ )
372
+ SELECT source_key, generation, lease_token, expires_at, created_at
373
+ FROM input
374
+ WHERE EXISTS (
375
+ SELECT 1
376
+ FROM _emdash_collections AS collection
377
+ INNER JOIN _emdash_media_usage_index_status AS status
378
+ ON status.collection_id = collection.id
379
+ AND status.scope_key = collection.slug
380
+ WHERE collection.id = input.collection_id
381
+ AND collection.slug = input.collection_slug
382
+ AND status.adapter_id = 'content-media'
383
+ AND status.scope_type = 'collection'
384
+ AND status.capture_state = 'active'
385
+ AND NOT EXISTS (
386
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
387
+ WHERE deletion.collection_id = input.collection_id
388
+ )
389
+ )
390
+ `.execute(this.db);
391
+ try {
392
+ await this.insertBatchOccurrences(occurrenceRows);
393
+ const tableName = `ec_${collectionSlug}`;
394
+ validateIdentifier(tableName, "content table");
395
+ const inserted = /* @__PURE__ */ new Set();
396
+ for (const sourceRowBatch of chunkJsonRows(sourceRows)) {
397
+ const result = await sql`
398
+ WITH input AS (${this.sourceBatchInput(JSON.stringify(sourceRowBatch))})
399
+ INSERT INTO _emdash_media_usage_sources (
400
+ source_key, source_type, collection_id, collection_slug, content_id,
401
+ source_variant, locale, translation_group, content_slug, content_title,
402
+ content_status, content_scheduled_at, content_deleted_at, revision_id,
403
+ current_generation, schema_version, source_updated_at, source_version,
404
+ source_fingerprint, identity_version, source_completeness,
405
+ last_attempted_at, last_error_code, indexed_at, updated_at
406
+ )
407
+ SELECT
408
+ input.source_key, input.source_type, input.collection_id, input.collection_slug,
409
+ input.content_id, input.source_variant, input.locale, input.translation_group,
410
+ input.content_slug, input.content_title, input.content_status,
411
+ input.content_scheduled_at, input.content_deleted_at, input.revision_id,
412
+ input.current_generation, input.schema_version, input.source_updated_at,
413
+ input.source_version, input.source_fingerprint, input.identity_version,
414
+ input.source_completeness, input.last_attempted_at, input.last_error_code,
415
+ input.indexed_at, input.updated_at
416
+ FROM input
417
+ WHERE EXISTS (
418
+ SELECT 1
419
+ FROM _emdash_media_usage_generation_writes AS writer
420
+ WHERE writer.source_key = input.source_key
421
+ AND writer.generation = input.current_generation
422
+ AND writer.lease_token = input.lease_token
423
+ AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
424
+ )
425
+ AND EXISTS (
426
+ SELECT 1
427
+ FROM _emdash_collections AS collection
428
+ INNER JOIN _emdash_media_usage_index_status AS status
429
+ ON status.collection_id = collection.id
430
+ AND status.scope_key = collection.slug
431
+ WHERE collection.id = input.collection_id
432
+ AND collection.slug = input.collection_slug
433
+ AND status.adapter_id = 'content-media'
434
+ AND status.scope_type = 'collection'
435
+ AND status.capture_state = 'active'
436
+ AND NOT EXISTS (
437
+ SELECT 1
438
+ FROM _emdash_media_usage_collection_deletions AS deletion
439
+ WHERE deletion.collection_id = input.collection_id
440
+ )
441
+ )
442
+ AND EXISTS (
443
+ SELECT 1
444
+ FROM ${sql.ref(tableName)} AS content
445
+ WHERE content.id = input.content_id
446
+ AND content.version = input.source_version
447
+ AND content.updated_at = input.source_updated_at
448
+ AND (
449
+ (input.source_variant = 'columns' AND ${nullableRefMatch("content.live_revision_id", "input.revision_id")})
450
+ OR
451
+ (input.source_variant = 'draft_overlay' AND ${nullableRefMatch("content.draft_revision_id", "input.revision_id")})
452
+ )
453
+ )
454
+ ON CONFLICT (source_key) DO NOTHING
455
+ RETURNING source_key
456
+ `.execute(this.db);
457
+ for (const row of result.rows) inserted.add(row.source_key);
458
+ }
459
+ return inserted;
460
+ } finally {
461
+ await sql`
462
+ WITH input AS (${leases})
463
+ DELETE FROM _emdash_media_usage_generation_writes AS writer
464
+ WHERE EXISTS (
465
+ SELECT 1
466
+ FROM input
467
+ WHERE input.source_key = writer.source_key
468
+ AND input.generation = writer.generation
469
+ AND input.lease_token = writer.lease_token
470
+ )
471
+ `.execute(this.db);
472
+ }
473
+ }
474
+ async replaceExistingSourcesBatch(projections) {
475
+ const unique = [...new Map(projections.map((projection) => [projection.source.sourceKey, projection])).values()];
476
+ if (unique.length === 0) return /* @__PURE__ */ new Set();
477
+ const collectionSlug = unique[0]?.source.collectionSlug;
478
+ const collectionId = unique[0]?.source.collectionId;
479
+ if (!collectionSlug || !collectionId) throw new Error("Canonical media usage batch requires collection identity");
480
+ validateIdentifier(collectionSlug, "collection slug");
481
+ if (unique.some((projection) => projection.source.collectionSlug !== collectionSlug || projection.source.collectionId !== collectionId)) throw new Error("Canonical media usage batch must contain one collection");
482
+ const now = (/* @__PURE__ */ new Date()).toISOString();
483
+ const expiresAt = new Date(Date.now() + MEDIA_USAGE_GENERATION_WRITE_LEASE_MS).toISOString();
484
+ const prepared = unique.map((projection) => {
485
+ const generation = ulid();
486
+ return {
487
+ projection,
488
+ generation,
489
+ leaseToken: ulid(),
490
+ row: this.buildSourceRow(projection.source, generation, now)
491
+ };
492
+ });
493
+ const sourceRows = prepared.map((item) => ({
494
+ ...item.row,
495
+ lease_token: item.leaseToken,
496
+ expected_generation: item.projection.expectedSource.currentGeneration,
497
+ expected_collection_id: item.projection.expectedSource.collectionId,
498
+ expected_updated_at: item.projection.expectedSource.updatedAt,
499
+ expected_source_fingerprint: item.projection.expectedSource.sourceFingerprint,
500
+ expected_source_updated_at: item.projection.expectedSource.sourceUpdatedAt,
501
+ expected_source_version: item.projection.expectedSource.sourceVersion,
502
+ expected_identity_version: item.projection.expectedSource.identityVersion,
503
+ expected_revision_id: item.projection.expectedSource.revisionId,
504
+ expected_source_completeness: item.projection.expectedSource.sourceCompleteness,
505
+ expected_last_attempted_at: item.projection.expectedSource.lastAttemptedAt,
506
+ expected_last_error_code: item.projection.expectedSource.lastErrorCode
507
+ }));
508
+ const occurrenceRows = batchOccurrenceRows(prepared, now);
509
+ if (!mediaUsageQueryBudgetAllows(2 + chunkJsonRows(occurrenceRows).length + chunkJsonRows(sourceRows).length)) {
510
+ if (unique.length === 1) return /* @__PURE__ */ new Set();
511
+ const midpoint = Math.ceil(unique.length / 2);
512
+ const replaced = /* @__PURE__ */ new Set();
513
+ mergeInto(replaced, await this.replaceExistingSourcesBatch(unique.slice(0, midpoint)));
514
+ mergeInto(replaced, await this.replaceExistingSourcesBatch(unique.slice(midpoint)));
515
+ return replaced;
516
+ }
517
+ const leasesPayload = JSON.stringify(prepared.map((item) => ({
518
+ source_key: item.row.source_key,
519
+ collection_id: item.row.collection_id,
520
+ collection_slug: item.row.collection_slug,
521
+ generation: item.generation,
522
+ lease_token: item.leaseToken,
523
+ expires_at: expiresAt,
524
+ created_at: now
525
+ })));
526
+ const leases = this.generationWriteBatchInput(leasesPayload);
527
+ await sql`
528
+ WITH input AS (${leases})
529
+ INSERT INTO _emdash_media_usage_generation_writes (
530
+ source_key, generation, lease_token, expires_at, created_at
531
+ )
532
+ SELECT source_key, generation, lease_token, expires_at, created_at
533
+ FROM input
534
+ WHERE EXISTS (
535
+ SELECT 1
536
+ FROM _emdash_collections AS collection
537
+ INNER JOIN _emdash_media_usage_index_status AS status
538
+ ON status.collection_id = collection.id
539
+ AND status.scope_key = collection.slug
540
+ WHERE collection.id = input.collection_id
541
+ AND collection.slug = input.collection_slug
542
+ AND status.adapter_id = 'content-media'
543
+ AND status.scope_type = 'collection'
544
+ AND status.capture_state = 'active'
545
+ AND NOT EXISTS (
546
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
547
+ WHERE deletion.collection_id = input.collection_id
548
+ )
549
+ )
550
+ `.execute(this.db);
551
+ try {
552
+ await this.insertBatchOccurrences(occurrenceRows);
553
+ const tableName = `ec_${collectionSlug}`;
554
+ validateIdentifier(tableName, "content table");
555
+ const replaced = /* @__PURE__ */ new Set();
556
+ for (const sourceRowBatch of chunkJsonRows(sourceRows)) {
557
+ const result = await sql`
558
+ WITH input AS (${this.sourceBatchInput(JSON.stringify(sourceRowBatch))})
559
+ UPDATE _emdash_media_usage_sources AS source
560
+ SET source_type = input.source_type,
561
+ collection_id = input.collection_id,
562
+ collection_slug = input.collection_slug,
563
+ content_id = input.content_id,
564
+ source_variant = input.source_variant,
565
+ locale = input.locale,
566
+ translation_group = input.translation_group,
567
+ content_slug = input.content_slug,
568
+ content_title = input.content_title,
569
+ content_status = input.content_status,
570
+ content_scheduled_at = input.content_scheduled_at,
571
+ content_deleted_at = input.content_deleted_at,
572
+ revision_id = input.revision_id,
573
+ current_generation = input.current_generation,
574
+ schema_version = input.schema_version,
575
+ source_updated_at = input.source_updated_at,
576
+ source_version = input.source_version,
577
+ source_fingerprint = input.source_fingerprint,
578
+ identity_version = input.identity_version,
579
+ source_completeness = input.source_completeness,
580
+ last_attempted_at = input.last_attempted_at,
581
+ last_error_code = input.last_error_code,
582
+ indexed_at = input.indexed_at,
583
+ updated_at = input.updated_at
584
+ FROM input
585
+ WHERE source.source_key = input.source_key
586
+ AND source.current_generation = input.expected_generation
587
+ AND ${this.batchRefsMatch("source.collection_id", "input.expected_collection_id")}
588
+ AND source.updated_at = input.expected_updated_at
589
+ AND ${this.batchRefsMatch("source.source_fingerprint", "input.expected_source_fingerprint")}
590
+ AND ${this.batchRefsMatch("source.source_updated_at", "input.expected_source_updated_at")}
591
+ AND ${this.batchRefsMatch("source.source_version", "input.expected_source_version")}
592
+ AND ${this.batchRefsMatch("source.identity_version", "input.expected_identity_version")}
593
+ AND ${this.batchRefsMatch("source.revision_id", "input.expected_revision_id")}
594
+ AND source.source_completeness = input.expected_source_completeness
595
+ AND ${this.batchRefsMatch("source.last_attempted_at", "input.expected_last_attempted_at")}
596
+ AND ${this.batchRefsMatch("source.last_error_code", "input.expected_last_error_code")}
597
+ AND EXISTS (
598
+ SELECT 1
599
+ FROM _emdash_media_usage_generation_writes AS writer
600
+ WHERE writer.source_key = input.source_key
601
+ AND writer.generation = input.current_generation
602
+ AND writer.lease_token = input.lease_token
603
+ AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
604
+ )
605
+ AND EXISTS (
606
+ SELECT 1
607
+ FROM _emdash_collections AS collection
608
+ INNER JOIN _emdash_media_usage_index_status AS status
609
+ ON status.collection_id = collection.id
610
+ AND status.scope_key = collection.slug
611
+ WHERE collection.id = input.collection_id
612
+ AND collection.slug = input.collection_slug
613
+ AND status.adapter_id = 'content-media'
614
+ AND status.scope_type = 'collection'
615
+ AND status.capture_state = 'active'
616
+ AND NOT EXISTS (
617
+ SELECT 1
618
+ FROM _emdash_media_usage_collection_deletions AS deletion
619
+ WHERE deletion.collection_id = input.collection_id
620
+ )
621
+ )
622
+ AND EXISTS (
623
+ SELECT 1
624
+ FROM ${sql.ref(tableName)} AS content
625
+ WHERE content.id = input.content_id
626
+ AND content.version = input.source_version
627
+ AND content.updated_at = input.source_updated_at
628
+ AND (
629
+ (input.source_variant = 'columns' AND ${nullableRefMatch("content.live_revision_id", "input.revision_id")})
630
+ OR
631
+ (input.source_variant = 'draft_overlay' AND ${nullableRefMatch("content.draft_revision_id", "input.revision_id")})
632
+ )
633
+ )
634
+ RETURNING ${isPostgres(this.db) ? sql`source.source_key` : sql`source_key`} AS source_key
635
+ `.execute(this.db);
636
+ for (const row of result.rows) replaced.add(row.source_key);
637
+ }
638
+ return replaced;
639
+ } finally {
640
+ await sql`
641
+ WITH input AS (${leases})
642
+ DELETE FROM _emdash_media_usage_generation_writes AS writer
643
+ WHERE EXISTS (
644
+ SELECT 1
645
+ FROM input
646
+ WHERE input.source_key = writer.source_key
647
+ AND input.generation = writer.generation
648
+ AND input.lease_token = writer.lease_token
649
+ )
650
+ `.execute(this.db);
651
+ }
652
+ }
653
+ async matchingExistingSourcesBatch(projections) {
654
+ if (projections.length === 0) return /* @__PURE__ */ new Set();
655
+ const now = (/* @__PURE__ */ new Date()).toISOString();
656
+ const rows = projections.map((projection) => ({
657
+ ...this.buildSourceRow(projection.source, projection.expectedSource.currentGeneration, now),
658
+ expected_generation: projection.expectedSource.currentGeneration,
659
+ expected_collection_id: projection.expectedSource.collectionId,
660
+ expected_updated_at: projection.expectedSource.updatedAt,
661
+ expected_source_fingerprint: projection.expectedSource.sourceFingerprint,
662
+ expected_source_updated_at: projection.expectedSource.sourceUpdatedAt,
663
+ expected_source_version: projection.expectedSource.sourceVersion,
664
+ expected_identity_version: projection.expectedSource.identityVersion,
665
+ expected_revision_id: projection.expectedSource.revisionId,
666
+ expected_source_completeness: projection.expectedSource.sourceCompleteness,
667
+ expected_last_attempted_at: projection.expectedSource.lastAttemptedAt,
668
+ expected_last_error_code: projection.expectedSource.lastErrorCode
669
+ }));
670
+ if (!mediaUsageQueryBudgetAllows(chunkJsonRows(rows).length)) {
671
+ if (projections.length === 1) return /* @__PURE__ */ new Set();
672
+ const midpoint = Math.ceil(projections.length / 2);
673
+ const matched = /* @__PURE__ */ new Set();
674
+ mergeInto(matched, await this.matchingExistingSourcesBatch(projections.slice(0, midpoint)));
675
+ mergeInto(matched, await this.matchingExistingSourcesBatch(projections.slice(midpoint)));
676
+ return matched;
677
+ }
678
+ const matched = /* @__PURE__ */ new Set();
679
+ for (const batch of chunkJsonRows(rows)) {
680
+ const result = await sql`
681
+ WITH input AS (${this.sourceBatchInput(JSON.stringify(batch))})
682
+ SELECT source.source_key
683
+ FROM _emdash_media_usage_sources AS source
684
+ INNER JOIN input ON input.source_key = source.source_key
685
+ WHERE source.current_generation = input.expected_generation
686
+ AND ${this.batchRefsMatch("source.collection_id", "input.expected_collection_id")}
687
+ AND source.updated_at = input.expected_updated_at
688
+ AND ${this.batchRefsMatch("source.source_fingerprint", "input.expected_source_fingerprint")}
689
+ AND ${this.batchRefsMatch("source.source_updated_at", "input.expected_source_updated_at")}
690
+ AND ${this.batchRefsMatch("source.source_version", "input.expected_source_version")}
691
+ AND ${this.batchRefsMatch("source.identity_version", "input.expected_identity_version")}
692
+ AND ${this.batchRefsMatch("source.revision_id", "input.expected_revision_id")}
693
+ AND source.source_completeness = input.expected_source_completeness
694
+ AND ${this.batchRefsMatch("source.last_attempted_at", "input.expected_last_attempted_at")}
695
+ AND ${this.batchRefsMatch("source.last_error_code", "input.expected_last_error_code")}
696
+ AND EXISTS (
697
+ SELECT 1
698
+ FROM _emdash_collections AS collection
699
+ WHERE collection.id = source.collection_id
700
+ AND collection.slug = source.collection_slug
701
+ )
702
+ `.execute(this.db);
703
+ for (const row of result.rows) matched.add(row.source_key);
704
+ }
705
+ return matched;
706
+ }
253
707
  async markSourceAttempted(source) {
254
708
  if (source.collectionId !== void 0 && source.collectionId !== null) {
255
709
  const expectedSource = await this.findSource(source.sourceKey);
@@ -317,6 +771,103 @@ var MediaUsageRepository = class {
317
771
  reconciliationRequired: row.reconciliation_required !== null && Number(row.reconciliation_required) !== 0
318
772
  }));
319
773
  }
774
+ async findCollectionProgress() {
775
+ const row = (await sql`
776
+ WITH collection_progress AS (
777
+ SELECT
778
+ CASE WHEN status.status = 'complete'
779
+ AND status.schema_version = ${CONTENT_SOURCE_SCHEMA_VERSION}
780
+ AND status.reconciliation_required = 0
781
+ AND status.capture_state = 'active'
782
+ AND NOT EXISTS (
783
+ SELECT 1 FROM _emdash_media_usage_work AS work
784
+ WHERE work.collection_id = collection.id
785
+ AND work.collection_slug = collection.slug
786
+ )
787
+ THEN 1 ELSE 0 END AS is_ready,
788
+ CASE WHEN status.collection_id IS NULL
789
+ OR COALESCE(status.capture_state, '') <> 'active'
790
+ OR status.status NOT IN ('complete', 'never', 'running', 'partial', 'failed', 'stale')
791
+ OR status.status = 'failed'
792
+ OR (
793
+ status.reconciliation_required = 0
794
+ AND (
795
+ status.status <> 'complete'
796
+ OR COALESCE(status.schema_version, -1) <> ${CONTENT_SOURCE_SCHEMA_VERSION}
797
+ )
798
+ AND NOT EXISTS (
799
+ SELECT 1 FROM _emdash_media_usage_work AS work
800
+ WHERE work.collection_id = collection.id
801
+ AND work.collection_slug = collection.slug
802
+ )
803
+ )
804
+ OR EXISTS (
805
+ SELECT 1 FROM _emdash_media_usage_work AS work
806
+ WHERE work.collection_id = collection.id
807
+ AND work.collection_slug = collection.slug
808
+ AND work.state = 'failed'
809
+ )
810
+ OR EXISTS (
811
+ SELECT 1 FROM _emdash_media_usage_reconciliations AS reconciliation
812
+ WHERE reconciliation.collection_id = collection.id
813
+ AND reconciliation.collection_slug = collection.slug
814
+ AND reconciliation.state = 'failed'
815
+ AND status.reconciliation_required = 1
816
+ AND (
817
+ reconciliation.target_epoch IS NULL
818
+ OR reconciliation.target_epoch >= status.change_epoch
819
+ )
820
+ )
821
+ THEN 1 ELSE 0 END AS needs_attention
822
+ FROM _emdash_collections AS collection
823
+ LEFT JOIN _emdash_media_usage_index_status AS status
824
+ ON status.adapter_id = 'content-media'
825
+ AND status.scope_type = 'collection'
826
+ AND status.collection_id = collection.id
827
+ AND status.scope_key = collection.slug
828
+ WHERE NOT EXISTS (
829
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
830
+ WHERE deletion.collection_id = collection.id
831
+ AND deletion.collection_slug = collection.slug
832
+ )
833
+ )
834
+ SELECT
835
+ (
836
+ SELECT activation.runtime_generation
837
+ FROM _emdash_media_usage_activation AS activation
838
+ WHERE activation.task_key = 'incremental_capture'
839
+ ) AS activation_generation,
840
+ EXISTS (
841
+ SELECT 1 FROM _emdash_media_usage_activation AS activation
842
+ WHERE activation.task_key = 'incremental_capture'
843
+ AND activation.state = 'active'
844
+ ) AS activation_active,
845
+ COUNT(*) AS total_collections,
846
+ COALESCE(SUM(is_ready), 0) AS ready_collections,
847
+ EXISTS (
848
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
849
+ WHERE deletion.state IN ('pending', 'retry', 'leased')
850
+ ) AS cleanup_pending,
851
+ CASE WHEN COALESCE(MAX(needs_attention), 0) = 1 OR EXISTS (
852
+ SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
853
+ WHERE deletion.state = 'failed'
854
+ ) THEN 1 ELSE 0 END AS needs_attention
855
+ FROM collection_progress
856
+ `.execute(this.db)).rows[0];
857
+ if (!row) throw new Error("Media usage progress query returned no result");
858
+ if (!row.activation_active) return null;
859
+ if (Number(row.activation_generation) !== MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION) throw new MediaUsageActivationVersionMismatchError("Media usage activation runtime generation is incompatible");
860
+ const readyCollections = Number(row.ready_collections);
861
+ const totalCollections = Number(row.total_collections);
862
+ const cleanupPending = Number(row.cleanup_pending);
863
+ const needsAttention = Number(row.needs_attention);
864
+ if (!Number.isSafeInteger(readyCollections) || !Number.isSafeInteger(totalCollections) || readyCollections < 0 || totalCollections < readyCollections || cleanupPending !== 0 && cleanupPending !== 1 || needsAttention !== 0 && needsAttention !== 1) throw new Error("Media usage progress query returned invalid counts");
865
+ return {
866
+ status: needsAttention ? "needs_attention" : readyCollections === totalCollections && cleanupPending === 0 ? "ready" : "indexing",
867
+ readyCollections,
868
+ totalCollections
869
+ };
870
+ }
320
871
  async findCurrentEntryUsagePageByMediaId(mediaId, options = {}) {
321
872
  const requestedLimit = Math.floor(options.limit ?? 50);
322
873
  const limit = Number.isFinite(requestedLimit) ? Math.min(Math.max(1, requestedLimit), 100) : 50;
@@ -469,7 +1020,14 @@ var MediaUsageRepository = class {
469
1020
  consecutiveFailures: row.consecutive_failures
470
1021
  };
471
1022
  }
1023
+ /**
1024
+ * Scans for reclaimable occurrences, or resolves to `null` when the sweep was
1025
+ * never issued because the lease is gone or the tick is out of budget. An
1026
+ * empty array means the scan ran and found nothing, which lets the caller
1027
+ * retire the sweep window; `null` must leave the cursor where it was.
1028
+ */
472
1029
  async findMediaUsageCleanupCandidates(input) {
1030
+ if (!await this.admitCleanupScan(input.cleanupLease, input.canIssueStatement)) return null;
473
1031
  let query = this.db.selectFrom("_emdash_media_usage as u").leftJoin("_emdash_media_usage_sources as s", "s.source_key", "u.source_key").leftJoin("_emdash_media_usage_generation_writes as writer", (join) => join.onRef("writer.source_key", "=", "u.source_key").onRef("writer.generation", "=", "u.generation")).select([
474
1032
  "u.id as id",
475
1033
  "u.source_key as source_key",
@@ -479,8 +1037,10 @@ var MediaUsageRepository = class {
479
1037
  "s.indexed_at as indexed_at",
480
1038
  "writer.expires_at as write_lease_expires_at"
481
1039
  ]).where("u.created_at", "<", input.cutoff).orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(Math.max(0, Math.floor(input.limit)));
482
- if (input.cleanupLease) query = query.where(this.activeCleanupLeaseExpression(input.cleanupLease));
483
- if (input.cursor) query = query.where((eb) => eb.or([eb("u.created_at", ">", input.cursor.createdAt), eb.and([eb("u.created_at", "=", input.cursor.createdAt), eb("u.id", ">", input.cursor.id)])]));
1040
+ if (input.cursor) {
1041
+ const cursor = input.cursor;
1042
+ query = query.where((eb) => eb(eb.refTuple("u.created_at", "u.id"), ">", eb.tuple(cursor.createdAt, cursor.id)));
1043
+ }
484
1044
  return (await query.execute()).map((row) => ({
485
1045
  id: row.id,
486
1046
  sourceKey: row.source_key,
@@ -532,38 +1092,31 @@ var MediaUsageRepository = class {
532
1092
  const batchLimit = Math.floor(limit);
533
1093
  if (batchLimit <= 0) return 0;
534
1094
  if (options.candidateIds) return this.deleteOrphanCandidateIds(options.candidateIds.slice(0, batchLimit), cutoff, options.cleanupLease, options.canIssueStatement);
535
- if (!canIssueCleanupStatement(options.canIssueStatement)) return 0;
536
- let query = this.db.selectFrom("_emdash_media_usage as u").leftJoin("_emdash_media_usage_sources as s", (join) => join.onRef("s.source_key", "=", "u.source_key")).leftJoin("_emdash_media_usage_generation_writes as writer", (join) => join.onRef("writer.source_key", "=", "u.source_key").onRef("writer.generation", "=", "u.generation")).select("u.id").where("s.source_key", "is", null).where("u.created_at", "<", cutoff).where(this.noActiveGenerationWriteExpression("u")).orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(batchLimit);
537
- if (options.cleanupLease) query = query.where(this.activeCleanupLeaseExpression(options.cleanupLease));
538
- const rows = await query.execute();
1095
+ if (!await this.admitCleanupScan(options.cleanupLease, options.canIssueStatement)) return 0;
1096
+ const rows = await this.db.selectFrom("_emdash_media_usage as u").leftJoin("_emdash_media_usage_sources as s", (join) => join.onRef("s.source_key", "=", "u.source_key")).leftJoin("_emdash_media_usage_generation_writes as writer", (join) => join.onRef("writer.source_key", "=", "u.source_key").onRef("writer.generation", "=", "u.generation")).select("u.id").where("s.source_key", "is", null).where("u.created_at", "<", cutoff).where(this.noActiveGenerationWriteExpression("u")).orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(batchLimit).execute();
539
1097
  return this.deleteOrphanCandidateIds(rows.map((row) => row.id), cutoff, options.cleanupLease, options.canIssueStatement);
540
1098
  }
541
1099
  async deleteStaleGenerationsOlderThan(cutoff, limit, options = {}) {
542
1100
  const batchLimit = Math.floor(limit);
543
1101
  if (batchLimit <= 0) return 0;
544
1102
  if (options.candidateIds) return this.deleteStaleCandidateIds(options.candidateIds.slice(0, batchLimit), cutoff, options.cleanupLease, options.canIssueStatement);
545
- if (!canIssueCleanupStatement(options.canIssueStatement)) return 0;
546
- let query = this.db.selectFrom("_emdash_media_usage as u").innerJoin("_emdash_media_usage_sources as s", (join) => join.onRef("s.source_key", "=", "u.source_key")).leftJoin("_emdash_media_usage_generation_writes as writer", (join) => join.onRef("writer.source_key", "=", "u.source_key").onRef("writer.generation", "=", "u.generation")).select("u.id").where("u.created_at", "<", cutoff).whereRef("u.generation", "!=", "s.current_generation").whereRef("u.created_at", "<", "s.indexed_at").where(this.noActiveGenerationWriteExpression("u")).orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(batchLimit);
547
- if (options.cleanupLease) query = query.where(this.activeCleanupLeaseExpression(options.cleanupLease));
548
- const rows = await query.execute();
1103
+ if (!await this.admitCleanupScan(options.cleanupLease, options.canIssueStatement)) return 0;
1104
+ const rows = await this.db.selectFrom("_emdash_media_usage as u").innerJoin("_emdash_media_usage_sources as s", (join) => join.onRef("s.source_key", "=", "u.source_key")).leftJoin("_emdash_media_usage_generation_writes as writer", (join) => join.onRef("writer.source_key", "=", "u.source_key").onRef("writer.generation", "=", "u.generation")).select("u.id").where("u.created_at", "<", cutoff).whereRef("u.generation", "!=", "s.current_generation").whereRef("u.created_at", "<", "s.indexed_at").where(this.noActiveGenerationWriteExpression("u")).orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(batchLimit).execute();
549
1105
  return this.deleteStaleCandidateIds(rows.map((row) => row.id), cutoff, options.cleanupLease, options.canIssueStatement);
550
1106
  }
551
1107
  async deleteAbandonedGenerationsOlderThan(cutoff, limit, options = {}) {
552
1108
  const batchLimit = Math.floor(limit);
553
1109
  if (batchLimit <= 0) return 0;
554
1110
  if (options.candidateIds) return this.deleteAbandonedCandidateIds(options.candidateIds.slice(0, batchLimit), cutoff, options.cleanupLease, options.canIssueStatement);
555
- if (!canIssueCleanupStatement(options.canIssueStatement)) return 0;
556
- let query = this.db.selectFrom("_emdash_media_usage as u").innerJoin("_emdash_media_usage_sources as s", (join) => join.onRef("s.source_key", "=", "u.source_key")).leftJoin("_emdash_media_usage_generation_writes as writer", (join) => join.onRef("writer.source_key", "=", "u.source_key").onRef("writer.generation", "=", "u.generation")).select("u.id").where("u.created_at", "<", cutoff).whereRef("u.generation", "!=", "s.current_generation").whereRef("u.created_at", ">=", "s.indexed_at").where(this.noActiveGenerationWriteExpression("u")).orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(batchLimit);
557
- if (options.cleanupLease) query = query.where(this.activeCleanupLeaseExpression(options.cleanupLease));
558
- const rows = await query.execute();
1111
+ if (!await this.admitCleanupScan(options.cleanupLease, options.canIssueStatement)) return 0;
1112
+ const rows = await this.db.selectFrom("_emdash_media_usage as u").innerJoin("_emdash_media_usage_sources as s", (join) => join.onRef("s.source_key", "=", "u.source_key")).leftJoin("_emdash_media_usage_generation_writes as writer", (join) => join.onRef("writer.source_key", "=", "u.source_key").onRef("writer.generation", "=", "u.generation")).select("u.id").where("u.created_at", "<", cutoff).whereRef("u.generation", "!=", "s.current_generation").whereRef("u.created_at", ">=", "s.indexed_at").where(this.noActiveGenerationWriteExpression("u")).orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(batchLimit).execute();
559
1113
  return this.deleteAbandonedCandidateIds(rows.map((row) => row.id), cutoff, options.cleanupLease, options.canIssueStatement);
560
1114
  }
561
1115
  async deleteExpiredGenerationWriteLeases(limit, cleanupLease, canIssueStatement) {
562
1116
  const batchLimit = Math.floor(limit);
563
- if (batchLimit <= 0 || !canIssueCleanupStatement(canIssueStatement)) return 0;
564
- let query = this.db.selectFrom("_emdash_media_usage_generation_writes").select("lease_token").where(this.generationWriteLeaseHasExpired("expires_at")).orderBy("expires_at", "asc").orderBy("lease_token", "asc").limit(batchLimit);
565
- if (cleanupLease) query = query.where(this.activeCleanupLeaseExpression(cleanupLease));
566
- const rows = await query.execute();
1117
+ if (batchLimit <= 0) return 0;
1118
+ if (!await this.admitCleanupScan(cleanupLease, canIssueStatement)) return 0;
1119
+ const rows = await this.db.selectFrom("_emdash_media_usage_generation_writes").select("lease_token").where(this.generationWriteLeaseHasExpired("expires_at")).orderBy("expires_at", "asc").orderBy("lease_token", "asc").limit(batchLimit).execute();
567
1120
  if (rows.length === 0 || !canIssueCleanupStatement(canIssueStatement)) return 0;
568
1121
  let deleteQuery = this.db.deleteFrom("_emdash_media_usage_generation_writes").where("lease_token", "in", rows.map((row) => row.lease_token)).where(this.generationWriteLeaseHasExpired("expires_at"));
569
1122
  if (cleanupLease) deleteQuery = deleteQuery.where(this.activeCleanupLeaseExpression(cleanupLease));
@@ -746,6 +1299,7 @@ var MediaUsageRepository = class {
746
1299
  status: sql`CASE WHEN ${canComplete} THEN 'complete' ELSE status END`,
747
1300
  completed_at: sql`CASE WHEN ${canComplete} THEN ${now} ELSE completed_at END`,
748
1301
  last_error_code: sql`CASE WHEN ${canComplete} THEN NULL ELSE last_error_code END`,
1302
+ cursor: sql`CASE WHEN ${canComplete} THEN NULL ELSE cursor END`,
749
1303
  last_incremental_success_at: now,
750
1304
  updated_at: now
751
1305
  }).where("adapter_id", "=", "content-media").where("scope_type", "=", "collection").where("scope_key", "=", input.collectionSlug).where("collection_id", "=", input.collectionId).where("change_epoch", "=", observed.change_epoch).where("capture_state", "=", "active").where(sql`EXISTS (
@@ -756,6 +1310,65 @@ var MediaUsageRepository = class {
756
1310
  )`).executeTakeFirst();
757
1311
  return Number(result.numUpdatedRows ?? 0) > 0;
758
1312
  }
1313
+ async prepareIncrementalFinalization(input) {
1314
+ const observed = await this.db.selectFrom("_emdash_media_usage_index_status").select(["change_epoch", "reconciliation_required"]).where("adapter_id", "=", "content-media").where("scope_type", "=", "collection").where("scope_key", "=", input.collectionSlug).where("collection_id", "=", input.collectionId).where("capture_state", "=", "active").executeTakeFirst();
1315
+ if (!observed) return { outcome: "lost" };
1316
+ if (observed.reconciliation_required !== 0) return { outcome: "not_required" };
1317
+ const marker = `incremental-finalize:${String(observed.change_epoch)}:${ulid()}`;
1318
+ const result = await this.db.updateTable("_emdash_media_usage_index_status").set({
1319
+ cursor: marker,
1320
+ updated_at: this.sortableUtcTimestamp()
1321
+ }).where("adapter_id", "=", "content-media").where("scope_type", "=", "collection").where("scope_key", "=", input.collectionSlug).where("collection_id", "=", input.collectionId).where("capture_state", "=", "active").where("reconciliation_required", "=", 0).where("change_epoch", "=", observed.change_epoch).where(sql`EXISTS (
1322
+ SELECT 1
1323
+ FROM _emdash_collections AS collection
1324
+ WHERE collection.id = ${input.collectionId}
1325
+ AND collection.slug = ${input.collectionSlug}
1326
+ )`).where(sql`NOT EXISTS (
1327
+ SELECT 1
1328
+ FROM _emdash_media_usage_collection_deletions AS deletion
1329
+ WHERE deletion.collection_id = ${input.collectionId}
1330
+ )`).executeTakeFirst();
1331
+ return Number(result.numUpdatedRows ?? 0) > 0 ? {
1332
+ outcome: "marked",
1333
+ marker
1334
+ } : { outcome: "lost" };
1335
+ }
1336
+ async recoverIncrementalFinalizations() {
1337
+ const markerMatchesEpoch = isPostgres(this.db) ? sql`status.cursor LIKE ('incremental-finalize:' || status.change_epoch::text || ':%')` : sql`status.cursor LIKE ('incremental-finalize:' || CAST(status.change_epoch AS text) || ':%')`;
1338
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1339
+ return (await sql`
1340
+ UPDATE _emdash_media_usage_index_status AS status
1341
+ SET status = 'complete',
1342
+ completed_at = ${now},
1343
+ cursor = NULL,
1344
+ last_error_code = NULL,
1345
+ last_incremental_success_at = ${now},
1346
+ updated_at = ${now}
1347
+ WHERE status.adapter_id = 'content-media'
1348
+ AND status.scope_type = 'collection'
1349
+ AND status.capture_state = 'active'
1350
+ AND status.reconciliation_required = 0
1351
+ AND status.cursor IS NOT NULL
1352
+ AND ${markerMatchesEpoch}
1353
+ AND EXISTS (
1354
+ SELECT 1
1355
+ FROM _emdash_collections AS collection
1356
+ WHERE collection.id = status.collection_id
1357
+ AND collection.slug = status.scope_key
1358
+ )
1359
+ AND NOT EXISTS (
1360
+ SELECT 1
1361
+ FROM _emdash_media_usage_collection_deletions AS deletion
1362
+ WHERE deletion.collection_id = status.collection_id
1363
+ )
1364
+ AND NOT EXISTS (
1365
+ SELECT 1
1366
+ FROM _emdash_media_usage_work AS work
1367
+ WHERE work.collection_id = status.collection_id
1368
+ )
1369
+ RETURNING collection_id
1370
+ `.execute(this.db)).rows.length;
1371
+ }
759
1372
  async recordIncrementalFailure(input) {
760
1373
  const now = this.sortableUtcTimestamp();
761
1374
  const automaticRunOwnsCoverage = sql`EXISTS (
@@ -799,6 +1412,67 @@ var MediaUsageRepository = class {
799
1412
  )`).executeTakeFirst();
800
1413
  return Number(result.numUpdatedRows ?? 0) > 0;
801
1414
  }
1415
+ async recordIncrementalFailuresByCollection(input) {
1416
+ const collectionIds = [...new Set(input.collectionIds)];
1417
+ if (collectionIds.length === 0) return /* @__PURE__ */ new Set();
1418
+ const collections = jsonTextValues(this.db, collectionIds);
1419
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1420
+ const result = await sql`
1421
+ WITH failed_collections AS (${collections})
1422
+ UPDATE _emdash_media_usage_index_status AS status
1423
+ SET status = CASE
1424
+ WHEN EXISTS (
1425
+ SELECT 1
1426
+ FROM _emdash_media_usage_reconciliations AS reconciliation
1427
+ WHERE reconciliation.collection_id = status.collection_id
1428
+ AND reconciliation.run_token = status.cursor
1429
+ ) THEN status.status
1430
+ WHEN status.reconciliation_required = 0 THEN 'partial'
1431
+ WHEN status.status = 'running' THEN 'stale'
1432
+ ELSE status.status
1433
+ END,
1434
+ completed_at = CASE
1435
+ WHEN EXISTS (
1436
+ SELECT 1
1437
+ FROM _emdash_media_usage_reconciliations AS reconciliation
1438
+ WHERE reconciliation.collection_id = status.collection_id
1439
+ AND reconciliation.run_token = status.cursor
1440
+ ) THEN status.completed_at
1441
+ WHEN status.reconciliation_required = 0 OR status.status = 'running' THEN NULL
1442
+ ELSE status.completed_at
1443
+ END,
1444
+ cursor = CASE
1445
+ WHEN EXISTS (
1446
+ SELECT 1
1447
+ FROM _emdash_media_usage_reconciliations AS reconciliation
1448
+ WHERE reconciliation.collection_id = status.collection_id
1449
+ AND reconciliation.run_token = status.cursor
1450
+ ) THEN status.cursor
1451
+ WHEN status.status = 'running' THEN NULL
1452
+ ELSE status.cursor
1453
+ END,
1454
+ last_error_code = ${input.errorCode},
1455
+ updated_at = ${now}
1456
+ WHERE status.adapter_id = 'content-media'
1457
+ AND status.scope_type = 'collection'
1458
+ AND status.collection_id IN (SELECT value FROM failed_collections)
1459
+ AND EXISTS (
1460
+ SELECT 1
1461
+ FROM _emdash_media_usage_work AS work
1462
+ WHERE work.collection_id = status.collection_id
1463
+ AND work.state = 'failed'
1464
+ AND work.last_error_code = ${input.errorCode}
1465
+ )
1466
+ AND EXISTS (
1467
+ SELECT 1
1468
+ FROM _emdash_collections AS collection
1469
+ WHERE collection.id = status.collection_id
1470
+ AND collection.slug = status.scope_key
1471
+ )
1472
+ RETURNING collection_id
1473
+ `.execute(this.db);
1474
+ return new Set(result.rows.map((row) => row.collection_id));
1475
+ }
802
1476
  async findIndexStatus(identity) {
803
1477
  const row = await this.db.selectFrom("_emdash_media_usage_index_status").selectAll().where("adapter_id", "=", identity.adapterId).where("scope_type", "=", identity.scopeType).where("scope_key", "=", identity.scopeKey).executeTakeFirst();
804
1478
  return row ? rowToIndexStatus(row) : null;
@@ -901,6 +1575,31 @@ var MediaUsageRepository = class {
901
1575
  AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
902
1576
  )`;
903
1577
  }
1578
+ /**
1579
+ * Gates a bounded cleanup scan on the lease and the tick's statement budget.
1580
+ *
1581
+ * The budget is re-read after the lease row because that read is itself a
1582
+ * statement: an isolate suspended across it can resume with the budget spent.
1583
+ */
1584
+ async admitCleanupScan(cleanupLease, canIssueStatement) {
1585
+ if (!canIssueCleanupStatement(canIssueStatement)) return false;
1586
+ if (!cleanupLease) return true;
1587
+ return await this.holdsCleanupLease(cleanupLease) && canIssueCleanupStatement(canIssueStatement);
1588
+ }
1589
+ /**
1590
+ * Reads the lease row on its own so a lost lease can short-circuit a sweep.
1591
+ *
1592
+ * As a row-level `EXISTS` inside a `LIMIT`ed scan this predicate is constant
1593
+ * across every candidate row: when it is false the limit is unreachable and
1594
+ * the scan reads the whole `created_at` range before returning nothing.
1595
+ * Mutating statements still carry {@link activeCleanupLeaseExpression},
1596
+ * which is what actually fences a displaced owner.
1597
+ */
1598
+ async holdsCleanupLease(cleanupLease) {
1599
+ let query = this.db.selectFrom("_emdash_media_usage_cleanup").select("task_key").where("task_key", "=", "projection_gc").where("lease_token", "=", cleanupLease.leaseToken).where(this.cleanupLeaseExpiryIsInFuture("_emdash_media_usage_cleanup.lease_expires_at"));
1600
+ if (isPostgres(this.db)) query = query.forUpdate();
1601
+ return await query.executeTakeFirst() !== void 0;
1602
+ }
904
1603
  activeCleanupLeaseExpression(cleanupLease) {
905
1604
  const rowLock = isPostgres(this.db) ? sql` FOR UPDATE` : sql``;
906
1605
  return sql`EXISTS (
@@ -982,6 +1681,180 @@ var MediaUsageRepository = class {
982
1681
  }));
983
1682
  for (const rowBatch of chunks(rows, OCCURRENCE_INSERT_BATCH_SIZE)) await db.insertInto("_emdash_media_usage").values(rowBatch).execute();
984
1683
  }
1684
+ async insertBatchOccurrences(rows) {
1685
+ for (const rowBatch of chunkJsonRows(rows)) await sql`
1686
+ WITH input AS (${this.occurrenceBatchInput(JSON.stringify(rowBatch))})
1687
+ INSERT INTO _emdash_media_usage (
1688
+ id, source_key, generation, field_slug, field_path, occurrence_index,
1689
+ reference_type, media_id, provider, provider_asset_id, media_kind,
1690
+ mime_type, created_at
1691
+ )
1692
+ SELECT
1693
+ id, source_key, generation, field_slug, field_path, occurrence_index,
1694
+ reference_type, media_id, provider, provider_asset_id, media_kind,
1695
+ mime_type, created_at
1696
+ FROM input
1697
+ WHERE EXISTS (
1698
+ SELECT 1
1699
+ FROM _emdash_media_usage_generation_writes AS writer
1700
+ WHERE writer.source_key = input.source_key
1701
+ AND writer.generation = input.generation
1702
+ AND writer.lease_token = input.lease_token
1703
+ AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
1704
+ )
1705
+ `.execute(this.db);
1706
+ }
1707
+ occurrenceBatchInput(payload) {
1708
+ if (isPostgres(this.db)) return sql`
1709
+ SELECT
1710
+ entry.value ->> 'id' AS id,
1711
+ entry.value ->> 'source_key' AS source_key,
1712
+ entry.value ->> 'generation' AS generation,
1713
+ entry.value ->> 'field_slug' AS field_slug,
1714
+ entry.value ->> 'field_path' AS field_path,
1715
+ CAST(entry.value ->> 'occurrence_index' AS integer) AS occurrence_index,
1716
+ entry.value ->> 'reference_type' AS reference_type,
1717
+ entry.value ->> 'media_id' AS media_id,
1718
+ entry.value ->> 'provider' AS provider,
1719
+ entry.value ->> 'provider_asset_id' AS provider_asset_id,
1720
+ entry.value ->> 'media_kind' AS media_kind,
1721
+ entry.value ->> 'mime_type' AS mime_type,
1722
+ entry.value ->> 'created_at' AS created_at,
1723
+ entry.value ->> 'lease_token' AS lease_token
1724
+ FROM jsonb_array_elements(${payload}::jsonb) AS entry(value)
1725
+ `;
1726
+ return sql`
1727
+ SELECT
1728
+ json_extract(entry.value, '$.id') AS id,
1729
+ json_extract(entry.value, '$.source_key') AS source_key,
1730
+ json_extract(entry.value, '$.generation') AS generation,
1731
+ json_extract(entry.value, '$.field_slug') AS field_slug,
1732
+ json_extract(entry.value, '$.field_path') AS field_path,
1733
+ CAST(json_extract(entry.value, '$.occurrence_index') AS integer) AS occurrence_index,
1734
+ json_extract(entry.value, '$.reference_type') AS reference_type,
1735
+ json_extract(entry.value, '$.media_id') AS media_id,
1736
+ json_extract(entry.value, '$.provider') AS provider,
1737
+ json_extract(entry.value, '$.provider_asset_id') AS provider_asset_id,
1738
+ json_extract(entry.value, '$.media_kind') AS media_kind,
1739
+ json_extract(entry.value, '$.mime_type') AS mime_type,
1740
+ json_extract(entry.value, '$.created_at') AS created_at,
1741
+ json_extract(entry.value, '$.lease_token') AS lease_token
1742
+ FROM json_each(${payload}) AS entry
1743
+ `;
1744
+ }
1745
+ sourceBatchInput(payload) {
1746
+ if (isPostgres(this.db)) return sql`
1747
+ SELECT
1748
+ entry.value ->> 'source_key' AS source_key,
1749
+ entry.value ->> 'source_type' AS source_type,
1750
+ entry.value ->> 'collection_id' AS collection_id,
1751
+ entry.value ->> 'collection_slug' AS collection_slug,
1752
+ entry.value ->> 'content_id' AS content_id,
1753
+ entry.value ->> 'source_variant' AS source_variant,
1754
+ entry.value ->> 'locale' AS locale,
1755
+ entry.value ->> 'translation_group' AS translation_group,
1756
+ entry.value ->> 'content_slug' AS content_slug,
1757
+ entry.value ->> 'content_title' AS content_title,
1758
+ entry.value ->> 'content_status' AS content_status,
1759
+ entry.value ->> 'content_scheduled_at' AS content_scheduled_at,
1760
+ entry.value ->> 'content_deleted_at' AS content_deleted_at,
1761
+ entry.value ->> 'revision_id' AS revision_id,
1762
+ entry.value ->> 'current_generation' AS current_generation,
1763
+ CAST(entry.value ->> 'schema_version' AS integer) AS schema_version,
1764
+ entry.value ->> 'source_updated_at' AS source_updated_at,
1765
+ CAST(entry.value ->> 'source_version' AS bigint) AS source_version,
1766
+ entry.value ->> 'source_fingerprint' AS source_fingerprint,
1767
+ CAST(entry.value ->> 'identity_version' AS integer) AS identity_version,
1768
+ entry.value ->> 'source_completeness' AS source_completeness,
1769
+ entry.value ->> 'last_attempted_at' AS last_attempted_at,
1770
+ entry.value ->> 'last_error_code' AS last_error_code,
1771
+ entry.value ->> 'indexed_at' AS indexed_at,
1772
+ entry.value ->> 'updated_at' AS updated_at,
1773
+ entry.value ->> 'lease_token' AS lease_token,
1774
+ entry.value ->> 'expected_generation' AS expected_generation,
1775
+ entry.value ->> 'expected_collection_id' AS expected_collection_id,
1776
+ entry.value ->> 'expected_updated_at' AS expected_updated_at,
1777
+ entry.value ->> 'expected_source_fingerprint' AS expected_source_fingerprint,
1778
+ entry.value ->> 'expected_source_updated_at' AS expected_source_updated_at,
1779
+ CAST(entry.value ->> 'expected_source_version' AS bigint) AS expected_source_version,
1780
+ CAST(entry.value ->> 'expected_identity_version' AS integer) AS expected_identity_version,
1781
+ entry.value ->> 'expected_revision_id' AS expected_revision_id,
1782
+ entry.value ->> 'expected_source_completeness' AS expected_source_completeness,
1783
+ entry.value ->> 'expected_last_attempted_at' AS expected_last_attempted_at,
1784
+ entry.value ->> 'expected_last_error_code' AS expected_last_error_code
1785
+ FROM jsonb_array_elements(${payload}::jsonb) AS entry(value)
1786
+ `;
1787
+ return sql`
1788
+ SELECT
1789
+ json_extract(entry.value, '$.source_key') AS source_key,
1790
+ json_extract(entry.value, '$.source_type') AS source_type,
1791
+ json_extract(entry.value, '$.collection_id') AS collection_id,
1792
+ json_extract(entry.value, '$.collection_slug') AS collection_slug,
1793
+ json_extract(entry.value, '$.content_id') AS content_id,
1794
+ json_extract(entry.value, '$.source_variant') AS source_variant,
1795
+ json_extract(entry.value, '$.locale') AS locale,
1796
+ json_extract(entry.value, '$.translation_group') AS translation_group,
1797
+ json_extract(entry.value, '$.content_slug') AS content_slug,
1798
+ json_extract(entry.value, '$.content_title') AS content_title,
1799
+ json_extract(entry.value, '$.content_status') AS content_status,
1800
+ json_extract(entry.value, '$.content_scheduled_at') AS content_scheduled_at,
1801
+ json_extract(entry.value, '$.content_deleted_at') AS content_deleted_at,
1802
+ json_extract(entry.value, '$.revision_id') AS revision_id,
1803
+ json_extract(entry.value, '$.current_generation') AS current_generation,
1804
+ CAST(json_extract(entry.value, '$.schema_version') AS integer) AS schema_version,
1805
+ json_extract(entry.value, '$.source_updated_at') AS source_updated_at,
1806
+ CAST(json_extract(entry.value, '$.source_version') AS integer) AS source_version,
1807
+ json_extract(entry.value, '$.source_fingerprint') AS source_fingerprint,
1808
+ CAST(json_extract(entry.value, '$.identity_version') AS integer) AS identity_version,
1809
+ json_extract(entry.value, '$.source_completeness') AS source_completeness,
1810
+ json_extract(entry.value, '$.last_attempted_at') AS last_attempted_at,
1811
+ json_extract(entry.value, '$.last_error_code') AS last_error_code,
1812
+ json_extract(entry.value, '$.indexed_at') AS indexed_at,
1813
+ json_extract(entry.value, '$.updated_at') AS updated_at,
1814
+ json_extract(entry.value, '$.lease_token') AS lease_token,
1815
+ json_extract(entry.value, '$.expected_generation') AS expected_generation,
1816
+ json_extract(entry.value, '$.expected_collection_id') AS expected_collection_id,
1817
+ json_extract(entry.value, '$.expected_updated_at') AS expected_updated_at,
1818
+ json_extract(entry.value, '$.expected_source_fingerprint') AS expected_source_fingerprint,
1819
+ json_extract(entry.value, '$.expected_source_updated_at') AS expected_source_updated_at,
1820
+ CAST(json_extract(entry.value, '$.expected_source_version') AS integer) AS expected_source_version,
1821
+ CAST(json_extract(entry.value, '$.expected_identity_version') AS integer) AS expected_identity_version,
1822
+ json_extract(entry.value, '$.expected_revision_id') AS expected_revision_id,
1823
+ json_extract(entry.value, '$.expected_source_completeness') AS expected_source_completeness,
1824
+ json_extract(entry.value, '$.expected_last_attempted_at') AS expected_last_attempted_at,
1825
+ json_extract(entry.value, '$.expected_last_error_code') AS expected_last_error_code
1826
+ FROM json_each(${payload}) AS entry
1827
+ `;
1828
+ }
1829
+ generationWriteBatchInput(payload) {
1830
+ if (isPostgres(this.db)) return sql`
1831
+ SELECT
1832
+ entry.value ->> 'source_key' AS source_key,
1833
+ entry.value ->> 'collection_id' AS collection_id,
1834
+ entry.value ->> 'collection_slug' AS collection_slug,
1835
+ entry.value ->> 'generation' AS generation,
1836
+ entry.value ->> 'lease_token' AS lease_token,
1837
+ entry.value ->> 'expires_at' AS expires_at,
1838
+ entry.value ->> 'created_at' AS created_at
1839
+ FROM jsonb_array_elements(${payload}::jsonb) AS entry(value)
1840
+ `;
1841
+ return sql`
1842
+ SELECT
1843
+ json_extract(entry.value, '$.source_key') AS source_key,
1844
+ json_extract(entry.value, '$.collection_id') AS collection_id,
1845
+ json_extract(entry.value, '$.collection_slug') AS collection_slug,
1846
+ json_extract(entry.value, '$.generation') AS generation,
1847
+ json_extract(entry.value, '$.lease_token') AS lease_token,
1848
+ json_extract(entry.value, '$.expires_at') AS expires_at,
1849
+ json_extract(entry.value, '$.created_at') AS created_at
1850
+ FROM json_each(${payload}) AS entry
1851
+ `;
1852
+ }
1853
+ batchRefsMatch(left, right) {
1854
+ const leftRef = sql.ref(left);
1855
+ const rightRef = sql.ref(right);
1856
+ return isPostgres(this.db) ? sql`${leftRef} IS NOT DISTINCT FROM ${rightRef}` : sql`${leftRef} IS ${rightRef}`;
1857
+ }
985
1858
  async lockCanonicalSourceCollection(db, source) {
986
1859
  if (source.collectionId === void 0 || source.collectionId === null) return true;
987
1860
  if (!source.collectionSlug) return false;
@@ -1507,10 +2380,6 @@ function rowToIndexStatus(row) {
1507
2380
  };
1508
2381
  }
1509
2382
 
1510
- //#endregion
1511
- //#region src/media/usage/types.ts
1512
- const CONTENT_SOURCE_SCHEMA_VERSION = 1;
1513
-
1514
2383
  //#endregion
1515
2384
  //#region src/media/usage/content-fields.ts
1516
2385
  async function buildContentMediaUsageFieldFingerprint(discovery) {
@@ -1627,13 +2496,7 @@ function extractMediaUsageOccurrences({ fields, data }) {
1627
2496
  for (const field of fields) {
1628
2497
  const value = data[field.slug];
1629
2498
  if (field.type === "image") {
1630
- addOccurrence(occurrences, seen, {
1631
- fieldSlug: field.slug,
1632
- fieldPath: field.slug,
1633
- referenceType: "image_field",
1634
- value,
1635
- fallbackKind: "image"
1636
- });
2499
+ addImageOccurrences(occurrences, seen, field.slug, field.slug, value);
1637
2500
  continue;
1638
2501
  }
1639
2502
  if (field.type === "file") {
@@ -1660,16 +2523,26 @@ function extractRepeaterOccurrences(occurrences, seen, fieldSlug, value, subFiel
1660
2523
  if (!isRecord$1(item)) continue;
1661
2524
  for (const subField of subFields) {
1662
2525
  if (subField.type !== "image") continue;
1663
- addOccurrence(occurrences, seen, {
1664
- fieldSlug,
1665
- fieldPath: `${fieldSlug}[${itemIndex}].${subField.slug}`,
1666
- referenceType: "image_field",
1667
- value: item[subField.slug],
1668
- fallbackKind: "image"
1669
- });
2526
+ addImageOccurrences(occurrences, seen, fieldSlug, `${fieldSlug}[${itemIndex}].${subField.slug}`, item[subField.slug]);
1670
2527
  }
1671
2528
  }
1672
2529
  }
2530
+ function addImageOccurrences(occurrences, seen, fieldSlug, fieldPath, value) {
2531
+ addOccurrence(occurrences, seen, {
2532
+ fieldSlug,
2533
+ fieldPath,
2534
+ referenceType: "image_field",
2535
+ value,
2536
+ fallbackKind: "image"
2537
+ });
2538
+ if (isRecord$1(value) && value.darkVariant != null) addOccurrence(occurrences, seen, {
2539
+ fieldSlug,
2540
+ fieldPath: `${fieldPath}.darkVariant`,
2541
+ referenceType: "image_field",
2542
+ value: value.darkVariant,
2543
+ fallbackKind: "image"
2544
+ });
2545
+ }
1673
2546
  function extractPortableTextOccurrences(occurrences, seen, fieldSlug, value) {
1674
2547
  if (!Array.isArray(value)) return;
1675
2548
  for (const [blockIndex, block] of value.entries()) {
@@ -1844,14 +2717,38 @@ const CONTENT_SYSTEM_COLUMNS = [
1844
2717
  "translation_group"
1845
2718
  ];
1846
2719
  async function loadContentMediaUsageSnapshots(db, collectionSlug, contentId, fieldDiscovery, options = {}) {
1847
- validateIdentifier(collectionSlug, "collection slug");
1848
- if (options.identityVersion !== void 0 && !options.collectionId) throw new Error("Canonical media usage snapshots require a collection identity");
1849
- const discovery = fieldDiscovery ?? await loadContentMediaUsageFields(db, collectionSlug);
1850
- const row = await loadContentRow(db, collectionSlug, contentId, [...discovery.extractionFields.map((field) => field.slug), ...discovery.displayFieldSlugs], options.collectionId);
1851
- if (!row) return {
2720
+ return (await loadContentMediaUsageSnapshotsBatch(db, collectionSlug, [contentId], fieldDiscovery, options)).get(contentId) ?? {
1852
2721
  success: false,
1853
2722
  error: "CONTENT_NOT_FOUND"
1854
2723
  };
2724
+ }
2725
+ async function loadContentMediaUsageSnapshotsBatch(db, collectionSlug, contentIds, fieldDiscovery, options = {}, control = {}) {
2726
+ validateIdentifier(collectionSlug, "collection slug");
2727
+ if (options.identityVersion !== void 0 && !options.collectionId) throw new Error("Canonical media usage snapshots require a collection identity");
2728
+ const discovery = fieldDiscovery ?? await loadContentMediaUsageFields(db, collectionSlug);
2729
+ const rows = await loadContentRows(db, collectionSlug, contentIds, [...discovery.extractionFields.map((field) => field.slug), ...discovery.displayFieldSlugs], options.collectionId);
2730
+ const revisions = await loadRevisionRows(db, Array.from(rows.values(), (row) => readNullableString(row.draft_revision_id)).filter((revisionId) => revisionId !== null));
2731
+ const results = /* @__PURE__ */ new Map();
2732
+ let occurrenceCount = 0;
2733
+ let projectionBytes = 0;
2734
+ for (const contentId of new Set(contentIds)) {
2735
+ if (control.shouldContinue && !control.shouldContinue()) break;
2736
+ const row = rows.get(contentId);
2737
+ const result = row ? await buildContentMediaUsageSnapshots(row, collectionSlug, discovery, options, revisions) : {
2738
+ success: false,
2739
+ error: "CONTENT_NOT_FOUND"
2740
+ };
2741
+ const snapshots = result.success ? result.snapshots : result.snapshots ?? [];
2742
+ const itemOccurrenceCount = snapshots.reduce((total, snapshot) => total + snapshot.occurrences.length, 0);
2743
+ const itemProjectionBytes = snapshots.reduce((total, snapshot) => total + snapshot.projectionByteLength, 0);
2744
+ if (results.size > 0 && (control.maxOccurrenceCount !== void 0 && occurrenceCount + itemOccurrenceCount > control.maxOccurrenceCount || control.maxProjectionBytes !== void 0 && projectionBytes + itemProjectionBytes > control.maxProjectionBytes)) break;
2745
+ results.set(contentId, result);
2746
+ occurrenceCount += itemOccurrenceCount;
2747
+ projectionBytes += itemProjectionBytes;
2748
+ }
2749
+ return results;
2750
+ }
2751
+ async function buildContentMediaUsageSnapshots(row, collectionSlug, discovery, options, revisions) {
1855
2752
  const collectionId = readString(row[CONTENT_COLLECTION_ID_RESULT]);
1856
2753
  if (!collectionId) throw new Error("Media usage snapshot query did not return a collection identity");
1857
2754
  const columnsData = projectData(row, discovery.extractionFields.map((field) => field.slug));
@@ -1894,7 +2791,7 @@ async function loadContentMediaUsageSnapshots(db, collectionSlug, contentId, fie
1894
2791
  sourceVariant: "draft_overlay",
1895
2792
  revisionId: draftRevisionId
1896
2793
  });
1897
- const revisionResult = await loadRevisionRow(db, draftRevisionId);
2794
+ const revisionResult = revisions.get(draftRevisionId);
1898
2795
  if (!revisionResult) return {
1899
2796
  success: false,
1900
2797
  error: "DRAFT_REVISION_NOT_FOUND",
@@ -1957,40 +2854,52 @@ async function loadContentMediaUsageSnapshots(db, collectionSlug, contentId, fie
1957
2854
  snapshots
1958
2855
  };
1959
2856
  }
1960
- async function loadContentRow(db, collectionSlug, contentId, fieldSlugs, expectedCollectionId) {
2857
+ async function loadContentRows(db, collectionSlug, contentIds, fieldSlugs, expectedCollectionId) {
1961
2858
  const tableName = getContentTableName(collectionSlug);
1962
2859
  const columnRefs = uniqueColumns([...CONTENT_SYSTEM_COLUMNS, ...fieldSlugs]).map((column) => sql.ref(`content.${column}`));
1963
- return (await sql`
2860
+ const rows = /* @__PURE__ */ new Map();
2861
+ const uniqueContentIds = [...new Set(contentIds)];
2862
+ if (uniqueContentIds.length === 0) return rows;
2863
+ const result = await sql`
2864
+ WITH requested AS (${jsonTextValues(db, uniqueContentIds)})
1964
2865
  SELECT
1965
2866
  ${sql.join(columnRefs, sql`, `)},
1966
2867
  collection.id AS __emdash_media_usage_collection_id
1967
2868
  FROM ${sql.ref(tableName)} AS content
2869
+ INNER JOIN requested ON requested.value = content.id
1968
2870
  INNER JOIN _emdash_collections AS collection
1969
2871
  ON collection.slug = ${collectionSlug}
1970
2872
  ${expectedCollectionId ? sql`AND collection.id = ${expectedCollectionId}` : sql``}
1971
- WHERE content.id = ${contentId}
1972
- LIMIT 1
1973
- `.execute(db)).rows[0] ?? null;
1974
- }
1975
- async function loadRevisionRow(db, revisionId) {
1976
- const row = await db.selectFrom("revisions").select([
1977
- "id",
1978
- "collection",
1979
- "entry_id",
1980
- "data"
1981
- ]).where("id", "=", revisionId).executeTakeFirst();
1982
- if (!row) return null;
1983
- const data = parseRevisionData(row.data);
1984
- if (!data) return { success: false };
1985
- return {
1986
- success: true,
1987
- revision: {
1988
- id: row.id,
1989
- collection: row.collection,
1990
- entryId: row.entry_id,
1991
- data
1992
- }
1993
- };
2873
+ `.execute(db);
2874
+ for (const row of result.rows) {
2875
+ const contentId = readString(row.id);
2876
+ if (contentId) rows.set(contentId, row);
2877
+ }
2878
+ return rows;
2879
+ }
2880
+ async function loadRevisionRows(db, revisionIds) {
2881
+ const revisions = /* @__PURE__ */ new Map();
2882
+ const uniqueRevisionIds = [...new Set(revisionIds)];
2883
+ if (uniqueRevisionIds.length === 0) return revisions;
2884
+ const rows = await sql`
2885
+ WITH requested AS (${jsonTextValues(db, uniqueRevisionIds)})
2886
+ SELECT revision.id, revision.collection, revision.entry_id, revision.data
2887
+ FROM revisions AS revision
2888
+ INNER JOIN requested ON requested.value = revision.id
2889
+ `.execute(db);
2890
+ for (const row of rows.rows) {
2891
+ const data = parseRevisionData(row.data);
2892
+ revisions.set(row.id, data ? {
2893
+ success: true,
2894
+ revision: {
2895
+ id: row.id,
2896
+ collection: row.collection,
2897
+ entryId: row.entry_id,
2898
+ data
2899
+ }
2900
+ } : { success: false });
2901
+ }
2902
+ return revisions;
1994
2903
  }
1995
2904
  function buildContentSource(input) {
1996
2905
  const { collectionId, collectionSlug, identityVersion, row, displayData, sourceVariant, revisionId } = input;
@@ -2102,8 +3011,11 @@ const CONTENT_USAGE_LOCKS_KEY = Symbol.for("emdash.mediaUsage.contentLocks");
2102
3011
  const CONTENT_USAGE_COLLECTION_LOCKS_KEY = Symbol.for("emdash.mediaUsage.collectionLocks");
2103
3012
  const CONTENT_USAGE_REFRESH_MAX_ATTEMPTS = 2;
2104
3013
  const MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS = Object.freeze({
2105
- maxOccurrenceMutationUnitsPerClaim: 12,
2106
- maxProjectionMutationBytesPerClaim: 512 * 1024
3014
+ maxOccurrenceMutationUnitsPerClaim: 500,
3015
+ maxProjectionMutationBytesPerVariant: 2e6,
3016
+ maxProjectionMutationBytesPerClaim: 4e6,
3017
+ maxOccurrenceMutationUnitsPerBatch: 5e4,
3018
+ maxProjectionMutationBytesPerBatch: 16e6
2107
3019
  });
2108
3020
  const ZERO_RESULT = {
2109
3021
  success: true,
@@ -2111,10 +3023,10 @@ const ZERO_RESULT = {
2111
3023
  deletedSourceCount: 0,
2112
3024
  failedSourceCount: 0
2113
3025
  };
2114
- function createContentMediaUsageAdmissionBudget() {
3026
+ function createContentMediaUsageAdmissionBudget(limits = {}) {
2115
3027
  return {
2116
- remainingOccurrenceMutationUnits: MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxOccurrenceMutationUnitsPerClaim,
2117
- remainingProjectionMutationBytes: MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxProjectionMutationBytesPerClaim,
3028
+ remainingOccurrenceMutationUnits: limits.maxOccurrenceMutationUnits ?? MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxOccurrenceMutationUnitsPerClaim,
3029
+ remainingProjectionMutationBytes: limits.maxProjectionMutationBytes ?? MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxProjectionMutationBytesPerClaim,
2118
3030
  hasReservedMutation: false
2119
3031
  };
2120
3032
  }
@@ -2123,20 +3035,23 @@ async function planContentMediaUsageProjectionAdmission(repo, snapshots, observe
2123
3035
  const absentSources = canonicalSourceKeys.filter((sourceKey) => !snapshotSourceKeys.has(sourceKey)).map((sourceKey) => observedSources.get(sourceKey)).filter((source) => source !== void 0);
2124
3036
  let deletionOccurrenceUnits = 0;
2125
3037
  let deletionBytes = 0;
3038
+ let largestDeletionBytes = 0;
2126
3039
  for (const source of absentSources) {
2127
3040
  const measurement = await repo.measureSourceGenerationDeletion(source.sourceKey, source.currentGeneration, MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxOccurrenceMutationUnitsPerClaim);
2128
3041
  if (measurement.exceedsOccurrenceLimit) return budget.hasReservedMutation ? { outcome: "claim_budget_deferred" } : { outcome: "intrinsic_resource_limit" };
2129
3042
  deletionOccurrenceUnits += measurement.occurrenceCount;
2130
- deletionBytes += storedMediaUsageSourceByteLength(source) + measurement.occurrenceBytes * 2;
3043
+ const sourceDeletionBytes = storedMediaUsageSourceByteLength(source) + measurement.occurrenceBytes * 2;
3044
+ deletionBytes += sourceDeletionBytes;
3045
+ largestDeletionBytes = Math.max(largestDeletionBytes, sourceDeletionBytes);
2131
3046
  }
2132
3047
  const noOpSourceKeys = /* @__PURE__ */ new Set();
2133
- let cost = projectionAdmissionCost(snapshots, noOpSourceKeys, deletionOccurrenceUnits, deletionBytes);
3048
+ let cost = projectionAdmissionCost(snapshots, noOpSourceKeys, deletionOccurrenceUnits, deletionBytes, largestDeletionBytes);
2134
3049
  if (exceedsProjectionAdmissionLimits(cost)) {
2135
3050
  for (const snapshot of snapshots) {
2136
3051
  const expectedSource = observedSources.get(snapshot.source.sourceKey);
2137
3052
  if (expectedSource && await repo.projectionMatchesExpectedSource(snapshot.source, expectedSource)) noOpSourceKeys.add(snapshot.source.sourceKey);
2138
3053
  }
2139
- cost = projectionAdmissionCost(snapshots, noOpSourceKeys, deletionOccurrenceUnits, deletionBytes);
3054
+ cost = projectionAdmissionCost(snapshots, noOpSourceKeys, deletionOccurrenceUnits, deletionBytes, largestDeletionBytes);
2140
3055
  }
2141
3056
  if (exceedsProjectionAdmissionLimits(cost)) return budget.hasReservedMutation ? { outcome: "claim_budget_deferred" } : { outcome: "intrinsic_resource_limit" };
2142
3057
  if (cost.occurrenceMutationUnits > budget.remainingOccurrenceMutationUnits || cost.projectionMutationBytes > budget.remainingProjectionMutationBytes) return { outcome: "claim_budget_deferred" };
@@ -2150,19 +3065,21 @@ async function planContentMediaUsageProjectionAdmission(repo, snapshots, observe
2150
3065
  ...cost
2151
3066
  };
2152
3067
  }
2153
- function projectionAdmissionCost(snapshots, noOpSourceKeys, deletionOccurrenceUnits, deletionBytes) {
3068
+ function projectionAdmissionCost(snapshots, noOpSourceKeys, deletionOccurrenceUnits, deletionBytes, largestDeletionBytes) {
2154
3069
  return snapshots.reduce((cost, snapshot) => {
2155
3070
  if (noOpSourceKeys.has(snapshot.source.sourceKey)) return cost;
2156
3071
  cost.occurrenceMutationUnits += snapshot.occurrences.length;
2157
3072
  cost.projectionMutationBytes += snapshot.projectionByteLength;
3073
+ cost.largestProjectionMutationBytes = Math.max(cost.largestProjectionMutationBytes, snapshot.projectionByteLength);
2158
3074
  return cost;
2159
3075
  }, {
2160
3076
  occurrenceMutationUnits: deletionOccurrenceUnits,
2161
- projectionMutationBytes: deletionBytes
3077
+ projectionMutationBytes: deletionBytes,
3078
+ largestProjectionMutationBytes: largestDeletionBytes
2162
3079
  });
2163
3080
  }
2164
3081
  function exceedsProjectionAdmissionLimits(cost) {
2165
- return cost.occurrenceMutationUnits > MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxOccurrenceMutationUnitsPerClaim || cost.projectionMutationBytes > MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxProjectionMutationBytesPerClaim;
3082
+ return cost.occurrenceMutationUnits > MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxOccurrenceMutationUnitsPerClaim || cost.largestProjectionMutationBytes > MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxProjectionMutationBytesPerVariant || cost.projectionMutationBytes > MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxProjectionMutationBytesPerClaim;
2166
3083
  }
2167
3084
  function storedMediaUsageSourceByteLength(source) {
2168
3085
  return new TextEncoder().encode(JSON.stringify(source)).byteLength;
@@ -2171,13 +3088,133 @@ async function refreshContentMediaUsage(db, collectionSlug, contentId) {
2171
3088
  validateIdentifier(collectionSlug, "collection slug");
2172
3089
  return withContentUsageCollectionLock(collectionSlug, () => withContentUsageLock(collectionSlug, contentId, () => refreshContentMediaUsageUnlocked(db, collectionSlug, contentId, {})));
2173
3090
  }
2174
- async function refreshContentMediaUsageForWork(db, collectionId, collectionSlug, contentId) {
2175
- validateIdentifier(collectionSlug, "collection slug");
2176
- if (!collectionId) throw new Error("Durable media usage work requires a collection identity");
2177
- return withContentUsageCollectionLock(collectionSlug, () => withContentUsageLock(collectionSlug, contentId, () => refreshContentMediaUsageUnlocked(db, collectionSlug, contentId, {
2178
- collectionId,
2179
- durableWork: true
2180
- })));
3091
+ async function refreshContentMediaUsageForWorkBatch(db, items, options = {}) {
3092
+ const results = /* @__PURE__ */ new Map();
3093
+ const batchBudget = createContentMediaUsageAdmissionBudget({
3094
+ maxOccurrenceMutationUnits: MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxOccurrenceMutationUnitsPerBatch,
3095
+ maxProjectionMutationBytes: MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxProjectionMutationBytesPerBatch
3096
+ });
3097
+ const collections = /* @__PURE__ */ new Map();
3098
+ for (const item of items) {
3099
+ const key = `${item.collectionId}\u0000${item.collectionSlug}`;
3100
+ const collectionItems = collections.get(key) ?? [];
3101
+ collectionItems.push(item);
3102
+ collections.set(key, collectionItems);
3103
+ }
3104
+ for (const collectionItems of collections.values()) {
3105
+ if (options.shouldContinue && !options.shouldContinue()) break;
3106
+ const first = collectionItems[0];
3107
+ if (!first) continue;
3108
+ validateIdentifier(first.collectionSlug, "collection slug");
3109
+ if (!first.collectionId) throw new Error("Durable media usage work requires a collection identity");
3110
+ await withContentUsageCollectionLock(first.collectionSlug, async () => {
3111
+ const fieldDiscovery = await loadContentMediaUsageFields(db, first.collectionSlug, first.collectionId);
3112
+ const sourceKeys = collectionItems.flatMap((item) => contentSourceKeys(item.collectionSlug, item.contentId, item.collectionId));
3113
+ const repo = new MediaUsageRepository(db);
3114
+ const observedSources = await repo.findSources(sourceKeys);
3115
+ const snapshots = await loadContentMediaUsageSnapshotsBatch(db, first.collectionSlug, collectionItems.map((item) => item.contentId), fieldDiscovery, {
3116
+ collectionId: first.collectionId,
3117
+ identityVersion: 1
3118
+ }, {
3119
+ shouldContinue: options.shouldContinue,
3120
+ maxOccurrenceCount: MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxOccurrenceMutationUnitsPerBatch,
3121
+ maxProjectionBytes: MEDIA_USAGE_PROJECTION_ADMISSION_LIMITS.maxProjectionMutationBytesPerBatch
3122
+ });
3123
+ const newSourceProjections = [];
3124
+ const newSourceKeys = /* @__PURE__ */ new Map();
3125
+ const existingSourceProjections = [];
3126
+ const unchangedSourceProjections = [];
3127
+ const existingSourceKeys = /* @__PURE__ */ new Map();
3128
+ for (const item of collectionItems) {
3129
+ if (options.shouldContinue && !options.shouldContinue()) break;
3130
+ const snapshotsResult = snapshots.get(item.contentId);
3131
+ const itemSourceKeys = contentSourceKeys(item.collectionSlug, item.contentId, item.collectionId);
3132
+ if (!snapshotsResult?.success) continue;
3133
+ const snapshotsByKey = new Map(snapshotsResult.snapshots.map((snapshot) => [snapshot.source.sourceKey, snapshot]));
3134
+ const existing = itemSourceKeys.map((sourceKey) => observedSources.get(sourceKey)).filter((source) => source !== void 0);
3135
+ const allNew = existing.length === 0;
3136
+ const allExisting = existing.length === snapshotsResult.snapshots.length && existing.every((source) => snapshotsByKey.has(source.sourceKey));
3137
+ if (!allNew && !allExisting) continue;
3138
+ const admission = await planContentMediaUsageProjectionAdmission(repo, snapshotsResult.snapshots, observedSources, itemSourceKeys, batchBudget);
3139
+ if (admission.outcome === "claim_budget_deferred") break;
3140
+ if (admission.outcome !== "admitted") {
3141
+ results.set(contentRefreshKey(item.collectionId, item.contentId), admissionFailureResult(admission.outcome));
3142
+ continue;
3143
+ }
3144
+ const key = contentRefreshKey(item.collectionId, item.contentId);
3145
+ if (allNew) {
3146
+ newSourceProjections.push(...snapshotsResult.snapshots.map((snapshot) => ({
3147
+ source: snapshot.source,
3148
+ occurrences: snapshot.occurrences
3149
+ })));
3150
+ newSourceKeys.set(key, snapshotsResult.snapshots.map((snapshot) => snapshot.source.sourceKey));
3151
+ continue;
3152
+ }
3153
+ const changed = [];
3154
+ const unchanged = [];
3155
+ for (const snapshot of snapshotsResult.snapshots) {
3156
+ const expectedSource = observedSources.get(snapshot.source.sourceKey);
3157
+ if (!expectedSource) continue;
3158
+ if (expectedSource.sourceFingerprint === snapshot.source.sourceFingerprint && expectedSource.sourceCompleteness === (snapshot.source.sourceCompleteness ?? "complete") && expectedSource.lastErrorCode === null) {
3159
+ unchanged.push(snapshot.source.sourceKey);
3160
+ unchangedSourceProjections.push({
3161
+ source: snapshot.source,
3162
+ occurrences: snapshot.occurrences,
3163
+ expectedSource
3164
+ });
3165
+ continue;
3166
+ }
3167
+ changed.push(snapshot.source.sourceKey);
3168
+ existingSourceProjections.push({
3169
+ source: snapshot.source,
3170
+ occurrences: snapshot.occurrences,
3171
+ expectedSource
3172
+ });
3173
+ }
3174
+ existingSourceKeys.set(key, {
3175
+ allCount: snapshotsResult.snapshots.length,
3176
+ changed,
3177
+ unchanged
3178
+ });
3179
+ }
3180
+ const insertedSourceKeys = await repo.replaceNewSourcesBatch(newSourceProjections);
3181
+ const replacedSourceKeys = await repo.replaceExistingSourcesBatch(existingSourceProjections);
3182
+ const matchedSourceKeys = await repo.matchingExistingSourcesBatch(unchangedSourceProjections);
3183
+ for (const [key, expectedSourceKeys] of newSourceKeys) results.set(key, expectedSourceKeys.every((sourceKey) => insertedSourceKeys.has(sourceKey)) ? {
3184
+ success: true,
3185
+ refreshedSourceCount: expectedSourceKeys.length,
3186
+ deletedSourceCount: 0,
3187
+ failedSourceCount: 0
3188
+ } : generationConflictResult({
3189
+ refreshedSourceCount: 0,
3190
+ deletedSourceCount: 0
3191
+ }));
3192
+ for (const [key, expected] of existingSourceKeys) results.set(key, expected.changed.every((sourceKey) => replacedSourceKeys.has(sourceKey)) && expected.unchanged.every((sourceKey) => matchedSourceKeys.has(sourceKey)) ? {
3193
+ success: true,
3194
+ refreshedSourceCount: expected.allCount,
3195
+ deletedSourceCount: 0,
3196
+ failedSourceCount: 0
3197
+ } : generationConflictResult({
3198
+ refreshedSourceCount: 0,
3199
+ deletedSourceCount: 0
3200
+ }));
3201
+ for (const item of collectionItems) {
3202
+ if (options.shouldContinue && !options.shouldContinue()) break;
3203
+ const key = contentRefreshKey(item.collectionId, item.contentId);
3204
+ if (!snapshots.has(item.contentId)) continue;
3205
+ if (results.has(key)) continue;
3206
+ const result = await withContentUsageLock(item.collectionSlug, item.contentId, () => refreshContentMediaUsageUnlocked(db, item.collectionSlug, item.contentId, {
3207
+ collectionId: item.collectionId,
3208
+ durableWork: true,
3209
+ fieldDiscovery,
3210
+ observedSources,
3211
+ snapshotsResult: snapshots.get(item.contentId)
3212
+ }));
3213
+ results.set(key, result);
3214
+ }
3215
+ });
3216
+ }
3217
+ return results;
2181
3218
  }
2182
3219
  async function refreshContentMediaUsageUnlocked(db, collectionSlug, contentId, options) {
2183
3220
  try {
@@ -2212,17 +3249,13 @@ async function refreshContentMediaUsageUnlocked(db, collectionSlug, contentId, o
2212
3249
  async function refreshContentMediaUsageAttempt(db, collectionSlug, contentId, options) {
2213
3250
  const repo = new MediaUsageRepository(db);
2214
3251
  const canonicalSourceKeys = contentSourceKeys(collectionSlug, contentId, options.collectionId);
2215
- const observedSources = await repo.findSources(canonicalSourceKeys);
2216
- const snapshotsResult = await loadContentMediaUsageSnapshots(db, collectionSlug, contentId, void 0, options.collectionId ? {
3252
+ const observedSources = options.observedSources ?? await repo.findSources(canonicalSourceKeys);
3253
+ const snapshotsResult = options.snapshotsResult ?? await loadContentMediaUsageSnapshots(db, collectionSlug, contentId, options.fieldDiscovery, options.collectionId ? {
2217
3254
  collectionId: options.collectionId,
2218
3255
  identityVersion: 1
2219
3256
  } : void 0);
2220
3257
  if (!snapshotsResult.success) {
2221
3258
  if (snapshotsResult.error === "CONTENT_NOT_FOUND" && options.collectionId) {
2222
- if (!await contentCollectionExists(db, collectionSlug, options.collectionId)) return generationConflictResult({
2223
- refreshedSourceCount: 0,
2224
- deletedSourceCount: 0
2225
- });
2226
3259
  if (!options.admissionBudget) throw new Error("Durable media usage work requires an admission budget");
2227
3260
  const admission = await planContentMediaUsageProjectionAdmission(repo, [], observedSources, canonicalSourceKeys, options.admissionBudget);
2228
3261
  if (admission.outcome !== "admitted") return admissionFailureResult(admission.outcome);
@@ -2237,11 +3270,7 @@ async function refreshContentMediaUsageAttempt(db, collectionSlug, contentId, op
2237
3270
  }
2238
3271
  return options.durableWork ? snapshotFailureResult(snapshotsResult) : markSnapshotFailure(db, collectionSlug, snapshotsResult);
2239
3272
  }
2240
- if (!await contentCollectionExists(db, collectionSlug, options.collectionId)) {
2241
- if (options.collectionId) return generationConflictResult({
2242
- refreshedSourceCount: 0,
2243
- deletedSourceCount: 0
2244
- });
3273
+ if (!options.collectionId && !await contentCollectionExists(db, collectionSlug)) {
2245
3274
  const deletedSourceCount = await repo.deleteContentSources(collectionSlug, contentId);
2246
3275
  return {
2247
3276
  ...ZERO_RESULT,
@@ -2267,11 +3296,7 @@ async function refreshContentMediaUsageAttempt(db, collectionSlug, contentId, op
2267
3296
  });
2268
3297
  refreshedSourceCount++;
2269
3298
  }
2270
- if (!await contentCollectionExists(db, collectionSlug, options.collectionId)) {
2271
- if (options.collectionId) return generationConflictResult({
2272
- refreshedSourceCount,
2273
- deletedSourceCount: 0
2274
- });
3299
+ if (!options.collectionId && !await contentCollectionExists(db, collectionSlug)) {
2275
3300
  const deletedSourceCount = await repo.deleteContentSources(collectionSlug, contentId);
2276
3301
  return {
2277
3302
  ...ZERO_RESULT,
@@ -2299,6 +3324,9 @@ async function refreshContentMediaUsageAttempt(db, collectionSlug, contentId, op
2299
3324
  failedSourceCount: 0
2300
3325
  };
2301
3326
  }
3327
+ function contentRefreshKey(collectionId, contentId) {
3328
+ return `${collectionId}\u0000${contentId}`;
3329
+ }
2302
3330
  function contentSourceKeys(collectionSlug, contentId, collectionId) {
2303
3331
  return MEDIA_USAGE_CONTENT_SOURCE_VARIANTS.map((sourceVariant) => buildContentMediaUsageSourceKey({
2304
3332
  collectionId,
@@ -2556,5 +3584,5 @@ function getContentUsageCollectionLocks() {
2556
3584
  }
2557
3585
 
2558
3586
  //#endregion
2559
- export { loadContentMediaUsageFields as _, findNonTranslatableSiblingContentIds as a, markContentMediaUsageCollectionStaleSafely as c, withContentUsageCollectionLock as d, loadContentMediaUsageSnapshots as f, buildContentMediaUsageFieldFingerprint as g, MediaUsageFieldDiscoveryError as h, deleteContentMediaUsageCollection as i, refreshContentMediaUsageAfterWrite as l, buildContentMediaUsageSourceKey as m, CONTENT_MEDIA_USAGE_COLLECTION_SCOPE as n, invalidateContentMediaUsageSchemaChange as o, MEDIA_USAGE_CONTENT_SOURCE_VARIANTS as p, deleteContentMediaUsage as r, markContentMediaUsageCollectionStale as s, CONTENT_MEDIA_USAGE_ADAPTER_ID as t, refreshContentMediaUsageForWork as u, CONTENT_SOURCE_SCHEMA_VERSION as v, MediaUsageRepository as y };
2560
- //# sourceMappingURL=content-refresh-B_8SJzuU.mjs.map
3587
+ export { buildContentMediaUsageFieldFingerprint as _, deleteContentMediaUsageCollection as a, jsonTextValues as b, markContentMediaUsageCollectionStale as c, refreshContentMediaUsageForWorkBatch as d, withContentUsageCollectionLock as f, MediaUsageFieldDiscoveryError as g, buildContentMediaUsageSourceKey as h, deleteContentMediaUsage as i, markContentMediaUsageCollectionStaleSafely as l, MEDIA_USAGE_CONTENT_SOURCE_VARIANTS as m, CONTENT_MEDIA_USAGE_COLLECTION_SCOPE as n, findNonTranslatableSiblingContentIds as o, loadContentMediaUsageSnapshots as p, contentRefreshKey as r, invalidateContentMediaUsageSchemaChange as s, CONTENT_MEDIA_USAGE_ADAPTER_ID as t, refreshContentMediaUsageAfterWrite as u, loadContentMediaUsageFields as v, CONTENT_SOURCE_SCHEMA_VERSION as x, MediaUsageRepository as y };
3588
+ //# sourceMappingURL=content-refresh-Bwp-8yGy.mjs.map