dineway 0.1.49 → 0.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 (418) hide show
  1. package/dist/{activity-events-CwM7yxPW.mjs → activity-events-y6eT8UW6.mjs} +6 -38
  2. package/dist/actor-CrObAPV0.mjs +37 -0
  3. package/dist/after-BfniwgIf.mjs +9 -0
  4. package/dist/{allowed-origins-CyGnbgBd.mjs → allowed-origins-_oeyP6x-.mjs} +1 -1
  5. package/dist/api/route-utils.d.mts +10 -10
  6. package/dist/api/route-utils.mjs +16 -13
  7. package/dist/api/schemas/index.d.mts +2 -2
  8. package/dist/api/schemas/index.mjs +5 -4
  9. package/dist/{api-BMIxRIEN.mjs → api-ByrU2q-Z.mjs} +60 -36
  10. package/dist/{api-tokens-DtvdtIkm.mjs → api-tokens-CjVMQT-X.mjs} +6 -2
  11. package/dist/{apply-D_RlTYtC.mjs → apply-bZswmZPm.mjs} +117 -77
  12. package/dist/astro/image-endpoint.d.mts +7 -0
  13. package/dist/astro/image-endpoint.mjs +54 -0
  14. package/dist/astro/index.d.mts +35 -11
  15. package/dist/astro/index.mjs +157 -23
  16. package/dist/astro/integration/shims/use-sync-external-store-with-selector.js +71 -0
  17. package/dist/astro/integration/shims/use-sync-external-store.js +1 -0
  18. package/dist/astro/middleware/auth.d.mts +10 -10
  19. package/dist/astro/middleware/auth.mjs +42 -24
  20. package/dist/astro/middleware/redirect.mjs +13 -4
  21. package/dist/astro/middleware/request-context.mjs +8 -5
  22. package/dist/astro/middleware/seed.d.mts +1 -1
  23. package/dist/astro/middleware/seed.mjs +2 -2
  24. package/dist/astro/middleware/setup.mjs +1 -1
  25. package/dist/astro/middleware.mjs +333 -192
  26. package/dist/astro/routes/admin.astro +6 -1
  27. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -6
  28. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -6
  29. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
  30. package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
  31. package/dist/astro/routes/api/admin/briefing.mjs +21 -17
  32. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  33. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -8
  34. package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -8
  35. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -8
  36. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +17 -14
  37. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +17 -14
  38. package/dist/astro/routes/api/admin/bylines/index.mjs +17 -14
  39. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +14 -12
  40. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -5
  41. package/dist/astro/routes/api/admin/comments/bulk.mjs +11 -9
  42. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -5
  43. package/dist/astro/routes/api/admin/comments/index.mjs +11 -9
  44. package/dist/astro/routes/api/admin/context/_id_/history.mjs +26 -24
  45. package/dist/astro/routes/api/admin/context/_id_/index.mjs +26 -24
  46. package/dist/astro/routes/api/admin/context/_id_/review.mjs +31 -28
  47. package/dist/astro/routes/api/admin/context/_id_/supersede.mjs +31 -28
  48. package/dist/astro/routes/api/admin/context/diff.mjs +30 -27
  49. package/dist/astro/routes/api/admin/context/index.mjs +31 -28
  50. package/dist/astro/routes/api/admin/context/stale.mjs +30 -27
  51. package/dist/astro/routes/api/admin/hitl-requests/_id_/index.mjs +26 -24
  52. package/dist/astro/routes/api/admin/hitl-requests/_id_/resolve.mjs +30 -27
  53. package/dist/astro/routes/api/admin/hitl-requests/index.mjs +30 -27
  54. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +27 -25
  55. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  56. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +6 -6
  57. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +6 -6
  58. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +55 -51
  59. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +55 -51
  60. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +47 -43
  61. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +53 -49
  62. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +52 -48
  63. package/dist/astro/routes/api/admin/plugins/index.mjs +47 -43
  64. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
  65. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +46 -42
  66. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +52 -48
  67. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +46 -42
  68. package/dist/astro/routes/api/admin/plugins/updates.mjs +46 -42
  69. package/dist/astro/routes/api/admin/review-requests/_id_/index.mjs +5 -5
  70. package/dist/astro/routes/api/admin/review-requests/_id_/resolve.mjs +31 -27
  71. package/dist/astro/routes/api/admin/review-requests/index.mjs +10 -9
  72. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +46 -42
  73. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
  74. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +46 -42
  75. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
  76. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  77. package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -7
  78. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +4 -4
  79. package/dist/astro/routes/api/admin/users/index.mjs +7 -6
  80. package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
  81. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  82. package/dist/astro/routes/api/auth/invite/complete.mjs +12 -11
  83. package/dist/astro/routes/api/auth/invite/index.mjs +9 -8
  84. package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -10
  85. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  86. package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -10
  87. package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
  88. package/dist/astro/routes/api/auth/me.mjs +9 -8
  89. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  90. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
  91. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  92. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -5
  93. package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
  94. package/dist/astro/routes/api/auth/passkey/options.mjs +12 -11
  95. package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -9
  96. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -10
  97. package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -10
  98. package/dist/astro/routes/api/auth/setup-token/index.mjs +5 -5
  99. package/dist/astro/routes/api/auth/setup-token/verify.mjs +2 -2
  100. package/dist/astro/routes/api/auth/signup/complete.mjs +12 -11
  101. package/dist/astro/routes/api/auth/signup/request.mjs +11 -10
  102. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  103. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +15 -13
  104. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts +8 -0
  105. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +160 -0
  106. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
  107. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +5 -5
  108. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +5 -5
  109. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +5 -5
  110. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +11 -10
  111. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +10 -9
  112. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +5 -5
  113. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
  114. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +10 -9
  115. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +16 -14
  116. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
  117. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +5 -5
  118. package/dist/astro/routes/api/content/_collection_/_id_.mjs +10 -9
  119. package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
  120. package/dist/astro/routes/api/content/_collection_/index.mjs +10 -9
  121. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -6
  122. package/dist/astro/routes/api/dashboard.mjs +9 -8
  123. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  124. package/dist/astro/routes/api/health.mjs +1 -1
  125. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  126. package/dist/astro/routes/api/import/probe.mjs +22 -10
  127. package/dist/astro/routes/api/import/wordpress/analyze.mjs +9 -6
  128. package/dist/astro/routes/api/import/wordpress/execute.d.mts +19 -1
  129. package/dist/astro/routes/api/import/wordpress/execute.mjs +84 -37
  130. package/dist/astro/routes/api/import/wordpress/media.d.mts +13 -1
  131. package/dist/astro/routes/api/import/wordpress/media.mjs +19 -13
  132. package/dist/astro/routes/api/import/wordpress/prepare.mjs +35 -32
  133. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +18 -1
  134. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +120 -100
  135. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  136. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +24 -12
  137. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +56 -2
  138. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +283 -55
  139. package/dist/astro/routes/api/manifest.mjs +14 -5
  140. package/dist/astro/routes/api/mcp.mjs +265 -69
  141. package/dist/astro/routes/api/media/_id_/confirm.mjs +38 -10
  142. package/dist/astro/routes/api/media/_id_.mjs +11 -7
  143. package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
  144. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
  145. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
  146. package/dist/astro/routes/api/media/providers/index.mjs +2 -2
  147. package/dist/astro/routes/api/media/upload-url.mjs +11 -10
  148. package/dist/astro/routes/api/media.mjs +27 -34
  149. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +31 -28
  150. package/dist/astro/routes/api/menus/_name_/items.mjs +31 -28
  151. package/dist/astro/routes/api/menus/_name_/reorder.mjs +31 -28
  152. package/dist/astro/routes/api/menus/_name_/translations.mjs +31 -28
  153. package/dist/astro/routes/api/menus/_name_.mjs +31 -28
  154. package/dist/astro/routes/api/menus/index.mjs +31 -28
  155. package/dist/astro/routes/api/oauth/authorize.mjs +7 -7
  156. package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
  157. package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
  158. package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
  159. package/dist/astro/routes/api/oauth/register.mjs +4 -4
  160. package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
  161. package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
  162. package/dist/astro/routes/api/oauth/token.mjs +6 -6
  163. package/dist/astro/routes/api/openapi.json.mjs +4 -3
  164. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +30 -30
  165. package/dist/astro/routes/api/redirects/404s/index.mjs +11 -10
  166. package/dist/astro/routes/api/redirects/404s/summary.mjs +11 -10
  167. package/dist/astro/routes/api/redirects/_id_.mjs +32 -29
  168. package/dist/astro/routes/api/redirects/index.mjs +32 -29
  169. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
  170. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +2 -2
  171. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +47 -43
  172. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +47 -43
  173. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +47 -43
  174. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +47 -43
  175. package/dist/astro/routes/api/schema/collections/index.mjs +47 -43
  176. package/dist/astro/routes/api/schema/index.mjs +13 -14
  177. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +46 -42
  178. package/dist/astro/routes/api/schema/orphans/index.mjs +46 -42
  179. package/dist/astro/routes/api/search/enable.mjs +11 -10
  180. package/dist/astro/routes/api/search/index.mjs +10 -9
  181. package/dist/astro/routes/api/search/rebuild.mjs +11 -10
  182. package/dist/astro/routes/api/search/stats.mjs +6 -6
  183. package/dist/astro/routes/api/search/suggest.mjs +10 -9
  184. package/dist/astro/routes/api/sections/_slug_.mjs +31 -28
  185. package/dist/astro/routes/api/sections/index.mjs +31 -28
  186. package/dist/astro/routes/api/settings/email.mjs +4 -4
  187. package/dist/astro/routes/api/settings.mjs +31 -28
  188. package/dist/astro/routes/api/setup/admin-verify.mjs +14 -13
  189. package/dist/astro/routes/api/setup/admin.mjs +12 -11
  190. package/dist/astro/routes/api/setup/dev-bypass.mjs +31 -25
  191. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  192. package/dist/astro/routes/api/setup/index.mjs +29 -25
  193. package/dist/astro/routes/api/setup/status.mjs +3 -3
  194. package/dist/astro/routes/api/snapshot.mjs +8 -7
  195. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +34 -31
  196. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +34 -31
  197. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +34 -31
  198. package/dist/astro/routes/api/taxonomies/index.mjs +34 -31
  199. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  200. package/dist/astro/routes/api/typegen.mjs +4 -4
  201. package/dist/astro/routes/api/well-known/auth.mjs +3 -3
  202. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +3 -3
  203. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +3 -3
  204. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +30 -27
  205. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +31 -28
  206. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +31 -28
  207. package/dist/astro/routes/api/widget-areas/_name_.mjs +28 -26
  208. package/dist/astro/routes/api/widget-areas/index.mjs +31 -28
  209. package/dist/astro/routes/api/widget-components.mjs +2 -2
  210. package/dist/astro/routes/robots.txt.mjs +8 -7
  211. package/dist/astro/routes/sitemap-_collection_.xml.mjs +23 -14
  212. package/dist/astro/routes/sitemap.xml.mjs +9 -8
  213. package/dist/astro/session-driver.d.mts +1 -1
  214. package/dist/astro/session-driver.mjs +2 -2
  215. package/dist/astro/types.d.mts +21 -13
  216. package/dist/{audit-BeNsSyfU.mjs → audit-ByVKIwWF.mjs} +1 -1
  217. package/dist/auth/providers/github.d.mts +1 -1
  218. package/dist/auth/providers/google.d.mts +1 -1
  219. package/dist/{auth-control-guard-Dr14I98m.mjs → auth-control-guard-CqeUmQsv.mjs} +1 -1
  220. package/dist/{authorize-BLO0m7fW.mjs → authorize-CcBxcHtz.mjs} +1 -1
  221. package/dist/{briefing-DJX6irto.mjs → briefing-BJaJI8dm.mjs} +2 -1
  222. package/dist/{briefing-CkTee1ex.mjs → briefing-CawZ59CY.mjs} +90 -28
  223. package/dist/bundle-7C_DpkEq.mjs +602 -0
  224. package/dist/{byline-BVC1lV6G.mjs → byline-e51-fWRs.mjs} +30 -62
  225. package/dist/{byline-fields-uJlygdzM.d.mts → byline-fields-CUvbslGc.d.mts} +80 -37
  226. package/dist/{byline-fields-Cj39WhXC.mjs → byline-fields-D-N5qVIG.mjs} +1 -1
  227. package/dist/{byline-fields-cNrLJVRs.mjs → byline-fields-DOJO16Gm.mjs} +3 -3
  228. package/dist/{byline-registry-4v6tXMNX.mjs → byline-registry-87nNnYl4.mjs} +2 -2
  229. package/dist/{bylines-LN9XISRo.mjs → bylines-CMQNCNFh.mjs} +9 -3
  230. package/dist/{bylines-DwokOFj6.mjs → bylines-y4LO-dTj.mjs} +5 -5
  231. package/dist/{cache-Bac05gBd.mjs → cache-C-FNTFYK.mjs} +2 -2
  232. package/dist/{chunks-CuUt7K50.mjs → chunks-CxBt-GxR.mjs} +1 -1
  233. package/dist/cli/index.mjs +94 -659
  234. package/dist/client/external-auth-headers.d.mts +1 -1
  235. package/dist/client/index.d.mts +1 -1
  236. package/dist/client/index.mjs +2 -1
  237. package/dist/{comment-Beue8-15.mjs → comment-DlA4iChg.mjs} +13 -3
  238. package/dist/comment-reaction-DfFao1ST.mjs +58 -0
  239. package/dist/{comments-DcjFIfqX.mjs → comments-DApFGMsP.mjs} +1 -1
  240. package/dist/{content-CBxYGWfL.mjs → content-Bh4BDjv8.mjs} +51 -34
  241. package/dist/content-refresh-CgutSUfO.mjs +1576 -0
  242. package/dist/{context-CQVNZaR8.mjs → context-C5W3G18y.mjs} +111 -50
  243. package/dist/{context-Df1xVYyr.mjs → context-CK4ym2Sf.mjs} +2 -2
  244. package/dist/{context-route-helpers-B9wj8Rmh.mjs → context-route-helpers-MASyGgWf.mjs} +2 -1
  245. package/dist/{cron-DxCAU_Sn.mjs → cron-cKcNTix_.mjs} +7 -3
  246. package/dist/{dashboard-B7ZUNuyF.mjs → dashboard-DQkVT7Ei.mjs} +4 -4
  247. package/dist/db/index.d.mts +3 -3
  248. package/dist/db/index.mjs +1 -1
  249. package/dist/db/libsql.d.mts +1 -1
  250. package/dist/db/postgres.d.mts +1 -1
  251. package/dist/db/sqlite.d.mts +1 -1
  252. package/dist/{device-flow-BZVGftqi.mjs → device-flow-BnqRH-T9.mjs} +5 -5
  253. package/dist/{placeholder-d54Qr_7n.mjs → enrich-DIFIFA8O.mjs} +36 -9
  254. package/dist/{error-DCSuUAZW.mjs → error-CytFlgcd.mjs} +5 -0
  255. package/dist/field-defs-cache-DRNdhBJv.mjs +49 -0
  256. package/dist/{fts-manager-B5l_qwrA.mjs → fts-manager-Crzypjqu.mjs} +1 -1
  257. package/dist/hitl-Hw1RmWOq.mjs +677 -0
  258. package/dist/{hitl-requests-7NlzVeUA.mjs → hitl-requests-B4Vbcmaq.mjs} +2 -2
  259. package/dist/{hitl-route-helpers-k---_H3-.mjs → hitl-route-helpers-hEVzvP8l.mjs} +5 -4
  260. package/dist/import-Bh9biJfx.mjs +1354 -0
  261. package/dist/{index-CZiWCQDp.d.mts → index-D4y6NGiy.d.mts} +75 -3
  262. package/dist/index.d.mts +17 -16
  263. package/dist/index.mjs +80 -68
  264. package/dist/{jsonld-DZ67souc.d.mts → jsonld-Dq0gsnOH.d.mts} +8 -4
  265. package/dist/{load-D9OiGr2m.mjs → load-DmYeniXl.mjs} +1 -1
  266. package/dist/{loader--GOYux8M.mjs → loader-CjWqjweO.mjs} +117 -31
  267. package/dist/{manifest-schema-qh2P7RDX.mjs → manifest-schema-BxVPYMJR.mjs} +34 -1
  268. package/dist/mcp-reviewed-executor-TwVbMzG6.mjs +555 -0
  269. package/dist/media/image-endpoint.d.mts +32 -0
  270. package/dist/media/image-endpoint.mjs +111 -0
  271. package/dist/media/index.d.mts +27 -2
  272. package/dist/media/index.mjs +5 -3
  273. package/dist/media/local-runtime.d.mts +16 -11
  274. package/dist/media/local-runtime.mjs +9 -7
  275. package/dist/{media-CbKyYukG.mjs → media-DMj0grzC.mjs} +3 -1
  276. package/dist/{media-allowlist-BYxJIcTO.mjs → media-allowlist-BlVCSzHx.mjs} +1 -1
  277. package/dist/media-url-lEKSrpr9.mjs +20 -0
  278. package/dist/{menus-BZbXf-ej.mjs → menus-TaW1gKnk.mjs} +19 -5
  279. package/dist/{menus-C7F0sCrC.mjs → menus-xvsnVZXw.mjs} +80 -15
  280. package/dist/{normalize-ByemhhfW.mjs → normalize-5ek_z7de.mjs} +8 -1
  281. package/dist/{oauth-authorization-DF-LCsIA.mjs → oauth-authorization-DiaGvRVz.mjs} +5 -5
  282. package/dist/{oauth-clients-Du-V0fEm.mjs → oauth-clients-Dl9E8dEM.mjs} +2 -2
  283. package/dist/object-cache/memory.d.mts +14 -0
  284. package/dist/object-cache/memory.mjs +55 -0
  285. package/dist/object-cache-D51qOseS.mjs +419 -0
  286. package/dist/page/index.d.mts +4 -3
  287. package/dist/page/index.mjs +2 -2
  288. package/dist/{parse-CneSgl7-.mjs → parse-CbymENaR.mjs} +1 -1
  289. package/dist/{parser-DxfsARXL.mjs → parser-C1nYd-81.mjs} +151 -9
  290. package/dist/{placeholder-CXfoCtCd.d.mts → placeholder-uvNRZBX9.d.mts} +12 -4
  291. package/dist/plugin-utils.d.mts +9 -9
  292. package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
  293. package/dist/plugins/adapt-sandbox-entry.mjs +11 -2
  294. package/dist/{plugins-DzfqRREq.mjs → plugins-BolVa-tl.mjs} +831 -63
  295. package/dist/{transport-LSdZVXHF.mjs → portable-text-dA_UlJwW.mjs} +1 -134
  296. package/dist/{preview-C-3YTX2U.mjs → preview-vSVg6fwG.mjs} +4 -3
  297. package/dist/process-cache-7xgAO9JY.mjs +106 -0
  298. package/dist/{provider-loader-9it4vd6L.d.mts → provider-loader-MD8zAbnH.d.mts} +1 -1
  299. package/dist/{public-url-B4xV4rGa.d.mts → public-url-gsp3oUbu.d.mts} +1 -1
  300. package/dist/{query-Ds0EnMFX.mjs → query-CfRCdaL3.mjs} +281 -76
  301. package/dist/{query-CZ3TH_Uu.mjs → query-jaAmw8E1.mjs} +11 -6
  302. package/dist/{rate-limit-i0G3IAOX.mjs → rate-limit-ZLdyWoZF.mjs} +2 -2
  303. package/dist/{redirect-CEACczxo.mjs → redirect-BAjt4Z6b.mjs} +2 -2
  304. package/dist/{redirects-DYj1yPxs.mjs → redirects-BTdwpQtE.mjs} +47 -24
  305. package/dist/{redirects-ocRJmQHP.mjs → redirects-CA-xylPG.mjs} +19 -11
  306. package/dist/{registry-D7HACema.mjs → registry-CtyVedcN.mjs} +130 -77
  307. package/dist/request-context.d.mts +2 -0
  308. package/dist/{request-meta-DAb6xaP9.mjs → request-meta-CUypToDJ.mjs} +1 -1
  309. package/dist/{review-requests-Qum4713s.mjs → review-requests-C9Ff11Vq.mjs} +1 -1
  310. package/dist/{review-requests-BW2NxY-l.mjs → review-requests-DUyEmtmG.mjs} +1 -1
  311. package/dist/{runner-09sTqtv0.d.mts → runner-1tC2tIRV.d.mts} +1 -1
  312. package/dist/runner-DB7CRfOF.mjs +6740 -0
  313. package/dist/{runtime-C4-2lSFg.d.mts → runtime-BOeiQsCv.d.mts} +1952 -1489
  314. package/dist/runtime.d.mts +10 -10
  315. package/dist/runtime.mjs +3 -3
  316. package/dist/schema-DXdYj4R3.mjs +8 -0
  317. package/dist/{search-Gdrqj582.mjs → search-DSaLgEFI.mjs} +5 -3
  318. package/dist/{secrets-CeGpBFV2.mjs → secrets-CDZo2k8L.mjs} +14 -11
  319. package/dist/{sections-BOjU13Y-.mjs → sections-PVVXHGfp.mjs} +2 -2
  320. package/dist/seed/index.d.mts +2 -2
  321. package/dist/seed/index.mjs +21 -18
  322. package/dist/seo/index.d.mts +1 -1
  323. package/dist/seo/index.mjs +3 -12
  324. package/dist/{seo-CLj8lS7Q.mjs → seo-CQG3GCHQ.mjs} +5 -2
  325. package/dist/{seo-contributions-CqVvReEj.mjs → seo-contributions-C1EnikHB.mjs} +11 -3
  326. package/dist/{seo-5lJOtB5J.mjs → seo-qa7Dn0jK.mjs} +22 -4
  327. package/dist/{service-jGlhrICs.mjs → service-DiN4ZuPp.mjs} +2 -2
  328. package/dist/session-user-CAKImna3.mjs +22 -0
  329. package/dist/{settings-CUXOxzsT.mjs → settings-BbqgHWpU.mjs} +28 -55
  330. package/dist/{settings-DlBsT2VS.mjs → settings-i4VPP8bT.mjs} +2 -2
  331. package/dist/{setup-complete-BRrdPYjZ.mjs → setup-complete-B8fl-mjV.mjs} +1 -1
  332. package/dist/{site-activity-DONFrMBJ.mjs → site-activity-Dy3FROs0.mjs} +1 -1
  333. package/dist/{site-context-C8Az_P_I.mjs → site-context-DQiL8Fl5.mjs} +23 -652
  334. package/dist/{site-url-DIMD68go.mjs → site-url-BML3GQYm.mjs} +1 -1
  335. package/dist/status-DANd8ERo.mjs +21 -0
  336. package/dist/storage/local.d.mts +1 -1
  337. package/dist/storage/local.mjs +1 -1
  338. package/dist/storage/s3.d.mts +1 -1
  339. package/dist/storage/s3.mjs +1 -1
  340. package/dist/{taxonomies-D9Sw4wWM.mjs → taxonomies-9s-v03H-.mjs} +36 -29
  341. package/dist/taxonomies-DRiixp_E.mjs +443 -0
  342. package/dist/{taxonomy-B169Nm6B.mjs → taxonomy-Dvsw6BOs.mjs} +44 -9
  343. package/dist/transport-BNmCqLu7.mjs +134 -0
  344. package/dist/types-4garmdWa.mjs +66 -0
  345. package/dist/{types-DSPGWjxO.d.mts → types-C8XW6L19.d.mts} +225 -4
  346. package/dist/{types-GaCtZ1k9.d.mts → types-CDpH6MA4.d.mts} +18 -0
  347. package/dist/{types-DRIKLmJI.d.mts → types-C_ly5eTp.d.mts} +17 -2
  348. package/dist/types-DF7ZuGGk.d.mts +118 -0
  349. package/dist/types-DU0bU2Wu.d.mts +2392 -0
  350. package/dist/{types-CwawBUTa.d.mts → types-DW_-sFyz.d.mts} +2 -1
  351. package/dist/{types-CSgR9nQB.mjs → types-Dq1qT19x.mjs} +1 -1
  352. package/dist/ui/server-runtime.d.mts +15 -15
  353. package/dist/ui/server-runtime.mjs +24 -20
  354. package/dist/url-Cac7a9QZ.mjs +33 -0
  355. package/dist/{user-DTLoZs16.mjs → user-BIZPzCb9.mjs} +2 -2
  356. package/dist/{utils-B8hBJ9Oz.mjs → utils-CMDhr5Xd.mjs} +129 -7
  357. package/dist/{validate-DzjCdy6v.d.mts → validate-CZvb-BKB.d.mts} +10 -7
  358. package/dist/{validate-DWmPO6oj.mjs → validate-DNs0pTSc.mjs} +11 -6
  359. package/dist/version-BTvfRGc6.mjs +6 -0
  360. package/dist/{widgets-BpJ8EpPd.mjs → widgets-f0ibZUIq.mjs} +46 -43
  361. package/dist/{wordpress-slugs-DHNR9LpG.mjs → wordpress-slugs-BhHJX8XE.mjs} +1 -1
  362. package/dist/{zod-generator-K91-g665.mjs → zod-generator-B7TMjyFJ.mjs} +41 -13
  363. package/package.json +30 -8
  364. package/src/components/Block.astro +50 -0
  365. package/src/components/Comments.astro +143 -1
  366. package/src/components/DinewayHead.astro +19 -2
  367. package/src/components/DinewayImage.astro +66 -33
  368. package/src/components/DinewayMedia.astro +1 -2
  369. package/src/components/Image.astro +85 -40
  370. package/src/components/InlinePortableTextEditor.tsx +12 -2
  371. package/src/components/JsonLdScript.astro +9 -0
  372. package/src/components/LiveSearch.astro +40 -4
  373. package/src/components/index.ts +4 -0
  374. package/src/components/live-search-routing.ts +30 -0
  375. package/src/components/portable-text-text-align.ts +15 -0
  376. package/src/ui.ts +2 -0
  377. package/dist/entity-aliases-4y93Yjuc.mjs +0 -51
  378. package/dist/import-4YBXbDkI.mjs +0 -684
  379. package/dist/runner-DzIpHGND.mjs +0 -2836
  380. package/dist/schema-Bsi-VpYv.mjs +0 -8
  381. package/dist/taxonomies-CZ-_DitR.mjs +0 -327
  382. package/dist/types-CuJOqHfM.d.mts +0 -758
  383. package/dist/version-C4gfn0PJ.mjs +0 -6
  384. /package/dist/{adapters-OQCImOL7.d.mts → adapters-NVSs4UH7.d.mts} +0 -0
  385. /package/dist/{api-tokens-CYUoFGo7.mjs → api-tokens-2r7OWi_I.mjs} +0 -0
  386. /package/dist/{challenge-store-DFxX0_15.mjs → challenge-store-Bjlepa2E.mjs} +0 -0
  387. /package/dist/{components--pg6zBvu.mjs → components-Bclf98Be.mjs} +0 -0
  388. /package/dist/{context-types-r8oFvyOq.mjs → context-types-DqTo6C79.mjs} +0 -0
  389. /package/dist/{default-B6iaLvP7.mjs → default--K40jqm0.mjs} +0 -0
  390. /package/dist/{email-console-BOVqIYpB.mjs → email-console-ChgKFO8-.mjs} +0 -0
  391. /package/dist/{escape-BDgouFFY.mjs → escape-Bd4P_xKg.mjs} +0 -0
  392. /package/dist/{experimental-workflows-CSMffP6q.mjs → experimental-workflows-BMsUnIAw.mjs} +0 -0
  393. /package/dist/{mime-CbKsKDCB.mjs → mime-CA7Lgovc.mjs} +0 -0
  394. /package/dist/{mode-CZqCF9dZ.mjs → mode-C4CIyjtR.mjs} +0 -0
  395. /package/dist/{oauth-state-store-qncPB_IU.mjs → oauth-state-store-CNoXafF7.mjs} +0 -0
  396. /package/dist/{oauth-user-lookup-BFjWF8wK.mjs → oauth-user-lookup-Ygw4bNyt.mjs} +0 -0
  397. /package/dist/{options-LsPjZlqD.mjs → options-DQPTH5n0.mjs} +0 -0
  398. /package/dist/{passkey-config-DzHQK90v.mjs → passkey-config-BUwu8BA3.mjs} +0 -0
  399. /package/dist/{patterns-DKAX6oqN.mjs → patterns-BhAvqO0S.mjs} +0 -0
  400. /package/dist/{provider-loader-DzUxejk7.mjs → provider-loader-D0w9dqjm.mjs} +0 -0
  401. /package/dist/{public-url-B_m9IwdV.mjs → public-url-BEz_sM9n.mjs} +0 -0
  402. /package/dist/{redirect-BjIBtDWY.mjs → redirect-wphRBv4y.mjs} +0 -0
  403. /package/dist/{request-cache-ho99schz.mjs → request-cache-D9GQBxMP.mjs} +0 -0
  404. /package/dist/{resolve-DTuDyYKs.mjs → resolve-Cb6Dbz-C.mjs} +0 -0
  405. /package/dist/{setup-nonce-pyGfc9hV.mjs → setup-nonce-CoyjILvW.mjs} +0 -0
  406. /package/dist/{sidecar-client-mJxwayYN.mjs → sidecar-client-C3mqXg4C.mjs} +0 -0
  407. /package/dist/{slugify-BqebcqBg.mjs → slugify-KSsVD_D3.mjs} +0 -0
  408. /package/dist/{ssrf-DiKqWd3N.mjs → ssrf-DWe8jwAp.mjs} +0 -0
  409. /package/dist/{tokens-SLmgowfd.mjs → tokens-1RK1n6ED.mjs} +0 -0
  410. /package/dist/{transaction-CFLhEOuH.mjs → transaction-C2EOMTJA.mjs} +0 -0
  411. /package/dist/{transport-BnsNxh2l.d.mts → transport-CcJu3Co8.d.mts} +0 -0
  412. /package/dist/{trusted-proxy-BgIIcbsN.mjs → trusted-proxy-DTXQ2IMv.mjs} +0 -0
  413. /package/dist/{types-CJ8DkL0q.mjs → types-B3t5cxhU.mjs} +0 -0
  414. /package/dist/{types-nP9vGUFq.d.mts → types-C46EvNdc.d.mts} +0 -0
  415. /package/dist/{types-Cg276-3z.mjs → types-C5CVgwsV.mjs} +0 -0
  416. /package/dist/{types-DZpT-PnG.d.mts → types-POH8PQZh.d.mts} +0 -0
  417. /package/dist/{types-Dryvv7LH.d.mts → types-Rc3gQqqw.d.mts} +0 -0
  418. /package/dist/{url-w7Mp5qAD.mjs → url-CpyFDFpO.mjs} +0 -0
