@koda-sl/baker-cli 0.476.0 → 0.477.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 (1323) hide show
  1. package/README.md +18 -0
  2. package/dist/account-63NUWSXK.js +49 -0
  3. package/dist/account-IFB6V6GL.js +80 -0
  4. package/dist/account-NCX3VHZT.js +79 -0
  5. package/dist/account-reads-Y3T65EI4.js +218 -0
  6. package/dist/account-writes-7YZ7PTHF.js +66 -0
  7. package/dist/accounts-2MQVGI4I.js +77 -0
  8. package/dist/accounts-66KZC7QI.js +114 -0
  9. package/dist/accounts-MQIYTIAX.js +611 -0
  10. package/dist/accounts-MVEZ2TRL.js +75 -0
  11. package/dist/accounts-T5DCHHZK.js +115 -0
  12. package/dist/actions-LRSHHDV2.js +55 -0
  13. package/dist/active-entities-QWPGPNGE.js +103 -0
  14. package/dist/activecampaign-G4XARU6N.js +935 -0
  15. package/dist/activities-FN7DOE5S.js +74 -0
  16. package/dist/ad-group-writes-JSOCSYYV.js +170 -0
  17. package/dist/ad-groups-UN52WNJN.js +68 -0
  18. package/dist/ad-writes-X23H4HLZ.js +174 -0
  19. package/dist/add-2MVZQJYG.js +302 -0
  20. package/dist/add-7EKI6ZA4.js +97 -0
  21. package/dist/add-ZP47HVHW.js +104 -0
  22. package/dist/add-ZTXN775K.js +105 -0
  23. package/dist/add-accounts-PEJLMYWR.js +91 -0
  24. package/dist/ads-7YYCWZIR.js +40 -0
  25. package/dist/ads-FOJ6P4OA.js +128 -0
  26. package/dist/ads-MKMWS36P.js +113 -0
  27. package/dist/ads-NEJKXB4L.js +77 -0
  28. package/dist/adsets-TIHFYWOU.js +176 -0
  29. package/dist/advertisers-246ZLRFZ.js +120 -0
  30. package/dist/advertisers-E77XAYCQ.js +125 -0
  31. package/dist/analytics-SL56NTIV.js +433 -0
  32. package/dist/analytics-WPYPKM6L.js +1976 -0
  33. package/dist/animate-3H3TL3N6.js +480 -0
  34. package/dist/assign-WGQVY3DW.js +77 -0
  35. package/dist/audience-size-C37XJ6GS.js +94 -0
  36. package/dist/audiences-BWDG2R2U.js +268 -0
  37. package/dist/audiences-GHMNXDPR.js +85 -0
  38. package/dist/audiences-Q7OCAMAQ.js +78 -0
  39. package/dist/audit-HG53XQJK.js +88 -0
  40. package/dist/audit-LGD4PMAI.js +151 -0
  41. package/dist/autocomplete-PD2EMWRU.js +111 -0
  42. package/dist/avatars-U5KAMVNV.js +38 -0
  43. package/dist/bid-pricing-PP6PV6S4.js +99 -0
  44. package/dist/brand-2JJ45TBO.js +22 -0
  45. package/dist/brief-IMTLJQN2.js +490 -0
  46. package/dist/businesses-24QYU6BL.js +53 -0
  47. package/dist/campaign-groups-ZXRHEEGS.js +95 -0
  48. package/dist/campaign-writes-7UHMSPJ3.js +170 -0
  49. package/dist/campaign-writes-WIBYW73K.js +165 -0
  50. package/dist/campaigns-CRRLTN74.js +107 -0
  51. package/dist/campaigns-DJV4W6UP.js +76 -0
  52. package/dist/campaigns-EY3AR4N7.js +109 -0
  53. package/dist/campaigns-WTZBLGVX.js +110 -0
  54. package/dist/canvas-WFYKZYZD.js +46 -0
  55. package/dist/capabilities-IGKT7D4H.js +144 -0
  56. package/dist/cards-IO2Q423H.js +85 -0
  57. package/dist/catalog-X3VJFC74.js +64 -0
  58. package/dist/catalogs-2HT3VRRI.js +128 -0
  59. package/dist/changes-AZ6EUVFN.js +135 -0
  60. package/dist/chatgpt-M27OEM7J.js +47 -0
  61. package/dist/chats-OXIYXI4U.js +262 -0
  62. package/dist/chunk-26HCUIW2.js +166 -0
  63. package/dist/chunk-2SDC7WNS.js +817 -0
  64. package/dist/chunk-2SQ6ISRI.js +392 -0
  65. package/dist/chunk-2VRD3HSQ.js +60 -0
  66. package/dist/chunk-37P6DTDJ.js +455 -0
  67. package/dist/chunk-3IGY2FWK.js +6667 -0
  68. package/dist/chunk-4EPGI6I2.js +76 -0
  69. package/dist/chunk-4LODOBIN.js +433 -0
  70. package/dist/chunk-5ADTL2LR.js +188 -0
  71. package/dist/chunk-5HABWBQX.js +139 -0
  72. package/dist/chunk-5TV4GYP2.js +1830 -0
  73. package/dist/chunk-6M6HUJ55.js +474 -0
  74. package/dist/chunk-6YZNHMPJ.js +74 -0
  75. package/dist/chunk-7ANWFQEA.js +220 -0
  76. package/dist/chunk-7ANWFQEA.js.map +1 -0
  77. package/dist/chunk-7FKRKMGL.js +133 -0
  78. package/dist/chunk-7MADMR5U.js +399 -0
  79. package/dist/chunk-7MADMR5U.js.map +1 -0
  80. package/dist/chunk-7WZHO6W3.js +225 -0
  81. package/dist/chunk-AAF7HL4I.js +320 -0
  82. package/dist/chunk-AAF7HL4I.js.map +1 -0
  83. package/dist/chunk-ANWV2VTO.js +136 -0
  84. package/dist/chunk-ANYMWCST.js +2122 -0
  85. package/dist/chunk-ANYMWCST.js.map +1 -0
  86. package/dist/chunk-AQADZOTG.js +137 -0
  87. package/dist/chunk-AQADZOTG.js.map +1 -0
  88. package/dist/chunk-AQYH6C32.js +544 -0
  89. package/dist/chunk-BODRDUYQ.js +111 -0
  90. package/dist/chunk-BPPT7YVK.js +274 -0
  91. package/dist/chunk-BWAFZSZ4.js +94 -0
  92. package/dist/chunk-CIWI7LHO.js +191 -0
  93. package/dist/chunk-CLST7PLN.js +26 -0
  94. package/dist/chunk-CMNVR6DX.js +486 -0
  95. package/dist/chunk-CPTOHQSJ.js +296 -0
  96. package/dist/chunk-CU2DEMQB.js +1079 -0
  97. package/dist/chunk-D5GVSJBK.js +66 -0
  98. package/dist/chunk-D5Z34AYL.js +297 -0
  99. package/dist/chunk-EBC64WAG.js +74 -0
  100. package/dist/chunk-EMPXRKIM.js +644 -0
  101. package/dist/chunk-EMPXRKIM.js.map +1 -0
  102. package/dist/chunk-EQ74MEJN.js +98 -0
  103. package/dist/chunk-ETFAIX4L.js +74 -0
  104. package/dist/chunk-F3VR6CTH.js +903 -0
  105. package/dist/chunk-FDPBTKXZ.js +25 -0
  106. package/dist/chunk-GAD2NYL4.js +1442 -0
  107. package/dist/chunk-GAJQPQWS.js +6239 -0
  108. package/dist/chunk-GAJQPQWS.js.map +1 -0
  109. package/dist/chunk-GRL25RKS.js +4408 -0
  110. package/dist/chunk-GRYP7Y5V.js +103 -0
  111. package/dist/chunk-H4DAEWJQ.js +51 -0
  112. package/dist/chunk-H5CGVMQW.js +472 -0
  113. package/dist/chunk-H5CGVMQW.js.map +1 -0
  114. package/dist/chunk-HDQBILJR.js +45 -0
  115. package/dist/chunk-HH5PFZ7U.js +84 -0
  116. package/dist/chunk-HKR7VCFK.js +42 -0
  117. package/dist/chunk-JQ4PQ2H6.js +197 -0
  118. package/dist/chunk-JRLGQA2T.js +333 -0
  119. package/dist/chunk-JU6X4XFU.js +23 -0
  120. package/dist/chunk-KODTY7PB.js +215 -0
  121. package/dist/chunk-LEGVJCL4.js +56 -0
  122. package/dist/chunk-LKM3FMYC.js +363 -0
  123. package/dist/chunk-LOTQBFUP.js +106 -0
  124. package/dist/chunk-LPBLKGH7.js +106 -0
  125. package/dist/chunk-MICLDQES.js +346 -0
  126. package/dist/chunk-MUHPBWE5.js +35 -0
  127. package/dist/chunk-NHQTEQPR.js +169 -0
  128. package/dist/chunk-NKXH3I65.js +1743 -0
  129. package/dist/chunk-NRRNY3TZ.js +55 -0
  130. package/dist/chunk-O5J5T4ZL.js +111 -0
  131. package/dist/chunk-O6TGA5HT.js +337 -0
  132. package/dist/chunk-O6VUX3O5.js +52 -0
  133. package/dist/chunk-OST3RA6Q.js +610 -0
  134. package/dist/chunk-PCSBF6GN.js +3151 -0
  135. package/dist/chunk-PLK7V2SJ.js +509 -0
  136. package/dist/chunk-PWABS2DB.js +172 -0
  137. package/dist/chunk-PXTFT35U.js +547 -0
  138. package/dist/chunk-QBCPZFAV.js +23992 -0
  139. package/dist/chunk-QEZEXMRB.js +96 -0
  140. package/dist/chunk-QGSV3IWT.js +304 -0
  141. package/dist/chunk-QGWCBGEC.js +1599 -0
  142. package/dist/chunk-QKUBK3AD.js +97 -0
  143. package/dist/chunk-QNH5NCLM.js +67 -0
  144. package/dist/chunk-QNLWBJUR.js +58 -0
  145. package/dist/chunk-R4CDO5YH.js +172 -0
  146. package/dist/chunk-RCMK4FM2.js +19 -0
  147. package/dist/chunk-RJFM6TJC.js +76 -0
  148. package/dist/chunk-RKPSE27R.js +98 -0
  149. package/dist/chunk-S6ODQYTC.js +96 -0
  150. package/dist/chunk-SBN5UIQY.js +665 -0
  151. package/dist/chunk-SOK7U4NT.js +592 -0
  152. package/dist/chunk-TLQQV5AY.js +159 -0
  153. package/dist/chunk-TOIFDXCW.js +57 -0
  154. package/dist/chunk-TWXM7HVR.js +149 -0
  155. package/dist/chunk-UARL36OD.js +846 -0
  156. package/dist/chunk-UDG37PC2.js +174 -0
  157. package/dist/chunk-UMFKMS4U.js +275 -0
  158. package/dist/chunk-UW7PZFWZ.js +75 -0
  159. package/dist/chunk-UWTAQM4W.js +160 -0
  160. package/dist/chunk-VDAYKWVH.js +125 -0
  161. package/dist/chunk-VY5CTURR.js +46 -0
  162. package/dist/chunk-WE6K2VVZ.js +494 -0
  163. package/dist/chunk-WOTX7TNV.js +84 -0
  164. package/dist/chunk-XKA4ZHGX.js +206 -0
  165. package/dist/chunk-XO3GZLWQ.js +5181 -0
  166. package/dist/chunk-XWB2L3HS.js +66 -0
  167. package/dist/chunk-XYOU3ROP.js +175 -0
  168. package/dist/chunk-YDSF4PXO.js +216 -0
  169. package/dist/chunk-YDWQVAFI.js +92 -0
  170. package/dist/claim-DEQYWQZ4.js +197 -0
  171. package/dist/cli.js +101 -82
  172. package/dist/cli.js.map +1 -1
  173. package/dist/client-TL7BITWM.js +19 -0
  174. package/dist/code-ORY2WROG.js +76 -0
  175. package/dist/companies-QM5MOLIZ.js +195 -0
  176. package/dist/complete-FM6VSFCT.js +81 -0
  177. package/dist/config-FMDQ323H.js +88 -0
  178. package/dist/connect-YTIE5PRU.js +42 -0
  179. package/dist/content-ZQB6PXGX.js +85 -0
  180. package/dist/conversation-VKWYWPB6.js +93 -0
  181. package/dist/conversion-report-U4UFXSD2.js +125 -0
  182. package/dist/conversion-upload-command-INQX73AK.js +438 -0
  183. package/dist/conversion-writes-MCKROLRA.js +120 -0
  184. package/dist/conversions-OPNDVQ56.js +76 -0
  185. package/dist/conversions-QILFJ34A.js +135 -0
  186. package/dist/countries-AZ3XSZVM.js +82 -0
  187. package/dist/create-A7O5CBUS.js +207 -0
  188. package/dist/create-BEH3DUUP.js +59 -0
  189. package/dist/create-C4LTWKD7.js +132 -0
  190. package/dist/create-C4LTWKD7.js.map +1 -0
  191. package/dist/create-LRVA4MSV.js +235 -0
  192. package/dist/creatives-43I65CHD.js +87 -0
  193. package/dist/creatives-DRRWFFOH.js +51 -0
  194. package/dist/creatives-XEKEYLNV.js +182 -0
  195. package/dist/credentials-GJMH4XRU.js +287 -0
  196. package/dist/critique-EJJQVV4D.js +213 -0
  197. package/dist/critique-GIJUTKIM.js +1449 -0
  198. package/dist/crm-ECMEYPQT.js +318 -0
  199. package/dist/crm-WHK3PL6S.js +224 -0
  200. package/dist/crop-Q2GJLOHE.js +113 -0
  201. package/dist/currency-SVDR5N7L.js +88 -0
  202. package/dist/custom-conversions-5NJNPZKM.js +125 -0
  203. package/dist/customers-XKE3DQR7.js +56 -0
  204. package/dist/decide-PCJTFCWC.js +199 -0
  205. package/dist/delete-7K4KFPUT.js +69 -0
  206. package/dist/delete-APRR2OYC.js +66 -0
  207. package/dist/delete-DTTEHY6E.js +64 -0
  208. package/dist/delete-DTTEHY6E.js.map +1 -0
  209. package/dist/delete-GOA4Y562.js +71 -0
  210. package/dist/delete-I2SIRTMZ.js +71 -0
  211. package/dist/delete-QCZUGDIJ.js +54 -0
  212. package/dist/demographics-HG7NWA5U.js +138 -0
  213. package/dist/describe-CRTFAH6Y.js +97 -0
  214. package/dist/describe-OAB2FJYK.js +97 -0
  215. package/dist/dimensions-PFGHRZ5T.js +101 -0
  216. package/dist/discard-U3DW6WV3.js +66 -0
  217. package/dist/discover-IOJZYJ6B.js +172 -0
  218. package/dist/discover-R3EUUGRZ.js +93 -0
  219. package/dist/domain-HTF3ARBJ.js +98 -0
  220. package/dist/download-2HGDCPR7.js +299 -0
  221. package/dist/draft-3KLT2YHI.js +233 -0
  222. package/dist/draft-5RL6HZ6A.js +306 -0
  223. package/dist/draft-7O4ABIKK.js +100 -0
  224. package/dist/draft-DK4NZ6PW.js +281 -0
  225. package/dist/draft-GPL2TZXA.js +328 -0
  226. package/dist/draft-KTOZ2AT7.js +217 -0
  227. package/dist/draft-PQGTIABK.js +103 -0
  228. package/dist/draft-SRMHEIRG.js +307 -0
  229. package/dist/engine/index.js +12 -12
  230. package/dist/env-NPSYSFXD.js +51 -0
  231. package/dist/experiment-V57PLATO.js +1397 -0
  232. package/dist/export-UN2PTCNO.js +168 -0
  233. package/dist/extract-5JOFQZVU.js +113 -0
  234. package/dist/facets-2XQEJNZZ.js +114 -0
  235. package/dist/favorites-4AWW5F5B.js +178 -0
  236. package/dist/feed-AK3IFTAL.js +177 -0
  237. package/dist/feedback-34M4VJ5S.js +66 -0
  238. package/dist/feedback-record-ZINYAVHA.js +175 -0
  239. package/dist/fetch-6ALLGDRN.js +139 -0
  240. package/dist/fetch-VYIMA5BI.js +176 -0
  241. package/dist/find-22HWPRFY.js +134 -0
  242. package/dist/fit-S4RO2DMX.js +45 -0
  243. package/dist/flows-ITS573ST.js +247 -0
  244. package/dist/follow-IUW3P6KH.js +94 -0
  245. package/dist/follow-competitors-YDF5WM4K.js +112 -0
  246. package/dist/following-PNOYUSHX.js +113 -0
  247. package/dist/fonts-U4YXUNNZ.js +34 -0
  248. package/dist/forecast-AFBIS6WT.js +104 -0
  249. package/dist/forecast-NHG3UHOP.js +168 -0
  250. package/dist/funding-WSJI56OH.js +85 -0
  251. package/dist/funnel-46QWG5DN.js +394 -0
  252. package/dist/ga4-4W3KJIS5.js +55 -0
  253. package/dist/generate-3MC7WDL2.js +263 -0
  254. package/dist/get-4DJXDO42.js +61 -0
  255. package/dist/get-BK4MBGQF.js +59 -0
  256. package/dist/get-IAJ57JLM.js +79 -0
  257. package/dist/get-IAJ57JLM.js.map +1 -0
  258. package/dist/get-MVWVS3YT.js +56 -0
  259. package/dist/get-P5ZTH7RQ.js +63 -0
  260. package/dist/get-PTOW6PVF.js +63 -0
  261. package/dist/get-SR2NWOPT.js +92 -0
  262. package/dist/gif-L6RYCB6L.js +94 -0
  263. package/dist/google-EMSAXPVB.js +136 -0
  264. package/dist/google-XE35SF3Z.js +3190 -0
  265. package/dist/google-ads-DYNDLOBO.js +195 -0
  266. package/dist/group-EUYJJ4LI.js +43 -0
  267. package/dist/group-MBRA2QID.js +43 -0
  268. package/dist/gsc-B3NPUNI6.js +30 -0
  269. package/dist/history-4S3YEIPK.js +67 -0
  270. package/dist/history-7HFGP7UI.js +133 -0
  271. package/dist/hubspot-3LWMILTD.js +779 -0
  272. package/dist/icon-4DAXBICB.js +137 -0
  273. package/dist/ideas-RHOV2ZJ6.js +32 -0
  274. package/dist/images-KXEPMLOC.js +90 -0
  275. package/dist/improve-U2RA5DPI.js +86 -0
  276. package/dist/ingest-H4F5TJC3.js +23 -0
  277. package/dist/ingest-URYQ7BTL.js +107 -0
  278. package/dist/insights-HQGBM6VA.js +423 -0
  279. package/dist/insights-K2CMI3K5.js +122 -0
  280. package/dist/inspect-IPQTTZK3.js +189 -0
  281. package/dist/inspiration-TRMFE64K.js +53 -0
  282. package/dist/intent-2EWV4KXW.js +95 -0
  283. package/dist/job-7GDSDRM4.js +202 -0
  284. package/dist/job-create-PE7G64WC.js +109 -0
  285. package/dist/job-status-LUSCFLZ2.js +74 -0
  286. package/dist/keep-5NFHEILH.js +78 -0
  287. package/dist/keyword-gap-T22T7RN6.js +130 -0
  288. package/dist/keyword-metrics-SLCXHPZS.js +197 -0
  289. package/dist/keywords-for-site-HBQSJKNN.js +128 -0
  290. package/dist/landing-PJJGQM3M.js +28 -0
  291. package/dist/landings-ZR4YMEPV.js +151 -0
  292. package/dist/languages-HXAZNLNU.js +55 -0
  293. package/dist/languages-VLMJOAJN.js +40 -0
  294. package/dist/launches-T5LAIOYS.js +19 -0
  295. package/dist/layerize-24PQKKPB.js +190 -0
  296. package/dist/leads-EDKSVSWP.js +293 -0
  297. package/dist/leads-HXYLSNMB.js +90 -0
  298. package/dist/learn-ZUZ7MEY5.js +203 -0
  299. package/dist/library-ZHREUZMU.js +109 -0
  300. package/dist/lighthouse-E2NTHAO4.js +75 -0
  301. package/dist/line-item-writes-JNHZFCRM.js +208 -0
  302. package/dist/line-items-WGLNILMH.js +103 -0
  303. package/dist/link-KLFRRU24.js +64 -0
  304. package/dist/linkedin-LPWIZ3SS.js +1009 -0
  305. package/dist/linkedin-LPWIZ3SS.js.map +1 -0
  306. package/dist/list-37F6C6P5.js +57 -0
  307. package/dist/list-7EEDCI3T.js +51 -0
  308. package/dist/list-E3MCY43O.js +303 -0
  309. package/dist/list-E7CJMIGC.js +76 -0
  310. package/dist/list-HA2NITR3.js +50 -0
  311. package/dist/list-MIGWTOQU.js +64 -0
  312. package/dist/list-MIGWTOQU.js.map +1 -0
  313. package/dist/list-SW3DF473.js +151 -0
  314. package/dist/list-TG6LBHCV.js +65 -0
  315. package/dist/locations-VYIDAVD4.js +67 -0
  316. package/dist/locations-ZWTDUUGL.js +40 -0
  317. package/dist/log-Y4NIMHFD.js +92 -0
  318. package/dist/logo-SHQJHJK7.js +18 -0
  319. package/dist/lookup-TWSJ7KRT.js +22 -0
  320. package/dist/make-IJN6E4Q7.js +633 -0
  321. package/dist/map-DHQTEDUH.js +433 -0
  322. package/dist/mcp-ZHASO273.js +463 -0
  323. package/dist/media-LDVWXWWZ.js +15 -0
  324. package/dist/media-TNB4DLTJ.js +89 -0
  325. package/dist/media-sources-MFBQDZNS.js +32 -0
  326. package/dist/members-HTHOAHPO.js +44 -0
  327. package/dist/meta-RAVCXVZA.js +69 -0
  328. package/dist/metrics-2VB46SDT.js +152 -0
  329. package/dist/models-TYRVCPS2.js +155 -0
  330. package/dist/normalize-PVOL256M.js +141 -0
  331. package/dist/normalize-Q7EN7JIF.js +806 -0
  332. package/dist/options-VDTCTJHY.js +122 -0
  333. package/dist/organization-VC5RO4FS.js +328 -0
  334. package/dist/organization-VC5RO4FS.js.map +1 -0
  335. package/dist/output-NHMCVVUU.js +24 -0
  336. package/dist/page-5PCVZ7HS.js +105 -0
  337. package/dist/pages-DDXB4NQ4.js +66 -0
  338. package/dist/performance-QOQTL4XM.js +24 -0
  339. package/dist/pinterest-KQYSCTVQ.js +96 -0
  340. package/dist/pixels-GH522EYV.js +108 -0
  341. package/dist/pixels-HJYPX6RR.js +60 -0
  342. package/dist/plan-ZS7SOXL6.js +207 -0
  343. package/dist/preview-W4VKXOCT.js +169 -0
  344. package/dist/profile-FSB4RT55.js +159 -0
  345. package/dist/promoted-tweet-writes-6PI2KR65.js +180 -0
  346. package/dist/promoted-tweets-UJRNHKKO.js +119 -0
  347. package/dist/properties-V74I56UK.js +101 -0
  348. package/dist/publish-R6RINQCF.js +322 -0
  349. package/dist/query-5F5MBR7H.js +247 -0
  350. package/dist/query-KBXZD4SW.js +61 -0
  351. package/dist/query-SFNCXH4H.js +236 -0
  352. package/dist/questionnaire-JK5JLQXK.js +93 -0
  353. package/dist/read-LGRFU7XB.js +141 -0
  354. package/dist/reddit-QWM5ZQ6P.js +26 -0
  355. package/dist/refresh-ZPF7UH3S.js +94 -0
  356. package/dist/release-BZPK2P3R.js +60 -0
  357. package/dist/relevant-pages-VMNEEEJK.js +99 -0
  358. package/dist/remove-C7ZJYFCG.js +50 -0
  359. package/dist/remove-PIHTPIHM.js +48 -0
  360. package/dist/reopen-6Q7NQX2C.js +63 -0
  361. package/dist/rerun-VRZVJDYS.js +84 -0
  362. package/dist/research-FXQ6X7BY.js +62 -0
  363. package/dist/research-X6O7IID6.js +262 -0
  364. package/dist/reset-FRNQUCL5.js +54 -0
  365. package/dist/reset-NXK6NCOO.js +54 -0
  366. package/dist/resolve-WHI4UP4V.js +121 -0
  367. package/dist/retry-VSFPEXLE.js +62 -0
  368. package/dist/retry-XMTYWCFA.js +60 -0
  369. package/dist/retry-failed-XTYV3QJS.js +45 -0
  370. package/dist/run-HBNAA56X.js +59 -0
  371. package/dist/run-TAASFDQ7.js +66 -0
  372. package/dist/run-TAASFDQ7.js.map +1 -0
  373. package/dist/scaffold-ad-BV5WK4IA.js +1548 -0
  374. package/dist/scaffold-static-ad-IWNJ6GM7.js +971 -0
  375. package/dist/scaffold-video-JBG7Q45T.js +872 -0
  376. package/dist/schedule-P6GVRZ4E.js +61 -0
  377. package/dist/schedule-WSO7ETS6.js +66 -0
  378. package/dist/scheduled-agents-TYT5BIBQ.js +50 -0
  379. package/dist/schema-DL34A626.js +69 -0
  380. package/dist/schema-DL34A626.js.map +1 -0
  381. package/dist/schema-O4CMQQGD.js +132 -0
  382. package/dist/scrape-ZG2OWZL4.js +2241 -0
  383. package/dist/screenshot-GJUY7FCW.js +102 -0
  384. package/dist/search-2BJ4OLMX.js +224 -0
  385. package/dist/search-52CSDYJS.js +91 -0
  386. package/dist/search-FHSQYZOC.js +92 -0
  387. package/dist/search-HBFAHH4W.js +174 -0
  388. package/dist/search-ZAY2QV3Y.js +26 -0
  389. package/dist/search-copy-O5AOEBXC.js +168 -0
  390. package/dist/seeds-XY3EZ33U.js +101 -0
  391. package/dist/select-ISR6YEIE.js +94 -0
  392. package/dist/sends-GECHL3NK.js +309 -0
  393. package/dist/sequences-5BB4S63A.js +146 -0
  394. package/dist/set-prompt-DF7Q6WOF.js +185 -0
  395. package/dist/setup-links-GF2IP4TH.js +141 -0
  396. package/dist/shared-JDIL3BNQ.js +31 -0
  397. package/dist/site-T6V5S5DJ.js +177 -0
  398. package/dist/sitemaps-UQJFRA2W.js +87 -0
  399. package/dist/sites-ZNNEPEGI.js +101 -0
  400. package/dist/skills-WRKEF2Q4.js +47 -0
  401. package/dist/sources-5YUE7KUI.js +30 -0
  402. package/dist/sources-UR3SLKVK.js +454 -0
  403. package/dist/stale-6LZAROBI.js +63 -0
  404. package/dist/stats-QFBTSDVV.js +34 -0
  405. package/dist/status-FDKP65DR.js +62 -0
  406. package/dist/sticker-2JQUC7XO.js +94 -0
  407. package/dist/stock-W3SNJCBV.js +179 -0
  408. package/dist/studio-2GAZ6QOH.js +49 -0
  409. package/dist/sync-4LHVBFMZ.js +265 -0
  410. package/dist/sync-NAJQQ6NQ.js +54 -0
  411. package/dist/sync-OGFGTKUK.js +54 -0
  412. package/dist/sync-definition-MSQFCXRK.js +54 -0
  413. package/dist/tag-manager-MNGOJ3GC.js +43 -0
  414. package/dist/tags-67KLYTJF.js +224 -0
  415. package/dist/tags-GLHFHQW2.js +20 -0
  416. package/dist/tags-PYLVQEVS.js +20 -0
  417. package/dist/tags-SL4F3LLY.js +20 -0
  418. package/dist/tags-XMRO4WF6.js +26 -0
  419. package/dist/targeting-constants-RQT4GLUA.js +118 -0
  420. package/dist/targeting-criteria-VK3IR6N2.js +113 -0
  421. package/dist/targets-RCM2ZXSU.js +103 -0
  422. package/dist/targets-RCM2ZXSU.js.map +1 -0
  423. package/dist/testimonials-KTHDZ3R5.js +32 -0
  424. package/dist/themes-VVID6Y73.js +94 -0
  425. package/dist/top-companies-MBTTJTRK.js +108 -0
  426. package/dist/translate-I6LZ3UZU.js +598 -0
  427. package/dist/triggers-PDIVJDLP.js +142 -0
  428. package/dist/triggers-PDIVJDLP.js.map +1 -0
  429. package/dist/tweets-L4CXZLKQ.js +123 -0
  430. package/dist/unfollow-5T2CT3EF.js +46 -0
  431. package/dist/unlink-IHEFAFG6.js +67 -0
  432. package/dist/unstage-NEX3EIJX.js +95 -0
  433. package/dist/unstage-NJQAIIPT.js +85 -0
  434. package/dist/unstage-NJQAIIPT.js.map +1 -0
  435. package/dist/update-CYQIGHT4.js +98 -0
  436. package/dist/update-K5MMOE5N.js +155 -0
  437. package/dist/update-LR7UW5LL.js +63 -0
  438. package/dist/update-P5BTLCGG.js +152 -0
  439. package/dist/update-P5BTLCGG.js.map +1 -0
  440. package/dist/upload-C2K66PR3.js +150 -0
  441. package/dist/upload-VVB2LTD2.js +126 -0
  442. package/dist/upscale-SGBB4GWF.js +109 -0
  443. package/dist/url-options-LUKI4Y46.js +130 -0
  444. package/dist/url-params-MSRUZOGT.js +98 -0
  445. package/dist/use-XGTUHBUL.js +95 -0
  446. package/dist/validate-XMEHRCAG.js +88 -0
  447. package/dist/variant-DQRDW6IZ.js +338 -0
  448. package/dist/videos-EQG7AAXT.js +39 -0
  449. package/dist/view-KJ6YNRXB.js +121 -0
  450. package/dist/viewer-check-7DZQDGD3.js +68 -0
  451. package/dist/web-MSYXLOFV.js +140 -0
  452. package/dist/winners-LD6PFXAS.js +113 -0
  453. package/dist/winning-ads-VWBU3BNQ.js +84 -0
  454. package/dist/write-commands-CKMHQ4YY.js +737 -0
  455. package/dist/write-commands-NW5CC3DP.js +206 -0
  456. package/dist/write-commands-PK23JVW3.js +1015 -0
  457. package/dist/write-commands-RLEYECTB.js +713 -0
  458. package/dist/x-ZSTQLK6Q.js +47 -0
  459. package/package.json +1 -1
  460. package/dist/account-MLAW7QO4.js +0 -49
  461. package/dist/account-QR5LCE74.js +0 -79
  462. package/dist/account-UP67V2FH.js +0 -80
  463. package/dist/account-reads-5RGYKIIJ.js +0 -218
  464. package/dist/account-writes-EGRMVWVQ.js +0 -66
  465. package/dist/accounts-CU4Q2AV7.js +0 -75
  466. package/dist/accounts-GUHRV3RA.js +0 -115
  467. package/dist/accounts-L2SNBNOO.js +0 -611
  468. package/dist/accounts-LG4B7NWO.js +0 -77
  469. package/dist/accounts-WH7SRFCB.js +0 -114
  470. package/dist/actions-LMKQCN4E.js +0 -55
  471. package/dist/active-entities-2CQHGACO.js +0 -103
  472. package/dist/activecampaign-RZEJ25GO.js +0 -935
  473. package/dist/activities-EDCSBYGM.js +0 -74
  474. package/dist/ad-group-writes-WUAYGWDY.js +0 -170
  475. package/dist/ad-groups-BVEJJHX6.js +0 -68
  476. package/dist/ad-writes-WTWRSFLW.js +0 -174
  477. package/dist/add-DDKP3OOS.js +0 -104
  478. package/dist/add-HQERVTGX.js +0 -302
  479. package/dist/add-PGD6PNXH.js +0 -97
  480. package/dist/add-ZMZJSBQT.js +0 -105
  481. package/dist/add-accounts-A2YRT5NR.js +0 -91
  482. package/dist/ads-HRCVRXPO.js +0 -77
  483. package/dist/ads-WB2X5SYO.js +0 -40
  484. package/dist/ads-ZOQM52YG.js +0 -128
  485. package/dist/ads-ZOYLP5R3.js +0 -113
  486. package/dist/adsets-RKSBDAHV.js +0 -176
  487. package/dist/advertisers-34GDHZC7.js +0 -125
  488. package/dist/advertisers-OEHGT3U2.js +0 -120
  489. package/dist/analytics-ILWIWU7C.js +0 -433
  490. package/dist/analytics-JYBKFGP4.js +0 -1976
  491. package/dist/animate-NVEQOLHK.js +0 -480
  492. package/dist/assign-2HJHKJKL.js +0 -77
  493. package/dist/audience-size-W74YHNBX.js +0 -94
  494. package/dist/audiences-FNDYOAAW.js +0 -85
  495. package/dist/audiences-SXBA4R3I.js +0 -78
  496. package/dist/audiences-ZUFCFB7M.js +0 -268
  497. package/dist/audit-76SDQ7C6.js +0 -151
  498. package/dist/audit-MADJMBNF.js +0 -88
  499. package/dist/autocomplete-GZ4XVGC4.js +0 -111
  500. package/dist/avatars-AILCY4T2.js +0 -38
  501. package/dist/bid-pricing-5435DI5H.js +0 -99
  502. package/dist/brand-4RWOQ7EN.js +0 -22
  503. package/dist/brief-2Y6PG6QI.js +0 -490
  504. package/dist/businesses-OFQG3YSL.js +0 -53
  505. package/dist/campaign-groups-XWQX7PSF.js +0 -95
  506. package/dist/campaign-writes-F4LVAD5N.js +0 -165
  507. package/dist/campaign-writes-GFOOQDZR.js +0 -170
  508. package/dist/campaigns-AFWP56ST.js +0 -109
  509. package/dist/campaigns-FHZLLATX.js +0 -76
  510. package/dist/campaigns-W7DMOOXW.js +0 -107
  511. package/dist/campaigns-YBOTJUV2.js +0 -110
  512. package/dist/canvas-RCRBTZKW.js +0 -46
  513. package/dist/capabilities-7DSVIC2L.js +0 -144
  514. package/dist/cards-WZSPTO4I.js +0 -85
  515. package/dist/catalog-YY33VIDD.js +0 -64
  516. package/dist/catalogs-NQX6UJ7C.js +0 -128
  517. package/dist/changes-OZ2D64FV.js +0 -135
  518. package/dist/chatgpt-33BYXDJP.js +0 -47
  519. package/dist/chats-4GAFW3QN.js +0 -262
  520. package/dist/chunk-2MTAKAHE.js +0 -509
  521. package/dist/chunk-2YXY56FK.js +0 -455
  522. package/dist/chunk-33STLGET.js +0 -67
  523. package/dist/chunk-365Z5TYR.js +0 -275
  524. package/dist/chunk-3RS4M7AR.js +0 -817
  525. package/dist/chunk-3SF5KBIO.js +0 -42
  526. package/dist/chunk-3WQ4ANUO.js +0 -84
  527. package/dist/chunk-3YNHIJCI.js +0 -75
  528. package/dist/chunk-42ESQ4AJ.js +0 -1079
  529. package/dist/chunk-4E27H5EG.js +0 -149
  530. package/dist/chunk-5B3AEQDZ.js +0 -125
  531. package/dist/chunk-5H2H77HR.js +0 -903
  532. package/dist/chunk-6DE3EHEF.js +0 -296
  533. package/dist/chunk-6QNEVETC.js +0 -46
  534. package/dist/chunk-6SOB7QMD.js +0 -172
  535. package/dist/chunk-7JFGOMJN.js +0 -363
  536. package/dist/chunk-7YOPIYCL.js +0 -92
  537. package/dist/chunk-AHQJAKAV.js +0 -544
  538. package/dist/chunk-BCKSBIQ4.js +0 -592
  539. package/dist/chunk-BEJ2AVPJ.js +0 -106
  540. package/dist/chunk-BKE32DQB.js +0 -96
  541. package/dist/chunk-CGOVXSSY.js +0 -309
  542. package/dist/chunk-CGOVXSSY.js.map +0 -1
  543. package/dist/chunk-CHZ3ZNGJ.js +0 -172
  544. package/dist/chunk-CNAPU47I.js +0 -433
  545. package/dist/chunk-D7KNUZ6Z.js +0 -1830
  546. package/dist/chunk-DRPSJE62.js +0 -216
  547. package/dist/chunk-EMDUW6QN.js +0 -206
  548. package/dist/chunk-ENY76SPM.js +0 -74
  549. package/dist/chunk-F4H6VVPE.js +0 -215
  550. package/dist/chunk-FD66LSFW.js +0 -392
  551. package/dist/chunk-FHY3BM2O.js +0 -98
  552. package/dist/chunk-FJIUMOVD.js +0 -225
  553. package/dist/chunk-FK6GBCQ4.js +0 -66
  554. package/dist/chunk-FSZEVFMI.js +0 -159
  555. package/dist/chunk-G3AOJ5FE.js +0 -35
  556. package/dist/chunk-G3UCXCC2.js +0 -455
  557. package/dist/chunk-G3UCXCC2.js.map +0 -1
  558. package/dist/chunk-H5GLPZ36.js +0 -103
  559. package/dist/chunk-HDYF3EIN.js +0 -166
  560. package/dist/chunk-IBCTXMQD.js +0 -98
  561. package/dist/chunk-IMH2ZKKC.js +0 -60
  562. package/dist/chunk-IPYG4OYK.js +0 -111
  563. package/dist/chunk-IWLWY2UD.js +0 -2122
  564. package/dist/chunk-IWLWY2UD.js.map +0 -1
  565. package/dist/chunk-IZNTYRKB.js +0 -55
  566. package/dist/chunk-J5BHSJYL.js +0 -547
  567. package/dist/chunk-JA4PDZNY.js +0 -106
  568. package/dist/chunk-JHIVRENV.js +0 -346
  569. package/dist/chunk-JLCK7K6Y.js +0 -216
  570. package/dist/chunk-JLCK7K6Y.js.map +0 -1
  571. package/dist/chunk-JQUPL6IQ.js +0 -136
  572. package/dist/chunk-K2DBAN3C.js +0 -26
  573. package/dist/chunk-KKXHGAYA.js +0 -74
  574. package/dist/chunk-KMMVQ2MR.js +0 -84
  575. package/dist/chunk-L66QWXPF.js +0 -97
  576. package/dist/chunk-LHKCZG2R.js +0 -136
  577. package/dist/chunk-LHKCZG2R.js.map +0 -1
  578. package/dist/chunk-LK64CCN4.js +0 -474
  579. package/dist/chunk-LL6T64OU.js +0 -174
  580. package/dist/chunk-LLN7NP3O.js +0 -52
  581. package/dist/chunk-LNZM6SUE.js +0 -56
  582. package/dist/chunk-M62WN522.js +0 -188
  583. package/dist/chunk-M6CI2HYA.js +0 -74
  584. package/dist/chunk-MCDU24JX.js +0 -57
  585. package/dist/chunk-MGKEUB7P.js +0 -96
  586. package/dist/chunk-MOMZOWYC.js +0 -23992
  587. package/dist/chunk-MSKB6I5E.js +0 -304
  588. package/dist/chunk-MU23X3VU.js +0 -23
  589. package/dist/chunk-MX5TREEM.js +0 -197
  590. package/dist/chunk-MZQBL7QU.js +0 -1442
  591. package/dist/chunk-NC3GOGUT.js +0 -19
  592. package/dist/chunk-NDITGFPZ.js +0 -5181
  593. package/dist/chunk-NFDAIY6J.js +0 -191
  594. package/dist/chunk-OKHSMDCY.js +0 -494
  595. package/dist/chunk-OLV66PDA.js +0 -139
  596. package/dist/chunk-OW6TNVRC.js +0 -382
  597. package/dist/chunk-OW6TNVRC.js.map +0 -1
  598. package/dist/chunk-P4UQBJNY.js +0 -486
  599. package/dist/chunk-P6SFEIA5.js +0 -3151
  600. package/dist/chunk-PHY3AERY.js +0 -6217
  601. package/dist/chunk-PHY3AERY.js.map +0 -1
  602. package/dist/chunk-RCWEJUSF.js +0 -337
  603. package/dist/chunk-RDQI3YD2.js +0 -66
  604. package/dist/chunk-RMB2MIOJ.js +0 -94
  605. package/dist/chunk-RV222F63.js +0 -51
  606. package/dist/chunk-SKYXCMVX.js +0 -846
  607. package/dist/chunk-SS445HDO.js +0 -25
  608. package/dist/chunk-TJ5N6IVL.js +0 -665
  609. package/dist/chunk-TLOFNP4Q.js +0 -45
  610. package/dist/chunk-TUVS5SPF.js +0 -133
  611. package/dist/chunk-TVKE3OXR.js +0 -76
  612. package/dist/chunk-UNHCWWAI.js +0 -111
  613. package/dist/chunk-V4H6Q4TB.js +0 -1599
  614. package/dist/chunk-VPBCR6DT.js +0 -1743
  615. package/dist/chunk-VTK2W2NI.js +0 -160
  616. package/dist/chunk-WBCQEXEH.js +0 -76
  617. package/dist/chunk-WJOQ6IEK.js +0 -4408
  618. package/dist/chunk-WOLE67OJ.js +0 -6667
  619. package/dist/chunk-WSD63XGR.js +0 -169
  620. package/dist/chunk-X3PVW3RM.js +0 -610
  621. package/dist/chunk-XA2S53QZ.js +0 -274
  622. package/dist/chunk-XLZMKMKD.js +0 -333
  623. package/dist/chunk-YDAEGV42.js +0 -297
  624. package/dist/chunk-Z2HNETUP.js +0 -58
  625. package/dist/chunk-ZZS6IUOY.js +0 -175
  626. package/dist/claim-XBVKGN7A.js +0 -197
  627. package/dist/client-2ABVGUQB.js +0 -19
  628. package/dist/code-GHG7DEPV.js +0 -76
  629. package/dist/companies-IGDIQ64M.js +0 -195
  630. package/dist/complete-CYPVVQWT.js +0 -81
  631. package/dist/config-L26WNV2G.js +0 -88
  632. package/dist/connect-FVP4HT6T.js +0 -42
  633. package/dist/content-2SL3VHIK.js +0 -85
  634. package/dist/conversation-DDZKRMCI.js +0 -93
  635. package/dist/conversion-report-VARF3FYY.js +0 -125
  636. package/dist/conversion-upload-command-ZWSNQFGS.js +0 -438
  637. package/dist/conversion-writes-7SXCEVCV.js +0 -120
  638. package/dist/conversions-5S5IMXGM.js +0 -135
  639. package/dist/conversions-ZCOIEPNI.js +0 -76
  640. package/dist/countries-D5JPHJ4F.js +0 -82
  641. package/dist/create-3WWCI3KY.js +0 -235
  642. package/dist/create-GLPBZEQO.js +0 -59
  643. package/dist/create-GNVYQPBW.js +0 -126
  644. package/dist/create-GNVYQPBW.js.map +0 -1
  645. package/dist/create-VHZWE4JQ.js +0 -207
  646. package/dist/creatives-5C4PRGFT.js +0 -87
  647. package/dist/creatives-7VEIUD3G.js +0 -182
  648. package/dist/creatives-CW3FDOPI.js +0 -51
  649. package/dist/credentials-2QHNU3SD.js +0 -287
  650. package/dist/critique-HU7ZONQ3.js +0 -213
  651. package/dist/critique-ZYSNLKNA.js +0 -1449
  652. package/dist/crm-BD4RK26E.js +0 -318
  653. package/dist/crm-LYRBIQOO.js +0 -224
  654. package/dist/crop-KR3LOQBX.js +0 -113
  655. package/dist/currency-EQ6PB7VF.js +0 -88
  656. package/dist/custom-conversions-YCOWWTQ7.js +0 -125
  657. package/dist/customers-MO6ORQAN.js +0 -56
  658. package/dist/decide-DZG2JUMG.js +0 -199
  659. package/dist/delete-FIDIBB2H.js +0 -69
  660. package/dist/delete-NHN73V54.js +0 -66
  661. package/dist/delete-O3ZTHK3M.js +0 -63
  662. package/dist/delete-O3ZTHK3M.js.map +0 -1
  663. package/dist/delete-OHTGL7ZJ.js +0 -71
  664. package/dist/delete-YKR5Y7EQ.js +0 -71
  665. package/dist/delete-YY6YJ3A6.js +0 -54
  666. package/dist/demographics-YU75YZFO.js +0 -138
  667. package/dist/describe-I7VNACXY.js +0 -97
  668. package/dist/describe-NAINLKWG.js +0 -97
  669. package/dist/dimensions-K6MDXS4B.js +0 -101
  670. package/dist/discard-DGK7PLWF.js +0 -66
  671. package/dist/discover-2UKANLTP.js +0 -172
  672. package/dist/discover-E7SVCXZ2.js +0 -93
  673. package/dist/domain-6NZKJW4W.js +0 -98
  674. package/dist/download-5FCLTMLA.js +0 -299
  675. package/dist/draft-67OFDA53.js +0 -306
  676. package/dist/draft-I7H6FVZT.js +0 -281
  677. package/dist/draft-NJNAGVVV.js +0 -103
  678. package/dist/draft-QSO3DQG6.js +0 -233
  679. package/dist/draft-RJ5HOJIR.js +0 -217
  680. package/dist/draft-RPCIHVRS.js +0 -307
  681. package/dist/draft-UNCGJCUN.js +0 -100
  682. package/dist/draft-W5BXN7H3.js +0 -328
  683. package/dist/env-FKVPPL35.js +0 -43
  684. package/dist/experiment-JF2QTPRV.js +0 -1397
  685. package/dist/export-3ROTRKFE.js +0 -168
  686. package/dist/extract-VXGXR5RD.js +0 -113
  687. package/dist/facets-3ZIRFE6P.js +0 -114
  688. package/dist/favorites-DI2EY34L.js +0 -178
  689. package/dist/feed-EH4FENF4.js +0 -177
  690. package/dist/feedback-Y7MDEA5O.js +0 -66
  691. package/dist/feedback-record-SLU5RROR.js +0 -175
  692. package/dist/fetch-LUOENFIX.js +0 -176
  693. package/dist/fetch-V3BFDMZF.js +0 -139
  694. package/dist/find-T7XJV535.js +0 -134
  695. package/dist/fit-VKSQTJ3Q.js +0 -45
  696. package/dist/flows-JVU42DAA.js +0 -247
  697. package/dist/follow-PIEAXZE7.js +0 -94
  698. package/dist/follow-competitors-7N2TPSLA.js +0 -112
  699. package/dist/following-74CI7UC6.js +0 -113
  700. package/dist/fonts-BTOLDILR.js +0 -34
  701. package/dist/forecast-35EBPP2K.js +0 -104
  702. package/dist/forecast-MHGSW73Z.js +0 -168
  703. package/dist/funding-FTWMCGZ7.js +0 -85
  704. package/dist/funnel-YH5LOG65.js +0 -394
  705. package/dist/ga4-T4ZHANT2.js +0 -55
  706. package/dist/generate-KVQLIBAX.js +0 -263
  707. package/dist/get-2IKPCXEM.js +0 -59
  708. package/dist/get-ABPBK4P3.js +0 -63
  709. package/dist/get-GSLIPHMD.js +0 -78
  710. package/dist/get-GSLIPHMD.js.map +0 -1
  711. package/dist/get-NE3EEHN6.js +0 -92
  712. package/dist/get-QOACNGXP.js +0 -56
  713. package/dist/get-V4BZ4NTK.js +0 -63
  714. package/dist/get-WZVNWFQA.js +0 -61
  715. package/dist/gif-O3FLM3CF.js +0 -94
  716. package/dist/google-I2Z7QOHV.js +0 -3190
  717. package/dist/google-Z5UAFS3C.js +0 -136
  718. package/dist/google-ads-E6VNIUEK.js +0 -195
  719. package/dist/group-34IDEULM.js +0 -43
  720. package/dist/group-HJMQ5H3L.js +0 -43
  721. package/dist/gsc-LDZKY5FW.js +0 -30
  722. package/dist/history-6TYCUXG2.js +0 -133
  723. package/dist/history-EBJTST3V.js +0 -67
  724. package/dist/hubspot-LO7ZJPNO.js +0 -779
  725. package/dist/icon-37BBWKJO.js +0 -137
  726. package/dist/ideas-2MSUALCW.js +0 -32
  727. package/dist/images-TZKGJVQ2.js +0 -90
  728. package/dist/improve-6J2UJ4JZ.js +0 -86
  729. package/dist/ingest-3RQSRCO5.js +0 -23
  730. package/dist/ingest-65T56KUI.js +0 -107
  731. package/dist/insights-5IBNZFHT.js +0 -423
  732. package/dist/insights-Y7V2UL7E.js +0 -122
  733. package/dist/inspect-QLFYFQU2.js +0 -189
  734. package/dist/inspiration-LYCDPQEY.js +0 -53
  735. package/dist/intent-PFN2OQEE.js +0 -95
  736. package/dist/job-ZEI4FC5Y.js +0 -202
  737. package/dist/job-create-QVTI3TP6.js +0 -109
  738. package/dist/job-status-GVL7X6EY.js +0 -74
  739. package/dist/keep-GD5LZFGG.js +0 -78
  740. package/dist/keyword-gap-WPVHYPVP.js +0 -130
  741. package/dist/keyword-metrics-Y5LWCCUJ.js +0 -197
  742. package/dist/keywords-for-site-HGIX4S4F.js +0 -128
  743. package/dist/landing-EP23XIYL.js +0 -28
  744. package/dist/landings-4YBHALDA.js +0 -151
  745. package/dist/languages-5WPQX3DS.js +0 -40
  746. package/dist/languages-KRXEHLM4.js +0 -55
  747. package/dist/launches-FOJQPCD2.js +0 -19
  748. package/dist/layerize-M6ZRXR7M.js +0 -190
  749. package/dist/leads-2VKYMGP4.js +0 -90
  750. package/dist/leads-NV57FPKX.js +0 -293
  751. package/dist/learn-6W55U4ZG.js +0 -203
  752. package/dist/library-HC5US7CE.js +0 -109
  753. package/dist/lighthouse-DXZIQLLK.js +0 -75
  754. package/dist/line-item-writes-LYWNJWZ2.js +0 -208
  755. package/dist/line-items-Q47IZGAD.js +0 -103
  756. package/dist/link-4CQUXZPK.js +0 -64
  757. package/dist/linkedin-TQCPNOIL.js +0 -1008
  758. package/dist/linkedin-TQCPNOIL.js.map +0 -1
  759. package/dist/list-3RZZFCIR.js +0 -151
  760. package/dist/list-IZNAL5LS.js +0 -57
  761. package/dist/list-JHNUOEG6.js +0 -63
  762. package/dist/list-JHNUOEG6.js.map +0 -1
  763. package/dist/list-LC7BZRD4.js +0 -65
  764. package/dist/list-MEGAZDWZ.js +0 -51
  765. package/dist/list-RCOUIFKY.js +0 -303
  766. package/dist/list-RGMBFJHB.js +0 -76
  767. package/dist/list-VXNYIVS5.js +0 -50
  768. package/dist/locations-CB6KK3ZU.js +0 -40
  769. package/dist/locations-ZMJ5MJ6Z.js +0 -67
  770. package/dist/log-5V5YMZGV.js +0 -92
  771. package/dist/logo-SOCGJLY5.js +0 -18
  772. package/dist/lookup-MQBDRZZT.js +0 -22
  773. package/dist/make-M5LD3MX2.js +0 -633
  774. package/dist/map-GD2BT7RO.js +0 -433
  775. package/dist/mcp-WFRPWK3W.js +0 -463
  776. package/dist/media-OYAN23YG.js +0 -15
  777. package/dist/media-QCWYLIO2.js +0 -89
  778. package/dist/media-sources-YQURN7ZH.js +0 -32
  779. package/dist/members-4UCL6OKS.js +0 -44
  780. package/dist/meta-B5XDM6F6.js +0 -69
  781. package/dist/metrics-W4SLBSPD.js +0 -152
  782. package/dist/models-IUO3XGQK.js +0 -155
  783. package/dist/normalize-2OUEDA6D.js +0 -806
  784. package/dist/normalize-O6ATWOWP.js +0 -141
  785. package/dist/options-U4PY24PS.js +0 -122
  786. package/dist/output-ZKNCKP7T.js +0 -24
  787. package/dist/page-EMJESGQZ.js +0 -105
  788. package/dist/pages-OMKAYKCF.js +0 -66
  789. package/dist/performance-WGCN2XIC.js +0 -24
  790. package/dist/pinterest-I6JO7LXL.js +0 -96
  791. package/dist/pixels-5JBTFWCO.js +0 -108
  792. package/dist/pixels-G64Y3J22.js +0 -60
  793. package/dist/plan-KNUKCIS7.js +0 -207
  794. package/dist/preview-TBINZJ2C.js +0 -169
  795. package/dist/profile-UV3FTEUC.js +0 -159
  796. package/dist/promoted-tweet-writes-YVSGOBAS.js +0 -180
  797. package/dist/promoted-tweets-DQDEHTP4.js +0 -119
  798. package/dist/properties-CTFQ7IK3.js +0 -101
  799. package/dist/publish-I2XOKEHC.js +0 -322
  800. package/dist/query-JMVLWKVY.js +0 -236
  801. package/dist/query-L3TGHKKM.js +0 -61
  802. package/dist/query-T5IBVQ3B.js +0 -247
  803. package/dist/questionnaire-APZ6FMPE.js +0 -93
  804. package/dist/read-W5ZK5G7Q.js +0 -141
  805. package/dist/reddit-GYVC7XPJ.js +0 -26
  806. package/dist/refresh-XYZ2Q2SN.js +0 -94
  807. package/dist/release-HOSW3VHR.js +0 -60
  808. package/dist/relevant-pages-F5AGXYC5.js +0 -99
  809. package/dist/remove-7QYX2OYJ.js +0 -50
  810. package/dist/remove-RFOFNOYU.js +0 -48
  811. package/dist/reopen-HIVOKJOR.js +0 -63
  812. package/dist/rerun-U6F4TEPA.js +0 -84
  813. package/dist/research-43V2DWVK.js +0 -62
  814. package/dist/research-7TN24S4D.js +0 -262
  815. package/dist/reset-B3FAY2EH.js +0 -54
  816. package/dist/reset-R5RM7ZTG.js +0 -54
  817. package/dist/resolve-I5YOKTRV.js +0 -121
  818. package/dist/retry-A335Q6HS.js +0 -62
  819. package/dist/retry-R4OWR7IK.js +0 -60
  820. package/dist/retry-failed-5XDCVPQA.js +0 -45
  821. package/dist/run-TNO2VF2U.js +0 -59
  822. package/dist/run-VZGXOWFL.js +0 -65
  823. package/dist/run-VZGXOWFL.js.map +0 -1
  824. package/dist/scaffold-ad-VKOXZENI.js +0 -1548
  825. package/dist/scaffold-static-ad-CMYP2ZZ6.js +0 -971
  826. package/dist/scaffold-video-AKA5LMEI.js +0 -872
  827. package/dist/schedule-4N6JTJJT.js +0 -66
  828. package/dist/schedule-HKIY6JI2.js +0 -61
  829. package/dist/scheduled-agents-QM5Z3UNW.js +0 -50
  830. package/dist/schema-J6AIGXZ7.js +0 -132
  831. package/dist/schema-RNVEYR2U.js +0 -63
  832. package/dist/schema-RNVEYR2U.js.map +0 -1
  833. package/dist/scrape-VK7RZO4M.js +0 -2241
  834. package/dist/screenshot-PWUROUXU.js +0 -102
  835. package/dist/search-3ZW3RRPX.js +0 -92
  836. package/dist/search-J2VQNY4A.js +0 -91
  837. package/dist/search-LRY5SYDM.js +0 -224
  838. package/dist/search-M23XX3N5.js +0 -26
  839. package/dist/search-PK4IUZ4C.js +0 -174
  840. package/dist/search-copy-AVQESTYJ.js +0 -168
  841. package/dist/seeds-C6JI3WTG.js +0 -101
  842. package/dist/select-NBRJ6WLY.js +0 -94
  843. package/dist/sends-DYIE7W5U.js +0 -309
  844. package/dist/sequences-CQVKVRC4.js +0 -146
  845. package/dist/set-prompt-VNJ334YW.js +0 -185
  846. package/dist/setup-links-77534G7K.js +0 -141
  847. package/dist/shared-2SQRS476.js +0 -31
  848. package/dist/site-EFQ2QFZX.js +0 -177
  849. package/dist/sitemaps-XFLHKP2D.js +0 -87
  850. package/dist/sites-5JNZWY3A.js +0 -101
  851. package/dist/skills-43P4WK6Y.js +0 -47
  852. package/dist/sources-JQM4AKNL.js +0 -30
  853. package/dist/sources-WJ3EZ46E.js +0 -454
  854. package/dist/stale-Y5HC7BNS.js +0 -63
  855. package/dist/stats-LLTHL5VR.js +0 -34
  856. package/dist/status-T25VZANV.js +0 -62
  857. package/dist/sticker-B5DJ56YG.js +0 -94
  858. package/dist/stock-WXAFJFAC.js +0 -179
  859. package/dist/studio-X546AIF2.js +0 -49
  860. package/dist/sync-GCVG6QTL.js +0 -54
  861. package/dist/sync-JLECDZYS.js +0 -265
  862. package/dist/sync-VDPGFUZS.js +0 -54
  863. package/dist/sync-definition-GKF2KKFC.js +0 -54
  864. package/dist/tag-manager-HG76EBNX.js +0 -43
  865. package/dist/tags-6KXDDSBW.js +0 -20
  866. package/dist/tags-NFBMGARL.js +0 -20
  867. package/dist/tags-SJIXKMQH.js +0 -20
  868. package/dist/tags-T4OK7QXX.js +0 -26
  869. package/dist/tags-UE2S4UCB.js +0 -224
  870. package/dist/targeting-constants-GR7RUKE3.js +0 -118
  871. package/dist/targeting-criteria-NZVF6ZOK.js +0 -113
  872. package/dist/targets-4II2CNRP.js +0 -102
  873. package/dist/targets-4II2CNRP.js.map +0 -1
  874. package/dist/testimonials-NODH7BWK.js +0 -32
  875. package/dist/themes-NO3DM5M3.js +0 -94
  876. package/dist/top-companies-MWOOBUSM.js +0 -108
  877. package/dist/translate-26KMJSWL.js +0 -598
  878. package/dist/triggers-LYPP4FKZ.js +0 -141
  879. package/dist/triggers-LYPP4FKZ.js.map +0 -1
  880. package/dist/tweets-ZWC7YOQ4.js +0 -123
  881. package/dist/unfollow-LBMJXF4E.js +0 -46
  882. package/dist/unlink-OL7UNUYB.js +0 -67
  883. package/dist/unstage-5AZAD2KF.js +0 -95
  884. package/dist/unstage-6GUUIEWL.js +0 -84
  885. package/dist/unstage-6GUUIEWL.js.map +0 -1
  886. package/dist/update-2YZVDSWQ.js +0 -142
  887. package/dist/update-2YZVDSWQ.js.map +0 -1
  888. package/dist/update-HSMB6JMZ.js +0 -155
  889. package/dist/update-PCVBEGSU.js +0 -98
  890. package/dist/update-QWWAXFWH.js +0 -63
  891. package/dist/upload-MPMP3JRS.js +0 -150
  892. package/dist/upload-VBPWOAKD.js +0 -126
  893. package/dist/upscale-TXJFZLUU.js +0 -109
  894. package/dist/url-options-ML2D74NM.js +0 -130
  895. package/dist/url-params-4AD467SA.js +0 -98
  896. package/dist/use-SUY57ZJA.js +0 -95
  897. package/dist/validate-W4QZZFXV.js +0 -88
  898. package/dist/variant-TE6CEHZW.js +0 -338
  899. package/dist/videos-DJHFHZI5.js +0 -39
  900. package/dist/view-EQZB7Z6N.js +0 -121
  901. package/dist/viewer-check-GL2UNAGR.js +0 -68
  902. package/dist/web-N4RNU7UH.js +0 -140
  903. package/dist/winners-UPM6DFRB.js +0 -113
  904. package/dist/winning-ads-SVUUR443.js +0 -84
  905. package/dist/write-commands-5HSNHURZ.js +0 -713
  906. package/dist/write-commands-BCU5YATY.js +0 -737
  907. package/dist/write-commands-WVJJJFBP.js +0 -1015
  908. package/dist/write-commands-YNI6KS2B.js +0 -206
  909. package/dist/x-65R63NFK.js +0 -47
  910. /package/dist/{account-MLAW7QO4.js.map → account-63NUWSXK.js.map} +0 -0
  911. /package/dist/{account-UP67V2FH.js.map → account-IFB6V6GL.js.map} +0 -0
  912. /package/dist/{account-QR5LCE74.js.map → account-NCX3VHZT.js.map} +0 -0
  913. /package/dist/{account-reads-5RGYKIIJ.js.map → account-reads-Y3T65EI4.js.map} +0 -0
  914. /package/dist/{account-writes-EGRMVWVQ.js.map → account-writes-7YZ7PTHF.js.map} +0 -0
  915. /package/dist/{accounts-LG4B7NWO.js.map → accounts-2MQVGI4I.js.map} +0 -0
  916. /package/dist/{accounts-WH7SRFCB.js.map → accounts-66KZC7QI.js.map} +0 -0
  917. /package/dist/{accounts-L2SNBNOO.js.map → accounts-MQIYTIAX.js.map} +0 -0
  918. /package/dist/{accounts-CU4Q2AV7.js.map → accounts-MVEZ2TRL.js.map} +0 -0
  919. /package/dist/{accounts-GUHRV3RA.js.map → accounts-T5DCHHZK.js.map} +0 -0
  920. /package/dist/{actions-LMKQCN4E.js.map → actions-LRSHHDV2.js.map} +0 -0
  921. /package/dist/{active-entities-2CQHGACO.js.map → active-entities-QWPGPNGE.js.map} +0 -0
  922. /package/dist/{activecampaign-RZEJ25GO.js.map → activecampaign-G4XARU6N.js.map} +0 -0
  923. /package/dist/{activities-EDCSBYGM.js.map → activities-FN7DOE5S.js.map} +0 -0
  924. /package/dist/{ad-group-writes-WUAYGWDY.js.map → ad-group-writes-JSOCSYYV.js.map} +0 -0
  925. /package/dist/{ad-groups-BVEJJHX6.js.map → ad-groups-UN52WNJN.js.map} +0 -0
  926. /package/dist/{ad-writes-WTWRSFLW.js.map → ad-writes-X23H4HLZ.js.map} +0 -0
  927. /package/dist/{add-HQERVTGX.js.map → add-2MVZQJYG.js.map} +0 -0
  928. /package/dist/{add-PGD6PNXH.js.map → add-7EKI6ZA4.js.map} +0 -0
  929. /package/dist/{add-DDKP3OOS.js.map → add-ZP47HVHW.js.map} +0 -0
  930. /package/dist/{add-ZMZJSBQT.js.map → add-ZTXN775K.js.map} +0 -0
  931. /package/dist/{add-accounts-A2YRT5NR.js.map → add-accounts-PEJLMYWR.js.map} +0 -0
  932. /package/dist/{ads-WB2X5SYO.js.map → ads-7YYCWZIR.js.map} +0 -0
  933. /package/dist/{ads-ZOQM52YG.js.map → ads-FOJ6P4OA.js.map} +0 -0
  934. /package/dist/{ads-ZOYLP5R3.js.map → ads-MKMWS36P.js.map} +0 -0
  935. /package/dist/{ads-HRCVRXPO.js.map → ads-NEJKXB4L.js.map} +0 -0
  936. /package/dist/{adsets-RKSBDAHV.js.map → adsets-TIHFYWOU.js.map} +0 -0
  937. /package/dist/{advertisers-OEHGT3U2.js.map → advertisers-246ZLRFZ.js.map} +0 -0
  938. /package/dist/{advertisers-34GDHZC7.js.map → advertisers-E77XAYCQ.js.map} +0 -0
  939. /package/dist/{analytics-ILWIWU7C.js.map → analytics-SL56NTIV.js.map} +0 -0
  940. /package/dist/{analytics-JYBKFGP4.js.map → analytics-WPYPKM6L.js.map} +0 -0
  941. /package/dist/{animate-NVEQOLHK.js.map → animate-3H3TL3N6.js.map} +0 -0
  942. /package/dist/{assign-2HJHKJKL.js.map → assign-WGQVY3DW.js.map} +0 -0
  943. /package/dist/{audience-size-W74YHNBX.js.map → audience-size-C37XJ6GS.js.map} +0 -0
  944. /package/dist/{audiences-ZUFCFB7M.js.map → audiences-BWDG2R2U.js.map} +0 -0
  945. /package/dist/{audiences-FNDYOAAW.js.map → audiences-GHMNXDPR.js.map} +0 -0
  946. /package/dist/{audiences-SXBA4R3I.js.map → audiences-Q7OCAMAQ.js.map} +0 -0
  947. /package/dist/{audit-MADJMBNF.js.map → audit-HG53XQJK.js.map} +0 -0
  948. /package/dist/{audit-76SDQ7C6.js.map → audit-LGD4PMAI.js.map} +0 -0
  949. /package/dist/{autocomplete-GZ4XVGC4.js.map → autocomplete-PD2EMWRU.js.map} +0 -0
  950. /package/dist/{avatars-AILCY4T2.js.map → avatars-U5KAMVNV.js.map} +0 -0
  951. /package/dist/{bid-pricing-5435DI5H.js.map → bid-pricing-PP6PV6S4.js.map} +0 -0
  952. /package/dist/{brand-4RWOQ7EN.js.map → brand-2JJ45TBO.js.map} +0 -0
  953. /package/dist/{brief-2Y6PG6QI.js.map → brief-IMTLJQN2.js.map} +0 -0
  954. /package/dist/{businesses-OFQG3YSL.js.map → businesses-24QYU6BL.js.map} +0 -0
  955. /package/dist/{campaign-groups-XWQX7PSF.js.map → campaign-groups-ZXRHEEGS.js.map} +0 -0
  956. /package/dist/{campaign-writes-GFOOQDZR.js.map → campaign-writes-7UHMSPJ3.js.map} +0 -0
  957. /package/dist/{campaign-writes-F4LVAD5N.js.map → campaign-writes-WIBYW73K.js.map} +0 -0
  958. /package/dist/{campaigns-W7DMOOXW.js.map → campaigns-CRRLTN74.js.map} +0 -0
  959. /package/dist/{campaigns-FHZLLATX.js.map → campaigns-DJV4W6UP.js.map} +0 -0
  960. /package/dist/{campaigns-AFWP56ST.js.map → campaigns-EY3AR4N7.js.map} +0 -0
  961. /package/dist/{campaigns-YBOTJUV2.js.map → campaigns-WTZBLGVX.js.map} +0 -0
  962. /package/dist/{canvas-RCRBTZKW.js.map → canvas-WFYKZYZD.js.map} +0 -0
  963. /package/dist/{capabilities-7DSVIC2L.js.map → capabilities-IGKT7D4H.js.map} +0 -0
  964. /package/dist/{cards-WZSPTO4I.js.map → cards-IO2Q423H.js.map} +0 -0
  965. /package/dist/{catalog-YY33VIDD.js.map → catalog-X3VJFC74.js.map} +0 -0
  966. /package/dist/{catalogs-NQX6UJ7C.js.map → catalogs-2HT3VRRI.js.map} +0 -0
  967. /package/dist/{changes-OZ2D64FV.js.map → changes-AZ6EUVFN.js.map} +0 -0
  968. /package/dist/{chatgpt-33BYXDJP.js.map → chatgpt-M27OEM7J.js.map} +0 -0
  969. /package/dist/{chats-4GAFW3QN.js.map → chats-OXIYXI4U.js.map} +0 -0
  970. /package/dist/{chunk-HDYF3EIN.js.map → chunk-26HCUIW2.js.map} +0 -0
  971. /package/dist/{chunk-3RS4M7AR.js.map → chunk-2SDC7WNS.js.map} +0 -0
  972. /package/dist/{chunk-FD66LSFW.js.map → chunk-2SQ6ISRI.js.map} +0 -0
  973. /package/dist/{chunk-IMH2ZKKC.js.map → chunk-2VRD3HSQ.js.map} +0 -0
  974. /package/dist/{chunk-2YXY56FK.js.map → chunk-37P6DTDJ.js.map} +0 -0
  975. /package/dist/{chunk-WOLE67OJ.js.map → chunk-3IGY2FWK.js.map} +0 -0
  976. /package/dist/{chunk-TVKE3OXR.js.map → chunk-4EPGI6I2.js.map} +0 -0
  977. /package/dist/{chunk-CNAPU47I.js.map → chunk-4LODOBIN.js.map} +0 -0
  978. /package/dist/{chunk-M62WN522.js.map → chunk-5ADTL2LR.js.map} +0 -0
  979. /package/dist/{chunk-OLV66PDA.js.map → chunk-5HABWBQX.js.map} +0 -0
  980. /package/dist/{chunk-D7KNUZ6Z.js.map → chunk-5TV4GYP2.js.map} +0 -0
  981. /package/dist/{chunk-LK64CCN4.js.map → chunk-6M6HUJ55.js.map} +0 -0
  982. /package/dist/{chunk-M6CI2HYA.js.map → chunk-6YZNHMPJ.js.map} +0 -0
  983. /package/dist/{chunk-TUVS5SPF.js.map → chunk-7FKRKMGL.js.map} +0 -0
  984. /package/dist/{chunk-FJIUMOVD.js.map → chunk-7WZHO6W3.js.map} +0 -0
  985. /package/dist/{chunk-JQUPL6IQ.js.map → chunk-ANWV2VTO.js.map} +0 -0
  986. /package/dist/{chunk-AHQJAKAV.js.map → chunk-AQYH6C32.js.map} +0 -0
  987. /package/dist/{chunk-UNHCWWAI.js.map → chunk-BODRDUYQ.js.map} +0 -0
  988. /package/dist/{chunk-XA2S53QZ.js.map → chunk-BPPT7YVK.js.map} +0 -0
  989. /package/dist/{chunk-RMB2MIOJ.js.map → chunk-BWAFZSZ4.js.map} +0 -0
  990. /package/dist/{chunk-NFDAIY6J.js.map → chunk-CIWI7LHO.js.map} +0 -0
  991. /package/dist/{chunk-K2DBAN3C.js.map → chunk-CLST7PLN.js.map} +0 -0
  992. /package/dist/{chunk-P4UQBJNY.js.map → chunk-CMNVR6DX.js.map} +0 -0
  993. /package/dist/{chunk-6DE3EHEF.js.map → chunk-CPTOHQSJ.js.map} +0 -0
  994. /package/dist/{chunk-42ESQ4AJ.js.map → chunk-CU2DEMQB.js.map} +0 -0
  995. /package/dist/{chunk-RDQI3YD2.js.map → chunk-D5GVSJBK.js.map} +0 -0
  996. /package/dist/{chunk-YDAEGV42.js.map → chunk-D5Z34AYL.js.map} +0 -0
  997. /package/dist/{chunk-ENY76SPM.js.map → chunk-EBC64WAG.js.map} +0 -0
  998. /package/dist/{chunk-IBCTXMQD.js.map → chunk-EQ74MEJN.js.map} +0 -0
  999. /package/dist/{chunk-KKXHGAYA.js.map → chunk-ETFAIX4L.js.map} +0 -0
  1000. /package/dist/{chunk-5H2H77HR.js.map → chunk-F3VR6CTH.js.map} +0 -0
  1001. /package/dist/{chunk-SS445HDO.js.map → chunk-FDPBTKXZ.js.map} +0 -0
  1002. /package/dist/{chunk-MZQBL7QU.js.map → chunk-GAD2NYL4.js.map} +0 -0
  1003. /package/dist/{chunk-WJOQ6IEK.js.map → chunk-GRL25RKS.js.map} +0 -0
  1004. /package/dist/{chunk-H5GLPZ36.js.map → chunk-GRYP7Y5V.js.map} +0 -0
  1005. /package/dist/{chunk-RV222F63.js.map → chunk-H4DAEWJQ.js.map} +0 -0
  1006. /package/dist/{chunk-TLOFNP4Q.js.map → chunk-HDQBILJR.js.map} +0 -0
  1007. /package/dist/{chunk-KMMVQ2MR.js.map → chunk-HH5PFZ7U.js.map} +0 -0
  1008. /package/dist/{chunk-3SF5KBIO.js.map → chunk-HKR7VCFK.js.map} +0 -0
  1009. /package/dist/{chunk-MX5TREEM.js.map → chunk-JQ4PQ2H6.js.map} +0 -0
  1010. /package/dist/{chunk-XLZMKMKD.js.map → chunk-JRLGQA2T.js.map} +0 -0
  1011. /package/dist/{chunk-MU23X3VU.js.map → chunk-JU6X4XFU.js.map} +0 -0
  1012. /package/dist/{chunk-F4H6VVPE.js.map → chunk-KODTY7PB.js.map} +0 -0
  1013. /package/dist/{chunk-LNZM6SUE.js.map → chunk-LEGVJCL4.js.map} +0 -0
  1014. /package/dist/{chunk-7JFGOMJN.js.map → chunk-LKM3FMYC.js.map} +0 -0
  1015. /package/dist/{chunk-BEJ2AVPJ.js.map → chunk-LOTQBFUP.js.map} +0 -0
  1016. /package/dist/{chunk-JA4PDZNY.js.map → chunk-LPBLKGH7.js.map} +0 -0
  1017. /package/dist/{chunk-JHIVRENV.js.map → chunk-MICLDQES.js.map} +0 -0
  1018. /package/dist/{chunk-G3AOJ5FE.js.map → chunk-MUHPBWE5.js.map} +0 -0
  1019. /package/dist/{chunk-WSD63XGR.js.map → chunk-NHQTEQPR.js.map} +0 -0
  1020. /package/dist/{chunk-VPBCR6DT.js.map → chunk-NKXH3I65.js.map} +0 -0
  1021. /package/dist/{chunk-IZNTYRKB.js.map → chunk-NRRNY3TZ.js.map} +0 -0
  1022. /package/dist/{chunk-IPYG4OYK.js.map → chunk-O5J5T4ZL.js.map} +0 -0
  1023. /package/dist/{chunk-RCWEJUSF.js.map → chunk-O6TGA5HT.js.map} +0 -0
  1024. /package/dist/{chunk-LLN7NP3O.js.map → chunk-O6VUX3O5.js.map} +0 -0
  1025. /package/dist/{chunk-X3PVW3RM.js.map → chunk-OST3RA6Q.js.map} +0 -0
  1026. /package/dist/{chunk-P6SFEIA5.js.map → chunk-PCSBF6GN.js.map} +0 -0
  1027. /package/dist/{chunk-2MTAKAHE.js.map → chunk-PLK7V2SJ.js.map} +0 -0
  1028. /package/dist/{chunk-CHZ3ZNGJ.js.map → chunk-PWABS2DB.js.map} +0 -0
  1029. /package/dist/{chunk-J5BHSJYL.js.map → chunk-PXTFT35U.js.map} +0 -0
  1030. /package/dist/{chunk-MOMZOWYC.js.map → chunk-QBCPZFAV.js.map} +0 -0
  1031. /package/dist/{chunk-BKE32DQB.js.map → chunk-QEZEXMRB.js.map} +0 -0
  1032. /package/dist/{chunk-MSKB6I5E.js.map → chunk-QGSV3IWT.js.map} +0 -0
  1033. /package/dist/{chunk-V4H6Q4TB.js.map → chunk-QGWCBGEC.js.map} +0 -0
  1034. /package/dist/{chunk-L66QWXPF.js.map → chunk-QKUBK3AD.js.map} +0 -0
  1035. /package/dist/{chunk-33STLGET.js.map → chunk-QNH5NCLM.js.map} +0 -0
  1036. /package/dist/{chunk-Z2HNETUP.js.map → chunk-QNLWBJUR.js.map} +0 -0
  1037. /package/dist/{chunk-6SOB7QMD.js.map → chunk-R4CDO5YH.js.map} +0 -0
  1038. /package/dist/{chunk-NC3GOGUT.js.map → chunk-RCMK4FM2.js.map} +0 -0
  1039. /package/dist/{chunk-WBCQEXEH.js.map → chunk-RJFM6TJC.js.map} +0 -0
  1040. /package/dist/{chunk-FHY3BM2O.js.map → chunk-RKPSE27R.js.map} +0 -0
  1041. /package/dist/{chunk-MGKEUB7P.js.map → chunk-S6ODQYTC.js.map} +0 -0
  1042. /package/dist/{chunk-TJ5N6IVL.js.map → chunk-SBN5UIQY.js.map} +0 -0
  1043. /package/dist/{chunk-BCKSBIQ4.js.map → chunk-SOK7U4NT.js.map} +0 -0
  1044. /package/dist/{chunk-FSZEVFMI.js.map → chunk-TLQQV5AY.js.map} +0 -0
  1045. /package/dist/{chunk-MCDU24JX.js.map → chunk-TOIFDXCW.js.map} +0 -0
  1046. /package/dist/{chunk-4E27H5EG.js.map → chunk-TWXM7HVR.js.map} +0 -0
  1047. /package/dist/{chunk-SKYXCMVX.js.map → chunk-UARL36OD.js.map} +0 -0
  1048. /package/dist/{chunk-LL6T64OU.js.map → chunk-UDG37PC2.js.map} +0 -0
  1049. /package/dist/{chunk-365Z5TYR.js.map → chunk-UMFKMS4U.js.map} +0 -0
  1050. /package/dist/{chunk-3YNHIJCI.js.map → chunk-UW7PZFWZ.js.map} +0 -0
  1051. /package/dist/{chunk-VTK2W2NI.js.map → chunk-UWTAQM4W.js.map} +0 -0
  1052. /package/dist/{chunk-5B3AEQDZ.js.map → chunk-VDAYKWVH.js.map} +0 -0
  1053. /package/dist/{chunk-6QNEVETC.js.map → chunk-VY5CTURR.js.map} +0 -0
  1054. /package/dist/{chunk-OKHSMDCY.js.map → chunk-WE6K2VVZ.js.map} +0 -0
  1055. /package/dist/{chunk-3WQ4ANUO.js.map → chunk-WOTX7TNV.js.map} +0 -0
  1056. /package/dist/{chunk-EMDUW6QN.js.map → chunk-XKA4ZHGX.js.map} +0 -0
  1057. /package/dist/{chunk-NDITGFPZ.js.map → chunk-XO3GZLWQ.js.map} +0 -0
  1058. /package/dist/{chunk-FK6GBCQ4.js.map → chunk-XWB2L3HS.js.map} +0 -0
  1059. /package/dist/{chunk-ZZS6IUOY.js.map → chunk-XYOU3ROP.js.map} +0 -0
  1060. /package/dist/{chunk-DRPSJE62.js.map → chunk-YDSF4PXO.js.map} +0 -0
  1061. /package/dist/{chunk-7YOPIYCL.js.map → chunk-YDWQVAFI.js.map} +0 -0
  1062. /package/dist/{claim-XBVKGN7A.js.map → claim-DEQYWQZ4.js.map} +0 -0
  1063. /package/dist/{client-2ABVGUQB.js.map → client-TL7BITWM.js.map} +0 -0
  1064. /package/dist/{code-GHG7DEPV.js.map → code-ORY2WROG.js.map} +0 -0
  1065. /package/dist/{companies-IGDIQ64M.js.map → companies-QM5MOLIZ.js.map} +0 -0
  1066. /package/dist/{complete-CYPVVQWT.js.map → complete-FM6VSFCT.js.map} +0 -0
  1067. /package/dist/{config-L26WNV2G.js.map → config-FMDQ323H.js.map} +0 -0
  1068. /package/dist/{connect-FVP4HT6T.js.map → connect-YTIE5PRU.js.map} +0 -0
  1069. /package/dist/{content-2SL3VHIK.js.map → content-ZQB6PXGX.js.map} +0 -0
  1070. /package/dist/{conversation-DDZKRMCI.js.map → conversation-VKWYWPB6.js.map} +0 -0
  1071. /package/dist/{conversion-report-VARF3FYY.js.map → conversion-report-U4UFXSD2.js.map} +0 -0
  1072. /package/dist/{conversion-upload-command-ZWSNQFGS.js.map → conversion-upload-command-INQX73AK.js.map} +0 -0
  1073. /package/dist/{conversion-writes-7SXCEVCV.js.map → conversion-writes-MCKROLRA.js.map} +0 -0
  1074. /package/dist/{conversions-ZCOIEPNI.js.map → conversions-OPNDVQ56.js.map} +0 -0
  1075. /package/dist/{conversions-5S5IMXGM.js.map → conversions-QILFJ34A.js.map} +0 -0
  1076. /package/dist/{countries-D5JPHJ4F.js.map → countries-AZ3XSZVM.js.map} +0 -0
  1077. /package/dist/{create-VHZWE4JQ.js.map → create-A7O5CBUS.js.map} +0 -0
  1078. /package/dist/{create-GLPBZEQO.js.map → create-BEH3DUUP.js.map} +0 -0
  1079. /package/dist/{create-3WWCI3KY.js.map → create-LRVA4MSV.js.map} +0 -0
  1080. /package/dist/{creatives-5C4PRGFT.js.map → creatives-43I65CHD.js.map} +0 -0
  1081. /package/dist/{creatives-CW3FDOPI.js.map → creatives-DRRWFFOH.js.map} +0 -0
  1082. /package/dist/{creatives-7VEIUD3G.js.map → creatives-XEKEYLNV.js.map} +0 -0
  1083. /package/dist/{credentials-2QHNU3SD.js.map → credentials-GJMH4XRU.js.map} +0 -0
  1084. /package/dist/{critique-HU7ZONQ3.js.map → critique-EJJQVV4D.js.map} +0 -0
  1085. /package/dist/{critique-ZYSNLKNA.js.map → critique-GIJUTKIM.js.map} +0 -0
  1086. /package/dist/{crm-BD4RK26E.js.map → crm-ECMEYPQT.js.map} +0 -0
  1087. /package/dist/{crm-LYRBIQOO.js.map → crm-WHK3PL6S.js.map} +0 -0
  1088. /package/dist/{crop-KR3LOQBX.js.map → crop-Q2GJLOHE.js.map} +0 -0
  1089. /package/dist/{currency-EQ6PB7VF.js.map → currency-SVDR5N7L.js.map} +0 -0
  1090. /package/dist/{custom-conversions-YCOWWTQ7.js.map → custom-conversions-5NJNPZKM.js.map} +0 -0
  1091. /package/dist/{customers-MO6ORQAN.js.map → customers-XKE3DQR7.js.map} +0 -0
  1092. /package/dist/{decide-DZG2JUMG.js.map → decide-PCJTFCWC.js.map} +0 -0
  1093. /package/dist/{delete-FIDIBB2H.js.map → delete-7K4KFPUT.js.map} +0 -0
  1094. /package/dist/{delete-NHN73V54.js.map → delete-APRR2OYC.js.map} +0 -0
  1095. /package/dist/{delete-YKR5Y7EQ.js.map → delete-GOA4Y562.js.map} +0 -0
  1096. /package/dist/{delete-OHTGL7ZJ.js.map → delete-I2SIRTMZ.js.map} +0 -0
  1097. /package/dist/{delete-YY6YJ3A6.js.map → delete-QCZUGDIJ.js.map} +0 -0
  1098. /package/dist/{demographics-YU75YZFO.js.map → demographics-HG7NWA5U.js.map} +0 -0
  1099. /package/dist/{describe-NAINLKWG.js.map → describe-CRTFAH6Y.js.map} +0 -0
  1100. /package/dist/{describe-I7VNACXY.js.map → describe-OAB2FJYK.js.map} +0 -0
  1101. /package/dist/{dimensions-K6MDXS4B.js.map → dimensions-PFGHRZ5T.js.map} +0 -0
  1102. /package/dist/{discard-DGK7PLWF.js.map → discard-U3DW6WV3.js.map} +0 -0
  1103. /package/dist/{discover-2UKANLTP.js.map → discover-IOJZYJ6B.js.map} +0 -0
  1104. /package/dist/{discover-E7SVCXZ2.js.map → discover-R3EUUGRZ.js.map} +0 -0
  1105. /package/dist/{domain-6NZKJW4W.js.map → domain-HTF3ARBJ.js.map} +0 -0
  1106. /package/dist/{download-5FCLTMLA.js.map → download-2HGDCPR7.js.map} +0 -0
  1107. /package/dist/{draft-QSO3DQG6.js.map → draft-3KLT2YHI.js.map} +0 -0
  1108. /package/dist/{draft-67OFDA53.js.map → draft-5RL6HZ6A.js.map} +0 -0
  1109. /package/dist/{draft-UNCGJCUN.js.map → draft-7O4ABIKK.js.map} +0 -0
  1110. /package/dist/{draft-I7H6FVZT.js.map → draft-DK4NZ6PW.js.map} +0 -0
  1111. /package/dist/{draft-W5BXN7H3.js.map → draft-GPL2TZXA.js.map} +0 -0
  1112. /package/dist/{draft-RJ5HOJIR.js.map → draft-KTOZ2AT7.js.map} +0 -0
  1113. /package/dist/{draft-NJNAGVVV.js.map → draft-PQGTIABK.js.map} +0 -0
  1114. /package/dist/{draft-RPCIHVRS.js.map → draft-SRMHEIRG.js.map} +0 -0
  1115. /package/dist/{env-FKVPPL35.js.map → env-NPSYSFXD.js.map} +0 -0
  1116. /package/dist/{experiment-JF2QTPRV.js.map → experiment-V57PLATO.js.map} +0 -0
  1117. /package/dist/{export-3ROTRKFE.js.map → export-UN2PTCNO.js.map} +0 -0
  1118. /package/dist/{extract-VXGXR5RD.js.map → extract-5JOFQZVU.js.map} +0 -0
  1119. /package/dist/{facets-3ZIRFE6P.js.map → facets-2XQEJNZZ.js.map} +0 -0
  1120. /package/dist/{favorites-DI2EY34L.js.map → favorites-4AWW5F5B.js.map} +0 -0
  1121. /package/dist/{feed-EH4FENF4.js.map → feed-AK3IFTAL.js.map} +0 -0
  1122. /package/dist/{feedback-Y7MDEA5O.js.map → feedback-34M4VJ5S.js.map} +0 -0
  1123. /package/dist/{feedback-record-SLU5RROR.js.map → feedback-record-ZINYAVHA.js.map} +0 -0
  1124. /package/dist/{fetch-V3BFDMZF.js.map → fetch-6ALLGDRN.js.map} +0 -0
  1125. /package/dist/{fetch-LUOENFIX.js.map → fetch-VYIMA5BI.js.map} +0 -0
  1126. /package/dist/{find-T7XJV535.js.map → find-22HWPRFY.js.map} +0 -0
  1127. /package/dist/{fit-VKSQTJ3Q.js.map → fit-S4RO2DMX.js.map} +0 -0
  1128. /package/dist/{flows-JVU42DAA.js.map → flows-ITS573ST.js.map} +0 -0
  1129. /package/dist/{follow-PIEAXZE7.js.map → follow-IUW3P6KH.js.map} +0 -0
  1130. /package/dist/{follow-competitors-7N2TPSLA.js.map → follow-competitors-YDF5WM4K.js.map} +0 -0
  1131. /package/dist/{following-74CI7UC6.js.map → following-PNOYUSHX.js.map} +0 -0
  1132. /package/dist/{fonts-BTOLDILR.js.map → fonts-U4YXUNNZ.js.map} +0 -0
  1133. /package/dist/{forecast-35EBPP2K.js.map → forecast-AFBIS6WT.js.map} +0 -0
  1134. /package/dist/{forecast-MHGSW73Z.js.map → forecast-NHG3UHOP.js.map} +0 -0
  1135. /package/dist/{funding-FTWMCGZ7.js.map → funding-WSJI56OH.js.map} +0 -0
  1136. /package/dist/{funnel-YH5LOG65.js.map → funnel-46QWG5DN.js.map} +0 -0
  1137. /package/dist/{ga4-T4ZHANT2.js.map → ga4-4W3KJIS5.js.map} +0 -0
  1138. /package/dist/{generate-KVQLIBAX.js.map → generate-3MC7WDL2.js.map} +0 -0
  1139. /package/dist/{get-WZVNWFQA.js.map → get-4DJXDO42.js.map} +0 -0
  1140. /package/dist/{get-2IKPCXEM.js.map → get-BK4MBGQF.js.map} +0 -0
  1141. /package/dist/{get-QOACNGXP.js.map → get-MVWVS3YT.js.map} +0 -0
  1142. /package/dist/{get-ABPBK4P3.js.map → get-P5ZTH7RQ.js.map} +0 -0
  1143. /package/dist/{get-V4BZ4NTK.js.map → get-PTOW6PVF.js.map} +0 -0
  1144. /package/dist/{get-NE3EEHN6.js.map → get-SR2NWOPT.js.map} +0 -0
  1145. /package/dist/{gif-O3FLM3CF.js.map → gif-L6RYCB6L.js.map} +0 -0
  1146. /package/dist/{google-Z5UAFS3C.js.map → google-EMSAXPVB.js.map} +0 -0
  1147. /package/dist/{google-I2Z7QOHV.js.map → google-XE35SF3Z.js.map} +0 -0
  1148. /package/dist/{google-ads-E6VNIUEK.js.map → google-ads-DYNDLOBO.js.map} +0 -0
  1149. /package/dist/{group-HJMQ5H3L.js.map → group-EUYJJ4LI.js.map} +0 -0
  1150. /package/dist/{group-34IDEULM.js.map → group-MBRA2QID.js.map} +0 -0
  1151. /package/dist/{gsc-LDZKY5FW.js.map → gsc-B3NPUNI6.js.map} +0 -0
  1152. /package/dist/{history-EBJTST3V.js.map → history-4S3YEIPK.js.map} +0 -0
  1153. /package/dist/{history-6TYCUXG2.js.map → history-7HFGP7UI.js.map} +0 -0
  1154. /package/dist/{hubspot-LO7ZJPNO.js.map → hubspot-3LWMILTD.js.map} +0 -0
  1155. /package/dist/{icon-37BBWKJO.js.map → icon-4DAXBICB.js.map} +0 -0
  1156. /package/dist/{ideas-2MSUALCW.js.map → ideas-RHOV2ZJ6.js.map} +0 -0
  1157. /package/dist/{images-TZKGJVQ2.js.map → images-KXEPMLOC.js.map} +0 -0
  1158. /package/dist/{improve-6J2UJ4JZ.js.map → improve-U2RA5DPI.js.map} +0 -0
  1159. /package/dist/{ingest-3RQSRCO5.js.map → ingest-H4F5TJC3.js.map} +0 -0
  1160. /package/dist/{ingest-65T56KUI.js.map → ingest-URYQ7BTL.js.map} +0 -0
  1161. /package/dist/{insights-5IBNZFHT.js.map → insights-HQGBM6VA.js.map} +0 -0
  1162. /package/dist/{insights-Y7V2UL7E.js.map → insights-K2CMI3K5.js.map} +0 -0
  1163. /package/dist/{inspect-QLFYFQU2.js.map → inspect-IPQTTZK3.js.map} +0 -0
  1164. /package/dist/{inspiration-LYCDPQEY.js.map → inspiration-TRMFE64K.js.map} +0 -0
  1165. /package/dist/{intent-PFN2OQEE.js.map → intent-2EWV4KXW.js.map} +0 -0
  1166. /package/dist/{job-ZEI4FC5Y.js.map → job-7GDSDRM4.js.map} +0 -0
  1167. /package/dist/{job-create-QVTI3TP6.js.map → job-create-PE7G64WC.js.map} +0 -0
  1168. /package/dist/{job-status-GVL7X6EY.js.map → job-status-LUSCFLZ2.js.map} +0 -0
  1169. /package/dist/{keep-GD5LZFGG.js.map → keep-5NFHEILH.js.map} +0 -0
  1170. /package/dist/{keyword-gap-WPVHYPVP.js.map → keyword-gap-T22T7RN6.js.map} +0 -0
  1171. /package/dist/{keyword-metrics-Y5LWCCUJ.js.map → keyword-metrics-SLCXHPZS.js.map} +0 -0
  1172. /package/dist/{keywords-for-site-HGIX4S4F.js.map → keywords-for-site-HBQSJKNN.js.map} +0 -0
  1173. /package/dist/{landing-EP23XIYL.js.map → landing-PJJGQM3M.js.map} +0 -0
  1174. /package/dist/{landings-4YBHALDA.js.map → landings-ZR4YMEPV.js.map} +0 -0
  1175. /package/dist/{languages-KRXEHLM4.js.map → languages-HXAZNLNU.js.map} +0 -0
  1176. /package/dist/{languages-5WPQX3DS.js.map → languages-VLMJOAJN.js.map} +0 -0
  1177. /package/dist/{launches-FOJQPCD2.js.map → launches-T5LAIOYS.js.map} +0 -0
  1178. /package/dist/{layerize-M6ZRXR7M.js.map → layerize-24PQKKPB.js.map} +0 -0
  1179. /package/dist/{leads-NV57FPKX.js.map → leads-EDKSVSWP.js.map} +0 -0
  1180. /package/dist/{leads-2VKYMGP4.js.map → leads-HXYLSNMB.js.map} +0 -0
  1181. /package/dist/{learn-6W55U4ZG.js.map → learn-ZUZ7MEY5.js.map} +0 -0
  1182. /package/dist/{library-HC5US7CE.js.map → library-ZHREUZMU.js.map} +0 -0
  1183. /package/dist/{lighthouse-DXZIQLLK.js.map → lighthouse-E2NTHAO4.js.map} +0 -0
  1184. /package/dist/{line-item-writes-LYWNJWZ2.js.map → line-item-writes-JNHZFCRM.js.map} +0 -0
  1185. /package/dist/{line-items-Q47IZGAD.js.map → line-items-WGLNILMH.js.map} +0 -0
  1186. /package/dist/{link-4CQUXZPK.js.map → link-KLFRRU24.js.map} +0 -0
  1187. /package/dist/{list-IZNAL5LS.js.map → list-37F6C6P5.js.map} +0 -0
  1188. /package/dist/{list-MEGAZDWZ.js.map → list-7EEDCI3T.js.map} +0 -0
  1189. /package/dist/{list-RCOUIFKY.js.map → list-E3MCY43O.js.map} +0 -0
  1190. /package/dist/{list-RGMBFJHB.js.map → list-E7CJMIGC.js.map} +0 -0
  1191. /package/dist/{list-VXNYIVS5.js.map → list-HA2NITR3.js.map} +0 -0
  1192. /package/dist/{list-3RZZFCIR.js.map → list-SW3DF473.js.map} +0 -0
  1193. /package/dist/{list-LC7BZRD4.js.map → list-TG6LBHCV.js.map} +0 -0
  1194. /package/dist/{locations-ZMJ5MJ6Z.js.map → locations-VYIDAVD4.js.map} +0 -0
  1195. /package/dist/{locations-CB6KK3ZU.js.map → locations-ZWTDUUGL.js.map} +0 -0
  1196. /package/dist/{log-5V5YMZGV.js.map → log-Y4NIMHFD.js.map} +0 -0
  1197. /package/dist/{logo-SOCGJLY5.js.map → logo-SHQJHJK7.js.map} +0 -0
  1198. /package/dist/{lookup-MQBDRZZT.js.map → lookup-TWSJ7KRT.js.map} +0 -0
  1199. /package/dist/{make-M5LD3MX2.js.map → make-IJN6E4Q7.js.map} +0 -0
  1200. /package/dist/{map-GD2BT7RO.js.map → map-DHQTEDUH.js.map} +0 -0
  1201. /package/dist/{mcp-WFRPWK3W.js.map → mcp-ZHASO273.js.map} +0 -0
  1202. /package/dist/{media-OYAN23YG.js.map → media-LDVWXWWZ.js.map} +0 -0
  1203. /package/dist/{media-QCWYLIO2.js.map → media-TNB4DLTJ.js.map} +0 -0
  1204. /package/dist/{media-sources-YQURN7ZH.js.map → media-sources-MFBQDZNS.js.map} +0 -0
  1205. /package/dist/{members-4UCL6OKS.js.map → members-HTHOAHPO.js.map} +0 -0
  1206. /package/dist/{meta-B5XDM6F6.js.map → meta-RAVCXVZA.js.map} +0 -0
  1207. /package/dist/{metrics-W4SLBSPD.js.map → metrics-2VB46SDT.js.map} +0 -0
  1208. /package/dist/{models-IUO3XGQK.js.map → models-TYRVCPS2.js.map} +0 -0
  1209. /package/dist/{normalize-O6ATWOWP.js.map → normalize-PVOL256M.js.map} +0 -0
  1210. /package/dist/{normalize-2OUEDA6D.js.map → normalize-Q7EN7JIF.js.map} +0 -0
  1211. /package/dist/{options-U4PY24PS.js.map → options-VDTCTJHY.js.map} +0 -0
  1212. /package/dist/{output-ZKNCKP7T.js.map → output-NHMCVVUU.js.map} +0 -0
  1213. /package/dist/{page-EMJESGQZ.js.map → page-5PCVZ7HS.js.map} +0 -0
  1214. /package/dist/{pages-OMKAYKCF.js.map → pages-DDXB4NQ4.js.map} +0 -0
  1215. /package/dist/{performance-WGCN2XIC.js.map → performance-QOQTL4XM.js.map} +0 -0
  1216. /package/dist/{pinterest-I6JO7LXL.js.map → pinterest-KQYSCTVQ.js.map} +0 -0
  1217. /package/dist/{pixels-5JBTFWCO.js.map → pixels-GH522EYV.js.map} +0 -0
  1218. /package/dist/{pixels-G64Y3J22.js.map → pixels-HJYPX6RR.js.map} +0 -0
  1219. /package/dist/{plan-KNUKCIS7.js.map → plan-ZS7SOXL6.js.map} +0 -0
  1220. /package/dist/{preview-TBINZJ2C.js.map → preview-W4VKXOCT.js.map} +0 -0
  1221. /package/dist/{profile-UV3FTEUC.js.map → profile-FSB4RT55.js.map} +0 -0
  1222. /package/dist/{promoted-tweet-writes-YVSGOBAS.js.map → promoted-tweet-writes-6PI2KR65.js.map} +0 -0
  1223. /package/dist/{promoted-tweets-DQDEHTP4.js.map → promoted-tweets-UJRNHKKO.js.map} +0 -0
  1224. /package/dist/{properties-CTFQ7IK3.js.map → properties-V74I56UK.js.map} +0 -0
  1225. /package/dist/{publish-I2XOKEHC.js.map → publish-R6RINQCF.js.map} +0 -0
  1226. /package/dist/{query-T5IBVQ3B.js.map → query-5F5MBR7H.js.map} +0 -0
  1227. /package/dist/{query-L3TGHKKM.js.map → query-KBXZD4SW.js.map} +0 -0
  1228. /package/dist/{query-JMVLWKVY.js.map → query-SFNCXH4H.js.map} +0 -0
  1229. /package/dist/{questionnaire-APZ6FMPE.js.map → questionnaire-JK5JLQXK.js.map} +0 -0
  1230. /package/dist/{read-W5ZK5G7Q.js.map → read-LGRFU7XB.js.map} +0 -0
  1231. /package/dist/{reddit-GYVC7XPJ.js.map → reddit-QWM5ZQ6P.js.map} +0 -0
  1232. /package/dist/{refresh-XYZ2Q2SN.js.map → refresh-ZPF7UH3S.js.map} +0 -0
  1233. /package/dist/{release-HOSW3VHR.js.map → release-BZPK2P3R.js.map} +0 -0
  1234. /package/dist/{relevant-pages-F5AGXYC5.js.map → relevant-pages-VMNEEEJK.js.map} +0 -0
  1235. /package/dist/{remove-7QYX2OYJ.js.map → remove-C7ZJYFCG.js.map} +0 -0
  1236. /package/dist/{remove-RFOFNOYU.js.map → remove-PIHTPIHM.js.map} +0 -0
  1237. /package/dist/{reopen-HIVOKJOR.js.map → reopen-6Q7NQX2C.js.map} +0 -0
  1238. /package/dist/{rerun-U6F4TEPA.js.map → rerun-VRZVJDYS.js.map} +0 -0
  1239. /package/dist/{research-43V2DWVK.js.map → research-FXQ6X7BY.js.map} +0 -0
  1240. /package/dist/{research-7TN24S4D.js.map → research-X6O7IID6.js.map} +0 -0
  1241. /package/dist/{reset-R5RM7ZTG.js.map → reset-FRNQUCL5.js.map} +0 -0
  1242. /package/dist/{reset-B3FAY2EH.js.map → reset-NXK6NCOO.js.map} +0 -0
  1243. /package/dist/{resolve-I5YOKTRV.js.map → resolve-WHI4UP4V.js.map} +0 -0
  1244. /package/dist/{retry-A335Q6HS.js.map → retry-VSFPEXLE.js.map} +0 -0
  1245. /package/dist/{retry-R4OWR7IK.js.map → retry-XMTYWCFA.js.map} +0 -0
  1246. /package/dist/{retry-failed-5XDCVPQA.js.map → retry-failed-XTYV3QJS.js.map} +0 -0
  1247. /package/dist/{run-TNO2VF2U.js.map → run-HBNAA56X.js.map} +0 -0
  1248. /package/dist/{scaffold-ad-VKOXZENI.js.map → scaffold-ad-BV5WK4IA.js.map} +0 -0
  1249. /package/dist/{scaffold-static-ad-CMYP2ZZ6.js.map → scaffold-static-ad-IWNJ6GM7.js.map} +0 -0
  1250. /package/dist/{scaffold-video-AKA5LMEI.js.map → scaffold-video-JBG7Q45T.js.map} +0 -0
  1251. /package/dist/{schedule-HKIY6JI2.js.map → schedule-P6GVRZ4E.js.map} +0 -0
  1252. /package/dist/{schedule-4N6JTJJT.js.map → schedule-WSO7ETS6.js.map} +0 -0
  1253. /package/dist/{scheduled-agents-QM5Z3UNW.js.map → scheduled-agents-TYT5BIBQ.js.map} +0 -0
  1254. /package/dist/{schema-J6AIGXZ7.js.map → schema-O4CMQQGD.js.map} +0 -0
  1255. /package/dist/{scrape-VK7RZO4M.js.map → scrape-ZG2OWZL4.js.map} +0 -0
  1256. /package/dist/{screenshot-PWUROUXU.js.map → screenshot-GJUY7FCW.js.map} +0 -0
  1257. /package/dist/{search-LRY5SYDM.js.map → search-2BJ4OLMX.js.map} +0 -0
  1258. /package/dist/{search-J2VQNY4A.js.map → search-52CSDYJS.js.map} +0 -0
  1259. /package/dist/{search-3ZW3RRPX.js.map → search-FHSQYZOC.js.map} +0 -0
  1260. /package/dist/{search-PK4IUZ4C.js.map → search-HBFAHH4W.js.map} +0 -0
  1261. /package/dist/{search-M23XX3N5.js.map → search-ZAY2QV3Y.js.map} +0 -0
  1262. /package/dist/{search-copy-AVQESTYJ.js.map → search-copy-O5AOEBXC.js.map} +0 -0
  1263. /package/dist/{seeds-C6JI3WTG.js.map → seeds-XY3EZ33U.js.map} +0 -0
  1264. /package/dist/{select-NBRJ6WLY.js.map → select-ISR6YEIE.js.map} +0 -0
  1265. /package/dist/{sends-DYIE7W5U.js.map → sends-GECHL3NK.js.map} +0 -0
  1266. /package/dist/{sequences-CQVKVRC4.js.map → sequences-5BB4S63A.js.map} +0 -0
  1267. /package/dist/{set-prompt-VNJ334YW.js.map → set-prompt-DF7Q6WOF.js.map} +0 -0
  1268. /package/dist/{setup-links-77534G7K.js.map → setup-links-GF2IP4TH.js.map} +0 -0
  1269. /package/dist/{shared-2SQRS476.js.map → shared-JDIL3BNQ.js.map} +0 -0
  1270. /package/dist/{site-EFQ2QFZX.js.map → site-T6V5S5DJ.js.map} +0 -0
  1271. /package/dist/{sitemaps-XFLHKP2D.js.map → sitemaps-UQJFRA2W.js.map} +0 -0
  1272. /package/dist/{sites-5JNZWY3A.js.map → sites-ZNNEPEGI.js.map} +0 -0
  1273. /package/dist/{skills-43P4WK6Y.js.map → skills-WRKEF2Q4.js.map} +0 -0
  1274. /package/dist/{sources-JQM4AKNL.js.map → sources-5YUE7KUI.js.map} +0 -0
  1275. /package/dist/{sources-WJ3EZ46E.js.map → sources-UR3SLKVK.js.map} +0 -0
  1276. /package/dist/{stale-Y5HC7BNS.js.map → stale-6LZAROBI.js.map} +0 -0
  1277. /package/dist/{stats-LLTHL5VR.js.map → stats-QFBTSDVV.js.map} +0 -0
  1278. /package/dist/{status-T25VZANV.js.map → status-FDKP65DR.js.map} +0 -0
  1279. /package/dist/{sticker-B5DJ56YG.js.map → sticker-2JQUC7XO.js.map} +0 -0
  1280. /package/dist/{stock-WXAFJFAC.js.map → stock-W3SNJCBV.js.map} +0 -0
  1281. /package/dist/{studio-X546AIF2.js.map → studio-2GAZ6QOH.js.map} +0 -0
  1282. /package/dist/{sync-JLECDZYS.js.map → sync-4LHVBFMZ.js.map} +0 -0
  1283. /package/dist/{sync-VDPGFUZS.js.map → sync-NAJQQ6NQ.js.map} +0 -0
  1284. /package/dist/{sync-GCVG6QTL.js.map → sync-OGFGTKUK.js.map} +0 -0
  1285. /package/dist/{sync-definition-GKF2KKFC.js.map → sync-definition-MSQFCXRK.js.map} +0 -0
  1286. /package/dist/{tag-manager-HG76EBNX.js.map → tag-manager-MNGOJ3GC.js.map} +0 -0
  1287. /package/dist/{tags-UE2S4UCB.js.map → tags-67KLYTJF.js.map} +0 -0
  1288. /package/dist/{tags-SJIXKMQH.js.map → tags-GLHFHQW2.js.map} +0 -0
  1289. /package/dist/{tags-6KXDDSBW.js.map → tags-PYLVQEVS.js.map} +0 -0
  1290. /package/dist/{tags-NFBMGARL.js.map → tags-SL4F3LLY.js.map} +0 -0
  1291. /package/dist/{tags-T4OK7QXX.js.map → tags-XMRO4WF6.js.map} +0 -0
  1292. /package/dist/{targeting-constants-GR7RUKE3.js.map → targeting-constants-RQT4GLUA.js.map} +0 -0
  1293. /package/dist/{targeting-criteria-NZVF6ZOK.js.map → targeting-criteria-VK3IR6N2.js.map} +0 -0
  1294. /package/dist/{testimonials-NODH7BWK.js.map → testimonials-KTHDZ3R5.js.map} +0 -0
  1295. /package/dist/{themes-NO3DM5M3.js.map → themes-VVID6Y73.js.map} +0 -0
  1296. /package/dist/{top-companies-MWOOBUSM.js.map → top-companies-MBTTJTRK.js.map} +0 -0
  1297. /package/dist/{translate-26KMJSWL.js.map → translate-I6LZ3UZU.js.map} +0 -0
  1298. /package/dist/{tweets-ZWC7YOQ4.js.map → tweets-L4CXZLKQ.js.map} +0 -0
  1299. /package/dist/{unfollow-LBMJXF4E.js.map → unfollow-5T2CT3EF.js.map} +0 -0
  1300. /package/dist/{unlink-OL7UNUYB.js.map → unlink-IHEFAFG6.js.map} +0 -0
  1301. /package/dist/{unstage-5AZAD2KF.js.map → unstage-NEX3EIJX.js.map} +0 -0
  1302. /package/dist/{update-PCVBEGSU.js.map → update-CYQIGHT4.js.map} +0 -0
  1303. /package/dist/{update-HSMB6JMZ.js.map → update-K5MMOE5N.js.map} +0 -0
  1304. /package/dist/{update-QWWAXFWH.js.map → update-LR7UW5LL.js.map} +0 -0
  1305. /package/dist/{upload-MPMP3JRS.js.map → upload-C2K66PR3.js.map} +0 -0
  1306. /package/dist/{upload-VBPWOAKD.js.map → upload-VVB2LTD2.js.map} +0 -0
  1307. /package/dist/{upscale-TXJFZLUU.js.map → upscale-SGBB4GWF.js.map} +0 -0
  1308. /package/dist/{url-options-ML2D74NM.js.map → url-options-LUKI4Y46.js.map} +0 -0
  1309. /package/dist/{url-params-4AD467SA.js.map → url-params-MSRUZOGT.js.map} +0 -0
  1310. /package/dist/{use-SUY57ZJA.js.map → use-XGTUHBUL.js.map} +0 -0
  1311. /package/dist/{validate-W4QZZFXV.js.map → validate-XMEHRCAG.js.map} +0 -0
  1312. /package/dist/{variant-TE6CEHZW.js.map → variant-DQRDW6IZ.js.map} +0 -0
  1313. /package/dist/{videos-DJHFHZI5.js.map → videos-EQG7AAXT.js.map} +0 -0
  1314. /package/dist/{view-EQZB7Z6N.js.map → view-KJ6YNRXB.js.map} +0 -0
  1315. /package/dist/{viewer-check-GL2UNAGR.js.map → viewer-check-7DZQDGD3.js.map} +0 -0
  1316. /package/dist/{web-N4RNU7UH.js.map → web-MSYXLOFV.js.map} +0 -0
  1317. /package/dist/{winners-UPM6DFRB.js.map → winners-LD6PFXAS.js.map} +0 -0
  1318. /package/dist/{winning-ads-SVUUR443.js.map → winning-ads-VWBU3BNQ.js.map} +0 -0
  1319. /package/dist/{write-commands-BCU5YATY.js.map → write-commands-CKMHQ4YY.js.map} +0 -0
  1320. /package/dist/{write-commands-YNI6KS2B.js.map → write-commands-NW5CC3DP.js.map} +0 -0
  1321. /package/dist/{write-commands-WVJJJFBP.js.map → write-commands-PK23JVW3.js.map} +0 -0
  1322. /package/dist/{write-commands-5HSNHURZ.js.map → write-commands-RLEYECTB.js.map} +0 -0
  1323. /package/dist/{x-65R63NFK.js.map → x-ZSTQLK6Q.js.map} +0 -0
@@ -1,4408 +0,0 @@
1
- import {
2
- bandViolations,
3
- blockingArea,
4
- bytesOf,
5
- checkStaticImage,
6
- normalizeCopy,
7
- safeRectPx,
8
- staticSafeArea,
9
- textMismatch
10
- } from "./chunk-MZQBL7QU.js";
11
- import {
12
- HAND_LETTERED,
13
- conceptSummary,
14
- plainText,
15
- readAdSpec,
16
- seesReferenceStructure,
17
- withRenderedLettering
18
- } from "./chunk-D7KNUZ6Z.js";
19
- import {
20
- AD_IMAGE_MODEL,
21
- MODEL_REGISTRY,
22
- bestSupportedQuality,
23
- canonicalModelId,
24
- isManagedNodeKind,
25
- maxClipSeconds,
26
- nearestSupportedAspectRatio,
27
- nearestSupportedImageSize,
28
- supportsLastFrame,
29
- supportsParam,
30
- validateModelInputs,
31
- validateModelParams
32
- } from "./chunk-2XQ5HU6D.js";
33
- import {
34
- askAboutImages
35
- } from "./chunk-X3PVW3RM.js";
36
- import {
37
- marketCountryName
38
- } from "./chunk-5HYSKH77.js";
39
- import {
40
- ASSET_KINDS,
41
- BackendHttpError,
42
- HttpClient,
43
- classifyBackendError,
44
- forEachString,
45
- isBlocking,
46
- looksLikeHttpUrl,
47
- resolveOutputKinds
48
- } from "./chunk-VPBCR6DT.js";
49
-
50
- // src/engine/scaffold/static-brief.ts
51
- var wordCount = (s) => s.split(/\s+/).filter(Boolean).length;
52
- var sentence = (s) => {
53
- const t = s.trim();
54
- return t && !/[.!?]$/.test(t) ? `${t}.` : t;
55
- };
56
- var capitalize = (s) => s ? s[0]?.toUpperCase() + s.slice(1) : s;
57
- function tidy(s) {
58
- return s.replace(/\.{3,}/g, "\u2026").replace(
59
- /([.!?,;:])(?:\s*[.,;:])+/g,
60
- (run, first) => /[!?]/.test(first) ? first : run.trim().at(-1) ?? ""
61
- );
62
- }
63
- var EMPTY_CLAUSE = /^(none|null|nil|n a|na|not applicable)$/;
64
- function keepClauses(text, drop = () => false) {
65
- const kept = [];
66
- const seen = /* @__PURE__ */ new Set();
67
- for (const part of text.split(/(?<=[,;.!?])\s+/)) {
68
- const body = normalized(part);
69
- if (body && !EMPTY_CLAUSE.test(body) && !seen.has(body) && !drop(part)) {
70
- seen.add(body);
71
- kept.push(part.trim());
72
- continue;
73
- }
74
- const end = part.trim().match(/[.!?]$/)?.[0];
75
- const last = kept.length - 1;
76
- if (end && last >= 0)
77
- kept[last] = (kept[last] ?? "").replace(/[,;:]?$/, (m) => /[.!?]$/.test(kept[last] ?? "") ? m : end);
78
- }
79
- return kept.join(" ");
80
- }
81
- var MIN_COPY_LETTERS = 3;
82
- var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
83
- var wordEdge = (ch, look) => ch && /[\p{L}\p{N}]/u.test(ch) ? look === "behind" ? "(?<![\\p{L}\\p{N}])" : "(?![\\p{L}\\p{N}])" : "";
84
- var phrase = (text, wrap = (core) => core) => new RegExp(`${wordEdge(text[0], "behind")}${wrap(escapeRegExp(text))}${wordEdge(text.at(-1), "ahead")}`, "giu");
85
- var longEnough = (copy) => (copy.match(new RegExp("\\p{L}", "gu")) ?? []).length >= MIN_COPY_LETTERS;
86
- function copyAsLineRefs(text, copy) {
87
- const byLength = copy.map((t, i) => ({ t, n: i + 1 })).sort((a, b) => b.t.length - a.t.length);
88
- return byLength.reduce(
89
- (out, { t, n }) => longEnough(t) ? out.replace(
90
- phrase(t, (core) => `['"\u201C\u2018]?${core}['"\u201D\u2019]?`),
91
- `line ${n}`
92
- ) : out,
93
- text
94
- );
95
- }
96
- function unwanted(clause, k) {
97
- if (k.logo && /\bno (logos?|wordmarks?|branding)\b/i.test(clause)) return true;
98
- if (HAND_LETTERED.test(clause)) return true;
99
- if (/\bsource (ad|composition)\b|\bORIGINAL\b|\bcompetitor|\breference ad\b/.test(clause)) return true;
100
- return k.positive && NEGATION.test(clause.replace(/"[^"\n]*"/g, ""));
101
- }
102
- var NEGATION = /\b(no|not|never|don't|do not|avoid|without)\b/i;
103
- function plain(text, k) {
104
- let out = plainText(text).replace(new RegExp("(?<=\\p{L})_(?=\\p{L})", "gu"), " ");
105
- if (k) out = copyAsLineRefs(out, k.copy).replace(/\bfaithful(ly)?\b/gi, (_m, ly) => `exact${ly ?? ""}`);
106
- return tidy(keepClauses(out, k ? (clause) => unwanted(clause, k) : void 0)).replace(/\s*[,;:]+$/, "");
107
- }
108
- var normalized = (s) => s.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
109
- function unsaid(text, said) {
110
- const known = ` ${normalized(said)} `;
111
- return (text.match(/[^.!?]+[.!?]*/g) ?? []).map((s) => s.trim()).filter((s) => normalized(s) && !known.includes(` ${normalized(s)} `)).join(" ");
112
- }
113
- function clip(text, max) {
114
- if (wordCount(text) <= max) return text;
115
- const sentences = text.match(/[^.!?]+[.!?]*/g) ?? [text];
116
- let out = "";
117
- for (const s of sentences) {
118
- if (wordCount(out + s) > max) break;
119
- out += s;
120
- }
121
- if (out.trim()) return sentence(out.trim().replace(/[\s,;:]+$/, ""));
122
- const head = text.split(/\s+/).slice(0, max).join(" ");
123
- const clause = head.match(/^(.*?)[,;:—–](?:\s[^,;:—–]*)?$/)?.[1];
124
- return sentence((clause?.trim() || head).replace(/[\s,;:]+$/, ""));
125
- }
126
- function neutralName(l) {
127
- if (l > 0.95) return "white";
128
- if (l > 0.8) return "off-white";
129
- if (l > 0.6) return "light grey";
130
- if (l > 0.35) return "grey";
131
- return l > 0.12 ? "dark grey" : "black";
132
- }
133
- var HUES = [
134
- [15, "red"],
135
- [40, "orange"],
136
- [65, "yellow"],
137
- [165, "green"],
138
- [185, "teal"],
139
- [250, "blue"],
140
- [290, "purple"],
141
- [340, "pink"],
142
- [360, "red"]
143
- ];
144
- function hueOf(r, g, b, max, d) {
145
- const raw = max === r ? (g - b) / d % 6 : max === g ? (b - r) / d + 2 : (r - g) / d + 4;
146
- return (raw * 60 + 360) % 360;
147
- }
148
- function colourName(hex) {
149
- const n = Number.parseInt(hex.slice(1), 16);
150
- const r = (n >> 16 & 255) / 255;
151
- const g = (n >> 8 & 255) / 255;
152
- const b = (n & 255) / 255;
153
- const max = Math.max(r, g, b);
154
- const min = Math.min(r, g, b);
155
- const l = (max + min) / 2;
156
- const d = max - min;
157
- const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));
158
- if (s < 0.15 || l > 0.95 || l < 0.07) return neutralName(l);
159
- const h = hueOf(r, g, b, max, d);
160
- const band = HUES.find(([edge]) => h < edge)?.[1] ?? "red";
161
- const hue = band === "orange" && l < 0.35 ? "brown" : band;
162
- const shade = l < 0.3 ? "dark " : l > 0.8 ? "pale " : "";
163
- return `${shade}${hue}`;
164
- }
165
- var VERTICAL_NOUN = {
166
- reels: "full-screen vertical ad for Stories and Reels",
167
- stories: "full-screen vertical Stories ad",
168
- feed: "full-screen vertical ad"
169
- };
170
- function deliverableNoun(format, placements) {
171
- if (format === "9:16") {
172
- const safe = staticSafeArea(format, placements);
173
- return safe.placement === "vertical" ? `full-screen vertical ${safe.surface} ad` : VERTICAL_NOUN[safe.placement] ?? "full-screen vertical ad";
174
- }
175
- const [w, h] = format.split(":").map(Number);
176
- if (!w || !h) return "ad";
177
- return w < h ? "vertical feed ad" : w === h ? "square feed ad" : "landscape ad";
178
- }
179
- function keptInside(logo, button) {
180
- const parts = ["every word", ...logo ? ["the logo"] : [], ...button ? ["the button"] : []];
181
- return parts.length === 1 ? parts[0] ?? "" : `${parts.slice(0, -1).join(", ")} and ${parts.at(-1)}`;
182
- }
183
- function safeAreaText(format, placements, items, structure) {
184
- const safe = blockingArea(format, placements);
185
- const pct2 = (f) => `${Math.round(f * 100)}%`;
186
- const side = pct2(Math.max(safe.left, safe.right));
187
- const sits = items.includes(" and ") ? "sit" : "sits";
188
- if (safe.placement !== "feed") {
189
- const surface = safe.placement === "reels" ? "Stories and Reels" : safe.surface;
190
- return structure === "labelled_sections" ? `Keep ${items} between the top ${pct2(safe.top)} and the bottom ${pct2(safe.bottom)} of the frame (the app's interface covers them; scene only there), at least ${side} in from each side.` : `In ${surface} the app's interface covers the top ${pct2(safe.top)} and the bottom ${pct2(safe.bottom)} of the screen, so those bands hold only the scene; ${items} ${sits} between them, at least ${side} in from each side.`;
191
- }
192
- return edgeText(safe, items, structure);
193
- }
194
- function edgeText(area, items, structure) {
195
- const pct2 = (f) => `${Math.round(f * 100)}%`;
196
- const side = pct2(Math.max(area.left, area.right));
197
- const sits = items.includes(" and ") ? "sit" : "sits";
198
- const edges = pct2(area.top) === side && pct2(area.bottom) === side ? `at least ${side} in from every edge` : `at least ${pct2(Math.max(area.top, area.bottom))} in from the top and bottom edges and ${side} from the sides`;
199
- return structure === "labelled_sections" ? `Keep ${items} ${edges}.` : `${capitalize(items)} ${sits} ${edges}, clear of the frame.`;
200
- }
201
- var MARGIN_HEADROOM = 0.03;
202
- function marginText(planned, items, structure) {
203
- const scene = structure === "labelled_sections" ? "The scene runs to every edge of the frame, with no border or frame around the ad." : "The scene runs to every edge of the frame and continues beyond it.";
204
- const { top, bottom, left, right } = planned.area;
205
- const asked = {
206
- top: top + MARGIN_HEADROOM,
207
- bottom: bottom + MARGIN_HEADROOM,
208
- left: left + MARGIN_HEADROOM,
209
- right: right + MARGIN_HEADROOM
210
- };
211
- return `${edgeText(asked, items, structure)} ${scene}`;
212
- }
213
- function bandText(planned, items, flowing, appChrome) {
214
- const band = planned.area;
215
- const pct2 = (f) => `${Math.round(f * 100)}%`;
216
- const region = `from ${pct2(band.top)} to ${pct2(1 - band.bottom)} of the height and from ${pct2(band.left)} to ${pct2(1 - band.right)} of the width`;
217
- const sits = items.includes(" and ") ? "sit" : "sits";
218
- const reasons = [
219
- ...appChrome ? [
220
- flowing ? "the app draws its interface over the top and bottom of the screen" : "the app's interface covers the top and bottom"
221
- ] : [],
222
- ...planned.cut ? ["the other sizes are cut from this picture"] : []
223
- ];
224
- if (reasons.length === 0)
225
- reasons.push(flowing ? "the edges of a feed crop tightly" : "a feed crops tightly at the edges");
226
- if (flowing) {
227
- return `${capitalize(items)} ${sits} inside one region, ${region}, because ${reasons.join(" and ")}; the rest of the frame carries the scene only.`;
228
- }
229
- return `${items} ${sits} inside one region: ${region}. Outside it the frame carries the scene only: ${reasons.join(", and ")}.`;
230
- }
231
- var isAnimal = (r) => r.id.startsWith("ANIMAL_") || !r.id.startsWith("PERSON_") && ANIMALS.test(r.what);
232
- var personOrAnimal = (r) => isAnimal(r) ? "animal" : "person";
233
- function thirdPartyLogo(brand, k) {
234
- const where = brand.what ? plain(brand.what, k) : "";
235
- return `the ${plain(brand.name)} logo${where ? `, on ${where.replace(/^on /i, "")}` : ""}`;
236
- }
237
- var thirdPartyNames = (spec) => spec.third_party_brands.map((b) => `the ${plain(b.name)} logo`).join(" and ");
238
- function otherBrandsText(c) {
239
- const brands = c.spec.third_party_brands;
240
- if (brands.length === 0 || c.job !== "generate") return "";
241
- const marks = brands.map((b) => thirdPartyLogo(b, c.clean)).join("; ");
242
- return c.flowing ? ` ${sentence(capitalize(marks))} Each appears exactly as that brand draws it.` : `
243
- Other brands: ${marks}, exactly as ${brands.length === 1 ? "that brand draws it" : "each brand draws its own"}.`;
244
- }
245
- var LOGO_SIZE_WORDS = /\b(small|smaller|tiny|little|subtle|discreet|faint|minimal)\b/i;
246
- function logoInstruction(p, ctx) {
247
- if (ctx.native) {
248
- return ctx.flowing ? `the brand's real logo, as the sender's picture or signature where the disguise puts it, small as such a mark is. Reproduce it once, exactly as drawn in Image ${p.index}.` : "the brand's real logo, as the sender's picture or signature where the disguise puts it, small as such a mark is. Reproduce it once, exactly as drawn.";
249
- }
250
- const position = ctx.logoPosition ? keepClauses(plain(ctx.logoPosition, ctx.clean), (clause) => LOGO_SIZE_WORDS.test(clause)) : "";
251
- const at = position ? `, ${position.replace(/[\s,;:.!?]+$/, "")}` : "";
252
- const plate = ctx.plates.length > 0 ? ctx.plates.map((u) => `${u.name} (${u.hex})`).join(" or ") : "white or black";
253
- const where = `at least a fifth of the frame wide, on an area it contrasts with or a small ${plate} plate`;
254
- if (ctx.short)
255
- return "the brand's real logo. Reproduce it once, apart from the product, exactly as drawn, at least a fifth of the frame wide.";
256
- return ctx.flowing ? `the brand's real logo. Reproduce it once, apart from the product${at}, exactly as drawn in Image ${p.index}, ${where}.` : `the brand's real logo. Reproduce it once, never on the product${at}, exactly as drawn, ${where}.`;
257
- }
258
- function identityInstruction(p, what, keep, flowing, short) {
259
- if (p.spec && personOrAnimal(p.spec) === "person") {
260
- if (short) return `${what} Same face, hair, skin tone and build; one person, once.${keep}`;
261
- return flowing ? `${what} Keep their facial features, hair, skin tone and build exactly the same as in Image ${p.index}, and show them once; several views in that picture are all this one person.${keep}` : `${what} Keep this exact face, hair, skin tone and build, and show them once; several views in the picture are all this one person.${keep}`;
262
- }
263
- return flowing ? `${what} Keep its face, markings and colours exactly the same as in Image ${p.index}, and show it once; several views in that picture are all this one animal.${keep}` : `${what} Keep its exact face, markings and colours, and show it once; several views in the picture are all this one animal.${keep}`;
264
- }
265
- function typeInstruction(ctx) {
266
- if (ctx.flowing) return "a sample of the brand typeface; its words are only a sample.";
267
- if (ctx.allDesigned) return "a sample of the brand typeface for every line; its words are only a sample.";
268
- const lines = `line${ctx.designedLines.length === 1 ? "" : "s"} ${ctx.designedLines.join(", ")}`;
269
- return `a sample of the brand typeface for ${lines}; its words are only a sample.`;
270
- }
271
- function pictureInstruction(p, ctx) {
272
- const what = p.spec ? sentence(plain(p.spec.what, ctx.clean)) : "";
273
- const keep = p.spec?.keep ? ` Keep ${plain(p.spec.keep, ctx.clean)}.` : "";
274
- switch (p.role) {
275
- case "base":
276
- return "screen content takes priority over other references unless explicitly changed.";
277
- case "identity":
278
- return identityInstruction(p, what, keep, ctx.flowing, ctx.short);
279
- // The user saw one label come back two ways (E4 gallery, BENCH.md § "E1 v2"): the label is the product's.
280
- case "product":
281
- return ctx.flowing ? `${what} Keep it and its label, text and design, exactly as in Image ${p.index}.${keep}` : `${what} Reproduce this exact product and its label, text and design unchanged.${keep}`;
282
- case "logo":
283
- return logoInstruction(p, ctx);
284
- case "prop": {
285
- const brand = ctx.thirdParty.find((b) => b.logo === p.label);
286
- if (brand) return `${thirdPartyLogo(brand, ctx.clean)}. Reproduce it exactly as drawn, readable.`;
287
- return `${what} Show it as it looks here.${keep}`;
288
- }
289
- case "type":
290
- return typeInstruction(ctx);
291
- case "layout":
292
- return "the ad this one is built on, for its structure only: how it is built (its container, its layout, its pacing); never its words, brand, logo, people, products or colours, which belong to another advertiser.";
293
- default:
294
- return `${what} Show it as it looks here.${keep}`;
295
- }
296
- }
297
- function wiredPictures(spec, opts, job, designed) {
298
- const images = opts.images;
299
- const refs = spec.references.flatMap((r) => {
300
- const ref = images ? images.refs?.[r.id] : r.id;
301
- return ref ? [{ role: r.role, label: r.id, ref, spec: r }] : [];
302
- });
303
- const optional = [
304
- [job !== "generate", "base", "AD", images?.base],
305
- [spec.logo, "logo", "LOGO", images?.logo],
306
- [designed, "type", "TYPE", images?.type],
307
- // The reference, for its structure only, when the ad is built on its concept (C5: the image
308
- // model never saw the reference, 0/127 renders); `sourceLayout` decides when given.
309
- [job === "generate" && (opts.sourceLayout ?? seesReferenceStructure(spec)), "layout", "LAYOUT", images?.layout]
310
- ];
311
- const extras = optional.flatMap(([wanted, role, label, ref]) => wanted && ref ? [{ role, label, ref }] : []);
312
- return [...refs, ...extras];
313
- }
314
- function pictures(spec, opts, job, profile, designed) {
315
- const order = profile.brief.referenceOrder;
316
- const sorted = wiredPictures(spec, opts, job, designed).map((p, i) => ({ p, i })).sort((a, b) => order.indexOf(a.p.role) - order.indexOf(b.p.role) || a.i - b.i).map(({ p }) => p);
317
- const seen = /* @__PURE__ */ new Set();
318
- const unique = [];
319
- for (const p of sorted) {
320
- if (seen.has(p.ref)) continue;
321
- seen.add(p.ref);
322
- unique.push(p);
323
- }
324
- return unique.map((p, i) => ({ ...p, index: i + 1 }));
325
- }
326
- var SYMBOL_NAMES = {
327
- "\xAE": "a small raised registered-trademark sign",
328
- "\u2122": "a small raised trademark sign",
329
- "\xA9": "a copyright sign",
330
- "\u20AC": "the euro sign",
331
- "\xA3": "the pound sign"
332
- };
333
- function quotedCopy(text) {
334
- return text.split(/\s*\n\s*/).filter(Boolean).join(" ");
335
- }
336
- function lineBreaks(text) {
337
- const lines = text.split(/\s*\n\s*/).filter(Boolean);
338
- if (lines.length < 2) return "";
339
- const ends = lines.slice(0, -1).map((l) => `\u201C${l.split(/\s+/).at(-1)}\u201D`);
340
- return `, on ${lines.length} lines, breaking after ${ends.join(" and ")}`;
341
- }
342
- function clipNote(text, max) {
343
- if (wordCount(text) <= max) return text;
344
- return text.split(/\s+/).slice(0, max).join(" ").replace(/[,;:.]+$/, "");
345
- }
346
- var ROLE_WORDS = {
347
- headline: "the headline",
348
- title: "the headline",
349
- subhead: "the subhead",
350
- subheadline: "the subhead",
351
- subtitle: "the subhead",
352
- price: "the price",
353
- stat: "the key figure",
354
- figure: "the key figure",
355
- number: "the key figure",
356
- discount: "the discount",
357
- cta: "the button",
358
- button: "the button",
359
- call_to_action: "the button",
360
- caption: "a caption",
361
- fine_print: "the fine print",
362
- legal: "the fine print",
363
- disclaimer: "the fine print",
364
- footnote: "the fine print",
365
- label: "a label",
366
- list_item: "a list item",
367
- bullet: "a list item",
368
- badge: "a badge",
369
- quote: "the quote",
370
- testimonial: "the quote",
371
- attribution: "who said it",
372
- rating: "the rating",
373
- tagline: "the tagline"
374
- };
375
- var roleKey = (role) => (role ?? "").trim().toLowerCase().replace(/[\s-]+/g, "_");
376
- var roleWords = (line) => ROLE_WORDS[roleKey(line.role)] ?? "";
377
- function copyLineText(line, n, face, k, level) {
378
- const cap = NOTE_CAP[level] ?? 0;
379
- const look = line.look ? clipNote(plain(line.look, k), cap) : "";
380
- const note = line.position ? clipNote(plain(line.position, k), cap) : "";
381
- const position = note ? `; ${note}` : "";
382
- const text = quotedCopy(line.text);
383
- const detail = `${look ? line.lettering === "in_scene" ? `: ${look}` : `, ${look}` : ""}${lineBreaks(line.text)}${position}`;
384
- const lettered = line.lettering === "in_scene" ? line.on ? `lettered on ${plain(line.on, k)}` : "lettered on its object in the scene" : face ? `set in ${face}` : "";
385
- const lead = [roleWords(line), lettered].filter(Boolean).join(", ") || "designed";
386
- return sentence(`${n}. "${text}" \u2014 ${lead}${detail}`);
387
- }
388
- function textNotes(copy, profile) {
389
- const notes = Object.entries(SYMBOL_NAMES).flatMap(([symbol, name]) => {
390
- const lines = copy.flatMap((line, i) => line.text.includes(symbol) ? [i + 1] : []);
391
- if (lines.length === 0) return [];
392
- const which = lines.length === 1 ? `line ${lines[0]}` : `lines ${lines.slice(0, -1).join(", ")} and ${lines.at(-1)}`;
393
- return [`The ${symbol} in ${which} is ${name}.`];
394
- });
395
- const marks = [...new Set(copy.flatMap((l) => l.text.match(/[À-ÖØ-öø-ÿ¿¡]/g) ?? []))];
396
- if (profile.brief.text.keepAccents && marks.length > 0)
397
- notes.push(`Keep every accent and mark exactly: ${marks.join(" ")}.`);
398
- return notes;
399
- }
400
- var BUTTON_ROLES = /* @__PURE__ */ new Set(["cta", "button", "call_to_action"]);
401
- var isButton = (l) => BUTTON_ROLES.has(roleKey(l.role)) || /button/i.test(l.look ?? "");
402
- var FIGURE_ROLES = /* @__PURE__ */ new Set(["price", "stat", "figure", "number", "discount"]);
403
- var HEADLINE_ROLES = /* @__PURE__ */ new Set(["headline", "title"]);
404
- var FINE_PRINT_ROLES = /* @__PURE__ */ new Set(["fine_print", "legal", "disclaimer", "footnote"]);
405
- var BUTTON_SIZE = "a solid button at least a third of the frame wide, its words large and bold";
406
- var designedLines = (copy) => copy.flatMap((line, i) => line.lettering === "designed" ? [{ line, n: i + 1 }] : []);
407
- var finePrint = (ranked) => ranked.designed.filter(({ line }) => FINE_PRINT_ROLES.has(roleKey(line.role)));
408
- var NATIVE_BUTTON = "as the native element would carry it (a link, a reply, a small button of its own), never a big ad button";
409
- function fullHierarchy(ranked, lead, native) {
410
- const { designed, figure, headline, button } = ranked;
411
- const size = lead === figure ? "very large" : "at least half the frame wide";
412
- return [
413
- ...lead ? [`line ${lead.n} largest (${size}), read first`] : [],
414
- ...lead && figure && headline ? [`then line ${headline.n}`] : [],
415
- ...button ? [`line ${button.n} ${native ? NATIVE_BUTTON : BUTTON_SIZE}`] : [],
416
- ...designed.length > 1 ? ["every line readable at a glance"] : [],
417
- ...finePrint(ranked).map(({ n }) => `line ${n} smallest, still legible`)
418
- ];
419
- }
420
- function shortHierarchy(ranked, lead, native) {
421
- return [
422
- ...lead ? [`line ${lead.n} largest, read first`] : [],
423
- ...ranked.button ? [
424
- native ? `line ${ranked.button.n} as the native element would carry it` : `line ${ranked.button.n} a solid button a third of the frame wide`
425
- ] : [],
426
- ...finePrint(ranked).map(({ n }) => `line ${n} smallest`)
427
- ];
428
- }
429
- function rankedLines(copy) {
430
- const designed = designedLines(copy);
431
- const numbered = copy.map((line, i) => ({ line, n: i + 1 }));
432
- const byRole = (roles) => numbered.find(({ line }) => roles.has(roleKey(line.role)));
433
- const figure = byRole(FIGURE_ROLES);
434
- const headline = byRole(HEADLINE_ROLES);
435
- const lead = figure ?? headline ?? designed.find(({ line }) => !isButton(line) && !FINE_PRINT_ROLES.has(roleKey(line.role)));
436
- return { designed, figure, headline, lead, button: designed.find(({ line }) => isButton(line)) };
437
- }
438
- function hierarchyText(c) {
439
- const ranked = rankedLines(c.spec.copy);
440
- const lead = c.opts.variant === 2 ? void 0 : ranked.lead;
441
- const native = c.spec.brand_presence === "native";
442
- const parts = c.level >= SHORT_DIRECTION_LEVEL ? shortHierarchy(ranked, lead, native) : fullHierarchy(ranked, lead, native);
443
- if (parts.length === 0) return "";
444
- return c.flowing ? sentence(capitalize(parts.join("; "))) : `Hierarchy: ${sentence(parts.join("; "))}`;
445
- }
446
- function brandRoleColours(spec, brand) {
447
- const designed = designedLines(spec.copy);
448
- const figure = designed.find(({ line }) => FIGURE_ROLES.has(roleKey(line.role)));
449
- const panel = spec.scene.look === "photo" && !isNativeCapture(spec);
450
- const roles = [
451
- [spec.scene.look === "designed", "the background", brand.ground],
452
- [panel && designed.length > 0, "any band or panel behind the words", brand.ground],
453
- [designed.length > 0, "the designed type", brand.ink],
454
- [spec.copy.some(isButton), "the button", brand.cta],
455
- [Boolean(figure), figure ? `line ${figure.n} (or the shape it sits on)` : "", brand.accent],
456
- [
457
- !figure && spec.copy.some((l) => /underline|highlight|arrow/i.test(l.look ?? "")),
458
- "underlines and highlights",
459
- brand.accent
460
- ]
461
- ];
462
- return roles.flatMap(([applies, object, hex]) => applies && hex ? [{ object, name: colourName(hex), hex }] : []);
463
- }
464
- var NATIVE_CAPTURE = /\b(selfie|phone[- ]camera|phone photo|shot on (?:a )?phone|taken on (?:a )?phone|smartphone photo|ugc|user[- ]generated|webcam|front[- ]camera|snapshot|native caption)\b/i;
465
- var PHONE_CAMERA = /\b(phone|smartphone|iphone)\b/i;
466
- function isNativeCapture(spec) {
467
- if (spec.scene.look !== "photo") return false;
468
- const capture = spec.scene.capture ?? "";
469
- return NATIVE_CAPTURE.test(`${spec.scene.description} ${capture}`) || PHONE_CAMERA.test(capture);
470
- }
471
- function colourUses(spec, brand, k) {
472
- const allowed = brand ? new Set(
473
- [...brand.palette, brand.ground, brand.ink, brand.accent, brand.cta].filter((h) => Boolean(h))
474
- ) : null;
475
- const own = spec.colors.flatMap((c) => {
476
- if (c.hex && allowed && !allowed.has(c.hex)) return [];
477
- const tied = { object: plain(c.object, k), name: plain(c.name, k) };
478
- return [c.hex && allowed ? { ...tied, hex: c.hex } : tied];
479
- });
480
- if (!brand) return own;
481
- const said = new Set(own.flatMap((u) => u.hex ? [u.hex] : []));
482
- return [...own, ...brandRoleColours(spec, brand).filter((u) => !u.hex || !said.has(u.hex))];
483
- }
484
- function luminance(hex) {
485
- const n = Number.parseInt(hex.slice(1), 16);
486
- return (0.2126 * (n >> 16 & 255) + 0.7152 * (n >> 8 & 255) + 0.0722 * (n & 255)) / 255;
487
- }
488
- function plateColours(spec, brand, k) {
489
- if (!brand) return [];
490
- const hexes = [.../* @__PURE__ */ new Set([brand.ink, brand.ground, brand.accent, brand.cta, ...brand.palette])].filter(
491
- (h) => Boolean(h)
492
- );
493
- if (hexes.length === 0) return [];
494
- const sorted = [...hexes].sort((a, b) => luminance(a) - luminance(b));
495
- const ends = [.../* @__PURE__ */ new Set([sorted[0], sorted.at(-1)])].filter((h) => Boolean(h));
496
- const named = (hex) => spec.colors.find((c) => c.hex === hex)?.name;
497
- return ends.map((hex) => ({ object: "the logo plate", name: plain(named(hex) ?? colourName(hex), k), hex }));
498
- }
499
- function heroOf(pics) {
500
- const product = pics.find((p) => p.role === "product");
501
- const identity = pics.find((p) => p.role === "identity" && p.spec);
502
- if (product) return `the product in Image ${product.index}`;
503
- if (identity?.spec) return `the ${personOrAnimal(identity.spec)} in Image ${identity.index}`;
504
- return MAIN_SUBJECT;
505
- }
506
- var MAIN_SUBJECT = "the main subject of the scene";
507
- function variantDirection(c) {
508
- const [long, short] = c.opts.variant === 1 ? pictureDirection(c) : hookDirection(c);
509
- const keep = mustKeep(c);
510
- const kept = keep.length > 0 ? ` Keep ${keep.join(" and ")} as it is.` : "";
511
- return c.level >= SHORT_DIRECTION_LEVEL ? `${short} ${DEPARTS_SHORT}${kept}` : `${long} ${DEPARTS}${kept}`;
512
- }
513
- var DEPARTS = "It leads where it differs from the scene and line notes above.";
514
- var DEPARTS_SHORT = "It leads over the notes above.";
515
- var isSupport = (l) => isButton(l) || FINE_PRINT_ROLES.has(roleKey(l.role)) || roleKey(l.role) === "attribution";
516
- function hookDirection(c) {
517
- const hooks = c.spec.copy.flatMap(
518
- (l, i) => l.lettering === "designed" && !isSupport(l) ? [{ n: i + 1, text: l.text }] : []
519
- );
520
- const [first, ...rest] = hooks;
521
- const lead = rest[0];
522
- if (first && lead)
523
- return [
524
- `a type-led layout with another hook: line ${lead.n} leads, set as the largest words in the ad and read first; line ${first.n} follows it, smaller; the picture sits behind or beside the words as support, with the same words.`,
525
- `line ${lead.n} leads, largest; line ${first.n} smaller.`
526
- ];
527
- if (first)
528
- return [
529
- `a type-led layout: line ${first.n} set very large, across most of the frame's width, at least three times the size of every other line; the picture sits small behind or beside it as support, with the same words.`,
530
- `line ${first.n} very large, every other line small.`
531
- ];
532
- const lettered = c.spec.copy.findIndex((l) => l.lettering === "in_scene");
533
- if (lettered >= 0) {
534
- const on = c.spec.copy[lettered]?.on;
535
- const object = on ? plain(on, c.clean) : `the object carrying line ${lettered + 1}`;
536
- return [
537
- `a closer framing of the words: ${object} seen close and large in the frame, so its words are the first thing read, with the same words.`,
538
- `${object} close and large.`
539
- ];
540
- }
541
- const hero = heroOf(c.pics);
542
- return [
543
- `another arrangement: ${hero} on the other side of the frame, seen from another angle, with the same words.`,
544
- `${hero} on the other side, from another angle.`
545
- ];
546
- }
547
- var CLOSE_FRAMING = /\b(?:close[- ]?up|macro|detail shot|close(?: [a-z-]+){0,2} shot|tight(?:ly)? (?:crop|cropped|framed|shot))\b/i;
548
- function pictureDirection(c) {
549
- const hero = heroOf(c.pics);
550
- if (c.spec.scene.look === "designed") {
551
- const shown = hero === MAIN_SUBJECT ? "the thing the message is about" : hero;
552
- return [
553
- `a picture-led layout: ${shown} shown as one large picture filling most of the frame, the words set compactly over a calm part of it, with the same words.`,
554
- `a picture-led layout: ${shown} large, the words compact.`
555
- ];
556
- }
557
- if (c.spec.scene.look === "illustration")
558
- return [
559
- `a close, simple drawing: ${hero} drawn large, filling most of the frame, on one flat background colour, with the same words.`,
560
- `a close, simple drawing of ${hero} on one flat colour.`
561
- ];
562
- const scene = `${c.spec.scene.description} ${c.spec.scene.capture ?? ""}`;
563
- if (CLOSE_FRAMING.test(scene))
564
- return [
565
- `a wide shot: ${hero} smaller in the frame, in the full place around it, so the setting tells the story, with the same words.`,
566
- `a wide shot of ${hero} in its full setting.`
567
- ];
568
- return [
569
- `a close-up: ${hero} fills most of the frame, seen from close, and the setting shrinks to a soft hint of its colour and light, with the same words.`,
570
- `a close-up of ${hero}, filling the frame.`
571
- ];
572
- }
573
- function compileStaticBrief(written, brandOverride, format, viewerOverride, profile, opts = {}) {
574
- const spec = withRenderedLettering(written);
575
- const job = opts.job ?? "generate";
576
- const designedLines2 = spec.copy.flatMap((l, i) => l.lettering === "designed" ? [i + 1] : []);
577
- const pics = pictures(spec, opts, job, profile, designedLines2.length > 0);
578
- const base = {
579
- spec,
580
- brand: brandOverride ?? spec.brand,
581
- viewer: viewerOverride ?? spec.viewer,
582
- format,
583
- profile,
584
- opts,
585
- job,
586
- flowing: profile.brief.structure === "flowing_sentences",
587
- pics,
588
- designedLines: designedLines2,
589
- level: 0,
590
- clean: {
591
- copy: spec.copy.map((l) => quotedCopy(l.text)),
592
- logo: pics.some((p2) => p2.role === "logo"),
593
- positive: profile.brief.constraints === "positive_last"
594
- }
595
- };
596
- let prompt = assemble(base);
597
- for (let level = 1; level <= MAX_TRIM && wordCount(prompt) > profile.brief.wordBudget; level++) {
598
- prompt = assemble({ ...base, level });
599
- }
600
- const p = profile.brief.params;
601
- return {
602
- prompt,
603
- references: base.pics.map(({ index, role, label, ref }) => ({ index, role, label, ref })),
604
- params: {
605
- n: job === "generate" ? p.n : 1,
606
- ...p.quality ? { quality: p.quality } : {},
607
- ...p.imageSize ? { image_size: p.imageSize } : {}
608
- },
609
- words: wordCount(prompt)
610
- };
611
- }
612
- var MAX_TRIM = 16;
613
- var LAST_RESORT_LEVEL = 16;
614
- var PRODUCT_NOTE_WORDS = 10;
615
- var EXTRAS_SHORT_LEVEL = 15;
616
- var CAPTURE_LEVEL = 9;
617
- var SHORT_DIRECTION_LEVEL = 10;
618
- var SHORT_PICTURES_LEVEL = 14;
619
- var NOTE_CAP = [...Array(10).fill(Number.POSITIVE_INFINITY), 12, 8, 4, 0];
620
- var SCENE_CAP = [
621
- Number.POSITIVE_INFINITY,
622
- Number.POSITIVE_INFINITY,
623
- Number.POSITIVE_INFINITY,
624
- Number.POSITIVE_INFINITY,
625
- 80,
626
- 50,
627
- 35,
628
- 25,
629
- 15
630
- ];
631
- function audienceText(viewer, level, k) {
632
- if (!viewer || level >= 3) return "";
633
- const warmth = {
634
- cold: ", who are new to the brand",
635
- warm: ", who already know the brand",
636
- hot: ", who are ready to act"
637
- };
638
- const knows = viewer.knows ? ` (what they already know: ${plain(viewer.knows, k)})` : warmth[viewer.temperature ?? ""] ?? "";
639
- return `, made for ${plain(viewer.who, k)}${knows}`;
640
- }
641
- function ideaText(spec, level, k) {
642
- const message = spec.message;
643
- if (!message || level >= 3) return "";
644
- const m = message.toLowerCase();
645
- const repeats = spec.copy.some((l) => m.includes(l.text.toLowerCase()) || l.text.toLowerCase().includes(m));
646
- return repeats ? "" : ` Its one idea: ${sentence(plain(message, k))}`;
647
- }
648
- function deliverableSection(c) {
649
- const noun = deliverableNoun(c.format, c.opts.placements);
650
- const forBrand = c.brand?.name ? ` for ${plain(c.brand.name)}` : "";
651
- if (c.job === "fix") {
652
- const change = sentence(c.opts.instruction?.trim() || "correct what is wrong");
653
- return c.flowing ? `Image 1 is the current version of this ad. ${capitalize(change)} Keep everything else in Image 1 exactly the same, preserving its picture, layout, colours, lettering and every word.` : `Edit: Image 1 is the current version of this ad. ${capitalize(change)} Keep everything else exactly as it is in Image 1: the picture, the layout, the colours, the lettering and every word.`;
654
- }
655
- if (c.job === "series") {
656
- const change = sentence(c.opts.instruction?.trim() || "set the new words");
657
- return c.flowing ? `Image 1 is a finished ad of this series. Make the next ad of the same series${forBrand}: ${change} Keep everything else in Image 1 exactly the same, preserving its picture, layout, background, shapes, colours, lettering style and logo, each word in the place and style of the one it replaces.` : `Edit: Image 1 is a finished ad of this series. Make the next ad of the same series${forBrand}. ${capitalize(change)} Keep everything else exactly as it is in Image 1: the picture, the layout, the background, the shapes, the colours, the lettering style and the logo, each new line in the place, size and style of the line it replaces.`;
658
- }
659
- if (c.job === "relay") {
660
- const ui = "Keep any depicted app interface exact, with marketing overlays visibly separate from its native controls.";
661
- return c.flowing ? `Image 1 is a finished ad. Adapt this same ad to a ${noun}${forBrand}: move and scale its existing elements, keeping its picture, background, colours, lettering and every word. Extend only its existing background where needed. ${ui}` : `Deliverable: the same ad as Image 1, re-laid as a finished ${noun}${forBrand}. Move and scale its existing elements; keep its picture, background, colours, lettering and every word. Extend only its existing background where needed. ${ui}`;
662
- }
663
- const lead = c.flowing ? "Create a finished" : "Deliverable: a finished";
664
- return `${lead} ${noun}${forBrand}${audienceText(c.viewer, c.level, c.clean)}.${marketText(c)}${recognitionText(c)}${ideaText(c.spec, c.level, c.clean)}${conceptText(c)}${deviceText(c)}`;
665
- }
666
- function marketText(c) {
667
- const market = c.viewer?.market;
668
- if (!market) return "";
669
- if (market.country === "global") {
670
- return c.flowing ? " It is set in no one country: no flags, landmarks, money or shop signs of any country, and people who could live in any of them." : "\nSet in no one country: no flags, landmarks, money or shop signs of any country.";
671
- }
672
- const where = marketCountryName(market) ?? market.country;
673
- const money = market.currency ? ` (${market.currency})` : "";
674
- return c.flowing ? ` It is set in ${where}: its places, shops, money${money} and people are ${where}'s, in everyday details, never a flag or a landmark added to say where.` : `
675
- Set in ${where}: its places, shops, money${money} and people are ${where}'s, in everyday details, never a flag or a landmark added to say where.`;
676
- }
677
- function recognitionText(c) {
678
- const what = c.spec.recognition ? plain(c.spec.recognition, c.clean) : "";
679
- if (!what) return "";
680
- return c.flowing ? ` A stranger knows at a glance it is ${sentence(what)}` : ` A stranger knows at a glance it is ${sentence(what)}`;
681
- }
682
- var keptConcept = (spec) => spec.concept && spec.concept.verdict !== "reject" ? spec.concept : void 0;
683
- function mustKeep(c) {
684
- if (!conceptSummary(c.spec)) return [];
685
- return (c.spec.concept?.load_bearing ?? []).map((l) => {
686
- const ours = l.ours ? plain(l.ours, c.clean) : "";
687
- return `${plain(l.element, c.clean)}${ours ? ` (${ours})` : ""}`;
688
- });
689
- }
690
- var CARRIED = {
691
- whole_ad: "Built on a proven ad as a whole: its disguise, its arrangement and its opening, in our words, brand and pictures.",
692
- layout: "Built on a proven ad's layout: the same arrangement, with our words, brand and pictures.",
693
- hook: "Built on a proven ad's hook: it opens on the same pattern, in our words; the look is ours.",
694
- angle: "Built on a proven ad's angle: the same argument, in our words; the look is ours."
695
- };
696
- function conceptText(c) {
697
- const kept = conceptSummary(c.spec) ? keptConcept(c.spec) : void 0;
698
- if (!kept) return "";
699
- const w = kept.why_it_wins;
700
- const layers = [w.disguise, w.voice, w.trigger, w.attention, w.logic].flatMap(
701
- (t) => t?.trim() ? [plain(t, c.clean)] : []
702
- );
703
- const continuing = c.job !== "generate";
704
- const idea = layers.length > 0 && (continuing || c.level < LAST_RESORT_LEVEL) ? sentence(layers.join("; ")) : "";
705
- const keep = mustKeep(c);
706
- const keepLine = keep.length > 0 ? sentence(keep.join("; ")) : "";
707
- const carry = kept.carry && (continuing || c.level < LAST_RESORT_LEVEL) ? CARRIED[kept.carry] : "";
708
- const parts = [
709
- carry,
710
- idea ? `The idea: ${idea}` : "",
711
- keepLine ? `${c.flowing ? "It must keep" : "Must keep:"} ${keepLine}` : ""
712
- ];
713
- const separator = c.flowing ? " " : "\n";
714
- return parts.flatMap((part) => part ? [`${separator}${part}`] : []).join("");
715
- }
716
- function deviceText(c) {
717
- if (c.job !== "generate") {
718
- const mechanisms = c.spec.mechanism.map(
719
- (m) => [plain(m.device, c.clean), m.why_it_works ? plain(m.why_it_works, c.clean) : ""].filter(Boolean).join(": ")
720
- );
721
- return mechanisms.length > 0 ? `Keep the persuasion mechanism: ${sentence(mechanisms.join("; "))}` : "";
722
- }
723
- const device = c.spec.mechanism[0]?.device;
724
- if (!device || c.level >= 1) return "";
725
- const text = plain(device, c.clean);
726
- if (!text) return "";
727
- if (!c.flowing) return `
728
- Idea: ${sentence(capitalize(text))}`;
729
- const lower = new RegExp("^\\p{Lu}\\p{Ll}", "u").test(text) ? `${text[0]?.toLowerCase()}${text.slice(1)}` : text;
730
- return ` The idea it is built on: ${sentence(lower)}`;
731
- }
732
- function pictureNoun(p) {
733
- if (p.role === "identity" && p.spec) return personOrAnimal(p.spec);
734
- return p.role === "product" ? "product" : "object";
735
- }
736
- function explainedBefore(p, pics) {
737
- if (!p.spec) return void 0;
738
- return pics.find((q) => q.index < p.index && q.spec && q.role === p.role && pictureNoun(q) === pictureNoun(p));
739
- }
740
- function picturesSection(c) {
741
- if (c.pics.length === 0) return [];
742
- const ctx = {
743
- clean: c.clean,
744
- job: c.job,
745
- designedLines: c.designedLines,
746
- allDesigned: c.designedLines.length === c.spec.copy.length,
747
- plates: plateColours(c.spec, c.brand, c.clean),
748
- thirdParty: c.spec.third_party_brands,
749
- flowing: c.flowing,
750
- short: c.level >= LAST_RESORT_LEVEL,
751
- native: c.spec.brand_presence === "native",
752
- ...c.spec.logo_position ? { logoPosition: c.spec.logo_position } : {}
753
- };
754
- const lines = c.pics.map((p) => {
755
- const label = p.label ? ` (${p.label})` : "";
756
- const same = c.level >= SHORT_PICTURES_LEVEL ? explainedBefore(p, c.pics) : void 0;
757
- const instruction = same ? `another ${pictureNoun(p)}${c.flowing ? ", under" : ";"} the same rules as Image ${same.index}.` : pictureInstruction(p, ctx);
758
- return c.flowing ? `Image ${p.index}${label} is ${instruction}` : `Image ${p.index}${label}: ${instruction}`;
759
- });
760
- return [c.flowing ? lines.join("\n") : `References:
761
- ${lines.join("\n")}`];
762
- }
763
- var DESIGNED_LOOK = {
764
- flowing: "It is a clean designed ad graphic with one dominant element and a clear reading order, and its type reads at a glance on a phone.",
765
- labelled: "Look: a clean designed ad graphic: one dominant element, a clear reading order, crisp type that reads on a phone."
766
- };
767
- var ILLUSTRATION_LOOK = {
768
- flowing: "It is an illustration with one dominant element and a clear reading order.",
769
- labelled: "Look: an illustration: one dominant element, a clear reading order."
770
- };
771
- function photoLook(c) {
772
- const r = isNativeCapture(c.spec) ? c.profile.brief.realism.native : c.profile.brief.realism.produced;
773
- const capture = c.spec.scene.capture && c.level < CAPTURE_LEVEL ? plain(c.spec.scene.capture, c.clean) : "";
774
- if (!c.flowing) return `Look: ${r.photo}${capture ? ` \u2014 ${capture}` : ""}; ${r.texture}.`;
775
- const purpose = r.purpose ? `, ${r.purpose}` : "";
776
- return `It is ${r.photo}${capture ? `, ${capture}` : ""}, with ${r.texture}${purpose}.`;
777
- }
778
- function animalsText(c) {
779
- if (!showsAnimals(c.spec) || c.level >= EXTRAS_SHORT_LEVEL) return "";
780
- return c.flowing ? " The animals look real, with real fur, natural light and a candid moment." : " Animals look real: real fur, natural light, a candid moment.";
781
- }
782
- function lookText(c) {
783
- const people = `${showsPeople(c.spec) ? peopleText(c) : ""}${animalsText(c)}`;
784
- if (c.spec.scene.look === "photo") return `${photoLook(c)}${people}`;
785
- const look = c.spec.scene.look === "illustration" ? ILLUSTRATION_LOOK : DESIGNED_LOOK;
786
- return `${c.flowing ? look.flowing : look.labelled}${people}`;
787
- }
788
- var PEOPLE = /\b(person|people|wom[ae]n|m[ae]n|girls?|boys?|child(?:ren)?|kids?|baby|parents?|mother|father|mum|mom|dad|family|couple|customers?|founder|presenter|technician|nurse|doctor|workers?|students?|friends|athlete|chef|faces?|selfie|portrait|plumbers?|electricians?|drivers?|owners?|managers?|employees?|engineers?|developers?|teachers?|mechanics?|carpenters?|tradesm[ae]n|advis[oe]rs?|agents?|riders?|couriers?)\b/i;
789
- function showsPeople(spec) {
790
- if (spec.scene.look === "illustration") return false;
791
- if (spec.references.some((r) => r.role === "identity" && !isAnimal(r))) return true;
792
- return spec.scene.look === "photo" && PEOPLE.test(withoutAbsences(`${spec.scene.description} ${spec.scene.layout ?? ""}`));
793
- }
794
- var CLAUSE_REST = String.raw`(?:(?!\b(?:and|but|while|with|as|where|whereas)\b)[^.;,])*`;
795
- var ABSENCE = new RegExp(
796
- String.raw`\b(?:no|without|nor|never any|not a single)\s+(?:\w+\s+){0,2}?(?:people|persons?|humans?|one|hands?|faces?|figures?)\b${CLAUSE_REST}|\b(?:nobody|no-one|no one)\b${CLAUSE_REST}`,
797
- "gi"
798
- );
799
- var SOFTWARE_ROLE = /\b(?:password|file|task|project|package|window|device|usb|printer|graphics|gpu|ai|software|browser|download|display|audio|print)\s+(?:managers?|agents?|drivers?)\b|\b(?:managers?|agents?|drivers?)\s+(?:apps?|software|tools?|dashboards?|platforms?|programs?|updates?|panels?|screens?|extensions?|plugins?)\b/gi;
800
- function withoutAbsences(text) {
801
- return text.replace(/\b(?:first|second|third)[- ]person\b/gi, " ").replace(/\b\w+['’]s\b/g, " ").replace(SOFTWARE_ROLE, " ").replace(ABSENCE, " ");
802
- }
803
- function peopleText(c) {
804
- const cue = c.profile.brief.realism.people;
805
- if (!cue) return "";
806
- const { button, lead } = rankedLines(c.spec.copy);
807
- const target = button ?? lead;
808
- const said = c.level >= EXTRAS_SHORT_LEVEL ? "People look real, faces well lit." : sentence(capitalize(cue));
809
- const who = c.viewer?.who ? " Ordinary people the viewer's age: real faces with lines, everyday clothes." : "";
810
- return ` ${said}${who}${target ? ` Their gaze or pose leads toward line ${target.n}.` : ""}`;
811
- }
812
- var looksRendered = (spec) => showsPeople(spec) || showsAnimals(spec) || showsScreen(spec);
813
- var ANIMALS = /\b(dogs?|puppy|puppies|cats?|kittens?|pets?|horses?|birds?|rabbits?|animals?)\b/i;
814
- function showsAnimals(spec) {
815
- if (spec.scene.look === "illustration") return false;
816
- if (spec.references.some((r) => r.role === "identity" && isAnimal(r))) return true;
817
- return spec.scene.look === "photo" && ANIMALS.test(`${spec.scene.description} ${spec.scene.layout ?? ""}`);
818
- }
819
- var SCREEN = /\b(apps?|screens?|lock[- ]screen|dashboard|interface|mockup|notification|laptop|tablet|browser|website|chat bubbles?|ui)\b/i;
820
- function showsScreen(spec) {
821
- const ons = spec.copy.map((l) => l.on ?? "").join(" ");
822
- return SCREEN.test(`${spec.scene.description} ${spec.scene.layout ?? ""} ${ons}`);
823
- }
824
- var SCREENS = {
825
- labelled: "Screens: a real phone photographed, or a real screenshot: real app density, a real status bar, flat true-to-life colours; any words on them from the quoted lines.",
826
- flowing: "Every phone or app screen looks like a real phone photographed or a real screenshot, with real app density, a real status bar and flat true-to-life colours, any words on it from the quoted lines."
827
- };
828
- var SCREENSHOT = /\b(screenshots?|screen ?grabs?|screens?)\b/i;
829
- var screenshotOf = (pics) => pics.find((p) => p.spec && p.role !== "identity" && SCREENSHOT.test(p.spec.what));
830
- function screensText(c) {
831
- const laptop = /\b(laptops?|notebook computers?|macbooks?)\b/i.test(c.spec.scene.description);
832
- const device = laptop ? "laptop" : "phone";
833
- const shot = screenshotOf(c.pics);
834
- if (shot) {
835
- return c.flowing ? ` Every screen shows Image ${shot.index} exactly as it is, on a real ${device}, photographed.` : `
836
- Screens: every screen shows Image ${shot.index} exactly as it is, on a real ${device}, photographed.`;
837
- }
838
- if (!showsScreen(c.spec)) return "";
839
- if (c.level >= EXTRAS_SHORT_LEVEL)
840
- return c.flowing ? ` Every ${device} or app screen looks like a real ${device} photographed or a real screenshot.` : `
841
- Screens: a real ${device} photographed, or a real screenshot.`;
842
- if (laptop) {
843
- const screen = "a real laptop photographed, or a real screenshot; display, keyboard and hinge in consistent perspective; any words on it from the quoted lines.";
844
- return c.flowing ? ` Every laptop screen looks like ${screen}` : `
845
- Screens: ${screen}`;
846
- }
847
- return c.flowing ? ` ${SCREENS.flowing}` : `
848
- ${SCREENS.labelled}`;
849
- }
850
- function unwiredProductsText(c) {
851
- const wired = new Set(c.pics.map((p) => p.label));
852
- return c.spec.references.filter((r) => r.role === "product" && !wired.has(r.id)).map((r) => {
853
- const what = plain(r.what, c.clean).replace(/[.!?]+$/, "");
854
- const told = c.level >= LAST_RESORT_LEVEL ? clipNote(what.split(/[,;]/)[0] ?? what, PRODUCT_NOTE_WORDS) : what;
855
- return ` The product, ${told}, is in the frame, whole and clearly visible.`;
856
- }).join("");
857
- }
858
- function unwiredPeopleText(c) {
859
- const wired = new Set(c.pics.map((p) => p.label));
860
- return c.spec.references.filter((r) => r.role === "identity" && r.id.startsWith("PERSON_") && !wired.has(r.id)).map((r) => {
861
- const what = plain(r.what, c.clean).replace(/[.!?]+$/, "");
862
- const told = c.level >= LAST_RESORT_LEVEL ? clipNote(what.split(/[,;]/)[0] ?? what, PRODUCT_NOTE_WORDS) : what;
863
- return ` The person, ${told}, is an invented, ordinary real person \u2014 real skin, unposed, never a model.`;
864
- }).join("");
865
- }
866
- var BEFORE_AFTER = /\bbefore\b[^.]{0,40}\bafter\b|\bantes\b[^.]{0,30}\bdespu[eé]s\b|\bavant\b[^.]{0,30}\bapr[eè]s\b|\bvorher\b[^.]{0,30}\bnachher\b|\bprima\b[^.]{0,30}\bdopo\b/i;
867
- function isBeforeAfter(spec) {
868
- const told = [spec.scene.description, spec.scene.layout, spec.direction, ...spec.mechanism.map((m) => m.device)];
869
- return BEFORE_AFTER.test(told.filter(Boolean).join(" \n "));
870
- }
871
- var BEFORE_AFTER_TEXT = " The two states it compares differ clearly at a glance, even as a thumbnail; only the result changes between them.";
872
- function sceneSection(c) {
873
- if (c.job !== "generate") return [];
874
- const scene = c.spec.scene;
875
- const products = `${unwiredProductsText(c)}${unwiredPeopleText(c)}${isBeforeAfter(c.spec) ? BEFORE_AFTER_TEXT : ""}`;
876
- const told = normalized(products);
877
- const own = (sentence(plain(scene.description, c.clean)).match(/[^.!?]+[.!?]*/g) ?? []).filter((s) => !products || !told.includes(normalized(s.replace(/^\s*in the frame:?/i, "")))).join("").trim();
878
- const description = capitalize(clip(own || sentence(plain(scene.description, c.clean)), SCENE_CAP[c.level] ?? 15));
879
- const mood = scene.mood && c.level < 1 ? ` The mood is ${sentence(plain(scene.mood, c.clean))}` : "";
880
- const out = [
881
- c.flowing ? `${description}${products}${mood} ${lookText(c)}${screensText(c)}${otherBrandsText(c)}` : `Scene: ${description}${products}${mood}
882
- ${lookText(c)}${screensText(c)}${otherBrandsText(c)}`
883
- ];
884
- const layout = scene.layout && c.level < 2 ? unsaid(plain(scene.layout, c.clean), plain(scene.description, c.clean)) : "";
885
- if (layout) out.push(c.flowing ? sentence(capitalize(layout)) : `Arrangement: ${sentence(layout)}`);
886
- return out;
887
- }
888
- function textSection(c) {
889
- const copy = c.spec.copy;
890
- if (copy.length === 0)
891
- return c.flowing ? "The picture carries the whole ad on its own." : "Text: none; the picture is the ad.";
892
- const typePic = c.pics.find((p) => p.role === "type");
893
- const face = typePic ? `the brand typeface (Image ${typePic.index})` : c.brand?.typeface ? plain(c.brand.typeface, c.clean) : "a clean, bold sans-serif";
894
- const faceOnce = c.designedLines.length > 1;
895
- const named = faceOnce && (!typePic || c.flowing);
896
- const label = productLabel(c);
897
- const header = c.flowing ? `The ad carries exactly these ${copy.length} lines of text, each once and spelled exactly as quoted${named ? `, the designed type set in ${face}` : ""}:` : `Text: exactly these ${copy.length} lines, each once, spelled exactly as quoted; they are the only words in the image${label}${named ? `; designed type in ${face}` : ""}:`;
898
- return [
899
- header,
900
- ...copy.map((l, i) => copyLineText(l, i + 1, faceOnce ? null : face, c.clean, c.level)),
901
- ...[hierarchyText(c)].filter(Boolean),
902
- ...textNotes(copy, c.profile)
903
- ].join("\n");
904
- }
905
- function productLabel(c) {
906
- const shot = screenshotOf(c.pics);
907
- if (shot) return ` besides the words on Image ${shot.index}'s screen`;
908
- return c.pics.some((p) => p.role === "product") ? " besides the product's own label" : "";
909
- }
910
- function colourSection(c) {
911
- if (c.job !== "generate") return [];
912
- const uses = colourUses(c.spec, c.brand, c.clean);
913
- if (uses.length === 0) return [];
914
- const list = uses.map((u) => `${u.object} in ${u.name}${u.hex ? ` (${u.hex})` : ""}`).join("; ");
915
- return [c.flowing ? sentence(capitalize(list)) : `Colour: ${sentence(list)}`];
916
- }
917
- function layoutSection(c) {
918
- const items = keptInside(
919
- c.pics.some((p) => p.role === "logo"),
920
- c.spec.copy.some(isButton)
921
- );
922
- const safe = c.opts.zoned ? "" : c.opts.band?.margin ? marginText(c.opts.band, items, c.profile.brief.structure) : c.opts.band ? bandText(c.opts.band, items, c.flowing, staticSafeArea(c.format, c.opts.placements).placement !== "feed") : safeAreaText(c.format, c.opts.placements, items, c.profile.brief.structure);
923
- const out = [safe, legibilityText(c), fillText(c)].filter(Boolean).join(" ");
924
- return c.flowing ? out : `Layout: ${out}`;
925
- }
926
- function legibilityText(c) {
927
- const logo = c.pics.some((p) => p.role === "logo");
928
- if (c.spec.copy.length === 0) return logo ? "The logo sits on a ground it reads against." : "";
929
- return logo ? "Every line and the logo sit on a ground they read against." : "Every line sits on a ground it reads against.";
930
- }
931
- var MINIMAL = /\b(minimal(?:ist)?|negative space|white space|empty space|sparse|airy)\b/i;
932
- function fillText(c) {
933
- if (c.job !== "generate" || c.level >= EXTRAS_SHORT_LEVEL || c.opts.variant === 1 || c.opts.variant === 2) return "";
934
- const { scene, direction } = c.spec;
935
- if (MINIMAL.test([scene.description, scene.mood, scene.layout, direction].join(" "))) return "";
936
- return c.flowing ? "The subject and the words fill the frame with purpose." : "Fill the frame; no large empty areas.";
937
- }
938
- function directionSection(c) {
939
- if (c.job !== "generate") return [];
940
- const note = c.spec.direction && c.level < 2 ? sentence(plain(c.spec.direction, c.clean)) : "";
941
- const variant = c.opts.variant ?? 0;
942
- if (variant !== 1 && variant !== 2) return note ? [c.flowing ? capitalize(note) : `Direction: ${note}`] : [];
943
- const direction = variantDirection(c);
944
- return [c.flowing ? `This version is ${direction} ${note}`.trim() : `Direction: ${direction} ${note}`.trim()];
945
- }
946
- function positiveConstraints(c) {
947
- const n = c.spec.copy.length;
948
- const logo = c.pics.find((p) => p.role === "logo");
949
- const others = thirdPartyNames(c.spec);
950
- if (n === 0)
951
- return `The only words in the finished image are none at all: it is picture only${logo ? `, with the one logo from Image ${logo.index}` : ""}.`;
952
- const label = productLabel(c) ? `,${productLabel(c)}` : "";
953
- const onlyLogo = !logo ? "" : others ? `, and the logos are the one in Image ${logo.index} and ${others}, the only brand marks anywhere, vehicles, packaging and clothing included` : `, and the one logo is the one in Image ${logo.index}, the only brand mark anywhere, vehicles, packaging and clothing included`;
954
- return `The only words in the finished image are the ${n} quoted lines above, each exactly once; every other surface carries picture only${label}${onlyLogo}.`;
955
- }
956
- function constraintsSection(c) {
957
- if (c.profile.brief.constraints === "positive_last") return positiveConstraints(c);
958
- const n = c.spec.copy.length;
959
- const logo = c.pics.find((p) => p.role === "logo");
960
- const logos = [
961
- ...logo ? [`Image ${logo.index}`] : [],
962
- ...thirdPartyNames(c.spec) ? [thirdPartyNames(c.spec)] : []
963
- ];
964
- const repeatedWordLimit = n > 0 && c.level >= LAST_RESORT_LEVEL;
965
- const wordLimit = n > 0 ? `any words other than the ${n} quoted lines` : "any words or letters";
966
- const avoid = [
967
- // At the final trim level the Text section already states the complete word allow-list.
968
- ...repeatedWordLimit ? [] : [wordLimit],
969
- // A Toyota badge appeared on a car-loan ad's SUV (BENCH.md § "Final"): the allow-list covers the scene's things too.
970
- // The words and the identity's "show them once" are said above; brief v2 spends the words on the logo and the button.
971
- `${logos.length > 0 ? `any logo, badge, icon, seal or sticker other than ${logos.join(" and ")}` : "any logo or wordmark, badge, icon, seal or sticker"}, on vehicles, packaging and clothing too`,
972
- "watermarks",
973
- ...c.job === "generate" && isNativeCapture(c.spec) ? ["a staged, retouched or studio-lit look"] : [],
974
- // The user found the people, a dog and a phone screen "very AI" (BENCH.md § "Fix round").
975
- ...c.job !== "generate" ? [] : showsPeople(c.spec) ? ["glossy, CGI or airbrushed skin; faces in shadow"] : looksRendered(c.spec) ? ["a glossy CGI look"] : []
976
- ];
977
- return `Avoid: ${avoid.join("; ")}.`;
978
- }
979
- function assemble(c) {
980
- const continuity = c.job !== "generate" ? (conceptText(c) || deviceText(c)).trim() : "";
981
- return [
982
- deliverableSection(c),
983
- "Real-world anatomy, devices, scale, perspective and supports stay plausible. Scale proportionally; reflow text; preserve letter shapes.",
984
- // A continuation needs the original idea as well as the previous picture. Keep this
985
- // separate: the per-format adapter replaces the deliverable paragraph.
986
- ...continuity ? [continuity] : [],
987
- ...c.job === "fix" || c.job === "series" ? ["Keep any depicted app interface exact, with marketing overlays visibly separate from its native controls."] : [],
988
- ...picturesSection(c),
989
- ...sceneSection(c),
990
- textSection(c),
991
- ...colourSection(c),
992
- layoutSection(c),
993
- ...directionSection(c),
994
- constraintsSection(c)
995
- ].join("\n\n");
996
- }
997
- function briefCopyLines(prompt) {
998
- return [...prompt.matchAll(/^\d+\. "(.*)" — /gm)].map((m) => m[1] ?? "");
999
- }
1000
- function judgeBrief(brief, opts = {}) {
1001
- const whose = opts.brand ? ` for ${opts.brand}` : "";
1002
- const forWhom = opts.viewer ? `, made for ${opts.viewer}` : "";
1003
- const mechanism = opts.mechanisms && opts.mechanisms.length > 0 ? `; the ad still rests on ${opts.mechanisms.join("; ")}` : "";
1004
- return [
1005
- `You compare candidate versions of ONE static ad${whose}${forWhom}, all rendered from the BRIEF below. Compare the candidates with each other and pick the one that best delivers the brief; a candidate that fails an earlier check loses to one that passes it:`,
1006
- "1. Words: every quoted line of the brief is printed exactly once, spelled exactly (accents and symbols included), and no other words appear.",
1007
- "2. Picture: no AI artifacts on people, animals or products (hands, faces, mouths, warped objects), no seams or smears.",
1008
- "3. Space: every word, the logo and the button sit where the brief's Layout keeps them; nothing is clipped or overlapping.",
1009
- "4. One idea: one dominant element carries the idea and still reads at a glance shrunk to a phone-feed thumbnail.",
1010
- `5. Brief: the scene, the lettering of each line, the colours and the pictures are the ones the brief asks for${mechanism}.`,
1011
- `6. Brand: with any logo covered, the ad still reads as ${opts.brand ? `${opts.brand}'s` : "the brand's"}.`,
1012
- 'Compare every pair both ways before deciding. Begin `reasoning` with one line per criterion, exactly "<criterion>: Image X > Image Y > \u2026" (best first), then one sentence on why the winner won.',
1013
- "",
1014
- "BRIEF:",
1015
- brief
1016
- ].join("\n");
1017
- }
1018
- var NUMBERED_COPY = /^(\d+\. )"(.*)"( — )/gm;
1019
- var withoutCopy = (prompt) => prompt.replace(NUMBERED_COPY, '$1""$3');
1020
- var briefWords = (prompt) => wordCount(withoutCopy(prompt));
1021
-
1022
- // src/engine/scaffold/static-sizes.ts
1023
- function ratioValue(ratio) {
1024
- const [w, h] = ratio.split(":").map(Number);
1025
- if (!w || !h || !Number.isFinite(w) || !Number.isFinite(h)) throw new Error(`not a W:H ratio: "${ratio}"`);
1026
- return w / h;
1027
- }
1028
- function place(full, length, band, margin) {
1029
- const lo = Math.max(0, Math.ceil(band.end - length * (1 - margin.end) - 1e-6));
1030
- const hi = Math.min(full - length, Math.floor(band.start - length * margin.start + 1e-6));
1031
- const centred = Math.round((band.start + band.end) / 2 - length * (margin.start + 1 - margin.end) / 2);
1032
- if (lo > hi) return { at: Math.min(full - length, Math.max(0, centred)), fits: false };
1033
- return { at: Math.min(hi, Math.max(lo, centred)), fits: true };
1034
- }
1035
- function bandCropGeometry(master, target, band, targetSafe = staticSafeArea(target)) {
1036
- const r = ratioValue(target);
1037
- let width = master.width;
1038
- let height = Math.round(master.width / r);
1039
- if (height > master.height) {
1040
- height = master.height;
1041
- width = Math.round(master.height * r);
1042
- }
1043
- const y = place(
1044
- master.height,
1045
- height,
1046
- { start: band.top * master.height, end: (1 - band.bottom) * master.height },
1047
- { start: targetSafe.top, end: targetSafe.bottom }
1048
- );
1049
- const x = place(
1050
- master.width,
1051
- width,
1052
- { start: band.left * master.width, end: (1 - band.right) * master.width },
1053
- { start: targetSafe.left, end: targetSafe.right }
1054
- );
1055
- return { x: x.at, y: y.at, width, height, fits: x.fits && y.fits };
1056
- }
1057
- var MASTER_SHAPES = ["relayout", "9x16_band", "4x5_adapt"];
1058
- var DEFAULT_MASTER_SHAPE = "relayout";
1059
- var META_PIXELS = {
1060
- "9:16": { width: 1080, height: 1920 },
1061
- "4:5": { width: 1080, height: 1350 },
1062
- "1:1": { width: 1080, height: 1080 },
1063
- "16:9": { width: 1920, height: 1080 },
1064
- "1.91:1": { width: 1200, height: 628 }
1065
- };
1066
- function deliveredSize(format) {
1067
- const known = META_PIXELS[format];
1068
- if (known) return known;
1069
- const r = ratioValue(format);
1070
- return r <= 1 ? { width: 1080, height: Math.round(1080 / r) } : { width: Math.round(1080 * r), height: 1080 };
1071
- }
1072
- var ASPECT_ADAPT_FORMATS = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"];
1073
- function nearestAdaptFormat(format) {
1074
- if (ASPECT_ADAPT_FORMATS.includes(format)) return format;
1075
- const wanted = ratioValue(format);
1076
- return [...ASPECT_ADAPT_FORMATS].sort(
1077
- (a, b) => Math.abs(Math.log(ratioValue(a) / wanted)) - Math.abs(Math.log(ratioValue(b) / wanted))
1078
- )[0];
1079
- }
1080
- function masterFormat(formats, shape) {
1081
- const portrait = formats.filter((f) => ratioValue(f) <= 1);
1082
- if (portrait.length === 0) return formats[0];
1083
- if (shape === "4x5_adapt" && portrait.includes("4:5")) return "4:5";
1084
- return [...portrait].sort((a, b) => ratioValue(a) - ratioValue(b))[0];
1085
- }
1086
- function masterContentBand(master, cuts, placements) {
1087
- const own = staticSafeArea(master, placements);
1088
- const band = { top: own.top, bottom: own.bottom, left: own.left, right: own.right };
1089
- const m = ratioValue(master);
1090
- for (const cut of cuts) {
1091
- const safe = staticSafeArea(cut, placements);
1092
- band.left = Math.max(band.left, safe.left);
1093
- band.right = Math.max(band.right, safe.right);
1094
- const share = m / ratioValue(cut);
1095
- const room = share * (1 - safe.top - safe.bottom) - 2e-3;
1096
- const height = 1 - band.top - band.bottom;
1097
- if (height > room) {
1098
- const shrink = (height - room) / 2;
1099
- band.top += shrink;
1100
- band.bottom += shrink;
1101
- }
1102
- }
1103
- return band;
1104
- }
1105
- function planStaticFormats(genModel, formats, opts = {}) {
1106
- if (formats.length === 0) throw new Error("a static ad needs at least one format");
1107
- const masterShape = opts.masterShape ?? DEFAULT_MASTER_SHAPE;
1108
- if (masterShape === "relayout") return planRelayout(genModel, formats, opts.placements);
1109
- const format = masterFormat(formats, masterShape);
1110
- const size = deliveredSize(format);
1111
- const m = ratioValue(format);
1112
- const cuts = formats.filter((f) => f !== format && ratioValue(f) >= m && ratioValue(f) <= Math.max(1, m));
1113
- const band = masterContentBand(format, cuts, opts.placements);
1114
- const sizes = formats.map((f) => {
1115
- if (f === format) return { format: f, size, from: "master" };
1116
- if (cuts.includes(f)) {
1117
- const crop = bandCropGeometry(size, f, band, staticSafeArea(f, opts.placements));
1118
- if (crop.fits) return { format: f, size: deliveredSize(f), from: "crop", crop };
1119
- }
1120
- return { format: f, size: deliveredSize(f), from: "adapt", adaptFormat: nearestAdaptFormat(f) };
1121
- });
1122
- return {
1123
- masterShape,
1124
- ...opts.placements && opts.placements.length > 0 ? { placements: [...opts.placements] } : {},
1125
- master: { format, renderRatio: nearestSupportedAspectRatio("image_generate", genModel, format), size },
1126
- band,
1127
- sizes
1128
- };
1129
- }
1130
- function leadFormat(formats) {
1131
- for (const lead of ["4:5", "1:1"]) if (formats.includes(lead)) return lead;
1132
- return formats.find((f) => ratioValue(f) <= 1) ?? formats[0];
1133
- }
1134
- function planRelayout(genModel, formats, placements) {
1135
- const format = leadFormat(formats);
1136
- const size = deliveredSize(format);
1137
- const { top, bottom, left, right } = staticSafeArea(format, placements);
1138
- return {
1139
- masterShape: "relayout",
1140
- ...placements && placements.length > 0 ? { placements: [...placements] } : {},
1141
- master: { format, renderRatio: nearestSupportedAspectRatio("image_generate", genModel, format), size },
1142
- band: { top, bottom, left, right },
1143
- sizes: formats.map(
1144
- (f) => f === format ? { format: f, size, from: "master" } : { format: f, size: deliveredSize(f), from: "relayout" }
1145
- )
1146
- };
1147
- }
1148
- var LOGO_WIDTH_SHARE = 0.15;
1149
- var LOGO_ASPECT = 3;
1150
- var MAX_TEXT_OVERLAP = 0.02;
1151
- function overlapArea(a, b) {
1152
- const w = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);
1153
- const h = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);
1154
- return w > 0 && h > 0 ? w * h : 0;
1155
- }
1156
- function logoSlot(frame, safe, opts = {}) {
1157
- const area = safeRectPx(safe, frame.width, frame.height);
1158
- const width = Math.round(frame.width * LOGO_WIDTH_SHARE);
1159
- const height = Math.round(width / (opts.aspect ?? LOGO_ASPECT));
1160
- const left = area.x;
1161
- const right = area.x + area.width - width;
1162
- const centre = Math.round(area.x + (area.width - width) / 2);
1163
- const top = area.y;
1164
- const bottom = area.y + area.height - height;
1165
- const candidates = [
1166
- [left, top],
1167
- [right, top],
1168
- [left, bottom],
1169
- [right, bottom],
1170
- [centre, top],
1171
- [centre, bottom]
1172
- ].map(([x, y]) => ({ x, y, width, height }));
1173
- const boxes = opts.textBoxes ?? [];
1174
- const clear = (r) => boxes.reduce((sum, b) => sum + overlapArea(r, b), 0) <= MAX_TEXT_OVERLAP * r.width * r.height;
1175
- const rect = candidates.find(clear) ?? candidates[0];
1176
- const ground = opts.luminance?.(rect) ?? 1;
1177
- return { rect, variant: ground >= 0.5 ? "dark" : "light" };
1178
- }
1179
-
1180
- // src/engine/scaffold/static-adapt.ts
1181
- var STATIC_ADAPT_FORMAT_ROLE = "static_adapt_format";
1182
- function adaptPlan(model, format, placements) {
1183
- const renderRatio = nearestSupportedAspectRatio("image_generate", model, format);
1184
- return {
1185
- format,
1186
- ...placements && placements.length > 0 ? { placements: [...placements] } : {},
1187
- renderRatio,
1188
- size: deliveredSize(format),
1189
- safe: safeAreaOnFrame(renderRatio, format, placements)
1190
- };
1191
- }
1192
- function safeAreaOnFrame(frame, format, placements) {
1193
- const own = blockingArea(format, placements);
1194
- const rendered = ratioValue(frame);
1195
- const delivered = ratioValue(format);
1196
- const keepH = rendered < delivered ? rendered / delivered : 1;
1197
- const keepW = rendered > delivered ? delivered / rendered : 1;
1198
- return {
1199
- top: (1 - keepH) / 2 + own.top * keepH,
1200
- bottom: (1 - keepH) / 2 + own.bottom * keepH,
1201
- left: (1 - keepW) / 2 + own.left * keepW,
1202
- right: (1 - keepW) / 2 + own.right * keepW
1203
- };
1204
- }
1205
- function trimmedRenderBand(plan) {
1206
- const own = blockingArea(plan.format, plan.placements);
1207
- const trimmed = ["top", "bottom", "left", "right"].some(
1208
- (edge) => Math.abs(own[edge] - plan.safe[edge]) > 1e-9
1209
- );
1210
- return trimmed ? { area: plan.safe, cut: false, margin: true } : void 0;
1211
- }
1212
- function fillArgs(size) {
1213
- const px = `${size.width}x${size.height}`;
1214
- return [
1215
- "{{in.image}}",
1216
- "-filter",
1217
- "Lanczos",
1218
- "-resize",
1219
- `${px}^`,
1220
- "-gravity",
1221
- "center",
1222
- "-extent",
1223
- px,
1224
- "+repage",
1225
- "{{out.image}}"
1226
- ];
1227
- }
1228
- function adaptNoun(plan) {
1229
- const r = ratioValue(plan.format);
1230
- if (plan.format === "9:16") {
1231
- const safe = staticSafeArea(plan.format, plan.placements);
1232
- return safe.placement === "stories" ? "full-screen vertical ad for Stories" : safe.placement === "vertical" ? `full-screen vertical ad for ${safe.surface}` : "full-screen vertical ad for Stories and Reels";
1233
- }
1234
- if (r === 1) return "square feed ad";
1235
- return r < 1 ? "vertical feed ad" : "landscape ad";
1236
- }
1237
- function adaptDeliverable(plan, spec, flowing, ground, short) {
1238
- const brand = spec.brand?.name;
1239
- const noun = `${adaptNoun(plan)}${brand ? ` for ${brand}` : ""}`;
1240
- const background = "Extend only the background at Image 1's outer edges; textures stay on their existing surfaces.";
1241
- if (short && !flowing) {
1242
- const keep = [
1243
- ...showsPeople2(spec) ? ["each person the same face and clothes"] : [],
1244
- ...isBeforeAfter(spec) ? ["the two states as different"] : []
1245
- ];
1246
- return `Deliverable: adapt Image 1 to a ${noun}. Move and scale existing elements first; reflow the contents for this frame, keeping its words, people, product, logo, colours and typeface. ${background} Keep any depicted app interface exact, with marketing overlays visibly separate from its native controls. Every person, product and line appears whole and once${keep.length > 0 ? `, ${keep.join(", ")}` : ""}; add nothing new; never Image 1 shrunk onto a background.`;
1247
- }
1248
- const cast = [
1249
- SAME_CAST,
1250
- ...showsPeople2(spec) ? [SAME_PERSON] : [],
1251
- ...isBeforeAfter(spec) ? [SAME_CHANGE] : [],
1252
- ...ground ? [GROUND_ONLY] : []
1253
- ].join(" ");
1254
- const continuity = `Move and scale existing elements first; reflow the contents for this frame instead of pasting a smaller copy on a backdrop. Keep the visual hierarchy. ${background} Keep any depicted app interface exact, with marketing overlays visibly separate from its native controls.`;
1255
- return flowing ? `Image 1 is a finished ad. Adapt this same ad to a ${noun}, keeping its message, words, people, product, logo, colours and typeface. ${continuity} ${cast}` : `Deliverable: the ad in Image 1, re-laid out as a finished ${noun}, keeping its message, words, people, product, logo, colours and typeface; never Image 1 shrunk onto a background. ${continuity} ${cast}`;
1256
- }
1257
- var SAME_CAST = "Every person, product and line appears whole and once, the same individuals as in Image 1; add no person, animal, product or object that Image 1 does not show.";
1258
- var SAME_PERSON = "Each person is the same person as in Image 1: same face, same hair, same clothes.";
1259
- var SAME_CHANGE = "The two states it compares stay as clearly different as in Image 1.";
1260
- var showsPeople2 = (spec) => spec.references.some((r) => r.id.startsWith("PERSON_")) || spec.scene.look !== "illustration" && PEOPLE.test(`${spec.scene.description} ${spec.scene.layout ?? ""}`);
1261
- var GROUND_ONLY = "Image 1 shows no people and no product, so the room this frame adds carries only Image 1's own background and the brand's colours already present there, never a new scene.";
1262
- function wordsOnly(spec) {
1263
- return spec.references.length === 0 && spec.scene.look !== "photo" && !PEOPLE.test(`${spec.scene.description} ${spec.scene.layout ?? ""}`);
1264
- }
1265
- var isTall = (plan) => ratioValue(plan.format) < 0.7;
1266
- var LIST_ROLES = /^(list_item|bullet|row|item)$/i;
1267
- var TABLE = /\b(tables?|columns?|rows?|checklists?|lists?)\b/i;
1268
- var hasRows = (spec) => spec.copy.some((l) => LIST_ROLES.test(l.role ?? "")) || TABLE.test(`${spec.scene.description} ${spec.scene.layout ?? ""}`);
1269
- function scaleText(plan, spec, flowing, zoned, short) {
1270
- if (short && !flowing) {
1271
- return isTall(plan) && zoned ? "Scale: larger than in Image 1, the headline across most of the band's width, the words spread through the band." : "Scale: larger than in Image 1, the largest line across most of the width.";
1272
- }
1273
- if (!(isTall(plan) && zoned)) {
1274
- return flowing ? "Every line is set at the size this frame needs rather than at its size in Image 1: the largest line spans most of the width between the margins, and the words fill their space." : "Scale: set every line for this frame, not at its size in Image 1: the largest line spans most of the width between the margins; no small text in a large empty area.";
1275
- }
1276
- const rows = hasRows(spec) ? flowing ? " Rows, columns and lists are restacked for the tall frame, one item per line, each larger." : " Rows, columns and lists are restacked for the tall frame, one item per line, each larger, rather than shrunk." : "";
1277
- return flowing ? `The words are set for this frame, larger than in Image 1: the headline large enough to fill most of the band's width, and every line, the logo and the button spread through the band's height, inside its edges.${rows}` : `Scale: larger than in Image 1: the headline set large enough to fill most of the band's width; every line, the logo and the button spread through the band's height, inside its edges, never a small block in its middle.${rows}`;
1278
- }
1279
- var COVERED_HEADROOM = { top: 0.06, bottom: 0.08, side: 0.02 };
1280
- var NO_HEADROOM = { top: 0, bottom: 0, side: 0 };
1281
- var pct = (share) => `${Math.round(share * 100)}%`;
1282
- function zonesText(plan, flowing, short) {
1283
- const covered = staticSafeArea(plan.format, plan.placements).placement !== "feed";
1284
- const room = isTall(plan) && covered ? COVERED_HEADROOM : NO_HEADROOM;
1285
- const top = plan.safe.top + room.top;
1286
- const bottom = plan.safe.bottom + room.bottom;
1287
- const side = Math.max(plan.safe.left, plan.safe.right) + room.side;
1288
- const band = `from ${pct(top)} to ${pct(1 - bottom)} of the height, at least ${pct(side)} in from each side`;
1289
- if (short && !flowing) {
1290
- const around = "Image 1's existing background treatment, including any intentional flat colour";
1291
- return `Zones: every word, the logo and the button sit ${band}; around them, ${around}.`;
1292
- }
1293
- if (!(isTall(plan) && covered)) {
1294
- return flowing ? `The logo, every line and the button sit ${band}; around them the frame shows only the ad's own picture.` : `Zones: the logo, every line and the button sit ${band}; around them, only the ad's own picture.`;
1295
- }
1296
- const picture = "Image 1's existing background treatment, including any intentional flat colour";
1297
- return flowing ? `The words' band runs ${band}, holding the logo, every line and the button. Above and below it the placement's interface covers the frame: continue only ${picture}.` : `Zones: the app's buttons and captions cover the top and the bottom of this frame, hiding any word there. The logo, every line and the button sit in one band, ${band}: the headline starts below the top fifth, and the button ends directly under the last line, never at the bottom edge where a feed ad puts it. Above and below it, where the placement's interface covers the frame, continue only ${picture}.`;
1298
- }
1299
- function adaptBrief(spec, profile, opts) {
1300
- const { plan, images } = opts;
1301
- const flowing = profile.brief.structure === "flowing_sentences";
1302
- const { logo_position: _logoPosition, ...rest } = spec;
1303
- const unplaced = { ...rest, copy: spec.copy.map(({ position: _position, ...line }) => line) };
1304
- const band = trimmedRenderBand(plan);
1305
- const zoned = opts.zones !== false;
1306
- const ground = wordsOnly(spec);
1307
- const compile = (wordBudget) => compileStaticBrief(
1308
- unplaced,
1309
- void 0,
1310
- plan.format,
1311
- void 0,
1312
- { ...profile, brief: { ...profile.brief, wordBudget } },
1313
- {
1314
- job: "relay",
1315
- images: {
1316
- base: images.base,
1317
- ...images.logo ? { logo: images.logo } : {},
1318
- ...images.type ? { type: images.type } : {},
1319
- refs: images.refs ?? {}
1320
- },
1321
- ...plan.placements ? { placements: plan.placements } : {},
1322
- ...band ? { band } : {},
1323
- ...zoned ? { zoned } : {}
1324
- }
1325
- );
1326
- const relaid = (compiled2, short = false) => {
1327
- const paragraphs = compiled2.prompt.split("\n\n");
1328
- paragraphs[0] = adaptDeliverable(plan, spec, flowing, ground && !zoned, short);
1329
- const first = spec.copy[0] ? quotedCopy(spec.copy[0].text) : void 0;
1330
- const textAt = first ? paragraphs.findIndex((p) => p.includes(first)) : -1;
1331
- if (textAt >= 0) {
1332
- const added = [...zoned ? [zonesText(plan, flowing, short)] : [], scaleText(plan, spec, flowing, zoned, short)];
1333
- paragraphs.splice(textAt + 1, 0, ...added);
1334
- } else if (zoned) {
1335
- const layoutAt = paragraphs.findIndex((p) => p.startsWith("Layout:"));
1336
- paragraphs.splice(layoutAt > 0 ? layoutAt : 1, 0, zonesText(plan, flowing, short));
1337
- }
1338
- return paragraphs.join("\n\n");
1339
- };
1340
- const budget = profile.brief.wordBudget;
1341
- let compiled = compile(budget);
1342
- let prompt = relaid(compiled);
1343
- for (let pass = 0; pass < 3 && briefWords(prompt) > budget; pass++) {
1344
- compiled = compile(compiled.words - (briefWords(prompt) - budget));
1345
- prompt = relaid(compiled);
1346
- }
1347
- if (briefWords(prompt) > budget) prompt = relaid(compiled, true);
1348
- return { ...compiled, prompt, words: wordsIn(prompt) };
1349
- }
1350
- var wordsIn = (text) => text.split(/\s+/).filter(Boolean).length;
1351
-
1352
- // src/engine/lib/prompt-profiles.ts
1353
- var VEO_PERSON_GENERATION = "allow_adult";
1354
- var VEO_NEGATIVE_PROMPT = "subtitles, captions, on-screen text, watermark, logo, warped face, distorted hands, extra fingers, low quality";
1355
- var KLING_NEGATIVE_PROMPT = "warped face, distorted hands, extra fingers, morphing, flicker, on-screen text, watermark, low quality";
1356
- var KLING_CFG_SCALE = 0.7;
1357
- var SPEAKS_PROSE = (line, accent) => `The person speaks to camera; lip-sync follows the dialogue verbatim, with delivery and emotion carried in the wording itself (no bracketed cues).${accent ? ` The speech is in ${accent}.` : ""} Dialogue: "${line}"`;
1358
- var SEEDANCE_PROFILE = {
1359
- id: "seedance",
1360
- dialogueDirective: SPEAKS_PROSE,
1361
- extraDirectives: [],
1362
- // Seedance has no negative_prompt field. Phrase the anti-artifact intent as the POSITIVE
1363
- // state we want (crisp hands, one stable face, smooth coherent motion, locked identity) —
1364
- // an "Avoid: warped fingers, morphing faces" list tends to plant those very artifacts.
1365
- stabilityDirectives: [
1366
- "hands and fingers crisp and anatomically correct",
1367
- "one consistent face and identity across every frame",
1368
- "smooth, temporally coherent motion that holds steady frame to frame"
1369
- ],
1370
- keyframeInstruction: "Preserve the composition and colors of the first frame; change only the motion described.",
1371
- // ~120 words is the Seedance quality sweet spot: past it the model starts dropping beats and
1372
- // the attention budget dilutes. The validator warns above this; composite scenes that stack
1373
- // many brief `parts` are the usual offenders.
1374
- wordBudget: 120,
1375
- paramDefaults: {}
1376
- };
1377
- var VEO_PROFILE = {
1378
- id: "veo",
1379
- dialogueDirective: SPEAKS_PROSE,
1380
- // Veo's known failure mode: quoted dialogue triggers burned-in subtitles. Belt
1381
- // (inline) and braces (the negative_prompt param, below) — the param is the real lever.
1382
- extraDirectives: ["No subtitles, no captions, no on-screen text of any kind."],
1383
- stabilityDirectives: [],
1384
- // Veo takes a negative_prompt PARAM instead (paramDefaults).
1385
- keyframeInstruction: "Describe the transition and camera move between the frames; the frames fix the content.",
1386
- wordBudget: 200,
1387
- paramDefaults: { person_generation: VEO_PERSON_GENERATION, negative_prompt: VEO_NEGATIVE_PROMPT }
1388
- };
1389
- var KLING_PROFILE = {
1390
- id: "kling",
1391
- dialogueDirective: SPEAKS_PROSE,
1392
- // Belt (inline) and braces (the negative_prompt param) — the param is the real lever.
1393
- extraDirectives: ["Single, deliberate camera move \u2014 no whip pans or rapid cuts within the clip."],
1394
- stabilityDirectives: [],
1395
- // Kling takes a negative_prompt PARAM instead (paramDefaults).
1396
- keyframeInstruction: "Preserve the composition and colors of the first frame; animate the motion described.",
1397
- wordBudget: 200,
1398
- paramDefaults: { negative_prompt: KLING_NEGATIVE_PROMPT, cfg_scale: KLING_CFG_SCALE }
1399
- };
1400
- var OMNI_PROFILE = {
1401
- id: "omni",
1402
- dialogueDirective: SPEAKS_PROSE,
1403
- // Omni renders speech and ambience natively in the same pass, so the prompt
1404
- // carries the audio direction; there is no separate audio toggle to set.
1405
- extraDirectives: ["No subtitles, no captions, no on-screen text of any kind."],
1406
- // Omni exposes NO negative_prompt param — the API rejects the field outright —
1407
- // so the anti-artifact intent has to be carried positively, Seedance-style,
1408
- // rather than as an "Avoid:" list Veo would have taken as a param.
1409
- stabilityDirectives: [
1410
- "hands and fingers crisp and anatomically correct",
1411
- "one consistent face and identity across every frame",
1412
- "smooth, temporally coherent motion that holds steady frame to frame"
1413
- ],
1414
- keyframeInstruction: "Preserve the composition and colors of the first frame; change only the motion described.",
1415
- // Omni reasons over a 1M-token context and rewards a fuller brief than the
1416
- // diffusion-style models, which dilute past their sweet spot.
1417
- wordBudget: 200,
1418
- // Nothing to set. Every knob the other profiles use (negative_prompt,
1419
- // person_generation, cfg_scale) is a pre-dispatch 400 on this model.
1420
- paramDefaults: {}
1421
- };
1422
- var CLIP_PROFILES = {
1423
- "bytedance/seedance-2.0": SEEDANCE_PROFILE,
1424
- "bytedance/seedance-2.5": SEEDANCE_PROFILE,
1425
- "google/gemini-omni-1.1-flash": OMNI_PROFILE,
1426
- "google/gemini-omni-flash": OMNI_PROFILE,
1427
- "google/veo-3.1": VEO_PROFILE,
1428
- "google/veo-3.1-fast": VEO_PROFILE,
1429
- "google/veo-3.1-lite": VEO_PROFILE,
1430
- "kwaivgi/kling-v3.0-pro": KLING_PROFILE,
1431
- "x-ai/grok-imagine-video": null,
1432
- "x-ai/grok-imagine-video-1.5": null,
1433
- "alibaba/wan-2.7": null,
1434
- "alibaba/wan-3.0": null,
1435
- "runway/gen-4.5": null,
1436
- "alibaba/happyhorse-1.1": null
1437
- };
1438
- function clipProfileFor(modelId) {
1439
- return CLIP_PROFILES[modelId] ?? void 0;
1440
- }
1441
- function clipParamRecipe(model, profile, intent) {
1442
- const out = {};
1443
- if (intent === "hero" && videoModelRenders(model, "1080p")) {
1444
- out.resolution = "1080p";
1445
- }
1446
- if (intent === "hook" && profile.id === "kling") {
1447
- out.cfg_scale = 0.85;
1448
- }
1449
- return out;
1450
- }
1451
- function videoModelRenders(model, resolution) {
1452
- const schema = MODEL_REGISTRY.video_generate[model]?.params.resolution;
1453
- return schema?.kind === "string" && (schema.enum ?? []).includes(resolution);
1454
- }
1455
- function nativeDialogueOf(prompt) {
1456
- if (typeof prompt !== "string") return void 0;
1457
- const m = prompt.match(/Dialogue: "(.*)"/);
1458
- return m?.[1]?.trim() || void 0;
1459
- }
1460
- var REFERENCE_ORDER = ["base", "identity", "product", "prop", "logo", "type", "layout"];
1461
- var GPT_IMAGE_BRIEF = {
1462
- // "organize the prompt as scene, subject, details, and constraints, using labeled sections";
1463
- // "written like a creative brief" (openai §1, S1/S5 §4.6).
1464
- structure: "labelled_sections",
1465
- // Bench E1: this structure vs the pre-rebuild wrapper + blueprint JSON on 181 paired ads:
1466
- // shippable 8.3% vs 4.4%, near-clone of the source 7% vs 17%, 0 lint contradictions vs 11 codes on
1467
- // every legacy prompt, 15% cheaper (BENCH.md#E1; pairwise pending).
1468
- // Official examples run 40-150 words; the synthesis caps briefs at 150-400
1469
- // (image-prompting-research-2026.md §6, openai §1). Ours carried 1,615 (prompt-audit.md §1).
1470
- wordBudget: 400,
1471
- referenceOrder: REFERENCE_ORDER,
1472
- // Quote each line, say how many times it appears, allow-list it, spell symbols out, keep
1473
- // accents: openai §2 ("Render the tagline exactly once", "Include ONLY this card text").
1474
- text: { numberedQuotes: true, nameSymbols: true, keepAccents: true },
1475
- // Official prompts close on a Constraints block or a "No…" sentence (openai §7).
1476
- constraints: "avoid_list_last",
1477
- realism: {
1478
- // "include the word 'photorealistic' directly in the prompt" (openai §5, S5).
1479
- // "real texture (pores, wrinkles, fabric wear, imperfections)"; camera specs are "cues,
1480
- // not a guarantee" (openai §5), so capture is described in words, never figures.
1481
- native: {
1482
- photo: "photorealistic: a real, unedited phone photo",
1483
- texture: "real texture and wear on every surface, natural light, light sensor noise, unposed and unretouched",
1484
- purpose: ""
1485
- },
1486
- // Brief v2: a studio or hero shot keeps its own light; the phone cues made them candid and busy
1487
- // (BENCH.md § E1, the judge's remaining reasons), and the user found the old prompt's more polished.
1488
- produced: {
1489
- photo: "photorealistic: a real photograph",
1490
- texture: "real textures, true-to-life light and colour",
1491
- purpose: ""
1492
- },
1493
- // The user: people look "too AI", faces too dark; wants natural, real-looking people (BENCH.md § "E1 v2"),
1494
- // and again on the final sets, "more natural please" (§ "Fix round"); and real people's photos are
1495
- // taken on a phone, not a professional camera (AV2, scripts/creative-evals/avatar-realism.mjs).
1496
- people: "candid people shot on a phone: real skin, natural light, relaxed expressions"
1497
- },
1498
- // n 1: the variants are one call per direction, not one n=3 call. Same cost (n is billed per
1499
- // image, E0) and returns 3/3 either way, but one n=3 call gave >=2 distinct options in 25% of
1500
- // ads vs 39% for three calls (n=99/100; n=2 + 1 call: 31% vs 36% on 39 shared ads)
1501
- // (.context/static-review/BENCH.md#E3).
1502
- // quality max: the deterministic checks tie (shippable 9-12% on every step, n=100), but on the
1503
- // E4 gallery (30 rated pairs) the user's win rate is max 69%, xhigh 54%, high 47%, Flare 34%,
1504
- // and the judge's on the same pairs 73%, 65%, 39%, 31%. Max costs $0.166 per image vs $0.085
1505
- // for xhigh and 80 s vs 44 s (BENCH.md#E4, § "E1 v2"); a round renders twice in a row (the
1506
- // options, then each size), so three ads took most of an hour. Oct 2026 the user chose xhigh.
1507
- params: { n: 1, quality: "xhigh", imageSize: "2K" }
1508
- };
1509
- var GEMINI_IMAGE_BRIEF = {
1510
- // "Use Natural Language & Full Sentences… as if you were briefing a human artist", and
1511
- // give the why (prompting-google-gemini-image.md §2). Bench E1 on Gemini 3 Pro Image, 182 paired
1512
- // ads: shippable 6.0% vs 7.1% for the legacy prompt (within the -2pp gate), near-clone 6% vs 15%
1513
- // (BENCH.md#E1; pairwise pending).
1514
- structure: "flowing_sentences",
1515
- // Google gives no limit; the synthesis budget holds (research §6).
1516
- wordBudget: 400,
1517
- referenceOrder: REFERENCE_ORDER,
1518
- // Quote each string and name the font per line (gemini §3, Cloud guide per-line pattern).
1519
- text: { numberedQuotes: true, nameSymbols: true, keepAccents: true },
1520
- // "Use positive framing … 'empty street' instead of 'no cars'" (gemini §2).
1521
- constraints: "positive_last",
1522
- realism: {
1523
- // Capture-device language, never "hyperrealistic"/"8k" tag soup (gemini §6).
1524
- native: {
1525
- photo: "a candid photo taken on a phone",
1526
- texture: "real texture and wear on every surface, natural light, a little sensor grain, unposed and unretouched",
1527
- // Pro reasons from purpose: say why it is lo-fi (gemini §2, §6).
1528
- purpose: "because a polished picture reads as an ad and this one has to read as a real post"
1529
- },
1530
- produced: { photo: "a real photograph", texture: "real textures and true-to-life light and colour", purpose: "" },
1531
- people: "the people look real and candid, shot on a phone, with natural skin, natural light and relaxed expressions"
1532
- },
1533
- // No `n` on Gemini image (gemini §0.4); Pro 2K costs the same as 1K and small text blurs
1534
- // at 1K (gemini §0.3).
1535
- params: { n: 1, imageSize: "2K" }
1536
- };
1537
- var RECRAFT_BRIEF = {
1538
- structure: "labelled_sections",
1539
- wordBudget: 400,
1540
- referenceOrder: REFERENCE_ORDER,
1541
- text: { numberedQuotes: true, nameSymbols: true, keepAccents: true },
1542
- constraints: "positive_last",
1543
- realism: {
1544
- native: { photo: "a flat vector illustration", texture: "clean shapes and even fills", purpose: "" },
1545
- produced: { photo: "a flat vector illustration", texture: "clean shapes and even fills", purpose: "" },
1546
- people: ""
1547
- },
1548
- params: { n: 1 }
1549
- };
1550
- var GPT_IMAGE_PROFILE = {
1551
- id: "gpt-image",
1552
- constraintPlacement: "last",
1553
- photorealCue: true,
1554
- // OpenRouter forwards `quality`; gpt-image already processes inputs at high fidelity
1555
- // automatically, so we deliberately do NOT send `input_fidelity`.
1556
- //
1557
- // No `quality` here any more: it is the one default that depends on WHICH gpt-image is
1558
- // pinned. 2.5 added `xhigh` and `max` above `high`, and the older entries would refuse
1559
- // them at validate — so callers ask for the step they want through
1560
- // `bestSupportedQuality` and get the best the chosen model actually has.
1561
- paramDefaults: {},
1562
- // gpt-image-2 has no `xhigh` (openai §0.2): the brief asks for `high`, its top step.
1563
- brief: { ...GPT_IMAGE_BRIEF, params: { n: 1, quality: "high" } }
1564
- };
1565
- var GPT_IMAGE_25_PROFILE = {
1566
- ...GPT_IMAGE_PROFILE,
1567
- brief: GPT_IMAGE_BRIEF
1568
- };
1569
- var GEMINI_IMAGE_PROFILE = {
1570
- id: "gemini",
1571
- constraintPlacement: "inline",
1572
- photorealCue: true,
1573
- // NO `quality`. OpenRouter accepts the field on a Gemini image call and returns
1574
- // 200 — but its per-endpoint schema does not list it and the cost is identical
1575
- // with and without, so Google drops it: an inert knob. Worse, the registry
1576
- // (correctly) does not declare it for Gemini, so emitting it fails `baker canvas
1577
- // validate` with `unknown_param` before the call is ever made.
1578
- paramDefaults: {},
1579
- brief: GEMINI_IMAGE_BRIEF
1580
- };
1581
- var RECRAFT_IMAGE_PROFILE = {
1582
- id: "recraft",
1583
- constraintPlacement: "inline",
1584
- photorealCue: false,
1585
- paramDefaults: {},
1586
- brief: RECRAFT_BRIEF
1587
- };
1588
- var IMAGE_PROFILES = {
1589
- "openai/gpt-image-2.5-sunburst": GPT_IMAGE_25_PROFILE,
1590
- "openai/gpt-image-2": GPT_IMAGE_PROFILE,
1591
- "google/gemini-3.1-flash-image": GEMINI_IMAGE_PROFILE,
1592
- "google/gemini-3.1-flash-lite-image": GEMINI_IMAGE_PROFILE,
1593
- "google/gemini-3-pro-image": GEMINI_IMAGE_PROFILE,
1594
- "recraft/recraft-v4.1-pro-vector": RECRAFT_IMAGE_PROFILE
1595
- };
1596
- function imageProfileFor(modelId) {
1597
- return IMAGE_PROFILES[canonicalModelId(modelId)] ?? void 0;
1598
- }
1599
-
1600
- // src/engine/scaffold/lib/image-params.ts
1601
- function imageRenderParams(model, want) {
1602
- const quality = bestSupportedQuality("image_generate", model, want.quality);
1603
- return {
1604
- ...supportsParam("image_generate", model, "image_size") ? { image_size: nearestSupportedImageSize("image_generate", model, want.size) } : {},
1605
- ...imageProfileFor(model)?.paramDefaults ?? {},
1606
- ...quality ? { quality } : {}
1607
- };
1608
- }
1609
-
1610
- // src/engine/scaffold/static-topology.ts
1611
- var STATIC_VARIANTS_ROLE = "static_variants";
1612
- var STATIC_VARIANT_CALL_ROLE = "static_variant_call";
1613
- var VARIANT_PREFILTER_ROLE = "variant_prefilter";
1614
- var STATIC_PICK_ROLE = "pick";
1615
- var STATIC_MASTER_ROLE = "static_master";
1616
- var STATIC_SIZE_ROLE = "static_size";
1617
- var STATIC_ADAPT_ROLE = "static_adapt";
1618
- var STATIC_SAFE_FIT_ROLE = "static_safe_fit";
1619
- var STATIC_PLACE_ROLE = "static_place";
1620
- var LOGO_COMPOSITE_ROLE = "logo_composite";
1621
- var SERIES_LEAD_ROLE = "series_lead";
1622
- var STATIC_FIX_ROLE = "static_fix";
1623
- var LOGO_COMPOSITE_DEFAULT = false;
1624
- var ADAPT_IMAGE_SIZE = "2K";
1625
- function nodesOf(canvas) {
1626
- const nodes = canvas?.nodes;
1627
- return Array.isArray(nodes) ? nodes : [];
1628
- }
1629
- function nodesWithRole(canvas, role) {
1630
- return nodesOf(canvas).filter(
1631
- (n) => n?.tags?.role === role && typeof n.id === "string" && typeof n.type === "string"
1632
- );
1633
- }
1634
- function rolesById(canvas) {
1635
- const out = {};
1636
- for (const n of nodesOf(canvas)) {
1637
- if (typeof n?.id === "string" && typeof n.tags?.role === "string") out[n.id] = n.tags.role;
1638
- }
1639
- return out;
1640
- }
1641
- function relaidPictureRole(canvas) {
1642
- const roles = rolesById(canvas);
1643
- const final = nodesOf(canvas).find((n) => n.id === "final" && n.type === "collect");
1644
- const labels = final?.params?.labels;
1645
- const images = final?.inputs?.images;
1646
- if (!Array.isArray(labels) || !Array.isArray(images)) return {};
1647
- const at = images.findIndex((ref) => {
1648
- const from = typeof ref === "string" ? /^\$ref:([^.]+)\./.exec(ref)?.[1] : void 0;
1649
- return from !== void 0 && roles[from] === STATIC_MASTER_ROLE;
1650
- });
1651
- const format = labels[at];
1652
- return typeof format === "string" ? { [`last_${formatSlug(format)}`]: STATIC_MASTER_ROLE } : {};
1653
- }
1654
- function formatSlug(format) {
1655
- return format.replace(":", "x").replace(".", "_");
1656
- }
1657
- var EXTENT = /^(\d+)x(\d+)$/;
1658
- function staticMasterFormat(canvas) {
1659
- const master = nodesWithRole(canvas, STATIC_MASTER_ROLE)[0];
1660
- const args = master?.params?.args;
1661
- if (!Array.isArray(args)) return null;
1662
- const at = args.indexOf("-extent");
1663
- const match = typeof args[at + 1] === "string" ? args[at + 1].match(EXTENT) : null;
1664
- if (at < 0 || !match) return null;
1665
- return formatOfSize({ width: Number(match[1]), height: Number(match[2]) });
1666
- }
1667
- var KNOWN_FORMATS = ["9:16", "4:5", "1:1", "16:9", "1.91:1", ...ASPECT_ADAPT_FORMATS];
1668
- function formatOfSize(size) {
1669
- for (const format of KNOWN_FORMATS) {
1670
- const d = deliveredSize(format);
1671
- if (d.width === size.width && d.height === size.height) return format;
1672
- }
1673
- const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
1674
- const g = gcd(size.width, size.height);
1675
- return `${size.width / g}:${size.height / g}`;
1676
- }
1677
- function safeAreaOf(adaptFormat, delivered) {
1678
- const { top, bottom, left, right } = staticSafeArea(adaptFormat);
1679
- return delivered.map((f) => safeAreaOnFrame(adaptFormat, f)).reduce(
1680
- (a, b) => ({
1681
- top: Math.max(a.top, b.top),
1682
- bottom: Math.max(a.bottom, b.bottom),
1683
- left: Math.max(a.left, b.left),
1684
- right: Math.max(a.right, b.right)
1685
- }),
1686
- { top, bottom, left, right }
1687
- );
1688
- }
1689
- function magick(id, role, image, args, extra = {}) {
1690
- return {
1691
- id,
1692
- type: "imagemagick",
1693
- inputs: { image, ...extra },
1694
- params: { args, outputs: { image: { kind: "image", ext: "png" } } },
1695
- tags: { role }
1696
- };
1697
- }
1698
- function variantNodes(g) {
1699
- const render = (id, extra, tags) => ({
1700
- id,
1701
- type: "image_generate",
1702
- ...g.reference.length > 0 ? { inputs: { reference: [...g.reference] } } : {},
1703
- params: { model: g.genModel, aspect_ratio: g.plan.master.renderRatio, ...g.genParams, ...extra, prompt: g.prompt },
1704
- tags
1705
- });
1706
- if (g.takes === 1) return [render("gen", {}, { role: STATIC_VARIANTS_ROLE })];
1707
- const calls = Array.from(
1708
- { length: g.takes },
1709
- (_, i) => render(`gen_${i + 1}`, {}, { role: STATIC_VARIANT_CALL_ROLE, region: i })
1710
- );
1711
- return [
1712
- ...calls,
1713
- {
1714
- id: "gen",
1715
- type: "collect",
1716
- inputs: { images: calls.map((c) => `$ref:${c.id}.images#0`) },
1717
- tags: { role: STATIC_VARIANTS_ROLE }
1718
- }
1719
- ];
1720
- }
1721
- function staticRenderNodes(g) {
1722
- const { plan } = g;
1723
- const nodes = [
1724
- ...variantNodes(g),
1725
- {
1726
- id: "prefilter",
1727
- type: "static_prefilter",
1728
- inputs: { images: "$ref:gen.images", spec: g.specRef },
1729
- params: {
1730
- formats: plan.sizes.map((s) => s.format),
1731
- master_shape: plan.masterShape,
1732
- ...plan.placements ? { placements: [...plan.placements] } : {},
1733
- // The judge breaks a real tie only; one variant never asks it.
1734
- ...g.takes > 1 ? { judge_model: g.judgeModel, judge_prompt: g.prompt } : {}
1735
- },
1736
- tags: { role: VARIANT_PREFILTER_ROLE }
1737
- },
1738
- magick("master", STATIC_MASTER_ROLE, "$ref:prefilter.image", fillArgs(plan.master.size))
1739
- ];
1740
- const adapted = plan.sizes.filter((s) => s.from === "adapt");
1741
- const adaptFormats = [...new Set(adapted.map((s) => s.adaptFormat ?? s.format))];
1742
- if (adaptFormats.length > 0) {
1743
- nodes.push({
1744
- id: "adapt",
1745
- type: "image_aspect_adapt",
1746
- inputs: { source: "$ref:master.image" },
1747
- params: {
1748
- model: g.adaptModel,
1749
- formats: adaptFormats,
1750
- image_size: ADAPT_IMAGE_SIZE,
1751
- safe_areas: Object.fromEntries(
1752
- adaptFormats.map((f) => [
1753
- f,
1754
- safeAreaOf(
1755
- f,
1756
- adapted.filter((s) => (s.adaptFormat ?? s.format) === f).map((s) => s.format)
1757
- )
1758
- ])
1759
- )
1760
- },
1761
- tags: { role: STATIC_ADAPT_ROLE }
1762
- });
1763
- }
1764
- nodes.push(...relayoutNodes(g));
1765
- const refs = plan.sizes.map((s) => {
1766
- const slug = formatSlug(s.format);
1767
- const made = sizeNodes(s, plan, adaptFormats);
1768
- nodes.push(...made);
1769
- const last = made[made.length - 1];
1770
- const ref = last ? `$ref:${last.id}.image` : "$ref:master.image";
1771
- if (!g.compositeLogo) return ref;
1772
- const slot = logoSlot(s.size, staticSafeArea(s.format)).rect;
1773
- nodes.push(
1774
- magick(
1775
- `logo_${slug}`,
1776
- LOGO_COMPOSITE_ROLE,
1777
- ref,
1778
- [
1779
- "{{in.image}}",
1780
- "(",
1781
- "{{in.logo}}",
1782
- "-resize",
1783
- `${slot.width}x${slot.height}`,
1784
- ")",
1785
- "-gravity",
1786
- "NorthWest",
1787
- "-geometry",
1788
- `+${slot.x}+${slot.y}`,
1789
- "-composite",
1790
- "{{out.image}}"
1791
- ],
1792
- { logo: g.compositeLogo }
1793
- )
1794
- );
1795
- return `$ref:logo_${slug}.image`;
1796
- });
1797
- nodes.push({
1798
- id: "final",
1799
- type: "collect",
1800
- inputs: { images: refs },
1801
- params: { labels: plan.sizes.map((s) => s.format) }
1802
- });
1803
- return nodes;
1804
- }
1805
- function sizeNodes(s, plan, adaptFormats) {
1806
- const size = sizeNode(s, adaptFormats);
1807
- const fit = safeFitNode(s.format, size ? `$ref:${size.id}.image` : "$ref:master.image", plan.placements);
1808
- return [...size ? [size] : [], ...fit ? [fit] : []];
1809
- }
1810
- function safeFitNode(format, image, placements) {
1811
- if (staticSafeArea(format, placements).placement === "feed") return null;
1812
- return {
1813
- id: `safe_${formatSlug(format)}`,
1814
- type: "static_safe_fit",
1815
- inputs: { image },
1816
- params: { format, ...placements ? { placements: [...placements] } : {} },
1817
- tags: { role: STATIC_SAFE_FIT_ROLE }
1818
- };
1819
- }
1820
- function sizeNode(s, adaptFormats) {
1821
- const id = `size_${formatSlug(s.format)}`;
1822
- if (s.from === "crop" && s.crop) {
1823
- const c = s.crop;
1824
- const exact = c.width === s.size.width && c.height === s.size.height;
1825
- return magick(id, STATIC_SIZE_ROLE, "$ref:master.image", [
1826
- "{{in.image}}",
1827
- "-crop",
1828
- `${c.width}x${c.height}+${c.x}+${c.y}`,
1829
- "+repage",
1830
- ...exact ? [] : ["-filter", "Lanczos", "-resize", `${s.size.width}x${s.size.height}!`],
1831
- "{{out.image}}"
1832
- ]);
1833
- }
1834
- if (s.from === "relayout") {
1835
- return magick(id, STATIC_SIZE_ROLE, `$ref:adapt_${formatSlug(s.format)}.images#0`, fillArgs(s.size));
1836
- }
1837
- if (s.from !== "adapt") return null;
1838
- const index = adaptFormats.indexOf(s.adaptFormat ?? s.format);
1839
- return magick(id, STATIC_SIZE_ROLE, `$ref:adapt.images#${index}`, fillArgs(s.size));
1840
- }
1841
- var RELAYOUT_QUALITY = "xhigh";
1842
- function relayoutNodes(g) {
1843
- return g.plan.sizes.filter((s) => s.from === "relayout").map((s) => relayoutNode(s, g));
1844
- }
1845
- function relayoutNode(s, g) {
1846
- return {
1847
- id: `adapt_${formatSlug(s.format)}`,
1848
- type: "image_generate",
1849
- inputs: { reference: ["$ref:master.image", ...g.logoRef ? [g.logoRef] : []] },
1850
- params: {
1851
- model: g.relayoutModel,
1852
- aspect_ratio: adaptPlan(g.relayoutModel, s.format, g.plan.placements).renderRatio,
1853
- ...imageRenderParams(g.relayoutModel, { size: ADAPT_IMAGE_SIZE, quality: RELAYOUT_QUALITY }),
1854
- prompt: g.prompt
1855
- },
1856
- tags: { role: STATIC_ADAPT_FORMAT_ROLE }
1857
- };
1858
- }
1859
- var RENDER_GRAPH_IDS = ["gen", "prefilter", "pick", "master", "adapt", "final", "fix", "previous"];
1860
- var LEGACY_RENDER_ID = /^(?:ad(?:_[a-c])?|ad_\d+x\d+|fit_\d+x\d+)$/;
1861
- function variantRenders(canvas) {
1862
- const fanned = nodesWithRole(canvas, STATIC_VARIANT_CALL_ROLE).map((n) => n.id);
1863
- if (fanned.length > 0) return fanned;
1864
- return nodesWithRole(canvas, STATIC_VARIANTS_ROLE).filter((n) => n.type === "image_generate").map((n) => n.id);
1865
- }
1866
- function regenerateTargets(canvas, requested) {
1867
- const known = new Set(nodesOf(canvas).flatMap((n) => typeof n?.id === "string" ? [n.id] : []));
1868
- const gathered = new Set(
1869
- nodesWithRole(canvas, STATIC_VARIANTS_ROLE).filter((n) => n.type === "collect").map((n) => n.id)
1870
- );
1871
- const ids = [];
1872
- const unknown = [];
1873
- for (const name of requested) {
1874
- const variants = name === STATIC_VARIANTS_ROLE || gathered.has(name) || !known.has(name) && LEGACY_RENDER_ID.test(name);
1875
- const found = variants ? variantRenders(canvas) : known.has(name) ? [name] : nodesWithRole(canvas, name).map((n) => n.id);
1876
- if (found.length === 0) unknown.push(name);
1877
- for (const id of found) if (!ids.includes(id)) ids.push(id);
1878
- }
1879
- return { ids, unknown };
1880
- }
1881
-
1882
- // src/engine/client/backend-client.ts
1883
- function isAsyncJob(res) {
1884
- return typeof res.job_id === "string";
1885
- }
1886
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1887
- function failedJobError(error) {
1888
- const message = error.message ?? "the node failed on the server without saying why";
1889
- return new BackendHttpError(
1890
- classifyBackendError(error.status ?? 502, { ...error, retryable: error.retryable ?? false }, message)
1891
- );
1892
- }
1893
- function pollInterval(attempt) {
1894
- return attempt < 15 ? 1e3 : 3e3;
1895
- }
1896
- var JOB_POLL_MAX_MS = 20 * 60 * 1e3;
1897
- var BackendClient = class {
1898
- http;
1899
- constructor(opts) {
1900
- this.http = new HttpClient(opts);
1901
- }
1902
- async exec(req, signal) {
1903
- const res = await this.http.postJson("/api/canvas/nodes/exec", req, signal);
1904
- if (isAsyncJob(res)) {
1905
- return await this.pollJob(res.job_id, signal);
1906
- }
1907
- return res;
1908
- }
1909
- async pollJob(jobId, signal) {
1910
- const deadline = Date.now() + JOB_POLL_MAX_MS;
1911
- const path4 = `/api/canvas/jobs/${encodeURIComponent(jobId)}`;
1912
- for (let attempt = 0; ; attempt++) {
1913
- if (signal?.aborted) {
1914
- throw new BackendHttpError({ kind: "network", cause: signal.reason ?? new Error("aborted") });
1915
- }
1916
- const job = await this.http.getJson(path4, signal);
1917
- if (job.status === "completed") return job.result;
1918
- if (job.status === "failed") throw failedJobError(job.error);
1919
- if (Date.now() > deadline) {
1920
- throw new BackendHttpError({ kind: "timeout", message: `job ${jobId} did not finish in time` });
1921
- }
1922
- await sleep(pollInterval(attempt));
1923
- }
1924
- }
1925
- presignAssetUpload(sha256, mime, signal, purpose, conditional) {
1926
- return this.http.postJson(
1927
- "/api/canvas/assets/presign",
1928
- { sha256, mime, purpose, ...conditional ? { conditional: true } : {} },
1929
- signal
1930
- );
1931
- }
1932
- /** Remote cache lookup. A miss (404) — or an old backend without the route — returns null. */
1933
- async getCacheEntry(cacheKey, signal) {
1934
- try {
1935
- const res = await this.http.getJson(`/api/canvas/cache/${encodeURIComponent(cacheKey)}`, signal);
1936
- return res.entry;
1937
- } catch (e) {
1938
- if (e instanceof BackendHttpError && "status" in e.detail && e.detail.status === 404) return null;
1939
- throw e;
1940
- }
1941
- }
1942
- async putCacheEntry(entry, signal) {
1943
- await this.http.putJson(
1944
- `/api/canvas/cache/${encodeURIComponent(entry.cacheKey)}`,
1945
- entry,
1946
- signal
1947
- );
1948
- }
1949
- /** Durable run-history record — POST /api/canvas/runs (idempotent server-side on runId). */
1950
- async recordRun(payload, signal) {
1951
- await this.http.postJson("/api/canvas/runs", payload, signal);
1952
- }
1953
- /**
1954
- * Resumable-run lookup — GET /api/canvas/runs/active. Returns the newest
1955
- * still-running/interrupted run for the creative (pinned to the exact canvas
1956
- * sha), so a fresh sandbox can adopt its run id and re-attach billed
1957
- * in-flight jobs. Null on no active run — or an older backend without the
1958
- * route (both 404).
1959
- */
1960
- async getActiveRun(creativeSlug, canvasSha, signal) {
1961
- const params = new URLSearchParams({ slug: creativeSlug });
1962
- if (canvasSha) params.set("sha", canvasSha);
1963
- try {
1964
- const res = await this.http.getJson(`/api/canvas/runs/active?${params}`, signal);
1965
- return res.run;
1966
- } catch (e) {
1967
- if (e instanceof BackendHttpError && "status" in e.detail && e.detail.status === 404) return null;
1968
- throw e;
1969
- }
1970
- }
1971
- /**
1972
- * Portable-rerun lookup — GET /api/canvas/runs/latest. The newest run for
1973
- * the creative that recorded a canvas snapshot manifest; null when none
1974
- * exists (or the backend predates the route — both 404).
1975
- */
1976
- async getLatestSnapshotRun(creativeSlug, signal) {
1977
- const params = new URLSearchParams({ slug: creativeSlug });
1978
- try {
1979
- const res = await this.http.getJson(`/api/canvas/runs/latest?${params}`, signal);
1980
- return res.run;
1981
- } catch (e) {
1982
- if (e instanceof BackendHttpError && "status" in e.detail && e.detail.status === 404) return null;
1983
- throw e;
1984
- }
1985
- }
1986
- /**
1987
- * Chat-scoped blueprint sync — POST /api/creatives/definition. Lets the
1988
- * dashboard draw a scaffolded creative's workflow graph BEFORE the first run.
1989
- * Additive on the backend (never archives siblings, never sets definitionPath).
1990
- */
1991
- async syncCreativeDefinition(payload, signal) {
1992
- await this.http.postJson("/api/creatives/definition", payload, signal);
1993
- }
1994
- getArtifact(kind, name, version, signal) {
1995
- const path4 = version ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}` : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;
1996
- return this.http.getJson(path4, signal);
1997
- }
1998
- };
1999
-
2000
- // src/engine/engine/validator.ts
2001
- import path3 from "path";
2002
- import { z as z2 } from "zod";
2003
-
2004
- // src/engine/schema/canvas.ts
2005
- import { z } from "zod";
2006
- var REF_PREFIX = "$ref:";
2007
- function parseRefExpr(input) {
2008
- if (!input.startsWith(REF_PREFIX)) return null;
2009
- const rest = input.slice(REF_PREFIX.length);
2010
- const hashIdx = rest.indexOf("#");
2011
- const head = hashIdx === -1 ? rest : rest.slice(0, hashIdx);
2012
- const tail = hashIdx === -1 ? null : rest.slice(hashIdx + 1);
2013
- const dot = head.indexOf(".");
2014
- if (dot <= 0) return null;
2015
- const nodeId = head.slice(0, dot);
2016
- const output = head.slice(dot + 1);
2017
- if (!nodeId || !output) return null;
2018
- let index;
2019
- if (tail !== null) {
2020
- const n = Number(tail);
2021
- if (tail === "" || !Number.isInteger(n) || n < 0) return null;
2022
- index = n;
2023
- }
2024
- return { kind: "ref", nodeId, output, index };
2025
- }
2026
- var NodeIdRe = /^[a-z][a-z0-9_]*$/;
2027
- var NodeTags = z.object({
2028
- /** The scene (0-based, in blueprint order) this node renders or voices. */
2029
- scene: z.number().int().min(0).optional(),
2030
- /** The node's job in the assembly, e.g. `clip`, `frame`, `composite`, `spine`, `overlay`, `mux`. */
2031
- role: z.string().min(1).optional(),
2032
- /** The region index inside a composited (split / picture-in-picture) scene. */
2033
- region: z.number().int().min(0).optional(),
2034
- /** True for a speaker's own on-camera voice (lip-synced), false for a voice read over the picture. */
2035
- on_camera: z.boolean().optional()
2036
- }).strict();
2037
- var NodeDecl = z.object({
2038
- id: z.string().regex(NodeIdRe, "node id must be lower_snake_case"),
2039
- type: z.string().min(1),
2040
- version: z.string().min(1).optional(),
2041
- inputs: z.record(z.string(), z.unknown()).optional(),
2042
- params: z.record(z.string(), z.unknown()).optional(),
2043
- // Regenerate knob. The engine is content-addressed: identical params + inputs
2044
- // return the cached render, so re-running an unchanged node NEVER re-bills or
2045
- // produces a new result. Bump this token (any string/number — a `2`, a `"v3"`,
2046
- // a note) and re-run to force THIS node to render fresh; because its new output
2047
- // changes downstream input hashes, everything depending on it regenerates too.
2048
- // This is the declarative "change a value, re-run, get a new render" affordance.
2049
- regenerate: z.union([z.string(), z.number()]).optional(),
2050
- tags: NodeTags.optional()
2051
- }).strict();
2052
- var OutputRef = z.object({
2053
- node: z.string(),
2054
- output: z.string()
2055
- }).strict();
2056
- var VideoMeta = z.object({
2057
- duration_s: z.number(),
2058
- // The one-clock contract: the spine's summed clip lengths and every audio
2059
- // timeline reachable from the final mux must equal `expected_total_s` — the
2060
- // validator recomputes both from the LIVE graph (`-shortest` at the final mux
2061
- // silently truncates the longer stream on any mismatch). Emitted by
2062
- // scaffold-video; optional so hand-authored canvases still parse.
2063
- timeline: z.object({
2064
- spine_node: z.string(),
2065
- expected_total_s: z.number(),
2066
- audio_mix_node: z.string().optional()
2067
- }).strict().optional(),
2068
- // Each sequenced voiceover turn on the absolute timeline.
2069
- vo_segments: z.array(
2070
- z.object({
2071
- slot: z.string(),
2072
- start_s: z.number(),
2073
- end_s: z.number(),
2074
- scene: z.number().optional(),
2075
- speaker: z.string().optional()
2076
- })
2077
- ).default([]),
2078
- // Scenes with a single on-camera speaker, voiced NATIVELY by the video model
2079
- // (Seedance generate_audio) then re-voiced to one brand voice. Each entry names
2080
- // the `audio_voice_convert` node the validator confirms is present — the
2081
- // native-audio replacement for the retired post-hoc lipsync check. A bare scene
2082
- // index (legacy) or `{ scene, lipsync_node }` (legacy veed canvases) still parse.
2083
- talking_scenes: z.array(
2084
- z.union([
2085
- z.number(),
2086
- z.object({
2087
- scene: z.number(),
2088
- voice_convert_node: z.string(),
2089
- // Advisory: the scene's visual length vs the estimated spoken length, so
2090
- // a reviewer can see a native line that may run past its cut. Not gated.
2091
- scene_s: z.number().optional(),
2092
- est_speech_s: z.number().optional(),
2093
- // Word count of the line est_speech_s was measured for. Together they carry
2094
- // the speaker's OBSERVED pace (from the deconstruct's word timings), so the
2095
- // overrun check budgets re-authored lines at the real rate, not a wps guess.
2096
- speech_words: z.number().optional()
2097
- }),
2098
- z.object({ scene: z.number(), lipsync_node: z.string() })
2099
- ])
2100
- ).default([]),
2101
- // Advisory, NOT gated: scenes where a presenter's later same-shot turns play as
2102
- // `tts` over their one native clip (a held shot can hold only ONE lip-synced clip,
2103
- // and voiceover turns can't overlap), so the lips may not match those words. The
2104
- // scaffold surfaces it so the agent can cut away or rely on the burned-in captions.
2105
- lip_sync_caution: z.array(z.object({ scene: z.number(), speaker: z.string(), tts_over_native: z.array(z.string()) })).optional(),
2106
- // Advisory, NOT gated by the validator: the reviewable "which graphic fires
2107
- // on which spoken beat" map emitted by scaffold-video (per-scene window,
2108
- // spoken line, storyboard frames, scheduled graphics). Free-form rows so the
2109
- // schema stays decoupled from the scaffold's exact shape.
2110
- motion_board: z.array(z.unknown()).optional(),
2111
- // The recurring identity elements the scaffold wired (one shared reference slot
2112
- // per cast member / pet / product / logo). The reference-completeness check reads
2113
- // this to warn when a frame's description mentions an element whose ref isn't
2114
- // wired on that frame node (the wrong-identity-animal failure).
2115
- elements: z.array(
2116
- z.object({
2117
- ref: z.string(),
2118
- label: z.string(),
2119
- type: z.string(),
2120
- description: z.string().optional()
2121
- })
2122
- ).optional(),
2123
- // Per video_generate node: the scene's natural visual span. The validator warns
2124
- // when a clip's span exceeds the assigned model's max clip duration (e.g. a 9.2s
2125
- // scene on Veo, which caps at 8s) — the clip would truncate.
2126
- clip_spans: z.array(z.object({ node: z.string(), span_s: z.number() })).optional(),
2127
- // WHY the graph has the nodes it has: one route per scene (clip / still_hold /
2128
- // screen_still / graphic_plate / brand_card / phrase_slice / phrase_run /
2129
- // composite) + a node-type histogram, stamped by scaffold-video. Read this first
2130
- // when a canvas looks big.
2131
- graph_stats: z.object({
2132
- nodes_total: z.number(),
2133
- nodes_billable: z.number(),
2134
- node_types: z.record(z.string(), z.number()),
2135
- scene_routes: z.array(z.object({ scene: z.number(), route: z.string(), window_s: z.number() }))
2136
- }).strict().optional()
2137
- }).strict().optional();
2138
- var CanvasMetadata = z.object({
2139
- name: z.string().optional(),
2140
- description: z.string().optional(),
2141
- // Free-form, human-facing next-steps guide embedded by scaffolds (e.g.
2142
- // `scaffold-video`) so the editable checklist travels inside the canvas
2143
- // instead of only printing to stdout. Ignored by the engine.
2144
- todo: z.unknown().optional(),
2145
- video: VideoMeta,
2146
- // A static ad of a series (`seriesMemberAd`): the lead ad its picture is an edit of, the
2147
- // lead's version, and the lead's words as that version drew them, which this ad's replace.
2148
- // Ignored by the engine; the brief projection reads it.
2149
- series: z.object({
2150
- lead: z.string().min(1),
2151
- run_id: z.string().min(1),
2152
- copy: z.array(z.object({ text: z.string(), role: z.string().optional() }).strict()),
2153
- design: z.string().optional(),
2154
- // A translation of the lead: the language (BCP-47) its words are in.
2155
- language: z.string().min(2).optional()
2156
- }).strict().optional()
2157
- }).strict().optional();
2158
- var CanvasSchema = z.object({
2159
- schema: z.literal("baker-canvas/1"),
2160
- cache_salt: z.string().optional(),
2161
- metadata: CanvasMetadata,
2162
- nodes: z.array(NodeDecl).min(1),
2163
- output: OutputRef.optional()
2164
- }).strict();
2165
-
2166
- // src/engine/schema/prompts.ts
2167
- var SLOT_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
2168
- function extractSlots(template) {
2169
- const out = /* @__PURE__ */ new Set();
2170
- for (const match of template.matchAll(SLOT_RE)) {
2171
- const name = match[1];
2172
- if (name) out.add(name);
2173
- }
2174
- return [...out];
2175
- }
2176
- function renderTemplate(template, values) {
2177
- return template.replace(SLOT_RE, (_full, name) => {
2178
- if (Object.hasOwn(values, name)) {
2179
- return values[name] ?? "";
2180
- }
2181
- return "";
2182
- });
2183
- }
2184
-
2185
- // src/engine/engine/credits.ts
2186
- function priceNode(def, params) {
2187
- if (!def.cost) return null;
2188
- try {
2189
- const estimate = def.cost({ params });
2190
- return Number.isFinite(estimate.credits) ? estimate : null;
2191
- } catch {
2192
- return null;
2193
- }
2194
- }
2195
- function estimateNodeCredits(def, params) {
2196
- if (!def?.cost) return 0;
2197
- const parsed = def.params.safeParse(params ?? {});
2198
- if (!parsed.success) return 0;
2199
- return priceNode(def, parsed.data)?.credits ?? 0;
2200
- }
2201
- function estimateCanvasCredits(canvas, registry) {
2202
- return canvas.nodes.map((n) => ({
2203
- node_id: n.id,
2204
- node_type: n.type,
2205
- credits: estimateNodeCredits(registry.get(n.type), n.params)
2206
- }));
2207
- }
2208
- function sumCredits(perNode) {
2209
- return perNode.reduce((total, n) => total + n.credits, 0);
2210
- }
2211
-
2212
- // src/engine/engine/didyoumean.ts
2213
- function distance(a, b) {
2214
- const m = a.length;
2215
- const n = b.length;
2216
- if (m === 0) return n;
2217
- if (n === 0) return m;
2218
- const dp = makeDp(m, n);
2219
- for (let i = 1; i <= m; i++) fillRow(dp, a, b, i, n);
2220
- return dp[m][n] ?? 0;
2221
- }
2222
- function makeDp(m, n) {
2223
- const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
2224
- for (let i = 0; i <= m; i++) dp[i][0] = i;
2225
- for (let j = 0; j <= n; j++) dp[0][j] = j;
2226
- return dp;
2227
- }
2228
- function fillRow(dp, a, b, i, n) {
2229
- const rowI = dp[i];
2230
- const rowPrev = dp[i - 1];
2231
- const rowPrev2 = i >= 2 ? dp[i - 2] : void 0;
2232
- for (let j = 1; j <= n; j++) {
2233
- rowI[j] = stepCost(a, b, i, j, rowI, rowPrev, rowPrev2);
2234
- }
2235
- }
2236
- function stepCost(a, b, i, j, rowI, rowPrev, rowPrev2) {
2237
- const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
2238
- const del = (rowPrev[j] ?? 0) + 1;
2239
- const ins = (rowI[j - 1] ?? 0) + 1;
2240
- const sub = (rowPrev[j - 1] ?? 0) + cost;
2241
- let best = Math.min(del, ins, sub);
2242
- if (i > 1 && j > 1 && rowPrev2 && a.charCodeAt(i - 1) === b.charCodeAt(j - 2) && a.charCodeAt(i - 2) === b.charCodeAt(j - 1)) {
2243
- best = Math.min(best, (rowPrev2[j - 2] ?? 0) + 1);
2244
- }
2245
- return best;
2246
- }
2247
- function didYouMean(query, candidates, max = 3) {
2248
- if (candidates.length === 0 || !query) return [];
2249
- const threshold = Math.max(1, Math.floor(query.length / 3) + 1);
2250
- const scored = [];
2251
- for (const c of candidates) {
2252
- const d = distance(query.toLowerCase(), c.toLowerCase());
2253
- if (d <= threshold) scored.push({ c, d });
2254
- }
2255
- scored.sort((a, b) => a.d - b.d || a.c.localeCompare(b.c));
2256
- return scored.slice(0, max).map((s) => s.c);
2257
- }
2258
-
2259
- // src/engine/engine/graph.ts
2260
- function nodeDependencies(node, known) {
2261
- const deps = /* @__PURE__ */ new Set();
2262
- const collect = (s) => {
2263
- const expr = parseRefExpr(s);
2264
- if (expr && known.has(expr.nodeId)) deps.add(expr.nodeId);
2265
- };
2266
- forEachString(node.inputs, collect);
2267
- forEachString(node.params, collect);
2268
- return deps;
2269
- }
2270
- function buildDependencyGraph(canvas) {
2271
- const known = new Set(canvas.nodes.map((n) => n.id));
2272
- const graph = /* @__PURE__ */ new Map();
2273
- for (const n of canvas.nodes) graph.set(n.id, /* @__PURE__ */ new Set());
2274
- for (const n of canvas.nodes) {
2275
- const deps = graph.get(n.id);
2276
- if (deps) for (const dep of nodeDependencies(n, known)) deps.add(dep);
2277
- }
2278
- return graph;
2279
- }
2280
-
2281
- // src/engine/engine/lint/context.ts
2282
- function refNodeOf(ctx, value) {
2283
- if (typeof value !== "string" || !value.startsWith(REF_PREFIX)) return null;
2284
- const parsed = parseRefExpr(value);
2285
- if (!parsed) return null;
2286
- const idx = ctx.idToIndex.get(parsed.nodeId);
2287
- return idx === void 0 ? null : ctx.canvas.nodes[idx];
2288
- }
2289
- function isTagged(ctx) {
2290
- return ctx.canvas.nodes.some((n) => n.tags !== void 0);
2291
- }
2292
- function onVideoCanvas(check) {
2293
- return (ctx) => {
2294
- const meta = ctx.canvas.metadata?.video;
2295
- if (meta) check(ctx, meta);
2296
- };
2297
- }
2298
-
2299
- // src/engine/engine/lint/rules/audio.ts
2300
- var CODES = {
2301
- DUCK_UNITS: "AUDIO_DUCK_UNITS",
2302
- MUSIC_BED_TOO_LOUD: "AUDIO_MUSIC_BED_TOO_LOUD",
2303
- MUSIC_BED_NOT_DUCKED: "AUDIO_MUSIC_BED_NOT_DUCKED",
2304
- MUSIC_BED_SCORED_TO_ARC: "AUDIO_MUSIC_BED_SCORED_TO_ARC"
2305
- };
2306
- var MUSIC_BED_MAX_GAIN_DB = -18;
2307
- var DUCK_MIN_ATTACK_MS = 0.5;
2308
- var DUCK_MIN_RELEASE_MS = 10;
2309
- var SPEECH_NODE_TYPES = /* @__PURE__ */ new Set(["tts", "dialogue", "audio_voice_convert", "video_lipsync"]);
2310
- var SPEECH_SLOT_RE = /voice|speech|\bvo\b|^vo|_vo|dialog|narration/i;
2311
- var MUSIC_SLOT_RE = /music|score|\bbed\b/i;
2312
- function audioTimelineTracks(node) {
2313
- const tracks = node.params?.tracks;
2314
- if (!Array.isArray(tracks)) return [];
2315
- return tracks.filter(
2316
- (t) => typeof t === "object" && t !== null && typeof t.slot === "string"
2317
- );
2318
- }
2319
- function checkAudioBedMix(ctx) {
2320
- for (const n of ctx.canvas.nodes) {
2321
- if (n.type === "audio_timeline") checkOneAudioMix(ctx, n);
2322
- }
2323
- }
2324
- function checkOneAudioMix(ctx, node) {
2325
- const tracks = audioTimelineTracks(node);
2326
- if (tracks.length === 0) return;
2327
- const duck = node.params?.duck;
2328
- checkDuckUnits(ctx, node, duck);
2329
- const sourceType = (slot) => refNodeOf(ctx, node.inputs?.[slot])?.type ?? null;
2330
- const speech = tracks.filter((t) => {
2331
- const type = sourceType(t.slot);
2332
- return type !== null && SPEECH_NODE_TYPES.has(type) || SPEECH_SLOT_RE.test(t.slot);
2333
- });
2334
- if (speech.length === 0) return;
2335
- const beds = tracks.filter(
2336
- (t) => (sourceType(t.slot) === "music" || MUSIC_SLOT_RE.test(t.slot)) && t.duration_s === void 0
2337
- );
2338
- if (beds.length === 0) return;
2339
- checkBedLevels(ctx, node, beds);
2340
- checkBedDucked(ctx, node, beds, speech, duck);
2341
- for (const bed of beds) checkBedNotScored(ctx, node, refNodeOf(ctx, node.inputs?.[bed.slot]));
2342
- }
2343
- var MUSIC_ARC_RE = /\b(?:emotional arc|follows? (?:her|his|their|the) arc|swell(?:s|ing)?|crescendo|climax|build(?:s|ing) (?:to|into)|resolv(?:e|es|ing)|dramatic|epic|tense|tension)\b/i;
2344
- function checkBedNotScored(ctx, node, source) {
2345
- if (source?.type !== "music") return;
2346
- const params = source.params;
2347
- if (params?.model !== "elevenlabs/video-background-music-v1") return;
2348
- const brief = [params?.description, params?.prompt].filter((v) => typeof v === "string").join(" ");
2349
- const found = MUSIC_ARC_RE.exec(brief);
2350
- if (!found) return;
2351
- ctx.issues.push({
2352
- path: `nodes[${ctx.idToIndex.get(source.id) ?? -1}].params.description`,
2353
- code: CODES.MUSIC_BED_SCORED_TO_ARC,
2354
- severity: "warning",
2355
- node_id: source.id,
2356
- node_type: source.type,
2357
- message: `"${source.id}" briefs the bed with "${found[0]}", and "${node.id}" mixes it under speech \u2014 this model watches the cut, so movement asked for here comes back as a film score (a testimonial briefed that way was rejected as "muy dram\xE1tica"). Under a voice, name ONE steady character and forbid movement: no build, no drop, no climax, nothing that follows the arc`
2358
- });
2359
- }
2360
- function checkDuckUnits(ctx, node, duck) {
2361
- const index = ctx.idToIndex.get(node.id) ?? -1;
2362
- if (typeof duck?.attack === "number" && duck.attack < DUCK_MIN_ATTACK_MS) {
2363
- ctx.issues.push({
2364
- path: `nodes[${index}].params.duck.attack`,
2365
- code: CODES.DUCK_UNITS,
2366
- node_id: node.id,
2367
- node_type: node.type,
2368
- message: `"${node.id}" ducks with attack ${duck.attack} \u2014 this parameter is MILLISECONDS, so that is instantaneous. Use 5-50 (default 5)`
2369
- });
2370
- }
2371
- if (typeof duck?.release === "number" && duck.release < DUCK_MIN_RELEASE_MS) {
2372
- ctx.issues.push({
2373
- path: `nodes[${index}].params.duck.release`,
2374
- code: CODES.DUCK_UNITS,
2375
- node_id: node.id,
2376
- node_type: node.type,
2377
- message: `"${node.id}" ducks with release ${duck.release} \u2014 this parameter is MILLISECONDS, so the bed jumps back to full level between syllables and the mix sounds like the music is too loud. Use 200-800 (default 300); 0.45 is a seconds value`
2378
- });
2379
- }
2380
- }
2381
- function checkBedLevels(ctx, node, beds) {
2382
- const index = ctx.idToIndex.get(node.id) ?? -1;
2383
- for (const bed of beds) {
2384
- if ((bed.gain_db ?? 0) <= MUSIC_BED_MAX_GAIN_DB) continue;
2385
- const level = bed.gain_db === void 0 ? "full level (no gain_db)" : `${bed.gain_db} dB`;
2386
- ctx.issues.push({
2387
- path: `nodes[${index}].params.tracks`,
2388
- code: CODES.MUSIC_BED_TOO_LOUD,
2389
- severity: "warning",
2390
- node_id: node.id,
2391
- node_type: node.type,
2392
- message: `"${node.id}" runs the music bed "${bed.slot}" at ${level} under speech \u2014 a bed belongs at ${MUSIC_BED_MAX_GAIN_DB} dB or below (the scaffolder pins -20; -13 was rejected by a client as too loud). If a silent tail sounds empty, add the same bed as a SECOND windowed track (start_s + duration_s) with its own higher gain instead of raising this one`
2393
- });
2394
- }
2395
- }
2396
- function checkBedDucked(ctx, node, beds, speech, duck) {
2397
- if (beds.some((bed) => bed.slot === duck?.track)) return;
2398
- const quoted = (list) => list.map((t) => `"${t.slot}"`).join(", ");
2399
- ctx.issues.push({
2400
- path: `nodes[${ctx.idToIndex.get(node.id) ?? -1}].params.duck`,
2401
- code: CODES.MUSIC_BED_NOT_DUCKED,
2402
- severity: "warning",
2403
- node_id: node.id,
2404
- node_type: node.type,
2405
- message: `"${node.id}" mixes a music bed (${quoted(beds)}) under speech (${quoted(speech)}) with no sidechain duck on it \u2014 a flat bed fights the voice in every gap. Add params.duck { track: "${beds[0]?.slot}", against: [${quoted(speech)}] }`
2406
- });
2407
- }
2408
- var audioBedMixRule = {
2409
- name: "audio-bed-mix",
2410
- codes: {
2411
- [CODES.DUCK_UNITS]: "error",
2412
- [CODES.MUSIC_BED_TOO_LOUD]: "warning",
2413
- [CODES.MUSIC_BED_NOT_DUCKED]: "warning",
2414
- [CODES.MUSIC_BED_SCORED_TO_ARC]: "warning"
2415
- },
2416
- check: checkAudioBedMix
2417
- };
2418
-
2419
- // src/engine/engine/lint/rules/image.ts
2420
- var CODES2 = {
2421
- IMAGE_PROFILE_MISSING: "IMAGE_PROMPT_PROFILE_MISSING",
2422
- PARALLEL_FORMAT_RENDERS: "IMAGE_PARALLEL_FORMAT_RENDERS"
2423
- };
2424
- function checkImageProfile(ctx) {
2425
- for (const n of ctx.canvas.nodes) {
2426
- if (n.type !== "image_generate") continue;
2427
- const model = n.params?.model;
2428
- if (typeof model !== "string" || imageProfileFor(model)) continue;
2429
- ctx.issues.push({
2430
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.model`,
2431
- code: CODES2.IMAGE_PROFILE_MISSING,
2432
- severity: "warning",
2433
- node_id: n.id,
2434
- message: `"${n.id}" renders on "${model}", an image model with no frame-prompt profile \u2014 it got the generic template (constraint placement / photoreal cue not tuned). Add an ImageModelProfile for this model`
2435
- });
2436
- }
2437
- }
2438
- var SAME_CREATIVE_PROMPT_SIMILARITY = 0.6;
2439
- function promptWordSet(text) {
2440
- return new Set(
2441
- text.toLowerCase().split(/[^a-z0-9']+/).filter((w) => w.length >= 3)
2442
- );
2443
- }
2444
- function jaccard(a, b) {
2445
- if (a.size === 0 || b.size === 0) return 0;
2446
- let shared = 0;
2447
- for (const w of a) if (b.has(w)) shared++;
2448
- return shared / (a.size + b.size - shared);
2449
- }
2450
- function parallelFormatRenderGroups(nodes) {
2451
- const parent = nodes.map((_, i) => i);
2452
- const find = (i) => {
2453
- let root = i;
2454
- while (parent[root] !== root) root = parent[root];
2455
- return root;
2456
- };
2457
- const words = nodes.map((n) => promptWordSet(n.prompt));
2458
- for (let i = 0; i < nodes.length; i++) {
2459
- for (let j = i + 1; j < nodes.length; j++) {
2460
- const a = nodes[i];
2461
- const b = nodes[j];
2462
- if (a.ratio === b.ratio) continue;
2463
- if (jaccard(words[i], words[j]) < SAME_CREATIVE_PROMPT_SIMILARITY) continue;
2464
- parent[find(i)] = find(j);
2465
- }
2466
- }
2467
- const groups = /* @__PURE__ */ new Map();
2468
- for (let i = 0; i < nodes.length; i++) {
2469
- const root = find(i);
2470
- const group = groups.get(root) ?? [];
2471
- group.push(nodes[i]);
2472
- groups.set(root, group);
2473
- }
2474
- return [...groups.values()].filter((g) => g.length > 1);
2475
- }
2476
- var STATIC_RENDER_ROLES = /* @__PURE__ */ new Set([
2477
- STATIC_VARIANTS_ROLE,
2478
- STATIC_VARIANT_CALL_ROLE,
2479
- STATIC_FIX_ROLE,
2480
- STATIC_ADAPT_FORMAT_ROLE
2481
- ]);
2482
- function checkParallelFormatRenders(ctx) {
2483
- const candidates = [];
2484
- for (const n of ctx.canvas.nodes) {
2485
- if (n.type !== "image_generate" || STATIC_RENDER_ROLES.has(n.tags?.role ?? "")) continue;
2486
- const params = n.params;
2487
- const prompt = params?.prompt;
2488
- if (typeof prompt !== "string" || prompt.length === 0) continue;
2489
- candidates.push({ id: n.id, prompt, ratio: String(params?.aspect_ratio ?? "(unset)") });
2490
- }
2491
- if (candidates.length < 2) return;
2492
- for (const group of parallelFormatRenderGroups(candidates)) {
2493
- const ids = group.map((g) => g.id);
2494
- const ratios = [...new Set(group.map((g) => g.ratio))].sort();
2495
- ctx.issues.push({
2496
- path: "nodes",
2497
- code: CODES2.PARALLEL_FORMAT_RENDERS,
2498
- severity: "warning",
2499
- message: `${ids.join(", ")} render the same creative once per aspect ratio (${ratios.join(", ")}) \u2014 each is an independent generation, so the formats come back with different type, crop, and color and stop reading as one ad. Keep ONE hero render and add a terminal image_aspect_adapt node fed from it (params.formats or params.platform) so every ratio is re-framed from the same image`
2500
- });
2501
- }
2502
- }
2503
- var parallelFormatRendersRule = {
2504
- name: "parallel-format-renders",
2505
- codes: { [CODES2.PARALLEL_FORMAT_RENDERS]: "warning" },
2506
- check: checkParallelFormatRenders
2507
- };
2508
- var imageProfileRule = {
2509
- name: "image-profile",
2510
- codes: { [CODES2.IMAGE_PROFILE_MISSING]: "warning" },
2511
- check: checkImageProfile
2512
- };
2513
-
2514
- // src/engine/engine/lint/rules/video-prompt.ts
2515
- var CODES3 = {
2516
- PROMPT_PROFILE_MISSING: "VIDEO_PROMPT_PROFILE_MISSING",
2517
- PROMPT_DECISION_MISSING: "VIDEO_PROMPT_DECISION_MISSING",
2518
- HOOK_LAYER_MISSING: "VIDEO_HOOK_LAYER_MISSING",
2519
- PERSON_GENERATION_MISSING: "VIDEO_PERSON_GENERATION_MISSING",
2520
- PROMPT_OVER_BUDGET: "VIDEO_PROMPT_OVER_BUDGET",
2521
- RAW_FACE_KEYFRAME: "VIDEO_RAW_FACE_KEYFRAME",
2522
- REFERENCE_MISSING: "VIDEO_REFERENCE_MISSING",
2523
- MODEL_IGNORES_INPUT: "VIDEO_MODEL_IGNORES_INPUT",
2524
- UI_IN_PROMPT: "VIDEO_UI_IN_PROMPT",
2525
- BRANDMARK_IN_PROMPT: "VIDEO_BRANDMARK_IN_PROMPT"
2526
- };
2527
- function checkPromptProfile(ctx) {
2528
- for (const n of ctx.canvas.nodes) {
2529
- if (n.type !== "video_generate") continue;
2530
- const model = n.params?.model;
2531
- if (typeof model !== "string" || clipProfileFor(model)) continue;
2532
- ctx.issues.push({
2533
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.model`,
2534
- code: CODES3.PROMPT_PROFILE_MISSING,
2535
- severity: "warning",
2536
- node_id: n.id,
2537
- message: `"${n.id}" runs on "${model}", a model with no clip-prompt profile \u2014 its prompt was authored with the default Seedance syntax. Review the dialogue/emotion markup for this model before billing`
2538
- });
2539
- }
2540
- }
2541
- var TECHNIQUE_CUE = /\b(camera|push[- ]?in|pull[- ]?out|pan|dolly|zoom|tilt|track|handheld|locked|orbit|crane|whip|motion|move)\b/i;
2542
- var NEGATIVES_CUE = /\bavoid:|\bno subtitles|\bnegative\b|keep it clean and stable/i;
2543
- var VIBE_CUE = /\b(light|lighting|golden|moody|warm|cool|tone|grade|contrast|shadow|glow|rim|backlit|neon|soft|harsh)\b/i;
2544
- function checkPromptDecisions(ctx) {
2545
- for (const n of ctx.canvas.nodes) {
2546
- if (n.type !== "video_generate") continue;
2547
- const params = n.params;
2548
- const prompt = typeof params?.prompt === "string" ? params.prompt : "";
2549
- if (!prompt) continue;
2550
- const hasTechnique = TECHNIQUE_CUE.test(prompt);
2551
- const hasNegatives = NEGATIVES_CUE.test(prompt) || typeof params?.negative_prompt === "string";
2552
- if (hasTechnique || hasNegatives) continue;
2553
- ctx.issues.push({
2554
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,
2555
- code: CODES3.PROMPT_DECISION_MISSING,
2556
- severity: "warning",
2557
- node_id: n.id,
2558
- message: `"${n.id}" is a thin clip prompt \u2014 it names no camera/motion move (TECHNIQUE) and no constraints (NEGATIVES: an affirmative "Keep it clean and stable" line, a negative_prompt param, or an "Avoid:" tail). Make the six decisions (route / spec / beats / copy / technique / negatives) so the model doesn't fill the gaps with drift. See prompt-anatomy.md`
2559
- });
2560
- }
2561
- }
2562
- function checkHookLayers(ctx) {
2563
- const scene0 = isTagged(ctx) ? ctx.canvas.nodes.filter((n) => n.tags?.scene === 0) : ctx.canvas.nodes.filter((n) => /^s0(?:[_a-z0-9])*_/.test(n.id) || n.id === "s0");
2564
- const hookClip = scene0.find((n) => n.type === "video_generate");
2565
- if (!hookClip) return;
2566
- const scene0Text = scene0.map((n) => {
2567
- const p = n.params?.prompt;
2568
- return typeof p === "string" ? p : "";
2569
- }).join("\n");
2570
- const hookParams = hookClip.params;
2571
- const hasOverlayLayer = ctx.canvas.nodes.some((n) => /hyperframe/.test(n.type));
2572
- const hasSound = hookParams?.generate_audio === true || /Dialogue:|Audio:/.test(scene0Text) || ctx.canvas.nodes.some((n) => n.type === "tts" || n.type === "dialogue" || n.type === "music");
2573
- const hasVisual = scene0Text.trim().length > 40;
2574
- const hasVibe = VIBE_CUE.test(scene0Text);
2575
- const missing = [];
2576
- if (!hasOverlayLayer) missing.push("text (overlay)");
2577
- if (!hasSound) missing.push("sound (line/SFX)");
2578
- if (!hasVisual) missing.push("visual (the frame)");
2579
- if (!hasVibe) missing.push("vibe (lighting/tone)");
2580
- if (missing.length < 2) return;
2581
- ctx.issues.push({
2582
- path: `nodes[${ctx.idToIndex.get(hookClip.id) ?? -1}]`,
2583
- code: CODES3.HOOK_LAYER_MISSING,
2584
- severity: "warning",
2585
- node_id: hookClip.id,
2586
- message: `the hook (scene 0) is thin across layers \u2014 missing ${missing.join(", ")}. A scroll-stopping hook works on all four layers (text / sound / visual / vibe); strengthen the missing ones. See hook-craft.md`
2587
- });
2588
- }
2589
- function checkPersonGeneration(ctx) {
2590
- for (const n of ctx.canvas.nodes) {
2591
- if (n.type !== "video_generate") continue;
2592
- const params = n.params;
2593
- const model = params?.model;
2594
- if (clipProfileFor(typeof model === "string" ? model : "")?.id !== "veo") continue;
2595
- const hasKeyframe = Boolean(n.inputs?.first_frame ?? n.inputs?.reference);
2596
- if (!hasKeyframe || params?.person_generation) continue;
2597
- ctx.issues.push({
2598
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params`,
2599
- code: CODES3.PERSON_GENERATION_MISSING,
2600
- severity: "warning",
2601
- node_id: n.id,
2602
- message: `Veo clip "${n.id}" drives from a keyframe but sets no person_generation \u2014 set "allow_adult" (the only legal value for image-to-video, and the only one allowed in the EU/UK)`
2603
- });
2604
- }
2605
- }
2606
- function checkPromptBudget(ctx) {
2607
- for (const n of ctx.canvas.nodes) {
2608
- if (n.type !== "video_generate") continue;
2609
- const params = n.params;
2610
- const model = params?.model;
2611
- const prompt = params?.prompt;
2612
- const profile = typeof model === "string" ? clipProfileFor(model) : void 0;
2613
- if (!profile || typeof prompt !== "string") continue;
2614
- const words = prompt.trim().split(/\s+/).filter(Boolean).length;
2615
- if (words <= profile.wordBudget) continue;
2616
- ctx.issues.push({
2617
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,
2618
- code: CODES3.PROMPT_OVER_BUDGET,
2619
- severity: "warning",
2620
- node_id: n.id,
2621
- message: `"${n.id}" prompt is ${words} words \u2014 over ${model}'s ~${profile.wordBudget}-word budget; trim it (the frames carry the content, so keep the clip prompt to motion + audio)`
2622
- });
2623
- }
2624
- }
2625
- function checkRawFaceKeyframe(ctx) {
2626
- for (const n of ctx.canvas.nodes) {
2627
- if (n.type !== "video_generate") continue;
2628
- const model = n.params?.model;
2629
- if (clipProfileFor(typeof model === "string" ? model : "")?.id !== "seedance") continue;
2630
- const src = refNodeOf(ctx, n.inputs?.first_frame ?? n.inputs?.reference);
2631
- if (src?.type !== "ingest") continue;
2632
- ctx.issues.push({
2633
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].inputs.first_frame`,
2634
- code: CODES3.RAW_FACE_KEYFRAME,
2635
- severity: "warning",
2636
- node_id: n.id,
2637
- message: `Seedance clip "${n.id}" animates a RAW ingested image ("${src.id}"), not a generated frame \u2014 if it shows a real human face, ByteDance's real-person filter rejects it (422). Anchor on an AI-generated portrait (recast), or route this clip to Veo`
2638
- });
2639
- }
2640
- }
2641
- var ELEMENT_TYPE_KEYWORDS = {
2642
- animal: ["dog", "puppy", "pup", "cat", "kitten", "kitty", "pet", "canine", "feline"],
2643
- logo: ["logo", "wordmark"],
2644
- badge: ["badge", "seal"],
2645
- product: []
2646
- };
2647
- var CHECKED_ELEMENT_TYPES = new Set(Object.keys(ELEMENT_TYPE_KEYWORDS));
2648
- var KEYWORD_STOPWORDS = /* @__PURE__ */ new Set([
2649
- "the",
2650
- "and",
2651
- "with",
2652
- "for",
2653
- "brand",
2654
- "logo",
2655
- "image",
2656
- "shot",
2657
- "main",
2658
- "hero",
2659
- "element",
2660
- "product",
2661
- "reference"
2662
- ]);
2663
- function keywordTokens(text) {
2664
- if (!text) return [];
2665
- return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 3 && !KEYWORD_STOPWORDS.has(t));
2666
- }
2667
- function elementMentionKeywords(el) {
2668
- const typeWords = ELEMENT_TYPE_KEYWORDS[el.type.toLowerCase()] ?? [];
2669
- return [.../* @__PURE__ */ new Set([...typeWords, ...keywordTokens(el.label), ...keywordTokens(el.description)])];
2670
- }
2671
- function keywordsForElement(el) {
2672
- return elementMentionKeywords(el);
2673
- }
2674
- function containsWord(text, word) {
2675
- const esc = word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2676
- return new RegExp(`\\b${esc}\\b`, "i").test(text);
2677
- }
2678
- var FRAME_DESCRIPTION_START = "FRAME DESCRIPTION (this frame's editable prompt):";
2679
- var FRAME_DESCRIPTION_END = "Render exactly what the FRAME DESCRIPTION";
2680
- function frameDescriptionOf(prompt) {
2681
- const i = prompt.indexOf(FRAME_DESCRIPTION_START);
2682
- if (i < 0) return prompt;
2683
- const rest = prompt.slice(i + FRAME_DESCRIPTION_START.length);
2684
- const j = rest.indexOf(FRAME_DESCRIPTION_END);
2685
- return j < 0 ? rest : rest.slice(0, j);
2686
- }
2687
- function checkFrameReferences(ctx, node, index, keyworded) {
2688
- const rawPrompt = node.params?.prompt;
2689
- if (typeof rawPrompt !== "string" || rawPrompt.length === 0) return;
2690
- const prompt = frameDescriptionOf(rawPrompt);
2691
- const inputsBlob = JSON.stringify(node.inputs ?? {});
2692
- for (const { el, keywords } of keyworded) {
2693
- if (inputsBlob.includes(el.ref)) continue;
2694
- const hit = keywords.find((kw) => containsWord(prompt, kw));
2695
- if (!hit) continue;
2696
- ctx.issues.push({
2697
- path: `nodes[${index}].inputs.reference`,
2698
- code: CODES3.REFERENCE_MISSING,
2699
- severity: "warning",
2700
- message: `frame "${node.id}" describes a "${hit}" but the "${el.label}" reference (${el.ref}) isn't wired on this node \u2014 the generator will invent a wrong-identity ${el.type} from prose. Add ${el.ref} to inputs.reference`,
2701
- node_id: node.id,
2702
- node_type: node.type
2703
- });
2704
- }
2705
- }
2706
- function checkReferenceCompleteness(ctx, meta) {
2707
- const elements = (meta.elements ?? []).filter((el) => CHECKED_ELEMENT_TYPES.has(el.type.toLowerCase()));
2708
- const keyworded = elements.map((el) => ({ el, keywords: keywordsForElement(el) })).filter((e) => e.keywords.length > 0);
2709
- if (keyworded.length === 0) return;
2710
- const sheetSources = JSON.stringify(
2711
- ctx.canvas.nodes.filter((n) => n.type === "image_reference_sheet").map((n) => n.inputs ?? {})
2712
- );
2713
- for (let i = 0; i < ctx.canvas.nodes.length; i++) {
2714
- const n = ctx.canvas.nodes[i];
2715
- if (n?.type !== "image_generate" || sheetSources.includes(`"$ref:${n.id}.`)) continue;
2716
- checkFrameReferences(ctx, n, i, keyworded);
2717
- }
2718
- }
2719
- function checkUnusableLastFrame(ctx) {
2720
- for (const n of ctx.canvas.nodes) {
2721
- if (n.type !== "video_generate") continue;
2722
- const model = n.params?.model;
2723
- if (typeof model !== "string" || supportsLastFrame(model)) continue;
2724
- const inputs = n.inputs;
2725
- if (!inputs?.last_frame) continue;
2726
- ctx.issues.push({
2727
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].inputs.last_frame`,
2728
- code: CODES3.MODEL_IGNORES_INPUT,
2729
- severity: "warning",
2730
- node_id: n.id,
2731
- node_type: "video_generate",
2732
- message: `"${n.id}" wires a last_frame but runs on ${model}, which takes only ONE conditioning image \u2014 the end keyframe is dropped and the image generated for it is wasted. Drop the last_frame, or move this clip to Veo/Seedance, which honour both frames`
2733
- });
2734
- }
2735
- }
2736
- var UI_IN_PROMPT_RE = /\bscreen[- ]?(?:recording|capture|grab|share)\b|\bapp (?:interface|screen)\b|\bphone screen overlay\b/i;
2737
- function checkUiInPrompt(ctx) {
2738
- for (const n of ctx.canvas.nodes) {
2739
- if (n.type !== "video_generate") continue;
2740
- const prompt = n.params?.prompt;
2741
- if (typeof prompt !== "string" || !UI_IN_PROMPT_RE.test(prompt)) continue;
2742
- ctx.issues.push({
2743
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,
2744
- code: CODES3.UI_IN_PROMPT,
2745
- severity: "warning",
2746
- message: `"${n.id}" asks the video model to render a screen/UI surface \u2014 generative video garbles UI text and chrome. Composite the real screen on the overlay layer (screenshot / brand HTML) and keep this prompt to the background plate`,
2747
- node_id: n.id,
2748
- node_type: "video_generate"
2749
- });
2750
- }
2751
- }
2752
- var BRANDMARK_IN_PROMPT_RE = /\b(?:logo|wordmark) (?:overlay|animation|sting|card|reveal)\b|\b(?:google|facebook|instagram|tiktok|youtube|amazon|apple|microsoft|whatsapp|netflix|spotify|excel|trustpilot) (?:logo|wordmark|branding)\b/i;
2753
- function checkBrandmarkInPrompt(ctx) {
2754
- for (const n of ctx.canvas.nodes) {
2755
- if (n.type !== "video_generate" && n.type !== "image_generate") continue;
2756
- const prompt = n.params?.prompt;
2757
- if (typeof prompt !== "string" || !BRANDMARK_IN_PROMPT_RE.test(prompt)) continue;
2758
- ctx.issues.push({
2759
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,
2760
- code: CODES3.BRANDMARK_IN_PROMPT,
2761
- severity: "warning",
2762
- message: `"${n.id}" asks the model to render a brand logo/wordmark \u2014 generation garbles marks and third-party logos carry IP exposure. Source the real mark (baker images logo <domain>) and composite it on the overlay layer`,
2763
- node_id: n.id,
2764
- node_type: n.type
2765
- });
2766
- }
2767
- }
2768
- var promptProfileRule = {
2769
- name: "video-prompt-profile",
2770
- codes: { [CODES3.PROMPT_PROFILE_MISSING]: "warning" },
2771
- check: checkPromptProfile
2772
- };
2773
- var personGenerationRule = {
2774
- name: "video-person-generation",
2775
- codes: { [CODES3.PERSON_GENERATION_MISSING]: "warning" },
2776
- check: checkPersonGeneration
2777
- };
2778
- var promptBudgetRule = {
2779
- name: "video-prompt-budget",
2780
- codes: { [CODES3.PROMPT_OVER_BUDGET]: "warning" },
2781
- check: checkPromptBudget
2782
- };
2783
- var rawFaceKeyframeRule = {
2784
- name: "video-raw-face-keyframe",
2785
- codes: { [CODES3.RAW_FACE_KEYFRAME]: "warning" },
2786
- check: checkRawFaceKeyframe
2787
- };
2788
- var promptDecisionsRule = {
2789
- name: "video-prompt-decisions",
2790
- codes: { [CODES3.PROMPT_DECISION_MISSING]: "warning" },
2791
- check: checkPromptDecisions
2792
- };
2793
- var hookLayersRule = {
2794
- name: "video-hook-layers",
2795
- codes: { [CODES3.HOOK_LAYER_MISSING]: "warning" },
2796
- check: checkHookLayers
2797
- };
2798
- var uiInPromptRule = {
2799
- name: "video-ui-in-prompt",
2800
- codes: { [CODES3.UI_IN_PROMPT]: "warning" },
2801
- check: onVideoCanvas((ctx) => checkUiInPrompt(ctx))
2802
- };
2803
- var brandmarkInPromptRule = {
2804
- name: "video-brandmark-in-prompt",
2805
- codes: { [CODES3.BRANDMARK_IN_PROMPT]: "warning" },
2806
- check: onVideoCanvas((ctx) => checkBrandmarkInPrompt(ctx))
2807
- };
2808
- var referenceCompletenessRule = {
2809
- name: "video-reference-completeness",
2810
- codes: { [CODES3.REFERENCE_MISSING]: "warning" },
2811
- check: onVideoCanvas(checkReferenceCompleteness)
2812
- };
2813
- var unusableLastFrameRule = {
2814
- name: "video-unusable-last-frame",
2815
- codes: { [CODES3.MODEL_IGNORES_INPUT]: "warning" },
2816
- check: onVideoCanvas((ctx) => checkUnusableLastFrame(ctx))
2817
- };
2818
-
2819
- // src/engine/engine/lint/rules/video-timeline.ts
2820
- import { readFile } from "fs/promises";
2821
- import path from "path";
2822
-
2823
- // src/engine/lib/speech.ts
2824
- var NATIVE_CLIP_WORDS_PER_SECOND = 2.5;
2825
- var READ_WORDS_PER_SECOND = 3.06;
2826
- function wordCount2(text) {
2827
- return text.trim().split(/\s+/).filter(Boolean).length;
2828
- }
2829
- function estSpeechS(text) {
2830
- return wordCount2(text) / NATIVE_CLIP_WORDS_PER_SECOND;
2831
- }
2832
- var OBSERVED_WPS_MIN = 1;
2833
- var OBSERVED_WPS_MAX = 6;
2834
- function estSpeechWindowS(text, startS, endS) {
2835
- const words = wordCount2(text);
2836
- const window = (endS ?? 0) - (startS ?? 0);
2837
- if (words > 0 && window > 0.3) {
2838
- const wps = words / window;
2839
- if (wps >= OBSERVED_WPS_MIN && wps <= OBSERVED_WPS_MAX) return window;
2840
- }
2841
- return estSpeechS(text);
2842
- }
2843
-
2844
- // src/engine/lib/spine-input.ts
2845
- function parseSpineInputLens(args) {
2846
- const lens = /* @__PURE__ */ new Map();
2847
- let inputIndex = 0;
2848
- for (let i = 0; i < args.length; i++) {
2849
- if (args[i] !== "-i") continue;
2850
- if (args[i - 2] === "-t") {
2851
- const v = Number(args[i - 1]);
2852
- if (Number.isFinite(v)) lens.set(inputIndex, v);
2853
- }
2854
- inputIndex++;
2855
- }
2856
- const graph = args.join(" ");
2857
- for (const m of graph.matchAll(/\[(\d+):v\][^;[]*?trim=(?:start=[\d.]+:)?duration=([\d.]+)/g)) {
2858
- const idx = Number(m[1]);
2859
- const v = Number(m[2]);
2860
- if (Number.isFinite(idx) && Number.isFinite(v)) lens.set(idx, v);
2861
- }
2862
- return lens;
2863
- }
2864
-
2865
- // src/engine/engine/lint/rules/video-timeline.ts
2866
- var CODES4 = {
2867
- VO_OVERLAP: "VIDEO_VO_OVERLAP",
2868
- AUDIO_DURATION: "VIDEO_AUDIO_DURATION",
2869
- LIPSYNC_MISSING: "VIDEO_LIPSYNC_MISSING",
2870
- SPEECH_OVERRUN: "VIDEO_SPEECH_OVERRUN",
2871
- SPEECH_EXCEEDS_EXTRACT: "VIDEO_SPEECH_EXCEEDS_EXTRACT",
2872
- ASPECT_MISMATCH: "VIDEO_ASPECT_MISMATCH",
2873
- SPAN_EXCEEDS_MODEL: "VIDEO_SPAN_EXCEEDS_MODEL",
2874
- TIMELINE_TOTAL: "VIDEO_TIMELINE_TOTAL_MISMATCH",
2875
- NATIVE_SEG_OVERLAP: "VIDEO_NATIVE_SEG_OVERLAP",
2876
- SPINE_UNNORMALIZED: "VIDEO_SPINE_UNNORMALIZED",
2877
- ODD_DIMENSIONS: "VIDEO_ODD_DIMENSIONS",
2878
- REGION_DROPPED: "VIDEO_REGION_DROPPED",
2879
- OVERLAY_OUT_OF_BOUNDS: "VIDEO_OVERLAY_OUT_OF_BOUNDS"
2880
- };
2881
- var SPAN_MODEL_SLACK_S = 0.25;
2882
- var VIDEO_TIME_SLACK_S = 0.75;
2883
- var SPEECH_OVERRUN_RATIO = 1.6;
2884
- function ffmpegTrimSeconds(node) {
2885
- const args = node.params?.args;
2886
- if (!Array.isArray(args)) return null;
2887
- let t = null;
2888
- for (let i = 0; i < args.length - 1; i++) {
2889
- if (args[i] === "-t") {
2890
- const v = Number(args[i + 1]);
2891
- if (Number.isFinite(v)) t = v;
2892
- }
2893
- }
2894
- return t;
2895
- }
2896
- function liveNodeDurationS(ctx, node, depth = 0) {
2897
- if (!node || depth > 3) return null;
2898
- if (node.type === "video_generate") {
2899
- const d = node.params?.duration;
2900
- return typeof d === "number" ? d : null;
2901
- }
2902
- if (node.type === "ffmpeg") {
2903
- const t = ffmpegTrimSeconds(node);
2904
- if (t !== null) return t;
2905
- for (const v of Object.values(node.inputs ?? {})) {
2906
- const upstream = refNodeOf(ctx, v);
2907
- const d = liveNodeDurationS(ctx, upstream, depth + 1);
2908
- if (d !== null) return d;
2909
- }
2910
- return null;
2911
- }
2912
- return null;
2913
- }
2914
- function overlayTimingIssues(html, durationS) {
2915
- const out = [];
2916
- for (const m of html.matchAll(/<[^>]*\bdata-start="([\d.]+)"[^>]*\bdata-dur="([\d.]+)"[^>]*>/g)) {
2917
- const start = Number(m[1]);
2918
- const dur = Number(m[2]);
2919
- if (!Number.isFinite(start) || !Number.isFinite(dur)) continue;
2920
- if (start >= durationS || start + dur > durationS + 0.25) out.push({ start, dur });
2921
- }
2922
- return out;
2923
- }
2924
- async function checkOverlayTimingBounds(canvas, issues) {
2925
- const durationS = canvas.metadata?.video?.duration_s;
2926
- if (typeof durationS !== "number") return;
2927
- for (let i = 0; i < canvas.nodes.length; i++) {
2928
- const n = canvas.nodes[i];
2929
- if (n?.type !== "hyperframe_render") continue;
2930
- const composition = n.params?.composition;
2931
- if (typeof composition !== "string" || !path.isAbsolute(composition)) continue;
2932
- const html = await readFile(path.join(composition, "index.html"), "utf8").catch(() => null);
2933
- if (!html) continue;
2934
- for (const w of overlayTimingIssues(html, durationS)) {
2935
- issues.push({
2936
- path: `nodes[${i}].params.composition`,
2937
- code: CODES4.OVERLAY_OUT_OF_BOUNDS,
2938
- severity: "warning",
2939
- node_id: n.id,
2940
- node_type: n.type,
2941
- message: `an overlay in ${path.basename(composition)}/index.html runs [${w.start}s, ${w.start + w.dur}s] but the video ends at ${durationS}s \u2014 it never hides on screen. Re-time its data-start/data-dur to fit the video`
2942
- });
2943
- }
2944
- }
2945
- }
2946
- function sceneClips(ctx, scene) {
2947
- if (isTagged(ctx)) return ctx.canvas.nodes.filter((n) => n.type === "video_generate" && n.tags?.scene === scene);
2948
- const nativeClipRe = new RegExp(`^s${scene}(_r\\d+)?_clip$`);
2949
- return ctx.canvas.nodes.filter((n) => nativeClipRe.test(n.id) && n.type === "video_generate");
2950
- }
2951
- function nativeAudioReachesMix(ctx, scene) {
2952
- if (isTagged(ctx)) {
2953
- return ctx.canvas.nodes.some(
2954
- (n) => n.type === "audio_timeline" && Object.values(n.inputs ?? {}).some((v) => {
2955
- const source = refNodeOf(ctx, v);
2956
- return source?.tags?.scene === scene && (source.tags.role === "voice_extract" || source.tags.role === "clip");
2957
- })
2958
- );
2959
- }
2960
- const wanted = [
2961
- `$ref:s${scene}_voextract.audio`,
2962
- `$ref:s${scene}_clip.audio`,
2963
- new RegExp(`\\$ref:s${scene}_r\\d+_(voextract|clip)\\.audio`)
2964
- ];
2965
- return ctx.canvas.nodes.some((n) => {
2966
- if (n.type !== "audio_timeline") return false;
2967
- const blob = JSON.stringify(n.inputs ?? {});
2968
- return wanted.some((w) => typeof w === "string" ? blob.includes(w) : w.test(blob));
2969
- });
2970
- }
2971
- function talkingSceneSatisfied(ctx, entry, scene) {
2972
- const nodes = ctx.canvas.nodes;
2973
- if (typeof entry === "object" && "voice_convert_node" in entry) {
2974
- const clipNativeAudio = sceneClips(ctx, scene).some((n) => {
2975
- const params = n.params;
2976
- if (typeof params?.model === "string" && !supportsParam("video_generate", params.model, "generate_audio")) {
2977
- return true;
2978
- }
2979
- return params?.generate_audio === true;
2980
- });
2981
- const converted = nodes.some((n) => n.id === entry.voice_convert_node && n.type === "audio_voice_convert");
2982
- return clipNativeAudio && (converted || nativeAudioReachesMix(ctx, scene));
2983
- }
2984
- if (typeof entry === "object") {
2985
- return nodes.some((n) => n.id === entry.lipsync_node && n.type === "video_lipsync");
2986
- }
2987
- return nodes.some((n) => {
2988
- if (n.type !== "video_lipsync") return false;
2989
- const video = n.inputs?.video;
2990
- return video === `$ref:s${scene}_trim.video` || video === `$ref:s${scene}_clip.video`;
2991
- });
2992
- }
2993
- var TIMING_MAX_DEPTH = 4;
2994
- function typedTiming(ctx, node, depth = 0) {
2995
- const def = ctx.registry.get(node.type);
2996
- if (!def?.staticTiming || depth > TIMING_MAX_DEPTH) return null;
2997
- const parsed = def.params.safeParse(node.params ?? {});
2998
- if (!parsed.success) return null;
2999
- const inputs = {};
3000
- for (const [slot, value] of Object.entries(node.inputs ?? {})) {
3001
- const upstream = refNodeOf(ctx, value);
3002
- if (!upstream) continue;
3003
- const timing = typedTiming(ctx, upstream, depth + 1) ?? legacyTiming(ctx, upstream);
3004
- if (timing) inputs[slot] = timing;
3005
- }
3006
- return def.staticTiming({ params: parsed.data, inputs });
3007
- }
3008
- function legacyTiming(ctx, node) {
3009
- const d = liveNodeDurationS(ctx, node);
3010
- return d === null ? null : { duration_s: d };
3011
- }
3012
- function spineIdOf(ctx, meta) {
3013
- if (meta.timeline?.spine_node) return meta.timeline.spine_node;
3014
- const tagged = ctx.canvas.nodes.find((n) => n.tags?.role === "spine");
3015
- if (tagged) return tagged.id;
3016
- return ctx.idToIndex.has("spine") ? "spine" : null;
3017
- }
3018
- function spineTotalS(ctx, spine) {
3019
- let total = 0;
3020
- const inputs = Object.entries(spine.inputs ?? {}).filter(([k]) => /^c\d+$/.test(k));
3021
- if (inputs.length === 0) return null;
3022
- const rawArgs = spine.params.args;
3023
- const absorbed = Array.isArray(rawArgs) ? parseSpineInputLens(rawArgs.map(String)) : /* @__PURE__ */ new Map();
3024
- for (const [k, v] of inputs) {
3025
- const inputIdx = Number(k.slice(1));
3026
- const d = absorbed.get(inputIdx) ?? liveNodeDurationS(ctx, refNodeOf(ctx, v));
3027
- if (d === null) return null;
3028
- total += d;
3029
- }
3030
- const graph = spine.params.args?.join?.(" ") ?? "";
3031
- for (const m of String(graph).matchAll(/xfade=transition=[^:]+:duration=([\d.]+)/g)) {
3032
- total -= Number(m[1]);
3033
- }
3034
- return total;
3035
- }
3036
- function checkTimelineContract(ctx, meta) {
3037
- const stamp = meta.timeline;
3038
- const spineId = spineIdOf(ctx, meta);
3039
- if (!spineId) return;
3040
- const expected = stamp?.expected_total_s ?? meta.duration_s;
3041
- checkSpineTotal(ctx, spineId, expected);
3042
- checkAudioTimelineTotals(ctx, stamp?.audio_mix_node, expected);
3043
- }
3044
- function checkSpineTotal(ctx, spineId, expected) {
3045
- const spineIdx = ctx.idToIndex.get(spineId);
3046
- const spine = spineIdx === void 0 ? void 0 : ctx.canvas.nodes[spineIdx];
3047
- if (!spine || spineIdx === void 0) return;
3048
- if (ctx.registry.get(spine.type)?.staticTiming) {
3049
- checkTypedSpineTotal(ctx, spine, spineIdx, expected);
3050
- return;
3051
- }
3052
- const spineLen = spineTotalS(ctx, spine);
3053
- if (spineLen === null || Math.abs(spineLen - expected) <= VIDEO_TIME_SLACK_S) return;
3054
- ctx.issues.push({
3055
- path: `nodes[${spineIdx}].params.args`,
3056
- code: CODES4.TIMELINE_TOTAL,
3057
- node_id: spineId,
3058
- message: `the picture sums to ${round2(spineLen)}s but the timeline is pinned to ${expected}s \u2014 \`-shortest\` at the final mux will silently cut the longer stream. Re-time the seg trims (or the audio total_ms) so both match`
3059
- });
3060
- }
3061
- function checkTypedSpineTotal(ctx, spine, spineIdx, expected) {
3062
- const spineLen = typedTiming(ctx, spine)?.duration_s;
3063
- if (spineLen === void 0 || Math.abs(spineLen - expected) <= VIDEO_TIME_SLACK_S) return;
3064
- ctx.issues.push({
3065
- path: `nodes[${spineIdx}].params.clips`,
3066
- code: CODES4.TIMELINE_TOTAL,
3067
- node_id: spine.id,
3068
- message: `the picture sums to ${round2(spineLen)}s but the timeline is pinned to ${expected}s. The final mux ends at the shorter stream and silently cuts the longer one. Re-time the clips' duration_s (or the audio total_ms) so both match`
3069
- });
3070
- }
3071
- function checkAudioTimelineTotals(ctx, audioMixNode, expected) {
3072
- const mustBeFullLength = /* @__PURE__ */ new Set();
3073
- if (audioMixNode) mustBeFullLength.add(audioMixNode);
3074
- ctx.canvas.nodes.forEach((n) => {
3075
- if (n.type !== "audio_voice_convert") return;
3076
- const track = refNodeOf(ctx, n.inputs?.audio);
3077
- if (track?.type === "audio_timeline") mustBeFullLength.add(track.id);
3078
- });
3079
- for (const id of mustBeFullLength) {
3080
- const idx = ctx.idToIndex.get(id);
3081
- const node = idx === void 0 ? null : ctx.canvas.nodes[idx];
3082
- const totalMs = node?.params?.total_ms;
3083
- if (typeof totalMs !== "number") continue;
3084
- if (Math.abs(totalMs / 1e3 - expected) > VIDEO_TIME_SLACK_S) {
3085
- ctx.issues.push({
3086
- path: `nodes[${idx}].params.total_ms`,
3087
- code: CODES4.TIMELINE_TOTAL,
3088
- node_id: id,
3089
- message: `audio timeline "${id}" is pinned to ${totalMs}ms but the picture timeline is ${expected}s \u2014 \`-shortest\` at the final mux will silently cut the longer stream. Pin total_ms to ${Math.round(expected * 1e3)}`
3090
- });
3091
- }
3092
- }
3093
- }
3094
- function checkNativeSegOverlap(ctx) {
3095
- for (const conv of ctx.canvas.nodes) {
3096
- if (conv.type !== "audio_voice_convert") continue;
3097
- const track = refNodeOf(ctx, conv.inputs?.audio);
3098
- if (track?.type !== "audio_timeline") continue;
3099
- const params = track.params;
3100
- const windows = (params.tracks ?? []).map((t) => {
3101
- const extract = refNodeOf(ctx, track.inputs?.[t.slot]);
3102
- const len = t.duration_s ?? (extract ? ffmpegTrimSeconds(extract) : null);
3103
- return len === null ? null : { slot: t.slot, start: t.start_s, end: t.start_s + len };
3104
- }).filter((w) => w !== null).sort((a, b) => a.start - b.start);
3105
- for (let i = 1; i < windows.length; i++) {
3106
- const prev = windows[i - 1];
3107
- const cur = windows[i];
3108
- if (!prev || !cur || cur.start >= prev.end - 0.01) continue;
3109
- const trackIdx = ctx.idToIndex.get(track.id);
3110
- ctx.issues.push({
3111
- path: `nodes[${trackIdx}].params.tracks`,
3112
- code: CODES4.NATIVE_SEG_OVERLAP,
3113
- node_id: track.id,
3114
- message: `voice track "${track.id}": "${prev.slot}" runs to ${round2(prev.end)}s but "${cur.slot}" starts at ${cur.start}s \u2014 both play at once (echo). Cap the first with duration_s: ${round2(cur.start - prev.start)} or re-time the windows`
3115
- });
3116
- }
3117
- }
3118
- }
3119
- function checkSpineNormalized(ctx, meta) {
3120
- const spineId = spineIdOf(ctx, meta);
3121
- const idx = spineId === null ? void 0 : ctx.idToIndex.get(spineId);
3122
- const spine = idx === void 0 ? void 0 : ctx.canvas.nodes[idx];
3123
- if (spine?.type !== "ffmpeg") return;
3124
- const args = spine.params.args;
3125
- const graph = Array.isArray(args) ? args.join(" ") : "";
3126
- if (!graph.includes("concat=n=")) return;
3127
- if (/\[\d+:v\](?:\[|concat)/.test(graph)) {
3128
- ctx.issues.push({
3129
- path: `nodes[${idx}].params.args`,
3130
- code: CODES4.SPINE_UNNORMALIZED,
3131
- severity: "warning",
3132
- node_id: spine.id,
3133
- message: `the spine concat feeds raw input labels \u2014 generated clips carry no fps/SAR guarantee, and one 24fps clip silently stretches the picture off the audio/overlay clock. Chain \`format=yuv420p,fps=30,setsar=1,settb=AVTB\` on every input before the concat`
3134
- });
3135
- }
3136
- }
3137
- function checkOddDimensions(ctx) {
3138
- ctx.canvas.nodes.forEach((node, idx) => {
3139
- if (node.type !== "ffmpeg") return;
3140
- const args = node.params.args;
3141
- const blob = Array.isArray(args) ? args.join(" ") : "";
3142
- for (const m of blob.matchAll(/(?:scale|crop|pad)=(\d+):(\d+)/g)) {
3143
- const w = Number(m[1]);
3144
- const h = Number(m[2]);
3145
- if (w % 2 === 0 && h % 2 === 0) continue;
3146
- ctx.issues.push({
3147
- path: `nodes[${idx}].params.args`,
3148
- code: CODES4.ODD_DIMENSIONS,
3149
- severity: "warning",
3150
- node_id: node.id,
3151
- message: `"${m[0]}" produces an odd dimension \u2014 libx264 yuv420p rejects odd sizes, so this node fails at render time. Round both to even numbers`
3152
- });
3153
- return;
3154
- }
3155
- });
3156
- }
3157
- function checkRegionDropped(ctx) {
3158
- if (isTagged(ctx)) {
3159
- checkTaggedRegionDropped(ctx);
3160
- return;
3161
- }
3162
- const regionClips = /* @__PURE__ */ new Map();
3163
- for (const node of ctx.canvas.nodes) {
3164
- const m = /^s(\d+)_r\d+_clip$/.exec(node.id);
3165
- if (m && node.type === "video_generate")
3166
- regionClips.set(m[1], (regionClips.get(m[1]) ?? 0) + 1);
3167
- }
3168
- for (const [scene, billed] of regionClips) {
3169
- const idx = ctx.idToIndex.get(`s${scene}_composite`);
3170
- if (idx === void 0) continue;
3171
- const composite = ctx.canvas.nodes[idx];
3172
- const consumed = Object.keys(composite.inputs ?? {}).filter((k) => /^c\d+$/.test(k)).length;
3173
- if (billed > consumed) {
3174
- ctx.issues.push({
3175
- path: `nodes[${idx}].inputs`,
3176
- code: CODES4.REGION_DROPPED,
3177
- severity: "warning",
3178
- node_id: composite.id,
3179
- message: `scene s${scene} bills ${billed} region clips but its composite consumes ${consumed} \u2014 the extra generation is paid for and never reaches the frame. Wire every region into the composite or delete the unused clip node`
3180
- });
3181
- }
3182
- }
3183
- }
3184
- function checkTaggedRegionDropped(ctx) {
3185
- for (const composite of ctx.canvas.nodes) {
3186
- const scene = composite.tags?.scene;
3187
- if (composite.tags?.role !== "composite" || scene === void 0) continue;
3188
- const billed = ctx.canvas.nodes.filter(
3189
- (n) => n.type === "video_generate" && n.tags?.scene === scene && n.tags.region !== void 0
3190
- );
3191
- const reached = upstreamIds(ctx, composite);
3192
- const consumed = billed.filter((n) => reached.has(n.id)).length;
3193
- if (billed.length <= consumed) continue;
3194
- ctx.issues.push({
3195
- path: `nodes[${ctx.idToIndex.get(composite.id) ?? -1}].inputs`,
3196
- code: CODES4.REGION_DROPPED,
3197
- severity: "warning",
3198
- node_id: composite.id,
3199
- message: `scene s${scene} bills ${billed.length} region clips but its composite consumes ${consumed} \u2014 the extra generation is paid for and never reaches the frame. Wire every region into the composite or delete the unused clip node`
3200
- });
3201
- }
3202
- }
3203
- function wiredNodes(ctx, node) {
3204
- return Object.values(node.inputs ?? {}).flatMap((v) => Array.isArray(v) ? v : [v]).map((ref) => refNodeOf(ctx, ref)).filter((up) => up !== null);
3205
- }
3206
- function upstreamIds(ctx, node) {
3207
- const seen = /* @__PURE__ */ new Set();
3208
- const stack = [node];
3209
- for (let cur = stack.pop(); cur; cur = stack.pop()) {
3210
- for (const up of wiredNodes(ctx, cur)) {
3211
- if (seen.has(up.id)) continue;
3212
- seen.add(up.id);
3213
- stack.push(up);
3214
- }
3215
- }
3216
- return seen;
3217
- }
3218
- function round2(n) {
3219
- return Math.round(n * 100) / 100;
3220
- }
3221
- function checkClipSpanFitsModel(ctx, meta) {
3222
- for (const { node, span_s } of meta.clip_spans ?? []) {
3223
- const target = ctx.canvas.nodes.find((n) => n.id === node && n.type === "video_generate");
3224
- if (!target) continue;
3225
- const model = target.params?.model;
3226
- if (typeof model !== "string") continue;
3227
- const max = maxClipSeconds(model);
3228
- if (max === void 0 || span_s <= max + SPAN_MODEL_SLACK_S) continue;
3229
- ctx.issues.push({
3230
- path: `nodes[${ctx.idToIndex.get(node) ?? -1}].params.duration`,
3231
- code: CODES4.SPAN_EXCEEDS_MODEL,
3232
- severity: "warning",
3233
- message: `scene for clip "${node}" needs ~${span_s}s but ${model} caps clips at ${max}s \u2014 the clip will truncate. Split the scene, or keep it on a model whose max duration covers the span`,
3234
- node_id: node,
3235
- node_type: "video_generate"
3236
- });
3237
- }
3238
- }
3239
- function secondsPerWord(stamped) {
3240
- const est = stamped?.est_speech_s;
3241
- const words = stamped?.speech_words;
3242
- if (est && words && est > 0 && words > 0) {
3243
- const wps = words / est;
3244
- if (wps >= OBSERVED_WPS_MIN && wps <= OBSERVED_WPS_MAX) return est / words;
3245
- }
3246
- return 1 / NATIVE_CLIP_WORDS_PER_SECOND;
3247
- }
3248
- function speechOverrunOf(node, secPerWord) {
3249
- const params = node.params;
3250
- if (params?.generate_audio !== true) return null;
3251
- const line = nativeDialogueOf(params.prompt);
3252
- const duration = typeof params.duration === "number" ? params.duration : void 0;
3253
- if (!line || !duration) return null;
3254
- const estSpeechS2 = wordCount2(line) * secPerWord;
3255
- return estSpeechS2 > duration * SPEECH_OVERRUN_RATIO ? { estSpeechS: estSpeechS2, duration } : null;
3256
- }
3257
- function checkSpeechOverrun(ctx, talkingScenes) {
3258
- for (const entry of talkingScenes) {
3259
- if (typeof entry !== "object" || !("voice_convert_node" in entry)) continue;
3260
- for (const n of sceneClips(ctx, entry.scene)) {
3261
- const overrun = speechOverrunOf(n, secondsPerWord(entry));
3262
- if (overrun) {
3263
- ctx.issues.push({
3264
- path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,
3265
- code: CODES4.SPEECH_OVERRUN,
3266
- message: `"${n.id}" asks Seedance to speak ~${Math.round(overrun.estSpeechS * 10) / 10}s of dialogue inside a ${overrun.duration}s clip \u2014 the line cannot fit (>${SPEECH_OVERRUN_RATIO}\xD7 the clip). Shorten the line, split the scene, or lengthen the clip duration`
3267
- });
3268
- continue;
3269
- }
3270
- checkSpeechExceedsExtract(ctx, n, entry);
3271
- }
3272
- }
3273
- }
3274
- function sceneExtract(ctx, clip2, scene) {
3275
- if (isTagged(ctx)) {
3276
- const extracts = ctx.canvas.nodes.filter((n) => n.tags?.role === "voice_extract" && n.tags.scene === scene);
3277
- const extract2 = extracts.find((n) => n.tags?.region === clip2.tags?.region) ?? extracts[0];
3278
- return extract2 ? { extract: extract2, label: extract2.id } : null;
3279
- }
3280
- const regionTag = /^s\d+(_r\d+)?_clip$/.exec(clip2.id)?.[1] ?? "";
3281
- const extractIdx = ctx.idToIndex.get(`s${scene}${regionTag}_voextract`) ?? ctx.idToIndex.get(`s${scene}_voextract`);
3282
- const extract = extractIdx === void 0 ? void 0 : ctx.canvas.nodes[extractIdx];
3283
- return extract ? { extract, label: `s${scene}_voextract` } : null;
3284
- }
3285
- function checkSpeechExceedsExtract(ctx, clip2, entry) {
3286
- const params = clip2.params;
3287
- if (params?.generate_audio !== true) return;
3288
- const line = nativeDialogueOf(params.prompt);
3289
- if (!line) return;
3290
- const found = sceneExtract(ctx, clip2, entry.scene);
3291
- const windowS = found ? ffmpegTrimSeconds(found.extract) : null;
3292
- if (!found || windowS === null) return;
3293
- const estSpeechS2 = wordCount2(line) * secondsPerWord(entry);
3294
- if (estSpeechS2 <= windowS * SPEECH_OVERRUN_RATIO) return;
3295
- ctx.issues.push({
3296
- path: `nodes[${ctx.idToIndex.get(clip2.id) ?? -1}].params.prompt`,
3297
- code: CODES4.SPEECH_EXCEEDS_EXTRACT,
3298
- node_id: clip2.id,
3299
- message: `"${clip2.id}"'s line is ~${Math.round(estSpeechS2 * 10) / 10}s of speech but its extract window (${found.label}) is ${windowS}s \u2014 the read gets cut mid-word on the spine. Shorten the Dialogue line, lengthen the scene in prompt.json and re-scaffold, or raise the voextract \`-t\` when the read should carry over the next cutaway`
3300
- });
3301
- }
3302
- function checkAspectConsistency(ctx) {
3303
- const clips = ctx.canvas.nodes.filter((n) => n.type === "video_generate");
3304
- if (clips.length < 2) return;
3305
- const ratios = new Set(clips.map((n) => String(n.params?.aspect_ratio ?? "(unset)")));
3306
- if (ratios.size <= 1) return;
3307
- ctx.issues.push({
3308
- path: "nodes",
3309
- code: CODES4.ASPECT_MISMATCH,
3310
- message: `video_generate nodes disagree on aspect_ratio (${[...ratios].sort().join(", ")}) \u2014 the spine's segments would render at different shapes and the composite silently crops. Pin every clip to the same aspect_ratio`
3311
- });
3312
- }
3313
- function sortedVoSegments(meta) {
3314
- return [...meta.vo_segments].sort((a, b) => a.start_s - b.start_s);
3315
- }
3316
- function checkVoOverlap(ctx, meta) {
3317
- const segments = sortedVoSegments(meta);
3318
- for (let i = 1; i < segments.length; i++) {
3319
- const prev = segments[i - 1];
3320
- const cur = segments[i];
3321
- if (!prev || !cur) continue;
3322
- if (cur.start_s < prev.end_s - VIDEO_TIME_SLACK_S) {
3323
- ctx.issues.push({
3324
- path: "metadata.video.vo_segments",
3325
- code: CODES4.VO_OVERLAP,
3326
- message: `voiceover turns overlap: "${prev.slot}" runs to ${prev.end_s}s but "${cur.slot}" starts at ${cur.start_s}s \u2014 sequence them so dialogue alternates instead of stacking`
3327
- });
3328
- }
3329
- }
3330
- }
3331
- function checkAudioDuration(ctx, meta) {
3332
- const audioEnd = sortedVoSegments(meta).reduce((m, s) => Math.max(m, s.end_s), 0);
3333
- if (audioEnd > meta.duration_s + VIDEO_TIME_SLACK_S) {
3334
- ctx.issues.push({
3335
- path: "metadata.video.duration_s",
3336
- code: CODES4.AUDIO_DURATION,
3337
- message: `audio runs to ${audioEnd}s but the video is ${meta.duration_s}s \u2014 trim the voiceover or extend the video so audio \u2248 video length`
3338
- });
3339
- }
3340
- }
3341
- function checkTalkingScenes(ctx, meta) {
3342
- for (const entry of meta.talking_scenes) {
3343
- const scene = typeof entry === "number" ? entry : entry.scene;
3344
- if (!talkingSceneSatisfied(ctx, entry, scene)) {
3345
- ctx.issues.push({
3346
- path: "metadata.video.talking_scenes",
3347
- code: CODES4.LIPSYNC_MISSING,
3348
- message: `scene ${scene} is a single-on-camera-speaker talking head but its clip lacks native audio (generate_audio: true) \u2014 the lips would drift. Generate the clip with native audio, then route it to the mix via either the audio_voice_convert node (one brand voice) or directly (s${scene}_voextract \u2192 audio_mix)`
3349
- });
3350
- }
3351
- }
3352
- }
3353
- var voOverlapRule = {
3354
- name: "video-vo-overlap",
3355
- codes: { [CODES4.VO_OVERLAP]: "error" },
3356
- check: onVideoCanvas(checkVoOverlap)
3357
- };
3358
- var audioDurationRule = {
3359
- name: "video-audio-duration",
3360
- codes: { [CODES4.AUDIO_DURATION]: "error" },
3361
- check: onVideoCanvas(checkAudioDuration)
3362
- };
3363
- var talkingScenesRule = {
3364
- name: "video-talking-scenes",
3365
- codes: { [CODES4.LIPSYNC_MISSING]: "error" },
3366
- check: onVideoCanvas(checkTalkingScenes)
3367
- };
3368
- var speechOverrunRule = {
3369
- name: "video-speech-overrun",
3370
- codes: { [CODES4.SPEECH_OVERRUN]: "error", [CODES4.SPEECH_EXCEEDS_EXTRACT]: "error" },
3371
- check: onVideoCanvas((ctx, meta) => checkSpeechOverrun(ctx, meta.talking_scenes))
3372
- };
3373
- var aspectConsistencyRule = {
3374
- name: "video-aspect-consistency",
3375
- codes: { [CODES4.ASPECT_MISMATCH]: "error" },
3376
- check: onVideoCanvas((ctx) => checkAspectConsistency(ctx))
3377
- };
3378
- var clipSpanRule = {
3379
- name: "video-clip-span",
3380
- codes: { [CODES4.SPAN_EXCEEDS_MODEL]: "warning" },
3381
- check: onVideoCanvas(checkClipSpanFitsModel)
3382
- };
3383
- var timelineContractRule = {
3384
- name: "video-timeline-contract",
3385
- codes: { [CODES4.TIMELINE_TOTAL]: "error" },
3386
- check: onVideoCanvas(checkTimelineContract)
3387
- };
3388
- var nativeSegOverlapRule = {
3389
- name: "video-native-seg-overlap",
3390
- codes: { [CODES4.NATIVE_SEG_OVERLAP]: "error" },
3391
- check: onVideoCanvas((ctx) => checkNativeSegOverlap(ctx))
3392
- };
3393
- var spineNormalizedRule = {
3394
- name: "video-spine-normalized",
3395
- codes: { [CODES4.SPINE_UNNORMALIZED]: "warning" },
3396
- check: onVideoCanvas(checkSpineNormalized)
3397
- };
3398
- var oddDimensionsRule = {
3399
- name: "video-odd-dimensions",
3400
- codes: { [CODES4.ODD_DIMENSIONS]: "warning" },
3401
- check: onVideoCanvas((ctx) => checkOddDimensions(ctx))
3402
- };
3403
- var regionDroppedRule = {
3404
- name: "video-region-dropped",
3405
- codes: { [CODES4.REGION_DROPPED]: "warning" },
3406
- check: onVideoCanvas((ctx) => checkRegionDropped(ctx))
3407
- };
3408
- var overlayTimingRule = {
3409
- name: "video-overlay-timing",
3410
- codes: { [CODES4.OVERLAY_OUT_OF_BOUNDS]: "warning" },
3411
- check: checkOverlayTimingBounds
3412
- };
3413
-
3414
- // src/engine/engine/lint/registry.ts
3415
- var LINT_RULES = [
3416
- parallelFormatRendersRule,
3417
- audioBedMixRule,
3418
- promptProfileRule,
3419
- imageProfileRule,
3420
- personGenerationRule,
3421
- promptBudgetRule,
3422
- rawFaceKeyframeRule,
3423
- promptDecisionsRule,
3424
- hookLayersRule,
3425
- voOverlapRule,
3426
- audioDurationRule,
3427
- talkingScenesRule,
3428
- speechOverrunRule,
3429
- aspectConsistencyRule,
3430
- uiInPromptRule,
3431
- brandmarkInPromptRule,
3432
- referenceCompletenessRule,
3433
- clipSpanRule,
3434
- timelineContractRule,
3435
- unusableLastFrameRule,
3436
- nativeSegOverlapRule,
3437
- spineNormalizedRule,
3438
- oddDimensionsRule,
3439
- regionDroppedRule
3440
- ];
3441
- var DEEP_LINT_RULES = [overlayTimingRule];
3442
- function assertDeclared(rule, raised) {
3443
- for (const issue of raised) {
3444
- const declared = rule.codes[issue.code];
3445
- const severity = issue.severity ?? "error";
3446
- if (declared !== severity) {
3447
- throw new Error(
3448
- `lint rule "${rule.name}" raised ${issue.code} as ${severity}, but declares ${declared ?? "no such code"}`
3449
- );
3450
- }
3451
- }
3452
- }
3453
- function runLintRules(ctx) {
3454
- for (const rule of LINT_RULES) {
3455
- const before = ctx.issues.length;
3456
- rule.check(ctx);
3457
- assertDeclared(rule, ctx.issues.slice(before));
3458
- }
3459
- }
3460
- async function runDeepLintRules(canvas, issues) {
3461
- for (const rule of DEEP_LINT_RULES) {
3462
- const before = issues.length;
3463
- await rule.check(canvas, issues);
3464
- assertDeclared(rule, issues.slice(before));
3465
- }
3466
- }
3467
-
3468
- // src/engine/engine/prompt-limit.ts
3469
- import { readFile as readFile2 } from "fs/promises";
3470
- import path2 from "path";
3471
- var PROMPT_OVER_MODEL_LIMIT = "PROMPT_OVER_MODEL_LIMIT";
3472
- async function staticTextOf(value, nodes) {
3473
- if (typeof value !== "string") return void 0;
3474
- if (!value.startsWith(REF_PREFIX)) return value;
3475
- const expr = parseRefExpr(value);
3476
- if (expr?.kind !== "ref" || expr.index !== void 0) return void 0;
3477
- const target = nodes.get(expr.nodeId);
3478
- const params = target?.params ?? {};
3479
- if (target?.type === "text" && expr.output === "text" && typeof params.value === "string") return params.value;
3480
- if (target?.type === "ingest" && expr.output === "asset" && params.source === "path" && (params.expect === "json" || params.expect === "text") && typeof params.path === "string" && path2.isAbsolute(params.path)) {
3481
- try {
3482
- return await readFile2(params.path, "utf8");
3483
- } catch {
3484
- return void 0;
3485
- }
3486
- }
3487
- return void 0;
3488
- }
3489
- function modelStringCaps(nodeType, params) {
3490
- if (!isManagedNodeKind(nodeType)) return [];
3491
- const model = params?.model;
3492
- if (typeof model !== "string") return [];
3493
- const spec = MODEL_REGISTRY[nodeType][canonicalModelId(model)];
3494
- if (!spec) return [];
3495
- return Object.entries(spec.params).flatMap(
3496
- ([key, schema]) => schema.kind === "string" && schema.maxLength !== void 0 ? [{ key, max: schema.maxLength, model }] : []
3497
- );
3498
- }
3499
- function renderedPromptOverLimit(nodeId, nodeType, renderedParams) {
3500
- const params = renderedParams ?? {};
3501
- return modelStringCaps(nodeType, params).flatMap(({ key, max, model }) => {
3502
- const value = params[key];
3503
- if (typeof value !== "string" || value.length <= max) return [];
3504
- return [
3505
- `"${nodeId}" ${key} is ${value.length} characters once its slots are filled, ${value.length - max} over the ${max} that "${model}" accepts. Shorten what feeds its {{slots}} (or the prompt around them) and run again; nothing was sent to the model.`
3506
- ];
3507
- });
3508
- }
3509
- async function filledParam(n, raw, nodes) {
3510
- const template = await staticTextOf(raw, nodes);
3511
- if (template === void 0) return void 0;
3512
- const names = extractSlots(template);
3513
- if (names.length === 0 && template === raw) return void 0;
3514
- const inputs = n.inputs ?? {};
3515
- const slots = {};
3516
- for (const name of names) {
3517
- const text = await staticTextOf(inputs[name], nodes);
3518
- if (text !== void 0) slots[name] = text;
3519
- }
3520
- return { length: renderTemplate(template, slots).length, slots };
3521
- }
3522
- function largestSlotNote(slots) {
3523
- const largest = Object.entries(slots).sort((a, b) => b[1].length - a[1].length)[0];
3524
- return largest ? `; {{${largest[0]}}} alone is ${largest[1].length}` : "";
3525
- }
3526
- async function filledPromptLimitIssues(canvas) {
3527
- const nodes = new Map(canvas.nodes.map((n) => [n.id, n]));
3528
- const issues = [];
3529
- for (const [i, n] of canvas.nodes.entries()) {
3530
- for (const { key, max, model } of modelStringCaps(n.type, n.params)) {
3531
- const filled = await filledParam(n, n.params?.[key], nodes);
3532
- if (!filled || filled.length <= max) continue;
3533
- issues.push({
3534
- path: `nodes[${i}].params.${key}`,
3535
- code: PROMPT_OVER_MODEL_LIMIT,
3536
- message: `"${n.id}" ${key} is ${filled.length} characters once its slots are filled \u2014 ${filled.length - max} over the ${max} that "${model}" accepts` + largestSlotNote(filled.slots) + `. Shorten it before running: the model refuses it, after every node before "${n.id}" has already been paid for.`,
3537
- received: filled.length,
3538
- expected: `<= ${max} characters`,
3539
- node_id: n.id,
3540
- node_type: n.type
3541
- });
3542
- }
3543
- }
3544
- return issues;
3545
- }
3546
-
3547
- // src/engine/engine/scheduler.ts
3548
- function topologicalLayers(graph) {
3549
- const remaining = cloneGraph(graph);
3550
- const layers = [];
3551
- while (remaining.size > 0) {
3552
- const ready = collectReady(remaining);
3553
- if (ready.length === 0) {
3554
- throw new Error(`scheduler: cycle detected among ${[...remaining.keys()].join(", ")}`);
3555
- }
3556
- layers.push(ready);
3557
- removeNodes(remaining, ready);
3558
- }
3559
- return layers;
3560
- }
3561
- function reachableNodes(graph, roots) {
3562
- const reachable = /* @__PURE__ */ new Set();
3563
- const stack = [...roots];
3564
- while (stack.length > 0) {
3565
- const id = stack.pop();
3566
- if (id === void 0 || reachable.has(id) || !graph.has(id)) continue;
3567
- reachable.add(id);
3568
- for (const dep of graph.get(id) ?? []) stack.push(dep);
3569
- }
3570
- return reachable;
3571
- }
3572
- function subgraph(graph, keep) {
3573
- const out = /* @__PURE__ */ new Map();
3574
- for (const [id, deps] of graph) {
3575
- if (!keep.has(id)) continue;
3576
- out.set(id, new Set([...deps].filter((d) => keep.has(d))));
3577
- }
3578
- return out;
3579
- }
3580
- function cloneGraph(graph) {
3581
- const out = /* @__PURE__ */ new Map();
3582
- for (const [k, deps] of graph) out.set(k, new Set(deps));
3583
- return out;
3584
- }
3585
- function collectReady(remaining) {
3586
- const ready = [];
3587
- for (const [id, deps] of remaining) if (deps.size === 0) ready.push(id);
3588
- ready.sort();
3589
- return ready;
3590
- }
3591
- function removeNodes(remaining, ready) {
3592
- for (const id of ready) remaining.delete(id);
3593
- for (const deps of remaining.values()) {
3594
- for (const id of ready) deps.delete(id);
3595
- }
3596
- }
3597
- function findCycle(graph) {
3598
- const reverseAdj = buildReverseAdj(graph);
3599
- const color = /* @__PURE__ */ new Map();
3600
- for (const k of graph.keys()) color.set(k, 0);
3601
- const stack = [];
3602
- for (const id of graph.keys()) {
3603
- if ((color.get(id) ?? 0) !== 0) continue;
3604
- const found = dfsCycle(id, color, stack, reverseAdj);
3605
- if (found) return found;
3606
- }
3607
- return null;
3608
- }
3609
- function buildReverseAdj(graph) {
3610
- const reverseAdj = /* @__PURE__ */ new Map();
3611
- for (const [node, deps] of graph) {
3612
- for (const dep of deps) {
3613
- const arr = reverseAdj.get(dep) ?? [];
3614
- arr.push(node);
3615
- reverseAdj.set(dep, arr);
3616
- }
3617
- }
3618
- return reverseAdj;
3619
- }
3620
- function dfsCycle(u, color, stack, reverseAdj) {
3621
- color.set(u, 1);
3622
- stack.push(u);
3623
- const children = reverseAdj.get(u) ?? [];
3624
- for (const v of children) {
3625
- const c = color.get(v) ?? 0;
3626
- if (c === 1) {
3627
- const idx = stack.indexOf(v);
3628
- return [...stack.slice(idx), v];
3629
- }
3630
- if (c === 0) {
3631
- const r = dfsCycle(v, color, stack, reverseAdj);
3632
- if (r) return r;
3633
- }
3634
- }
3635
- color.set(u, 2);
3636
- stack.pop();
3637
- return null;
3638
- }
3639
-
3640
- // src/engine/engine/validator.ts
3641
- var MODEL_INPUT_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio"]);
3642
- var STAGE_CODES = {
3643
- PARSE: "PARSE_ERROR",
3644
- SHAPE: "SHAPE_ERROR",
3645
- UNKNOWN_NODE: "UNKNOWN_NODE_TYPE",
3646
- PARAMS: "INVALID_PARAMS",
3647
- PARAMS_EXTRA: "INVALID_PARAMS_EXTRA",
3648
- MODEL_PARAMS: "MODEL_INVALID_PARAMS",
3649
- MODEL_INPUTS: "MODEL_INVALID_INPUTS",
3650
- REF_UNRESOLVED: "UNRESOLVED_REF",
3651
- REF_OUT_OF_BOUNDS: "REF_INDEX_OUT_OF_BOUNDS",
3652
- REF_TYPE: "REF_TYPE_MISMATCH",
3653
- DUPLICATE_ID: "DUPLICATE_NODE_ID",
3654
- CYCLE: "GRAPH_CYCLE",
3655
- SLOT: "PROMPT_SLOT_UNRESOLVED",
3656
- OUTPUT: "OUTPUT_REF_INVALID",
3657
- ORPHAN_NODE: "ORPHAN_NODE",
3658
- INGEST_PATH_URL_HEALED: "INGEST_PATH_URL_HEALED"
3659
- };
3660
- function validateCanvas(input, registry) {
3661
- const issues = [];
3662
- const shape = CanvasSchema.safeParse(input);
3663
- if (!shape.success) {
3664
- pushZodIssues(issues, shape.error, "", STAGE_CODES.SHAPE);
3665
- return { ok: false, issues };
3666
- }
3667
- const canvas = shape.data;
3668
- const idToIndex = buildIdToIndex(canvas);
3669
- const ctx = { canvas, registry, issues, idToIndex };
3670
- checkDuplicateIds(ctx);
3671
- checkUnknownNodeTypes(ctx);
3672
- if (issues.some((i) => i.code === STAGE_CODES.UNKNOWN_NODE)) return { ok: false, issues };
3673
- checkAllParams(ctx);
3674
- checkIngestPathUrlHeals(ctx);
3675
- checkAllModelParams(ctx);
3676
- checkAllRefs(ctx);
3677
- checkAllModelInputs(ctx);
3678
- checkCycle(ctx);
3679
- checkAllSlots(ctx);
3680
- const estimatedCredits = sumCredits(estimateCanvasCredits(canvas, registry));
3681
- checkOutputRef(ctx);
3682
- checkOrphanNodes(ctx);
3683
- runLintRules(ctx);
3684
- const hasBlocking = issues.some(isBlocking);
3685
- if (hasBlocking) return { ok: false, issues };
3686
- const warnings = issues.filter((i) => !isBlocking(i));
3687
- return { ok: true, canvas, estimatedCredits, warnings: warnings.length > 0 ? warnings : void 0 };
3688
- }
3689
- async function validateCanvasDeep(input, registry) {
3690
- const shallow = validateCanvas(input, registry);
3691
- if (!shallow.ok) return shallow;
3692
- const canvas = shallow.canvas;
3693
- const issues = [];
3694
- const ctx = { canvas, registry, issues, idToIndex: buildIdToIndex(canvas) };
3695
- const inputKindOf = (value) => staticInputKind(ctx, value);
3696
- const inputSourceOf = (value) => staticInputSource(ctx, value);
3697
- for (let i = 0; i < canvas.nodes.length; i++) {
3698
- const n = canvas.nodes[i];
3699
- if (!n) continue;
3700
- const def = registry.get(n.type);
3701
- if (!def?.validateExtra) continue;
3702
- let extra;
3703
- try {
3704
- extra = await def.validateExtra({
3705
- rawParams: n.params,
3706
- rawInputs: n.inputs,
3707
- nodeId: n.id,
3708
- inputKindOf,
3709
- inputSourceOf
3710
- });
3711
- } catch (e) {
3712
- issues.push({
3713
- path: `nodes[${i}].params`,
3714
- code: STAGE_CODES.PARAMS_EXTRA,
3715
- message: e.message,
3716
- node_id: n.id,
3717
- node_type: n.type
3718
- });
3719
- continue;
3720
- }
3721
- for (const item of extra) {
3722
- issues.push({
3723
- path: `nodes[${i}].${item.path}`,
3724
- code: STAGE_CODES.PARAMS_EXTRA,
3725
- message: item.message,
3726
- node_id: n.id,
3727
- node_type: n.type
3728
- });
3729
- }
3730
- }
3731
- await runDeepLintRules(canvas, issues);
3732
- issues.push(...await filledPromptLimitIssues(canvas));
3733
- const hasBlocking = issues.some(isBlocking);
3734
- if (hasBlocking) return { ok: false, issues };
3735
- const warnings = [...shallow.warnings ?? [], ...issues.filter((i) => !isBlocking(i))];
3736
- const perNodeCredits = estimateCanvasCredits(canvas, registry);
3737
- return {
3738
- ok: true,
3739
- canvas,
3740
- estimatedCredits: shallow.estimatedCredits,
3741
- perNodeCredits,
3742
- warnings: warnings.length > 0 ? warnings : void 0
3743
- };
3744
- }
3745
- function buildIdToIndex(canvas) {
3746
- const m = /* @__PURE__ */ new Map();
3747
- for (let i = 0; i < canvas.nodes.length; i++) {
3748
- const n = canvas.nodes[i];
3749
- if (n) m.set(n.id, i);
3750
- }
3751
- return m;
3752
- }
3753
- function checkDuplicateIds(ctx) {
3754
- const seen = /* @__PURE__ */ new Set();
3755
- for (let i = 0; i < ctx.canvas.nodes.length; i++) {
3756
- const n = ctx.canvas.nodes[i];
3757
- if (!n) continue;
3758
- if (seen.has(n.id)) {
3759
- ctx.issues.push({
3760
- path: `nodes[${i}].id`,
3761
- code: STAGE_CODES.DUPLICATE_ID,
3762
- message: `duplicate node id "${n.id}"`,
3763
- node_id: n.id
3764
- });
3765
- }
3766
- seen.add(n.id);
3767
- }
3768
- }
3769
- function checkUnknownNodeTypes(ctx) {
3770
- const known = ctx.registry.ids();
3771
- for (let i = 0; i < ctx.canvas.nodes.length; i++) {
3772
- const n = ctx.canvas.nodes[i];
3773
- if (!n || ctx.registry.has(n.type)) continue;
3774
- ctx.issues.push({
3775
- path: `nodes[${i}].type`,
3776
- code: STAGE_CODES.UNKNOWN_NODE,
3777
- message: `unknown node type "${n.type}"`,
3778
- received: n.type,
3779
- did_you_mean: didYouMean(n.type, known),
3780
- node_id: n.id,
3781
- node_type: n.type
3782
- });
3783
- }
3784
- }
3785
- function checkAllParams(ctx) {
3786
- for (let i = 0; i < ctx.canvas.nodes.length; i++) {
3787
- const n = ctx.canvas.nodes[i];
3788
- if (!n) continue;
3789
- const def = ctx.registry.get(n.type);
3790
- if (!def) continue;
3791
- const parsed = def.params.safeParse(n.params ?? {});
3792
- if (parsed.success) continue;
3793
- pushZodIssues(ctx.issues, parsed.error, `nodes[${i}].params`, STAGE_CODES.PARAMS, n.id, n.type);
3794
- }
3795
- }
3796
- function checkIngestPathUrlHeals(ctx) {
3797
- for (let i = 0; i < ctx.canvas.nodes.length; i++) {
3798
- const n = ctx.canvas.nodes[i];
3799
- if (n?.type !== "ingest") continue;
3800
- const params = n.params ?? {};
3801
- if (params.source !== "path" || !looksLikeHttpUrl(params.path)) continue;
3802
- ctx.issues.push({
3803
- path: `nodes[${i}].params.source`,
3804
- code: STAGE_CODES.INGEST_PATH_URL_HEALED,
3805
- message: `ingest "${n.id}" declares source:"path" but its value is a URL \u2014 running it as source:"url". Update the canvas JSON to source:"url" so it matches what baker runs.`,
3806
- severity: "warning",
3807
- node_id: n.id,
3808
- node_type: n.type
3809
- });
3810
- }
3811
- }
3812
- function checkAllModelParams(ctx) {
3813
- for (let i = 0; i < ctx.canvas.nodes.length; i++) {
3814
- const n = ctx.canvas.nodes[i];
3815
- if (!n || !isManagedNodeKind(n.type)) continue;
3816
- const params = n.params ?? {};
3817
- const model = params.model;
3818
- if (typeof model !== "string" || model.length === 0) continue;
3819
- const r = validateModelParams(n.type, model, params);
3820
- if (r.ok) continue;
3821
- for (const err of r.errors) {
3822
- ctx.issues.push({
3823
- path: `nodes[${i}].params${"param" in err ? `.${err.param}` : ""}`,
3824
- code: STAGE_CODES.MODEL_PARAMS,
3825
- message: err.message,
3826
- node_id: n.id,
3827
- node_type: n.type
3828
- });
3829
- }
3830
- }
3831
- }
3832
- function checkAllRefs(ctx) {
3833
- for (let i = 0; i < ctx.canvas.nodes.length; i++) {
3834
- const n = ctx.canvas.nodes[i];
3835
- if (!n) continue;
3836
- walkRefs(n.inputs, (refStr, jsonPath) => {
3837
- checkOneRef(ctx, n, i, refStr, jsonPath, "inputs");
3838
- });
3839
- walkRefs(n.params, (refStr, jsonPath) => {
3840
- checkOneRef(ctx, n, i, refStr, jsonPath, "params");
3841
- });
3842
- }
3843
- }
3844
- function checkAllModelInputs(ctx) {
3845
- for (let i = 0; i < ctx.canvas.nodes.length; i++) {
3846
- const n = ctx.canvas.nodes[i];
3847
- if (!n || !isManagedNodeKind(n.type)) continue;
3848
- const params = n.params ?? {};
3849
- const model = params.model;
3850
- if (typeof model !== "string" || model.length === 0) continue;
3851
- const providedKinds = collectProvidedKinds(ctx, n);
3852
- const r = validateModelInputs(n.type, model, providedKinds);
3853
- if (r.ok) continue;
3854
- for (const err of r.errors) {
3855
- ctx.issues.push({
3856
- path: `nodes[${i}].inputs`,
3857
- code: STAGE_CODES.MODEL_INPUTS,
3858
- message: err.message,
3859
- node_id: n.id,
3860
- node_type: n.type
3861
- });
3862
- }
3863
- }
3864
- }
3865
- function collectProvidedKinds(ctx, n) {
3866
- const kinds = /* @__PURE__ */ new Set();
3867
- walkRefs(n.inputs, (refStr) => {
3868
- const kind = resolveRefKind(ctx, refStr);
3869
- if (kind) kinds.add(kind);
3870
- });
3871
- return [...kinds];
3872
- }
3873
- function resolveRefKind(ctx, refStr) {
3874
- const target = refTarget(ctx, refStr);
3875
- const kind = target ? refOutputKind(target) : void 0;
3876
- return kind && MODEL_INPUT_KINDS.has(kind) ? kind : null;
3877
- }
3878
- function refTarget(ctx, refStr) {
3879
- const expr = parseRefExpr(refStr);
3880
- if (!expr) return null;
3881
- const targetIdx = ctx.idToIndex.get(expr.nodeId);
3882
- const node = targetIdx === void 0 ? void 0 : ctx.canvas.nodes[targetIdx];
3883
- const def = node ? ctx.registry.get(node.type) : void 0;
3884
- return node && def ? { expr, node, def } : null;
3885
- }
3886
- function refOutputKind({ expr, node, def }) {
3887
- const targetParams = def.params.safeParse(node.params ?? {});
3888
- const resolvedKinds = resolveOutputKinds(def.outputKinds, targetParams.success ? targetParams.data : {});
3889
- const declaredKind = node.params?.outputs?.[expr.output]?.kind;
3890
- return resolvedKinds[expr.output] ?? declaredKind;
3891
- }
3892
- function staticInputKind(ctx, value) {
3893
- if (Array.isArray(value)) return "list";
3894
- if (typeof value !== "string") return value && typeof value === "object" ? "unknown" : "value";
3895
- if (!value.startsWith(REF_PREFIX)) return "value";
3896
- const target = refTarget(ctx, value);
3897
- if (!target) return "unknown";
3898
- const outputSchema = unwrap(shapeOf(target.def.outputs)?.[target.expr.output] ?? z2.unknown());
3899
- if (outputSchema instanceof z2.ZodArray && target.expr.index === void 0) return "list";
3900
- const kind = refOutputKind(target);
3901
- if (kind && ASSET_KINDS.has(kind)) return kind;
3902
- const plain2 = outputSchema instanceof z2.ZodString || outputSchema instanceof z2.ZodNumber;
3903
- return plain2 || outputSchema instanceof z2.ZodBoolean ? "value" : "unknown";
3904
- }
3905
- function staticInputSource(ctx, value) {
3906
- if (typeof value !== "string" || !value.startsWith(REF_PREFIX)) return void 0;
3907
- const target = refTarget(ctx, value);
3908
- if (target?.node.type !== "ingest" || target.expr.output !== "asset" || target.expr.index !== void 0)
3909
- return void 0;
3910
- const parsed = target.def.params.safeParse(target.node.params ?? {});
3911
- if (!parsed.success) return void 0;
3912
- const params = parsed.data;
3913
- if (params.source === "path" && typeof params.path === "string") return { path: path3.resolve(params.path) };
3914
- if (params.source === "url" && typeof params.url === "string") return { url: params.url };
3915
- return void 0;
3916
- }
3917
- function checkOneRef(ctx, n, i, refStr, jsonPath, field) {
3918
- if (!refStr.startsWith(REF_PREFIX)) return;
3919
- const expr = parseRefExpr(refStr);
3920
- if (expr?.kind !== "ref") {
3921
- ctx.issues.push({
3922
- path: `nodes[${i}].${field}.${jsonPath}`,
3923
- code: STAGE_CODES.REF_UNRESOLVED,
3924
- message: `malformed ref "${refStr}"`,
3925
- node_id: n.id,
3926
- node_type: n.type
3927
- });
3928
- return;
3929
- }
3930
- const targetIdx = ctx.idToIndex.get(expr.nodeId);
3931
- if (targetIdx === void 0) {
3932
- ctx.issues.push({
3933
- path: `nodes[${i}].${field}.${jsonPath}`,
3934
- code: STAGE_CODES.REF_UNRESOLVED,
3935
- message: `ref "${refStr}" \u2014 no node with id "${expr.nodeId}"`,
3936
- did_you_mean: didYouMean(expr.nodeId, [...ctx.idToIndex.keys()]),
3937
- node_id: n.id,
3938
- node_type: n.type
3939
- });
3940
- return;
3941
- }
3942
- checkRefOutput(ctx, n, i, jsonPath, field, refStr, expr.nodeId, expr.output, targetIdx);
3943
- }
3944
- function checkRefOutput(ctx, n, i, jsonPath, field, refStr, nodeId, output, targetIdx) {
3945
- const target = ctx.canvas.nodes[targetIdx];
3946
- if (!target) return;
3947
- const targetDef = ctx.registry.get(target.type);
3948
- if (!targetDef) return;
3949
- const outputNames = listShapeKeys(targetDef.outputs);
3950
- if (!outputNames || outputNames.includes(output)) return;
3951
- ctx.issues.push({
3952
- path: `nodes[${i}].${field}.${jsonPath}`,
3953
- code: STAGE_CODES.REF_UNRESOLVED,
3954
- message: `ref "${refStr}" \u2014 node "${nodeId}" (${target.type}) has no output "${output}"`,
3955
- did_you_mean: didYouMean(output, outputNames),
3956
- node_id: n.id,
3957
- node_type: n.type
3958
- });
3959
- }
3960
- function checkCycle(ctx) {
3961
- const graph = buildDependencyGraph(ctx.canvas);
3962
- const cycle = findCycle(graph);
3963
- if (cycle) {
3964
- ctx.issues.push({ path: "nodes", code: STAGE_CODES.CYCLE, message: `cycle detected: ${cycle.join(" \u2192 ")}` });
3965
- }
3966
- }
3967
- function checkAllSlots(ctx) {
3968
- for (let i = 0; i < ctx.canvas.nodes.length; i++) {
3969
- const n = ctx.canvas.nodes[i];
3970
- if (!n) continue;
3971
- if (!ctx.registry.get(n.type)) continue;
3972
- checkSlotsForNode(ctx, n, i);
3973
- }
3974
- }
3975
- function checkSlotsForNode(ctx, n, _i) {
3976
- const slots = /* @__PURE__ */ new Set();
3977
- forEachString(n.params, (text) => {
3978
- for (const s of extractSlots(text)) slots.add(s);
3979
- });
3980
- if (slots.size === 0) return;
3981
- const inputKeys = Object.keys(n.inputs ?? {});
3982
- for (const slot of slots) {
3983
- if (inputKeys.includes(slot)) continue;
3984
- ctx.issues.push({
3985
- path: `nodes[${_i}].params`,
3986
- code: STAGE_CODES.SLOT,
3987
- message: `prompt slot "{{${slot}}}" has no matching input on node "${n.id}"`,
3988
- did_you_mean: didYouMean(slot, inputKeys),
3989
- node_id: n.id,
3990
- node_type: n.type
3991
- });
3992
- }
3993
- }
3994
- function checkOrphanNodes(ctx) {
3995
- const out = ctx.canvas.output;
3996
- if (!out || !ctx.idToIndex.has(out.node)) return;
3997
- const graph = buildDependencyGraph(ctx.canvas);
3998
- const reachable = /* @__PURE__ */ new Set();
3999
- const stack = [out.node];
4000
- while (stack.length > 0) {
4001
- const id = stack.pop();
4002
- if (id === void 0 || reachable.has(id)) continue;
4003
- reachable.add(id);
4004
- for (const dep of graph.get(id) ?? []) stack.push(dep);
4005
- }
4006
- for (const n of ctx.canvas.nodes) {
4007
- if (reachable.has(n.id) || n.type === "ingest") continue;
4008
- ctx.issues.push({
4009
- path: `nodes[${ctx.idToIndex.get(n.id)}]`,
4010
- code: STAGE_CODES.ORPHAN_NODE,
4011
- severity: "warning",
4012
- message: `node "${n.id}" (${n.type}) is not reachable from output "${out.node}": a run skips it, so nothing it would make is used; wire it in or remove it`
4013
- });
4014
- }
4015
- }
4016
- function checkOutputRef(ctx) {
4017
- const out = ctx.canvas.output;
4018
- if (!out) return;
4019
- const targetIdx = ctx.idToIndex.get(out.node);
4020
- if (targetIdx === void 0) {
4021
- ctx.issues.push({
4022
- path: "output.node",
4023
- code: STAGE_CODES.OUTPUT,
4024
- message: `output references unknown node "${out.node}"`,
4025
- did_you_mean: didYouMean(out.node, [...ctx.idToIndex.keys()])
4026
- });
4027
- return;
4028
- }
4029
- const target = ctx.canvas.nodes[targetIdx];
4030
- if (!target) return;
4031
- const targetDef = ctx.registry.get(target.type);
4032
- if (!targetDef) return;
4033
- const outs = listShapeKeys(targetDef.outputs);
4034
- if (!outs || outs.includes(out.output)) return;
4035
- ctx.issues.push({
4036
- path: "output.output",
4037
- code: STAGE_CODES.OUTPUT,
4038
- message: `node "${out.node}" (${target.type}) has no output "${out.output}"`,
4039
- did_you_mean: didYouMean(out.output, outs)
4040
- });
4041
- }
4042
- function pushZodIssues(issues, err, pathPrefix, code, nodeId, nodeType) {
4043
- for (const issue of err.issues) {
4044
- const tail = pathToString(issue.path);
4045
- const path4 = pathPrefix ? tail ? `${pathPrefix}.${tail}` : pathPrefix : tail;
4046
- issues.push({
4047
- path: path4,
4048
- code,
4049
- message: issue.message,
4050
- received: issue.code === "invalid_type" ? issue.received : void 0,
4051
- node_id: nodeId,
4052
- node_type: nodeType
4053
- });
4054
- }
4055
- }
4056
- function pathToString(path4) {
4057
- return path4.map((p) => typeof p === "number" ? `[${p}]` : `.${String(p)}`).join("").replace(/^\./, "");
4058
- }
4059
- function walkRefs(value, cb) {
4060
- forEachString(value, (s, path4) => {
4061
- if (s.startsWith(REF_PREFIX)) cb(s, path4);
4062
- });
4063
- }
4064
- function listShapeKeys(schema) {
4065
- const shape = shapeOf(schema);
4066
- return shape ? Object.keys(shape) : void 0;
4067
- }
4068
- function shapeOf(schema) {
4069
- const obj = unwrap(schema);
4070
- return obj instanceof z2.ZodObject ? obj.shape : void 0;
4071
- }
4072
- function unwrap(schema) {
4073
- let cur = schema;
4074
- for (let i = 0; i < 8; i++) {
4075
- const inner = cur._def?.innerType;
4076
- if (!inner) return cur;
4077
- cur = inner;
4078
- }
4079
- return cur;
4080
- }
4081
-
4082
- // src/engine/nodes/local/lib/variant-prefilter.ts
4083
- import { createHash } from "crypto";
4084
- import sharp from "sharp";
4085
- function coverOf(from, to) {
4086
- const scale = Math.max(to.width / from.width, to.height / from.height);
4087
- const width = from.width * scale;
4088
- const height = from.height * scale;
4089
- return { scale, width, height, centre: { left: (width - to.width) / 2, top: (height - to.height) / 2 } };
4090
- }
4091
- function framingOf(from, to) {
4092
- let frame;
4093
- return {
4094
- place: (boxes) => {
4095
- frame = frameKeeping(boxes, from, to);
4096
- return coverFitBoxes(boxes, from, to, frame);
4097
- },
4098
- moved: () => {
4099
- const { centre } = coverOf(from, to);
4100
- return frame && (Math.abs(frame.left - centre.left) >= 1 || Math.abs(frame.top - centre.top) >= 1) ? frame : void 0;
4101
- }
4102
- };
4103
- }
4104
- async function cutToFrame(bytes, frame, to) {
4105
- const { width = 0, height = 0 } = await sharp(bytes).metadata();
4106
- const { scale } = coverOf({ width, height }, to);
4107
- const w = Math.min(width, Math.round(to.width / scale));
4108
- const h = Math.min(height, Math.round(to.height / scale));
4109
- const left = Math.min(width - w, Math.max(0, Math.round(frame.left / scale)));
4110
- const top = Math.min(height - h, Math.max(0, Math.round(frame.top / scale)));
4111
- return sharp(bytes).extract({ left, top, width: w, height: h }).png().toBuffer();
4112
- }
4113
- var FRAME_MARGIN = 0.02;
4114
- function frameKeeping(boxes, from, to) {
4115
- const cover = coverOf(from, to);
4116
- if (boxes.length === 0) return cover.centre;
4117
- const along = (offset, extent, frame, lo, hi) => {
4118
- const low = lo * extent;
4119
- const high = hi * extent;
4120
- const room = extent - frame;
4121
- const margin = FRAME_MARGIN * frame;
4122
- for (const m of [margin, 0]) {
4123
- const min = Math.max(0, high + m - frame);
4124
- const max = Math.min(room, low - m);
4125
- if (min <= max) return Math.min(max, Math.max(min, offset));
4126
- }
4127
- return offset;
4128
- };
4129
- const x0 = Math.min(...boxes.map((b) => b.x0));
4130
- const x1 = Math.max(...boxes.map((b) => b.x1));
4131
- const y0 = Math.min(...boxes.map((b) => b.y0));
4132
- const y1 = Math.max(...boxes.map((b) => b.y1));
4133
- return {
4134
- left: along(cover.centre.left, cover.width, to.width, x0, x1),
4135
- top: along(cover.centre.top, cover.height, to.height, y0, y1)
4136
- };
4137
- }
4138
- function coverFitBoxes(boxes, from, to, frame) {
4139
- const cover = coverOf(from, to);
4140
- const { left: offX, top: offY } = frame ?? cover.centre;
4141
- const x = (f) => (f * cover.width - offX) / to.width;
4142
- const y = (f) => (f * cover.height - offY) / to.height;
4143
- return boxes.map((b) => ({ ...b, x0: x(b.x0), y0: y(b.y0), x1: x(b.x1), y1: y(b.y1) }));
4144
- }
4145
- var NO_LOGO = "The brand's logo was not found on it.";
4146
- function rankOf(boxes, blocking, want) {
4147
- const allowed = want.brand ? [want.brand] : [];
4148
- const off = textMismatch(want.lines, boxes, { allowed }).lines;
4149
- const brand = want.brand ? normalizeCopy(want.brand) : null;
4150
- const logoFound = boxes.some((b) => b.kind === "logo" || brand !== null && normalizeCopy(b.text) === brand);
4151
- const noLogo = want.logo && !logoFound ? 1 : 0;
4152
- const noButton = off.some((l) => want.buttons.has(l.text)) ? 1 : 0;
4153
- const outOfBand = bandViolations(boxes, want.format, want.grade).length;
4154
- return {
4155
- rank: [blocking.length, off.length, noLogo, noButton, outOfBand],
4156
- shortfalls: noLogo ? [NO_LOGO] : []
4157
- };
4158
- }
4159
- var isButton2 = (line) => /cta|button/i.test(`${line.role ?? ""} ${line.look ?? ""}`);
4160
- function staticChecksOf(spec) {
4161
- if (spec && typeof spec === "object" && spec.kind === "static_checks")
4162
- return spec;
4163
- try {
4164
- const parsed = readAdSpec(spec);
4165
- const native = parsed.brand_presence === "native";
4166
- return {
4167
- kind: "static_checks",
4168
- lines: parsed.copy.map((l) => ({ text: l.text, lettering: l.lettering, ...l.role ? { role: l.role } : {} })),
4169
- buttons: native ? [] : parsed.copy.filter(isButton2).map((l) => l.text),
4170
- ...parsed.brand?.name ? { brand: parsed.brand.name } : {},
4171
- logo: parsed.logo === true && !native
4172
- };
4173
- } catch {
4174
- return null;
4175
- }
4176
- }
4177
- async function prefilterVariants(variants, spec, formats, opts = {}) {
4178
- const everything = {
4179
- kept: seededOrder(
4180
- variants.map((v) => v.index),
4181
- variants
4182
- ),
4183
- dropped: []
4184
- };
4185
- const checks = spec === null || spec === void 0 ? null : staticChecksOf(spec);
4186
- if (!checks) return everything;
4187
- const { lines, brand, logo } = checks;
4188
- const buttons = new Set(checks.buttons);
4189
- if (formats.length === 0 || variants.length === 0) return everything;
4190
- const plan = planStaticFormats(AD_IMAGE_MODEL, formats, {
4191
- masterShape: opts.masterShape ?? DEFAULT_MASTER_SHAPE,
4192
- ...opts.placements ? { placements: opts.placements } : {}
4193
- });
4194
- const format = plan.master.format;
4195
- const grade = plan.masterShape === "relayout" ? plan.placements ? { placements: plan.placements } : {} : { band: plan.band };
4196
- const want = { lines, buttons, ...brand ? { brand } : {}, logo, format, grade };
4197
- const checked = await Promise.all(
4198
- variants.map(
4199
- (variant) => checkVariant(variant, { format, size: plan.master.size, lines, brand, grade, want, opts })
4200
- )
4201
- );
4202
- const outcome = prefilterOutcome(checked);
4203
- const frames = Object.fromEntries(checked.flatMap((c) => "frame" in c && c.frame ? [[c.index, c.frame]] : []));
4204
- return {
4205
- ...outcome,
4206
- kept: seededOrder(outcome.kept, variants),
4207
- ...Object.keys(frames).length > 0 ? { frames, masterSize: plan.master.size } : {}
4208
- };
4209
- }
4210
- async function checkVariant(variant, c) {
4211
- const { format, lines, brand, grade, want, opts } = c;
4212
- const source = variant.path ?? variant.url;
4213
- if (!source) return { index: variant.index, blocking: null };
4214
- try {
4215
- const bytes = await bytesOf(source);
4216
- const { width = 0, height = 0 } = await sharp(bytes).metadata();
4217
- const framing = framingOf({ width, height }, c.size);
4218
- const check = await checkStaticImage(bytes, {
4219
- format,
4220
- lines,
4221
- allowed: brand ? [brand] : [],
4222
- ...grade,
4223
- toFrame: framing.place,
4224
- ...opts.read ? { read: opts.read } : {},
4225
- ...opts.onSpend ? { onSpend: opts.onSpend } : {}
4226
- });
4227
- if (check.status === "not_checked") return { index: variant.index, blocking: null };
4228
- const prefix = `${format}: `;
4229
- const blocking = check.findings.filter((f) => f.severity === "blocking").map((f) => f.what.startsWith(prefix) ? f.what.slice(prefix.length) : f.what);
4230
- const frame = framing.moved();
4231
- return { index: variant.index, blocking, ...rankOf(check.boxes, blocking, want), ...frame ? { frame } : {} };
4232
- } catch {
4233
- return { index: variant.index, blocking: null };
4234
- }
4235
- }
4236
- function seededOrder(indexes, variants) {
4237
- const shaOf = new Map(variants.map((v) => [v.index, v.sha256]));
4238
- const seed = [...variants.map((v) => v.sha256)].sort().join(":");
4239
- const key = (index) => createHash("sha256").update(`${seed}|${shaOf.get(index) ?? index}`).digest("hex");
4240
- return [...indexes].sort((a, b) => key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : a - b);
4241
- }
4242
- var compareRanks = (a, b) => {
4243
- for (let i = 0; i < a.length; i++) {
4244
- const d = (a[i] ?? 0) - (b[i] ?? 0);
4245
- if (d !== 0) return d;
4246
- }
4247
- return 0;
4248
- };
4249
- function prefilterOutcome(checked) {
4250
- const unchecked = checked.filter((c) => c.blocking === null).map((c) => c.index);
4251
- const read = checked.flatMap((c) => c.blocking === null ? [] : [c]);
4252
- const extra = unchecked.length > 0 ? { unchecked } : {};
4253
- const clean = read.filter((c) => c.blocking.length === 0);
4254
- if (clean.length === 0 && unchecked.length === 0) {
4255
- const best = [...read].sort((a, b) => compareRanks(a.rank, b.rank) || a.index - b.index)[0];
4256
- if (!best) return { kept: [], dropped: [], ...extra };
4257
- const dropped2 = read.filter((c) => c.index !== best.index).map((c) => ({ index: c.index, reasons: c.blocking }));
4258
- return { kept: [best.index], dropped: dropped2, flagged: { index: best.index, reasons: best.blocking }, ...extra };
4259
- }
4260
- const top = [...clean].sort((a, b) => compareRanks(a.rank, b.rank))[0]?.rank;
4261
- const kept = checked.filter((c) => c.blocking === null || top !== void 0 && compareRanks(c.rank, top) === 0).map((c) => c.index);
4262
- const dropped = read.filter((c) => !kept.includes(c.index)).map((c) => ({ index: c.index, reasons: c.blocking.length > 0 ? c.blocking : c.shortfalls }));
4263
- return { kept, dropped, ...extra };
4264
- }
4265
- function judgeAnswerOf(raw) {
4266
- const answer = raw;
4267
- const best = Number(answer?.best);
4268
- if (typeof answer?.reasoning !== "string" || !Number.isInteger(best)) return null;
4269
- return { reasoning: answer.reasoning, best };
4270
- }
4271
- async function judgeTie(tied, opts) {
4272
- const jpegs = [];
4273
- for (const variant of tied) {
4274
- const source = variant.path ?? variant.url;
4275
- if (!source) return null;
4276
- try {
4277
- const bytes = await bytesOf(source);
4278
- jpegs.push(
4279
- (await sharp(bytes).resize(1024, 1024, { fit: "inside" }).jpeg({ quality: 85 }).toBuffer()).toString("base64")
4280
- );
4281
- } catch {
4282
- return null;
4283
- }
4284
- }
4285
- const prompt = `${opts.prompt}
4286
-
4287
- The candidates are Image 1 to Image ${jpegs.length}, in the order shown. Reply with only this JSON: {"reasoning":"<the ranking lines, then one sentence>","best":<the number of the image that wins>}`;
4288
- const ask = opts.ask ?? ((model, images, text) => askAboutImages(model, images, text, opts.onSpend));
4289
- for (let attempt = 0; attempt < 2; attempt++) {
4290
- const answer = judgeAnswerOf(await ask(opts.model, jpegs, prompt));
4291
- const winner = answer ? tied[answer.best - 1] : void 0;
4292
- if (answer && winner) return { chosen: winner.index, reasoning: answer.reasoning };
4293
- }
4294
- return null;
4295
- }
4296
-
4297
- // src/engine/lib/spell-number.ts
4298
- import { toCardinal as nwAr } from "n2words/ar-SA";
4299
- import { toCardinal as nwDe } from "n2words/de-DE";
4300
- import { toCardinal as nwEn } from "n2words/en-US";
4301
- import { toCardinal as nwEs } from "n2words/es-ES";
4302
- import { toCardinal as nwFr } from "n2words/fr-FR";
4303
- import { toCardinal as nwHi } from "n2words/hi-IN";
4304
- import { toCardinal as nwIt } from "n2words/it-IT";
4305
- import { toCardinal as nwJa } from "n2words/ja-JP";
4306
- import { toCardinal as nwKo } from "n2words/ko-KR";
4307
- import { toCardinal as nwNl } from "n2words/nl-NL";
4308
- import { toCardinal as nwPl } from "n2words/pl-PL";
4309
- import { toCardinal as nwPt } from "n2words/pt-PT";
4310
- var INTEGER_SPELLERS = {
4311
- fr: nwFr,
4312
- es: nwEs,
4313
- en: nwEn,
4314
- de: nwDe,
4315
- it: nwIt,
4316
- pt: nwPt,
4317
- nl: nwNl,
4318
- pl: nwPl,
4319
- ar: nwAr,
4320
- ja: nwJa,
4321
- ko: nwKo,
4322
- hi: nwHi
4323
- };
4324
- function canSpellNumbers(langCode) {
4325
- return langCode in INTEGER_SPELLERS;
4326
- }
4327
- function spellNumber(langCode, n) {
4328
- const spell = langCode ? INTEGER_SPELLERS[langCode] : void 0;
4329
- if (!spell || !Number.isFinite(n)) return String(n);
4330
- try {
4331
- return spell(n);
4332
- } catch {
4333
- return String(n);
4334
- }
4335
- }
4336
-
4337
- export {
4338
- SEEDANCE_PROFILE,
4339
- clipProfileFor,
4340
- clipParamRecipe,
4341
- nativeDialogueOf,
4342
- GPT_IMAGE_25_PROFILE,
4343
- imageProfileFor,
4344
- MASTER_SHAPES,
4345
- DEFAULT_MASTER_SHAPE,
4346
- deliveredSize,
4347
- ASPECT_ADAPT_FORMATS,
4348
- planStaticFormats,
4349
- cutToFrame,
4350
- staticChecksOf,
4351
- prefilterVariants,
4352
- judgeTie,
4353
- compileStaticBrief,
4354
- briefCopyLines,
4355
- judgeBrief,
4356
- STATIC_ADAPT_FORMAT_ROLE,
4357
- adaptPlan,
4358
- trimmedRenderBand,
4359
- adaptBrief,
4360
- imageRenderParams,
4361
- STATIC_VARIANTS_ROLE,
4362
- STATIC_VARIANT_CALL_ROLE,
4363
- VARIANT_PREFILTER_ROLE,
4364
- STATIC_PICK_ROLE,
4365
- STATIC_MASTER_ROLE,
4366
- STATIC_SIZE_ROLE,
4367
- STATIC_ADAPT_ROLE,
4368
- STATIC_SAFE_FIT_ROLE,
4369
- STATIC_PLACE_ROLE,
4370
- LOGO_COMPOSITE_ROLE,
4371
- SERIES_LEAD_ROLE,
4372
- STATIC_FIX_ROLE,
4373
- LOGO_COMPOSITE_DEFAULT,
4374
- nodesWithRole,
4375
- rolesById,
4376
- relaidPictureRole,
4377
- formatSlug,
4378
- staticMasterFormat,
4379
- staticRenderNodes,
4380
- safeFitNode,
4381
- RENDER_GRAPH_IDS,
4382
- regenerateTargets,
4383
- BackendClient,
4384
- REF_PREFIX,
4385
- parseRefExpr,
4386
- NodeDecl,
4387
- renderTemplate,
4388
- priceNode,
4389
- estimateNodeCredits,
4390
- sumCredits,
4391
- nodeDependencies,
4392
- buildDependencyGraph,
4393
- elementMentionKeywords,
4394
- frameDescriptionOf,
4395
- READ_WORDS_PER_SECOND,
4396
- wordCount2 as wordCount,
4397
- estSpeechS,
4398
- estSpeechWindowS,
4399
- renderedPromptOverLimit,
4400
- topologicalLayers,
4401
- reachableNodes,
4402
- subgraph,
4403
- validateCanvas,
4404
- validateCanvasDeep,
4405
- canSpellNumbers,
4406
- spellNumber
4407
- };
4408
- //# sourceMappingURL=chunk-WJOQ6IEK.js.map