@koda-sl/baker-cli 0.489.0 → 0.490.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 (311) hide show
  1. package/README.md +169 -7
  2. package/dist/{account-Z65CPOWE.js → account-3H6BZO5H.js} +2 -2
  3. package/dist/account-IKFDY52P.js +138 -0
  4. package/dist/account-IKFDY52P.js.map +1 -0
  5. package/dist/account-assets-LKZVWZAR.js +289 -0
  6. package/dist/account-assets-LKZVWZAR.js.map +1 -0
  7. package/dist/account-level-UJOZDVXY.js +608 -0
  8. package/dist/account-level-UJOZDVXY.js.map +1 -0
  9. package/dist/{account-writes-4N2MA2ZR.js → account-writes-AE5IJGGW.js} +2 -2
  10. package/dist/{accounts-UA2ZWRN4.js → accounts-K4INATCW.js} +2 -2
  11. package/dist/{activities-IQPJPP4B.js → activities-T253KK4Q.js} +4 -4
  12. package/dist/{ad-group-writes-EUTMPBMK.js → ad-group-writes-VRSBI2Z2.js} +2 -2
  13. package/dist/{ad-groups-OVPAI5RM.js → ad-groups-IEBVEAMS.js} +6 -6
  14. package/dist/{ad-writes-DACTYFEO.js → ad-writes-ALV4KFPZ.js} +2 -2
  15. package/dist/{ads-L65JIKCD.js → ads-CQZHD7FC.js} +7 -7
  16. package/dist/{ads-G6AS5L7G.js → ads-QIKZ6O2N.js} +5 -5
  17. package/dist/{ads-HXX2IAFF.js → ads-ZEY72UBN.js} +14 -13
  18. package/dist/ads-ZEY72UBN.js.map +1 -0
  19. package/dist/{adsets-LPN6WFGF.js → adsets-5H3MOT3D.js} +15 -14
  20. package/dist/{adsets-LPN6WFGF.js.map → adsets-5H3MOT3D.js.map} +1 -1
  21. package/dist/{analytics-QKP3A5F3.js → analytics-ALOZ7ZZ4.js} +1 -1
  22. package/dist/asset-writes-L5TMKIZC.js +47 -0
  23. package/dist/audience-writes-X52VG35Y.js +324 -0
  24. package/dist/audience-writes-X52VG35Y.js.map +1 -0
  25. package/dist/{audiences-RPLVMO4H.js → audiences-AAZH56WD.js} +2 -2
  26. package/dist/{audiences-XEZHACKH.js → audiences-X3RLGOVM.js} +12 -6
  27. package/dist/audiences-X3RLGOVM.js.map +1 -0
  28. package/dist/{avatars-MXTI74VC.js → avatars-YSFFRHDK.js} +1 -1
  29. package/dist/{brief-BN4UGUXF.js → brief-7S6XCJIA.js} +15 -15
  30. package/dist/{businesses-PRRYPR7H.js → businesses-H3HIDQX6.js} +2 -2
  31. package/dist/{campaign-writes-BAFB5E7A.js → campaign-writes-7ZF57KLH.js} +3 -3
  32. package/dist/campaign-writes-7ZF57KLH.js.map +1 -0
  33. package/dist/{campaign-writes-U5LTDTYK.js → campaign-writes-U5PUVSMT.js} +2 -2
  34. package/dist/{campaigns-4SOLY7OR.js → campaigns-AV6G3AUU.js} +32 -15
  35. package/dist/campaigns-AV6G3AUU.js.map +1 -0
  36. package/dist/{campaigns-TFEQTSO3.js → campaigns-JEIKY2M4.js} +6 -6
  37. package/dist/{campaigns-NR6TNQFN.js → campaigns-VTRMKSAT.js} +5 -5
  38. package/dist/{canvas-GJAQAVS6.js → canvas-SVJEQO4V.js} +13 -13
  39. package/dist/{catalog-USZL4SGX.js → catalog-S32MX453.js} +5 -5
  40. package/dist/{catalogs-A7XMEEEP.js → catalogs-OCVXZLIF.js} +2 -2
  41. package/dist/catalogs-RXJTZTVX.js +778 -0
  42. package/dist/catalogs-RXJTZTVX.js.map +1 -0
  43. package/dist/{chatgpt-4PEWDJKP.js → chatgpt-QVF2FD3E.js} +12 -12
  44. package/dist/{chats-INN2OHSH.js → chats-QG72JB6Y.js} +22 -8
  45. package/dist/chats-QG72JB6Y.js.map +1 -0
  46. package/dist/{check-MMJGFFZS.js → check-SLNHVYGS.js} +4 -4
  47. package/dist/{chunk-7RBJ2Z7Z.js → chunk-2C7AHPM4.js} +25 -6
  48. package/dist/chunk-2C7AHPM4.js.map +1 -0
  49. package/dist/{chunk-KPYDTIAI.js → chunk-2WZI36D3.js} +2 -2
  50. package/dist/{chunk-FUKDI2UK.js → chunk-3W6G6ARX.js} +2 -2
  51. package/dist/{chunk-JWSEPW7F.js → chunk-46GWHUKN.js} +2 -2
  52. package/dist/{chunk-3TZVZXM6.js → chunk-4KJMLYCD.js} +6 -6
  53. package/dist/{chunk-6JWEGFM5.js → chunk-5C35OW7L.js} +38 -7
  54. package/dist/chunk-5C35OW7L.js.map +1 -0
  55. package/dist/chunk-5GQCN2QZ.js +391 -0
  56. package/dist/chunk-5GQCN2QZ.js.map +1 -0
  57. package/dist/{chunk-KIGGMKAE.js → chunk-65Y3RCWP.js} +4 -2
  58. package/dist/chunk-65Y3RCWP.js.map +1 -0
  59. package/dist/{chunk-R3EKM2P2.js → chunk-6NCJGU6I.js} +5 -5
  60. package/dist/chunk-A4MVVPJ2.js +4204 -0
  61. package/dist/chunk-A4MVVPJ2.js.map +1 -0
  62. package/dist/{chunk-LPXSM2DI.js → chunk-AO45BX6Q.js} +3 -3
  63. package/dist/{chunk-3UXMQ3K2.js → chunk-AQM4JH62.js} +2 -2
  64. package/dist/chunk-AQM4JH62.js.map +1 -0
  65. package/dist/{chunk-NPMAATZG.js → chunk-C7NWUCLQ.js} +2 -2
  66. package/dist/chunk-CP4P6YKM.js +287 -0
  67. package/dist/chunk-CP4P6YKM.js.map +1 -0
  68. package/dist/{chunk-OWA7ZLC4.js → chunk-D4CKCNGW.js} +4 -4
  69. package/dist/{chunk-H5HDZ7IC.js → chunk-DCBZ5CHZ.js} +4 -4
  70. package/dist/{chunk-C6EYCD5J.js → chunk-FVHJCERJ.js} +5 -5
  71. package/dist/{chunk-BITS62MF.js → chunk-G6XKD7QE.js} +2 -2
  72. package/dist/{chunk-QE3JEJXF.js → chunk-GSZZLW5Q.js} +4 -4
  73. package/dist/chunk-HLVRHBKL.js +1078 -0
  74. package/dist/chunk-HLVRHBKL.js.map +1 -0
  75. package/dist/{chunk-TPANP5A2.js → chunk-JKJQWRLO.js} +2 -2
  76. package/dist/{chunk-62JQQUAU.js → chunk-JQH5VG6F.js} +2 -2
  77. package/dist/{chunk-6WXV5GTA.js → chunk-JVH5A3XC.js} +2 -2
  78. package/dist/{chunk-AVNSH2JE.js → chunk-KOXDXSKN.js} +7 -7
  79. package/dist/{chunk-2XRMPSEV.js → chunk-LXQB4ZGE.js} +2 -2
  80. package/dist/{chunk-ZL23HA4N.js → chunk-M6PN3VH6.js} +2 -2
  81. package/dist/{chunk-TZEVD4Q6.js → chunk-P5FRWPG7.js} +2 -2
  82. package/dist/{chunk-CSZFZKVX.js → chunk-PQJXIY64.js} +5 -5
  83. package/dist/{chunk-OP7DNHD2.js → chunk-QTOZ32SU.js} +27 -1
  84. package/dist/chunk-QTOZ32SU.js.map +1 -0
  85. package/dist/chunk-SVAB6KV5.js +219 -0
  86. package/dist/chunk-SVAB6KV5.js.map +1 -0
  87. package/dist/{chunk-LBO2YAZY.js → chunk-UNX6BS4U.js} +15 -15
  88. package/dist/{chunk-UWXDNVS5.js → chunk-ZJSGGJ5R.js} +2 -2
  89. package/dist/cli.js +20 -20
  90. package/dist/{connect-WBGCBBFJ.js → connect-S54TJ6OV.js} +3 -3
  91. package/dist/{conversion-writes-JU7Q2MUO.js → conversion-writes-ECZBVY7D.js} +2 -2
  92. package/dist/{conversions-3ISBR3TS.js → conversions-5QCSCRLI.js} +3 -3
  93. package/dist/copy-writes-D2RTHAL6.js +167 -0
  94. package/dist/copy-writes-D2RTHAL6.js.map +1 -0
  95. package/dist/{creatives-VQO2TKE3.js → creatives-5I3G5YP3.js} +2 -2
  96. package/dist/{creatives-YJ3CXZQC.js → creatives-MZ6ICNSY.js} +5 -4
  97. package/dist/{creatives-YJ3CXZQC.js.map → creatives-MZ6ICNSY.js.map} +1 -1
  98. package/dist/{creatives-S5QUWUL6.js → creatives-PCAGWPW3.js} +10 -10
  99. package/dist/{critique-5LCG5TYF.js → critique-NRHUPIWF.js} +7 -7
  100. package/dist/{custom-conversions-YRYE65PU.js → custom-conversions-FIP6ZS34.js} +5 -3
  101. package/dist/{custom-conversions-YRYE65PU.js.map → custom-conversions-FIP6ZS34.js.map} +1 -1
  102. package/dist/{draft-Y2QJE2NM.js → draft-34CGWUVA.js} +2 -2
  103. package/dist/{draft-GPN3HOAG.js → draft-4P3YO67N.js} +9 -5
  104. package/dist/draft-4P3YO67N.js.map +1 -0
  105. package/dist/{draft-EWDOIXSH.js → draft-L45PGCYZ.js} +7 -3
  106. package/dist/draft-L45PGCYZ.js.map +1 -0
  107. package/dist/engine/index.js +4 -4
  108. package/dist/{experiment-G3FQVAJU.js → experiment-7N4QFRWV.js} +4 -4
  109. package/dist/{feedback-CAOZPGTT.js → feedback-O3H42V4I.js} +10 -10
  110. package/dist/{feedback-record-EXV5H6OS.js → feedback-record-FAPN2GBS.js} +12 -12
  111. package/dist/{find-EVY4XEIQ.js → find-FSQAEGGF.js} +4 -4
  112. package/dist/{flows-Z7355RMD.js → flows-T22B77J3.js} +3 -3
  113. package/dist/{funnel-2B3JXJPX.js → funnel-7OA33VRP.js} +5 -5
  114. package/dist/{ga4-2BL5YAYN.js → ga4-LPDIBNYP.js} +1 -1
  115. package/dist/{google-7STODZQR.js → google-5ESVO76E.js} +5 -5
  116. package/dist/{gsc-UU3T2C6B.js → gsc-FIZTQX3N.js} +1 -1
  117. package/dist/{history-IQ33J5PB.js → history-QKDYBSYM.js} +2 -2
  118. package/dist/{icon-GB7UH5RF.js → icon-CSVAZ7NE.js} +4 -4
  119. package/dist/{ideas-F6CIGTVU.js → ideas-JQT422P7.js} +4 -4
  120. package/dist/{images-WCKTD6Z2.js → images-WBNWK2ZH.js} +6 -6
  121. package/dist/{insights-4HT2NAYP.js → insights-YWVJMATY.js} +4 -3
  122. package/dist/{insights-4HT2NAYP.js.map → insights-YWVJMATY.js.map} +1 -1
  123. package/dist/{inspiration-XQYHBJY2.js → inspiration-IX5N724J.js} +1 -1
  124. package/dist/instant-experiences-NQ2GWIWL.js +257 -0
  125. package/dist/instant-experiences-NQ2GWIWL.js.map +1 -0
  126. package/dist/{landing-WUILAWDS.js → landing-Y6QCTXCZ.js} +2 -2
  127. package/dist/leads-THI37ZMD.js +537 -0
  128. package/dist/leads-THI37ZMD.js.map +1 -0
  129. package/dist/{learn-CC6Q5IK2.js → learn-3BDZUWDT.js} +5 -5
  130. package/dist/{linkedin-XKVPSCLV.js → linkedin-UGMAFSGO.js} +2 -2
  131. package/dist/{locations-YA7NZY44.js → locations-HM56X2MK.js} +2 -2
  132. package/dist/locations-HM56X2MK.js.map +1 -0
  133. package/dist/{logo-YSNUUT33.js → logo-VHXH4J6A.js} +3 -3
  134. package/dist/{make-ED7HBLH3.js → make-W2PYZXYF.js} +39 -39
  135. package/dist/media-H6B7VDON.js +18 -0
  136. package/dist/media-H6B7VDON.js.map +1 -0
  137. package/dist/{media-sources-PEHUMH7C.js → media-sources-EZKAJNVD.js} +1 -1
  138. package/dist/meta-YHDTGJQ6.js +122 -0
  139. package/dist/meta-YHDTGJQ6.js.map +1 -0
  140. package/dist/{options-6FCNFWUF.js → options-NSCZSQ5P.js} +3 -3
  141. package/dist/page-photos-CXRG4AXV.js +115 -0
  142. package/dist/page-photos-CXRG4AXV.js.map +1 -0
  143. package/dist/{pages-FTI44OSH.js → pages-PTWRXAZT.js} +2 -2
  144. package/dist/{pixels-6IUB3Y3H.js → pixels-MYT3TCHZ.js} +2 -2
  145. package/dist/{pixels-MBKR4XPX.js → pixels-ZZCQUS3J.js} +2 -2
  146. package/dist/{preview-V4YBD45S.js → preview-XOXAAPMQ.js} +73 -27
  147. package/dist/preview-XOXAAPMQ.js.map +1 -0
  148. package/dist/{rerun-A2XHGFZS.js → rerun-5THJLQQJ.js} +18 -18
  149. package/dist/{research-BLDY5UWH.js → research-JHDWWFL7.js} +1 -1
  150. package/dist/rule-writes-6ZCAJDYZ.js +522 -0
  151. package/dist/rule-writes-6ZCAJDYZ.js.map +1 -0
  152. package/dist/rules-BMYOY2BM.js +216 -0
  153. package/dist/rules-BMYOY2BM.js.map +1 -0
  154. package/dist/{rules-SK5DFFPY.js → rules-YLLFDZEM.js} +3 -3
  155. package/dist/{rules-add-YQNYSME7.js → rules-add-U2GU4AGP.js} +4 -4
  156. package/dist/{run-KBN5K7LE.js → run-V5YQB6TH.js} +18 -18
  157. package/dist/{scaffold-ad-6BN5LWQH.js → scaffold-ad-H4GJOKAW.js} +12 -12
  158. package/dist/{scaffold-static-ad-VVZRAWBR.js → scaffold-static-ad-2YIFHCTL.js} +25 -25
  159. package/dist/{scaffold-video-LFIYI4GP.js → scaffold-video-3EJLKR2O.js} +14 -14
  160. package/dist/{select-QBU6ED4K.js → select-I22NPSIW.js} +9 -9
  161. package/dist/{set-prompt-TL64CRBQ.js → set-prompt-S75OMQ7L.js} +8 -8
  162. package/dist/{shared-NDRFKAH7.js → shared-3IBNDEFL.js} +2 -2
  163. package/dist/{sources-RMNFV4CA.js → sources-VA23PDND.js} +1 -1
  164. package/dist/{stale-ITFCPOER.js → stale-MHTU2NQJ.js} +8 -8
  165. package/dist/{stats-RCLQE4HJ.js → stats-ZAGJH4AD.js} +1 -1
  166. package/dist/{stock-ECRBD7RP.js → stock-6SHHLSKG.js} +5 -5
  167. package/dist/studies-4NIV4D5Y.js +249 -0
  168. package/dist/studies-4NIV4D5Y.js.map +1 -0
  169. package/dist/{studio-MARZ22J2.js → studio-ZLCGXFAN.js} +1 -1
  170. package/dist/study-writes-L2AA2PGL.js +467 -0
  171. package/dist/study-writes-L2AA2PGL.js.map +1 -0
  172. package/dist/{sync-definition-C6ROLKJ4.js → sync-definition-2EBSQA6A.js} +11 -11
  173. package/dist/{tag-manager-G3LBQZUL.js → tag-manager-3CJ66GKJ.js} +1 -1
  174. package/dist/targeting-LNDQV4EK.js +759 -0
  175. package/dist/targeting-LNDQV4EK.js.map +1 -0
  176. package/dist/{testimonials-YMMA5YWL.js → testimonials-GR6TIS5S.js} +2 -2
  177. package/dist/{translate-OXVIMYHE.js → translate-GEFUP6KQ.js} +18 -18
  178. package/dist/{validate-ZS2M5Q3V.js → validate-NOWIOAHE.js} +11 -11
  179. package/dist/{videos-I6YXFBFT.js → videos-XMTDEHEJ.js} +1 -1
  180. package/dist/{viewer-check-7ZG7KHEN.js → viewer-check-U4XZMMS7.js} +6 -6
  181. package/dist/viewer-check-U4XZMMS7.js.map +1 -0
  182. package/dist/{winning-ads-ABN5OULS.js → winning-ads-QOAYIR5U.js} +1 -1
  183. package/dist/{write-commands-HCQI4Y27.js → write-commands-PEJBTME6.js} +485 -57
  184. package/dist/write-commands-PEJBTME6.js.map +1 -0
  185. package/dist/{x-WK2VQDPF.js → x-VCSMV7T5.js} +4 -4
  186. package/package.json +1 -1
  187. package/dist/account-7LUQWY6Q.js +0 -49
  188. package/dist/account-7LUQWY6Q.js.map +0 -1
  189. package/dist/ads-HXX2IAFF.js.map +0 -1
  190. package/dist/audiences-XEZHACKH.js.map +0 -1
  191. package/dist/campaign-writes-BAFB5E7A.js.map +0 -1
  192. package/dist/campaigns-4SOLY7OR.js.map +0 -1
  193. package/dist/chats-INN2OHSH.js.map +0 -1
  194. package/dist/chunk-3UXMQ3K2.js.map +0 -1
  195. package/dist/chunk-6JWEGFM5.js.map +0 -1
  196. package/dist/chunk-7RBJ2Z7Z.js.map +0 -1
  197. package/dist/chunk-GOJXKIFQ.js +0 -1605
  198. package/dist/chunk-GOJXKIFQ.js.map +0 -1
  199. package/dist/chunk-KIGGMKAE.js.map +0 -1
  200. package/dist/chunk-OP7DNHD2.js.map +0 -1
  201. package/dist/draft-EWDOIXSH.js.map +0 -1
  202. package/dist/draft-GPN3HOAG.js.map +0 -1
  203. package/dist/leads-2XOSAYP4.js +0 -293
  204. package/dist/leads-2XOSAYP4.js.map +0 -1
  205. package/dist/locations-YA7NZY44.js.map +0 -1
  206. package/dist/media-ES2UBGYW.js +0 -15
  207. package/dist/media-ES2UBGYW.js.map +0 -1
  208. package/dist/meta-AOIPEDV5.js +0 -69
  209. package/dist/meta-AOIPEDV5.js.map +0 -1
  210. package/dist/preview-V4YBD45S.js.map +0 -1
  211. package/dist/write-commands-HCQI4Y27.js.map +0 -1
  212. /package/dist/{account-Z65CPOWE.js.map → account-3H6BZO5H.js.map} +0 -0
  213. /package/dist/{account-writes-4N2MA2ZR.js.map → account-writes-AE5IJGGW.js.map} +0 -0
  214. /package/dist/{accounts-UA2ZWRN4.js.map → accounts-K4INATCW.js.map} +0 -0
  215. /package/dist/{activities-IQPJPP4B.js.map → activities-T253KK4Q.js.map} +0 -0
  216. /package/dist/{ad-group-writes-EUTMPBMK.js.map → ad-group-writes-VRSBI2Z2.js.map} +0 -0
  217. /package/dist/{ad-groups-OVPAI5RM.js.map → ad-groups-IEBVEAMS.js.map} +0 -0
  218. /package/dist/{ad-writes-DACTYFEO.js.map → ad-writes-ALV4KFPZ.js.map} +0 -0
  219. /package/dist/{ads-L65JIKCD.js.map → ads-CQZHD7FC.js.map} +0 -0
  220. /package/dist/{ads-G6AS5L7G.js.map → ads-QIKZ6O2N.js.map} +0 -0
  221. /package/dist/{analytics-QKP3A5F3.js.map → analytics-ALOZ7ZZ4.js.map} +0 -0
  222. /package/dist/{connect-WBGCBBFJ.js.map → asset-writes-L5TMKIZC.js.map} +0 -0
  223. /package/dist/{audiences-RPLVMO4H.js.map → audiences-AAZH56WD.js.map} +0 -0
  224. /package/dist/{avatars-MXTI74VC.js.map → avatars-YSFFRHDK.js.map} +0 -0
  225. /package/dist/{brief-BN4UGUXF.js.map → brief-7S6XCJIA.js.map} +0 -0
  226. /package/dist/{businesses-PRRYPR7H.js.map → businesses-H3HIDQX6.js.map} +0 -0
  227. /package/dist/{campaign-writes-U5LTDTYK.js.map → campaign-writes-U5PUVSMT.js.map} +0 -0
  228. /package/dist/{campaigns-TFEQTSO3.js.map → campaigns-JEIKY2M4.js.map} +0 -0
  229. /package/dist/{campaigns-NR6TNQFN.js.map → campaigns-VTRMKSAT.js.map} +0 -0
  230. /package/dist/{canvas-GJAQAVS6.js.map → canvas-SVJEQO4V.js.map} +0 -0
  231. /package/dist/{catalog-USZL4SGX.js.map → catalog-S32MX453.js.map} +0 -0
  232. /package/dist/{catalogs-A7XMEEEP.js.map → catalogs-OCVXZLIF.js.map} +0 -0
  233. /package/dist/{chatgpt-4PEWDJKP.js.map → chatgpt-QVF2FD3E.js.map} +0 -0
  234. /package/dist/{check-MMJGFFZS.js.map → check-SLNHVYGS.js.map} +0 -0
  235. /package/dist/{chunk-KPYDTIAI.js.map → chunk-2WZI36D3.js.map} +0 -0
  236. /package/dist/{chunk-FUKDI2UK.js.map → chunk-3W6G6ARX.js.map} +0 -0
  237. /package/dist/{chunk-JWSEPW7F.js.map → chunk-46GWHUKN.js.map} +0 -0
  238. /package/dist/{chunk-3TZVZXM6.js.map → chunk-4KJMLYCD.js.map} +0 -0
  239. /package/dist/{chunk-R3EKM2P2.js.map → chunk-6NCJGU6I.js.map} +0 -0
  240. /package/dist/{chunk-LPXSM2DI.js.map → chunk-AO45BX6Q.js.map} +0 -0
  241. /package/dist/{chunk-NPMAATZG.js.map → chunk-C7NWUCLQ.js.map} +0 -0
  242. /package/dist/{chunk-OWA7ZLC4.js.map → chunk-D4CKCNGW.js.map} +0 -0
  243. /package/dist/{chunk-H5HDZ7IC.js.map → chunk-DCBZ5CHZ.js.map} +0 -0
  244. /package/dist/{chunk-C6EYCD5J.js.map → chunk-FVHJCERJ.js.map} +0 -0
  245. /package/dist/{chunk-BITS62MF.js.map → chunk-G6XKD7QE.js.map} +0 -0
  246. /package/dist/{chunk-QE3JEJXF.js.map → chunk-GSZZLW5Q.js.map} +0 -0
  247. /package/dist/{chunk-TPANP5A2.js.map → chunk-JKJQWRLO.js.map} +0 -0
  248. /package/dist/{chunk-62JQQUAU.js.map → chunk-JQH5VG6F.js.map} +0 -0
  249. /package/dist/{chunk-6WXV5GTA.js.map → chunk-JVH5A3XC.js.map} +0 -0
  250. /package/dist/{chunk-AVNSH2JE.js.map → chunk-KOXDXSKN.js.map} +0 -0
  251. /package/dist/{chunk-2XRMPSEV.js.map → chunk-LXQB4ZGE.js.map} +0 -0
  252. /package/dist/{chunk-ZL23HA4N.js.map → chunk-M6PN3VH6.js.map} +0 -0
  253. /package/dist/{chunk-TZEVD4Q6.js.map → chunk-P5FRWPG7.js.map} +0 -0
  254. /package/dist/{chunk-CSZFZKVX.js.map → chunk-PQJXIY64.js.map} +0 -0
  255. /package/dist/{chunk-LBO2YAZY.js.map → chunk-UNX6BS4U.js.map} +0 -0
  256. /package/dist/{chunk-UWXDNVS5.js.map → chunk-ZJSGGJ5R.js.map} +0 -0
  257. /package/dist/{ideas-F6CIGTVU.js.map → connect-S54TJ6OV.js.map} +0 -0
  258. /package/dist/{conversion-writes-JU7Q2MUO.js.map → conversion-writes-ECZBVY7D.js.map} +0 -0
  259. /package/dist/{conversions-3ISBR3TS.js.map → conversions-5QCSCRLI.js.map} +0 -0
  260. /package/dist/{creatives-VQO2TKE3.js.map → creatives-5I3G5YP3.js.map} +0 -0
  261. /package/dist/{creatives-S5QUWUL6.js.map → creatives-PCAGWPW3.js.map} +0 -0
  262. /package/dist/{critique-5LCG5TYF.js.map → critique-NRHUPIWF.js.map} +0 -0
  263. /package/dist/{draft-Y2QJE2NM.js.map → draft-34CGWUVA.js.map} +0 -0
  264. /package/dist/{experiment-G3FQVAJU.js.map → experiment-7N4QFRWV.js.map} +0 -0
  265. /package/dist/{feedback-CAOZPGTT.js.map → feedback-O3H42V4I.js.map} +0 -0
  266. /package/dist/{feedback-record-EXV5H6OS.js.map → feedback-record-FAPN2GBS.js.map} +0 -0
  267. /package/dist/{find-EVY4XEIQ.js.map → find-FSQAEGGF.js.map} +0 -0
  268. /package/dist/{flows-Z7355RMD.js.map → flows-T22B77J3.js.map} +0 -0
  269. /package/dist/{funnel-2B3JXJPX.js.map → funnel-7OA33VRP.js.map} +0 -0
  270. /package/dist/{ga4-2BL5YAYN.js.map → ga4-LPDIBNYP.js.map} +0 -0
  271. /package/dist/{google-7STODZQR.js.map → google-5ESVO76E.js.map} +0 -0
  272. /package/dist/{gsc-UU3T2C6B.js.map → gsc-FIZTQX3N.js.map} +0 -0
  273. /package/dist/{history-IQ33J5PB.js.map → history-QKDYBSYM.js.map} +0 -0
  274. /package/dist/{icon-GB7UH5RF.js.map → icon-CSVAZ7NE.js.map} +0 -0
  275. /package/dist/{logo-YSNUUT33.js.map → ideas-JQT422P7.js.map} +0 -0
  276. /package/dist/{images-WCKTD6Z2.js.map → images-WBNWK2ZH.js.map} +0 -0
  277. /package/dist/{inspiration-XQYHBJY2.js.map → inspiration-IX5N724J.js.map} +0 -0
  278. /package/dist/{landing-WUILAWDS.js.map → landing-Y6QCTXCZ.js.map} +0 -0
  279. /package/dist/{learn-CC6Q5IK2.js.map → learn-3BDZUWDT.js.map} +0 -0
  280. /package/dist/{linkedin-XKVPSCLV.js.map → linkedin-UGMAFSGO.js.map} +0 -0
  281. /package/dist/{run-KBN5K7LE.js.map → logo-VHXH4J6A.js.map} +0 -0
  282. /package/dist/{make-ED7HBLH3.js.map → make-W2PYZXYF.js.map} +0 -0
  283. /package/dist/{media-sources-PEHUMH7C.js.map → media-sources-EZKAJNVD.js.map} +0 -0
  284. /package/dist/{options-6FCNFWUF.js.map → options-NSCZSQ5P.js.map} +0 -0
  285. /package/dist/{pages-FTI44OSH.js.map → pages-PTWRXAZT.js.map} +0 -0
  286. /package/dist/{pixels-6IUB3Y3H.js.map → pixels-MYT3TCHZ.js.map} +0 -0
  287. /package/dist/{pixels-MBKR4XPX.js.map → pixels-ZZCQUS3J.js.map} +0 -0
  288. /package/dist/{rerun-A2XHGFZS.js.map → rerun-5THJLQQJ.js.map} +0 -0
  289. /package/dist/{research-BLDY5UWH.js.map → research-JHDWWFL7.js.map} +0 -0
  290. /package/dist/{rules-SK5DFFPY.js.map → rules-YLLFDZEM.js.map} +0 -0
  291. /package/dist/{rules-add-YQNYSME7.js.map → rules-add-U2GU4AGP.js.map} +0 -0
  292. /package/dist/{shared-NDRFKAH7.js.map → run-V5YQB6TH.js.map} +0 -0
  293. /package/dist/{scaffold-ad-6BN5LWQH.js.map → scaffold-ad-H4GJOKAW.js.map} +0 -0
  294. /package/dist/{scaffold-static-ad-VVZRAWBR.js.map → scaffold-static-ad-2YIFHCTL.js.map} +0 -0
  295. /package/dist/{scaffold-video-LFIYI4GP.js.map → scaffold-video-3EJLKR2O.js.map} +0 -0
  296. /package/dist/{select-QBU6ED4K.js.map → select-I22NPSIW.js.map} +0 -0
  297. /package/dist/{set-prompt-TL64CRBQ.js.map → set-prompt-S75OMQ7L.js.map} +0 -0
  298. /package/dist/{sync-definition-C6ROLKJ4.js.map → shared-3IBNDEFL.js.map} +0 -0
  299. /package/dist/{sources-RMNFV4CA.js.map → sources-VA23PDND.js.map} +0 -0
  300. /package/dist/{stale-ITFCPOER.js.map → stale-MHTU2NQJ.js.map} +0 -0
  301. /package/dist/{stats-RCLQE4HJ.js.map → stats-ZAGJH4AD.js.map} +0 -0
  302. /package/dist/{stock-ECRBD7RP.js.map → stock-6SHHLSKG.js.map} +0 -0
  303. /package/dist/{studio-MARZ22J2.js.map → studio-ZLCGXFAN.js.map} +0 -0
  304. /package/dist/{viewer-check-7ZG7KHEN.js.map → sync-definition-2EBSQA6A.js.map} +0 -0
  305. /package/dist/{tag-manager-G3LBQZUL.js.map → tag-manager-3CJ66GKJ.js.map} +0 -0
  306. /package/dist/{testimonials-YMMA5YWL.js.map → testimonials-GR6TIS5S.js.map} +0 -0
  307. /package/dist/{translate-OXVIMYHE.js.map → translate-GEFUP6KQ.js.map} +0 -0
  308. /package/dist/{validate-ZS2M5Q3V.js.map → validate-NOWIOAHE.js.map} +0 -0
  309. /package/dist/{videos-I6YXFBFT.js.map → videos-XMTDEHEJ.js.map} +0 -0
  310. /package/dist/{winning-ads-ABN5OULS.js.map → winning-ads-QOAYIR5U.js.map} +0 -0
  311. /package/dist/{x-WK2VQDPF.js.map → x-VCSMV7T5.js.map} +0 -0
