emdash 0.26.0 → 0.28.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 (564) hide show
  1. package/dist/{adapters-DAAjOID4.d.mts → adapters-hUy8vOB-.d.mts} +1 -1
  2. package/dist/{adapters-DAAjOID4.d.mts.map → adapters-hUy8vOB-.d.mts.map} +1 -1
  3. package/dist/{allowed-origins-D5FxMUo8.mjs → allowed-origins-CRsJ6BPM.mjs} +2 -2
  4. package/dist/{allowed-origins-D5FxMUo8.mjs.map → allowed-origins-CRsJ6BPM.mjs.map} +1 -1
  5. package/dist/api/route-utils.d.mts +3 -3
  6. package/dist/api/route-utils.mjs +17 -16
  7. package/dist/api/route-utils.mjs.map +1 -1
  8. package/dist/api/schemas/index.d.mts +1 -1
  9. package/dist/api/schemas/index.mjs +3 -3
  10. package/dist/{api-BMfveMoB.mjs → api-BFURZn1i.mjs} +104 -38
  11. package/dist/api-BFURZn1i.mjs.map +1 -0
  12. package/dist/{api-tokens-D6ppjIVi.mjs → api-tokens-DvqeZmVq.mjs} +2 -2
  13. package/dist/{api-tokens-D6ppjIVi.mjs.map → api-tokens-DvqeZmVq.mjs.map} +1 -1
  14. package/dist/{apply--IVNSdXn.mjs → apply-Bu-8nYHL.mjs} +113 -76
  15. package/dist/apply-Bu-8nYHL.mjs.map +1 -0
  16. package/dist/astro/index.d.mts +12 -11
  17. package/dist/astro/index.d.mts.map +1 -1
  18. package/dist/astro/index.mjs +13 -5
  19. package/dist/astro/index.mjs.map +1 -1
  20. package/dist/astro/middleware/auth.d.mts +10 -9
  21. package/dist/astro/middleware/auth.d.mts.map +1 -1
  22. package/dist/astro/middleware/auth.mjs +52 -12
  23. package/dist/astro/middleware/auth.mjs.map +1 -1
  24. package/dist/astro/middleware/redirect.mjs +5 -5
  25. package/dist/astro/middleware/request-context.mjs +2 -2
  26. package/dist/astro/middleware/setup.mjs +1 -1
  27. package/dist/astro/middleware.d.mts +2 -9
  28. package/dist/astro/middleware.d.mts.map +1 -1
  29. package/dist/astro/middleware.mjs +125 -71
  30. package/dist/astro/middleware.mjs.map +1 -1
  31. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
  32. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
  33. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
  34. package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
  35. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  36. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +8 -8
  37. package/dist/astro/routes/api/admin/byline-fields/index.mjs +8 -8
  38. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +8 -8
  39. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +14 -14
  40. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +14 -14
  41. package/dist/astro/routes/api/admin/bylines/index.mjs +14 -14
  42. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +12 -12
  43. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
  44. package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
  45. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
  46. package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
  47. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  48. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  49. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
  50. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
  51. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +38 -37
  52. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  53. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +38 -37
  54. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  55. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +37 -36
  56. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  57. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +36 -35
  58. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  59. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +36 -35
  60. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  61. package/dist/astro/routes/api/admin/plugins/index.mjs +37 -36
  62. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  63. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  64. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +36 -35
  65. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  66. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +36 -35
  67. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  68. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +36 -35
  69. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  70. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +36 -35
  71. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  72. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +37 -36
  73. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  74. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +36 -35
  75. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +37 -36
  77. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/updates.mjs +36 -35
  79. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +36 -35
  81. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  83. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +36 -35
  84. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  85. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  86. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  87. package/dist/astro/routes/api/admin/users/_id_/index.mjs +6 -6
  88. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
  89. package/dist/astro/routes/api/admin/users/index.mjs +6 -6
  90. package/dist/astro/routes/api/auth/dev-bypass.mjs +5 -5
  91. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  92. package/dist/astro/routes/api/auth/invite/complete.mjs +10 -10
  93. package/dist/astro/routes/api/auth/invite/index.mjs +7 -7
  94. package/dist/astro/routes/api/auth/invite/register-options.mjs +9 -9
  95. package/dist/astro/routes/api/auth/logout.mjs +3 -3
  96. package/dist/astro/routes/api/auth/magic-link/send.mjs +9 -9
  97. package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
  98. package/dist/astro/routes/api/auth/me.mjs +7 -7
  99. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  100. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
  101. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  102. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
  103. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  104. package/dist/astro/routes/api/auth/passkey/options.mjs +11 -11
  105. package/dist/astro/routes/api/auth/passkey/register/options.mjs +9 -9
  106. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +10 -10
  107. package/dist/astro/routes/api/auth/passkey/verify.mjs +10 -10
  108. package/dist/astro/routes/api/auth/signup/complete.mjs +10 -10
  109. package/dist/astro/routes/api/auth/signup/request.mjs +9 -9
  110. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  111. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +13 -13
  112. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +12 -12
  113. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  114. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  115. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  116. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  117. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +10 -10
  118. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +7 -7
  119. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  120. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  121. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +7 -7
  122. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +13 -13
  123. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  124. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  125. package/dist/astro/routes/api/content/_collection_/_id_.mjs +7 -7
  126. package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
  127. package/dist/astro/routes/api/content/_collection_/index.mjs +7 -7
  128. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
  129. package/dist/astro/routes/api/dashboard.mjs +8 -8
  130. package/dist/astro/routes/api/dev/emails.mjs +3 -3
  131. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  132. package/dist/astro/routes/api/import/probe.mjs +19 -11
  133. package/dist/astro/routes/api/import/probe.mjs.map +1 -1
  134. package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
  135. package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -40
  136. package/dist/astro/routes/api/import/wordpress/execute.d.mts.map +1 -1
  137. package/dist/astro/routes/api/import/wordpress/execute.mjs +12 -407
  138. package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
  139. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -9
  140. package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
  141. package/dist/astro/routes/api/import/wordpress/media.mjs +9 -9
  142. package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
  143. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +19 -1
  144. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts.map +1 -1
  145. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +97 -79
  146. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
  147. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  148. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +19 -11
  149. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -1
  150. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +40 -2
  151. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts.map +1 -1
  152. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +519 -39
  153. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
  154. package/dist/astro/routes/api/manifest.d.mts.map +1 -1
  155. package/dist/astro/routes/api/manifest.mjs +14 -5
  156. package/dist/astro/routes/api/manifest.mjs.map +1 -1
  157. package/dist/astro/routes/api/mcp.mjs +39 -39
  158. package/dist/astro/routes/api/media/_id_/confirm.mjs +7 -7
  159. package/dist/astro/routes/api/media/_id_.mjs +7 -7
  160. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  161. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  162. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  163. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  164. package/dist/astro/routes/api/media/upload-url.mjs +8 -8
  165. package/dist/astro/routes/api/media.mjs +9 -9
  166. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +9 -9
  167. package/dist/astro/routes/api/menus/_name_/items.mjs +9 -9
  168. package/dist/astro/routes/api/menus/_name_/reorder.mjs +9 -9
  169. package/dist/astro/routes/api/menus/_name_/translations.mjs +9 -9
  170. package/dist/astro/routes/api/menus/_name_.mjs +9 -9
  171. package/dist/astro/routes/api/menus/index.mjs +9 -9
  172. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  173. package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
  174. package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
  175. package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
  176. package/dist/astro/routes/api/oauth/register.mjs +3 -3
  177. package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
  178. package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
  179. package/dist/astro/routes/api/oauth/token.mjs +6 -6
  180. package/dist/astro/routes/api/openapi.json.mjs +4 -4
  181. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
  182. package/dist/astro/routes/api/redirects/404s/index.mjs +9 -9
  183. package/dist/astro/routes/api/redirects/404s/summary.mjs +9 -9
  184. package/dist/astro/routes/api/redirects/_id_.mjs +10 -10
  185. package/dist/astro/routes/api/redirects/index.mjs +10 -10
  186. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  187. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  188. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +36 -35
  189. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  190. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +36 -35
  191. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  192. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +36 -35
  193. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  194. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +36 -35
  195. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  196. package/dist/astro/routes/api/schema/collections/index.mjs +36 -35
  197. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  198. package/dist/astro/routes/api/schema/index.mjs +7 -6
  199. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  200. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +36 -35
  201. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  202. package/dist/astro/routes/api/schema/orphans/index.mjs +36 -35
  203. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  204. package/dist/astro/routes/api/search/enable.mjs +10 -10
  205. package/dist/astro/routes/api/search/index.mjs +9 -9
  206. package/dist/astro/routes/api/search/rebuild.mjs +10 -10
  207. package/dist/astro/routes/api/search/stats.mjs +6 -6
  208. package/dist/astro/routes/api/search/suggest.mjs +9 -9
  209. package/dist/astro/routes/api/sections/_slug_.mjs +9 -9
  210. package/dist/astro/routes/api/sections/index.mjs +9 -9
  211. package/dist/astro/routes/api/settings/email.mjs +4 -4
  212. package/dist/astro/routes/api/settings.mjs +12 -12
  213. package/dist/astro/routes/api/setup/admin-verify.mjs +11 -11
  214. package/dist/astro/routes/api/setup/admin.mjs +10 -10
  215. package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
  216. package/dist/astro/routes/api/setup/dev-bypass.mjs +28 -25
  217. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  218. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  219. package/dist/astro/routes/api/setup/index.mjs +26 -25
  220. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  221. package/dist/astro/routes/api/setup/status.mjs +4 -4
  222. package/dist/astro/routes/api/snapshot.mjs +6 -6
  223. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +13 -13
  224. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +13 -13
  225. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +13 -13
  226. package/dist/astro/routes/api/taxonomies/index.mjs +13 -13
  227. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  228. package/dist/astro/routes/api/typegen.mjs +5 -5
  229. package/dist/astro/routes/api/well-known/auth.mjs +1 -1
  230. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  231. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  232. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +7 -7
  233. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +9 -9
  234. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +9 -9
  235. package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
  236. package/dist/astro/routes/api/widget-areas/index.mjs +9 -9
  237. package/dist/astro/routes/api/widget-components.mjs +3 -3
  238. package/dist/astro/routes/robots.txt.mjs +6 -6
  239. package/dist/astro/routes/sitemap-_collection_.xml.mjs +9 -9
  240. package/dist/astro/routes/sitemap.xml.mjs +7 -7
  241. package/dist/astro/types.d.mts +14 -12
  242. package/dist/astro/types.d.mts.map +1 -1
  243. package/dist/auth/providers/github.d.mts +1 -1
  244. package/dist/auth/providers/google.d.mts +1 -1
  245. package/dist/{authorize-BZnOGJFo.mjs → authorize-Dz88F_Fw.mjs} +2 -2
  246. package/dist/{authorize-BZnOGJFo.mjs.map → authorize-Dz88F_Fw.mjs.map} +1 -1
  247. package/dist/{byline-LL8lyqdb.mjs → byline-Bm8ZgUP9.mjs} +6 -6
  248. package/dist/{byline-LL8lyqdb.mjs.map → byline-Bm8ZgUP9.mjs.map} +1 -1
  249. package/dist/{byline-fields-eyJMblSr.mjs → byline-fields-2Lp5Hbgf.mjs} +18 -3
  250. package/dist/byline-fields-2Lp5Hbgf.mjs.map +1 -0
  251. package/dist/{byline-fields-DaMKzkhO.mjs → byline-fields-CIx_GuN4.mjs} +1 -1
  252. package/dist/{byline-fields-DaMKzkhO.mjs.map → byline-fields-CIx_GuN4.mjs.map} +1 -1
  253. package/dist/{byline-fields-KD3JFRuH.d.mts → byline-fields-Cgrt9-aL.d.mts} +18 -3
  254. package/dist/{byline-fields-KD3JFRuH.d.mts.map → byline-fields-Cgrt9-aL.d.mts.map} +1 -1
  255. package/dist/{bylines-BmJ7Vg7J.mjs → bylines-6tm9mgQb.mjs} +4 -4
  256. package/dist/{bylines-BmJ7Vg7J.mjs.map → bylines-6tm9mgQb.mjs.map} +1 -1
  257. package/dist/{bylines-DE47vP8d.mjs → bylines-Woq87129.mjs} +6 -6
  258. package/dist/{bylines-DE47vP8d.mjs.map → bylines-Woq87129.mjs.map} +1 -1
  259. package/dist/{cache-D9jF5bYw.mjs → cache-CEBAZjir.mjs} +2 -2
  260. package/dist/{cache-D9jF5bYw.mjs.map → cache-CEBAZjir.mjs.map} +1 -1
  261. package/dist/{challenge-store-LhiqMccz.mjs → challenge-store-anhwVER0.mjs} +1 -1
  262. package/dist/{challenge-store-LhiqMccz.mjs.map → challenge-store-anhwVER0.mjs.map} +1 -1
  263. package/dist/{chunks-OCG163rN.mjs → chunks-B2isWQhr.mjs} +2 -2
  264. package/dist/{chunks-OCG163rN.mjs.map → chunks-B2isWQhr.mjs.map} +1 -1
  265. package/dist/cli/index.mjs +23 -22
  266. package/dist/cli/index.mjs.map +1 -1
  267. package/dist/client/cf-access.d.mts +1 -1
  268. package/dist/client/index.d.mts +1 -1
  269. package/dist/client/index.mjs +2 -2
  270. package/dist/{comment-D9BLDpZI.mjs → comment-CzeatwBV.mjs} +3 -3
  271. package/dist/{comment-D9BLDpZI.mjs.map → comment-CzeatwBV.mjs.map} +1 -1
  272. package/dist/{comment-reaction-Drl9SkNu.mjs → comment-reaction-Cj9QJY7I.mjs} +2 -2
  273. package/dist/{comment-reaction-Drl9SkNu.mjs.map → comment-reaction-Cj9QJY7I.mjs.map} +1 -1
  274. package/dist/{comments-BjJ9SKGU.mjs → comments-BLE-jv5k.mjs} +3 -3
  275. package/dist/{comments-BjJ9SKGU.mjs.map → comments-BLE-jv5k.mjs.map} +1 -1
  276. package/dist/{components-CYt4uVK9.mjs → components-B_sATNUc.mjs} +1 -1
  277. package/dist/{components-CYt4uVK9.mjs.map → components-B_sATNUc.mjs.map} +1 -1
  278. package/dist/{content-ncUWWjna.mjs → content-DhgEth4k.mjs} +4 -4
  279. package/dist/{content-ncUWWjna.mjs.map → content-DhgEth4k.mjs.map} +1 -1
  280. package/dist/content-refresh-CA4uKwzH.mjs +1531 -0
  281. package/dist/content-refresh-CA4uKwzH.mjs.map +1 -0
  282. package/dist/{context-C_g-iJVA.mjs → context-CUsR9dXL.mjs} +76 -54
  283. package/dist/context-CUsR9dXL.mjs.map +1 -0
  284. package/dist/{cron-C5LVoNmP.mjs → cron-v4FTEFR_.mjs} +1 -1
  285. package/dist/{cron-C5LVoNmP.mjs.map → cron-v4FTEFR_.mjs.map} +1 -1
  286. package/dist/{dashboard-CotyqQrW.mjs → dashboard-EvEKF5en.mjs} +4 -4
  287. package/dist/{dashboard-CotyqQrW.mjs.map → dashboard-EvEKF5en.mjs.map} +1 -1
  288. package/dist/db/index.d.mts +3 -3
  289. package/dist/db/index.mjs +1 -1
  290. package/dist/db/libsql.d.mts +1 -1
  291. package/dist/db/postgres.d.mts +1 -1
  292. package/dist/db/sqlite.d.mts +1 -1
  293. package/dist/{default-CeRG-Ot4.mjs → default-CM_D83Mg.mjs} +1 -1
  294. package/dist/{default-CeRG-Ot4.mjs.map → default-CM_D83Mg.mjs.map} +1 -1
  295. package/dist/{device-flow-DENDCQ9F.mjs → device-flow-CfGP18Uw.mjs} +4 -4
  296. package/dist/{device-flow-DENDCQ9F.mjs.map → device-flow-CfGP18Uw.mjs.map} +1 -1
  297. package/dist/{email-console-DJP32ucW.mjs → email-console-CIf4Lfvv.mjs} +1 -1
  298. package/dist/{email-console-DJP32ucW.mjs.map → email-console-CIf4Lfvv.mjs.map} +1 -1
  299. package/dist/{error-rVGiqWDt.mjs → error-CkAseSqZ.mjs} +2 -2
  300. package/dist/{error-rVGiqWDt.mjs.map → error-CkAseSqZ.mjs.map} +1 -1
  301. package/dist/{escape-Bjio4ZsM.mjs → escape-DqD0aCKu.mjs} +1 -1
  302. package/dist/{escape-Bjio4ZsM.mjs.map → escape-DqD0aCKu.mjs.map} +1 -1
  303. package/dist/{field-defs-cache-ljXzJCfc.mjs → field-defs-cache-WUI88LX5.mjs} +2 -2
  304. package/dist/{field-defs-cache-ljXzJCfc.mjs.map → field-defs-cache-WUI88LX5.mjs.map} +1 -1
  305. package/dist/{fts-manager-BbCCbEKA.mjs → fts-manager-BbFaF_V8.mjs} +2 -2
  306. package/dist/{fts-manager-BbCCbEKA.mjs.map → fts-manager-BbFaF_V8.mjs.map} +1 -1
  307. package/dist/{import-Bb1T9WJS.mjs → import-fYLf14Co.mjs} +514 -25
  308. package/dist/import-fYLf14Co.mjs.map +1 -0
  309. package/dist/{index-BRNabKrx.d.mts → index-B9sIxwe4.d.mts} +2420 -2379
  310. package/dist/index-B9sIxwe4.d.mts.map +1 -0
  311. package/dist/{index-Dt0OKJB4.d.mts → index-Bm-Gy8Mh.d.mts} +25 -4
  312. package/dist/index-Bm-Gy8Mh.d.mts.map +1 -0
  313. package/dist/index.d.mts +18 -17
  314. package/dist/index.mjs +54 -53
  315. package/dist/{load-DMjXghbZ.mjs → load-Djn3HWQS.mjs} +2 -2
  316. package/dist/{load-DMjXghbZ.mjs.map → load-Djn3HWQS.mjs.map} +1 -1
  317. package/dist/{loader-BA_zhkS_.mjs → loader-CB6YHeul.mjs} +6 -5
  318. package/dist/loader-CB6YHeul.mjs.map +1 -0
  319. package/dist/{manifest-schema-kPGX7VS-.mjs → manifest-schema-BHlgZM1W.mjs} +1 -1
  320. package/dist/{manifest-schema-kPGX7VS-.mjs.map → manifest-schema-BHlgZM1W.mjs.map} +1 -1
  321. package/dist/media/index.d.mts +1 -1
  322. package/dist/media/local-runtime.d.mts +12 -11
  323. package/dist/media/local-runtime.d.mts.map +1 -1
  324. package/dist/media/local-runtime.mjs +5 -5
  325. package/dist/{media-C5t_eCnk.mjs → media-BrXIaMFN.mjs} +2 -2
  326. package/dist/{media-C5t_eCnk.mjs.map → media-BrXIaMFN.mjs.map} +1 -1
  327. package/dist/{media-allowlist-Du8t1u6a.mjs → media-allowlist-CRHEeJ_i.mjs} +1 -1
  328. package/dist/{media-allowlist-Du8t1u6a.mjs.map → media-allowlist-CRHEeJ_i.mjs.map} +1 -1
  329. package/dist/{media-url-O4rm9-SQ.mjs → media-url-CKi5XSw4.mjs} +1 -1
  330. package/dist/{media-url-O4rm9-SQ.mjs.map → media-url-CKi5XSw4.mjs.map} +1 -1
  331. package/dist/{menus-CBb6UrMU.mjs → menus-Cfivc2Ad.mjs} +3 -3
  332. package/dist/{menus-CBb6UrMU.mjs.map → menus-Cfivc2Ad.mjs.map} +1 -1
  333. package/dist/{menus-CYH09a41.mjs → menus-CsZ2ZEK2.mjs} +16 -12
  334. package/dist/{menus-CYH09a41.mjs.map → menus-CsZ2ZEK2.mjs.map} +1 -1
  335. package/dist/{mode-BB0F8xTC.mjs → mode-BM9QgEmt.mjs} +1 -1
  336. package/dist/{mode-BB0F8xTC.mjs.map → mode-BM9QgEmt.mjs.map} +1 -1
  337. package/dist/{oauth-authorization-CsvzIp_F.mjs → oauth-authorization-CXWkIGT4.mjs} +4 -4
  338. package/dist/{oauth-authorization-CsvzIp_F.mjs.map → oauth-authorization-CXWkIGT4.mjs.map} +1 -1
  339. package/dist/{oauth-clients-C9SYwEbZ.mjs → oauth-clients-BmTI5rsl.mjs} +1 -1
  340. package/dist/{oauth-clients-C9SYwEbZ.mjs.map → oauth-clients-BmTI5rsl.mjs.map} +1 -1
  341. package/dist/{oauth-state-store---zrApfB.mjs → oauth-state-store-YLFLcH5F.mjs} +1 -1
  342. package/dist/{oauth-state-store---zrApfB.mjs.map → oauth-state-store-YLFLcH5F.mjs.map} +1 -1
  343. package/dist/{oauth-user-lookup-SHsWRlG9.mjs → oauth-user-lookup-Bsc_GsX-.mjs} +1 -1
  344. package/dist/{oauth-user-lookup-SHsWRlG9.mjs.map → oauth-user-lookup-Bsc_GsX-.mjs.map} +1 -1
  345. package/dist/object-cache/memory.d.mts +1 -1
  346. package/dist/{object-cache-CHbHv83-.mjs → object-cache-Bok5j2ae.mjs} +43 -3
  347. package/dist/object-cache-Bok5j2ae.mjs.map +1 -0
  348. package/dist/{options-41nCWqi9.d.mts → options-iMpRRxIe.d.mts} +3 -3
  349. package/dist/{options-41nCWqi9.d.mts.map → options-iMpRRxIe.d.mts.map} +1 -1
  350. package/dist/page/index.d.mts +2 -2
  351. package/dist/{parse-BwAdS7FZ.mjs → parse-GmjecGct.mjs} +2 -2
  352. package/dist/{parse-BwAdS7FZ.mjs.map → parse-GmjecGct.mjs.map} +1 -1
  353. package/dist/{passkey-config-C0YfSBko.mjs → passkey-config-De0ahnRg.mjs} +1 -1
  354. package/dist/{passkey-config-C0YfSBko.mjs.map → passkey-config-De0ahnRg.mjs.map} +1 -1
  355. package/dist/{placeholder-Cuce9U-m.d.mts → placeholder-BSKwzRe4.d.mts} +1 -1
  356. package/dist/{placeholder-Cuce9U-m.d.mts.map → placeholder-BSKwzRe4.d.mts.map} +1 -1
  357. package/dist/plugin-types.d.mts +1 -1
  358. package/dist/plugin-utils.d.mts +10 -9
  359. package/dist/plugin-utils.d.mts.map +1 -1
  360. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -9
  361. package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
  362. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  363. package/dist/{portable-text-BICg8bTk.mjs → portable-text-cCa0cGV6.mjs} +1 -1
  364. package/dist/{portable-text-BICg8bTk.mjs.map → portable-text-cCa0cGV6.mjs.map} +1 -1
  365. package/dist/{preview-DKGCt2_p.mjs → preview-SSXNVPAC.mjs} +2 -2
  366. package/dist/{preview-DKGCt2_p.mjs.map → preview-SSXNVPAC.mjs.map} +1 -1
  367. package/dist/{public-url-CTVqgMmg.mjs → public-url-Danv47cS.mjs} +1 -1
  368. package/dist/{public-url-CTVqgMmg.mjs.map → public-url-Danv47cS.mjs.map} +1 -1
  369. package/dist/{query-BpQb-blG.mjs → query-ByGq6ver.mjs} +14 -14
  370. package/dist/{query-BpQb-blG.mjs.map → query-ByGq6ver.mjs.map} +1 -1
  371. package/dist/{rate-limit-Dap6HMNM.mjs → rate-limit-C_Lf63Dd.mjs} +2 -2
  372. package/dist/{rate-limit-Dap6HMNM.mjs.map → rate-limit-C_Lf63Dd.mjs.map} +1 -1
  373. package/dist/{redirect-CS-PHtNh.mjs → redirect-CoW3G-0U.mjs} +1 -1
  374. package/dist/{redirect-CS-PHtNh.mjs.map → redirect-CoW3G-0U.mjs.map} +1 -1
  375. package/dist/{redirect-_M0trWoo.mjs → redirect-IyfNirgS.mjs} +2 -2
  376. package/dist/{redirect-_M0trWoo.mjs.map → redirect-IyfNirgS.mjs.map} +1 -1
  377. package/dist/{redirects-w0V9vj_j.mjs → redirects-Bs1g42Ri.mjs} +4 -4
  378. package/dist/{redirects-w0V9vj_j.mjs.map → redirects-Bs1g42Ri.mjs.map} +1 -1
  379. package/dist/{redirects-Dcbuisoj.mjs → redirects-DXEXBAII.mjs} +2 -2
  380. package/dist/{redirects-Dcbuisoj.mjs.map → redirects-DXEXBAII.mjs.map} +1 -1
  381. package/dist/{registry-g8yYt4cC.mjs → registry-B65mUtPa.mjs} +122 -79
  382. package/dist/registry-B65mUtPa.mjs.map +1 -0
  383. package/dist/{request-meta-CmS1tDFf.mjs → request-meta-DbvXaAvU.mjs} +2 -2
  384. package/dist/{request-meta-CmS1tDFf.mjs.map → request-meta-DbvXaAvU.mjs.map} +1 -1
  385. package/dist/{resolve-C7I0qiR0.mjs → resolve-DR3wqMTy.mjs} +1 -1
  386. package/dist/{resolve-C7I0qiR0.mjs.map → resolve-DR3wqMTy.mjs.map} +1 -1
  387. package/dist/{runner-BbR3DfrL.d.mts → runner-DvFWoz8-.d.mts} +2 -2
  388. package/dist/{runner-BbR3DfrL.d.mts.map → runner-DvFWoz8-.d.mts.map} +1 -1
  389. package/dist/{runner-DPdt_Wpa.mjs → runner-K8XK101B.mjs} +351 -199
  390. package/dist/runner-K8XK101B.mjs.map +1 -0
  391. package/dist/runtime.d.mts +11 -10
  392. package/dist/runtime.d.mts.map +1 -1
  393. package/dist/runtime.mjs +2 -2
  394. package/dist/scheduled-publish-Drffrf2a.d.mts +11 -0
  395. package/dist/scheduled-publish-Drffrf2a.d.mts.map +1 -0
  396. package/dist/{schema-DoLd3j2e.mjs → schema-Blhoxp3P.mjs} +5 -5
  397. package/dist/{schema-DoLd3j2e.mjs.map → schema-Blhoxp3P.mjs.map} +1 -1
  398. package/dist/{search-BR0ZeZmh.mjs → search-DZN2HZ-V.mjs} +6 -4
  399. package/dist/search-DZN2HZ-V.mjs.map +1 -0
  400. package/dist/{secrets-BSf9pRRY.mjs → secrets-kp760LlD.mjs} +2 -2
  401. package/dist/{secrets-BSf9pRRY.mjs.map → secrets-kp760LlD.mjs.map} +1 -1
  402. package/dist/{sections-5J2YJPqJ.mjs → sections-CpF5SaGr.mjs} +3 -3
  403. package/dist/{sections-5J2YJPqJ.mjs.map → sections-CpF5SaGr.mjs.map} +1 -1
  404. package/dist/seed/index.d.mts +2 -2
  405. package/dist/seed/index.mjs +19 -18
  406. package/dist/seo/index.d.mts +1 -1
  407. package/dist/seo/index.mjs +1 -1
  408. package/dist/{seo-BkhuuaaE.mjs → seo-89-53DDs.mjs} +1 -1
  409. package/dist/{seo-BkhuuaaE.mjs.map → seo-89-53DDs.mjs.map} +1 -1
  410. package/dist/{seo-DKeXHkzE.mjs → seo-DXu2UQnC.mjs} +3 -3
  411. package/dist/{seo-DKeXHkzE.mjs.map → seo-DXu2UQnC.mjs.map} +1 -1
  412. package/dist/{service-BbO2s8gT.mjs → service-DQmiBMzt.mjs} +3 -3
  413. package/dist/{service-BbO2s8gT.mjs.map → service-DQmiBMzt.mjs.map} +1 -1
  414. package/dist/{session-user-B8aLtKAH.mjs → session-user-Cascbax7.mjs} +1 -1
  415. package/dist/{session-user-B8aLtKAH.mjs.map → session-user-Cascbax7.mjs.map} +1 -1
  416. package/dist/{settings-BMNr9Dmi.mjs → settings-rZyHEcNT.mjs} +3 -3
  417. package/dist/{settings-BMNr9Dmi.mjs.map → settings-rZyHEcNT.mjs.map} +1 -1
  418. package/dist/{settings-DJeki-97.mjs → settings-zhmdKyJQ.mjs} +5 -5
  419. package/dist/{settings-DJeki-97.mjs.map → settings-zhmdKyJQ.mjs.map} +1 -1
  420. package/dist/{setup-complete-gEiySUc-.mjs → setup-complete-Cb3LRyzy.mjs} +1 -1
  421. package/dist/{setup-complete-gEiySUc-.mjs.map → setup-complete-Cb3LRyzy.mjs.map} +1 -1
  422. package/dist/{setup-nonce-DzS50zme.mjs → setup-nonce-Z8x0tHf8.mjs} +1 -1
  423. package/dist/{setup-nonce-DzS50zme.mjs.map → setup-nonce-Z8x0tHf8.mjs.map} +1 -1
  424. package/dist/{single-flight-cache-Cdfkic3t.mjs → single-flight-cache-BG3B_GhN.mjs} +1 -1
  425. package/dist/{single-flight-cache-Cdfkic3t.mjs.map → single-flight-cache-BG3B_GhN.mjs.map} +1 -1
  426. package/dist/{site-url-BLebyON8.mjs → site-url-BBfDTgZB.mjs} +1 -1
  427. package/dist/{site-url-BLebyON8.mjs.map → site-url-BBfDTgZB.mjs.map} +1 -1
  428. package/dist/{ssrf-BRKb343l.mjs → ssrf-1g7usGBg.mjs} +1 -1
  429. package/dist/{ssrf-BRKb343l.mjs.map → ssrf-1g7usGBg.mjs.map} +1 -1
  430. package/dist/{status-vUK0SA17.mjs → status-CqJQX1QN.mjs} +1 -1
  431. package/dist/{status-vUK0SA17.mjs.map → status-CqJQX1QN.mjs.map} +1 -1
  432. package/dist/storage/local.d.mts +1 -1
  433. package/dist/storage/local.mjs +1 -1
  434. package/dist/storage/s3.d.mts +1 -1
  435. package/dist/storage/s3.mjs +1 -1
  436. package/dist/{taxonomies-CM_fUqLU.mjs → taxonomies-BGBd16Eu.mjs} +4 -4
  437. package/dist/{taxonomies-CM_fUqLU.mjs.map → taxonomies-BGBd16Eu.mjs.map} +1 -1
  438. package/dist/{taxonomies-D13i2ctt.mjs → taxonomies-Cnb4F-Pb.mjs} +7 -7
  439. package/dist/{taxonomies-D13i2ctt.mjs.map → taxonomies-Cnb4F-Pb.mjs.map} +1 -1
  440. package/dist/{taxonomy-BU0LjdVb.mjs → taxonomy-BxGxZZdu.mjs} +4 -4
  441. package/dist/{taxonomy-BU0LjdVb.mjs.map → taxonomy-BxGxZZdu.mjs.map} +1 -1
  442. package/dist/{tokens-LeuXF9gG.mjs → tokens-BGT0TUUo.mjs} +1 -1
  443. package/dist/{tokens-LeuXF9gG.mjs.map → tokens-BGT0TUUo.mjs.map} +1 -1
  444. package/dist/{transport-BZ-03aFT.d.mts → transport-BMDWHSgZ.d.mts} +1 -1
  445. package/dist/{transport-BZ-03aFT.d.mts.map → transport-BMDWHSgZ.d.mts.map} +1 -1
  446. package/dist/{transport-_2nBz7e9.mjs → transport-DPefPMcz.mjs} +1 -1
  447. package/dist/{transport-_2nBz7e9.mjs.map → transport-DPefPMcz.mjs.map} +1 -1
  448. package/dist/{trusted-proxy-DZY5WCn2.mjs → trusted-proxy-VTPRiX2s.mjs} +1 -1
  449. package/dist/{trusted-proxy-DZY5WCn2.mjs.map → trusted-proxy-VTPRiX2s.mjs.map} +1 -1
  450. package/dist/{types-ETmO_jQr.d.mts → types-BcLUUbbR.d.mts} +2 -2
  451. package/dist/{types-ETmO_jQr.d.mts.map → types-BcLUUbbR.d.mts.map} +1 -1
  452. package/dist/{types-BYnDBVwe.d.mts → types-CGbYowGQ.d.mts} +2 -2
  453. package/dist/{types-BYnDBVwe.d.mts.map → types-CGbYowGQ.d.mts.map} +1 -1
  454. package/dist/{types-BWqGVhFn.d.mts → types-CgvhU-hF.d.mts} +1 -1
  455. package/dist/{types-BWqGVhFn.d.mts.map → types-CgvhU-hF.d.mts.map} +1 -1
  456. package/dist/{types-fS7-VTdR.d.mts → types-ClUkdbWb.d.mts} +1 -1
  457. package/dist/{types-fS7-VTdR.d.mts.map → types-ClUkdbWb.d.mts.map} +1 -1
  458. package/dist/{types-CZgK3EMW.mjs → types-CuW0zWDA.mjs} +2 -2
  459. package/dist/{types-CZgK3EMW.mjs.map → types-CuW0zWDA.mjs.map} +1 -1
  460. package/dist/{types-BG8_XTLj.d.mts → types-DmHk7vAt.d.mts} +1 -1
  461. package/dist/{types-BG8_XTLj.d.mts.map → types-DmHk7vAt.d.mts.map} +1 -1
  462. package/dist/{types-CQAugunJ.mjs → types-Jo3UYd6b.mjs} +1 -1
  463. package/dist/{types-CQAugunJ.mjs.map → types-Jo3UYd6b.mjs.map} +1 -1
  464. package/dist/{types-xIfVRNLp.d.mts → types-_HFLRzTn.d.mts} +22 -1
  465. package/dist/types-_HFLRzTn.d.mts.map +1 -0
  466. package/dist/{types-DrYMm9s5.d.mts → types-ea9sAPck.d.mts} +1 -1
  467. package/dist/{types-DrYMm9s5.d.mts.map → types-ea9sAPck.d.mts.map} +1 -1
  468. package/dist/{types-CNlaBFzx.d.mts → types-g_aFIdW0.d.mts} +1 -1
  469. package/dist/{types-CNlaBFzx.d.mts.map → types-g_aFIdW0.d.mts.map} +1 -1
  470. package/dist/{types-CUsXGRVj.d.mts → types-vgEWF18A.d.mts} +19 -1
  471. package/dist/{types-CUsXGRVj.d.mts.map → types-vgEWF18A.d.mts.map} +1 -1
  472. package/dist/{user-DkG2jKCp.mjs → user-BjAOvU3Z.mjs} +3 -3
  473. package/dist/{user-DkG2jKCp.mjs.map → user-BjAOvU3Z.mjs.map} +1 -1
  474. package/dist/{utils-B7A57fm9.mjs → utils-BJXElxdw.mjs} +156 -7
  475. package/dist/utils-BJXElxdw.mjs.map +1 -0
  476. package/dist/{validate-CuJ1Ps9j.mjs → validate-CWSnKVIJ.mjs} +2 -2
  477. package/dist/{validate-CuJ1Ps9j.mjs.map → validate-CWSnKVIJ.mjs.map} +1 -1
  478. package/dist/{validate-CG0V0wNR.d.mts → validate-Z1b86_3B.d.mts} +11 -6
  479. package/dist/{validate-CG0V0wNR.d.mts.map → validate-Z1b86_3B.d.mts.map} +1 -1
  480. package/dist/{validation-CwUfTdGF.mjs → validation-c8WjhH8z.mjs} +8 -5
  481. package/dist/{validation-CwUfTdGF.mjs.map → validation-c8WjhH8z.mjs.map} +1 -1
  482. package/dist/version-CaccU7DM.mjs +7 -0
  483. package/dist/{version-BoNBItP_.mjs.map → version-CaccU7DM.mjs.map} +1 -1
  484. package/dist/{widgets-BfOZ-ajY.mjs → widgets-hWcIicX8.mjs} +3 -3
  485. package/dist/{widgets-BfOZ-ajY.mjs.map → widgets-hWcIicX8.mjs.map} +1 -1
  486. package/dist/wxr-taxonomies-0_uxSrmf.d.mts +34 -0
  487. package/dist/wxr-taxonomies-0_uxSrmf.d.mts.map +1 -0
  488. package/dist/wxr-taxonomies-BC3OYpvx.mjs +425 -0
  489. package/dist/wxr-taxonomies-BC3OYpvx.mjs.map +1 -0
  490. package/dist/{zod-generator-gFsRuNNy.mjs → zod-generator-CacrR-i8.mjs} +2 -2
  491. package/dist/{zod-generator-gFsRuNNy.mjs.map → zod-generator-CacrR-i8.mjs.map} +1 -1
  492. package/package.json +6 -6
  493. package/src/api/handlers/content.ts +69 -23
  494. package/src/api/handlers/plugins.ts +77 -10
  495. package/src/api/schemas/import.ts +19 -0
  496. package/src/astro/integration/runtime.ts +3 -6
  497. package/src/astro/integration/vite-config.ts +19 -1
  498. package/src/astro/middleware/auth.ts +10 -3
  499. package/src/astro/middleware/csp.ts +54 -3
  500. package/src/astro/middleware.ts +1 -0
  501. package/src/astro/routes/admin.astro +9 -0
  502. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +6 -1
  503. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +6 -1
  504. package/src/astro/routes/api/admin/plugins/[id]/index.ts +1 -0
  505. package/src/astro/routes/api/admin/plugins/index.ts +1 -0
  506. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +122 -98
  507. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +794 -44
  508. package/src/astro/routes/api/manifest.ts +20 -1
  509. package/src/astro/routes/api/setup/dev-bypass.ts +7 -2
  510. package/src/astro/types.ts +4 -0
  511. package/src/cli/commands/seed.ts +1 -1
  512. package/src/components/EmDashImage.astro +59 -23
  513. package/src/components/Image.astro +36 -35
  514. package/src/components/LiveSearch.astro +40 -4
  515. package/src/components/live-search-routing.ts +30 -0
  516. package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +23 -18
  517. package/src/database/migrations/048_restore_content_taxonomies_term_index.ts +32 -0
  518. package/src/database/migrations/049_taxonomies_name_locale_index.ts +42 -0
  519. package/src/database/migrations/050_media_usage_index_status.ts +142 -0
  520. package/src/database/migrations/runner.ts +6 -0
  521. package/src/database/repositories/media-usage.ts +932 -58
  522. package/src/database/types.ts +22 -0
  523. package/src/emdash-runtime.ts +123 -3
  524. package/src/import/comments.ts +159 -0
  525. package/src/import/index.ts +8 -1
  526. package/src/import/settings.ts +65 -189
  527. package/src/import/sources/wordpress-plugin.ts +338 -42
  528. package/src/import/sources/wxr.ts +4 -1
  529. package/src/import/types.ts +12 -0
  530. package/src/import/utils.ts +207 -0
  531. package/src/import/wxr-taxonomies.ts +39 -0
  532. package/src/loader.ts +13 -2
  533. package/src/media/usage/content-fields.ts +119 -0
  534. package/src/media/usage/content-refresh.ts +524 -0
  535. package/src/media/usage/content-repair.ts +491 -0
  536. package/src/media/usage/content-snapshots.ts +435 -0
  537. package/src/media/usage/extractor.ts +4 -4
  538. package/src/media/usage/source-key.ts +22 -0
  539. package/src/menus/index.ts +17 -1
  540. package/src/object-cache/index.ts +82 -7
  541. package/src/plugins/context.ts +100 -71
  542. package/src/schema/registry.ts +240 -154
  543. package/src/search/query.ts +3 -0
  544. package/src/search/types.ts +2 -0
  545. package/src/seed/apply.ts +158 -101
  546. package/src/seed/types.ts +6 -1
  547. package/dist/api-BMfveMoB.mjs.map +0 -1
  548. package/dist/apply--IVNSdXn.mjs.map +0 -1
  549. package/dist/byline-fields-eyJMblSr.mjs.map +0 -1
  550. package/dist/context-C_g-iJVA.mjs.map +0 -1
  551. package/dist/import-Bb1T9WJS.mjs.map +0 -1
  552. package/dist/index-BRNabKrx.d.mts.map +0 -1
  553. package/dist/index-Dt0OKJB4.d.mts.map +0 -1
  554. package/dist/loader-BA_zhkS_.mjs.map +0 -1
  555. package/dist/object-cache-CHbHv83-.mjs.map +0 -1
  556. package/dist/registry-g8yYt4cC.mjs.map +0 -1
  557. package/dist/runner-DPdt_Wpa.mjs.map +0 -1
  558. package/dist/search-BR0ZeZmh.mjs.map +0 -1
  559. package/dist/types-xIfVRNLp.d.mts.map +0 -1
  560. package/dist/utils-B7A57fm9.mjs.map +0 -1
  561. package/dist/version-BoNBItP_.mjs +0 -7
  562. /package/dist/{api-tokens-B4BQybOp.mjs → api-tokens-DsvvCtdh.mjs} +0 -0
  563. /package/dist/{ssrf-CcX9zvMK.mjs → ssrf-CmuGgCq3.mjs} +0 -0
  564. /package/dist/{types-BvbeGEtr.mjs → types-DROWRxYV.mjs} +0 -0
