emdash 0.25.1 → 0.26.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 (346) hide show
  1. package/dist/api/route-utils.d.mts +3 -3
  2. package/dist/api/route-utils.mjs +11 -11
  3. package/dist/{api-D2_sbavn.mjs → api-BMfveMoB.mjs} +15 -15
  4. package/dist/{api-D2_sbavn.mjs.map → api-BMfveMoB.mjs.map} +1 -1
  5. package/dist/{apply-Bk2mAuLn.mjs → apply--IVNSdXn.mjs} +18 -16
  6. package/dist/apply--IVNSdXn.mjs.map +1 -0
  7. package/dist/astro/index.d.mts +7 -7
  8. package/dist/astro/index.mjs +2 -1
  9. package/dist/astro/index.mjs.map +1 -1
  10. package/dist/astro/middleware/auth.d.mts +7 -7
  11. package/dist/astro/middleware/auth.mjs +2 -2
  12. package/dist/astro/middleware/redirect.mjs +4 -4
  13. package/dist/astro/middleware.d.mts +1 -1
  14. package/dist/astro/middleware.mjs +85 -85
  15. package/dist/astro/middleware.mjs.map +1 -1
  16. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +3 -3
  17. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +3 -3
  18. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
  19. package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
  20. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +3 -3
  21. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +4 -4
  22. package/dist/astro/routes/api/admin/byline-fields/index.mjs +4 -4
  23. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +4 -4
  24. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +10 -10
  25. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +10 -10
  26. package/dist/astro/routes/api/admin/bylines/index.mjs +10 -10
  27. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +8 -8
  28. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
  29. package/dist/astro/routes/api/admin/comments/bulk.mjs +7 -7
  30. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
  31. package/dist/astro/routes/api/admin/comments/index.mjs +7 -7
  32. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  33. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  34. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
  35. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
  36. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +28 -28
  37. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +28 -28
  38. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +28 -28
  39. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +28 -28
  40. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +28 -28
  41. package/dist/astro/routes/api/admin/plugins/index.mjs +28 -28
  42. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  43. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +28 -28
  44. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +28 -28
  45. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +28 -28
  46. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +28 -28
  47. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +29 -29
  48. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +28 -28
  49. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +29 -29
  50. package/dist/astro/routes/api/admin/plugins/updates.mjs +28 -28
  51. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +28 -28
  52. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  53. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +28 -28
  54. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  55. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  56. package/dist/astro/routes/api/admin/users/_id_/index.mjs +3 -3
  57. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
  58. package/dist/astro/routes/api/admin/users/index.mjs +3 -3
  59. package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
  60. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  61. package/dist/astro/routes/api/auth/invite/complete.mjs +3 -3
  62. package/dist/astro/routes/api/auth/invite/index.mjs +3 -3
  63. package/dist/astro/routes/api/auth/invite/register-options.mjs +3 -3
  64. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  65. package/dist/astro/routes/api/auth/magic-link/send.mjs +4 -4
  66. package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
  67. package/dist/astro/routes/api/auth/me.mjs +4 -4
  68. package/dist/astro/routes/api/auth/passkey/_id_.mjs +3 -3
  69. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  70. package/dist/astro/routes/api/auth/passkey/options.mjs +4 -4
  71. package/dist/astro/routes/api/auth/passkey/register/options.mjs +3 -3
  72. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +3 -3
  73. package/dist/astro/routes/api/auth/passkey/verify.mjs +3 -3
  74. package/dist/astro/routes/api/auth/signup/complete.mjs +3 -3
  75. package/dist/astro/routes/api/auth/signup/request.mjs +4 -4
  76. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  77. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +7 -7
  78. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +7 -7
  79. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  80. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  81. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  82. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  83. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +4 -4
  84. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +4 -4
  85. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  86. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  87. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +4 -4
  88. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +9 -9
  89. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  90. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  91. package/dist/astro/routes/api/content/_collection_/_id_.mjs +4 -4
  92. package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
  93. package/dist/astro/routes/api/content/_collection_/index.mjs +4 -4
  94. package/dist/astro/routes/api/content/_collection_/trash.mjs +4 -4
  95. package/dist/astro/routes/api/dashboard.mjs +8 -8
  96. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  97. package/dist/astro/routes/api/import/probe.d.mts +2 -2
  98. package/dist/astro/routes/api/import/probe.mjs +4 -4
  99. package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
  100. package/dist/astro/routes/api/import/wordpress/execute.d.mts +7 -7
  101. package/dist/astro/routes/api/import/wordpress/execute.mjs +9 -9
  102. package/dist/astro/routes/api/import/wordpress/media.d.mts +7 -7
  103. package/dist/astro/routes/api/import/wordpress/media.mjs +4 -4
  104. package/dist/astro/routes/api/import/wordpress/prepare.mjs +6 -6
  105. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +5 -5
  106. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +4 -4
  107. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +7 -7
  108. package/dist/astro/routes/api/manifest.mjs +3 -3
  109. package/dist/astro/routes/api/mcp.mjs +34 -34
  110. package/dist/astro/routes/api/media/_id_/confirm.mjs +4 -4
  111. package/dist/astro/routes/api/media/_id_.mjs +4 -4
  112. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  113. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  114. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  115. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  116. package/dist/astro/routes/api/media/upload-url.mjs +4 -4
  117. package/dist/astro/routes/api/media.mjs +5 -5
  118. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +6 -6
  119. package/dist/astro/routes/api/menus/_name_/items.mjs +6 -6
  120. package/dist/astro/routes/api/menus/_name_/reorder.mjs +6 -6
  121. package/dist/astro/routes/api/menus/_name_/translations.mjs +6 -6
  122. package/dist/astro/routes/api/menus/_name_.mjs +6 -6
  123. package/dist/astro/routes/api/menus/index.mjs +6 -6
  124. package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
  125. package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
  126. package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
  127. package/dist/astro/routes/api/oauth/register.mjs +2 -2
  128. package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
  129. package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
  130. package/dist/astro/routes/api/oauth/token.mjs +2 -2
  131. package/dist/astro/routes/api/openapi.json.mjs +2 -2
  132. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
  133. package/dist/astro/routes/api/redirects/404s/index.mjs +6 -6
  134. package/dist/astro/routes/api/redirects/404s/summary.mjs +6 -6
  135. package/dist/astro/routes/api/redirects/_id_.mjs +7 -7
  136. package/dist/astro/routes/api/redirects/index.mjs +7 -7
  137. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  138. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  139. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +28 -28
  140. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +28 -28
  141. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +28 -28
  142. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +28 -28
  143. package/dist/astro/routes/api/schema/collections/index.mjs +28 -28
  144. package/dist/astro/routes/api/schema/index.mjs +6 -6
  145. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +28 -28
  146. package/dist/astro/routes/api/schema/orphans/index.mjs +28 -28
  147. package/dist/astro/routes/api/search/enable.mjs +7 -7
  148. package/dist/astro/routes/api/search/index.mjs +6 -6
  149. package/dist/astro/routes/api/search/rebuild.mjs +7 -7
  150. package/dist/astro/routes/api/search/stats.mjs +6 -6
  151. package/dist/astro/routes/api/search/suggest.mjs +6 -6
  152. package/dist/astro/routes/api/sections/_slug_.mjs +6 -6
  153. package/dist/astro/routes/api/sections/index.mjs +6 -6
  154. package/dist/astro/routes/api/settings/email.mjs +4 -4
  155. package/dist/astro/routes/api/settings.mjs +9 -9
  156. package/dist/astro/routes/api/setup/admin-verify.mjs +3 -3
  157. package/dist/astro/routes/api/setup/admin.mjs +3 -3
  158. package/dist/astro/routes/api/setup/dev-bypass.mjs +17 -17
  159. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  160. package/dist/astro/routes/api/setup/index.mjs +18 -18
  161. package/dist/astro/routes/api/setup/status.mjs +3 -3
  162. package/dist/astro/routes/api/snapshot.mjs +3 -3
  163. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +9 -9
  164. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +9 -9
  165. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +9 -9
  166. package/dist/astro/routes/api/taxonomies/index.mjs +9 -9
  167. package/dist/astro/routes/api/themes/preview.mjs +3 -3
  168. package/dist/astro/routes/api/typegen.mjs +5 -5
  169. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +4 -4
  170. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +6 -6
  171. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +6 -6
  172. package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
  173. package/dist/astro/routes/api/widget-areas/index.mjs +6 -6
  174. package/dist/astro/routes/api/widget-components.mjs +2 -2
  175. package/dist/astro/routes/robots.txt.mjs +5 -5
  176. package/dist/astro/routes/sitemap-_collection_.xml.mjs +10 -9
  177. package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
  178. package/dist/astro/routes/sitemap.xml.mjs +5 -5
  179. package/dist/astro/types.d.mts +7 -7
  180. package/dist/{authorize-CuxZ0i2t.mjs → authorize-BZnOGJFo.mjs} +2 -2
  181. package/dist/{authorize-CuxZ0i2t.mjs.map → authorize-BZnOGJFo.mjs.map} +1 -1
  182. package/dist/{byline-C-YajfV7.mjs → byline-LL8lyqdb.mjs} +6 -6
  183. package/dist/{byline-C-YajfV7.mjs.map → byline-LL8lyqdb.mjs.map} +1 -1
  184. package/dist/{bylines-D4KADDAi.mjs → bylines-BmJ7Vg7J.mjs} +4 -4
  185. package/dist/{bylines-D4KADDAi.mjs.map → bylines-BmJ7Vg7J.mjs.map} +1 -1
  186. package/dist/{bylines-CPc_q2Vd.mjs → bylines-DE47vP8d.mjs} +5 -5
  187. package/dist/{bylines-CPc_q2Vd.mjs.map → bylines-DE47vP8d.mjs.map} +1 -1
  188. package/dist/{cache-C35VWD5D.mjs → cache-D9jF5bYw.mjs} +2 -2
  189. package/dist/{cache-C35VWD5D.mjs.map → cache-D9jF5bYw.mjs.map} +1 -1
  190. package/dist/{chunks-BHgcHh2C.mjs → chunks-OCG163rN.mjs} +2 -2
  191. package/dist/{chunks-BHgcHh2C.mjs.map → chunks-OCG163rN.mjs.map} +1 -1
  192. package/dist/cli/index.mjs +16 -16
  193. package/dist/{comment-BDx50EX5.mjs → comment-D9BLDpZI.mjs} +3 -3
  194. package/dist/{comment-BDx50EX5.mjs.map → comment-D9BLDpZI.mjs.map} +1 -1
  195. package/dist/{comment-reaction-Gv_pgfI3.mjs → comment-reaction-Drl9SkNu.mjs} +2 -2
  196. package/dist/{comment-reaction-Gv_pgfI3.mjs.map → comment-reaction-Drl9SkNu.mjs.map} +1 -1
  197. package/dist/{comments-CXHuQ6jI.mjs → comments-BjJ9SKGU.mjs} +3 -3
  198. package/dist/{comments-CXHuQ6jI.mjs.map → comments-BjJ9SKGU.mjs.map} +1 -1
  199. package/dist/{content-B43-YrIq.mjs → content-ncUWWjna.mjs} +4 -4
  200. package/dist/{content-B43-YrIq.mjs.map → content-ncUWWjna.mjs.map} +1 -1
  201. package/dist/{context-BW3N7wyt.mjs → context-C_g-iJVA.mjs} +7 -7
  202. package/dist/{context-BW3N7wyt.mjs.map → context-C_g-iJVA.mjs.map} +1 -1
  203. package/dist/{dashboard-FIe0S512.mjs → dashboard-CotyqQrW.mjs} +4 -4
  204. package/dist/{dashboard-FIe0S512.mjs.map → dashboard-CotyqQrW.mjs.map} +1 -1
  205. package/dist/db/index.d.mts +2 -2
  206. package/dist/db/index.mjs +1 -1
  207. package/dist/{error-DXnK2wF4.mjs → error-rVGiqWDt.mjs} +2 -2
  208. package/dist/{error-DXnK2wF4.mjs.map → error-rVGiqWDt.mjs.map} +1 -1
  209. package/dist/{field-defs-cache-CYYx8xlG.mjs → field-defs-cache-ljXzJCfc.mjs} +2 -2
  210. package/dist/{field-defs-cache-CYYx8xlG.mjs.map → field-defs-cache-ljXzJCfc.mjs.map} +1 -1
  211. package/dist/{fts-manager-DW_NR-Fs.mjs → fts-manager-BbCCbEKA.mjs} +2 -2
  212. package/dist/{fts-manager-DW_NR-Fs.mjs.map → fts-manager-BbCCbEKA.mjs.map} +1 -1
  213. package/dist/{index-Cz6_JBL3.d.mts → index-BRNabKrx.d.mts} +36 -11
  214. package/dist/index-BRNabKrx.d.mts.map +1 -0
  215. package/dist/{index-qbL02uhs.d.mts → index-Dt0OKJB4.d.mts} +2 -2
  216. package/dist/{index-qbL02uhs.d.mts.map → index-Dt0OKJB4.d.mts.map} +1 -1
  217. package/dist/index.d.mts +8 -8
  218. package/dist/index.mjs +38 -38
  219. package/dist/{load-DMFi4n1p.mjs → load-DMjXghbZ.mjs} +2 -2
  220. package/dist/{load-DMFi4n1p.mjs.map → load-DMjXghbZ.mjs.map} +1 -1
  221. package/dist/{loader-DrpofRon.mjs → loader-BA_zhkS_.mjs} +81 -18
  222. package/dist/loader-BA_zhkS_.mjs.map +1 -0
  223. package/dist/{manifest-schema-BOfPXse0.mjs → manifest-schema-kPGX7VS-.mjs} +3 -1
  224. package/dist/manifest-schema-kPGX7VS-.mjs.map +1 -0
  225. package/dist/media/local-runtime.d.mts +7 -7
  226. package/dist/media/local-runtime.mjs +5 -5
  227. package/dist/{media-BjKxjzk8.mjs → media-C5t_eCnk.mjs} +2 -2
  228. package/dist/{media-BjKxjzk8.mjs.map → media-C5t_eCnk.mjs.map} +1 -1
  229. package/dist/{menus-8O6xNcYW.mjs → menus-CBb6UrMU.mjs} +3 -3
  230. package/dist/{menus-8O6xNcYW.mjs.map → menus-CBb6UrMU.mjs.map} +1 -1
  231. package/dist/{menus-CxqJ-z-N.mjs → menus-CYH09a41.mjs} +50 -68
  232. package/dist/menus-CYH09a41.mjs.map +1 -0
  233. package/dist/{object-cache-SEb2IM4Z.mjs → object-cache-CHbHv83-.mjs} +8 -4
  234. package/dist/object-cache-CHbHv83-.mjs.map +1 -0
  235. package/dist/{options-BvClpP5Q.d.mts → options-41nCWqi9.d.mts} +2 -2
  236. package/dist/{options-BvClpP5Q.d.mts.map → options-41nCWqi9.d.mts.map} +1 -1
  237. package/dist/page/index.d.mts +5 -2
  238. package/dist/page/index.d.mts.map +1 -1
  239. package/dist/page/index.mjs +3 -2
  240. package/dist/page/index.mjs.map +1 -1
  241. package/dist/{parse-DqMWHQ8x.mjs → parse-BwAdS7FZ.mjs} +2 -2
  242. package/dist/{parse-DqMWHQ8x.mjs.map → parse-BwAdS7FZ.mjs.map} +1 -1
  243. package/dist/plugin-types.d.mts +4 -2
  244. package/dist/plugin-types.d.mts.map +1 -1
  245. package/dist/plugin-utils.d.mts +7 -7
  246. package/dist/plugins/adapt-sandbox-entry.d.mts +7 -7
  247. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  248. package/dist/{query-34pfm02Q.mjs → query-BpQb-blG.mjs} +14 -14
  249. package/dist/{query-34pfm02Q.mjs.map → query-BpQb-blG.mjs.map} +1 -1
  250. package/dist/{rate-limit-CBQK4ygJ.mjs → rate-limit-Dap6HMNM.mjs} +2 -2
  251. package/dist/{rate-limit-CBQK4ygJ.mjs.map → rate-limit-Dap6HMNM.mjs.map} +1 -1
  252. package/dist/{redirect-C1KVGYWe.mjs → redirect-_M0trWoo.mjs} +2 -2
  253. package/dist/{redirect-C1KVGYWe.mjs.map → redirect-_M0trWoo.mjs.map} +1 -1
  254. package/dist/{redirects-DgnypVir.mjs → redirects-w0V9vj_j.mjs} +3 -3
  255. package/dist/{redirects-DgnypVir.mjs.map → redirects-w0V9vj_j.mjs.map} +1 -1
  256. package/dist/{registry-3aOAS4My.mjs → registry-g8yYt4cC.mjs} +4 -4
  257. package/dist/{registry-3aOAS4My.mjs.map → registry-g8yYt4cC.mjs.map} +1 -1
  258. package/dist/{runner-CEtq-6Sm.d.mts → runner-BbR3DfrL.d.mts} +2 -2
  259. package/dist/{runner-CEtq-6Sm.d.mts.map → runner-BbR3DfrL.d.mts.map} +1 -1
  260. package/dist/{runner-BsquExlK.mjs → runner-DPdt_Wpa.mjs} +250 -178
  261. package/dist/runner-DPdt_Wpa.mjs.map +1 -0
  262. package/dist/runtime.d.mts +7 -7
  263. package/dist/runtime.mjs +2 -2
  264. package/dist/{schema-BGwx8AQY.mjs → schema-DoLd3j2e.mjs} +5 -5
  265. package/dist/{schema-BGwx8AQY.mjs.map → schema-DoLd3j2e.mjs.map} +1 -1
  266. package/dist/{search-Gl747zMP.mjs → search-BR0ZeZmh.mjs} +4 -4
  267. package/dist/{search-Gl747zMP.mjs.map → search-BR0ZeZmh.mjs.map} +1 -1
  268. package/dist/{sections-C-umuuLj.mjs → sections-5J2YJPqJ.mjs} +3 -3
  269. package/dist/{sections-C-umuuLj.mjs.map → sections-5J2YJPqJ.mjs.map} +1 -1
  270. package/dist/seed/index.d.mts +2 -2
  271. package/dist/seed/index.mjs +15 -15
  272. package/dist/{seo-BNzkiK_W.mjs → seo-DKeXHkzE.mjs} +3 -3
  273. package/dist/{seo-BNzkiK_W.mjs.map → seo-DKeXHkzE.mjs.map} +1 -1
  274. package/dist/{service-CQHK_10l.mjs → service-BbO2s8gT.mjs} +2 -2
  275. package/dist/{service-CQHK_10l.mjs.map → service-BbO2s8gT.mjs.map} +1 -1
  276. package/dist/{settings-fPSR6dJa.mjs → settings-BMNr9Dmi.mjs} +3 -3
  277. package/dist/{settings-fPSR6dJa.mjs.map → settings-BMNr9Dmi.mjs.map} +1 -1
  278. package/dist/{settings-C4tXRYiG.mjs → settings-DJeki-97.mjs} +4 -4
  279. package/dist/{settings-C4tXRYiG.mjs.map → settings-DJeki-97.mjs.map} +1 -1
  280. package/dist/{taxonomies-DbAtL6m4.mjs → taxonomies-CM_fUqLU.mjs} +5 -4
  281. package/dist/taxonomies-CM_fUqLU.mjs.map +1 -0
  282. package/dist/{taxonomies-n8w72lLQ.mjs → taxonomies-D13i2ctt.mjs} +75 -20
  283. package/dist/taxonomies-D13i2ctt.mjs.map +1 -0
  284. package/dist/{taxonomy-BBUlGug5.mjs → taxonomy-BU0LjdVb.mjs} +4 -4
  285. package/dist/{taxonomy-BBUlGug5.mjs.map → taxonomy-BU0LjdVb.mjs.map} +1 -1
  286. package/dist/{types-h2Cwbzi2.d.mts → types-BYnDBVwe.d.mts} +24 -5
  287. package/dist/{types-h2Cwbzi2.d.mts.map → types-BYnDBVwe.d.mts.map} +1 -1
  288. package/dist/{types-CmI1OyQg.mjs → types-CZgK3EMW.mjs} +2 -2
  289. package/dist/{types-CmI1OyQg.mjs.map → types-CZgK3EMW.mjs.map} +1 -1
  290. package/dist/{types-B-gIxFCE.d.mts → types-xIfVRNLp.d.mts} +38 -1
  291. package/dist/types-xIfVRNLp.d.mts.map +1 -0
  292. package/dist/{user-JAuc8VUf.mjs → user-DkG2jKCp.mjs} +3 -3
  293. package/dist/{user-JAuc8VUf.mjs.map → user-DkG2jKCp.mjs.map} +1 -1
  294. package/dist/{validate-Va_An3qb.d.mts → validate-CG0V0wNR.d.mts} +2 -2
  295. package/dist/{validate-Va_An3qb.d.mts.map → validate-CG0V0wNR.d.mts.map} +1 -1
  296. package/dist/{validate-BzXHWnkV.mjs → validate-CuJ1Ps9j.mjs} +2 -2
  297. package/dist/{validate-BzXHWnkV.mjs.map → validate-CuJ1Ps9j.mjs.map} +1 -1
  298. package/dist/{validation-B6DKPTtU.mjs → validation-CwUfTdGF.mjs} +5 -5
  299. package/dist/{validation-B6DKPTtU.mjs.map → validation-CwUfTdGF.mjs.map} +1 -1
  300. package/dist/version-BoNBItP_.mjs +7 -0
  301. package/dist/{version-DHdCA24u.mjs.map → version-BoNBItP_.mjs.map} +1 -1
  302. package/dist/{widgets-C2RbFN0G.mjs → widgets-BfOZ-ajY.mjs} +2 -2
  303. package/dist/{widgets-C2RbFN0G.mjs.map → widgets-BfOZ-ajY.mjs.map} +1 -1
  304. package/dist/{zod-generator-xEHODMJf.mjs → zod-generator-gFsRuNNy.mjs} +6 -3
  305. package/dist/zod-generator-gFsRuNNy.mjs.map +1 -0
  306. package/package.json +5 -5
  307. package/src/api/handlers/taxonomies.ts +5 -1
  308. package/src/astro/integration/virtual-modules.ts +15 -0
  309. package/src/astro/routes/sitemap-[collection].xml.ts +5 -4
  310. package/src/components/EmDashHead.astro +23 -3
  311. package/src/components/JsonLdScript.astro +9 -0
  312. package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +16 -0
  313. package/src/database/migrations/046_media_usage_index.ts +115 -0
  314. package/src/database/migrations/047_restore_taxonomy_parent_index.ts +29 -0
  315. package/src/database/migrations/runner.ts +4 -0
  316. package/src/database/repositories/media-usage.ts +444 -0
  317. package/src/database/types.ts +39 -0
  318. package/src/emdash-runtime.ts +61 -52
  319. package/src/index.ts +3 -0
  320. package/src/loader.ts +110 -31
  321. package/src/media/usage/extractor.ts +302 -0
  322. package/src/media/usage/types.ts +46 -0
  323. package/src/object-cache/index.ts +8 -2
  324. package/src/page/metadata.ts +23 -3
  325. package/src/plugin-types.ts +10 -0
  326. package/src/plugins/define-plugin.ts +14 -5
  327. package/src/plugins/hooks.ts +53 -65
  328. package/src/plugins/index.ts +5 -0
  329. package/src/plugins/manager.ts +22 -0
  330. package/src/plugins/manifest-schema.ts +2 -0
  331. package/src/plugins/types.ts +35 -3
  332. package/src/schema/zod-generator.ts +9 -1
  333. package/src/seed/apply.ts +6 -0
  334. package/src/taxonomies/index.ts +121 -18
  335. package/dist/apply-Bk2mAuLn.mjs.map +0 -1
  336. package/dist/index-Cz6_JBL3.d.mts.map +0 -1
  337. package/dist/loader-DrpofRon.mjs.map +0 -1
  338. package/dist/manifest-schema-BOfPXse0.mjs.map +0 -1
  339. package/dist/menus-CxqJ-z-N.mjs.map +0 -1
  340. package/dist/object-cache-SEb2IM4Z.mjs.map +0 -1
  341. package/dist/runner-BsquExlK.mjs.map +0 -1
  342. package/dist/taxonomies-DbAtL6m4.mjs.map +0 -1
  343. package/dist/taxonomies-n8w72lLQ.mjs.map +0 -1
  344. package/dist/types-B-gIxFCE.d.mts.map +0 -1
  345. package/dist/version-DHdCA24u.mjs +0 -7
  346. package/dist/zod-generator-xEHODMJf.mjs.map +0 -1
