emdash 1.0.1 → 1.2.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 (1439) hide show
  1. package/dist/action-token-BdWad2YG.mjs +26 -0
  2. package/dist/action-token-BdWad2YG.mjs.map +1 -0
  3. package/dist/activation-xomsCMJg.mjs +387 -0
  4. package/dist/activation-xomsCMJg.mjs.map +1 -0
  5. package/dist/adapters-B4-nbElt.d.mts +173 -0
  6. package/dist/adapters-B4-nbElt.d.mts.map +1 -0
  7. package/dist/allowed-origins-CLpHGGlc.mjs +116 -0
  8. package/dist/allowed-origins-CLpHGGlc.mjs.map +1 -0
  9. package/dist/api/route-utils.d.mts +4 -4
  10. package/dist/api/route-utils.d.mts.map +1 -1
  11. package/dist/api/route-utils.mjs +49 -48
  12. package/dist/api/route-utils.mjs.map +1 -1
  13. package/dist/api/schemas/index.d.mts +2 -2
  14. package/dist/api/schemas/index.mjs +10 -10
  15. package/dist/api-DBw_IWoi.mjs +5024 -0
  16. package/dist/api-DBw_IWoi.mjs.map +1 -0
  17. package/dist/api-tokens-B0ZipcZ-.mjs +173 -0
  18. package/dist/api-tokens-B0ZipcZ-.mjs.map +1 -0
  19. package/dist/apply-Cx48saFC.mjs +1680 -0
  20. package/dist/apply-Cx48saFC.mjs.map +1 -0
  21. package/dist/approvals-BXgtXA2s.mjs +460 -0
  22. package/dist/approvals-BXgtXA2s.mjs.map +1 -0
  23. package/dist/artifact-fetch-DsRczRGG.mjs +659 -0
  24. package/dist/artifact-fetch-DsRczRGG.mjs.map +1 -0
  25. package/dist/artifacts-D-O-qj9C.mjs +307 -0
  26. package/dist/artifacts-D-O-qj9C.mjs.map +1 -0
  27. package/dist/astro/image-endpoint.d.mts.map +1 -1
  28. package/dist/astro/image-endpoint.mjs +26 -6
  29. package/dist/astro/image-endpoint.mjs.map +1 -1
  30. package/dist/astro/index.d.mts +14 -14
  31. package/dist/astro/index.d.mts.map +1 -1
  32. package/dist/astro/index.mjs +253 -17
  33. package/dist/astro/index.mjs.map +1 -1
  34. package/dist/astro/middleware/auth.d.mts +12 -12
  35. package/dist/astro/middleware/auth.d.mts.map +1 -1
  36. package/dist/astro/middleware/auth.mjs +18 -13
  37. package/dist/astro/middleware/auth.mjs.map +1 -1
  38. package/dist/astro/middleware/media-usage-write-fence.mjs +6 -6
  39. package/dist/astro/middleware/redirect.d.mts.map +1 -1
  40. package/dist/astro/middleware/redirect.mjs +34 -32
  41. package/dist/astro/middleware/redirect.mjs.map +1 -1
  42. package/dist/astro/middleware/request-context.d.mts.map +1 -1
  43. package/dist/astro/middleware/request-context.mjs +99 -60
  44. package/dist/astro/middleware/request-context.mjs.map +1 -1
  45. package/dist/astro/middleware/setup.mjs +1 -1
  46. package/dist/astro/middleware.d.mts +12 -12
  47. package/dist/astro/middleware.d.mts.map +1 -1
  48. package/dist/astro/middleware.mjs +233 -128
  49. package/dist/astro/middleware.mjs.map +1 -1
  50. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +15 -15
  51. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +15 -15
  52. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +8 -7
  53. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs.map +1 -1
  54. package/dist/astro/routes/api/admin/api-tokens/index.mjs +9 -8
  55. package/dist/astro/routes/api/admin/api-tokens/index.mjs.map +1 -1
  56. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +7 -7
  57. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +17 -17
  58. package/dist/astro/routes/api/admin/byline-fields/index.mjs +17 -17
  59. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +17 -17
  60. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +22 -22
  61. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +22 -23
  62. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
  63. package/dist/astro/routes/api/admin/bylines/index.mjs +23 -23
  64. package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
  65. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +19 -19
  66. package/dist/astro/routes/api/admin/comments/_id_.mjs +8 -8
  67. package/dist/astro/routes/api/admin/comments/bulk.mjs +18 -18
  68. package/dist/astro/routes/api/admin/comments/counts.mjs +8 -8
  69. package/dist/astro/routes/api/admin/comments/index.mjs +18 -18
  70. package/dist/astro/routes/api/admin/core-update.mjs +8 -8
  71. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +7 -7
  72. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +6 -6
  73. package/dist/astro/routes/api/admin/media-usage/activation.mjs +21 -21
  74. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +22 -22
  75. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +22 -22
  76. package/dist/astro/routes/api/admin/media-usage/progress.mjs +23 -23
  77. package/dist/astro/routes/api/admin/media-usage/repair.mjs +29 -29
  78. package/dist/astro/routes/api/admin/media-usage/work/index.mjs +22 -22
  79. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +22 -22
  80. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +7 -7
  81. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +7 -7
  82. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +9 -9
  83. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +9 -9
  84. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +73 -72
  85. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  86. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +8 -8
  87. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +71 -70
  88. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
  89. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +72 -71
  90. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  91. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +82 -81
  92. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  93. package/dist/astro/routes/api/admin/plugins/index.mjs +72 -71
  94. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  95. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +8 -8
  96. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +71 -70
  97. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  98. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +81 -80
  99. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  100. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +71 -70
  101. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  102. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +72 -71
  103. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  104. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +83 -82
  105. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  106. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +10 -10
  107. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +74 -73
  108. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  109. package/dist/astro/routes/api/admin/plugins/registry/publisher-handle.mjs +8 -8
  110. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +72 -71
  111. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs.map +1 -1
  112. package/dist/astro/routes/api/admin/plugins/updates.mjs +71 -70
  113. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  114. package/dist/astro/routes/api/admin/scheduled-policy-rejections/_collection_/_id_.mjs +6 -6
  115. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +71 -70
  116. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  117. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +8 -8
  118. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +71 -70
  119. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  120. package/dist/astro/routes/api/admin/transfer/_...path_.mjs +5 -5
  121. package/dist/astro/routes/api/admin/transfer/approvals/_id_/approve.mjs +39 -39
  122. package/dist/astro/routes/api/admin/transfer/approvals/_id_/deny.mjs +39 -39
  123. package/dist/astro/routes/api/admin/transfer/approvals/index.mjs +41 -41
  124. package/dist/astro/routes/api/admin/transfer/capabilities.mjs +41 -41
  125. package/dist/astro/routes/api/admin/transfer/exports/_id_/advance.mjs +41 -41
  126. package/dist/astro/routes/api/admin/transfer/exports/_id_/archive.mjs +42 -42
  127. package/dist/astro/routes/api/admin/transfer/exports/_id_/files/_...path_.mjs +42 -42
  128. package/dist/astro/routes/api/admin/transfer/exports/_id_/index.mjs +41 -41
  129. package/dist/astro/routes/api/admin/transfer/exports/_id_/manifest.mjs +42 -42
  130. package/dist/astro/routes/api/admin/transfer/exports/index.mjs +43 -43
  131. package/dist/astro/routes/api/admin/transfer/imports/_id_/abandon.mjs +41 -41
  132. package/dist/astro/routes/api/admin/transfer/imports/_id_/advance.mjs +41 -41
  133. package/dist/astro/routes/api/admin/transfer/imports/_id_/analyze.mjs +43 -43
  134. package/dist/astro/routes/api/admin/transfer/imports/_id_/cancel.mjs +41 -41
  135. package/dist/astro/routes/api/admin/transfer/imports/_id_/execute.mjs +43 -43
  136. package/dist/astro/routes/api/admin/transfer/imports/_id_/files/_...path_.mjs +41 -41
  137. package/dist/astro/routes/api/admin/transfer/imports/_id_/index.mjs +41 -41
  138. package/dist/astro/routes/api/admin/transfer/imports/_id_/missing.mjs +43 -43
  139. package/dist/astro/routes/api/admin/transfer/imports/_id_/plan.mjs +41 -41
  140. package/dist/astro/routes/api/admin/transfer/imports/_id_/receipt.mjs +41 -41
  141. package/dist/astro/routes/api/admin/transfer/imports/index.mjs +43 -43
  142. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +5 -5
  143. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +5 -5
  144. package/dist/astro/routes/api/admin/users/_id_/index.mjs +15 -15
  145. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +7 -7
  146. package/dist/astro/routes/api/admin/users/index.mjs +15 -15
  147. package/dist/astro/routes/api/auth/dev-bypass.mjs +7 -7
  148. package/dist/astro/routes/api/auth/handover.d.mts +8 -0
  149. package/dist/astro/routes/api/auth/handover.d.mts.map +1 -0
  150. package/dist/astro/routes/api/auth/handover.mjs +49 -0
  151. package/dist/astro/routes/api/auth/handover.mjs.map +1 -0
  152. package/dist/astro/routes/api/auth/invite/accept.mjs +5 -5
  153. package/dist/astro/routes/api/auth/invite/complete.mjs +20 -20
  154. package/dist/astro/routes/api/auth/invite/index.mjs +17 -17
  155. package/dist/astro/routes/api/auth/invite/register-options.mjs +18 -18
  156. package/dist/astro/routes/api/auth/logout.mjs +6 -6
  157. package/dist/astro/routes/api/auth/magic-link/send.mjs +20 -19
  158. package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -1
  159. package/dist/astro/routes/api/auth/magic-link/verify.d.mts.map +1 -1
  160. package/dist/astro/routes/api/auth/magic-link/verify.mjs +24 -18
  161. package/dist/astro/routes/api/auth/magic-link/verify.mjs.map +1 -1
  162. package/dist/astro/routes/api/auth/me.mjs +16 -16
  163. package/dist/astro/routes/api/auth/mode.mjs +6 -6
  164. package/dist/astro/routes/api/auth/oauth/_provider_/callback.d.mts.map +1 -1
  165. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +36 -13
  166. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs.map +1 -1
  167. package/dist/astro/routes/api/auth/oauth/_provider_.d.mts.map +1 -1
  168. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +20 -5
  169. package/dist/astro/routes/api/auth/oauth/_provider_.mjs.map +1 -1
  170. package/dist/astro/routes/api/auth/passkey/_id_.mjs +15 -15
  171. package/dist/astro/routes/api/auth/passkey/index.mjs +5 -5
  172. package/dist/astro/routes/api/auth/passkey/options.d.mts.map +1 -1
  173. package/dist/astro/routes/api/auth/passkey/options.mjs +28 -21
  174. package/dist/astro/routes/api/auth/passkey/options.mjs.map +1 -1
  175. package/dist/astro/routes/api/auth/passkey/register/options.mjs +18 -18
  176. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +19 -19
  177. package/dist/astro/routes/api/auth/passkey/verify.mjs +20 -20
  178. package/dist/astro/routes/api/auth/signup/complete.mjs +20 -20
  179. package/dist/astro/routes/api/auth/signup/request.mjs +19 -18
  180. package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -1
  181. package/dist/astro/routes/api/auth/signup/verify.mjs +5 -5
  182. package/dist/astro/routes/api/calendar.d.mts +8 -0
  183. package/dist/astro/routes/api/calendar.d.mts.map +1 -0
  184. package/dist/astro/routes/api/calendar.mjs +204 -0
  185. package/dist/astro/routes/api/calendar.mjs.map +1 -0
  186. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +14 -14
  187. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +20 -20
  188. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +6 -6
  189. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +17 -17
  190. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +6 -6
  191. package/dist/astro/routes/api/content/_collection_/_id_/lock.mjs +17 -17
  192. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +6 -6
  193. package/dist/astro/routes/api/content/_collection_/_id_/plugin-extensions/_pluginId_/_kind_/_extensionId_.mjs +12 -12
  194. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +31 -31
  195. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +17 -17
  196. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +35 -35
  197. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +35 -35
  198. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +6 -6
  199. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +6 -6
  200. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +17 -17
  201. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +25 -25
  202. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +6 -6
  203. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +17 -17
  204. package/dist/astro/routes/api/content/_collection_/_id_.mjs +17 -17
  205. package/dist/astro/routes/api/content/_collection_/authors.mjs +6 -6
  206. package/dist/astro/routes/api/content/_collection_/index.mjs +16 -16
  207. package/dist/astro/routes/api/content/_collection_/trash.mjs +16 -16
  208. package/dist/astro/routes/api/dashboard.mjs +12 -12
  209. package/dist/astro/routes/api/dev/emails.mjs +6 -6
  210. package/dist/astro/routes/api/health.mjs +7 -7
  211. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  212. package/dist/astro/routes/api/import/probe.mjs +25 -25
  213. package/dist/astro/routes/api/import/wordpress/analyze.mjs +6 -6
  214. package/dist/astro/routes/api/import/wordpress/execute.d.mts +13 -13
  215. package/dist/astro/routes/api/import/wordpress/execute.mjs +31 -24
  216. package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
  217. package/dist/astro/routes/api/import/wordpress/media.d.mts +12 -12
  218. package/dist/astro/routes/api/import/wordpress/media.mjs +20 -20
  219. package/dist/astro/routes/api/import/wordpress/prepare.mjs +18 -18
  220. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.d.mts +8 -0
  221. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.d.mts.map +1 -1
  222. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.mjs +67 -31
  223. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.mjs.map +1 -1
  224. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +13 -13
  225. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +27 -27
  226. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  227. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +25 -25
  228. package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
  229. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +13 -13
  230. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +40 -33
  231. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
  232. package/dist/astro/routes/api/manifest.mjs +10 -7
  233. package/dist/astro/routes/api/manifest.mjs.map +1 -1
  234. package/dist/astro/routes/api/mcp.mjs +94 -94
  235. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  236. package/dist/astro/routes/api/media/_id_/confirm.d.mts.map +1 -1
  237. package/dist/astro/routes/api/media/_id_/confirm.mjs +36 -20
  238. package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
  239. package/dist/astro/routes/api/media/_id_/replace.mjs +17 -17
  240. package/dist/astro/routes/api/media/_id_/upload.mjs +10 -10
  241. package/dist/astro/routes/api/media/_id_/usage.mjs +31 -31
  242. package/dist/astro/routes/api/media/_id_.mjs +29 -29
  243. package/dist/astro/routes/api/media/asset/_id_/_filename_.mjs +7 -7
  244. package/dist/astro/routes/api/media/file/_...key_.d.mts.map +1 -1
  245. package/dist/astro/routes/api/media/file/_...key_.mjs +46 -16
  246. package/dist/astro/routes/api/media/file/_...key_.mjs.map +1 -1
  247. package/dist/astro/routes/api/media/folders/_id_.mjs +17 -17
  248. package/dist/astro/routes/api/media/folders/index.mjs +17 -17
  249. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +6 -6
  250. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +6 -6
  251. package/dist/astro/routes/api/media/providers/index.mjs +6 -6
  252. package/dist/astro/routes/api/media/upload-url.mjs +45 -25
  253. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
  254. package/dist/astro/routes/api/media.d.mts.map +1 -1
  255. package/dist/astro/routes/api/media.mjs +47 -36
  256. package/dist/astro/routes/api/media.mjs.map +1 -1
  257. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +18 -18
  258. package/dist/astro/routes/api/menus/_name_/items.mjs +18 -18
  259. package/dist/astro/routes/api/menus/_name_/reorder.mjs +18 -18
  260. package/dist/astro/routes/api/menus/_name_/translations.mjs +18 -18
  261. package/dist/astro/routes/api/menus/_name_.mjs +18 -18
  262. package/dist/astro/routes/api/menus/index.mjs +18 -18
  263. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  264. package/dist/astro/routes/api/oauth/device/authorize.mjs +9 -9
  265. package/dist/astro/routes/api/oauth/device/code.mjs +13 -12
  266. package/dist/astro/routes/api/oauth/device/code.mjs.map +1 -1
  267. package/dist/astro/routes/api/oauth/device/token.mjs +12 -11
  268. package/dist/astro/routes/api/oauth/device/token.mjs.map +1 -1
  269. package/dist/astro/routes/api/oauth/register.mjs +9 -8
  270. package/dist/astro/routes/api/oauth/register.mjs.map +1 -1
  271. package/dist/astro/routes/api/oauth/token/refresh.mjs +9 -9
  272. package/dist/astro/routes/api/oauth/token/revoke.mjs +9 -9
  273. package/dist/astro/routes/api/oauth/token.mjs +9 -9
  274. package/dist/astro/routes/api/openapi.json.mjs +23 -23
  275. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  276. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +12 -12
  277. package/dist/astro/routes/api/redirects/404s/index.mjs +20 -19
  278. package/dist/astro/routes/api/redirects/404s/index.mjs.map +1 -1
  279. package/dist/astro/routes/api/redirects/404s/summary.mjs +20 -19
  280. package/dist/astro/routes/api/redirects/404s/summary.mjs.map +1 -1
  281. package/dist/astro/routes/api/redirects/_id_.mjs +20 -19
  282. package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
  283. package/dist/astro/routes/api/redirects/index.mjs +20 -19
  284. package/dist/astro/routes/api/redirects/index.mjs.map +1 -1
  285. package/dist/astro/routes/api/relations/_id_/index.mjs +35 -35
  286. package/dist/astro/routes/api/relations/index.mjs +35 -35
  287. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +6 -6
  288. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +17 -17
  289. package/dist/astro/routes/api/schema/block-types/_slug_/index.mjs +71 -70
  290. package/dist/astro/routes/api/schema/block-types/_slug_/index.mjs.map +1 -1
  291. package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.mjs +71 -70
  292. package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.mjs.map +1 -1
  293. package/dist/astro/routes/api/schema/block-types/index.mjs +71 -70
  294. package/dist/astro/routes/api/schema/block-types/index.mjs.map +1 -1
  295. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +71 -70
  296. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  297. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +71 -70
  298. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  299. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +71 -70
  300. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  301. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +71 -70
  302. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  303. package/dist/astro/routes/api/schema/collections/index.mjs +71 -70
  304. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  305. package/dist/astro/routes/api/schema/collections/reorder.mjs +71 -70
  306. package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
  307. package/dist/astro/routes/api/schema/index.mjs +17 -17
  308. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  309. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +71 -70
  310. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  311. package/dist/astro/routes/api/schema/orphans/index.mjs +71 -70
  312. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  313. package/dist/astro/routes/api/search/enable.mjs +19 -19
  314. package/dist/astro/routes/api/search/index.mjs +18 -18
  315. package/dist/astro/routes/api/search/rebuild.mjs +19 -19
  316. package/dist/astro/routes/api/search/stats.mjs +9 -9
  317. package/dist/astro/routes/api/search/suggest.mjs +18 -18
  318. package/dist/astro/routes/api/sections/_slug_.mjs +18 -18
  319. package/dist/astro/routes/api/sections/index.mjs +18 -18
  320. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +8 -8
  321. package/dist/astro/routes/api/settings/backups/archives/index.mjs +8 -8
  322. package/dist/astro/routes/api/settings/backups/export.mjs +8 -8
  323. package/dist/astro/routes/api/settings/backups/index.mjs +9 -9
  324. package/dist/astro/routes/api/settings/domain-notify.d.mts +8 -0
  325. package/dist/astro/routes/api/settings/domain-notify.d.mts.map +1 -0
  326. package/dist/astro/routes/api/settings/domain-notify.mjs +125 -0
  327. package/dist/astro/routes/api/settings/domain-notify.mjs.map +1 -0
  328. package/dist/astro/routes/api/settings/domain.d.mts +9 -0
  329. package/dist/astro/routes/api/settings/domain.d.mts.map +1 -0
  330. package/dist/astro/routes/api/settings/domain.mjs +61 -0
  331. package/dist/astro/routes/api/settings/domain.mjs.map +1 -0
  332. package/dist/astro/routes/api/settings/email.mjs +7 -7
  333. package/dist/astro/routes/api/settings.mjs +22 -22
  334. package/dist/astro/routes/api/setup/admin-verify.mjs +21 -21
  335. package/dist/astro/routes/api/setup/admin.mjs +19 -19
  336. package/dist/astro/routes/api/setup/dev-bypass.mjs +43 -43
  337. package/dist/astro/routes/api/setup/dev-reset.mjs +5 -5
  338. package/dist/astro/routes/api/setup/index.d.mts.map +1 -1
  339. package/dist/astro/routes/api/setup/index.mjs +59 -50
  340. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  341. package/dist/astro/routes/api/setup/status.mjs +7 -7
  342. package/dist/astro/routes/api/site/domain-proof.d.mts +8 -0
  343. package/dist/astro/routes/api/site/domain-proof.d.mts.map +1 -0
  344. package/dist/astro/routes/api/site/domain-proof.mjs +33 -0
  345. package/dist/astro/routes/api/site/domain-proof.mjs.map +1 -0
  346. package/dist/astro/routes/api/snapshot.mjs +10 -10
  347. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +23 -23
  348. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +23 -23
  349. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +23 -23
  350. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +23 -23
  351. package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +22 -22
  352. package/dist/astro/routes/api/taxonomies/_name_.mjs +23 -23
  353. package/dist/astro/routes/api/taxonomies/bulk-tag.mjs +36 -36
  354. package/dist/astro/routes/api/taxonomies/index.mjs +23 -23
  355. package/dist/astro/routes/api/themes/preview.mjs +8 -8
  356. package/dist/astro/routes/api/typegen.mjs +7 -7
  357. package/dist/astro/routes/api/visual-editing/action-token.mjs +8 -8
  358. package/dist/astro/routes/api/visual-editing/content/_collection_/_id_/publish.mjs +20 -20
  359. package/dist/astro/routes/api/visual-editing/toolbar-labels.mjs +5 -5
  360. package/dist/astro/routes/api/well-known/auth.mjs +1 -1
  361. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  362. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  363. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +17 -17
  364. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +19 -19
  365. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +19 -19
  366. package/dist/astro/routes/api/widget-areas/_name_.mjs +9 -9
  367. package/dist/astro/routes/api/widget-areas/index.mjs +19 -19
  368. package/dist/astro/routes/api/widget-components.mjs +6 -6
  369. package/dist/astro/routes/robots.txt.mjs +7 -7
  370. package/dist/astro/routes/sitemap-_collection_.xml.d.mts.map +1 -1
  371. package/dist/astro/routes/sitemap-_collection_.xml.mjs +30 -13
  372. package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
  373. package/dist/astro/routes/sitemap.xml.d.mts.map +1 -1
  374. package/dist/astro/routes/sitemap.xml.mjs +14 -13
  375. package/dist/astro/routes/sitemap.xml.mjs.map +1 -1
  376. package/dist/astro/types.d.mts +30 -18
  377. package/dist/astro/types.d.mts.map +1 -1
  378. package/dist/audit-CCH3MA4Y.mjs +127 -0
  379. package/dist/audit-CCH3MA4Y.mjs.map +1 -0
  380. package/dist/auth/providers/github.d.mts +1 -1
  381. package/dist/auth/providers/google.d.mts +1 -1
  382. package/dist/auth/providers/microsoft.d.mts +27 -0
  383. package/dist/auth/providers/microsoft.d.mts.map +1 -0
  384. package/dist/auth/providers/microsoft.mjs +20 -0
  385. package/dist/auth/providers/microsoft.mjs.map +1 -0
  386. package/dist/authoritative-records-Dcs7l2H5.mjs +5183 -0
  387. package/dist/authoritative-records-Dcs7l2H5.mjs.map +1 -0
  388. package/dist/authorize-TdIGpRSE.mjs +49 -0
  389. package/dist/authorize-TdIGpRSE.mjs.map +1 -0
  390. package/dist/backup-CZt5Mzwx.mjs +246 -0
  391. package/dist/backup-CZt5Mzwx.mjs.map +1 -0
  392. package/dist/base64-BQBZwXCx.mjs +63 -0
  393. package/dist/base64-BQBZwXCx.mjs.map +1 -0
  394. package/dist/block-types-DHnkzE6u.mjs +132 -0
  395. package/dist/block-types-DHnkzE6u.mjs.map +1 -0
  396. package/dist/block-values-C4N_F2-G.mjs +175 -0
  397. package/dist/block-values-C4N_F2-G.mjs.map +1 -0
  398. package/dist/byline-BmVKOQ8l.mjs +909 -0
  399. package/dist/byline-BmVKOQ8l.mjs.map +1 -0
  400. package/dist/byline-fields-B8SDs3M_.mjs +123 -0
  401. package/dist/byline-fields-B8SDs3M_.mjs.map +1 -0
  402. package/dist/byline-fields-Bze3ZdAw.mjs +280 -0
  403. package/dist/byline-fields-Bze3ZdAw.mjs.map +1 -0
  404. package/dist/bylines-CHhR28r9.mjs +150 -0
  405. package/dist/bylines-CHhR28r9.mjs.map +1 -0
  406. package/dist/bylines-DZS4OflA.mjs +234 -0
  407. package/dist/bylines-DZS4OflA.mjs.map +1 -0
  408. package/dist/cache-x0z8t_m9.mjs +133 -0
  409. package/dist/cache-x0z8t_m9.mjs.map +1 -0
  410. package/dist/canonical-BUcKRYpG.mjs +174 -0
  411. package/dist/canonical-BUcKRYpG.mjs.map +1 -0
  412. package/dist/capture-triggers-C9JKoIdT.mjs +362 -0
  413. package/dist/capture-triggers-C9JKoIdT.mjs.map +1 -0
  414. package/dist/challenge-store-BHgdPVze.mjs +51 -0
  415. package/dist/challenge-store-BHgdPVze.mjs.map +1 -0
  416. package/dist/checksum-BYR5i7qe.mjs +162 -0
  417. package/dist/checksum-BYR5i7qe.mjs.map +1 -0
  418. package/dist/chrome-tags-BIKBMZYO.mjs +24 -0
  419. package/dist/chrome-tags-BIKBMZYO.mjs.map +1 -0
  420. package/dist/chunks-Dr5Vr0zA.mjs +25 -0
  421. package/dist/chunks-Dr5Vr0zA.mjs.map +1 -0
  422. package/dist/cli/index.mjs +54 -52
  423. package/dist/cli/index.mjs.map +1 -1
  424. package/dist/client/cf-access.d.mts +1 -1
  425. package/dist/client/index.d.mts +34 -34
  426. package/dist/client/index.d.mts.map +1 -1
  427. package/dist/client/index.mjs +2 -2
  428. package/dist/client/index.mjs.map +1 -1
  429. package/dist/collection-slugs-cache-B2Gs9Jwx.mjs +85 -0
  430. package/dist/collection-slugs-cache-B2Gs9Jwx.mjs.map +1 -0
  431. package/dist/comment-CWla6OHV.mjs +325 -0
  432. package/dist/comment-CWla6OHV.mjs.map +1 -0
  433. package/dist/comment-reaction-RZrcs5cW.mjs +85 -0
  434. package/dist/comment-reaction-RZrcs5cW.mjs.map +1 -0
  435. package/dist/comments-BzmIKVb2.mjs +99 -0
  436. package/dist/comments-BzmIKVb2.mjs.map +1 -0
  437. package/dist/comments-Da_dI2O1.mjs +195 -0
  438. package/dist/comments-Da_dI2O1.mjs.map +1 -0
  439. package/dist/components-oFXumpf5.mjs +112 -0
  440. package/dist/components-oFXumpf5.mjs.map +1 -0
  441. package/dist/config--wamYxBW.d.mts +39 -0
  442. package/dist/config--wamYxBW.d.mts.map +1 -0
  443. package/dist/config-Bo4eOOdV.mjs +228 -0
  444. package/dist/config-Bo4eOOdV.mjs.map +1 -0
  445. package/dist/config-C4-UxIk7.d.mts +6790 -0
  446. package/dist/config-C4-UxIk7.d.mts.map +1 -0
  447. package/dist/connection-UpzYxtE2.mjs +36 -0
  448. package/dist/connection-UpzYxtE2.mjs.map +1 -0
  449. package/dist/content-CcHrqkgp.mjs +2072 -0
  450. package/dist/content-CcHrqkgp.mjs.map +1 -0
  451. package/dist/content-list-query-Bxxdd9fv.d.mts +26 -0
  452. package/dist/content-list-query-Bxxdd9fv.d.mts.map +1 -0
  453. package/dist/content-repair-havk1F0-.mjs +442 -0
  454. package/dist/content-repair-havk1F0-.mjs.map +1 -0
  455. package/dist/context-CvhgHgSH.mjs +2043 -0
  456. package/dist/context-CvhgHgSH.mjs.map +1 -0
  457. package/dist/credits-CygUrPOi.mjs +110 -0
  458. package/dist/credits-CygUrPOi.mjs.map +1 -0
  459. package/dist/cron-C6ytTOLw.mjs +306 -0
  460. package/dist/cron-C6ytTOLw.mjs.map +1 -0
  461. package/dist/dashboard-7_FYfafc.mjs +151 -0
  462. package/dist/dashboard-7_FYfafc.mjs.map +1 -0
  463. package/dist/db/index.d.mts +3 -3
  464. package/dist/db/index.mjs +1 -1
  465. package/dist/db/index.mjs.map +1 -1
  466. package/dist/db/libsql-migrations.d.mts +1 -1
  467. package/dist/db/libsql-migrations.mjs +5 -5
  468. package/dist/db/libsql.d.mts +1 -1
  469. package/dist/db/postgres-migrations.d.mts +1 -1
  470. package/dist/db/postgres-migrations.mjs +4 -4
  471. package/dist/db/postgres.d.mts +1 -1
  472. package/dist/db/sqlite-migrations.d.mts +1 -1
  473. package/dist/db/sqlite-migrations.mjs +4 -4
  474. package/dist/db/sqlite.d.mts +1 -1
  475. package/dist/db/sqlite.mjs +1 -1
  476. package/dist/default-CHCUkV4Z.mjs +81 -0
  477. package/dist/default-CHCUkV4Z.mjs.map +1 -0
  478. package/dist/deferred-tasks-G0xhcJTl.d.mts +9 -0
  479. package/dist/deferred-tasks-G0xhcJTl.d.mts.map +1 -0
  480. package/dist/dev-typegen-Dtl8AfS6.mjs +29 -0
  481. package/dist/dev-typegen-Dtl8AfS6.mjs.map +1 -0
  482. package/dist/device-flow-CzDJmQ1q.mjs +521 -0
  483. package/dist/device-flow-CzDJmQ1q.mjs.map +1 -0
  484. package/dist/digest-xJmyPv2r.mjs +194 -0
  485. package/dist/digest-xJmyPv2r.mjs.map +1 -0
  486. package/dist/direct-executor-BXiYIVJ9.mjs +104 -0
  487. package/dist/direct-executor-BXiYIVJ9.mjs.map +1 -0
  488. package/dist/email-console-7ZPApLlm.mjs +50 -0
  489. package/dist/email-console-7ZPApLlm.mjs.map +1 -0
  490. package/dist/email-locale-DFdUA6UF.mjs +25 -0
  491. package/dist/email-locale-DFdUA6UF.mjs.map +1 -0
  492. package/dist/emdash-runtime-C4oFeVpN.mjs +4363 -0
  493. package/dist/emdash-runtime-C4oFeVpN.mjs.map +1 -0
  494. package/dist/enrich-Duu4sV3a.mjs +194 -0
  495. package/dist/enrich-Duu4sV3a.mjs.map +1 -0
  496. package/dist/entry-lock-B4n0HdQ_.mjs +163 -0
  497. package/dist/entry-lock-B4n0HdQ_.mjs.map +1 -0
  498. package/dist/entry-lock-BAAlKYrz.d.mts +4566 -0
  499. package/dist/entry-lock-BAAlKYrz.d.mts.map +1 -0
  500. package/dist/entry-lock-Cjz0h45X.mjs +1209 -0
  501. package/dist/entry-lock-Cjz0h45X.mjs.map +1 -0
  502. package/dist/env-types-4bkF6Bqg.mjs +38 -0
  503. package/dist/env-types-4bkF6Bqg.mjs.map +1 -0
  504. package/dist/error-DxgQdaZ_.mjs +90 -0
  505. package/dist/error-DxgQdaZ_.mjs.map +1 -0
  506. package/dist/errors-CjtE5aWF.mjs +120 -0
  507. package/dist/errors-CjtE5aWF.mjs.map +1 -0
  508. package/dist/errors-hskQ0C8G.mjs +479 -0
  509. package/dist/errors-hskQ0C8G.mjs.map +1 -0
  510. package/dist/escape-ESFhYzwG.mjs +9 -0
  511. package/dist/escape-ESFhYzwG.mjs.map +1 -0
  512. package/dist/fence-vc3aHgRA.mjs +162 -0
  513. package/dist/fence-vc3aHgRA.mjs.map +1 -0
  514. package/dist/field-defs-cache-zoow36nz.mjs +77 -0
  515. package/dist/field-defs-cache-zoow36nz.mjs.map +1 -0
  516. package/dist/field-map-JD1wG0Tq.mjs +83 -0
  517. package/dist/field-map-JD1wG0Tq.mjs.map +1 -0
  518. package/dist/fts-manager-WJCN8uU6.mjs +443 -0
  519. package/dist/fts-manager-WJCN8uU6.mjs.map +1 -0
  520. package/dist/gc-CsGZiKdH.mjs +75 -0
  521. package/dist/gc-CsGZiKdH.mjs.map +1 -0
  522. package/dist/hash-C4DFoJ7Z.mjs +37 -0
  523. package/dist/hash-C4DFoJ7Z.mjs.map +1 -0
  524. package/dist/hook-result-CXjtMk7Q.d.mts +31 -0
  525. package/dist/hook-result-CXjtMk7Q.d.mts.map +1 -0
  526. package/dist/hreflang-BAma7iRr.mjs +105 -0
  527. package/dist/hreflang-BAma7iRr.mjs.map +1 -0
  528. package/dist/hreflang-BsYo4x_M.d.mts +50 -0
  529. package/dist/hreflang-BsYo4x_M.d.mts.map +1 -0
  530. package/dist/http-route-dispatch-C8G7hAK6.mjs +342 -0
  531. package/dist/http-route-dispatch-C8G7hAK6.mjs.map +1 -0
  532. package/dist/identity-BwBKM1fH.mjs +35 -0
  533. package/dist/identity-BwBKM1fH.mjs.map +1 -0
  534. package/dist/import-DJ3Z2wxE.mjs +2021 -0
  535. package/dist/import-DJ3Z2wxE.mjs.map +1 -0
  536. package/dist/index-DYsn97dY.d.mts +623 -0
  537. package/dist/index-DYsn97dY.d.mts.map +1 -0
  538. package/dist/index.d.mts +24 -24
  539. package/dist/index.mjs +99 -98
  540. package/dist/init-lock-BGmew3r1.mjs +56 -0
  541. package/dist/init-lock-BGmew3r1.mjs.map +1 -0
  542. package/dist/install-finalization-DCFnFSHv.mjs +58 -0
  543. package/dist/install-finalization-DCFnFSHv.mjs.map +1 -0
  544. package/dist/keys-CGvAndSp.mjs +61 -0
  545. package/dist/keys-CGvAndSp.mjs.map +1 -0
  546. package/dist/kinds-DT1W4T4Y.mjs +974 -0
  547. package/dist/kinds-DT1W4T4Y.mjs.map +1 -0
  548. package/dist/lifecycle-BzglCSY0.mjs +25 -0
  549. package/dist/lifecycle-BzglCSY0.mjs.map +1 -0
  550. package/dist/load-DjUqmf3s.mjs +28 -0
  551. package/dist/load-DjUqmf3s.mjs.map +1 -0
  552. package/dist/loader-CetY659y.mjs +1355 -0
  553. package/dist/loader-CetY659y.mjs.map +1 -0
  554. package/dist/loops-DkT1zkPH.mjs +205 -0
  555. package/dist/loops-DkT1zkPH.mjs.map +1 -0
  556. package/dist/manifest-builder-BOFCd9rX.mjs +189 -0
  557. package/dist/manifest-builder-BOFCd9rX.mjs.map +1 -0
  558. package/dist/manifest-schema-BiFBPEsp.mjs +497 -0
  559. package/dist/manifest-schema-BiFBPEsp.mjs.map +1 -0
  560. package/dist/marketplace-CUlenvZm.mjs +257 -0
  561. package/dist/marketplace-CUlenvZm.mjs.map +1 -0
  562. package/dist/media/image-endpoint.d.mts +26 -2
  563. package/dist/media/image-endpoint.d.mts.map +1 -1
  564. package/dist/media/image-endpoint.mjs +73 -3
  565. package/dist/media/image-endpoint.mjs.map +1 -1
  566. package/dist/media/index.d.mts +1 -1
  567. package/dist/media/index.mjs +1 -1
  568. package/dist/media/local-runtime.d.mts +14 -14
  569. package/dist/media/local-runtime.mjs +7 -7
  570. package/dist/media-CRKsKlfK.mjs +416 -0
  571. package/dist/media-CRKsKlfK.mjs.map +1 -0
  572. package/dist/media-C_z7sRuC.mjs +294 -0
  573. package/dist/media-C_z7sRuC.mjs.map +1 -0
  574. package/dist/media-IT8hQleU.mjs +373 -0
  575. package/dist/media-IT8hQleU.mjs.map +1 -0
  576. package/dist/media-allowlist-Ct6wvnT2.mjs +45 -0
  577. package/dist/media-allowlist-Ct6wvnT2.mjs.map +1 -0
  578. package/dist/media-folders-DtMt-vAA.mjs +210 -0
  579. package/dist/media-folders-DtMt-vAA.mjs.map +1 -0
  580. package/dist/media-url-BpXLqCmf.mjs +43 -0
  581. package/dist/media-url-BpXLqCmf.mjs.map +1 -0
  582. package/dist/media-usage-DEPHZZ9X.mjs +282 -0
  583. package/dist/media-usage-DEPHZZ9X.mjs.map +1 -0
  584. package/dist/media-usage-activation-V2Siabtw.mjs +78 -0
  585. package/dist/media-usage-activation-V2Siabtw.mjs.map +1 -0
  586. package/dist/media-usage-work-Cv1OE59F.mjs +151 -0
  587. package/dist/media-usage-work-Cv1OE59F.mjs.map +1 -0
  588. package/dist/media-usage-work-cgDYGvrt.mjs +1015 -0
  589. package/dist/media-usage-work-cgDYGvrt.mjs.map +1 -0
  590. package/dist/menus-CvVYfsuk.mjs +753 -0
  591. package/dist/menus-CvVYfsuk.mjs.map +1 -0
  592. package/dist/menus-D1HC8npj.mjs +92 -0
  593. package/dist/menus-D1HC8npj.mjs.map +1 -0
  594. package/dist/menus-DL8IPIcm.mjs +3721 -0
  595. package/dist/menus-DL8IPIcm.mjs.map +1 -0
  596. package/dist/migrations/index.d.mts +1 -1
  597. package/dist/migrations/index.mjs +3 -3
  598. package/dist/mime-B5M9R53H.mjs +81 -0
  599. package/dist/mime-B5M9R53H.mjs.map +1 -0
  600. package/dist/mode-eieqAWLe.mjs +23 -0
  601. package/dist/mode-eieqAWLe.mjs.map +1 -0
  602. package/dist/node-sqlite-compat-VuyzJw1b.mjs +63 -0
  603. package/dist/node-sqlite-compat-VuyzJw1b.mjs.map +1 -0
  604. package/dist/oauth-authorization-B6ZJbMd2.mjs +274 -0
  605. package/dist/oauth-authorization-B6ZJbMd2.mjs.map +1 -0
  606. package/dist/oauth-clients-DVjRc9v3.mjs +266 -0
  607. package/dist/oauth-clients-DVjRc9v3.mjs.map +1 -0
  608. package/dist/oauth-state-store-CnmYYsVQ.mjs +50 -0
  609. package/dist/oauth-state-store-CnmYYsVQ.mjs.map +1 -0
  610. package/dist/oauth-user-lookup-Dz1_R5vT.mjs +26 -0
  611. package/dist/oauth-user-lookup-Dz1_R5vT.mjs.map +1 -0
  612. package/dist/object-cache/memory.d.mts +1 -1
  613. package/dist/options-BR9XZl7h.d.mts +81 -0
  614. package/dist/options-BR9XZl7h.d.mts.map +1 -0
  615. package/dist/ownership-COPyVysP.mjs +12 -0
  616. package/dist/ownership-COPyVysP.mjs.map +1 -0
  617. package/dist/page/index.d.mts +2 -2
  618. package/dist/page/index.mjs +6 -6
  619. package/dist/parse-BOGhrwSq.d.mts +174 -0
  620. package/dist/parse-BOGhrwSq.d.mts.map +1 -0
  621. package/dist/parse-Cn5aSk1N.mjs +81 -0
  622. package/dist/parse-Cn5aSk1N.mjs.map +1 -0
  623. package/dist/passkey-config-mMFRQUCj.mjs +46 -0
  624. package/dist/passkey-config-mMFRQUCj.mjs.map +1 -0
  625. package/dist/paths-BmcAdBo9.mjs +77 -0
  626. package/dist/paths-BmcAdBo9.mjs.map +1 -0
  627. package/dist/patterns-CMNnYCgf.mjs +159 -0
  628. package/dist/patterns-CMNnYCgf.mjs.map +1 -0
  629. package/dist/placeholder-nOep3_Gn.d.mts +306 -0
  630. package/dist/placeholder-nOep3_Gn.d.mts.map +1 -0
  631. package/dist/plan-CDY4K8CH.mjs +135 -0
  632. package/dist/plan-CDY4K8CH.mjs.map +1 -0
  633. package/dist/plugin-test-runtime.d.mts +14 -14
  634. package/dist/plugin-test-runtime.d.mts.map +1 -1
  635. package/dist/plugin-test-runtime.mjs +111 -110
  636. package/dist/plugin-types.d.mts +2 -2
  637. package/dist/plugin-utils.d.mts +12 -12
  638. package/dist/plugin-utils.mjs +1 -1
  639. package/dist/plugin-utils.mjs.map +1 -1
  640. package/dist/plugins/adapt-sandbox-entry.d.mts +12 -12
  641. package/dist/plugins/adapt-sandbox-entry.mjs +3 -3
  642. package/dist/plugins/host.d.mts +5 -5
  643. package/dist/plugins/host.mjs +45 -44
  644. package/dist/plugins-DFBpfbO3.mjs +261 -0
  645. package/dist/plugins-DFBpfbO3.mjs.map +1 -0
  646. package/dist/policy-B_2qnHSy.mjs +45 -0
  647. package/dist/policy-B_2qnHSy.mjs.map +1 -0
  648. package/dist/portable-text-Dc2YAusw.mjs +307 -0
  649. package/dist/portable-text-Dc2YAusw.mjs.map +1 -0
  650. package/dist/preview-BtzikJ9O.mjs +107 -0
  651. package/dist/preview-BtzikJ9O.mjs.map +1 -0
  652. package/dist/protocol-M268ox8L.d.mts +61 -0
  653. package/dist/protocol-M268ox8L.d.mts.map +1 -0
  654. package/dist/public-submission-BQZavgZU.mjs +251 -0
  655. package/dist/public-submission-BQZavgZU.mjs.map +1 -0
  656. package/dist/public-url-uT1Ok2Tu.mjs +101 -0
  657. package/dist/public-url-uT1Ok2Tu.mjs.map +1 -0
  658. package/dist/query-BGG3sDEd.mjs +1127 -0
  659. package/dist/query-BGG3sDEd.mjs.map +1 -0
  660. package/dist/query-C_CWlifp.mjs +41 -0
  661. package/dist/query-C_CWlifp.mjs.map +1 -0
  662. package/dist/range-CXK_kDbI.mjs +51 -0
  663. package/dist/range-CXK_kDbI.mjs.map +1 -0
  664. package/dist/rate-limit-Dvamvay-.mjs +127 -0
  665. package/dist/rate-limit-Dvamvay-.mjs.map +1 -0
  666. package/dist/receipt-BmQGzquP.mjs +43 -0
  667. package/dist/receipt-BmQGzquP.mjs.map +1 -0
  668. package/dist/redirect-B39kTQ_0.mjs +423 -0
  669. package/dist/redirect-B39kTQ_0.mjs.map +1 -0
  670. package/dist/redirect-BguJ00Pv.mjs +19 -0
  671. package/dist/redirect-BguJ00Pv.mjs.map +1 -0
  672. package/dist/redirects-C8G4BsfP.mjs +446 -0
  673. package/dist/redirects-C8G4BsfP.mjs.map +1 -0
  674. package/dist/redirects-DFT7Wr-e.mjs +148 -0
  675. package/dist/redirects-DFT7Wr-e.mjs.map +1 -0
  676. package/dist/registry/testing.mjs +3 -3
  677. package/dist/registry-DeZ27gVi.mjs +8374 -0
  678. package/dist/registry-DeZ27gVi.mjs.map +1 -0
  679. package/dist/relations-DBB1bRJW.mjs +776 -0
  680. package/dist/relations-DBB1bRJW.mjs.map +1 -0
  681. package/dist/request-context.d.mts +2 -2
  682. package/dist/request-context.mjs.map +1 -1
  683. package/dist/request-meta-BSvkhIBX.mjs +141 -0
  684. package/dist/request-meta-BSvkhIBX.mjs.map +1 -0
  685. package/dist/resolve-BxH64HN9.mjs +100 -0
  686. package/dist/resolve-BxH64HN9.mjs.map +1 -0
  687. package/dist/revision-BWEH2qQB.mjs +368 -0
  688. package/dist/revision-BWEH2qQB.mjs.map +1 -0
  689. package/dist/route-wire-CwbWB_6O.mjs +197 -0
  690. package/dist/route-wire-CwbWB_6O.mjs.map +1 -0
  691. package/dist/routes-BfIxc7ft.mjs +272 -0
  692. package/dist/routes-BfIxc7ft.mjs.map +1 -0
  693. package/dist/runner-DkeafPBA.d.mts +68 -0
  694. package/dist/runner-DkeafPBA.d.mts.map +1 -0
  695. package/dist/runner-wqt4BrIW.mjs +6383 -0
  696. package/dist/runner-wqt4BrIW.mjs.map +1 -0
  697. package/dist/runtime.d.mts +13 -13
  698. package/dist/runtime.mjs +3 -3
  699. package/dist/schema/project-env-types.mjs +38 -38
  700. package/dist/schema-BXIW-JHi.mjs +815 -0
  701. package/dist/schema-BXIW-JHi.mjs.map +1 -0
  702. package/dist/schema-I74JwYte.mjs +13 -0
  703. package/dist/schema-I74JwYte.mjs.map +1 -0
  704. package/dist/scopes-RWZ5ft_Z.mjs +34 -0
  705. package/dist/scopes-RWZ5ft_Z.mjs.map +1 -0
  706. package/dist/search-DxwagyWS.mjs +450 -0
  707. package/dist/search-DxwagyWS.mjs.map +1 -0
  708. package/dist/secrets-Cc_O3Kp-.mjs +369 -0
  709. package/dist/secrets-Cc_O3Kp-.mjs.map +1 -0
  710. package/dist/sections-Ccq9ZgyD.mjs +346 -0
  711. package/dist/sections-Ccq9ZgyD.mjs.map +1 -0
  712. package/dist/seed/index.d.mts +2 -2
  713. package/dist/seed/index.mjs +36 -36
  714. package/dist/seo/index.d.mts +3 -3
  715. package/dist/seo/index.mjs +23 -23
  716. package/dist/seo-CCnfPWAQ.mjs +133 -0
  717. package/dist/seo-CCnfPWAQ.mjs.map +1 -0
  718. package/dist/seo-QCtJ1jjV.mjs +188 -0
  719. package/dist/seo-QCtJ1jjV.mjs.map +1 -0
  720. package/dist/service-DX84s2rw.mjs +150 -0
  721. package/dist/service-DX84s2rw.mjs.map +1 -0
  722. package/dist/session-user-1dtJc2zv.mjs +61 -0
  723. package/dist/session-user-1dtJc2zv.mjs.map +1 -0
  724. package/dist/settings-B1jOxAIl.mjs +443 -0
  725. package/dist/settings-B1jOxAIl.mjs.map +1 -0
  726. package/dist/settings-Bc1IBl1t.mjs +66 -0
  727. package/dist/settings-Bc1IBl1t.mjs.map +1 -0
  728. package/dist/settings-GaZ5sVgg.mjs +73 -0
  729. package/dist/settings-GaZ5sVgg.mjs.map +1 -0
  730. package/dist/setup-complete-DidMAWJS.mjs +65 -0
  731. package/dist/setup-complete-DidMAWJS.mjs.map +1 -0
  732. package/dist/setup-nonce-DKl4S4c6.mjs +25 -0
  733. package/dist/setup-nonce-DKl4S4c6.mjs.map +1 -0
  734. package/dist/single-flight-cache-D0EaGbN1.mjs +104 -0
  735. package/dist/single-flight-cache-D0EaGbN1.mjs.map +1 -0
  736. package/dist/site-domain-CbI51Pc2.mjs +113 -0
  737. package/dist/site-domain-CbI51Pc2.mjs.map +1 -0
  738. package/dist/site-settings-CDdT4mVm.mjs +50 -0
  739. package/dist/site-settings-CDdT4mVm.mjs.map +1 -0
  740. package/dist/site-url-CrDZaAvo.mjs +45 -0
  741. package/dist/site-url-CrDZaAvo.mjs.map +1 -0
  742. package/dist/sitemap-name-W3BTor6X.mjs +26 -0
  743. package/dist/sitemap-name-W3BTor6X.mjs.map +1 -0
  744. package/dist/slug-BJhHKoOH.mjs +16 -0
  745. package/dist/slug-BJhHKoOH.mjs.map +1 -0
  746. package/dist/slugify-bu2-jKia.mjs +22 -0
  747. package/dist/slugify-bu2-jKia.mjs.map +1 -0
  748. package/dist/snapshot-BuI_2uDe.mjs +414 -0
  749. package/dist/snapshot-BuI_2uDe.mjs.map +1 -0
  750. package/dist/ssrf-LM_0wpGA.mjs +406 -0
  751. package/dist/ssrf-LM_0wpGA.mjs.map +1 -0
  752. package/dist/stage-DhzZuYaT.mjs +662 -0
  753. package/dist/stage-DhzZuYaT.mjs.map +1 -0
  754. package/dist/staged-DboEaWsV.mjs +686 -0
  755. package/dist/staged-DboEaWsV.mjs.map +1 -0
  756. package/dist/state-DCXDzjHq.mjs +178 -0
  757. package/dist/state-DCXDzjHq.mjs.map +1 -0
  758. package/dist/status-Dl85BK39.mjs +30 -0
  759. package/dist/status-Dl85BK39.mjs.map +1 -0
  760. package/dist/storage/local.d.mts +2 -2
  761. package/dist/storage/local.d.mts.map +1 -1
  762. package/dist/storage/local.mjs +12 -5
  763. package/dist/storage/local.mjs.map +1 -1
  764. package/dist/storage/s3.d.mts +4 -2
  765. package/dist/storage/s3.d.mts.map +1 -1
  766. package/dist/storage/s3.mjs +56 -9
  767. package/dist/storage/s3.mjs.map +1 -1
  768. package/dist/storage-query-BOBmeDMD.d.mts +1615 -0
  769. package/dist/storage-query-BOBmeDMD.d.mts.map +1 -0
  770. package/dist/tar-K40edFMt.mjs +449 -0
  771. package/dist/tar-K40edFMt.mjs.map +1 -0
  772. package/dist/target-BOighEY1.mjs +27 -0
  773. package/dist/target-BOighEY1.mjs.map +1 -0
  774. package/dist/taxonomies-DQSywBvX.mjs +697 -0
  775. package/dist/taxonomies-DQSywBvX.mjs.map +1 -0
  776. package/dist/taxonomies-Dj5eaobC.mjs +939 -0
  777. package/dist/taxonomies-Dj5eaobC.mjs.map +1 -0
  778. package/dist/taxonomy-QrNmTfIZ.mjs +550 -0
  779. package/dist/taxonomy-QrNmTfIZ.mjs.map +1 -0
  780. package/dist/tokens-j5L0as2H.mjs +171 -0
  781. package/dist/tokens-j5L0as2H.mjs.map +1 -0
  782. package/dist/transfer-CYEw8Tu4.mjs +8873 -0
  783. package/dist/transfer-CYEw8Tu4.mjs.map +1 -0
  784. package/dist/transfer-DDX10hkD.mjs +166 -0
  785. package/dist/transfer-DDX10hkD.mjs.map +1 -0
  786. package/dist/transfer-download-BAqq6GJv.mjs +33 -0
  787. package/dist/transfer-download-BAqq6GJv.mjs.map +1 -0
  788. package/dist/transformations-De44wqV_.mjs +196 -0
  789. package/dist/transformations-De44wqV_.mjs.map +1 -0
  790. package/dist/transport-1QjUhCRO.d.mts +42 -0
  791. package/dist/transport-1QjUhCRO.d.mts.map +1 -0
  792. package/dist/transport-Ds5pVX7p.mjs +136 -0
  793. package/dist/transport-Ds5pVX7p.mjs.map +1 -0
  794. package/dist/trusted-proxy-d4Tk6biT.mjs +51 -0
  795. package/dist/trusted-proxy-d4Tk6biT.mjs.map +1 -0
  796. package/dist/turnstile-DaFLtyrs.mjs +58 -0
  797. package/dist/turnstile-DaFLtyrs.mjs.map +1 -0
  798. package/dist/types-B4-1P2DK.d.mts +227 -0
  799. package/dist/types-B4-1P2DK.d.mts.map +1 -0
  800. package/dist/types-BKXA0f-c.mjs +16 -0
  801. package/dist/types-BKXA0f-c.mjs.map +1 -0
  802. package/dist/types-BiHcpUoO.d.mts +374 -0
  803. package/dist/types-BiHcpUoO.d.mts.map +1 -0
  804. package/dist/types-BihhvkyN.d.mts +85 -0
  805. package/dist/types-BihhvkyN.d.mts.map +1 -0
  806. package/dist/types-Bq9Wohd7.d.mts +363 -0
  807. package/dist/types-Bq9Wohd7.d.mts.map +1 -0
  808. package/dist/types-C-Xp0CcN.mjs +103 -0
  809. package/dist/types-C-Xp0CcN.mjs.map +1 -0
  810. package/dist/types-C4P7F1Vq.d.mts +1741 -0
  811. package/dist/types-C4P7F1Vq.d.mts.map +1 -0
  812. package/dist/types-CGaqwHw4.mjs +37 -0
  813. package/dist/types-CGaqwHw4.mjs.map +1 -0
  814. package/dist/types-Cj0_yZkn.mjs +18 -0
  815. package/dist/types-Cj0_yZkn.mjs.map +1 -0
  816. package/dist/types-D4ECCE4X.mjs +77 -0
  817. package/dist/types-D4ECCE4X.mjs.map +1 -0
  818. package/dist/types-DcRJp0vS.d.mts +125 -0
  819. package/dist/types-DcRJp0vS.d.mts.map +1 -0
  820. package/dist/types-Ff85fTzU.d.mts +289 -0
  821. package/dist/types-Ff85fTzU.d.mts.map +1 -0
  822. package/dist/types-UKr5Sf4y.d.mts +916 -0
  823. package/dist/types-UKr5Sf4y.d.mts.map +1 -0
  824. package/dist/types-YpN8hZW7.d.mts +204 -0
  825. package/dist/types-YpN8hZW7.d.mts.map +1 -0
  826. package/dist/update-check-DrlsK-tt.mjs +161 -0
  827. package/dist/update-check-DrlsK-tt.mjs.map +1 -0
  828. package/dist/upload-attempts-D8CPPvRU.mjs +32 -0
  829. package/dist/upload-attempts-D8CPPvRU.mjs.map +1 -0
  830. package/dist/url-pattern-Di4Nn3vP.mjs +41 -0
  831. package/dist/url-pattern-Di4Nn3vP.mjs.map +1 -0
  832. package/dist/user-B-ZdGjhI.mjs +171 -0
  833. package/dist/user-B-ZdGjhI.mjs.map +1 -0
  834. package/dist/utils-D7wL3r6-.mjs +437 -0
  835. package/dist/utils-D7wL3r6-.mjs.map +1 -0
  836. package/dist/validate-BSZbSUDk.mjs +524 -0
  837. package/dist/validate-BSZbSUDk.mjs.map +1 -0
  838. package/dist/validate-Dfv0DpZi.d.mts +468 -0
  839. package/dist/validate-Dfv0DpZi.d.mts.map +1 -0
  840. package/dist/validation-DRyC_Vm5.mjs +219 -0
  841. package/dist/validation-DRyC_Vm5.mjs.map +1 -0
  842. package/dist/version-C4kz0PcO.mjs +7 -0
  843. package/dist/version-C4kz0PcO.mjs.map +1 -0
  844. package/dist/version-jhs2BRtl.mjs +9 -0
  845. package/dist/version-jhs2BRtl.mjs.map +1 -0
  846. package/dist/video-DReZ48Un.mjs +54 -0
  847. package/dist/video-DReZ48Un.mjs.map +1 -0
  848. package/dist/virtual-modules-D0e6VbEv.mjs +516 -0
  849. package/dist/virtual-modules-D0e6VbEv.mjs.map +1 -0
  850. package/dist/widgets-BkcMzKct.mjs +121 -0
  851. package/dist/widgets-BkcMzKct.mjs.map +1 -0
  852. package/dist/wxr-taxonomies-BxI0CbTg.mjs +453 -0
  853. package/dist/wxr-taxonomies-BxI0CbTg.mjs.map +1 -0
  854. package/dist/wxr-taxonomies-Dad33GJi.d.mts +34 -0
  855. package/dist/wxr-taxonomies-Dad33GJi.d.mts.map +1 -0
  856. package/dist/zod-generator-BQilAboC.mjs +424 -0
  857. package/dist/zod-generator-BQilAboC.mjs.map +1 -0
  858. package/package.json +19 -10
  859. package/src/api/errors.ts +12 -0
  860. package/src/api/handlers/api-tokens.ts +12 -6
  861. package/src/api/handlers/bylines.ts +24 -3
  862. package/src/api/handlers/calendar.ts +246 -0
  863. package/src/api/handlers/content.ts +69 -13
  864. package/src/api/handlers/domain-move-notice.ts +94 -0
  865. package/src/api/handlers/index.ts +2 -0
  866. package/src/api/handlers/manifest.ts +5 -0
  867. package/src/api/handlers/media-allowlist.ts +1 -0
  868. package/src/api/handlers/media-upload.ts +13 -10
  869. package/src/api/handlers/media-usage.ts +2 -1
  870. package/src/api/handlers/media.ts +1 -0
  871. package/src/api/handlers/plugins.ts +21 -29
  872. package/src/api/handlers/redirects.ts +6 -6
  873. package/src/api/handlers/seo.ts +155 -59
  874. package/src/api/handlers/settings.ts +31 -1
  875. package/src/api/handlers/site-domain.ts +143 -0
  876. package/src/api/openapi/document.ts +2 -2
  877. package/src/api/public-url.ts +1 -1
  878. package/src/api/schemas/bylines.ts +3 -1
  879. package/src/api/schemas/content.ts +36 -0
  880. package/src/api/schemas/media-usage.ts +7 -1
  881. package/src/api/schemas/media.ts +12 -0
  882. package/src/api/site-url.ts +53 -5
  883. package/src/api/types.ts +2 -0
  884. package/src/astro/image-endpoint.ts +46 -4
  885. package/src/astro/integration/admin-locales.ts +168 -0
  886. package/src/astro/integration/dev-env.ts +84 -0
  887. package/src/astro/integration/index.ts +67 -10
  888. package/src/astro/integration/routes.ts +25 -0
  889. package/src/astro/integration/runtime.ts +15 -0
  890. package/src/astro/integration/vite-config.ts +29 -1
  891. package/src/astro/middleware/auth.ts +16 -3
  892. package/src/astro/middleware/csp.ts +2 -0
  893. package/src/astro/middleware/redirect.ts +47 -21
  894. package/src/astro/middleware/request-context.ts +64 -31
  895. package/src/astro/middleware.ts +150 -26
  896. package/src/astro/prefetch.ts +10 -3
  897. package/src/astro/routes/api/admin/bylines/[id]/translations.ts +0 -5
  898. package/src/astro/routes/api/admin/bylines/index.ts +1 -1
  899. package/src/astro/routes/api/admin/plugins/[id]/index.ts +1 -0
  900. package/src/astro/routes/api/auth/handover.ts +58 -0
  901. package/src/astro/routes/api/auth/magic-link/verify.ts +11 -3
  902. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +56 -4
  903. package/src/astro/routes/api/auth/oauth/[provider].ts +37 -4
  904. package/src/astro/routes/api/auth/passkey/options.ts +10 -2
  905. package/src/astro/routes/api/calendar.ts +35 -0
  906. package/src/astro/routes/api/import/wordpress/execute.ts +19 -0
  907. package/src/astro/routes/api/import/wordpress/rewrite-url-helpers.ts +80 -42
  908. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +15 -0
  909. package/src/astro/routes/api/manifest.ts +10 -0
  910. package/src/astro/routes/api/media/[id]/confirm.ts +19 -0
  911. package/src/astro/routes/api/media/file/[...key].ts +60 -12
  912. package/src/astro/routes/api/media/upload-url.ts +35 -7
  913. package/src/astro/routes/api/media.ts +24 -5
  914. package/src/astro/routes/api/schema/index.ts +1 -1
  915. package/src/astro/routes/api/settings/domain-notify.ts +76 -0
  916. package/src/astro/routes/api/settings/domain.ts +63 -0
  917. package/src/astro/routes/api/setup/index.ts +21 -2
  918. package/src/astro/routes/api/site/domain-proof.ts +28 -0
  919. package/src/astro/routes/sitemap-[collection].xml.ts +31 -8
  920. package/src/astro/routes/sitemap.xml.ts +12 -10
  921. package/src/astro/types.ts +13 -1
  922. package/src/auth/providers/microsoft-admin.tsx +31 -0
  923. package/src/auth/providers/microsoft.ts +47 -0
  924. package/src/auth/rate-limit.ts +18 -13
  925. package/src/auth/types.ts +7 -0
  926. package/src/aws-sdk.d.ts +2 -1
  927. package/src/cleanup.ts +26 -0
  928. package/src/cli/commands/export-seed.ts +2 -0
  929. package/src/cli/commands/seed.ts +5 -4
  930. package/src/client/index.ts +6 -1
  931. package/src/comments/public-submission.ts +15 -8
  932. package/src/comments/turnstile.ts +6 -1
  933. package/src/components/CommentForm.astro +48 -8
  934. package/src/components/Comments.astro +50 -28
  935. package/src/components/EmDashImage.astro +6 -4
  936. package/src/components/Gallery.astro +4 -3
  937. package/src/components/HtmlBlock.astro +17 -6
  938. package/src/components/HtmlBlockFrame.astro +63 -0
  939. package/src/components/Iframe.astro +141 -0
  940. package/src/components/Image.astro +4 -3
  941. package/src/components/InlinePortableTextEditor.tsx +156 -33
  942. package/src/components/Video.astro +100 -0
  943. package/src/components/WebMcpSearch.astro +80 -0
  944. package/src/components/index.ts +8 -2
  945. package/src/components/inline-code-block.tsx +8 -0
  946. package/src/components/webmcp-search.ts +138 -0
  947. package/src/components/widgets/RecentPosts.astro +57 -17
  948. package/src/config/secrets.ts +27 -15
  949. package/src/content/converters/mark-safety.ts +3 -3
  950. package/src/content/converters/portable-text-identity.ts +2 -0
  951. package/src/content/converters/portable-text-to-prosemirror.ts +43 -2
  952. package/src/content/converters/prosemirror-to-portable-text.ts +44 -2
  953. package/src/content/converters/types.ts +46 -0
  954. package/src/content/converters/video.ts +77 -0
  955. package/src/database/content-datetime.ts +11 -4
  956. package/src/database/datetime-storage.ts +1 -1
  957. package/src/database/migrations/090_redirect_enable_loop_guard.ts +70 -0
  958. package/src/database/migrations/091_redirect_artifacts.ts +141 -0
  959. package/src/database/migrations/runner.ts +17 -1
  960. package/src/database/repositories/comment.ts +17 -3
  961. package/src/database/repositories/content.ts +103 -29
  962. package/src/database/repositories/plugin-storage.ts +28 -19
  963. package/src/database/repositories/redirect.ts +30 -3
  964. package/src/database/repositories/taxonomy.ts +61 -46
  965. package/src/database/repositories/types.ts +2 -0
  966. package/src/database/request-scoped.ts +34 -0
  967. package/src/database/types.ts +23 -0
  968. package/src/db/adapters.ts +2 -0
  969. package/src/emdash-runtime.ts +245 -71
  970. package/src/import/menus.ts +3 -0
  971. package/src/import/sources/wordpress-plugin.ts +3 -2
  972. package/src/import/utils.ts +2 -3
  973. package/src/index.ts +4 -0
  974. package/src/loader.ts +360 -98
  975. package/src/mcp/server.ts +12 -4
  976. package/src/media/image-endpoint.ts +101 -1
  977. package/src/media/mime.ts +21 -0
  978. package/src/media/responsive.ts +14 -7
  979. package/src/media/usage/extractor.ts +19 -2
  980. package/src/media/usage/types.ts +5 -1
  981. package/src/menus/index.ts +30 -26
  982. package/src/plugin-utils.ts +1 -1
  983. package/src/plugins/context.ts +3 -7
  984. package/src/plugins/hooks.ts +11 -1
  985. package/src/plugins/http-route-dispatch.ts +5 -1
  986. package/src/plugins/manager.ts +12 -2
  987. package/src/plugins/manifest-schema.ts +1 -0
  988. package/src/plugins/route-error.ts +14 -0
  989. package/src/plugins/routes.ts +2 -2
  990. package/src/plugins/state.ts +36 -0
  991. package/src/plugins/types.ts +22 -0
  992. package/src/query.ts +20 -29
  993. package/src/redirects/artifacts.ts +460 -0
  994. package/src/redirects/cache.ts +76 -41
  995. package/src/redirects/patterns.ts +26 -2
  996. package/src/request-context.ts +1 -1
  997. package/src/schema/registry.ts +7 -4
  998. package/src/schema/slug.ts +19 -0
  999. package/src/search/text-extraction.ts +6 -1
  1000. package/src/seed/apply.ts +65 -57
  1001. package/src/seed/validate.ts +31 -0
  1002. package/src/seo/sitemap-name.ts +22 -0
  1003. package/src/settings/index.ts +17 -12
  1004. package/src/storage/index.ts +2 -0
  1005. package/src/storage/local.ts +10 -2
  1006. package/src/storage/range.ts +54 -0
  1007. package/src/storage/s3.ts +52 -8
  1008. package/src/storage/types.ts +30 -3
  1009. package/src/transfer/format/columns.ts +3 -0
  1010. package/src/transfer/import/rebuild.ts +17 -2
  1011. package/src/transfer/import/scaffold.ts +16 -9
  1012. package/src/ui.ts +2 -0
  1013. package/src/utils/sanitize.ts +14 -9
  1014. package/src/utils/slugify.ts +6 -1
  1015. package/src/virtual-modules.d.ts +3 -0
  1016. package/src/visual-editing/toolbar.ts +80 -35
  1017. package/src/widgets/components.ts +4 -0
  1018. package/src/widgets/index.ts +74 -82
  1019. package/src/widgets/recent-posts.ts +62 -0
  1020. package/dist/action-token-D2KE1Rqf.mjs +0 -26
  1021. package/dist/action-token-D2KE1Rqf.mjs.map +0 -1
  1022. package/dist/activation-DDuqAat2.mjs +0 -387
  1023. package/dist/activation-DDuqAat2.mjs.map +0 -1
  1024. package/dist/adapters-Ds-1Pmkm.d.mts +0 -171
  1025. package/dist/adapters-Ds-1Pmkm.d.mts.map +0 -1
  1026. package/dist/allowed-origins-D78QvbTO.mjs +0 -116
  1027. package/dist/allowed-origins-D78QvbTO.mjs.map +0 -1
  1028. package/dist/api-BBZlif0y.mjs +0 -4974
  1029. package/dist/api-BBZlif0y.mjs.map +0 -1
  1030. package/dist/api-tokens-BGbfWZVO.mjs +0 -166
  1031. package/dist/api-tokens-BGbfWZVO.mjs.map +0 -1
  1032. package/dist/apply-CDNV4JJ-.mjs +0 -1672
  1033. package/dist/apply-CDNV4JJ-.mjs.map +0 -1
  1034. package/dist/approvals-CVvJSEBF.mjs +0 -460
  1035. package/dist/approvals-CVvJSEBF.mjs.map +0 -1
  1036. package/dist/artifact-fetch-CGK0DQLj.mjs +0 -659
  1037. package/dist/artifact-fetch-CGK0DQLj.mjs.map +0 -1
  1038. package/dist/audit-BszbE_BP.mjs +0 -127
  1039. package/dist/audit-BszbE_BP.mjs.map +0 -1
  1040. package/dist/authoritative-records-ChM8dTeV.mjs +0 -5172
  1041. package/dist/authoritative-records-ChM8dTeV.mjs.map +0 -1
  1042. package/dist/authorize-CTGywswM.mjs +0 -49
  1043. package/dist/authorize-CTGywswM.mjs.map +0 -1
  1044. package/dist/backup-CDrPP8fI.mjs +0 -246
  1045. package/dist/backup-CDrPP8fI.mjs.map +0 -1
  1046. package/dist/base64-DvnNgP7e.mjs +0 -63
  1047. package/dist/base64-DvnNgP7e.mjs.map +0 -1
  1048. package/dist/block-types-nNEaPkvx.mjs +0 -132
  1049. package/dist/block-types-nNEaPkvx.mjs.map +0 -1
  1050. package/dist/block-values-Bho1nsZF.mjs +0 -175
  1051. package/dist/block-values-Bho1nsZF.mjs.map +0 -1
  1052. package/dist/byline-02tu_Uc-.mjs +0 -909
  1053. package/dist/byline-02tu_Uc-.mjs.map +0 -1
  1054. package/dist/byline-fields-BYbTptTU.mjs +0 -280
  1055. package/dist/byline-fields-BYbTptTU.mjs.map +0 -1
  1056. package/dist/byline-fields-BjQH-1z9.mjs +0 -123
  1057. package/dist/byline-fields-BjQH-1z9.mjs.map +0 -1
  1058. package/dist/bylines-VytjeHBI.mjs +0 -214
  1059. package/dist/bylines-VytjeHBI.mjs.map +0 -1
  1060. package/dist/bylines-gyWBWUUc.mjs +0 -150
  1061. package/dist/bylines-gyWBWUUc.mjs.map +0 -1
  1062. package/dist/cache-CPanL9IO.mjs +0 -114
  1063. package/dist/cache-CPanL9IO.mjs.map +0 -1
  1064. package/dist/canonical-CAecl1UQ.mjs +0 -174
  1065. package/dist/canonical-CAecl1UQ.mjs.map +0 -1
  1066. package/dist/capture-triggers-CdZfvHtF.mjs +0 -362
  1067. package/dist/capture-triggers-CdZfvHtF.mjs.map +0 -1
  1068. package/dist/challenge-store-VePApay4.mjs +0 -51
  1069. package/dist/challenge-store-VePApay4.mjs.map +0 -1
  1070. package/dist/checksum-BSIhEAYW.mjs +0 -162
  1071. package/dist/checksum-BSIhEAYW.mjs.map +0 -1
  1072. package/dist/chrome-tags-CKELm-bo.mjs +0 -24
  1073. package/dist/chrome-tags-CKELm-bo.mjs.map +0 -1
  1074. package/dist/chunks-DBmkKmBH.mjs +0 -25
  1075. package/dist/chunks-DBmkKmBH.mjs.map +0 -1
  1076. package/dist/collection-slugs-cache-BNRAPNiu.mjs +0 -85
  1077. package/dist/collection-slugs-cache-BNRAPNiu.mjs.map +0 -1
  1078. package/dist/comment-Cq1UYN8_.mjs +0 -313
  1079. package/dist/comment-Cq1UYN8_.mjs.map +0 -1
  1080. package/dist/comment-reaction-B5sZzSoA.mjs +0 -85
  1081. package/dist/comment-reaction-B5sZzSoA.mjs.map +0 -1
  1082. package/dist/comments-B3F-ETyh.mjs +0 -195
  1083. package/dist/comments-B3F-ETyh.mjs.map +0 -1
  1084. package/dist/comments-zrTgWBSg.mjs +0 -99
  1085. package/dist/comments-zrTgWBSg.mjs.map +0 -1
  1086. package/dist/components-Dbf4Eq16.mjs +0 -108
  1087. package/dist/components-Dbf4Eq16.mjs.map +0 -1
  1088. package/dist/config-BCM5sQP7.d.mts +0 -6688
  1089. package/dist/config-BCM5sQP7.d.mts.map +0 -1
  1090. package/dist/config-CXPcy2iY.mjs +0 -228
  1091. package/dist/config-CXPcy2iY.mjs.map +0 -1
  1092. package/dist/config-CZkZBuBK.d.mts +0 -39
  1093. package/dist/config-CZkZBuBK.d.mts.map +0 -1
  1094. package/dist/connection-C5GIciX9.mjs +0 -36
  1095. package/dist/connection-C5GIciX9.mjs.map +0 -1
  1096. package/dist/content-RzEXY3QX.mjs +0 -2020
  1097. package/dist/content-RzEXY3QX.mjs.map +0 -1
  1098. package/dist/content-list-query-B6P37Ug_.d.mts +0 -26
  1099. package/dist/content-list-query-B6P37Ug_.d.mts.map +0 -1
  1100. package/dist/content-repair-BDVsYXWK.mjs +0 -442
  1101. package/dist/content-repair-BDVsYXWK.mjs.map +0 -1
  1102. package/dist/context-BpTIzY5h.mjs +0 -2026
  1103. package/dist/context-BpTIzY5h.mjs.map +0 -1
  1104. package/dist/credits-JOE92Gr7.mjs +0 -110
  1105. package/dist/credits-JOE92Gr7.mjs.map +0 -1
  1106. package/dist/cron-ktyapbdB.mjs +0 -306
  1107. package/dist/cron-ktyapbdB.mjs.map +0 -1
  1108. package/dist/dashboard-BoV4pwPP.mjs +0 -151
  1109. package/dist/dashboard-BoV4pwPP.mjs.map +0 -1
  1110. package/dist/default-B7KnYwtQ.mjs +0 -81
  1111. package/dist/default-B7KnYwtQ.mjs.map +0 -1
  1112. package/dist/deferred-tasks-eaUIqXj8.d.mts +0 -9
  1113. package/dist/deferred-tasks-eaUIqXj8.d.mts.map +0 -1
  1114. package/dist/dev-typegen-8JlYYFno.mjs +0 -29
  1115. package/dist/dev-typegen-8JlYYFno.mjs.map +0 -1
  1116. package/dist/device-flow-BlV_z24g.mjs +0 -521
  1117. package/dist/device-flow-BlV_z24g.mjs.map +0 -1
  1118. package/dist/digest-DiYAoZpf.mjs +0 -194
  1119. package/dist/digest-DiYAoZpf.mjs.map +0 -1
  1120. package/dist/direct-executor-Db8xA3zg.mjs +0 -104
  1121. package/dist/direct-executor-Db8xA3zg.mjs.map +0 -1
  1122. package/dist/email-console-Dm78wSmg.mjs +0 -50
  1123. package/dist/email-console-Dm78wSmg.mjs.map +0 -1
  1124. package/dist/email-locale-B4X9LXe9.mjs +0 -25
  1125. package/dist/email-locale-B4X9LXe9.mjs.map +0 -1
  1126. package/dist/emdash-runtime-BakQ6io7.mjs +0 -4239
  1127. package/dist/emdash-runtime-BakQ6io7.mjs.map +0 -1
  1128. package/dist/enrich-BDdVNeDK.mjs +0 -194
  1129. package/dist/enrich-BDdVNeDK.mjs.map +0 -1
  1130. package/dist/entry-lock-DY8B-xcr.d.mts +0 -4544
  1131. package/dist/entry-lock-DY8B-xcr.d.mts.map +0 -1
  1132. package/dist/entry-lock-DrUAdTzf.mjs +0 -163
  1133. package/dist/entry-lock-DrUAdTzf.mjs.map +0 -1
  1134. package/dist/entry-lock-yIkBduqJ.mjs +0 -1180
  1135. package/dist/entry-lock-yIkBduqJ.mjs.map +0 -1
  1136. package/dist/env-types-DkHYR2pl.mjs +0 -38
  1137. package/dist/env-types-DkHYR2pl.mjs.map +0 -1
  1138. package/dist/error-DIYXAVk4.mjs +0 -90
  1139. package/dist/error-DIYXAVk4.mjs.map +0 -1
  1140. package/dist/errors-CNvATh_7.mjs +0 -469
  1141. package/dist/errors-CNvATh_7.mjs.map +0 -1
  1142. package/dist/errors-CjXd1LgH.mjs +0 -120
  1143. package/dist/errors-CjXd1LgH.mjs.map +0 -1
  1144. package/dist/escape-B1GM0yJU.mjs +0 -9
  1145. package/dist/escape-B1GM0yJU.mjs.map +0 -1
  1146. package/dist/fence-8MaKKWhp.mjs +0 -162
  1147. package/dist/fence-8MaKKWhp.mjs.map +0 -1
  1148. package/dist/field-defs-cache-DEkFYCDE.mjs +0 -77
  1149. package/dist/field-defs-cache-DEkFYCDE.mjs.map +0 -1
  1150. package/dist/field-map-D7ioN0r0.mjs +0 -83
  1151. package/dist/field-map-D7ioN0r0.mjs.map +0 -1
  1152. package/dist/fts-manager-DU03MxWu.mjs +0 -443
  1153. package/dist/fts-manager-DU03MxWu.mjs.map +0 -1
  1154. package/dist/gc-DMiE7ezk.mjs +0 -75
  1155. package/dist/gc-DMiE7ezk.mjs.map +0 -1
  1156. package/dist/hash-CY4QdeMJ.mjs +0 -37
  1157. package/dist/hash-CY4QdeMJ.mjs.map +0 -1
  1158. package/dist/hook-result-Cb67D3P3.d.mts +0 -31
  1159. package/dist/hook-result-Cb67D3P3.d.mts.map +0 -1
  1160. package/dist/hreflang-Bterc3b7.mjs +0 -105
  1161. package/dist/hreflang-Bterc3b7.mjs.map +0 -1
  1162. package/dist/hreflang-uIBOE5yg.d.mts +0 -50
  1163. package/dist/hreflang-uIBOE5yg.d.mts.map +0 -1
  1164. package/dist/http-route-dispatch-DubjTibD.mjs +0 -339
  1165. package/dist/http-route-dispatch-DubjTibD.mjs.map +0 -1
  1166. package/dist/identity-DJ-5AcdG.mjs +0 -35
  1167. package/dist/identity-DJ-5AcdG.mjs.map +0 -1
  1168. package/dist/import-DEfuIgRz.mjs +0 -2020
  1169. package/dist/import-DEfuIgRz.mjs.map +0 -1
  1170. package/dist/index-xCy3j0f6.d.mts +0 -623
  1171. package/dist/index-xCy3j0f6.d.mts.map +0 -1
  1172. package/dist/init-lock-4eRRB4Le.mjs +0 -56
  1173. package/dist/init-lock-4eRRB4Le.mjs.map +0 -1
  1174. package/dist/install-finalization-DeGRbaVa.mjs +0 -58
  1175. package/dist/install-finalization-DeGRbaVa.mjs.map +0 -1
  1176. package/dist/keys-DWwMxdZh.mjs +0 -61
  1177. package/dist/keys-DWwMxdZh.mjs.map +0 -1
  1178. package/dist/kinds-D1A-00-F.mjs +0 -974
  1179. package/dist/kinds-D1A-00-F.mjs.map +0 -1
  1180. package/dist/lifecycle-BWtBGNEn.mjs +0 -25
  1181. package/dist/lifecycle-BWtBGNEn.mjs.map +0 -1
  1182. package/dist/load-BR1NEa_3.mjs +0 -28
  1183. package/dist/load-BR1NEa_3.mjs.map +0 -1
  1184. package/dist/loader-D4edocWH.mjs +0 -1197
  1185. package/dist/loader-D4edocWH.mjs.map +0 -1
  1186. package/dist/loops-DLaHFrvI.mjs +0 -205
  1187. package/dist/loops-DLaHFrvI.mjs.map +0 -1
  1188. package/dist/manifest-builder-8jsSLR7s.mjs +0 -189
  1189. package/dist/manifest-builder-8jsSLR7s.mjs.map +0 -1
  1190. package/dist/manifest-schema-DcZjRNEv.mjs +0 -496
  1191. package/dist/manifest-schema-DcZjRNEv.mjs.map +0 -1
  1192. package/dist/marketplace-CNxjq10S.mjs +0 -257
  1193. package/dist/marketplace-CNxjq10S.mjs.map +0 -1
  1194. package/dist/media-3v7mqAVX.mjs +0 -294
  1195. package/dist/media-3v7mqAVX.mjs.map +0 -1
  1196. package/dist/media-Bs6qmlVl.mjs +0 -408
  1197. package/dist/media-Bs6qmlVl.mjs.map +0 -1
  1198. package/dist/media-CputjGE1.mjs +0 -373
  1199. package/dist/media-CputjGE1.mjs.map +0 -1
  1200. package/dist/media-allowlist-isqF1gAh.mjs +0 -44
  1201. package/dist/media-allowlist-isqF1gAh.mjs.map +0 -1
  1202. package/dist/media-folders-D2VdVBf6.mjs +0 -210
  1203. package/dist/media-folders-D2VdVBf6.mjs.map +0 -1
  1204. package/dist/media-url-DbkMY3y6.mjs +0 -43
  1205. package/dist/media-url-DbkMY3y6.mjs.map +0 -1
  1206. package/dist/media-usage-activation-BaT4AlNI.mjs +0 -78
  1207. package/dist/media-usage-activation-BaT4AlNI.mjs.map +0 -1
  1208. package/dist/media-usage-v43L0kR_.mjs +0 -282
  1209. package/dist/media-usage-v43L0kR_.mjs.map +0 -1
  1210. package/dist/media-usage-work-CY8gIeU-.mjs +0 -151
  1211. package/dist/media-usage-work-CY8gIeU-.mjs.map +0 -1
  1212. package/dist/media-usage-work-VO108Q9E.mjs +0 -1015
  1213. package/dist/media-usage-work-VO108Q9E.mjs.map +0 -1
  1214. package/dist/menus-BYyeRemR.mjs +0 -3647
  1215. package/dist/menus-BYyeRemR.mjs.map +0 -1
  1216. package/dist/menus-mC_XTxD7.mjs +0 -92
  1217. package/dist/menus-mC_XTxD7.mjs.map +0 -1
  1218. package/dist/menus-trvc6_fW.mjs +0 -753
  1219. package/dist/menus-trvc6_fW.mjs.map +0 -1
  1220. package/dist/mime-IZf-LHIS.mjs +0 -36
  1221. package/dist/mime-IZf-LHIS.mjs.map +0 -1
  1222. package/dist/mode-B9-gMXL9.mjs +0 -23
  1223. package/dist/mode-B9-gMXL9.mjs.map +0 -1
  1224. package/dist/node-sqlite-compat-DztcWs4y.mjs +0 -63
  1225. package/dist/node-sqlite-compat-DztcWs4y.mjs.map +0 -1
  1226. package/dist/oauth-authorization-BIHy9SGU.mjs +0 -274
  1227. package/dist/oauth-authorization-BIHy9SGU.mjs.map +0 -1
  1228. package/dist/oauth-clients-BZWZvKXa.mjs +0 -266
  1229. package/dist/oauth-clients-BZWZvKXa.mjs.map +0 -1
  1230. package/dist/oauth-state-store-CxXTjXdI.mjs +0 -50
  1231. package/dist/oauth-state-store-CxXTjXdI.mjs.map +0 -1
  1232. package/dist/oauth-user-lookup-GWojfxRl.mjs +0 -26
  1233. package/dist/oauth-user-lookup-GWojfxRl.mjs.map +0 -1
  1234. package/dist/options-DRF6dlXY.d.mts +0 -81
  1235. package/dist/options-DRF6dlXY.d.mts.map +0 -1
  1236. package/dist/ownership-Bd77AmJz.mjs +0 -12
  1237. package/dist/ownership-Bd77AmJz.mjs.map +0 -1
  1238. package/dist/parse-5Y0W39xR.mjs +0 -81
  1239. package/dist/parse-5Y0W39xR.mjs.map +0 -1
  1240. package/dist/parse-BPblgoa-.d.mts +0 -172
  1241. package/dist/parse-BPblgoa-.d.mts.map +0 -1
  1242. package/dist/passkey-config-PiQ5cBnt.mjs +0 -46
  1243. package/dist/passkey-config-PiQ5cBnt.mjs.map +0 -1
  1244. package/dist/paths-DkS74lBM.mjs +0 -77
  1245. package/dist/paths-DkS74lBM.mjs.map +0 -1
  1246. package/dist/patterns-DyZJ67I7.mjs +0 -140
  1247. package/dist/patterns-DyZJ67I7.mjs.map +0 -1
  1248. package/dist/placeholder-Bez2282x.d.mts +0 -306
  1249. package/dist/placeholder-Bez2282x.d.mts.map +0 -1
  1250. package/dist/plan-Lw7iGnGD.mjs +0 -135
  1251. package/dist/plan-Lw7iGnGD.mjs.map +0 -1
  1252. package/dist/plugins-D7xSh1Lu.mjs +0 -277
  1253. package/dist/plugins-D7xSh1Lu.mjs.map +0 -1
  1254. package/dist/policy-DMRiMdLn.mjs +0 -45
  1255. package/dist/policy-DMRiMdLn.mjs.map +0 -1
  1256. package/dist/portable-text-kcyN3F-a.mjs +0 -307
  1257. package/dist/portable-text-kcyN3F-a.mjs.map +0 -1
  1258. package/dist/preview--2VDLaND.mjs +0 -107
  1259. package/dist/preview--2VDLaND.mjs.map +0 -1
  1260. package/dist/protocol-FvwLA8P-.d.mts +0 -61
  1261. package/dist/protocol-FvwLA8P-.d.mts.map +0 -1
  1262. package/dist/public-submission-BRyK76U8.mjs +0 -250
  1263. package/dist/public-submission-BRyK76U8.mjs.map +0 -1
  1264. package/dist/public-url-7j0cpCFc.mjs +0 -101
  1265. package/dist/public-url-7j0cpCFc.mjs.map +0 -1
  1266. package/dist/query-BRNTde3V.mjs +0 -41
  1267. package/dist/query-BRNTde3V.mjs.map +0 -1
  1268. package/dist/query-DyYYWOKk.mjs +0 -1161
  1269. package/dist/query-DyYYWOKk.mjs.map +0 -1
  1270. package/dist/rate-limit-D0BcE76H.mjs +0 -120
  1271. package/dist/rate-limit-D0BcE76H.mjs.map +0 -1
  1272. package/dist/receipt-C4icyHlC.mjs +0 -43
  1273. package/dist/receipt-C4icyHlC.mjs.map +0 -1
  1274. package/dist/redirect-9DbtxSNG.mjs +0 -19
  1275. package/dist/redirect-9DbtxSNG.mjs.map +0 -1
  1276. package/dist/redirect-BvFKh8Sz.mjs +0 -416
  1277. package/dist/redirect-BvFKh8Sz.mjs.map +0 -1
  1278. package/dist/redirects-1nhdEaTq.mjs +0 -148
  1279. package/dist/redirects-1nhdEaTq.mjs.map +0 -1
  1280. package/dist/redirects-D1yMnSR9.mjs +0 -446
  1281. package/dist/redirects-D1yMnSR9.mjs.map +0 -1
  1282. package/dist/registry-CzPJNu3H.mjs +0 -8366
  1283. package/dist/registry-CzPJNu3H.mjs.map +0 -1
  1284. package/dist/relations-C8JpSkU6.mjs +0 -776
  1285. package/dist/relations-C8JpSkU6.mjs.map +0 -1
  1286. package/dist/request-meta-Bs60XW1m.mjs +0 -141
  1287. package/dist/request-meta-Bs60XW1m.mjs.map +0 -1
  1288. package/dist/resolve-BHWgMU_X.mjs +0 -100
  1289. package/dist/resolve-BHWgMU_X.mjs.map +0 -1
  1290. package/dist/revision-CGkQA0e5.mjs +0 -366
  1291. package/dist/revision-CGkQA0e5.mjs.map +0 -1
  1292. package/dist/route-wire-ClUVQduA.mjs +0 -197
  1293. package/dist/route-wire-ClUVQduA.mjs.map +0 -1
  1294. package/dist/routes-D2GFl3B7.mjs +0 -272
  1295. package/dist/routes-D2GFl3B7.mjs.map +0 -1
  1296. package/dist/runner-CLpxmhl8.mjs +0 -6197
  1297. package/dist/runner-CLpxmhl8.mjs.map +0 -1
  1298. package/dist/runner-CWl76CH3.d.mts +0 -68
  1299. package/dist/runner-CWl76CH3.d.mts.map +0 -1
  1300. package/dist/schema-27XefSYG.mjs +0 -13
  1301. package/dist/schema-27XefSYG.mjs.map +0 -1
  1302. package/dist/schema-Btvj3BMh.mjs +0 -815
  1303. package/dist/schema-Btvj3BMh.mjs.map +0 -1
  1304. package/dist/scopes-DGCj6K4p.mjs +0 -34
  1305. package/dist/scopes-DGCj6K4p.mjs.map +0 -1
  1306. package/dist/search-pZRRmWjh.mjs +0 -448
  1307. package/dist/search-pZRRmWjh.mjs.map +0 -1
  1308. package/dist/secrets-CIi0nFiR.mjs +0 -341
  1309. package/dist/secrets-CIi0nFiR.mjs.map +0 -1
  1310. package/dist/sections-CIhqs3Ao.mjs +0 -346
  1311. package/dist/sections-CIhqs3Ao.mjs.map +0 -1
  1312. package/dist/seo-DifeiGdl.mjs +0 -114
  1313. package/dist/seo-DifeiGdl.mjs.map +0 -1
  1314. package/dist/seo-DpYxmGaL.mjs +0 -133
  1315. package/dist/seo-DpYxmGaL.mjs.map +0 -1
  1316. package/dist/service-COVdVpSK.mjs +0 -150
  1317. package/dist/service-COVdVpSK.mjs.map +0 -1
  1318. package/dist/session-user-C6IEjuty.mjs +0 -61
  1319. package/dist/session-user-C6IEjuty.mjs.map +0 -1
  1320. package/dist/settings-CwcrX-Ay.mjs +0 -61
  1321. package/dist/settings-CwcrX-Ay.mjs.map +0 -1
  1322. package/dist/settings-D_cJtT48.mjs +0 -439
  1323. package/dist/settings-D_cJtT48.mjs.map +0 -1
  1324. package/dist/settings-tnPQN3Z2.mjs +0 -51
  1325. package/dist/settings-tnPQN3Z2.mjs.map +0 -1
  1326. package/dist/setup-complete-BOrI10UY.mjs +0 -65
  1327. package/dist/setup-complete-BOrI10UY.mjs.map +0 -1
  1328. package/dist/setup-nonce-0XTBLTsX.mjs +0 -25
  1329. package/dist/setup-nonce-0XTBLTsX.mjs.map +0 -1
  1330. package/dist/single-flight-cache-B5O2qhO1.mjs +0 -104
  1331. package/dist/single-flight-cache-B5O2qhO1.mjs.map +0 -1
  1332. package/dist/site-settings-B7vGFRDT.mjs +0 -50
  1333. package/dist/site-settings-B7vGFRDT.mjs.map +0 -1
  1334. package/dist/site-url-wEYRMHBv.mjs +0 -16
  1335. package/dist/site-url-wEYRMHBv.mjs.map +0 -1
  1336. package/dist/slugify-T5GHH42D.mjs +0 -17
  1337. package/dist/slugify-T5GHH42D.mjs.map +0 -1
  1338. package/dist/snapshot-W36KbLmc.mjs +0 -414
  1339. package/dist/snapshot-W36KbLmc.mjs.map +0 -1
  1340. package/dist/ssrf-CRJ-xom5.mjs +0 -406
  1341. package/dist/ssrf-CRJ-xom5.mjs.map +0 -1
  1342. package/dist/stage-DA0NrTRZ.mjs +0 -662
  1343. package/dist/stage-DA0NrTRZ.mjs.map +0 -1
  1344. package/dist/staged-Bw5eHbKS.mjs +0 -686
  1345. package/dist/staged-Bw5eHbKS.mjs.map +0 -1
  1346. package/dist/state-BTlprvUF.mjs +0 -153
  1347. package/dist/state-BTlprvUF.mjs.map +0 -1
  1348. package/dist/status-C6IrSLMT.mjs +0 -30
  1349. package/dist/status-C6IrSLMT.mjs.map +0 -1
  1350. package/dist/storage-query-CdVF2bng.d.mts +0 -1614
  1351. package/dist/storage-query-CdVF2bng.d.mts.map +0 -1
  1352. package/dist/tar-BGOZARjy.mjs +0 -449
  1353. package/dist/tar-BGOZARjy.mjs.map +0 -1
  1354. package/dist/target-YhYH_0uc.mjs +0 -27
  1355. package/dist/target-YhYH_0uc.mjs.map +0 -1
  1356. package/dist/taxonomies-DK_XBrmT.mjs +0 -697
  1357. package/dist/taxonomies-DK_XBrmT.mjs.map +0 -1
  1358. package/dist/taxonomies-S1M3rUC0.mjs +0 -939
  1359. package/dist/taxonomies-S1M3rUC0.mjs.map +0 -1
  1360. package/dist/taxonomy-BesdHIwi.mjs +0 -525
  1361. package/dist/taxonomy-BesdHIwi.mjs.map +0 -1
  1362. package/dist/tokens-B_ffPF1q.mjs +0 -171
  1363. package/dist/tokens-B_ffPF1q.mjs.map +0 -1
  1364. package/dist/transfer-9fVSxyrV.mjs +0 -166
  1365. package/dist/transfer-9fVSxyrV.mjs.map +0 -1
  1366. package/dist/transfer-B7LUTaYD.mjs +0 -8855
  1367. package/dist/transfer-B7LUTaYD.mjs.map +0 -1
  1368. package/dist/transfer-download-BdNq4VWN.mjs +0 -33
  1369. package/dist/transfer-download-BdNq4VWN.mjs.map +0 -1
  1370. package/dist/transformations-BtQdvyly.mjs +0 -196
  1371. package/dist/transformations-BtQdvyly.mjs.map +0 -1
  1372. package/dist/transport-CriYXY1c.mjs +0 -136
  1373. package/dist/transport-CriYXY1c.mjs.map +0 -1
  1374. package/dist/transport-SnhS6xku.d.mts +0 -42
  1375. package/dist/transport-SnhS6xku.d.mts.map +0 -1
  1376. package/dist/trusted-proxy-C0eHXPd-.mjs +0 -51
  1377. package/dist/trusted-proxy-C0eHXPd-.mjs.map +0 -1
  1378. package/dist/turnstile-BNPrWd1e.mjs +0 -53
  1379. package/dist/turnstile-BNPrWd1e.mjs.map +0 -1
  1380. package/dist/types-B8sF_kE5.d.mts +0 -287
  1381. package/dist/types-B8sF_kE5.d.mts.map +0 -1
  1382. package/dist/types-BYVIYOlD.d.mts +0 -1720
  1383. package/dist/types-BYVIYOlD.d.mts.map +0 -1
  1384. package/dist/types-Bs4bi95j.d.mts +0 -374
  1385. package/dist/types-Bs4bi95j.d.mts.map +0 -1
  1386. package/dist/types-CGuODCZi.d.mts +0 -198
  1387. package/dist/types-CGuODCZi.d.mts.map +0 -1
  1388. package/dist/types-CVsmpTcU.mjs +0 -18
  1389. package/dist/types-CVsmpTcU.mjs.map +0 -1
  1390. package/dist/types-ClFEExxn.mjs +0 -37
  1391. package/dist/types-ClFEExxn.mjs.map +0 -1
  1392. package/dist/types-D994zUWB.d.mts +0 -85
  1393. package/dist/types-D994zUWB.d.mts.map +0 -1
  1394. package/dist/types-DAj1ByJQ.d.mts +0 -125
  1395. package/dist/types-DAj1ByJQ.d.mts.map +0 -1
  1396. package/dist/types-Dlv6esp4.mjs +0 -77
  1397. package/dist/types-Dlv6esp4.mjs.map +0 -1
  1398. package/dist/types-Dr1LVwL7.d.mts +0 -363
  1399. package/dist/types-Dr1LVwL7.d.mts.map +0 -1
  1400. package/dist/types-MN9bOQ1g.d.mts +0 -198
  1401. package/dist/types-MN9bOQ1g.d.mts.map +0 -1
  1402. package/dist/types-d2Szb1oz.mjs +0 -16
  1403. package/dist/types-d2Szb1oz.mjs.map +0 -1
  1404. package/dist/types-ibtP7GEA.d.mts +0 -896
  1405. package/dist/types-ibtP7GEA.d.mts.map +0 -1
  1406. package/dist/types-j6MBDjlZ.mjs +0 -103
  1407. package/dist/types-j6MBDjlZ.mjs.map +0 -1
  1408. package/dist/update-check-D8No-E5s.mjs +0 -161
  1409. package/dist/update-check-D8No-E5s.mjs.map +0 -1
  1410. package/dist/upload-attempts-BodUoFBu.mjs +0 -32
  1411. package/dist/upload-attempts-BodUoFBu.mjs.map +0 -1
  1412. package/dist/url-pattern-DGAG6ZP2.mjs +0 -41
  1413. package/dist/url-pattern-DGAG6ZP2.mjs.map +0 -1
  1414. package/dist/user-BOvuF1sQ.mjs +0 -171
  1415. package/dist/user-BOvuF1sQ.mjs.map +0 -1
  1416. package/dist/utils-Dr01wRKt.mjs +0 -439
  1417. package/dist/utils-Dr01wRKt.mjs.map +0 -1
  1418. package/dist/validate-BttYU7NK.mjs +0 -516
  1419. package/dist/validate-BttYU7NK.mjs.map +0 -1
  1420. package/dist/validate-Df09inbC.d.mts +0 -468
  1421. package/dist/validate-Df09inbC.d.mts.map +0 -1
  1422. package/dist/validation-DH2WRwWO.mjs +0 -219
  1423. package/dist/validation-DH2WRwWO.mjs.map +0 -1
  1424. package/dist/version-BbygH8CT.mjs +0 -7
  1425. package/dist/version-BbygH8CT.mjs.map +0 -1
  1426. package/dist/version-CseoDDOd.mjs +0 -9
  1427. package/dist/version-CseoDDOd.mjs.map +0 -1
  1428. package/dist/virtual-modules-82GlnQyG.mjs +0 -516
  1429. package/dist/virtual-modules-82GlnQyG.mjs.map +0 -1
  1430. package/dist/widgets-DMAgGcQt.mjs +0 -126
  1431. package/dist/widgets-DMAgGcQt.mjs.map +0 -1
  1432. package/dist/wxr-taxonomies-Bi5kFSxQ.mjs +0 -453
  1433. package/dist/wxr-taxonomies-Bi5kFSxQ.mjs.map +0 -1
  1434. package/dist/wxr-taxonomies-y_FpYlDn.d.mts +0 -34
  1435. package/dist/wxr-taxonomies-y_FpYlDn.d.mts.map +0 -1
  1436. package/dist/zod-generator-DnfG5sGQ.mjs +0 -424
  1437. package/dist/zod-generator-DnfG5sGQ.mjs.map +0 -1
  1438. /package/dist/{api-tokens-B4COohsb.mjs → api-tokens-D2zgAIEt.mjs} +0 -0
  1439. /package/dist/{ssrf-sC4_5rxm.mjs → ssrf-BvNItdxk.mjs} +0 -0
