ghost 6.61.0 → 6.62.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 (472) hide show
  1. package/components/ghost-storage-base-3.0.0.tgz +0 -0
  2. package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
  3. package/components/tryghost-adapter-base-jobs-0.0.0.tgz +0 -0
  4. package/components/tryghost-adapter-base-redirects-0.0.0.tgz +0 -0
  5. package/components/tryghost-adapter-base-route-settings-0.0.0.tgz +0 -0
  6. package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
  7. package/components/tryghost-adapter-base-sso-0.1.3.tgz +0 -0
  8. package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
  9. package/components/tryghost-checkout-0.0.0.tgz +0 -0
  10. package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
  11. package/components/tryghost-i18n-0.0.0.tgz +0 -0
  12. package/components/tryghost-kg-card-factory-5.2.4.tgz +0 -0
  13. package/components/tryghost-kg-clean-basic-html-4.3.4.tgz +0 -0
  14. package/components/tryghost-kg-converters-1.2.5.tgz +0 -0
  15. package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
  16. package/components/tryghost-kg-default-nodes-2.2.1.tgz +0 -0
  17. package/components/tryghost-kg-default-transforms-1.3.4.tgz +0 -0
  18. package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
  19. package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
  20. package/components/tryghost-kg-markdown-html-renderer-7.2.4.tgz +0 -0
  21. package/components/tryghost-kg-utils-1.1.4.tgz +0 -0
  22. package/components/tryghost-nql-string-0.0.0.tgz +0 -0
  23. package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
  24. package/content/settings/README.md +1 -1
  25. package/content/themes/casper/.gitignore +28 -0
  26. package/content/themes/casper/assets/built/casper.js +1 -1
  27. package/content/themes/casper/assets/built/screen.css +1 -1
  28. package/content/themes/casper/assets/css/screen.css +2 -1
  29. package/content/themes/casper/assets/js/lightbox.js +63 -46
  30. package/content/themes/casper/package.json +6 -6
  31. package/content/themes/casper/page.hbs +1 -0
  32. package/content/themes/casper/post.hbs +1 -0
  33. package/content/themes/source/.gitignore +28 -0
  34. package/content/themes/source/assets/built/screen.css +1 -1
  35. package/content/themes/source/assets/built/source.js +1 -1
  36. package/content/themes/source/assets/css/screen.css +2 -1
  37. package/content/themes/source/assets/js/lightbox.js +62 -45
  38. package/content/themes/source/assets/js/main.js +2 -2
  39. package/content/themes/source/package.json +4 -4
  40. package/content/themes/source/partials/feature-image.hbs +1 -0
  41. package/core/boot.js +54 -22
  42. package/core/built/admin/assets/Combination-B0l4MKVh.js +41 -0
  43. package/core/built/admin/assets/InterVariable-DiVDrmQJ.woff2 +0 -0
  44. package/core/built/admin/assets/InterVariable-Italic-FCBEiFp6.woff2 +0 -0
  45. package/core/built/admin/assets/{about-Bve7IJS2.js → about-Dyae_UT6.js} +1 -1
  46. package/core/built/admin/assets/account-migration-CESa0mnX.js +1 -0
  47. package/core/built/admin/assets/activitypub/{account-migration-C-aej4x_.js → account-migration-By69CgCp.js} +16 -16
  48. package/core/built/admin/assets/activitypub/activitypub.js +9 -9
  49. package/core/built/admin/assets/activitypub/{bluesky-sharing-BXkA5mqV.js → bluesky-sharing-BJcPMrDd.js} +36 -36
  50. package/core/built/admin/assets/activitypub/{content-formatters-CHcyMFk4.js → content-formatters-3rTHg3mp.js} +3 -3
  51. package/core/built/admin/assets/activitypub/{domain-B3ihkPde.js → domain-DUR4xSkV.js} +121 -121
  52. package/core/built/admin/assets/activitypub/{edit-profile-DsZM3rTX.js → edit-profile-BqtfzxCp.js} +40 -40
  53. package/core/built/admin/assets/activitypub/{explore-BodNrZgR.js → explore-BxuJd-J4.js} +26 -26
  54. package/core/built/admin/assets/activitypub/{feed-PwH1iJ84.js → feed-BM1TcksA.js} +48 -48
  55. package/core/built/admin/assets/activitypub/{field-D06-kmOJ.js → field-Dn7HHx2-.js} +2 -2
  56. package/core/built/admin/assets/activitypub/inbox-CCjkuow2.js +1284 -0
  57. package/core/built/admin/assets/activitypub/label-1AA_s9Fa.js +16 -0
  58. package/core/built/admin/assets/activitypub/layout-2-0bw6HN.js +1 -0
  59. package/core/built/admin/assets/activitypub/{moderation-D1Yapsod.js → moderation-CnmxEB8w.js} +91 -91
  60. package/core/built/admin/assets/activitypub/{note-C5zjqq5C.js → note-ZKc_Bnba.js} +23 -23
  61. package/core/built/admin/assets/activitypub/{notifications-B9p8eCHD.js → notifications-CB2tJZau.js} +48 -48
  62. package/core/built/admin/assets/activitypub/{onboarding-CNelD_uF.js → onboarding-BsJ9E5qf.js} +2 -2
  63. package/core/built/admin/assets/activitypub/{onboarding-mat5Hrl0.js → onboarding-CSUJJIlh.js} +2 -2
  64. package/core/built/admin/assets/activitypub/{preferences-BCqUdC3v.js → preferences-0-JZnUkI.js} +1519 -1519
  65. package/core/built/admin/assets/activitypub/{profile-BRCWoFem.js → profile-C35cFIhM.js} +183 -183
  66. package/core/built/admin/assets/activitypub/{routes-BSXT992i.js → routes-BD4vscyc.js} +740 -731
  67. package/core/built/admin/assets/activitypub/settings-BsMROdqh.js +315 -0
  68. package/core/built/admin/assets/activitypub/{site-DHoMupSB.js → site-O0RL9YCN.js} +2 -2
  69. package/core/built/admin/assets/activitypub/{step-1-B-YfsVeO.js → step-1-BMSx7c_X.js} +4 -4
  70. package/core/built/admin/assets/activitypub/{step-3-D6BFMXL_.js → step-3-CLlOZTyt.js} +3 -3
  71. package/core/built/admin/assets/activitypub/{tabs-GpNecvfB.js → tabs-Dxk-b9M-.js} +306 -306
  72. package/core/built/admin/assets/activitypub/{topic-filter-BPDeQQys.js → topic-filter-5yZH6tId.js} +2 -2
  73. package/core/built/admin/assets/activitypub/{use-activity-pub-queries-BoVOxXkV.js → use-activity-pub-queries-CV50qe03.js} +2 -2
  74. package/core/built/admin/assets/activitypub/{use-reply-chain-data-wVBX7swM.js → use-reply-chain-data-Cbvzgjzd.js} +30 -30
  75. package/core/built/admin/assets/activitypub/{users-CoX6FT_K.js → users-BjcoP_HC.js} +7 -5
  76. package/core/built/admin/assets/add-integration-modal-BlF6rs_f.js +1 -0
  77. package/core/built/admin/assets/add-newsletter-modal-CglVQqah.js +1 -0
  78. package/core/built/admin/assets/add-recommendation-modal-wz7BsuaG.js +1 -0
  79. package/core/built/admin/assets/analytics-C4HE1gBg.js +1 -0
  80. package/core/built/admin/assets/{announcement-bar-modal-DM3cYOpo.js → announcement-bar-modal-Ci4zz8jO.js} +1 -1
  81. package/core/built/admin/assets/{api-keys-BLm2NDBh.js → api-keys-Ck-Yteiv.js} +1 -1
  82. package/core/built/admin/assets/{audience-9mwU5TW6.js → audience-CtbA54Sn.js} +1 -1
  83. package/core/built/admin/assets/{automated-emails-te1D5kq-.js → automated-emails-D5VVMND9.js} +1 -1
  84. package/core/built/admin/assets/automations-CzKt5fb8.js +1 -0
  85. package/core/built/admin/assets/bluesky-sharing-BEtJ_gL_.js +1 -0
  86. package/core/built/admin/assets/{breadcrumb-DLiqCyFc.js → breadcrumb-zUrZrBtW.js} +1 -1
  87. package/core/built/admin/assets/{button-B2jcu6bk.js → button-eNMKQWXr.js} +1 -1
  88. package/core/built/admin/assets/{chart-helpers-Fo3bUL3K.js → chart-helpers-CfBKt0vT.js} +1 -1
  89. package/core/built/admin/assets/{checkbox-BfKktMph.js → checkbox-CgOn_tKW.js} +1 -1
  90. package/core/built/admin/assets/{chunk.524.60ca4692d91c6022042f.js → chunk.524.a697dee88db67fdf6021.js} +4 -4
  91. package/core/built/admin/assets/{chunk.582.525c7617c2d9e94c940a.js → chunk.582.5ed5d124cfca9c259b9c.js} +6 -6
  92. package/core/built/admin/assets/{chunk.896.435fe0637944d4a69810.js → chunk.598.7e056fabb47ce7dbd73f.js} +14 -14
  93. package/core/built/admin/assets/{code-editor-view-CYVu4EW6.js → code-editor-view-BYNoAoNV.js} +1 -1
  94. package/core/built/admin/assets/{code-editor-view-DCQJxsfU.js → code-editor-view-K2wI-I5f.js} +1 -1
  95. package/core/built/admin/assets/color-picker-Ddw80xX6.js +3 -0
  96. package/core/built/admin/assets/color-picker-field-D9nUhvA7.js +1 -0
  97. package/core/built/admin/assets/combobox-w9j8PPQT.js +1 -0
  98. package/core/built/admin/assets/command-BfpHORBv.js +1 -0
  99. package/core/built/admin/assets/comments-BLmCmyCs.js +1 -0
  100. package/core/built/admin/assets/confirmation-context-CBM8G0Vj.js +1 -0
  101. package/core/built/admin/assets/{content-api-modal-CyQEC3fr.js → content-api-modal-TXRz3U7S.js} +1 -1
  102. package/core/built/admin/assets/context-menu-_QMdd3kp.js +1 -0
  103. package/core/built/admin/assets/{copy-field-B3M1s78L.js → copy-field-6N01S07E.js} +1 -1
  104. package/core/built/admin/assets/create-ghost-browse-value-source-qIBymvvf.js +1 -0
  105. package/core/built/admin/assets/custom-field-type-option-BddFlJFa.js +1 -0
  106. package/core/built/admin/assets/custom-integration-modal-DmNquxUV.js +1 -0
  107. package/core/built/admin/assets/{date-range-select-DD2RKkgB.js → date-range-select-uvz_DtPh.js} +1 -1
  108. package/core/built/admin/assets/design-and-theme-modal-BSmo12Op.js +2 -0
  109. package/core/built/admin/assets/dirty-confirm-dialog-BZ1iytEd.js +1 -0
  110. package/core/built/admin/assets/{disabled-sources-indicator-Cvy47bE2.js → disabled-sources-indicator-DMRrxsiJ.js} +1 -1
  111. package/core/built/admin/assets/dist-Dcsslyo72.js +1 -0
  112. package/core/built/admin/assets/domain-BNYrt0aD.js +1 -0
  113. package/core/built/admin/assets/dropzone-D7nFdAe5.js +5 -0
  114. package/core/built/admin/assets/edit-profile-C5Asm4LA.js +1 -0
  115. package/core/built/admin/assets/editor-REokrzci.js +8 -0
  116. package/core/built/admin/assets/{embed-signup-form-modal-DeBMLxlL.js → embed-signup-form-modal-Ds6mPEi5.js} +1 -1
  117. package/core/built/admin/assets/en-B1jX3WMs.js +1 -0
  118. package/core/built/admin/assets/explore-UY10Egsp.js +1 -0
  119. package/core/built/admin/assets/feature-image-placeholder-D1iF_8bc.js +1 -0
  120. package/core/built/admin/assets/feed-81C8mJWR.js +1 -0
  121. package/core/built/admin/assets/{field-Cj8lmDOo.js → field-Dihp5p18.js} +1 -1
  122. package/core/built/admin/assets/filter-bar-Da1qxybu.js +1 -0
  123. package/core/built/admin/assets/filter-sources-BO9xUPj7.js +1 -0
  124. package/core/built/admin/assets/filters-CRUWecfY.js +1 -0
  125. package/core/built/admin/assets/filters-DirqNXjH.js +1 -0
  126. package/core/built/admin/assets/first-promoter-modal-CT6MxYZb.js +1 -0
  127. package/core/built/admin/assets/{ghost-c8807202a07c20030acb719ee0114815.css → ghost-8eabfc1ece81b03cf7b90780d31487e8.css} +1 -1
  128. package/core/built/admin/assets/{ghost-3894eedcc6968f658578c0abfe942aab.js → ghost-c2848508292937415f20f5a830eea59e.js} +31 -28
  129. package/core/built/admin/assets/{ghost-dark-fbf1e384aaef3ae315e540f0eb7373a7.css → ghost-dark-a7bebe2c561f55369dcf29f4c820f7ff.css} +1 -1
  130. package/core/built/admin/assets/gift-link-modal-CQVAye17.js +1 -0
  131. package/core/built/admin/assets/growth-BWIu-6xj.js +3 -0
  132. package/core/built/admin/assets/growth-DU29oxeA.js +1 -0
  133. package/core/built/admin/assets/history-modal-CFd1Hvx6.js +1 -0
  134. package/core/built/admin/assets/{html-field-C8qVGGC1.js → html-field-CJBCLTvv.js} +1 -1
  135. package/core/built/admin/assets/{images-h8VZEQ9k.js → images-scfkWZiK.js} +1 -1
  136. package/core/built/admin/assets/{inbox-BYd1M06Q.js → inbox-Mcp4DXZN.js} +14 -14
  137. package/core/built/admin/assets/index-6MkPdCtr.js +4 -0
  138. package/core/built/admin/assets/index-Bn7QhcZu.css +2 -0
  139. package/core/built/admin/assets/index.es-CNnuNH0d.js +1 -0
  140. package/core/built/admin/assets/{input-group-D8sCXRF5.js → input-group-CMhe2DvY.js} +1 -1
  141. package/core/built/admin/assets/invite-user-modal-DfheMXs0.js +1 -0
  142. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +14 -14
  143. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +3 -3
  144. package/core/built/admin/assets/koenig-lexical/style.css +1 -1
  145. package/core/built/admin/assets/{koenig-lexical-_-IsVI7E.js → koenig-lexical-DBSqAuiI.js} +3 -3
  146. package/core/built/admin/assets/koenig-loader-aFQNlJlD.js +2 -0
  147. package/core/built/admin/assets/{kpi-card-DZQk_0W8.js → kpi-card-DBRMfGOW.js} +1 -1
  148. package/core/built/admin/assets/{kpi-card-DW_2y3ug.js → kpi-card-DFafD1qq.js} +1 -1
  149. package/core/built/admin/assets/{kpi-helpers-PueTKuA6.js → kpi-helpers-Cv8i8hns.js} +1 -1
  150. package/core/built/admin/assets/{kpi-tabs-CojDs-Qz.js → kpi-tabs-BJItvLYm.js} +1 -1
  151. package/core/built/admin/assets/{label-DjbT91GM.js → label-BnSCvffu.js} +1 -1
  152. package/core/built/admin/assets/label-picker-KVoo91sh.js +1 -0
  153. package/core/built/admin/assets/layout-Bjv2Ll-5.js +1 -0
  154. package/core/built/admin/assets/member-custom-fields-DDQV6QeG.js +1 -0
  155. package/core/built/admin/assets/member-detail-CdJF6Agw.js +1 -0
  156. package/core/built/admin/assets/members-DjQbq9Z2.js +4 -0
  157. package/core/built/admin/assets/moderation-AgCIEso4.js +1 -0
  158. package/core/built/admin/assets/{multi-select-combobox-B6LiRzDO.js → multi-select-combobox-BcITkUVr.js} +1 -1
  159. package/core/built/admin/assets/navigation-modal-BkT7b1M2.js +1 -0
  160. package/core/built/admin/assets/newsletter-BMMAuvgq.js +1 -0
  161. package/core/built/admin/assets/newsletter-detail-modal-0SCJ7Zpm.js +1 -0
  162. package/core/built/admin/assets/{newsletter-emails-CFeLlt4z.js → newsletter-emails-DKzwafuh.js} +1 -1
  163. package/core/built/admin/assets/newsletters-BRBb_uQC.js +1 -0
  164. package/core/built/admin/assets/note-DyGHtS9a.js +1 -0
  165. package/core/built/admin/assets/notifications-ZgWniKna.js +1 -0
  166. package/core/built/admin/assets/nql-BLpCn7qX.js +21 -0
  167. package/core/built/admin/assets/offers-container-modal-B-E8TIWr.js +1 -0
  168. package/core/built/admin/assets/{onboarding-B4YhlZ0i.js → onboarding-CzIsre-d.js} +1 -1
  169. package/core/built/admin/assets/onboarding-qg8Akovm.js +1 -0
  170. package/core/built/admin/assets/{onboarding-route-Dmc4IPm3.js → onboarding-route-CaavZulg.js} +1 -1
  171. package/core/built/admin/assets/overview-BW8GLeYi.js +1 -0
  172. package/core/built/admin/assets/overview-BkXZNzaZ.js +1 -0
  173. package/core/built/admin/assets/{page-header-BR_nbIPp.js → page-header-De0VzKBi.js} +1 -1
  174. package/core/built/admin/assets/pagemenu-B5KKCpbA.js +1 -0
  175. package/core/built/admin/assets/pages-jgaFm69d.js +1 -0
  176. package/core/built/admin/assets/pages-route-CIUAQxtY.js +1 -0
  177. package/core/built/admin/assets/papaparse.min-cQwnxGCG.js +10 -0
  178. package/core/built/admin/assets/{pintura-modal-BR9hEvS8.js → pintura-modal-BZ2J9w3n.js} +1 -1
  179. package/core/built/admin/assets/portal-frame-B6Hr9wHc.js +1 -0
  180. package/core/built/admin/assets/portal-modal-D4ceei3t.js +1 -0
  181. package/core/built/admin/assets/post-analytics-header-BYCsT0GP.js +1 -0
  182. package/core/built/admin/assets/post-analytics-ot7f6pih.js +1 -0
  183. package/core/built/admin/assets/{post-analytics-provider-CLfKGWZy.js → post-analytics-provider-Bo1gmYaP.js} +1 -1
  184. package/core/built/admin/assets/{post-share-modal-Cl0NXRJr.js → post-share-modal-C-PaYZ_e.js} +1 -1
  185. package/core/built/admin/assets/posts-DuGazgTb.js +1 -0
  186. package/core/built/admin/assets/posts-list-screen-CBGGgjjj.js +2 -0
  187. package/core/built/admin/assets/posts-route-D6xmS7Vf.js +1 -0
  188. package/core/built/admin/assets/preferences-C5YJLp74.js +5 -0
  189. package/core/built/admin/assets/{preview-modal-C-BL-F9k.js → preview-modal-BfeFqi5y.js} +1 -1
  190. package/core/built/admin/assets/profile-B8mx2yAu.js +1 -0
  191. package/core/built/admin/assets/radio-group-DczlLKOn.js +1 -0
  192. package/core/built/admin/assets/{schemas-4UPic-oD.js → schemas-DppqmpeJ.js} +1 -1
  193. package/core/built/admin/assets/select-cb0t_TAJ.js +1 -0
  194. package/core/built/admin/assets/settings-DZqxR0Da.js +1 -0
  195. package/core/built/admin/assets/settings-DvfTNdHQ.js +4 -0
  196. package/core/built/admin/assets/{settings-breadcrumbs-CyP_9zTo.js → settings-breadcrumbs-DLzbRRSt.js} +1 -1
  197. package/core/built/admin/assets/share-modal-D2-Bz1_I.js +1 -0
  198. package/core/built/admin/assets/slack-modal-DYDTUria.js +1 -0
  199. package/core/built/admin/assets/{sort-button-Cc-Z96sA.js → sort-button-VVeQjjbu.js} +1 -1
  200. package/core/built/admin/assets/{source-icon-v6PUnmyM.js → source-icon-Ck5_5Kgq.js} +1 -1
  201. package/core/built/admin/assets/stats-Rjmk5owg.js +1 -0
  202. package/core/built/admin/assets/stats-view-BGGmkmgv.js +1 -0
  203. package/core/built/admin/assets/{step-1-DSRp2vBj.js → step-1-CsaBjeN4.js} +1 -1
  204. package/core/built/admin/assets/{step-2-COqv2g0u.js → step-2-D2vnxOOM.js} +1 -1
  205. package/core/built/admin/assets/{step-3-6W1NMVQc.js → step-3-wBoLdGEb.js} +2 -2
  206. package/core/built/admin/assets/{stripe-button-D2K42Uty.js → stripe-button-CDgLz0S0.js} +1 -1
  207. package/core/built/admin/assets/stripe-connect-modal-CSjMJetP.js +1 -0
  208. package/core/built/admin/assets/{table-B2pIKjVh.js → table-Do6iQ83Q.js} +1 -1
  209. package/core/built/admin/assets/{tabs-SaW_UT-Q.js → tabs-D2yWLMp_.js} +1 -1
  210. package/core/built/admin/assets/tag-detail-BhXlOsCa.js +2 -0
  211. package/core/built/admin/assets/tags-ldukRApe.js +1 -0
  212. package/core/built/admin/assets/testimonials-modal-CtnZBJKl.js +1 -0
  213. package/core/built/admin/assets/tier-detail-modal-BOs8e_G8.js +1 -0
  214. package/core/built/admin/assets/{toggle-group-EM9WpESu.js → toggle-group-ByUJsxwD.js} +1 -1
  215. package/core/built/admin/assets/{topic-filter-DY4gNHn2.js → topic-filter-BgC3g5_v.js} +1 -1
  216. package/core/built/admin/assets/{transistor-modal-CpyPG-MH.js → transistor-modal-Drr0SNfo.js} +1 -1
  217. package/core/built/admin/assets/{trend-badge-ByMkuAuH.js → trend-badge-6ye93YAW.js} +1 -1
  218. package/core/built/admin/assets/{unsplash-modal-Cv2Q6GtY.js → unsplash-modal-p6AoQoWp.js} +1 -1
  219. package/core/built/admin/assets/{use-analytics-data-CHvjMYuT.js → use-analytics-data-CWCMAESo.js} +1 -1
  220. package/core/built/admin/assets/use-check-theme-limit-error-B8bjF-gO.css +1 -0
  221. package/core/built/admin/assets/{use-check-theme-limit-error-Qv4SVXqw.js → use-check-theme-limit-error-Cw3WQHy8.js} +1 -1
  222. package/core/built/admin/assets/use-filter-params-BIni9taJ.js +1 -0
  223. package/core/built/admin/assets/use-growth-stats-DCVG3s9w.js +1 -0
  224. package/core/built/admin/assets/{use-limiter-cwJvsNBo.js → use-limiter-D2WRRx0H.js} +1 -1
  225. package/core/built/admin/assets/{use-pintura-config-CzTfnufz.js → use-pintura-config-h-J0Lvta.js} +1 -1
  226. package/core/built/admin/assets/{use-pintura-editor-BDBXWUYq.js → use-pintura-editor-BfN1UwzH.js} +1 -1
  227. package/core/built/admin/assets/use-post-referrers-Czg47m8s.js +1 -0
  228. package/core/built/admin/assets/use-reply-chain-data-ns0caK8k.js +1 -0
  229. package/core/built/admin/assets/{use-router-history-entry-Bg6BhBQQ.js → use-router-history-entry-uldn7zJO.js} +1 -1
  230. package/core/built/admin/assets/{use-setting-group-VM7FQwMY.js → use-setting-group-DpcuCUDg.js} +1 -1
  231. package/core/built/admin/assets/use-settings-navigation-CZjGrhsp.js +1 -0
  232. package/core/built/admin/assets/{use-simple-pagination-Dum6Irbs.js → use-simple-pagination-DzJWobPR.js} +1 -1
  233. package/core/built/admin/assets/{use-staff-users-cZg4ugOF.js → use-staff-users-Csipb-O0.js} +1 -1
  234. package/core/built/admin/assets/use-tinybird-query-CSnGXzZh.js +1 -0
  235. package/core/built/admin/assets/use-tinybird-token-DTOKRR7e.js +1 -0
  236. package/core/built/admin/assets/{use-unsaved-changes-guard-BAy0RANR.js → use-unsaved-changes-guard-CBzs-KPi.js} +1 -1
  237. package/core/built/admin/assets/{use-url-input-Df5QzNa7.js → use-url-input-DFr8lCbS.js} +1 -1
  238. package/core/built/admin/assets/user-detail-modal-B_iUClI6.js +1 -0
  239. package/core/built/admin/assets/users-Cg-OeGzC.js +1 -0
  240. package/core/built/admin/assets/{virtual-list-DEx8WgiO.js → virtual-list-BW-padMe.js} +1 -1
  241. package/core/built/admin/assets/web-C1v33ehA.js +2 -0
  242. package/core/built/admin/assets/web-C4BKzn2T.js +1 -0
  243. package/core/built/admin/assets/{zapier-modal-Cw3BTBUu.js → zapier-modal-BBFfI6Ml.js} +1 -1
  244. package/core/built/admin/index.html +17 -22
  245. package/core/frontend/apps/private-blogging/lib/views/private.hbs +44 -43
  246. package/core/frontend/helpers/cancel_link.js +2 -2
  247. package/core/frontend/helpers/pagination.js +2 -2
  248. package/core/frontend/helpers/social_accounts.js +2 -2
  249. package/core/frontend/public/ghost.css +1 -1
  250. package/core/frontend/public/ghost.min.css +1 -1
  251. package/core/frontend/services/sitemap/user-map-generator.js +9 -2
  252. package/core/server/api/endpoints/index.js +2 -2
  253. package/core/server/api/endpoints/mail.js +1 -1
  254. package/core/server/api/endpoints/{member-custom-fields.js → member-metafields.js} +27 -28
  255. package/core/server/api/endpoints/members.js +4 -4
  256. package/core/server/api/endpoints/posts.js +3 -3
  257. package/core/server/api/endpoints/utils/serializers/input/members.js +4 -7
  258. package/core/server/api/endpoints/utils/serializers/output/index.js +2 -2
  259. package/core/server/api/endpoints/utils/serializers/output/{member-custom-fields.js → member-metafields.js} +2 -4
  260. package/core/server/api/endpoints/utils/serializers/output/members.js +2 -4
  261. package/core/server/api/endpoints/utils/validators/input/invitations.js +0 -5
  262. package/core/server/api/endpoints/utils/validators/input/pages.js +1 -1
  263. package/core/server/api/endpoints/utils/validators/input/posts.js +1 -1
  264. package/core/server/data/importer/importers/data/data-importer.js +3 -3
  265. package/core/server/data/migrations/utils/migrations.js +1 -1
  266. package/core/server/lib/image/image-size.js +24 -0
  267. package/core/server/lib/package-json/parse.js +2 -2
  268. package/core/server/models/member.js +6 -17
  269. package/core/server/models/post.js +2 -2
  270. package/core/server/models/relations/authors.js +2 -16
  271. package/core/server/models/user.js +9 -2
  272. package/core/server/services/auth/passwordreset.js +13 -0
  273. package/core/server/services/comments/comments-controller.js +30 -9
  274. package/core/server/services/comments/comments-service.js +55 -5
  275. package/core/server/services/content-import/csv/formula.js +12 -0
  276. package/core/server/services/content-import/csv/index.js +6 -1
  277. package/core/server/services/content-import/csv/parse.js +27 -6
  278. package/core/server/services/content-import/csv/serialize.js +13 -0
  279. package/core/server/services/content-import/import/completion-email.js +164 -0
  280. package/core/server/services/content-import/import/errors-file.js +36 -0
  281. package/core/server/services/content-import/import/importer.js +160 -53
  282. package/core/server/services/content-import/import/local-media-url.js +63 -0
  283. package/core/server/services/content-import/import/media.js +254 -0
  284. package/core/server/services/content-import/import/post-data.js +0 -1
  285. package/core/server/services/content-import/import/post-link.js +11 -0
  286. package/core/server/services/content-import/import/post-repository.js +25 -3
  287. package/core/server/services/content-import/import/reader.js +9 -2
  288. package/core/server/services/content-import/import/report.js +34 -0
  289. package/core/server/services/content-import/import/row.js +0 -2
  290. package/core/server/services/content-import/import/schema.js +1 -0
  291. package/core/server/services/content-import/import/staged-file.js +29 -0
  292. package/core/server/services/content-import/import/store.js +25 -2
  293. package/core/server/services/content-import/index.js +52 -6
  294. package/core/server/services/content-import/jobs/content-csv-import-job.js +20 -0
  295. package/core/server/services/custom-redirects/redirect-config-parser.js +2 -2
  296. package/core/server/services/custom-redirects/redirects-service.js +1 -1
  297. package/core/server/services/custom-redirects/validation.js +1 -1
  298. package/core/server/services/email-address/email-address-parser.js +30 -38
  299. package/core/server/services/email-address/email-address-service.js +37 -4
  300. package/core/server/services/email-analytics/index.js +2 -7
  301. package/core/server/services/email-service/batch-sending-service.js +0 -4
  302. package/core/server/services/email-service/email-renderer.js +14 -4
  303. package/core/server/services/email-service/email-service-wrapper.js +0 -9
  304. package/core/server/services/email-service/mailgun-email-provider.js +20 -9
  305. package/core/server/services/gifts/email-templates/gift-delivery.hbs +3 -3
  306. package/core/server/services/gifts/gift-email-service.js +0 -1
  307. package/core/server/services/gifts/index.js +3 -3
  308. package/core/server/services/gifts/jobs/index.js +4 -4
  309. package/core/server/services/indexnow-ping/indexnow-ping-service.js +2 -2
  310. package/core/server/services/invitations/accept.js +4 -2
  311. package/core/server/services/jobs/job-service.js +0 -9
  312. package/core/server/services/jobs-service/register-job-handlers.js +21 -11
  313. package/core/server/services/mail/ghost-mailer.js +4 -4
  314. package/core/server/services/member-welcome-emails/service.js +7 -7
  315. package/core/server/services/members/api.js +1 -0
  316. package/core/server/services/members/import-export/csv/parse.js +0 -7
  317. package/core/server/services/members/import-export/import/completion-email.js +2 -5
  318. package/core/server/services/members/import-export/import/row.js +0 -3
  319. package/core/server/services/members/import-export/index.js +4 -11
  320. package/core/server/services/members/jobs/clean-expired-comped-job.js +7 -0
  321. package/core/server/services/members/jobs/clean-expired-comped-task.js +145 -0
  322. package/core/server/services/members/jobs/index.js +48 -24
  323. package/core/server/services/members/members-api/controllers/member-controller.js +11 -0
  324. package/core/server/services/members/members-api/controllers/router-controller.js +3 -6
  325. package/core/server/services/members/members-api/members-api.js +4 -6
  326. package/core/server/services/members/members-api/repositories/event-repository.js +0 -4
  327. package/core/server/services/members/members-api/services/member-bread-service.js +34 -62
  328. package/core/server/services/members/members-api/services/payments-service.js +1 -1
  329. package/core/server/services/members/service.js +1 -5
  330. package/core/server/services/members-custom-fields/bindings-service.js +2 -1
  331. package/core/server/services/members-custom-fields/codec.js +3 -4
  332. package/core/server/services/members-custom-fields/definitions-service.js +62 -10
  333. package/core/server/services/members-custom-fields/filter.js +75 -93
  334. package/core/server/services/members-custom-fields/key.js +2 -1
  335. package/core/server/services/members-custom-fields/models.js +1 -1
  336. package/core/server/services/members-custom-fields/serializers.js +3 -5
  337. package/core/server/services/members-custom-fields/values-service.js +50 -28
  338. package/core/server/services/mentions/mention-controller.js +23 -21
  339. package/core/server/services/mentions/process-webmention-job.js +16 -0
  340. package/core/server/services/mentions/service.js +8 -4
  341. package/core/server/services/route-settings/validation-errors.js +1 -1
  342. package/core/server/services/route-settings/yaml-parser.js +1 -1
  343. package/core/server/services/settings/settings-service.js +1 -8
  344. package/core/server/services/settings-helpers/settings-helpers.js +4 -6
  345. package/core/server/services/slack-ping/slack-ping-service.js +1 -1
  346. package/core/server/services/staff/staff-service-emails.js +2 -2
  347. package/core/server/services/stripe/config.js +14 -8
  348. package/core/server/services/stripe/service.js +10 -1
  349. package/core/server/services/stripe/services/checkout/completed-session.js +2 -2
  350. package/core/server/services/stripe/services/checkout/session-options.js +18 -15
  351. package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +26 -5
  352. package/core/server/services/stripe/stripe-api.js +2 -0
  353. package/core/server/services/stripe/stripe-service.js +20 -10
  354. package/core/server/services/stripe/webhook-controller.js +10 -0
  355. package/core/server/services/tier-checkout-config/codec.js +9 -5
  356. package/core/server/services/tier-checkout-config/models.js +9 -3
  357. package/core/server/services/tier-checkout-config/queries.js +4 -4
  358. package/core/server/services/tier-checkout-config/serializers.js +16 -10
  359. package/core/server/services/tier-checkout-config/service.js +13 -12
  360. package/core/server/services/update-check/index.js +13 -13
  361. package/core/server/services/update-check/jobs/update-check-job.js +7 -0
  362. package/core/server/services/update-check/update-check-service.js +2 -2
  363. package/core/server/web/admin/controller.js +1 -1
  364. package/core/server/web/api/endpoints/admin/routes.js +19 -16
  365. package/core/shared/config/env/config.testing-mysql.json +1 -1
  366. package/core/shared/labs.js +5 -2
  367. package/package.json +24 -23
  368. package/pnpm-lock.yaml +704 -69
  369. package/pnpm-workspace.yaml +8 -5
  370. package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
  371. package/core/built/admin/assets/Combination-5fYEzgbs.js +0 -41
  372. package/core/built/admin/assets/account-migration-DRhAehfw.js +0 -1
  373. package/core/built/admin/assets/activitypub/inbox-BluD2ucT.js +0 -1284
  374. package/core/built/admin/assets/activitypub/label-FHSh6ZAw.js +0 -16
  375. package/core/built/admin/assets/activitypub/layout-Cq4NG0Xr.js +0 -1
  376. package/core/built/admin/assets/activitypub/settings-DQDkr06X.js +0 -315
  377. package/core/built/admin/assets/add-integration-modal-BC0jExDZ.js +0 -1
  378. package/core/built/admin/assets/add-newsletter-modal-DchTkA7l.js +0 -1
  379. package/core/built/admin/assets/add-recommendation-modal-DMvaJZKp.js +0 -1
  380. package/core/built/admin/assets/alert-dialog-QhXzdHfc.js +0 -1
  381. package/core/built/admin/assets/analytics-No1DDY3A.js +0 -1
  382. package/core/built/admin/assets/automations-DpUNUdcZ.js +0 -1
  383. package/core/built/admin/assets/bluesky-sharing-Cjrv_f62.js +0 -1
  384. package/core/built/admin/assets/color-picker-BTY_NdEm.js +0 -3
  385. package/core/built/admin/assets/color-picker-field-CPlkzOH0.js +0 -1
  386. package/core/built/admin/assets/combobox-DrMtv_4P.js +0 -1
  387. package/core/built/admin/assets/command-ZdgYJpoI.js +0 -1
  388. package/core/built/admin/assets/comments-kidj60-h.js +0 -1
  389. package/core/built/admin/assets/confirmation-context-BhbmQQEl.js +0 -1
  390. package/core/built/admin/assets/custom-field-type-option-CsGIZk1J.js +0 -10
  391. package/core/built/admin/assets/custom-integration-modal-BY37O_9X.js +0 -1
  392. package/core/built/admin/assets/design-and-theme-modal-DUSKM4uC.js +0 -22
  393. package/core/built/admin/assets/dialog-B44iRwlc.js +0 -1
  394. package/core/built/admin/assets/dirty-confirm-dialog-BxgQgQ6H.js +0 -1
  395. package/core/built/admin/assets/dist-Cy8tfrp_.js +0 -1
  396. package/core/built/admin/assets/domain-Ca6KhoQt.js +0 -1
  397. package/core/built/admin/assets/dropzone-Bb-5zQTk.js +0 -5
  398. package/core/built/admin/assets/edit-profile-BdupLlus.js +0 -1
  399. package/core/built/admin/assets/editor-BP883Lpn.js +0 -8
  400. package/core/built/admin/assets/explore-FB-CimXw.js +0 -1
  401. package/core/built/admin/assets/feed-C1GxsmI2.js +0 -1
  402. package/core/built/admin/assets/filter-sources-D5n5bFA2.js +0 -1
  403. package/core/built/admin/assets/filters-DJEzkMUK.js +0 -1
  404. package/core/built/admin/assets/filters-MZXvdH5D.js +0 -1
  405. package/core/built/admin/assets/first-promoter-modal-CGTGLCr5.js +0 -1
  406. package/core/built/admin/assets/gift-link-modal-B6v0tnRN.js +0 -1
  407. package/core/built/admin/assets/growth-BGAJD1rs.js +0 -3
  408. package/core/built/admin/assets/growth-D6mhcx3q.js +0 -1
  409. package/core/built/admin/assets/history-modal-iKwUY3XR.js +0 -1
  410. package/core/built/admin/assets/index-DjQe-e5s.js +0 -4
  411. package/core/built/admin/assets/index-Dqf2x-ry.css +0 -1
  412. package/core/built/admin/assets/index.es-VihPwfeH.js +0 -1
  413. package/core/built/admin/assets/input--W-rCTrX.js +0 -1
  414. package/core/built/admin/assets/invite-user-modal-MdImLCaJ.js +0 -1
  415. package/core/built/admin/assets/koenig-loader-DYZpm6El.js +0 -2
  416. package/core/built/admin/assets/label-picker-cB51xl1r.js +0 -1
  417. package/core/built/admin/assets/layout-c_B_1hax.js +0 -1
  418. package/core/built/admin/assets/loading-indicator-BpFWTVgh.js +0 -1
  419. package/core/built/admin/assets/member-custom-fields-qs0dIkxM.js +0 -1
  420. package/core/built/admin/assets/member-detail-B6uec2a6.js +0 -1
  421. package/core/built/admin/assets/members-BQF3CqhI.js +0 -2
  422. package/core/built/admin/assets/moderation-DY4wF1Q3.js +0 -1
  423. package/core/built/admin/assets/navigation-modal-DX-gNF3X.js +0 -1
  424. package/core/built/admin/assets/newsletter-CyQA7m3m.js +0 -1
  425. package/core/built/admin/assets/newsletter-detail-modal-BDrptuxn.js +0 -1
  426. package/core/built/admin/assets/newsletters-DXuNmC42.js +0 -1
  427. package/core/built/admin/assets/note-Cgo2LCJ6.js +0 -1
  428. package/core/built/admin/assets/notifications-BC50AMku.js +0 -1
  429. package/core/built/admin/assets/offers-container-modal-DWycIMzT.js +0 -1
  430. package/core/built/admin/assets/onboarding-DGQuV7Ew.js +0 -1
  431. package/core/built/admin/assets/overview-CVbFtn-k.js +0 -1
  432. package/core/built/admin/assets/overview-YKj7SZaK.js +0 -1
  433. package/core/built/admin/assets/pages-B6vr9F_D.js +0 -1
  434. package/core/built/admin/assets/popover-CCy0h5rq.js +0 -1
  435. package/core/built/admin/assets/portal-frame-dc4XKr0e.js +0 -1
  436. package/core/built/admin/assets/portal-modal-BvvdF_B9.js +0 -1
  437. package/core/built/admin/assets/post-analytics-DX470WRG.js +0 -1
  438. package/core/built/admin/assets/post-analytics-header-CXpJmdDv.js +0 -1
  439. package/core/built/admin/assets/posts-KY3aUhVV.js +0 -1
  440. package/core/built/admin/assets/preferences-DO0UUWFp.js +0 -5
  441. package/core/built/admin/assets/profile-CxT-0l4d.js +0 -1
  442. package/core/built/admin/assets/radio-group-Bgz60i81.js +0 -1
  443. package/core/built/admin/assets/select-DuDUFdPq.js +0 -1
  444. package/core/built/admin/assets/settings-BjCii7BM.js +0 -1
  445. package/core/built/admin/assets/settings-b8kNzZSQ.js +0 -4
  446. package/core/built/admin/assets/share-modal-BV70mr9k.js +0 -1
  447. package/core/built/admin/assets/shim-ClF_m5ux.js +0 -1
  448. package/core/built/admin/assets/slack-modal-Be2_KK-z.js +0 -1
  449. package/core/built/admin/assets/stats-C4reGh9O.js +0 -1
  450. package/core/built/admin/assets/stats-view-DP9pQ62k.js +0 -1
  451. package/core/built/admin/assets/stripe-connect-modal-Z-UT_BGc.js +0 -1
  452. package/core/built/admin/assets/tag-detail-Dc0nTOtU.js +0 -2
  453. package/core/built/admin/assets/tags-BLaXBwnb.js +0 -1
  454. package/core/built/admin/assets/testimonials-modal-sFwRnNLh.js +0 -1
  455. package/core/built/admin/assets/tier-detail-modal-BCFpWwUS.js +0 -1
  456. package/core/built/admin/assets/use-filter-params-CAvfDvf1.js +0 -1
  457. package/core/built/admin/assets/use-growth-stats-D6mZXGhK.js +0 -1
  458. package/core/built/admin/assets/use-post-referrers-DbY-mzci.js +0 -1
  459. package/core/built/admin/assets/use-reply-chain-data-BeQwz8oF.js +0 -1
  460. package/core/built/admin/assets/use-settings-navigation-BbrSUVcf.js +0 -1
  461. package/core/built/admin/assets/use-tinybird-query-B1IMyAcv.js +0 -39
  462. package/core/built/admin/assets/use-tinybird-token-Bow0Zwn8.js +0 -1
  463. package/core/built/admin/assets/user-detail-modal-yipSoQfv.js +0 -1
  464. package/core/built/admin/assets/users-5msE1WMs.js +0 -1
  465. package/core/built/admin/assets/web-CKcXAu86.js +0 -1
  466. package/core/built/admin/assets/web-DR4TqM3E.js +0 -2
  467. package/core/server/services/jobs/worker-model-event-bridge.js +0 -149
  468. package/core/server/services/members/jobs/clean-expired-comped.js +0 -132
  469. package/core/server/services/stripe/services/checkout/allowed-countries.js +0 -267
  470. package/core/server/services/stripe/services/checkout/field-ports.js +0 -34
  471. package/core/server/services/tier-checkout-config/destinations.js +0 -17
  472. package/core/server/services/update-check/run-update-check.js +0 -63