@@ -0,0 +1,115 @@
1
+ import type { Kysely } from "kysely";
2
+
3
+ import { currentTimestamp } from "../dialect-helpers.js";
4
+
5
+ /**
6
+ * Internal media usage projection tables.
7
+ *
8
+ * This migration is DDL-only by design: no backfill, no runtime media/indexing
9
+ * imports, and no content-table scans. Production rows are introduced by later
10
+ * phases once the central snapshot/indexer path is reviewed.
11
+ */
12
+ export async function up(db: Kysely<unknown>): Promise<void> {
13
+ await db.schema
14
+ .createTable("_emdash_media_usage_sources")
15
+ .ifNotExists()
16
+ .addColumn("source_key", "text", (c) => c.primaryKey())
17
+ .addColumn("source_type", "text", (c) => c.notNull())
18
+ .addColumn("collection_slug", "text")
19
+ .addColumn("content_id", "text")
20
+ .addColumn("source_variant", "text", (c) => c.notNull())
21
+ .addColumn("locale", "text")
22
+ .addColumn("translation_group", "text")
23
+ .addColumn("content_slug", "text")
24
+ .addColumn("content_title", "text")
25
+ .addColumn("content_status", "text")
26
+ .addColumn("content_scheduled_at", "text")
27
+ .addColumn("content_deleted_at", "text")
28
+ .addColumn("revision_id", "text")
29
+ .addColumn("current_generation", "text", (c) => c.notNull())
30
+ .addColumn("schema_version", "integer", (c) => c.notNull().defaultTo(1))
31
+ .addColumn("indexed_at", "text", (c) => c.notNull().defaultTo(currentTimestamp(db)))
32
+ .addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db)))
33
+ .addColumn("updated_at", "text", (c) => c.defaultTo(currentTimestamp(db)))
34
+ .execute();
35
+
36
+ await db.schema
37
+ .createIndex("idx__emdash_media_usage_sources_content")
38
+ .ifNotExists()
39
+ .on("_emdash_media_usage_sources")
40
+ .columns(["source_type", "collection_slug", "content_id"])
41
+ .execute();
42
+ await db.schema
43
+ .createIndex("idx__emdash_media_usage_sources_variant")
44
+ .ifNotExists()
45
+ .on("_emdash_media_usage_sources")
46
+ .columns(["source_type", "source_variant"])
47
+ .execute();
48
+ await db.schema
49
+ .createIndex("idx__emdash_media_usage_sources_locale")
50
+ .ifNotExists()
51
+ .on("_emdash_media_usage_sources")
52
+ .columns(["collection_slug", "locale"])
53
+ .execute();
54
+ await db.schema
55
+ .createIndex("idx__emdash_media_usage_sources_deleted")
56
+ .ifNotExists()
57
+ .on("_emdash_media_usage_sources")
58
+ .column("content_deleted_at")
59
+ .execute();
60
+ await db.schema
61
+ .createIndex("idx__emdash_media_usage_sources_translation_group")
62
+ .ifNotExists()
63
+ .on("_emdash_media_usage_sources")
64
+ .columns(["collection_slug", "translation_group"])
65
+ .execute();
66
+
67
+ await db.schema
68
+ .createTable("_emdash_media_usage")
69
+ .ifNotExists()
70
+ .addColumn("id", "text", (c) => c.primaryKey())
71
+ .addColumn("source_key", "text", (c) => c.notNull())
72
+ .addColumn("generation", "text", (c) => c.notNull())
73
+ .addColumn("field_slug", "text", (c) => c.notNull())
74
+ .addColumn("field_path", "text", (c) => c.notNull())
75
+ .addColumn("occurrence_index", "integer", (c) => c.notNull().defaultTo(0))
76
+ .addColumn("reference_type", "text", (c) => c.notNull())
77
+ .addColumn("media_id", "text")
78
+ .addColumn("provider", "text", (c) => c.notNull().defaultTo("local"))
79
+ .addColumn("provider_asset_id", "text", (c) => c.notNull())
80
+ .addColumn("media_kind", "text")
81
+ .addColumn("mime_type", "text")
82
+ .addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db)))
83
+ .execute();
84
+
85
+ await db.schema
86
+ .createIndex("idx__emdash_media_usage_unique_occurrence")
87
+ .ifNotExists()
88
+ .unique()
89
+ .on("_emdash_media_usage")
90
+ .columns(["source_key", "generation", "field_path", "occurrence_index"])
91
+ .execute();
92
+ await db.schema
93
+ .createIndex("idx__emdash_media_usage_media_id")
94
+ .ifNotExists()
95
+ .on("_emdash_media_usage")
96
+ .column("media_id")
97
+ .execute();
98
+ await db.schema
99
+ .createIndex("idx__emdash_media_usage_provider_asset")
100
+ .ifNotExists()
101
+ .on("_emdash_media_usage")
102
+ .columns(["provider", "provider_asset_id"])
103
+ .execute();
104
+ await db.schema
105
+ .createIndex("idx__emdash_media_usage_source_generation")
106
+ .ifNotExists()
107
+ .on("_emdash_media_usage")
108
+ .columns(["source_key", "generation"])
109
+ .execute();
110
+ }
111
+
112
+ export async function down(db: Kysely<unknown>): Promise<void> {
113
+ await db.schema.dropTable("_emdash_media_usage").ifExists().execute();
114
+ await db.schema.dropTable("_emdash_media_usage_sources").ifExists().execute();
115
+ }
@@ -0,0 +1,29 @@
1
+ import type { Kysely } from "kysely";
2
+
3
+ /**
4
+ * Restore `idx_taxonomies_parent` on `taxonomies(parent_id)` (#1665).
5
+ *
6
+ * Migration 015 created this index, but 036's SQLite table rebuild
7
+ * (`taxonomies_new` → drop → rename) dropped every index attached to the old
8
+ * table and only recreated `name`/`locale`/`translation_group`. The parent
9
+ * index — which backs hierarchical (parent/child) lookups — was silently lost
10
+ * on any install migrated through 036. The Postgres path in 036 alters the
11
+ * table in place and keeps its indexes, so it is unaffected; `ifNotExists`
12
+ * makes this a no-op there.
13
+ *
14
+ * Forward-only: 036 has already shipped, so existing installs only recover the
15
+ * index here. Fresh installs also get it in 036's rebuild for parity with the
16
+ * other rebuilt tables, making this a no-op for them.
17
+ */
18
+ export async function up(db: Kysely<unknown>): Promise<void> {
19
+ await db.schema
20
+ .createIndex("idx_taxonomies_parent")
21
+ .ifNotExists()
22
+ .on("taxonomies")
23
+ .column("parent_id")
24
+ .execute();
25
+ }
26
+
27
+ export async function down(db: Kysely<unknown>): Promise<void> {
28
+ await db.schema.dropIndex("idx_taxonomies_parent").ifExists().execute();
29
+ }
@@ -47,6 +47,8 @@ import * as m042 from "./042_byline_fields.js";
47
47
  import * as m043 from "./043_content_references.js";