@@ -1,2836 +0,0 @@
1
- import { t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
2
- import { a as currentTimestampValue, i as currentTimestamp, l as listTablesLike, n as columnExists, s as isSqlite, t as binaryType } from "./dialect-helpers-fk43GbDb.mjs";
3
- import { n as getI18nConfig } from "./config-CVssduLe.mjs";
4
- import { n as contentLocaleUpdatedIndexName, t as contentLocaleCreatedIndexName } from "./content-index-names-Bbu1FY1r.mjs";
5
- import { createRequire } from "node:module";
6
- import { createHash } from "node:crypto";
7
- import { Migrator, sql } from "kysely";
8
-
9
- //#region \0rolldown/runtime.js
10
- var __defProp = Object.defineProperty;
11
- var __exportAll = (all, no_symbols) => {
12
- let target = {};
13
- for (var name in all) {
14
- __defProp(target, name, {
15
- get: all[name],
16
- enumerable: true
17
- });
18
- }
19
- if (!no_symbols) {
20
- __defProp(target, Symbol.toStringTag, { value: "Module" });
21
- }
22
- return target;
23
- };
24
-
25
- //#endregion
26
- //#region src/database/migrations/001_initial.ts
27
- var _001_initial_exports = /* @__PURE__ */ __exportAll({
28
- down: () => down$49,
29
- up: () => up$49
30
- });
31
- /**
32
- * Initial schema migration
33
- *
34
- * Note: Content tables (ec_posts, ec_pages, etc.) are created dynamically
35
- * by the SchemaRegistry when collections are added via the admin UI.
36
- * This migration only creates system tables.
37
- */
38
- async function up$49(db) {
39
- await db.schema.createTable("revisions").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("collection", "text", (col) => col.notNull()).addColumn("entry_id", "text", (col) => col.notNull()).addColumn("data", "text", (col) => col.notNull()).addColumn("author_id", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
40
- await db.schema.createIndex("idx_revisions_entry").ifNotExists().on("revisions").columns(["collection", "entry_id"]).execute();
41
- await db.schema.createTable("taxonomies").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull()).addColumn("slug", "text", (col) => col.notNull()).addColumn("label", "text", (col) => col.notNull()).addColumn("parent_id", "text").addColumn("data", "text").addUniqueConstraint("taxonomies_name_slug_unique", ["name", "slug"]).addForeignKeyConstraint("taxonomies_parent_fk", ["parent_id"], "taxonomies", ["id"], (cb) => cb.onDelete("set null")).execute();
42
- await db.schema.createIndex("idx_taxonomies_name").ifNotExists().on("taxonomies").column("name").execute();
43
- await db.schema.createTable("content_taxonomies").ifNotExists().addColumn("collection", "text", (col) => col.notNull()).addColumn("entry_id", "text", (col) => col.notNull()).addColumn("taxonomy_id", "text", (col) => col.notNull()).addPrimaryKeyConstraint("content_taxonomies_pk", [
44
- "collection",
45
- "entry_id",
46
- "taxonomy_id"
47
- ]).addForeignKeyConstraint("content_taxonomies_taxonomy_fk", ["taxonomy_id"], "taxonomies", ["id"], (cb) => cb.onDelete("cascade")).execute();
48
- await db.schema.createTable("media").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("filename", "text", (col) => col.notNull()).addColumn("mime_type", "text", (col) => col.notNull()).addColumn("size", "integer").addColumn("width", "integer").addColumn("height", "integer").addColumn("alt", "text").addColumn("caption", "text").addColumn("storage_key", "text", (col) => col.notNull()).addColumn("content_hash", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("author_id", "text").execute();
49
- await db.schema.createIndex("idx_media_content_hash").ifNotExists().on("media").column("content_hash").execute();
50
- await db.schema.createTable("users").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("email", "text", (col) => col.notNull().unique()).addColumn("password_hash", "text", (col) => col.notNull()).addColumn("name", "text").addColumn("role", "text", (col) => col.defaultTo("subscriber")).addColumn("avatar_id", "text").addColumn("data", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
51
- await db.schema.createIndex("idx_users_email").ifNotExists().on("users").column("email").execute();
52
- await db.schema.createTable("options").ifNotExists().addColumn("name", "text", (col) => col.primaryKey()).addColumn("value", "text", (col) => col.notNull()).execute();
53
- await db.schema.createTable("audit_logs").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("timestamp", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("actor_id", "text").addColumn("actor_ip", "text").addColumn("action", "text", (col) => col.notNull()).addColumn("resource_type", "text").addColumn("resource_id", "text").addColumn("details", "text").addColumn("status", "text").execute();
54
- await db.schema.createIndex("idx_audit_actor").ifNotExists().on("audit_logs").column("actor_id").execute();
55
- await db.schema.createIndex("idx_audit_action").ifNotExists().on("audit_logs").column("action").execute();
56
- await db.schema.createIndex("idx_audit_timestamp").ifNotExists().on("audit_logs").column("timestamp").execute();
57
- }
58
- async function down$49(db) {
59
- await db.schema.dropTable("audit_logs").execute();
60
- await db.schema.dropTable("options").execute();
61
- await db.schema.dropTable("users").execute();
62
- await db.schema.dropTable("media").execute();
63
- await db.schema.dropTable("content_taxonomies").execute();
64
- await db.schema.dropTable("taxonomies").execute();
65
- await db.schema.dropTable("revisions").execute();
66
- }
67
-
68
- //#endregion
69
- //#region src/database/migrations/002_media_status.ts
70
- var _002_media_status_exports = /* @__PURE__ */ __exportAll({
71
- down: () => down$48,
72
- up: () => up$48
73
- });
74
- /**
75
- * Add status column to media table for tracking upload state.
76
- * Status values: 'pending' | 'ready' | 'failed'
77
- */
78
- async function up$48(db) {
79
- await db.schema.alterTable("media").addColumn("status", "text", (col) => col.notNull().defaultTo("ready")).execute();
80
- await db.schema.createIndex("idx_media_status").on("media").column("status").execute();
81
- }
82
- async function down$48(db) {
83
- await db.schema.dropIndex("idx_media_status").execute();
84
- }
85
-
86
- //#endregion
87
- //#region src/database/migrations/003_schema_registry.ts
88
- var _003_schema_registry_exports = /* @__PURE__ */ __exportAll({
89
- down: () => down$47,
90
- up: () => up$47
91
- });
92
- /**
93
- * Migration: Schema Registry Tables
94
- *
95
- * Creates the schema registry tables that store collection and field definitions.
96
- * This enables dynamic schema management where the database is the source of truth.
97
- */
98
- async function up$47(db) {
99
- await db.schema.createTable("_dineway_collections").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("label_singular", "text").addColumn("description", "text").addColumn("icon", "text").addColumn("supports", "text").addColumn("source", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
100
- await db.schema.createTable("_dineway_fields").addColumn("id", "text", (col) => col.primaryKey()).addColumn("collection_id", "text", (col) => col.notNull()).addColumn("slug", "text", (col) => col.notNull()).addColumn("label", "text", (col) => col.notNull()).addColumn("type", "text", (col) => col.notNull()).addColumn("column_type", "text", (col) => col.notNull()).addColumn("required", "integer", (col) => col.defaultTo(0)).addColumn("unique", "integer", (col) => col.defaultTo(0)).addColumn("default_value", "text").addColumn("validation", "text").addColumn("widget", "text").addColumn("options", "text").addColumn("sort_order", "integer", (col) => col.defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("fields_collection_fk", ["collection_id"], "_dineway_collections", ["id"], (cb) => cb.onDelete("cascade")).execute();
101
- await db.schema.createIndex("idx_fields_collection_slug").on("_dineway_fields").columns(["collection_id", "slug"]).unique().execute();
102
- await db.schema.createIndex("idx_fields_collection").on("_dineway_fields").column("collection_id").execute();
103
- await db.schema.createIndex("idx_fields_sort").on("_dineway_fields").columns(["collection_id", "sort_order"]).execute();
104
- }
105
- async function down$47(db) {
106
- await db.schema.dropTable("_dineway_fields").execute();
107
- await db.schema.dropTable("_dineway_collections").execute();
108
- }
109
-
110
- //#endregion
111
- //#region src/database/migrations/004_plugins.ts
112
- var _004_plugins_exports = /* @__PURE__ */ __exportAll({
113
- down: () => down$46,
114
- up: () => up$46
115
- });
116
- /**
117
- * Migration: Plugin System Tables
118
- *
119
- * Creates the plugin storage table and plugin state tracking.
120
- * Plugin storage uses a document store with declared indexes.
121
- *
122
- * @see PLUGIN-SYSTEM.md § Plugin Storage
123
- */
124
- async function up$46(db) {
125
- await db.schema.createTable("_plugin_storage").addColumn("plugin_id", "text", (col) => col.notNull()).addColumn("collection", "text", (col) => col.notNull()).addColumn("id", "text", (col) => col.notNull()).addColumn("data", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addPrimaryKeyConstraint("pk_plugin_storage", [
126
- "plugin_id",
127
- "collection",
128
- "id"
129
- ]).execute();
130
- await db.schema.createIndex("idx_plugin_storage_list").on("_plugin_storage").columns([
131
- "plugin_id",
132
- "collection",
133
- "created_at"
134
- ]).execute();
135
- await db.schema.createTable("_plugin_state").addColumn("plugin_id", "text", (col) => col.primaryKey()).addColumn("version", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().defaultTo("installed")).addColumn("installed_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("activated_at", "text").addColumn("deactivated_at", "text").addColumn("data", "text").execute();
136
- await db.schema.createTable("_plugin_indexes").addColumn("plugin_id", "text", (col) => col.notNull()).addColumn("collection", "text", (col) => col.notNull()).addColumn("index_name", "text", (col) => col.notNull()).addColumn("fields", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addPrimaryKeyConstraint("pk_plugin_indexes", [
137
- "plugin_id",
138
- "collection",
139
- "index_name"
140
- ]).execute();
141
- }
142
- async function down$46(db) {
143
- await db.schema.dropTable("_plugin_indexes").execute();
144
- await db.schema.dropTable("_plugin_state").execute();
145
- await db.schema.dropTable("_plugin_storage").execute();
146
- }
147
-
148
- //#endregion
149
- //#region src/database/migrations/005_menus.ts
150
- var _005_menus_exports = /* @__PURE__ */ __exportAll({
151
- down: () => down$45,
152
- up: () => up$45
153
- });
154
- /**
155
- * Navigation Menus migration
156
- *
157
- * Creates tables for admin-editable navigation menus.
158
- * Menu items can reference content entries, taxonomy terms, or custom URLs.
159
- */
160
- async function up$45(db) {
161
- await db.schema.createTable("_dineway_menus").addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
162
- await db.schema.createTable("_dineway_menu_items").addColumn("id", "text", (col) => col.primaryKey()).addColumn("menu_id", "text", (col) => col.notNull()).addColumn("parent_id", "text").addColumn("sort_order", "integer", (col) => col.notNull().defaultTo(0)).addColumn("type", "text", (col) => col.notNull()).addColumn("reference_collection", "text").addColumn("reference_id", "text").addColumn("custom_url", "text").addColumn("label", "text", (col) => col.notNull()).addColumn("title_attr", "text").addColumn("target", "text").addColumn("css_classes", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("menu_items_menu_fk", ["menu_id"], "_dineway_menus", ["id"], (cb) => cb.onDelete("cascade")).addForeignKeyConstraint("menu_items_parent_fk", ["parent_id"], "_dineway_menu_items", ["id"], (cb) => cb.onDelete("cascade")).execute();
163
- await db.schema.createIndex("idx_menu_items_menu").on("_dineway_menu_items").columns(["menu_id", "sort_order"]).execute();
164
- await db.schema.createIndex("idx_menu_items_parent").on("_dineway_menu_items").column("parent_id").execute();
165
- }
166
- async function down$45(db) {
167
- await db.schema.dropTable("_dineway_menu_items").execute();
168
- await db.schema.dropTable("_dineway_menus").execute();
169
- }
170
-
171
- //#endregion
172
- //#region src/database/migrations/006_taxonomy_defs.ts
173
- var _006_taxonomy_defs_exports = /* @__PURE__ */ __exportAll({
174
- down: () => down$44,
175
- up: () => up$44
176
- });
177
- /**
178
- * Taxonomy definitions migration
179
- *
180
- * Adds _dineway_taxonomy_defs table to store taxonomy definitions (category, tag, custom)
181
- * and seeds default category and tag taxonomies.
182
- */
183
- async function up$44(db) {
184
- await db.schema.createTable("_dineway_taxonomy_defs").addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("label_singular", "text").addColumn("hierarchical", "integer", (col) => col.defaultTo(0)).addColumn("collections", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
185
- await db.insertInto("_dineway_taxonomy_defs").values([{
186
- id: "taxdef_category",
187
- name: "category",
188
- label: "Categories",
189
- label_singular: "Category",
190
- hierarchical: 1,
191
- collections: JSON.stringify(["posts"])
192
- }, {
193
- id: "taxdef_tag",
194
- name: "tag",
195
- label: "Tags",
196
- label_singular: "Tag",
197
- hierarchical: 0,
198
- collections: JSON.stringify(["posts"])
199
- }]).execute();
200
- }
201
- async function down$44(db) {
202
- await db.schema.dropTable("_dineway_taxonomy_defs").execute();
203
- }
204
-
205
- //#endregion
206
- //#region src/database/migrations/007_widgets.ts
207
- var _007_widgets_exports = /* @__PURE__ */ __exportAll({
208
- down: () => down$43,
209
- up: () => up$43
210
- });
211
- async function up$43(db) {
212
- await db.schema.createTable("_dineway_widget_areas").addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("description", "text").addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
213
- await db.schema.createTable("_dineway_widgets").addColumn("id", "text", (col) => col.primaryKey()).addColumn("area_id", "text", (col) => col.notNull().references("_dineway_widget_areas.id").onDelete("cascade")).addColumn("sort_order", "integer", (col) => col.notNull().defaultTo(0)).addColumn("type", "text", (col) => col.notNull()).addColumn("title", "text").addColumn("content", "text").addColumn("menu_name", "text").addColumn("component_id", "text").addColumn("component_props", "text").addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
214
- await db.schema.createIndex("idx_widgets_area").on("_dineway_widgets").columns(["area_id", "sort_order"]).execute();
215
- }
216
- async function down$43(db) {
217
- await db.schema.dropTable("_dineway_widgets").execute();
218
- await db.schema.dropTable("_dineway_widget_areas").execute();
219
- }
220
-
221
- //#endregion
222
- //#region src/database/migrations/008_auth.ts
223
- var _008_auth_exports = /* @__PURE__ */ __exportAll({
224
- down: () => down$42,
225
- up: () => up$42
226
- });
227
- /**
228
- * Auth migration - passkey-first authentication
229
- *
230
- * Changes:
231
- * - Removes password_hash from users (no passwords)
232
- * - Adds role as integer (RBAC levels)
233
- * - Adds email_verified, avatar_url, updated_at to users
234
- * - Creates credentials table (passkeys)
235
- * - Creates auth_tokens table (magic links, invites)
236
- * - Creates oauth_accounts table (external provider links)
237
- * - Creates allowed_domains table (self-signup)
238
- */
239
- async function up$42(db) {
240
- await db.schema.createTable("users_new").addColumn("id", "text", (col) => col.primaryKey()).addColumn("email", "text", (col) => col.notNull().unique()).addColumn("name", "text").addColumn("avatar_url", "text").addColumn("role", "integer", (col) => col.notNull().defaultTo(10)).addColumn("email_verified", "integer", (col) => col.notNull().defaultTo(0)).addColumn("data", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
241
- await sql`
242
- INSERT INTO users_new (id, email, name, role, data, created_at, updated_at)
243
- SELECT
244
- id,
245
- email,
246
- name,
247
- CASE role
248
- WHEN 'admin' THEN 50
249
- WHEN 'editor' THEN 40
250
- WHEN 'author' THEN 30
251
- WHEN 'contributor' THEN 20
252
- ELSE 10
253
- END,
254
- data,
255
- created_at,
256
- ${currentTimestampValue(db)}
257
- FROM users
258
- `.execute(db);
259
- await db.schema.dropTable("users").execute();
260
- await sql`ALTER TABLE users_new RENAME TO users`.execute(db);
261
- await db.schema.createIndex("idx_users_email").on("users").column("email").execute();
262
- await db.schema.createTable("credentials").addColumn("id", "text", (col) => col.primaryKey()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("public_key", binaryType(db), (col) => col.notNull()).addColumn("algorithm", "integer", (col) => col.notNull().defaultTo(-7)).addColumn("counter", "integer", (col) => col.notNull().defaultTo(0)).addColumn("device_type", "text", (col) => col.notNull()).addColumn("backed_up", "integer", (col) => col.notNull().defaultTo(0)).addColumn("transports", "text").addColumn("name", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("last_used_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("credentials_user_fk", ["user_id"], "users", ["id"], (cb) => cb.onDelete("cascade")).execute();
263
- await db.schema.createIndex("idx_credentials_user").on("credentials").column("user_id").execute();
264
- await db.schema.createTable("auth_tokens").addColumn("hash", "text", (col) => col.primaryKey()).addColumn("user_id", "text").addColumn("email", "text").addColumn("type", "text", (col) => col.notNull()).addColumn("role", "integer").addColumn("invited_by", "text").addColumn("expires_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("auth_tokens_user_fk", ["user_id"], "users", ["id"], (cb) => cb.onDelete("cascade")).addForeignKeyConstraint("auth_tokens_invited_by_fk", ["invited_by"], "users", ["id"], (cb) => cb.onDelete("set null")).execute();
265
- await db.schema.createIndex("idx_auth_tokens_email").on("auth_tokens").column("email").execute();
266
- await db.schema.createTable("oauth_accounts").addColumn("provider", "text", (col) => col.notNull()).addColumn("provider_account_id", "text", (col) => col.notNull()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addPrimaryKeyConstraint("oauth_accounts_pk", ["provider", "provider_account_id"]).addForeignKeyConstraint("oauth_accounts_user_fk", ["user_id"], "users", ["id"], (cb) => cb.onDelete("cascade")).execute();
267
- await db.schema.createIndex("idx_oauth_accounts_user").on("oauth_accounts").column("user_id").execute();
268
- await db.schema.createTable("allowed_domains").addColumn("domain", "text", (col) => col.primaryKey()).addColumn("default_role", "integer", (col) => col.notNull().defaultTo(20)).addColumn("enabled", "integer", (col) => col.notNull().defaultTo(1)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
269
- await db.schema.createTable("auth_challenges").addColumn("challenge", "text", (col) => col.primaryKey()).addColumn("type", "text", (col) => col.notNull()).addColumn("user_id", "text").addColumn("data", "text").addColumn("expires_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
270
- await db.schema.createIndex("idx_auth_challenges_expires").on("auth_challenges").column("expires_at").execute();
271
- }
272
- async function down$42(db) {
273
- await db.schema.dropTable("auth_challenges").execute();
274
- await db.schema.dropTable("allowed_domains").execute();
275
- await db.schema.dropTable("oauth_accounts").execute();
276
- await db.schema.dropTable("auth_tokens").execute();
277
- await db.schema.dropTable("credentials").execute();
278
- await db.schema.createTable("users_old").addColumn("id", "text", (col) => col.primaryKey()).addColumn("email", "text", (col) => col.notNull().unique()).addColumn("password_hash", "text", (col) => col.notNull()).addColumn("name", "text").addColumn("role", "text", (col) => col.defaultTo("subscriber")).addColumn("avatar_id", "text").addColumn("data", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
279
- await sql`
280
- INSERT INTO users_old (id, email, password_hash, name, role, data, created_at)
281
- SELECT
282
- id,
283
- email,
284
- '', -- No way to restore password
285
- name,
286
- CASE role
287
- WHEN 50 THEN 'admin'
288
- WHEN 40 THEN 'editor'
289
- WHEN 30 THEN 'author'
290
- WHEN 20 THEN 'contributor'
291
- ELSE 'subscriber'
292
- END,
293
- data,
294
- created_at
295
- FROM users
296
- `.execute(db);
297
- await db.schema.dropTable("users").execute();
298
- await sql`ALTER TABLE users_old RENAME TO users`.execute(db);
299
- await db.schema.createIndex("idx_users_email").on("users").column("email").execute();
300
- }
301
-
302
- //#endregion
303
- //#region src/database/migrations/009_user_disabled.ts
304
- var _009_user_disabled_exports = /* @__PURE__ */ __exportAll({
305
- down: () => down$41,
306
- up: () => up$41
307
- });
308
- /**
309
- * User disabled column - for soft-disabling users
310
- *
311
- * Changes:
312
- * - Adds disabled column to users table (INTEGER, default 0)
313
- * - Disabled users cannot log in
314
- */
315
- async function up$41(db) {
316
- await sql`ALTER TABLE users ADD COLUMN disabled INTEGER NOT NULL DEFAULT 0`.execute(db);
317
- await db.schema.createIndex("idx_users_disabled").on("users").column("disabled").execute();
318
- }
319
- async function down$41(db) {
320
- await db.schema.dropIndex("idx_users_disabled").execute();
321
- }
322
-
323
- //#endregion
324
- //#region src/database/migrations/011_sections.ts
325
- var _011_sections_exports = /* @__PURE__ */ __exportAll({
326
- down: () => down$40,
327
- up: () => up$40
328
- });
329
- /**
330
- * Migration: Add sections tables and performance indexes
331
- *
332
- * Sections are reusable content blocks that can be inserted into any Portable Text field.
333
- * They provide a library of pre-built page sections (heroes, CTAs, testimonials, etc.)
334
- * that content authors can browse and insert with a single click.
335
- */
336
- async function up$40(db) {
337
- await db.schema.createTable("_dineway_section_categories").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("sort_order", "integer", (col) => col.defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
338
- await db.schema.createTable("_dineway_sections").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("title", "text", (col) => col.notNull()).addColumn("description", "text").addColumn("category_id", "text", (col) => col.references("_dineway_section_categories.id").onDelete("set null")).addColumn("keywords", "text").addColumn("content", "text", (col) => col.notNull()).addColumn("preview_media_id", "text").addColumn("source", "text", (col) => col.notNull().defaultTo("user")).addColumn("theme_id", "text").addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).addColumn("updated_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
339
- await db.schema.createIndex("idx_sections_category").on("_dineway_sections").columns(["category_id"]).execute();
340
- await db.schema.createIndex("idx_sections_source").on("_dineway_sections").columns(["source"]).execute();
341
- }
342
- async function down$40(db) {
343
- await db.schema.dropIndex("idx_sections_source").execute();
344
- await db.schema.dropIndex("idx_sections_category").execute();
345
- await db.schema.dropTable("_dineway_sections").execute();
346
- await db.schema.dropTable("_dineway_section_categories").execute();
347
- }
348
-
349
- //#endregion
350
- //#region src/database/migrations/012_search.ts
351
- var _012_search_exports = /* @__PURE__ */ __exportAll({
352
- down: () => down$39,
353
- up: () => up$39
354
- });
355
- /**
356
- * Migration: Search Support
357
- *
358
- * Adds search configuration to collections and searchable flag to fields.
359
- * FTS5 tables are created dynamically when search is enabled for a collection.
360
- */
361
- async function up$39(db) {
362
- await db.schema.alterTable("_dineway_collections").addColumn("search_config", "text").execute();
363
- await db.schema.alterTable("_dineway_fields").addColumn("searchable", "integer", (col) => col.defaultTo(0)).execute();
364
- }
365
- async function down$39(db) {
366
- await db.schema.alterTable("_dineway_fields").dropColumn("searchable").execute();
367
- await db.schema.alterTable("_dineway_collections").dropColumn("search_config").execute();
368
- }
369
-
370
- //#endregion
371
- //#region src/database/migrations/013_scheduled_publishing.ts
372
- var _013_scheduled_publishing_exports = /* @__PURE__ */ __exportAll({
373
- down: () => down$38,
374
- up: () => up$38
375
- });
376
- /**
377
- * Migration: Add scheduled publishing support
378
- *
379
- * Adds scheduled_at column to all ec_* content tables.
380
- * When scheduled_at is set and status is 'scheduled', the content
381
- * will be auto-published when the scheduled time is reached.
382
- */
383
- async function up$38(db) {
384
- const tableNames = await listTablesLike(db, "ec_%");
385
- for (const tableName of tableNames) {
386
- const table = { name: tableName };
387
- await sql`
388
- ALTER TABLE ${sql.ref(table.name)}
389
- ADD COLUMN scheduled_at TEXT
390
- `.execute(db);
391
- await sql`
392
- CREATE INDEX ${sql.ref(`idx_${table.name}_scheduled`)}
393
- ON ${sql.ref(table.name)} (scheduled_at)
394
- WHERE scheduled_at IS NOT NULL AND status = 'scheduled'
395
- `.execute(db);
396
- }
397
- }
398
- async function down$38(db) {
399
- const tableNames = await listTablesLike(db, "ec_%");
400
- for (const tableName of tableNames) {
401
- const table = { name: tableName };
402
- await sql`
403
- DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_scheduled`)}
404
- `.execute(db);
405
- await sql`
406
- ALTER TABLE ${sql.ref(table.name)}
407
- DROP COLUMN scheduled_at
408
- `.execute(db);
409
- }
410
- }
411
-
412
- //#endregion
413
- //#region src/database/migrations/014_draft_revisions.ts
414
- var _014_draft_revisions_exports = /* @__PURE__ */ __exportAll({
415
- down: () => down$37,
416
- up: () => up$37
417
- });
418
- async function up$37(db) {
419
- const tables = await db.selectFrom("_dineway_collections").select("slug").execute();
420
- for (const row of tables) {
421
- const tableName = `ec_${row.slug}`;
422
- await sql`
423
- ALTER TABLE ${sql.ref(tableName)}
424
- ADD COLUMN live_revision_id TEXT REFERENCES revisions(id)
425
- `.execute(db);
426
- await sql`
427
- ALTER TABLE ${sql.ref(tableName)}
428
- ADD COLUMN draft_revision_id TEXT REFERENCES revisions(id)
429
- `.execute(db);
430
- await sql`
431
- CREATE INDEX ${sql.ref(`idx_${row.slug}_live_revision`)}
432
- ON ${sql.ref(tableName)} (live_revision_id)
433
- `.execute(db);
434
- await sql`
435
- CREATE INDEX ${sql.ref(`idx_${row.slug}_draft_revision`)}
436
- ON ${sql.ref(tableName)} (draft_revision_id)
437
- `.execute(db);
438
- }
439
- }
440
- async function down$37(db) {
441
- const tables = await db.selectFrom("_dineway_collections").select("slug").execute();
442
- for (const row of tables) {
443
- const tableName = `ec_${row.slug}`;
444
- await sql`
445
- DROP INDEX IF EXISTS ${sql.ref(`idx_${row.slug}_draft_revision`)}
446
- `.execute(db);
447
- await sql`
448
- DROP INDEX IF EXISTS ${sql.ref(`idx_${row.slug}_live_revision`)}
449
- `.execute(db);
450
- await sql`
451
- ALTER TABLE ${sql.ref(tableName)}
452
- DROP COLUMN draft_revision_id
453
- `.execute(db);
454
- await sql`
455
- ALTER TABLE ${sql.ref(tableName)}
456
- DROP COLUMN live_revision_id
457
- `.execute(db);
458
- }
459
- }
460
-
461
- //#endregion
462
- //#region src/database/migrations/015_indexes.ts
463
- var _015_indexes_exports = /* @__PURE__ */ __exportAll({
464
- down: () => down$36,
465
- up: () => up$36
466
- });
467
- /**
468
- * Add performance indexes for common query patterns.
469
- *
470
- * Covers:
471
- * 1. Media table: mime_type, filename, created_at
472
- * 2. content_taxonomies: reverse lookup by taxonomy_id
473
- * 3. taxonomies: parent_id FK
474
- * 4. audit_logs: compound (resource_type, resource_id)
475
- * 5. Retroactive author_id + updated_at on existing ec_* content tables
476
- * (new tables get these from createContentTable() in registry.ts)
477
- */
478
- async function up$36(db) {
479
- await db.schema.createIndex("idx_media_mime_type").on("media").column("mime_type").execute();
480
- await db.schema.createIndex("idx_media_filename").on("media").column("filename").execute();
481
- await db.schema.createIndex("idx_media_created_at").on("media").column("created_at").execute();
482
- await db.schema.createIndex("idx_content_taxonomies_term").on("content_taxonomies").column("taxonomy_id").execute();
483
- await db.schema.createIndex("idx_taxonomies_parent").on("taxonomies").column("parent_id").execute();
484
- await db.schema.createIndex("idx_audit_resource").on("audit_logs").columns(["resource_type", "resource_id"]).execute();
485
- const tableNames = await listTablesLike(db, "ec_%");
486
- for (const tableName of tableNames) {
487
- const table = { name: tableName };
488
- await sql`
489
- CREATE INDEX ${sql.ref(`idx_${table.name}_author`)}
490
- ON ${sql.ref(table.name)} (author_id)
491
- `.execute(db);
492
- await sql`
493
- CREATE INDEX ${sql.ref(`idx_${table.name}_updated`)}
494
- ON ${sql.ref(table.name)} (updated_at)
495
- `.execute(db);
496
- }
497
- }
498
- async function down$36(db) {
499
- const tableNames = await listTablesLike(db, "ec_%");
500
- for (const tableName of tableNames) {
501
- const table = { name: tableName };
502
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_updated`)}`.execute(db);
503
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_author`)}`.execute(db);
504
- }
505
- await db.schema.dropIndex("idx_audit_resource").execute();
506
- await db.schema.dropIndex("idx_taxonomies_parent").execute();
507
- await db.schema.dropIndex("idx_content_taxonomies_term").execute();
508
- await db.schema.dropIndex("idx_media_created_at").execute();
509
- await db.schema.dropIndex("idx_media_filename").execute();
510
- await db.schema.dropIndex("idx_media_mime_type").execute();
511
- }
512
-
513
- //#endregion
514
- //#region src/database/migrations/016_api_tokens.ts
515
- var _016_api_tokens_exports = /* @__PURE__ */ __exportAll({
516
- down: () => down$35,
517
- up: () => up$35
518
- });
519
- /**
520
- * API token tables for programmatic access.
521
- *
522
- * Three tables:
523
- * 1. _dineway_api_tokens — Personal Access Tokens (ec_pat_...)
524
- * 2. _dineway_oauth_tokens — OAuth access/refresh tokens (ec_oat_/ec_ort_...)
525
- * 3. _dineway_device_codes — OAuth Device Flow state (RFC 8628)
526
- *
527
- * Every CREATE is guarded so this migration can recover if a previous run
528
- * created only part of the schema before failing and never recorded the
529
- * migration as applied.
530
- */
531
- async function up$35(db) {
532
- await db.schema.createTable("_dineway_api_tokens").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull()).addColumn("token_hash", "text", (col) => col.notNull().unique()).addColumn("prefix", "text", (col) => col.notNull()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("scopes", "text", (col) => col.notNull()).addColumn("expires_at", "text").addColumn("last_used_at", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("api_tokens_user_fk", ["user_id"], "users", ["id"], (cb) => cb.onDelete("cascade")).execute();
533
- await db.schema.createIndex("idx_api_tokens_token_hash").ifNotExists().on("_dineway_api_tokens").column("token_hash").execute();
534
- await db.schema.createIndex("idx_api_tokens_user_id").ifNotExists().on("_dineway_api_tokens").column("user_id").execute();
535
- await db.schema.createTable("_dineway_oauth_tokens").ifNotExists().addColumn("token_hash", "text", (col) => col.primaryKey()).addColumn("token_type", "text", (col) => col.notNull()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("scopes", "text", (col) => col.notNull()).addColumn("client_type", "text", (col) => col.notNull().defaultTo("cli")).addColumn("expires_at", "text", (col) => col.notNull()).addColumn("refresh_token_hash", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("oauth_tokens_user_fk", ["user_id"], "users", ["id"], (cb) => cb.onDelete("cascade")).execute();
536
- await db.schema.createIndex("idx_oauth_tokens_user_id").ifNotExists().on("_dineway_oauth_tokens").column("user_id").execute();
537
- await db.schema.createIndex("idx_oauth_tokens_expires").ifNotExists().on("_dineway_oauth_tokens").column("expires_at").execute();
538
- await db.schema.createTable("_dineway_device_codes").ifNotExists().addColumn("device_code", "text", (col) => col.primaryKey()).addColumn("user_code", "text", (col) => col.notNull().unique()).addColumn("scopes", "text", (col) => col.notNull()).addColumn("user_id", "text").addColumn("status", "text", (col) => col.notNull().defaultTo("pending")).addColumn("expires_at", "text", (col) => col.notNull()).addColumn("interval", "integer", (col) => col.notNull().defaultTo(5)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
539
- }
540
- async function down$35(db) {
541
- await db.schema.dropTable("_dineway_device_codes").ifExists().execute();
542
- await db.schema.dropTable("_dineway_oauth_tokens").ifExists().execute();
543
- await db.schema.dropTable("_dineway_api_tokens").ifExists().execute();
544
- }
545
-
546
- //#endregion
547
- //#region src/database/migrations/017_authorization_codes.ts
548
- var _017_authorization_codes_exports = /* @__PURE__ */ __exportAll({
549
- down: () => down$34,
550
- up: () => up$34
551
- });
552
- /**
553
- * Authorization codes for OAuth 2.1 Authorization Code + PKCE flow.
554
- *
555
- * Used by MCP clients (Claude Desktop, VS Code, etc.) to authenticate
556
- * via the standard OAuth authorization code grant.
557
- *
558
- * Also adds client_id tracking to oauth_tokens for per-client revocation.
559
- */
560
- async function up$34(db) {
561
- await db.schema.createTable("_dineway_authorization_codes").addColumn("code_hash", "text", (col) => col.primaryKey()).addColumn("client_id", "text", (col) => col.notNull()).addColumn("redirect_uri", "text", (col) => col.notNull()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("scopes", "text", (col) => col.notNull()).addColumn("code_challenge", "text", (col) => col.notNull()).addColumn("code_challenge_method", "text", (col) => col.notNull().defaultTo("S256")).addColumn("resource", "text").addColumn("expires_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("auth_codes_user_fk", ["user_id"], "users", ["id"], (cb) => cb.onDelete("cascade")).execute();
562
- await db.schema.createIndex("idx_auth_codes_expires").on("_dineway_authorization_codes").column("expires_at").execute();
563
- await sql`ALTER TABLE _dineway_oauth_tokens ADD COLUMN client_id TEXT`.execute(db);
564
- }
565
- async function down$34(db) {
566
- await db.schema.dropTable("_dineway_authorization_codes").execute();
567
- }
568
-
569
- //#endregion
570
- //#region src/database/migrations/018_seo.ts
571
- var _018_seo_exports = /* @__PURE__ */ __exportAll({
572
- down: () => down$33,
573
- up: () => up$33
574
- });
575
- /**
576
- * Migration: SEO support
577
- *
578
- * Creates:
579
- * - `_dineway_seo` table: per-content SEO metadata (separate from content tables)
580
- * - `has_seo` column on `_dineway_collections`: opt-in flag per collection
581
- *
582
- * SEO is not a universal concern — only collections representing web pages
583
- * need it. The `has_seo` flag controls whether the admin shows SEO fields
584
- * and whether the collection's content appears in sitemaps.
585
- */
586
- async function up$33(db) {
587
- await db.schema.createTable("_dineway_seo").addColumn("collection", "text", (col) => col.notNull()).addColumn("content_id", "text", (col) => col.notNull()).addColumn("seo_title", "text").addColumn("seo_description", "text").addColumn("seo_image", "text").addColumn("seo_canonical", "text").addColumn("seo_no_index", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addPrimaryKeyConstraint("_dineway_seo_pk", ["collection", "content_id"]).execute();
588
- await sql`
589
- CREATE INDEX idx_dineway_seo_collection
590
- ON _dineway_seo (collection)
591
- `.execute(db);
592
- await sql`
593
- ALTER TABLE _dineway_collections
594
- ADD COLUMN has_seo INTEGER NOT NULL DEFAULT 0
595
- `.execute(db);
596
- }
597
- async function down$33(db) {
598
- await sql`DROP TABLE IF EXISTS _dineway_seo`.execute(db);
599
- await sql`
600
- ALTER TABLE _dineway_collections
601
- DROP COLUMN has_seo
602
- `.execute(db);
603
- }
604
-
605
- //#endregion
606
- //#region src/database/migrations/019_i18n.ts
607
- var _019_i18n_exports = /* @__PURE__ */ __exportAll({
608
- down: () => down$32,
609
- up: () => up$32
610
- });
611
- /**
612
- * Quote an identifier for use in raw SQL. Escapes embedded double-quotes
613
- * per SQL standard (double them). The name should first pass
614
- * validateIdentifier() or validateTableName() for defense-in-depth.
615
- */
616
- const DOUBLE_QUOTE_RE = /"/g;
617
- function quoteIdent(name) {
618
- return `"${name.replace(DOUBLE_QUOTE_RE, "\"\"")}"`;
619
- }
620
- /** Suffix added to tmp tables during i18n migration rebuild. */
621
- const I18N_TMP_SUFFIX = /_i18n_tmp$/;
622
- /** Table names from sqlite_master are ec_{slug} — validate the pattern. */
623
- const TABLE_NAME_PATTERN = /^ec_[a-z][a-z0-9_]*$/;
624
- function validateTableName(name) {
625
- if (!TABLE_NAME_PATTERN.test(name)) throw new Error(`Invalid content table name: "${name}"`);
626
- }
627
- /** SQLite column types produced by Dineway's schema registry. */
628
- const ALLOWED_COLUMN_TYPES = new Set([
629
- "TEXT",
630
- "INTEGER",
631
- "REAL",
632
- "BLOB",
633
- "JSON",
634
- "NUMERIC",
635
- ""
636
- ]);
637
- function validateColumnType(type, colName) {
638
- if (!ALLOWED_COLUMN_TYPES.has(type.toUpperCase())) throw new Error(`Unexpected column type "${type}" for column "${colName}"`);
639
- }
640
- /**
641
- * Validate that a default value expression from PRAGMA table_info is safe
642
- * to interpolate into DDL. Allows: string literals, numeric literals,
643
- * NULL, and known function calls like datetime('now').
644
- *
645
- * Note: PRAGMA table_info strips the outer parens from expression defaults,
646
- * so `DEFAULT (datetime('now'))` is reported as `datetime('now')`.
647
- * We accept both forms and re-wrap in parens via normalizeDdlDefault().
648
- */
649
- const SAFE_DEFAULT_PATTERN = /^(?:'[^']*'|NULL|-?\d+(?:\.\d+)?|\(?datetime\('now'\)\)?|\(?json\('[^']*'\)\)?|0|1)$/i;
650
- function validateDefaultValue(value, colName) {
651
- if (!SAFE_DEFAULT_PATTERN.test(value)) throw new Error(`Unexpected default value "${value}" for column "${colName}"`);
652
- }
653
- /**
654
- * Normalize a PRAGMA table_info default value for use in DDL.
655
- * Function-call defaults (e.g. `datetime('now')`) must be wrapped in parens
656
- * to form valid expression defaults: `DEFAULT (datetime('now'))`.
657
- * PRAGMA strips the outer parens, so we re-add them here.
658
- */
659
- const FUNCTION_DEFAULT_PATTERN = /^(?:datetime|json)\(/i;
660
- function normalizeDdlDefault(value) {
661
- if (value.startsWith("(")) return value;
662
- if (FUNCTION_DEFAULT_PATTERN.test(value)) return `(${value})`;
663
- return value;
664
- }
665
- /**
666
- * Validate that a CREATE INDEX statement from sqlite_master is safe to replay.
667
- * Must start with CREATE [UNIQUE] INDEX and not contain suspicious patterns.
668
- */
669
- const CREATE_INDEX_PATTERN = /^CREATE\s+(UNIQUE\s+)?INDEX\s+/i;
670
- function validateCreateIndexSql(sqlStr, idxName) {
671
- if (!CREATE_INDEX_PATTERN.test(sqlStr)) throw new Error(`Unexpected index SQL for "${idxName}": does not match CREATE INDEX pattern`);
672
- if (sqlStr.includes(";")) throw new Error(`Unexpected index SQL for "${idxName}": contains semicolon`);
673
- }
674
- /**
675
- * PostgreSQL path: ALTER TABLE supports ADD COLUMN and DROP CONSTRAINT directly.
676
- * No table rebuild needed.
677
- */
678
- async function upPostgres(db) {
679
- const tableNames = await listTablesLike(db, "ec_%");
680
- for (const t of tableNames) {
681
- validateTableName(t);
682
- if ((await sql`
683
- SELECT EXISTS(
684
- SELECT 1 FROM information_schema.columns
685
- WHERE table_schema = current_schema() AND table_name = ${t} AND column_name = 'locale'
686
- ) as exists
687
- `.execute(db)).rows[0]?.exists === true) continue;
688
- await sql`ALTER TABLE ${sql.ref(t)} ADD COLUMN locale TEXT NOT NULL DEFAULT 'en'`.execute(db);
689
- await sql`ALTER TABLE ${sql.ref(t)} ADD COLUMN translation_group TEXT`.execute(db);
690
- const constraints = await sql`
691
- SELECT conname FROM pg_constraint
692
- WHERE conrelid = ${t}::regclass
693
- AND contype = 'u'
694
- AND array_length(conkey, 1) = 1
695
- AND conkey[1] = (
696
- SELECT attnum FROM pg_attribute
697
- WHERE attrelid = ${t}::regclass AND attname = 'slug'
698
- )
699
- `.execute(db);
700
- for (const c of constraints.rows) await sql`ALTER TABLE ${sql.ref(t)} DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(db);
701
- await sql`
702
- ALTER TABLE ${sql.ref(t)}
703
- ADD CONSTRAINT ${sql.ref(`${t}_slug_locale_unique`)} UNIQUE (slug, locale)
704
- `.execute(db);
705
- await sql`UPDATE ${sql.ref(t)} SET translation_group = id`.execute(db);
706
- await sql`CREATE INDEX ${sql.ref(`idx_${t}_locale`)} ON ${sql.ref(t)} (locale)`.execute(db);
707
- await sql`
708
- CREATE INDEX ${sql.ref(`idx_${t}_translation_group`)}
709
- ON ${sql.ref(t)} (translation_group)
710
- `.execute(db);
711
- }
712
- if ((await sql`
713
- SELECT EXISTS(
714
- SELECT 1 FROM information_schema.columns
715
- WHERE table_schema = current_schema() AND table_name = '_dineway_fields' AND column_name = 'translatable'
716
- ) as exists
717
- `.execute(db)).rows[0]?.exists !== true) await sql`
718
- ALTER TABLE _dineway_fields
719
- ADD COLUMN translatable INTEGER NOT NULL DEFAULT 1
720
- `.execute(db);
721
- }
722
- async function up$32(db) {
723
- if (!isSqlite(db)) return upPostgres(db);
724
- const orphanedTmps = await listTablesLike(db, "ec_%_i18n_tmp");
725
- for (const tmpName of orphanedTmps) {
726
- validateTableName(tmpName.replace(I18N_TMP_SUFFIX, ""));
727
- await sql`DROP TABLE IF EXISTS ${sql.ref(tmpName)}`.execute(db);
728
- }
729
- const tables = { rows: (await listTablesLike(db, "ec_%")).map((name) => ({ name })) };
730
- for (const table of tables.rows) {
731
- const t = table.name;
732
- validateTableName(t);
733
- const tmp = `${t}_i18n_tmp`;
734
- {
735
- const trx = db;
736
- const columns = (await sql`
737
- PRAGMA table_info(${sql.ref(t)})
738
- `.execute(trx)).rows;
739
- if (columns.some((col) => col.name === "locale")) continue;
740
- const idxResult = await sql`
741
- PRAGMA index_list(${sql.ref(t)})
742
- `.execute(trx);
743
- const indexDefs = [];
744
- for (const idx of idxResult.rows) {
745
- if (idx.origin === "pk" || idx.name.startsWith("sqlite_autoindex_")) continue;
746
- const idxColResult = await sql`
747
- PRAGMA index_info(${sql.ref(idx.name)})
748
- `.execute(trx);
749
- indexDefs.push({
750
- name: idx.name,
751
- unique: idx.unique === 1,
752
- columns: idxColResult.rows.map((c) => c.name),
753
- partial: idx.partial
754
- });
755
- }
756
- const partialSqls = /* @__PURE__ */ new Map();
757
- for (const idx of indexDefs) if (idx.partial) {
758
- const createResult = await sql`
759
- SELECT sql FROM sqlite_master
760
- WHERE type = 'index' AND name = ${idx.name}
761
- `.execute(trx);
762
- if (createResult.rows[0]?.sql) partialSqls.set(idx.name, createResult.rows[0].sql);
763
- }
764
- for (const col of columns) validateIdentifier(col.name, "column name");
765
- const colDefs = [];
766
- const colNames = [];
767
- for (const col of columns) {
768
- validateColumnType(col.type || "TEXT", col.name);
769
- colNames.push(quoteIdent(col.name));
770
- let def = `${quoteIdent(col.name)} ${col.type || "TEXT"}`;
771
- if (col.pk) def += " PRIMARY KEY";
772
- else if (col.name === "slug") {} else if (col.notnull) def += " NOT NULL";
773
- if (col.dflt_value !== null) {
774
- validateDefaultValue(col.dflt_value, col.name);
775
- def += ` DEFAULT ${normalizeDdlDefault(col.dflt_value)}`;
776
- }
777
- colDefs.push(def);
778
- }
779
- colDefs.push("\"locale\" TEXT NOT NULL DEFAULT 'en'");
780
- colDefs.push("\"translation_group\" TEXT");
781
- colDefs.push("UNIQUE(\"slug\", \"locale\")");
782
- const createColsSql = colDefs.join(",\n ");
783
- const selectColsSql = colNames.join(", ");
784
- for (const idx of indexDefs) await sql`DROP INDEX IF EXISTS ${sql.ref(idx.name)}`.execute(trx);
785
- await sql.raw(`CREATE TABLE ${quoteIdent(tmp)} (\n\t\t\t\t${createColsSql}\n\t\t\t)`).execute(trx);
786
- await sql.raw(`INSERT INTO ${quoteIdent(tmp)} (${selectColsSql}, "locale", "translation_group")\n\t\t\t SELECT ${selectColsSql}, 'en', "id" FROM ${quoteIdent(t)}`).execute(trx);
787
- await sql`DROP TABLE ${sql.ref(t)}`.execute(trx);
788
- await sql.raw(`ALTER TABLE ${quoteIdent(tmp)} RENAME TO ${quoteIdent(t)}`).execute(trx);
789
- for (const idx of indexDefs) {
790
- if (idx.name === `idx_${t}_slug`) continue;
791
- if (idx.partial && partialSqls.has(idx.name)) {
792
- const idxSql = partialSqls.get(idx.name);
793
- validateCreateIndexSql(idxSql, idx.name);
794
- await sql.raw(idxSql).execute(trx);
795
- } else {
796
- for (const c of idx.columns) validateIdentifier(c, "index column name");
797
- const cols = idx.columns.map((c) => quoteIdent(c)).join(", ");
798
- const unique = idx.unique ? "UNIQUE " : "";
799
- await sql.raw(`CREATE ${unique}INDEX ${quoteIdent(idx.name)} ON ${quoteIdent(t)} (${cols})`).execute(trx);
800
- }
801
- }
802
- await sql`
803
- CREATE INDEX ${sql.ref(`idx_${t}_slug`)}
804
- ON ${sql.ref(t)} (slug)
805
- `.execute(trx);
806
- await sql`
807
- CREATE INDEX ${sql.ref(`idx_${t}_locale`)}
808
- ON ${sql.ref(t)} (locale)
809
- `.execute(trx);
810
- await sql`
811
- CREATE INDEX ${sql.ref(`idx_${t}_translation_group`)}
812
- ON ${sql.ref(t)} (translation_group)
813
- `.execute(trx);
814
- }
815
- }
816
- if (!(await sql`
817
- PRAGMA table_info(_dineway_fields)
818
- `.execute(db)).rows.some((col) => col.name === "translatable")) await sql`
819
- ALTER TABLE _dineway_fields
820
- ADD COLUMN translatable INTEGER NOT NULL DEFAULT 1
821
- `.execute(db);
822
- }
823
- /**
824
- * PostgreSQL down path: straightforward ALTER TABLE operations.
825
- */
826
- async function downPostgres(db) {
827
- await sql`ALTER TABLE _dineway_fields DROP COLUMN translatable`.execute(db);
828
- const tableNames = await listTablesLike(db, "ec_%");
829
- for (const t of tableNames) {
830
- validateTableName(t);
831
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${t}_locale`)}`.execute(db);
832
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${t}_translation_group`)}`.execute(db);
833
- await sql`ALTER TABLE ${sql.ref(t)} DROP CONSTRAINT IF EXISTS ${sql.ref(`${t}_slug_locale_unique`)}`.execute(db);
834
- await sql`ALTER TABLE ${sql.ref(t)} ADD CONSTRAINT ${sql.ref(`${t}_slug_unique`)} UNIQUE (slug)`.execute(db);
835
- await sql`ALTER TABLE ${sql.ref(t)} DROP COLUMN locale`.execute(db);
836
- await sql`ALTER TABLE ${sql.ref(t)} DROP COLUMN translation_group`.execute(db);
837
- }
838
- }
839
- async function down$32(db) {
840
- if (!isSqlite(db)) return downPostgres(db);
841
- await sql`
842
- ALTER TABLE _dineway_fields
843
- DROP COLUMN translatable
844
- `.execute(db);
845
- const tableNames = await listTablesLike(db, "ec_%");
846
- for (const tableName of tableNames) {
847
- const t = tableName;
848
- validateTableName(t);
849
- const tmp = `${t}_i18n_tmp`;
850
- {
851
- const trx = db;
852
- const columns = (await sql`
853
- PRAGMA table_info(${sql.ref(t)})
854
- `.execute(trx)).rows;
855
- const idxResult = await sql`
856
- PRAGMA index_list(${sql.ref(t)})
857
- `.execute(trx);
858
- const indexDefs = [];
859
- for (const idx of idxResult.rows) {
860
- if (idx.origin === "pk" || idx.name.startsWith("sqlite_autoindex_")) continue;
861
- const idxColResult = await sql`
862
- PRAGMA index_info(${sql.ref(idx.name)})
863
- `.execute(trx);
864
- indexDefs.push({
865
- name: idx.name,
866
- unique: idx.unique === 1,
867
- columns: idxColResult.rows.map((c) => c.name),
868
- partial: idx.partial
869
- });
870
- }
871
- const partialSqls = /* @__PURE__ */ new Map();
872
- for (const idx of indexDefs) if (idx.partial) {
873
- const createResult = await sql`
874
- SELECT sql FROM sqlite_master
875
- WHERE type = 'index' AND name = ${idx.name}
876
- `.execute(trx);
877
- if (createResult.rows[0]?.sql) partialSqls.set(idx.name, createResult.rows[0].sql);
878
- }
879
- for (const col of columns) {
880
- if (col.name === "locale" || col.name === "translation_group") continue;
881
- validateIdentifier(col.name, "column name");
882
- }
883
- const colDefs = [];
884
- const colNames = [];
885
- for (const col of columns) {
886
- if (col.name === "locale" || col.name === "translation_group") continue;
887
- validateColumnType(col.type || "TEXT", col.name);
888
- colNames.push(quoteIdent(col.name));
889
- let def = `${quoteIdent(col.name)} ${col.type || "TEXT"}`;
890
- if (col.pk) def += " PRIMARY KEY";
891
- else if (col.name === "slug") def += " UNIQUE";
892
- else if (col.notnull) def += " NOT NULL";
893
- if (col.dflt_value !== null) {
894
- validateDefaultValue(col.dflt_value, col.name);
895
- def += ` DEFAULT ${normalizeDdlDefault(col.dflt_value)}`;
896
- }
897
- colDefs.push(def);
898
- }
899
- const createColsSql = colDefs.join(",\n ");
900
- const selectColsSql = colNames.join(", ");
901
- for (const idx of indexDefs) await sql`DROP INDEX IF EXISTS ${sql.ref(idx.name)}`.execute(trx);
902
- await sql.raw(`CREATE TABLE ${quoteIdent(tmp)} (\n\t\t\t\t${createColsSql}\n\t\t\t)`).execute(trx);
903
- await sql.raw(`INSERT OR IGNORE INTO ${quoteIdent(tmp)} (${selectColsSql})
904
- SELECT ${selectColsSql} FROM ${quoteIdent(t)}
905
- WHERE "locale" = 'en'`).execute(trx);
906
- await sql.raw(`INSERT OR IGNORE INTO ${quoteIdent(tmp)} (${selectColsSql})
907
- SELECT ${selectColsSql} FROM ${quoteIdent(t)}
908
- WHERE "id" NOT IN (SELECT "id" FROM ${quoteIdent(tmp)})
909
- AND "id" IN (
910
- SELECT "id" FROM ${quoteIdent(t)} AS t2
911
- WHERE t2."translation_group" IS NOT NULL
912
- AND t2."locale" = (
913
- SELECT MIN(t3."locale") FROM ${quoteIdent(t)} AS t3
914
- WHERE t3."translation_group" = t2."translation_group"
915
- )
916
- )`).execute(trx);
917
- await sql.raw(`INSERT OR IGNORE INTO ${quoteIdent(tmp)} (${selectColsSql})
918
- SELECT ${selectColsSql} FROM ${quoteIdent(t)}
919
- WHERE "id" NOT IN (SELECT "id" FROM ${quoteIdent(tmp)})
920
- AND "translation_group" IS NULL`).execute(trx);
921
- await sql`DROP TABLE ${sql.ref(t)}`.execute(trx);
922
- await sql.raw(`ALTER TABLE ${quoteIdent(tmp)} RENAME TO ${quoteIdent(t)}`).execute(trx);
923
- for (const idx of indexDefs) {
924
- if (idx.name === `idx_${t}_locale`) continue;
925
- if (idx.name === `idx_${t}_translation_group`) continue;
926
- if (idx.partial && partialSqls.has(idx.name)) {
927
- const idxSql = partialSqls.get(idx.name);
928
- validateCreateIndexSql(idxSql, idx.name);
929
- await sql.raw(idxSql).execute(trx);
930
- } else {
931
- const cols = idx.columns.filter((c) => c !== "locale" && c !== "translation_group");
932
- if (cols.length === 0) continue;
933
- for (const c of cols) validateIdentifier(c, "index column name");
934
- const colsSql = cols.map((c) => quoteIdent(c)).join(", ");
935
- const unique = idx.unique ? "UNIQUE " : "";
936
- await sql.raw(`CREATE ${unique}INDEX ${quoteIdent(idx.name)} ON ${quoteIdent(t)} (${colsSql})`).execute(trx);
937
- }
938
- }
939
- }
940
- }
941
- }
942
-
943
- //#endregion
944
- //#region src/database/migrations/020_collection_url_pattern.ts
945
- var _020_collection_url_pattern_exports = /* @__PURE__ */ __exportAll({
946
- down: () => down$31,
947
- up: () => up$31
948
- });
949
- /**
950
- * Migration: URL pattern for collections
951
- *
952
- * Adds `url_pattern` column to `_dineway_collections` so each collection
953
- * can declare its own URL structure (e.g. "/{slug}" for pages, "/blog/{slug}"
954
- * for posts). Used for menu URL resolution, sitemaps, and path-based lookups.
955
- */
956
- async function up$31(db) {
957
- await sql`
958
- ALTER TABLE _dineway_collections
959
- ADD COLUMN url_pattern TEXT
960
- `.execute(db);
961
- }
962
- async function down$31(db) {
963
- await sql`
964
- ALTER TABLE _dineway_collections
965
- DROP COLUMN url_pattern
966
- `.execute(db);
967
- }
968
-
969
- //#endregion
970
- //#region src/database/migrations/021_remove_section_categories.ts
971
- var _021_remove_section_categories_exports = /* @__PURE__ */ __exportAll({
972
- down: () => down$30,
973
- up: () => up$30
974
- });
975
- /**
976
- * Migration: Remove section categories
977
- *
978
- * Section categories had a complete backend but no UI to create or manage them.
979
- * Rather than building the missing UI for a feature with very little need at this stage,
980
- * we're removing the feature entirely.
981
- */
982
- async function up$30(db) {
983
- await db.schema.dropIndex("idx_sections_category").ifExists().execute();
984
- await db.schema.alterTable("_dineway_sections").dropColumn("category_id").execute();
985
- await db.schema.dropTable("_dineway_section_categories").execute();
986
- }
987
- async function down$30(db) {
988
- await db.schema.createTable("_dineway_section_categories").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("sort_order", "integer", (col) => col.defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
989
- await db.schema.alterTable("_dineway_sections").addColumn("category_id", "text", (col) => col.references("_dineway_section_categories.id").onDelete("set null")).execute();
990
- await db.schema.createIndex("idx_sections_category").on("_dineway_sections").columns(["category_id"]).execute();
991
- }
992
-
993
- //#endregion
994
- //#region src/database/migrations/022_marketplace_plugin_state.ts
995
- var _022_marketplace_plugin_state_exports = /* @__PURE__ */ __exportAll({
996
- down: () => down$29,
997
- up: () => up$29
998
- });
999
- /**
1000
- * Migration: Add marketplace fields to _plugin_state
1001
- *
1002
- * Adds `source` and `marketplace_version` columns to track
1003
- * whether a plugin was installed from config or marketplace,
1004
- * and which marketplace version is installed.
1005
- */
1006
- async function up$29(db) {
1007
- await sql`
1008
- ALTER TABLE _plugin_state
1009
- ADD COLUMN source TEXT NOT NULL DEFAULT 'config'
1010
- `.execute(db);
1011
- await sql`
1012
- ALTER TABLE _plugin_state
1013
- ADD COLUMN marketplace_version TEXT
1014
- `.execute(db);
1015
- await sql`
1016
- CREATE INDEX idx_plugin_state_source
1017
- ON _plugin_state (source)
1018
- WHERE source = 'marketplace'
1019
- `.execute(db);
1020
- }
1021
- async function down$29(db) {
1022
- await sql`
1023
- DROP INDEX IF EXISTS idx_plugin_state_source
1024
- `.execute(db);
1025
- await sql`
1026
- ALTER TABLE _plugin_state
1027
- DROP COLUMN marketplace_version
1028
- `.execute(db);
1029
- await sql`
1030
- ALTER TABLE _plugin_state
1031
- DROP COLUMN source
1032
- `.execute(db);
1033
- }
1034
-
1035
- //#endregion
1036
- //#region src/database/migrations/023_plugin_metadata.ts
1037
- var _023_plugin_metadata_exports = /* @__PURE__ */ __exportAll({
1038
- down: () => down$28,
1039
- up: () => up$28
1040
- });
1041
- /**
1042
- * Migration: Add display metadata to _plugin_state
1043
- *
1044
- * Stores display_name and description for marketplace plugins
1045
- * so the admin UI can show meaningful info without re-fetching
1046
- * from the marketplace on every page load.
1047
- */
1048
- async function up$28(db) {
1049
- await sql`
1050
- ALTER TABLE _plugin_state
1051
- ADD COLUMN display_name TEXT
1052
- `.execute(db);
1053
- await sql`
1054
- ALTER TABLE _plugin_state
1055
- ADD COLUMN description TEXT
1056
- `.execute(db);
1057
- }
1058
- async function down$28(db) {
1059
- await sql`
1060
- ALTER TABLE _plugin_state
1061
- DROP COLUMN description
1062
- `.execute(db);
1063
- await sql`
1064
- ALTER TABLE _plugin_state
1065
- DROP COLUMN display_name
1066
- `.execute(db);
1067
- }
1068
-
1069
- //#endregion
1070
- //#region src/database/migrations/024_media_placeholders.ts
1071
- var _024_media_placeholders_exports = /* @__PURE__ */ __exportAll({
1072
- down: () => down$27,
1073
- up: () => up$27
1074
- });
1075
- /**
1076
- * Migration: Add placeholder columns to media table
1077
- *
1078
- * Stores blurhash and dominant_color for LQIP (Low Quality Image Placeholder)
1079
- * support. Generated at upload time from image pixel data.
1080
- */
1081
- async function up$27(db) {
1082
- await sql`
1083
- ALTER TABLE media
1084
- ADD COLUMN blurhash TEXT
1085
- `.execute(db);
1086
- await sql`
1087
- ALTER TABLE media
1088
- ADD COLUMN dominant_color TEXT
1089
- `.execute(db);
1090
- }
1091
- async function down$27(db) {
1092
- await sql`
1093
- ALTER TABLE media
1094
- DROP COLUMN dominant_color
1095
- `.execute(db);
1096
- await sql`
1097
- ALTER TABLE media
1098
- DROP COLUMN blurhash
1099
- `.execute(db);
1100
- }
1101
-
1102
- //#endregion
1103
- //#region src/database/migrations/025_oauth_clients.ts
1104
- var _025_oauth_clients_exports = /* @__PURE__ */ __exportAll({
1105
- down: () => down$26,
1106
- up: () => up$26
1107
- });
1108
- /**
1109
- * Migration: Create OAuth clients table
1110
- *
1111
- * Implements the oauth_clients registry so that the authorization endpoint
1112
- * can validate client_id and enforce a per-client redirect URI allowlist.
1113
- *
1114
- * Each client has a set of pre-registered redirect URIs (JSON array).
1115
- * The authorize endpoint rejects any redirect_uri not in the client's list.
1116
- */
1117
- async function up$26(db) {
1118
- await db.schema.createTable("_dineway_oauth_clients").addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull()).addColumn("redirect_uris", "text", (col) => col.notNull()).addColumn("scopes", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
1119
- }
1120
- async function down$26(db) {
1121
- await db.schema.dropTable("_dineway_oauth_clients").execute();
1122
- }
1123
-
1124
- //#endregion
1125
- //#region src/database/migrations/026_cron_tasks.ts
1126
- var _026_cron_tasks_exports = /* @__PURE__ */ __exportAll({
1127
- down: () => down$25,
1128
- up: () => up$25
1129
- });
1130
- /**
1131
- * Migration: Create cron tasks table for plugin scheduled tasks.
1132
- *
1133
- * Each plugin can register cron tasks (recurring or one-shot) which are
1134
- * stored here and executed by the platform-specific scheduler.
1135
- *
1136
- * The `next_run_at` + `status` + `enabled` index drives the "find overdue
1137
- * tasks" query used by CronExecutor.tick().
1138
- */
1139
- async function up$25(db) {
1140
- await db.schema.createTable("_dineway_cron_tasks").addColumn("id", "text", (col) => col.primaryKey()).addColumn("plugin_id", "text", (col) => col.notNull()).addColumn("task_name", "text", (col) => col.notNull()).addColumn("schedule", "text", (col) => col.notNull()).addColumn("is_oneshot", "integer", (col) => col.notNull().defaultTo(0)).addColumn("data", "text").addColumn("next_run_at", "text", (col) => col.notNull()).addColumn("last_run_at", "text").addColumn("status", "text", (col) => col.notNull().defaultTo("idle")).addColumn("locked_at", "text").addColumn("enabled", "integer", (col) => col.notNull().defaultTo(1)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addUniqueConstraint("uq_cron_tasks_plugin_task", ["plugin_id", "task_name"]).execute();
1141
- await db.schema.createIndex("idx_cron_tasks_due").on("_dineway_cron_tasks").columns([
1142
- "enabled",
1143
- "status",
1144
- "next_run_at"
1145
- ]).execute();
1146
- await db.schema.createIndex("idx_cron_tasks_plugin").on("_dineway_cron_tasks").column("plugin_id").execute();
1147
- }
1148
- async function down$25(db) {
1149
- await db.schema.dropTable("_dineway_cron_tasks").execute();
1150
- }
1151
-
1152
- //#endregion
1153
- //#region src/database/migrations/027_comments.ts
1154
- var _027_comments_exports = /* @__PURE__ */ __exportAll({
1155
- down: () => down$24,
1156
- up: () => up$24
1157
- });
1158
- async function up$24(db) {
1159
- await db.schema.createTable("_dineway_comments").addColumn("id", "text", (col) => col.primaryKey()).addColumn("collection", "text", (col) => col.notNull()).addColumn("content_id", "text", (col) => col.notNull()).addColumn("parent_id", "text", (col) => col.references("_dineway_comments.id").onDelete("cascade")).addColumn("author_name", "text", (col) => col.notNull()).addColumn("author_email", "text", (col) => col.notNull()).addColumn("author_url", "text").addColumn("author_user_id", "text", (col) => col.references("users.id").onDelete("set null")).addColumn("body", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().defaultTo("pending")).addColumn("ip_hash", "text").addColumn("user_agent", "text").addColumn("moderation_metadata", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
1160
- await db.schema.createIndex("idx_comments_content").on("_dineway_comments").columns([
1161
- "collection",
1162
- "content_id",
1163
- "status"
1164
- ]).execute();
1165
- await db.schema.createIndex("idx_comments_parent").on("_dineway_comments").column("parent_id").execute();
1166
- await db.schema.createIndex("idx_comments_status").on("_dineway_comments").columns(["status", "created_at"]).execute();
1167
- await db.schema.createIndex("idx_comments_author_email").on("_dineway_comments").column("author_email").execute();
1168
- await db.schema.createIndex("idx_comments_author_user").on("_dineway_comments").column("author_user_id").execute();
1169
- await db.schema.alterTable("_dineway_collections").addColumn("comments_enabled", "integer", (col) => col.defaultTo(0)).execute();
1170
- await db.schema.alterTable("_dineway_collections").addColumn("comments_moderation", "text", (col) => col.defaultTo("first_time")).execute();
1171
- await db.schema.alterTable("_dineway_collections").addColumn("comments_closed_after_days", "integer", (col) => col.defaultTo(90)).execute();
1172
- await db.schema.alterTable("_dineway_collections").addColumn("comments_auto_approve_users", "integer", (col) => col.defaultTo(1)).execute();
1173
- }
1174
- async function down$24(db) {
1175
- await db.schema.dropTable("_dineway_comments").execute();
1176
- }
1177
-
1178
- //#endregion
1179
- //#region src/database/migrations/028_drop_author_url.ts
1180
- var _028_drop_author_url_exports = /* @__PURE__ */ __exportAll({
1181
- down: () => down$23,
1182
- up: () => up$23
1183
- });
1184
- async function up$23(db) {
1185
- await sql`ALTER TABLE _dineway_comments DROP COLUMN author_url`.execute(db);
1186
- }
1187
- async function down$23(db) {
1188
- await db.schema.alterTable("_dineway_comments").addColumn("author_url", "text").execute();
1189
- }
1190
-
1191
- //#endregion
1192
- //#region src/database/migrations/029_redirects.ts
1193
- var _029_redirects_exports = /* @__PURE__ */ __exportAll({
1194
- down: () => down$22,
1195
- up: () => up$22
1196
- });
1197
- async function up$22(db) {
1198
- await db.schema.createTable("_dineway_redirects").addColumn("id", "text", (col) => col.primaryKey()).addColumn("source", "text", (col) => col.notNull()).addColumn("destination", "text", (col) => col.notNull()).addColumn("type", "integer", (col) => col.notNull().defaultTo(301)).addColumn("is_pattern", "integer", (col) => col.notNull().defaultTo(0)).addColumn("enabled", "integer", (col) => col.notNull().defaultTo(1)).addColumn("hits", "integer", (col) => col.notNull().defaultTo(0)).addColumn("last_hit_at", "text").addColumn("group_name", "text").addColumn("auto", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
1199
- await db.schema.createIndex("idx_redirects_source").on("_dineway_redirects").column("source").execute();
1200
- await db.schema.createIndex("idx_redirects_enabled").on("_dineway_redirects").column("enabled").execute();
1201
- await db.schema.createIndex("idx_redirects_group").on("_dineway_redirects").column("group_name").execute();
1202
- await db.schema.createTable("_dineway_404_log").addColumn("id", "text", (col) => col.primaryKey()).addColumn("path", "text", (col) => col.notNull()).addColumn("referrer", "text").addColumn("user_agent", "text").addColumn("ip", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
1203
- await db.schema.createIndex("idx_404_log_path").on("_dineway_404_log").column("path").execute();
1204
- await db.schema.createIndex("idx_404_log_created").on("_dineway_404_log").column("created_at").execute();
1205
- }
1206
- async function down$22(db) {
1207
- await db.schema.dropTable("_dineway_404_log").execute();
1208
- await db.schema.dropTable("_dineway_redirects").execute();
1209
- }
1210
-
1211
- //#endregion
1212
- //#region src/database/migrations/030_widen_scheduled_index.ts
1213
- var _030_widen_scheduled_index_exports = /* @__PURE__ */ __exportAll({
1214
- down: () => down$21,
1215
- up: () => up$21
1216
- });
1217
- /**
1218
- * Migration: Widen scheduled publishing index
1219
- *
1220
- * The original partial index (013) only covered status='scheduled'.
1221
- * Published posts can now have scheduled draft changes, so widen the
1222
- * index to cover all rows where scheduled_at IS NOT NULL.
1223
- */
1224
- async function up$21(db) {
1225
- const tableNames = await listTablesLike(db, "ec_%");
1226
- for (const tableName of tableNames) {
1227
- const table = { name: tableName };
1228
- await sql`
1229
- DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_scheduled`)}
1230
- `.execute(db);
1231
- await sql`
1232
- CREATE INDEX ${sql.ref(`idx_${table.name}_scheduled`)}
1233
- ON ${sql.ref(table.name)} (scheduled_at)
1234
- WHERE scheduled_at IS NOT NULL
1235
- `.execute(db);
1236
- }
1237
- }
1238
- async function down$21(db) {
1239
- const tableNames = await listTablesLike(db, "ec_%");
1240
- for (const tableName of tableNames) {
1241
- const table = { name: tableName };
1242
- await sql`
1243
- DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_scheduled`)}
1244
- `.execute(db);
1245
- await sql`
1246
- CREATE INDEX ${sql.ref(`idx_${table.name}_scheduled`)}
1247
- ON ${sql.ref(table.name)} (scheduled_at)
1248
- WHERE scheduled_at IS NOT NULL AND status = 'scheduled'
1249
- `.execute(db);
1250
- }
1251
- }
1252
-
1253
- //#endregion
1254
- //#region src/database/migrations/031_bylines.ts
1255
- var _031_bylines_exports = /* @__PURE__ */ __exportAll({
1256
- down: () => down$20,
1257
- up: () => up$20
1258
- });
1259
- async function up$20(db) {
1260
- await db.schema.createTable("_dineway_bylines").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("display_name", "text", (col) => col.notNull()).addColumn("bio", "text").addColumn("avatar_media_id", "text", (col) => col.references("media.id").onDelete("set null")).addColumn("website_url", "text").addColumn("user_id", "text", (col) => col.references("users.id").onDelete("set null")).addColumn("is_guest", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
1261
- await sql`
1262
- CREATE UNIQUE INDEX ${sql.ref("idx_bylines_user_id_unique")}
1263
- ON ${sql.ref("_dineway_bylines")} (user_id)
1264
- WHERE user_id IS NOT NULL
1265
- `.execute(db);
1266
- await db.schema.createIndex("idx_bylines_slug").on("_dineway_bylines").column("slug").execute();
1267
- await db.schema.createIndex("idx_bylines_display_name").on("_dineway_bylines").column("display_name").execute();
1268
- await db.schema.createTable("_dineway_content_bylines").addColumn("id", "text", (col) => col.primaryKey()).addColumn("collection_slug", "text", (col) => col.notNull()).addColumn("content_id", "text", (col) => col.notNull()).addColumn("byline_id", "text", (col) => col.notNull().references("_dineway_bylines.id").onDelete("cascade")).addColumn("sort_order", "integer", (col) => col.notNull().defaultTo(0)).addColumn("role_label", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addUniqueConstraint("content_bylines_unique", [
1269
- "collection_slug",
1270
- "content_id",
1271
- "byline_id"
1272
- ]).execute();
1273
- await db.schema.createIndex("idx_content_bylines_content").on("_dineway_content_bylines").columns([
1274
- "collection_slug",
1275
- "content_id",
1276
- "sort_order"
1277
- ]).execute();
1278
- await db.schema.createIndex("idx_content_bylines_byline").on("_dineway_content_bylines").column("byline_id").execute();
1279
- const tableNames = await listTablesLike(db, "ec_%");
1280
- for (const tableName of tableNames) {
1281
- await sql`
1282
- ALTER TABLE ${sql.ref(tableName)}
1283
- ADD COLUMN primary_byline_id TEXT
1284
- `.execute(db);
1285
- await sql`
1286
- CREATE INDEX ${sql.ref(`idx_${tableName}_primary_byline`)}
1287
- ON ${sql.ref(tableName)} (primary_byline_id)
1288
- `.execute(db);
1289
- }
1290
- }
1291
- async function down$20(db) {
1292
- const tableNames = await listTablesLike(db, "ec_%");
1293
- for (const tableName of tableNames) {
1294
- await sql`
1295
- DROP INDEX IF EXISTS ${sql.ref(`idx_${tableName}_primary_byline`)}
1296
- `.execute(db);
1297
- await sql`
1298
- ALTER TABLE ${sql.ref(tableName)}
1299
- DROP COLUMN primary_byline_id
1300
- `.execute(db);
1301
- }
1302
- await db.schema.dropTable("_dineway_content_bylines").execute();
1303
- await db.schema.dropTable("_dineway_bylines").execute();
1304
- }
1305
-
1306
- //#endregion
1307
- //#region src/database/migrations/032_rate_limits.ts
1308
- var _032_rate_limits_exports = /* @__PURE__ */ __exportAll({
1309
- down: () => down$19,
1310
- up: () => up$19
1311
- });
1312
- /**
1313
- * Migration: Rate limits table + device code polling tracking.
1314
- *
1315
- * 1. Create _dineway_rate_limits for database-backed rate limiting
1316
- * of unauthenticated endpoints (device code, magic link, passkey).
1317
- *
1318
- * 2. Add last_polled_at column to _dineway_device_codes for
1319
- * RFC 8628 slow_down enforcement.
1320
- */
1321
- async function up$19(db) {
1322
- await db.schema.createTable("_dineway_rate_limits").addColumn("key", "text", (col) => col.notNull()).addColumn("window", "text", (col) => col.notNull()).addColumn("count", "integer", (col) => col.notNull().defaultTo(1)).addPrimaryKeyConstraint("pk_rate_limits", ["key", "window"]).execute();
1323
- await db.schema.createIndex("idx_rate_limits_window").on("_dineway_rate_limits").column("window").execute();
1324
- await db.schema.alterTable("_dineway_device_codes").addColumn("last_polled_at", "text").execute();
1325
- }
1326
- async function down$19(db) {
1327
- await db.schema.dropTable("_dineway_rate_limits").execute();
1328
- await db.schema.alterTable("_dineway_device_codes").dropColumn("last_polled_at").execute();
1329
- }
1330
-
1331
- //#endregion
1332
- //#region src/database/migrations/033_optimize_content_indexes.ts
1333
- var _033_optimize_content_indexes_exports = /* @__PURE__ */ __exportAll({
1334
- down: () => down$18,
1335
- up: () => up$18
1336
- });
1337
- /**
1338
- * Migration: Optimize content table indexes for SQLite-compatible performance
1339
- *
1340
- * Addresses GitHub issue #131: Full table scans causing excessive row reads.
1341
- *
1342
- * Changes:
1343
- * 1. Replaces single-column indexes with composite indexes on ec_* tables
1344
- * 2. Adds partial indexes for _dineway_comments status counting
1345
- *
1346
- * Impact: Reduces row reads by 90%+ for admin panel operations on SQLite-compatible adapters.
1347
- */
1348
- async function up$18(db) {
1349
- const tableNames = await listTablesLike(db, "ec_%");
1350
- for (const tableName of tableNames) {
1351
- const table = { name: tableName };
1352
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_status`)}`.execute(db);
1353
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_created`)}`.execute(db);
1354
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted`)}`.execute(db);
1355
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_updated`)}`.execute(db);
1356
- await sql`
1357
- CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table.name}_deleted_updated_id`)}
1358
- ON ${sql.ref(table.name)} (deleted_at, updated_at DESC, id DESC)
1359
- `.execute(db);
1360
- await sql`
1361
- CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table.name}_deleted_status`)}
1362
- ON ${sql.ref(table.name)} (deleted_at, status)
1363
- `.execute(db);
1364
- await sql`
1365
- CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table.name}_deleted_created_id`)}
1366
- ON ${sql.ref(table.name)} (deleted_at, created_at DESC, id DESC)
1367
- `.execute(db);
1368
- }
1369
- await sql`
1370
- CREATE INDEX IF NOT EXISTS idx_comments_pending
1371
- ON _dineway_comments (id)
1372
- WHERE status = 'pending'
1373
- `.execute(db);
1374
- await sql`
1375
- CREATE INDEX IF NOT EXISTS idx_comments_approved
1376
- ON _dineway_comments (id)
1377
- WHERE status = 'approved'
1378
- `.execute(db);
1379
- await sql`
1380
- CREATE INDEX IF NOT EXISTS idx_comments_spam
1381
- ON _dineway_comments (id)
1382
- WHERE status = 'spam'
1383
- `.execute(db);
1384
- await sql`
1385
- CREATE INDEX IF NOT EXISTS idx_comments_trash
1386
- ON _dineway_comments (id)
1387
- WHERE status = 'trash'
1388
- `.execute(db);
1389
- }
1390
- async function down$18(db) {
1391
- const tableNames = await listTablesLike(db, "ec_%");
1392
- for (const tableName of tableNames) {
1393
- const table = { name: tableName };
1394
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted_updated_id`)}`.execute(db);
1395
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted_status`)}`.execute(db);
1396
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted_created_id`)}`.execute(db);
1397
- await sql`
1398
- CREATE INDEX ${sql.ref(`idx_${table.name}_status`)}
1399
- ON ${sql.ref(table.name)} (status)
1400
- `.execute(db);
1401
- await sql`
1402
- CREATE INDEX ${sql.ref(`idx_${table.name}_created`)}
1403
- ON ${sql.ref(table.name)} (created_at)
1404
- `.execute(db);
1405
- await sql`
1406
- CREATE INDEX ${sql.ref(`idx_${table.name}_deleted`)}
1407
- ON ${sql.ref(table.name)} (deleted_at)
1408
- `.execute(db);
1409
- await sql`
1410
- CREATE INDEX ${sql.ref(`idx_${table.name}_updated`)}
1411
- ON ${sql.ref(table.name)} (updated_at)
1412
- `.execute(db);
1413
- }
1414
- await sql`DROP INDEX IF EXISTS idx_comments_pending`.execute(db);
1415
- await sql`DROP INDEX IF EXISTS idx_comments_approved`.execute(db);
1416
- await sql`DROP INDEX IF EXISTS idx_comments_spam`.execute(db);
1417
- await sql`DROP INDEX IF EXISTS idx_comments_trash`.execute(db);
1418
- }
1419
-
1420
- //#endregion
1421
- //#region src/database/migrations/034_published_at_index.ts
1422
- var _034_published_at_index_exports = /* @__PURE__ */ __exportAll({
1423
- down: () => down$17,
1424
- up: () => up$17
1425
- });
1426
- async function up$17(db) {
1427
- const tableNames = await listTablesLike(db, "ec_%");
1428
- for (const tableName of tableNames) {
1429
- const table = { name: tableName };
1430
- await sql`
1431
- CREATE INDEX ${sql.ref(`idx_${table.name}_deleted_published_id`)}
1432
- ON ${sql.ref(table.name)} (deleted_at, published_at DESC, id DESC)
1433
- `.execute(db);
1434
- }
1435
- }
1436
- async function down$17(db) {
1437
- const tableNames = await listTablesLike(db, "ec_%");
1438
- for (const tableName of tableNames) {
1439
- const table = { name: tableName };
1440
- await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted_published_id`)}`.execute(db);
1441
- }
1442
- }
1443
-
1444
- //#endregion
1445
- //#region src/database/migrations/035_backfill_url_patterns.ts
1446
- var _035_backfill_url_patterns_exports = /* @__PURE__ */ __exportAll({
1447
- down: () => down$16,
1448
- up: () => up$16
1449
- });
1450
- /**
1451
- * Backfill default URL patterns for the built-in posts and pages collections.
1452
- *
1453
- * Existing rows keep custom patterns; only missing defaults are filled.
1454
- */
1455
- async function up$16(db) {
1456
- await sql`
1457
- UPDATE _dineway_collections
1458
- SET url_pattern = '/posts/{slug}'
1459
- WHERE slug = 'posts' AND url_pattern IS NULL
1460
- `.execute(db);
1461
- await sql`
1462
- UPDATE _dineway_collections
1463
- SET url_pattern = '/{slug}'
1464
- WHERE slug = 'pages' AND url_pattern IS NULL
1465
- `.execute(db);
1466
- }
1467
- async function down$16(db) {
1468
- await sql`
1469
- UPDATE _dineway_collections
1470
- SET url_pattern = NULL
1471
- WHERE slug = 'posts' AND url_pattern = '/posts/{slug}'
1472
- `.execute(db);
1473
- await sql`
1474
- UPDATE _dineway_collections
1475
- SET url_pattern = NULL
1476
- WHERE slug = 'pages' AND url_pattern = '/{slug}'
1477
- `.execute(db);
1478
- }
1479
-
1480
- //#endregion
1481
- //#region src/database/migrations/036_activity_log.ts
1482
- var _036_activity_log_exports = /* @__PURE__ */ __exportAll({
1483
- down: () => down$15,
1484
- up: () => up$15
1485
- });
1486
- /**
1487
- * Site Context Engine activity foundation.
1488
- *
1489
- * This is an append-only domain activity log for meaningful site operations,
1490
- * not a request log. Actor IDs intentionally have no foreign key because the
1491
- * actor source can be users, API tokens, or a stable system actor.
1492
- */
1493
- async function up$15(db) {
1494
- await db.schema.createTable("_dineway_activity_log").addColumn("id", "text", (col) => col.primaryKey()).addColumn("actor_type", "text", (col) => col.notNull().check(sql`actor_type in ('user', 'api_token', 'system')`)).addColumn("actor_id", "text", (col) => col.notNull()).addColumn("auth_metadata", "text").addColumn("action_type", "text", (col) => col.notNull()).addColumn("subject_type", "text", (col) => col.notNull()).addColumn("subject_id", "text").addColumn("related_subject_type", "text").addColumn("related_subject_id", "text").addColumn("source_type", "text", (col) => col.check(sql`source_type is null or source_type in ('api_route', 'mcp_tool', 'system', 'plugin', 'unknown')`)).addColumn("source_name", "text").addColumn("result_status", "text", (col) => col.notNull().defaultTo("succeeded").check(sql`result_status in ('succeeded', 'pending', 'failed', 'blocked')`)).addColumn("scope", "text", (col) => col.notNull()).addColumn("summary", "text", (col) => col.notNull()).addColumn("detail", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
1495
- await db.schema.createIndex("idx_dineway_activity_actor").on("_dineway_activity_log").columns([
1496
- "actor_type",
1497
- "actor_id",
1498
- "created_at"
1499
- ]).execute();
1500
- await db.schema.createIndex("idx_dineway_activity_scope").on("_dineway_activity_log").columns(["scope", "created_at"]).execute();
1501
- await db.schema.createIndex("idx_dineway_activity_action_type").on("_dineway_activity_log").columns(["action_type", "created_at"]).execute();
1502
- await db.schema.createIndex("idx_dineway_activity_subject").on("_dineway_activity_log").columns([
1503
- "subject_type",
1504
- "subject_id",
1505
- "created_at"
1506
- ]).execute();
1507
- await db.schema.createIndex("idx_dineway_activity_related_subject").on("_dineway_activity_log").columns([
1508
- "related_subject_type",
1509
- "related_subject_id",
1510
- "created_at"
1511
- ]).execute();
1512
- await db.schema.createIndex("idx_dineway_activity_source").on("_dineway_activity_log").columns([
1513
- "source_type",
1514
- "source_name",
1515
- "created_at"
1516
- ]).execute();
1517
- await db.schema.createIndex("idx_dineway_activity_result_status").on("_dineway_activity_log").columns(["result_status", "created_at"]).execute();
1518
- await db.schema.createIndex("idx_dineway_activity_created_at").on("_dineway_activity_log").column("created_at").execute();
1519
- }
1520
- async function down$15(db) {
1521
- await db.schema.dropTable("_dineway_activity_log").execute();
1522
- }
1523
-
1524
- //#endregion
1525
- //#region src/database/migrations/037_context_entries.ts
1526
- var _037_context_entries_exports = /* @__PURE__ */ __exportAll({
1527
- down: () => down$14,
1528
- up: () => up$14
1529
- });
1530
- const CONTEXT_TYPE_SQL = [
1531
- "brand_voice",
1532
- "seo_strategy",
1533
- "audience",
1534
- "content_guideline",
1535
- "forbidden_terms",
1536
- "editorial_policy",
1537
- "migration_note",
1538
- "plugin_note",
1539
- "agent_reasoning",
1540
- "decision",
1541
- "risk",
1542
- "content_gap",
1543
- "style_exception"
1544
- ].map((type) => `'${type}'`).join(", ");
1545
- /**
1546
- * Typed, versioned Site Context Engine entries.
1547
- *
1548
- * Entries are scoped by deterministic strings (`site`, `collection:{slug}`,
1549
- * `content:{collection}:{id}`, `plugin:{id}`). Supersede keeps audit history by
1550
- * marking old entries non-current and linking replacements through
1551
- * `supersedes_id`.
1552
- */
1553
- async function up$14(db) {
1554
- await db.schema.createTable("_dineway_context_entries").addColumn("id", "text", (col) => col.primaryKey()).addColumn("scope", "text", (col) => col.notNull()).addColumn("context_type", "text", (col) => col.notNull().check(sql`context_type in (${sql.raw(CONTEXT_TYPE_SQL)})`)).addColumn("title", "text", (col) => col.notNull()).addColumn("body", "text", (col) => col.notNull()).addColumn("policy_key", "text").addColumn("source_activity_id", "text").addColumn("created_by_actor_type", "text", (col) => col.notNull().check(sql`created_by_actor_type in ('user', 'api_token', 'system')`)).addColumn("created_by_actor_id", "text", (col) => col.notNull()).addColumn("created_auth_metadata", "text").addColumn("supersedes_id", "text").addColumn("version", "integer", (col) => col.notNull().defaultTo(1)).addColumn("is_current", "integer", (col) => col.notNull().defaultTo(1).check(sql`is_current in (0, 1)`)).addColumn("valid_until", "text").addColumn("reviewed_at", "text").addColumn("reviewed_by_actor_type", "text", (col) => col.check(sql`reviewed_by_actor_type is null or reviewed_by_actor_type in ('user', 'api_token', 'system')`)).addColumn("reviewed_by_actor_id", "text").addColumn("review_note", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addForeignKeyConstraint("fk_dineway_context_source_activity", ["source_activity_id"], "_dineway_activity_log", ["id"], (cb) => cb.onDelete("set null")).addForeignKeyConstraint("fk_dineway_context_supersedes", ["supersedes_id"], "_dineway_context_entries", ["id"], (cb) => cb.onDelete("set null")).execute();
1555
- await db.schema.createTable("_dineway_context_tags").addColumn("context_entry_id", "text", (col) => col.notNull()).addColumn("tag", "text", (col) => col.notNull()).addPrimaryKeyConstraint("pk_dineway_context_tags", ["context_entry_id", "tag"]).addForeignKeyConstraint("fk_dineway_context_tags_entry", ["context_entry_id"], "_dineway_context_entries", ["id"], (cb) => cb.onDelete("cascade")).execute();
1556
- await db.schema.createIndex("idx_dineway_context_scope_current").on("_dineway_context_entries").columns([
1557
- "scope",
1558
- "is_current",
1559
- "context_type",
1560
- "updated_at"
1561
- ]).execute();
1562
- await db.schema.createIndex("idx_dineway_context_type").on("_dineway_context_entries").columns(["context_type", "updated_at"]).execute();
1563
- await db.schema.createIndex("idx_dineway_context_stale").on("_dineway_context_entries").columns(["is_current", "valid_until"]).execute();
1564
- await db.schema.createIndex("idx_dineway_context_policy").on("_dineway_context_entries").columns([
1565
- "scope",
1566
- "policy_key",
1567
- "is_current"
1568
- ]).execute();
1569
- await db.schema.createIndex("idx_dineway_context_supersedes").on("_dineway_context_entries").column("supersedes_id").execute();
1570
- await db.schema.createIndex("idx_dineway_context_actor").on("_dineway_context_entries").columns([
1571
- "created_by_actor_type",
1572
- "created_by_actor_id",
1573
- "created_at"
1574
- ]).execute();
1575
- await db.schema.createIndex("idx_dineway_context_created_at").on("_dineway_context_entries").column("created_at").execute();
1576
- await db.schema.createIndex("idx_dineway_context_tags_tag").on("_dineway_context_tags").columns(["tag", "context_entry_id"]).execute();
1577
- }
1578
- async function down$14(db) {
1579
- await db.schema.dropTable("_dineway_context_tags").execute();
1580
- await db.schema.dropTable("_dineway_context_entries").execute();
1581
- }
1582
-
1583
- //#endregion
1584
- //#region src/database/migrations/038_review_requests.ts
1585
- var _038_review_requests_exports = /* @__PURE__ */ __exportAll({
1586
- down: () => down$13,
1587
- up: () => up$13
1588
- });
1589
- /**
1590
- * Lightweight content review requests for Phase 5a.
1591
- *
1592
- * Review requests bind human approval to immutable content revision/action
1593
- * identifiers so an approval cannot be reused after the draft or action changes.
1594
- */
1595
- async function up$13(db) {
1596
- await db.schema.createTable("_dineway_review_requests").addColumn("id", "text", (col) => col.primaryKey()).addColumn("status", "text", (col) => col.notNull().defaultTo("pending").check(sql`status in ('pending', 'approved', 'rejected')`)).addColumn("collection", "text", (col) => col.notNull()).addColumn("entry_id", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("live_revision_id", "text").addColumn("draft_revision_id", "text").addColumn("reviewed_rev", "text", (col) => col.notNull()).addColumn("action_type", "text", (col) => col.notNull()).addColumn("action_hash", "text", (col) => col.notNull()).addColumn("risk_reason", "text").addColumn("review_payload", "text", (col) => col.notNull()).addColumn("requested_by_actor_type", "text", (col) => col.notNull().check(sql`requested_by_actor_type in ('user', 'api_token', 'system')`)).addColumn("requested_by_actor_id", "text", (col) => col.notNull()).addColumn("requested_auth_metadata", "text").addColumn("resolved_by_actor_type", "text", (col) => col.check(sql`resolved_by_actor_type is null or resolved_by_actor_type in ('user', 'api_token', 'system')`)).addColumn("resolved_by_actor_id", "text").addColumn("resolved_auth_metadata", "text").addColumn("review_note", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("resolved_at", "text").execute();
1597
- await db.schema.createIndex("idx_dineway_review_status_created").on("_dineway_review_requests").columns(["status", "created_at"]).execute();
1598
- await db.schema.createIndex("idx_dineway_review_target").on("_dineway_review_requests").columns([
1599
- "collection",
1600
- "entry_id",
1601
- "status"
1602
- ]).execute();
1603
- await db.schema.createIndex("idx_dineway_review_scope").on("_dineway_review_requests").columns([
1604
- "scope",
1605
- "status",
1606
- "created_at"
1607
- ]).execute();
1608
- await db.schema.createIndex("idx_dineway_review_action").on("_dineway_review_requests").columns(["action_type", "action_hash"]).execute();
1609
- await db.schema.createIndex("idx_dineway_review_requested_actor").on("_dineway_review_requests").columns([
1610
- "requested_by_actor_type",
1611
- "requested_by_actor_id",
1612
- "created_at"
1613
- ]).execute();
1614
- await db.schema.createIndex("idx_dineway_review_resolved_at").on("_dineway_review_requests").column("resolved_at").execute();
1615
- }
1616
- async function down$13(db) {
1617
- await db.schema.dropTable("_dineway_review_requests").execute();
1618
- }
1619
-
1620
- //#endregion
1621
- //#region src/database/migrations/039_entity_aliases.ts
1622
- var _039_entity_aliases_exports = /* @__PURE__ */ __exportAll({
1623
- down: () => down$12,
1624
- up: () => up$12
1625
- });
1626
- const ENTITY_ALIAS_TYPES = [
1627
- "content",
1628
- "collection",
1629
- "plugin",
1630
- "taxonomy",
1631
- "menu"
1632
- ];
1633
- /**
1634
- * Portable alias table for deterministic entity resolution.
1635
- *
1636
- * This replaces CRMy's PostgreSQL-specific alias arrays with a normalized
1637
- * mapping table that works across SQLite/libSQL and PostgreSQL.
1638
- */
1639
- async function up$12(db) {
1640
- const entityTypesSql = sql.join(ENTITY_ALIAS_TYPES.map((value) => sql.lit(value)), sql`, `);
1641
- await db.schema.createTable("_dineway_entity_aliases").addColumn("id", "text", (col) => col.primaryKey()).addColumn("entity_type", "text", (col) => col.notNull().check(sql`entity_type in (${entityTypesSql})`)).addColumn("collection_slug", "text").addColumn("entity_id", "text", (col) => col.notNull()).addColumn("alias", "text", (col) => col.notNull()).addColumn("normalized_alias", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
1642
- await db.schema.createIndex("idx_dineway_entity_alias_lookup").on("_dineway_entity_aliases").columns(["entity_type", "normalized_alias"]).execute();
1643
- await db.schema.createIndex("idx_dineway_entity_alias_collection_lookup").on("_dineway_entity_aliases").columns([
1644
- "entity_type",
1645
- "collection_slug",
1646
- "normalized_alias"
1647
- ]).execute();
1648
- await db.schema.createIndex("idx_dineway_entity_alias_entity").on("_dineway_entity_aliases").columns(["entity_type", "entity_id"]).execute();
1649
- await db.schema.createIndex("idx_dineway_entity_alias_unique").on("_dineway_entity_aliases").columns([
1650
- "entity_type",
1651
- "collection_slug",
1652
- "entity_id",
1653
- "normalized_alias"
1654
- ]).unique().execute();
1655
- }
1656
- async function down$12(db) {
1657
- await db.schema.dropTable("_dineway_entity_aliases").execute();
1658
- }
1659
-
1660
- //#endregion
1661
- //#region src/database/migrations/040_handoff_snapshots.ts
1662
- var _040_handoff_snapshots_exports = /* @__PURE__ */ __exportAll({
1663
- down: () => down$11,
1664
- up: () => up$11
1665
- });
1666
- const ACTOR_TYPES$2 = [
1667
- "user",
1668
- "api_token",
1669
- "system"
1670
- ];
1671
- const HANDOFF_KINDS = [
1672
- "pause",
1673
- "review_request",
1674
- "assignment"
1675
- ];
1676
- const REFERENCE_TYPES = ["review_request", "assignment"];
1677
- async function up$11(db) {
1678
- const actorTypesSql = sql.join(ACTOR_TYPES$2.map((value) => sql.lit(value)), sql`, `);
1679
- const handoffKindsSql = sql.join(HANDOFF_KINDS.map((value) => sql.lit(value)), sql`, `);
1680
- const referenceTypesSql = sql.join(REFERENCE_TYPES.map((value) => sql.lit(value)), sql`, `);
1681
- await db.schema.createTable("_dineway_handoff_snapshots").addColumn("id", "text", (col) => col.primaryKey()).addColumn("scope", "text", (col) => col.notNull()).addColumn("actor_type", "text", (col) => col.notNull().check(sql`actor_type in (${actorTypesSql})`)).addColumn("actor_id", "text", (col) => col.notNull()).addColumn("auth_metadata", "text").addColumn("objective", "text", (col) => col.notNull()).addColumn("reasoning", "text", (col) => col.notNull()).addColumn("key_findings", "text", (col) => col.notNull().defaultTo(sql`'[]'`)).addColumn("touched_objects", "text", (col) => col.notNull().defaultTo(sql`'[]'`)).addColumn("pending_actions", "text", (col) => col.notNull().defaultTo(sql`'[]'`)).addColumn("tool_evidence", "text", (col) => col.notNull().defaultTo(sql`'[]'`)).addColumn("confidence", "real").addColumn("handoff_kind", "text", (col) => col.notNull().check(sql`handoff_kind in (${handoffKindsSql})`).defaultTo(sql`'pause'`)).addColumn("reference_type", "text", (col) => col.check(sql`reference_type is null or reference_type in (${referenceTypesSql})`)).addColumn("reference_id", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("resumed_at", "text").execute();
1682
- await db.schema.createIndex("idx_dineway_handoff_scope_created").on("_dineway_handoff_snapshots").columns(["scope", "created_at"]).execute();
1683
- await db.schema.createIndex("idx_dineway_handoff_actor_created").on("_dineway_handoff_snapshots").columns([
1684
- "actor_type",
1685
- "actor_id",
1686
- "created_at"
1687
- ]).execute();
1688
- await db.schema.createIndex("idx_dineway_handoff_kind_created").on("_dineway_handoff_snapshots").columns(["handoff_kind", "created_at"]).execute();
1689
- await db.schema.createIndex("idx_dineway_handoff_reference").on("_dineway_handoff_snapshots").columns(["reference_type", "reference_id"]).execute();
1690
- }
1691
- async function down$11(db) {
1692
- await db.schema.dropTable("_dineway_handoff_snapshots").execute();
1693
- }
1694
-
1695
- //#endregion
1696
- //#region src/database/migrations/041_assignments.ts
1697
- var _041_assignments_exports = /* @__PURE__ */ __exportAll({
1698
- down: () => down$10,
1699
- up: () => up$10
1700
- });
1701
- const ACTOR_TYPES$1 = [
1702
- "user",
1703
- "api_token",
1704
- "system"
1705
- ];
1706
- const ASSIGNMENT_STATUSES = [
1707
- "pending",
1708
- "accepted",
1709
- "in_progress",
1710
- "blocked",
1711
- "completed",
1712
- "declined",
1713
- "cancelled"
1714
- ];
1715
- const ASSIGNMENT_PRIORITIES = [
1716
- "low",
1717
- "normal",
1718
- "high",
1719
- "urgent"
1720
- ];
1721
- async function up$10(db) {
1722
- const actorTypesSql = sql.join(ACTOR_TYPES$1.map((value) => sql.lit(value)), sql`, `);
1723
- const statusesSql = sql.join(ASSIGNMENT_STATUSES.map((value) => sql.lit(value)), sql`, `);
1724
- const prioritiesSql = sql.join(ASSIGNMENT_PRIORITIES.map((value) => sql.lit(value)), sql`, `);
1725
- await db.schema.createTable("_dineway_assignments").addColumn("id", "text", (col) => col.primaryKey()).addColumn("scope", "text", (col) => col.notNull()).addColumn("assignment_type", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().defaultTo(sql`'pending'`).check(sql`status in (${statusesSql})`)).addColumn("priority", "text", (col) => col.notNull().defaultTo(sql`'normal'`).check(sql`priority in (${prioritiesSql})`)).addColumn("title", "text", (col) => col.notNull()).addColumn("summary", "text").addColumn("details", "text").addColumn("assigned_by_actor_type", "text", (col) => col.notNull().check(sql`assigned_by_actor_type in (${actorTypesSql})`)).addColumn("assigned_by_actor_id", "text", (col) => col.notNull()).addColumn("assigned_to_actor_type", "text", (col) => col.check(sql`assigned_to_actor_type is null or assigned_to_actor_type in (${actorTypesSql})`)).addColumn("assigned_to_actor_id", "text").addColumn("metadata", "text", (col) => col.notNull().defaultTo(sql`'{}'`)).addColumn("related_handoff_snapshot_id", "text", (col) => col.references("_dineway_handoff_snapshots.id").onDelete("set null")).addColumn("due_at", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("accepted_at", "text").addColumn("started_at", "text").addColumn("blocked_at", "text").addColumn("completed_at", "text").addColumn("declined_at", "text").addColumn("cancelled_at", "text").execute();
1726
- await db.schema.createIndex("idx_dineway_assignment_status_created").on("_dineway_assignments").columns(["status", "created_at"]).execute();
1727
- await db.schema.createIndex("idx_dineway_assignment_scope_status").on("_dineway_assignments").columns([
1728
- "scope",
1729
- "status",
1730
- "created_at"
1731
- ]).execute();
1732
- await db.schema.createIndex("idx_dineway_assignment_type_status").on("_dineway_assignments").columns([
1733
- "assignment_type",
1734
- "status",
1735
- "created_at"
1736
- ]).execute();
1737
- await db.schema.createIndex("idx_dineway_assignment_assigned_to").on("_dineway_assignments").columns([
1738
- "assigned_to_actor_type",
1739
- "assigned_to_actor_id",
1740
- "status",
1741
- "created_at"
1742
- ]).execute();
1743
- await db.schema.createIndex("idx_dineway_assignment_assigned_by").on("_dineway_assignments").columns([
1744
- "assigned_by_actor_type",
1745
- "assigned_by_actor_id",
1746
- "status",
1747
- "created_at"
1748
- ]).execute();
1749
- await db.schema.createIndex("idx_dineway_assignment_priority_created").on("_dineway_assignments").columns(["priority", "created_at"]).execute();
1750
- await db.schema.createIndex("idx_dineway_assignment_due_at").on("_dineway_assignments").column("due_at").execute();
1751
- await db.schema.createIndex("idx_dineway_assignment_handoff").on("_dineway_assignments").column("related_handoff_snapshot_id").execute();
1752
- }
1753
- async function down$10(db) {
1754
- await db.schema.dropTable("_dineway_assignments").execute();
1755
- }
1756
-
1757
- //#endregion
1758
- //#region src/database/migrations/042_hitl_requests.ts
1759
- var _042_hitl_requests_exports = /* @__PURE__ */ __exportAll({
1760
- down: () => down$9,
1761
- up: () => up$9
1762
- });
1763
- const ACTOR_TYPES = [
1764
- "user",
1765
- "api_token",
1766
- "system"
1767
- ];
1768
- const HITL_STATUSES = [
1769
- "pending",
1770
- "approved",
1771
- "rejected"
1772
- ];
1773
- const HITL_PRIORITIES = [
1774
- "low",
1775
- "normal",
1776
- "high",
1777
- "urgent"
1778
- ];
1779
- async function up$9(db) {
1780
- const actorTypesSql = sql.join(ACTOR_TYPES.map((value) => sql.lit(value)), sql`, `);
1781
- const statusesSql = sql.join(HITL_STATUSES.map((value) => sql.lit(value)), sql`, `);
1782
- const prioritiesSql = sql.join(HITL_PRIORITIES.map((value) => sql.lit(value)), sql`, `);
1783
- await db.schema.createTable("_dineway_hitl_requests").addColumn("id", "text", (col) => col.primaryKey()).addColumn("scope", "text", (col) => col.notNull()).addColumn("action_type", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().defaultTo(sql`'pending'`).check(sql`status in (${statusesSql})`)).addColumn("priority", "text", (col) => col.notNull().defaultTo(sql`'normal'`).check(sql`priority in (${prioritiesSql})`)).addColumn("title", "text", (col) => col.notNull()).addColumn("summary", "text").addColumn("risk_reason", "text", (col) => col.notNull()).addColumn("review_payload", "text", (col) => col.notNull()).addColumn("target_ref_type", "text", (col) => col.notNull()).addColumn("target_ref_id", "text", (col) => col.notNull()).addColumn("requested_by_actor_type", "text", (col) => col.notNull().check(sql`requested_by_actor_type in (${actorTypesSql})`)).addColumn("requested_by_actor_id", "text", (col) => col.notNull()).addColumn("requested_auth_metadata", "text").addColumn("resolved_by_actor_type", "text", (col) => col.check(sql`resolved_by_actor_type is null or resolved_by_actor_type in (${actorTypesSql})`)).addColumn("resolved_by_actor_id", "text").addColumn("resolved_auth_metadata", "text").addColumn("review_note", "text").addColumn("metadata", "text", (col) => col.notNull().defaultTo(sql`'{}'`)).addColumn("related_assignment_id", "text", (col) => col.references("_dineway_assignments.id").onDelete("set null")).addColumn("related_handoff_snapshot_id", "text", (col) => col.references("_dineway_handoff_snapshots.id").onDelete("set null")).addColumn("sla_due_at", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("resolved_at", "text").execute();
1784
- await db.schema.alterTable("_dineway_assignments").addColumn("related_hitl_request_id", "text", (col) => col.references("_dineway_hitl_requests.id").onDelete("set null")).execute();
1785
- await db.schema.createIndex("idx_dineway_hitl_status_created").on("_dineway_hitl_requests").columns(["status", "created_at"]).execute();
1786
- await db.schema.createIndex("idx_dineway_hitl_scope_status").on("_dineway_hitl_requests").columns([
1787
- "scope",
1788
- "status",
1789
- "created_at"
1790
- ]).execute();
1791
- await db.schema.createIndex("idx_dineway_hitl_action_status").on("_dineway_hitl_requests").columns([
1792
- "action_type",
1793
- "status",
1794
- "created_at"
1795
- ]).execute();
1796
- await db.schema.createIndex("idx_dineway_hitl_requested_actor").on("_dineway_hitl_requests").columns([
1797
- "requested_by_actor_type",
1798
- "requested_by_actor_id",
1799
- "status",
1800
- "created_at"
1801
- ]).execute();
1802
- await db.schema.createIndex("idx_dineway_hitl_target_ref").on("_dineway_hitl_requests").columns([
1803
- "target_ref_type",
1804
- "target_ref_id",
1805
- "status",
1806
- "created_at"
1807
- ]).execute();
1808
- await db.schema.createIndex("idx_dineway_hitl_priority_created").on("_dineway_hitl_requests").columns(["priority", "created_at"]).execute();
1809
- await db.schema.createIndex("idx_dineway_hitl_assignment").on("_dineway_hitl_requests").column("related_assignment_id").execute();
1810
- await db.schema.createIndex("idx_dineway_hitl_handoff").on("_dineway_hitl_requests").column("related_handoff_snapshot_id").execute();
1811
- await db.schema.createIndex("idx_dineway_hitl_sla_due_at").on("_dineway_hitl_requests").column("sla_due_at").execute();
1812
- await db.schema.createIndex("idx_dineway_assignment_hitl").on("_dineway_assignments").column("related_hitl_request_id").execute();
1813
- }
1814
- async function down$9(db) {
1815
- await db.schema.dropIndex("idx_dineway_assignment_hitl").execute();
1816
- await db.schema.alterTable("_dineway_assignments").dropColumn("related_hitl_request_id").execute();
1817
- await db.schema.dropTable("_dineway_hitl_requests").execute();
1818
- }
1819
-
1820
- //#endregion
1821
- //#region src/database/migrations/043_bounded_404_log.ts
1822
- var _043_bounded_404_log_exports = /* @__PURE__ */ __exportAll({
1823
- down: () => down$8,
1824
- up: () => up$8
1825
- });
1826
- /**
1827
- * Migration: Bounded 404 logging
1828
- *
1829
- * Hardens `_dineway_404_log` against unauthenticated storage growth.
1830
- * Previously every 404 inserted a new row, so a requester could grow the
1831
- * table without bound by hitting unique URLs.
1832
- */
1833
- async function up$8(db) {
1834
- await db.schema.alterTable("_dineway_404_log").addColumn("hits", "integer", (col) => col.notNull().defaultTo(1)).execute();
1835
- await db.schema.alterTable("_dineway_404_log").addColumn("last_seen_at", "text").execute();
1836
- await sql`
1837
- UPDATE _dineway_404_log
1838
- SET last_seen_at = created_at
1839
- WHERE last_seen_at IS NULL
1840
- `.execute(db);
1841
- await sql`
1842
- WITH ranked AS (
1843
- SELECT
1844
- id,
1845
- path,
1846
- ROW_NUMBER() OVER (
1847
- PARTITION BY path
1848
- ORDER BY created_at DESC, id DESC
1849
- ) AS rn,
1850
- COUNT(*) OVER (PARTITION BY path) AS path_count,
1851
- MAX(created_at) OVER (PARTITION BY path) AS latest_created_at
1852
- FROM _dineway_404_log
1853
- )
1854
- UPDATE _dineway_404_log
1855
- SET
1856
- hits = (SELECT path_count FROM ranked WHERE ranked.id = _dineway_404_log.id),
1857
- last_seen_at = (SELECT latest_created_at FROM ranked WHERE ranked.id = _dineway_404_log.id)
1858
- WHERE id IN (SELECT id FROM ranked WHERE rn = 1)
1859
- `.execute(db);
1860
- await sql`
1861
- DELETE FROM _dineway_404_log
1862
- WHERE id IN (
1863
- SELECT id FROM (
1864
- SELECT
1865
- id,
1866
- ROW_NUMBER() OVER (
1867
- PARTITION BY path
1868
- ORDER BY created_at DESC, id DESC
1869
- ) AS rn
1870
- FROM _dineway_404_log
1871
- ) AS ranked
1872
- WHERE rn > 1
1873
- )
1874
- `.execute(db);
1875
- await db.schema.createIndex("idx_404_log_path_unique").on("_dineway_404_log").column("path").unique().execute();
1876
- await db.schema.dropIndex("idx_404_log_path").execute();
1877
- await db.schema.createIndex("idx_404_log_last_seen").on("_dineway_404_log").column("last_seen_at").execute();
1878
- }
1879
- async function down$8(db) {
1880
- await db.schema.dropIndex("idx_404_log_last_seen").execute();
1881
- await db.schema.dropIndex("idx_404_log_path_unique").execute();
1882
- await db.schema.createIndex("idx_404_log_path").on("_dineway_404_log").column("path").execute();
1883
- await db.schema.alterTable("_dineway_404_log").dropColumn("last_seen_at").execute();
1884
- await db.schema.alterTable("_dineway_404_log").dropColumn("hits").execute();
1885
- }
1886
-
1887
- //#endregion
1888
- //#region src/database/migrations/044_credential_algorithm.ts
1889
- var _044_credential_algorithm_exports = /* @__PURE__ */ __exportAll({
1890
- down: () => down$7,
1891
- up: () => up$7
1892
- });
1893
- async function up$7(db) {
1894
- if (!await columnExists(db, "credentials", "algorithm")) await db.schema.alterTable("credentials").addColumn("algorithm", "integer", (col) => col.notNull().defaultTo(-7)).execute();
1895
- }
1896
- async function down$7(db) {
1897
- if (await columnExists(db, "credentials", "algorithm")) await db.schema.alterTable("credentials").dropColumn("algorithm").execute();
1898
- }
1899
-
1900
- //#endregion
1901
- //#region src/database/migrations/045_i18n_menus_and_taxonomies.ts
1902
- var _045_i18n_menus_and_taxonomies_exports = /* @__PURE__ */ __exportAll({
1903
- down: () => down$6,
1904
- up: () => up$6
1905
- });
1906
- /**
1907
- * i18n for menus + taxonomies. Adds `locale` + `translation_group` to system
1908
- * tables and stores translation_groups (not row ids) in
1909
- * `_dineway_menu_items.reference_id` and `content_taxonomies.taxonomy_id`.
1910
- * Backfill locale and column DEFAULTs use the site's configured defaultLocale.
1911
- */
1912
- function getDefaultLocale$1() {
1913
- return getI18nConfig()?.defaultLocale ?? "en";
1914
- }
1915
- async function up$6(db) {
1916
- const defaultLocale = getDefaultLocale$1();
1917
- if (isSqlite(db)) {
1918
- await sql.raw(`PRAGMA foreign_keys = OFF`).execute(db);
1919
- try {
1920
- await rebuildMenus(db, defaultLocale);
1921
- await addItemColumns(db, defaultLocale);
1922
- await rebuildTaxonomies(db, defaultLocale);
1923
- await rebuildTaxonomyDefs(db, defaultLocale);
1924
- await rebuildContentTaxonomies(db);
1925
- await remapMenuItemRefs(db);
1926
- } finally {
1927
- await sql.raw(`PRAGMA foreign_keys = ON`).execute(db);
1928
- }
1929
- return;
1930
- }
1931
- await pgWiden(db, "_dineway_menus", ["name"], ["name", "locale"], defaultLocale);
1932
- await pgWiden(db, "_dineway_menu_items", null, null, defaultLocale);
1933
- await pgWiden(db, "taxonomies", ["name", "slug"], [
1934
- "name",
1935
- "slug",
1936
- "locale"
1937
- ], defaultLocale);
1938
- await pgWiden(db, "_dineway_taxonomy_defs", ["name"], ["name", "locale"], defaultLocale);
1939
- await pgRemapContentTaxonomies(db);
1940
- await remapMenuItemRefs(db);
1941
- }
1942
- async function rebuildMenus(db, defaultLocale) {
1943
- if (await hasColumn$1(db, "_dineway_menus", "locale")) return;
1944
- await sql.raw(`DROP TABLE IF EXISTS "_dineway_menus_new"`).execute(db);
1945
- await db.schema.createTable("_dineway_menus_new").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull()).addColumn("label", "text", (c) => c.notNull()).addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("locale", "text", (c) => c.notNull().defaultTo(defaultLocale)).addColumn("translation_group", "text").addUniqueConstraint("_dineway_menus_name_locale_unique", ["name", "locale"]).execute();
1946
- await sql`
1947
- INSERT INTO _dineway_menus_new (id, name, label, created_at, updated_at, locale, translation_group)
1948
- SELECT id, name, label, created_at, updated_at, ${defaultLocale}, id FROM _dineway_menus
1949
- `.execute(db);
1950
- await db.schema.dropTable("_dineway_menus").execute();
1951
- await sql`ALTER TABLE _dineway_menus_new RENAME TO _dineway_menus`.execute(db);
1952
- await db.schema.createIndex("idx__dineway_menus_locale").on("_dineway_menus").column("locale").execute();
1953
- await db.schema.createIndex("idx__dineway_menus_translation_group").on("_dineway_menus").column("translation_group").execute();
1954
- }
1955
- async function addItemColumns(db, defaultLocale) {
1956
- if (await hasColumn$1(db, "_dineway_menu_items", "locale")) return;
1957
- await db.schema.alterTable("_dineway_menu_items").addColumn("locale", "text", (c) => c.notNull().defaultTo(defaultLocale)).execute();
1958
- await db.schema.alterTable("_dineway_menu_items").addColumn("translation_group", "text").execute();
1959
- await sql`UPDATE _dineway_menu_items SET translation_group = id`.execute(db);
1960
- await db.schema.createIndex("idx__dineway_menu_items_locale").on("_dineway_menu_items").column("locale").execute();
1961
- await db.schema.createIndex("idx__dineway_menu_items_translation_group").on("_dineway_menu_items").column("translation_group").execute();
1962
- }
1963
- async function rebuildTaxonomies(db, defaultLocale) {
1964
- if (await hasColumn$1(db, "taxonomies", "locale")) return;
1965
- await sql.raw(`DROP TABLE IF EXISTS "taxonomies_new"`).execute(db);
1966
- await sql`DROP INDEX IF EXISTS idx_taxonomies_name`.execute(db);
1967
- await db.schema.createTable("taxonomies_new").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull()).addColumn("slug", "text", (c) => c.notNull()).addColumn("label", "text", (c) => c.notNull()).addColumn("parent_id", "text").addColumn("data", "text").addColumn("locale", "text", (c) => c.notNull().defaultTo(defaultLocale)).addColumn("translation_group", "text").addUniqueConstraint("taxonomies_name_slug_locale_unique", [
1968
- "name",
1969
- "slug",
1970
- "locale"
1971
- ]).addForeignKeyConstraint("taxonomies_parent_fk", ["parent_id"], "taxonomies", ["id"], (cb) => cb.onDelete("set null")).execute();
1972
- await sql`
1973
- INSERT INTO taxonomies_new (id, name, slug, label, parent_id, data, locale, translation_group)
1974
- SELECT id, name, slug, label, parent_id, data, ${defaultLocale}, id FROM taxonomies
1975
- `.execute(db);
1976
- await db.schema.dropTable("taxonomies").execute();
1977
- await sql`ALTER TABLE taxonomies_new RENAME TO taxonomies`.execute(db);
1978
- await db.schema.createIndex("idx_taxonomies_name").on("taxonomies").column("name").execute();
1979
- await db.schema.createIndex("idx_taxonomies_locale").on("taxonomies").column("locale").execute();
1980
- await db.schema.createIndex("idx_taxonomies_translation_group").on("taxonomies").column("translation_group").execute();
1981
- }
1982
- async function rebuildTaxonomyDefs(db, defaultLocale) {
1983
- if (await hasColumn$1(db, "_dineway_taxonomy_defs", "locale")) return;
1984
- await sql.raw(`DROP TABLE IF EXISTS "_dineway_taxonomy_defs_new"`).execute(db);
1985
- await db.schema.createTable("_dineway_taxonomy_defs_new").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull()).addColumn("label", "text", (c) => c.notNull()).addColumn("label_singular", "text").addColumn("hierarchical", "integer", (c) => c.defaultTo(0)).addColumn("collections", "text").addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("locale", "text", (c) => c.notNull().defaultTo(defaultLocale)).addColumn("translation_group", "text").addUniqueConstraint("_dineway_taxonomy_defs_name_locale_unique", ["name", "locale"]).execute();
1986
- await sql`
1987
- INSERT INTO _dineway_taxonomy_defs_new
1988
- (id, name, label, label_singular, hierarchical, collections, created_at, locale, translation_group)
1989
- SELECT id, name, label, label_singular, hierarchical, collections, created_at, ${defaultLocale}, id
1990
- FROM _dineway_taxonomy_defs
1991
- `.execute(db);
1992
- await db.schema.dropTable("_dineway_taxonomy_defs").execute();
1993
- await sql`ALTER TABLE _dineway_taxonomy_defs_new RENAME TO _dineway_taxonomy_defs`.execute(db);
1994
- await db.schema.createIndex("idx__dineway_taxonomy_defs_locale").on("_dineway_taxonomy_defs").column("locale").execute();
1995
- await db.schema.createIndex("idx__dineway_taxonomy_defs_translation_group").on("_dineway_taxonomy_defs").column("translation_group").execute();
1996
- }
1997
- async function rebuildContentTaxonomies(db) {
1998
- if ((await sql`PRAGMA foreign_key_list(content_taxonomies)`.execute(db)).rows.length === 0) return;
1999
- await sql.raw(`DROP TABLE IF EXISTS "content_taxonomies_new"`).execute(db);
2000
- await db.schema.createTable("content_taxonomies_new").addColumn("collection", "text", (c) => c.notNull()).addColumn("entry_id", "text", (c) => c.notNull()).addColumn("taxonomy_id", "text", (c) => c.notNull()).addPrimaryKeyConstraint("content_taxonomies_pk", [
2001
- "collection",
2002
- "entry_id",
2003
- "taxonomy_id"
2004
- ]).execute();
2005
- await sql`
2006
- INSERT OR IGNORE INTO content_taxonomies_new (collection, entry_id, taxonomy_id)
2007
- SELECT ct.collection, ct.entry_id, COALESCE(
2008
- (SELECT t.translation_group FROM taxonomies t WHERE t.id = ct.taxonomy_id),
2009
- ct.taxonomy_id
2010
- )
2011
- FROM content_taxonomies ct
2012
- `.execute(db);
2013
- await db.schema.dropTable("content_taxonomies").execute();
2014
- await sql`ALTER TABLE content_taxonomies_new RENAME TO content_taxonomies`.execute(db);
2015
- await db.schema.createIndex("idx_content_taxonomies_term").on("content_taxonomies").column("taxonomy_id").execute();
2016
- }
2017
- async function remapMenuItemRefs(db) {
2018
- const collections = await sql`SELECT slug FROM _dineway_collections`.execute(db);
2019
- for (const { slug } of collections.rows) {
2020
- validateIdentifier(slug, "collection slug");
2021
- const ec = sql.ref(`ec_${slug}`);
2022
- await sql`
2023
- UPDATE _dineway_menu_items SET reference_id = (
2024
- SELECT translation_group FROM ${ec} WHERE ${ec}.id = _dineway_menu_items.reference_id
2025
- )
2026
- WHERE reference_collection = ${slug} AND reference_id IS NOT NULL
2027
- AND EXISTS (SELECT 1 FROM ${ec} WHERE ${ec}.id = _dineway_menu_items.reference_id)
2028
- `.execute(db);
2029
- }
2030
- await sql`
2031
- UPDATE _dineway_menu_items SET reference_id = (
2032
- SELECT translation_group FROM taxonomies WHERE taxonomies.id = _dineway_menu_items.reference_id
2033
- )
2034
- WHERE type = 'taxonomy' AND reference_id IS NOT NULL
2035
- AND EXISTS (SELECT 1 FROM taxonomies WHERE taxonomies.id = _dineway_menu_items.reference_id)
2036
- `.execute(db);
2037
- }
2038
- async function pgWiden(db, table, oldCols, newCols, defaultLocale) {
2039
- validateSystemIdent(table);
2040
- const ref = sql.ref(table);
2041
- await sql`ALTER TABLE ${ref} ADD COLUMN IF NOT EXISTS locale TEXT NOT NULL DEFAULT ${sql.lit(defaultLocale)}`.execute(db);
2042
- await sql`ALTER TABLE ${ref} ADD COLUMN IF NOT EXISTS translation_group TEXT`.execute(db);
2043
- await sql`UPDATE ${ref} SET translation_group = id WHERE translation_group IS NULL`.execute(db);
2044
- await sql`CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table}_locale`)} ON ${ref} (locale)`.execute(db);
2045
- await sql`
2046
- CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table}_translation_group`)} ON ${ref} (translation_group)
2047
- `.execute(db);
2048
- if (!oldCols || !newCols) return;
2049
- for (const c of [...oldCols, ...newCols]) validateSystemIdent(c);
2050
- const cons = await sql`
2051
- SELECT conname FROM pg_constraint c
2052
- WHERE c.conrelid = ${table}::regclass AND c.contype = 'u'
2053
- AND array_length(c.conkey, 1) = ${oldCols.length}
2054
- AND (
2055
- SELECT array_agg(a.attname ORDER BY pos.ord)
2056
- FROM unnest(c.conkey) WITH ORDINALITY AS pos(attnum, ord)
2057
- JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = pos.attnum
2058
- )::text[] = ${oldCols}::text[]
2059
- `.execute(db);
2060
- for (const c of cons.rows) await sql`ALTER TABLE ${ref} DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(db);
2061
- const cols = sql.join(newCols.map((c) => sql.ref(c)), sql`, `);
2062
- await sql`
2063
- ALTER TABLE ${ref}
2064
- ADD CONSTRAINT ${sql.ref(`${table}_${newCols.join("_")}_unique`)} UNIQUE (${cols})
2065
- `.execute(db);
2066
- }
2067
- async function pgRemapContentTaxonomies(db) {
2068
- const fks = await sql`
2069
- SELECT conname FROM pg_constraint
2070
- WHERE conrelid = 'content_taxonomies'::regclass AND contype = 'f'
2071
- `.execute(db);
2072
- for (const c of fks.rows) await sql`ALTER TABLE content_taxonomies DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(db);
2073
- await sql`
2074
- UPDATE content_taxonomies SET taxonomy_id = t.translation_group
2075
- FROM taxonomies t WHERE t.id = content_taxonomies.taxonomy_id
2076
- `.execute(db);
2077
- }
2078
- async function hasColumn$1(db, table, column) {
2079
- return (await sql`PRAGMA table_info(${sql.ref(table)})`.execute(db)).rows.some((r) => r.name === column);
2080
- }
2081
- const SYSTEM_IDENT = /^[_a-z][a-z0-9_]*$/;
2082
- function validateSystemIdent(name) {
2083
- if (!SYSTEM_IDENT.test(name)) throw new Error(`Invalid identifier: "${name}"`);
2084
- }
2085
- /**
2086
- * down() is destructive on multi-locale installs (dropping `locale` collapses
2087
- * translated rows onto an ambiguous unique key). Refuse to run when any row
2088
- * sits at a locale other than the configured defaultLocale.
2089
- */
2090
- async function assertSingleLocale$1(db, defaultLocale) {
2091
- for (const table of [
2092
- "_dineway_menus",
2093
- "_dineway_menu_items",
2094
- "taxonomies",
2095
- "_dineway_taxonomy_defs"
2096
- ]) {
2097
- validateSystemIdent(table);
2098
- const result = await sql`
2099
- SELECT COUNT(*) AS count FROM ${sql.ref(table)} WHERE locale != ${defaultLocale}
2100
- `.execute(db);
2101
- const count = Number(result.rows[0]?.count ?? 0);
2102
- if (count > 0) throw new Error(`Cannot revert migration 045_i18n_menus_and_taxonomies: ${count} row(s) in "${table}" use a non-default locale (defaultLocale="${defaultLocale}"). Reverting would drop them silently. Export translations first (or delete them) and re-run the rollback. See packages/core/src/database/migrations/045_i18n_menus_and_taxonomies.ts.`);
2103
- }
2104
- }
2105
- async function down$6(db) {
2106
- const defaultLocale = getDefaultLocale$1();
2107
- await assertSingleLocale$1(db, defaultLocale);
2108
- const widenedTables = [
2109
- "_dineway_menus",
2110
- "_dineway_menu_items",
2111
- "taxonomies",
2112
- "_dineway_taxonomy_defs"
2113
- ];
2114
- if (isSqlite(db)) {
2115
- await sql.raw(`PRAGMA foreign_keys = OFF`).execute(db);
2116
- try {
2117
- for (const t of widenedTables) {
2118
- await sql.raw(`DROP INDEX IF EXISTS idx_${t}_locale`).execute(db);
2119
- await sql.raw(`DROP INDEX IF EXISTS idx_${t}_translation_group`).execute(db);
2120
- }
2121
- await rebuildContentTaxonomiesDown(db, defaultLocale);
2122
- await rebuildMenusDown(db);
2123
- await rebuildMenuItemsDown(db);
2124
- await rebuildTaxonomiesDown(db);
2125
- await rebuildTaxonomyDefsDown(db);
2126
- } finally {
2127
- await sql.raw(`PRAGMA foreign_keys = ON`).execute(db);
2128
- }
2129
- return;
2130
- }
2131
- for (const t of widenedTables) {
2132
- await sql.raw(`DROP INDEX IF EXISTS idx_${t}_locale`).execute(db);
2133
- await sql.raw(`DROP INDEX IF EXISTS idx_${t}_translation_group`).execute(db);
2134
- await sql.raw(`ALTER TABLE "${t}" DROP COLUMN IF EXISTS locale`).execute(db);
2135
- await sql.raw(`ALTER TABLE "${t}" DROP COLUMN IF EXISTS translation_group`).execute(db);
2136
- }
2137
- }
2138
- async function rebuildContentTaxonomiesDown(db, defaultLocale) {
2139
- await sql.raw(`DROP TABLE IF EXISTS "content_taxonomies_new"`).execute(db);
2140
- await db.schema.createTable("content_taxonomies_new").addColumn("collection", "text", (c) => c.notNull()).addColumn("entry_id", "text", (c) => c.notNull()).addColumn("taxonomy_id", "text", (c) => c.notNull()).addPrimaryKeyConstraint("content_taxonomies_pk", [
2141
- "collection",
2142
- "entry_id",
2143
- "taxonomy_id"
2144
- ]).addForeignKeyConstraint("content_taxonomies_taxonomy_fk", ["taxonomy_id"], "taxonomies", ["id"], (cb) => cb.onDelete("cascade")).execute();
2145
- await sql`
2146
- INSERT OR IGNORE INTO content_taxonomies_new (collection, entry_id, taxonomy_id)
2147
- SELECT ct.collection, ct.entry_id, COALESCE(
2148
- (SELECT t.id FROM taxonomies t WHERE t.translation_group = ct.taxonomy_id AND t.locale = ${defaultLocale}),
2149
- ct.taxonomy_id
2150
- )
2151
- FROM content_taxonomies ct
2152
- `.execute(db);
2153
- await db.schema.dropTable("content_taxonomies").execute();
2154
- await sql`ALTER TABLE content_taxonomies_new RENAME TO content_taxonomies`.execute(db);
2155
- await db.schema.createIndex("idx_content_taxonomies_term").on("content_taxonomies").column("taxonomy_id").execute();
2156
- }
2157
- async function rebuildMenusDown(db) {
2158
- await sql.raw(`DROP TABLE IF EXISTS "_dineway_menus_old"`).execute(db);
2159
- await db.schema.createTable("_dineway_menus_old").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull().unique()).addColumn("label", "text", (c) => c.notNull()).addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (c) => c.defaultTo(currentTimestamp(db))).execute();
2160
- await sql`
2161
- INSERT INTO _dineway_menus_old (id, name, label, created_at, updated_at)
2162
- SELECT id, name, label, created_at, updated_at FROM _dineway_menus
2163
- `.execute(db);
2164
- await db.schema.dropTable("_dineway_menus").execute();
2165
- await sql`ALTER TABLE _dineway_menus_old RENAME TO _dineway_menus`.execute(db);
2166
- }
2167
- async function rebuildMenuItemsDown(db) {
2168
- await sql.raw(`ALTER TABLE _dineway_menu_items DROP COLUMN locale`).execute(db);
2169
- await sql.raw(`ALTER TABLE _dineway_menu_items DROP COLUMN translation_group`).execute(db);
2170
- }
2171
- async function rebuildTaxonomiesDown(db) {
2172
- await sql.raw(`DROP TABLE IF EXISTS "taxonomies_old"`).execute(db);
2173
- await db.schema.createTable("taxonomies_old").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull()).addColumn("slug", "text", (c) => c.notNull()).addColumn("label", "text", (c) => c.notNull()).addColumn("parent_id", "text").addColumn("data", "text").addUniqueConstraint("taxonomies_name_slug_unique", ["name", "slug"]).addForeignKeyConstraint("taxonomies_parent_fk", ["parent_id"], "taxonomies_old", ["id"], (cb) => cb.onDelete("set null")).execute();
2174
- await sql`
2175
- INSERT INTO taxonomies_old (id, name, slug, label, parent_id, data)
2176
- SELECT id, name, slug, label, parent_id, data FROM taxonomies
2177
- `.execute(db);
2178
- await db.schema.dropTable("taxonomies").execute();
2179
- await sql`ALTER TABLE taxonomies_old RENAME TO taxonomies`.execute(db);
2180
- await db.schema.createIndex("idx_taxonomies_name").on("taxonomies").column("name").execute();
2181
- }
2182
- async function rebuildTaxonomyDefsDown(db) {
2183
- await sql.raw(`DROP TABLE IF EXISTS "_dineway_taxonomy_defs_old"`).execute(db);
2184
- await db.schema.createTable("_dineway_taxonomy_defs_old").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull().unique()).addColumn("label", "text", (c) => c.notNull()).addColumn("label_singular", "text").addColumn("hierarchical", "integer", (c) => c.defaultTo(0)).addColumn("collections", "text").addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).execute();
2185
- await sql`
2186
- INSERT INTO _dineway_taxonomy_defs_old
2187
- (id, name, label, label_singular, hierarchical, collections, created_at)
2188
- SELECT id, name, label, label_singular, hierarchical, collections, created_at
2189
- FROM _dineway_taxonomy_defs
2190
- `.execute(db);
2191
- await db.schema.dropTable("_dineway_taxonomy_defs").execute();
2192
- await sql`ALTER TABLE _dineway_taxonomy_defs_old RENAME TO _dineway_taxonomy_defs`.execute(db);
2193
- }
2194
-
2195
- //#endregion
2196
- //#region src/database/migrations/046_astro_sessions.ts
2197
- var _046_astro_sessions_exports = /* @__PURE__ */ __exportAll({
2198
- down: () => down$5,
2199
- up: () => up$5
2200
- });
2201
- async function up$5(db) {
2202
- await db.schema.createTable("_dineway_sessions").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("value", "text", (col) => col.notNull()).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2203
- }
2204
- async function down$5(db) {
2205
- await db.schema.dropTable("_dineway_sessions").ifExists().execute();
2206
- }
2207
-
2208
- //#endregion
2209
- //#region src/database/migrations/047_content_locale_list_index.ts
2210
- var _047_content_locale_list_index_exports = /* @__PURE__ */ __exportAll({
2211
- down: () => down$4,
2212
- up: () => up$4
2213
- });
2214
- /**
2215
- * Add locale-aware composite indexes for content list queries.
2216
- *
2217
- * When i18n is enabled the admin content list filters by locale and orders by
2218
- * updated_at/created_at. The existing composite indexes cover the ordered list
2219
- * path but omit locale, so locale-filtered lists cannot be served by one index
2220
- * on large tables.
2221
- */
2222
- async function up$4(db) {
2223
- const tableNames = await listTablesLike(db, "ec_%");
2224
- for (const tableName of tableNames) {
2225
- await sql`
2226
- CREATE INDEX IF NOT EXISTS ${sql.ref(contentLocaleUpdatedIndexName(tableName))}
2227
- ON ${sql.ref(tableName)} (deleted_at, locale, updated_at DESC, id DESC)
2228
- `.execute(db);
2229
- await sql`
2230
- CREATE INDEX IF NOT EXISTS ${sql.ref(contentLocaleCreatedIndexName(tableName))}
2231
- ON ${sql.ref(tableName)} (deleted_at, locale, created_at DESC, id DESC)
2232
- `.execute(db);
2233
- }
2234
- }
2235
- async function down$4(db) {
2236
- const tableNames = await listTablesLike(db, "ec_%");
2237
- for (const tableName of tableNames) {
2238
- await sql`DROP INDEX IF EXISTS ${sql.ref(contentLocaleUpdatedIndexName(tableName))}`.execute(db);
2239
- await sql`DROP INDEX IF EXISTS ${sql.ref(contentLocaleCreatedIndexName(tableName))}`.execute(db);
2240
- }
2241
- }
2242
-
2243
- //#endregion
2244
- //#region src/database/migrations/048_byline_i18n.ts
2245
- var _048_byline_i18n_exports = /* @__PURE__ */ __exportAll({
2246
- down: () => down$3,
2247
- up: () => up$3
2248
- });
2249
- /**
2250
- * Adds row-per-locale bylines.
2251
- *
2252
- * Bylines are stored as one row per locale variant. Rows that translate the
2253
- * same profile share `translation_group`, and content credit tables store that
2254
- * group rather than a specific byline row id. Rendering remains strict per
2255
- * locale: a credit appears only when a byline row exists for the entry locale.
2256
- */
2257
- function getDefaultLocale() {
2258
- return getI18nConfig()?.defaultLocale ?? "en";
2259
- }
2260
- async function up$3(db) {
2261
- const defaultLocale = getDefaultLocale();
2262
- if (isSqlite(db)) {
2263
- await rebuildContentBylines(db);
2264
- await rebuildBylines(db, defaultLocale);
2265
- await remapPrimaryBylineIds(db);
2266
- return;
2267
- }
2268
- await pgWidenBylines(db, defaultLocale);
2269
- await pgDropContentBylinesFk(db);
2270
- await remapPrimaryBylineIds(db);
2271
- }
2272
- async function rebuildContentBylines(db) {
2273
- if ((await sql`PRAGMA foreign_key_list(_dineway_content_bylines)`.execute(db)).rows.length === 0) return;
2274
- await sql.raw(`DROP TABLE IF EXISTS "_dineway_content_bylines_new"`).execute(db);
2275
- await db.schema.createTable("_dineway_content_bylines_new").addColumn("id", "text", (c) => c.primaryKey()).addColumn("collection_slug", "text", (c) => c.notNull()).addColumn("content_id", "text", (c) => c.notNull()).addColumn("byline_id", "text", (c) => c.notNull()).addColumn("sort_order", "integer", (c) => c.notNull().defaultTo(0)).addColumn("role_label", "text").addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addUniqueConstraint("content_bylines_unique", [
2276
- "collection_slug",
2277
- "content_id",
2278
- "byline_id"
2279
- ]).execute();
2280
- await sql`
2281
- INSERT INTO _dineway_content_bylines_new
2282
- (id, collection_slug, content_id, byline_id, sort_order, role_label, created_at)
2283
- SELECT id, collection_slug, content_id, byline_id, sort_order, role_label, created_at
2284
- FROM _dineway_content_bylines
2285
- `.execute(db);
2286
- await db.schema.dropTable("_dineway_content_bylines").execute();
2287
- await sql`
2288
- ALTER TABLE _dineway_content_bylines_new
2289
- RENAME TO _dineway_content_bylines
2290
- `.execute(db);
2291
- await db.schema.createIndex("idx_content_bylines_content").on("_dineway_content_bylines").columns([
2292
- "collection_slug",
2293
- "content_id",
2294
- "sort_order"
2295
- ]).execute();
2296
- await db.schema.createIndex("idx_content_bylines_byline").on("_dineway_content_bylines").column("byline_id").execute();
2297
- }
2298
- async function rebuildBylines(db, defaultLocale) {
2299
- if (await hasColumn(db, "_dineway_bylines", "locale")) return;
2300
- await sql.raw(`DROP TABLE IF EXISTS "_dineway_bylines_new"`).execute(db);
2301
- await db.schema.createTable("_dineway_bylines_new").addColumn("id", "text", (c) => c.primaryKey()).addColumn("slug", "text", (c) => c.notNull()).addColumn("display_name", "text", (c) => c.notNull()).addColumn("bio", "text").addColumn("avatar_media_id", "text", (c) => c.references("media.id").onDelete("set null")).addColumn("website_url", "text").addColumn("user_id", "text", (c) => c.references("users.id").onDelete("set null")).addColumn("is_guest", "integer", (c) => c.notNull().defaultTo(0)).addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("locale", "text", (c) => c.notNull().defaultTo(defaultLocale)).addColumn("translation_group", "text").addUniqueConstraint("_dineway_bylines_slug_locale_unique", ["slug", "locale"]).execute();
2302
- await sql`
2303
- INSERT INTO _dineway_bylines_new (
2304
- id, slug, display_name, bio, avatar_media_id, website_url,
2305
- user_id, is_guest, created_at, updated_at, locale, translation_group
2306
- )
2307
- SELECT
2308
- id, slug, display_name, bio, avatar_media_id, website_url,
2309
- user_id, is_guest, created_at, updated_at, ${defaultLocale}, id
2310
- FROM _dineway_bylines
2311
- `.execute(db);
2312
- await db.schema.dropTable("_dineway_bylines").execute();
2313
- await sql`ALTER TABLE _dineway_bylines_new RENAME TO _dineway_bylines`.execute(db);
2314
- await db.schema.createIndex("idx_bylines_slug").on("_dineway_bylines").column("slug").execute();
2315
- await db.schema.createIndex("idx_bylines_display_name").on("_dineway_bylines").column("display_name").execute();
2316
- await sql`
2317
- CREATE UNIQUE INDEX ${sql.ref("idx_bylines_user_id_locale_unique")}
2318
- ON ${sql.ref("_dineway_bylines")} (user_id, locale)
2319
- WHERE user_id IS NOT NULL
2320
- `.execute(db);
2321
- await db.schema.createIndex("idx__dineway_bylines_locale").on("_dineway_bylines").column("locale").execute();
2322
- await db.schema.createIndex("idx__dineway_bylines_translation_group").on("_dineway_bylines").column("translation_group").execute();
2323
- await sql`
2324
- CREATE UNIQUE INDEX ${sql.ref("idx_bylines_group_locale_unique")}
2325
- ON ${sql.ref("_dineway_bylines")} (translation_group, locale)
2326
- WHERE translation_group IS NOT NULL
2327
- `.execute(db);
2328
- }
2329
- async function remapPrimaryBylineIds(db) {
2330
- const collections = await listTablesLike(db, "ec_%");
2331
- for (const table of collections) {
2332
- validateIdentifier(table, "content table");
2333
- await sql`
2334
- UPDATE ${sql.ref(table)}
2335
- SET primary_byline_id = (
2336
- SELECT translation_group FROM _dineway_bylines
2337
- WHERE _dineway_bylines.id = ${sql.ref(table)}.primary_byline_id
2338
- )
2339
- WHERE primary_byline_id IS NOT NULL
2340
- AND EXISTS (
2341
- SELECT 1 FROM _dineway_bylines
2342
- WHERE _dineway_bylines.id = ${sql.ref(table)}.primary_byline_id
2343
- )
2344
- `.execute(db);
2345
- }
2346
- }
2347
- async function pgWidenBylines(db, defaultLocale) {
2348
- const ref = sql.ref("_dineway_bylines");
2349
- await sql`
2350
- ALTER TABLE ${ref}
2351
- ADD COLUMN IF NOT EXISTS locale TEXT NOT NULL DEFAULT ${sql.lit(defaultLocale)}
2352
- `.execute(db);
2353
- await sql`ALTER TABLE ${ref} ADD COLUMN IF NOT EXISTS translation_group TEXT`.execute(db);
2354
- await sql`UPDATE ${ref} SET translation_group = id WHERE translation_group IS NULL`.execute(db);
2355
- await sql`
2356
- CREATE INDEX IF NOT EXISTS ${sql.ref("idx__dineway_bylines_locale")}
2357
- ON ${ref} (locale)
2358
- `.execute(db);
2359
- await sql`
2360
- CREATE INDEX IF NOT EXISTS ${sql.ref("idx__dineway_bylines_translation_group")}
2361
- ON ${ref} (translation_group)
2362
- `.execute(db);
2363
- const slugConstraints = await sql`
2364
- SELECT conname FROM pg_constraint c
2365
- WHERE c.conrelid = '_dineway_bylines'::regclass AND c.contype = 'u'
2366
- AND array_length(c.conkey, 1) = 1
2367
- AND (
2368
- SELECT array_agg(a.attname ORDER BY pos.ord)
2369
- FROM unnest(c.conkey) WITH ORDINALITY AS pos(attnum, ord)
2370
- JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = pos.attnum
2371
- )::text[] = ARRAY['slug']
2372
- `.execute(db);
2373
- for (const constraint of slugConstraints.rows) await sql`ALTER TABLE ${ref} DROP CONSTRAINT ${sql.ref(constraint.conname)}`.execute(db);
2374
- await sql`
2375
- ALTER TABLE ${ref}
2376
- ADD CONSTRAINT _dineway_bylines_slug_locale_unique UNIQUE (slug, locale)
2377
- `.execute(db);
2378
- await sql`DROP INDEX IF EXISTS idx_bylines_user_id_unique`.execute(db);
2379
- await sql`
2380
- CREATE UNIQUE INDEX IF NOT EXISTS ${sql.ref("idx_bylines_user_id_locale_unique")}
2381
- ON ${ref} (user_id, locale) WHERE user_id IS NOT NULL
2382
- `.execute(db);
2383
- await sql`
2384
- CREATE UNIQUE INDEX IF NOT EXISTS ${sql.ref("idx_bylines_group_locale_unique")}
2385
- ON ${ref} (translation_group, locale) WHERE translation_group IS NOT NULL
2386
- `.execute(db);
2387
- }
2388
- async function pgDropContentBylinesFk(db) {
2389
- const fks = await sql`
2390
- SELECT conname FROM pg_constraint
2391
- WHERE conrelid = '_dineway_content_bylines'::regclass AND contype = 'f'
2392
- `.execute(db);
2393
- for (const fk of fks.rows) await sql`
2394
- ALTER TABLE _dineway_content_bylines
2395
- DROP CONSTRAINT ${sql.ref(fk.conname)}
2396
- `.execute(db);
2397
- }
2398
- async function hasColumn(db, table, column) {
2399
- return (await sql`PRAGMA table_info(${sql.ref(table)})`.execute(db)).rows.some((r) => r.name === column);
2400
- }
2401
- async function assertContentBylinesResolve(db, defaultLocale) {
2402
- const result = await sql`
2403
- SELECT COUNT(*) AS count FROM _dineway_content_bylines cb
2404
- WHERE NOT EXISTS (
2405
- SELECT 1 FROM _dineway_bylines b
2406
- WHERE b.translation_group = cb.byline_id AND b.locale = ${defaultLocale}
2407
- )
2408
- `.execute(db);
2409
- const count = Number(result.rows[0]?.count ?? 0);
2410
- if (count > 0) throw new Error(`Cannot revert migration 048_byline_i18n: ${count} row(s) in "_dineway_content_bylines" reference a translation group with no "_dineway_bylines" row at locale="${defaultLocale}".`);
2411
- }
2412
- async function assertSingleLocale(db, defaultLocale) {
2413
- const result = await sql`
2414
- SELECT COUNT(*) AS count FROM _dineway_bylines WHERE locale != ${defaultLocale}
2415
- `.execute(db);
2416
- const count = Number(result.rows[0]?.count ?? 0);
2417
- if (count > 0) throw new Error(`Cannot revert migration 048_byline_i18n: ${count} row(s) in "_dineway_bylines" use a non-default locale (defaultLocale="${defaultLocale}").`);
2418
- }
2419
- async function down$3(db) {
2420
- const defaultLocale = getDefaultLocale();
2421
- await assertSingleLocale(db, defaultLocale);
2422
- await assertContentBylinesResolve(db, defaultLocale);
2423
- if (isSqlite(db)) {
2424
- await sql.raw(`DROP INDEX IF EXISTS idx__dineway_bylines_locale`).execute(db);
2425
- await sql.raw(`DROP INDEX IF EXISTS idx__dineway_bylines_translation_group`).execute(db);
2426
- await sql.raw(`DROP INDEX IF EXISTS idx_bylines_user_id_locale_unique`).execute(db);
2427
- await sql.raw(`DROP INDEX IF EXISTS idx_bylines_group_locale_unique`).execute(db);
2428
- await remapPrimaryBylineIdsDown(db, defaultLocale);
2429
- await remapContentBylinesDown(db, defaultLocale);
2430
- await rebuildBylinesDown(db);
2431
- await restoreContentBylinesFk(db);
2432
- return;
2433
- }
2434
- await remapPrimaryBylineIdsDown(db, defaultLocale);
2435
- await sql`
2436
- UPDATE _dineway_content_bylines
2437
- SET byline_id = COALESCE(
2438
- (SELECT b.id FROM _dineway_bylines b
2439
- WHERE b.translation_group = _dineway_content_bylines.byline_id
2440
- AND b.locale = ${defaultLocale}),
2441
- byline_id
2442
- )
2443
- `.execute(db);
2444
- await sql.raw(`DROP INDEX IF EXISTS idx__dineway_bylines_locale`).execute(db);
2445
- await sql.raw(`DROP INDEX IF EXISTS idx__dineway_bylines_translation_group`).execute(db);
2446
- await sql.raw(`DROP INDEX IF EXISTS idx_bylines_user_id_locale_unique`).execute(db);
2447
- await sql.raw(`DROP INDEX IF EXISTS idx_bylines_group_locale_unique`).execute(db);
2448
- await sql.raw(`ALTER TABLE "_dineway_bylines" DROP CONSTRAINT IF EXISTS _dineway_bylines_slug_locale_unique`).execute(db);
2449
- await sql.raw(`ALTER TABLE "_dineway_bylines" DROP COLUMN IF EXISTS locale`).execute(db);
2450
- await sql.raw(`ALTER TABLE "_dineway_bylines" DROP COLUMN IF EXISTS translation_group`).execute(db);
2451
- await sql.raw(`ALTER TABLE "_dineway_bylines" ADD CONSTRAINT _dineway_bylines_slug_unique UNIQUE (slug)`).execute(db);
2452
- await sql`
2453
- CREATE UNIQUE INDEX IF NOT EXISTS ${sql.ref("idx_bylines_user_id_unique")}
2454
- ON _dineway_bylines (user_id) WHERE user_id IS NOT NULL
2455
- `.execute(db);
2456
- await sql`
2457
- ALTER TABLE _dineway_content_bylines
2458
- ADD CONSTRAINT _dineway_content_bylines_byline_fk
2459
- FOREIGN KEY (byline_id) REFERENCES _dineway_bylines(id) ON DELETE CASCADE
2460
- `.execute(db);
2461
- }
2462
- async function remapPrimaryBylineIdsDown(db, defaultLocale) {
2463
- const collections = await listTablesLike(db, "ec_%");
2464
- for (const table of collections) {
2465
- validateIdentifier(table, "content table");
2466
- await sql`
2467
- UPDATE ${sql.ref(table)}
2468
- SET primary_byline_id = COALESCE(
2469
- (SELECT b.id FROM _dineway_bylines b
2470
- WHERE b.translation_group = ${sql.ref(table)}.primary_byline_id
2471
- AND b.locale = ${defaultLocale}),
2472
- primary_byline_id
2473
- )
2474
- WHERE primary_byline_id IS NOT NULL
2475
- `.execute(db);
2476
- }
2477
- }
2478
- async function remapContentBylinesDown(db, defaultLocale) {
2479
- await sql`
2480
- UPDATE _dineway_content_bylines
2481
- SET byline_id = COALESCE(
2482
- (SELECT b.id FROM _dineway_bylines b
2483
- WHERE b.translation_group = _dineway_content_bylines.byline_id
2484
- AND b.locale = ${defaultLocale}),
2485
- byline_id
2486
- )
2487
- `.execute(db);
2488
- }
2489
- async function rebuildBylinesDown(db) {
2490
- await sql.raw(`DROP TABLE IF EXISTS "_dineway_bylines_old"`).execute(db);
2491
- await db.schema.createTable("_dineway_bylines_old").addColumn("id", "text", (c) => c.primaryKey()).addColumn("slug", "text", (c) => c.notNull().unique()).addColumn("display_name", "text", (c) => c.notNull()).addColumn("bio", "text").addColumn("avatar_media_id", "text", (c) => c.references("media.id").onDelete("set null")).addColumn("website_url", "text").addColumn("user_id", "text", (c) => c.references("users.id").onDelete("set null")).addColumn("is_guest", "integer", (c) => c.notNull().defaultTo(0)).addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (c) => c.defaultTo(currentTimestamp(db))).execute();
2492
- await sql`
2493
- INSERT INTO _dineway_bylines_old (
2494
- id, slug, display_name, bio, avatar_media_id, website_url,
2495
- user_id, is_guest, created_at, updated_at
2496
- )
2497
- SELECT
2498
- id, slug, display_name, bio, avatar_media_id, website_url,
2499
- user_id, is_guest, created_at, updated_at
2500
- FROM _dineway_bylines
2501
- `.execute(db);
2502
- await db.schema.dropTable("_dineway_bylines").execute();
2503
- await sql`ALTER TABLE _dineway_bylines_old RENAME TO _dineway_bylines`.execute(db);
2504
- await db.schema.createIndex("idx_bylines_slug").on("_dineway_bylines").column("slug").execute();
2505
- await db.schema.createIndex("idx_bylines_display_name").on("_dineway_bylines").column("display_name").execute();
2506
- await sql`
2507
- CREATE UNIQUE INDEX ${sql.ref("idx_bylines_user_id_unique")}
2508
- ON ${sql.ref("_dineway_bylines")} (user_id)
2509
- WHERE user_id IS NOT NULL
2510
- `.execute(db);
2511
- }
2512
- async function restoreContentBylinesFk(db) {
2513
- await sql.raw(`DROP TABLE IF EXISTS "_dineway_content_bylines_old"`).execute(db);
2514
- await db.schema.createTable("_dineway_content_bylines_old").addColumn("id", "text", (c) => c.primaryKey()).addColumn("collection_slug", "text", (c) => c.notNull()).addColumn("content_id", "text", (c) => c.notNull()).addColumn("byline_id", "text", (c) => c.notNull().references("_dineway_bylines.id").onDelete("cascade")).addColumn("sort_order", "integer", (c) => c.notNull().defaultTo(0)).addColumn("role_label", "text").addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addUniqueConstraint("content_bylines_unique", [
2515
- "collection_slug",
2516
- "content_id",
2517
- "byline_id"
2518
- ]).execute();
2519
- await sql`
2520
- INSERT INTO _dineway_content_bylines_old
2521
- (id, collection_slug, content_id, byline_id, sort_order, role_label, created_at)
2522
- SELECT id, collection_slug, content_id, byline_id, sort_order, role_label, created_at
2523
- FROM _dineway_content_bylines
2524
- `.execute(db);
2525
- await db.schema.dropTable("_dineway_content_bylines").execute();
2526
- await sql`
2527
- ALTER TABLE _dineway_content_bylines_old
2528
- RENAME TO _dineway_content_bylines
2529
- `.execute(db);
2530
- await db.schema.createIndex("idx_content_bylines_content").on("_dineway_content_bylines").columns([
2531
- "collection_slug",
2532
- "content_id",
2533
- "sort_order"
2534
- ]).execute();
2535
- await db.schema.createIndex("idx_content_bylines_byline").on("_dineway_content_bylines").column("byline_id").execute();
2536
- }
2537
-
2538
- //#endregion
2539
- //#region src/database/migrations/049_byline_fields.ts
2540
- var _049_byline_fields_exports = /* @__PURE__ */ __exportAll({
2541
- down: () => down$2,
2542
- up: () => up$2
2543
- });
2544
- async function up$2(db) {
2545
- await db.schema.createTable("_dineway_byline_fields").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("type", "text", (col) => col.notNull()).addColumn("required", "integer", (col) => col.notNull().defaultTo(0)).addColumn("translatable", "integer", (col) => col.notNull().defaultTo(1)).addColumn("validation", "text").addColumn("sort_order", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
2546
- await db.schema.createIndex("idx__dineway_byline_fields_sort_order").ifNotExists().on("_dineway_byline_fields").column("sort_order").execute();
2547
- await db.schema.createTable("_dineway_byline_field_values").ifNotExists().addColumn("byline_id", "text", (col) => col.notNull().references("_dineway_bylines.id").onDelete("cascade")).addColumn("field_id", "text", (col) => col.notNull().references("_dineway_byline_fields.id").onDelete("cascade")).addColumn("value", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addPrimaryKeyConstraint("_dineway_byline_field_values_pk", ["byline_id", "field_id"]).execute();
2548
- await db.schema.createIndex("idx__dineway_byline_field_values_byline").ifNotExists().on("_dineway_byline_field_values").column("byline_id").execute();
2549
- await db.schema.createIndex("idx__dineway_byline_field_values_field").ifNotExists().on("_dineway_byline_field_values").column("field_id").execute();
2550
- await db.schema.createTable("_dineway_byline_field_group_values").ifNotExists().addColumn("translation_group", "text", (col) => col.notNull()).addColumn("field_id", "text", (col) => col.notNull().references("_dineway_byline_fields.id").onDelete("cascade")).addColumn("value", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addPrimaryKeyConstraint("_dineway_byline_field_group_values_pk", ["translation_group", "field_id"]).execute();
2551
- await db.schema.createIndex("idx__dineway_byline_field_group_values_group").ifNotExists().on("_dineway_byline_field_group_values").column("translation_group").execute();
2552
- await db.schema.createIndex("idx__dineway_byline_field_group_values_field").ifNotExists().on("_dineway_byline_field_group_values").column("field_id").execute();
2553
- await sql`
2554
- INSERT INTO options (name, value) VALUES ('byline_fields_version', '0')
2555
- ON CONFLICT (name) DO NOTHING
2556
- `.execute(db);
2557
- }
2558
- async function down$2(db) {
2559
- await db.schema.dropTable("_dineway_byline_field_group_values").ifExists().execute();
2560
- await db.schema.dropTable("_dineway_byline_field_values").ifExists().execute();
2561
- await db.schema.dropTable("_dineway_byline_fields").ifExists().execute();
2562
- await sql`DELETE FROM options WHERE name = 'byline_fields_version'`.execute(db);
2563
- }
2564
-
2565
- //#endregion
2566
- //#region src/database/migrations/050_content_references.ts
2567
- var _050_content_references_exports = /* @__PURE__ */ __exportAll({
2568
- down: () => down$1,
2569
- up: () => up$1
2570
- });
2571
- /**
2572
- * Content references.
2573
- *
2574
- * `_dineway_relations` defines directed relation types between collections,
2575
- * with one localized row per locale. `_dineway_content_references` stores
2576
- * parent -> child edges keyed by translation groups so edges are locale-agnostic.
2577
- */
2578
- async function up$1(db) {
2579
- const defaultLocale = getI18nConfig()?.defaultLocale ?? "en";
2580
- await db.schema.createTable("_dineway_relations").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull()).addColumn("parent_collection", "text", (col) => col.notNull()).addColumn("child_collection", "text", (col) => col.notNull()).addColumn("parent_label", "text", (col) => col.notNull()).addColumn("child_label", "text", (col) => col.notNull()).addColumn("locale", "text", (col) => col.notNull().defaultTo(defaultLocale)).addColumn("translation_group", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addUniqueConstraint("_dineway_relations_name_locale_unique", ["name", "locale"]).execute();
2581
- await db.schema.createIndex("idx__dineway_relations_locale").ifNotExists().on("_dineway_relations").column("locale").execute();
2582
- await db.schema.createIndex("idx__dineway_relations_translation_group").ifNotExists().on("_dineway_relations").column("translation_group").execute();
2583
- await db.schema.createIndex("idx__dineway_relations_parent_collection").ifNotExists().on("_dineway_relations").column("parent_collection").execute();
2584
- await db.schema.createIndex("idx__dineway_relations_child_collection").ifNotExists().on("_dineway_relations").column("child_collection").execute();
2585
- await db.schema.createIndex("idx__dineway_relations_group_locale_unique").ifNotExists().unique().on("_dineway_relations").columns(["translation_group", "locale"]).execute();
2586
- await db.schema.createTable("_dineway_content_references").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("relation_group", "text", (col) => col.notNull()).addColumn("parent_group", "text", (col) => col.notNull()).addColumn("child_group", "text", (col) => col.notNull()).addColumn("sort_order", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addUniqueConstraint("_dineway_content_references_unique", [
2587
- "relation_group",
2588
- "parent_group",
2589
- "child_group"
2590
- ]).execute();
2591
- await db.schema.createIndex("idx__dineway_content_references_parent").ifNotExists().on("_dineway_content_references").columns([
2592
- "parent_group",
2593
- "relation_group",
2594
- "sort_order"
2595
- ]).execute();
2596
- await db.schema.createIndex("idx__dineway_content_references_child").ifNotExists().on("_dineway_content_references").columns(["child_group", "relation_group"]).execute();
2597
- await db.schema.createIndex("idx__dineway_content_references_relation").ifNotExists().on("_dineway_content_references").column("relation_group").execute();
2598
- }
2599
- async function down$1(db) {
2600
- await db.schema.dropTable("_dineway_content_references").ifExists().execute();
2601
- await db.schema.dropTable("_dineway_relations").ifExists().execute();
2602
- }
2603
-
2604
- //#endregion
2605
- //#region src/database/migrations/051_dineway_web_plugin.ts
2606
- var _051_dineway_web_plugin_exports = /* @__PURE__ */ __exportAll({
2607
- down: () => down,
2608
- up: () => up
2609
- });
2610
- const SESSION_STATUSES = [
2611
- "active",
2612
- "revoked",
2613
- "expired"
2614
- ];
2615
- const ONBOARDING_STATUSES = [
2616
- "started",
2617
- "checkout_required",
2618
- "checkout_pending",
2619
- "complete"
2620
- ];
2621
- const CHECKOUT_STATUSES = [
2622
- "created",
2623
- "succeeded",
2624
- "canceled",
2625
- "pending"
2626
- ];
2627
- const HANDOFF_STATUSES = [
2628
- "pending",
2629
- "consumed",
2630
- "expired",
2631
- "revoked"
2632
- ];
2633
- const HANDOFF_TARGETS = ["pricing", "onboarding"];
2634
- const HANDOFF_AUDIT_EVENTS = [
2635
- "created",
2636
- "consumed",
2637
- "expired",
2638
- "revoked"
2639
- ];
2640
- function enumValues(values) {
2641
- return sql.join(values.map((value) => sql.lit(value)), sql`, `);
2642
- }
2643
- async function up(db) {
2644
- const sessionStatuses = enumValues(SESSION_STATUSES);
2645
- const onboardingStatuses = enumValues(ONBOARDING_STATUSES);
2646
- const checkoutStatuses = enumValues(CHECKOUT_STATUSES);
2647
- const handoffStatuses = enumValues(HANDOFF_STATUSES);
2648
- const handoffTargets = enumValues(HANDOFF_TARGETS);
2649
- const handoffAuditEvents = enumValues(HANDOFF_AUDIT_EVENTS);
2650
- await db.schema.createTable("_dineway_dineway_web_sessions").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("forgeway_user_id", "text", (col) => col.notNull()).addColumn("email", "text").addColumn("encrypted_refresh_token", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().check(sql`status in (${sessionStatuses})`).defaultTo(sql`'active'`)).addColumn("expires_at", "text", (col) => col.notNull()).addColumn("last_seen_at", "text").addColumn("revoked_at", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2651
- await db.schema.createIndex("idx_dineway_web_sessions_user").ifNotExists().on("_dineway_dineway_web_sessions").column("forgeway_user_id").execute();
2652
- await db.schema.createIndex("idx_dineway_web_sessions_email").ifNotExists().on("_dineway_dineway_web_sessions").column("email").execute();
2653
- await db.schema.createIndex("idx_dineway_web_sessions_expires").ifNotExists().on("_dineway_dineway_web_sessions").column("expires_at").execute();
2654
- await db.schema.createIndex("idx_dineway_web_sessions_revoked").ifNotExists().on("_dineway_dineway_web_sessions").column("revoked_at").execute();
2655
- await db.schema.createTable("_dineway_dineway_web_onboarding").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("session_id", "text", (col) => col.notNull().unique()).addColumn("forgeway_user_id", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().check(sql`status in (${onboardingStatuses})`).defaultTo(sql`'started'`)).addColumn("current_step", "text", (col) => col.notNull()).addColumn("requested_plan_code", "text").addColumn("source", "text").addColumn("handoff_id", "text").addColumn("context_json", "text", (col) => col.notNull().defaultTo(sql`'{}'`)).addColumn("completed_at", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2656
- await db.schema.createIndex("idx_dineway_web_onboarding_session").ifNotExists().on("_dineway_dineway_web_onboarding").column("session_id").execute();
2657
- await db.schema.createIndex("idx_dineway_web_onboarding_user").ifNotExists().on("_dineway_dineway_web_onboarding").column("forgeway_user_id").execute();
2658
- await db.schema.createIndex("idx_dineway_web_onboarding_status").ifNotExists().on("_dineway_dineway_web_onboarding").column("status").execute();
2659
- await db.schema.createIndex("idx_dineway_web_onboarding_handoff").ifNotExists().on("_dineway_dineway_web_onboarding").column("handoff_id").execute();
2660
- await db.schema.createTable("_dineway_dineway_web_checkout_sessions").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("session_id", "text", (col) => col.notNull()).addColumn("forgeway_user_id", "text", (col) => col.notNull()).addColumn("plan_code", "text", (col) => col.notNull()).addColumn("forgeway_checkout_session_id", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().check(sql`status in (${checkoutStatuses})`).defaultTo(sql`'created'`)).addColumn("success_url", "text", (col) => col.notNull()).addColumn("cancel_url", "text", (col) => col.notNull()).addColumn("completed_at", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2661
- await db.schema.createIndex("idx_dineway_web_checkout_session").ifNotExists().on("_dineway_dineway_web_checkout_sessions").column("session_id").execute();
2662
- await db.schema.createIndex("idx_dineway_web_checkout_user").ifNotExists().on("_dineway_dineway_web_checkout_sessions").column("forgeway_user_id").execute();
2663
- await db.schema.createIndex("idx_dineway_web_checkout_status").ifNotExists().on("_dineway_dineway_web_checkout_sessions").column("status").execute();
2664
- await db.schema.createIndex("idx_dineway_web_checkout_forgeway").ifNotExists().unique().on("_dineway_dineway_web_checkout_sessions").column("forgeway_checkout_session_id").execute();
2665
- await db.schema.createTable("_dineway_dineway_web_account_projections").ifNotExists().addColumn("forgeway_user_id", "text", (col) => col.primaryKey()).addColumn("email", "text").addColumn("plan_code", "text", (col) => col.notNull()).addColumn("projection_json", "text", (col) => col.notNull()).addColumn("synced_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2666
- await db.schema.createIndex("idx_dineway_web_account_plan").ifNotExists().on("_dineway_dineway_web_account_projections").column("plan_code").execute();
2667
- await db.schema.createIndex("idx_dineway_web_account_synced").ifNotExists().on("_dineway_dineway_web_account_projections").column("synced_at").execute();
2668
- await db.schema.createTable("_dineway_dineway_web_billing_projections").ifNotExists().addColumn("forgeway_user_id", "text", (col) => col.primaryKey()).addColumn("plan_code", "text", (col) => col.notNull()).addColumn("projection_json", "text", (col) => col.notNull()).addColumn("synced_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2669
- await db.schema.createIndex("idx_dineway_web_billing_plan").ifNotExists().on("_dineway_dineway_web_billing_projections").column("plan_code").execute();
2670
- await db.schema.createIndex("idx_dineway_web_billing_synced").ifNotExists().on("_dineway_dineway_web_billing_projections").column("synced_at").execute();
2671
- await db.schema.createTable("_dineway_dineway_web_credits_projections").ifNotExists().addColumn("forgeway_user_id", "text", (col) => col.primaryKey()).addColumn("plan_code", "text", (col) => col.notNull()).addColumn("available_credits", "integer", (col) => col.notNull()).addColumn("projection_json", "text", (col) => col.notNull()).addColumn("synced_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2672
- await db.schema.createIndex("idx_dineway_web_credits_plan").ifNotExists().on("_dineway_dineway_web_credits_projections").column("plan_code").execute();
2673
- await db.schema.createIndex("idx_dineway_web_credits_synced").ifNotExists().on("_dineway_dineway_web_credits_projections").column("synced_at").execute();
2674
- await db.schema.createTable("_dineway_dineway_web_sites_projections").ifNotExists().addColumn("forgeway_user_id", "text", (col) => col.primaryKey()).addColumn("plan_code", "text", (col) => col.notNull()).addColumn("used_hosted_sites", "integer", (col) => col.notNull()).addColumn("hosted_site_limit", "integer").addColumn("projection_json", "text", (col) => col.notNull()).addColumn("synced_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2675
- await db.schema.createIndex("idx_dineway_web_sites_plan").ifNotExists().on("_dineway_dineway_web_sites_projections").column("plan_code").execute();
2676
- await db.schema.createIndex("idx_dineway_web_sites_synced").ifNotExists().on("_dineway_dineway_web_sites_projections").column("synced_at").execute();
2677
- await db.schema.createTable("_dineway_dineway_web_handoffs").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("token_hash", "text", (col) => col.notNull().unique()).addColumn("forgeway_user_id", "text", (col) => col.notNull()).addColumn("email", "text").addColumn("target", "text", (col) => col.notNull().check(sql`target in (${handoffTargets})`)).addColumn("reason", "text", (col) => col.notNull()).addColumn("context_json", "text", (col) => col.notNull().defaultTo(sql`'{}'`)).addColumn("status", "text", (col) => col.notNull().check(sql`status in (${handoffStatuses})`).defaultTo(sql`'pending'`)).addColumn("expires_at", "text", (col) => col.notNull()).addColumn("consumed_at", "text").addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2678
- await db.schema.createIndex("idx_dineway_web_handoff_hash").ifNotExists().unique().on("_dineway_dineway_web_handoffs").column("token_hash").execute();
2679
- await db.schema.createIndex("idx_dineway_web_handoff_status_expires").ifNotExists().on("_dineway_dineway_web_handoffs").columns(["status", "expires_at"]).execute();
2680
- await db.schema.createIndex("idx_dineway_web_handoff_user").ifNotExists().on("_dineway_dineway_web_handoffs").column("forgeway_user_id").execute();
2681
- await db.schema.createIndex("idx_dineway_web_handoff_target").ifNotExists().on("_dineway_dineway_web_handoffs").column("target").execute();
2682
- await db.schema.createTable("_dineway_dineway_web_handoff_audit").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("handoff_id", "text", (col) => col.notNull()).addColumn("event", "text", (col) => col.notNull().check(sql`event in (${handoffAuditEvents})`)).addColumn("actor_forgeway_user_id", "text").addColumn("metadata_json", "text", (col) => col.notNull().defaultTo(sql`'{}'`)).addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).execute();
2683
- await db.schema.createIndex("idx_dineway_web_handoff_audit_handoff").ifNotExists().on("_dineway_dineway_web_handoff_audit").column("handoff_id").execute();
2684
- await db.schema.createIndex("idx_dineway_web_handoff_audit_event").ifNotExists().on("_dineway_dineway_web_handoff_audit").column("event").execute();
2685
- }
2686
- async function down(db) {
2687
- await db.schema.dropTable("_dineway_dineway_web_handoff_audit").ifExists().execute();
2688
- await db.schema.dropTable("_dineway_dineway_web_handoffs").ifExists().execute();
2689
- await db.schema.dropTable("_dineway_dineway_web_sites_projections").ifExists().execute();
2690
- await db.schema.dropTable("_dineway_dineway_web_credits_projections").ifExists().execute();
2691
- await db.schema.dropTable("_dineway_dineway_web_billing_projections").ifExists().execute();
2692
- await db.schema.dropTable("_dineway_dineway_web_account_projections").ifExists().execute();
2693
- await db.schema.dropTable("_dineway_dineway_web_checkout_sessions").ifExists().execute();
2694
- await db.schema.dropTable("_dineway_dineway_web_onboarding").ifExists().execute();
2695
- await db.schema.dropTable("_dineway_dineway_web_sessions").ifExists().execute();
2696
- }
2697
-
2698
- //#endregion
2699
- //#region src/database/migrations/runner.ts
2700
- const MIGRATIONS = Object.freeze({
2701
- "001_initial": _001_initial_exports,
2702
- "002_media_status": _002_media_status_exports,
2703
- "003_schema_registry": _003_schema_registry_exports,
2704
- "004_plugins": _004_plugins_exports,
2705
- "005_menus": _005_menus_exports,
2706
- "006_taxonomy_defs": _006_taxonomy_defs_exports,
2707
- "007_widgets": _007_widgets_exports,
2708
- "008_auth": _008_auth_exports,
2709
- "009_user_disabled": _009_user_disabled_exports,
2710
- "011_sections": _011_sections_exports,
2711
- "012_search": _012_search_exports,
2712
- "013_scheduled_publishing": _013_scheduled_publishing_exports,
2713
- "014_draft_revisions": _014_draft_revisions_exports,
2714
- "015_indexes": _015_indexes_exports,
2715
- "016_api_tokens": _016_api_tokens_exports,
2716
- "017_authorization_codes": _017_authorization_codes_exports,
2717
- "018_seo": _018_seo_exports,
2718
- "019_i18n": _019_i18n_exports,
2719
- "020_collection_url_pattern": _020_collection_url_pattern_exports,
2720
- "021_remove_section_categories": _021_remove_section_categories_exports,
2721
- "022_marketplace_plugin_state": _022_marketplace_plugin_state_exports,
2722
- "023_plugin_metadata": _023_plugin_metadata_exports,
2723
- "024_media_placeholders": _024_media_placeholders_exports,
2724
- "025_oauth_clients": _025_oauth_clients_exports,
2725
- "026_cron_tasks": _026_cron_tasks_exports,
2726
- "027_comments": _027_comments_exports,
2727
- "028_drop_author_url": _028_drop_author_url_exports,
2728
- "029_redirects": _029_redirects_exports,
2729
- "030_widen_scheduled_index": _030_widen_scheduled_index_exports,
2730
- "031_bylines": _031_bylines_exports,
2731
- "032_rate_limits": _032_rate_limits_exports,
2732
- "033_optimize_content_indexes": _033_optimize_content_indexes_exports,
2733
- "034_published_at_index": _034_published_at_index_exports,
2734
- "035_backfill_url_patterns": _035_backfill_url_patterns_exports,
2735
- "036_activity_log": _036_activity_log_exports,
2736
- "037_context_entries": _037_context_entries_exports,
2737
- "038_review_requests": _038_review_requests_exports,
2738
- "039_entity_aliases": _039_entity_aliases_exports,
2739
- "040_handoff_snapshots": _040_handoff_snapshots_exports,
2740
- "041_assignments": _041_assignments_exports,
2741
- "042_hitl_requests": _042_hitl_requests_exports,
2742
- "043_bounded_404_log": _043_bounded_404_log_exports,
2743
- "044_credential_algorithm": _044_credential_algorithm_exports,
2744
- "045_i18n_menus_and_taxonomies": _045_i18n_menus_and_taxonomies_exports,
2745
- "046_astro_sessions": _046_astro_sessions_exports,
2746
- "047_content_locale_list_index": _047_content_locale_list_index_exports,
2747
- "048_byline_i18n": _048_byline_i18n_exports,
2748
- "049_byline_fields": _049_byline_fields_exports,
2749
- "050_content_references": _050_content_references_exports,
2750
- "051_dineway_web_plugin": _051_dineway_web_plugin_exports
2751
- });
2752
- const MIGRATION_NAMES = Object.freeze(Object.keys(MIGRATIONS).toSorted());
2753
- /** Total number of registered migrations. Exported for use in tests. */
2754
- const MIGRATION_COUNT = MIGRATION_NAMES.length;
2755
- function getMigrationFingerprint() {
2756
- const hash = createHash("sha256");
2757
- for (const name of MIGRATION_NAMES) {
2758
- hash.update(name);
2759
- hash.update("\0");
2760
- }
2761
- return {
2762
- migrationHash: `sha256:${hash.digest("hex")}`,
2763
- migrationCount: MIGRATION_COUNT
2764
- };
2765
- }
2766
- /**
2767
- * Migration provider that uses statically imported migrations.
2768
- * This approach works well with bundlers and avoids filesystem access.
2769
- */
2770
- var StaticMigrationProvider = class {
2771
- async getMigrations() {
2772
- return MIGRATIONS;
2773
- }
2774
- };
2775
- /** Custom migration table name */
2776
- const MIGRATION_TABLE = "_dineway_migrations";
2777
- const MIGRATION_LOCK_TABLE = "_dineway_migrations_lock";
2778
- /**
2779
- * Get migration status
2780
- */
2781
- async function getMigrationStatus(db) {
2782
- const migrations = await new Migrator({
2783
- db,
2784
- provider: new StaticMigrationProvider(),
2785
- migrationTableName: MIGRATION_TABLE,
2786
- migrationLockTableName: MIGRATION_LOCK_TABLE
2787
- }).getMigrations();
2788
- const applied = [];
2789
- const pending = [];
2790
- for (const migration of migrations) if (migration.executedAt) applied.push(migration.name);
2791
- else pending.push(migration.name);
2792
- return {
2793
- applied,
2794
- pending
2795
- };
2796
- }
2797
- /**
2798
- * Run all pending migrations
2799
- */
2800
- async function runMigrations(db) {
2801
- const { error, results } = await new Migrator({
2802
- db,
2803
- provider: new StaticMigrationProvider(),
2804
- migrationTableName: MIGRATION_TABLE,
2805
- migrationLockTableName: MIGRATION_LOCK_TABLE
2806
- }).migrateToLatest();
2807
- const applied = results?.filter((r) => r.status === "Success").map((r) => r.migrationName) ?? [];
2808
- if (error) {
2809
- let msg = error instanceof Error ? error.message : JSON.stringify(error);
2810
- if (!msg && error instanceof Error && error.cause) msg = error.cause instanceof Error ? error.cause.message : JSON.stringify(error.cause);
2811
- const failedMigration = results?.find((r) => r.status === "Error");
2812
- if (failedMigration) msg = `${msg || "unknown error"} (migration: ${failedMigration.migrationName})`;
2813
- throw new Error(`Migration failed: ${msg}`);
2814
- }
2815
- return { applied };
2816
- }
2817
- /**
2818
- * Rollback the last migration
2819
- */
2820
- async function rollbackMigration(db) {
2821
- const { error, results } = await new Migrator({
2822
- db,
2823
- provider: new StaticMigrationProvider(),
2824
- migrationTableName: MIGRATION_TABLE,
2825
- migrationLockTableName: MIGRATION_LOCK_TABLE
2826
- }).migrateDown();
2827
- const rolledBack = results?.[0]?.status === "Success" ? results[0].migrationName : null;
2828
- if (error) {
2829
- const msg = error instanceof Error ? error.message : JSON.stringify(error);
2830
- throw new Error(`Rollback failed: ${msg}`);
2831
- }
2832
- return { rolledBack };
2833
- }
2834
-
2835
- //#endregion
2836
- export { __exportAll as a, runMigrations as i, getMigrationStatus as n, rollbackMigration as r, getMigrationFingerprint as t };