@@ -8,6 +8,7 @@ const bson_objectid_1 = __importDefault(require("bson-objectid"));
8
8
  const errors_1 = __importDefault(require("@tryghost/errors"));
9
9
  const zod_1 = require("zod");
10
10
  const custom_field_types_1 = require("@tryghost/custom-field-types");
11
+ const identity_1 = require("@tryghost/custom-field-types/identity");
11
12
  const codec_1 = require("./codec");
12
13
  const schema_1 = require("./schema");
13
14
  const queries_1 = require("./queries");
@@ -92,7 +93,29 @@ class CustomFieldDefinitionsService {
92
93
  // restart. Where the number comes from is the caller's business.
93
94
  this.getMaxDefinitions = getMaxDefinitions;
94
95
  }
96
+ async hasAnyActive() {
97
+ const [field] = await this.list((0, queries_1.activeFields)(this.knex).limit(1));
98
+ return Boolean(field);
99
+ }
100
+ /**
101
+ * The fields table has no namespace column: every row in it belongs to the publisher's
102
+ * `custom` namespace, so no other namespace can have a stored field.
103
+ */
104
+ isStored(namespace) {
105
+ return namespace === identity_1.CUSTOM_NAMESPACE;
106
+ }
107
+ assertDefinable(namespace) {
108
+ if (!this.isStored(namespace)) {
109
+ throw new errors_1.default.ValidationError({
110
+ message: `Fields cannot be defined in the "${namespace}" namespace.`,
111
+ property: 'namespace',
112
+ });
113
+ }
114
+ }
95
115
  async browse(options = {}) {
116
+ if (options.namespace !== undefined && !this.isStored(options.namespace)) {
117
+ return [];
118
+ }
96
119
  // Archived fields are hidden by default: most surfaces (member details, the
97
120
  // filter picker, the importer) only ever want active fields. A caller-
98
121
  // supplied `filter` can widen that — Settings pulls active and archived
@@ -115,8 +138,8 @@ class CustomFieldDefinitionsService {
115
138
  const rows = await (0, queries_1.inFieldOrder)(query).select('*');
116
139
  return rows.map((row) => zod_1.z.decode(codec_1.customFieldCodec, row));
117
140
  }
118
- async read(key) {
119
- const [field] = await this.list((0, queries_1.fieldByKey)(this.knex, key));
141
+ async read(namespace, key) {
142
+ const [field] = this.isStored(namespace) ? await this.list((0, queries_1.fieldByKey)(this.knex, key)) : [];
120
143
  if (!field) {
121
144
  throw new errors_1.default.NotFoundError({ message: 'Custom field not found.' });
122
145
  }
@@ -132,7 +155,8 @@ class CustomFieldDefinitionsService {
132
155
  * batch, so two items sharing a name are caught and two items deriving the same
133
156
  * key get distinct ones, exactly as if they had arrived as separate requests.
134
157
  */
135
- async add(context, input) {
158
+ async add(context, namespace, input) {
159
+ this.assertDefinable(namespace);
136
160
  const requestedCount = Array.isArray(input) ? input.length : 0;
137
161
  const parsed = AddFieldsInput.safeParse(input);
138
162
  if (!parsed.success) {
@@ -310,7 +334,8 @@ class CustomFieldDefinitionsService {
310
334
  * fresh rank, so a move is well-defined even where every rank is still the default.
311
335
  * Returns every definition, archived included, matching what the request named.
312
336
  */
313
- async reorder(context, input) {
337
+ async reorder(context, namespace, input) {
338
+ this.assertDefinable(namespace);
314
339
  const parsed = ReorderInput.safeParse(input);
315
340
  if (!parsed.success) {
316
341
  const issue = parsed.error.issues[0];
@@ -429,7 +454,10 @@ class CustomFieldDefinitionsService {
429
454
  });
430
455
  }
431
456
  }
432
- async edit(context, key, input) {
457
+ async edit(context, namespace, key, input) {
458
+ if (!this.isStored(namespace)) {
459
+ throw new errors_1.default.NotFoundError({ message: 'Custom field not found.' });
460
+ }
433
461
  const parsed = EditFieldInput.safeParse(input);
434
462
  if (!parsed.success) {
435
463
  throw new errors_1.default.ValidationError({
@@ -438,7 +466,7 @@ class CustomFieldDefinitionsService {
438
466
  });
439
467
  }
440
468
  const patch = parsed.data;
441
- const existing = await this.read(key);
469
+ const existing = await this.read(identity_1.CUSTOM_NAMESPACE, key);
442
470
  // Key and type are immutable after creation: values are addressed by key
443
471
  // and interpreted by type, so changing either would silently orphan or
444
472
  // corrupt stored values. Reject a differing value rather than ignore it.
@@ -492,7 +520,7 @@ class CustomFieldDefinitionsService {
492
520
  details: { primary_name: patch.name ?? existing.name, key },
493
521
  });
494
522
  }
495
- return this.read(key);
523
+ return this.read(identity_1.CUSTOM_NAMESPACE, key);
496
524
  }
497
525
  /**
498
526
  * Permanently delete a field and every member value attached to it (the FK
@@ -501,7 +529,10 @@ class CustomFieldDefinitionsService {
501
529
  * which makes accidental data loss a deliberate two-step. Archiving is the
502
530
  * reversible soft state (see `edit` with a status change).
503
531
  */
504
- async destroy(context, key) {
532
+ async destroy(context, namespace, key) {
533
+ if (!this.isStored(namespace)) {
534
+ throw new errors_1.default.NotFoundError({ message: 'Custom field not found.' });
535
+ }
505
536
  const field = await this.knex(TABLE).where('key', key).first();
506
537
  if (!field) {
507
538
  throw new errors_1.default.NotFoundError({ message: 'Custom field not found.' });
@@ -581,6 +612,24 @@ function applyFilter(query, filter) {
581
612
  let mongoQuery;
582
613
  try {
583
614
  mongoQuery = nql(filter).toJSON();
615
+ }
616
+ catch (err) {
617
+ throw new errors_1.default.BadRequestError({
618
+ message: 'Could not parse the filter parameter.',
619
+ property: 'filter',
620
+ err: err,
621
+ });
622
+ }
623
+ // The table has no `namespace` column, so mongo-knex would compile this into SQL against a
624
+ // column that does not exist. Refuse it at the edge, naming the URL as the way to pick a
625
+ // namespace, rather than surface a database error.
626
+ if (filterReferencesAttribute(mongoQuery, 'namespace')) {
627
+ throw new errors_1.default.BadRequestError({
628
+ message: 'Filtering on namespace is not supported. Scope by the namespace route instead.',
629
+ property: 'filter',
630
+ });
631
+ }
632
+ try {
584
633
  knexify(query, mongoQuery, { tableName: TABLE });
585
634
  }
586
635
  catch (err) {
@@ -599,12 +648,15 @@ function applyFilter(query, filter) {
599
648
  // $and/$or/$nor combinators — so a status filter at any nesting counts as the
600
649
  // caller opting in to (or out of) archived fields.
601
650
  function filterReferencesStatus(query) {
651
+ return filterReferencesAttribute(query, 'status');
652
+ }
653
+ function filterReferencesAttribute(query, attribute) {
602
654
  return Object.entries(query).some(([key, value]) => {
603
- if (key === 'status') {
655
+ if (key === attribute) {
604
656
  return true;
605
657
  }
606
658
  if ((key === '$and' || key === '$or' || key === '$nor') && Array.isArray(value)) {
607
- return value.some((sub) => filterReferencesStatus(sub));
659
+ return value.some((sub) => filterReferencesAttribute(sub, attribute));
608
660
  }
609
661
  return false;
610
662
  });
@@ -5,34 +5,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CUSTOM_FIELDS_RELATION = void 0;
7
7
  exports.createCustomFieldsFilterTransformer = createCustomFieldsFilterTransformer;
8
- // The members filter reaches custom field values through a `custom_fields` relation
9
- // on the Member model (the values table joined on member_id). A field is named by its
10
- // `key` the stable public slug and matched on its `value`; a composite field's
11
- // part is named as `value.<part>`, and a part's presence as `path.<part>`:
8
+ // Translates the metafield clauses of a members filter into a query over the
9
+ // values table. A metafield is a publisher-defined field on a member; it is
10
+ // addressed as `namespace.key`, and a composite value such as an address names
11
+ // one of its parts by appending it: `namespace.key.part`.
12
12
  //
13
- // (custom_fields.key:'company'+custom_fields.value:'Ghost') // value
14
- // (custom_fields.key:'shipping_address'+custom_fields.value.country:'GB')
15
- // custom_fields.key:'phone' / custom_fields.key:-'phone' // field set / not set
16
- // (custom_fields.key:'shipping_address'+custom_fields.path:'country') // part set
17
- // (custom_fields.key:'shipping_address'+custom_fields.path:-'country') // part not set
13
+ // (metafields.key:'custom.company'+metafields.value:'Ghost')
14
+ // (metafields.key:'custom.shipping_address.country'+metafields.value:'GB')
15
+ // metafields.key:'custom.company' // has a value
16
+ // metafields.key:-'custom.shipping_address.country' // that part is not set
18
17
  //
19
- // The values table stores one row per leaf: `custom_field_key` (the field's stable key),
20
- // `path` (empty for a scalar, the part's key for a composite), and `value_text`. A scalar
21
- // field is just a leaf at path ''; "the field", "a part", and "the whole field" are the
22
- // same thing a set of leaf rows pinned by (custom_field_key, [path], [value]). So every
23
- // filter is one `$elemMatch` over those columns: positive asserts a matching leaf exists,
24
- // `$not` asserts none does. The public grammar is the storage vocabulary here — the key
25
- // sits on the row — so the transformer emits it straight, with no id lookup and no field
26
- // catalog read. That is what lets the Member model chain it inline on every members query,
27
- // rather than each call site remembering to wire it. A key that names no current field
28
- // matches no leaf; the admin never offers an archived or deleted field in the filter UI,
29
- // though the API leaves them queryable.
18
+ // The values table holds one row per stored value: `custom_field_key`, `path`
19
+ // (empty for a plain field, the part's name for one part of a composite) and
20
+ // `value_text`. A filter addresses those columns directly, so each one becomes a
21
+ // single $elemMatch over them positive asserts a matching row exists, $not
22
+ // asserts none does with no lookup against the field definitions.
30
23
  const errors_1 = __importDefault(require("@tryghost/errors"));
31
- const RELATION = 'custom_fields';
24
+ const identity_1 = require("@tryghost/custom-field-types/identity");
25
+ const RELATION = 'metafields';
32
26
  const PREFIX = `${RELATION}.`;
33
27
  const KEY_ATTRIBUTE = `${PREFIX}key`;
34
28
  const VALUE_ATTRIBUTE = `${PREFIX}value`;
35
- const PATH_ATTRIBUTE = `${PREFIX}path`;
36
29
  const ROOT_PATH = '';
37
30
  // Only recurse into plain objects/arrays; a RegExp, Date, etc. is a leaf value that
38
31
  // must pass through by reference (recursing would strip a regex to `{}`).
@@ -42,8 +35,7 @@ function isPlainObject(value) {
42
35
  !Array.isArray(value) &&
43
36
  (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null));
44
37
  }
45
- // A single-clause object whose one key targets the custom_fields relation.
46
- function isCustomFieldClause(node) {
38
+ function isMetafieldClause(node) {
47
39
  if (!isPlainObject(node)) {
48
40
  return false;
49
41
  }
@@ -57,100 +49,94 @@ function isCustomFieldClause(node) {
57
49
  // `key:'a'+key:'b'`, since a bare key clause needs no group of its own. That is two
58
50
  // filters — a member with a value for both — not one leaf that is somehow both fields, so
59
51
  // it has to fall through and be transformed a clause at a time.
60
- function isCustomFieldCompound(clauses) {
61
- if (!Array.isArray(clauses) || clauses.length < 2 || !clauses.every(isCustomFieldClause)) {
52
+ function isMetafieldCompound(clauses) {
53
+ if (!Array.isArray(clauses) || clauses.length < 2 || !clauses.every(isMetafieldClause)) {
62
54
  return false;
63
55
  }
64
56
  return clauses.filter((clause) => Object.keys(clause)[0] === KEY_ATTRIBUTE).length === 1;
65
57
  }
66
- // `custom_fields.value` addresses the scalar (root) leaf; `custom_fields.value.<part>`
67
- // a composite's part. Returns the row `path` the attribute selects, or null if the
68
- // attribute isn't a value clause.
69
- function pathForValueAttribute(attribute) {
70
- if (attribute === VALUE_ATTRIBUTE) {
71
- return ROOT_PATH;
72
- }
73
- if (attribute.startsWith(`${VALUE_ATTRIBUTE}.`)) {
74
- return attribute.slice(`${VALUE_ATTRIBUTE}.`.length);
75
- }
76
- return null;
77
- }
78
- // The single `{$ne}` shape all negations arrive as (is-not-set on a key or part).
58
+ // NQL compiles every negation in this grammar to `{$ne: <string>}`, so a negated clause
59
+ // can arrive in no other shape.
79
60
  function negatedString(value) {
80
61
  return isPlainObject(value) && typeof value.$ne === 'string' ? value.$ne : null;
81
62
  }
63
+ const ANY_PATH = null;
64
+ // A stored key never contains a dot, so a full dotted identity is a key no stored
65
+ // row can equal.
66
+ function unmatchableLeaf(identity) {
67
+ return { key: (0, identity_1.formatIdentity)(identity), path: identity.partPath };
68
+ }
69
+ function parseLeafAddress(raw) {
70
+ const identity = typeof raw === 'string' ? (0, identity_1.parseIdentity)(raw) : null;
71
+ if (!identity) {
72
+ throw new errors_1.default.BadRequestError({
73
+ message: `A metafield filter names a field as namespace.key, for example (${KEY_ATTRIBUTE}:'custom.company'+${VALUE_ATTRIBUTE}:'Ghost').`,
74
+ });
75
+ }
76
+ if (identity.namespace !== identity_1.CUSTOM_NAMESPACE) {
77
+ return unmatchableLeaf(identity);
78
+ }
79
+ return { key: identity.key, path: identity.partPath };
80
+ }
82
81
  function createCustomFieldsFilterTransformer() {
83
- // The grammar carries the field key in the clause value; take it as a string,
84
- // defaulting to one no field can hold so a malformed clause matches nothing.
85
- const keyOf = (value) => (typeof value === 'string' ? value : '');
86
82
  // One (maybe-negated) $elemMatch over the leaf columns: positive is "a leaf pinned
87
83
  // by these matches", `$not` is "no leaf does".
88
- function buildElemMatch(fieldKey, conditions, negate) {
89
- const match = { custom_field_key: fieldKey, ...conditions };
84
+ function buildElemMatch(target, value, negate) {
85
+ const match = { custom_field_key: target.key };
86
+ if (value !== undefined) {
87
+ match.path = target.path ?? ROOT_PATH;
88
+ match.value_text = value;
89
+ }
90
+ else if (target.path !== ANY_PATH) {
91
+ match.path = target.path;
92
+ }
90
93
  if (negate) {
91
94
  return { [RELATION]: { $not: { $elemMatch: match } } };
92
95
  }
93
96
  return { [RELATION]: { $elemMatch: match } };
94
97
  }
95
- // A `(key + value[.part] | path)` compound. A `value` clause pins the part and its
96
- // value; a `path` clause pins the part's presence (no value), and a negated path is
97
- // is-not-set — no such leaf — which negates the whole match.
98
98
  function toElemMatch(clauses) {
99
- let fieldKey = '';
100
- let hasKey = false;
101
- let hasLeaf = false;
102
- const conditions = {};
103
- let negate = false;
104
- const oneLeafOnly = () => {
105
- if (hasLeaf) {
106
- throw new errors_1.default.BadRequestError({
107
- message: 'A custom field filter takes one value or path clause.',
108
- });
109
- }
110
- hasLeaf = true;
111
- };
99
+ let target;
100
+ let value;
101
+ let hasValue = false;
112
102
  for (const clause of clauses) {
113
- const [attribute, value] = Object.entries(clause)[0];
103
+ const [attribute, clauseValue] = Object.entries(clause)[0];
114
104
  if (attribute === KEY_ATTRIBUTE) {
115
- if (hasKey) {
105
+ if (negatedString(clauseValue) !== null) {
116
106
  throw new errors_1.default.BadRequestError({
117
- message: `A custom field filter takes one "${KEY_ATTRIBUTE}" clause.`,
107
+ message: `A negated "${KEY_ATTRIBUTE}" stands alone; it cannot be grouped with a value.`,
118
108
  });
119
109
  }
120
- hasKey = true;
121
- fieldKey = keyOf(value);
122
- }
123
- else if (attribute === PATH_ATTRIBUTE) {
124
- oneLeafOnly();
125
- const negatedPath = negatedString(value);
126
- conditions.path = negatedPath ?? value;
127
- negate = negatedPath !== null;
110
+ target = parseLeafAddress(clauseValue);
128
111
  }
129
- else {
130
- const path = pathForValueAttribute(attribute);
131
- if (path === null) {
132
- // Not the key, a value/value.<part>, or a path: it names no leaf
133
- // column, so dropping it would silently widen the match to the key
134
- // alone. Fail closed instead.
112
+ else if (attribute === VALUE_ATTRIBUTE) {
113
+ if (hasValue) {
135
114
  throw new errors_1.default.BadRequestError({
136
- message: `Unsupported custom field filter clause "${attribute}".`,
115
+ message: `A metafield filter takes one "${VALUE_ATTRIBUTE}" clause.`,
137
116
  });
138
117
  }
139
- oneLeafOnly();
140
- conditions.path = path;
141
- conditions.value_text = value;
118
+ hasValue = true;
119
+ value = clauseValue;
142
120
  }
121
+ else {
122
+ throw new errors_1.default.BadRequestError({
123
+ message: `Unsupported metafield filter clause "${attribute}".`,
124
+ });
125
+ }
126
+ }
127
+ if (!target) {
128
+ throw new errors_1.default.BadRequestError({
129
+ message: `A metafield filter needs a "${KEY_ATTRIBUTE}" clause.`,
130
+ });
143
131
  }
144
- return buildElemMatch(fieldKey, conditions, negate);
132
+ return buildElemMatch(target, hasValue ? value : undefined, false);
145
133
  }
146
- // A standalone `key` clause is whole-field is-set / is-not-set: a leaf for this
147
- // field exists (any part), or none does.
148
134
  function fromKeyClause(value) {
149
135
  const negatedKey = negatedString(value);
150
136
  if (negatedKey !== null) {
151
- return buildElemMatch(keyOf(negatedKey), {}, true);
137
+ return buildElemMatch(parseLeafAddress(negatedKey), undefined, true);
152
138
  }
153
- return buildElemMatch(keyOf(value), {}, false);
139
+ return buildElemMatch(parseLeafAddress(value), undefined, false);
154
140
  }
155
141
  function transform(node) {
156
142
  if (Array.isArray(node)) {
@@ -159,7 +145,7 @@ function createCustomFieldsFilterTransformer() {
159
145
  if (!isPlainObject(node)) {
160
146
  return node;
161
147
  }
162
- if (isCustomFieldCompound(node.$and)) {
148
+ if (isMetafieldCompound(node.$and)) {
163
149
  return toElemMatch(node.$and);
164
150
  }
165
151
  const out = {};
@@ -168,12 +154,8 @@ function createCustomFieldsFilterTransformer() {
168
154
  Object.assign(out, fromKeyClause(value));
169
155
  }
170
156
  else if (key.startsWith(PREFIX)) {
171
- // A value or path clause only means something paired with its key in one
172
- // leaf, which arrives as the compound handled above. A `custom_fields.*`
173
- // clause reaching here is unpaired (or its pair was not grouped), so fail
174
- // closed rather than pass it to mongo-knex as a column that does not exist.
175
157
  throw new errors_1.default.BadRequestError({
176
- message: `A "${key}" filter must be grouped with its "${KEY_ATTRIBUTE}" clause, for example (${KEY_ATTRIBUTE}:'a_field'+${VALUE_ATTRIBUTE}:'a value').`,
158
+ message: `A "${key}" filter must be grouped with its "${KEY_ATTRIBUTE}" clause, for example (${KEY_ATTRIBUTE}:'custom.a_field'+${VALUE_ATTRIBUTE}:'a value').`,
177
159
  });
178
160
  }
179
161
  else {
@@ -189,7 +171,7 @@ function createCustomFieldsFilterTransformer() {
189
171
  * rows (one per field, or per part of a composite field), and a predicate asks whether
190
172
  * one of them matches, so this joins the values table on member_id and mongo-knex emits
191
173
  * it as a correlated `members.id IN (…)` subquery — composing with every other member
192
- * filter. Registered only behind the `membersCustomFields` flag.
174
+ * filter.
193
175
  */
194
176
  exports.CUSTOM_FIELDS_RELATION = {
195
177
  tableName: 'members_custom_field_values',
@@ -2,11 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KEY_CHARACTERS = void 0;
4
4
  exports.mintableKey = mintableKey;
5
+ const identity_1 = require("@tryghost/custom-field-types/identity");
5
6
  // Neither package ships types.
6
7
  const { stripInvisibleChars } = require('@tryghost/string');
7
8
  const unidecode = require('unidecode');
8
9
  /** Every character a key may contain. Nothing else survives minting. */
9
- exports.KEY_CHARACTERS = /^[a-z0-9_]+$/;
10
+ exports.KEY_CHARACTERS = identity_1.IDENTITY_SEGMENT;
10
11
  /**
11
12
  * Mint the key a field is addressed by, from the name a publisher chose.
12
13
  *
@@ -4,9 +4,9 @@ exports.CustomField = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const custom_field_types_1 = require("@tryghost/custom-field-types");
6
6
  const schema_1 = require("./schema");
7
- // The domain shape of a field definition (camelCase; distinct from the DB row).
8
7
  exports.CustomField = zod_1.z.object({
9
8
  id: zod_1.z.string(),
9
+ namespace: zod_1.z.string(),
10
10
  key: zod_1.z.string(),
11
11
  name: zod_1.z.string(),
12
12
  type: custom_field_types_1.FieldTypeSchema,
@@ -4,10 +4,8 @@ exports.toCustomFieldsResponse = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const case_keys_1 = require("../../lib/case-keys");
6
6
  const models_1 = require("./models");
7
- // Response schema — the field-definition shape the admin endpoints emit. `id` is
8
- // the DB primary key and deliberately never leaves the API; a field is addressed
9
- // by its immutable `key`. The `.pipe` below strips id from the mapped output.
10
7
  const CustomFieldResource = zod_1.z.object({
8
+ namespace: zod_1.z.string(),
11
9
  key: zod_1.z.string(),
12
10
  name: zod_1.z.string(),
13
11
  type: zod_1.z.string(),
@@ -15,10 +13,10 @@ const CustomFieldResource = zod_1.z.object({
15
13
  created_at: zod_1.z.date(),
16
14
  updated_at: zod_1.z.date().nullable(),
17
15
  });
18
- const CustomFieldsResponse = zod_1.z.object({ members_custom_fields: zod_1.z.array(CustomFieldResource) });
16
+ const CustomFieldsResponse = zod_1.z.object({ members_metafields: zod_1.z.array(CustomFieldResource) });
19
17
  exports.toCustomFieldsResponse = zod_1.z
20
18
  .array(models_1.CustomField)
21
19
  .transform((fields) => ({
22
- members_custom_fields: fields.map((field) => (0, case_keys_1.snakeKeys)(field)),
20
+ members_metafields: fields.map((field) => (0, case_keys_1.snakeKeys)(field)),
23
21
  }))
24
22
  .pipe(CustomFieldsResponse);
@@ -9,6 +9,7 @@ const errors_1 = __importDefault(require("@tryghost/errors"));
9
9
  const logging_1 = __importDefault(require("@tryghost/logging"));
10
10
  const zod_1 = require("zod");
11
11
  const custom_field_types_1 = require("@tryghost/custom-field-types");
12
+ const identity_1 = require("@tryghost/custom-field-types/identity");
12
13
  const schema_1 = require("./schema");
13
14
  const queries_1 = require("./queries");
14
15
  const storage_1 = require("./storage");
@@ -29,9 +30,9 @@ const MAX_KEY_LENGTH = require('../../data/schema').tables[FIELDS_TABLE].key.max
29
30
  * go when that lands; until then the alternative is hand-written upsert SQL per engine.
30
31
  */
31
32
  const UPSERT_CHUNK = 400;
32
- // Values stay `unknown`: each is validated by its own field type, which is not known
33
- // until the key is resolved to a definition.
34
- const ValuesInput = zod_1.z.record(zod_1.z.string().max(MAX_KEY_LENGTH), zod_1.z.unknown());
33
+ const MAX_IDENTITY_LENGTH = MAX_KEY_LENGTH * 2 + 1;
34
+ const ValuesInput = zod_1.z.record(zod_1.z.string().max(MAX_IDENTITY_LENGTH), zod_1.z.unknown());
35
+ const wireProperty = (identity) => [identity_1.QUALIFIER, identity].join('.');
35
36
  /**
36
37
  * What a member holds for each defined field. Separate from the definitions service
37
38
  * because a value belongs to the member and a definition belongs to the site's settings,
@@ -45,22 +46,22 @@ class CustomFieldValuesService {
45
46
  this.knex = knex;
46
47
  this.getMaxDefinitions = getMaxDefinitions;
47
48
  }
48
- async activeFieldsByKey(keys) {
49
+ async activeFieldsByIdentity(identities) {
50
+ const keys = identities
51
+ .map((identity) => (0, identity_1.parseIdentity)(identity))
52
+ .filter((parsed) => parsed !== null && parsed.namespace === identity_1.CUSTOM_NAMESPACE)
53
+ .map((parsed) => parsed.key);
49
54
  if (keys.length === 0) {
50
55
  return new Map();
51
56
  }
52
57
  const fields = await (0, queries_1.activeFields)(this.knex)
53
58
  .whereIn('key', keys)
54
59
  .select('id', 'key', 'name', 'type');
55
- return new Map(fields.map((field) => [field.key, field]));
60
+ return new Map(fields.map((field) => [
61
+ (0, identity_1.formatIdentity)({ namespace: identity_1.CUSTOM_NAMESPACE, key: field.key, partPath: null }),
62
+ { ...field, namespace: identity_1.CUSTOM_NAMESPACE },
63
+ ]));
56
64
  }
57
- /**
58
- * Members' values, keyed by member id then field key; anything absent is unset.
59
- *
60
- * Archived fields are excluded to match the definitions browse: their values stay in
61
- * the database and stop being addressable. A row that will not parse is dropped and
62
- * logged rather than failing the read, so one stale row cannot take down a member.
63
- */
64
65
  async getValuesForMembers(memberIds) {
65
66
  if (memberIds.length === 0) {
66
67
  return new Map();
@@ -88,19 +89,43 @@ class CustomFieldValuesService {
88
89
  }, 'Skipping an unreadable custom field value');
89
90
  }
90
91
  }
91
- return (0, storage_1.valuesFromLeaves)(leaves);
92
+ const flat = (0, storage_1.valuesFromLeaves)(leaves);
93
+ return new Map(memberIds.map((memberId) => [memberId, { [identity_1.CUSTOM_NAMESPACE]: flat.get(memberId) ?? {} }]));
92
94
  }
93
- /** Shared by every caller so they cannot disagree on what a values object is. */
94
95
  parseValues(input) {
95
96
  const parsed = ValuesInput.safeParse(input);
96
97
  if (!parsed.success) {
97
98
  throw new errors_1.default.ValidationError({
98
- message: 'Custom field values must be an object keyed by field key.',
99
- property: 'custom_fields',
99
+ message: 'Custom field values must be an object keyed by field identity.',
100
+ property: identity_1.QUALIFIER,
100
101
  });
101
102
  }
102
103
  return parsed.data;
103
104
  }
105
+ unwrapWire(input) {
106
+ if (input === undefined) {
107
+ return undefined;
108
+ }
109
+ if (typeof input !== 'object' || input === null || Array.isArray(input)) {
110
+ throw new errors_1.default.ValidationError({
111
+ message: 'Metafields must be an object keyed by namespace.',
112
+ property: identity_1.QUALIFIER,
113
+ });
114
+ }
115
+ const identified = {};
116
+ for (const [namespace, values] of Object.entries(input)) {
117
+ if (typeof values !== 'object' || values === null || Array.isArray(values)) {
118
+ throw new errors_1.default.ValidationError({
119
+ message: 'Metafield values must be an object keyed by field key.',
120
+ property: [identity_1.QUALIFIER, namespace].join('.'),
121
+ });
122
+ }
123
+ for (const [key, raw] of Object.entries(values)) {
124
+ identified[`${namespace}.${key}`] = raw;
125
+ }
126
+ }
127
+ return identified;
128
+ }
104
129
  /**
105
130
  * Whether input names any values. Asks the shape question alone, with no catalog
106
131
  * lookup, so it can be asked before a write is known to be permitted.
@@ -118,27 +143,24 @@ class CustomFieldValuesService {
118
143
  */
119
144
  async planWrite(input) {
120
145
  const values = this.parseValues(input);
121
- const keys = Object.keys(values);
146
+ const identities = Object.keys(values);
122
147
  // Bounded by the definitions ceiling, which also holds the lookup below inside
123
148
  // the driver's bound-parameter limit.
124
149
  const maxKeys = this.getMaxDefinitions();
125
- if (keys.length > maxKeys) {
150
+ if (identities.length > maxKeys) {
126
151
  throw new errors_1.default.ValidationError({
127
152
  message: `Custom field values are limited to ${maxKeys} fields per request.`,
128
- property: 'custom_fields',
153
+ property: identity_1.QUALIFIER,
129
154
  });
130
155
  }
131
- const byKey = await this.activeFieldsByKey(keys);
156
+ const byIdentity = await this.activeFieldsByIdentity(identities);
132
157
  const writes = [];
133
- for (const [key, raw] of Object.entries(values)) {
134
- const field = byKey.get(key);
158
+ for (const [identity, raw] of Object.entries(values)) {
159
+ const field = byIdentity.get(identity);
135
160
  if (!field) {
136
- // Unknown or archived. Refused rather than ignored: a typo that silently
137
- // drops what somebody typed is worse than a save that fails. The catalog
138
- // applies the same rule to the parts of a composite value.
139
161
  throw new errors_1.default.ValidationError({
140
- message: `Unknown custom field: ${key}`,
141
- property: `custom_fields.${key}`,
162
+ message: `Unknown custom field: ${identity}`,
163
+ property: wireProperty(identity),
142
164
  });
143
165
  }
144
166
  // `null` clears any field, and `''` clears one with no parts. For a value
@@ -157,7 +179,7 @@ class CustomFieldValuesService {
157
179
  const issue = value.error.issues[0];
158
180
  throw new errors_1.default.ValidationError({
159
181
  message: issue.message,
160
- property: [`custom_fields.${key}`, ...issue.path].join('.'),
182
+ property: [wireProperty(identity), ...issue.path].join('.'),
161
183
  });
162
184
  }
163
185
  writes.push({ field, value: value.data });