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
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GiftDelivery = void 0;
7
+ const errors_1 = __importDefault(require("@tryghost/errors"));
8
+ const ghostBookshelf = require('./base');
9
+ const GiftDeliveryModel = ghostBookshelf.Model.extend({
10
+ tableName: 'gift_deliveries',
11
+ hasTimestamps: false,
12
+ defaults: {
13
+ status: 'pending'
14
+ },
15
+ gift() {
16
+ return this.belongsTo('Gift', 'gift_id', 'id');
17
+ }
18
+ }, {
19
+ async destroy() {
20
+ throw new errors_1.default.IncorrectUsageError({ message: 'Cannot destroy GiftDelivery' });
21
+ }
22
+ });
23
+ exports.GiftDelivery = ghostBookshelf.model('GiftDelivery', GiftDeliveryModel);
@@ -4,6 +4,11 @@ const ghostBookshelf = require('./base');
4
4
  const Gift = ghostBookshelf.Model.extend({
5
5
  tableName: 'gifts',
6
6
  hasTimestamps: false,
7
+ defaults() {
8
+ return {
9
+ checkout_started_at: new Date()
10
+ };
11
+ },
7
12
 
8
13
  buyer() {
9
14
  return this.belongsTo('Member', 'buyer_member_id', 'id');
@@ -15,6 +20,10 @@ const Gift = ghostBookshelf.Model.extend({
15
20
 
16
21
  tier() {
17
22
  return this.belongsTo('Product', 'tier_id', 'id');
23
+ },
24
+
25
+ delivery() {
26
+ return this.hasOne('GiftDelivery', 'gift_id', 'id');
18
27
  }
19
28
  }, {
20
29
  async destroy() {
@@ -25,6 +25,7 @@ const {EmailRecipient, EmailRecipients} = require('./email-recipient');
25
25
  const {EmailSpamComplaintEvent} = require('./email-spam-complaint-event');
26
26
  const {Email, Emails} = require('./email');
27
27
  const {Gift} = require('./gift');
28
+ const {GiftDelivery} = require('./gift-delivery');
28
29
  const {Integration, Integrations} = require('./integration');
29
30
  const {Invite, Invites} = require('./invite');
30
31
  const {Job} = require('./job');
@@ -61,7 +62,7 @@ const {Redirect} = require('./redirect');
61
62
  const {setIsRoles, checkUserPermissionsForRole} = require('./role-utils');
62
63
  const {Role, Roles} = require('./role');
63
64
  const {Session, Sessions} = require('./session');
64
- const {Settings, getOrGenerateSiteUuid} = require('./settings');
65
+ const {Settings} = require('./settings');
65
66
  const {SingleUseToken, SingleUseTokens} = require('./single-use-token');
66
67
  const {Snippet, Snippets} = require('./snippet');
67
68
  const {StripeCustomerSubscription} = require('./stripe-customer-subscription');
@@ -111,6 +112,7 @@ exports.EmailSpamComplaintEvent = EmailSpamComplaintEvent;
111
112
  exports.Email = Email;
112
113
  exports.Emails = Emails;
113
114
  exports.Gift = Gift;
115
+ exports.GiftDelivery = GiftDelivery;
114
116
  exports.Integration = Integration;
115
117
  exports.Integrations = Integrations;
116
118
  exports.Invite = Invite;
@@ -166,7 +168,6 @@ exports.Roles = Roles;
166
168
  exports.Session = Session;
167
169
  exports.Sessions = Sessions;
168
170
  exports.Settings = Settings;
169
- exports.getOrGenerateSiteUuid = getOrGenerateSiteUuid;
170
171
  exports.SingleUseToken = SingleUseToken;
171
172
  exports.SingleUseTokens = SingleUseTokens;
172
173
  exports.Snippet = Snippet;
@@ -18,6 +18,8 @@ const messages = {
18
18
  const internalContext = {context: {internal: true}};
19
19
  let Settings;
20
20
  let defaultSettings;
21
+ const imageSettingKeys = ['cover_image', 'logo', 'icon', 'portal_button_icon', 'og_image', 'twitter_image', 'pintura_js_url', 'pintura_css_url'];
22
+ const navigationSettingKeys = ['navigation', 'secondary_navigation'];
21
23
 
22
24
  const doBlock = fn => fn();
23
25
 
@@ -103,6 +105,36 @@ function getDefaultSettings() {
103
105
  return defaultSettings;
104
106
  }
105
107
 
108
+ function transformNavigationIconUrls(value, transformUrl) {
109
+ if (!value) {
110
+ return value;
111
+ }
112
+
113
+ let navigationItems;
114
+ try {
115
+ navigationItems = _.isString(value) ? JSON.parse(value) : value;
116
+ } catch (e) {
117
+ return value;
118
+ }
119
+
120
+ if (!_.isArray(navigationItems)) {
121
+ return value;
122
+ }
123
+
124
+ const transformed = navigationItems.map((item) => {
125
+ if (!_.isObject(item) || _.isFunction(item) || !item.icon) {
126
+ return item;
127
+ }
128
+
129
+ return {
130
+ ...item,
131
+ icon: transformUrl(item.icon)
132
+ };
133
+ });
134
+
135
+ return _.isString(value) ? JSON.stringify(transformed) : transformed;
136
+ }
137
+
106
138
  // Each setting is saved as a separate row in the database,
107
139
  // but the overlying API treats them as a single key:value mapping
108
140
  Settings = ghostBookshelf.Model.extend({
@@ -179,10 +211,14 @@ Settings = ghostBookshelf.Model.extend({
179
211
  },
180
212
 
181
213
  formatOnWrite(attrs) {
182
- if (attrs.value && ['cover_image', 'logo', 'icon', 'portal_button_icon', 'og_image', 'twitter_image', 'pintura_js_url', 'pintura_css_url'].includes(attrs.key)) {
214
+ if (attrs.value && imageSettingKeys.includes(attrs.key)) {
183
215
  attrs.value = urlUtils.toTransformReady(attrs.value);
184
216
  }
185
217
 
218
+ if (attrs.value && navigationSettingKeys.includes(attrs.key)) {
219
+ attrs.value = transformNavigationIconUrls(attrs.value, urlUtils.toTransformReady.bind(urlUtils));
220
+ }
221
+
186
222
  return attrs;
187
223
  },
188
224
 
@@ -201,10 +237,14 @@ Settings = ghostBookshelf.Model.extend({
201
237
  }
202
238
 
203
239
  // transform URLs from __GHOST_URL__ to absolute
204
- if (['cover_image', 'logo', 'icon', 'portal_button_icon', 'og_image', 'twitter_image', 'pintura_js_url', 'pintura_css_url'].includes(attrs.key)) {
240
+ if (imageSettingKeys.includes(attrs.key)) {
205
241
  attrs.value = urlUtils.transformReadyToAbsolute(attrs.value);
206
242
  }
207
243
 
244
+ if (navigationSettingKeys.includes(attrs.key)) {
245
+ attrs.value = transformNavigationIconUrls(attrs.value, urlUtils.transformReadyToAbsolute.bind(urlUtils));
246
+ }
247
+
208
248
  return attrs;
209
249
  }
210
250
  }, {
@@ -478,6 +518,5 @@ Settings = ghostBookshelf.Model.extend({
478
518
  });
479
519
 
480
520
  module.exports = {
481
- Settings: ghostBookshelf.model('Settings', Settings),
482
- getOrGenerateSiteUuid: getOrGenerateSiteUuid
521
+ Settings: ghostBookshelf.model('Settings', Settings)
483
522
  };
@@ -13,7 +13,7 @@ const dequal_1 = require("dequal");
13
13
  const lexical_1 = __importDefault(require("../../lib/lexical"));
14
14
  const url_utils_1 = __importDefault(require("../../../shared/url-utils"));
15
15
  const constants_1 = require("../member-welcome-emails/constants");
16
- const database_date_1 = require("./database-date");
16
+ const db_date_1 = require("../../lib/db-date");
17
17
  const stale_lock_cutoff_1 = require("./stale-lock-cutoff");
18
18
  const HOUR_MS = 60 * 60 * 1000;
19
19
  const DEFAULT_WELCOME_EMAIL_AUTOMATIONS = [{
@@ -90,7 +90,7 @@ function createDatabaseAutomationsRepository({ knex, fakeWaitHoursMultiplier })
90
90
  const updatedAutomation = await updateAutomation(trx, {
91
91
  ...automation,
92
92
  status: data.status,
93
- updated_at: (0, database_date_1.toDatabaseDate)(now)
93
+ updated_at: (0, db_date_1.toDatabaseDate)(now)
94
94
  });
95
95
  await replaceAutomationGraph(trx, updatedAutomation.id, data.actions, data.edges);
96
96
  if (updatedAutomation.status === 'inactive') {
@@ -127,7 +127,7 @@ function createDatabaseAutomationsRepository({ knex, fakeWaitHoursMultiplier })
127
127
  .update({
128
128
  email_sent_count: trx.raw('COALESCE(??, 0) + ?', ['email_sent_count', 1])
129
129
  });
130
- const now = (0, database_date_1.toDatabaseDate)(new Date());
130
+ const now = (0, db_date_1.toDatabaseDate)(new Date());
131
131
  await trx('automated_email_recipients').insert({
132
132
  id: (0, bson_objectid_1.default)().toHexString(),
133
133
  member_id: options.memberId,
@@ -281,7 +281,7 @@ async function ensureDefaultAutomations(trx) {
281
281
  }
282
282
  }
283
283
  async function ensureAutomation(trx, defaults) {
284
- const now = (0, database_date_1.toDatabaseDate)(new Date());
284
+ const now = (0, db_date_1.toDatabaseDate)(new Date());
285
285
  const id = (0, bson_objectid_1.default)().toHexString();
286
286
  await trx('automations')
287
287
  .insert({
@@ -320,7 +320,7 @@ async function ensureWelcomeEmailAction(trx, automationId) {
320
320
  if (!email) {
321
321
  return;
322
322
  }
323
- const now = (0, database_date_1.toDatabaseDate)(new Date());
323
+ const now = (0, db_date_1.toDatabaseDate)(new Date());
324
324
  const actionId = (0, bson_objectid_1.default)().toHexString();
325
325
  await insertActions(trx, [{
326
326
  id: actionId,
@@ -350,7 +350,7 @@ async function trigger(trx, options) {
350
350
  return;
351
351
  }
352
352
  const now = new Date();
353
- const nowString = (0, database_date_1.toDatabaseDate)(now);
353
+ const nowString = (0, db_date_1.toDatabaseDate)(now);
354
354
  const readyAt = getReadyAtForAction(firstAction, now, fakeWaitHoursMultiplier);
355
355
  const run = {
356
356
  id: (0, bson_objectid_1.default)().toHexString(),
@@ -369,14 +369,14 @@ async function trigger(trx, options) {
369
369
  });
370
370
  }
371
371
  async function insertRunStep(trx, { automationRunId, automationActionRevisionId, now, readyAt }) {
372
- const nowString = (0, database_date_1.toDatabaseDate)(now);
372
+ const nowString = (0, db_date_1.toDatabaseDate)(now);
373
373
  await trx('automation_run_steps').insert({
374
374
  id: (0, bson_objectid_1.default)().toHexString(),
375
375
  created_at: nowString,
376
376
  updated_at: nowString,
377
377
  automation_run_id: automationRunId,
378
378
  automation_action_revision_id: automationActionRevisionId,
379
- ready_at: (0, database_date_1.toDatabaseDate)(readyAt)
379
+ ready_at: (0, db_date_1.toDatabaseDate)(readyAt)
380
380
  });
381
381
  }
382
382
  async function fetchAndLockSteps(trx, limit) {
@@ -391,9 +391,9 @@ async function fetchAndLockSteps(trx, limit) {
391
391
  // 2. Try to lock those rows.
392
392
  // 3. Select any rows we successfully locked.
393
393
  const now = new Date();
394
- const nowString = (0, database_date_1.toDatabaseDate)(now);
394
+ const nowString = (0, db_date_1.toDatabaseDate)(now);
395
395
  const staleLockCutoff = (0, stale_lock_cutoff_1.getStaleLockCutoff)(now);
396
- const staleLockCutoffString = (0, database_date_1.toDatabaseDate)(staleLockCutoff);
396
+ const staleLockCutoffString = (0, db_date_1.toDatabaseDate)(staleLockCutoff);
397
397
  const lockId = node_crypto_1.default.randomUUID();
398
398
  // 1. Select up to `limit` candidate rows.
399
399
  const candidates = await trx('automation_run_steps')
@@ -461,17 +461,17 @@ async function findNextPendingReadyAt(trx, staleLockCutoff) {
461
461
  .where((builder) => {
462
462
  builder
463
463
  .whereNull('locked_by')
464
- .orWhere('locked_at', '<', (0, database_date_1.toDatabaseDate)(staleLockCutoff));
464
+ .orWhere('locked_at', '<', (0, db_date_1.toDatabaseDate)(staleLockCutoff));
465
465
  })
466
466
  .orderBy('ready_at')
467
467
  .first();
468
- return row?.next_ready_at ? (0, database_date_1.fromDatabaseDate)(row.next_ready_at) : null;
468
+ return row?.next_ready_at ? (0, db_date_1.fromDatabaseDate)(row.next_ready_at) : null;
469
469
  }
470
470
  function buildStepToRun(row) {
471
471
  const base = {
472
472
  id: row.id,
473
473
  step_attempts: row.step_attempts,
474
- ready_at: (0, database_date_1.fromDatabaseDate)(row.ready_at),
474
+ ready_at: (0, db_date_1.fromDatabaseDate)(row.ready_at),
475
475
  locked_by: row.locked_by,
476
476
  automation_run_id: row.automation_run_id,
477
477
  automation_id: row.automation_id,
@@ -566,7 +566,7 @@ async function findNextActionRevision(trx, sourceActionId) {
566
566
  return row ?? null;
567
567
  }
568
568
  async function markStepTerminal(trx, step, status) {
569
- const nowString = (0, database_date_1.toDatabaseDate)(new Date());
569
+ const nowString = (0, db_date_1.toDatabaseDate)(new Date());
570
570
  return await updateStep(trx, step, {
571
571
  status,
572
572
  finished_at: nowString,
@@ -578,12 +578,12 @@ async function retryStep(trx, step, retryAt) {
578
578
  await markStepTerminal(trx, step, 'automation disabled');
579
579
  return false;
580
580
  }
581
- const nowString = (0, database_date_1.toDatabaseDate)(new Date());
581
+ const nowString = (0, db_date_1.toDatabaseDate)(new Date());
582
582
  return await updateStep(trx, step, {
583
583
  status: 'pending',
584
584
  started_at: null,
585
585
  finished_at: null,
586
- ready_at: (0, database_date_1.toDatabaseDate)(retryAt),
586
+ ready_at: (0, db_date_1.toDatabaseDate)(retryAt),
587
587
  updated_at: nowString
588
588
  });
589
589
  }
@@ -608,8 +608,8 @@ function getReadyAtForAction(action, now, fakeWaitHoursMultiplier) {
608
608
  }
609
609
  }
610
610
  async function cancelCancelablePendingStepsForAutomation(trx, automationId, now) {
611
- const nowString = (0, database_date_1.toDatabaseDate)(now);
612
- const staleLockCutoff = (0, database_date_1.toDatabaseDate)((0, stale_lock_cutoff_1.getStaleLockCutoff)(now));
611
+ const nowString = (0, db_date_1.toDatabaseDate)(now);
612
+ const staleLockCutoff = (0, db_date_1.toDatabaseDate)((0, stale_lock_cutoff_1.getStaleLockCutoff)(now));
613
613
  await trx('automation_run_steps')
614
614
  .update({
615
615
  status: 'automation disabled',
@@ -697,14 +697,20 @@ async function loadAutomationBySlug(trx, slug) {
697
697
  return row ?? null;
698
698
  }
699
699
  async function loadAutomations(trx) {
700
+ const inProgressRuns = trx('automation_run_steps')
701
+ .distinct('automation_run_id')
702
+ .where('status', 'pending')
703
+ .as('in_progress_runs');
700
704
  const runStats = trx('automation_runs')
701
- .select('automation_id')
702
- .max({ last_run_created_at: 'created_at' })
705
+ .select('automation_runs.automation_id')
706
+ .max({ last_run_created_at: 'automation_runs.created_at' })
703
707
  .count({ total_run_count: '*' })
704
- .groupBy('automation_id')
708
+ .count({ in_progress_run_count: 'in_progress_runs.automation_run_id' })
709
+ .leftJoin(inProgressRuns, 'automation_runs.id', 'in_progress_runs.automation_run_id')
710
+ .groupBy('automation_runs.automation_id')
705
711
  .as('run_stats');
706
712
  return await trx('automations')
707
- .select('automations.id', 'automations.slug', 'automations.name', 'automations.status', 'automations.created_at', 'automations.updated_at', 'run_stats.last_run_created_at', 'run_stats.total_run_count')
713
+ .select('automations.id', 'automations.slug', 'automations.name', 'automations.status', 'automations.created_at', 'automations.updated_at', 'run_stats.last_run_created_at', 'run_stats.total_run_count', 'run_stats.in_progress_run_count')
708
714
  .leftJoin(runStats, 'automations.id', 'run_stats.automation_id')
709
715
  .orderBy('automations.name');
710
716
  }
@@ -724,7 +730,7 @@ async function replaceAutomationGraph(trx, automationId, submittedActions, edges
724
730
  const submittedActionIds = new Set(actions.map(action => action.id));
725
731
  const actionIdsWithOwners = await loadActionIdsWithOwners(trx, [...submittedActionIds]);
726
732
  const latestRevisionByActionId = new Map((await loadLatestActionRevisions(trx, [...submittedActionIds])).map(revision => [revision.action_id, revision]));
727
- const now = (0, database_date_1.toDatabaseDate)(new Date());
733
+ const now = (0, db_date_1.toDatabaseDate)(new Date());
728
734
  const actionsToInsert = [];
729
735
  const revisionsToInsert = [];
730
736
  for (const action of actions) {
@@ -902,14 +908,14 @@ async function insertActionRevisions(trx, revisions) {
902
908
  }
903
909
  function getNextRevisionCreatedAt(latestCreatedAt, requestedCreatedAt) {
904
910
  if (!latestCreatedAt) {
905
- return (0, database_date_1.toDatabaseDate)(requestedCreatedAt);
911
+ return (0, db_date_1.toDatabaseDate)(requestedCreatedAt);
906
912
  }
907
- const requestedTime = (0, database_date_1.fromDatabaseDate)(requestedCreatedAt).getTime();
908
- const latestTime = (0, database_date_1.fromDatabaseDate)(latestCreatedAt).getTime();
913
+ const requestedTime = (0, db_date_1.fromDatabaseDate)(requestedCreatedAt).getTime();
914
+ const latestTime = (0, db_date_1.fromDatabaseDate)(latestCreatedAt).getTime();
909
915
  if (requestedTime > latestTime) {
910
- return (0, database_date_1.toDatabaseDate)(requestedCreatedAt);
916
+ return (0, db_date_1.toDatabaseDate)(requestedCreatedAt);
911
917
  }
912
- return (0, database_date_1.toDatabaseDate)(new Date(latestTime + 1000));
918
+ return (0, db_date_1.toDatabaseDate)(new Date(latestTime + 1000));
913
919
  }
914
920
  function buildActionRevision(actionId, action, createdAt) {
915
921
  switch (action.type) {
@@ -989,13 +995,14 @@ function buildAutomationBrowseResult(automation) {
989
995
  return {
990
996
  ...buildAutomationSummary(automation),
991
997
  stats: {
992
- last_run_created_at: automation.last_run_created_at ? (0, database_date_1.fromDatabaseDate)(automation.last_run_created_at) : null,
993
- total_run_count: Number(automation.total_run_count ?? 0)
998
+ last_run_created_at: automation.last_run_created_at ? (0, db_date_1.fromDatabaseDate)(automation.last_run_created_at) : null,
999
+ total_run_count: Number(automation.total_run_count ?? 0),
1000
+ in_progress_run_count: Number(automation.in_progress_run_count ?? 0)
994
1001
  }
995
1002
  };
996
1003
  }
997
1004
  function serializeDate(date) {
998
- const normalizedDate = (0, database_date_1.fromDatabaseDate)(date);
1005
+ const normalizedDate = (0, db_date_1.fromDatabaseDate)(date);
999
1006
  normalizedDate.setMilliseconds(0);
1000
1007
  return normalizedDate.toISOString();
1001
1008
  }
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.poll = void 0;
7
- const mailgun_message_id_1 = require("./mailgun-message-id");
7
+ const mailgun_message_id_1 = require("../lib/mailgun-message-id");
8
8
  const logging_1 = __importDefault(require("@tryghost/logging"));
9
9
  const errors_1 = __importDefault(require("@tryghost/errors"));
10
10
  const constants_1 = require("../member-welcome-emails/constants");
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parse = void 0;
7
+ var parse_1 = require("./parse");
8
+ Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return __importDefault(parse_1).default; } });
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = parse;
7
+ const papaparse_1 = __importDefault(require("papaparse"));
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const errors = require('@tryghost/errors');
10
+ // A column named after an Object.prototype member is unsafe as a key.
11
+ function isSafeColumnName(name) {
12
+ return !(name in Object.prototype);
13
+ }
14
+ // Quote errors mean cells are being glued together and rows can no longer be told
15
+ // apart, so the file is refused. Ragged rows (FieldMismatch) and delimiter guesses
16
+ // stay tolerated: an overflow cell is dropped per row, not per file.
17
+ function isFatal(error) {
18
+ return error.type === 'Quotes';
19
+ }
20
+ // headerMapping renames headers to the columns they emit under; unmapped columns
21
+ // carry through, and one mapped to an empty target is dropped.
22
+ async function parse(path, headerMapping) {
23
+ // Buffered rather than streamed: files are bounded by the interim row cap, and
24
+ // papaparse's stream mode drops results.errors, which is what catches a
25
+ // malformed quoted field before it imports as garbage.
26
+ const content = (await fs_extra_1.default.readFile(path, 'utf8')).replace(/^\ufeff/, '');
27
+ const parsed = papaparse_1.default.parse(content, {
28
+ header: true,
29
+ skipEmptyLines: true
30
+ });
31
+ const fatal = parsed.errors.find(isFatal);
32
+ if (fatal) {
33
+ throw new errors.ValidationError({ message: `${fatal.code}: ${fatal.message}` });
34
+ }
35
+ const rows = [];
36
+ for (const parsedRow of parsed.data) {
37
+ const row = {};
38
+ for (const [header, value] of Object.entries(parsedRow)) {
39
+ // non-string values are papaparse's __parsed_extra overflow from ragged rows
40
+ if (typeof value !== 'string') {
41
+ continue;
42
+ }
43
+ // hasOwn: a prototype-named header must not match an inherited method on the mapping
44
+ if (headerMapping && Object.hasOwn(headerMapping, header)) {
45
+ if (!headerMapping[header]) {
46
+ continue;
47
+ }
48
+ row[headerMapping[header]] = value;
49
+ }
50
+ else if (isSafeColumnName(header)) {
51
+ row[header] = value;
52
+ }
53
+ }
54
+ if (!Object.keys(row).length) {
55
+ continue;
56
+ }
57
+ rows.push(row);
58
+ }
59
+ return rows;
60
+ }
@@ -0,0 +1,222 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const moment_timezone_1 = __importDefault(require("moment-timezone"));
40
+ const post_data_1 = __importStar(require("./post-data"));
41
+ const errors = require('@tryghost/errors');
42
+ const tpl = require('@tryghost/tpl');
43
+ const messages = {
44
+ unreadableFile: 'The file could not be parsed as a CSV file.',
45
+ tooManyPosts: 'This file contains more than {max} posts. Imports are temporarily limited to {max} posts at a time — please split the file into smaller files and try again.',
46
+ allWritesFailed: 'Content import failed to write all {count} attempted {postNoun}.',
47
+ urlResolutionFailed: 'Content import could not resolve a URL for {count} created {postNoun}.'
48
+ };
49
+ const MAX_POSTS = 100;
50
+ // Two batch tags for every imported post: a date stamp matching the JSON
51
+ // importer's (api/endpoints/db.js), and a run tag unique to this import, which
52
+ // the report milestones key on.
53
+ function buildImportTagNames(runId, timezone, now) {
54
+ return [
55
+ `#Import ${(0, moment_timezone_1.default)(now).tz(timezone).format('YYYY-MM-DD HH:mm')}`,
56
+ `#Import Run ${runId}`
57
+ ];
58
+ }
59
+ class ContentCSVImporter {
60
+ _readRows;
61
+ _posts;
62
+ _getHtmlToLexical;
63
+ _addJob;
64
+ _report;
65
+ _store;
66
+ _urlForPost;
67
+ _newRunId;
68
+ _getTimezone;
69
+ _now;
70
+ constructor({ readRows, posts, getHtmlToLexical, addJob, report, store, urlForPost, newRunId, getTimezone, now = () => new Date() }) {
71
+ this._readRows = readRows;
72
+ this._posts = posts;
73
+ this._getHtmlToLexical = getHtmlToLexical;
74
+ this._addJob = addJob;
75
+ this._report = report;
76
+ this._store = store;
77
+ this._urlForPost = urlForPost;
78
+ this._newRunId = newRunId;
79
+ this._getTimezone = getTimezone;
80
+ this._now = now;
81
+ }
82
+ async importCSV(request) {
83
+ let rows;
84
+ try {
85
+ rows = await this._readRows(request.filePath);
86
+ }
87
+ catch (error) {
88
+ throw new errors.ValidationError({
89
+ message: tpl(messages.unreadableFile),
90
+ err: error
91
+ });
92
+ }
93
+ // Temporary while import state is held in memory: the durable job
94
+ // system milestone removes the cap.
95
+ if (rows.length > MAX_POSTS) {
96
+ throw new errors.ValidationError({
97
+ message: tpl(messages.tooManyPosts, { max: MAX_POSTS })
98
+ });
99
+ }
100
+ const runId = this._newRunId();
101
+ const importTagNames = buildImportTagNames(runId, this._getTimezone(), this._now());
102
+ this._store.create(runId, rows.length);
103
+ this._addJob({
104
+ job: () => this.runImportJob(runId, importTagNames, rows),
105
+ offloaded: false,
106
+ name: 'content-import'
107
+ });
108
+ return { importId: runId, total: rows.length };
109
+ }
110
+ // Must resolve in every case: the job manager reads a rejected inline job as a
111
+ // defect in the job itself, and there is no retry behind it.
112
+ async runImportJob(runId, importTagNames, rows) {
113
+ let urlFailureCount = 0;
114
+ let firstUrlFailure;
115
+ try {
116
+ const htmlToLexical = this._getHtmlToLexical();
117
+ let attemptedWrites = 0;
118
+ let successfulWrites = 0;
119
+ let failedWrites = 0;
120
+ let firstWriteFailure;
121
+ for (const [index, row] of rows.entries()) {
122
+ const line = index + 2;
123
+ let data;
124
+ try {
125
+ data = (0, post_data_1.default)(row, htmlToLexical, importTagNames);
126
+ }
127
+ catch (error) {
128
+ if (error instanceof post_data_1.RowSkipped) {
129
+ this._store.record(runId, {
130
+ line,
131
+ title: row.title || null,
132
+ status: 'skipped',
133
+ reason: messageOf(error)
134
+ });
135
+ continue;
136
+ }
137
+ // Anything other than an expected source-row refusal is an importer
138
+ // failure. Stop the run before misclassifying it as a lost write.
139
+ throw error;
140
+ }
141
+ attemptedWrites += 1;
142
+ let post;
143
+ try {
144
+ // options.importing preserves the supplied timestamps and keeps the import silent:
145
+ // the webhook, Slack, IndexNow and mention consumers all stand down on it, and a
146
+ // newsletter can only be attached in the API layer (post-scheduling does not check
147
+ // it, one reason status is never 'scheduled'). Pinned by
148
+ // test/e2e-webhooks/posts-importer.test.js. A fresh options object per row: the
149
+ // model layer mutates it.
150
+ post = await this._posts.create(data, {
151
+ importing: true,
152
+ context: { internal: true }
153
+ });
154
+ successfulWrites += 1;
155
+ }
156
+ catch (error) {
157
+ if (failedWrites === 0) {
158
+ firstWriteFailure = error;
159
+ }
160
+ failedWrites += 1;
161
+ this._store.record(runId, {
162
+ line,
163
+ title: row.title,
164
+ status: 'failed',
165
+ reason: messageOf(error)
166
+ });
167
+ continue;
168
+ }
169
+ const outcome = {
170
+ line,
171
+ title: row.title,
172
+ status: 'created',
173
+ postId: post.id
174
+ };
175
+ try {
176
+ outcome.url = this._urlForPost(post);
177
+ }
178
+ catch (error) {
179
+ if (urlFailureCount === 0) {
180
+ firstUrlFailure = error;
181
+ }
182
+ urlFailureCount += 1;
183
+ }
184
+ this._store.record(runId, outcome);
185
+ }
186
+ if (attemptedWrites > 0 && successfulWrites === 0 && failedWrites === attemptedWrites) {
187
+ this._report(new errors.InternalServerError({
188
+ message: tpl(messages.allWritesFailed, {
189
+ count: failedWrites,
190
+ postNoun: failedWrites === 1 ? 'post' : 'posts'
191
+ }),
192
+ err: firstWriteFailure
193
+ }));
194
+ }
195
+ this.reportUrlFailures(urlFailureCount, firstUrlFailure);
196
+ this._store.finish(runId);
197
+ }
198
+ catch (error) {
199
+ this.reportUrlFailures(urlFailureCount, firstUrlFailure);
200
+ this._report(error);
201
+ this._store.fail(runId, messageOf(error));
202
+ }
203
+ }
204
+ reportUrlFailures(count, firstFailure) {
205
+ if (count > 0) {
206
+ this._report(new errors.InternalServerError({
207
+ message: tpl(messages.urlResolutionFailed, {
208
+ count,
209
+ postNoun: count === 1 ? 'post' : 'posts'
210
+ }),
211
+ err: firstFailure
212
+ }));
213
+ }
214
+ }
215
+ }
216
+ function messageOf(error) {
217
+ if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string' && error.message) {
218
+ return error.message;
219
+ }
220
+ return 'Unknown error';
221
+ }
222
+ exports.default = ContentCSVImporter;