@@ -1,2020 +0,0 @@
1
- import { u as __exportAll } from "./runner-CLpxmhl8.mjs";
2
- import { n as isMissingTableError } from "./db-errors-CBD5yMro.mjs";
3
- import { t as validateIdentifier } from "./validate-DANX1-Qi.mjs";
4
- import { c as executeAtomicBatchIfSupported } from "./dialect-helpers-7_IqPdh9.mjs";
5
- import { i as normalizeExplicitDatetime } from "./datetime-normalization-PeiEdcRY.mjs";
6
- import { l as invalidateCollectionCache } from "./object-cache-Bo6DGaC9.mjs";
7
- import { f as isIndexableFieldType, m as isStoragelessFieldRow } from "./types-BGmQ0uXs.mjs";
8
- import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-DBmkKmBH.mjs";
9
- import { n as slugify } from "./slugify-T5GHH42D.mjs";
10
- import { c as encodeCursor, i as InvalidCursorError, n as ContentMutationConflictError, o as ScheduledNotDueError, r as EmDashValidationError, s as decodeCursor, t as ContentCollectionNotFoundError } from "./types-j6MBDjlZ.mjs";
11
- import { i as ContentDatetimeNormalizer, n as createRevisionId, t as RevisionRepository } from "./revision-CGkQA0e5.mjs";
12
- import { t as withTransaction } from "./transaction-BVYIo_yu.mjs";
13
- import { sql } from "kysely";
14
- import { ulid } from "ulidx";
15
-
16
- //#region src/content/known-fields.ts
17
- /**
18
- * Reconcile an entry's keys with the fields its collection currently declares.
19
- *
20
- * A draft revision stores the whole `data` as JSON, so deleting a field leaves the old
21
- * value inside the revision. Every content-write surface needs the same two rules to keep
22
- * that from stranding the entry, so they live here rather than in one caller.
23
- */
24
- /** Keys EmDash reserves (`_slug` and the like). No field declares them. */
25
- function isReservedKey(key) {
26
- return key.startsWith("_");
27
- }
28
- /**
29
- * The entry data without the keys the collection has no field for.
30
- *
31
- * Reserved keys are kept.
32
- */
33
- function keepKnownFields(data, knownFieldSlugs) {
34
- const result = {};
35
- for (const [key, value] of Object.entries(data)) if (isReservedKey(key) || knownFieldSlugs.has(key)) result[key] = value;
36
- return result;
37
- }
38
- /**
39
- * Incoming keys with no matching field that the entry already stores.
40
- *
41
- * Dropping these lets a read-then-write save succeed after the field was deleted. A key the
42
- * entry has never stored is not returned, so a genuinely unknown field is still reported.
43
- */
44
- function staleStoredKeys(data, stored, knownFieldSlugs) {
45
- return Object.keys(data).filter((key) => !isReservedKey(key) && !knownFieldSlugs.has(key) && Object.hasOwn(stored, key));
46
- }
47
-
48
- //#endregion
49
- //#region src/search/match.ts
50
- /**
51
- * FTS5 match-expression builder for structured (non-user-syntax) queries.
52
- *
53
- * Unlike `escapeQuery` in `query.ts` (which powers the public search API and
54
- * deliberately passes through FTS5 operators like AND/OR/NOT), this builder
55
- * treats the input as plain words: every term is double-quoted with interior
56
- * quotes escaped, so the result can never produce an FTS5 syntax error. Used
57
- * by the admin content-list filter, where the input is a filter box, not a
58
- * search-syntax field.
59
- */
60
- const WHITESPACE_RE = /\s+/;
61
- const DOUBLE_QUOTE_RE = /"/g;
62
- const GLOB_SPECIAL_RE = /[[\]*?]/g;
63
- /**
64
- * Build a prefix-matching FTS5 MATCH expression from free-form input.
65
- *
66
- * `hello wor` becomes `"hello"* "wor"*` — implicit AND with per-term prefix
67
- * matching. Returns `""` when the input contains no usable terms; callers
68
- * must fall back to their non-FTS path in that case.
69
- */
70
- function buildFtsPrefixMatch(input) {
71
- const terms = input.trim().split(WHITESPACE_RE).map((term) => term.replace(DOUBLE_QUOTE_RE, "\"\"")).filter((term) => term.length > 0);
72
- if (terms.length === 0) return "";
73
- return terms.map((term) => `"${term}"*`).join(" ");
74
- }
75
- /**
76
- * Build a GLOB prefix pattern from free-form input, treating GLOB
77
- * metacharacters (`* ? [ ]`) literally by wrapping each in a character
78
- * class (GLOB has no ESCAPE clause).
79
- *
80
- * GLOB (unlike default LIKE) is case-sensitive, so with a lowercased
81
- * pattern it matches slugs (lowercase by construction) while staying
82
- * servable by the ordinary BINARY-collated slug index — SQLite's GLOB
83
- * optimization turns a `prefix*` pattern into an index range scan.
84
- */
85
- function buildSlugGlobPrefix(input) {
86
- return `${input.trim().toLowerCase().replace(GLOB_SPECIAL_RE, (c) => `[${c}]`)}*`;
87
- }
88
-
89
- //#endregion
90
- //#region src/database/repositories/content.ts
91
- var content_exports = /* @__PURE__ */ __exportAll({
92
- ContentRepository: () => ContentRepository,
93
- isConfirmedStatementFailure: () => isConfirmedStatementFailure,
94
- isSystemOrderField: () => isSystemOrderField
95
- });
96
- const ULID_PATTERN = /^[0-9A-Z]{26}$/;
97
- const SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/;
98
- const MAX_DRAFT_STAGE_ATTEMPTS = 32;
99
- const LIKE_WILDCARD_RE = /[\\%_]/g;
100
- const MAX_INDEXED_FIELD_FILTERS = 20;
101
- const MAX_IN_FILTER_VALUES = SQL_BATCH_SIZE;
102
- const MAX_FILTER_STRING_LENGTH = 2048;
103
- function assertRevisionPrecondition(item, expected) {
104
- if (expected && (item.version !== expected.version || item.updatedAt !== expected.updatedAt)) throw new ContentMutationConflictError();
105
- }
106
- function nullableColumnMatch(column, value) {
107
- validateIdentifier(column, "content column");
108
- return value === null ? sql`${sql.ref(column)} IS NULL` : sql`${sql.ref(column)} = ${value}`;
109
- }
110
- function sameStoredValue(left, right) {
111
- return Object.is(serializeValue(left), serializeValue(right));
112
- }
113
- function matchesPublication(observed, existing, revision, revisionId, slug, publishedAt, updatedAt) {
114
- if (observed.version !== existing.version + 1 || observed.status !== "published" || observed.slug !== slug || observed.liveRevisionId !== revisionId || observed.draftRevisionId !== null || observed.scheduledAt !== null || observed.publishedAt !== publishedAt || observed.updatedAt !== updatedAt) return false;
115
- return Object.entries(revision.data).every(([key, value]) => SYSTEM_COLUMNS.has(key) || key.startsWith("_") || sameStoredValue(observed.data[key], value));
116
- }
117
- function matchesLifecyclePublication(observed, existing, liveRevisionId, publishedAt, updatedAt) {
118
- if (observed.version !== existing.version + 1 || observed.status !== "published" || observed.slug !== existing.slug || observed.liveRevisionId !== liveRevisionId || observed.draftRevisionId !== null || observed.scheduledAt !== null || observed.publishedAt !== publishedAt || observed.updatedAt !== updatedAt) return false;
119
- return Object.entries(existing.data).every(([key, value]) => sameStoredValue(observed.data[key], value));
120
- }
121
- function matchesPublicationFence(observed, existing) {
122
- return observed.version === existing.version && observed.status === existing.status && observed.liveRevisionId === existing.liveRevisionId && observed.draftRevisionId === existing.draftRevisionId && observed.scheduledAt === existing.scheduledAt;
123
- }
124
- function isConfirmedStatementFailure(error) {
125
- if (typeof error !== "object" || error === null || !("code" in error)) return false;
126
- const code = error.code;
127
- return typeof code === "string" && (code === "ERR_SQLITE_ERROR" || code.startsWith("SQLITE_") || SQLSTATE_PATTERN.test(code));
128
- }
129
- function encodeIndexedFieldCursor(field, value, id) {
130
- const payload = {
131
- version: 1,
132
- field,
133
- value
134
- };
135
- return encodeCursor(JSON.stringify(payload), id);
136
- }
137
- function decodeIndexedFieldCursor(cursor, field) {
138
- const { orderValue, id } = decodeCursor(cursor);
139
- let payload;
140
- try {
141
- payload = JSON.parse(orderValue);
142
- } catch {
143
- throw new InvalidCursorError(cursor);
144
- }
145
- if (payload === null || typeof payload !== "object") throw new InvalidCursorError(cursor);
146
- const candidate = payload;
147
- const validValue = candidate.value === null || typeof candidate.value === "string" || typeof candidate.value === "number";
148
- if (candidate.version !== 1 || candidate.field !== field || !validValue) throw new InvalidCursorError(cursor);
149
- return {
150
- value: candidate.value,
151
- id
152
- };
153
- }
154
- /**
155
- * Whitelist mapping a public date-filter field to its physical column. Keeping
156
- * this separate from `mapOrderField` makes the filterable set explicit and
157
- * prevents filtering on arbitrary columns.
158
- */
159
- const DATE_FILTER_COLUMNS = {
160
- createdAt: "created_at",
161
- updatedAt: "updated_at",
162
- publishedAt: "published_at"
163
- };
164
- /**
165
- * Built-in sort fields → their physical columns. A closed set that blocks
166
- * sorting by arbitrary columns; per-collection fields are allowed
167
- * separately via `mapOrderField`'s `sortableExtras`.
168
- */
169
- const ORDER_FIELD_COLUMNS = {
170
- createdAt: "created_at",
171
- updatedAt: "updated_at",
172
- publishedAt: "published_at",
173
- scheduledAt: "scheduled_at",
174
- deletedAt: "deleted_at",
175
- title: "title",
176
- name: "name",
177
- slug: "slug",
178
- status: "status",
179
- locale: "locale"
180
- };
181
- /** True when `field` maps to a system column and needs no per-collection resolution. */
182
- function isSystemOrderField(field) {
183
- return field in ORDER_FIELD_COLUMNS;
184
- }
185
- /**
186
- * System columns that exist in every ec_* table
187
- */
188
- const SYSTEM_COLUMNS = new Set([
189
- "id",
190
- "slug",
191
- "status",
192
- "author_id",
193
- "primary_byline_id",
194
- "created_at",
195
- "updated_at",
196
- "published_at",
197
- "scheduled_at",
198
- "deleted_at",
199
- "version",
200
- "live_revision_id",
201
- "draft_revision_id",
202
- "locale",
203
- "translation_group"
204
- ]);
205
- /**
206
- * Get the table name for a collection type
207
- */
208
- function getTableName(type) {
209
- validateIdentifier(type, "collection type");
210
- return `ec_${type}`;
211
- }
212
- /**
213
- * Serialize a value for database storage
214
- * Objects/arrays are JSON-stringified
215
- * Booleans are converted to 0/1 for SQLite
216
- */
217
- function serializeValue(value) {
218
- if (value === null || value === void 0) return null;
219
- if (typeof value === "boolean") return value ? 1 : 0;
220
- if (typeof value === "object") return JSON.stringify(value);
221
- return value;
222
- }
223
- function writableContentData(data) {
224
- const writable = {};
225
- for (const [key, value] of Object.entries(data)) {
226
- if (SYSTEM_COLUMNS.has(key)) continue;
227
- validateIdentifier(key, "content field name");
228
- writable[key] = value;
229
- }
230
- return writable;
231
- }
232
- /**
233
- * Deserialize a value from database storage
234
- * Attempts to parse JSON strings that look like objects/arrays
235
- */
236
- function deserializeValue(value) {
237
- if (typeof value === "string") {
238
- if (value.startsWith("{") || value.startsWith("[")) try {
239
- return JSON.parse(value);
240
- } catch {
241
- return value;
242
- }
243
- }
244
- return value;
245
- }
246
- /** Pattern for escaping special regex characters */
247
- const REGEX_ESCAPE_PATTERN = /[.*+?^${}()|[\]\\]/g;
248
- /**
249
- * Escape special regex characters in a string for use in `new RegExp()`
250
- */
251
- function escapeRegExp(s) {
252
- return s.replace(REGEX_ESCAPE_PATTERN, "\\$&");
253
- }
254
- /**
255
- * Repository for content CRUD operations
256
- *
257
- * Content is stored in per-collection tables (ec_posts, ec_pages, etc.)
258
- * Each field becomes a real column in the table.
259
- */
260
- var ContentRepository = class {
261
- datetimes;
262
- constructor(db, datetimeContexts) {
263
- this.db = db;
264
- this.datetimeContexts = datetimeContexts;
265
- this.datetimes = new ContentDatetimeNormalizer(db, datetimeContexts);
266
- }
267
- /**
268
- * Create a new content item
269
- */
270
- async create(input) {
271
- const id = input.id ?? ulid();
272
- const now = (/* @__PURE__ */ new Date()).toISOString();
273
- const { type, slug, data: inputData, status = "draft", authorId, primaryBylineId, locale, translationOf, inheritFields = [], publishedAt, createdAt } = input;
274
- const data = await this.datetimes.normalizeInput(type, inputData);
275
- const normalizedCreatedAt = createdAt ? await this.datetimes.normalizeValue(type, createdAt) : now;
276
- const normalizedPublishedAt = publishedAt ? await this.datetimes.normalizeValue(type, publishedAt) : null;
277
- if (!type) throw new EmDashValidationError("Content type is required");
278
- const tableName = getTableName(type);
279
- const columns = [
280
- "id",
281
- "slug",
282
- "status",
283
- "author_id",
284
- "primary_byline_id",
285
- "created_at",
286
- "updated_at",
287
- "published_at",
288
- "version",
289
- "locale",
290
- "translation_group"
291
- ];
292
- const values = [
293
- id,
294
- slug || null,
295
- status,
296
- authorId || null,
297
- primaryBylineId ?? null,
298
- normalizedCreatedAt,
299
- now,
300
- normalizedPublishedAt,
301
- 1,
302
- locale || "en",
303
- id
304
- ];
305
- const inherited = new Set(inheritFields);
306
- for (const field of inherited) {
307
- validateIdentifier(field, "inherited content field name");
308
- if (!SYSTEM_COLUMNS.has(field) && !Object.hasOwn(data, field)) {
309
- columns.push(field);
310
- values.push(null);
311
- }
312
- }
313
- if (data && typeof data === "object") {
314
- for (const [key, value] of Object.entries(data)) if (!SYSTEM_COLUMNS.has(key)) {
315
- validateIdentifier(key, "content field name");
316
- columns.push(key);
317
- values.push(serializeValue(value));
318
- }
319
- }
320
- const columnRefs = columns.map((c) => sql.ref(c));
321
- const valuePlaceholders = values.map((value, index) => {
322
- const column = columns[index];
323
- if (translationOf && column === "translation_group") return sql`COALESCE(${sql.ref("translation_source.translation_group")}, ${sql.ref("translation_source.id")})`;
324
- if (translationOf && inherited.has(column)) return sql.ref(`translation_source.${column}`);
325
- return value === null ? sql`NULL` : sql`${value}`;
326
- });
327
- if (translationOf) await sql`
328
- INSERT INTO ${sql.ref(tableName)} (${sql.join(columnRefs, sql`, `)})
329
- SELECT ${sql.join(valuePlaceholders, sql`, `)}
330
- FROM ${sql.ref(tableName)} AS translation_source
331
- WHERE translation_source.id = ${translationOf}
332
- AND translation_source.deleted_at IS NULL
333
- `.execute(this.db);
334
- else await sql`
335
- INSERT INTO ${sql.ref(tableName)} (${sql.join(columnRefs, sql`, `)})
336
- VALUES (${sql.join(valuePlaceholders, sql`, `)})
337
- `.execute(this.db);
338
- invalidateCollectionCache(type);
339
- const item = await this.findById(type, id);
340
- if (!item) {
341
- if (translationOf) throw new EmDashValidationError("Translation source content not found");
342
- throw new Error("Failed to create content");
343
- }
344
- return item;
345
- }
346
- /**
347
- * Generate a unique slug for a content item within a collection.
348
- *
349
- * Checks the collection table for existing slugs that match `baseSlug`
350
- * (optionally scoped to a locale) and appends a numeric suffix (`-1`,
351
- * `-2`, etc.) on collision to guarantee uniqueness.
352
- *
353
- * Returns null when slug normalization cannot produce a value.
354
- */
355
- async generateUniqueSlug(type, text, locale) {
356
- const baseSlug = slugify(text);
357
- if (!baseSlug) return null;
358
- const tableName = getTableName(type);
359
- if ((locale ? await sql`
360
- SELECT slug FROM ${sql.ref(tableName)}
361
- WHERE slug = ${baseSlug}
362
- AND locale = ${locale}
363
- LIMIT 1
364
- `.execute(this.db) : await sql`
365
- SELECT slug FROM ${sql.ref(tableName)}
366
- WHERE slug = ${baseSlug}
367
- LIMIT 1
368
- `.execute(this.db)).rows.length === 0) return baseSlug;
369
- const pattern = `${baseSlug}-%`;
370
- const candidates = locale ? await sql`
371
- SELECT slug FROM ${sql.ref(tableName)}
372
- WHERE (slug = ${baseSlug} OR slug LIKE ${pattern})
373
- AND locale = ${locale}
374
- `.execute(this.db) : await sql`
375
- SELECT slug FROM ${sql.ref(tableName)}
376
- WHERE slug = ${baseSlug} OR slug LIKE ${pattern}
377
- `.execute(this.db);
378
- let maxSuffix = 0;
379
- const suffixPattern = new RegExp(`^${escapeRegExp(baseSlug)}-(\\d+)$`);
380
- for (const row of candidates.rows) {
381
- const match = suffixPattern.exec(row.slug);
382
- if (match) {
383
- const n = parseInt(match[1], 10);
384
- if (n > maxSuffix) maxSuffix = n;
385
- }
386
- }
387
- return `${baseSlug}-${maxSuffix + 1}`;
388
- }
389
- /**
390
- * Duplicate a content item
391
- * Creates a new draft copy with "(Copy)" appended to the title.
392
- * A slug is auto-generated from the new title by the handler layer.
393
- */
394
- async duplicate(type, id, authorId) {
395
- const original = await this.findById(type, id);
396
- if (!original) throw new EmDashValidationError("Content item not found");
397
- const newData = { ...original.data };
398
- if (typeof newData.title === "string") newData.title = `${newData.title} (Copy)`;
399
- else if (typeof newData.name === "string") newData.name = `${newData.name} (Copy)`;
400
- const slugSource = typeof newData.title === "string" ? newData.title : typeof newData.name === "string" ? newData.name : null;
401
- const slug = slugSource ? await this.generateUniqueSlug(type, slugSource, original.locale ?? void 0) : null;
402
- return this.create({
403
- type,
404
- slug,
405
- data: newData,
406
- status: "draft",
407
- authorId: authorId || original.authorId || void 0,
408
- locale: original.locale ?? void 0
409
- });
410
- }
411
- /**
412
- * Find content by ID
413
- */
414
- async findById(type, id) {
415
- const tableName = getTableName(type);
416
- const row = (await sql`
417
- SELECT * FROM ${sql.ref(tableName)}
418
- WHERE id = ${id}
419
- AND deleted_at IS NULL
420
- `.execute(this.db)).rows[0];
421
- if (!row) return null;
422
- return this.mapRow(type, row);
423
- }
424
- async findManyByIds(type, ids, options = {}) {
425
- const items = /* @__PURE__ */ new Map();
426
- if (ids.length === 0) return items;
427
- const tableName = getTableName(type);
428
- const deletedFilter = options.includeTrashed ? sql`` : sql`AND deleted_at IS NULL`;
429
- for (const batch of chunks([...new Set(ids)], SQL_BATCH_SIZE)) {
430
- const result = await sql`
431
- SELECT * FROM ${sql.ref(tableName)}
432
- WHERE id IN (${sql.join(batch)}) ${deletedFilter}
433
- `.execute(this.db);
434
- for (const row of result.rows) {
435
- const item = this.mapRow(type, row);
436
- items.set(item.id, item);
437
- }
438
- }
439
- return items;
440
- }
441
- async findManyBySlugsInLocale(type, slugs, locale, options = {}) {
442
- const items = /* @__PURE__ */ new Map();
443
- if (slugs.length === 0) return items;
444
- const tableName = getTableName(type);
445
- const deletedFilter = options.includeTrashed ? sql`` : sql`AND deleted_at IS NULL`;
446
- for (const batch of chunks([...new Set(slugs)], SQL_BATCH_SIZE)) {
447
- const result = await sql`
448
- SELECT * FROM ${sql.ref(tableName)}
449
- WHERE slug IN (${sql.join(batch)}) AND locale = ${locale} ${deletedFilter}
450
- `.execute(this.db);
451
- for (const row of result.rows) {
452
- const item = this.mapRow(type, row);
453
- if (item.slug !== null) items.set(item.slug, item);
454
- }
455
- }
456
- return items;
457
- }
458
- /**
459
- * Find content by id, including trashed (soft-deleted) items.
460
- * Used by restore endpoint for ownership checks.
461
- *
462
- * `deletedAt` rides along because the row is already in hand: a caller that
463
- * has to know whether the row is trashed before touching anything else would
464
- * otherwise pay a second read for a column this row already carries.
465
- */
466
- async findByIdIncludingTrashed(type, id) {
467
- const tableName = getTableName(type);
468
- const row = (await sql`
469
- SELECT * FROM ${sql.ref(tableName)}
470
- WHERE id = ${id}
471
- `.execute(this.db)).rows[0];
472
- if (!row) return null;
473
- return {
474
- ...this.mapRow(type, row),
475
- deletedAt: typeof row.deleted_at === "string" ? row.deleted_at : null
476
- };
477
- }
478
- /**
479
- * Find content by ID or slug. Tries ID first if it looks like a ULID,
480
- * otherwise tries slug. Falls back to the other if the first lookup misses.
481
- */
482
- async findByIdOrSlug(type, identifier, locale) {
483
- return this._findByIdOrSlug(type, identifier, false, locale);
484
- }
485
- /**
486
- * Find content by ID or slug, including trashed (soft-deleted) items.
487
- * Used by restore/permanent-delete endpoints.
488
- */
489
- async findByIdOrSlugIncludingTrashed(type, identifier, locale) {
490
- return this._findByIdOrSlug(type, identifier, true, locale);
491
- }
492
- async isTrashed(type, id) {
493
- const tableName = getTableName(type);
494
- return await this.db.selectFrom(tableName).select("id").where("id", "=", id).where("deleted_at", "is not", null).executeTakeFirst() !== void 0;
495
- }
496
- async _findByIdOrSlug(type, identifier, includeTrashed, locale) {
497
- const looksLikeUlid = ULID_PATTERN.test(identifier);
498
- const findById = includeTrashed ? (t, id) => this.findByIdIncludingTrashed(t, id) : (t, id) => this.findById(t, id);
499
- const findBySlug = includeTrashed ? (t, s) => this.findBySlugIncludingTrashed(t, s, locale) : (t, s) => this.findBySlug(t, s, locale);
500
- try {
501
- if (looksLikeUlid) {
502
- const byId = await findById(type, identifier);
503
- if (byId) return byId;
504
- return await findBySlug(type, identifier);
505
- }
506
- const bySlug = await findBySlug(type, identifier);
507
- if (bySlug) return bySlug;
508
- return await findById(type, identifier);
509
- } catch (error) {
510
- if (isMissingTableError(error)) return null;
511
- throw error;
512
- }
513
- }
514
- /**
515
- * Find content by slug
516
- */
517
- async findBySlug(type, slug, locale) {
518
- const tableName = getTableName(type);
519
- const row = (locale ? await sql`
520
- SELECT * FROM ${sql.ref(tableName)}
521
- WHERE slug = ${slug}
522
- AND locale = ${locale}
523
- AND deleted_at IS NULL
524
- `.execute(this.db) : await sql`
525
- SELECT * FROM ${sql.ref(tableName)}
526
- WHERE slug = ${slug}
527
- AND deleted_at IS NULL
528
- ORDER BY locale ASC
529
- LIMIT 1
530
- `.execute(this.db)).rows[0];
531
- if (!row) return null;
532
- return this.mapRow(type, row);
533
- }
534
- /**
535
- * Find content by slug, including trashed (soft-deleted) items.
536
- * Used by restore/permanent-delete endpoints.
537
- */
538
- async findBySlugIncludingTrashed(type, slug, locale) {
539
- const tableName = getTableName(type);
540
- const row = (locale ? await sql`
541
- SELECT * FROM ${sql.ref(tableName)}
542
- WHERE slug = ${slug}
543
- AND locale = ${locale}
544
- `.execute(this.db) : await sql`
545
- SELECT * FROM ${sql.ref(tableName)}
546
- WHERE slug = ${slug}
547
- ORDER BY locale ASC
548
- LIMIT 1
549
- `.execute(this.db)).rows[0];
550
- if (!row) return null;
551
- return this.mapRow(type, row);
552
- }
553
- /**
554
- * Find many content items with filtering and pagination
555
- */
556
- async findMany(type, options = {}) {
557
- const tableName = getTableName(type);
558
- const limit = Math.max(1, Math.min(options.limit || 50, 100));
559
- const orderField = options.orderBy?.field || "createdAt";
560
- const orderDirection = options.orderBy?.direction || "desc";
561
- const resolvedOrderField = await this.resolveOrderField(type, orderField, options.sortableExtras);
562
- const dbField = resolvedOrderField.column;
563
- const resolvedFieldFilters = await this.resolveFieldFilters(type, options.where?.fieldFilters);
564
- const safeOrderDirection = orderDirection.toLowerCase() === "asc" ? "ASC" : "DESC";
565
- let query = this.db.selectFrom(tableName).selectAll().where("deleted_at", "is", null);
566
- if (options.where?.status) query = query.where("status", "=", options.where.status);
567
- if (options.where?.authorId) query = query.where("author_id", "=", options.where.authorId);
568
- if (options.where?.locale) query = query.where("locale", "=", options.where.locale);
569
- query = this.applySearchFilter(query, options.where, type);
570
- query = this.applyDateFilter(query, options.where);
571
- query = this.applyBylineFilter(query, options.where, type);
572
- query = this.applyFieldFilters(query, resolvedFieldFilters);
573
- if (options.cursor) if (resolvedOrderField.indexedCustomField) {
574
- const { value, id: cursorId } = decodeIndexedFieldCursor(options.cursor, orderField);
575
- const isPresent = sql`${sql.ref(dbField)} IS NOT NULL`;
576
- const falseLiteral = sql`FALSE`;
577
- const trueLiteral = sql`TRUE`;
578
- if (safeOrderDirection === "ASC" && value === null) query = query.where(sql`
579
- (${isPresent}) > ${falseLiteral}
580
- OR ((${isPresent}) = ${falseLiteral} AND ${sql.ref("id")} > ${cursorId})
581
- `);
582
- else if (safeOrderDirection === "DESC" && value === null) query = query.where(sql`
583
- (${isPresent}) = ${falseLiteral} AND ${sql.ref("id")} < ${cursorId}
584
- `);
585
- else if (safeOrderDirection === "ASC") query = query.where(sql`
586
- (${isPresent}, ${sql.ref(dbField)}, ${sql.ref("id")})
587
- > (${trueLiteral}, ${value}, ${cursorId})
588
- `);
589
- else query = query.where(sql`
590
- (${isPresent}, ${sql.ref(dbField)}, ${sql.ref("id")})
591
- < (${trueLiteral}, ${value}, ${cursorId})
592
- `);
593
- } else {
594
- const { orderValue, id: cursorId } = decodeCursor(options.cursor);
595
- if (safeOrderDirection === "DESC") query = query.where((eb) => eb.or([eb(dbField, "<", orderValue), eb.and([eb(dbField, "=", orderValue), eb("id", "<", cursorId)])]));
596
- else query = query.where((eb) => eb.or([eb(dbField, ">", orderValue), eb.and([eb(dbField, "=", orderValue), eb("id", ">", cursorId)])]));
597
- }
598
- const indexedOrderFilter = resolvedOrderField.indexedCustomField ? resolvedFieldFilters.find((filter) => filter.column === dbField) : void 0;
599
- if (resolvedOrderField.indexedCustomField && !indexedOrderFilter) query = query.orderBy(sql`${sql.ref(dbField)} IS NOT NULL`, safeOrderDirection === "ASC" ? "asc" : "desc");
600
- if (indexedOrderFilter?.kind !== "null") query = query.orderBy(dbField, safeOrderDirection === "ASC" ? "asc" : "desc");
601
- query = query.orderBy("id", safeOrderDirection === "ASC" ? "asc" : "desc").limit(limit + 1);
602
- const [rowsResult, countResult] = await Promise.allSettled([query.execute(), this.countWithResolvedFilters(type, options.where, resolvedFieldFilters)]);
603
- if (rowsResult.status === "rejected") throw rowsResult.reason;
604
- if (countResult.status === "rejected") throw countResult.reason;
605
- const rows = rowsResult.value;
606
- const total = countResult.value;
607
- const hasMore = rows.length > limit;
608
- const items = rows.slice(0, limit);
609
- const mappedResult = {
610
- items: items.map((row) => this.mapRow(type, row)),
611
- total
612
- };
613
- if (hasMore && items.length > 0) {
614
- const lastRow = items.at(-1);
615
- const lastOrderValue = lastRow[dbField];
616
- if (resolvedOrderField.indexedCustomField) {
617
- if (lastOrderValue !== null && typeof lastOrderValue !== "string" && typeof lastOrderValue !== "number") throw new EmDashValidationError(`Invalid indexed value for order field: ${orderField}`);
618
- mappedResult.nextCursor = encodeIndexedFieldCursor(orderField, lastOrderValue, String(lastRow.id));
619
- } else mappedResult.nextCursor = encodeCursor(typeof lastOrderValue === "string" || typeof lastOrderValue === "number" ? String(lastOrderValue) : "", String(lastRow.id));
620
- }
621
- return mappedResult;
622
- }
623
- /**
624
- * Update content
625
- */
626
- async update(type, id, input) {
627
- const tableName = getTableName(type);
628
- const now = (/* @__PURE__ */ new Date()).toISOString();
629
- const updates = {};
630
- if (input.status !== void 0) updates.status = input.status;
631
- if (input.slug !== void 0) updates.slug = input.slug;
632
- if (input.publishedAt !== void 0) updates.published_at = input.publishedAt === null ? null : await this.datetimes.normalizeValue(type, input.publishedAt);
633
- if (input.scheduledAt !== void 0) updates.scheduled_at = input.scheduledAt === null ? null : await this.datetimes.normalizeValue(type, input.scheduledAt);
634
- if (input.authorId !== void 0) updates.author_id = input.authorId;
635
- if (input.primaryBylineId !== void 0) updates.primary_byline_id = input.primaryBylineId;
636
- if (input.data !== void 0 && typeof input.data === "object") {
637
- const data = await this.datetimes.normalizeInput(type, writableContentData(input.data));
638
- for (const [key, value] of Object.entries(data)) updates[key] = serializeValue(value);
639
- }
640
- const hasColumnWrites = Object.keys(updates).length > 0;
641
- if (hasColumnWrites) updates.updated_at = now;
642
- updates.version = sql`version + 1`;
643
- await this.db.updateTable(tableName).set(updates).where("id", "=", id).where("deleted_at", "is", null).execute();
644
- if (hasColumnWrites) invalidateCollectionCache(type);
645
- const updated = await this.findById(type, id);
646
- if (!updated) throw new Error("Content not found");
647
- return updated;
648
- }
649
- async restoreRevision(type, id, revisionData, authorId) {
650
- const tableName = getTableName(type);
651
- const existing = await this.findById(type, id);
652
- if (!existing) throw new EmDashValidationError("Content item not found");
653
- const normalizedSnapshot = await this.datetimes.normalizeData(type, revisionData);
654
- const { _slug } = normalizedSnapshot;
655
- const snapshotFields = {};
656
- for (const [key, value] of Object.entries(normalizedSnapshot)) if (!key.startsWith("_")) snapshotFields[key] = value;
657
- const fieldData = writableContentData(snapshotFields);
658
- const revisionId = createRevisionId();
659
- const now = (/* @__PURE__ */ new Date()).toISOString();
660
- const assignments = [];
661
- if (typeof _slug === "string") assignments.push(sql`slug = ${_slug}`);
662
- for (const [field, value] of Object.entries(fieldData)) {
663
- validateIdentifier(field, "content field name");
664
- assignments.push(sql`${sql.ref(field)} = ${serializeValue(value)}`);
665
- }
666
- assignments.push(sql`draft_revision_id = ${revisionId}`, sql`updated_at = ${now}`, sql`version = version + 1`);
667
- const buildQueries = () => {
668
- return [
669
- sql`
670
- INSERT INTO revisions (id, collection, entry_id, data, author_id)
671
- VALUES (${revisionId}, ${type}, ${id}, ${JSON.stringify(normalizedSnapshot)}, ${authorId})
672
- RETURNING id
673
- `,
674
- sql`
675
- UPDATE ${sql.ref(tableName)}
676
- SET ${sql.join(assignments, sql`, `)}
677
- WHERE id = ${id}
678
- AND deleted_at IS NULL
679
- AND version = ${existing.version}
680
- AND updated_at = ${existing.updatedAt}
681
- AND status = ${existing.status}
682
- AND ${nullableColumnMatch("live_revision_id", existing.liveRevisionId)}
683
- AND ${nullableColumnMatch("draft_revision_id", existing.draftRevisionId)}
684
- AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
685
- RETURNING id
686
- `,
687
- sql`
688
- DELETE FROM revisions
689
- WHERE id = ${revisionId}
690
- AND NOT EXISTS (
691
- SELECT 1 FROM ${sql.ref(tableName)}
692
- WHERE id = ${id}
693
- AND draft_revision_id = ${revisionId}
694
- AND version = ${existing.version + 1}
695
- )
696
- `,
697
- sql`
698
- UPDATE ${sql.ref(tableName)}
699
- SET draft_revision_id = ${existing.draftRevisionId}
700
- WHERE id = ${id}
701
- AND draft_revision_id = ${revisionId}
702
- AND version = ${existing.version + 1}
703
- RETURNING id
704
- `
705
- ];
706
- };
707
- const batched = await executeAtomicBatchIfSupported(this.db, buildQueries());
708
- if (batched) {
709
- if (batched[0]?.rows.length !== 1 || batched[1]?.rows.length !== 1 || batched[3]?.rows.length !== 1) throw new ContentMutationConflictError();
710
- } else await withTransaction(this.db, async (trx) => {
711
- const [audit, update, removeUnappliedAudit, releaseMarker] = buildQueries();
712
- if ((await audit.execute(trx)).rows.length !== 1) throw new ContentMutationConflictError();
713
- const updateResult = await update.execute(trx);
714
- await removeUnappliedAudit.execute(trx);
715
- const releaseResult = await releaseMarker.execute(trx);
716
- if (updateResult.rows.length !== 1 || releaseResult.rows.length !== 1) throw new ContentMutationConflictError();
717
- });
718
- invalidateCollectionCache(type);
719
- const item = await this.findById(type, id);
720
- if (!item) throw new Error("Content not found");
721
- await new RevisionRepository(this.db, this.datetimeContexts).queuePruning(type, id, revisionId);
722
- return {
723
- item,
724
- revisionId
725
- };
726
- }
727
- async restoreDraftRevision(type, id, revisionData, authorId) {
728
- const tableName = getTableName(type);
729
- const existing = await this.findById(type, id);
730
- if (!existing) return null;
731
- const normalizedSnapshot = await this.datetimes.normalizeData(type, revisionData);
732
- const revisionId = createRevisionId();
733
- const buildQueries = () => {
734
- return [
735
- sql`
736
- INSERT INTO revisions (id, collection, entry_id, data, author_id)
737
- VALUES (${revisionId}, ${type}, ${id}, ${JSON.stringify(normalizedSnapshot)}, ${authorId})
738
- RETURNING id
739
- `,
740
- sql`
741
- UPDATE ${sql.ref(tableName)}
742
- SET draft_revision_id = ${revisionId},
743
- version = version + 1
744
- WHERE id = ${id}
745
- AND deleted_at IS NULL
746
- AND version = ${existing.version}
747
- AND updated_at = ${existing.updatedAt}
748
- AND status = ${existing.status}
749
- AND ${nullableColumnMatch("live_revision_id", existing.liveRevisionId)}
750
- AND ${nullableColumnMatch("draft_revision_id", existing.draftRevisionId)}
751
- AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
752
- RETURNING id
753
- `,
754
- sql`
755
- DELETE FROM revisions
756
- WHERE id = ${revisionId}
757
- AND NOT EXISTS (
758
- SELECT 1 FROM ${sql.ref(tableName)}
759
- WHERE id = ${id}
760
- AND draft_revision_id = ${revisionId}
761
- AND version = ${existing.version + 1}
762
- )
763
- `
764
- ];
765
- };
766
- const batched = await executeAtomicBatchIfSupported(this.db, buildQueries());
767
- if (batched) {
768
- if (batched[0]?.rows.length !== 1 || batched[1]?.rows.length !== 1) throw new ContentMutationConflictError();
769
- } else await withTransaction(this.db, async (trx) => {
770
- const [audit, stage, removeUnstagedAudit] = buildQueries();
771
- if ((await audit.execute(trx)).rows.length !== 1) throw new ContentMutationConflictError();
772
- const stageResult = await stage.execute(trx);
773
- await removeUnstagedAudit.execute(trx);
774
- if (stageResult.rows.length !== 1) throw new ContentMutationConflictError();
775
- });
776
- invalidateCollectionCache(type);
777
- await new RevisionRepository(this.db, this.datetimeContexts).queuePruning(type, id, revisionId);
778
- return revisionId;
779
- }
780
- /**
781
- * Update plugin-authored fields without letting content columns diverge
782
- * from the revision pointers that publication promotes.
783
- */
784
- async updateDraftAware(type, id, input) {
785
- const data = input.data ? await this.datetimes.normalizeInput(type, writableContentData(input.data)) : {};
786
- const stagedSlug = typeof input.slug === "string" ? input.slug : void 0;
787
- if (!(Object.keys(data).length > 0 || stagedSlug !== void 0)) return this.update(type, id, {
788
- ...input,
789
- data
790
- });
791
- const collectionRows = await this.db.selectFrom("_emdash_collections as collection").leftJoin("_emdash_fields as field", "field.collection_id", "collection.id").select(["collection.supports", "field.slug as fieldSlug"]).where("collection.slug", "=", type).execute();
792
- const supportsRaw = collectionRows[0]?.supports;
793
- const supports = supportsRaw ? JSON.parse(supportsRaw) : [];
794
- if (!Array.isArray(supports) || !supports.includes("revisions")) return this.update(type, id, {
795
- ...input,
796
- data
797
- });
798
- const fieldSlugs = new Set(collectionRows.map((row) => row.fieldSlug).filter((slug) => Boolean(slug)));
799
- const revisionRepo = new RevisionRepository(this.db, this.datetimeContexts);
800
- let existing = await this.findById(type, id);
801
- let incoming = data;
802
- if (existing) {
803
- let stored = existing.data ?? {};
804
- if (existing.draftRevisionId) {
805
- const draft = await revisionRepo.findById(existing.draftRevisionId);
806
- if (draft?.data) stored = draft.data;
807
- }
808
- const stale = staleStoredKeys(data, stored, fieldSlugs);
809
- if (stale.length > 0) {
810
- incoming = { ...data };
811
- for (const key of stale) delete incoming[key];
812
- }
813
- }
814
- for (const field of Object.keys(incoming)) if (!fieldSlugs.has(field)) throw new EmDashValidationError(`Unknown field '${field}' in collection '${type}'`);
815
- for (let attempt = 0; existing && attempt < MAX_DRAFT_STAGE_ATTEMPTS; attempt++) {
816
- let baseData = existing.data;
817
- if (existing.draftRevisionId) {
818
- const draft = await revisionRepo.findById(existing.draftRevisionId);
819
- if (draft) baseData = draft.data;
820
- }
821
- const mergedData = keepKnownFields({
822
- ...baseData,
823
- ...incoming
824
- }, fieldSlugs);
825
- if (stagedSlug !== void 0) mergedData._slug = stagedSlug;
826
- const revision = await revisionRepo.create({
827
- collection: type,
828
- entryId: id,
829
- data: mergedData,
830
- ...input.revisionAuthorId ? { authorId: input.revisionAuthorId } : {}
831
- });
832
- let staged;
833
- try {
834
- staged = await this.replaceDraftRevisionForUpdate(type, id, revision.id, existing, input);
835
- } catch (error) {
836
- await this.deleteUnstagedRevision(revisionRepo, type, id, revision.id);
837
- throw error;
838
- }
839
- if (staged) {
840
- const updated = await this.findById(type, id);
841
- if (!updated) throw new Error("Content not found");
842
- const draftData = {};
843
- for (const [key, value] of Object.entries(mergedData)) if (!key.startsWith("_")) draftData[key] = value;
844
- return {
845
- ...updated,
846
- data: {
847
- ...updated.data,
848
- ...draftData
849
- }
850
- };
851
- }
852
- await this.deleteUnstagedRevision(revisionRepo, type, id, revision.id);
853
- existing = await this.findById(type, id);
854
- }
855
- if (!existing) throw new Error("Content not found");
856
- throw new ContentMutationConflictError();
857
- }
858
- async deleteUnstagedRevision(revisionRepo, type, id, revisionId) {
859
- try {
860
- await revisionRepo.deleteIfUnreferenced(type, id, revisionId);
861
- } catch (error) {
862
- console.error(`[content] Failed to clean up unstaged revision ${revisionId}:`, error);
863
- }
864
- }
865
- async replaceDraftRevisionForUpdate(type, id, revisionId, expected, input) {
866
- const tableName = getTableName(type);
867
- const assignments = [sql`draft_revision_id = ${revisionId}`];
868
- let liveMetadataChanged = false;
869
- if (input.status !== void 0) {
870
- assignments.push(sql`status = ${input.status}`);
871
- liveMetadataChanged = true;
872
- }
873
- if (input.slug === null) {
874
- assignments.push(sql`slug = NULL`);
875
- liveMetadataChanged = true;
876
- }
877
- if (input.publishedAt !== void 0) {
878
- const publishedAt = input.publishedAt === null ? null : await this.datetimes.normalizeValue(type, input.publishedAt);
879
- assignments.push(sql`published_at = ${publishedAt}`);
880
- liveMetadataChanged = true;
881
- }
882
- if (input.scheduledAt !== void 0) {
883
- const scheduledAt = input.scheduledAt === null ? null : await this.datetimes.normalizeValue(type, input.scheduledAt);
884
- assignments.push(sql`scheduled_at = ${scheduledAt}`);
885
- liveMetadataChanged = true;
886
- }
887
- if (input.authorId !== void 0) {
888
- assignments.push(sql`author_id = ${input.authorId}`);
889
- liveMetadataChanged = true;
890
- }
891
- if (input.primaryBylineId !== void 0) {
892
- assignments.push(sql`primary_byline_id = ${input.primaryBylineId}`);
893
- liveMetadataChanged = true;
894
- }
895
- if (liveMetadataChanged) assignments.push(sql`updated_at = ${(/* @__PURE__ */ new Date()).toISOString()}`);
896
- assignments.push(sql`version = version + 1`);
897
- const changed = ((await sql`
898
- UPDATE ${sql.ref(tableName)}
899
- SET ${sql.join(assignments, sql`, `)}
900
- WHERE id = ${id}
901
- AND deleted_at IS NULL
902
- AND version = ${expected.version}
903
- AND ${nullableColumnMatch("live_revision_id", expected.liveRevisionId)}
904
- AND ${nullableColumnMatch("draft_revision_id", expected.draftRevisionId)}
905
- AND EXISTS (
906
- SELECT 1 FROM revisions
907
- WHERE revisions.id = ${revisionId}
908
- AND revisions.collection = ${type}
909
- AND revisions.entry_id = ${id}
910
- )
911
- `.execute(this.db)).numAffectedRows ?? 0n) > 0n;
912
- if (changed && liveMetadataChanged) invalidateCollectionCache(type);
913
- return changed;
914
- }
915
- /**
916
- * Copy non-translatable field values to every other entry in the
917
- * translation group, trashed entries included.
918
- *
919
- * Only the fields present in `data` are copied. With `previous`, `data` is
920
- * the entry's complete new state and only the fields whose value differs
921
- * from `previous` are copied, an absent field as null. An entry that already
922
- * holds every copied value is left untouched, so its `updated_at` does not
923
- * move.
924
- *
925
- * On collections with revisions a changed entry also gets a new live
926
- * revision, because republishing and unpublishing read that revision rather
927
- * than the columns. A pending draft takes a value only where it still holds
928
- * the entry's previous value, so an unpublished edit to the field survives.
929
- */
930
- async syncNonTranslatableFields(type, sourceId, translationGroup, data, options = {}) {
931
- const tableName = getTableName(type);
932
- const collectionRows = await this.db.selectFrom("_emdash_collections as collection").leftJoin("_emdash_fields as field", (join) => join.onRef("field.collection_id", "=", "collection.id").on("field.translatable", "=", 0)).select(["collection.supports", "field.slug as fieldSlug"]).where("collection.slug", "=", type).execute();
933
- const { previous } = options;
934
- const values = {};
935
- for (const { fieldSlug } of collectionRows) {
936
- if (!fieldSlug) continue;
937
- if (previous) {
938
- if (!sameStoredValue(previous[fieldSlug], data[fieldSlug])) values[fieldSlug] = data[fieldSlug] ?? null;
939
- } else if (fieldSlug in data) values[fieldSlug] = data[fieldSlug];
940
- }
941
- if (Object.keys(values).length === 0) return;
942
- const normalizedValues = await this.datetimes.normalizeData(type, values);
943
- const supportsRaw = collectionRows[0]?.supports;
944
- const supports = supportsRaw ? JSON.parse(supportsRaw) : [];
945
- const usesRevisions = Array.isArray(supports) && supports.includes("revisions");
946
- const siblings = await sql`
947
- SELECT * FROM ${sql.ref(tableName)}
948
- WHERE translation_group = ${translationGroup}
949
- AND id != ${sourceId}
950
- `.execute(this.db);
951
- let changed = false;
952
- for (const row of siblings.rows) if (await this.syncSiblingValues(type, this.mapRow(type, row), normalizedValues, usesRevisions)) changed = true;
953
- if (changed) invalidateCollectionCache(type);
954
- }
955
- async syncSiblingValues(type, initial, values, usesRevisions) {
956
- const tableName = getTableName(type);
957
- const revisionRepo = new RevisionRepository(this.db, this.datetimeContexts);
958
- let sibling = initial;
959
- for (let attempt = 0; sibling && attempt < MAX_DRAFT_STAGE_ATTEMPTS; attempt++) {
960
- const current = sibling;
961
- const changed = {};
962
- for (const [field, value] of Object.entries(values)) if (!sameStoredValue(current.data[field], value)) changed[field] = value;
963
- if (Object.keys(changed).length === 0) return false;
964
- const assignments = [];
965
- for (const [field, value] of Object.entries(changed)) {
966
- validateIdentifier(field, "content field name");
967
- assignments.push(sql`${sql.ref(field)} = ${serializeValue(value)}`);
968
- }
969
- const createdRevisionIds = [];
970
- const cleanUp = async () => {
971
- for (const revisionId of createdRevisionIds) await this.deleteUnstagedRevision(revisionRepo, type, current.id, revisionId);
972
- };
973
- let synced;
974
- try {
975
- const live = usesRevisions && current.liveRevisionId ? await revisionRepo.findById(current.liveRevisionId) : null;
976
- if (live) {
977
- const revision = await revisionRepo.create({
978
- collection: type,
979
- entryId: current.id,
980
- data: {
981
- ...live.data,
982
- ...changed
983
- }
984
- });
985
- createdRevisionIds.push(revision.id);
986
- assignments.push(sql`live_revision_id = ${revision.id}`);
987
- }
988
- const draft = usesRevisions && current.draftRevisionId ? await revisionRepo.findById(current.draftRevisionId) : null;
989
- const carried = {};
990
- for (const [field, value] of Object.entries(changed)) if (draft && sameStoredValue(draft.data[field], current.data[field])) carried[field] = value;
991
- if (draft && Object.keys(carried).length > 0) {
992
- const revision = await revisionRepo.create({
993
- collection: type,
994
- entryId: current.id,
995
- data: {
996
- ...draft.data,
997
- ...carried
998
- }
999
- });
1000
- createdRevisionIds.push(revision.id);
1001
- assignments.push(sql`draft_revision_id = ${revision.id}`);
1002
- }
1003
- assignments.push(sql`updated_at = ${(/* @__PURE__ */ new Date()).toISOString()}`, sql`version = version + 1`);
1004
- synced = ((await sql`
1005
- UPDATE ${sql.ref(tableName)}
1006
- SET ${sql.join(assignments, sql`, `)}
1007
- WHERE id = ${current.id}
1008
- AND version = ${current.version}
1009
- AND ${nullableColumnMatch("live_revision_id", current.liveRevisionId)}
1010
- AND ${nullableColumnMatch("draft_revision_id", current.draftRevisionId)}
1011
- `.execute(this.db)).numAffectedRows ?? 0n) > 0n;
1012
- } catch (error) {
1013
- await cleanUp();
1014
- throw error;
1015
- }
1016
- if (synced) return true;
1017
- await cleanUp();
1018
- sibling = await this.findByIdIncludingTrashed(type, current.id);
1019
- }
1020
- if (!sibling) return false;
1021
- throw new ContentMutationConflictError();
1022
- }
1023
- /**
1024
- * Delete content (soft delete - moves to trash)
1025
- */
1026
- async delete(type, id) {
1027
- const tableName = getTableName(type);
1028
- const now = (/* @__PURE__ */ new Date()).toISOString();
1029
- const changed = ((await sql`
1030
- UPDATE ${sql.ref(tableName)}
1031
- SET deleted_at = ${now}
1032
- WHERE id = ${id}
1033
- AND deleted_at IS NULL
1034
- `.execute(this.db)).numAffectedRows ?? 0n) > 0n;
1035
- if (changed) invalidateCollectionCache(type);
1036
- return changed;
1037
- }
1038
- /**
1039
- * Restore content from trash
1040
- *
1041
- * The entry comes back as a draft with no schedule. The live version is not
1042
- * copied into a draft revision: collections without revisions save to the
1043
- * columns, and a draft revision would hide those saves.
1044
- */
1045
- async restore(type, id, expectedRevision) {
1046
- const tableName = getTableName(type);
1047
- const existing = await this.findByIdOrSlugIncludingTrashed(type, id);
1048
- if (!existing) return null;
1049
- assertRevisionPrecondition(existing, expectedRevision);
1050
- const now = (/* @__PURE__ */ new Date()).toISOString();
1051
- const restored = (await sql`
1052
- UPDATE ${sql.ref(tableName)}
1053
- SET deleted_at = NULL,
1054
- live_revision_id = NULL,
1055
- status = 'draft',
1056
- scheduled_at = NULL,
1057
- updated_at = ${now},
1058
- version = ${existing.version + 1}
1059
- WHERE id = ${existing.id}
1060
- AND deleted_at IS NOT NULL
1061
- AND version = ${existing.version}
1062
- AND updated_at = ${existing.updatedAt}
1063
- RETURNING *
1064
- `.execute(this.db)).rows[0];
1065
- if (!restored) throw new ContentMutationConflictError("Content changed while restoring");
1066
- invalidateCollectionCache(type);
1067
- return this.mapRow(type, restored);
1068
- }
1069
- /**
1070
- * Permanently delete content (cannot be undone)
1071
- */
1072
- /**
1073
- * Permanently delete a soft-deleted content row.
1074
- *
1075
- * Returns `true` only when a soft-deleted (trashed) row was removed.
1076
- * Returns `false` when no row exists OR when the row exists but is live —
1077
- * the caller is responsible for distinguishing these cases (typically via
1078
- * a follow-up `findByIdOrSlugIncludingTrashed` to surface NOT_FOUND vs
1079
- * NOT_TRASHED). The `AND deleted_at IS NOT NULL` clause is the safety net
1080
- * that prevents permanent delete from bypassing the trash workflow.
1081
- */
1082
- async permanentDelete(type, id) {
1083
- const tableName = getTableName(type);
1084
- const changed = ((await sql`
1085
- DELETE FROM ${sql.ref(tableName)}
1086
- WHERE id = ${id}
1087
- AND deleted_at IS NOT NULL
1088
- `.execute(this.db)).numAffectedRows ?? 0n) > 0n;
1089
- if (changed) invalidateCollectionCache(type);
1090
- return changed;
1091
- }
1092
- /**
1093
- * Find trashed content items
1094
- */
1095
- async findTrashed(type, options = {}) {
1096
- const tableName = getTableName(type);
1097
- const limit = Math.max(1, Math.min(options.limit || 50, 100));
1098
- const orderField = options.orderBy?.field || "deletedAt";
1099
- const orderDirection = options.orderBy?.direction || "desc";
1100
- const dbField = this.mapOrderField(orderField);
1101
- const safeOrderDirection = orderDirection.toLowerCase() === "asc" ? "ASC" : "DESC";
1102
- let query = this.db.selectFrom(tableName).selectAll().where("deleted_at", "is not", null);
1103
- if (options.where?.locale) query = query.where("locale", "=", options.where.locale);
1104
- if (options.cursor) {
1105
- const { orderValue, id: cursorId } = decodeCursor(options.cursor);
1106
- if (safeOrderDirection === "DESC") query = query.where((eb) => eb.or([eb(dbField, "<", orderValue), eb.and([eb(dbField, "=", orderValue), eb("id", "<", cursorId)])]));
1107
- else query = query.where((eb) => eb.or([eb(dbField, ">", orderValue), eb.and([eb(dbField, "=", orderValue), eb("id", ">", cursorId)])]));
1108
- }
1109
- query = query.orderBy(dbField, safeOrderDirection === "ASC" ? "asc" : "desc").orderBy("id", safeOrderDirection === "ASC" ? "asc" : "desc").limit(limit + 1);
1110
- const rows = await query.execute();
1111
- const hasMore = rows.length > limit;
1112
- const items = rows.slice(0, limit);
1113
- const mappedResult = { items: items.map((row) => {
1114
- const record = row;
1115
- return {
1116
- ...this.mapRow(type, record),
1117
- deletedAt: typeof record.deleted_at === "string" ? record.deleted_at : ""
1118
- };
1119
- }) };
1120
- if (hasMore && items.length > 0) {
1121
- const lastRow = items.at(-1);
1122
- const lastOrderValue = lastRow[dbField];
1123
- mappedResult.nextCursor = encodeCursor(typeof lastOrderValue === "string" || typeof lastOrderValue === "number" ? String(lastOrderValue) : "", String(lastRow.id));
1124
- }
1125
- return mappedResult;
1126
- }
1127
- /**
1128
- * Count trashed content items
1129
- */
1130
- async countTrashed(type, options = {}) {
1131
- const tableName = getTableName(type);
1132
- let query = this.db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count")).where("deleted_at", "is not", null);
1133
- if (options.locale) query = query.where("locale", "=", options.locale);
1134
- const result = await query.executeTakeFirst();
1135
- return Number(result?.count || 0);
1136
- }
1137
- /**
1138
- * Apply the optional `q` filter.
1139
- *
1140
- * When the handler sets `useFts` (collection has a healthy FTS5 index
1141
- * covering the display columns; SQLite only), the filter is served from
1142
- * the index: a token-prefix MATCH against `_emdash_fts_<slug>` OR'd with
1143
- * an index-served `slug GLOB 'term*'` prefix (the slug is not in the FTS
1144
- * index). Both sides are index-backed, so SQLite's OR optimization avoids
1145
- * the full-table scan the LIKE fallback needs (#1517). The trade-off is
1146
- * search semantics: token-prefix matching instead of arbitrary substring.
1147
- *
1148
- * Fallback (Postgres, search disabled, or no usable terms): case-
1149
- * insensitive substring LIKE across the handler-resolved `searchColumns`
1150
- * (OR'd). User input is treated literally (LIKE wildcards escaped) and
1151
- * `lower()` is applied on both sides for SQLite/Postgres parity.
1152
- */
1153
- applySearchFilter(query, where, type) {
1154
- const term = where?.q?.trim();
1155
- const columns = where?.searchColumns;
1156
- if (!term || !columns || columns.length === 0) return query;
1157
- if (where.useFts) {
1158
- const match = buildFtsPrefixMatch(term);
1159
- if (match) {
1160
- validateIdentifier(type, "collection slug");
1161
- const ftsTable = `_emdash_fts_${type}`;
1162
- const slugPrefix = buildSlugGlobPrefix(term);
1163
- return query.where((eb) => eb.or([sql`id IN (SELECT id FROM ${sql.ref(ftsTable)} WHERE ${sql.ref(ftsTable)} MATCH ${match})`, sql`slug GLOB ${slugPrefix}`]));
1164
- }
1165
- }
1166
- const pattern = `%${term.replace(LIKE_WILDCARD_RE, (c) => `\\${c}`)}%`;
1167
- return query.where((eb) => eb.or(columns.map((col) => {
1168
- validateIdentifier(col, "search column");
1169
- return sql`lower(CAST(${sql.ref(col)} AS TEXT)) LIKE lower(${pattern}) ESCAPE '\\'`;
1170
- })));
1171
- }
1172
- /**
1173
- * Apply the optional inclusive date-range filter. The field is mapped
1174
- * through `DATE_FILTER_COLUMNS` (a closed whitelist), and bounds compare
1175
- * lexicographically against the stored ISO 8601 timestamps. A `publishedAt`
1176
- * range naturally excludes never-published rows (their column is NULL).
1177
- */
1178
- applyDateFilter(query, where) {
1179
- const filter = where?.dateFilter;
1180
- if (!filter) return query;
1181
- const column = DATE_FILTER_COLUMNS[filter.field];
1182
- if (!column) throw new EmDashValidationError(`Invalid date filter field: ${filter.field}`);
1183
- const { from, to } = filter;
1184
- if (!from && !to) return query;
1185
- let next = query;
1186
- if (from) next = next.where((eb) => eb(column, ">=", from));
1187
- if (to) next = next.where((eb) => eb(column, "<=", to));
1188
- return next;
1189
- }
1190
- /**
1191
- * Apply the optional byline filter as a correlated (NOT) EXISTS against
1192
- * `_emdash_content_bylines`.
1193
- *
1194
- * Correlating from the content table preserves the outer sort index so
1195
- * `LIMIT` can short-circuit. `mode: "none"` tests the junction rather than
1196
- * `primary_byline_id` because the two are written in the same call but
1197
- * are not atomically consistent, so the junction is authoritative.
1198
- *
1199
- * Whether a credit *renders* is locale-scoped; whether one *exists* is
1200
- * not. Both are needed: the first decides what the filter matches, the
1201
- * second decides whether the author fallback applies at all.
1202
- */
1203
- applyBylineFilter(query, where, type) {
1204
- const filter = where?.bylineFilter;
1205
- if (!filter) return query;
1206
- const tableName = getTableName(type);
1207
- const idColumn = `${tableName}.id`;
1208
- const authorColumn = `${tableName}.author_id`;
1209
- const localeColumn = `${tableName}.locale`;
1210
- const creditRenders = (eb, bylineIds) => {
1211
- let sub = eb.selectFrom("_emdash_content_bylines as cb").innerJoin("_emdash_bylines as b", "b.translation_group", "cb.byline_id").select("cb.id").where("cb.collection_slug", "=", type).whereRef("cb.content_id", "=", idColumn);
1212
- sub = filter.locale ? sub.where("b.locale", "=", filter.locale) : sub.whereRef("b.locale", "=", localeColumn);
1213
- if (bylineIds) sub = sub.where("cb.byline_id", "in", bylineIds);
1214
- return eb.exists(sub);
1215
- };
1216
- const hasExplicitCredit = (eb) => eb.exists(eb.selectFrom("_emdash_content_bylines as cb").select("cb.id").where("cb.collection_slug", "=", type).whereRef("cb.content_id", "=", idColumn));
1217
- const authorHasByline = (eb, bylineIds) => {
1218
- let sub = eb.selectFrom("_emdash_bylines as b").select("b.id").whereRef("b.user_id", "=", authorColumn);
1219
- sub = filter.locale ? sub.where("b.locale", "=", filter.locale) : sub.whereRef("b.locale", "=", localeColumn);
1220
- if (bylineIds) sub = sub.where("b.translation_group", "in", bylineIds);
1221
- return eb.exists(sub);
1222
- };
1223
- if (filter.mode === "none") return query.where((eb) => {
1224
- const uncredited = eb.not(creditRenders(eb));
1225
- return filter.includeInferred ? eb.and([uncredited, eb.or([hasExplicitCredit(eb), eb.not(authorHasByline(eb))])]) : uncredited;
1226
- });
1227
- const bylineIds = filter.bylineIds ?? [];
1228
- if (bylineIds.length === 0) return query.where(() => sql`1 = 0`);
1229
- return query.where((eb) => {
1230
- if (!filter.includeInferred) return creditRenders(eb, bylineIds);
1231
- return eb.or([creditRenders(eb, bylineIds), eb.and([eb.not(hasExplicitCredit(eb)), authorHasByline(eb, bylineIds)])]);
1232
- });
1233
- }
1234
- /**
1235
- * Count content items
1236
- */
1237
- async count(type, where) {
1238
- const resolvedFieldFilters = await this.resolveFieldFilters(type, where?.fieldFilters);
1239
- return this.countWithResolvedFilters(type, where, resolvedFieldFilters);
1240
- }
1241
- async countWithResolvedFilters(type, where, resolvedFieldFilters) {
1242
- const tableName = getTableName(type);
1243
- let query = this.db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count")).where("deleted_at", "is", null);
1244
- if (where?.status) query = query.where("status", "=", where.status);
1245
- if (where?.authorId) query = query.where("author_id", "=", where.authorId);
1246
- if (where?.locale) query = query.where("locale", "=", where.locale);
1247
- query = this.applySearchFilter(query, where, type);
1248
- query = this.applyDateFilter(query, where);
1249
- query = this.applyBylineFilter(query, where, type);
1250
- query = this.applyFieldFilters(query, resolvedFieldFilters);
1251
- const result = await query.executeTakeFirst();
1252
- return Number(result?.count || 0);
1253
- }
1254
- /**
1255
- * Distinct, non-null `author_id` values across the collection's live
1256
- * (non-trashed) content. Used to populate the admin author filter with
1257
- * only the users who have actually authored entries, rather than the
1258
- * full user directory (which requires admin privileges to read).
1259
- */
1260
- async findDistinctAuthorIds(type) {
1261
- const tableName = getTableName(type);
1262
- return (await this.db.selectFrom(tableName).select("author_id").distinct().where("deleted_at", "is", null).where("author_id", "is not", null).execute()).map((row) => row.author_id).filter((id) => id !== null);
1263
- }
1264
- async getStats(type, now = /* @__PURE__ */ new Date()) {
1265
- const tableName = getTableName(type);
1266
- const nowIso = now.toISOString();
1267
- const result = await this.db.selectFrom(tableName).select((eb) => [
1268
- eb.fn.count("id").as("total"),
1269
- eb.fn.sum(eb.case().when("status", "=", "published").then(1).else(0).end()).as("published"),
1270
- eb.fn.sum(eb.case().when("status", "=", "draft").then(1).else(0).end()).as("draft"),
1271
- sql`SUM(CASE WHEN scheduled_at IS NOT NULL THEN 1 ELSE 0 END)`.as("scheduled"),
1272
- sql`SUM(CASE WHEN scheduled_at IS NOT NULL AND scheduled_at <= ${nowIso} THEN 1 ELSE 0 END)`.as("overdue_scheduled")
1273
- ]).where("deleted_at", "is", null).executeTakeFirst();
1274
- return {
1275
- total: Number(result?.total || 0),
1276
- published: Number(result?.published || 0),
1277
- draft: Number(result?.draft || 0),
1278
- scheduled: Number(result?.scheduled || 0),
1279
- overdueScheduled: Number(result?.overdue_scheduled || 0)
1280
- };
1281
- }
1282
- /**
1283
- * Schedule content for future publishing
1284
- *
1285
- * Sets status to 'scheduled' and stores the scheduled publish time.
1286
- * The content will be auto-published when the scheduled time is reached.
1287
- */
1288
- async schedule(type, id, scheduledAt, currentTime = /* @__PURE__ */ new Date(), expectedRevision) {
1289
- const tableName = getTableName(type);
1290
- const now = currentTime.toISOString();
1291
- const normalizedScheduledAt = await this.datetimes.normalizeValue(type, scheduledAt);
1292
- if (new Date(normalizedScheduledAt) <= currentTime) throw new EmDashValidationError("Scheduled date must be in the future");
1293
- const existing = await this.findById(type, id);
1294
- if (!existing) throw new EmDashValidationError("Content item not found");
1295
- assertRevisionPrecondition(existing, expectedRevision);
1296
- const newStatus = existing.status === "published" ? "published" : "scheduled";
1297
- if (((await sql`
1298
- UPDATE ${sql.ref(tableName)}
1299
- SET status = ${newStatus},
1300
- scheduled_at = ${normalizedScheduledAt},
1301
- updated_at = ${now}
1302
- WHERE id = ${id}
1303
- AND deleted_at IS NULL
1304
- AND version = ${existing.version}
1305
- AND updated_at = ${existing.updatedAt}
1306
- AND status = ${existing.status}
1307
- AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
1308
- `.execute(this.db)).numAffectedRows ?? 0n) === 0n) throw new ContentMutationConflictError("Content changed while scheduling");
1309
- invalidateCollectionCache(type);
1310
- const updated = await this.findById(type, id);
1311
- if (!updated) throw new Error("Content not found");
1312
- return updated;
1313
- }
1314
- /**
1315
- * Unschedule content
1316
- *
1317
- * Clears the scheduled time. Published posts stay published;
1318
- * draft/scheduled posts revert to 'draft'.
1319
- */
1320
- async unschedule(type, id, expectedRevision) {
1321
- const tableName = getTableName(type);
1322
- const now = (/* @__PURE__ */ new Date()).toISOString();
1323
- const existing = await this.findById(type, id);
1324
- if (!existing) throw new EmDashValidationError("Content item not found");
1325
- assertRevisionPrecondition(existing, expectedRevision);
1326
- const newStatus = existing.status === "published" ? "published" : "draft";
1327
- const result = await sql`
1328
- UPDATE ${sql.ref(tableName)}
1329
- SET status = ${newStatus},
1330
- scheduled_at = NULL,
1331
- updated_at = ${now}
1332
- WHERE id = ${id}
1333
- AND scheduled_at IS NOT NULL
1334
- AND deleted_at IS NULL
1335
- AND version = ${existing.version}
1336
- AND updated_at = ${existing.updatedAt}
1337
- AND status = ${existing.status}
1338
- AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
1339
- `.execute(this.db);
1340
- if (existing.scheduledAt !== null && (result.numAffectedRows ?? 0n) === 0n) throw new ContentMutationConflictError("Content changed while unscheduling");
1341
- invalidateCollectionCache(type);
1342
- const updated = await this.findById(type, id);
1343
- if (!updated) throw new Error("Content not found");
1344
- return updated;
1345
- }
1346
- /**
1347
- * Find content that is ready to be published
1348
- *
1349
- * Returns all content where scheduled_at <= now, regardless of status.
1350
- * This covers both draft-scheduled posts (status='scheduled') and
1351
- * published posts with scheduled draft changes (status='published').
1352
- *
1353
- * `limit` (optional) caps how many due rows are returned, oldest-due first.
1354
- * The scheduled-publishing sweep passes a limit so a large backlog can't
1355
- * fan out unbounded publish/webhook work in a single tick (and blow a Worker
1356
- * invocation's CPU/subrequest budget); the remainder drains on later ticks.
1357
- */
1358
- async findReadyToPublish(type, limit, currentTime = /* @__PURE__ */ new Date(), after) {
1359
- const tableName = getTableName(type);
1360
- const now = currentTime.toISOString();
1361
- const limitClause = typeof limit === "number" && Number.isInteger(limit) && limit > 0 ? sql`LIMIT ${limit}` : sql``;
1362
- const afterClause = after ? sql`AND (scheduled_at > ${after.scheduledAt} OR (scheduled_at = ${after.scheduledAt} AND id > ${after.id}))` : sql``;
1363
- return (await sql`
1364
- SELECT * FROM ${sql.ref(tableName)}
1365
- WHERE scheduled_at IS NOT NULL
1366
- AND scheduled_at <= ${now}
1367
- AND deleted_at IS NULL
1368
- ${afterClause}
1369
- ORDER BY scheduled_at ASC, id ASC
1370
- ${limitClause}
1371
- `.execute(this.db)).rows.map((row) => this.mapRow(type, row));
1372
- }
1373
- async findTranslationIds(type, translationGroup) {
1374
- const tableName = getTableName(type);
1375
- return (await sql`
1376
- SELECT id FROM ${sql.ref(tableName)}
1377
- WHERE translation_group = ${translationGroup}
1378
- AND deleted_at IS NULL
1379
- `.execute(this.db)).rows.map((row) => row.id);
1380
- }
1381
- async findTranslationIdsForGroups(type, translationGroups) {
1382
- const ids = /* @__PURE__ */ new Map();
1383
- if (translationGroups.length === 0) return ids;
1384
- const tableName = getTableName(type);
1385
- for (const batch of chunks([...new Set(translationGroups)], SQL_BATCH_SIZE)) {
1386
- const result = await sql`
1387
- SELECT id, translation_group FROM ${sql.ref(tableName)}
1388
- WHERE translation_group IN (${sql.join(batch)}) AND deleted_at IS NULL
1389
- `.execute(this.db);
1390
- for (const row of result.rows) {
1391
- const group = ids.get(row.translation_group) ?? [];
1392
- group.push(row.id);
1393
- ids.set(row.translation_group, group);
1394
- }
1395
- }
1396
- return ids;
1397
- }
1398
- /**
1399
- * Find all translations in a translation group
1400
- */
1401
- async findTranslations(type, translationGroup) {
1402
- const tableName = getTableName(type);
1403
- return (await sql`
1404
- SELECT * FROM ${sql.ref(tableName)}
1405
- WHERE translation_group = ${translationGroup}
1406
- AND deleted_at IS NULL
1407
- ORDER BY locale ASC
1408
- `.execute(this.db)).rows.map((row) => this.mapRow(type, row));
1409
- }
1410
- /**
1411
- * Whether any row still shares `translationGroup`, trashed rows included.
1412
- * Group-keyed satellite data (term assignments, reference edges) is owned by
1413
- * the group, not by a single locale row, so a purge may only cascade to it
1414
- * once nothing is left to own it — and a trashed sibling is still restorable.
1415
- */
1416
- async hasTranslationsIncludingTrashed(type, translationGroup, options = {}) {
1417
- const tableName = getTableName(type);
1418
- const exclusion = options.excludeId ? sql`AND id != ${options.excludeId}` : sql``;
1419
- return (await sql`
1420
- SELECT id FROM ${sql.ref(tableName)}
1421
- WHERE translation_group = ${translationGroup}
1422
- ${exclusion}
1423
- LIMIT 1
1424
- `.execute(this.db)).rows.length > 0;
1425
- }
1426
- /**
1427
- * Batch variant of {@link findTranslations}: every (non-deleted) locale
1428
- * variant for any of `translationGroups`, in one `WHERE translation_group IN
1429
- * (...)` query chunked at `SQL_BATCH_SIZE` for D1's bind-parameter limit.
1430
- * Lets callers resolve many edge groups without an N+1 per group. The caller
1431
- * groups the flat result by `translationGroup` itself.
1432
- *
1433
- * `translation_group` leads the sort so the ordering follows
1434
- * `idx_{table}_del_tg_locale` past its `deleted_at` equality; callers group by
1435
- * `translationGroup`, so the per-group locale order they rely on is preserved.
1436
- *
1437
- * `publishedOnly` restricts the result to `status = 'published'` — reference
1438
- * reads pass this for callers without `content:read_drafts` so draft/scheduled
1439
- * entries never leak through an edge traversal.
1440
- *
1441
- * A reference edge stores only a collection slug (no SQL FK), so the table may
1442
- * have been dropped since the edge was written. That is a tolerated dangling
1443
- * state, not an error: a missing table resolves to no rows, mirroring how the
1444
- * content read handlers treat `isMissingTableError`.
1445
- */
1446
- async findTranslationsForGroups(type, translationGroups, options = {}) {
1447
- if (translationGroups.length === 0) return [];
1448
- const tableName = getTableName(type);
1449
- const publishedFilter = options.publishedOnly ? sql`AND status = 'published'` : sql``;
1450
- const items = [];
1451
- try {
1452
- for (const chunk of chunks(translationGroups, SQL_BATCH_SIZE)) {
1453
- const result = await sql`
1454
- SELECT * FROM ${sql.ref(tableName)}
1455
- WHERE translation_group IN (${sql.join(chunk)})
1456
- AND deleted_at IS NULL
1457
- ${publishedFilter}
1458
- ORDER BY translation_group ASC, locale ASC
1459
- `.execute(this.db);
1460
- for (const row of result.rows) items.push(this.mapRow(type, row));
1461
- }
1462
- } catch (error) {
1463
- if (isMissingTableError(error)) return [];
1464
- throw error;
1465
- }
1466
- return items;
1467
- }
1468
- /**
1469
- * Batch variant of {@link findByIdOrSlug}: resolve many identifiers (each an
1470
- * id OR a slug) within `type` in a constant number of queries — one `WHERE id
1471
- * IN (...)` and one `WHERE slug IN (...)`, each chunked at `SQL_BATCH_SIZE`.
1472
- * Returns a map from the input identifier to its resolved item; identifiers
1473
- * that match nothing are absent. Used on write paths that accept a list of
1474
- * references, so a single request doesn't fan out to an N+1 of point lookups.
1475
- *
1476
- * Resolution mirrors {@link findByIdOrSlug}: a ULID-shaped identifier prefers
1477
- * the id match and falls back to slug; anything else prefers the slug match
1478
- * and falls back to id. Slug matches collapse to the lowest-locale variant
1479
- * (`ORDER BY locale ASC`), matching the slug-without-locale lookup.
1480
- */
1481
- async findManyByIdOrSlug(type, identifiers) {
1482
- const resolved = /* @__PURE__ */ new Map();
1483
- const unique = [...new Set(identifiers)];
1484
- if (unique.length === 0) return resolved;
1485
- const tableName = getTableName(type);
1486
- const byId = /* @__PURE__ */ new Map();
1487
- const bySlug = /* @__PURE__ */ new Map();
1488
- try {
1489
- for (const chunk of chunks(unique, SQL_BATCH_SIZE)) {
1490
- const idRows = await sql`
1491
- SELECT * FROM ${sql.ref(tableName)}
1492
- WHERE id IN (${sql.join(chunk)})
1493
- AND deleted_at IS NULL
1494
- `.execute(this.db);
1495
- for (const row of idRows.rows) {
1496
- const item = this.mapRow(type, row);
1497
- byId.set(item.id, item);
1498
- }
1499
- const slugRows = await sql`
1500
- SELECT * FROM ${sql.ref(tableName)}
1501
- WHERE slug IN (${sql.join(chunk)})
1502
- AND deleted_at IS NULL
1503
- ORDER BY locale ASC
1504
- `.execute(this.db);
1505
- for (const row of slugRows.rows) {
1506
- const item = this.mapRow(type, row);
1507
- if (item.slug != null && !bySlug.has(item.slug)) bySlug.set(item.slug, item);
1508
- }
1509
- }
1510
- } catch (error) {
1511
- if (isMissingTableError(error)) return resolved;
1512
- throw error;
1513
- }
1514
- for (const identifier of unique) {
1515
- const item = ULID_PATTERN.test(identifier) ? byId.get(identifier) ?? bySlug.get(identifier) : bySlug.get(identifier) ?? byId.get(identifier);
1516
- if (item) resolved.set(identifier, item);
1517
- }
1518
- return resolved;
1519
- }
1520
- /**
1521
- * Publish the current draft
1522
- *
1523
- * Promotes draft_revision_id to live_revision_id and clears draft pointer.
1524
- * Syncs the draft revision's data into the content table columns so the
1525
- * content table always reflects the published version.
1526
- * If no draft revision exists, creates one from current data and publishes it.
1527
- * When `promoteRevision` is false, publishes the current content-table data
1528
- * by changing lifecycle metadata only.
1529
- *
1530
- * `publishedAt` (optional) overrides the publication timestamp. If omitted,
1531
- * the existing `published_at` is preserved (idempotent re-publish keeps the
1532
- * original date) and falls back to the current time on first publish. Pass
1533
- * an explicit value to backdate a publish (e.g. when migrating content from
1534
- * another CMS).
1535
- *
1536
- * `requireDue` gates the final update on the row still being due.
1537
- * `expectedScheduledAt` additionally fences changes made after a sweep
1538
- * selected the row but before publication preparation began.
1539
- */
1540
- async publish(type, id, publishedAt, requireDue = false, expectedScheduledAt, promoteRevision = true, requireSlug = true, expectedRevision, currentTime = /* @__PURE__ */ new Date()) {
1541
- const tableName = getTableName(type);
1542
- const now = currentTime.toISOString();
1543
- const existing = await this.findById(type, id);
1544
- if (!existing) throw new EmDashValidationError("Content item not found");
1545
- assertRevisionPrecondition(existing, expectedRevision);
1546
- const requestedPublishedAt = publishedAt ? await this.datetimes.normalizeValue(type, publishedAt) : void 0;
1547
- const currentPublishedAt = existing.publishedAt ? await this.datetimes.normalizeValue(type, existing.publishedAt) : null;
1548
- if (requireDue && expectedScheduledAt !== void 0 && existing.scheduledAt !== expectedScheduledAt) throw new ScheduledNotDueError();
1549
- if (!promoteRevision && requireSlug && !existing.slug?.trim()) throw new EmDashValidationError("Cannot publish routable content without a slug");
1550
- if (!promoteRevision) {
1551
- const revisionRepo = new RevisionRepository(this.db, this.datetimeContexts);
1552
- let provisionalRevisionId = null;
1553
- try {
1554
- let liveRevisionId = existing.liveRevisionId;
1555
- if (!liveRevisionId) {
1556
- const revision = await revisionRepo.create({
1557
- collection: type,
1558
- entryId: id,
1559
- data: existing.data
1560
- });
1561
- liveRevisionId = revision.id;
1562
- provisionalRevisionId = revision.id;
1563
- }
1564
- const intendedPublishedAt = requestedPublishedAt ?? currentPublishedAt ?? now;
1565
- const duePredicate = requireDue ? sql`AND scheduled_at IS NOT NULL AND scheduled_at <= ${now}` : sql``;
1566
- let published = false;
1567
- try {
1568
- published = ((await sql`
1569
- UPDATE ${sql.ref(tableName)}
1570
- SET live_revision_id = ${liveRevisionId},
1571
- draft_revision_id = NULL,
1572
- status = 'published',
1573
- scheduled_at = NULL,
1574
- published_at = ${intendedPublishedAt},
1575
- updated_at = ${now},
1576
- version = version + 1
1577
- WHERE id = ${id}
1578
- AND deleted_at IS NULL
1579
- AND version = ${existing.version}
1580
- AND updated_at = ${existing.updatedAt}
1581
- AND status = ${existing.status}
1582
- AND ${nullableColumnMatch("live_revision_id", existing.liveRevisionId)}
1583
- AND ${nullableColumnMatch("draft_revision_id", existing.draftRevisionId)}
1584
- AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
1585
- ${duePredicate}
1586
- `.execute(this.db)).numAffectedRows ?? 0n) > 0n;
1587
- } catch (error) {
1588
- if (isConfirmedStatementFailure(error)) throw error;
1589
- let observed;
1590
- try {
1591
- observed = await this.findById(type, id);
1592
- } catch (reconciliationError) {
1593
- throw new Error("Unable to confirm whether content publication completed", { cause: reconciliationError });
1594
- }
1595
- if (!observed) throw new Error("Unable to confirm whether content publication completed", { cause: error });
1596
- published = matchesLifecyclePublication(observed, existing, liveRevisionId, intendedPublishedAt, now);
1597
- if (!published && matchesPublicationFence(observed, existing)) throw error;
1598
- if (!published) throw new Error("Unable to confirm whether content publication completed", { cause: error });
1599
- }
1600
- if (!published) throw requireDue ? new ScheduledNotDueError() : new ContentMutationConflictError();
1601
- invalidateCollectionCache(type);
1602
- const updated = await this.findById(type, id);
1603
- if (!updated) throw new Error("Content not found");
1604
- return updated;
1605
- } catch (error) {
1606
- if (provisionalRevisionId) try {
1607
- await revisionRepo.deleteIfUnreferenced(type, id, provisionalRevisionId);
1608
- } catch (cleanupError) {
1609
- console.error(`[content] Failed to clean up provisional revision ${provisionalRevisionId}:`, cleanupError);
1610
- }
1611
- throw error;
1612
- }
1613
- }
1614
- const revisionRepo = new RevisionRepository(this.db, this.datetimeContexts);
1615
- let provisionalRevisionId = null;
1616
- try {
1617
- let revisionToPublish = existing.draftRevisionId || existing.liveRevisionId;
1618
- if (!revisionToPublish) {
1619
- const revision = await revisionRepo.create({
1620
- collection: type,
1621
- entryId: id,
1622
- data: existing.data
1623
- });
1624
- revisionToPublish = revision.id;
1625
- provisionalRevisionId = revision.id;
1626
- }
1627
- const revision = await revisionRepo.findById(revisionToPublish);
1628
- if (!revision || revision.collection !== type || revision.entryId !== id) throw new EmDashValidationError("Revision does not belong to the specified content item");
1629
- const stagedSlug = typeof revision.data._slug === "string" ? revision.data._slug : null;
1630
- const intendedSlug = stagedSlug ?? existing.slug;
1631
- if (requireSlug && !intendedSlug?.trim()) throw new EmDashValidationError("Cannot publish routable content without a slug");
1632
- const intendedPublishedAt = requestedPublishedAt ?? currentPublishedAt ?? now;
1633
- if (stagedSlug !== null && stagedSlug !== existing.slug && existing.locale !== null) {
1634
- const conflict = await this.findBySlugIncludingTrashed(type, stagedSlug, existing.locale);
1635
- if (conflict && conflict.id !== id) throw new EmDashValidationError(`Cannot publish: slug '${stagedSlug}' is already used by another entry in this collection (id: ${conflict.id}). Choose a different slug.`, { code: "SLUG_CONFLICT" });
1636
- }
1637
- const assignments = [];
1638
- if (stagedSlug !== null) assignments.push(sql`slug = ${stagedSlug}`);
1639
- for (const [key, value] of Object.entries(revision.data)) {
1640
- if (SYSTEM_COLUMNS.has(key) || key.startsWith("_")) continue;
1641
- validateIdentifier(key, "content field name");
1642
- assignments.push(sql`${sql.ref(key)} = ${serializeValue(value)}`);
1643
- }
1644
- assignments.push(sql`live_revision_id = ${revisionToPublish}`, sql`draft_revision_id = NULL`, sql`status = 'published'`, sql`scheduled_at = NULL`, sql`published_at = ${intendedPublishedAt}`, sql`updated_at = ${now}`, sql`version = version + 1`);
1645
- const duePredicate = requireDue ? sql`AND scheduled_at IS NOT NULL AND scheduled_at <= ${now}` : sql``;
1646
- let promoted = false;
1647
- try {
1648
- promoted = ((await sql`
1649
- UPDATE ${sql.ref(tableName)}
1650
- SET ${sql.join(assignments, sql`, `)}
1651
- WHERE id = ${id}
1652
- AND deleted_at IS NULL
1653
- AND version = ${existing.version}
1654
- AND updated_at = ${existing.updatedAt}
1655
- AND status = ${existing.status}
1656
- AND ${nullableColumnMatch("live_revision_id", existing.liveRevisionId)}
1657
- AND ${nullableColumnMatch("draft_revision_id", existing.draftRevisionId)}
1658
- AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
1659
- ${duePredicate}
1660
- AND EXISTS (
1661
- SELECT 1 FROM revisions
1662
- WHERE revisions.id = ${revisionToPublish}
1663
- AND revisions.collection = ${type}
1664
- AND revisions.entry_id = ${id}
1665
- )
1666
- `.execute(this.db)).numAffectedRows ?? 0n) > 0n;
1667
- } catch (error) {
1668
- if (isConfirmedStatementFailure(error)) throw error;
1669
- let observed;
1670
- try {
1671
- observed = await this.findById(type, id);
1672
- } catch (reconciliationError) {
1673
- throw new Error("Unable to confirm whether content publication completed", { cause: reconciliationError });
1674
- }
1675
- if (!observed) throw new Error("Unable to confirm whether content publication completed", { cause: error });
1676
- promoted = matchesPublication(observed, existing, revision, revisionToPublish, intendedSlug, intendedPublishedAt, now);
1677
- if (!promoted && matchesPublicationFence(observed, existing)) throw error;
1678
- if (!promoted) throw new Error("Unable to confirm whether content publication completed", { cause: error });
1679
- }
1680
- if (!promoted) throw requireDue ? new ScheduledNotDueError() : new ContentMutationConflictError();
1681
- invalidateCollectionCache(type);
1682
- const updated = await this.findById(type, id);
1683
- if (!updated) throw new Error("Content not found");
1684
- return updated;
1685
- } catch (error) {
1686
- if (provisionalRevisionId) try {
1687
- await revisionRepo.deleteIfUnreferenced(type, id, provisionalRevisionId);
1688
- } catch (cleanupError) {
1689
- console.error(`[content] Failed to clean up provisional revision ${provisionalRevisionId}:`, cleanupError);
1690
- }
1691
- throw error;
1692
- }
1693
- }
1694
- /**
1695
- * Unpublish content
1696
- *
1697
- * Removes live pointer and cancels any pending schedule, but preserves the
1698
- * draft and publication date. If no draft exists, creates one from the live
1699
- * version so the content isn't lost.
1700
- */
1701
- async unpublish(type, id, expectedRevision) {
1702
- const tableName = getTableName(type);
1703
- const now = (/* @__PURE__ */ new Date()).toISOString();
1704
- const existing = await this.findById(type, id);
1705
- if (!existing) throw new EmDashValidationError("Content item not found");
1706
- assertRevisionPrecondition(existing, expectedRevision);
1707
- if (existing.status === "draft" && !existing.liveRevisionId && !existing.scheduledAt) return existing;
1708
- const revisionRepo = new RevisionRepository(this.db, this.datetimeContexts);
1709
- let provisionalRevisionId = null;
1710
- try {
1711
- let draftRevisionId = existing.draftRevisionId;
1712
- if (!draftRevisionId && existing.liveRevisionId) {
1713
- const liveRevision = await revisionRepo.findById(existing.liveRevisionId);
1714
- if (liveRevision) {
1715
- const draft = await revisionRepo.create({
1716
- collection: type,
1717
- entryId: id,
1718
- data: liveRevision.data
1719
- });
1720
- draftRevisionId = draft.id;
1721
- provisionalRevisionId = draft.id;
1722
- }
1723
- }
1724
- if (((await sql`
1725
- UPDATE ${sql.ref(tableName)}
1726
- SET live_revision_id = NULL,
1727
- draft_revision_id = ${draftRevisionId},
1728
- status = 'draft',
1729
- scheduled_at = NULL,
1730
- updated_at = ${now},
1731
- version = version + 1
1732
- WHERE id = ${id}
1733
- AND deleted_at IS NULL
1734
- AND version = ${existing.version}
1735
- AND updated_at = ${existing.updatedAt}
1736
- AND status = ${existing.status}
1737
- AND ${nullableColumnMatch("live_revision_id", existing.liveRevisionId)}
1738
- AND ${nullableColumnMatch("draft_revision_id", existing.draftRevisionId)}
1739
- AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
1740
- `.execute(this.db)).numAffectedRows ?? 0n) === 0n) throw new ContentMutationConflictError();
1741
- invalidateCollectionCache(type);
1742
- const updated = await this.findById(type, id);
1743
- if (!updated) throw new Error("Content not found");
1744
- return updated;
1745
- } catch (error) {
1746
- if (provisionalRevisionId) try {
1747
- await revisionRepo.deleteIfUnreferenced(type, id, provisionalRevisionId);
1748
- } catch (cleanupError) {
1749
- console.error(`[content] Failed to clean up provisional revision ${provisionalRevisionId}:`, cleanupError);
1750
- }
1751
- throw error;
1752
- }
1753
- }
1754
- /**
1755
- * Set the draft revision pointer for a content item.
1756
- *
1757
- * Used by seed/import paths that stage a new revision's data before
1758
- * promoting it to live via `publish()`.
1759
- *
1760
- * Validates that the content item exists and is not soft-deleted, that
1761
- * the revision exists, and that the revision belongs to the same
1762
- * collection and entry. Without these checks, a caller could leave the
1763
- * content row pointing at a missing or unrelated revision.
1764
- */
1765
- async setDraftRevision(type, id, revisionId) {
1766
- const existing = await this.findById(type, id);
1767
- if (!existing) throw new EmDashValidationError("Content item not found");
1768
- const revision = await new RevisionRepository(this.db, this.datetimeContexts).findById(revisionId);
1769
- if (!revision) throw new EmDashValidationError("Revision not found");
1770
- if (revision.collection !== type || revision.entryId !== id) throw new EmDashValidationError("Revision does not belong to the specified content item");
1771
- if (!await this.replaceDraftRevision(type, id, revisionId, existing)) throw new ContentMutationConflictError();
1772
- }
1773
- async replaceDraftRevision(type, id, revisionId, expected) {
1774
- const tableName = getTableName(type);
1775
- return ((await sql`
1776
- UPDATE ${sql.ref(tableName)}
1777
- SET draft_revision_id = ${revisionId},
1778
- version = version + 1
1779
- WHERE id = ${id}
1780
- AND deleted_at IS NULL
1781
- AND version = ${expected.version}
1782
- AND ${nullableColumnMatch("live_revision_id", expected.liveRevisionId)}
1783
- AND ${nullableColumnMatch("draft_revision_id", expected.draftRevisionId)}
1784
- AND EXISTS (
1785
- SELECT 1 FROM revisions
1786
- WHERE revisions.id = ${revisionId}
1787
- AND revisions.collection = ${type}
1788
- AND revisions.entry_id = ${id}
1789
- )
1790
- `.execute(this.db)).numAffectedRows ?? 0n) > 0n;
1791
- }
1792
- /**
1793
- * Discard pending draft changes
1794
- *
1795
- * Clears draft_revision_id. The content table columns already hold the
1796
- * published version, so no data sync is needed.
1797
- */
1798
- async discardDraft(type, id, expectedRevision) {
1799
- const tableName = getTableName(type);
1800
- const existing = await this.findById(type, id);
1801
- if (!existing) throw new EmDashValidationError("Content item not found");
1802
- assertRevisionPrecondition(existing, expectedRevision);
1803
- if (!existing.draftRevisionId) return existing;
1804
- if (((await sql`
1805
- UPDATE ${sql.ref(tableName)}
1806
- SET draft_revision_id = NULL,
1807
- version = version + 1
1808
- WHERE id = ${id}
1809
- AND deleted_at IS NULL
1810
- AND version = ${existing.version}
1811
- AND updated_at = ${existing.updatedAt}
1812
- AND status = ${existing.status}
1813
- AND ${nullableColumnMatch("live_revision_id", existing.liveRevisionId)}
1814
- AND ${nullableColumnMatch("draft_revision_id", existing.draftRevisionId)}
1815
- AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
1816
- `.execute(this.db)).numAffectedRows ?? 0n) === 0n) throw new ContentMutationConflictError();
1817
- const updated = await this.findById(type, id);
1818
- if (!updated) throw new Error("Content not found");
1819
- return updated;
1820
- }
1821
- /**
1822
- * Count content items with a pending schedule.
1823
- * Includes both draft-scheduled (status='scheduled') and published
1824
- * posts with scheduled draft changes (status='published', scheduled_at set).
1825
- */
1826
- async countScheduled(type) {
1827
- const tableName = getTableName(type);
1828
- const result = await sql`
1829
- SELECT COUNT(id) as count FROM ${sql.ref(tableName)}
1830
- WHERE scheduled_at IS NOT NULL
1831
- AND deleted_at IS NULL
1832
- `.execute(this.db);
1833
- return Number(result.rows[0]?.count || 0);
1834
- }
1835
- /**
1836
- * Map database row to ContentItem
1837
- * Extracts system columns and puts content fields in data
1838
- * Excludes null values from data to match input semantics
1839
- */
1840
- mapRow(type, row) {
1841
- const data = {};
1842
- for (const [key, value] of Object.entries(row)) if (!SYSTEM_COLUMNS.has(key) && value !== null) data[key] = deserializeValue(value);
1843
- return {
1844
- id: row.id,
1845
- type,
1846
- slug: row.slug,
1847
- status: row.status,
1848
- data,
1849
- authorId: row.author_id,
1850
- primaryBylineId: row.primary_byline_id ?? null,
1851
- createdAt: row.created_at,
1852
- updatedAt: row.updated_at,
1853
- publishedAt: row.published_at,
1854
- scheduledAt: row.scheduled_at,
1855
- liveRevisionId: row.live_revision_id ?? null,
1856
- draftRevisionId: row.draft_revision_id ?? null,
1857
- version: typeof row.version === "number" ? row.version : 1,
1858
- locale: row.locale ?? null,
1859
- translationGroup: row.translation_group ?? null
1860
- };
1861
- }
1862
- normalizeFilterScalar(field, type, value) {
1863
- if (type === "number" || type === "integer") {
1864
- if (typeof value !== "number" || !Number.isFinite(value)) throw new EmDashValidationError(`Filter for field "${field}" must use a finite number`);
1865
- if (type === "integer" && !Number.isInteger(value)) throw new EmDashValidationError(`Filter for field "${field}" must use an integer`);
1866
- return value;
1867
- }
1868
- if (type === "boolean") {
1869
- if (typeof value !== "boolean") throw new EmDashValidationError(`Filter for field "${field}" must use a boolean`);
1870
- return value ? 1 : 0;
1871
- }
1872
- if (typeof value !== "string") throw new EmDashValidationError(`Filter for field "${field}" must use a string`);
1873
- if (value.length > MAX_FILTER_STRING_LENGTH) throw new EmDashValidationError(`Filter value for field "${field}" exceeds ${MAX_FILTER_STRING_LENGTH} characters`);
1874
- if (type === "datetime") try {
1875
- return normalizeExplicitDatetime(value);
1876
- } catch {
1877
- throw new EmDashValidationError(`Filter for datetime field "${field}" must use an ISO 8601 datetime with Z or an explicit offset`);
1878
- }
1879
- return value;
1880
- }
1881
- normalizeFieldFilter(field, type, value) {
1882
- if (value === null) return {
1883
- column: field,
1884
- kind: "null"
1885
- };
1886
- if (typeof value !== "object") return {
1887
- column: field,
1888
- kind: "exact",
1889
- value: this.normalizeFilterScalar(field, type, value)
1890
- };
1891
- if (Array.isArray(value)) throw new EmDashValidationError(`Invalid filter for field "${field}"`);
1892
- const record = value;
1893
- const keys = Object.keys(record);
1894
- if (keys.length === 1 && keys[0] === "in") {
1895
- if (!Array.isArray(record.in) || record.in.length === 0) throw new EmDashValidationError(`IN filter for field "${field}" must not be empty`);
1896
- if (record.in.length > MAX_IN_FILTER_VALUES) throw new EmDashValidationError(`IN filter for field "${field}" exceeds ${MAX_IN_FILTER_VALUES} values`);
1897
- return {
1898
- column: field,
1899
- kind: "in",
1900
- values: record.in.map((entry) => this.normalizeFilterScalar(field, type, entry))
1901
- };
1902
- }
1903
- const rangeKeys = new Set([
1904
- "gt",
1905
- "gte",
1906
- "lt",
1907
- "lte"
1908
- ]);
1909
- if (keys.length === 0 || keys.some((key) => !rangeKeys.has(key))) throw new EmDashValidationError(`Invalid filter operator for field "${field}"`);
1910
- if (type === "boolean") throw new EmDashValidationError(`Boolean field "${field}" does not support range filters`);
1911
- const bounds = {};
1912
- for (const key of keys) {
1913
- if (record[key] === void 0) continue;
1914
- bounds[key] = this.normalizeFilterScalar(field, type, record[key]);
1915
- }
1916
- if (Object.keys(bounds).length === 0) throw new EmDashValidationError(`Range filter for field "${field}" has no bounds`);
1917
- return {
1918
- column: field,
1919
- kind: "range",
1920
- bounds
1921
- };
1922
- }
1923
- async collectionExists(type) {
1924
- return await this.db.selectFrom("_emdash_collections").where("slug", "=", type).select("id").executeTakeFirst() !== void 0;
1925
- }
1926
- async resolveFieldFilters(type, filters) {
1927
- const resolvedFilters = filters ?? {};
1928
- const fields = Object.keys(resolvedFilters);
1929
- if (fields.length === 0) return [];
1930
- if (fields.length > MAX_INDEXED_FIELD_FILTERS) {
1931
- if (!await this.collectionExists(type)) return [];
1932
- throw new EmDashValidationError(`Content list queries support at most ${MAX_INDEXED_FIELD_FILTERS} indexed field filters`);
1933
- }
1934
- const rows = await this.db.selectFrom("_emdash_fields as field").innerJoin("_emdash_collections as collection", "collection.id", "field.collection_id").where("collection.slug", "=", type).where("field.slug", "in", fields).where("field.indexed", "=", 1).select([
1935
- "field.slug",
1936
- "field.type",
1937
- "field.validation"
1938
- ]).execute();
1939
- const metadata = new Map(rows.filter((row) => !isStoragelessFieldRow(row)).map((row) => [row.slug, row.type]));
1940
- if (metadata.size === 0 && !await this.collectionExists(type)) return [];
1941
- for (const field of fields) try {
1942
- validateIdentifier(field, "content filter field");
1943
- } catch {
1944
- throw new EmDashValidationError(`Invalid content filter field: ${field}`);
1945
- }
1946
- const normalized = fields.map((field) => {
1947
- const fieldType = metadata.get(field);
1948
- if (!fieldType || !isIndexableFieldType(fieldType)) throw new EmDashValidationError(`Cannot filter by field "${field}". Custom fields must be indexed before filtering.`);
1949
- return this.normalizeFieldFilter(field, fieldType, resolvedFilters[field]);
1950
- });
1951
- if (normalized.reduce((total, filter) => {
1952
- if (filter.kind === "null") return total;
1953
- if (filter.kind === "exact") return total + 1;
1954
- if (filter.kind === "in") return total + filter.values.length;
1955
- return total + Object.keys(filter.bounds).length;
1956
- }, 0) > SQL_BATCH_SIZE) throw new EmDashValidationError(`Indexed field filters have a total operand budget of ${SQL_BATCH_SIZE}`);
1957
- return normalized;
1958
- }
1959
- applyFieldFilters(query, filters) {
1960
- let next = query;
1961
- for (const filter of filters) {
1962
- const column = sql.ref(filter.column);
1963
- const isPresent = sql`${column} IS NOT NULL`;
1964
- if (filter.kind === "null") {
1965
- next = next.where(() => sql`(${isPresent}) = FALSE AND ${column} IS NULL`);
1966
- continue;
1967
- }
1968
- if (filter.kind === "exact") {
1969
- next = next.where(() => sql`(${isPresent}) = TRUE AND ${column} = ${filter.value}`);
1970
- continue;
1971
- }
1972
- if (filter.kind === "in") {
1973
- const values = sql.join(filter.values.map((value) => sql`${value}`), sql`, `);
1974
- next = next.where(() => sql`(${isPresent}) = TRUE AND ${column} IN (${values})`);
1975
- continue;
1976
- }
1977
- next = next.where(() => sql`(${isPresent}) = TRUE`);
1978
- if (filter.bounds.gt !== void 0) next = next.where(() => sql`${column} > ${filter.bounds.gt}`);
1979
- if (filter.bounds.gte !== void 0) next = next.where(() => sql`${column} >= ${filter.bounds.gte}`);
1980
- if (filter.bounds.lt !== void 0) next = next.where(() => sql`${column} < ${filter.bounds.lt}`);
1981
- if (filter.bounds.lte !== void 0) next = next.where(() => sql`${column} <= ${filter.bounds.lte}`);
1982
- }
1983
- return next;
1984
- }
1985
- /**
1986
- * Map order field names to database columns.
1987
- * Only allows known fields to prevent column enumeration via crafted orderBy values.
1988
- */
1989
- mapOrderField(field, sortableExtras = []) {
1990
- const mapped = ORDER_FIELD_COLUMNS[field];
1991
- if (mapped) return mapped;
1992
- if (sortableExtras.includes(field)) {
1993
- validateIdentifier(field, "order field");
1994
- return field;
1995
- }
1996
- throw new EmDashValidationError(`Invalid order field: ${field}`);
1997
- }
1998
- async resolveOrderField(type, field, sortableExtras = []) {
1999
- try {
2000
- return {
2001
- column: this.mapOrderField(field, sortableExtras),
2002
- indexedCustomField: false
2003
- };
2004
- } catch (error) {
2005
- if (!(error instanceof EmDashValidationError)) throw error;
2006
- }
2007
- const customField = await this.db.selectFrom("_emdash_collections as collection").leftJoin("_emdash_fields as field", (join) => join.onRef("field.collection_id", "=", "collection.id").on("field.slug", "=", field).on("field.indexed", "=", 1)).where("collection.slug", "=", type).select(["collection.id as collectionId", "field.slug as fieldSlug"]).executeTakeFirst();
2008
- if (!customField) throw new ContentCollectionNotFoundError(type);
2009
- if (!customField.fieldSlug) throw new EmDashValidationError(`Invalid order field: ${field}. Custom fields must be indexed before sorting.`);
2010
- validateIdentifier(customField.fieldSlug, "content order field");
2011
- return {
2012
- column: customField.fieldSlug,
2013
- indexedCustomField: true
2014
- };
2015
- }
2016
- };
2017
-
2018
- //#endregion
2019
- export { staleStoredKeys as a, keepKnownFields as i, content_exports as n, isSystemOrderField as r, ContentRepository as t };
2020
- //# sourceMappingURL=content-RzEXY3QX.mjs.map