ghost 6.58.0 → 6.59.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 (453) hide show
  1. package/components/tryghost-adapter-base-scheduling-0.2.2.tgz +0 -0
  2. package/components/tryghost-adapter-base-sso-0.1.2.tgz +0 -0
  3. package/components/tryghost-i18n-0.0.0.tgz +0 -0
  4. package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
  5. package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
  6. package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
  7. package/content/themes/casper/.github/workflows/test.yml +11 -1
  8. package/content/themes/casper/AGENTS.md +25 -0
  9. package/content/themes/casper/assets/built/screen.css +1 -1
  10. package/content/themes/casper/assets/css/screen.css +15 -1
  11. package/content/themes/casper/gulpfile.js +3 -0
  12. package/content/themes/casper/package.json +8 -8
  13. package/content/themes/source/.gitattributes +5 -0
  14. package/content/themes/source/.github/workflows/test.yml +39 -0
  15. package/content/themes/source/AGENTS.md +25 -0
  16. package/content/themes/source/README.md +33 -4
  17. package/content/themes/source/assets/built/screen.css +1 -1
  18. package/content/themes/source/assets/css/screen.css +15 -0
  19. package/content/themes/source/gulpfile.js +6 -3
  20. package/content/themes/source/package.json +13 -10
  21. package/content/themes/source/pnpm-workspace.yaml +3 -0
  22. package/core/boot.js +11 -4
  23. package/core/built/admin/assets/Combination-BCm9FL4o.js +41 -0
  24. package/core/built/admin/assets/about-BdSCpjXG.js +1 -0
  25. package/core/built/admin/assets/account-migration-BpKAqo8k.js +1 -0
  26. package/core/built/admin/assets/activitypub/{account-migration-BpDeIF3G.js → account-migration-DVnYS1XG.js} +8 -8
  27. package/core/built/admin/assets/activitypub/activitypub.js +2 -2
  28. package/core/built/admin/assets/activitypub/{bluesky-sharing-aPMtrS3L.js → bluesky-sharing-DLVeE9ii.js} +31 -31
  29. package/core/built/admin/assets/activitypub/{content-formatters-BORfuA9G.js → content-formatters-DCsVic-t.js} +2 -2
  30. package/core/built/admin/assets/activitypub/{dist-ByFmDSNP2.js → dist-ByFmDSNP.js} +1 -1
  31. package/core/built/admin/assets/activitypub/{domain-CPdiB27_.js → domain-rrvDLfnK.js} +8 -8
  32. package/core/built/admin/assets/activitypub/{edit-profile-BY2PawIe.js → edit-profile-VT-piVxq.js} +23 -23
  33. package/core/built/admin/assets/activitypub/{explore-Sp9bgAwq.js → explore-DZg3m48P.js} +14 -14
  34. package/core/built/admin/assets/activitypub/{feed-BjtF4ohL.js → feed-BJqQvDMI.js} +41 -41
  35. package/core/built/admin/assets/activitypub/{field-BDOYVTg6.js → field-BezS4-1J.js} +2 -2
  36. package/core/built/admin/assets/activitypub/inbox-Uf0GVrI_.js +1284 -0
  37. package/core/built/admin/assets/activitypub/{label-1jhN-qhD.js → label-CZ6if4zK.js} +2 -2
  38. package/core/built/admin/assets/activitypub/layout-DNuYT4Gu.js +1 -0
  39. package/core/built/admin/assets/activitypub/moderation-CP6mDVky.js +340 -0
  40. package/core/built/admin/assets/activitypub/{note-nr_m_5yb.js → note-BlFkye2g.js} +16 -16
  41. package/core/built/admin/assets/activitypub/{notifications-BdcyivbT.js → notifications-GGTrGEdu.js} +163 -126
  42. package/core/built/admin/assets/activitypub/{preferences-CNqy1x9C.js → preferences-CjZXrZqr.js} +1550 -1551
  43. package/core/built/admin/assets/activitypub/{profile-DlY4rhbO.js → profile-CcXYxydm.js} +248 -248
  44. package/core/built/admin/assets/activitypub/{routes-CEHdEZzS.js → routes-nHCKlZl-.js} +1971 -1747
  45. package/core/built/admin/assets/activitypub/settings-pDzybcb3.js +315 -0
  46. package/core/built/admin/assets/activitypub/{site-CtBUCPBz.js → site-BZF0F8LN.js} +101 -101
  47. package/core/built/admin/assets/activitypub/{step-1-D9jGWyAy.js → step-1-C-Ns1Tfg.js} +3 -3
  48. package/core/built/admin/assets/activitypub/{step-3-KpUqpGWi.js → step-3-rDMNGmLg.js} +8 -8
  49. package/core/built/admin/assets/activitypub/{tabs-DvlsxTCI.js → tabs-CDjh3_Jp.js} +198 -198
  50. package/core/built/admin/assets/activitypub/{topic-filter-CIN1E2jg.js → topic-filter-CmTc8hpN.js} +2 -2
  51. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-CATFd6YV.js → use-activity-pub-queries-DUDEdSdR.js} +286 -243
  52. package/core/built/admin/assets/activitypub/{use-reply-chain-data-DJ0vhA8V.js → use-reply-chain-data-TunGtmJl.js} +14 -14
  53. package/core/built/admin/assets/add-integration-modal-CXRQi1x0.js +1 -0
  54. package/core/built/admin/assets/add-newsletter-modal-t447KRYz.js +1 -0
  55. package/core/built/admin/assets/add-recommendation-modal-D882MdnR.js +1 -0
  56. package/core/built/admin/assets/{analytics-BC2oR8ay.js → analytics-Dj-WlzPf.js} +1 -1
  57. package/core/built/admin/assets/announcement-bar-modal-CwHKKw-a.js +1 -0
  58. package/core/built/admin/assets/api-keys-Boy5pLbD.js +1 -0
  59. package/core/built/admin/assets/audience-Cc40y7zQ.js +1 -0
  60. package/core/built/admin/assets/automations-DNLK5Udr.js +1 -0
  61. package/core/built/admin/assets/bluesky-sharing-Cm4aFaOl.js +1 -0
  62. package/core/built/admin/assets/breadcrumb-CcFr_ltt.js +1 -0
  63. package/core/built/admin/assets/button-BshWPHvn.js +1 -0
  64. package/core/built/admin/assets/chart-helpers-BsbzoWhK.js +1 -0
  65. package/core/built/admin/assets/{checkbox-LmClhqcS.js → checkbox-1G6KfK2q.js} +1 -1
  66. package/core/built/admin/assets/chunk-KS7C4IRE-DCsL4i4D.js +4 -0
  67. package/core/built/admin/assets/{chunk.408.1a258af360eb8d905508.js → chunk.301.5673a0caf7c49bb00212.js} +3 -3
  68. package/core/built/admin/assets/{chunk.524.4c693c3a00ac91df34fa.js → chunk.524.86db0d945468c852374b.js} +4 -4
  69. package/core/built/admin/assets/{chunk.582.20b600e329e58d12d7c2.js → chunk.582.c2f6216d3c4af303e236.js} +6 -6
  70. package/core/built/admin/assets/{code-editor-view-DqTOUnbM.js → code-editor-view-BVjtWyst.js} +1 -1
  71. package/core/built/admin/assets/{code-editor-view-bA0lfxUL.js → code-editor-view-y_zVfmCa.js} +1 -1
  72. package/core/built/admin/assets/color-picker-XEMPgNJE.js +3 -0
  73. package/core/built/admin/assets/{color-picker-field-Cv9GOJQZ.js → color-picker-field-BhplXcy5.js} +1 -1
  74. package/core/built/admin/assets/combobox-CRMJrvcQ.js +1 -0
  75. package/core/built/admin/assets/command-151T2eRx.js +1 -0
  76. package/core/built/admin/assets/comments-Bjbl9toF.js +1 -0
  77. package/core/built/admin/assets/confirmation-context-QmFxSjWy.js +1 -0
  78. package/core/built/admin/assets/{content-api-modal-BOFWRUkC.js → content-api-modal-CpeABXne.js} +1 -1
  79. package/core/built/admin/assets/{copy-field-8biQvNMD.js → copy-field-CglgRZyG.js} +1 -1
  80. package/core/built/admin/assets/custom-field-type-option-DSyJw4T4.js +1 -0
  81. package/core/built/admin/assets/custom-integration-modal-DWB9bRVp.js +1 -0
  82. package/core/built/admin/assets/date-range-select-kaKtab5O.js +1 -0
  83. package/core/built/admin/assets/design-and-theme-modal-86s2AaZ-.js +23 -0
  84. package/core/built/admin/assets/dirty-confirm-dialog-CTFdXXy7.js +1 -0
  85. package/core/built/admin/assets/disabled-sources-indicator-BFyCxUOx.js +1 -0
  86. package/core/built/admin/assets/dist-C1-Oy3Db.js +1 -0
  87. package/core/built/admin/assets/dist-CJJqYPep.js +1 -0
  88. package/core/built/admin/assets/dist-CNlMj7ML.js +1 -0
  89. package/core/built/admin/assets/dist-Kll2twSG.js +1 -0
  90. package/core/built/admin/assets/domain-BOoMvBG3.js +1 -0
  91. package/core/built/admin/assets/{dropzone-B21Entqz.js → dropzone-B6KWzsrb.js} +2 -2
  92. package/core/built/admin/assets/edit-profile-BagrYOrN.js +1 -0
  93. package/core/built/admin/assets/editor-AaJjqbpk.js +8 -0
  94. package/core/built/admin/assets/embed-signup-form-modal-B8L95KwR.js +8 -0
  95. package/core/built/admin/assets/explore-xbykZTlO.js +1 -0
  96. package/core/built/admin/assets/feed-BAHSipWv.js +1 -0
  97. package/core/built/admin/assets/{field-BQqLTgHM.js → field-kJYZ2T3C.js} +1 -1
  98. package/core/built/admin/assets/{filter-sources-FgqoUIL7.js → filter-sources-QeqFGNRB.js} +1 -1
  99. package/core/built/admin/assets/{filters-D0uIMD8n.js → filters-By5KQeV6.js} +1 -1
  100. package/core/built/admin/assets/filters-DtYpAkDd.js +1 -0
  101. package/core/built/admin/assets/first-promoter-modal-8A02oyjA.js +1 -0
  102. package/core/built/admin/assets/{ghost-ba408063a5922b458b4a8f216cf23251.js → ghost-f110822e67868c9fdf9a6c23754b2559.js} +9 -8
  103. package/core/built/admin/assets/gift-link-modal-DbYCyuhF.js +1 -0
  104. package/core/built/admin/assets/global-data-context-gAMDH9u1.js +1 -0
  105. package/core/built/admin/assets/growth-BSOxJGey.js +3 -0
  106. package/core/built/admin/assets/growth-BZTjURq5.js +1 -0
  107. package/core/built/admin/assets/history-modal-CCgWtlZE.js +1 -0
  108. package/core/built/admin/assets/html-field-BQC7XyLA.js +1 -0
  109. package/core/built/admin/assets/iframe-buffering-DnOlWTD4.js +1 -0
  110. package/core/built/admin/assets/{images-3cQgwfTD.js → images-D5UKr2SG.js} +1 -1
  111. package/core/built/admin/assets/{inbox-Dz_JblyJ.js → inbox-DvN15_YH.js} +45 -39
  112. package/core/built/admin/assets/index-BJz9Zlsc.js +67 -0
  113. package/core/built/admin/assets/index-Dg8cdR8E.css +1 -0
  114. package/core/built/admin/assets/{input-group-DpogNUIt.js → input-group-BPAxETpQ.js} +1 -1
  115. package/core/built/admin/assets/invite-user-modal-jcUaaNkW.js +1 -0
  116. package/core/built/admin/assets/{koenig-lexical-2KuqRwZI.js → koenig-lexical-CfXTYeli.js} +1 -1
  117. package/core/built/admin/assets/koenig-loader-DktFM3Uh.js +1 -0
  118. package/core/built/admin/assets/{kpi-card-Ck3tT2Wg.js → kpi-card-fUHZlzBX.js} +1 -1
  119. package/core/built/admin/assets/{kpi-card-DDIxptaS.js → kpi-card-o0urVtNL.js} +1 -1
  120. package/core/built/admin/assets/kpi-helpers-2FTF4a3-.js +1 -0
  121. package/core/built/admin/assets/kpi-tabs-B8-_k2dQ.js +1 -0
  122. package/core/built/admin/assets/{label-DodDsMzn.js → label-CiRS3i_f.js} +1 -1
  123. package/core/built/admin/assets/label-picker-ao7tuPaK.js +1 -0
  124. package/core/built/admin/assets/layout-DC6QGDaw.js +1 -0
  125. package/core/built/admin/assets/member-custom-fields-BW756-EV.js +1 -0
  126. package/core/built/admin/assets/member-detail-CGGgUTSl.js +1 -0
  127. package/core/built/admin/assets/members-ZcPthwG0.js +11 -0
  128. package/core/built/admin/assets/moderation-24bElWs3.js +1 -0
  129. package/core/built/admin/assets/multi-select-combobox-CZPrgz-Z.js +1 -0
  130. package/core/built/admin/assets/navigation-modal-BiTP4U_J.js +1 -0
  131. package/core/built/admin/assets/newsletter-B3Ihwizp.js +1 -0
  132. package/core/built/admin/assets/newsletter-detail-modal-CYsu4qhl.js +1 -0
  133. package/core/built/admin/assets/newsletter-emails-CDPR4-SP.js +1 -0
  134. package/core/built/admin/assets/newsletters-bJiYLJWl.js +1 -0
  135. package/core/built/admin/assets/note-7f6urVVA.js +1 -0
  136. package/core/built/admin/assets/notifications-rosQCyXH.js +1 -0
  137. package/core/built/admin/assets/offers-container-modal-J2j6NpDL.js +1 -0
  138. package/core/built/admin/assets/onboarding-route-DTjcThKl.js +1 -0
  139. package/core/built/admin/assets/overview-BxvWzGaf.js +1 -0
  140. package/core/built/admin/assets/overview-D6JG0hkZ.js +1 -0
  141. package/core/built/admin/assets/{page-header-VLPJfj_d.js → page-header-l7vXmLIw.js} +1 -1
  142. package/core/built/admin/assets/pintura-modal-BGHmudVB.js +1 -0
  143. package/core/built/admin/assets/portal-frame-DVc4VHX_.js +1 -0
  144. package/core/built/admin/assets/portal-modal-BCOcdiRi.js +1 -0
  145. package/core/built/admin/assets/{post-analytics-Gv81eN_g.js → post-analytics-CTSZMK6I.js} +1 -1
  146. package/core/built/admin/assets/post-analytics-header-Dt4RQGq0.js +1 -0
  147. package/core/built/admin/assets/{post-analytics-provider-O2YjZMhN.js → post-analytics-provider-C4ydiboO.js} +1 -1
  148. package/core/built/admin/assets/post-share-modal-DjH_a_1L.js +1 -0
  149. package/core/built/admin/assets/preferences-BLo6VJUS.js +5 -0
  150. package/core/built/admin/assets/preview-modal-BOgjgsJL.js +1 -0
  151. package/core/built/admin/assets/profile-DG6iCdRH.js +1 -0
  152. package/core/built/admin/assets/radio-group-ChHD4EQJ.js +1 -0
  153. package/core/built/admin/assets/router-provider-Bexg1t-V.js +1 -0
  154. package/core/built/admin/assets/select-ChxknEcT.js +1 -0
  155. package/core/built/admin/assets/settings-B0VDAhgV.js +1 -0
  156. package/core/built/admin/assets/settings-Dwo3CeAD.js +4 -0
  157. package/core/built/admin/assets/settings-breadcrumbs-sLDNedBW.js +1 -0
  158. package/core/built/admin/assets/share-modal-BDb2nacg.js +1 -0
  159. package/core/built/admin/assets/skeleton-D7hwjdry.js +1 -0
  160. package/core/built/admin/assets/slack-modal-CwNAD69x.js +1 -0
  161. package/core/built/admin/assets/sort-button-DhqpxkrO.js +1 -0
  162. package/core/built/admin/assets/source-icon-BPsD5ees.js +1 -0
  163. package/core/built/admin/assets/stats-WdDJ8c_1.js +1 -0
  164. package/core/built/admin/assets/stats-view-J3PI2eT_.js +1 -0
  165. package/core/built/admin/assets/step-1-2q1SjerQ.js +1 -0
  166. package/core/built/admin/assets/step-2-D5mJB3Uu.js +1 -0
  167. package/core/built/admin/assets/step-3-BpAuYTGZ.js +2 -0
  168. package/core/built/admin/assets/{stripe-button-Bh4p6Wud.js → stripe-button-DDoUKN5a.js} +1 -1
  169. package/core/built/admin/assets/stripe-connect-modal-pMdF3t4S.js +1 -0
  170. package/core/built/admin/assets/switch-B_1yU3Aw.js +1 -0
  171. package/core/built/admin/assets/{table-DghJxZMH.js → table-CrqQtkR2.js} +1 -1
  172. package/core/built/admin/assets/tabs-BhzVdh5p.js +1 -0
  173. package/core/built/admin/assets/tag-detail-CwJhI4qg.js +2 -0
  174. package/core/built/admin/assets/tags-CvJ0PgrB.js +1 -0
  175. package/core/built/admin/assets/testimonials-modal-CV82IAnI.js +1 -0
  176. package/core/built/admin/assets/tier-detail-modal-pLzCOfeg.js +1 -0
  177. package/core/built/admin/assets/toggle-group-BW_T4yn6.js +1 -0
  178. package/core/built/admin/assets/topic-filter-DkXjDejV.js +1 -0
  179. package/core/built/admin/assets/transistor-modal-TMRvHZ6B.js +1 -0
  180. package/core/built/admin/assets/unsplash-modal-CV92RdPw.js +1 -0
  181. package/core/built/admin/assets/{use-analytics-data-IxJcSjc9.js → use-analytics-data-BM_uV9SU.js} +1 -1
  182. package/core/built/admin/assets/use-check-theme-limit-error-DkBnXMSY.js +1 -0
  183. package/core/built/admin/assets/use-confirm-unload-SzlkOIpm.js +1 -0
  184. package/core/built/admin/assets/use-feature-flag-cahOlMva.js +1 -0
  185. package/core/built/admin/assets/use-filter-params-BX5qf0Cg.js +1 -0
  186. package/core/built/admin/assets/{use-growth-stats-1NW-8eEZ.js → use-growth-stats-D0LlWBe6.js} +1 -1
  187. package/core/built/admin/assets/{use-hash-link-navigation-guard-B9j6HIrV.js → use-hash-link-navigation-guard-B_Y3FhYH.js} +1 -1
  188. package/core/built/admin/assets/use-limiter-D3DHSoAj.js +2 -0
  189. package/core/built/admin/assets/{use-pintura-config-BjFChXV1.js → use-pintura-config-D63Xu0SG.js} +1 -1
  190. package/core/built/admin/assets/use-pintura-editor-Z51aFnC8.js +1 -0
  191. package/core/built/admin/assets/{use-post-referrers-Dp1BnTAK.js → use-post-referrers-Cvymr3gt.js} +1 -1
  192. package/core/built/admin/assets/use-query-params-BfzuqbcD.js +1 -0
  193. package/core/built/admin/assets/use-reply-chain-data-Dc18Ujqo.js +1 -0
  194. package/core/built/admin/assets/use-setting-group-DYRIzqC0.js +1 -0
  195. package/core/built/admin/assets/use-settings-navigation-aDSLtmEl.js +1 -0
  196. package/core/built/admin/assets/{use-simple-pagination-DsEGSJhe.js → use-simple-pagination-B2TdNX67.js} +1 -1
  197. package/core/built/admin/assets/{use-tinybird-query-Ci_DiPkX.js → use-tinybird-query-BvQIVuRc.js} +1 -1
  198. package/core/built/admin/assets/{use-tinybird-token-CsUwc722.js → use-tinybird-token-COQTPIwL.js} +1 -1
  199. package/core/built/admin/assets/use-upgrade-route-BZf1g59m.js +1 -0
  200. package/core/built/admin/assets/{use-url-input-BEZJpVgN.js → use-url-input-PcqZadXa.js} +1 -1
  201. package/core/built/admin/assets/user-detail-modal-DeO3SAZq.js +1 -0
  202. package/core/built/admin/assets/{virtual-list-D5ugXyZY.js → virtual-list-D3MfPNOh.js} +1 -1
  203. package/core/built/admin/assets/web-CDdyG7z1.js +2 -0
  204. package/core/built/admin/assets/web-CwQ8_QUc.js +1 -0
  205. package/core/built/admin/assets/zapier-modal-a7t75iNI.js +1 -0
  206. package/core/built/admin/index.html +15 -12
  207. package/core/frontend/apps/private-blogging/lib/helpers/input_password.js +2 -1
  208. package/core/frontend/helpers/body_class.js +2 -1
  209. package/core/frontend/helpers/cancel_link.js +2 -1
  210. package/core/frontend/helpers/color_to_rgba.js +2 -1
  211. package/core/frontend/helpers/content_api_key.js +2 -1
  212. package/core/frontend/helpers/content_api_url.js +2 -1
  213. package/core/frontend/helpers/contrast_text_color.js +2 -1
  214. package/core/frontend/helpers/facebook_url.js +2 -1
  215. package/core/frontend/helpers/ghost_foot.js +2 -1
  216. package/core/frontend/helpers/ghost_head.js +2 -1
  217. package/core/frontend/helpers/has.js +4 -2
  218. package/core/frontend/helpers/link_class.js +2 -1
  219. package/core/frontend/helpers/meta_description.js +2 -1
  220. package/core/frontend/helpers/meta_title.js +2 -1
  221. package/core/frontend/helpers/navigation.js +113 -22
  222. package/core/frontend/helpers/page_url.js +2 -1
  223. package/core/frontend/helpers/post_class.js +2 -1
  224. package/core/frontend/helpers/reading_time.js +2 -1
  225. package/core/frontend/helpers/social_accounts.js +2 -1
  226. package/core/frontend/helpers/social_url.js +2 -1
  227. package/core/frontend/helpers/total_members.js +2 -1
  228. package/core/frontend/helpers/total_paid_members.js +2 -1
  229. package/core/frontend/helpers/tpl/navigation.hbs +1 -1
  230. package/core/frontend/helpers/twitter_url.js +2 -1
  231. package/core/frontend/public/cards.manifest.json +1 -1
  232. package/core/server/api/endpoints/posts.js +7 -4
  233. package/core/server/api/endpoints/utils/permissions.js +2 -1
  234. package/core/server/api/endpoints/utils/validators/input/settings.js +83 -1
  235. package/core/server/data/exporter/table-lists.js +1 -0
  236. package/core/server/data/importer/handlers/json.js +2 -1
  237. package/core/server/data/migrations/versions/5.19/2022-09-02-20-52-backfill-new-product-columns.js +2 -1
  238. package/core/server/data/migrations/versions/5.21/2022-10-26-04-50-member-subscription-created-batch-id.js +4 -2
  239. package/core/server/data/migrations/versions/5.22/2022-10-31-12-03-backfill-new-product-columns.js +2 -1
  240. package/core/server/data/migrations/versions/6.58/2026-08-11-21-22-48-rename-email-batches-provider-id.js +1 -1
  241. package/core/server/data/migrations/versions/6.59/2026-08-18-17-06-46-add-gift-delivery-fields-to-gifts.js +26 -0
  242. package/core/server/data/migrations/versions/6.59/2026-08-18-17-07-01-add-gift-deliveries.js +20 -0
  243. package/core/server/data/schema/commands.js +48 -24
  244. package/core/server/data/schema/schema.js +28 -7
  245. package/core/server/data/seeders/importers/automation-action-revisions-importer.js +3 -36
  246. package/core/server/data/seeders/importers/automation-actions-importer.js +5 -41
  247. package/core/server/data/seeders/importers/automation-run-steps-importer.js +3 -36
  248. package/core/server/data/seeders/importers/automation-runs-importer.js +7 -42
  249. package/core/server/data/seeders/importers/automations-importer.js +3 -36
  250. package/core/server/data/seeders/importers/comment-reports-importer.js +5 -5
  251. package/core/server/data/seeders/importers/comments-importer.js +5 -5
  252. package/core/server/data/seeders/importers/email-batches-importer.js +5 -4
  253. package/core/server/data/seeders/importers/email-recipients-importer.js +12 -20
  254. package/core/server/data/seeders/importers/emails-importer.js +6 -6
  255. package/core/server/data/seeders/importers/labels-importer.js +3 -3
  256. package/core/server/data/seeders/importers/members-click-events-importer.js +5 -5
  257. package/core/server/data/seeders/importers/members-created-events-importer.js +13 -12
  258. package/core/server/data/seeders/importers/members-feedback-importer.js +7 -7
  259. package/core/server/data/seeders/importers/members-importer.js +4 -4
  260. package/core/server/data/seeders/importers/members-login-events-importer.js +3 -3
  261. package/core/server/data/seeders/importers/members-products-importer.js +2 -2
  262. package/core/server/data/seeders/importers/members-status-events-importer.js +4 -3
  263. package/core/server/data/seeders/importers/members-stripe-customers-importer.js +2 -2
  264. package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +7 -7
  265. package/core/server/data/seeders/importers/members-subscribe-events-importer.js +3 -3
  266. package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +3 -3
  267. package/core/server/data/seeders/importers/offer-redemptions-importer.js +7 -7
  268. package/core/server/data/seeders/importers/offers-importer.js +3 -3
  269. package/core/server/data/seeders/importers/posts-importer.js +4 -4
  270. package/core/server/data/seeders/importers/table-importer.js +2 -35
  271. package/core/server/data/seeders/importers/tags-importer.js +2 -2
  272. package/core/server/data/seeders/importers/users-importer.js +2 -2
  273. package/core/server/data/seeders/importers/web-mentions-importer.js +2 -2
  274. package/core/server/data/seeders/utils/random.js +7 -0
  275. package/core/server/ghost-server.js +19 -5
  276. package/core/server/lib/db-date.js +72 -3
  277. package/core/server/models/gift-delivery.js +23 -0
  278. package/core/server/models/gift.js +9 -0
  279. package/core/server/models/index.js +3 -2
  280. package/core/server/models/settings.js +43 -4
  281. package/core/server/services/automations/database-automations-repository.js +38 -31
  282. package/core/server/services/automations/poll.js +1 -1
  283. package/core/server/services/content-import/csv/index.js +8 -0
  284. package/core/server/services/content-import/csv/parse.js +60 -0
  285. package/core/server/services/content-import/import/importer.js +222 -0
  286. package/core/server/services/content-import/import/post-data.js +52 -0
  287. package/core/server/services/content-import/import/reader.js +15 -0
  288. package/core/server/services/content-import/import/row.js +50 -0
  289. package/core/server/services/content-import/import/store.js +71 -0
  290. package/core/server/services/content-import/index.js +67 -0
  291. package/core/server/services/email-analytics/automation-email-analytics-batch-processor.js +1 -1
  292. package/core/server/services/email-analytics/index.js +3 -1
  293. package/core/server/services/email-analytics/jobs/index.js +2 -2
  294. package/core/server/services/email-service/email-service-wrapper.js +1 -1
  295. package/core/server/services/email-suppression-list/email-suppression-list.js +4 -2
  296. package/core/server/services/gifts/CONTEXT.md +27 -3
  297. package/core/server/services/gifts/README.md +8 -4
  298. package/core/server/services/gifts/constants.js +2 -1
  299. package/core/server/services/gifts/email-templates/gift-delivery.hbs +20 -0
  300. package/core/server/services/gifts/email-templates/gift-delivery.js +39 -0
  301. package/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs +2 -2
  302. package/core/server/services/gifts/email-templates/gift-purchase-confirmation.js +19 -4
  303. package/core/server/services/gifts/events/send-gift-delivery-event.js +15 -0
  304. package/core/server/services/gifts/gift-bookshelf-repository.js +23 -3
  305. package/core/server/services/gifts/gift-checkout-adapter.js +5 -3
  306. package/core/server/services/gifts/gift-codec.js +8 -0
  307. package/core/server/services/gifts/gift-delivery-bookshelf-repository.js +107 -0
  308. package/core/server/services/gifts/gift-delivery-codec.js +12 -0
  309. package/core/server/services/gifts/gift-delivery-schema.js +15 -0
  310. package/core/server/services/gifts/gift-delivery-service.js +209 -0
  311. package/core/server/services/gifts/gift-email-renderer.js +13 -1
  312. package/core/server/services/gifts/gift-email-service.js +97 -17
  313. package/core/server/services/gifts/gift-schema.js +10 -6
  314. package/core/server/services/gifts/gift-service.js +259 -34
  315. package/core/server/services/gifts/gift.js +59 -5
  316. package/core/server/services/gifts/index.js +69 -3
  317. package/core/server/services/lib/mailgun-client.js +5 -2
  318. package/core/server/services/mail/ghost-mailer.js +9 -1
  319. package/core/server/services/member-welcome-emails/service.js +6 -1
  320. package/core/server/services/members/members-api/controllers/router-controller.js +26 -8
  321. package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +100 -12
  322. package/core/server/services/stripe/stripe-api.js +3 -2
  323. package/core/server/services/stripe/webhook-controller.js +6 -3
  324. package/core/server/services/stripe/webhook-manager.js +2 -0
  325. package/core/shared/labs.js +1 -0
  326. package/package.json +9 -8
  327. package/pnpm-lock.yaml +26 -11
  328. package/pnpm-workspace.yaml +5 -6
  329. package/content/themes/source/yarn.lock +0 -6232
  330. package/core/built/admin/assets/Combination-BDvCdg1t.js +0 -41
  331. package/core/built/admin/assets/about-B_BvSyLF.js +0 -1
  332. package/core/built/admin/assets/account-migration-oJUGxnzT.js +0 -1
  333. package/core/built/admin/assets/activitypub/dist-Dqggkccr.js +0 -215
  334. package/core/built/admin/assets/activitypub/inbox-z1o2j1Tb.js +0 -1221
  335. package/core/built/admin/assets/activitypub/layout-BRMezuty.js +0 -1
  336. package/core/built/admin/assets/activitypub/moderation-CZudJi8N.js +0 -160
  337. package/core/built/admin/assets/activitypub/settings-CrSV1cux.js +0 -107
  338. package/core/built/admin/assets/add-integration-modal-BW23PDYs.js +0 -1
  339. package/core/built/admin/assets/add-newsletter-modal-M0ls8BKO.js +0 -1
  340. package/core/built/admin/assets/add-recommendation-modal-B0NeBMzl.js +0 -1
  341. package/core/built/admin/assets/announcement-bar-modal-pXBqjQE_.js +0 -1
  342. package/core/built/admin/assets/api-keys-Df6uv1dT.js +0 -1
  343. package/core/built/admin/assets/audience-BY8od8If.js +0 -1
  344. package/core/built/admin/assets/automations-DCs6Wady.js +0 -1
  345. package/core/built/admin/assets/bluesky-sharing-D0P42Cws.js +0 -1
  346. package/core/built/admin/assets/breadcrumb-MTj_cKLb.js +0 -1
  347. package/core/built/admin/assets/chart-helpers-DcO3k_vA.js +0 -1
  348. package/core/built/admin/assets/chunk-KS7C4IRE-DG9HTHQ3.js +0 -4
  349. package/core/built/admin/assets/color-picker-CBlJMv3y.js +0 -3
  350. package/core/built/admin/assets/combobox-BbPJzjCp.js +0 -1
  351. package/core/built/admin/assets/command-BrhXIlDr.js +0 -1
  352. package/core/built/admin/assets/comments-Cu4rD1aJ.js +0 -1
  353. package/core/built/admin/assets/confirmation-provider-BEdrnHns.js +0 -1
  354. package/core/built/admin/assets/custom-field-type-option-CNhAgHbJ.js +0 -1
  355. package/core/built/admin/assets/custom-integration-modal-CrGOI8IB.js +0 -1
  356. package/core/built/admin/assets/date-range-select-Bsh4gIek.js +0 -1
  357. package/core/built/admin/assets/design-and-theme-modal-DWZguwWX.js +0 -23
  358. package/core/built/admin/assets/dirty-confirm-dialog-DmdMpgyN.js +0 -1
  359. package/core/built/admin/assets/disabled-sources-indicator-BLnmyWgt.js +0 -1
  360. package/core/built/admin/assets/dist-DMFGvqqU.js +0 -1
  361. package/core/built/admin/assets/dist-DlCyLrOC.js +0 -1
  362. package/core/built/admin/assets/dist-j1xxxnY8.js +0 -1
  363. package/core/built/admin/assets/domain-Dp9TRrFp.js +0 -1
  364. package/core/built/admin/assets/edit-profile-DG6lpMsr.js +0 -1
  365. package/core/built/admin/assets/editor-CPQOrhrn.js +0 -8
  366. package/core/built/admin/assets/embed-signup-form-modal-CgWniDha.js +0 -9
  367. package/core/built/admin/assets/explore-C3rzONJv.js +0 -1
  368. package/core/built/admin/assets/feed-DpUwS1cU.js +0 -1
  369. package/core/built/admin/assets/filters-DZJT29GW.js +0 -1
  370. package/core/built/admin/assets/first-promoter-modal-DJhQQheG.js +0 -1
  371. package/core/built/admin/assets/gift-link-modal-sdFLjND9.js +0 -1
  372. package/core/built/admin/assets/global-data-provider-CixmTX00.js +0 -1
  373. package/core/built/admin/assets/growth-0YYkBEeS.js +0 -1
  374. package/core/built/admin/assets/growth-DpjAPeQT.js +0 -3
  375. package/core/built/admin/assets/history-modal-BWJ4-cJ0.js +0 -1
  376. package/core/built/admin/assets/html-field-C-oa5DEl.js +0 -1
  377. package/core/built/admin/assets/iframe-buffering-Ba9sDpi2.js +0 -1
  378. package/core/built/admin/assets/index-BOJzlYiz.js +0 -67
  379. package/core/built/admin/assets/index-Bid6LAyc.css +0 -1
  380. package/core/built/admin/assets/invite-user-modal-DCiQSiuD.js +0 -1
  381. package/core/built/admin/assets/koenig-editor-base-BApy8Q7C.js +0 -1
  382. package/core/built/admin/assets/kpi-helpers-ssbuy-hD.js +0 -1
  383. package/core/built/admin/assets/kpi-tabs-BqoIM6bI.js +0 -1
  384. package/core/built/admin/assets/label-picker-Cvvbmzb5.js +0 -1
  385. package/core/built/admin/assets/layout-DkkGyev2.js +0 -1
  386. package/core/built/admin/assets/member-custom-fields-CvGcpaiW.js +0 -1
  387. package/core/built/admin/assets/member-detail-CMCMZ47_.js +0 -1
  388. package/core/built/admin/assets/members-sRl6fNfq.js +0 -11
  389. package/core/built/admin/assets/moderation-KhKwGqI1.js +0 -1
  390. package/core/built/admin/assets/multi-select-combobox-nhCff0_K.js +0 -1
  391. package/core/built/admin/assets/navigation-modal-MDJExWDJ.js +0 -1
  392. package/core/built/admin/assets/newsletter-BIBHumfV.js +0 -1
  393. package/core/built/admin/assets/newsletter-detail-modal-CfjBMH3v.js +0 -1
  394. package/core/built/admin/assets/newsletter-emails-ClDL-7dA.js +0 -1
  395. package/core/built/admin/assets/newsletters-CBe-IPzK.js +0 -1
  396. package/core/built/admin/assets/note-HnNUOt4d.js +0 -1
  397. package/core/built/admin/assets/notifications-Cj5e-258.js +0 -1
  398. package/core/built/admin/assets/offers-container-modal-BHwHtixC.js +0 -1
  399. package/core/built/admin/assets/onboarding-route-D_4jp7Nq.js +0 -1
  400. package/core/built/admin/assets/overview-CezDdg-F.js +0 -1
  401. package/core/built/admin/assets/overview-D5mM51Lj.js +0 -1
  402. package/core/built/admin/assets/pintura-modal-BXXJ8Iaa.js +0 -1
  403. package/core/built/admin/assets/portal-frame-BLCvF8f-.js +0 -1
  404. package/core/built/admin/assets/portal-modal-DN80tAcy.js +0 -1
  405. package/core/built/admin/assets/post-analytics-header-_ydSY8qO.js +0 -1
  406. package/core/built/admin/assets/post-share-modal-CzYak_a5.js +0 -1
  407. package/core/built/admin/assets/preferences-gneLb6LV.js +0 -5
  408. package/core/built/admin/assets/preview-modal-Bkq-tgVm.js +0 -1
  409. package/core/built/admin/assets/profile-CzXmCNTC.js +0 -1
  410. package/core/built/admin/assets/radio-group-Dsq5ZEbV.js +0 -1
  411. package/core/built/admin/assets/router-provider-CkB_ArLF.js +0 -1
  412. package/core/built/admin/assets/select-xuZAuDyS.js +0 -1
  413. package/core/built/admin/assets/settings-Ct8pabhr.js +0 -1
  414. package/core/built/admin/assets/settings-KSWWQanQ.js +0 -4
  415. package/core/built/admin/assets/settings-breadcrumbs-DbPGu-Tu.js +0 -1
  416. package/core/built/admin/assets/share-modal-CdiUx9WM.js +0 -1
  417. package/core/built/admin/assets/slack-modal-BP--Z3jE.js +0 -1
  418. package/core/built/admin/assets/sort-button-CjfKdlsL.js +0 -1
  419. package/core/built/admin/assets/source-icon-DHj1h6Ts.js +0 -1
  420. package/core/built/admin/assets/stats-C615kulJ.js +0 -1
  421. package/core/built/admin/assets/stats-view-BSMRdYiN.js +0 -1
  422. package/core/built/admin/assets/step-1-pSdGYtIF.js +0 -1
  423. package/core/built/admin/assets/step-2-T9Uy4eBD.js +0 -1
  424. package/core/built/admin/assets/step-3-D4uXe_XG.js +0 -2
  425. package/core/built/admin/assets/stripe-connect-modal-C2vTvepi.js +0 -1
  426. package/core/built/admin/assets/switch-CE2NuMn-.js +0 -1
  427. package/core/built/admin/assets/tabs-l9c7FjzI.js +0 -1
  428. package/core/built/admin/assets/tag-detail-DBw_M2-p.js +0 -2
  429. package/core/built/admin/assets/tags-DoWsAG6l.js +0 -1
  430. package/core/built/admin/assets/testimonials-modal-BZKePE2T.js +0 -1
  431. package/core/built/admin/assets/tier-detail-modal-D65pVWsN.js +0 -1
  432. package/core/built/admin/assets/toggle-group-uJnl42w3.js +0 -1
  433. package/core/built/admin/assets/topic-filter-BM7hz0kW.js +0 -1
  434. package/core/built/admin/assets/transistor-modal-VZOGN3od.js +0 -1
  435. package/core/built/admin/assets/unsplash-modal-CKGfACxF.js +0 -1
  436. package/core/built/admin/assets/use-check-theme-limit-error-CGp3CGT5.js +0 -1
  437. package/core/built/admin/assets/use-feature-flag-CSq2Lwzt.js +0 -1
  438. package/core/built/admin/assets/use-filter-params-0hcJJuEJ.js +0 -1
  439. package/core/built/admin/assets/use-limiter-ffwpsZPp.js +0 -2
  440. package/core/built/admin/assets/use-pintura-editor-Cb41oJyL.js +0 -1
  441. package/core/built/admin/assets/use-query-params-DHhhu8wi.js +0 -1
  442. package/core/built/admin/assets/use-reply-chain-data-C0ZUTkfk.js +0 -1
  443. package/core/built/admin/assets/use-setting-group-Bx8M6FL2.js +0 -1
  444. package/core/built/admin/assets/use-settings-navigation-BOZcat_V.js +0 -1
  445. package/core/built/admin/assets/use-upgrade-route-B3_DUiuT.js +0 -1
  446. package/core/built/admin/assets/user-detail-modal-595z1N5r.js +0 -1
  447. package/core/built/admin/assets/web-CAta3lRE.js +0 -1
  448. package/core/built/admin/assets/web-Ds9_wWJK.js +0 -2
  449. package/core/built/admin/assets/zapier-modal-CcfDZY53.js +0 -1
  450. package/core/server/data/seeders/utils/database-date.js +0 -28
  451. package/core/server/services/automations/database-date.js +0 -59
  452. /package/core/built/admin/assets/{dist-CqO2lWS02.js → dist-CqO2lWS0.js} +0 -0
  453. /package/core/server/services/{automations → lib}/mailgun-message-id.js +0 -0