@@ -0,0 +1,1531 @@
1
+ import { t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
2
+ import { r as isI18nEnabled } from "./config-CVssduLe.mjs";
3
+ import { a as encodeCursor, i as decodeCursor } from "./types-CuW0zWDA.mjs";
4
+ import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-B2isWQhr.mjs";
5
+ import { t as withTransaction } from "./transaction-qfqpPVpu.mjs";
6
+ import { t as INTERNAL_MEDIA_PREFIX } from "./normalize-CeglrBT9.mjs";
7
+ import { n as normalizeMime } from "./mime-CCEzze7W.mjs";
8
+ import { n as hashString } from "./hash-DrvzIXcz.mjs";
9
+ import { sql } from "kysely";
10
+ import { ulid } from "ulidx";
11
+
12
+ //#region src/database/repositories/media-usage.ts
13
+ const OCCURRENCE_INSERT_BATCH_SIZE = Math.max(1, Math.floor(SQL_BATCH_SIZE / 13));
14
+ /** Persistence-only repository for the internal media usage projection tables. */
15
+ var MediaUsageRepository = class {
16
+ constructor(db) {
17
+ this.db = db;
18
+ }
19
+ async replaceSource(source, occurrences) {
20
+ const generation = ulid();
21
+ const now = (/* @__PURE__ */ new Date()).toISOString();
22
+ await withTransaction(this.db, async (trx) => {
23
+ await this.insertOccurrences(trx, source.sourceKey, generation, occurrences, now);
24
+ await this.upsertSource(trx, source, generation, now);
25
+ });
26
+ const replaced = await this.findSource(source.sourceKey);
27
+ if (!replaced) throw new Error(`Media usage source ${source.sourceKey} was not persisted`);
28
+ return replaced;
29
+ }
30
+ async replaceSourceIfCurrent(source, occurrences, expectedCurrentGeneration) {
31
+ const generation = ulid();
32
+ const now = (/* @__PURE__ */ new Date()).toISOString();
33
+ const row = this.buildSourceRow(source, generation, now);
34
+ let replaced = false;
35
+ await withTransaction(this.db, async (trx) => {
36
+ await this.insertOccurrences(trx, source.sourceKey, generation, occurrences, now);
37
+ if (expectedCurrentGeneration === null) {
38
+ replaced = await this.insertSourceIfAbsent(trx, row);
39
+ return;
40
+ }
41
+ replaced = await this.updateSourceIfGeneration(trx, row, expectedCurrentGeneration);
42
+ });
43
+ return {
44
+ replaced,
45
+ source: replaced ? null : await this.findSource(source.sourceKey)
46
+ };
47
+ }
48
+ async findSource(sourceKey) {
49
+ const row = await this.db.selectFrom("_emdash_media_usage_sources").selectAll().where("source_key", "=", sourceKey).executeTakeFirst();
50
+ return row ? rowToSource(row) : null;
51
+ }
52
+ async findSources(sourceKeys) {
53
+ const uniqueSourceKeys = [...new Set(sourceKeys)];
54
+ const sources = /* @__PURE__ */ new Map();
55
+ if (uniqueSourceKeys.length === 0) return sources;
56
+ for (const sourceKeyBatch of chunks(uniqueSourceKeys, SQL_BATCH_SIZE)) {
57
+ const rows = await this.db.selectFrom("_emdash_media_usage_sources").selectAll().where("source_key", "in", sourceKeyBatch).execute();
58
+ for (const row of rows) {
59
+ const source = rowToSource(row);
60
+ sources.set(source.sourceKey, source);
61
+ }
62
+ }
63
+ return sources;
64
+ }
65
+ async replaceSourceIfMatching(source, occurrences, expectedSource) {
66
+ const generation = ulid();
67
+ const now = (/* @__PURE__ */ new Date()).toISOString();
68
+ const row = this.buildSourceRow(source, generation, now);
69
+ let replaced = false;
70
+ await withTransaction(this.db, async (trx) => {
71
+ await this.insertOccurrences(trx, source.sourceKey, generation, occurrences, now);
72
+ if (expectedSource === null) {
73
+ replaced = await this.insertSourceIfAbsent(trx, row);
74
+ return;
75
+ }
76
+ replaced = await this.updateSourceIfMatching(trx, row, expectedSource);
77
+ });
78
+ return {
79
+ replaced,
80
+ source: replaced ? null : await this.findSource(source.sourceKey)
81
+ };
82
+ }
83
+ async markSourceAttempted(source) {
84
+ const now = (/* @__PURE__ */ new Date()).toISOString();
85
+ const row = this.buildAttemptedSourceRow(source, now);
86
+ const updates = this.attemptedSourceUpdateSet(source, row);
87
+ await this.db.insertInto("_emdash_media_usage_sources").values(row).onConflict((oc) => oc.column("source_key").doUpdateSet(updates)).execute();
88
+ const attempted = await this.findSource(source.sourceKey);
89
+ if (!attempted) throw new Error(`Media usage source ${source.sourceKey} was not persisted`);
90
+ return attempted;
91
+ }
92
+ async markSourceAttemptedIfMatching(source, expectedSource) {
93
+ const now = (/* @__PURE__ */ new Date()).toISOString();
94
+ const row = this.buildAttemptedSourceRow(source, now);
95
+ let attempted = false;
96
+ if (expectedSource === null) attempted = await this.insertSourceIfAbsent(this.db, row);
97
+ else attempted = await this.updateAttemptedSourceIfMatching(this.db, source, row, expectedSource);
98
+ return {
99
+ attempted,
100
+ source: attempted ? null : await this.findSource(source.sourceKey)
101
+ };
102
+ }
103
+ async findCurrentUsageByMediaId(mediaId) {
104
+ return (await this.db.selectFrom("_emdash_media_usage_sources as s").innerJoin("_emdash_media_usage as u", (join) => join.onRef("u.source_key", "=", "s.source_key").onRef("u.generation", "=", "s.current_generation")).select(currentUsageSelect).where("u.media_id", "=", mediaId).orderBy("s.source_key", "asc").orderBy("u.field_path", "asc").orderBy("u.occurrence_index", "asc").execute()).map(rowToUsageRecord);
105
+ }
106
+ async findCurrentUsageByProviderAsset(provider, providerAssetId) {
107
+ return (await this.db.selectFrom("_emdash_media_usage_sources as s").innerJoin("_emdash_media_usage as u", (join) => join.onRef("u.source_key", "=", "s.source_key").onRef("u.generation", "=", "s.current_generation")).select(currentUsageSelect).where("u.provider", "=", provider).where("u.provider_asset_id", "=", providerAssetId).orderBy("s.source_key", "asc").orderBy("u.field_path", "asc").orderBy("u.occurrence_index", "asc").execute()).map(rowToUsageRecord);
108
+ }
109
+ async findCurrentUsagePageByMediaId(mediaId, options = {}) {
110
+ return this.findCurrentUsagePage((query) => query.where("u.media_id", "=", mediaId), options);
111
+ }
112
+ async findCurrentUsagePageByProviderAsset(provider, providerAssetId, options = {}) {
113
+ return this.findCurrentUsagePage((query) => query.where("u.provider", "=", provider).where("u.provider_asset_id", "=", providerAssetId), options);
114
+ }
115
+ async deleteSource(sourceKey) {
116
+ return this.deleteSources([sourceKey]);
117
+ }
118
+ async deleteSourceIfCurrent(sourceKey, expectedCurrentGeneration) {
119
+ let deleted = false;
120
+ await withTransaction(this.db, async (trx) => {
121
+ const result = await trx.deleteFrom("_emdash_media_usage_sources").where("source_key", "=", sourceKey).where("current_generation", "=", expectedCurrentGeneration).executeTakeFirst();
122
+ deleted = Number(result.numDeletedRows ?? 0) > 0;
123
+ if (!deleted) return;
124
+ await trx.deleteFrom("_emdash_media_usage").where("source_key", "=", sourceKey).execute();
125
+ });
126
+ return {
127
+ deleted,
128
+ source: await this.findSource(sourceKey)
129
+ };
130
+ }
131
+ async deleteSourceIfMatching(sourceKey, expectedSource) {
132
+ let deleted = false;
133
+ await withTransaction(this.db, async (trx) => {
134
+ const result = await trx.deleteFrom("_emdash_media_usage_sources").where("source_key", "=", sourceKey).where(this.sourceMatchExpression(expectedSource)).executeTakeFirst();
135
+ deleted = Number(result.numDeletedRows ?? 0) > 0;
136
+ if (!deleted) return;
137
+ await trx.deleteFrom("_emdash_media_usage").where("source_key", "=", sourceKey).execute();
138
+ });
139
+ return {
140
+ deleted,
141
+ source: await this.findSource(sourceKey)
142
+ };
143
+ }
144
+ async deleteSourceIfMatchingContentAbsent(sourceKey, expectedSource, collectionSlug, contentId) {
145
+ validateIdentifier(collectionSlug, "collection slug");
146
+ const tableName = `ec_${collectionSlug}`;
147
+ let deleted = false;
148
+ await withTransaction(this.db, async (trx) => {
149
+ const result = await trx.deleteFrom("_emdash_media_usage_sources").where("source_key", "=", sourceKey).where(this.sourceMatchExpression(expectedSource)).where(sql`NOT EXISTS (SELECT 1 FROM ${sql.ref(tableName)} WHERE id = ${contentId})`).executeTakeFirst();
150
+ deleted = Number(result.numDeletedRows ?? 0) > 0;
151
+ if (!deleted) return;
152
+ await trx.deleteFrom("_emdash_media_usage").where("source_key", "=", sourceKey).execute();
153
+ });
154
+ const contentPresent = deleted ? false : await this.contentRowExists(tableName, contentId);
155
+ return {
156
+ deleted,
157
+ contentPresent,
158
+ source: deleted || contentPresent ? null : await this.findSource(sourceKey)
159
+ };
160
+ }
161
+ async deleteSources(sourceKeys) {
162
+ return this.deleteSourceKeys(sourceKeys);
163
+ }
164
+ async deleteContentSources(collectionSlug, contentId) {
165
+ const sourceKeys = (await this.db.selectFrom("_emdash_media_usage_sources").select("source_key").where("source_type", "=", "content").where("collection_slug", "=", collectionSlug).where("content_id", "=", contentId).execute()).map((row) => row.source_key);
166
+ return this.deleteSourceKeys(sourceKeys);
167
+ }
168
+ async deleteCollectionSources(collectionSlug) {
169
+ let deleted = 0;
170
+ while (true) {
171
+ const sourceRows = await this.db.selectFrom("_emdash_media_usage_sources").select("source_key").where("source_type", "=", "content").where("collection_slug", "=", collectionSlug).orderBy("source_key", "asc").limit(SQL_BATCH_SIZE).execute();
172
+ if (sourceRows.length === 0) break;
173
+ deleted += await this.deleteSourceKeys(sourceRows.map((row) => row.source_key));
174
+ }
175
+ return deleted;
176
+ }
177
+ async findCollectionContentSources(collectionSlug) {
178
+ return (await this.db.selectFrom("_emdash_media_usage_sources").selectAll().where("source_type", "=", "content").where("collection_slug", "=", collectionSlug).orderBy("source_key", "asc").execute()).map((row) => rowToSource(row));
179
+ }
180
+ async deleteOrphanOccurrencesOlderThan(cutoff, limit) {
181
+ const batchLimit = Math.floor(limit);
182
+ if (batchLimit <= 0) return 0;
183
+ 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")).select("u.id").where("s.source_key", "is", null).where("u.created_at", "<", cutoff).orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(batchLimit).execute();
184
+ let deleted = 0;
185
+ for (const idBatch of chunks(rows.map((row) => row.id), SQL_BATCH_SIZE)) {
186
+ const result = await this.db.deleteFrom("_emdash_media_usage").where("id", "in", idBatch).where("created_at", "<", cutoff).where(sql`NOT EXISTS (SELECT 1 FROM _emdash_media_usage_sources s WHERE s.source_key = _emdash_media_usage.source_key)`).executeTakeFirst();
187
+ deleted += Number(result.numDeletedRows ?? 0);
188
+ }
189
+ return deleted;
190
+ }
191
+ async deleteStaleGenerationsOlderThan(cutoff, limit) {
192
+ const batchLimit = Math.floor(limit);
193
+ if (batchLimit <= 0) return 0;
194
+ const ids = (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")).select("u.id").where("u.created_at", "<", cutoff).whereRef("u.generation", "!=", "s.current_generation").whereRef("u.created_at", "<", "s.indexed_at").orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(batchLimit).execute()).map((row) => row.id);
195
+ if (ids.length === 0) return 0;
196
+ let deleted = 0;
197
+ for (const idBatch of chunks(ids, SQL_BATCH_SIZE)) {
198
+ const result = await this.db.deleteFrom("_emdash_media_usage").where("id", "in", idBatch).where("created_at", "<", cutoff).where((eb) => eb.exists(eb.selectFrom("_emdash_media_usage_sources as s").select("s.source_key").whereRef("s.source_key", "=", "_emdash_media_usage.source_key").whereRef("s.current_generation", "!=", "_emdash_media_usage.generation").whereRef("_emdash_media_usage.created_at", "<", "s.indexed_at"))).executeTakeFirst();
199
+ deleted += Number(result.numDeletedRows ?? 0);
200
+ }
201
+ return deleted;
202
+ }
203
+ async deleteAbandonedGenerationsOlderThan(cutoff, limit) {
204
+ const batchLimit = Math.floor(limit);
205
+ if (batchLimit <= 0) return 0;
206
+ 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")).select("u.id").where("u.created_at", "<", cutoff).whereRef("u.generation", "!=", "s.current_generation").whereRef("u.created_at", ">=", "s.indexed_at").orderBy("u.created_at", "asc").orderBy("u.id", "asc").limit(batchLimit).execute();
207
+ let deleted = 0;
208
+ for (const idBatch of chunks(rows.map((row) => row.id), SQL_BATCH_SIZE)) {
209
+ const result = await this.db.deleteFrom("_emdash_media_usage").where("id", "in", idBatch).where("created_at", "<", cutoff).where((eb) => eb.exists(eb.selectFrom("_emdash_media_usage_sources as s").select("s.source_key").whereRef("s.source_key", "=", "_emdash_media_usage.source_key").whereRef("s.current_generation", "!=", "_emdash_media_usage.generation").whereRef("_emdash_media_usage.created_at", ">=", "s.indexed_at"))).executeTakeFirst();
210
+ deleted += Number(result.numDeletedRows ?? 0);
211
+ }
212
+ return deleted;
213
+ }
214
+ async upsertIndexStatus(input) {
215
+ const now = input.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
216
+ const row = {
217
+ adapter_id: input.adapterId,
218
+ scope_type: input.scopeType,
219
+ scope_key: input.scopeKey,
220
+ status: input.status,
221
+ schema_version: input.schemaVersion ?? 1,
222
+ started_at: input.startedAt ?? null,
223
+ completed_at: input.completedAt ?? null,
224
+ cursor: input.cursor ?? null,
225
+ indexed_source_count: input.indexedSourceCount ?? 0,
226
+ failed_source_count: input.failedSourceCount ?? 0,
227
+ last_error_code: input.lastErrorCode ?? null,
228
+ updated_at: now
229
+ };
230
+ await this.db.insertInto("_emdash_media_usage_index_status").values(row).onConflict((oc) => oc.columns([
231
+ "adapter_id",
232
+ "scope_type",
233
+ "scope_key"
234
+ ]).doUpdateSet({
235
+ status: row.status,
236
+ schema_version: row.schema_version,
237
+ started_at: row.started_at,
238
+ completed_at: row.completed_at,
239
+ cursor: row.cursor,
240
+ indexed_source_count: row.indexed_source_count,
241
+ failed_source_count: row.failed_source_count,
242
+ last_error_code: row.last_error_code,
243
+ updated_at: row.updated_at
244
+ })).execute();
245
+ const status = await this.findIndexStatus(input);
246
+ if (!status) throw new Error(`Media usage index status ${input.adapterId}:${input.scopeType}:${input.scopeKey} was not persisted`);
247
+ return status;
248
+ }
249
+ async beginIndexStatusRepair(input) {
250
+ return this.upsertIndexStatus({
251
+ adapterId: input.adapterId,
252
+ scopeType: input.scopeType,
253
+ scopeKey: input.scopeKey,
254
+ status: "running",
255
+ schemaVersion: input.schemaVersion,
256
+ startedAt: input.startedAt,
257
+ completedAt: null,
258
+ cursor: input.runToken,
259
+ indexedSourceCount: 0,
260
+ failedSourceCount: 0,
261
+ lastErrorCode: null,
262
+ updatedAt: input.updatedAt
263
+ });
264
+ }
265
+ async finalizeIndexStatusRepairIfRunning(input) {
266
+ const updates = {
267
+ status: input.status,
268
+ completed_at: input.completedAt,
269
+ cursor: null,
270
+ indexed_source_count: input.indexedSourceCount ?? 0,
271
+ failed_source_count: input.failedSourceCount ?? 0,
272
+ last_error_code: input.lastErrorCode ?? null,
273
+ updated_at: input.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
274
+ };
275
+ if (input.schemaVersion !== void 0) updates.schema_version = input.schemaVersion;
276
+ const result = await this.db.updateTable("_emdash_media_usage_index_status").set(updates).where("adapter_id", "=", input.adapterId).where("scope_type", "=", input.scopeType).where("scope_key", "=", input.scopeKey).where("status", "=", "running").where("cursor", "=", input.runToken).executeTakeFirst();
277
+ return {
278
+ finalized: Number(result.numUpdatedRows ?? 0) > 0,
279
+ status: await this.findIndexStatus(input)
280
+ };
281
+ }
282
+ async findIndexStatus(identity) {
283
+ 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();
284
+ return row ? rowToIndexStatus(row) : null;
285
+ }
286
+ async deleteIndexStatus(identity) {
287
+ const result = await this.db.deleteFrom("_emdash_media_usage_index_status").where("adapter_id", "=", identity.adapterId).where("scope_type", "=", identity.scopeType).where("scope_key", "=", identity.scopeKey).executeTakeFirst();
288
+ return Number(result.numDeletedRows ?? 0);
289
+ }
290
+ async findCurrentUsagePage(applyFilter, options) {
291
+ const limit = Math.min(Math.max(1, options.limit ?? 50), 100);
292
+ let query = applyFilter(this.currentUsageBaseQuery()).orderBy("u.id", "asc").limit(limit + 1);
293
+ if (options.cursor) {
294
+ const { id } = decodeCursor(options.cursor);
295
+ query = query.where("u.id", ">", id);
296
+ }
297
+ const rows = await query.execute();
298
+ const items = rows.slice(0, limit).map(rowToUsageRecord);
299
+ const result = { items };
300
+ if (rows.length > limit && items.length > 0) {
301
+ const last = items.at(-1);
302
+ result.nextCursor = encodeCursor(last.occurrence.id, last.occurrence.id);
303
+ }
304
+ return result;
305
+ }
306
+ currentUsageBaseQuery() {
307
+ return this.db.selectFrom("_emdash_media_usage_sources as s").innerJoin("_emdash_media_usage as u", (join) => join.onRef("u.source_key", "=", "s.source_key").onRef("u.generation", "=", "s.current_generation")).select(currentUsageSelect);
308
+ }
309
+ async deleteSourceKeys(sourceKeys) {
310
+ const uniqueSourceKeys = [...new Set(sourceKeys)];
311
+ if (uniqueSourceKeys.length === 0) return 0;
312
+ return withTransaction(this.db, async (trx) => {
313
+ let deleted = 0;
314
+ for (const sourceKeyBatch of chunks(uniqueSourceKeys, SQL_BATCH_SIZE)) {
315
+ const result = await trx.deleteFrom("_emdash_media_usage_sources").where("source_key", "in", sourceKeyBatch).executeTakeFirst();
316
+ deleted += Number(result.numDeletedRows ?? 0);
317
+ await trx.deleteFrom("_emdash_media_usage").where("source_key", "in", sourceKeyBatch).execute();
318
+ }
319
+ return deleted;
320
+ });
321
+ }
322
+ async insertOccurrences(db, sourceKey, generation, occurrences, now) {
323
+ if (occurrences.length === 0) return;
324
+ const rows = occurrences.map((occurrence) => ({
325
+ id: ulid(),
326
+ source_key: sourceKey,
327
+ generation,
328
+ field_slug: occurrence.fieldSlug,
329
+ field_path: occurrence.fieldPath,
330
+ occurrence_index: occurrence.occurrenceIndex ?? 0,
331
+ reference_type: occurrence.referenceType,
332
+ media_id: occurrence.mediaId,
333
+ provider: occurrence.provider,
334
+ provider_asset_id: occurrence.providerAssetId,
335
+ media_kind: occurrence.mediaKind ?? null,
336
+ mime_type: occurrence.mimeType ?? null,
337
+ created_at: now
338
+ }));
339
+ for (const rowBatch of chunks(rows, OCCURRENCE_INSERT_BATCH_SIZE)) await db.insertInto("_emdash_media_usage").values(rowBatch).execute();
340
+ }
341
+ async upsertSource(db, source, generation, now) {
342
+ const row = this.buildSourceRow(source, generation, now);
343
+ await db.insertInto("_emdash_media_usage_sources").values(row).onConflict((oc) => oc.column("source_key").doUpdateSet(this.sourceUpdateSet(row))).execute();
344
+ }
345
+ async insertSourceIfAbsent(db, row) {
346
+ return ((await db.insertInto("_emdash_media_usage_sources").values(row).onConflict((oc) => oc.column("source_key").doNothing()).executeTakeFirst()).numInsertedOrUpdatedRows ?? 0n) > 0n;
347
+ }
348
+ async updateSourceIfGeneration(db, row, expectedCurrentGeneration) {
349
+ const result = await db.updateTable("_emdash_media_usage_sources").set(this.sourceUpdateSet(row)).where("source_key", "=", row.source_key).where("current_generation", "=", expectedCurrentGeneration).executeTakeFirst();
350
+ return Number(result.numUpdatedRows ?? 0) > 0;
351
+ }
352
+ async updateSourceIfMatching(db, row, expectedSource) {
353
+ const result = await db.updateTable("_emdash_media_usage_sources").set(this.sourceUpdateSet(row)).where("source_key", "=", row.source_key).where(this.sourceMatchExpression(expectedSource)).executeTakeFirst();
354
+ return Number(result.numUpdatedRows ?? 0) > 0;
355
+ }
356
+ async updateAttemptedSourceIfMatching(db, source, row, expectedSource) {
357
+ const result = await db.updateTable("_emdash_media_usage_sources").set(this.attemptedSourceUpdateSet(source, row)).where("source_key", "=", row.source_key).where(this.sourceMatchExpression(expectedSource)).executeTakeFirst();
358
+ return Number(result.numUpdatedRows ?? 0) > 0;
359
+ }
360
+ sourceMatchExpression(expectedSource) {
361
+ return (eb) => eb.and([
362
+ eb("current_generation", "=", expectedSource.currentGeneration),
363
+ eb("source_completeness", "=", expectedSource.sourceCompleteness),
364
+ this.nullableStringExpression(eb, "updated_at", expectedSource.updatedAt),
365
+ this.nullableStringExpression(eb, "source_fingerprint", expectedSource.sourceFingerprint),
366
+ this.nullableStringExpression(eb, "source_updated_at", expectedSource.sourceUpdatedAt),
367
+ this.nullableNumberExpression(eb, "source_version", expectedSource.sourceVersion),
368
+ this.nullableStringExpression(eb, "revision_id", expectedSource.revisionId),
369
+ this.nullableStringExpression(eb, "last_attempted_at", expectedSource.lastAttemptedAt),
370
+ this.nullableStringExpression(eb, "last_error_code", expectedSource.lastErrorCode)
371
+ ]);
372
+ }
373
+ nullableStringExpression(eb, column, value) {
374
+ return value === null ? eb(column, "is", null) : eb(column, "=", value);
375
+ }
376
+ nullableNumberExpression(eb, column, value) {
377
+ return value === null ? eb(column, "is", null) : eb(column, "=", value);
378
+ }
379
+ async contentRowExists(tableName, contentId) {
380
+ return (await sql`
381
+ SELECT id
382
+ FROM ${sql.ref(tableName)}
383
+ WHERE id = ${contentId}
384
+ LIMIT 1
385
+ `.execute(this.db)).rows.length > 0;
386
+ }
387
+ buildSourceRow(source, generation, now) {
388
+ return {
389
+ source_key: source.sourceKey,
390
+ source_type: source.sourceType,
391
+ collection_slug: source.collectionSlug ?? null,
392
+ content_id: source.contentId ?? null,
393
+ source_variant: source.sourceVariant,
394
+ locale: source.locale ?? null,
395
+ translation_group: source.translationGroup ?? null,
396
+ content_slug: source.contentSlug ?? null,
397
+ content_title: source.contentTitle ?? null,
398
+ content_status: source.contentStatus ?? null,
399
+ content_scheduled_at: source.contentScheduledAt ?? null,
400
+ content_deleted_at: source.contentDeletedAt ?? null,
401
+ revision_id: source.revisionId ?? null,
402
+ current_generation: generation,
403
+ schema_version: source.schemaVersion ?? 1,
404
+ source_updated_at: source.sourceUpdatedAt ?? null,
405
+ source_version: source.sourceVersion ?? null,
406
+ source_fingerprint: source.sourceFingerprint ?? null,
407
+ source_completeness: source.sourceCompleteness ?? "complete",
408
+ last_attempted_at: source.lastAttemptedAt ?? now,
409
+ last_error_code: null,
410
+ indexed_at: now,
411
+ updated_at: now
412
+ };
413
+ }
414
+ buildAttemptedSourceRow(source, now) {
415
+ return {
416
+ source_key: source.sourceKey,
417
+ source_type: source.sourceType,
418
+ collection_slug: source.collectionSlug ?? null,
419
+ content_id: source.contentId ?? null,
420
+ source_variant: source.sourceVariant,
421
+ locale: source.locale ?? null,
422
+ translation_group: source.translationGroup ?? null,
423
+ content_slug: source.contentSlug ?? null,
424
+ content_title: source.contentTitle ?? null,
425
+ content_status: source.contentStatus ?? null,
426
+ content_scheduled_at: source.contentScheduledAt ?? null,
427
+ content_deleted_at: source.contentDeletedAt ?? null,
428
+ revision_id: source.revisionId ?? null,
429
+ current_generation: ulid(),
430
+ schema_version: source.schemaVersion ?? 1,
431
+ source_updated_at: source.sourceUpdatedAt ?? null,
432
+ source_version: source.sourceVersion ?? null,
433
+ source_fingerprint: source.sourceFingerprint ?? null,
434
+ source_completeness: source.sourceCompleteness ?? (source.lastErrorCode ? "failed" : "unknown"),
435
+ last_attempted_at: source.lastAttemptedAt ?? now,
436
+ last_error_code: source.lastErrorCode ?? null,
437
+ indexed_at: now,
438
+ updated_at: now
439
+ };
440
+ }
441
+ attemptedSourceUpdateSet(source, row) {
442
+ const updates = {
443
+ source_type: row.source_type,
444
+ source_variant: row.source_variant,
445
+ source_completeness: row.source_completeness,
446
+ last_attempted_at: row.last_attempted_at,
447
+ last_error_code: row.last_error_code,
448
+ updated_at: row.updated_at
449
+ };
450
+ if (source.collectionSlug !== void 0) updates.collection_slug = row.collection_slug;
451
+ if (source.contentId !== void 0) updates.content_id = row.content_id;
452
+ if (source.locale !== void 0) updates.locale = row.locale;
453
+ if (source.translationGroup !== void 0) updates.translation_group = row.translation_group;
454
+ if (source.contentSlug !== void 0) updates.content_slug = row.content_slug;
455
+ if (source.contentTitle !== void 0) updates.content_title = row.content_title;
456
+ if (source.contentStatus !== void 0) updates.content_status = row.content_status;
457
+ if (source.contentScheduledAt !== void 0) updates.content_scheduled_at = row.content_scheduled_at;
458
+ if (source.contentDeletedAt !== void 0) updates.content_deleted_at = row.content_deleted_at;
459
+ if (source.revisionId !== void 0) updates.revision_id = row.revision_id;
460
+ if (source.schemaVersion !== void 0) updates.schema_version = row.schema_version;
461
+ if (source.sourceUpdatedAt !== void 0) updates.source_updated_at = row.source_updated_at;
462
+ if (source.sourceVersion !== void 0) updates.source_version = row.source_version;
463
+ if (source.sourceFingerprint !== void 0) updates.source_fingerprint = row.source_fingerprint;
464
+ return updates;
465
+ }
466
+ sourceUpdateSet(row) {
467
+ return {
468
+ source_type: row.source_type,
469
+ collection_slug: row.collection_slug,
470
+ content_id: row.content_id,
471
+ source_variant: row.source_variant,
472
+ locale: row.locale,
473
+ translation_group: row.translation_group,
474
+ content_slug: row.content_slug,
475
+ content_title: row.content_title,
476
+ content_status: row.content_status,
477
+ content_scheduled_at: row.content_scheduled_at,
478
+ content_deleted_at: row.content_deleted_at,
479
+ revision_id: row.revision_id,
480
+ current_generation: row.current_generation,
481
+ schema_version: row.schema_version,
482
+ source_updated_at: row.source_updated_at,
483
+ source_version: row.source_version,
484
+ source_fingerprint: row.source_fingerprint,
485
+ source_completeness: row.source_completeness,
486
+ last_attempted_at: row.last_attempted_at,
487
+ last_error_code: row.last_error_code,
488
+ indexed_at: row.indexed_at,
489
+ updated_at: row.updated_at
490
+ };
491
+ }
492
+ };
493
+ const currentUsageSelect = [
494
+ "s.source_key as source_key",
495
+ "s.source_type as source_type",
496
+ "s.collection_slug as collection_slug",
497
+ "s.content_id as content_id",
498
+ "s.source_variant as source_variant",
499
+ "s.locale as locale",
500
+ "s.translation_group as translation_group",
501
+ "s.content_slug as content_slug",
502
+ "s.content_title as content_title",
503
+ "s.content_status as content_status",
504
+ "s.content_scheduled_at as content_scheduled_at",
505
+ "s.content_deleted_at as content_deleted_at",
506
+ "s.revision_id as revision_id",
507
+ "s.current_generation as current_generation",
508
+ "s.schema_version as schema_version",
509
+ "s.source_updated_at as source_updated_at",
510
+ "s.source_version as source_version",
511
+ "s.source_fingerprint as source_fingerprint",
512
+ "s.source_completeness as source_completeness",
513
+ "s.last_attempted_at as last_attempted_at",
514
+ "s.last_error_code as last_error_code",
515
+ "s.indexed_at as indexed_at",
516
+ "s.created_at as source_created_at",
517
+ "s.updated_at as source_row_updated_at",
518
+ "u.id as occurrence_id",
519
+ "u.generation as generation",
520
+ "u.field_slug as field_slug",
521
+ "u.field_path as field_path",
522
+ "u.occurrence_index as occurrence_index",
523
+ "u.reference_type as reference_type",
524
+ "u.media_id as media_id",
525
+ "u.provider as provider",
526
+ "u.provider_asset_id as provider_asset_id",
527
+ "u.media_kind as media_kind",
528
+ "u.mime_type as mime_type",
529
+ "u.created_at as occurrence_created_at"
530
+ ];
531
+ function rowToSource(row) {
532
+ return {
533
+ sourceKey: row.source_key,
534
+ sourceType: row.source_type,
535
+ collectionSlug: row.collection_slug,
536
+ contentId: row.content_id,
537
+ sourceVariant: row.source_variant,
538
+ locale: row.locale,
539
+ translationGroup: row.translation_group,
540
+ contentSlug: row.content_slug,
541
+ contentTitle: row.content_title,
542
+ contentStatus: row.content_status,
543
+ contentScheduledAt: row.content_scheduled_at,
544
+ contentDeletedAt: row.content_deleted_at,
545
+ revisionId: row.revision_id,
546
+ currentGeneration: row.current_generation,
547
+ schemaVersion: Number(row.schema_version),
548
+ sourceUpdatedAt: row.source_updated_at,
549
+ sourceVersion: row.source_version === null ? null : Number(row.source_version),
550
+ sourceFingerprint: row.source_fingerprint,
551
+ sourceCompleteness: row.source_completeness,
552
+ lastAttemptedAt: row.last_attempted_at,
553
+ lastErrorCode: row.last_error_code,
554
+ indexedAt: row.indexed_at,
555
+ createdAt: row.created_at,
556
+ updatedAt: row.updated_at
557
+ };
558
+ }
559
+ function rowToOccurrence(row) {
560
+ return {
561
+ id: row.id,
562
+ sourceKey: row.source_key,
563
+ generation: row.generation,
564
+ fieldSlug: row.field_slug,
565
+ fieldPath: row.field_path,
566
+ occurrenceIndex: Number(row.occurrence_index),
567
+ referenceType: row.reference_type,
568
+ mediaId: row.media_id,
569
+ provider: row.provider,
570
+ providerAssetId: row.provider_asset_id,
571
+ mediaKind: row.media_kind,
572
+ mimeType: row.mime_type,
573
+ createdAt: row.created_at
574
+ };
575
+ }
576
+ function rowToUsageRecord(row) {
577
+ return {
578
+ source: rowToSource({
579
+ source_key: row.source_key,
580
+ source_type: row.source_type,
581
+ collection_slug: row.collection_slug,
582
+ content_id: row.content_id,
583
+ source_variant: row.source_variant,
584
+ locale: row.locale,
585
+ translation_group: row.translation_group,
586
+ content_slug: row.content_slug,
587
+ content_title: row.content_title,
588
+ content_status: row.content_status,
589
+ content_scheduled_at: row.content_scheduled_at,
590
+ content_deleted_at: row.content_deleted_at,
591
+ revision_id: row.revision_id,
592
+ current_generation: row.current_generation,
593
+ schema_version: row.schema_version,
594
+ source_updated_at: row.source_updated_at,
595
+ source_version: row.source_version,
596
+ source_fingerprint: row.source_fingerprint,
597
+ source_completeness: row.source_completeness,
598
+ last_attempted_at: row.last_attempted_at,
599
+ last_error_code: row.last_error_code,
600
+ indexed_at: row.indexed_at,
601
+ created_at: row.source_created_at,
602
+ updated_at: row.source_row_updated_at
603
+ }),
604
+ occurrence: rowToOccurrence({
605
+ id: row.occurrence_id,
606
+ source_key: row.source_key,
607
+ generation: row.generation,
608
+ field_slug: row.field_slug,
609
+ field_path: row.field_path,
610
+ occurrence_index: row.occurrence_index,
611
+ reference_type: row.reference_type,
612
+ media_id: row.media_id,
613
+ provider: row.provider,
614
+ provider_asset_id: row.provider_asset_id,
615
+ media_kind: row.media_kind,
616
+ mime_type: row.mime_type,
617
+ created_at: row.occurrence_created_at
618
+ })
619
+ };
620
+ }
621
+ function rowToIndexStatus(row) {
622
+ return {
623
+ adapterId: row.adapter_id,
624
+ scopeType: row.scope_type,
625
+ scopeKey: row.scope_key,
626
+ status: row.status,
627
+ schemaVersion: Number(row.schema_version),
628
+ startedAt: row.started_at,
629
+ completedAt: row.completed_at,
630
+ cursor: row.cursor,
631
+ indexedSourceCount: Number(row.indexed_source_count),
632
+ failedSourceCount: Number(row.failed_source_count),
633
+ lastErrorCode: row.last_error_code,
634
+ updatedAt: row.updated_at
635
+ };
636
+ }
637
+
638
+ //#endregion
639
+ //#region src/media/usage/content-fields.ts
640
+ var MediaUsageFieldDiscoveryError = class extends Error {
641
+ constructor(message, code) {
642
+ super(message);
643
+ this.code = code;
644
+ this.name = "MediaUsageFieldDiscoveryError";
645
+ }
646
+ };
647
+ const DISPLAY_FIELD_SLUGS = ["title", "name"];
648
+ const SUPPORTED_TOP_LEVEL_TYPES = [
649
+ "file",
650
+ "image",
651
+ "portableText"
652
+ ];
653
+ async function loadContentMediaUsageFields(db, collectionSlug) {
654
+ validateIdentifier(collectionSlug, "collection slug");
655
+ const rows = await db.selectFrom("_emdash_fields").innerJoin("_emdash_collections", "_emdash_collections.id", "_emdash_fields.collection_id").select([
656
+ "_emdash_fields.slug",
657
+ "_emdash_fields.type",
658
+ "_emdash_fields.validation"
659
+ ]).where("_emdash_collections.slug", "=", collectionSlug).execute();
660
+ const extractionFields = [];
661
+ const rowBySlug = /* @__PURE__ */ new Map();
662
+ for (const row of rows) {
663
+ rowBySlug.set(row.slug, row);
664
+ if (isSupportedTopLevelType(row.type)) {
665
+ validateIdentifier(row.slug, "media usage field slug");
666
+ extractionFields.push({
667
+ slug: row.slug,
668
+ type: row.type
669
+ });
670
+ continue;
671
+ }
672
+ if (row.type === "repeater") {
673
+ validateIdentifier(row.slug, "media usage field slug");
674
+ const subFields = normalizeRepeaterImageSubFields(row.validation);
675
+ if (subFields.length > 0) extractionFields.push({
676
+ slug: row.slug,
677
+ type: "repeater",
678
+ validation: { subFields }
679
+ });
680
+ }
681
+ }
682
+ extractionFields.sort((a, b) => a.slug.localeCompare(b.slug));
683
+ return {
684
+ extractionFields,
685
+ displayFieldSlugs: DISPLAY_FIELD_SLUGS.filter((slug) => {
686
+ if (!rowBySlug.has(slug)) return false;
687
+ validateIdentifier(slug, "media usage display field slug");
688
+ return true;
689
+ })
690
+ };
691
+ }
692
+ function normalizeRepeaterImageSubFields(rawValidation) {
693
+ const validation = parseValidation(rawValidation);
694
+ if (!isRecord$2(validation) || !Array.isArray(validation.subFields)) return [];
695
+ const subFields = [];
696
+ for (const subField of validation.subFields) {
697
+ if (!isRecord$2(subField) || subField.type !== "image") continue;
698
+ if (typeof subField.slug !== "string") continue;
699
+ validateIdentifier(subField.slug, "media usage repeater sub-field slug");
700
+ subFields.push({
701
+ slug: subField.slug,
702
+ type: "image"
703
+ });
704
+ }
705
+ return subFields.toSorted((a, b) => a.slug.localeCompare(b.slug));
706
+ }
707
+ function parseValidation(rawValidation) {
708
+ if (!rawValidation) return null;
709
+ try {
710
+ return JSON.parse(rawValidation);
711
+ } catch {
712
+ throw new MediaUsageFieldDiscoveryError("Repeater field validation must be valid JSON before media usage can be discovered", "INVALID_REPEATER_VALIDATION");
713
+ }
714
+ }
715
+ function isSupportedTopLevelType(value) {
716
+ return SUPPORTED_TOP_LEVEL_TYPES.includes(value);
717
+ }
718
+ function isRecord$2(value) {
719
+ return typeof value === "object" && value !== null && !Array.isArray(value);
720
+ }
721
+
722
+ //#endregion
723
+ //#region src/media/usage/extractor.ts
724
+ const URL_LIKE_RE = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
725
+ function extractMediaUsageOccurrences({ fields, data }) {
726
+ const occurrences = [];
727
+ const seen = /* @__PURE__ */ new Set();
728
+ for (const field of fields) {
729
+ const value = data[field.slug];
730
+ if (field.type === "image") {
731
+ addOccurrence(occurrences, seen, {
732
+ fieldSlug: field.slug,
733
+ fieldPath: field.slug,
734
+ referenceType: "image_field",
735
+ value,
736
+ fallbackKind: "image"
737
+ });
738
+ continue;
739
+ }
740
+ if (field.type === "file") {
741
+ addOccurrence(occurrences, seen, {
742
+ fieldSlug: field.slug,
743
+ fieldPath: field.slug,
744
+ referenceType: "file_field",
745
+ value,
746
+ fallbackKind: null
747
+ });
748
+ continue;
749
+ }
750
+ if (field.type === "repeater") {
751
+ extractRepeaterOccurrences(occurrences, seen, field.slug, value, field.validation?.subFields);
752
+ continue;
753
+ }
754
+ if (field.type === "portableText") extractPortableTextOccurrences(occurrences, seen, field.slug, value);
755
+ }
756
+ return occurrences;
757
+ }
758
+ function extractRepeaterOccurrences(occurrences, seen, fieldSlug, value, subFields) {
759
+ if (!Array.isArray(value) || !Array.isArray(subFields)) return;
760
+ for (const [itemIndex, item] of value.entries()) {
761
+ if (!isRecord$1(item)) continue;
762
+ for (const subField of subFields) {
763
+ if (subField.type !== "image") continue;
764
+ addOccurrence(occurrences, seen, {
765
+ fieldSlug,
766
+ fieldPath: `${fieldSlug}[${itemIndex}].${subField.slug}`,
767
+ referenceType: "image_field",
768
+ value: item[subField.slug],
769
+ fallbackKind: "image"
770
+ });
771
+ }
772
+ }
773
+ }
774
+ function extractPortableTextOccurrences(occurrences, seen, fieldSlug, value) {
775
+ if (!Array.isArray(value)) return;
776
+ for (const [blockIndex, block] of value.entries()) {
777
+ if (!isRecord$1(block) || block._type !== "image" || !isRecord$1(block.asset)) continue;
778
+ const provider = normalizeProvider(block.asset.provider);
779
+ const ref = readPortableTextAssetRef(block.asset, provider);
780
+ if (!ref) continue;
781
+ addRefOccurrence(occurrences, seen, {
782
+ fieldSlug,
783
+ fieldPath: `${fieldSlug}[${blockIndex}].asset.${ref.key}`,
784
+ referenceType: "portable_text_image",
785
+ ref: buildMediaRef({
786
+ id: ref.id,
787
+ provider,
788
+ mimeType: normalizeMimeValue(block.asset.mimeType),
789
+ fallbackKind: "image"
790
+ })
791
+ });
792
+ }
793
+ }
794
+ function addOccurrence(occurrences, seen, input) {
795
+ const ref = readMediaRef(input.value, input.fallbackKind);
796
+ if (!ref) return;
797
+ addRefOccurrence(occurrences, seen, {
798
+ fieldSlug: input.fieldSlug,
799
+ fieldPath: input.fieldPath,
800
+ referenceType: input.referenceType,
801
+ ref
802
+ });
803
+ }
804
+ function addRefOccurrence(occurrences, seen, input) {
805
+ if (!input.ref) return;
806
+ const occurrence = {
807
+ fieldSlug: input.fieldSlug,
808
+ fieldPath: input.fieldPath,
809
+ occurrenceIndex: 0,
810
+ referenceType: input.referenceType,
811
+ mediaId: input.ref.mediaId,
812
+ provider: input.ref.provider,
813
+ providerAssetId: input.ref.providerAssetId,
814
+ mediaKind: input.ref.mediaKind,
815
+ mimeType: input.ref.mimeType
816
+ };
817
+ const key = [
818
+ occurrence.fieldSlug,
819
+ occurrence.fieldPath,
820
+ occurrence.occurrenceIndex,
821
+ occurrence.referenceType,
822
+ occurrence.provider,
823
+ occurrence.providerAssetId,
824
+ occurrence.mediaId ?? ""
825
+ ].join("\0");
826
+ if (seen.has(key)) return;
827
+ seen.add(key);
828
+ occurrences.push(occurrence);
829
+ }
830
+ function readMediaRef(value, fallbackKind) {
831
+ if (typeof value === "string") {
832
+ const id = normalizeLocalMediaId(value);
833
+ return id ? buildMediaRef({
834
+ id,
835
+ provider: "local",
836
+ mimeType: null,
837
+ fallbackKind
838
+ }) : null;
839
+ }
840
+ if (!isRecord$1(value)) return null;
841
+ const provider = normalizeProvider(value.provider);
842
+ const id = provider === "local" ? normalizeLocalMediaId(value.id) : normalizeStableId(value.id);
843
+ if (!id) return null;
844
+ return buildMediaRef({
845
+ id,
846
+ provider,
847
+ mimeType: normalizeMimeValue(value.mimeType),
848
+ fallbackKind
849
+ });
850
+ }
851
+ function buildMediaRef(input) {
852
+ const provider = normalizeProvider(input.provider);
853
+ if (provider === "external") return null;
854
+ return {
855
+ mediaId: provider === "local" ? input.id : null,
856
+ provider,
857
+ providerAssetId: input.id,
858
+ mediaKind: mediaKindFromMime(input.mimeType) ?? input.fallbackKind,
859
+ mimeType: input.mimeType
860
+ };
861
+ }
862
+ function readPortableTextAssetRef(asset, provider) {
863
+ const normalizeId = provider === "local" ? normalizeLocalMediaId : normalizeStableId;
864
+ const ref = normalizeId(asset._ref);
865
+ if (ref) return {
866
+ key: "_ref",
867
+ id: ref
868
+ };
869
+ const id = normalizeId(asset.id);
870
+ if (id) return {
871
+ key: "id",
872
+ id
873
+ };
874
+ return null;
875
+ }
876
+ function normalizeProvider(value) {
877
+ return readString$1(value)?.trim() || "local";
878
+ }
879
+ function normalizeLocalMediaId(value) {
880
+ const id = normalizeStableId(value);
881
+ if (!id) return null;
882
+ return id.includes("/") ? null : id;
883
+ }
884
+ function normalizeStableId(value) {
885
+ if (typeof value !== "string") return null;
886
+ const trimmed = value.trim();
887
+ if (!trimmed) return null;
888
+ if (URL_LIKE_RE.test(trimmed)) return null;
889
+ if (trimmed.startsWith(INTERNAL_MEDIA_PREFIX)) return null;
890
+ return trimmed;
891
+ }
892
+ function normalizeMimeValue(value) {
893
+ if (typeof value !== "string") return null;
894
+ const normalized = normalizeMime(value);
895
+ return normalized.includes("/") ? normalized : null;
896
+ }
897
+ function mediaKindFromMime(mimeType) {
898
+ if (!mimeType) return null;
899
+ if (mimeType.startsWith("image/")) return "image";
900
+ if (mimeType.startsWith("video/")) return "video";
901
+ if (mimeType.startsWith("audio/")) return "audio";
902
+ if (mimeType.startsWith("font/") || mimeType.startsWith("application/font-")) return "font";
903
+ if (mimeType.startsWith("text/")) return "text";
904
+ if (isDocumentMime(mimeType)) return "document";
905
+ if (isArchiveMime(mimeType)) return "archive";
906
+ return "other";
907
+ }
908
+ function isDocumentMime(mimeType) {
909
+ return mimeType === "application/pdf" || mimeType === "application/msword" || mimeType === "application/rtf" || mimeType === "application/vnd.ms-excel" || mimeType === "application/vnd.ms-powerpoint" || mimeType.startsWith("application/vnd.openxmlformats-officedocument.");
910
+ }
911
+ function isArchiveMime(mimeType) {
912
+ return mimeType === "application/zip" || mimeType === "application/gzip" || mimeType === "application/x-tar" || mimeType === "application/x-7z-compressed" || mimeType === "application/x-rar-compressed" || mimeType === "application/vnd.rar";
913
+ }
914
+ function readString$1(value) {
915
+ return typeof value === "string" ? value : null;
916
+ }
917
+ function isRecord$1(value) {
918
+ return typeof value === "object" && value !== null && !Array.isArray(value);
919
+ }
920
+
921
+ //#endregion
922
+ //#region src/media/usage/source-key.ts
923
+ const MEDIA_USAGE_CONTENT_SOURCE_VARIANTS = ["columns", "draft_overlay"];
924
+ function buildContentMediaUsageSourceKey(input) {
925
+ return `content:${input.collectionSlug}:${input.contentId}:${input.sourceVariant}`;
926
+ }
927
+
928
+ //#endregion
929
+ //#region src/media/usage/content-snapshots.ts
930
+ const CONTENT_SOURCE_SCHEMA_VERSION = 1;
931
+ const CONTENT_SYSTEM_COLUMNS = [
932
+ "id",
933
+ "slug",
934
+ "status",
935
+ "created_at",
936
+ "updated_at",
937
+ "published_at",
938
+ "scheduled_at",
939
+ "deleted_at",
940
+ "version",
941
+ "live_revision_id",
942
+ "draft_revision_id",
943
+ "locale",
944
+ "translation_group"
945
+ ];
946
+ async function loadContentMediaUsageSnapshots(db, collectionSlug, contentId, fieldDiscovery) {
947
+ validateIdentifier(collectionSlug, "collection slug");
948
+ const discovery = fieldDiscovery ?? await loadContentMediaUsageFields(db, collectionSlug);
949
+ const row = await loadContentRow(db, collectionSlug, contentId, [...discovery.extractionFields.map((field) => field.slug), ...discovery.displayFieldSlugs]);
950
+ if (!row) return {
951
+ success: false,
952
+ error: "CONTENT_NOT_FOUND"
953
+ };
954
+ const columnsData = projectData(row, discovery.extractionFields.map((field) => field.slug));
955
+ const displayData = projectRawData(row, discovery.displayFieldSlugs);
956
+ const occurrences = extractMediaUsageOccurrences({
957
+ fields: discovery.extractionFields,
958
+ data: columnsData
959
+ });
960
+ const columnsRevisionId = readNullableString(row.live_revision_id);
961
+ const snapshots = [{
962
+ source: buildContentSource({
963
+ collectionSlug,
964
+ row,
965
+ displayData,
966
+ sourceVariant: "columns",
967
+ revisionId: columnsRevisionId,
968
+ sourceFingerprint: await buildSourceFingerprint({
969
+ collectionSlug,
970
+ sourceVariant: "columns",
971
+ revisionId: columnsRevisionId,
972
+ fields: discovery.extractionFields,
973
+ data: columnsData
974
+ })
975
+ }),
976
+ occurrences,
977
+ fields: discovery.extractionFields
978
+ }];
979
+ const draftRevisionId = readNullableString(row.draft_revision_id);
980
+ if (draftRevisionId) {
981
+ const attemptedDraftSource = buildContentSource({
982
+ collectionSlug,
983
+ row,
984
+ displayData,
985
+ sourceVariant: "draft_overlay",
986
+ revisionId: draftRevisionId
987
+ });
988
+ const revisionResult = await loadRevisionRow(db, draftRevisionId);
989
+ if (!revisionResult) return {
990
+ success: false,
991
+ error: "DRAFT_REVISION_NOT_FOUND",
992
+ source: attemptedDraftSource
993
+ };
994
+ if (!revisionResult.success) return {
995
+ success: false,
996
+ error: "DRAFT_REVISION_INVALID",
997
+ source: attemptedDraftSource
998
+ };
999
+ const revision = revisionResult.revision;
1000
+ if (revision.collection !== collectionSlug || revision.entryId !== row.id) return {
1001
+ success: false,
1002
+ error: "DRAFT_REVISION_MISMATCH",
1003
+ source: attemptedDraftSource
1004
+ };
1005
+ const revisionData = stripRevisionMetadata(revision.data);
1006
+ const draftOverlayData = {
1007
+ ...columnsData,
1008
+ ...revisionData
1009
+ };
1010
+ const draftDisplayData = {
1011
+ ...displayData,
1012
+ ...projectPresentData(revisionData, discovery.displayFieldSlugs)
1013
+ };
1014
+ const draftContentSlug = readNullableString(revision.data._slug) ?? readNullableString(row.slug);
1015
+ const draftFingerprint = await buildSourceFingerprint({
1016
+ collectionSlug,
1017
+ sourceVariant: "draft_overlay",
1018
+ revisionId: draftRevisionId,
1019
+ fields: discovery.extractionFields,
1020
+ data: draftOverlayData
1021
+ });
1022
+ snapshots.push({
1023
+ source: buildContentSource({
1024
+ collectionSlug,
1025
+ row,
1026
+ displayData: draftDisplayData,
1027
+ sourceVariant: "draft_overlay",
1028
+ revisionId: draftRevisionId,
1029
+ contentSlug: draftContentSlug,
1030
+ sourceFingerprint: draftFingerprint
1031
+ }),
1032
+ occurrences: extractMediaUsageOccurrences({
1033
+ fields: discovery.extractionFields,
1034
+ data: draftOverlayData
1035
+ }),
1036
+ fields: discovery.extractionFields
1037
+ });
1038
+ }
1039
+ return {
1040
+ success: true,
1041
+ snapshots
1042
+ };
1043
+ }
1044
+ async function loadContentRow(db, collectionSlug, contentId, fieldSlugs) {
1045
+ const tableName = getContentTableName(collectionSlug);
1046
+ const columnRefs = uniqueColumns([...CONTENT_SYSTEM_COLUMNS, ...fieldSlugs]).map((column) => sql.ref(column));
1047
+ return (await sql`
1048
+ SELECT ${sql.join(columnRefs, sql`, `)}
1049
+ FROM ${sql.ref(tableName)}
1050
+ WHERE id = ${contentId}
1051
+ LIMIT 1
1052
+ `.execute(db)).rows[0] ?? null;
1053
+ }
1054
+ async function loadRevisionRow(db, revisionId) {
1055
+ const row = await db.selectFrom("revisions").select([
1056
+ "id",
1057
+ "collection",
1058
+ "entry_id",
1059
+ "data"
1060
+ ]).where("id", "=", revisionId).executeTakeFirst();
1061
+ if (!row) return null;
1062
+ const data = parseRevisionData(row.data);
1063
+ if (!data) return { success: false };
1064
+ return {
1065
+ success: true,
1066
+ revision: {
1067
+ id: row.id,
1068
+ collection: row.collection,
1069
+ entryId: row.entry_id,
1070
+ data
1071
+ }
1072
+ };
1073
+ }
1074
+ function buildContentSource(input) {
1075
+ const { collectionSlug, row, displayData, sourceVariant, revisionId } = input;
1076
+ const contentId = readString(row.id) ?? "";
1077
+ const contentSlug = input.contentSlug ?? readNullableString(row.slug);
1078
+ const source = {
1079
+ sourceKey: buildContentMediaUsageSourceKey({
1080
+ collectionSlug,
1081
+ contentId,
1082
+ sourceVariant
1083
+ }),
1084
+ sourceType: "content",
1085
+ collectionSlug,
1086
+ contentId,
1087
+ sourceVariant,
1088
+ locale: readNullableString(row.locale),
1089
+ translationGroup: readNullableString(row.translation_group),
1090
+ contentSlug,
1091
+ contentTitle: deriveContentTitle(displayData, contentSlug, contentId),
1092
+ contentStatus: readNullableString(row.status),
1093
+ contentScheduledAt: readNullableString(row.scheduled_at),
1094
+ contentDeletedAt: readNullableString(row.deleted_at),
1095
+ revisionId,
1096
+ schemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,
1097
+ sourceUpdatedAt: readNullableString(row.updated_at),
1098
+ sourceVersion: readNumber(row.version)
1099
+ };
1100
+ if (input.sourceFingerprint !== void 0) source.sourceFingerprint = input.sourceFingerprint;
1101
+ return source;
1102
+ }
1103
+ async function buildSourceFingerprint(input) {
1104
+ return hashString(canonicalJson({
1105
+ schemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,
1106
+ collectionSlug: input.collectionSlug,
1107
+ sourceVariant: input.sourceVariant,
1108
+ fields: normalizeFingerprintFields(input.fields),
1109
+ values: projectFingerprintData(input.data, input.fields),
1110
+ revisionId: input.sourceVariant === "draft_overlay" ? input.revisionId : null
1111
+ }));
1112
+ }
1113
+ function normalizeFingerprintFields(fields) {
1114
+ return fields.map((field) => {
1115
+ if (field.type !== "repeater") return {
1116
+ slug: field.slug,
1117
+ type: field.type
1118
+ };
1119
+ return {
1120
+ slug: field.slug,
1121
+ type: field.type,
1122
+ subFields: (field.validation?.subFields ?? []).map((subField) => ({
1123
+ slug: subField.slug,
1124
+ type: subField.type
1125
+ })).toSorted((a, b) => a.slug.localeCompare(b.slug))
1126
+ };
1127
+ }).toSorted((a, b) => String(a.slug).localeCompare(String(b.slug)));
1128
+ }
1129
+ function projectFingerprintData(data, fields) {
1130
+ const projected = {};
1131
+ for (const field of fields) projected[field.slug] = Object.hasOwn(data, field.slug) ? data[field.slug] : null;
1132
+ return projected;
1133
+ }
1134
+ function canonicalJson(value) {
1135
+ return JSON.stringify(canonicalize(value));
1136
+ }
1137
+ function canonicalize(value) {
1138
+ if (value === void 0) return null;
1139
+ if (typeof value === "bigint") return value.toString();
1140
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
1141
+ if (Array.isArray(value)) return value.map((item) => canonicalize(item));
1142
+ if (!isRecord(value)) return value;
1143
+ const canonical = {};
1144
+ for (const key of Object.keys(value).toSorted()) canonical[key] = canonicalize(value[key]);
1145
+ return canonical;
1146
+ }
1147
+ function projectData(row, fieldSlugs) {
1148
+ const data = {};
1149
+ for (const fieldSlug of fieldSlugs) data[fieldSlug] = deserializeValue(row[fieldSlug] ?? null);
1150
+ return data;
1151
+ }
1152
+ function projectRawData(row, fieldSlugs) {
1153
+ const data = {};
1154
+ for (const fieldSlug of fieldSlugs) data[fieldSlug] = row[fieldSlug] ?? null;
1155
+ return data;
1156
+ }
1157
+ function projectPresentData(row, fieldSlugs) {
1158
+ const data = {};
1159
+ for (const fieldSlug of fieldSlugs) if (Object.hasOwn(row, fieldSlug)) data[fieldSlug] = row[fieldSlug];
1160
+ return data;
1161
+ }
1162
+ function uniqueColumns(columns) {
1163
+ const unique = [...new Set(columns)];
1164
+ for (const column of unique) validateIdentifier(column, "content media usage column");
1165
+ return unique;
1166
+ }
1167
+ function getContentTableName(collectionSlug) {
1168
+ validateIdentifier(collectionSlug, "collection slug");
1169
+ return `ec_${collectionSlug}`;
1170
+ }
1171
+ function deserializeValue(value) {
1172
+ if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) try {
1173
+ return JSON.parse(value);
1174
+ } catch {
1175
+ return value;
1176
+ }
1177
+ return value;
1178
+ }
1179
+ function parseRevisionData(value) {
1180
+ if (typeof value === "string") try {
1181
+ const parsed = JSON.parse(value);
1182
+ return isRecord(parsed) ? parsed : null;
1183
+ } catch {
1184
+ return null;
1185
+ }
1186
+ return isRecord(value) ? value : null;
1187
+ }
1188
+ function stripRevisionMetadata(data) {
1189
+ const stripped = {};
1190
+ for (const [key, value] of Object.entries(data)) if (!key.startsWith("_")) stripped[key] = value;
1191
+ return stripped;
1192
+ }
1193
+ function deriveContentTitle(displayData, contentSlug, contentId) {
1194
+ for (const fieldSlug of ["title", "name"]) {
1195
+ const value = displayData[fieldSlug];
1196
+ if (typeof value === "string" && value.trim()) return value;
1197
+ }
1198
+ return contentSlug ?? contentId;
1199
+ }
1200
+ function readString(value) {
1201
+ return typeof value === "string" ? value : null;
1202
+ }
1203
+ function readNullableString(value) {
1204
+ return value === null || value === void 0 ? null : readString(value);
1205
+ }
1206
+ function readNumber(value) {
1207
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1208
+ if (typeof value === "bigint") return Number(value);
1209
+ if (typeof value === "string" && value) {
1210
+ const parsed = Number(value);
1211
+ return Number.isFinite(parsed) ? parsed : null;
1212
+ }
1213
+ return null;
1214
+ }
1215
+ function isRecord(value) {
1216
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1217
+ }
1218
+
1219
+ //#endregion
1220
+ //#region src/media/usage/content-refresh.ts
1221
+ const CONTENT_MEDIA_USAGE_ADAPTER_ID = "content-media";
1222
+ const CONTENT_MEDIA_USAGE_COLLECTION_SCOPE = "collection";
1223
+ const CONTENT_USAGE_LOCKS_KEY = Symbol.for("emdash.mediaUsage.contentLocks");
1224
+ const CONTENT_USAGE_COLLECTION_LOCKS_KEY = Symbol.for("emdash.mediaUsage.collectionLocks");
1225
+ const CONTENT_USAGE_REFRESH_MAX_ATTEMPTS = 2;
1226
+ const ZERO_RESULT = {
1227
+ success: true,
1228
+ refreshedSourceCount: 0,
1229
+ deletedSourceCount: 0,
1230
+ failedSourceCount: 0
1231
+ };
1232
+ async function refreshContentMediaUsage(db, collectionSlug, contentId) {
1233
+ validateIdentifier(collectionSlug, "collection slug");
1234
+ return withContentUsageCollectionLock(collectionSlug, () => withContentUsageLock(collectionSlug, contentId, () => refreshContentMediaUsageUnlocked(db, collectionSlug, contentId)));
1235
+ }
1236
+ async function refreshContentMediaUsageUnlocked(db, collectionSlug, contentId) {
1237
+ try {
1238
+ let conflictResult = null;
1239
+ for (let attempt = 0; attempt < CONTENT_USAGE_REFRESH_MAX_ATTEMPTS; attempt++) {
1240
+ const result = await refreshContentMediaUsageAttempt(db, collectionSlug, contentId);
1241
+ if (result.errorCode !== "CONTENT_USAGE_GENERATION_CONFLICT") return result;
1242
+ conflictResult = result;
1243
+ }
1244
+ return markGenerationConflict(db, collectionSlug, {
1245
+ refreshedSourceCount: conflictResult?.refreshedSourceCount ?? 0,
1246
+ deletedSourceCount: conflictResult?.deletedSourceCount ?? 0
1247
+ });
1248
+ } catch (error) {
1249
+ console.error(`[media-usage] Failed to refresh ${collectionSlug}/${contentId}:`, error);
1250
+ await markContentMediaUsageCollectionStaleSafely(db, collectionSlug, "CONTENT_USAGE_REFRESH_ERROR");
1251
+ return {
1252
+ success: false,
1253
+ refreshedSourceCount: 0,
1254
+ deletedSourceCount: 0,
1255
+ failedSourceCount: 0,
1256
+ errorCode: "CONTENT_USAGE_REFRESH_ERROR"
1257
+ };
1258
+ }
1259
+ }
1260
+ async function refreshContentMediaUsageAttempt(db, collectionSlug, contentId) {
1261
+ const repo = new MediaUsageRepository(db);
1262
+ const observedGenerations = await loadObservedContentSourceGenerations(db, collectionSlug, contentId);
1263
+ const snapshotsResult = await loadContentMediaUsageSnapshots(db, collectionSlug, contentId);
1264
+ if (!snapshotsResult.success) return markSnapshotFailure(db, collectionSlug, snapshotsResult);
1265
+ if (!await contentCollectionExists(db, collectionSlug)) {
1266
+ const deletedSourceCount = await repo.deleteContentSources(collectionSlug, contentId);
1267
+ return {
1268
+ ...ZERO_RESULT,
1269
+ deletedSourceCount
1270
+ };
1271
+ }
1272
+ let refreshedSourceCount = 0;
1273
+ for (const snapshot of snapshotsResult.snapshots) {
1274
+ if (!(await repo.replaceSourceIfCurrent(snapshot.source, snapshot.occurrences, observedGenerations.get(snapshot.source.sourceKey) ?? null)).replaced) return generationConflictResult({
1275
+ refreshedSourceCount,
1276
+ deletedSourceCount: 0
1277
+ });
1278
+ refreshedSourceCount++;
1279
+ }
1280
+ if (!await contentCollectionExists(db, collectionSlug)) {
1281
+ const deletedSourceCount = await repo.deleteContentSources(collectionSlug, contentId);
1282
+ return {
1283
+ ...ZERO_RESULT,
1284
+ deletedSourceCount
1285
+ };
1286
+ }
1287
+ const expectedSourceKeys = new Set(snapshotsResult.snapshots.map((snapshot) => snapshot.source.sourceKey));
1288
+ const absentSourceKeys = MEDIA_USAGE_CONTENT_SOURCE_VARIANTS.map((sourceVariant) => buildContentMediaUsageSourceKey({
1289
+ collectionSlug,
1290
+ contentId,
1291
+ sourceVariant
1292
+ })).filter((sourceKey) => !expectedSourceKeys.has(sourceKey));
1293
+ let deletedSourceCount = 0;
1294
+ for (const sourceKey of absentSourceKeys) {
1295
+ const expectedGeneration = observedGenerations.get(sourceKey) ?? null;
1296
+ if (expectedGeneration === null) continue;
1297
+ const result = await repo.deleteSourceIfCurrent(sourceKey, expectedGeneration);
1298
+ if (result.deleted) {
1299
+ deletedSourceCount++;
1300
+ continue;
1301
+ }
1302
+ if (result.source) return generationConflictResult({
1303
+ refreshedSourceCount,
1304
+ deletedSourceCount
1305
+ });
1306
+ }
1307
+ return {
1308
+ success: true,
1309
+ refreshedSourceCount,
1310
+ deletedSourceCount,
1311
+ failedSourceCount: 0
1312
+ };
1313
+ }
1314
+ async function loadObservedContentSourceGenerations(db, collectionSlug, contentId) {
1315
+ const generations = /* @__PURE__ */ new Map();
1316
+ const sourceKeys = MEDIA_USAGE_CONTENT_SOURCE_VARIANTS.map((sourceVariant) => buildContentMediaUsageSourceKey({
1317
+ collectionSlug,
1318
+ contentId,
1319
+ sourceVariant
1320
+ }));
1321
+ for (const sourceKey of sourceKeys) generations.set(sourceKey, null);
1322
+ const rows = await db.selectFrom("_emdash_media_usage_sources").select(["source_key", "current_generation"]).where("source_key", "in", sourceKeys).execute();
1323
+ for (const row of rows) generations.set(row.source_key, row.current_generation);
1324
+ return generations;
1325
+ }
1326
+ async function markGenerationConflict(db, collectionSlug, counts) {
1327
+ await markContentMediaUsageCollectionStaleSafely(db, collectionSlug, "CONTENT_USAGE_GENERATION_CONFLICT");
1328
+ return {
1329
+ success: false,
1330
+ refreshedSourceCount: counts.refreshedSourceCount,
1331
+ deletedSourceCount: counts.deletedSourceCount,
1332
+ failedSourceCount: 0,
1333
+ errorCode: "CONTENT_USAGE_GENERATION_CONFLICT"
1334
+ };
1335
+ }
1336
+ function generationConflictResult(counts) {
1337
+ return {
1338
+ success: false,
1339
+ refreshedSourceCount: counts.refreshedSourceCount,
1340
+ deletedSourceCount: counts.deletedSourceCount,
1341
+ failedSourceCount: 0,
1342
+ errorCode: "CONTENT_USAGE_GENERATION_CONFLICT"
1343
+ };
1344
+ }
1345
+ async function contentCollectionExists(db, collectionSlug) {
1346
+ return await db.selectFrom("_emdash_collections").select("id").where("slug", "=", collectionSlug).executeTakeFirst() !== void 0;
1347
+ }
1348
+ async function deleteContentMediaUsage(db, collectionSlug, contentId) {
1349
+ validateIdentifier(collectionSlug, "collection slug");
1350
+ return withContentUsageCollectionLock(collectionSlug, () => withContentUsageLock(collectionSlug, contentId, () => deleteContentMediaUsageUnlocked(db, collectionSlug, contentId)));
1351
+ }
1352
+ async function deleteContentMediaUsageUnlocked(db, collectionSlug, contentId) {
1353
+ try {
1354
+ const deletedSourceCount = await new MediaUsageRepository(db).deleteContentSources(collectionSlug, contentId);
1355
+ return {
1356
+ ...ZERO_RESULT,
1357
+ deletedSourceCount
1358
+ };
1359
+ } catch (error) {
1360
+ console.error(`[media-usage] Failed to delete usage for ${collectionSlug}/${contentId}:`, error);
1361
+ await markContentMediaUsageCollectionStaleSafely(db, collectionSlug, "CONTENT_USAGE_DELETE_ERROR");
1362
+ return {
1363
+ success: false,
1364
+ refreshedSourceCount: 0,
1365
+ deletedSourceCount: 0,
1366
+ failedSourceCount: 0,
1367
+ errorCode: "CONTENT_USAGE_DELETE_ERROR"
1368
+ };
1369
+ }
1370
+ }
1371
+ async function deleteContentMediaUsageCollection(db, collectionSlug) {
1372
+ validateIdentifier(collectionSlug, "collection slug");
1373
+ return withContentUsageCollectionLock(collectionSlug, () => deleteContentMediaUsageCollectionUnlocked(db, collectionSlug));
1374
+ }
1375
+ async function deleteContentMediaUsageCollectionUnlocked(db, collectionSlug) {
1376
+ try {
1377
+ const repo = new MediaUsageRepository(db);
1378
+ const deletedSourceCount = await repo.deleteCollectionSources(collectionSlug);
1379
+ await repo.deleteIndexStatus({
1380
+ adapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,
1381
+ scopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,
1382
+ scopeKey: collectionSlug
1383
+ });
1384
+ return {
1385
+ ...ZERO_RESULT,
1386
+ deletedSourceCount
1387
+ };
1388
+ } catch (error) {
1389
+ console.error(`[media-usage] Failed to delete usage for collection ${collectionSlug}:`, error);
1390
+ try {
1391
+ await new MediaUsageRepository(db).deleteIndexStatus({
1392
+ adapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,
1393
+ scopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,
1394
+ scopeKey: collectionSlug
1395
+ });
1396
+ } catch (statusError) {
1397
+ console.error(`[media-usage] Failed to clear usage status for deleted collection ${collectionSlug}:`, statusError);
1398
+ }
1399
+ return {
1400
+ success: false,
1401
+ refreshedSourceCount: 0,
1402
+ deletedSourceCount: 0,
1403
+ failedSourceCount: 0,
1404
+ errorCode: "CONTENT_USAGE_DELETE_ERROR"
1405
+ };
1406
+ }
1407
+ }
1408
+ async function refreshContentMediaUsageAfterWrite(db, collectionSlug, contentId) {
1409
+ const result = await refreshContentMediaUsage(db, collectionSlug, contentId);
1410
+ if (!result.success) console.error(`[media-usage] Usage refresh for ${collectionSlug}/${contentId} finished with ${result.errorCode}`);
1411
+ }
1412
+ async function markContentMediaUsageCollectionStale(db, collectionSlug, lastErrorCode) {
1413
+ validateIdentifier(collectionSlug, "collection slug");
1414
+ const repo = new MediaUsageRepository(db);
1415
+ const identity = {
1416
+ adapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,
1417
+ scopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,
1418
+ scopeKey: collectionSlug
1419
+ };
1420
+ const existing = await repo.findIndexStatus(identity);
1421
+ await repo.upsertIndexStatus({
1422
+ ...identity,
1423
+ status: "stale",
1424
+ schemaVersion: existing?.schemaVersion ?? CONTENT_SOURCE_SCHEMA_VERSION,
1425
+ startedAt: existing?.startedAt ?? null,
1426
+ completedAt: existing?.completedAt ?? null,
1427
+ cursor: existing?.cursor ?? null,
1428
+ indexedSourceCount: existing?.indexedSourceCount ?? 0,
1429
+ failedSourceCount: existing?.failedSourceCount ?? 0,
1430
+ lastErrorCode
1431
+ });
1432
+ }
1433
+ async function findNonTranslatableSiblingContentIds(db, collectionSlug, updatedContentId, translationGroup, updatedData) {
1434
+ if (!isI18nEnabled() || !updatedData || !translationGroup) return [];
1435
+ validateIdentifier(collectionSlug, "collection slug");
1436
+ const collection = await db.selectFrom("_emdash_collections").select("id").where("slug", "=", collectionSlug).executeTakeFirst();
1437
+ if (!collection) return [];
1438
+ const touchedNonTranslatableSlugs = (await db.selectFrom("_emdash_fields").select("slug").where("collection_id", "=", collection.id).where("translatable", "=", 0).execute()).filter((field) => field.slug in updatedData).map((field) => field.slug);
1439
+ if (touchedNonTranslatableSlugs.length === 0) return [];
1440
+ const usageFields = await loadContentMediaUsageFields(db, collectionSlug);
1441
+ const usageRelevantSlugs = new Set([...usageFields.extractionFields.map((field) => field.slug), ...usageFields.displayFieldSlugs]);
1442
+ if (!touchedNonTranslatableSlugs.some((slug) => usageRelevantSlugs.has(slug))) return [];
1443
+ const tableName = `ec_${collectionSlug}`;
1444
+ return (await sql`
1445
+ SELECT id
1446
+ FROM ${sql.ref(tableName)}
1447
+ WHERE translation_group = ${translationGroup}
1448
+ AND id != ${updatedContentId}
1449
+ ORDER BY id ASC
1450
+ `.execute(db)).rows.map((row) => row.id);
1451
+ }
1452
+ async function markSnapshotFailure(db, collectionSlug, result) {
1453
+ const repo = new MediaUsageRepository(db);
1454
+ if (result.source) await repo.markSourceAttempted({
1455
+ ...result.source,
1456
+ sourceCompleteness: "failed",
1457
+ lastErrorCode: result.error
1458
+ });
1459
+ await markContentMediaUsageCollectionStale(db, collectionSlug, result.error);
1460
+ return {
1461
+ success: false,
1462
+ refreshedSourceCount: 0,
1463
+ deletedSourceCount: 0,
1464
+ failedSourceCount: result.source ? 1 : 0,
1465
+ errorCode: result.error
1466
+ };
1467
+ }
1468
+ async function markContentMediaUsageCollectionStaleSafely(db, collectionSlug, lastErrorCode) {
1469
+ try {
1470
+ await markContentMediaUsageCollectionStale(db, collectionSlug, lastErrorCode);
1471
+ return true;
1472
+ } catch (error) {
1473
+ console.error(`[media-usage] Failed to mark ${collectionSlug} stale:`, error);
1474
+ return false;
1475
+ }
1476
+ }
1477
+ async function withContentUsageLock(collectionSlug, contentId, fn) {
1478
+ const locks = getContentUsageLocks();
1479
+ const lockKey = `${collectionSlug}\0${contentId}`;
1480
+ const previous = locks.get(lockKey) ?? Promise.resolve();
1481
+ let releaseCurrent;
1482
+ const current = new Promise((resolve) => {
1483
+ releaseCurrent = resolve;
1484
+ });
1485
+ const next = previous.catch(() => {}).then(() => current);
1486
+ locks.set(lockKey, next);
1487
+ try {
1488
+ await previous.catch(() => {});
1489
+ return await fn();
1490
+ } finally {
1491
+ releaseCurrent();
1492
+ if (locks.get(lockKey) === next) locks.delete(lockKey);
1493
+ }
1494
+ }
1495
+ async function withContentUsageCollectionLock(collectionSlug, fn) {
1496
+ const locks = getContentUsageCollectionLocks();
1497
+ const previous = locks.get(collectionSlug) ?? Promise.resolve();
1498
+ let releaseCurrent;
1499
+ const current = new Promise((resolve) => {
1500
+ releaseCurrent = resolve;
1501
+ });
1502
+ const next = previous.catch(() => {}).then(() => current);
1503
+ locks.set(collectionSlug, next);
1504
+ try {
1505
+ await previous.catch(() => {});
1506
+ return await fn();
1507
+ } finally {
1508
+ releaseCurrent();
1509
+ if (locks.get(collectionSlug) === next) locks.delete(collectionSlug);
1510
+ }
1511
+ }
1512
+ function getContentUsageLocks() {
1513
+ const global = globalThis;
1514
+ const existing = global[CONTENT_USAGE_LOCKS_KEY];
1515
+ if (existing instanceof Map) return existing;
1516
+ const locks = /* @__PURE__ */ new Map();
1517
+ global[CONTENT_USAGE_LOCKS_KEY] = locks;
1518
+ return locks;
1519
+ }
1520
+ function getContentUsageCollectionLocks() {
1521
+ const global = globalThis;
1522
+ const existing = global[CONTENT_USAGE_COLLECTION_LOCKS_KEY];
1523
+ if (existing instanceof Map) return existing;
1524
+ const locks = /* @__PURE__ */ new Map();
1525
+ global[CONTENT_USAGE_COLLECTION_LOCKS_KEY] = locks;
1526
+ return locks;
1527
+ }
1528
+
1529
+ //#endregion
1530
+ export { markContentMediaUsageCollectionStaleSafely as a, markContentMediaUsageCollectionStale as i, deleteContentMediaUsageCollection as n, refreshContentMediaUsageAfterWrite as o, findNonTranslatableSiblingContentIds as r, deleteContentMediaUsage as t };
1531
+ //# sourceMappingURL=content-refresh-CA4uKwzH.mjs.map