48
48
  import * as m044 from "./044_comment_reactions.js";
49
49
  import * as m045 from "./045_taxonomy_parent_group.js";
50
+ import * as m046 from "./046_media_usage_index.js";
51
+ import * as m047 from "./047_restore_taxonomy_parent_index.js";
50
52
 
51
53
  const MIGRATIONS: Readonly<Record<string, Migration>> = Object.freeze({
52
54
  "001_initial": m001,
@@ -93,6 +95,8 @@ const MIGRATIONS: Readonly<Record<string, Migration>> = Object.freeze({
93
95
  "043_content_references": m043,
94
96
  "044_comment_reactions": m044,
95
97
  "045_taxonomy_parent_group": m045,
98
+ "046_media_usage_index": m046,
99
+ "047_restore_taxonomy_parent_index": m047,
96
100
  });
97
101
 
98
102
  /** Total number of registered migrations. Exported for use in tests. */
@@ -0,0 +1,444 @@
1
+ import type { Kysely, Selectable, Transaction } from "kysely";
2
+ import { ulid } from "ulidx";
3
+
4
+ import type { MediaKind, MediaUsageReferenceType } from "../../media/usage/types.js";
5
+ import { chunks, SQL_BATCH_SIZE } from "../../utils/chunks.js";
6
+ import { withTransaction } from "../transaction.js";
7
+ import type { Database, MediaUsageSourceTable, MediaUsageTable } from "../types.js";
8
+
9
+ type DatabaseExecutor = Kysely<Database> | Transaction<Database>;
10
+
11
+ const OCCURRENCE_BIND_COLUMNS = 12;
12
+ const OCCURRENCE_INSERT_BATCH_SIZE = Math.max(
13
+ 1,
14
+ Math.floor(SQL_BATCH_SIZE / OCCURRENCE_BIND_COLUMNS),
15
+ );
16
+
17
+ export interface MediaUsageSourceInput {
18
+ sourceKey: string;
19
+ sourceType: string;
20
+ collectionSlug?: string | null;
21
+ contentId?: string | null;
22
+ sourceVariant: string;
23
+ locale?: string | null;
24
+ translationGroup?: string | null;
25
+ contentSlug?: string | null;
26
+ contentTitle?: string | null;
27
+ contentStatus?: string | null;
28
+ contentScheduledAt?: string | null;
29
+ contentDeletedAt?: string | null;
30
+ revisionId?: string | null;
31
+ schemaVersion?: number;
32
+ }
33
+
34
+ export interface MediaUsageOccurrenceInput {
35
+ fieldSlug: string;
36
+ fieldPath: string;
37
+ occurrenceIndex?: number;
38
+ referenceType: MediaUsageReferenceType;
39
+ mediaId: string | null;
40
+ provider: string;
41
+ providerAssetId: string;
42
+ mediaKind?: MediaKind | null;
43
+ mimeType?: string | null;
44
+ }
45
+
46
+ export interface MediaUsageSource {
47
+ sourceKey: string;
48
+ sourceType: string;
49
+ collectionSlug: string | null;
50
+ contentId: string | null;
51
+ sourceVariant: string;
52
+ locale: string | null;
53
+ translationGroup: string | null;
54
+ contentSlug: string | null;
55
+ contentTitle: string | null;
56
+ contentStatus: string | null;
57
+ contentScheduledAt: string | null;
58
+ contentDeletedAt: string | null;
59
+ revisionId: string | null;
60
+ currentGeneration: string;
61
+ schemaVersion: number;
62
+ indexedAt: string;
63
+ createdAt: string;
64
+ updatedAt: string;
65
+ }
66
+
67
+ export interface MediaUsageOccurrence {
68
+ id: string;
69
+ sourceKey: string;
70
+ generation: string;
71
+ fieldSlug: string;
72
+ fieldPath: string;
73
+ occurrenceIndex: number;
74
+ referenceType: string;
75
+ mediaId: string | null;
76
+ provider: string;
77
+ providerAssetId: string;
78
+ mediaKind: string | null;
79
+ mimeType: string | null;
80
+ createdAt: string;
81
+ }
82
+
83
+ export interface MediaUsageRecord {
84
+ source: MediaUsageSource;
85
+ occurrence: MediaUsageOccurrence;
86
+ }
87
+
88
+ interface JoinedUsageRow {
89
+ source_key: string;
90
+ source_type: string;
91
+ collection_slug: string | null;
92
+ content_id: string | null;
93
+ source_variant: string;
94
+ locale: string | null;
95
+ translation_group: string | null;
96
+ content_slug: string | null;
97
+ content_title: string | null;
98
+ content_status: string | null;
99
+ content_scheduled_at: string | null;
100
+ content_deleted_at: string | null;
101
+ revision_id: string | null;
102
+ current_generation: string;
103
+ schema_version: number;
104
+ indexed_at: string;
105
+ source_created_at: string;
106
+ source_updated_at: string;
107
+ occurrence_id: string;
108
+ generation: string;
109
+ field_slug: string;
110
+ field_path: string;
111
+ occurrence_index: number;
112
+ reference_type: string;
113
+ media_id: string | null;
114
+ provider: string;
115
+ provider_asset_id: string;
116
+ media_kind: string | null;
117
+ mime_type: string | null;
118
+ occurrence_created_at: string;
119
+ }
120
+
121
+ /** Persistence-only repository for the internal media usage projection tables. */
122
+ export class MediaUsageRepository {
123
+ constructor(private db: Kysely<Database>) {}
124
+
125
+ async replaceSource(
126
+ source: MediaUsageSourceInput,
127
+ occurrences: readonly MediaUsageOccurrenceInput[],
128
+ ): Promise<MediaUsageSource> {
129
+ const generation = ulid();
130
+ const now = new Date().toISOString();
131
+
132
+ await withTransaction(this.db, async (trx) => {
133
+ await this.insertOccurrences(trx, source.sourceKey, generation, occurrences);
134
+ await this.upsertSource(trx, source, generation, now);
135
+
136
+ try {
137
+ await this.deleteStaleGenerations(trx, source.sourceKey, generation);
138
+ } catch (error) {
139
+ console.error("[media-usage] failed to delete stale generations:", error);
140
+ }
141
+ });
142
+
143
+ const replaced = await this.findSource(source.sourceKey);
144
+ if (!replaced) {
145
+ throw new Error(`Media usage source ${source.sourceKey} was not persisted`);
146
+ }
147
+ return replaced;
148
+ }
149
+
150
+ async findSource(sourceKey: string): Promise<MediaUsageSource | null> {
151
+ const row = await this.db
152
+ .selectFrom("_emdash_media_usage_sources")
153
+ .selectAll()
154
+ .where("source_key", "=", sourceKey)
155
+ .executeTakeFirst();
156
+
157
+ return row ? rowToSource(row) : null;
158
+ }
159
+
160
+ async findCurrentUsageByMediaId(mediaId: string): Promise<MediaUsageRecord[]> {
161
+ const rows = await this.db
162
+ .selectFrom("_emdash_media_usage_sources as s")
163
+ .innerJoin("_emdash_media_usage as u", (join) =>
164
+ join
165
+ .onRef("u.source_key", "=", "s.source_key")
166
+ .onRef("u.generation", "=", "s.current_generation"),
167
+ )
168
+ .select(currentUsageSelect)
169
+ .where("u.media_id", "=", mediaId)
170
+ .orderBy("s.source_key", "asc")
171
+ .orderBy("u.field_path", "asc")
172
+ .orderBy("u.occurrence_index", "asc")
173
+ .execute();
174
+
175
+ return rows.map(rowToUsageRecord);
176
+ }
177
+
178
+ async findCurrentUsageByProviderAsset(
179
+ provider: string,
180
+ providerAssetId: string,
181
+ ): Promise<MediaUsageRecord[]> {
182
+ const rows = await this.db
183
+ .selectFrom("_emdash_media_usage_sources as s")
184
+ .innerJoin("_emdash_media_usage as u", (join) =>
185
+ join
186
+ .onRef("u.source_key", "=", "s.source_key")
187
+ .onRef("u.generation", "=", "s.current_generation"),
188
+ )
189
+ .select(currentUsageSelect)
190
+ .where("u.provider", "=", provider)
191
+ .where("u.provider_asset_id", "=", providerAssetId)
192
+ .orderBy("s.source_key", "asc")
193
+ .orderBy("u.field_path", "asc")
194
+ .orderBy("u.occurrence_index", "asc")
195
+ .execute();
196
+
197
+ return rows.map(rowToUsageRecord);
198
+ }
199
+
200
+ async deleteSource(sourceKey: string): Promise<number> {
201
+ return withTransaction(this.db, async (trx) => {
202
+ await trx.deleteFrom("_emdash_media_usage").where("source_key", "=", sourceKey).execute();
203
+ const result = await trx
204
+ .deleteFrom("_emdash_media_usage_sources")
205
+ .where("source_key", "=", sourceKey)
206
+ .executeTakeFirst();
207
+ return Number(result.numDeletedRows ?? 0);
208
+ });
209
+ }
210
+
211
+ async deleteContentSources(collectionSlug: string, contentId: string): Promise<number> {
212
+ const sourceRows = await this.db
213
+ .selectFrom("_emdash_media_usage_sources")
214
+ .select("source_key")
215
+ .where("source_type", "=", "content")
216
+ .where("collection_slug", "=", collectionSlug)
217
+ .where("content_id", "=", contentId)
218
+ .execute();
219
+ const sourceKeys = sourceRows.map((row) => row.source_key);
220
+ if (sourceKeys.length === 0) return 0;
221
+
222
+ return withTransaction(this.db, async (trx) => {
223
+ let deleted = 0;
224
+ for (const sourceKeyBatch of chunks(sourceKeys, SQL_BATCH_SIZE)) {
225
+ await trx
226
+ .deleteFrom("_emdash_media_usage")
227
+ .where("source_key", "in", sourceKeyBatch)
228
+ .execute();
229
+ const result = await trx
230
+ .deleteFrom("_emdash_media_usage_sources")
231
+ .where("source_key", "in", sourceKeyBatch)
232
+ .executeTakeFirst();
233
+ deleted += Number(result.numDeletedRows ?? 0);
234
+ }
235
+ return deleted;
236
+ });
237
+ }
238
+
239
+ private async insertOccurrences(
240
+ db: DatabaseExecutor,
241
+ sourceKey: string,
242
+ generation: string,
243
+ occurrences: readonly MediaUsageOccurrenceInput[],
244
+ ): Promise<void> {
245
+ if (occurrences.length === 0) return;
246
+
247
+ const rows = occurrences.map((occurrence) => ({
248
+ id: ulid(),
249
+ source_key: sourceKey,
250
+ generation,
251
+ field_slug: occurrence.fieldSlug,
252
+ field_path: occurrence.fieldPath,
253
+ occurrence_index: occurrence.occurrenceIndex ?? 0,
254
+ reference_type: occurrence.referenceType,
255
+ media_id: occurrence.mediaId,
256
+ provider: occurrence.provider,
257
+ provider_asset_id: occurrence.providerAssetId,
258
+ media_kind: occurrence.mediaKind ?? null,
259
+ mime_type: occurrence.mimeType ?? null,
260
+ }));
261
+
262
+ for (const rowBatch of chunks(rows, OCCURRENCE_INSERT_BATCH_SIZE)) {
263
+ await db.insertInto("_emdash_media_usage").values(rowBatch).execute();
264
+ }
265
+ }
266
+
267
+ private async upsertSource(
268
+ db: DatabaseExecutor,
269
+ source: MediaUsageSourceInput,
270
+ generation: string,
271
+ now: string,
272
+ ): Promise<void> {
273
+ const row = {
274
+ source_key: source.sourceKey,
275
+ source_type: source.sourceType,
276
+ collection_slug: source.collectionSlug ?? null,
277
+ content_id: source.contentId ?? null,
278
+ source_variant: source.sourceVariant,
279
+ locale: source.locale ?? null,
280
+ translation_group: source.translationGroup ?? null,
281
+ content_slug: source.contentSlug ?? null,
282
+ content_title: source.contentTitle ?? null,
283
+ content_status: source.contentStatus ?? null,
284
+ content_scheduled_at: source.contentScheduledAt ?? null,
285
+ content_deleted_at: source.contentDeletedAt ?? null,
286
+ revision_id: source.revisionId ?? null,
287
+ current_generation: generation,
288
+ schema_version: source.schemaVersion ?? 1,
289
+ indexed_at: now,
290
+ updated_at: now,
291
+ };
292
+
293
+ await db
294
+ .insertInto("_emdash_media_usage_sources")
295
+ .values(row)
296
+ .onConflict((oc) =>
297
+ oc.column("source_key").doUpdateSet({
298
+ source_type: row.source_type,
299
+ collection_slug: row.collection_slug,
300
+ content_id: row.content_id,
301
+ source_variant: row.source_variant,
302
+ locale: row.locale,
303
+ translation_group: row.translation_group,
304
+ content_slug: row.content_slug,
305
+ content_title: row.content_title,
306
+ content_status: row.content_status,
307
+ content_scheduled_at: row.content_scheduled_at,
308
+ content_deleted_at: row.content_deleted_at,
309
+ revision_id: row.revision_id,
310
+ current_generation: row.current_generation,
311
+ schema_version: row.schema_version,
312
+ indexed_at: row.indexed_at,
313
+ updated_at: row.updated_at,
314
+ }),
315
+ )
316
+ .execute();
317
+ }
318
+
319
+ private async deleteStaleGenerations(
320
+ db: DatabaseExecutor,
321
+ sourceKey: string,
322
+ currentGeneration: string,
323
+ ): Promise<void> {
324
+ await db
325
+ .deleteFrom("_emdash_media_usage")
326
+ .where("source_key", "=", sourceKey)
327
+ .where("generation", "!=", currentGeneration)
328
+ .execute();
329
+ }
330
+ }
331
+
332
+ const currentUsageSelect = [
333
+ "s.source_key as source_key",
334
+ "s.source_type as source_type",
335
+ "s.collection_slug as collection_slug",
336
+ "s.content_id as content_id",
337
+ "s.source_variant as source_variant",
338
+ "s.locale as locale",
339
+ "s.translation_group as translation_group",
340
+ "s.content_slug as content_slug",
341
+ "s.content_title as content_title",
342
+ "s.content_status as content_status",
343
+ "s.content_scheduled_at as content_scheduled_at",
344
+ "s.content_deleted_at as content_deleted_at",
345
+ "s.revision_id as revision_id",
346
+ "s.current_generation as current_generation",
347
+ "s.schema_version as schema_version",
348
+ "s.indexed_at as indexed_at",
349
+ "s.created_at as source_created_at",
350
+ "s.updated_at as source_updated_at",
351
+ "u.id as occurrence_id",
352
+ "u.generation as generation",
353
+ "u.field_slug as field_slug",
354
+ "u.field_path as field_path",
355
+ "u.occurrence_index as occurrence_index",
356
+ "u.reference_type as reference_type",
357
+ "u.media_id as media_id",
358
+ "u.provider as provider",
359
+ "u.provider_asset_id as provider_asset_id",
360
+ "u.media_kind as media_kind",
361
+ "u.mime_type as mime_type",
362
+ "u.created_at as occurrence_created_at",
363
+ ] as const;
364
+
365
+ function rowToSource(row: Selectable<MediaUsageSourceTable>): MediaUsageSource {
366
+ return {
367
+ sourceKey: row.source_key,
368
+ sourceType: row.source_type,
369
+ collectionSlug: row.collection_slug,
370
+ contentId: row.content_id,
371
+ sourceVariant: row.source_variant,
372
+ locale: row.locale,
373
+ translationGroup: row.translation_group,
374
+ contentSlug: row.content_slug,
375
+ contentTitle: row.content_title,
376
+ contentStatus: row.content_status,
377
+ contentScheduledAt: row.content_scheduled_at,
378
+ contentDeletedAt: row.content_deleted_at,
379
+ revisionId: row.revision_id,
380
+ currentGeneration: row.current_generation,
381
+ schemaVersion: Number(row.schema_version),
382
+ indexedAt: row.indexed_at,
383
+ createdAt: row.created_at,
384
+ updatedAt: row.updated_at,
385
+ };
386
+ }
387
+
388
+ function rowToOccurrence(row: Selectable<MediaUsageTable>): MediaUsageOccurrence {
389
+ return {
390
+ id: row.id,
391
+ sourceKey: row.source_key,
392
+ generation: row.generation,
393
+ fieldSlug: row.field_slug,
394
+ fieldPath: row.field_path,
395
+ occurrenceIndex: Number(row.occurrence_index),
396
+ referenceType: row.reference_type,
397
+ mediaId: row.media_id,
398
+ provider: row.provider,
399
+ providerAssetId: row.provider_asset_id,
400
+ mediaKind: row.media_kind,
401
+ mimeType: row.mime_type,
402
+ createdAt: row.created_at,
403
+ };
404
+ }
405
+
406
+ function rowToUsageRecord(row: JoinedUsageRow): MediaUsageRecord {
407
+ return {
408
+ source: rowToSource({
409
+ source_key: row.source_key,
410
+ source_type: row.source_type,
411
+ collection_slug: row.collection_slug,
412
+ content_id: row.content_id,
413
+ source_variant: row.source_variant,
414
+ locale: row.locale,
415
+ translation_group: row.translation_group,
416
+ content_slug: row.content_slug,
417
+ content_title: row.content_title,
418
+ content_status: row.content_status,
419
+ content_scheduled_at: row.content_scheduled_at,
420
+ content_deleted_at: row.content_deleted_at,
421
+ revision_id: row.revision_id,
422
+ current_generation: row.current_generation,
423
+ schema_version: row.schema_version,
424
+ indexed_at: row.indexed_at,
425
+ created_at: row.source_created_at,
426
+ updated_at: row.source_updated_at,
427
+ }),
428
+ occurrence: rowToOccurrence({
429
+ id: row.occurrence_id,
430
+ source_key: row.source_key,
431
+ generation: row.generation,
432
+ field_slug: row.field_slug,
433
+ field_path: row.field_path,
434
+ occurrence_index: row.occurrence_index,
435
+ reference_type: row.reference_type,
436
+ media_id: row.media_id,
437
+ provider: row.provider,
438
+ provider_asset_id: row.provider_asset_id,
439
+ media_kind: row.media_kind,
440
+ mime_type: row.mime_type,
441
+ created_at: row.occurrence_created_at,
442
+ }),
443
+ };
444
+ }
@@ -60,6 +60,43 @@ export interface MediaTable {
60
60
  author_id: string | null;
61
61
  }
62
62
 
63
+ export interface MediaUsageSourceTable {
64
+ source_key: string;
65
+ source_type: string;
66
+ collection_slug: string | null;
67
+ content_id: string | null;
68
+ source_variant: string;
69
+ locale: string | null;
70
+ translation_group: string | null;
71
+ content_slug: string | null;
72
+ content_title: string | null;
73
+ content_status: string | null;
74
+ content_scheduled_at: string | null;
75
+ content_deleted_at: string | null;
76
+ revision_id: string | null;
77
+ current_generation: string;
78
+ schema_version: Generated<number>;
79
+ indexed_at: Generated<string>;
80
+ created_at: Generated<string>;
81
+ updated_at: Generated<string>;
82
+ }
83
+
84
+ export interface MediaUsageTable {
85
+ id: string;
86
+ source_key: string;
87
+ generation: string;
88
+ field_slug: string;
89
+ field_path: string;
90
+ occurrence_index: Generated<number>;
91
+ reference_type: string;
92
+ media_id: string | null;
93
+ provider: Generated<string>;
94
+ provider_asset_id: string;
95
+ media_kind: string | null;
96
+ mime_type: string | null;
97
+ created_at: Generated<string>;
98
+ }
99
+
63
100
  export interface UserTable {
64
101
  id: string;
65
102
  email: string;
@@ -414,6 +451,8 @@ export interface Database {
414
451
  content_taxonomies: ContentTaxonomyTable;
415
452
  _emdash_taxonomy_defs: TaxonomyDefTable;
416
453
  media: MediaTable;
454
+ _emdash_media_usage_sources: MediaUsageSourceTable;
455
+ _emdash_media_usage: MediaUsageTable;
417
456
  users: UserTable;
418
457
  credentials: CredentialTable;
419
458
  auth_tokens: AuthTokenTable;