@@ -1,1605 +0,0 @@
1
- // ../api/src/ads-meta/customConversions.ts
2
- var RULE_OPERATORS = {
3
- eq: "is",
4
- neq: "is not",
5
- contains: "contains",
6
- i_contains: "contains",
7
- not_contains: "does not contain",
8
- i_not_contains: "does not contain",
9
- starts_with: "starts with",
10
- i_starts_with: "starts with",
11
- ends_with: "ends with",
12
- i_ends_with: "ends with",
13
- gt: "is more than",
14
- gte: "is at least",
15
- lt: "is less than",
16
- lte: "is at most",
17
- regex_match: "matches",
18
- exists: "exists"
19
- };
20
- function isRecord(value) {
21
- return typeof value === "object" && value !== null && !Array.isArray(value);
22
- }
23
- var DEFAULT_CUSTOM_CONVERSION_EVENT = "PageView";
24
- function buildCustomConversionRule(input) {
25
- return {
26
- and: [
27
- { event: { eq: input.event ?? DEFAULT_CUSTOM_CONVERSION_EVENT } },
28
- { URL: { i_contains: input.urlContains } }
29
- ]
30
- };
31
- }
32
- function customConversionRuleRefusal(rule) {
33
- const refusal = ruleNodeRefusal(rule, "rule");
34
- if (refusal) {
35
- return refusal;
36
- }
37
- if (!ruleFields(rule).some((field) => field !== "event")) {
38
- return 'rule only names the event, and Meta refuses a custom conversion with no condition beyond it \u2014 narrow it with a URL, referrer or event parameter the event carries, e.g. {"and":[{"event":{"eq":"Lead"}},{"URL":{"i_contains":"/gracias"}}]}';
39
- }
40
- return void 0;
41
- }
42
- function ruleFields(node) {
43
- if (!isRecord(node)) {
44
- return [];
45
- }
46
- return Object.entries(node).flatMap(
47
- ([key, value]) => (key === "and" || key === "or") && Array.isArray(value) ? value.flatMap(ruleFields) : [key]
48
- );
49
- }
50
- function branchRefusal(key, value, path) {
51
- if (!Array.isArray(value) || value.length === 0) {
52
- return `${path}.${key} must be a non-empty array of conditions \u2014 an empty one counts every event the pixel sends`;
53
- }
54
- for (const [index, child] of value.entries()) {
55
- const refusal = ruleNodeRefusal(child, `${path}.${key}[${index}]`);
56
- if (refusal) {
57
- return refusal;
58
- }
59
- }
60
- return void 0;
61
- }
62
- function conditionRefusal(key, value, path) {
63
- return key === "and" || key === "or" ? branchRefusal(key, value, path) : leafRefusal(key, value, path);
64
- }
65
- function ruleNodeRefusal(node, path) {
66
- if (!isRecord(node)) {
67
- return `${path} must be an object like {"URL":{"i_contains":"/thank-you"}}`;
68
- }
69
- const entries = Object.entries(node);
70
- if (entries.length === 0) {
71
- return `${path} is empty \u2014 a rule with no condition counts every event the pixel sends`;
72
- }
73
- if (entries.length > 1) {
74
- return `${path} names ${entries.length} conditions side by side (${entries.map(([key]) => key).join(", ")}) \u2014 Meta reads one branch or one field test per object, so combine them with {"and":[\u2026]}`;
75
- }
76
- for (const [key, value] of entries) {
77
- const refusal = conditionRefusal(key, value, path);
78
- if (refusal) {
79
- return refusal;
80
- }
81
- }
82
- return void 0;
83
- }
84
- function leafRefusal(field, condition, path) {
85
- if (!isRecord(condition)) {
86
- return `${path}.${field} must be a test like {"i_contains":"/thank-you"}, not a bare value`;
87
- }
88
- const tests = Object.entries(condition);
89
- if (tests.length === 0) {
90
- return `${path}.${field} names no test \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")}`;
91
- }
92
- if (tests.length > 1) {
93
- return `${path}.${field} names ${tests.length} operators at once (${tests.map(([operator]) => operator).join(", ")}) \u2014 test a field once per condition and combine them with {"and":[\u2026]}`;
94
- }
95
- for (const [operator] of tests) {
96
- if (!RULE_OPERATORS[operator]) {
97
- return `${path}.${field} uses "${operator}", which is not a Meta rule operator \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")} (URL matching is i_contains, not contains or includes)`;
98
- }
99
- }
100
- return void 0;
101
- }
102
-
103
- // ../api/src/ads-meta/leadAds.ts
104
- var META_LEAD_ADS_NOT_ENABLED_MESSAGE = "Using Meta instant forms and their leads is not switched on for this company. Everything else on Meta keeps working. Tell the user this needs switching on by the Baker team, and carry on with the rest of the job.";
105
- var META_LEAD_ADS_RECONNECT_MESSAGE = "This Meta connection was made before lead ads were switched on for this company, so it cannot work with instant forms or read their leads. Ask the user to reconnect Meta in the dashboard \u2014 that grants the extra permissions \u2014 then ask for this again. Everything else on Meta keeps working meanwhile.";
106
- function isMetaLeadAdsRefusal(message) {
107
- return message === META_LEAD_ADS_NOT_ENABLED_MESSAGE || message === META_LEAD_ADS_RECONNECT_MESSAGE;
108
- }
109
-
110
- // ../api/src/ads-meta/placements.ts
111
- var FACEBOOK_POSITIONS_REQUIRING_FEED = ["marketplace", "search", "profile_feed", "notification"];
112
- var PLACEMENT_TARGETING_DOC = "https://developers.facebook.com/docs/marketing-api/audiences/reference/placement-targeting/";
113
- var PUBLISHER_PLATFORMS = ["facebook", "instagram", "threads", "messenger", "audience_network"];
114
- var FACEBOOK_POSITIONS = [
115
- "feed",
116
- "right_hand_column",
117
- "marketplace",
118
- "story",
119
- "search",
120
- "instream_video",
121
- "facebook_reels",
122
- "facebook_reels_overlay",
123
- "profile_feed",
124
- "notification"
125
- ];
126
- var INSTAGRAM_POSITIONS = [
127
- "stream",
128
- "story",
129
- "explore",
130
- "explore_home",
131
- "reels",
132
- "profile_feed",
133
- "ig_search",
134
- "profile_reels"
135
- ];
136
- var AUDIENCE_NETWORK_POSITIONS = ["classic", "rewarded_video"];
137
- var MESSENGER_POSITIONS = ["sponsored_messages", "story"];
138
- var THREADS_POSITIONS = ["threads_stream"];
139
- var DEVICE_PLATFORMS = ["mobile", "desktop", "connected_tv"];
140
- var PLACEMENT_VOCABULARIES = {
141
- publisher_platforms: PUBLISHER_PLATFORMS,
142
- facebook_positions: FACEBOOK_POSITIONS,
143
- instagram_positions: INSTAGRAM_POSITIONS,
144
- audience_network_positions: AUDIENCE_NETWORK_POSITIONS,
145
- messenger_positions: MESSENGER_POSITIONS,
146
- threads_positions: THREADS_POSITIONS,
147
- device_platforms: DEVICE_PLATFORMS
148
- };
149
- var SAME_SURFACE_ON_THIS_PLATFORM = {
150
- facebook_positions: { reels: "facebook_reels", stream: "feed", stories: "story", ig_search: "search" },
151
- instagram_positions: {
152
- feed: "stream",
153
- facebook_reels: "reels",
154
- stories: "story",
155
- search: "ig_search"
156
- }
157
- };
158
- var RETIRED_PLACEMENTS = {
159
- facebook_positions: {
160
- video_feeds: 'Meta retired Facebook video feeds \u2014 use "instream_video" for in-stream video, or "facebook_reels", and drop "video_feeds"'
161
- }
162
- };
163
- function placementFieldsAccepting(value, except) {
164
- return Object.keys(PLACEMENT_VOCABULARIES).filter(
165
- (field) => field !== except && PLACEMENT_VOCABULARIES[field].includes(value)
166
- );
167
- }
168
- function placementValueIssue(field, value) {
169
- const retired = RETIRED_PLACEMENTS[field]?.[value];
170
- if (retired) {
171
- return { field, value, kind: "retired", fix: retired };
172
- }
173
- const renamed = SAME_SURFACE_ON_THIS_PLATFORM[field]?.[value];
174
- if (renamed) {
175
- return {
176
- field,
177
- value,
178
- kind: "wrong-field",
179
- fix: `Meta calls that surface "${renamed}" in ${field} \u2014 use "${renamed}"`
180
- };
181
- }
182
- const elsewhere = placementFieldsAccepting(value, field);
183
- if (elsewhere.length > 0) {
184
- return {
185
- field,
186
- value,
187
- kind: "wrong-field",
188
- fix: `"${value}" belongs to ${elsewhere.join(" / ")}, not ${field} \u2014 move it there or drop it`
189
- };
190
- }
191
- return {
192
- field,
193
- value,
194
- kind: "unknown",
195
- fix: `"${value}" is not a placement Baker knows; ${field} accepts ${PLACEMENT_VOCABULARIES[field].join(", ")}`
196
- };
197
- }
198
- function invalidPlacementValues(targeting) {
199
- const issues = [];
200
- for (const field of Object.keys(PLACEMENT_VOCABULARIES)) {
201
- const allowed = PLACEMENT_VOCABULARIES[field];
202
- const selected = targeting?.[field] ?? [];
203
- for (const value of selected) {
204
- if (!allowed.includes(value)) {
205
- issues.push(placementValueIssue(field, value));
206
- }
207
- }
208
- }
209
- return issues;
210
- }
211
- function placementCoRequirementIssues(targeting) {
212
- const facebook = targeting?.facebook_positions ?? [];
213
- const instagram = targeting?.instagram_positions ?? [];
214
- const threads = targeting?.threads_positions ?? [];
215
- const devices = targeting?.device_platforms;
216
- const issues = [];
217
- if (facebook.includes("story")) {
218
- if (!facebook.includes("feed") && !instagram.includes("story")) {
219
- issues.push(
220
- 'Meta refuses Facebook "story" on its own \u2014 select "feed" in facebook_positions or "story" in instagram_positions alongside it'
221
- );
222
- }
223
- if (devices && !devices.includes("mobile")) {
224
- issues.push(
225
- 'Facebook "story" only runs on mobile \u2014 add "mobile" to device_platforms or drop the story placement'
226
- );
227
- }
228
- }
229
- if (instagram.includes("explore_home") && !instagram.includes("explore")) {
230
- issues.push(
231
- 'Meta refuses Instagram "explore_home" without "explore" \u2014 add "explore" to instagram_positions or drop explore_home'
232
- );
233
- }
234
- if (threads.includes("threads_stream") && !instagram.includes("stream")) {
235
- issues.push('Threads "threads_stream" cannot be selected alone \u2014 add "stream" to instagram_positions');
236
- }
237
- return issues;
238
- }
239
- var CONSTRAINED_PLACEMENTS = [
240
- {
241
- position: "facebook_reels_overlay",
242
- reportedAs: "facebook_reels_overlay",
243
- platform: "facebook",
244
- kind: "constraint",
245
- note: "Ads on Facebook Reels take two shapes and a stock 9:16 video fits neither: overlay ads are 'a single, static image ad' (JPG/PNG, 1:1, 0% ratio tolerance, headline capped at 10 characters), and post-loop ads are 1:1 video of 4-15 seconds. Expect a long-headline 9:16 creative to look wrong here.",
246
- contested: false,
247
- source: "https://www.facebook.com/business/ads-guide/update/image/facebook-facebook-reels-overlay"
248
- },
249
- {
250
- position: "explore_home",
251
- reportedAs: "instagram_explore_grid_home",
252
- platform: "instagram",
253
- kind: "constraint",
254
- note: "Marketing API placement asset customization states 'The explore_home placement only supports the SINGLE_IMAGE format', but Meta's own Ads Guide lists Instagram Explore home under video specs. Check the account's play rate for this position before treating it as image-only.",
255
- contested: true,
256
- source: "https://developers.facebook.com/docs/marketing-api/dynamic-creative/placement-asset-customization/"
257
- },
258
- {
259
- position: "right_hand_column",
260
- reportedAs: "right_hand_column",
261
- platform: "facebook",
262
- kind: "correction",
263
- note: "Facebook right column publishes full video specs (MP4/MOV/GIF, 1:1 at 1440x1440 or mobile-only 4:5 at 1440x1800, 1 second to 241 minutes) \u2014 it is NOT image-only. Its real constraint is that it cannot be the only placement for a video, collection, or canvas ad.",
264
- contested: false,
265
- source: "https://www.facebook.com/business/ads-guide/update/video/facebook-right-hand-column"
266
- }
267
- ];
268
- var FACEBOOK_PREVIEW_FORMATS = {
269
- feed: "MOBILE_FEED_STANDARD",
270
- right_hand_column: "RIGHT_COLUMN_STANDARD",
271
- marketplace: "MARKETPLACE_MOBILE",
272
- facebook_stories: "FACEBOOK_STORY_MOBILE",
273
- facebook_reels: "FACEBOOK_REELS_MOBILE",
274
- facebook_reels_overlay: "FACEBOOK_REELS_BANNER",
275
- instream_video: "INSTREAM_VIDEO_MOBILE",
276
- // Retired for targeting (RETIRED_PLACEMENTS) and still reported by a breakdown
277
- // over any window that predates the retirement — an ad set created before it
278
- // keeps delivering there. Previewing what a historic row measured has to keep
279
- // working, so this stays.
280
- video_feeds: "INSTREAM_VIDEO_MOBILE"
281
- };
282
- var INSTAGRAM_PREVIEW_FORMATS = {
283
- feed: "INSTAGRAM_STANDARD",
284
- instagram_stories: "INSTAGRAM_STORY",
285
- instagram_reels: "INSTAGRAM_REELS",
286
- instagram_explore: "INSTAGRAM_EXPLORE_CONTEXTUAL",
287
- instagram_explore_grid_home: "INSTAGRAM_EXPLORE_GRID_HOME",
288
- instagram_search: "INSTAGRAM_SEARCH_CHAIN",
289
- instagram_profile_feed: "INSTAGRAM_PROFILE_FEED"
290
- };
291
- var PLATFORM_SCOPED_POSITIONS = /* @__PURE__ */ new Set(["feed"]);
292
- function previewFormatForPosition(position, platform) {
293
- if (platform === "facebook") return FACEBOOK_PREVIEW_FORMATS[position];
294
- if (platform === "instagram") return INSTAGRAM_PREVIEW_FORMATS[position];
295
- if (PLATFORM_SCOPED_POSITIONS.has(position)) return void 0;
296
- return FACEBOOK_PREVIEW_FORMATS[position] ?? INSTAGRAM_PREVIEW_FORMATS[position];
297
- }
298
- function placementFormatNote(reportedPosition) {
299
- return CONSTRAINED_PLACEMENTS.find((p) => p.reportedAs === reportedPosition);
300
- }
301
- function positionsMissingRequiredFeed(targeting) {
302
- const positions = targeting?.facebook_positions;
303
- if (!positions?.length || positions.includes("feed")) {
304
- return [];
305
- }
306
- return FACEBOOK_POSITIONS_REQUIRING_FEED.filter((required) => positions.includes(required));
307
- }
308
-
309
- // ../api/src/ads-meta/limits.ts
310
- var META_LIMITS = {
311
- campaign: {
312
- nameMax: 400
313
- },
314
- adSet: {
315
- nameMax: 400
316
- },
317
- ad: {
318
- nameMax: 400
319
- },
320
- creative: {
321
- nameMax: 400,
322
- /** Feed truncates the primary text past this with "… See more". */
323
- messageSoftMax: 125,
324
- messageHardMax: 5e3,
325
- /** Below this, primary text reads as too thin to carry a hook + value + reason to act. */
326
- messageGoodMin: 40,
327
- headlineMax: 255,
328
- descriptionMax: 300,
329
- carouselCardsMin: 2,
330
- carouselCardsMax: 10,
331
- /** Carousels tell a fuller story and give Meta more to optimize at this many cards. */
332
- carouselCardsGoodMin: 3,
333
- dynamicTextsMin: 1,
334
- dynamicTextsMax: 5,
335
- /** Dynamic ads mix and match best with at least this many media assets. */
336
- dynamicAssetsGoodMin: 3
337
- },
338
- audience: {
339
- nameMax: 100,
340
- /** Website/engagement audiences below this rarely serve. */
341
- minLookalikeSourceRows: 100,
342
- retentionDaysMax: 180
343
- },
344
- customConversion: {
345
- nameMax: 100,
346
- descriptionMax: 500,
347
- /**
348
- * Meta's hard cap per ad account. It matters because deleting a custom
349
- * conversion archives it rather than removing it, so a chat that creates
350
- * one per campaign spends slots that only a person, in Events Manager, can
351
- * think about reclaiming.
352
- */
353
- perAccountMax: 100
354
- }
355
- };
356
- var OBJECTIVES = [
357
- "OUTCOME_TRAFFIC",
358
- "OUTCOME_ENGAGEMENT",
359
- "OUTCOME_LEADS",
360
- "OUTCOME_SALES",
361
- "OUTCOME_AWARENESS",
362
- "OUTCOME_APP_PROMOTION"
363
- ];
364
- var SPECIAL_AD_CATEGORIES = [
365
- "NONE",
366
- "HOUSING",
367
- "EMPLOYMENT",
368
- /** Deprecated by Meta on 2025-01-14 — replaced by FINANCIAL_PRODUCTS_SERVICES. Still accepted as input and mapped on the wire. */
369
- "CREDIT",
370
- "ISSUES_ELECTIONS_POLITICS",
371
- "FINANCIAL_PRODUCTS_SERVICES",
372
- "ONLINE_GAMBLING_AND_GAMING"
373
- ];
374
- var SPECIAL_CATEGORIES_REQUIRING_COUNTRY = ["ISSUES_ELECTIONS_POLITICS"];
375
- var BUYING_TYPES = ["AUCTION", "RESERVED"];
376
- var BID_STRATEGIES = [
377
- "LOWEST_COST_WITHOUT_CAP",
378
- "LOWEST_COST_WITH_BID_CAP",
379
- "COST_CAP",
380
- "LOWEST_COST_WITH_MIN_ROAS"
381
- ];
382
- var BID_STRATEGIES_REQUIRING_AMOUNT = ["LOWEST_COST_WITH_BID_CAP", "COST_CAP"];
383
- var BID_STRATEGIES_FORBIDDING_AMOUNT = [
384
- "LOWEST_COST_WITHOUT_CAP",
385
- "LOWEST_COST_WITH_MIN_ROAS"
386
- ];
387
- var BILLING_EVENTS = [
388
- "APP_INSTALLS",
389
- "CLICKS",
390
- "IMPRESSIONS",
391
- "LINK_CLICKS",
392
- "NONE",
393
- "OFFER_CLAIMS",
394
- "PAGE_LIKES",
395
- "POST_ENGAGEMENT",
396
- "THRUPLAY",
397
- "PURCHASE",
398
- "LISTING_INTERACTION"
399
- ];
400
- var OPTIMIZATION_GOALS = [
401
- "NONE",
402
- "APP_INSTALLS",
403
- "AD_RECALL_LIFT",
404
- "ENGAGED_USERS",
405
- "EVENT_RESPONSES",
406
- "IMPRESSIONS",
407
- "LEAD_GENERATION",
408
- "QUALITY_LEAD",
409
- "QUALITY_CALL",
410
- "LINK_CLICKS",
411
- "OFFSITE_CONVERSIONS",
412
- "LANDING_PAGE_VIEWS",
413
- "PAGE_LIKES",
414
- "POST_ENGAGEMENT",
415
- "REACH",
416
- "THRUPLAY",
417
- "VALUE",
418
- "CONVERSATIONS",
419
- "DERIVED_EVENTS",
420
- // Added 2026-09-18 from Meta's own generated enum spec
421
- // (facebook-business-sdk-codegen › AdSet_optimization_goal). Every one of these
422
- // is a goal Meta accepts and Baker used to refuse at stage time — the messaging
423
- // and Instagram-profile goals are the ones that cost real campaigns, because a
424
- // click-to-WhatsApp ad set cannot be expressed without them.
425
- "ADVERTISER_SILOED_VALUE",
426
- "APP_INSTALLS_AND_OFFSITE_CONVERSIONS",
427
- "AUTOMATIC_OBJECTIVE",
428
- "ENGAGED_PAGE_VIEWS",
429
- "IN_APP_VALUE",
430
- "MEANINGFUL_CALL_ATTEMPT",
431
- "MESSAGING_APPOINTMENT_CONVERSION",
432
- "MESSAGING_DEEP_CONVERSATION_AND_FOLLOW",
433
- "MESSAGING_PURCHASE_CONVERSION",
434
- "PROFILE_AND_PAGE_ENGAGEMENT",
435
- "PROFILE_VISIT",
436
- "REMINDERS_SET",
437
- "SUBSCRIBERS",
438
- "VISIT_INSTAGRAM_PROFILE"
439
- ];
440
- var OPTIMIZATION_GOALS_REQUIRING_PAGE = [
441
- "LEAD_GENERATION",
442
- "QUALITY_LEAD",
443
- "PAGE_LIKES"
444
- ];
445
- var OPTIMIZATION_GOALS_REQUIRING_PIXEL = ["OFFSITE_CONVERSIONS"];
446
- var OPTIMIZATION_GOALS_REQUIRING_CONVERSION_SOURCE = ["VALUE"];
447
- var OPTIMIZATION_GOALS_REQUIRING_APP = ["APP_INSTALLS"];
448
- var DESTINATION_TYPES = [
449
- "WEBSITE",
450
- "APP",
451
- "MESSENGER",
452
- "INSTAGRAM_DIRECT",
453
- "ON_AD",
454
- "ON_PAGE",
455
- "ON_POST",
456
- "ON_VIDEO",
457
- "WHATSAPP",
458
- "PHONE_CALL",
459
- "INSTAGRAM_PROFILE",
460
- "SHOP_AUTOMATIC",
461
- // Added 2026-09-18 from adcampaign_destination_type. The four MESSAGING_* values
462
- // are the click-to-message destinations (Messenger / Instagram Direct / WhatsApp
463
- // in every combination Meta ships); without them Baker could not stage a
464
- // click-to-WhatsApp ad set at all.
465
- "APPLINKS_AUTOMATIC",
466
- "FACEBOOK",
467
- "FACEBOOK_LIVE",
468
- "FACEBOOK_PAGE",
469
- "IMAGINE",
470
- "INSTAGRAM_LIVE",
471
- "INSTAGRAM_PROFILE_AND_FACEBOOK_PAGE",
472
- "MESSAGING_INSTAGRAM_DIRECT_MESSENGER",
473
- "MESSAGING_INSTAGRAM_DIRECT_MESSENGER_WHATSAPP",
474
- "MESSAGING_INSTAGRAM_DIRECT_WHATSAPP",
475
- "MESSAGING_MESSENGER_WHATSAPP",
476
- "ON_EVENT"
477
- ];
478
- var CUSTOM_EVENT_TYPES = [
479
- "PURCHASE",
480
- "LEAD",
481
- "COMPLETE_REGISTRATION",
482
- "ADD_TO_CART",
483
- "ADD_PAYMENT_INFO",
484
- "INITIATED_CHECKOUT",
485
- "CONTENT_VIEW",
486
- "SEARCH",
487
- "SUBSCRIBE",
488
- "START_TRIAL",
489
- "CONTACT",
490
- "OTHER",
491
- // Added 2026-09-18 from AdPromotedObject_custom_event_type. SCHEDULE,
492
- // SUBMIT_APPLICATION, SERVICE_BOOKING_REQUEST and
493
- // MESSAGING_CONVERSATION_STARTED_7D are the conversions service businesses and
494
- // click-to-message campaigns actually optimize toward.
495
- "ACHIEVEMENT_UNLOCKED",
496
- "ADD_TO_WISHLIST",
497
- "AD_IMPRESSION",
498
- "CUSTOMIZE_PRODUCT",
499
- "D2_RETENTION",
500
- "D7_RETENTION",
501
- "DONATE",
502
- "FIND_LOCATION",
503
- "LEVEL_ACHIEVED",
504
- "LISTING_INTERACTION",
505
- "MESSAGING_CONVERSATION_STARTED_7D",
506
- "RATE",
507
- "SCHEDULE",
508
- "SERVICE_BOOKING_REQUEST",
509
- "SPENT_CREDITS",
510
- "SUBMIT_APPLICATION",
511
- "TUTORIAL_COMPLETION"
512
- ];
513
- var ATTRIBUTION_EVENT_TYPES = ["CLICK_THROUGH", "VIEW_THROUGH", "ENGAGED_VIDEO_VIEW"];
514
- var ADVANTAGE_AUDIENCE_OPEN_AGE_MAX = 65;
515
- var ADVANTAGE_AUDIENCE_MAX_HARD_AGE_MIN = 25;
516
- var CTA_TYPES = [
517
- "BOOK_TRAVEL",
518
- "CONTACT_US",
519
- "DONATE",
520
- "DONATE_NOW",
521
- "DOWNLOAD",
522
- "GET_DIRECTIONS",
523
- "GO_LIVE",
524
- "INTERESTED",
525
- "LEARN_MORE",
526
- "SEE_DETAILS",
527
- "LIKE_PAGE",
528
- "MESSAGE_PAGE",
529
- "RAISE_MONEY",
530
- "SAVE",
531
- "SEND_TIP",
532
- "SHOP_NOW",
533
- "SIGN_UP",
534
- "VIEW_INSTAGRAM_PROFILE",
535
- "INSTAGRAM_MESSAGE",
536
- "LOYALTY_LEARN_MORE",
537
- "PURCHASE_GIFT_CARDS",
538
- "PAY_TO_ACCESS",
539
- "SEE_MORE",
540
- "TRY_IN_CAMERA",
541
- "WHATSAPP_LINK",
542
- "GET_IN_TOUCH",
543
- "TRY_NOW",
544
- "ASK_A_QUESTION",
545
- "START_A_CHAT",
546
- "CHAT_NOW",
547
- "ASK_US",
548
- "CHAT_WITH_US",
549
- "BOOK_NOW",
550
- "CHECK_AVAILABILITY",
551
- "ORDER_NOW",
552
- "WHATSAPP_MESSAGE",
553
- "GET_MOBILE_APP",
554
- "INSTALL_MOBILE_APP",
555
- "USE_MOBILE_APP",
556
- "INSTALL_APP",
557
- "USE_APP",
558
- "PLAY_GAME",
559
- "TRY_DEMO",
560
- "WATCH_VIDEO",
561
- "WATCH_MORE",
562
- "OPEN_LINK",
563
- "NO_BUTTON",
564
- "LISTEN_MUSIC",
565
- "MOBILE_DOWNLOAD",
566
- "GET_OFFER",
567
- "GET_OFFER_VIEW",
568
- "BUY_NOW",
569
- "BUY_TICKETS",
570
- "UPDATE_APP",
571
- "BET_NOW",
572
- "ADD_TO_CART",
573
- "SELL_NOW",
574
- "GET_SHOWTIMES",
575
- "LISTEN_NOW",
576
- "GET_EVENT_TICKETS",
577
- "REMIND_ME",
578
- "SEARCH_MORE",
579
- "PRE_REGISTER",
580
- "SWIPE_UP_PRODUCT",
581
- "SWIPE_UP_SHOP",
582
- "PLAY_GAME_ON_FACEBOOK",
583
- "VISIT_WORLD",
584
- "OPEN_INSTANT_APP",
585
- "JOIN_GROUP",
586
- "GET_PROMOTIONS",
587
- "SEND_UPDATES",
588
- "INQUIRE_NOW",
589
- "VISIT_PROFILE",
590
- "CHAT_ON_WHATSAPP",
591
- "EXPLORE_MORE",
592
- "CONFIRM",
593
- "JOIN_CHANNEL",
594
- "MAKE_AN_APPOINTMENT",
595
- "ASK_ABOUT_SERVICES",
596
- "BOOK_A_CONSULTATION",
597
- "GET_A_QUOTE",
598
- "BUY_VIA_MESSAGE",
599
- "ASK_FOR_MORE_INFO",
600
- "VIEW_PRODUCT",
601
- "VIEW_CHANNEL",
602
- "WATCH_LIVE_VIDEO",
603
- "JOIN_LIVE_VIDEO",
604
- "IMAGINE",
605
- "CALL",
606
- "MISSED_CALL",
607
- "CALL_NOW",
608
- "CALL_ME",
609
- "APPLY_NOW",
610
- "BUY",
611
- "GET_QUOTE",
612
- "SUBSCRIBE",
613
- "RECORD_NOW",
614
- "VOTE_NOW",
615
- "GIVE_FREE_RIDES",
616
- "REGISTER_NOW",
617
- "OPEN_MESSENGER_EXT",
618
- "EVENT_RSVP",
619
- "CIVIC_ACTION",
620
- "SEND_INVITES",
621
- "REFER_FRIENDS",
622
- "REQUEST_TIME",
623
- "SEE_MENU",
624
- "SEARCH",
625
- "TRY_IT",
626
- "TRY_ON",
627
- "LINK_CARD",
628
- "DIAL_CODE",
629
- "FIND_YOUR_GROUPS",
630
- "START_ORDER",
631
- // Added 2026-09-18 from Meta's generated enum spec
632
- // (AdCreative_call_to_action_type). Additive on purpose: 37 of the values above
633
- // are absent from that spec and present in the live 400, which is the
634
- // disagreement the note above is about — so the spec widens this list and never
635
- // prunes it.
636
- "AUDIO_CALL",
637
- "BROWSE_SHOP",
638
- "CONTACT",
639
- "FIND_A_GROUP",
640
- "FIND_OUT_MORE",
641
- "FOLLOW_NEWS_STORYLINE",
642
- "FOLLOW_PAGE",
643
- "FOLLOW_USER",
644
- "GET_DETAILS",
645
- "GET_STARTED",
646
- "SAY_THANKS",
647
- "SEE_SHOP",
648
- "SEND_A_GIFT",
649
- "SEND_GIFT_MONEY",
650
- "SHARE",
651
- "SHOP_WITH_AI",
652
- "SOTTO_SUBSCRIBE",
653
- "TRY_ON_WITH_AI",
654
- "VIDEO_ANNOTATION",
655
- "VIDEO_CALL",
656
- "VIEW_CART",
657
- "VIEW_IN_CART",
658
- "VISIT_PAGES_FEED",
659
- "VISIT_WEBSITE",
660
- "WOODHENGE_SUPPORT"
661
- ];
662
- var ASSET_FEED_CTA_TYPES = [
663
- "OPEN_LINK",
664
- "LIKE_PAGE",
665
- "SHOP_NOW",
666
- "PLAY_GAME",
667
- "INSTALL_APP",
668
- "USE_APP",
669
- "CALL",
670
- "CALL_ME",
671
- "VIDEO_CALL",
672
- "INSTALL_MOBILE_APP",
673
- "USE_MOBILE_APP",
674
- "MOBILE_DOWNLOAD",
675
- "BOOK_TRAVEL",
676
- "LISTEN_MUSIC",
677
- "WATCH_VIDEO",
678
- "LEARN_MORE",
679
- "SIGN_UP",
680
- "DOWNLOAD",
681
- "WATCH_MORE",
682
- "NO_BUTTON",
683
- "VISIT_PAGES_FEED",
684
- "CALL_NOW",
685
- "APPLY_NOW",
686
- "CONTACT",
687
- "BUY_NOW",
688
- "GET_OFFER",
689
- "GET_OFFER_VIEW",
690
- "BUY_TICKETS",
691
- "UPDATE_APP",
692
- "GET_DIRECTIONS",
693
- "BUY",
694
- "SEND_UPDATES",
695
- "MESSAGE_PAGE",
696
- "DONATE",
697
- "SUBSCRIBE",
698
- "SAY_THANKS",
699
- "SELL_NOW",
700
- "SHARE",
701
- "DONATE_NOW",
702
- "GET_QUOTE",
703
- "CONTACT_US",
704
- "ORDER_NOW",
705
- "START_ORDER",
706
- "ADD_TO_CART",
707
- "VIEW_CART",
708
- "VIEW_IN_CART",
709
- "VIDEO_ANNOTATION",
710
- "RECORD_NOW",
711
- "INQUIRE_NOW",
712
- "CONFIRM",
713
- "REFER_FRIENDS",
714
- "REQUEST_TIME",
715
- "GET_SHOWTIMES",
716
- "LISTEN_NOW",
717
- "TRY_DEMO",
718
- "WOODHENGE_SUPPORT",
719
- "SOTTO_SUBSCRIBE",
720
- "FOLLOW_USER",
721
- "RAISE_MONEY",
722
- "SEE_SHOP",
723
- "GET_DETAILS",
724
- "FIND_OUT_MORE",
725
- "VISIT_WEBSITE",
726
- "BROWSE_SHOP",
727
- "EVENT_RSVP",
728
- "WHATSAPP_MESSAGE",
729
- "FOLLOW_NEWS_STORYLINE",
730
- "SEE_MORE",
731
- "BOOK_NOW",
732
- "FIND_A_GROUP",
733
- "FIND_YOUR_GROUPS",
734
- "PAY_TO_ACCESS",
735
- "PURCHASE_GIFT_CARDS",
736
- "FOLLOW_PAGE",
737
- "SEND_A_GIFT",
738
- "SWIPE_UP_SHOP",
739
- "SWIPE_UP_PRODUCT",
740
- "SEND_GIFT_MONEY",
741
- "PLAY_GAME_ON_FACEBOOK",
742
- "GET_STARTED",
743
- "OPEN_INSTANT_APP",
744
- "AUDIO_CALL",
745
- "GET_PROMOTIONS",
746
- "JOIN_CHANNEL",
747
- "MAKE_AN_APPOINTMENT",
748
- "ASK_ABOUT_SERVICES",
749
- "BOOK_A_CONSULTATION",
750
- "GET_A_QUOTE",
751
- "BUY_VIA_MESSAGE",
752
- "ASK_FOR_MORE_INFO",
753
- "CHAT_WITH_US",
754
- "VIEW_PRODUCT",
755
- "VIEW_CHANNEL",
756
- "GET_IN_TOUCH",
757
- "ASK_A_QUESTION",
758
- "START_A_CHAT",
759
- "CHAT_NOW",
760
- "ASK_US",
761
- "WATCH_LIVE_VIDEO",
762
- "JOIN_LIVE_VIDEO",
763
- "SHOP_WITH_AI",
764
- "TRY_ON_WITH_AI"
765
- ];
766
- var LEAD_FORM_CTA_TYPES = [
767
- "APPLY_NOW",
768
- "DOWNLOAD",
769
- "GET_QUOTE",
770
- "LEARN_MORE",
771
- "SIGN_UP",
772
- "SUBSCRIBE"
773
- ];
774
- var AD_FORMATS = ["SINGLE_IMAGE", "CAROUSEL", "SINGLE_VIDEO"];
775
- var ENROLL_STATUSES = ["OPT_IN", "OPT_OUT"];
776
- var STAGEABLE_CREATE_STATUSES = ["ACTIVE", "PAUSED"];
777
- var UPDATE_STATUSES = ["ACTIVE", "PAUSED", "ARCHIVED", "DELETED"];
778
- var CREATIVE_UPDATE_STATUSES = ["ACTIVE", "PAUSED", "ARCHIVED"];
779
- var CUSTOM_AUDIENCE_SUBTYPES = ["CUSTOM", "WEBSITE", "ENGAGEMENT", "LOOKALIKE"];
780
- var CUSTOMER_FILE_SOURCES = [
781
- "USER_PROVIDED_ONLY",
782
- "PARTNER_PROVIDED_ONLY",
783
- "BOTH_USER_AND_PARTNER_PROVIDED"
784
- ];
785
- var MEDIA_KINDS = ["image", "video"];
786
- var CURRENCY_MINIMUMS = {
787
- USD: { dailyBudgetMin: 1 },
788
- EUR: { dailyBudgetMin: 1 },
789
- GBP: { dailyBudgetMin: 1 }
790
- };
791
- var DEFAULT_CURRENCY_MINIMUM = { dailyBudgetMin: 1 };
792
- var TEMP_REF_REGEX = /^meta_temp_[A-Za-z0-9_-]{4,}$/;
793
- var NUMERIC_ID_REGEX = /^\d+$/;
794
- var IMAGE_HASH_REGEX = /^[A-Fa-f0-9]{16,}$/;
795
-
796
- // ../api/src/ads-meta/ops.ts
797
- import { z } from "zod";
798
- var tempRefSchema = z.string().regex(TEMP_REF_REGEX, "expected a meta_temp_* reference");
799
- var parentRefSchema = z.union([z.string().regex(NUMERIC_ID_REGEX, "expected a numeric id"), tempRefSchema]);
800
- var moneySchema = z.strictObject({
801
- amount: z.string().regex(/^\d+(\.\d{1,3})?$/, "expected a decimal amount like 50 or 50.00").refine((val) => Number(val) > 0, "amount must be greater than zero"),
802
- currencyCode: z.string().length(3).optional()
803
- });
804
- var httpsUrlSchema = z.string().url().refine((u) => u.startsWith("https://"), "destination URLs must be https");
805
- var bakerMediaIdSchema = z.string().min(1);
806
- var stageableStatusSchema = z.enum(STAGEABLE_CREATE_STATUSES);
807
- var updateStatusSchema = z.enum(UPDATE_STATUSES);
808
- function currencyMinimums(currencyCode) {
809
- return CURRENCY_MINIMUMS[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM;
810
- }
811
- function validateDailyBudgetFloor(money, ctx, path) {
812
- if (money?.currencyCode) {
813
- const min = currencyMinimums(money.currencyCode).dailyBudgetMin;
814
- if (Number(money.amount) < min) {
815
- ctx.addIssue({ code: "custom", path, message: `below the ${min} ${money.currencyCode} daily minimum` });
816
- }
817
- }
818
- }
819
- var geoLocationsSchema = z.object({
820
- countries: z.array(z.string().length(2)).optional(),
821
- regions: z.array(z.object({ key: z.string() })).optional(),
822
- cities: z.array(z.object({ key: z.string(), radius: z.number().optional(), distance_unit: z.string().optional() })).optional(),
823
- zips: z.array(z.object({ key: z.string() })).optional(),
824
- location_types: z.array(z.string()).optional()
825
- }).catchall(z.unknown());
826
- var idNameSchema = z.object({ id: z.string(), name: z.string().optional() });
827
- var metaTargetingSchema = z.object({
828
- geo_locations: geoLocationsSchema.optional(),
829
- excluded_geo_locations: geoLocationsSchema.optional(),
830
- age_min: z.number().int().min(13).max(65).optional(),
831
- age_max: z.number().int().min(13).max(65).optional(),
832
- genders: z.array(z.union([z.literal(1), z.literal(2)])).optional(),
833
- locales: z.array(z.number().int()).optional(),
834
- interests: z.array(idNameSchema).optional(),
835
- behaviors: z.array(idNameSchema).optional(),
836
- custom_audiences: z.array(z.object({ id: parentRefSchema })).optional(),
837
- excluded_custom_audiences: z.array(z.object({ id: parentRefSchema })).optional(),
838
- flexible_spec: z.array(z.record(z.string(), z.unknown())).optional(),
839
- exclusions: z.record(z.string(), z.unknown()).optional(),
840
- publisher_platforms: z.array(z.string()).optional(),
841
- facebook_positions: z.array(z.string()).optional(),
842
- instagram_positions: z.array(z.string()).optional(),
843
- audience_network_positions: z.array(z.string()).optional(),
844
- messenger_positions: z.array(z.string()).optional(),
845
- threads_positions: z.array(z.string()).optional(),
846
- device_platforms: z.array(z.string()).optional(),
847
- targeting_automation: z.object({ advantage_audience: z.union([z.literal(0), z.literal(1)]) }).partial().optional()
848
- }).catchall(z.unknown());
849
- var GEO_INCLUSION_KEYS = [
850
- "countries",
851
- "country_groups",
852
- "regions",
853
- "cities",
854
- "zips",
855
- "geo_markets",
856
- "places",
857
- "custom_locations",
858
- "electoral_districts",
859
- "subcities",
860
- "neighborhoods"
861
- ];
862
- function targetingHasGeo(t) {
863
- const geo = t?.geo_locations;
864
- return Boolean(
865
- geo && GEO_INCLUSION_KEYS.some((key) => Array.isArray(geo[key]) && geo[key].length > 0)
866
- );
867
- }
868
- function targetingHasGeoOrAudience(t) {
869
- if (!t) {
870
- return false;
871
- }
872
- return targetingHasGeo(t) || Boolean(t.custom_audiences?.length);
873
- }
874
- function advantageAudienceSetting(t) {
875
- const value = t.targeting_automation?.advantage_audience;
876
- return value === 0 || value === 1 ? value : void 0;
877
- }
878
- function targetingHardLimitsDemographics(t) {
879
- if (!t) {
880
- return false;
881
- }
882
- const narrowsAgeMax = typeof t.age_max === "number" && t.age_max < ADVANTAGE_AUDIENCE_OPEN_AGE_MAX;
883
- const narrowsAgeMin = typeof t.age_min === "number" && t.age_min > ADVANTAGE_AUDIENCE_MAX_HARD_AGE_MIN;
884
- const narrowsGender = Array.isArray(t.genders) && t.genders.length === 1;
885
- return narrowsAgeMax || narrowsAgeMin || narrowsGender;
886
- }
887
- var specialAdCategoriesSchema = z.array(z.enum(SPECIAL_AD_CATEGORIES)).default(["NONE"]);
888
- var campaignCreateSchema = z.strictObject({
889
- name: z.string().min(1).max(META_LIMITS.campaign.nameMax),
890
- objective: z.enum(OBJECTIVES),
891
- status: stageableStatusSchema.default("PAUSED"),
892
- special_ad_categories: specialAdCategoriesSchema,
893
- special_ad_category_country: z.array(z.string().length(2)).optional(),
894
- buying_type: z.enum(BUYING_TYPES).default("AUCTION"),
895
- bid_strategy: z.enum(BID_STRATEGIES).optional(),
896
- /** Campaign Budget Optimization (Advantage campaign budget) — mutually exclusive with ad-set budgets. */
897
- dailyBudget: moneySchema.optional(),
898
- lifetimeBudget: moneySchema.optional(),
899
- spendCap: moneySchema.optional(),
900
- /**
901
- * Ad-set budget sharing: lets ad sets lend up to 20% of their budget to each
902
- * other. Meta requires an explicit true/false on any campaign that carries no
903
- * campaign budget (subcode 4834011); Baker sends false unless asked, because
904
- * turning it on moves real spend between ad sets nobody asked to move.
905
- */
906
- adSetBudgetSharing: z.boolean().optional(),
907
- start_time: z.number().int().positive().optional(),
908
- stop_time: z.number().int().positive().optional()
909
- }).superRefine((p, ctx) => {
910
- if (p.dailyBudget && p.lifetimeBudget) {
911
- ctx.addIssue({ code: "custom", path: ["dailyBudget"], message: "set only one of dailyBudget or lifetimeBudget" });
912
- }
913
- validateDailyBudgetFloor(p.dailyBudget, ctx, ["dailyBudget", "amount"]);
914
- if (p.stop_time && p.start_time && p.stop_time <= p.start_time) {
915
- ctx.addIssue({ code: "custom", path: ["stop_time"], message: "stop_time must be after start_time" });
916
- }
917
- const needsCountry = p.special_ad_categories.some((c) => SPECIAL_CATEGORIES_REQUIRING_COUNTRY.includes(c));
918
- if (needsCountry && !p.special_ad_category_country?.length) {
919
- ctx.addIssue({
920
- code: "custom",
921
- path: ["special_ad_category_country"],
922
- message: "ISSUES_ELECTIONS_POLITICS campaigns need special_ad_category_country \u2014 pass the 2-letter country code(s) the ads run in"
923
- });
924
- }
925
- });
926
- var campaignUpdateSchema = z.strictObject({
927
- name: z.string().min(1).max(META_LIMITS.campaign.nameMax).optional(),
928
- status: updateStatusSchema.optional(),
929
- bid_strategy: z.enum(BID_STRATEGIES).optional(),
930
- dailyBudget: moneySchema.optional(),
931
- lifetimeBudget: moneySchema.optional(),
932
- spendCap: moneySchema.optional(),
933
- start_time: z.number().int().positive().optional(),
934
- stop_time: z.number().int().positive().optional()
935
- }).superRefine((p, ctx) => {
936
- if (!Object.values(p).some((val) => val !== void 0)) {
937
- ctx.addIssue({ code: "custom", message: "update needs at least one field" });
938
- }
939
- if (p.dailyBudget && p.lifetimeBudget) {
940
- ctx.addIssue({ code: "custom", path: ["dailyBudget"], message: "set only one of dailyBudget or lifetimeBudget" });
941
- }
942
- validateDailyBudgetFloor(p.dailyBudget, ctx, ["dailyBudget", "amount"]);
943
- });
944
- var promotedObjectFields = {
945
- page_id: parentRefSchema.optional(),
946
- pixel_id: z.string().regex(NUMERIC_ID_REGEX).optional(),
947
- custom_event_type: z.enum(CUSTOM_EVENT_TYPES).optional(),
948
- /**
949
- * A Custom Conversion to optimize toward, instead of a raw pixel event.
950
- * Named alone by the caller: the stage reads the conversion off Meta and
951
- * fills `pixel_id`, `custom_event_type` and `pixel_rule` from it, so the
952
- * staged payload matches what Ads Manager stores and the review shows the
953
- * event the ad set will actually chase.
954
- */
955
- custom_conversion_id: z.union([z.string().regex(NUMERIC_ID_REGEX), tempRefSchema]).optional(),
956
- /** The custom conversion's own filter, filled by the stage. Never written by hand. */
957
- pixel_rule: z.string().optional(),
958
- application_id: z.string().regex(NUMERIC_ID_REGEX).optional(),
959
- object_store_url: z.string().url().optional(),
960
- product_catalog_id: z.string().regex(NUMERIC_ID_REGEX).optional(),
961
- product_set_id: z.string().regex(NUMERIC_ID_REGEX).optional(),
962
- whatsapp_phone_number: z.string().optional(),
963
- offline_conversion_data_set_id: z.string().regex(NUMERIC_ID_REGEX).optional()
964
- };
965
- var promotedObjectSchema = z.strictObject(promotedObjectFields, {
966
- error: (issue) => issue.code === "unrecognized_keys" ? `promoted_object does not take ${issue.keys.map((key) => `"${key}"`).join(", ")} \u2014 it takes ${Object.keys(promotedObjectFields).join(", ")}` : void 0
967
- });
968
- var attributionSpecSchema = z.array(
969
- z.strictObject({
970
- event_type: z.enum(ATTRIBUTION_EVENT_TYPES),
971
- window_days: z.union([z.literal(1), z.literal(7), z.literal(28)])
972
- })
973
- );
974
- var adSetFields = {
975
- name: z.string().min(1).max(META_LIMITS.adSet.nameMax),
976
- campaign_id: parentRefSchema,
977
- status: stageableStatusSchema.default("PAUSED"),
978
- dailyBudget: moneySchema.optional(),
979
- lifetimeBudget: moneySchema.optional(),
980
- bidAmount: moneySchema.optional(),
981
- bid_strategy: z.enum(BID_STRATEGIES).optional(),
982
- billing_event: z.enum(BILLING_EVENTS),
983
- optimization_goal: z.enum(OPTIMIZATION_GOALS),
984
- destination_type: z.enum(DESTINATION_TYPES).optional(),
985
- promoted_object: promotedObjectSchema.optional(),
986
- attribution_spec: attributionSpecSchema.optional(),
987
- start_time: z.number().int().positive().optional(),
988
- end_time: z.number().int().positive().optional(),
989
- targeting: metaTargetingSchema,
990
- /**
991
- * The live ad set this one was copied from, verbatim, by `duplicate`. Baker
992
- * only — `adSetCommonBody` never sends it to Meta. Set exclusively when the
993
- * targeting IS the source's targeting, because that is what it licenses:
994
- * placement validation is skipped for it (see `validateAdSetPlacements`).
995
- */
996
- clonedFrom: z.string().regex(NUMERIC_ID_REGEX).optional(),
997
- /** EU Digital Services Act: who benefits from / pays for the ad. Auto-filled from the account for EU geo when omitted. */
998
- dsa_beneficiary: z.string().min(1).max(100).optional(),
999
- dsa_payor: z.string().min(1).max(100).optional()
1000
- };
1001
- function validateBidStrategy(p, ctx) {
1002
- const strategy = p.bid_strategy;
1003
- if (!strategy) {
1004
- return;
1005
- }
1006
- if (BID_STRATEGIES_REQUIRING_AMOUNT.includes(strategy) && !p.bidAmount) {
1007
- ctx.addIssue({ code: "custom", path: ["bidAmount"], message: `${strategy} requires a bidAmount` });
1008
- }
1009
- if (BID_STRATEGIES_FORBIDDING_AMOUNT.includes(strategy) && p.bidAmount) {
1010
- ctx.addIssue({
1011
- code: "custom",
1012
- path: ["bidAmount"],
1013
- message: `${strategy} must not set a bidAmount \u2014 Meta rejects one (it bids automatically)`
1014
- });
1015
- }
1016
- if (strategy === "COST_CAP" && p.billing_event && p.billing_event !== "IMPRESSIONS") {
1017
- ctx.addIssue({
1018
- code: "custom",
1019
- path: ["billing_event"],
1020
- message: "COST_CAP requires billing_event IMPRESSIONS"
1021
- });
1022
- }
1023
- if (strategy === "LOWEST_COST_WITH_MIN_ROAS" && p.optimization_goal && p.optimization_goal !== "VALUE") {
1024
- ctx.addIssue({
1025
- code: "custom",
1026
- path: ["bid_strategy"],
1027
- message: "LOWEST_COST_WITH_MIN_ROAS requires optimization_goal VALUE"
1028
- });
1029
- }
1030
- }
1031
- function validateAdSetBudgetAndBid(p, ctx) {
1032
- if (p.dailyBudget && p.lifetimeBudget) {
1033
- ctx.addIssue({ code: "custom", path: ["dailyBudget"], message: "set only one of dailyBudget or lifetimeBudget" });
1034
- }
1035
- validateDailyBudgetFloor(p.dailyBudget, ctx, ["dailyBudget", "amount"]);
1036
- if (p.lifetimeBudget && !p.end_time) {
1037
- ctx.addIssue({ code: "custom", path: ["end_time"], message: "lifetimeBudget ad sets need an end_time" });
1038
- }
1039
- validateBidStrategy(p, ctx);
1040
- if (p.end_time && p.start_time && p.end_time <= p.start_time) {
1041
- ctx.addIssue({ code: "custom", path: ["end_time"], message: "end_time must be after start_time" });
1042
- }
1043
- }
1044
- function validateAdSetPromotedObject(p, ctx) {
1045
- const goal = p.optimization_goal;
1046
- if (!goal) {
1047
- return;
1048
- }
1049
- const po = p.promoted_object;
1050
- const namesConversion = Boolean(po?.custom_conversion_id);
1051
- const hasPixel = Boolean(po?.pixel_id) || namesConversion;
1052
- const hasEventType = Boolean(po?.custom_event_type) || namesConversion;
1053
- if (OPTIMIZATION_GOALS_REQUIRING_PAGE.includes(goal) && !po?.page_id) {
1054
- ctx.addIssue({
1055
- code: "custom",
1056
- path: ["promoted_object", "page_id"],
1057
- message: `optimization_goal ${goal} needs promoted_object.page_id \u2014 pass --page-id with the Facebook Page the leads/likes belong to`
1058
- });
1059
- }
1060
- if (OPTIMIZATION_GOALS_REQUIRING_PIXEL.includes(goal) && !hasPixel) {
1061
- ctx.addIssue({
1062
- code: "custom",
1063
- path: ["promoted_object", "pixel_id"],
1064
- message: `optimization_goal ${goal} needs promoted_object.pixel_id \u2014 pass --pixel-id with the conversion pixel, or --custom-conversion-id to optimize toward a custom conversion`
1065
- });
1066
- }
1067
- if (OPTIMIZATION_GOALS_REQUIRING_CONVERSION_SOURCE.includes(goal) && !hasPixel && !po?.application_id) {
1068
- ctx.addIssue({
1069
- code: "custom",
1070
- path: ["promoted_object", "pixel_id"],
1071
- message: `optimization_goal ${goal} needs a conversion source \u2014 pass promoted_object.pixel_id (web value optimization) or promoted_object.application_id (app value optimization)`
1072
- });
1073
- }
1074
- if ((OPTIMIZATION_GOALS_REQUIRING_PIXEL.includes(goal) || OPTIMIZATION_GOALS_REQUIRING_CONVERSION_SOURCE.includes(goal)) && !hasEventType) {
1075
- ctx.addIssue({
1076
- code: "custom",
1077
- path: ["promoted_object", "custom_event_type"],
1078
- message: `optimization_goal ${goal} needs promoted_object.custom_event_type \u2014 pass --custom-event-type (e.g. PURCHASE, LEAD), or --custom-conversion-id to optimize toward a custom conversion`
1079
- });
1080
- }
1081
- if (OPTIMIZATION_GOALS_REQUIRING_APP.includes(goal) && !po?.application_id) {
1082
- ctx.addIssue({
1083
- code: "custom",
1084
- path: ["promoted_object", "application_id"],
1085
- message: `optimization_goal ${goal} needs promoted_object.application_id`
1086
- });
1087
- }
1088
- }
1089
- function validateAdvantageAudience(p, ctx) {
1090
- const t = p.targeting;
1091
- if (t && targetingHardLimitsDemographics(t) && advantageAudienceSetting(t) === 1) {
1092
- ctx.addIssue({
1093
- code: "custom",
1094
- path: ["targeting", "targeting_automation", "advantage_audience"],
1095
- message: "Advantage+ Audience (advantage_audience: 1) treats age and gender as suggestions, so a hard age/gender limit is rejected \u2014 set advantage_audience: 0 to enforce the limit, or drop the age_max/age_min/gender restriction"
1096
- });
1097
- }
1098
- }
1099
- function validateAdSetPlacements(p, ctx) {
1100
- if (p.clonedFrom) {
1101
- for (const issue of invalidPlacementValues(p.targeting)) {
1102
- if (issue.kind === "retired") {
1103
- ctx.addIssue({
1104
- code: "custom",
1105
- path: ["targeting", issue.field],
1106
- message: `${issue.field} no longer accepts "${issue.value}". ${issue.fix} (${PLACEMENT_TARGETING_DOC})`
1107
- });
1108
- }
1109
- }
1110
- return;
1111
- }
1112
- const missingFeed = positionsMissingRequiredFeed(p.targeting);
1113
- if (missingFeed.length > 0) {
1114
- ctx.addIssue({
1115
- code: "custom",
1116
- path: ["targeting", "facebook_positions"],
1117
- message: `Meta requires "feed" alongside ${missingFeed.join(", ")} \u2014 add "feed" to facebook_positions or drop those positions (${PLACEMENT_TARGETING_DOC})`
1118
- });
1119
- }
1120
- for (const issue of invalidPlacementValues(p.targeting)) {
1121
- if (issue.kind === "unknown") {
1122
- continue;
1123
- }
1124
- ctx.addIssue({
1125
- code: "custom",
1126
- path: ["targeting", issue.field],
1127
- message: issue.kind === "retired" ? `${issue.field} no longer accepts "${issue.value}". ${issue.fix} (${PLACEMENT_TARGETING_DOC})` : `${issue.field} does not accept "${issue.value}". ${issue.fix} (${PLACEMENT_TARGETING_DOC})`
1128
- });
1129
- }
1130
- for (const message of placementCoRequirementIssues(p.targeting)) {
1131
- ctx.addIssue({ code: "custom", path: ["targeting"], message: `${message} (${PLACEMENT_TARGETING_DOC})` });
1132
- }
1133
- }
1134
- var adSetCreateSchema = z.strictObject(adSetFields).superRefine((p, ctx) => {
1135
- validateAdSetBudgetAndBid(p, ctx);
1136
- validateAdSetPromotedObject(p, ctx);
1137
- validateAdvantageAudience(p, ctx);
1138
- validateAdSetPlacements(p, ctx);
1139
- if (!targetingHasGeoOrAudience(p.targeting)) {
1140
- ctx.addIssue({
1141
- code: "custom",
1142
- path: ["targeting", "geo_locations"],
1143
- message: 'targeting needs a location \u2014 set targeting.geo_locations (e.g. countries: ["US"]) or include a custom audience; Meta rejects an ad set with no geo'
1144
- });
1145
- }
1146
- });
1147
- var adSetUpdateSchema = z.strictObject({
1148
- name: adSetFields.name.optional(),
1149
- status: updateStatusSchema.optional(),
1150
- dailyBudget: moneySchema.optional(),
1151
- lifetimeBudget: moneySchema.optional(),
1152
- bidAmount: moneySchema.optional(),
1153
- bid_strategy: z.enum(BID_STRATEGIES).optional(),
1154
- optimization_goal: z.enum(OPTIMIZATION_GOALS).optional(),
1155
- destination_type: z.enum(DESTINATION_TYPES).optional(),
1156
- promoted_object: promotedObjectSchema.optional(),
1157
- attribution_spec: attributionSpecSchema.optional(),
1158
- start_time: z.number().int().positive().optional(),
1159
- end_time: z.number().int().positive().optional(),
1160
- targeting: metaTargetingSchema.optional(),
1161
- dsa_beneficiary: z.string().min(1).max(100).optional(),
1162
- dsa_payor: z.string().min(1).max(100).optional()
1163
- }).superRefine((p, ctx) => {
1164
- if (!Object.values(p).some((val) => val !== void 0)) {
1165
- ctx.addIssue({ code: "custom", message: "update needs at least one field" });
1166
- }
1167
- validateAdSetBudgetAndBid(p, ctx);
1168
- if (p.optimization_goal) {
1169
- validateAdSetPromotedObject(p, ctx);
1170
- }
1171
- validateAdvantageAudience(p, ctx);
1172
- validateAdSetPlacements(p, ctx);
1173
- });
1174
- var messageSchema = z.string().min(1).max(META_LIMITS.creative.messageHardMax);
1175
- var headlineSchema = z.string().min(1).max(META_LIMITS.creative.headlineMax);
1176
- var descriptionSchema = z.string().min(1).max(META_LIMITS.creative.descriptionMax);
1177
- var callToActionSchema = z.strictObject({
1178
- type: z.enum(CTA_TYPES),
1179
- /** Overrides the base link for the CTA button; defaults to the ad's link. */
1180
- link: httpsUrlSchema.optional(),
1181
- /**
1182
- * The instant form the button opens — Meta's `call_to_action.value.lead_gen_form_id`.
1183
- * This is the ONLY place an instant form is named: the ad set says leads open
1184
- * on the ad (`destination_type: ON_AD`), and the creative says which form.
1185
- * Without it Meta refuses every ad in that ad set with subcode 3390001,
1186
- * "Choose or create an instant form for your leads campaign".
1187
- */
1188
- lead_gen_form_id: parentRefSchema.optional()
1189
- }).superRefine((p, ctx) => {
1190
- if (p.lead_gen_form_id && !LEAD_FORM_CTA_TYPES.some((allowed) => allowed === p.type)) {
1191
- ctx.addIssue({
1192
- code: "custom",
1193
- path: ["type"],
1194
- message: `${p.type} cannot open an instant form \u2014 Meta refuses it (subcode 1856030). Use one of ${LEAD_FORM_CTA_TYPES.join(", ")}, or drop lead_gen_form_id if this ad should go to the website.`
1195
- });
1196
- }
1197
- });
1198
- var creativeEnhancementsSchema = z.strictObject({
1199
- standardEnhancements: z.enum(ENROLL_STATUSES).optional(),
1200
- features: z.record(z.string(), z.enum(ENROLL_STATUSES)).optional()
1201
- });
1202
- var creativeSharedFields = {
1203
- name: z.string().max(META_LIMITS.creative.nameMax).optional(),
1204
- /** Facebook Page id backing the ad's identity. */
1205
- page_id: parentRefSchema,
1206
- /** Instagram account id for IG placements (aka instagram_actor_id on read). */
1207
- instagram_user_id: z.string().regex(NUMERIC_ID_REGEX).optional(),
1208
- /** URL tracking parameters appended to the destination, e.g. "utm_source=fb&utm_campaign=x". */
1209
- url_tags: z.string().max(1e3).optional(),
1210
- enhancements: creativeEnhancementsSchema.optional()
1211
- };
1212
- var imageMediaFields = {
1213
- imageHash: z.string().regex(IMAGE_HASH_REGEX).optional(),
1214
- imageRef: tempRefSchema.optional()
1215
- };
1216
- var videoMediaFields = {
1217
- videoId: z.string().regex(NUMERIC_ID_REGEX).optional(),
1218
- videoRef: tempRefSchema.optional(),
1219
- /** Thumbnail for a video creative — image hash, ref, or public url. */
1220
- thumbnailHash: z.string().regex(IMAGE_HASH_REGEX).optional(),
1221
- imageUrl: z.string().url().optional()
1222
- };
1223
- function countImageRefs(p) {
1224
- return [p.imageHash, p.imageRef].filter(Boolean).length;
1225
- }
1226
- function countVideoRefs(p) {
1227
- return [p.videoId, p.videoRef].filter(Boolean).length;
1228
- }
1229
- var singleCreativeSchema = z.strictObject({
1230
- creativeType: z.literal("single"),
1231
- ...creativeSharedFields,
1232
- /** Primary text. */
1233
- message: messageSchema,
1234
- link: httpsUrlSchema,
1235
- /** Headline. */
1236
- headline: headlineSchema.optional(),
1237
- description: descriptionSchema.optional(),
1238
- /** Display URL / caption shown under the headline. */
1239
- caption: z.string().max(255).optional(),
1240
- call_to_action: callToActionSchema.optional(),
1241
- ...imageMediaFields,
1242
- ...videoMediaFields
1243
- }).superRefine((p, ctx) => {
1244
- const images = countImageRefs(p);
1245
- const videos = countVideoRefs(p);
1246
- if (images + videos === 0) {
1247
- ctx.addIssue({ code: "custom", path: ["imageHash"], message: "a single ad needs one image or one video" });
1248
- }
1249
- if (images > 1) {
1250
- ctx.addIssue({ code: "custom", path: ["imageHash"], message: "provide exactly one image reference" });
1251
- }
1252
- if (videos > 1) {
1253
- ctx.addIssue({ code: "custom", path: ["videoId"], message: "provide exactly one video reference" });
1254
- }
1255
- if (images > 0 && videos > 0) {
1256
- ctx.addIssue({
1257
- code: "custom",
1258
- path: ["videoId"],
1259
- message: "a single ad is either an image OR a video, not both"
1260
- });
1261
- }
1262
- });
1263
- var carouselCardSchema = z.strictObject({
1264
- link: httpsUrlSchema,
1265
- headline: z.string().max(META_LIMITS.creative.headlineMax).optional(),
1266
- description: z.string().max(META_LIMITS.creative.descriptionMax).optional(),
1267
- call_to_action: callToActionSchema.optional(),
1268
- ...imageMediaFields,
1269
- ...videoMediaFields
1270
- }).superRefine((p, ctx) => {
1271
- const images = countImageRefs(p);
1272
- const videos = countVideoRefs(p);
1273
- if (images + videos === 0) {
1274
- ctx.addIssue({ code: "custom", path: ["imageHash"], message: "each card needs an image or a video" });
1275
- }
1276
- if (images > 1) {
1277
- ctx.addIssue({ code: "custom", path: ["imageHash"], message: "each card needs exactly one image reference" });
1278
- }
1279
- if (videos > 1) {
1280
- ctx.addIssue({ code: "custom", path: ["videoId"], message: "each card needs exactly one video reference" });
1281
- }
1282
- if (images > 0 && videos > 0) {
1283
- ctx.addIssue({ code: "custom", path: ["videoId"], message: "each card is an image OR a video, not both" });
1284
- }
1285
- });
1286
- var carouselCreativeSchema = z.strictObject({
1287
- creativeType: z.literal("carousel"),
1288
- ...creativeSharedFields,
1289
- message: messageSchema,
1290
- /** Optional "see more" card destination applied when a card has no own link. */
1291
- link: httpsUrlSchema.optional(),
1292
- call_to_action: callToActionSchema.optional(),
1293
- cards: z.array(carouselCardSchema).min(META_LIMITS.creative.carouselCardsMin).max(META_LIMITS.creative.carouselCardsMax)
1294
- }).superRefine((p, ctx) => {
1295
- const forms = new Set(
1296
- [p.call_to_action?.lead_gen_form_id, ...p.cards.map((card) => card.call_to_action?.lead_gen_form_id)].filter(
1297
- Boolean
1298
- )
1299
- );
1300
- if (forms.size > 1) {
1301
- ctx.addIssue({
1302
- code: "custom",
1303
- path: ["cards"],
1304
- message: `every card of a carousel must open the SAME instant form \u2014 this one names ${[...forms].join(" and ")}`
1305
- });
1306
- }
1307
- });
1308
- var dynamicImageSchema = z.strictObject({ ...imageMediaFields }).refine((p) => countImageRefs(p) === 1, "each dynamic image needs exactly one reference");
1309
- var dynamicVideoSchema = z.strictObject({
1310
- videoId: videoMediaFields.videoId,
1311
- videoRef: videoMediaFields.videoRef,
1312
- thumbnailHash: videoMediaFields.thumbnailHash
1313
- }).refine((p) => countVideoRefs(p) === 1, "each dynamic video needs exactly one reference");
1314
- var DYN = META_LIMITS.creative;
1315
- var dynamicCreativeSchema = z.strictObject({
1316
- creativeType: z.literal("dynamic"),
1317
- ...creativeSharedFields,
1318
- bodies: z.array(z.strictObject({ text: messageSchema })).min(DYN.dynamicTextsMin).max(DYN.dynamicTextsMax),
1319
- titles: z.array(z.strictObject({ text: headlineSchema })).min(DYN.dynamicTextsMin).max(DYN.dynamicTextsMax),
1320
- descriptions: z.array(z.strictObject({ text: descriptionSchema })).max(DYN.dynamicTextsMax).optional(),
1321
- images: z.array(dynamicImageSchema).optional(),
1322
- videos: z.array(dynamicVideoSchema).optional(),
1323
- /**
1324
- * Optional: Meta infers the format from the assets when it is omitted, and
1325
- * every flexible creative read back off a live account omits it.
1326
- */
1327
- ad_formats: z.array(z.enum(AD_FORMATS)).min(1).optional(),
1328
- call_to_action_types: z.array(z.string()).optional(),
1329
- /**
1330
- * The button on the creative's own `link_data` — a flexible ad carries both
1331
- * envelopes, and this is the one that takes `SEE_DETAILS`. Kept separate
1332
- * from `call_to_action_types` because Meta validates the two against
1333
- * different enums.
1334
- */
1335
- call_to_action: callToActionSchema.optional(),
1336
- link_urls: z.array(z.strictObject({ website_url: httpsUrlSchema, display_url: z.string().optional() })).min(1),
1337
- /**
1338
- * `asset_feed_spec.optimization_type` — passed through as a free string on
1339
- * purpose. Meta's own 400 enumerates ten values and then accepts
1340
- * `DEGREES_OF_FREEDOM`, which is not among them and is what every
1341
- * Advantage+ flexible creative on a live account reports. An enum here
1342
- * would refuse the value Meta itself hands back.
1343
- */
1344
- optimization_type: z.string().optional(),
1345
- /** Multi-language / placement customization — structural passthrough for v1. */
1346
- asset_customization_rules: z.array(z.record(z.string(), z.unknown())).optional()
1347
- }).superRefine((p, ctx) => {
1348
- if (!(p.images?.length || p.videos?.length)) {
1349
- ctx.addIssue({
1350
- code: "custom",
1351
- path: ["images"],
1352
- message: "a dynamic creative needs at least one image or video"
1353
- });
1354
- }
1355
- p.call_to_action_types?.forEach((type, index) => {
1356
- if (ASSET_FEED_CTA_TYPES.some((allowed) => allowed === type)) {
1357
- return;
1358
- }
1359
- const isLinkButton = CTA_TYPES.some((allowed) => allowed === type);
1360
- ctx.addIssue({
1361
- code: "custom",
1362
- path: ["call_to_action_types", index],
1363
- message: isLinkButton ? `${type} is not one of the buttons Meta accepts in asset_feed_spec.call_to_action_types \u2014 it is a link_data button. Pass it as call_to_action: { type: "${type}" } instead.` : `${type} is not a Meta call-to-action button.`
1364
- });
1365
- });
1366
- });
1367
- var existingPostCreativeSchema = z.strictObject({
1368
- creativeType: z.literal("existing_post"),
1369
- name: creativeSharedFields.name,
1370
- /** "<page_id>_<post_id>" object story id of the post to promote. */
1371
- object_story_id: z.string().regex(/^\d+_\d+$/, 'expected "<page_id>_<post_id>"'),
1372
- instagram_user_id: creativeSharedFields.instagram_user_id,
1373
- url_tags: creativeSharedFields.url_tags,
1374
- enhancements: creativeSharedFields.enhancements
1375
- });
1376
- var specCreativeSchema = z.strictObject({
1377
- creativeType: z.literal("spec"),
1378
- name: creativeSharedFields.name,
1379
- url_tags: creativeSharedFields.url_tags,
1380
- /** Promote the existing post — keeps the ad's likes, comments and shares. */
1381
- object_story_id: z.string().regex(/^\d+_\d+$/, 'expected "<page_id>_<post_id>"').optional(),
1382
- object_story_spec: z.record(z.string(), z.unknown()).optional(),
1383
- asset_feed_spec: z.record(z.string(), z.unknown()).optional(),
1384
- degrees_of_freedom_spec: z.record(z.string(), z.unknown()).optional(),
1385
- instagram_user_id: creativeSharedFields.instagram_user_id,
1386
- /** The creative this spec was cloned from, for the review diff. */
1387
- clonedFrom: z.string().regex(NUMERIC_ID_REGEX).optional()
1388
- }).superRefine((p, ctx) => {
1389
- if (!(p.object_story_id || p.object_story_spec || p.asset_feed_spec)) {
1390
- ctx.addIssue({
1391
- code: "custom",
1392
- path: ["object_story_spec"],
1393
- message: "a spec creative needs object_story_id, object_story_spec or asset_feed_spec"
1394
- });
1395
- }
1396
- });
1397
- var creativeContentSchema = z.discriminatedUnion("creativeType", [
1398
- singleCreativeSchema,
1399
- carouselCreativeSchema,
1400
- dynamicCreativeSchema,
1401
- existingPostCreativeSchema,
1402
- specCreativeSchema
1403
- ]);
1404
- var adCreativeCreateSchema = creativeContentSchema;
1405
- var adCreativeUpdateSchema = z.strictObject({
1406
- name: z.string().max(META_LIMITS.creative.nameMax).optional(),
1407
- status: z.enum(CREATIVE_UPDATE_STATUSES).optional(),
1408
- /** Content patch — only honored when the target is a staged meta_temp_* creative. */
1409
- content: z.record(z.string(), z.unknown()).optional()
1410
- }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1411
- var adCreateSchema = z.strictObject({
1412
- name: z.string().min(1).max(META_LIMITS.ad.nameMax),
1413
- adset_id: parentRefSchema,
1414
- status: stageableStatusSchema.default("PAUSED"),
1415
- creative: z.strictObject({ creative_id: parentRefSchema }),
1416
- /** Conversion pixel / offline event set / view tags — structural passthrough. */
1417
- tracking_specs: z.array(z.record(z.string(), z.unknown())).optional()
1418
- });
1419
- var adUpdateSchema = z.strictObject({
1420
- name: z.string().min(1).max(META_LIMITS.ad.nameMax).optional(),
1421
- status: updateStatusSchema.optional(),
1422
- /** Swapping the creative is the Meta way to "edit" an ad's creative. */
1423
- creative: z.strictObject({ creative_id: parentRefSchema }).optional(),
1424
- tracking_specs: z.array(z.record(z.string(), z.unknown())).optional()
1425
- }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1426
- var lookalikeSpecSchema = z.strictObject({
1427
- origin: z.array(z.strictObject({ id: parentRefSchema })).min(1),
1428
- ratio: z.number().min(0.01).max(0.2).optional(),
1429
- country: z.string().length(2).optional()
1430
- });
1431
- function valuesOfKey(node, key) {
1432
- if (Array.isArray(node)) return node.flatMap((entry) => valuesOfKey(entry, key));
1433
- if (!node || typeof node !== "object") return [];
1434
- const record = node;
1435
- return [
1436
- ...key in record ? [record[key]] : [],
1437
- ...Object.values(record).flatMap((value) => valuesOfKey(value, key))
1438
- ];
1439
- }
1440
- function mixedEventSourceTypes(rule) {
1441
- const types = valuesOfKey(rule, "event_sources").flatMap((sources) => Array.isArray(sources) ? sources : []).map((source) => source?.type).filter((type) => typeof type === "string");
1442
- const distinct = [...new Set(types)].sort();
1443
- return distinct.length > 1 ? distinct : [];
1444
- }
1445
- var customAudienceCreateSchema = z.strictObject({
1446
- name: z.string().min(1).max(META_LIMITS.audience.nameMax),
1447
- subtype: z.enum(CUSTOM_AUDIENCE_SUBTYPES),
1448
- description: z.string().max(500).optional(),
1449
- customer_file_source: z.enum(CUSTOMER_FILE_SOURCES).optional(),
1450
- retention_days: z.number().int().min(1).max(META_LIMITS.audience.retentionDaysMax).optional(),
1451
- lookalike_spec: lookalikeSpecSchema.optional(),
1452
- /** Website/engagement rule — structural passthrough validated by Meta. */
1453
- rule: z.record(z.string(), z.unknown()).optional()
1454
- }).superRefine((p, ctx) => {
1455
- if (p.subtype === "LOOKALIKE" && !p.lookalike_spec) {
1456
- ctx.addIssue({ code: "custom", path: ["lookalike_spec"], message: "LOOKALIKE audiences need a lookalike_spec" });
1457
- }
1458
- if ((p.subtype === "WEBSITE" || p.subtype === "ENGAGEMENT") && !p.rule) {
1459
- ctx.addIssue({ code: "custom", path: ["rule"], message: `${p.subtype} audiences need a rule (use --file)` });
1460
- }
1461
- if (p.rule && p.retention_days !== void 0) {
1462
- ctx.addIssue({
1463
- code: "custom",
1464
- path: ["retention_days"],
1465
- message: "a rule carries its own window in retention_seconds, so drop retention_days \u2014 Meta takes it only on a customer-file audience, and sending both leaves the review showing a window the audience will not have"
1466
- });
1467
- }
1468
- const mixed = mixedEventSourceTypes(p.rule);
1469
- if (mixed.length > 0) {
1470
- ctx.addIssue({
1471
- code: "custom",
1472
- path: ["rule"],
1473
- message: `one audience covers one kind of source, but this rule mixes ${mixed.join(" and ")} \u2014 split it into one audience per source`
1474
- });
1475
- }
1476
- });
1477
- var customAudienceUpdateSchema = z.strictObject({
1478
- name: z.string().min(1).max(META_LIMITS.audience.nameMax).optional(),
1479
- description: z.string().max(500).optional()
1480
- }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1481
- var customConversionCreateSchema = z.strictObject({
1482
- name: z.string().min(1).max(META_LIMITS.customConversion.nameMax),
1483
- description: z.string().max(META_LIMITS.customConversion.descriptionMax).optional(),
1484
- /** The pixel the conversion counts from — `event_source_id` on the wire. Baker never creates pixels, so this is always a real id. */
1485
- pixel_id: z.string().regex(NUMERIC_ID_REGEX),
1486
- custom_event_type: z.enum(CUSTOM_EVENT_TYPES),
1487
- /** Meta's filter JSON. Built by `buildCustomConversionRule` for the URL case. */
1488
- rule: z.record(z.string(), z.unknown()),
1489
- /** What one conversion is worth, when the site cannot send a value with the event. */
1490
- default_conversion_value: z.number().min(0).optional()
1491
- }).superRefine((p, ctx) => {
1492
- const refusal = customConversionRuleRefusal(p.rule);
1493
- if (refusal) {
1494
- ctx.addIssue({ code: "custom", path: ["rule"], message: refusal });
1495
- }
1496
- });
1497
- var mediaUploadSchema = z.strictObject({
1498
- kind: z.enum(MEDIA_KINDS),
1499
- bakerImageId: bakerMediaIdSchema.optional(),
1500
- bakerVideoId: bakerMediaIdSchema.optional(),
1501
- /** Optional display name / filename hint. */
1502
- name: z.string().max(255).optional()
1503
- }).superRefine((p, ctx) => {
1504
- if (p.kind === "image" && !p.bakerImageId) {
1505
- ctx.addIssue({ code: "custom", path: ["bakerImageId"], message: "image uploads need a bakerImageId" });
1506
- }
1507
- if (p.kind === "video" && !p.bakerVideoId) {
1508
- ctx.addIssue({ code: "custom", path: ["bakerVideoId"], message: "video uploads need a bakerVideoId" });
1509
- }
1510
- });
1511
- var leadFormCreateSchema = z.strictObject({
1512
- page_id: z.string().regex(NUMERIC_ID_REGEX),
1513
- name: z.string().min(1).max(255),
1514
- locale: z.enum(["EN_US", "EN_GB", "ES_ES", "ES_LA", "FR_FR", "DE_DE", "PT_BR", "PT_PT"]).default("EN_US"),
1515
- questionPageHeadline: z.string().min(1).max(255).optional(),
1516
- questions: z.array(
1517
- z.strictObject({
1518
- type: z.enum([
1519
- "FULL_NAME",
1520
- "FIRST_NAME",
1521
- "LAST_NAME",
1522
- "EMAIL",
1523
- "PHONE",
1524
- "CITY",
1525
- "STATE",
1526
- "COUNTRY",
1527
- "ZIP_CODE",
1528
- "COMPANY_NAME",
1529
- "JOB_TITLE",
1530
- "CUSTOM"
1531
- ]),
1532
- label: z.string().min(1).max(255).optional()
1533
- })
1534
- ).min(1).max(15),
1535
- privacyPolicyUrl: httpsUrlSchema,
1536
- privacyPolicyLinkText: z.string().min(1).max(70).default("Privacy Policy"),
1537
- followUpActionUrl: httpsUrlSchema,
1538
- optimizedForQuality: z.boolean().default(false),
1539
- thankYou: z.strictObject({
1540
- title: z.string().min(1).max(255),
1541
- body: z.string().min(1).max(1e3),
1542
- buttonText: z.string().min(1).max(70).default("View website")
1543
- }).optional()
1544
- }).superRefine((form, ctx) => {
1545
- const seen = /* @__PURE__ */ new Set();
1546
- for (const [index, question] of form.questions.entries()) {
1547
- if (question.type === "CUSTOM" && !question.label) {
1548
- ctx.addIssue({ code: "custom", path: ["questions", index, "label"], message: "custom questions need a label" });
1549
- }
1550
- const key = `${question.type}:${question.label ?? ""}`;
1551
- if (seen.has(key)) ctx.addIssue({ code: "custom", path: ["questions", index], message: "duplicate question" });
1552
- seen.add(key);
1553
- }
1554
- });
1555
- var META_DRAFT_OP_KINDS = [
1556
- "campaign.create",
1557
- "campaign.update",
1558
- "adSet.create",
1559
- "adSet.update",
1560
- "adCreative.create",
1561
- "adCreative.update",
1562
- "ad.create",
1563
- "ad.update",
1564
- "customAudience.create",
1565
- "customAudience.update",
1566
- "customConversion.create",
1567
- "leadForm.create",
1568
- "media.upload"
1569
- ];
1570
- var metaDraftOpKindSchema = z.enum(META_DRAFT_OP_KINDS);
1571
- var accountIdSchema = z.string().regex(NUMERIC_ID_REGEX, "accountId must be the bare numeric ad account id");
1572
- var updateTargetSchema = z.union([z.string().regex(NUMERIC_ID_REGEX), tempRefSchema]);
1573
- function createOp(kind, payload) {
1574
- return z.object({ kind: z.literal(kind), accountId: accountIdSchema, payload });
1575
- }
1576
- function updateOp(kind, payload) {
1577
- return z.object({ kind: z.literal(kind), accountId: accountIdSchema, target: updateTargetSchema, payload });
1578
- }
1579
- var metaDraftOpInputSchema = z.discriminatedUnion("kind", [
1580
- createOp("campaign.create", campaignCreateSchema),
1581
- updateOp("campaign.update", campaignUpdateSchema),
1582
- createOp("adSet.create", adSetCreateSchema),
1583
- updateOp("adSet.update", adSetUpdateSchema),
1584
- createOp("adCreative.create", adCreativeCreateSchema),
1585
- updateOp("adCreative.update", adCreativeUpdateSchema),
1586
- createOp("ad.create", adCreateSchema),
1587
- updateOp("ad.update", adUpdateSchema),
1588
- createOp("customAudience.create", customAudienceCreateSchema),
1589
- updateOp("customAudience.update", customAudienceUpdateSchema),
1590
- createOp("customConversion.create", customConversionCreateSchema),
1591
- createOp("leadForm.create", leadFormCreateSchema),
1592
- createOp("media.upload", mediaUploadSchema)
1593
- ]);
1594
-
1595
- export {
1596
- buildCustomConversionRule,
1597
- isMetaLeadAdsRefusal,
1598
- FACEBOOK_PREVIEW_FORMATS,
1599
- INSTAGRAM_PREVIEW_FORMATS,
1600
- PLATFORM_SCOPED_POSITIONS,
1601
- previewFormatForPosition,
1602
- placementFormatNote,
1603
- metaDraftOpInputSchema
1604
- };
1605
- //# sourceMappingURL=chunk-GOJXKIFQ.js.map