@@ -1,7 +1,7 @@
1
1
  const {TableImporter} = require('./table-importer');
2
2
  const {faker} = require('@faker-js/faker');
3
3
  const {luck} = require('../utils/random');
4
- const databaseDate = require('../utils/database-date');
4
+ const {fromDatabaseDate, toDatabaseDate} = require('../../../lib/db-date');
5
5
 
6
6
  class MembersCreatedEventsImporter extends TableImporter {
7
7
  static table = 'members_created_events';
@@ -13,22 +13,23 @@ class MembersCreatedEventsImporter extends TableImporter {
13
13
 
14
14
  async import(quantity) {
15
15
  const members = await this.transaction.select('id', 'created_at').from('members');
16
- this.posts = await this.transaction.select('id', 'published_at', 'visibility', 'type', 'slug').from('posts').orderBy('published_at', 'desc');
16
+ this.posts = await this.transaction.select('id', 'published_at', 'visibility', 'type', 'slug').from('posts').whereNotNull('published_at').orderBy('published_at', 'desc');
17
17
  this.incomingRecommendations = await this.transaction.select('id', 'source', 'created_at').from('mentions');
18
18
 
19
19
  await this.importForEach(members, quantity ? quantity / members.length : 1);
20
20
  }
21
21
 
22
22
  generateSource() {
23
- let source = 'member';
24
23
  if (luck(10)) {
25
- source = 'admin';
26
- } else if (luck(5)) {
27
- source = 'api';
28
- } else if (luck(5)) { // eslint-disable-line no-dupe-else-if
29
- source = 'import';
24
+ return 'admin';
30
25
  }
31
- return source;
26
+ if (luck(5)) {
27
+ return 'api';
28
+ }
29
+ if (luck(5)) {
30
+ return 'import';
31
+ }
32
+ return 'member';
32
33
  }
33
34
 
34
35
  generate() {
@@ -47,8 +48,8 @@ class MembersCreatedEventsImporter extends TableImporter {
47
48
  };
48
49
 
49
50
  if (source === 'member' && luck(30)) {
50
- const memberCreatedAt = databaseDate.parse(this.model.created_at);
51
- const post = this.posts.find(p => p.visibility === 'public' && databaseDate.parse(p.published_at) < memberCreatedAt);
51
+ const memberCreatedAt = fromDatabaseDate(this.model.created_at);
52
+ const post = this.posts.find(p => p.visibility === 'public' && fromDatabaseDate(p.published_at) < memberCreatedAt);
52
53
  if (post) {
53
54
  attribution = {
54
55
  attribution_id: post.id,
@@ -92,7 +93,7 @@ class MembersCreatedEventsImporter extends TableImporter {
92
93
 
93
94
  return {
94
95
  id: this.fastFakeObjectId(),
95
- created_at: databaseDate.dateToDatabaseString(this.model.created_at),
96
+ created_at: toDatabaseDate(this.model.created_at),
96
97
  member_id: this.model.id,
97
98
  source,
98
99
  ...attribution,
@@ -1,6 +1,6 @@
1
1
  const {TableImporter} = require('./table-importer');
2
- const {luck} = require('../utils/random');
3
- const databaseDate = require('../utils/database-date');
2
+ const {luck, randomDateBetween} = require('../utils/random');
3
+ const {fromDatabaseDate, toDatabaseDate} = require('../../../lib/db-date');
4
4
 
5
5
  class MembersFeedbackImporter extends TableImporter {
6
6
  static table = 'members_feedback';
@@ -24,10 +24,10 @@ class MembersFeedbackImporter extends TableImporter {
24
24
  return null;
25
25
  }
26
26
 
27
- const openedAt = databaseDate.parse(this.model.opened_at);
28
- const laterOn = databaseDate.parse(this.model.opened_at);
27
+ const openedAt = fromDatabaseDate(this.model.opened_at);
28
+ const laterOn = new Date(openedAt);
29
29
  laterOn.setMinutes(laterOn.getMinutes() + 60);
30
- const feedbackTime = databaseDate.randomBetween(openedAt, laterOn);
30
+ const feedbackTime = randomDateBetween(openedAt, laterOn);
31
31
 
32
32
  const postId = this.emails.find(email => email.id === this.model.email_id).post_id;
33
33
  return {
@@ -35,8 +35,8 @@ class MembersFeedbackImporter extends TableImporter {
35
35
  score: luck(70) ? 1 : 0,
36
36
  member_id: this.model.member_id,
37
37
  post_id: postId,
38
- created_at: databaseDate.dateToDatabaseString(feedbackTime),
39
- updated_at: databaseDate.dateToDatabaseString(feedbackTime)
38
+ created_at: toDatabaseDate(feedbackTime),
39
+ updated_at: toDatabaseDate(feedbackTime)
40
40
  };
41
41
  }
42
42
  }
@@ -4,7 +4,7 @@ const {faker: americanFaker} = require('@faker-js/faker/locale/en_US');
4
4
  const {blogStartDate: startTime} = require('../utils/blog-info');
5
5
  const generateEvents = require('../utils/event-generator');
6
6
  const {luck} = require('../utils/random');
7
- const databaseDate = require('../utils/database-date');
7
+ const {toDatabaseDate} = require('../../../lib/db-date');
8
8
  const debug = require('@tryghost/debug')('MembersImporter');
9
9
 
10
10
  class MembersImporter extends TableImporter {
@@ -88,9 +88,9 @@ class MembersImporter extends TableImporter {
88
88
  email_opened_count: 0,
89
89
  email_open_rate: null,
90
90
  // 40% of users logged in within a week, 60% sometime since registering
91
- last_seen_at: luck(40) ? databaseDate.dateToDatabaseString(faker.date.recent({days: 7})) : databaseDate.dateToDatabaseString(faker.date.between({from: timestamp, to: new Date()})),
92
- created_at: databaseDate.dateToDatabaseString(timestamp),
93
- updated_at: databaseDate.dateToDatabaseString(timestamp)
91
+ last_seen_at: luck(40) ? toDatabaseDate(faker.date.recent({days: 7})) : toDatabaseDate(faker.date.between({from: timestamp, to: new Date()})),
92
+ created_at: toDatabaseDate(timestamp),
93
+ updated_at: toDatabaseDate(timestamp)
94
94
  };
95
95
  }
96
96
  }
@@ -1,7 +1,7 @@
1
1
  const {TableImporter} = require('./table-importer');
2
2
  const {luck} = require('../utils/random');
3
3
  const generateEvents = require('../utils/event-generator');
4
- const databaseDate = require('../utils/database-date');
4
+ const {fromDatabaseDate, toDatabaseDate} = require('../../../lib/db-date');
5
5
 
6
6
  class MembersLoginEventsImporter extends TableImporter {
7
7
  static table = 'members_login_events';
@@ -34,7 +34,7 @@ class MembersLoginEventsImporter extends TableImporter {
34
34
 
35
35
  setReferencedModel(model) {
36
36
  this.model = model;
37
- const memberCreatedAt = databaseDate.parse(model.created_at);
37
+ const memberCreatedAt = fromDatabaseDate(model.created_at);
38
38
 
39
39
  const endDate = new Date();
40
40
  const daysBetween = Math.ceil((endDate.valueOf() - memberCreatedAt.valueOf()) / (1000 * 60 * 60 * 24));
@@ -60,7 +60,7 @@ class MembersLoginEventsImporter extends TableImporter {
60
60
  }
61
61
  return {
62
62
  id: this.fastFakeObjectId(),
63
- created_at: databaseDate.dateToDatabaseString(timestamp),
63
+ created_at: toDatabaseDate(timestamp),
64
64
  member_id: this.model.id
65
65
  };
66
66
  }
@@ -1,7 +1,7 @@
1
1
  const {faker} = require('@faker-js/faker');
2
2
  const {TableImporter} = require('./table-importer');
3
3
  const {luck} = require('../utils/random');
4
- const databaseDate = require('../utils/database-date');
4
+ const {toDatabaseDate} = require('../../../lib/db-date');
5
5
 
6
6
  class MembersProductsImporter extends TableImporter {
7
7
  static table = 'members_products';
@@ -34,7 +34,7 @@ class MembersProductsImporter extends TableImporter {
34
34
  member_id: this.model.id,
35
35
  product_id: this.getProduct().id,
36
36
  sort_order: 0,
37
- expiry_at: this.model.status === 'paid' ? null : (luck(50) ? null : databaseDate.dateToDatabaseString(faker.date.future()))
37
+ expiry_at: this.model.status === 'paid' ? null : (luck(50) ? null : toDatabaseDate(faker.date.future()))
38
38
  };
39
39
  }
40
40
  }
@@ -1,5 +1,6 @@
1
1
  const {TableImporter} = require('./table-importer');
2
- const databaseDate = require('../utils/database-date');
2
+ const {randomDateBetween} = require('../utils/random');
3
+ const {toDatabaseDate} = require('../../../lib/db-date');
3
4
 
4
5
  class MembersStatusEventsImporter extends TableImporter {
5
6
  static table = 'members_status_events';
@@ -31,7 +32,7 @@ class MembersStatusEventsImporter extends TableImporter {
31
32
  member_id: model.id,
32
33
  from_status: null,
33
34
  to_status: 'free',
34
- created_at: databaseDate.dateToDatabaseString(model.created_at)
35
+ created_at: toDatabaseDate(model.created_at)
35
36
  }];
36
37
  if (model.status !== 'free') {
37
38
  this.events.push({
@@ -39,7 +40,7 @@ class MembersStatusEventsImporter extends TableImporter {
39
40
  member_id: model.id,
40
41
  from_status: 'free',
41
42
  to_status: model.status,
42
- created_at: databaseDate.dateToDatabaseString(databaseDate.randomBetween(model.created_at, new Date()))
43
+ created_at: toDatabaseDate(randomDateBetween(model.created_at, new Date()))
43
44
  });
44
45
  }
45
46
  }
@@ -1,6 +1,6 @@
1
1
  const {faker} = require('@faker-js/faker');
2
2
  const {TableImporter} = require('./table-importer');
3
- const databaseDate = require('../utils/database-date');
3
+ const {fromDatabaseDate} = require('../../../lib/db-date');
4
4
 
5
5
  class MembersStripeCustomersImporter extends TableImporter {
6
6
  static table = 'members_stripe_customers';
@@ -35,7 +35,7 @@ class MembersStripeCustomersImporter extends TableImporter {
35
35
  // Only 30% of free members should have a stripe customer = have had a subscription in the past or tried to subscribe
36
36
  // The number should increase the older the member is
37
37
 
38
- const daysSinceMemberCreated = Math.floor((new Date() - databaseDate.parse(this.model.created_at)) / (1000 * 60 * 60 * 24));
38
+ const daysSinceMemberCreated = Math.floor((new Date() - fromDatabaseDate(this.model.created_at)) / (1000 * 60 * 60 * 24));
39
39
  const shouldHaveStripeCustomer = faker.number.int({min: 0, max: 100}) < Math.max(Math.min(daysSinceMemberCreated / 60, 15), 2);
40
40
 
41
41
  if (!shouldHaveStripeCustomer) {
@@ -1,8 +1,8 @@
1
1
  const {faker} = require('@faker-js/faker');
2
2
  const {TableImporter} = require('./table-importer');
3
- const databaseDate = require('../utils/database-date');
4
3
  const generateEvents = require('../utils/event-generator');
5
4
  const {luck} = require('../utils/random');
5
+ const {fromDatabaseDate, toDatabaseDate} = require('../../../lib/db-date');
6
6
 
7
7
  class MembersStripeCustomersSubscriptionsImporter extends TableImporter {
8
8
  static table = 'members_stripe_customers_subscriptions';
@@ -95,7 +95,7 @@ class MembersStripeCustomersSubscriptionsImporter extends TableImporter {
95
95
  (isMonthly ? price.interval === 'month' : price.interval === 'year');
96
96
  });
97
97
  const mrr = createValid ? (isMonthly ? stripePrice.amount : Math.floor(stripePrice.amount / 12)) : 0;
98
- const memberCreatedAt = databaseDate.parse(member.created_at);
98
+ const memberCreatedAt = fromDatabaseDate(member.created_at);
99
99
 
100
100
  const referenceEndDate = this.lastSubscriptionStart ?? new Date();
101
101
 
@@ -193,8 +193,8 @@ class MembersStripeCustomersSubscriptionsImporter extends TableImporter {
193
193
  validStatusses.push({
194
194
  status: 'trialing',
195
195
  cancel_at_period_end: false,
196
- trial_end_at: databaseDate.dateToDatabaseString(endDate),
197
- trial_start_at: databaseDate.dateToDatabaseString(startDate)
196
+ trial_end_at: toDatabaseDate(endDate),
197
+ trial_start_at: toDatabaseDate(startDate)
198
198
  });
199
199
  }
200
200
 
@@ -236,8 +236,8 @@ class MembersStripeCustomersSubscriptionsImporter extends TableImporter {
236
236
  customer_id: customer.customer_id,
237
237
  subscription_id: `sub_${faker.string.alphanumeric(14)}`,
238
238
  stripe_price_id: stripePrice.stripe_price_id,
239
- start_date: databaseDate.dateToDatabaseString(startDate),
240
- created_at: databaseDate.dateToDatabaseString(startDate),
239
+ start_date: toDatabaseDate(startDate),
240
+ created_at: toDatabaseDate(startDate),
241
241
  mrr,
242
242
  plan_id: stripeProduct.stripe_product_id,
243
243
  plan_nickname: `${ghostProduct.name} - ${stripePrice.nickname}`,
@@ -248,7 +248,7 @@ class MembersStripeCustomersSubscriptionsImporter extends TableImporter {
248
248
  // Defaults
249
249
  status: 'active',
250
250
  cancel_at_period_end: false,
251
- current_period_end: databaseDate.dateToDatabaseString(endDate),
251
+ current_period_end: toDatabaseDate(endDate),
252
252
 
253
253
  // Override
254
254
  ...status
@@ -1,6 +1,6 @@
1
1
  const {TableImporter} = require('./table-importer');
2
- const {luck} = require('../utils/random');
3
- const databaseDate = require('../utils/database-date');
2
+ const {luck, randomDateBetween} = require('../utils/random');
3
+ const {toDatabaseDate} = require('../../../lib/db-date');
4
4
 
5
5
  class MembersSubscribeEventsImporter extends TableImporter {
6
6
  static table = 'members_subscribe_events';
@@ -50,7 +50,7 @@ class MembersSubscribeEventsImporter extends TableImporter {
50
50
  return null;
51
51
  }
52
52
 
53
- const createdAt = databaseDate.dateToDatabaseString(databaseDate.randomBetween(this.model.created_at, new Date()));
53
+ const createdAt = toDatabaseDate(randomDateBetween(this.model.created_at, new Date()));
54
54
  const newsletterId = this.newsletters[count % this.newsletters.length].id;
55
55
 
56
56
  return {
@@ -1,7 +1,7 @@
1
1
  const {TableImporter} = require('./table-importer');
2
2
  const {faker} = require('@faker-js/faker');
3
3
  const {luck} = require('../utils/random');
4
- const databaseDate = require('../utils/database-date');
4
+ const {fromDatabaseDate} = require('../../../lib/db-date');
5
5
 
6
6
  class MembersSubscriptionCreatedEventsImporter extends TableImporter {
7
7
  static table = 'members_subscription_created_events';
@@ -48,8 +48,8 @@ class MembersSubscriptionCreatedEventsImporter extends TableImporter {
48
48
  };
49
49
 
50
50
  if (luck(30)) {
51
- const createdAt = databaseDate.parse(this.model.created_at);
52
- const post = this.posts.find(p => p.visibility === 'public' && databaseDate.parse(p.published_at) < createdAt);
51
+ const createdAt = fromDatabaseDate(this.model.created_at);
52
+ const post = this.posts.find(p => p.visibility === 'public' && fromDatabaseDate(p.published_at) < createdAt);
53
53
  if (post) {
54
54
  attribution = {
55
55
  attribution_id: post.id,
@@ -1,7 +1,8 @@
1
1
  const {faker} = require('@faker-js/faker');
2
2
  const errors = require('@tryghost/errors');
3
3
  const {TableImporter} = require('./table-importer');
4
- const databaseDate = require('../utils/database-date');
4
+ const {randomDateBetween} = require('../utils/random');
5
+ const {fromDatabaseDate, toDatabaseDate} = require('../../../lib/db-date');
5
6
 
6
7
  class OfferRedemptionsImporter extends TableImporter {
7
8
  static table = 'offer_redemptions';
@@ -53,7 +54,7 @@ class OfferRedemptionsImporter extends TableImporter {
53
54
  this.subscriptionPool.push({
54
55
  memberId,
55
56
  subscriptionId: subscription.id,
56
- subscriptionCreatedAt: databaseDate.parse(subscription.created_at),
57
+ subscriptionCreatedAt: fromDatabaseDate(subscription.created_at),
57
58
  redemptionEndAt: this.getRedemptionEndDate(subscription.current_period_end),
58
59
  availableOffers: [...matchingOffers],
59
60
  lastRedeemedAt: null
@@ -78,7 +79,7 @@ class OfferRedemptionsImporter extends TableImporter {
78
79
 
79
80
  getRedemptionEndDate(currentPeriodEnd) {
80
81
  const now = new Date();
81
- const endDate = currentPeriodEnd ? databaseDate.parse(currentPeriodEnd) : now;
82
+ const endDate = currentPeriodEnd ? fromDatabaseDate(currentPeriodEnd) : now;
82
83
 
83
84
  return endDate > now ? now : endDate;
84
85
  }
@@ -86,19 +87,18 @@ class OfferRedemptionsImporter extends TableImporter {
86
87
  getCreatedAt(subscriptionState, offer) {
87
88
  const candidateEarliest = new Date(Math.max(
88
89
  subscriptionState.subscriptionCreatedAt.valueOf(),
89
- databaseDate.parse(offer.created_at).valueOf(),
90
+ fromDatabaseDate(offer.created_at).valueOf(),
90
91
  subscriptionState.lastRedeemedAt ? subscriptionState.lastRedeemedAt.valueOf() + 1000 : 0
91
92
  ));
92
93
  const earliest = new Date(Math.min(
93
94
  candidateEarliest.valueOf(),
94
95
  subscriptionState.redemptionEndAt.valueOf()
95
96
  ));
96
- const latest = subscriptionState.redemptionEndAt > earliest ? subscriptionState.redemptionEndAt : earliest;
97
- const createdAt = latest.valueOf() === earliest.valueOf() ? earliest : faker.date.between({from: earliest, to: latest});
97
+ const createdAt = randomDateBetween(earliest, subscriptionState.redemptionEndAt);
98
98
 
99
99
  subscriptionState.lastRedeemedAt = createdAt;
100
100
 
101
- return databaseDate.dateToDatabaseString(createdAt);
101
+ return toDatabaseDate(createdAt);
102
102
  }
103
103
 
104
104
  generate() {
@@ -1,7 +1,7 @@
1
1
  const {TableImporter} = require('./table-importer');
2
2
  const {slugify} = require('@tryghost/string');
3
3
  const {blogStartDate} = require('../utils/blog-info');
4
- const databaseDate = require('../utils/database-date');
4
+ const {toDatabaseDate} = require('../../../lib/db-date');
5
5
 
6
6
  const offerTemplates = [{
7
7
  name: 'Black Friday',
@@ -107,8 +107,8 @@ class OffersImporter extends TableImporter {
107
107
  portal_title: name,
108
108
  portal_description: template.description,
109
109
  redemption_type: 'signup',
110
- created_at: databaseDate.dateToDatabaseString(blogStartDate),
111
- updated_at: databaseDate.dateToDatabaseString(blogStartDate)
110
+ created_at: toDatabaseDate(blogStartDate),
111
+ updated_at: toDatabaseDate(blogStartDate)
112
112
  };
113
113
  }
114
114
  }
@@ -2,7 +2,7 @@ const {faker} = require('@faker-js/faker');
2
2
  const {slugify} = require('@tryghost/string');
3
3
  const {luck} = require('../utils/random');
4
4
  const {TableImporter} = require('./table-importer');
5
- const databaseDate = require('../utils/database-date');
5
+ const {toDatabaseDate} = require('../../../lib/db-date');
6
6
 
7
7
  class PostsImporter extends TableImporter {
8
8
  static table = 'posts';
@@ -53,9 +53,9 @@ class PostsImporter extends TableImporter {
53
53
 
54
54
  return {
55
55
  id,
56
- created_at: databaseDate.dateToDatabaseString(timestamp),
57
- updated_at: databaseDate.dateToDatabaseString(timestamp),
58
- published_at: status === 'published' ? databaseDate.dateToDatabaseString(timestamp) : status === 'scheduled' ? databaseDate.dateToDatabaseString(faker.date.soon({days: 5, refDate: timestamp})) : null,
56
+ created_at: toDatabaseDate(timestamp),
57
+ updated_at: toDatabaseDate(timestamp),
58
+ published_at: status === 'published' ? toDatabaseDate(timestamp) : status === 'scheduled' ? toDatabaseDate(faker.date.soon({days: 5, refDate: timestamp})) : null,
59
59
  uuid: faker.string.uuid(),
60
60
  comment_id: this.type === 'post' ? id : null,
61
61
  title: title,
@@ -1,44 +1,11 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
6
  exports.TableImporter = void 0;
40
7
  const debug_1 = __importDefault(require("@tryghost/debug"));
41
- const databaseDate = __importStar(require("../utils/database-date"));
8
+ const db_date_1 = require("../../../lib/db-date");
42
9
  const node_path_1 = __importDefault(require("node:path"));
43
10
  const node_fs_1 = __importDefault(require("node:fs"));
44
11
  const papaparse_1 = __importDefault(require("papaparse"));
@@ -134,7 +101,7 @@ class TableImporter {
134
101
  mutableObj[key] = value ? 1 : 0;
135
102
  }
136
103
  else if (value instanceof Date) {
137
- mutableObj[key] = databaseDate.dateToDatabaseString(value);
104
+ mutableObj[key] = (0, db_date_1.toDatabaseDate)(value);
138
105
  }
139
106
  else if (value === null) {
140
107
  mutableObj[key] = '\\N';
@@ -1,7 +1,7 @@
1
1
  const {faker} = require('@faker-js/faker');
2
2
  const {slugify} = require('@tryghost/string');
3
3
  const {TableImporter} = require('./table-importer');
4
- const databaseDate = require('../utils/database-date');
4
+ const {toDatabaseDate} = require('../../../lib/db-date');
5
5
 
6
6
  class TagsImporter extends TableImporter {
7
7
  static table = 'tags';
@@ -32,7 +32,7 @@ class TagsImporter extends TableImporter {
32
32
  name: name,
33
33
  slug: slugify(name),
34
34
  description: faker.lorem.sentence(),
35
- created_at: databaseDate.dateToDatabaseString(faker.date.between({from: threeYearsAgo, to: twoYearsAgo}))
35
+ created_at: toDatabaseDate(faker.date.between({from: threeYearsAgo, to: twoYearsAgo}))
36
36
  };
37
37
  }
38
38
  }
@@ -2,7 +2,7 @@ const {TableImporter} = require('./table-importer');
2
2
  const {faker} = require('@faker-js/faker');
3
3
  const {slugify} = require('@tryghost/string');
4
4
  const security = require('@tryghost/security');
5
- const databaseDate = require('../utils/database-date');
5
+ const {toDatabaseDate} = require('../../../lib/db-date');
6
6
 
7
7
  class UsersImporter extends TableImporter {
8
8
  static table = 'users';
@@ -24,7 +24,7 @@ class UsersImporter extends TableImporter {
24
24
  password: await security.password.hash(faker.color.human()),
25
25
  email: faker.internet.email({firstName, lastName}),
26
26
  profile_image: faker.image.avatar(),
27
- created_at: databaseDate.dateToDatabaseString(faker.date.between({from: new Date(2016, 0), to: new Date()}))
27
+ created_at: toDatabaseDate(faker.date.between({from: new Date(2016, 0), to: new Date()}))
28
28
  };
29
29
  }
30
30
  }
@@ -1,6 +1,6 @@
1
1
  const {TableImporter} = require('./table-importer');
2
2
  const {faker} = require('@faker-js/faker');
3
- const databaseDate = require('../utils/database-date');
3
+ const {toDatabaseDate} = require('../../../lib/db-date');
4
4
 
5
5
  class WebMentionsImporter extends TableImporter {
6
6
  static table = 'mentions';
@@ -31,7 +31,7 @@ class WebMentionsImporter extends TableImporter {
31
31
  target: `${this.baseUrl}`,
32
32
  resource_id: null,
33
33
  resource_type: null,
34
- created_at: databaseDate.dateToDatabaseString(faker.date.past()),
34
+ created_at: toDatabaseDate(faker.date.past()),
35
35
  payload: JSON.stringify({}),
36
36
  deleted: 0,
37
37
  verified: 1
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.luck = void 0;
4
+ exports.randomDateBetween = randomDateBetween;
4
5
  const faker_1 = require("@faker-js/faker");
6
+ const db_date_1 = require("../../../lib/db-date");
5
7
  /**
6
8
  * Adds another degree of randomness into some decisions
7
9
  * @param lowerThan Only this % of people will achieve this luck
@@ -12,3 +14,8 @@ const luck = (lowerThan) => faker_1.faker.number.int({
12
14
  max: 100
13
15
  }) <= lowerThan;
14
16
  exports.luck = luck;
17
+ function randomDateBetween(start, end) {
18
+ const earliest = (0, db_date_1.fromDatabaseDate)(start);
19
+ const latest = (0, db_date_1.fromDatabaseDate)(end);
20
+ return latest > earliest ? faker_1.faker.date.between({ from: earliest, to: latest }) : earliest;
21
+ }
@@ -200,9 +200,11 @@ class GhostServer {
200
200
 
201
201
  /**
202
202
  * Add a task that should be called on shutdown
203
+ * @param {() => Promise<any>} task
204
+ * @param {string} [label] - name used in shutdown timing logs
203
205
  */
204
- registerCleanupTask(task) {
205
- this.cleanupTasks.push(task);
206
+ registerCleanupTask(task, label) {
207
+ this.cleanupTasks.push({task, label: label || `cleanup task #${this.cleanupTasks.length + 1}`});
206
208
  }
207
209
 
208
210
  /**
@@ -217,12 +219,24 @@ class GhostServer {
217
219
  */
218
220
  async _stopServer() {
219
221
  const util = require('util');
220
- return util.promisify(this.httpServer.stop)();
222
+ const startTime = Date.now();
223
+ try {
224
+ return await util.promisify(this.httpServer.stop)();
225
+ } finally {
226
+ logging.info(`Shutdown: stopped HTTP server in ${Date.now() - startTime}ms`);
227
+ }
221
228
  }
222
229
 
223
230
  async _cleanup() {
224
- // Wait for all cleanup tasks to finish
225
- return Promise.all(this.cleanupTasks.map(task => task()));
231
+ // Wait for all cleanup tasks to finish, timing each so a slow one is identifiable
232
+ return Promise.all(this.cleanupTasks.map(async ({task, label}) => {
233
+ const startTime = Date.now();
234
+ try {
235
+ return await task();
236
+ } finally {
237
+ logging.info(`Shutdown: ${label} finished in ${Date.now() - startTime}ms`);
238
+ }
239
+ }));
226
240
  }
227
241
 
228
242
  /**
@@ -1,10 +1,79 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DbDate = void 0;
36
+ exports.DbDate = exports.fromDatabaseDate = exports.toDatabaseDate = void 0;
37
+ const luxon_1 = require("luxon");
38
+ const errors = __importStar(require("@tryghost/errors"));
4
39
  const zod_1 = require("zod");
40
+ const DATABASE_DATE_FORMAT = 'yyyy-MM-dd HH:mm:ss';
41
+ const databaseDateInput = zod_1.z.union([zod_1.z.date(), zod_1.z.string(), zod_1.z.number()]);
42
+ const parseDatabaseDateString = (value) => {
43
+ const sql = luxon_1.DateTime.fromSQL(value, { zone: 'utc' });
44
+ return sql.isValid ? sql : luxon_1.DateTime.fromISO(value, { zone: 'utc' });
45
+ };
46
+ const parseDatabaseDate = (date) => {
47
+ const input = databaseDateInput.safeParse(date);
48
+ if (!input.success) {
49
+ throw new errors.InternalServerError({ message: 'Invalid database date' });
50
+ }
51
+ let parsed;
52
+ if (typeof input.data === 'string') {
53
+ parsed = parseDatabaseDateString(input.data);
54
+ }
55
+ else if (typeof input.data === 'number') {
56
+ parsed = luxon_1.DateTime.fromMillis(input.data, { zone: 'utc' });
57
+ }
58
+ else {
59
+ parsed = luxon_1.DateTime.fromJSDate(input.data, { zone: 'utc' });
60
+ }
61
+ if (!parsed.isValid) {
62
+ throw new errors.InternalServerError({ message: 'Invalid database date' });
63
+ }
64
+ return parsed;
65
+ };
66
+ // Raw Knex queries need this UTC datetime format for consistent MySQL and SQLite behavior.
67
+ const toDatabaseDate = (date) => parseDatabaseDate(date).toFormat(DATABASE_DATE_FORMAT);
68
+ exports.toDatabaseDate = toDatabaseDate;
69
+ const fromDatabaseDate = (date) => parseDatabaseDate(date).toJSDate();
70
+ exports.fromDatabaseDate = fromDatabaseDate;
5
71
  // A zod codec for datetime columns: MySQL returns a Date, SQLite a string/number;
6
72
  // normalise to a Date on read and pass a Date through on write.
7
- exports.DbDate = zod_1.z.codec(zod_1.z.union([zod_1.z.date(), zod_1.z.string(), zod_1.z.number()]), zod_1.z.date(), {
8
- decode: value => new Date(value),
73
+ // On SQLite, Knex binds the Date that DbDate passes through as epoch milliseconds (stored as
74
+ // INTEGER) while toDatabaseDate writes TEXT; SQLite orders INTEGER before TEXT, so never compare
75
+ // or order those two representations.
76
+ exports.DbDate = zod_1.z.codec(databaseDateInput, zod_1.z.date(), {
77
+ decode: exports.fromDatabaseDate,
9
78
  encode: date => date
10
79
  });