@sonordev/site-kit 8.8.0 → 8.9.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 (461) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +9 -0
  3. package/agent-manifest.json +8 -1
  4. package/dist/AnalyticsProvider-X7JJN3QL.js +19 -0
  5. package/dist/{ArticleViewTracker-PV2RD3P6.js → ArticleViewTracker-7Q6AMCKX.js} +11 -10
  6. package/dist/{BlocksPopup-NEEFV2L3.js → BlocksPopup-U5VIYEBA.js} +4 -5
  7. package/dist/ChatWidget-EAATOFCT.js +30 -0
  8. package/dist/{FileField-MJ3RCNFJ.js → FileField-YWMSK7RF.js} +1 -3
  9. package/dist/{FormSpotlight-NQPQFZS7.js → FormSpotlight-MAXDW3TK.js} +1 -1
  10. package/dist/{FormStage-4T5PJR5K.js → FormStage-VAHRVOIZ.js} +1 -1
  11. package/dist/ManagedForm-7IZT7ZII.js +29 -0
  12. package/dist/{ManagedNewsletterForm-TUYGN6O3.js → ManagedNewsletterForm-VVXZSEAL.js} +21 -11
  13. package/dist/{SignalCore-OW3YIH6M.js → SignalCore-HAN4O7QG.js} +90 -36
  14. package/dist/{SiteChat-XMKHBSEK.js → SiteChat-3JLCEL2N.js} +2 -2
  15. package/dist/{SiteDesignReporter-BRH7ORQS.js → SiteDesignReporter-6CCO645E.js} +5 -6
  16. package/dist/SitePopups-ZGHQNGGH.js +19 -0
  17. package/dist/SitemapSync-5LSYCBKS.js +7 -0
  18. package/dist/SitemapSync.d.ts +1 -1
  19. package/dist/_client/booking-widget.d.ts +1 -1
  20. package/dist/_client/booking-widget.js +6 -7
  21. package/dist/_client/reviewed-signal-experiment.d.ts +9 -0
  22. package/dist/_client/reviewed-signal-experiment.js +89 -0
  23. package/dist/_client/testimonial-section.d.ts +1 -1
  24. package/dist/admin-auth/index.d.ts +3 -3
  25. package/dist/affiliates/AffiliatesWidget.d.ts +1 -1
  26. package/dist/affiliates/api.d.ts +1 -1
  27. package/dist/affiliates/index.d.ts +6 -6
  28. package/dist/affiliates/index.js +1 -3
  29. package/dist/affiliates/useAffiliates.d.ts +1 -1
  30. package/dist/analytics/AnalyticsProvider.d.ts +1 -1
  31. package/dist/analytics/dispatch.d.ts +1 -1
  32. package/dist/analytics/index.d.ts +6 -6
  33. package/dist/analytics/index.js +22 -12
  34. package/dist/analytics/publication-cta.d.ts +1 -1
  35. package/dist/analytics/send-gate.d.ts +8 -4
  36. package/dist/articles/Article.d.ts +1 -1
  37. package/dist/articles/ArticleFAQ.d.ts +1 -1
  38. package/dist/articles/ArticleFaqSection.d.ts +1 -1
  39. package/dist/articles/ArticleList.d.ts +1 -1
  40. package/dist/articles/ArticleViewTracker.d.ts +1 -1
  41. package/dist/articles/AuthorCard.d.ts +1 -1
  42. package/dist/articles/AuthorPage.d.ts +1 -1
  43. package/dist/articles/ClusterLandingPage.d.ts +1 -1
  44. package/dist/articles/ClusterNavigation.d.ts +1 -1
  45. package/dist/articles/ManagedNewsletterForm.d.ts +2 -2
  46. package/dist/articles/NewsletterShell.d.ts +1 -1
  47. package/dist/articles/NewsletterWidget.d.ts +1 -1
  48. package/dist/articles/PublicationLayout.d.ts +1 -1
  49. package/dist/articles/PublicationSidebar.d.ts +1 -1
  50. package/dist/articles/RelatedPosts.d.ts +1 -1
  51. package/dist/articles/ServiceCallout.d.ts +1 -1
  52. package/dist/articles/TableOfContents.d.ts +1 -1
  53. package/dist/articles/article-photos.d.ts +1 -1
  54. package/dist/articles/article-styles.d.ts +1 -1
  55. package/dist/articles/article-tables.d.ts +1 -1
  56. package/dist/articles/artwork.d.ts +1 -1
  57. package/dist/articles/author-schema.d.ts +1 -1
  58. package/dist/articles/author-social.d.ts +1 -1
  59. package/dist/articles/excerpt.d.ts +1 -1
  60. package/dist/articles/index.d.ts +1 -1
  61. package/dist/articles/index.js +3 -3
  62. package/dist/articles/news-sitemap.d.ts +1 -1
  63. package/dist/articles/processArticleHtml.d.ts +1 -1
  64. package/dist/articles/public-reads.d.ts +1 -1
  65. package/dist/articles/reading-time.d.ts +1 -1
  66. package/dist/articles/routes.d.ts +1 -1
  67. package/dist/articles/server-core.d.ts +1 -1
  68. package/dist/articles/server-ui.d.ts +1 -1
  69. package/dist/articles/server-ui.js +6 -6
  70. package/dist/articles/server.d.ts +1 -1
  71. package/dist/articles/server.js +5 -5
  72. package/dist/articles/types.d.ts +1 -1
  73. package/dist/articles/widget-styles.d.ts +1 -1
  74. package/dist/brand/extract-brand-profile.d.ts +1 -1
  75. package/dist/chat/ChatWidget.d.ts +1 -1
  76. package/dist/chat/EchoUiActions.d.ts +1 -1
  77. package/dist/chat/SiteChat.d.ts +2 -2
  78. package/dist/chat/chat-messages.d.ts +1 -1
  79. package/dist/chat/index.d.ts +7 -7
  80. package/dist/chat/index.js +17 -9
  81. package/dist/chat/launcher-placement.d.ts +1 -1
  82. package/dist/{chunk-OPUY4RS6.js → chunk-2LBKIE2K.js} +2 -1
  83. package/dist/{chunk-XIDWNXZN.js → chunk-3W3MN6Z7.js} +2 -2
  84. package/dist/{chunk-UPFU63L4.js → chunk-55GFMOI6.js} +1 -1
  85. package/dist/chunk-5IVD6G77.js +37 -0
  86. package/dist/{chunk-URLTBVRX.js → chunk-6EDE7BOE.js} +3 -3
  87. package/dist/{chunk-DYROJGI2.js → chunk-6NLVHTYY.js} +1 -1
  88. package/dist/{chunk-ZQSCMYZO.js → chunk-7643MCD2.js} +6 -3
  89. package/dist/chunk-7IZUUXXO.js +1 -0
  90. package/dist/{chunk-C7TLGL3R.js → chunk-7VMA3KCN.js} +30 -4
  91. package/dist/{chunk-OWZWRP7Y.js → chunk-ADQ3VCKK.js} +2 -2
  92. package/dist/chunk-AG2RDHYZ.js +45 -0
  93. package/dist/chunk-AWLPFFPO.js +36 -0
  94. package/dist/{chunk-LNSNA3W4.js → chunk-C4XK472G.js} +4 -3
  95. package/dist/{chunk-TP32MRXV.js → chunk-CJ43K7RA.js} +2 -2
  96. package/dist/chunk-D4M5K6JN.js +25 -0
  97. package/dist/{chunk-F6WLHVMQ.js → chunk-DXE6SXWW.js} +1 -1
  98. package/dist/{chunk-YEX5RAEU.js → chunk-EBEM5BI5.js} +38 -27
  99. package/dist/chunk-F3RKFGRW.js +2 -0
  100. package/dist/chunk-FAY6MOLL.js +62 -0
  101. package/dist/chunk-FRY6PBT6.js +31 -0
  102. package/dist/{chunk-JYITKUUB.js → chunk-G5WUX7VE.js} +1 -1
  103. package/dist/chunk-HJD6KXKH.js +147 -0
  104. package/dist/{chunk-6BFQI3ZE.js → chunk-IO6BWRZ5.js} +2 -2
  105. package/dist/{chunk-THXELZTQ.js → chunk-ISMHDUBW.js} +1 -1
  106. package/dist/{chunk-SLFYTELD.js → chunk-J4LJE4NB.js} +1 -1
  107. package/dist/{chunk-LQJC5S2M.js → chunk-J6D3NEDE.js} +57 -0
  108. package/dist/chunk-JT7GHHMV.js +56 -0
  109. package/dist/{chunk-NFGAXWCN.js → chunk-KGIQYVQ2.js} +31 -13
  110. package/dist/chunk-KMNHNCVG.js +20 -0
  111. package/dist/chunk-KQKUXIUD.js +25 -0
  112. package/dist/{chunk-M5JIHQ42.js → chunk-L3S5WH4C.js} +2 -2
  113. package/dist/chunk-MDBXGN6W.js +13 -0
  114. package/dist/chunk-MOACHHOJ.js +20 -0
  115. package/dist/{chunk-6DXQHNZK.js → chunk-MP35JETP.js} +1 -1
  116. package/dist/chunk-NHDRA4Y5.js +6 -0
  117. package/dist/{chunk-AYQEIMGS.js → chunk-NZHECYLF.js} +103 -22
  118. package/dist/{chunk-EATJPSTS.js → chunk-O5LQUBBN.js} +1 -1
  119. package/dist/{chunk-Y6SHFILE.js → chunk-OEOSR5EG.js} +1 -1
  120. package/dist/{chunk-GQN4BGK6.js → chunk-OUD4RTBD.js} +1 -1
  121. package/dist/chunk-PMXQPO5Q.js +78 -0
  122. package/dist/chunk-PVY7X5UR.js +21 -0
  123. package/dist/{chunk-KG5IEV7I.js → chunk-QLJ2SC3T.js} +1 -1
  124. package/dist/{chunk-2WM4TNDF.js → chunk-QXAJPB6O.js} +1 -1
  125. package/dist/{chunk-2WDWQI7K.js → chunk-RYR6N4X4.js} +1 -1
  126. package/dist/{chunk-G7BRCSV6.js → chunk-S33CUULL.js} +3 -3
  127. package/dist/{chunk-YRQS7VAH.js → chunk-S63QY5XW.js} +2 -2
  128. package/dist/{chunk-GXWM5KNS.js → chunk-SO6RSRUP.js} +40 -45
  129. package/dist/{chunk-ODOV76VW.js → chunk-STIVFCWM.js} +1 -1
  130. package/dist/{chunk-SVHPILS3.js → chunk-T4A5J6SJ.js} +1 -1
  131. package/dist/chunk-TPHOIE4I.js +73 -0
  132. package/dist/{chunk-IWNJCRXW.js → chunk-UG53ZW7S.js} +125 -118
  133. package/dist/chunk-VXCUXHZO.js +9 -0
  134. package/dist/{chunk-SBPUFTMP.js → chunk-WHFWDK2H.js} +2 -2
  135. package/dist/chunk-WYNDBJG7.js +66 -0
  136. package/dist/{chunk-HN7GVGWC.js → chunk-XDH5B3PR.js} +3 -3
  137. package/dist/{chunk-PAYCK7FC.js → chunk-XJZLXUTG.js} +1 -1
  138. package/dist/{chunk-IDC5CVBR.js → chunk-YVEYO3OD.js} +1 -1
  139. package/dist/{chunk-PSS6M4CG.js → chunk-ZRSXCT4O.js} +2 -2
  140. package/dist/client/index.d.ts +8 -8
  141. package/dist/client/index.js +9 -8
  142. package/dist/config/index.d.ts +1 -1
  143. package/dist/contracts/broadcast-layouts.d.ts +8 -0
  144. package/dist/contracts/crm-disposition.d.ts +87 -0
  145. package/dist/contracts/decision-roles.d.ts +39 -0
  146. package/dist/contracts/entries.d.ts +1 -1
  147. package/dist/contracts/funnels.d.ts +85 -2
  148. package/dist/contracts/portfolio.d.ts +1 -1
  149. package/dist/contracts/publication-branding.d.ts +3 -3
  150. package/dist/contracts/resonance-constants.d.ts +4 -0
  151. package/dist/contracts/resonance.d.ts +8461 -0
  152. package/dist/contracts/signal-experiments.d.ts +219 -0
  153. package/dist/contracts/site-edit.d.ts +2 -2
  154. package/dist/contracts/sites.d.ts +1 -1
  155. package/dist/contracts/slots.d.ts +2 -2
  156. package/dist/contracts/wire-schema.d.ts +36 -0
  157. package/dist/cta-bar/CtaBar.d.ts +1 -1
  158. package/dist/cta-bar/client.js +3 -2
  159. package/dist/cta-bar/index.d.ts +4 -4
  160. package/dist/cta-bar/index.js +4 -3
  161. package/dist/fleet/FleetHeartbeat.d.ts +2 -2
  162. package/dist/fleet/contract.d.ts +1 -1
  163. package/dist/fleet/heartbeat.d.ts +2 -2
  164. package/dist/fleet/index.d.ts +6 -6
  165. package/dist/fleet/index.js +4 -5
  166. package/dist/forms/FileField.d.ts +1 -1
  167. package/dist/forms/FormClient.d.ts +2 -2
  168. package/dist/forms/FormEnhancer.d.ts +1 -1
  169. package/dist/forms/FormField.d.ts +1 -1
  170. package/dist/forms/FormSpotlight.d.ts +1 -1
  171. package/dist/forms/FormStage.d.ts +1 -1
  172. package/dist/forms/ManagedForm.d.ts +1 -1
  173. package/dist/forms/PrivateFileField.d.ts +1 -1
  174. package/dist/forms/ServerForm.d.ts +3 -3
  175. package/dist/forms/StaticForm.d.ts +1 -1
  176. package/dist/forms/contract.d.ts +1 -1
  177. package/dist/forms/experience-plan.d.ts +1 -1
  178. package/dist/forms/field-autocomplete.d.ts +1 -1
  179. package/dist/forms/field-controls.d.ts +1 -1
  180. package/dist/forms/field-rules.d.ts +1 -1
  181. package/dist/forms/form-dom-values.d.ts +1 -1
  182. package/dist/forms/index.d.ts +18 -18
  183. package/dist/forms/index.js +21 -11
  184. package/dist/forms/normalize-config.d.ts +1 -1
  185. package/dist/forms/server.d.ts +4 -4
  186. package/dist/forms/server.js +3 -3
  187. package/dist/forms/static.d.ts +2 -2
  188. package/dist/forms/submitForm.d.ts +2 -2
  189. package/dist/forms/tracking-session.d.ts +1 -1
  190. package/dist/forms/useForm.d.ts +2 -2
  191. package/dist/forms/webmcp.d.ts +2 -2
  192. package/dist/images/api.d.ts +1 -1
  193. package/dist/images/index.d.ts +6 -6
  194. package/dist/images/index.js +2 -4
  195. package/dist/images/server.d.ts +2 -2
  196. package/dist/index.d.ts +13 -13
  197. package/dist/index.js +1 -1
  198. package/dist/landing/contract.d.ts +1 -1
  199. package/dist/landing/contract.js +2 -1
  200. package/dist/landing/index.d.ts +3 -3
  201. package/dist/landing/index.js +3 -2
  202. package/dist/landing/managed-server.d.ts +2 -2
  203. package/dist/landing/managed.d.ts +10 -2
  204. package/dist/landing/server.d.ts +7 -7
  205. package/dist/landing/server.js +11 -6
  206. package/dist/layout/AiDiscoveryLinks.d.ts +1 -1
  207. package/dist/layout/SiteKitClientProviders.d.ts +1 -1
  208. package/dist/layout/SiteKitLayout.d.ts +2 -2
  209. package/dist/layout/client.d.ts +1 -1
  210. package/dist/layout/client.js +15 -11
  211. package/dist/layout/index.d.ts +5 -5
  212. package/dist/layout/index.js +17 -13
  213. package/dist/layout/types.d.ts +2 -2
  214. package/dist/llms/AEOComponents.d.ts +1 -1
  215. package/dist/llms/SpeakableSchema.d.ts +1 -1
  216. package/dist/llms/api.d.ts +1 -1
  217. package/dist/llms/client.d.ts +3 -3
  218. package/dist/llms/contract.d.ts +1 -1
  219. package/dist/llms/generateLLMsTxt.d.ts +1 -1
  220. package/dist/llms/handlers.d.ts +1 -1
  221. package/dist/llms/index.d.ts +21 -21
  222. package/dist/llms/index.js +5 -5
  223. package/dist/llms/merge.d.ts +1 -1
  224. package/dist/llms/sourcedFacts.d.ts +1 -1
  225. package/dist/llms/types.d.ts +5 -5
  226. package/dist/llms/writeLLMsTxt.d.ts +3 -3
  227. package/dist/maps/GoogleMap.d.ts +1 -1
  228. package/dist/maps/NeighborhoodMap.d.ts +1 -1
  229. package/dist/maps/api.d.ts +1 -1
  230. package/dist/maps/index.d.ts +5 -5
  231. package/dist/maps/index.js +1 -3
  232. package/dist/mcp/WebMcpTools.d.ts +1 -1
  233. package/dist/mcp/client.d.ts +2 -2
  234. package/dist/mcp/discovery.d.ts +2 -2
  235. package/dist/mcp/handlers.d.ts +4 -4
  236. package/dist/mcp/index.d.ts +12 -12
  237. package/dist/mcp/protocol.d.ts +1 -1
  238. package/dist/mcp/report.d.ts +1 -1
  239. package/dist/mcp/serverCard.d.ts +1 -1
  240. package/dist/mcp/sonor.d.ts +5 -5
  241. package/dist/mcp/sonor.js +10 -10
  242. package/dist/mcp/transport.d.ts +1 -1
  243. package/dist/motion/Parallax.d.ts +1 -1
  244. package/dist/motion/ScrollScene.d.ts +1 -1
  245. package/dist/motion/gsap.d.ts +2 -2
  246. package/dist/motion/index.d.ts +9 -9
  247. package/dist/motion/three.d.ts +1 -1
  248. package/dist/og/index.d.ts +5 -5
  249. package/dist/og/pages.d.ts +1 -1
  250. package/dist/og/route-fit.d.ts +17 -0
  251. package/dist/og/route.d.ts +8 -0
  252. package/dist/og/route.js +1 -1
  253. package/dist/og/template.d.ts +1 -1
  254. package/dist/portfolio/contract.d.ts +1 -1
  255. package/dist/press-kit/articles/Article.d.ts +2 -2
  256. package/dist/press-kit/articles/ArticleFaqSection.d.ts +1 -1
  257. package/dist/press-kit/articles/ArticleList.d.ts +2 -2
  258. package/dist/press-kit/articles/AuthorCard.d.ts +1 -1
  259. package/dist/press-kit/articles/AuthorPage.d.ts +1 -1
  260. package/dist/press-kit/articles/ClusterLandingPage.d.ts +2 -2
  261. package/dist/press-kit/articles/ClusterNavigation.d.ts +2 -2
  262. package/dist/press-kit/articles/ManagedNewsletterForm.d.ts +2 -2
  263. package/dist/press-kit/articles/PublicationLayout.d.ts +2 -2
  264. package/dist/press-kit/articles/PublicationSidebar.d.ts +1 -1
  265. package/dist/press-kit/articles/RelatedPosts.d.ts +2 -2
  266. package/dist/press-kit/articles/TableOfContents.d.ts +1 -1
  267. package/dist/press-kit/articles/article-body.d.ts +1 -1
  268. package/dist/press-kit/articles/article-headings.d.ts +1 -1
  269. package/dist/press-kit/articles/article-photos.d.ts +1 -1
  270. package/dist/press-kit/articles/artwork.d.ts +2 -2
  271. package/dist/press-kit/articles/author-schema.d.ts +2 -2
  272. package/dist/press-kit/articles/author-social.d.ts +1 -1
  273. package/dist/press-kit/articles/index.d.ts +23 -23
  274. package/dist/press-kit/articles/news-sitemap.d.ts +2 -2
  275. package/dist/press-kit/articles/public-reads.d.ts +1 -1
  276. package/dist/press-kit/articles/publication-reads.d.ts +2 -2
  277. package/dist/press-kit/articles/reading-time.d.ts +1 -1
  278. package/dist/press-kit/articles/server-core.d.ts +20 -20
  279. package/dist/press-kit/articles/server-ui.d.ts +20 -20
  280. package/dist/press-kit/articles/types.d.ts +1 -1
  281. package/dist/proxy/index.d.ts +5 -5
  282. package/dist/proxy/index.js +1 -1
  283. package/dist/reputation/TestimonialSection.d.ts +1 -1
  284. package/dist/reputation/VisualReviewCard.d.ts +1 -1
  285. package/dist/reputation/api.d.ts +1 -1
  286. package/dist/reputation/index.d.ts +4 -4
  287. package/dist/reputation/server.d.ts +3 -3
  288. package/dist/resonance/ResonanceRuntime.d.ts +7 -0
  289. package/dist/resonance/bridge.d.ts +16 -0
  290. package/dist/resonance/consent-adapter.d.ts +26 -0
  291. package/dist/resonance/controller-registry.d.ts +14 -0
  292. package/dist/resonance/controller.d.ts +103 -0
  293. package/dist/resonance/form-context.d.ts +5 -0
  294. package/dist/resonance/index.d.ts +4 -0
  295. package/dist/resonance/index.js +9 -0
  296. package/dist/resonance/meta-pixel.d.ts +67 -0
  297. package/dist/resonance/providers.d.ts +32 -0
  298. package/dist/resonance/runtime-core.d.ts +5 -0
  299. package/dist/revalidate/index.d.ts +3 -3
  300. package/dist/revalidate/index.js +2 -2
  301. package/dist/robots/index.d.ts +3 -3
  302. package/dist/robots/indexnow.js +2 -2
  303. package/dist/runtime/index.d.ts +7 -7
  304. package/dist/runtime/index.js +4 -5
  305. package/dist/runtime-core-WT7KKLWQ.js +2213 -0
  306. package/dist/seo/ManagedFAQ.d.ts +1 -1
  307. package/dist/seo/ManagedInternalLinks.d.ts +1 -1
  308. package/dist/seo/ManagedSchema.d.ts +1 -1
  309. package/dist/seo/ManagedScripts.d.ts +1 -1
  310. package/dist/seo/client.d.ts +1 -1
  311. package/dist/seo/client.js +3 -5
  312. package/dist/seo/entry-indexnow.d.ts +1 -1
  313. package/dist/seo/entry-llms-client.d.ts +1 -1
  314. package/dist/seo/entry-llms-contract.d.ts +1 -1
  315. package/dist/seo/entry-llms.d.ts +1 -1
  316. package/dist/seo/entry-meta-contract.d.ts +1 -1
  317. package/dist/seo/entry-og-route.d.ts +1 -1
  318. package/dist/seo/entry-og.d.ts +1 -1
  319. package/dist/seo/entry-pages-contract.d.ts +1 -1
  320. package/dist/seo/entry-redirects.d.ts +1 -1
  321. package/dist/seo/entry-robots.d.ts +1 -1
  322. package/dist/seo/entry-sitemap.d.ts +1 -1
  323. package/dist/seo/getManagedMetadata.d.ts +1 -1
  324. package/dist/seo/index.d.ts +12 -12
  325. package/dist/seo/index.js +9 -11
  326. package/dist/seo/indexnow.d.ts +1 -1
  327. package/dist/seo/indexnow.js +2 -2
  328. package/dist/seo/llms/client.d.ts +1 -1
  329. package/dist/seo/llms/contract.d.ts +1 -1
  330. package/dist/seo/llms.d.ts +1 -1
  331. package/dist/seo/llms.js +5 -5
  332. package/dist/seo/meta/contract.d.ts +1 -1
  333. package/dist/seo/og/route.d.ts +1 -1
  334. package/dist/seo/og/route.js +1 -1
  335. package/dist/seo/og.d.ts +1 -1
  336. package/dist/seo/pages/contract.d.ts +1 -1
  337. package/dist/seo/redirects.d.ts +1 -1
  338. package/dist/seo/register-sitemap-cli-impl.d.ts +1 -1
  339. package/dist/seo/register-sitemap-cli.js +1 -1
  340. package/dist/seo/robots.d.ts +1 -1
  341. package/dist/seo/routing.d.ts +1 -1
  342. package/dist/seo/server.d.ts +4 -4
  343. package/dist/seo/server.js +3 -3
  344. package/dist/seo/sitemap.d.ts +1 -1
  345. package/dist/seo/sitemap.js +3 -3
  346. package/dist/seo/types.d.ts +1 -1
  347. package/dist/seo-meta/contract.d.ts +1 -1
  348. package/dist/seo-pages/contract.d.ts +1 -1
  349. package/dist/server/index.d.ts +5 -5
  350. package/dist/server/index.js +3 -3
  351. package/dist/server/mint-site-token.d.ts +1 -1
  352. package/dist/server/server-fetch.d.ts +8 -0
  353. package/dist/{server-api-XIKDWSNO.js → server-api-A34TABT2.js} +2 -2
  354. package/dist/shared/build-entries.d.ts +3 -1
  355. package/dist/shared/color.d.ts +1 -1
  356. package/dist/shared/fetchWithRetry.d.ts +6 -0
  357. package/dist/shared/frame.d.ts +1 -1
  358. package/dist/shared/identity-storage.d.ts +6 -4
  359. package/dist/shared/identity.d.ts +1 -3
  360. package/dist/shared/index.d.ts +5 -5
  361. package/dist/shared/index.js +1 -1
  362. package/dist/shared/measurement-analytics.d.ts +1 -0
  363. package/dist/shared/measurement-policy.d.ts +6 -0
  364. package/dist/shared/measurement-proof.d.ts +5 -0
  365. package/dist/shared/measurement-store.d.ts +34 -0
  366. package/dist/shared/measurement-writer.d.ts +6 -0
  367. package/dist/shared/publishCredential.d.ts +1 -1
  368. package/dist/shared/reporting-gate.d.ts +3 -1
  369. package/dist/shared/site-token-claims.d.ts +18 -0
  370. package/dist/shared/site-token-configuration.d.ts +9 -0
  371. package/dist/shared/site-token-refresh.d.ts +7 -0
  372. package/dist/shared/site-token-state.d.ts +23 -0
  373. package/dist/shared/siteToken.d.ts +5 -39
  374. package/dist/shared/sonor-auth-state.d.ts +3 -0
  375. package/dist/shared/sonor-beacon.d.ts +13 -0
  376. package/dist/shared/sonor-fetch-runtime.d.ts +2 -0
  377. package/dist/shared/sonor-fetch.d.ts +7 -0
  378. package/dist/shared/sonorFetch.d.ts +4 -49
  379. package/dist/shared/version.d.ts +1 -1
  380. package/dist/signal/ReviewedSignalExperiment.d.ts +9 -0
  381. package/dist/signal/SignalBridge.d.ts +4 -4
  382. package/dist/signal/SignalCore.d.ts +2 -1
  383. package/dist/signal/SignalExperiment.d.ts +13 -5
  384. package/dist/signal/index.d.ts +3 -3
  385. package/dist/signal/index.js +4 -73
  386. package/dist/signal/store.d.ts +10 -7
  387. package/dist/signal/types.d.ts +4 -0
  388. package/dist/sitemap/index.d.ts +5 -5
  389. package/dist/sitemap/index.js +3 -3
  390. package/dist/sites/contract.d.ts +1 -1
  391. package/dist/sites/normalize.d.ts +1 -1
  392. package/dist/slots/ManagedLink.d.ts +2 -2
  393. package/dist/slots/ManagedList.d.ts +2 -2
  394. package/dist/slots/ManagedRichText.d.ts +1 -1
  395. package/dist/slots/ManagedSlot.d.ts +1 -1
  396. package/dist/slots/contract.d.ts +1 -1
  397. package/dist/slots/edit/locate.d.ts +1 -1
  398. package/dist/slots/index.d.ts +14 -14
  399. package/dist/slots/index.js +2 -2
  400. package/dist/slots/server-api.d.ts +1 -1
  401. package/dist/{chunk-FLOJUMFR.js → sonor-fetch-runtime-3PFGUXMF.js} +27 -36
  402. package/dist/sync/AddToCalendar.d.ts +1 -1
  403. package/dist/sync/BookingWidget.d.ts +1 -1
  404. package/dist/sync/MeetingFormatStep.d.ts +1 -1
  405. package/dist/sync/api.d.ts +2 -2
  406. package/dist/sync/booking-flow.d.ts +1 -1
  407. package/dist/sync/index.d.ts +3 -3
  408. package/dist/sync/index.js +6 -7
  409. package/dist/types.d.ts +6 -6
  410. package/dist/website/BlocksPopup.d.ts +1 -1
  411. package/dist/website/PopupBlocks.d.ts +1 -1
  412. package/dist/website/SiteDesignReporter.d.ts +2 -2
  413. package/dist/website/SitePopups.d.ts +1 -1
  414. package/dist/website/contract.d.ts +1 -1
  415. package/dist/website/cta-bar.d.ts +1 -1
  416. package/dist/website/cta-bar.js +4 -3
  417. package/dist/website/design.d.ts +2 -2
  418. package/dist/website/images/server.d.ts +1 -1
  419. package/dist/website/images-server.d.ts +1 -1
  420. package/dist/website/images.d.ts +1 -1
  421. package/dist/website/images.js +2 -4
  422. package/dist/website/index.d.ts +4 -4
  423. package/dist/website/index.js +5 -6
  424. package/dist/website/landing/server.d.ts +1 -1
  425. package/dist/website/landing/server.js +11 -6
  426. package/dist/website/landing-server.d.ts +1 -1
  427. package/dist/website/landing.d.ts +1 -1
  428. package/dist/website/landing.js +3 -2
  429. package/dist/website/popup-rules.d.ts +2 -2
  430. package/dist/website/popup-types.d.ts +1 -1
  431. package/dist/website/popups.d.ts +4 -4
  432. package/dist/website/popups.js +14 -8
  433. package/dist/website/slots/contract.d.ts +1 -1
  434. package/dist/website/slots-contract.d.ts +1 -1
  435. package/dist/website/slots.d.ts +2 -2
  436. package/dist/website/slots.js +2 -2
  437. package/dist/{writeLLMsTxt-4HHUAOA6.js → writeLLMsTxt-JUACXQKP.js} +2 -2
  438. package/docs.json +5 -0
  439. package/package.json +8 -2
  440. package/src/analytics/README.md +21 -0
  441. package/src/cta-bar/README.md +4 -0
  442. package/src/forms/README.md +29 -0
  443. package/src/landing/README.md +14 -0
  444. package/src/layout/README.md +3 -0
  445. package/src/og/README.md +28 -0
  446. package/src/resonance/README.md +273 -0
  447. package/src/runtime/README.md +6 -1
  448. package/src/signal/README.md +21 -0
  449. package/src/website/README.md +14 -2
  450. package/dist/AnalyticsProvider-7SBJ27SI.js +0 -13
  451. package/dist/ChatWidget-V46UBDOM.js +0 -20
  452. package/dist/ManagedForm-DDDVMKLJ.js +0 -19
  453. package/dist/SitePopups-VU2C3Z6B.js +0 -13
  454. package/dist/SitemapSync-C4H3HF3L.js +0 -9
  455. package/dist/chunk-5ROTTMBL.js +0 -19
  456. package/dist/chunk-5TM3WPCI.js +0 -7
  457. package/dist/chunk-7HW4TZB5.js +0 -148
  458. package/dist/chunk-IPQKQEWI.js +0 -52
  459. package/dist/chunk-LCDVPAMR.js +0 -24
  460. package/dist/chunk-SHAUX2MQ.js +0 -163
  461. /package/dist/{chunk-3MJNBOUO.js → chunk-2QPVU63T.js} +0 -0
@@ -0,0 +1,2213 @@
1
+ import { updateMeasurementPolicy } from './chunk-KQKUXIUD.js';
2
+ import { registerResonanceController, subscribeConsentAdapter, setProviderBlockers, getConsentAdapter } from './chunk-WYNDBJG7.js';
3
+ import { isBlockedPublicUrlHost } from './chunk-XS2ORMUG.js';
4
+ import { normalizeCampaignSlug } from './chunk-2QPVU63T.js';
5
+ import './chunk-MDBXGN6W.js';
6
+ import { RESONANCE_ATTRIBUTION_MODEL, RESONANCE_SOURCE_MARKER_PARAMETER, RESONANCE_SUBJECT_HEADER } from './chunk-NHDRA4Y5.js';
7
+ import { uuid } from './chunk-GJWI74ZZ.js';
8
+ import { isNormalizedSiteHost } from './chunk-NZOJHERI.js';
9
+ import { advertisingPurposesPermitted, experimentMeasurementPermitted } from './chunk-KMNHNCVG.js';
10
+ import { resolveAnalyticsTarget, isFramed, isPrivatePreview, isLocalHostname } from './chunk-7643MCD2.js';
11
+ import './chunk-64XODP7M.js';
12
+ import { sonorFetch } from './chunk-VXCUXHZO.js';
13
+ import './chunk-W7OPEMGJ.js';
14
+ import './chunk-A4CC4ZG3.js';
15
+ import './chunk-MOACHHOJ.js';
16
+ import { measurementPolicySnapshot, subscribeMeasurementPolicy, unknownChoices, MEASUREMENT_PURPOSES } from './chunk-FRY6PBT6.js';
17
+ import './chunk-PKBMQBKP.js';
18
+
19
+ // src/contracts/wire-schema.ts
20
+ function createWireSchema(fail3, isValidationError) {
21
+ const own = (v, k) => Object.prototype.hasOwnProperty.call(v, k);
22
+ const field3 = (schema, read) => ({ schema, read });
23
+ const text3 = (max = 200, pattern, min = 1) => field3({ type: "string", minLength: min, maxLength: max, ...pattern ? { pattern: pattern.source } : {} }, (v, p) => typeof v === "string" && v.length >= min && v.length <= max && (!pattern || pattern.test(v)) ? v : fail3(p, "invalid string"));
24
+ const choice3 = (...values) => field3({ type: "string", enum: values }, (v, p) => typeof v === "string" && values.includes(v) ? v : fail3(p, "unsupported value"));
25
+ const literal3 = (value) => field3({ const: value }, (v, p) => v === value ? value : fail3(p, "unexpected value"));
26
+ const integer3 = (min = 0, max = Number.MAX_SAFE_INTEGER) => field3({ type: "integer", minimum: min, maximum: max }, (v, p) => typeof v === "number" && Number.isSafeInteger(v) && v >= min && v <= max ? v : fail3(p, "invalid integer"));
27
+ const bool2 = field3({ type: "boolean" }, (v, p) => typeof v === "boolean" ? v : fail3(p, "must be boolean"));
28
+ const optional3 = (f) => ({ ...f, optional: true });
29
+ const nullable3 = (f) => field3({ anyOf: [f.schema, { type: "null" }] }, (v, p) => v === null ? null : f.read(v, p));
30
+ const list3 = (f, max = 100, min = 0) => field3({ type: "array", items: f.schema, minItems: min, maxItems: max }, (v, p) => {
31
+ if (!Array.isArray(v) || v.length < min || v.length > max) fail3(p, "invalid array");
32
+ return v.map((x, i) => f.read(x, `${p}[${i}]`));
33
+ });
34
+ const object3 = (fields2) => field3({ type: "object", additionalProperties: false, properties: Object.fromEntries(Object.entries(fields2).map(([k, f]) => [k, f.schema])), required: Object.keys(fields2).filter((k) => !("optional" in fields2[k])) }, (v, p) => {
35
+ if (!v || typeof v !== "object" || Array.isArray(v) || ![Object.prototype, null].includes(Object.getPrototypeOf(v))) fail3(p, "must be a plain object");
36
+ const source2 = v, result = {};
37
+ for (const key of Reflect.ownKeys(source2)) if (typeof key !== "string" || !own(fields2, key)) fail3(p, "unknown field");
38
+ for (const [key, f] of Object.entries(fields2)) {
39
+ if (!own(source2, key)) {
40
+ if ("optional" in f) continue;
41
+ fail3(`${p}.${key}`, "required");
42
+ }
43
+ const descriptor = Object.getOwnPropertyDescriptor(source2, key);
44
+ if (!own(descriptor, "value")) fail3(`${p}.${key}`, "accessors are not wire data");
45
+ result[key] = f.read(descriptor.value, `${p}.${key}`);
46
+ }
47
+ return result;
48
+ });
49
+ const union3 = (...fields2) => field3({ oneOf: fields2.map((f) => f.schema) }, (v, p) => {
50
+ const found = [];
51
+ for (const f of fields2) {
52
+ try {
53
+ found.push(f.read(v, p));
54
+ } catch (e) {
55
+ if (!isValidationError(e)) throw e;
56
+ }
57
+ }
58
+ return found.length === 1 ? found[0] : fail3(p, "must match exactly one supported shape");
59
+ });
60
+ const refine3 = (f, check) => field3(f.schema, (v, p) => {
61
+ const result = f.read(v, p);
62
+ check(result, p);
63
+ return result;
64
+ });
65
+ return { field: field3, text: text3, choice: choice3, literal: literal3, integer: integer3, bool: bool2, optional: optional3, nullable: nullable3, list: list3, object: object3, union: union3, refine: refine3 };
66
+ }
67
+
68
+ // src/contracts/signal-experiments.ts
69
+ var SignalExperimentValidationError = class extends Error {
70
+ constructor(path2, reason) {
71
+ super(`${path2}: ${reason}`);
72
+ this.path = path2;
73
+ this.name = "SignalExperimentValidationError";
74
+ }
75
+ };
76
+ var fail = (path2, reason) => {
77
+ throw new SignalExperimentValidationError(path2, reason);
78
+ };
79
+ var { field, text, choice, literal, integer, optional, nullable, list, object, union, refine } = createWireSchema(fail, (error) => error instanceof SignalExperimentValidationError);
80
+ var percent = field({ type: "number", minimum: 0, maximum: 100 }, (v, p) => typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= 100 ? v : fail(p, "must be a finite percentage from 0 to 100"));
81
+ var page = refine(text(2048, /^\/(?!\/)[^?#\s\\]*$/), (v, p) => {
82
+ const parsed = new URL(v, "https://example.com");
83
+ if (parsed.pathname !== v) fail(p, "must be a canonical page path");
84
+ });
85
+ var variant = object({ key: text(80), name: text(160), weight: percent, description: optional(text(1e3, void 0, 0)) });
86
+ var variants = refine(list(variant, 50, 2), (v, p) => {
87
+ if (new Set(v.map((x) => x.key)).size !== v.length) fail(p, "variant keys must be unique");
88
+ if (Math.abs(v.reduce((sum, x) => sum + x.weight, 0) - 100) > 0.01) fail(p, "variant weights must sum to 100");
89
+ });
90
+ var targeting = object({ pages: optional(list(page, 100)), segments: optional(list(text(160), 100)), devices: optional(list(choice("desktop", "mobile", "tablet"), 3)), geo: optional(list(text(160), 100)) });
91
+ var fields = { name: text(160), description: text(1e4, void 0, 0), variants, traffic_allocation: percent, targeting_rules: targeting };
92
+ var create = object({ ...fields, description: optional(fields.description), traffic_allocation: optional(fields.traffic_allocation), targeting_rules: optional(fields.targeting_rules) });
93
+ var update = object({ name: optional(fields.name), description: optional(fields.description), variants: optional(fields.variants), traffic_allocation: optional(fields.traffic_allocation), targeting_rules: optional(fields.targeting_rules) });
94
+ var parseSignalExperimentCreate = (v) => create.read(v, "experimentCreate");
95
+ var parseSignalExperimentUpdate = (v) => update.read(v, "experimentUpdate");
96
+ var SIGNAL_EXPERIMENT_CREATE_JSON_SCHEMA = create.schema;
97
+ var SIGNAL_EXPERIMENT_UPDATE_JSON_SCHEMA = update.schema;
98
+ var measurementUuid = text(36, /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
99
+ var measurementRevision = text(128, /^[a-zA-Z0-9._:-]+$/);
100
+ var measurementInstant = refine(text(30, /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/), (v, p) => {
101
+ if (!Number.isFinite(Date.parse(v)) || new Date(v).toISOString().replace(".000Z", "Z") !== v.replace(".000Z", "Z")) fail(p, "invalid UTC instant");
102
+ });
103
+ var measurementSite = refine(text(253), (v, p) => {
104
+ if (!isNormalizedSiteHost(v) || isBlockedPublicUrlHost(v)) fail(p, "must be a normalized public site host");
105
+ });
106
+ var measurementCount = text(30, /^(?:0|[1-9][0-9]*)$/);
107
+ var measurementScope = object({
108
+ projectId: measurementUuid,
109
+ experimentId: measurementUuid,
110
+ site: measurementSite,
111
+ experimentRevision: integer(1),
112
+ goalRevision: measurementRevision,
113
+ campaignId: measurementUuid,
114
+ campaignVersionId: measurementUuid,
115
+ assignedFrom: measurementInstant,
116
+ assignedTo: measurementInstant
117
+ });
118
+ var assignmentCounts = {
119
+ variantKey: text(80),
120
+ assignedUnits: measurementCount,
121
+ exposureEvents: measurementCount,
122
+ exposedUnits: measurementCount,
123
+ matureAssignedUnits: measurementCount,
124
+ immatureAssignedUnits: measurementCount,
125
+ assignmentsWithoutExposure: measurementCount
126
+ };
127
+ var availableVariant = object({ ...assignmentCounts, acceptedFormEvents: measurementCount, acceptedFormUnits: measurementCount, matureAcceptedFormUnits: measurementCount });
128
+ var unsupportedVariant = object({ ...assignmentCounts, acceptedFormEvents: literal(null), acceptedFormUnits: literal(null), matureAcceptedFormUnits: literal(null) });
129
+ var businessMetrics = object({
130
+ uniqueQualifiedContacts: object({ state: literal("unavailable"), reasonCode: literal("original_qualified_lineage_not_bound") }),
131
+ wonValue: object({ state: literal("unavailable"), reasonCode: literal("original_deal_lineage_not_bound") }),
132
+ paidRevenue: object({ state: literal("unavailable"), reasonCode: literal("original_paid_source_lineage_not_bound") })
133
+ });
134
+ var goalCountingUnit = choice("unique_source", "unique_qualified_contact", "net_order");
135
+ var originalGoalFields = { sourceId: measurementUuid, sourceRevision: measurementRevision, countingUnit: goalCountingUnit };
136
+ var formSourceKind = choice("accepted_form", "qualified_lead");
137
+ var unsupportedSourceKind = choice("completed_booking", "net_paid_order", "approved_application");
138
+ var observedFields = {
139
+ schemaVersion: literal(1),
140
+ mode: literal("trusted_source"),
141
+ state: literal("observed"),
142
+ observedAt: measurementInstant,
143
+ scope: measurementScope,
144
+ countingUnit: literal("consent_subject"),
145
+ coverage: literal("assigned_population_only"),
146
+ semanticRole: literal("accepted_form_diagnostic"),
147
+ businessMetrics
148
+ };
149
+ var observedMeasurement = refine(union(
150
+ object({
151
+ ...observedFields,
152
+ originalGoal: object({ ...originalGoalFields, sourceKind: formSourceKind }),
153
+ diagnosticType: literal("accepted_form"),
154
+ diagnosticState: literal("available"),
155
+ variants: list(availableVariant, 50)
156
+ }),
157
+ object({
158
+ ...observedFields,
159
+ originalGoal: object({ ...originalGoalFields, sourceKind: unsupportedSourceKind }),
160
+ diagnosticType: literal(null),
161
+ diagnosticState: literal("unsupported_original_source"),
162
+ variants: list(unsupportedVariant, 50)
163
+ })
164
+ ), (value, p) => {
165
+ if (Date.parse(value.scope.assignedFrom) >= Date.parse(value.scope.assignedTo) || Date.parse(value.scope.assignedTo) > Date.parse(value.observedAt))
166
+ fail(p + ".scope", "requires an increasing assignment cohort ending by the native observation");
167
+ if (new Set(value.variants.map((v) => v.variantKey)).size !== value.variants.length) fail(p + ".variants", "variant keys must be unique");
168
+ for (const [index, v] of value.variants.entries()) {
169
+ const assigned = BigInt(v.assignedUnits), exposed = BigInt(v.exposedUnits), mature = BigInt(v.matureAssignedUnits);
170
+ const at = `${p}.variants[${index}]`;
171
+ if (exposed > assigned || BigInt(v.exposureEvents) < exposed || BigInt(v.assignmentsWithoutExposure) !== assigned - exposed || mature + BigInt(v.immatureAssignedUnits) !== assigned)
172
+ fail(at, "assignment, exposure and maturity counts are inconsistent");
173
+ if (v.acceptedFormUnits !== null && v.acceptedFormEvents !== null && v.matureAcceptedFormUnits !== null) {
174
+ const accepted = BigInt(v.acceptedFormUnits), matureAccepted = BigInt(v.matureAcceptedFormUnits);
175
+ if (accepted > exposed || accepted > BigInt(v.acceptedFormEvents) || matureAccepted > mature || matureAccepted > accepted)
176
+ fail(at, "accepted form units must remain within their exposed, event and mature cohorts");
177
+ }
178
+ }
179
+ });
180
+ var measurement = union(
181
+ object({ schemaVersion: literal(1), mode: literal("legacy"), state: literal("unavailable"), reasonCode: literal("legacy_assignment_outcome_events_only") }),
182
+ object({ schemaVersion: literal(1), mode: literal("trusted_source"), state: literal("unavailable"), reasonCode: literal("measurement_scope_required") }),
183
+ observedMeasurement
184
+ );
185
+ measurement.schema;
186
+ var scopeOption = object({
187
+ campaignId: measurementUuid,
188
+ campaignVersionId: measurementUuid,
189
+ campaignDefinitionRevision: integer(1),
190
+ acceptedAt: measurementInstant,
191
+ site: measurementSite,
192
+ experimentId: measurementUuid,
193
+ experimentRevision: integer(1),
194
+ goalRevision: measurementRevision,
195
+ goalLabel: nullable(text(200)),
196
+ assignmentExtent: nullable(object({ firstAssignedAt: measurementInstant, lastAssignedAt: measurementInstant })),
197
+ suggestedCohort: nullable(object({ assignedFrom: measurementInstant, assignedTo: measurementInstant }))
198
+ });
199
+ var scopeOptions = refine(object({ observedAt: measurementInstant, items: list(scopeOption, 100), nextCursor: nullable(text(4096)) }), (value, p) => {
200
+ const observed = Date.parse(value.observedAt), tuples = /* @__PURE__ */ new Set();
201
+ for (const [index, item] of value.items.entries()) {
202
+ const at = `${p}.items[${index}]`, accepted = Date.parse(item.acceptedAt);
203
+ const tuple = JSON.stringify([
204
+ item.campaignId.toLowerCase(),
205
+ item.campaignVersionId.toLowerCase(),
206
+ item.site,
207
+ item.experimentId.toLowerCase(),
208
+ item.experimentRevision,
209
+ item.goalRevision
210
+ ]);
211
+ if (tuples.has(tuple)) fail(at, "original scope tuples must be unique");
212
+ tuples.add(tuple);
213
+ if (accepted > observed) fail(at + ".acceptedAt", "must not follow the native observation");
214
+ if (item.assignmentExtent) {
215
+ const first = Date.parse(item.assignmentExtent.firstAssignedAt), last = Date.parse(item.assignmentExtent.lastAssignedAt);
216
+ if (first > last || last > observed) fail(at + ".assignmentExtent", "requires first <= last <= native observation");
217
+ }
218
+ if (item.suggestedCohort) {
219
+ const from = Date.parse(item.suggestedCohort.assignedFrom), to = Date.parse(item.suggestedCohort.assignedTo);
220
+ if (from !== accepted || from >= to || to > observed) fail(at + ".suggestedCohort", "requires original acceptance start and increasing cohort ending by native observation");
221
+ }
222
+ }
223
+ });
224
+ scopeOptions.schema;
225
+
226
+ // src/contracts/resonance.ts
227
+ var ResonanceValidationError = class extends Error {
228
+ constructor(path2, reason) {
229
+ super(`${path2}: ${reason}`);
230
+ this.path = path2;
231
+ this.name = "ResonanceValidationError";
232
+ }
233
+ };
234
+ var fail2 = (path2, reason) => {
235
+ throw new ResonanceValidationError(path2, reason);
236
+ };
237
+ var { field: field2, text: text2, choice: choice2, literal: literal2, integer: integer2, bool, optional: optional2, nullable: nullable2, list: list2, object: object2, union: union2, refine: refine2 } = createWireSchema(fail2, (error) => error instanceof ResonanceValidationError);
238
+ var uuid2 = text2(36, /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
239
+ var digest = text2(64, /^[a-f0-9]{64}$/);
240
+ var revision = text2(128, /^[a-zA-Z0-9._:-]+$/);
241
+ var instant = refine2(text2(30, /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/), (v, p) => {
242
+ if (!Number.isFinite(Date.parse(v)) || new Date(v).toISOString().replace(".000Z", "Z") !== v.replace(".000Z", "Z")) fail2(p, "invalid UTC instant");
243
+ });
244
+ var timeZone = refine2(text2(80), (v, p) => {
245
+ try {
246
+ new Intl.DateTimeFormat("en", { timeZone: v });
247
+ } catch {
248
+ fail2(p, "invalid IANA timezone");
249
+ }
250
+ });
251
+ var site = refine2(text2(253), (v, p) => {
252
+ if (!isNormalizedSiteHost(v) || isBlockedPublicUrlHost(v)) fail2(p, "must be a normalized public site host");
253
+ });
254
+ var path = text2(2048, /^\/(?!\/)[^?#\s\\]*$/);
255
+ var decimal = text2(30, /^(?:0|[1-9][0-9]{0,29})$/);
256
+ var operationKey = text2(200, /^[a-zA-Z0-9._:-]+$/);
257
+ var objectiveCountingUnit = choice2("unique_source", "unique_qualified_contact", "net_order");
258
+ var RESONANCE_CURRENCIES = ["AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD", "CAD", "CDF", "CHF", "CLP", "CNY", "COP", "CRC", "CUC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "INR", "IQD", "IRR", "ISK", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRU", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLE", "SLL", "SOS", "SRD", "SSP", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VES", "VND", "VUV", "WST", "XAF", "XCD", "XCG", "XDR", "XOF", "XPF", "XSU", "YER", "ZAR", "ZMW", "ZWG", "ZWL"];
259
+ var currency = choice2(...RESONANCE_CURRENCIES);
260
+ var money = object2({ currency, amountMicros: decimal });
261
+ money.schema;
262
+ var sourceMarker = text2(1536, /^rsm1\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]{43}$/);
263
+ var parseResonanceSourceMarker = (v) => sourceMarker.read(v, "sourceMarker");
264
+ sourceMarker.schema;
265
+ var scope = { projectId: uuid2, site };
266
+ var resource = object2({ ...scope, module: choice2("funnel", "broadcast", "outreach", "experiment", "files", "business_brain", "publication", "crm", "commerce", "forms", "booking", "prepared_plan"), id: uuid2, revision, digest });
267
+ var source = object2({ ...scope, kind: choice2("accepted_form", "qualified_lead", "completed_booking", "net_paid_order", "approved_application"), id: uuid2, revision });
268
+ var qualificationFields = { schemaVersion: literal2(1), semantics: literal2("current_at_observation"), positiveStageKeys: list2(revision, 500), finalNegativeStageKeys: list2(revision, 500), pendingStageKeys: list2(revision, 500), existingContactPolicy: literal2("include_source_cohort") };
269
+ var checkQualificationPartitions = (v, p) => {
270
+ unique([...v.positiveStageKeys, ...v.finalNegativeStageKeys, ...v.pendingStageKeys], p + ".stagePartitions");
271
+ };
272
+ var qualificationRule = refine2(object2({ ...qualificationFields, attributionModel: literal2(RESONANCE_ATTRIBUTION_MODEL) }), checkQualificationPartitions);
273
+ qualificationRule.schema;
274
+ var resolvedQualificationRule = refine2(object2({
275
+ ...qualificationFields,
276
+ ref: resource,
277
+ crmVariant: text2(128),
278
+ ladderSnapshot: list2(object2({ id: nullable2(uuid2), stage_key: revision, semantic_key: nullable2(revision), is_won: bool, is_lost: bool }), 500, 1),
279
+ eligibleFormIds: list2(uuid2, 500, 1),
280
+ attribution: object2({ model: literal2(RESONANCE_ATTRIBUTION_MODEL), resolverRevision: literal2(1) }),
281
+ cohort: object2({ startAt: instant, endAt: instant, maturityDelaySeconds: integer2(0, 31536e3) })
282
+ }), (v, p) => {
283
+ checkQualificationPartitions(v, p);
284
+ if (v.ref.module !== "crm" || v.ref.id !== v.ref.projectId) fail2(p + ".ref", "must reference the actual CRM project configuration");
285
+ unique(v.eligibleFormIds, p + ".eligibleFormIds");
286
+ unique(v.ladderSnapshot.flatMap((x) => x.id ? [x.id] : []), p + ".ladderSnapshot.id");
287
+ unique(v.ladderSnapshot.map((x) => x.stage_key), p + ".ladderSnapshot.stage_key");
288
+ const partitions = [...v.positiveStageKeys, ...v.finalNegativeStageKeys, ...v.pendingStageKeys], keys = new Set(v.ladderSnapshot.map((x) => x.stage_key));
289
+ if (partitions.length !== keys.size || partitions.some((k) => !keys.has(k))) fail2(p + ".stagePartitions", "must exhaust the actual native ladder");
290
+ if (Date.parse(v.cohort.endAt) <= Date.parse(v.cohort.startAt)) fail2(p + ".cohort", "invalid cohort interval");
291
+ });
292
+ resolvedQualificationRule.schema;
293
+ var objective = refine2(object2({ source, measurementRevision: revision, countingUnit: objectiveCountingUnit, attributionWindowDays: integer2(1, 365), maturityDelaySeconds: integer2(0, 31536e3), qualificationRuleRef: nullable2(resource), qualificationRule: optional2(qualificationRule) }), (v, p) => {
294
+ if (v.qualificationRule && v.source.kind !== "qualified_lead") fail2(p + ".qualificationRule", "requires the native qualified-lead source");
295
+ if (v.qualificationRule && v.qualificationRuleRef && (v.qualificationRuleRef.module !== "crm" || v.qualificationRuleRef.id !== v.source.projectId)) fail2(p + ".qualificationRuleRef", "must reference actual CRM project configuration");
296
+ });
297
+ var creative = object2({ id: uuid2, assetRef: resource, preparedArtifactRef: nullable2(resource), headline: text2(160), body: text2(1e4), callToAction: text2(80), cropDigest: digest });
298
+ var destination = union2(
299
+ object2({ kind: literal2("funnel"), id: uuid2, ...scope, path, funnelRef: resource, formSlug: text2(80, /^[a-z0-9]+(?:-[a-z0-9]+)*$/), installationRevision: revision }),
300
+ object2({ kind: literal2("existing_page"), id: uuid2, ...scope, path, pageRevision: revision, evidenceDigest: digest })
301
+ );
302
+ var audience = object2({ ...scope, id: uuid2, revision, policyRevision: revision, retentionDays: integer2(1, 540), exclusionRuleIds: list2(uuid2, 50), minDaysSinceVisit: optional2(integer2(1, 179)), consentPurpose: literal2("ad_personalization") });
303
+ var experiment = object2({ ...scope, id: uuid2, revision: integer2(1), digest });
304
+ var budget = refine2(object2({ total: money, dailyAllocationCeiling: nullable2(money), reserveMargin: money, startAt: instant, endAt: instant, timeZone, maxChange: money, maxChangeBasisPoints: integer2(0, 1e4), cooldownSeconds: integer2(0, 31536e3) }), (v, p) => {
305
+ if (Date.parse(v.endAt) <= Date.parse(v.startAt)) fail2(p, "end must follow start");
306
+ for (const m of [v.reserveMargin, v.maxChange, ...v.dailyAllocationCeiling ? [v.dailyAllocationCeiling] : []]) if (m.currency !== v.total.currency) fail2(p, "currency mismatch");
307
+ if (BigInt(v.reserveMargin.amountMicros) > BigInt(v.total.amountMicros)) fail2(p, "reserve exceeds total");
308
+ });
309
+ var allocationPolicyFields = {
310
+ schemaVersion: literal2(1),
311
+ sourceRef: nullable2(resource),
312
+ decisionKind: literal2("observational_allocation"),
313
+ eligibleGoal: literal2("qualified_lead"),
314
+ algorithm: literal2("mature_cpql_conserved_transfer_v1"),
315
+ minimumMatureExposures: integer2(1),
316
+ minimumMatureOutcomes: integer2(1),
317
+ minimumCoverageBasisPoints: integer2(0, 1e4),
318
+ maxEvidenceAgeSeconds: integer2(1),
319
+ maturityDelaySeconds: integer2(0),
320
+ cooldownSeconds: integer2(0),
321
+ donorTransferBasisPoints: integer2(1, 1e4),
322
+ maxUncertaintyBasisPoints: integer2(0, 1e4),
323
+ uncertaintyMethod: literal2("observational_cpql_missing_outcome_bound_v1"),
324
+ attributionModel: choice2("first_party_source", "first_touch", "last_touch", "provider_reported", RESONANCE_ATTRIBUTION_MODEL),
325
+ requiredChecks: list2(choice2("tracking", "dedupe", "qualification", "uncertainty", "assignment", "power"), 6, 1)
326
+ };
327
+ var matureOptimizationPolicy = object2(allocationPolicyFields);
328
+ var reportedPolicyFields = { ...allocationPolicyFields, algorithm: literal2("reported_cpql_conserved_transfer_v1"), costBasis: literal2("provider_reported_snapshot_v1"), costRevisionRisk: literal2("accepted_provisional") };
329
+ var reportedOptimizationPolicy = object2({ ...reportedPolicyFields, readClockKind: literal2("sql_before_provider_read_lower_bound_v1") });
330
+ function uniqueList(item, max, min = 0) {
331
+ const base = list2(item, max, min);
332
+ return field2({ ...base.schema, uniqueItems: true }, (v, p) => {
333
+ const r = base.read(v, p);
334
+ unique(r, p);
335
+ return r;
336
+ });
337
+ }
338
+ var optimizationCheck = choice2("tracking", "dedupe", "qualification", "uncertainty", "assignment", "power");
339
+ var finiteCandidateAction = choice2("materialize_and_activate", "activate_held", "pause_candidate");
340
+ var finiteStopRule = choice2("exploration_cap", "flight_end", "policy_or_mandate_expiry", "kill", "candidate_identity_or_settings_drift", "source_or_consent_invalid", "required_tracking_unavailable_or_stale", "unknown_effect");
341
+ var positiveMicrosPattern = /^[1-9][0-9]*(?![\s\S])/;
342
+ var positiveMoneyShape = field2({ allOf: [money.schema, { properties: { amountMicros: { pattern: positiveMicrosPattern.source } } }] }, (v, p) => {
343
+ const m = money.read(v, p);
344
+ if (!positiveMicrosPattern.test(m.amountMicros)) fail2(p, "requires positive canonical money");
345
+ return m;
346
+ });
347
+ var finiteMetaCandidatePolicy = object2({
348
+ schemaVersion: literal2(1),
349
+ algorithm: literal2("finite_meta_candidates_v1"),
350
+ decisionKind: literal2("preapproved_candidate_operation"),
351
+ eligibleGoal: literal2("qualified_lead"),
352
+ sourceRef: nullable2(resource),
353
+ candidateChannelIds: uniqueList(uuid2, 100, 1),
354
+ selection: object2({ kind: literal2("fixed_reviewed_order"), candidateOrder: uniqueList(uuid2, 100, 1), eligibilitySchedule: list2(object2({ channelId: uuid2, notBefore: instant, notAfter: instant }), 100, 1) }),
355
+ allowedActions: uniqueList(finiteCandidateAction, 3, 1),
356
+ maxConcurrentCandidates: integer2(1, 100),
357
+ maxActivationsPerCandidate: literal2(1),
358
+ totalExplorationCap: positiveMoneyShape,
359
+ measurementGuard: object2({ sourceRef: source, qualificationRuleRef: resource, attributionModel: literal2(RESONANCE_ATTRIBUTION_MODEL), maturityDelaySeconds: integer2(0, 31536e3), minimumCoverageBasisPoints: integer2(0, 1e4), maxEvidenceAgeSeconds: integer2(1), requiredChecks: uniqueList(optimizationCheck, 6, 1) }),
360
+ performanceDecisionPolicy: literal2(null),
361
+ stopRules: uniqueList(finiteStopRule, 8, 8),
362
+ expiresAt: instant
363
+ });
364
+ var optimizationPolicy = union2(matureOptimizationPolicy, finiteMetaCandidatePolicy, reportedOptimizationPolicy);
365
+ optimizationPolicy.schema;
366
+ var optimization = object2({ enabled: bool, policy: optimizationPolicy, stopLoss: object2({ confirmedSpend: money, unreconciledExposure: money }) });
367
+ var channelBase = { id: uuid2, ...scope, destinationId: uuid2, creativeIds: list2(uuid2, 50, 1) };
368
+ var search = object2({ ...channelBase, localCampaignKey: text2(80), kind: literal2("google_search"), provider: literal2("google_ads"), accountBindingId: uuid2, capabilityRevision: revision, allocation: money, budgetMode: choice2("campaign_total", "average_daily"), keywords: list2(object2({ text: text2(80), match: choice2("exact", "phrase", "broad") }), 200, 1), negativeKeywords: list2(text2(80), 200), bidding: union2(object2({ strategy: literal2("maximize_conversions") }), object2({ strategy: literal2("target_cpa"), target: money })), locationIds: list2(text2(80), 100, 1), network: object2({ googleSearch: literal2(true), searchPartners: bool, displayExpansion: literal2(false) }), geoPresence: choice2("presence", "presence_or_interest"), excludedLocationIds: list2(text2(80), 100), adGroups: list2(object2({ localKey: text2(80), ads: list2(object2({ localKey: text2(80), creativeId: uuid2, headlines: list2(text2(30), 15, 3), descriptions: list2(text2(90), 4, 2) }), 20, 1) }), 100, 1), assetAutomation: literal2("disabled") });
369
+ var metaAgeConstraint = union2(refine2(object2({ mode: literal2("bounded"), min: integer2(1, 120), max: integer2(1, 120) }), (v, p) => {
370
+ if (v.min > v.max) fail2(p, "minimum age exceeds maximum");
371
+ }), object2({ mode: literal2("omitted") }), object2({ mode: literal2("provider_managed") }));
372
+ var propertyKey = object2({ key: text2(40, /^[a-z0-9]+(?:-[a-z0-9]+)*$/), label: text2(80) });
373
+ var meta = object2({
374
+ ...channelBase,
375
+ localCampaignKey: text2(80),
376
+ kind: literal2("meta_retargeting"),
377
+ provider: literal2("meta_ads"),
378
+ accountBindingId: uuid2,
379
+ capabilityRevision: revision,
380
+ allocation: money,
381
+ budgetMode: choice2("lifetime", "daily"),
382
+ audienceRuleRefs: list2(audience, 50, 1),
383
+ objective: choice2("OUTCOME_LEADS", "OUTCOME_SALES", "OUTCOME_TRAFFIC"),
384
+ pageRef: object2({ connectionId: uuid2, externalId: text2(160), revision }),
385
+ instagramRef: nullable2(object2({ connectionId: uuid2, externalId: text2(160), revision })),
386
+ datasetRef: object2({ id: text2(160), revision }),
387
+ conversionGoalRef: resource,
388
+ optimization: choice2("landing_page_views", "lead", "purchase"),
389
+ billing: literal2("impressions"),
390
+ bid: union2(object2({ strategy: literal2("lowest_cost") }), object2({ strategy: literal2("cost_cap"), cap: money })),
391
+ localAdSetKey: text2(80),
392
+ localAdKey: text2(80),
393
+ budgetOwner: choice2("campaign", "adset"),
394
+ specialCategories: list2(choice2("housing", "employment", "financial_products_services"), 3),
395
+ countryCodes: list2(text2(2, /^[A-Z]{2}$/), 30, 1),
396
+ placement: literal2("automatic"),
397
+ ageConstraint: optional2(metaAgeConstraint),
398
+ // Property retargeting (one channel per property under one group). Every field is optional and omitted from the canonical form when absent, so no existing reviewed digest changes.
399
+ channelGroup: optional2(propertyKey),
400
+ property: optional2(propertyKey),
401
+ tier: optional2(object2({ label: text2(40) })),
402
+ primaryText: optional2(text2(2200)),
403
+ convertersAcknowledged: optional2(literal2(true))
404
+ });
405
+ var owned = object2({ ...channelBase, kind: choice2("funnel", "broadcast", "outreach"), resourceRef: resource });
406
+ var channel = union2(search, meta, owned);
407
+ var definitionFields = { schemaVersion: literal2(1), projectId: uuid2, name: text2(160), slug: refine2(text2(80), (v, p) => {
408
+ if (normalizeCampaignSlug(v) !== v) fail2(p, "must be canonical campaign slug");
409
+ }), sites: list2(site, 50, 1), objective, brandRef: resource, creativeRefs: list2(creative, 100, 1), destinations: list2(destination, 100, 1), channels: list2(channel, 100, 1), experimentRefs: list2(experiment, 50), budget, optimization };
410
+ var definition = object2(definitionFields);
411
+ var draft = object2({ schemaVersion: literal2(1), projectId: uuid2, name: text2(160, void 0, 0), slug: definitionFields.slug, sites: list2(site, 50), objective: optional2(objective), brandRef: optional2(resource), creativeRefs: optional2(list2(creative, 100)), destinations: optional2(list2(destination, 100)), channels: optional2(list2(channel, 100)), experimentRefs: optional2(list2(experiment, 50)), budget: optional2(budget), optimization: optional2(optimization) });
412
+ function scoped(value, projectId, sites, at) {
413
+ if (!value || typeof value !== "object") return;
414
+ if (Array.isArray(value)) {
415
+ value.forEach((x, i) => scoped(x, projectId, sites, `${at}[${i}]`));
416
+ return;
417
+ }
418
+ const v = value;
419
+ if ("projectId" in v && v.projectId !== projectId) fail2(at, "cross-project reference");
420
+ if ("site" in v && !sites.includes(v.site)) fail2(at, "site outside campaign scope");
421
+ for (const [k, x] of Object.entries(v)) scoped(x, projectId, sites, `${at}.${k}`);
422
+ }
423
+ function unique(values, at) {
424
+ if (new Set(values).size !== values.length) fail2(at, "duplicate identity");
425
+ }
426
+ definition.schema;
427
+ draft.schema;
428
+ var capability = object2({ id: uuid2, projectId: uuid2, connectionId: uuid2, provider: choice2("google_ads", "meta_ads"), externalAccountId: text2(160), currency, timeZone, connectionRevision: revision, capabilityRevision: revision, observedAt: instant, expiresAt: instant, advertiserState: choice2("eligible", "blocked", "unknown"), operations: list2(object2({ operation: choice2("discover", "prepare", "launch", "update", "pause", "resume", "finish", "reconcile", "measure", "audience", "conversion", "experiment"), state: choice2("supported", "blocked", "unknown"), reasonCodes: list2(text2(80), 30), prerequisites: list2(text2(100), 30) }), 20), profiles: list2(object2({ profile: choice2("google_search", "meta_retargeting"), state: choice2("supported", "blocked", "unknown"), reasonCodes: list2(text2(80), 30), budgetModes: list2(choice2("campaign_total", "average_daily", "lifetime", "daily"), 4), providerApiVersion: text2(30) }), 2) });
429
+ var mandateRef = object2({ ledgerActionId: uuid2, campaignId: uuid2, preparedPlanId: uuid2, reviewDigest: digest, mandateDigest: digest, expectedKillRevision: integer2(0) });
430
+ var websiteVisitMaxAgeMs = 3e5;
431
+ var RESONANCE_WEBSITE_VISIT_PROFILE = Object.freeze({
432
+ profile: "sonor_sdk_page_view_fresh_v1",
433
+ source: "sdk_reported_first_party_cookie",
434
+ assurance: "sdk_reported",
435
+ matchingFields: Object.freeze(["fbp", "fbc"]),
436
+ maxCaptureAgeMs: websiteVisitMaxAgeMs,
437
+ maxDispatchAgeMs: websiteVisitMaxAgeMs,
438
+ maxFutureSkewMs: 0,
439
+ browserServerPairing: "unsupported"
440
+ });
441
+ var measurementRuleRef = object2({ id: uuid2, revision });
442
+ var measurementConfigRef = object2({ configRevision: revision, policyRevision: revision, digest });
443
+ var matchingProfileRef = object2({ id: uuid2, revision: integer2(1), digest });
444
+ var mandateAllowedOperations = list2(choice2("launch", "update", "resume", "optimize", "deliver_conversion"), 5, 1);
445
+ var websiteVisitFeed = object2({ profile: literal2(RESONANCE_WEBSITE_VISIT_PROFILE.profile), channels: list2(object2({
446
+ channelId: uuid2,
447
+ measurementConfigRef,
448
+ resourceRuleRefs: list2(measurementRuleRef, 500, 1),
449
+ matchingProfileRef
450
+ }), 100, 1) });
451
+ websiteVisitFeed.schema;
452
+ var providerReportingChannelIds = refine2(uniqueList(uuid2, 100, 1), (ids, p) => unique(ids.map((id) => id.toLowerCase()), p));
453
+ var providerReportingSelection = object2({ agentContactId: uuid2, channelIds: providerReportingChannelIds });
454
+ var providerReportingGrant = refine2(object2({ purpose: literal2("provider_reporting_refresh"), orgId: uuid2, projectId: uuid2, agentContactId: uuid2, principalContactId: uuid2, channelIds: providerReportingChannelIds }), (v, p) => {
455
+ if (v.agentContactId.toLowerCase() === v.principalContactId.toLowerCase()) fail2(p + ".principalContactId", "agent and human principal must remain distinct");
456
+ });
457
+ providerReportingSelection.schema;
458
+ providerReportingGrant.schema;
459
+ var mandateScope = object2({ projectId: uuid2, campaignId: uuid2, preparedPlanId: uuid2, reviewDigest: digest, accountBindingIds: list2(uuid2, 100), sites: list2(site, 50, 1), budget, creativeRefs: list2(creative, 100, 1), audienceRuleRefs: list2(audience, 100), goal: objective, allowedOperations: mandateAllowedOperations, allowedTransformationRef: nullable2(resource), expiresAt: instant, websiteVisitFeed: optional2(websiteVisitFeed), providerReporting: optional2(providerReportingGrant) });
460
+ object2({ occurredAt: instant, receivedAt: instant });
461
+ var RESONANCE_GOOGLE_SIGNAL_SEMANTICS = "qualified_at_observation_v1";
462
+ var googleSignalSemantics = literal2(RESONANCE_GOOGLE_SIGNAL_SEMANTICS);
463
+ var RESONANCE_GOOGLE_SOURCE_CATEGORIES = Object.freeze({ accepted_form: "SUBMIT_LEAD_FORM", qualified_lead: "QUALIFIED_LEAD", completed_booking: "BOOK_APPOINTMENT", net_paid_order: "PURCHASE", approved_application: "CONVERTED_LEAD" });
464
+ var googleSemanticsInput = object2({ sourceKind: choice2(...Object.keys(RESONANCE_GOOGLE_SOURCE_CATEGORIES)), counting: choice2("one", "every"), clickWindowDays: integer2(1, 90) });
465
+ function googleConversionActionSemantics(input) {
466
+ const v = googleSemanticsInput.read(input, "googleConversionActionSemantics");
467
+ return { type: "UPLOAD_CLICKS", category: RESONANCE_GOOGLE_SOURCE_CATEGORIES[v.sourceKind], countingType: v.counting === "one" ? "ONE_PER_CLICK" : "MANY_PER_CLICK", clickThroughLookbackWindowDays: v.clickWindowDays, primaryForGoal: false };
468
+ }
469
+ var googleSetupPolicyInput = object2({ source, countingUnit: objectiveCountingUnit, attributionWindowDays: integer2(1, 365) });
470
+ function googleUploadClicksSetupSemantics(input) {
471
+ const v = googleSetupPolicyInput.read(input, "googleSetupPolicy");
472
+ if (v.source.kind !== "qualified_lead" || v.countingUnit !== "unique_qualified_contact") fail2("googleSetupPolicy", "unsupported_counting_policy");
473
+ if (v.attributionWindowDays > 90) fail2("googleSetupPolicy.attributionWindowDays", "unsupported_attribution_window");
474
+ return { ...googleConversionActionSemantics({ sourceKind: v.source.kind, counting: "one", clickWindowDays: v.attributionWindowDays }), category: RESONANCE_GOOGLE_SOURCE_CATEGORIES.qualified_lead, countingType: "ONE_PER_CLICK" };
475
+ }
476
+ var googleGoalChoiceFields = {
477
+ externalId: text2(160, /^customers\/[0-9]+\/customConversionGoals\/[0-9]+$/),
478
+ actionIds: list2(text2(160, /^customers\/[0-9]+\/conversionActions\/[0-9]+$/), 100, 1)
479
+ };
480
+ var googleDeclarationChoiceFields = {
481
+ statement: literal2("DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING"),
482
+ category: choice2("general", "housing", "employment", "financial_products_services")
483
+ };
484
+ function normalizeGoogleGoal(value, at) {
485
+ unique(value.actionIds, at + ".actionIds");
486
+ value.actionIds.sort();
487
+ return value;
488
+ }
489
+ var googleSetupSpec = object2({
490
+ schemaVersion: literal2(1),
491
+ kind: literal2("google_upload_clicks_custom_goal_v1"),
492
+ signalSemantics: googleSignalSemantics,
493
+ goalLocalKey: literal2("setup:custom-goal"),
494
+ requestedGoalName: text2(160),
495
+ action: object2({
496
+ localKey: literal2("setup:conversion-action"),
497
+ requestedActionName: text2(160),
498
+ type: literal2("UPLOAD_CLICKS"),
499
+ category: literal2(RESONANCE_GOOGLE_SOURCE_CATEGORIES.qualified_lead),
500
+ countingType: literal2("ONE_PER_CLICK"),
501
+ clickThroughLookbackWindowDays: integer2(1, 90),
502
+ primaryForGoal: literal2(false)
503
+ }),
504
+ goalActionKey: literal2("setup:conversion-action"),
505
+ goalStrategy: literal2("exclusive_custom_goal_v1")
506
+ });
507
+ var googleSaveCommonFields = { schemaVersion: literal2(1), expectedCampaignRevision: integer2(1), expectedRevision: integer2(0), idempotencyKey: operationKey, signalSemantics: googleSignalSemantics, declaration: object2(googleDeclarationChoiceFields) };
508
+ var googleSave = union2(
509
+ object2({ ...googleSaveCommonFields, mode: literal2("existing"), nativeGoal: object2(googleGoalChoiceFields) }),
510
+ object2({ ...googleSaveCommonFields, mode: literal2("admitted_setup"), setupIntent: object2({ requestedGoalName: text2(160), requestedActionName: text2(160) }) })
511
+ );
512
+ var googleGoalDraft = object2({ ...googleGoalChoiceFields, name: nullable2(text2(200, void 0, 0)), revision: nullable2(digest) });
513
+ var googleGoalReviewed = object2({ ...googleGoalChoiceFields, name: text2(200, void 0, 0), revision: digest });
514
+ var googleDeclared = object2({
515
+ ...googleDeclarationChoiceFields,
516
+ creativeDigest: digest,
517
+ locationIds: list2(text2(80), 100, 1),
518
+ excludedLocationIds: list2(text2(80), 100),
519
+ declaredByActorId: uuid2,
520
+ declaredAt: instant,
521
+ declaredForCampaignRevision: integer2(1)
522
+ });
523
+ var googleConfigurationFields = {
524
+ projectId: uuid2,
525
+ campaignId: uuid2,
526
+ channelId: uuid2,
527
+ site,
528
+ accountBindingId: uuid2,
529
+ configurationRevision: integer2(1),
530
+ configurationDigest: digest,
531
+ signalSemantics: googleSignalSemantics,
532
+ source,
533
+ measurementRevision: revision,
534
+ countingUnit: objectiveCountingUnit,
535
+ attributionWindowDays: integer2(1, 365),
536
+ declaration: googleDeclared
537
+ };
538
+ var googleExistingChoiceFields = { mode: literal2("existing"), nativeGoal: googleGoalDraft };
539
+ var googleSetupChoiceFields = { mode: literal2("admitted_setup"), setupSpec: googleSetupSpec, setupDigest: digest };
540
+ function checkGoogleConfiguration(value, at) {
541
+ if (value.source.projectId !== value.projectId || value.source.site !== value.site) fail2(at + ".source", "source scope mismatch");
542
+ unique(value.declaration.locationIds, at + ".declaration.locationIds");
543
+ unique(value.declaration.excludedLocationIds, at + ".declaration.excludedLocationIds");
544
+ value.declaration.locationIds.sort();
545
+ value.declaration.excludedLocationIds.sort();
546
+ const expected = googleUploadClicksSetupSemantics({ source: value.source, countingUnit: value.countingUnit, attributionWindowDays: value.attributionWindowDays });
547
+ if (value.mode === "existing") {
548
+ normalizeGoogleGoal(value.nativeGoal, at + ".nativeGoal");
549
+ return;
550
+ }
551
+ if (canonical({ type: value.setupSpec.action.type, category: value.setupSpec.action.category, countingType: value.setupSpec.action.countingType, clickThroughLookbackWindowDays: value.setupSpec.action.clickThroughLookbackWindowDays, primaryForGoal: value.setupSpec.action.primaryForGoal }) !== canonical(expected)) fail2(at + ".setupSpec.action", "canonical setup semantics mismatch");
552
+ }
553
+ var googleSnapshotBase = union2(object2({ ...googleConfigurationFields, ...googleExistingChoiceFields }), object2({ ...googleConfigurationFields, ...googleSetupChoiceFields }));
554
+ var googleConfigurationSnapshot = refine2(googleSnapshotBase, checkGoogleConfiguration);
555
+ var googleConfigurationReview = refine2(union2(object2({ ...googleConfigurationFields, mode: literal2("existing"), nativeGoal: googleGoalReviewed }), object2({ ...googleConfigurationFields, ...googleSetupChoiceFields })), checkGoogleConfiguration);
556
+ var { configurationRevision: _configurationRevisionField, ...googleViewContentFields } = googleConfigurationFields;
557
+ var googleReadinessFields = { ready: bool, activationReadiness: choice2("blocked", "pending_setup", "requires_final_verification", "verified"), reasonCodes: list2(text2(200), 100) };
558
+ var googleViewFields = { ...googleViewContentFields, revision: integer2(1), ...googleReadinessFields };
559
+ var googleViewBase = union2(object2({ ...googleViewFields, ...googleExistingChoiceFields }), object2({ ...googleViewFields, ...googleSetupChoiceFields }));
560
+ function checkGoogleView(value, at) {
561
+ checkGoogleConfiguration(value, at);
562
+ if (!value.ready && value.activationReadiness !== "blocked") fail2(at + ".activationReadiness", "unready configuration must be blocked");
563
+ if (value.mode === "existing") {
564
+ if (value.ready && (value.nativeGoal.name === null || value.nativeGoal.revision === null)) fail2(at + ".ready", "unavailable native evidence cannot be ready");
565
+ if (value.ready && value.activationReadiness !== "requires_final_verification" && value.activationReadiness !== "verified") fail2(at + ".activationReadiness", "existing native setup needs current verification");
566
+ } else if (value.ready && value.activationReadiness !== "pending_setup") fail2(at + ".activationReadiness", "reviewed setup intent is not native activation");
567
+ }
568
+ var googleConfigurationView = refine2(googleViewBase, checkGoogleView);
569
+ var googleSaveResultFields = { operationId: uuid2, replayed: bool, campaignRevision: integer2(1) };
570
+ var googleConfigurationSaveResponse = refine2(union2(object2({ ...googleViewFields, ...googleExistingChoiceFields, ...googleSaveResultFields }), object2({ ...googleViewFields, ...googleSetupChoiceFields, ...googleSaveResultFields })), checkGoogleView);
571
+ refine2(union2(object2({ ...googleConfigurationFields, ...googleExistingChoiceFields, configurationDigest: optional2(digest) }), object2({ ...googleConfigurationFields, ...googleSetupChoiceFields, configurationDigest: optional2(digest) })), checkGoogleConfiguration);
572
+ googleSetupSpec.schema;
573
+ googleSave.schema;
574
+ googleConfigurationSnapshot.schema;
575
+ googleConfigurationReview.schema;
576
+ googleConfigurationView.schema;
577
+ googleConfigurationSaveResponse.schema;
578
+ var preparedManifestRef = object2({ id: uuid2, digest, providerVersion: text2(30), revision });
579
+ var preview = object2({ channelId: uuid2, site, accountBindingId: nullable2(uuid2), accountName: text2(200), accountTimeZone: timeZone, destinationUrl: text2(4096), attributionMarker: optional2(object2({ parameter: literal2(RESONANCE_SOURCE_MARKER_PARAMETER), value: sourceMarker })), orderedCreatives: list2(creative, 50, 1), audienceRuleRefs: list2(audience, 100), allocation: nullable2(money), capabilityRevision: revision, snapshotDigest: digest, manifestRef: preparedManifestRef, conversionRevision: revision, policyRevision: revision, googleConfiguration: nullable2(googleConfigurationReview) });
580
+ var prepared = object2({ schemaVersion: literal2(1), id: uuid2, campaignId: uuid2, projectId: uuid2, expectedRevision: integer2(1), definitionDigest: digest, reviewDigest: digest, definition, resolvedEvidencePolicy: optimizationPolicy, resolvedQualificationRule: nullable2(resolvedQualificationRule), accounts: list2(capability, 100), channelPreviews: list2(preview, 100, 1), preparedAt: instant, expiresAt: instant, blockingReasons: list2(text2(200), 100) });
581
+ var commandBase = { schemaVersion: literal2(1), campaignId: uuid2, expectedRevision: integer2(1), idempotencyKey: operationKey, channelIds: list2(uuid2, 100) };
582
+ var effect = object2({ ...commandBase, operation: choice2("launch", "update", "resume"), preparedPlanId: uuid2, reviewDigest: digest, mandateRef, expectedRemoteSnapshots: list2(object2({ channelId: uuid2, digest }), 100) });
583
+ var optimize = object2({ ...commandBase, operation: literal2("optimize"), preparedPlanId: uuid2, reviewDigest: digest, mandateRef, decisionRef: object2({ id: uuid2, revision, evidenceDigest: digest }), expectedRemoteSnapshots: list2(object2({ channelId: uuid2, digest }), 100) });
584
+ var safety = object2({ ...commandBase, operation: choice2("pause", "finish", "reconcile") });
585
+ var deliverConversion = object2({ ...commandBase, operation: literal2("deliver_conversion"), channelIds: list2(uuid2, 1, 1), preparedPlanId: uuid2, reviewDigest: digest, mandateRef, conversionId: uuid2 });
586
+ var deliverWebsiteVisit = object2({ ...commandBase, operation: literal2("deliver_website_visit"), channelIds: list2(uuid2, 1, 1), preparedPlanId: uuid2, reviewDigest: digest, mandateRef, occurrenceId: uuid2 });
587
+ var command = union2(effect, optimize, safety, deliverConversion, deliverWebsiteVisit);
588
+ command.schema;
589
+ var step = object2({ id: uuid2, channelId: uuid2, state: choice2("planned", "admitted", "confirmed", "failed", "uncertain"), providerRequestId: nullable2(text2(200)), admission: choice2("not_dispatched", "accepted", "rejected", "unknown"), configuredStatus: nullable2(text2(80)), effectiveStatus: nullable2(text2(80)), reviewStatus: nullable2(text2(80)), retryAfter: nullable2(instant), errorSubcode: nullable2(text2(80)), errorFieldPath: nullable2(text2(200)), resourceRefs: list2(object2({ type: text2(80), externalId: text2(200) }), 100), evidenceDigest: nullable2(digest), errorCode: nullable2(text2(80)), observedAt: nullable2(instant) });
590
+ var receipt = object2({ schemaVersion: literal2(1), id: uuid2, projectId: uuid2, campaignId: uuid2, preparedPlanId: nullable2(uuid2), effectKey: text2(200), requestDigest: digest, ledgerActionId: uuid2, actorId: text2(160), state: choice2("pending", "applying", "accepted", "failed", "needs_reconciliation"), steps: list2(step, 1e3), reservedExposure: money, unreconciledExposure: money, providerSemanticsNotice: list2(text2(500), 30), nextActions: list2(choice2("read", "reconcile", "retry_same_operation", "pause", "escalate"), 5) });
591
+ var lifecycle = choice2("not_materialized", "pending", "partially_live", "live", "pausing", "paused", "finished", "needs_attention", "unknown");
592
+ var view = object2({ id: uuid2, projectId: uuid2, managementMode: choice2("tracking_only", "managed"), revision: integer2(0), definition: nullable2(draft), acceptedPlanId: nullable2(uuid2), desiredLifecycle: choice2("draft", "running", "paused", "finished"), effectiveLifecycle: lifecycle, blockingReasons: list2(text2(200), 100), activeOperation: nullable2(receipt), unresolvedExposure: nullable2(money) });
593
+ var metricValue = union2(object2({ kind: literal2("count"), count: decimal }), object2({ kind: literal2("money"), money }), object2({ kind: literal2("ratio"), numerator: decimal, denominator: refine2(decimal, (v, p) => {
594
+ if (v === "0") fail2(p, "zero denominator");
595
+ }) }));
596
+ var metricBase = { ...scope, campaignId: uuid2, name: choice2("impressions", "clicks", "spend", "accepted_goals", "qualified_goals", "revenue", "conversion_rate", "reach", "results"), source: choice2("first_party", "provider", "experiment"), sourceRef: revision, goalRevision: nullable2(revision), countingUnit: choice2("unique_source", "unique_qualified_contact", "net_order", "provider_aggregate", "provider_unique_reach"), windowStart: instant, windowEnd: instant, timeZone, attributionModel: nullable2(text2(100)), asOf: instant, dataThrough: nullable2(instant), maturityDelaySeconds: integer2(0), coverageBasisPoints: nullable2(integer2(0, 1e4)), eligibleExposures: nullable2(decimal), matureOutcomes: nullable2(decimal), native: nullable2(object2({ unit: text2(80), amount: decimal, currency: nullable2(currency), unitExponent: integer2(0, 9), providerVersion: text2(30) })) };
597
+ var metric = union2(object2({ ...metricBase, state: literal2("observed"), value: metricValue }), object2({ ...metricBase, state: choice2("unknown", "insufficient", "delayed", "unavailable"), value: literal2(null), reasonCode: text2(80) }));
598
+ var purpose = choice2("analytics_storage", "ad_storage", "ad_user_data", "ad_personalization");
599
+ var consent = object2({ ...scope, receiptId: uuid2, visitorNamespace: text2(160), configRevision: revision, policyRevision: revision, consentRevision: integer2(1), state: choice2("unknown", "granted", "denied", "withdrawn"), purpose, observedAt: instant, evidenceRef: revision });
600
+ var exposure = object2({ ...scope, experimentId: uuid2, experimentRevision: integer2(1), assignmentId: uuid2, variantKey: text2(80), exposureId: uuid2, occurredAt: instant });
601
+ var conversion = object2({ schemaVersion: literal2(1), ...scope, eventId: uuid2, sourceRef: source, campaignId: nullable2(uuid2), goalRevision: revision, occurredAt: instant, receivedAt: instant, value: union2(object2({ state: literal2("observed"), money }), object2({ state: choice2("unknown", "unavailable"), money: literal2(null), reasonCode: text2(80) })), consent: list2(consent, 4), experiment: nullable2(exposure) });
602
+ conversion.schema;
603
+ prepared.schema;
604
+ capability.schema;
605
+ mandateScope.schema;
606
+ mandateRef.schema;
607
+ receipt.schema;
608
+ view.schema;
609
+ metric.schema;
610
+ consent.schema;
611
+ var mandateTarget = object2({ project_id: uuid2, campaign_id: uuid2 });
612
+ var mandateApproval = object2({ schemaVersion: literal2(1), expectedRevision: integer2(1), preparedPlanId: uuid2, reviewDigest: digest, mandateDigest: digest, mandate: mandateScope });
613
+ mandateApproval.schema;
614
+ mandateTarget.schema;
615
+ function canonical(value) {
616
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
617
+ if (Array.isArray(value)) return "[" + value.map(canonical).join(",") + "]";
618
+ return "{" + Object.keys(value).sort().map((k) => JSON.stringify(k) + ":" + canonical(value[k])).join(",") + "}";
619
+ }
620
+ var prepareCommand = object2({ expectedRevision: integer2(1), idempotencyKey: operationKey });
621
+ var commandPreparation = object2({ command: nullable2(command), covered: bool, approvalRequirement: nullable2(choice2("existing_mandate", "current_owner")), reasonCodes: list2(text2(200), 100) });
622
+ prepareCommand.schema;
623
+ commandPreparation.schema;
624
+ var saveCommand = object2({ definition: draft, expectedRevision: optional2(integer2(0)), idempotencyKey: operationKey });
625
+ var acceptCommand = object2({ expectedRevision: integer2(1), ledgerActionId: uuid2 });
626
+ var bindingCommand = object2({ connectionId: uuid2, provider: choice2("google_ads", "meta_ads"), externalAccountId: text2(160), expectedConnectionRevision: revision, idempotencyKey: operationKey });
627
+ saveCommand.schema;
628
+ acceptCommand.schema;
629
+ bindingCommand.schema;
630
+ var RESONANCE_CPQL_UNCERTAINTY_METHOD = "observational_cpql_missing_outcome_bound_v1";
631
+ var allocationHorizon = union2(
632
+ refine2(object2({ kind: literal2("finite_period"), startAt: instant, endAt: instant, timeZone, sourceRef: revision }), (r, p) => {
633
+ if (Date.parse(r.endAt) <= Date.parse(r.startAt)) fail2(p, "invalid finite horizon");
634
+ }),
635
+ object2({ kind: literal2("daily_average"), timeZone, sourceRef: revision })
636
+ );
637
+ var uncertaintySource = object2({ method: literal2(RESONANCE_CPQL_UNCERTAINTY_METHOD), eligibleContacts: decimal, fullyResolvedContacts: decimal, qualifiedContacts: decimal, verifiedSpend: money, sourceRevision: revision, qualificationRuleRef: resource, universeComplete: literal2(true), spendPeriodComplete: literal2(true) });
638
+ var allocationEvidenceFields = {
639
+ id: uuid2,
640
+ revision,
641
+ evidenceDigest: digest,
642
+ projectId: uuid2,
643
+ campaignId: uuid2,
644
+ preparedPlanId: uuid2,
645
+ reviewDigest: digest,
646
+ observedAt: instant,
647
+ windowStart: instant,
648
+ windowEnd: instant,
649
+ dataThrough: nullable2(instant),
650
+ lastAdjustmentAt: nullable2(instant),
651
+ currentKillRevision: integer2(0),
652
+ goalRevision: revision,
653
+ objective,
654
+ policyDigest: digest,
655
+ resolvedPolicy: matureOptimizationPolicy,
656
+ trackingState: choice2("healthy", "blocked", "unknown"),
657
+ coverageBasisPoints: nullable2(integer2(0, 1e4)),
658
+ assignmentCheck: choice2("pass", "failed", "unknown", "not_applicable"),
659
+ powerCheck: choice2("pass", "failed", "unknown", "not_applicable")
660
+ };
661
+ var allocationChannelFields = { channelId: uuid2, site, accountBindingId: uuid2, trackingState: choice2("healthy", "blocked", "unknown"), coverageBasisPoints: nullable2(integer2(0, 1e4)), coverageFamily: choice2("crm_attribution", "provider_import", "experiment_exposure"), coverageDefinitionRef: resource, coveredNumerator: nullable2(decimal), eligibleDenominator: nullable2(decimal), uncertaintyBasisPoints: nullable2(integer2(0, 1e4)), uncertaintySource: nullable2(uncertaintySource), dedupeCheck: choice2("pass", "failed", "unknown", "not_applicable"), qualificationCheck: choice2("pass", "failed", "unknown", "not_applicable"), reasonCodes: list2(text2(80), 50), capabilityRevision: revision, budgetMode: choice2("campaign_total", "average_daily", "lifetime", "daily"), allocationHorizon: nullable2(allocationHorizon), matureThrough: nullable2(instant), dataThrough: nullable2(instant), goalRevision: revision, countingUnit: choice2("unique_source", "unique_qualified_contact", "net_order"), attributionModel: text2(100), attributionWindowDays: integer2(1, 365), policyRevision: revision, currentAllocation: money, remoteSnapshotDigest: digest, observations: list2(metric, 100), providerUnitExponent: refine2(integer2(0, 6), (v, p) => {
662
+ if (![0, 2, 3, 6].includes(v)) fail2(p, "unsupported provider exponent");
663
+ }), providerMaxUnits: decimal };
664
+ var { uncertaintySource: _legacyUncertaintyField, ...reportedChannelFields } = allocationChannelFields;
665
+ var matureOptimizationEvidence = object2({ ...allocationEvidenceFields, resolvedPolicy: matureOptimizationPolicy, channels: list2(object2({ ...allocationChannelFields, uncertaintySource: nullable2(uncertaintySource) }), 100, 1) });
666
+ var reportedCostFields = { basis: literal2("provider_reported_snapshot_v1"), observationId: uuid2, sourceEvidenceDigest: digest, reportedSpend: money, queriedFrom: instant, queriedThrough: instant, firstReceivedAt: instant, receivedAt: instant, recordedAt: instant, providerFinality: literal2("unknown"), correctionsPossible: literal2(true) };
667
+ var reportedOutcomeSource = object2({ method: literal2(RESONANCE_CPQL_UNCERTAINTY_METHOD), eligibleContacts: decimal, fullyResolvedContacts: decimal, qualifiedContacts: decimal, sourceRevision: revision, qualificationRuleRef: resource, universeComplete: literal2(true) });
668
+ var nativeSqlReadInstant = refine2(text2(32, /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{1,6}))?(?:Z|\+00:00)$/), (v, p) => {
669
+ const match = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{1,6}))?(?:Z|\+00:00)$/.exec(v);
670
+ const projected = match[1] + "." + (match[2] ?? "").padEnd(3, "0").slice(0, 3) + "Z";
671
+ if (!Number.isFinite(Date.parse(projected)) || new Date(projected).toISOString() !== projected) fail2(p, "invalid native SQL UTC instant");
672
+ });
673
+ var providerReadClock = object2({ kind: literal2("sql_before_provider_read_lower_bound_v1"), notBeforeAt: nativeSqlReadInstant });
674
+ var channelReportedCost = object2({ ...reportedCostFields, readClock: providerReadClock });
675
+ var reportedOptimizationEvidence = object2({
676
+ ...allocationEvidenceFields,
677
+ evidenceKind: literal2("reported_cpql_sql_clock_v1"),
678
+ resolvedPolicy: reportedOptimizationPolicy,
679
+ channels: list2(object2({ ...reportedChannelFields, channelReportedCost, outcomeSource: reportedOutcomeSource }), 100, 1)
680
+ });
681
+ var optimizationEvidenceRefFields = { id: uuid2, revision, evidenceDigest: digest };
682
+ var optimizationEvidenceRef = object2(optimizationEvidenceRefFields);
683
+ var finiteOwnMeasurement = union2(
684
+ object2({ state: literal2("unobserved"), observations: literal2(null), reasonCodes: list2(text2(80), 50) }),
685
+ object2({ state: choice2("pending", "blocked"), observations: nullable2(list2(metric, 100)), reasonCodes: list2(text2(80), 50) }),
686
+ object2({ state: literal2("mature"), observations: list2(metric, 100, 1), reasonCodes: list2(text2(80), 50) })
687
+ );
688
+ var finiteCandidateObservation = object2({
689
+ channelId: uuid2,
690
+ site,
691
+ accountBindingId: uuid2,
692
+ graphDigest: digest,
693
+ manifestRef: preparedManifestRef,
694
+ cap: positiveMoneyShape,
695
+ flight: object2({ startAt: instant, endAt: instant, timeZone }),
696
+ lifecycle: choice2("not_materialized", "paused", "active", "stopped", "unknown"),
697
+ nativeStateEvidenceRef: nullable2(optimizationEvidenceRef),
698
+ commitmentEvidenceRef: nullable2(object2({ ...optimizationEvidenceRefFields, commitmentOperationId: uuid2 })),
699
+ activationsObserved: integer2(0, 1),
700
+ trackingState: choice2("healthy", "blocked", "unknown"),
701
+ coverageBasisPoints: nullable2(integer2(0, 1e4)),
702
+ dedupeCheck: choice2("pass", "failed", "unknown", "not_applicable"),
703
+ qualificationCheck: choice2("pass", "failed", "unknown", "not_applicable"),
704
+ reasonCodes: list2(text2(80), 50),
705
+ ownMeasurement: finiteOwnMeasurement
706
+ });
707
+ var finiteOptimizationEvidence = object2({
708
+ id: uuid2,
709
+ revision,
710
+ evidenceDigest: digest,
711
+ projectId: uuid2,
712
+ campaignId: uuid2,
713
+ preparedPlanId: uuid2,
714
+ reviewDigest: digest,
715
+ observedAt: instant,
716
+ windowStart: instant,
717
+ windowEnd: instant,
718
+ dataThrough: nullable2(instant),
719
+ lastAdjustmentAt: nullable2(instant),
720
+ currentKillRevision: integer2(0),
721
+ goalRevision: revision,
722
+ objective,
723
+ policyDigest: digest,
724
+ resolvedPolicy: finiteMetaCandidatePolicy,
725
+ trackingState: choice2("healthy", "blocked", "unknown"),
726
+ coverageBasisPoints: nullable2(integer2(0, 1e4)),
727
+ assignmentCheck: choice2("pass", "failed", "unknown", "not_applicable"),
728
+ powerCheck: choice2("pass", "failed", "unknown", "not_applicable"),
729
+ evidenceKind: literal2("finite_candidate_lifecycle_v1"),
730
+ inventoryState: choice2("complete", "incomplete", "unknown"),
731
+ candidates: list2(finiteCandidateObservation, 100, 1)
732
+ });
733
+ var optimizationEvidence = union2(matureOptimizationEvidence, finiteOptimizationEvidence, reportedOptimizationEvidence);
734
+ var matureOptimizationDecision = object2({
735
+ id: uuid2,
736
+ revision,
737
+ projectId: uuid2,
738
+ campaignId: uuid2,
739
+ preparedPlanId: uuid2,
740
+ reviewDigest: digest,
741
+ evidenceRef: optimizationEvidenceRef,
742
+ policyDigest: digest,
743
+ requestDigest: digest,
744
+ expiresAt: instant,
745
+ algorithmRevision: revision,
746
+ sourceRefs: list2(revision, 200),
747
+ state: choice2("actionable", "noop", "blocked"),
748
+ decisionKind: choice2("observational_allocation", "experimental_promotion"),
749
+ executionOrder: literal2("reduce_then_increase"),
750
+ reasonCodes: list2(text2(80), 50),
751
+ adjustments: list2(object2({ channelId: uuid2, site, budgetMode: choice2("campaign_total", "average_daily", "lifetime", "daily"), providerUnitExponent: refine2(integer2(0, 6), (v, p) => {
752
+ if (![0, 2, 3, 6].includes(v)) fail2(p, "unsupported provider exponent");
753
+ }), providerMaxUnits: decimal, before: money, after: money, remoteSnapshotDigest: digest }), 100),
754
+ createdAt: instant
755
+ });
756
+ var finiteCandidateActionFields = { channelId: uuid2, graphDigest: digest, nativeStateEvidenceRef: optimizationEvidenceRef };
757
+ var finiteCandidateActionBody = union2(
758
+ object2({ action: literal2("materialize_and_activate"), ...finiteCandidateActionFields }),
759
+ object2({ action: literal2("activate_held"), ...finiteCandidateActionFields, commitmentOperationId: uuid2 }),
760
+ object2({ action: literal2("pause_candidate"), ...finiteCandidateActionFields })
761
+ );
762
+ var finiteDecisionFields = {
763
+ id: uuid2,
764
+ revision,
765
+ projectId: uuid2,
766
+ campaignId: uuid2,
767
+ preparedPlanId: uuid2,
768
+ reviewDigest: digest,
769
+ evidenceRef: optimizationEvidenceRef,
770
+ policyDigest: digest,
771
+ requestDigest: digest,
772
+ expiresAt: instant,
773
+ createdAt: instant,
774
+ algorithmRevision: literal2("finite_meta_candidates_v1"),
775
+ sourceRefs: list2(revision, 200),
776
+ decisionKind: literal2("preapproved_candidate_operation"),
777
+ reasonCodes: list2(text2(80), 50, 1)
778
+ };
779
+ var finiteOptimizationDecision = union2(
780
+ object2({ ...finiteDecisionFields, state: literal2("actionable"), candidateActions: list2(finiteCandidateActionBody, 1, 1) }),
781
+ object2({ ...finiteDecisionFields, state: choice2("noop", "blocked"), candidateActions: list2(finiteCandidateActionBody, 0) })
782
+ );
783
+ var optimizationDecision = union2(matureOptimizationDecision, finiteOptimizationDecision);
784
+ optimizationEvidence.schema;
785
+ optimizationDecision.schema;
786
+ var metaBrowserWitness = object2({ controlsRevision: text2(60, /^[A-Z][A-Z0-9_]*$/), policyRevision: revision, witnessId: uuid2, assertedAt: instant });
787
+ var publicDestination = union2(
788
+ object2({ provider: literal2("google_ads"), id: uuid2, tagId: text2(40, /^AW-[0-9]+$/) }),
789
+ // browserWitness and tagManagerAttested are optional so configurations from before the Meta loader keep parsing; absent means no Meta browser loading.
790
+ object2({ provider: literal2("meta_ads"), id: uuid2, pixelId: text2(40, /^[0-9]+$/), browserWitness: optional2(metaBrowserWitness), tagManagerAttested: optional2(bool) })
791
+ );
792
+ var interestResource = object2({ ...scope, type: choice2("page", "article", "product", "listing"), id: text2(160, /^[a-zA-Z0-9._:-]+$/), revision });
793
+ var measurementConfig = object2({
794
+ schemaVersion: literal2(1),
795
+ ...scope,
796
+ configRevision: revision,
797
+ policyRevision: revision,
798
+ expiresAt: instant,
799
+ enabled: bool,
800
+ consentPolicy: object2({ mode: literal2("basic"), requiredPurposes: list2(purpose, 4, 1) }),
801
+ destinations: list2(publicDestination, 10),
802
+ // metaEligible is set by a person with owner or admin standing; absent means false. The browser never decides it.
803
+ resourceRules: list2(object2({ id: uuid2, revision, site, resource: interestResource, path, interestKey: text2(100, /^[a-zA-Z0-9._:-]+$/), metaEligible: optional2(bool) }), 500)
804
+ });
805
+ function parseResonanceMeasurementConfig(v) {
806
+ const r = measurementConfig.read(v, "measurementConfig");
807
+ scoped(r, r.projectId, [r.site], "measurementConfig");
808
+ unique(r.destinations.map((d) => d.id), "measurementConfig.destinations");
809
+ unique(r.consentPolicy.requiredPurposes, "measurementConfig.consentPolicy.requiredPurposes");
810
+ unique(r.resourceRules.map((rule) => rule.id), "measurementConfig.resourceRules");
811
+ if (r.destinations.length && ["ad_storage", "ad_user_data", "ad_personalization"].some((p) => !r.consentPolicy.requiredPurposes.includes(p))) fail2("measurementConfig.consentPolicy", "provider minimum purposes are mandatory");
812
+ return r;
813
+ }
814
+ var clickIdentifierPattern = /^[^\s\u0000-\u001f\u007f-\u009f]+(?![\s\S])/;
815
+ var clickIdentifier = field2({ type: "string", minLength: 1, maxLength: 1024, pattern: clickIdentifierPattern.source }, (v, p) => typeof v === "string" && v.length <= 2048 && [...v].length <= 1024 && clickIdentifierPattern.test(v) ? v : fail2(p, "invalid click identifier"));
816
+ var clickIdentifiersShape = union2(object2({ gclid: clickIdentifier }), object2({ gbraid: clickIdentifier }), object2({ wbraid: clickIdentifier }));
817
+ var clickIdentifiers = field2(clickIdentifiersShape.schema, clickIdentifiersShape.read);
818
+ var parseResonanceClickIdentifiers = (v) => clickIdentifiers.read(v, "clickIdentifiers");
819
+ clickIdentifiers.schema;
820
+ var observedPathControl = /[\s\u0000-\u001f\u007f-\u009f]/;
821
+ var observedPagePath = field2({ ...path.schema, allOf: [{ not: { pattern: observedPathControl.source } }] }, (v, p) => {
822
+ const result = path.read(v, p);
823
+ if (observedPathControl.test(result)) fail2(p, "invalid observed path");
824
+ return result;
825
+ });
826
+ var metaCookieShape = object2({ source: literal2(RESONANCE_WEBSITE_VISIT_PROFILE.source), fbp: optional2(clickIdentifier), fbc: optional2(clickIdentifier) });
827
+ var metaCookieObservation = field2({ ...metaCookieShape.schema, anyOf: [{ required: ["fbp"] }, { required: ["fbc"] }] }, (v, p) => {
828
+ const result = metaCookieShape.read(v, p);
829
+ if (!result.fbp && !result.fbc) fail2(p, "requires an actual reported cookie value");
830
+ return result;
831
+ });
832
+ var occurrenceShape = object2({
833
+ schemaVersion: literal2(1),
834
+ ...scope,
835
+ configRevision: revision,
836
+ eventId: uuid2,
837
+ navigationId: uuid2,
838
+ occurredAt: instant,
839
+ kind: choice2("page_view", "content_interest", "experiment_exposure"),
840
+ sourceMarker: optional2(sourceMarker),
841
+ resourceRef: nullable2(interestResource),
842
+ campaignPlanId: nullable2(uuid2),
843
+ experiment: nullable2(exposure),
844
+ clickIdentifiers: optional2(clickIdentifiers),
845
+ pagePath: optional2(observedPagePath),
846
+ metaCookieObservation: optional2(metaCookieObservation),
847
+ consentReceiptRefs: list2(object2({ purpose, receiptId: uuid2, consentRevision: integer2(1) }), 4)
848
+ });
849
+ var occurrence = field2({ ...occurrenceShape.schema, allOf: [
850
+ { if: { required: ["clickIdentifiers"] }, then: { properties: { kind: { const: "page_view" } } } },
851
+ { if: { required: ["pagePath"] }, then: { properties: { kind: { enum: ["page_view", "experiment_exposure"] } } } },
852
+ { if: { required: ["metaCookieObservation"] }, then: { required: ["pagePath"], properties: { kind: { const: "page_view" }, resourceRef: interestResource.schema } } }
853
+ ] }, (v, p) => {
854
+ const r = occurrenceShape.read(v, p);
855
+ if (r.clickIdentifiers && r.kind !== "page_view") fail2(p + ".clickIdentifiers", "requires page_view");
856
+ if (r.pagePath !== void 0 && r.kind !== "page_view" && r.kind !== "experiment_exposure") fail2(p + ".pagePath", "requires page_view or experiment_exposure");
857
+ if (r.metaCookieObservation && (r.kind !== "page_view" || r.pagePath === void 0 || !r.resourceRef)) fail2(p + ".metaCookieObservation", "requires the same page_view path and resource");
858
+ return r;
859
+ });
860
+ function parseResonanceMeasurementOccurrence(v) {
861
+ const r = occurrence.read(v, "occurrence");
862
+ scoped(r, r.projectId, [r.site], "occurrence");
863
+ unique(r.consentReceiptRefs.map((c) => c.purpose), "occurrence.consentReceiptRefs");
864
+ if (r.kind === "content_interest" && !r.resourceRef) fail2("occurrence.resourceRef", "content interest requires resource");
865
+ if (r.kind === "experiment_exposure" && !r.experiment) fail2("occurrence.experiment", "exposure requires assignment context");
866
+ return r;
867
+ }
868
+ var consentUpdate = object2({ schemaVersion: literal2(1), ...scope, configRevision: revision, policyRevision: revision, expectedConsentRevision: integer2(0), occurredAt: instant, idempotencyKey: operationKey, source: choice2("cmp", "user_preference"), purposes: list2(object2({ purpose, state: choice2("unknown", "granted", "denied", "withdrawn") }), 4, 1) });
869
+ function parseResonanceConsentUpdate(v) {
870
+ const r = consentUpdate.read(v, "consentUpdate");
871
+ unique(r.purposes.map((p) => p.purpose), "consentUpdate.purposes");
872
+ return r;
873
+ }
874
+ measurementConfig.schema;
875
+ occurrence.schema;
876
+ consentUpdate.schema;
877
+ var consentResult = object2({ subjectProof: text2(200, /^[a-zA-Z0-9_-]{32,}$/), expiresAt: instant, currentConsentRevision: integer2(1), receipts: list2(consent, 4, 4) });
878
+ function parseResonanceConsentResponse(v) {
879
+ const r = consentResult.read(v, "consentResponse"), first = r.receipts[0];
880
+ if (r.receipts.some((c) => c.consentRevision !== r.currentConsentRevision)) fail2("consentResponse.receipts", "global consent revision mismatch");
881
+ unique(r.receipts.map((c) => c.purpose), "consentResult.receipts");
882
+ for (const c of r.receipts) if (c.projectId !== first.projectId || c.site !== first.site || c.configRevision !== first.configRevision || c.policyRevision !== first.policyRevision || c.visitorNamespace !== first.visitorNamespace) fail2("consentResult.receipts", "mixed consent subjects or scope");
883
+ return r;
884
+ }
885
+ var occurrenceResult = object2({ eventId: uuid2, accepted: literal2(true), replayed: bool, exposureId: nullable2(uuid2) });
886
+ var parseResonanceOccurrenceResponse = (v) => occurrenceResult.read(v, "occurrenceResult");
887
+ consentResult.schema;
888
+ occurrenceResult.schema;
889
+ var subjectRenew = object2({ schemaVersion: literal2(1), ...scope, expectedConsentRevision: integer2(1), configRevision: revision, policyRevision: revision });
890
+ var parseResonanceSubjectRenewCommand = (v) => subjectRenew.read(v, "subjectRenew");
891
+ subjectRenew.schema;
892
+ resource.schema;
893
+ audience.schema;
894
+ experiment.schema;
895
+ var audienceDraft = object2({
896
+ schemaVersion: literal2(1),
897
+ expectedRevision: optional2(integer2(1)),
898
+ idempotencyKey: operationKey,
899
+ definition: object2({ name: text2(160), site, accountBindingId: uuid2, resourceRuleId: uuid2, excludedResourceRuleIds: list2(uuid2, 50), retentionDays: integer2(1, 540), minDaysSinceVisit: optional2(integer2(1, 179)), policyRevision: revision, currentConsentPolicyRevision: revision })
900
+ });
901
+ audienceDraft.schema;
902
+ var managedExperimentBase = { expectedRevision: integer2(1), idempotencyKey: operationKey };
903
+ var experimentCreateField = field2(SIGNAL_EXPERIMENT_CREATE_JSON_SCHEMA, (v, p) => {
904
+ try {
905
+ return parseSignalExperimentCreate(v);
906
+ } catch (error) {
907
+ return fail2(p, error instanceof Error ? error.message : "invalid experiment");
908
+ }
909
+ });
910
+ var experimentUpdateField = field2(SIGNAL_EXPERIMENT_UPDATE_JSON_SCHEMA, (v, p) => {
911
+ try {
912
+ return parseSignalExperimentUpdate(v);
913
+ } catch (error) {
914
+ return fail2(p, error instanceof Error ? error.message : "invalid experiment");
915
+ }
916
+ });
917
+ var managedExperimentCreate = object2({ ...managedExperimentBase, experiment: experimentCreateField });
918
+ var managedExperimentUpdate = object2({ ...managedExperimentBase, experiment: experimentUpdateField });
919
+ var managedExperimentLifecycle = object2(managedExperimentBase);
920
+ var managedExperimentComplete = object2({ ...managedExperimentBase, winnerVariant: text2(80) });
921
+ var managedExperimentLink = object2({ ...managedExperimentBase, experimentId: uuid2 });
922
+ managedExperimentCreate.schema;
923
+ managedExperimentUpdate.schema;
924
+ managedExperimentLifecycle.schema;
925
+ managedExperimentComplete.schema;
926
+ managedExperimentLink.schema;
927
+ var nativePageRevision = field2({ ...instant.schema, pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z(?![\\s\\S])" }, (v, p) => {
928
+ const result = instant.read(v, p);
929
+ if (new Date(result).toISOString() !== result) fail2(p, "requires canonical UTC milliseconds");
930
+ return result;
931
+ });
932
+ var positiveRevision = text2(128, /^[1-9][0-9]*(?![\s\S])/);
933
+ var friendlyName = field2({ type: "string", minLength: 0, maxLength: 200 }, (v, p) => typeof v === "string" && [...v].length <= 200 ? v : fail2(p, "invalid friendly name"));
934
+ var setupConsentPolicyShape = object2({ mode: literal2("basic"), requiredPurposes: uniqueList(purpose, 4, 3) });
935
+ var setupConsentPolicy = field2({ ...setupConsentPolicyShape.schema, allOf: [{ properties: { requiredPurposes: { allOf: ["ad_storage", "ad_user_data", "ad_personalization"].map((value) => ({ contains: { const: value } })) } } }] }, (input, p) => {
936
+ const v = setupConsentPolicyShape.read(input, p);
937
+ for (const required of ["ad_storage", "ad_user_data", "ad_personalization"]) if (!v.requiredPurposes.includes(required)) fail2(p, "all advertising purpose requirements are mandatory");
938
+ return v;
939
+ });
940
+ var measurementSave = refine2(object2({
941
+ schemaVersion: literal2(1),
942
+ site,
943
+ expectedCampaignRevision: integer2(1),
944
+ expectedConfigurationRevision: integer2(0),
945
+ idempotencyKey: operationKey,
946
+ enabled: bool,
947
+ consentPolicy: setupConsentPolicy,
948
+ pages: list2(object2({ pageId: uuid2, observedRevision: nativePageRevision, observedDigest: digest }), 500, 1)
949
+ }), (v, p) => unique(v.pages.map((page2) => page2.pageId), p + ".pages"));
950
+ var measurementPage = object2({ pageId: uuid2, label: friendlyName, path: observedPagePath, pageRevision: nativePageRevision, evidenceDigest: digest, resourceRuleId: uuid2, resourceRuleRevision: digest });
951
+ var measurementSavedFields = { configurationRevision: integer2(1), configurationDigest: digest, configuration: measurementConfig, saveOperationId: uuid2, pages: list2(measurementPage, 500, 1) };
952
+ object2(measurementSavedFields);
953
+ function checkMeasurementSaved(v, p) {
954
+ const config = parseResonanceMeasurementConfig(v.configuration);
955
+ if (config.projectId !== v.projectId || config.site !== v.site) fail2(p + ".configuration", "configuration scope mismatch");
956
+ positiveRevision.read(config.configRevision, p + ".configuration.configRevision");
957
+ positiveRevision.read(config.policyRevision, p + ".configuration.policyRevision");
958
+ setupConsentPolicy.read(config.consentPolicy, p + ".configuration.consentPolicy");
959
+ unique(v.pages.map((page2) => page2.pageId), p + ".pages");
960
+ if (v.pages.length !== config.resourceRules.length) fail2(p + ".pages", "must cover the exact native page rules");
961
+ for (const page2 of v.pages) {
962
+ const rule = config.resourceRules.find((rule2) => rule2.id === page2.resourceRuleId);
963
+ if (page2.resourceRuleId !== page2.pageId || page2.resourceRuleRevision !== page2.evidenceDigest || !rule || rule.revision !== page2.evidenceDigest || rule.path !== page2.path || rule.interestKey !== page2.pageId || rule.resource.type !== "page" || rule.resource.id !== page2.pageId || rule.resource.revision !== page2.pageRevision) fail2(p + ".pages", "native page/resource/rule version mismatch");
964
+ }
965
+ }
966
+ var measurementCurrent = refine2(union2(
967
+ object2({ schemaVersion: literal2(1), kind: literal2("unconfigured"), ...scope, configurationRevision: literal2(0) }),
968
+ object2({ schemaVersion: literal2(1), kind: literal2("configured"), ...scope, ...measurementSavedFields, reasonCodes: uniqueList(text2(200), 100) })
969
+ ), (v, p) => {
970
+ if (v.kind === "configured") checkMeasurementSaved(v, p);
971
+ });
972
+ var measurementSnapshotFields = { schemaVersion: literal2(1), ...scope, campaignId: uuid2, operationId: uuid2, configurationRevision: integer2(1), configurationDigest: digest, configuration: measurementConfig, pages: list2(measurementPage, 500, 1) };
973
+ var measurementSnapshotShape = object2(measurementSnapshotFields);
974
+ function checkMeasurementSnapshot(v, p) {
975
+ checkMeasurementSaved({ ...v, saveOperationId: v.operationId }, p);
976
+ }
977
+ var measurementSnapshot = refine2(measurementSnapshotShape, checkMeasurementSnapshot);
978
+ var measurementSaveResponse = refine2(object2({ ...measurementSnapshotFields, replayed: bool }), checkMeasurementSnapshot);
979
+ object2({ projectId: uuid2, current: measurementCurrent, pages: list2(object2({ pageId: uuid2, label: friendlyName, path: observedPagePath, pageRevision: nativePageRevision, evidenceDigest: digest }), 500, 1), expiresAt: instant });
980
+ measurementSave.schema;
981
+ measurementCurrent.schema;
982
+ measurementSnapshot.schema;
983
+ measurementSaveResponse.schema;
984
+ var nativeAudienceRef = object2({ externalId: text2(160, /^[0-9]+(?![\s\S])/), revision: digest });
985
+ var metaSave = refine2(object2({
986
+ schemaVersion: literal2(1),
987
+ expectedCampaignRevision: integer2(1),
988
+ expectedConfigurationRevision: integer2(0),
989
+ idempotencyKey: operationKey,
990
+ audiences: list2(union2(object2({ audienceRuleId: uuid2, mode: literal2("existing"), nativeAudienceRef }), object2({ audienceRuleId: uuid2, mode: literal2("admitted_setup") })), 50, 1)
991
+ }), (v, p) => unique(v.audiences.map((a) => a.audienceRuleId), p + ".audiences"));
992
+ var metaResolvedAudience = object2({ audienceRuleRef: audience, name: friendlyName, resourceRuleRef: measurementRuleRef, excludedResourceRuleRefs: list2(measurementRuleRef, 50), selection: union2(
993
+ object2({ mode: literal2("existing"), nativeAudienceRef, nativeAudienceName: nullable2(friendlyName) }),
994
+ object2({ mode: literal2("admitted_setup"), setup: object2({ kind: literal2("meta_website_audience_v1"), localOutputKey: operationKey, specDigest: digest }) })
995
+ ) });
996
+ var metaMaterialFields = {
997
+ schemaVersion: literal2(1),
998
+ projectId: uuid2,
999
+ campaignId: uuid2,
1000
+ channelId: uuid2,
1001
+ site,
1002
+ campaignRevision: integer2(1),
1003
+ reviewedDefinitionDigest: digest,
1004
+ configurationRevision: integer2(1),
1005
+ saveOperationId: uuid2,
1006
+ channel: meta,
1007
+ account: object2({ id: uuid2, externalId: text2(160), name: nullable2(friendlyName) }),
1008
+ datasetName: nullable2(friendlyName),
1009
+ pageName: nullable2(friendlyName),
1010
+ instagramName: nullable2(friendlyName),
1011
+ measurementConfigRef,
1012
+ matchingProfileRef,
1013
+ rules: list2(object2({ id: uuid2, revision, resource: interestResource, path: observedPagePath, label: friendlyName }), 500, 1),
1014
+ audiences: list2(metaResolvedAudience, 50, 1)
1015
+ };
1016
+ object2(metaMaterialFields);
1017
+ function checkMetaMaterial(v, p) {
1018
+ scoped(v, v.projectId, [v.site], p);
1019
+ if (v.channel.id !== v.channelId || v.channel.accountBindingId !== v.account.id || v.matchingProfileRef.id !== v.saveOperationId || v.matchingProfileRef.revision !== v.configurationRevision) fail2(p, "original channel/account/profile receipt mismatch");
1020
+ if (!v.channel.instagramRef && v.instagramName !== null) fail2(p + ".instagramName", "absent original Instagram identity requires null");
1021
+ unique(v.rules.map((rule) => rule.id), p + ".rules");
1022
+ unique(v.audiences.map((a) => a.audienceRuleRef.id), p + ".audiences");
1023
+ unique(v.channel.audienceRuleRefs.map((a) => a.id), p + ".channel.audienceRuleRefs");
1024
+ if (v.audiences.length !== v.channel.audienceRuleRefs.length) fail2(p + ".audiences", "must cover exactly the original logical audiences");
1025
+ checkMetaAudienceBands(v.channel, p + ".channel.audienceRuleRefs");
1026
+ for (const a of v.audiences) {
1027
+ if (!v.channel.audienceRuleRefs.some((ref) => canonical(ref) === canonical(a.audienceRuleRef))) fail2(p + ".audiences", "original logical audience version mismatch");
1028
+ unique(a.excludedResourceRuleRefs.map((ref) => ref.id), p + ".audiences.excludedResourceRuleRefs");
1029
+ unique(a.audienceRuleRef.exclusionRuleIds, p + ".audiences.audienceRuleRef.exclusionRuleIds");
1030
+ if (canonical([...a.audienceRuleRef.exclusionRuleIds].sort()) !== canonical(a.excludedResourceRuleRefs.map((ref) => ref.id).sort()) || a.excludedResourceRuleRefs.some((ref) => ref.id === a.resourceRuleRef.id)) fail2(p + ".audiences", "positive/excluded resource scope mismatch");
1031
+ for (const selected of [a.resourceRuleRef, ...a.excludedResourceRuleRefs]) if (!v.rules.some((rule) => rule.id === selected.id && rule.revision === selected.revision)) fail2(p + ".audiences", "resource rule version mismatch");
1032
+ if (a.selection.mode === "admitted_setup" && a.selection.setup.localOutputKey !== `audience:${a.audienceRuleRef.id}:${a.audienceRuleRef.revision}`) fail2(p + ".audiences.selection.setup.localOutputKey", "must retain the actual original SDK creator key");
1033
+ }
1034
+ }
1035
+ var metaSnapshot = refine2(object2({ ...metaMaterialFields, configurationDigest: digest }), checkMetaMaterial);
1036
+ var parseResonanceMetaConfigurationSnapshot = (v) => metaSnapshot.read(v, "metaConfigurationSnapshot");
1037
+ refine2(object2({ ...metaMaterialFields, configurationDigest: optional2(digest) }), checkMetaMaterial);
1038
+ object2({ currentCampaignRevision: integer2(1), currentDefinitionDigest: digest, observedAt: instant, expiresAt: nullable2(instant), reasonCodes: uniqueList(text2(200), 100), pendingRequirements: uniqueList(text2(200), 100) });
1039
+ function metaReviewable(snapshot, metadata) {
1040
+ return metadata.currentCampaignRevision >= snapshot.campaignRevision && metadata.currentDefinitionDigest === snapshot.reviewedDefinitionDigest && metadata.reasonCodes.length === 0 && metadata.expiresAt !== null && Date.parse(metadata.expiresAt) > Date.parse(metadata.observedAt);
1041
+ }
1042
+ var metaView = refine2(union2(
1043
+ object2({ schemaVersion: literal2(1), kind: literal2("unconfigured"), projectId: uuid2, campaignId: uuid2, channelId: uuid2, configurationRevision: literal2(0) }),
1044
+ object2({
1045
+ schemaVersion: literal2(1),
1046
+ kind: literal2("configured"),
1047
+ projectId: uuid2,
1048
+ campaignId: uuid2,
1049
+ channelId: uuid2,
1050
+ configurationRevision: integer2(1),
1051
+ snapshot: metaSnapshot,
1052
+ currentCampaignRevision: integer2(1),
1053
+ currentDefinitionDigest: digest,
1054
+ observedAt: instant,
1055
+ expiresAt: nullable2(instant),
1056
+ readyForReview: bool,
1057
+ reasonCodes: uniqueList(text2(200), 100),
1058
+ pendingRequirements: uniqueList(text2(200), 100)
1059
+ })
1060
+ ), (v, p) => {
1061
+ if (v.kind !== "configured") return;
1062
+ const s = parseResonanceMetaConfigurationSnapshot(v.snapshot);
1063
+ if (v.projectId !== s.projectId || v.campaignId !== s.campaignId || v.channelId !== s.channelId || v.configurationRevision !== s.configurationRevision) fail2(p, "snapshot identity or configuration CAS mismatch");
1064
+ if (v.readyForReview !== metaReviewable(s, v)) fail2(p + ".readyForReview", "must reflect current review prerequisites only");
1065
+ });
1066
+ var metaSaveResponse = object2({ schemaVersion: literal2(1), snapshot: metaSnapshot, replayed: bool });
1067
+ metaSave.schema;
1068
+ metaSnapshot.schema;
1069
+ metaView.schema;
1070
+ metaSaveResponse.schema;
1071
+ var feedSelection = refine2(object2({ channels: list2(object2({ channelId: uuid2, expectedConfigurationRevision: integer2(1), resourceRuleRefs: list2(measurementRuleRef, 500, 1) }), 100, 1) }), (v, p) => {
1072
+ unique(v.channels.map((c) => c.channelId), p + ".channels");
1073
+ for (const c of v.channels) unique(c.resourceRuleRefs.map((ref) => ref.id), p + ".resourceRuleRefs");
1074
+ });
1075
+ var mandateDraftRequest = object2({ expectedRevision: integer2(1), allowedOperations: mandateAllowedOperations, expiresAt: instant, websiteVisitFeedSelection: optional2(feedSelection), providerReporting: optional2(providerReportingSelection) });
1076
+ mandateDraftRequest.schema;
1077
+ var feedReview = object2({ schemaVersion: literal2(1), approval: mandateApproval, mandateRef, configurations: list2(metaSnapshot, 100), current: object2({ state: choice2("not_enabled", "eligible", "invalidated", "unavailable"), checkedAt: instant, expiresAt: nullable2(instant), reasonCodes: uniqueList(text2(200), 100) }) });
1078
+ feedReview.schema;
1079
+ var observationIdentity = { observationId: uuid2, channelId: uuid2, versionId: uuid2, accountBindingId: uuid2 };
1080
+ var publicObservationMetric = union2(
1081
+ object2({ ...metricBase, ...observationIdentity, source: choice2("provider", "first_party"), state: literal2("observed"), value: metricValue }),
1082
+ object2({ ...metricBase, ...observationIdentity, source: choice2("provider", "first_party"), state: choice2("unknown", "insufficient", "delayed", "unavailable"), value: literal2(null), reasonCode: text2(80) })
1083
+ );
1084
+ var observationDenominators = object2({ eligibleContacts: nullable2(decimal), fullyResolvedContacts: nullable2(decimal), qualifiedContacts: nullable2(decimal) });
1085
+ var observationSupportFields = { ...observationIdentity, currency, fetchedAt: instant, dataThrough: nullable2(instant), coverage: choice2("unknown", "partial", "complete"), reasonCode: nullable2(text2(80)), denominators: observationDenominators };
1086
+ var firstPartyObservationSupport = object2({ ...observationSupportFields, source: literal2("first_party") });
1087
+ var providerObservationSupport = object2({
1088
+ ...observationSupportFields,
1089
+ source: literal2("provider"),
1090
+ firstReceivedAt: nullable2(instant),
1091
+ receivedAt: nullable2(instant),
1092
+ recordedAt: nullable2(instant),
1093
+ readClock: nullable2(providerReadClock),
1094
+ providerFinality: literal2("unknown"),
1095
+ correctionsPossible: literal2(true),
1096
+ coverageBasis: choice2("closed_query_snapshot", "not_established")
1097
+ });
1098
+ var observationSupport = union2(firstPartyObservationSupport, providerObservationSupport);
1099
+ var observationsPage = object2({ items: list2(publicObservationMetric, 500), nextCursor: nullable2(uuid2), observedAt: instant, support: list2(observationSupport, 500) });
1100
+ publicObservationMetric.schema;
1101
+ observationSupport.schema;
1102
+ observationsPage.schema;
1103
+ var META_WEBSITE_AUDIENCE_MAX_RETENTION_DAYS = 180;
1104
+ var META_AUDIENCE_RULE_LIMIT = 10;
1105
+ var usesPropertyModel = (c) => c.channelGroup !== void 0 || c.audienceRuleRefs.some((ref) => ref.minDaysSinceVisit !== void 0);
1106
+ function checkAudienceBand(ref, provider = "meta_ads", at = "audienceRuleRef") {
1107
+ const min = ref.minDaysSinceVisit;
1108
+ if (min !== void 0 && min >= ref.retentionDays) fail2(at + ".minDaysSinceVisit", "must be shorter than the retention window");
1109
+ if (provider === "google_ads") {
1110
+ if (min !== void 0) fail2(at + ".minDaysSinceVisit", "Google audiences have no recency band");
1111
+ return;
1112
+ }
1113
+ if (ref.retentionDays > META_WEBSITE_AUDIENCE_MAX_RETENTION_DAYS) fail2(at + ".retentionDays", `exceeds the ${META_WEBSITE_AUDIENCE_MAX_RETENTION_DAYS}-day Meta website audience maximum`);
1114
+ if (1 + ref.exclusionRuleIds.length + (min === void 0 ? 0 : 1) > META_AUDIENCE_RULE_LIMIT) fail2(at + ".exclusionRuleIds", `a Meta audience holds at most ${META_AUDIENCE_RULE_LIMIT} rules including the page and any recency band`);
1115
+ }
1116
+ function checkMetaAudienceBands(channel2, at) {
1117
+ if (!usesPropertyModel(channel2)) return;
1118
+ channel2.audienceRuleRefs.forEach((ref, index) => checkAudienceBand(ref, "meta_ads", `${at}[${index}]`));
1119
+ }
1120
+
1121
+ // src/resonance/controller.ts
1122
+ var payload = (body) => {
1123
+ if (!body || typeof body !== "object" || body.success !== true || !("data" in body)) throw new Error("measurement_response_unavailable");
1124
+ return body.data;
1125
+ };
1126
+ var ResonanceController = class {
1127
+ constructor(options) {
1128
+ this.options = options;
1129
+ this.config = null;
1130
+ this.proof = null;
1131
+ this.adapter = null;
1132
+ this.unsubscribe = null;
1133
+ this.configEpoch = 0;
1134
+ this.readEpoch = 0;
1135
+ this.deliveryEpoch = 0;
1136
+ this.stopped = false;
1137
+ this.busy = false;
1138
+ this.latest = null;
1139
+ this.latestEpoch = 0;
1140
+ this.completedEpoch = 0;
1141
+ this.path = "";
1142
+ this.sourceMarker = null;
1143
+ this.navigationSequence = 0;
1144
+ this.recordedNavigationKey = "";
1145
+ this.navigation = null;
1146
+ this.renderedFrame = null;
1147
+ this.acceptedExposure = null;
1148
+ this.retry = /* @__PURE__ */ new Map();
1149
+ this.expiryTimer = null;
1150
+ this.configExpiryTimer = null;
1151
+ this.refreshTimer = null;
1152
+ this.refreshing = false;
1153
+ this.renewing = false;
1154
+ this.renewalTimer = null;
1155
+ this.status = { state: "unknown", reason: null };
1156
+ }
1157
+ now() {
1158
+ return this.options.now?.() ?? Date.now();
1159
+ }
1160
+ changed() {
1161
+ this.options.onState?.();
1162
+ }
1163
+ /** One current-navigation slot, not a history cache. A true identity change
1164
+ * retires its receipt and retry work; late deliveries cannot rejoin it. */
1165
+ discardRenderedFrame() {
1166
+ const frame = this.renderedFrame;
1167
+ if (!frame) return;
1168
+ frame.active = false;
1169
+ this.renderedFrame = null;
1170
+ if (this.acceptedExposure?.frame === frame) this.acceptedExposure = null;
1171
+ for (const [id, pending] of this.retry) if (pending.frame === frame) this.retry.delete(id);
1172
+ }
1173
+ invalidate() {
1174
+ this.deliveryEpoch++;
1175
+ this.navigation = null;
1176
+ this.renderedFrame = null;
1177
+ this.acceptedExposure = null;
1178
+ this.retry.clear();
1179
+ updateMeasurementPolicy({ proof: null, preferencePending: !!this.config?.enabled });
1180
+ this.changed();
1181
+ }
1182
+ timerDelay(milliseconds) {
1183
+ return Math.max(0, Math.min(2147483647, milliseconds));
1184
+ }
1185
+ timers() {
1186
+ if (this.expiryTimer) clearTimeout(this.expiryTimer);
1187
+ if (this.renewalTimer) clearTimeout(this.renewalTimer);
1188
+ if (!this.proof) return;
1189
+ const proofRemaining = Date.parse(this.proof.expiresAt) - this.now();
1190
+ this.expiryTimer = setTimeout(() => {
1191
+ if (this.proof && Date.parse(this.proof.expiresAt) > this.now()) {
1192
+ this.timers();
1193
+ return;
1194
+ }
1195
+ this.invalidate();
1196
+ this.status.state = "blocked";
1197
+ this.status.reason = "proof_expired";
1198
+ this.changed();
1199
+ }, this.timerDelay(proofRemaining));
1200
+ this.renewalTimer = setTimeout(() => {
1201
+ void this.renew();
1202
+ }, this.timerDelay(Math.max(1e3, proofRemaining - Math.min(6e4, proofRemaining / 2))));
1203
+ this.scheduleConfigRefresh();
1204
+ }
1205
+ scheduleConfigRefresh(delay) {
1206
+ if (this.refreshTimer) clearTimeout(this.refreshTimer);
1207
+ if (!this.config || this.stopped) return;
1208
+ const remaining = Date.parse(this.config.expiresAt) - this.now();
1209
+ if (this.configExpiryTimer) clearTimeout(this.configExpiryTimer);
1210
+ this.configExpiryTimer = setTimeout(() => {
1211
+ if (this.config && Date.parse(this.config.expiresAt) > this.now()) {
1212
+ this.scheduleConfigRefresh();
1213
+ return;
1214
+ }
1215
+ this.invalidate();
1216
+ updateMeasurementPolicy({ collectionEpoch: measurementPolicySnapshot().collectionEpoch + 1 });
1217
+ this.status.state = "blocked";
1218
+ this.status.reason = "config_expired";
1219
+ this.changed();
1220
+ }, this.timerDelay(remaining));
1221
+ this.refreshTimer = setTimeout(() => {
1222
+ void this.refreshConfig();
1223
+ }, this.timerDelay(delay ?? Math.max(1e3, remaining - Math.min(6e4, remaining / 2))));
1224
+ }
1225
+ /** A safe public read refreshes policy freshness without re-prompting or changing consent. */
1226
+ async refreshConfig() {
1227
+ if (!this.config || this.stopped || this.refreshing) return;
1228
+ if (this.busy) {
1229
+ this.scheduleConfigRefresh(1e3);
1230
+ return;
1231
+ }
1232
+ const previous = this.config;
1233
+ this.refreshing = true;
1234
+ try {
1235
+ const response = await this.options.transport.request(`/config?site=${encodeURIComponent(this.options.site)}`, { method: "GET", cache: "no-store" }, () => !this.stopped);
1236
+ if (this.stopped || previous !== this.config) return;
1237
+ if (response.status !== 200) throw new Error("config_refresh_unavailable");
1238
+ const next = parseResonanceMeasurementConfig(payload(response.body));
1239
+ if (next.projectId !== previous.projectId || next.site !== this.options.site || Date.parse(next.expiresAt) <= this.now()) throw new Error("config_scope_or_expiry");
1240
+ if (this.busy) {
1241
+ this.scheduleConfigRefresh(1e3);
1242
+ return;
1243
+ }
1244
+ const sameRevision = next.configRevision === previous.configRevision && next.policyRevision === previous.policyRevision;
1245
+ if (sameRevision && JSON.stringify({ ...next, expiresAt: "" }) !== JSON.stringify({ ...previous, expiresAt: "" })) throw new Error("config_revision_inconsistent");
1246
+ this.config = next;
1247
+ if (sameRevision) {
1248
+ const resumable = this.latest?.valid && this.proof && Date.parse(this.proof.expiresAt) > this.now() && this.proof.receipts.every((r) => r.state === this.latest?.choices[r.purpose]);
1249
+ updateMeasurementPolicy({ policy: this.configPolicy(next), config: next, proof: resumable ? this.proof : null });
1250
+ if (this.proof) this.timers();
1251
+ else this.scheduleConfigRefresh();
1252
+ this.recordCurrentNavigation();
1253
+ } else {
1254
+ this.invalidate();
1255
+ this.acceptedExposure = null;
1256
+ this.proof = null;
1257
+ this.latest = null;
1258
+ this.latestEpoch++;
1259
+ this.completedEpoch = this.latestEpoch;
1260
+ updateMeasurementPolicy({
1261
+ policy: this.configPolicy(next),
1262
+ config: next,
1263
+ identityScope: `${next.projectId}:${next.site}:${next.configRevision}`,
1264
+ proof: null,
1265
+ choices: unknownChoices(),
1266
+ cmpValid: false,
1267
+ preferencePending: !!this.adapter,
1268
+ analyticsRequired: next.consentPolicy.requiredPurposes.includes("analytics_storage") || !!this.adapter?.requiredPurposes.includes("analytics_storage")
1269
+ });
1270
+ this.status.state = "waiting_for_consent";
1271
+ this.status.reason = null;
1272
+ this.scheduleConfigRefresh();
1273
+ await this.readConsent();
1274
+ }
1275
+ this.changed();
1276
+ } catch {
1277
+ if (this.stopped || previous !== this.config) return;
1278
+ if (this.configPolicy(previous) !== "legacy_unconfigured") {
1279
+ this.invalidate();
1280
+ updateMeasurementPolicy({ policy: "resolving" });
1281
+ this.status.state = "blocked";
1282
+ }
1283
+ this.status.reason = "config_refresh_unavailable";
1284
+ this.changed();
1285
+ this.scheduleConfigRefresh(1e4);
1286
+ } finally {
1287
+ this.refreshing = false;
1288
+ }
1289
+ }
1290
+ async start() {
1291
+ const epoch = ++this.configEpoch;
1292
+ this.proof = null;
1293
+ this.invalidate();
1294
+ updateMeasurementPolicy({ policy: "resolving", config: null, proof: null, choices: unknownChoices(), cmpValid: false, preferencePending: !!this.adapter });
1295
+ try {
1296
+ const result = await this.options.transport.request(`/config?site=${encodeURIComponent(this.options.site)}`, { method: "GET", cache: "no-store" });
1297
+ if (this.stopped || epoch !== this.configEpoch) return;
1298
+ if (result.status === 404) {
1299
+ this.status.state = "legacy_unconfigured";
1300
+ updateMeasurementPolicy({ policy: "legacy_unconfigured", config: null, analyticsRequired: this.adapter?.requiredPurposes.includes("analytics_storage") ?? false });
1301
+ await this.readConsent();
1302
+ return;
1303
+ }
1304
+ if (result.status !== 200) throw new Error("config_unavailable");
1305
+ const config = parseResonanceMeasurementConfig(payload(result.body));
1306
+ if (config.site !== this.options.site || Date.parse(config.expiresAt) <= this.now()) throw new Error("config_scope_or_expiry");
1307
+ this.config = config;
1308
+ updateMeasurementPolicy({ policy: this.configPolicy(config), config, identityScope: `${config.projectId}:${config.site}:${config.configRevision}`, preferencePending: config.enabled && !!this.adapter, analyticsRequired: config.consentPolicy.requiredPurposes.includes("analytics_storage") || !!this.adapter?.requiredPurposes.includes("analytics_storage") });
1309
+ this.status.state = config.enabled ? "waiting_for_consent" : "blocked";
1310
+ this.status.reason = config.enabled ? null : "measurement_disabled";
1311
+ this.scheduleConfigRefresh();
1312
+ await this.readConsent();
1313
+ } catch {
1314
+ if (this.stopped || epoch !== this.configEpoch) return;
1315
+ this.status.state = "blocked";
1316
+ this.status.reason = "config_unavailable";
1317
+ updateMeasurementPolicy({ policy: "resolving", proof: null });
1318
+ }
1319
+ this.changed();
1320
+ }
1321
+ setAdapter(adapter) {
1322
+ this.unsubscribe?.();
1323
+ this.unsubscribe = null;
1324
+ this.adapter = adapter;
1325
+ this.invalidate();
1326
+ updateMeasurementPolicy({ analyticsRequired: !!adapter?.requiredPurposes.includes("analytics_storage") || !!this.config?.consentPolicy.requiredPurposes.includes("analytics_storage"), choices: unknownChoices(), cmpValid: false, preferencePending: !!adapter });
1327
+ if (adapter) this.unsubscribe = adapter.subscribe(() => {
1328
+ this.observe({ valid: false, choices: unknownChoices(), source: "cmp", observedAt: new Date(this.now()).toISOString() });
1329
+ void this.readConsent();
1330
+ });
1331
+ void this.readConsent();
1332
+ }
1333
+ consentContext() {
1334
+ return {
1335
+ projectId: this.config?.projectId ?? null,
1336
+ site: this.options.site,
1337
+ configRevision: this.config?.configRevision ?? null,
1338
+ policyRevision: this.config?.policyRevision ?? null,
1339
+ requiredPurposes: this.config?.consentPolicy.requiredPurposes ?? this.adapter?.requiredPurposes ?? []
1340
+ };
1341
+ }
1342
+ configPolicy(config) {
1343
+ return !config.enabled && config.configRevision === "not_configured" && config.policyRevision === "not_configured" && !config.destinations.length && !config.resourceRules.length ? "legacy_unconfigured" : "configured";
1344
+ }
1345
+ async readConsent() {
1346
+ if (!this.adapter || this.stopped) return;
1347
+ const adapter = this.adapter;
1348
+ const readEpoch = ++this.readEpoch;
1349
+ try {
1350
+ const observation = await adapter.read(this.consentContext());
1351
+ if (this.stopped || this.adapter !== adapter || readEpoch !== this.readEpoch) return;
1352
+ this.observe(observation);
1353
+ } catch {
1354
+ if (readEpoch !== this.readEpoch) return;
1355
+ this.observe({ valid: false, choices: unknownChoices(), source: "cmp", observedAt: new Date(this.now()).toISOString() });
1356
+ }
1357
+ }
1358
+ /** Maintained CMP observations may resume valid consent; this never prompts or infers a grant. */
1359
+ observe(observation) {
1360
+ if (this.stopped) return;
1361
+ if (this.latest?.valid === observation.valid && MEASUREMENT_PURPOSES.every((p) => this.latest?.choices[p] === observation.choices[p]) && (this.busy || this.proof)) return;
1362
+ if (observation.valid && !this.busy && this.proof && this.config && this.proof.receipts.every((r) => r.state === observation.choices[r.purpose])) {
1363
+ this.latest = observation;
1364
+ this.latestEpoch++;
1365
+ this.completedEpoch = this.latestEpoch;
1366
+ const expired = Date.parse(this.proof.expiresAt) <= this.now();
1367
+ updateMeasurementPolicy({ choices: { ...observation.choices }, cmpValid: true, preferencePending: expired, proof: expired ? null : this.proof });
1368
+ if (expired) {
1369
+ void this.renew();
1370
+ return;
1371
+ }
1372
+ this.status.state = "ready";
1373
+ this.status.reason = null;
1374
+ this.timers();
1375
+ this.changed();
1376
+ this.recordCurrentNavigation();
1377
+ return;
1378
+ }
1379
+ if (observation.valid) this.acceptedExposure = null;
1380
+ this.latest = observation;
1381
+ this.latestEpoch++;
1382
+ this.invalidate();
1383
+ updateMeasurementPolicy({ choices: observation.valid ? { ...observation.choices } : unknownChoices(), cmpValid: observation.valid, preferencePending: !observation.valid || !!this.config?.enabled });
1384
+ this.status.state = this.config ? this.config.enabled ? "waiting_for_consent" : "blocked" : "unknown";
1385
+ this.status.reason = this.config && !this.config.enabled ? "measurement_disabled" : null;
1386
+ if (observation.valid && this.config?.enabled) void this.captureLatest();
1387
+ this.changed();
1388
+ }
1389
+ async captureLatest() {
1390
+ if (this.busy || this.stopped || !this.config || !this.latest?.valid) return;
1391
+ this.busy = true;
1392
+ let processingEpoch = this.latestEpoch;
1393
+ try {
1394
+ while (!this.stopped && this.latest?.valid && this.completedEpoch < this.latestEpoch) {
1395
+ const observation = this.latest, epoch = this.latestEpoch, config = this.config;
1396
+ processingEpoch = epoch;
1397
+ const prior = this.proof;
1398
+ this.status.state = "pending";
1399
+ this.changed();
1400
+ const body = parseResonanceConsentUpdate({
1401
+ schemaVersion: 1,
1402
+ projectId: config.projectId,
1403
+ site: config.site,
1404
+ configRevision: config.configRevision,
1405
+ policyRevision: config.policyRevision,
1406
+ expectedConsentRevision: prior?.currentConsentRevision ?? 0,
1407
+ occurredAt: observation.observedAt,
1408
+ idempotencyKey: uuid(),
1409
+ source: observation.source,
1410
+ purposes: MEASUREMENT_PURPOSES.map((purpose2) => ({ purpose: purpose2, state: observation.choices[purpose2] }))
1411
+ });
1412
+ const result = await this.options.transport.request("/consent", { method: "POST", cache: "no-store", headers: { "Content-Type": "application/json", ...prior ? { [RESONANCE_SUBJECT_HEADER]: prior.subjectProof } : {} }, body: JSON.stringify(body) }, () => !this.stopped && epoch === this.latestEpoch && config === this.config);
1413
+ if (this.stopped) return;
1414
+ if (result.status !== 200) throw new Error("consent_not_accepted");
1415
+ const accepted = parseResonanceConsentResponse(payload(result.body));
1416
+ this.verifyProofScope(accepted, config);
1417
+ if (prior && (accepted.receipts[0].visitorNamespace !== prior.receipts[0].visitorNamespace || accepted.currentConsentRevision < prior.currentConsentRevision)) throw new Error("consent_subject_or_revision_changed");
1418
+ this.proof = accepted;
1419
+ this.completedEpoch = epoch;
1420
+ if (epoch !== this.latestEpoch) continue;
1421
+ for (const receipt2 of accepted.receipts) if (receipt2.state !== observation.choices[receipt2.purpose]) throw new Error("consent_state_mismatch");
1422
+ updateMeasurementPolicy({ proof: accepted, preferencePending: false });
1423
+ this.status.state = "ready";
1424
+ this.status.reason = null;
1425
+ this.timers();
1426
+ this.changed();
1427
+ this.recordCurrentNavigation();
1428
+ }
1429
+ } catch {
1430
+ if (this.stopped) return;
1431
+ this.completedEpoch = processingEpoch;
1432
+ this.status.state = "blocked";
1433
+ this.status.reason = "consent_unavailable";
1434
+ updateMeasurementPolicy({ proof: null });
1435
+ this.changed();
1436
+ } finally {
1437
+ this.busy = false;
1438
+ if (!this.stopped && this.latest?.valid && this.completedEpoch < this.latestEpoch) void this.captureLatest();
1439
+ }
1440
+ }
1441
+ verifyProofScope(proof, config) {
1442
+ const first = proof.receipts[0];
1443
+ if (first.projectId !== config.projectId || first.site !== config.site || first.configRevision !== config.configRevision || first.policyRevision !== config.policyRevision || Date.parse(proof.expiresAt) <= this.now()) throw new Error("proof_scope_or_expiry");
1444
+ }
1445
+ async renew() {
1446
+ if (!this.proof || !this.config || !this.latest?.valid || this.stopped || this.busy || this.renewing) return;
1447
+ this.renewing = true;
1448
+ const previous = this.proof, config = this.config, epoch = this.latestEpoch;
1449
+ try {
1450
+ const current = await this.adapter?.read(this.consentContext());
1451
+ if (this.stopped || epoch !== this.latestEpoch || this.proof !== previous) return;
1452
+ if (!current?.valid || !MEASUREMENT_PURPOSES.every((p) => current.choices[p] === this.latest?.choices[p])) {
1453
+ if (current) this.observe(current);
1454
+ else this.invalidate();
1455
+ return;
1456
+ }
1457
+ const body = parseResonanceSubjectRenewCommand({ schemaVersion: 1, projectId: config.projectId, site: config.site, configRevision: config.configRevision, policyRevision: config.policyRevision, expectedConsentRevision: previous.currentConsentRevision });
1458
+ const result = await this.options.transport.request("/consent/renew", { method: "POST", cache: "no-store", headers: { "Content-Type": "application/json", [RESONANCE_SUBJECT_HEADER]: previous.subjectProof }, body: JSON.stringify(body) }, () => !this.stopped && epoch === this.latestEpoch && previous === this.proof);
1459
+ if (this.stopped || epoch !== this.latestEpoch || this.proof !== previous) return;
1460
+ if (result.status !== 200) throw new Error("renewal_failed");
1461
+ const renewed = parseResonanceConsentResponse(payload(result.body));
1462
+ this.verifyProofScope(renewed, config);
1463
+ if (renewed.currentConsentRevision !== previous.currentConsentRevision || renewed.receipts.some((r) => {
1464
+ const old = previous.receipts.find((p) => p.purpose === r.purpose);
1465
+ return !old || r.visitorNamespace !== old.visitorNamespace || r.state !== old.state || r.receiptId !== old.receiptId;
1466
+ })) throw new Error("renewal_changed_consent_or_subject");
1467
+ this.proof = renewed;
1468
+ updateMeasurementPolicy({ proof: renewed, preferencePending: false });
1469
+ this.status.state = "ready";
1470
+ this.status.reason = null;
1471
+ this.timers();
1472
+ this.changed();
1473
+ this.recordCurrentNavigation();
1474
+ } catch {
1475
+ if (this.stopped || epoch !== this.latestEpoch || this.proof !== previous) return;
1476
+ this.invalidate();
1477
+ this.status.state = "blocked";
1478
+ this.status.reason = "renewal_unavailable";
1479
+ this.changed();
1480
+ } finally {
1481
+ this.renewing = false;
1482
+ }
1483
+ }
1484
+ /** Connectivity recovery renews existing proof; it never replays a failed grant. */
1485
+ async recover() {
1486
+ if (this.stopped) return;
1487
+ if (this.config) await this.refreshConfig();
1488
+ else await this.start();
1489
+ await this.renew();
1490
+ this.retryPending();
1491
+ }
1492
+ navigate(path2, sourceMarker2 = null) {
1493
+ let marker = null;
1494
+ if (sourceMarker2) {
1495
+ try {
1496
+ marker = parseResonanceSourceMarker(sourceMarker2);
1497
+ } catch {
1498
+ }
1499
+ }
1500
+ if (path2 === this.path && marker === this.sourceMarker) return;
1501
+ this.sourceMarker = marker;
1502
+ this.navigationSequence++;
1503
+ this.path = path2;
1504
+ this.navigation = null;
1505
+ this.discardRenderedFrame();
1506
+ this.acceptedExposure = null;
1507
+ this.recordCurrentNavigation();
1508
+ }
1509
+ recordCurrentNavigation() {
1510
+ if (!this.config || !this.path || !advertisingPurposesPermitted(this.now())) return;
1511
+ const navigationKey = `${this.navigationSequence}:${this.proof?.currentConsentRevision}`;
1512
+ if (navigationKey === this.recordedNavigationKey) return;
1513
+ this.recordedNavigationKey = navigationKey;
1514
+ this.navigation = { path: this.path, id: uuid(), epoch: this.deliveryEpoch };
1515
+ const navigation = this.navigation;
1516
+ const rule = this.config.resourceRules.find((r) => r.path === this.path);
1517
+ const pages = this.config.resourceRules.filter((r) => r.path === this.path && r.resource.type === "page");
1518
+ void this.emit("page_view", pages.length === 1 ? pages[0].resource : null);
1519
+ if (rule && navigation === this.navigation) void this.emit("content_interest", rule.resource);
1520
+ }
1521
+ async exposure(experiment2) {
1522
+ await this.submitExposure(experiment2);
1523
+ }
1524
+ /** Internal rendered occurrence seam; retry queues retain the resulting immutable event and ID. */
1525
+ renderedExposure(experiment2, context) {
1526
+ if (!this.exposurePermitted(experiment2) || context && (!context.pagePath || context.pagePath !== this.path)) return null;
1527
+ if (!this.navigation) this.navigation = { path: this.path, id: uuid(), epoch: this.deliveryEpoch };
1528
+ const previous = this.renderedFrame;
1529
+ if (previous && previous.navigation === this.navigation && previous.context?.pagePath === context?.pagePath && previous.context?.pageVersionId === context?.pageVersionId && previous.input.projectId === experiment2.projectId && previous.input.site === experiment2.site && previous.input.experimentId === experiment2.experimentId && previous.input.experimentRevision === experiment2.experimentRevision && previous.input.assignmentId === experiment2.assignmentId && previous.input.variantKey === experiment2.variantKey) {
1530
+ previous.input = experiment2;
1531
+ previous.active = true;
1532
+ return previous.pending ?? Promise.resolve();
1533
+ }
1534
+ const frame = { input: experiment2, context: context ? { ...context } : void 0, navigation: this.navigation, active: true, pending: null };
1535
+ this.discardRenderedFrame();
1536
+ this.renderedFrame = frame;
1537
+ this.acceptedExposure = null;
1538
+ const pending = this.submitExposure(experiment2, frame);
1539
+ frame.pending = pending;
1540
+ if (pending) void pending.then(() => {
1541
+ frame.pending = null;
1542
+ }, () => {
1543
+ frame.pending = null;
1544
+ });
1545
+ return pending;
1546
+ }
1547
+ /** Close form attachment immediately, but retain this single bounded frame
1548
+ * while the current assignment is revalidated. An exact native-identity rejoin
1549
+ * keeps its original occurrence/receipt even after an asynchronous fallback.
1550
+ * Navigation, replacement, consent/scope invalidation and stop retire it. */
1551
+ releaseRenderedExposure(experiment2) {
1552
+ const frame = this.renderedFrame;
1553
+ if (!frame || frame.input !== experiment2) return;
1554
+ frame.active = false;
1555
+ }
1556
+ exposurePermitted(experiment2) {
1557
+ return !this.stopped && !!this.proof && !!this.config && experimentMeasurementPermitted(this.now()) && experiment2.projectId === this.config.projectId && experiment2.site === this.config.site;
1558
+ }
1559
+ submitExposure(experiment2, frame) {
1560
+ if (!this.exposurePermitted(experiment2)) return null;
1561
+ if (!this.navigation) this.navigation = { path: this.path, id: uuid(), epoch: this.deliveryEpoch };
1562
+ const resolved = "exposureId" in experiment2 ? experiment2 : { ...experiment2, exposureId: uuid() };
1563
+ return this.emit("experiment_exposure", null, resolved, frame);
1564
+ }
1565
+ async emit(kind, resourceRef, experiment2 = null, frame) {
1566
+ if (!this.config || !this.proof || !this.navigation) return;
1567
+ const navigation = this.navigation, proof = this.proof, config = this.config, epoch = this.deliveryEpoch;
1568
+ let clickIdentifiers2;
1569
+ if (kind === "page_view" && advertisingPurposesPermitted(this.now())) {
1570
+ try {
1571
+ const observed = this.options.readClickIdentifiers?.(this.navigation.path);
1572
+ if (observed != null) clickIdentifiers2 = parseResonanceClickIdentifiers(observed);
1573
+ } catch {
1574
+ }
1575
+ }
1576
+ if (navigation !== this.navigation || proof !== this.proof || epoch !== this.deliveryEpoch) return;
1577
+ let event = parseResonanceMeasurementOccurrence({
1578
+ schemaVersion: 1,
1579
+ projectId: config.projectId,
1580
+ site: config.site,
1581
+ configRevision: config.configRevision,
1582
+ eventId: uuid(),
1583
+ navigationId: navigation.id,
1584
+ occurredAt: new Date(this.now()).toISOString(),
1585
+ kind,
1586
+ resourceRef,
1587
+ ...clickIdentifiers2 ? { clickIdentifiers: clickIdentifiers2 } : {},
1588
+ ...frame?.context ? { pagePath: frame.context.pagePath } : {},
1589
+ ...this.sourceMarker && advertisingPurposesPermitted(this.now()) ? { sourceMarker: this.sourceMarker } : {},
1590
+ campaignPlanId: null,
1591
+ experiment: experiment2,
1592
+ consentReceiptRefs: this.proof.receipts.map((r) => ({ purpose: r.purpose, receiptId: r.receiptId, consentRevision: r.consentRevision }))
1593
+ });
1594
+ if (kind === "page_view" && resourceRef?.type === "page" && advertisingPurposesPermitted(this.now())) {
1595
+ try {
1596
+ event = parseResonanceMeasurementOccurrence({ ...event, pagePath: navigation.path });
1597
+ } catch {
1598
+ }
1599
+ if (event.pagePath !== void 0) {
1600
+ try {
1601
+ const observed = this.options.readMetaCookieObservation?.(navigation.path);
1602
+ if (observed != null) event = parseResonanceMeasurementOccurrence({ ...event, metaCookieObservation: observed });
1603
+ } catch {
1604
+ }
1605
+ }
1606
+ }
1607
+ if (navigation !== this.navigation || proof !== this.proof || config !== this.config || epoch !== this.deliveryEpoch) return;
1608
+ await this.deliver(event, epoch, frame);
1609
+ }
1610
+ async deliver(event, epoch, frame) {
1611
+ const allowed = () => !this.stopped && epoch === this.deliveryEpoch && (!frame || this.renderedFrame === frame && frame.navigation === this.navigation) && (event.kind === "experiment_exposure" ? experimentMeasurementPermitted(this.now()) : advertisingPurposesPermitted(this.now()));
1612
+ if (!allowed() || !this.proof) return;
1613
+ try {
1614
+ const result = await this.options.transport.request("/occurrences", { method: "POST", cache: "no-store", headers: { "Content-Type": "application/json", [RESONANCE_SUBJECT_HEADER]: this.proof.subjectProof }, body: JSON.stringify(event) }, allowed);
1615
+ if (!allowed()) return;
1616
+ if (result.status !== 200) throw new Error("occurrence_not_accepted");
1617
+ const receipt2 = parseResonanceOccurrenceResponse(payload(result.body));
1618
+ if (receipt2.eventId !== event.eventId) throw new Error("occurrence_identity_mismatch");
1619
+ this.retry.delete(event.eventId);
1620
+ if (event.kind === "experiment_exposure" && receipt2.exposureId && this.proof && this.navigation?.id === event.navigationId && (!frame || this.renderedFrame === frame && frame.navigation === this.navigation)) {
1621
+ this.acceptedExposure = {
1622
+ id: receipt2.exposureId,
1623
+ namespace: this.proof.receipts[0].visitorNamespace,
1624
+ consentRevision: this.proof.currentConsentRevision,
1625
+ configRevision: this.config.configRevision,
1626
+ policyRevision: this.config.policyRevision,
1627
+ navigation: this.navigation,
1628
+ frame: frame ?? null
1629
+ };
1630
+ }
1631
+ if (!receipt2.replayed) this.options.onOccurrence?.(event);
1632
+ } catch {
1633
+ if (allowed() && this.retry.size < 50) this.retry.set(event.eventId, { event, epoch, frame });
1634
+ }
1635
+ }
1636
+ /** Explicit network recovery only; identical occurrence ID, never old preference/scope epochs. */
1637
+ retryPending() {
1638
+ for (const { event, epoch, frame } of this.retry.values()) void this.deliver(event, epoch, frame);
1639
+ }
1640
+ /** Optional server receipt reference, never a claim that a form converted. */
1641
+ formExposureId() {
1642
+ const exposure2 = this.acceptedExposure, proof = this.proof, config = this.config;
1643
+ return exposure2 && proof && config && exposure2.navigation === this.navigation && (!exposure2.frame || exposure2.frame === this.renderedFrame && exposure2.frame.active) && exposure2.namespace === proof.receipts[0].visitorNamespace && exposure2.consentRevision === proof.currentConsentRevision && exposure2.configRevision === config.configRevision && exposure2.policyRevision === config.policyRevision ? exposure2.id : null;
1644
+ }
1645
+ stop() {
1646
+ if (this.stopped) return;
1647
+ this.stopped = true;
1648
+ this.configEpoch++;
1649
+ this.readEpoch++;
1650
+ this.unsubscribe?.();
1651
+ this.unsubscribe = null;
1652
+ if (this.expiryTimer) clearTimeout(this.expiryTimer);
1653
+ if (this.renewalTimer) clearTimeout(this.renewalTimer);
1654
+ if (this.refreshTimer) clearTimeout(this.refreshTimer);
1655
+ if (this.configExpiryTimer) clearTimeout(this.configExpiryTimer);
1656
+ this.invalidate();
1657
+ this.acceptedExposure = null;
1658
+ this.proof = null;
1659
+ updateMeasurementPolicy({ config: null, cmpValid: false, proof: null, choices: unknownChoices(), preferencePending: true });
1660
+ }
1661
+ };
1662
+
1663
+ // src/resonance/meta-pixel.ts
1664
+ var META_BROWSER_CONTROLS_IMPLEMENTED = [];
1665
+ var META_SDK_URL = "https://connect.facebook.net/en_US/fbevents.js";
1666
+ var ALLOWED_QUERY_KEYS = /* @__PURE__ */ new Set(["fbclid", "sonor_source", "utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"]);
1667
+ var QUERY_VALUE = /^[^\s\u0000-\u001f\u007f-\u009f]{1,256}$/;
1668
+ var SONOR_APPLICATION_HOSTS = /* @__PURE__ */ new Set(["app.sonor.io", "api.sonor.io", "signal.sonor.io"]);
1669
+ var DENIED_PATH_ROOTS = ["/admin", "/account", "/dashboard", "/login", "/signin", "/signup", "/checkout", "/cart", "/portal", "/api", "/preview"];
1670
+ var COOKIES = ["_fbp", "_fbc"];
1671
+ var WITNESS_MAX_AGE_MS = 48 * 36e5;
1672
+ function queryClean(search2) {
1673
+ if (!search2) return true;
1674
+ const entries = [...new URLSearchParams(search2).entries()];
1675
+ const seen = /* @__PURE__ */ new Set();
1676
+ for (const [key, value] of entries) {
1677
+ if (!ALLOWED_QUERY_KEYS.has(key) || seen.has(key) || !QUERY_VALUE.test(value)) return false;
1678
+ seen.add(key);
1679
+ }
1680
+ return true;
1681
+ }
1682
+ function referrerClean(referrer) {
1683
+ if (!referrer) return true;
1684
+ let url;
1685
+ try {
1686
+ url = new URL(referrer);
1687
+ } catch {
1688
+ return false;
1689
+ }
1690
+ if (url.hash || referrer.includes("#") || url.username || url.password) return false;
1691
+ if (url.hostname === window.location.hostname) return !pathDenied(url.pathname) && queryClean(url.search);
1692
+ return !url.search && !referrer.includes("?") && (url.pathname === "/" || url.pathname === "");
1693
+ }
1694
+ function pathDenied(path2) {
1695
+ const lower = path2.toLowerCase();
1696
+ return DENIED_PATH_ROOTS.some((root) => lower === root || lower.startsWith(root + "/"));
1697
+ }
1698
+ function decideMetaPage(config) {
1699
+ if (typeof window === "undefined" || typeof document === "undefined") return { ok: false, blocker: "meta_page_not_eligible" };
1700
+ const location = window.location;
1701
+ let framed = true;
1702
+ try {
1703
+ framed = isFramed();
1704
+ } catch {
1705
+ }
1706
+ if (framed || isPrivatePreview() || isLocalHostname(location.hostname) || SONOR_APPLICATION_HOSTS.has(location.hostname.toLowerCase()) || document.querySelector("[data-sk-authenticated]") || pathDenied(location.pathname)) return { ok: false, blocker: "meta_page_not_eligible" };
1707
+ const pages = config.resourceRules.filter((rule) => rule.path === location.pathname && rule.resource.type === "page");
1708
+ if (pages.length !== 1 || pages[0].metaEligible !== true) return { ok: false, blocker: "meta_page_not_eligible" };
1709
+ if (location.hash || location.href.includes("#") || !queryClean(location.search) || !referrerClean(document.referrer)) return { ok: false, blocker: "meta_url_not_clean" };
1710
+ return { ok: true };
1711
+ }
1712
+ function expireMetaCookies() {
1713
+ const host = window.location.hostname;
1714
+ if (!host || host.includes(":") || /^\d+(\.\d+){3}$/.test(host)) {
1715
+ for (const name of COOKIES) document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
1716
+ return;
1717
+ }
1718
+ const labels = host.split(".");
1719
+ const domains = ["", ...labels.map((_, index) => "." + labels.slice(index).join("."))];
1720
+ for (const name of COOKIES) for (const domain of domains) document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/${domain ? `; domain=${domain}` : ""}`;
1721
+ }
1722
+ var MetaPixelLoader = class {
1723
+ constructor(config, hooks) {
1724
+ this.config = config;
1725
+ this.hooks = hooks;
1726
+ this.generation = 0;
1727
+ this.owned = null;
1728
+ this.granted = false;
1729
+ this.cookiesExpired = false;
1730
+ }
1731
+ destinations() {
1732
+ return this.config.destinations.filter((d) => d.provider === "meta_ads");
1733
+ }
1734
+ /** Configuration-level facts, independent of consent and of the page. */
1735
+ inspect() {
1736
+ const destinations = this.destinations();
1737
+ const witness = destinations.length === 1 ? destinations[0].browserWitness : void 0;
1738
+ const implemented = this.hooks.implementedRevisions ?? META_BROWSER_CONTROLS_IMPLEMENTED;
1739
+ const age = witness ? Date.now() - Date.parse(witness.assertedAt) : Number.NaN;
1740
+ return {
1741
+ destinations: destinations.length,
1742
+ multiple: destinations.length > 1,
1743
+ witnessed: !!witness && implemented.includes(witness.controlsRevision) && witness.policyRevision === this.config.policyRevision && age >= -6e5 && age <= WITNESS_MAX_AGE_MS
1744
+ };
1745
+ }
1746
+ /** Every SDK command re-checks the fence; a withdrawn runtime pushes nothing. */
1747
+ command(...args) {
1748
+ const fbq = this.owned?.fbq;
1749
+ if (!fbq || !this.hooks.allowed()) return false;
1750
+ try {
1751
+ fbq(...args);
1752
+ } catch {
1753
+ }
1754
+ return true;
1755
+ }
1756
+ blockers(decision) {
1757
+ this.hooks.setBlocker("meta_page_not_eligible", !decision.ok && decision.blocker === "meta_page_not_eligible");
1758
+ this.hooks.setBlocker("meta_url_not_clean", !decision.ok && decision.blocker === "meta_url_not_clean");
1759
+ }
1760
+ foreign(w, destination2) {
1761
+ if (w.fbq || w._fbq || document.querySelector('script[src*="connect.facebook.net"][src*="fbevents"]') || document.querySelector('img[src*="facebook.com/tr"]')) return true;
1762
+ for (const noscript of document.querySelectorAll("noscript")) if ((noscript.textContent ?? "").includes("facebook.com/tr") || noscript.innerHTML.includes("facebook.com/tr")) return true;
1763
+ const tagManager = !!w.google_tag_manager || !!document.querySelector('script[src*="googletagmanager.com/gtm.js"]') || w.dataLayer !== void 0 && w.dataLayer !== w.__SONOR_RESONANCE_GOOGLE_V1__?.data;
1764
+ return tagManager && destination2.tagManagerAttested !== true;
1765
+ }
1766
+ update() {
1767
+ const { destinations, multiple, witnessed } = this.inspect();
1768
+ if (typeof window === "undefined" || !destinations) return;
1769
+ if (multiple) {
1770
+ this.hooks.setBlocker("meta_multiple_destinations", true);
1771
+ return;
1772
+ }
1773
+ if (!witnessed) {
1774
+ this.withdraw();
1775
+ return;
1776
+ }
1777
+ if (!this.hooks.allowed()) {
1778
+ this.withdraw();
1779
+ return;
1780
+ }
1781
+ const decision = decideMetaPage(this.config);
1782
+ this.blockers(decision);
1783
+ if (!decision.ok) {
1784
+ this.pause();
1785
+ return;
1786
+ }
1787
+ const w = window, destination2 = this.destinations()[0];
1788
+ const record = w.__SONOR_RESONANCE_META_V1__;
1789
+ if (this.owned || record) return this.resume(w, destination2, record);
1790
+ if (this.foreign(w, destination2)) {
1791
+ this.hooks.setBlocker("meta_duplicate_installation", true);
1792
+ return;
1793
+ }
1794
+ this.install(w, destination2);
1795
+ }
1796
+ /** Single-page navigation: re-evaluate; never re-initialize. */
1797
+ navigate() {
1798
+ this.update();
1799
+ }
1800
+ resume(w, destination2, record) {
1801
+ if (!record) return;
1802
+ if (this.owned && this.owned !== record) return;
1803
+ if (record.scope !== `${this.config.projectId}:${this.config.site}` || record.pixelId !== destination2.pixelId) {
1804
+ this.hooks.setBlocker("meta_scope_change_requires_reload", true);
1805
+ return;
1806
+ }
1807
+ if (w.fbq !== record.fbq) {
1808
+ this.hooks.setBlocker("meta_duplicate_installation", true);
1809
+ return;
1810
+ }
1811
+ if (!this.owned && !record.loaded && !record.failed) {
1812
+ this.hooks.setBlocker("meta_duplicate_installation", true);
1813
+ return;
1814
+ }
1815
+ this.owned = record;
1816
+ if (record.failed) {
1817
+ this.hooks.setBlocker("meta_load_failed", true);
1818
+ return;
1819
+ }
1820
+ if (record.loaded && record.consent === "revoked") this.grant();
1821
+ }
1822
+ install(w, destination2) {
1823
+ const generation = this.generation;
1824
+ const queue = [];
1825
+ const fbq = function(..._args) {
1826
+ if (fbq.callMethod) fbq.callMethod.apply(fbq, arguments);
1827
+ else fbq.queue.push(arguments);
1828
+ };
1829
+ fbq.push = fbq;
1830
+ fbq.loaded = true;
1831
+ fbq.version = "2.0";
1832
+ fbq.queue = queue;
1833
+ const script = document.createElement("script");
1834
+ this.owned = { scope: `${this.config.projectId}:${this.config.site}`, pixelId: destination2.pixelId, fbq, script, loaded: false, failed: false, consent: "revoked" };
1835
+ w.fbq = fbq;
1836
+ w._fbq = fbq;
1837
+ w.__SONOR_RESONANCE_META_V1__ = this.owned;
1838
+ if (!this.command("consent", "revoke") || !this.command("set", "autoConfig", false, destination2.pixelId) || !this.command("init", destination2.pixelId)) {
1839
+ this.discard(w);
1840
+ return;
1841
+ }
1842
+ const owned2 = this.owned;
1843
+ script.async = true;
1844
+ script.src = META_SDK_URL;
1845
+ script.referrerPolicy = "no-referrer";
1846
+ script.onload = () => {
1847
+ owned2.loaded = true;
1848
+ this.afterLoad(generation);
1849
+ };
1850
+ script.onerror = () => {
1851
+ owned2.failed = true;
1852
+ this.hooks.setBlocker("meta_load_failed", true);
1853
+ };
1854
+ if (generation !== this.generation || !this.hooks.allowed()) {
1855
+ this.discard(w);
1856
+ return;
1857
+ }
1858
+ document.head.appendChild(script);
1859
+ }
1860
+ /** The grant is issued live, after the SDK has executed: a grant queued ahead of the script never releases its lock. */
1861
+ afterLoad(generation) {
1862
+ if (generation !== this.generation || !this.hooks.allowed()) return;
1863
+ const decision = decideMetaPage(this.config);
1864
+ this.blockers(decision);
1865
+ if (decision.ok) this.grant();
1866
+ }
1867
+ grant() {
1868
+ if (!this.owned?.loaded || this.owned.consent === "granted") return;
1869
+ if (this.command("consent", "grant")) {
1870
+ this.owned.consent = "granted";
1871
+ this.granted = true;
1872
+ this.cookiesExpired = false;
1873
+ }
1874
+ }
1875
+ /** The page stopped being eligible or clean: pause the SDK without forgetting the installation. */
1876
+ pause() {
1877
+ const owned2 = this.owned;
1878
+ if (!owned2) return;
1879
+ if (!owned2.loaded) {
1880
+ this.discard(window);
1881
+ return;
1882
+ }
1883
+ if (owned2.consent === "granted") {
1884
+ try {
1885
+ owned2.fbq("consent", "revoke");
1886
+ } catch {
1887
+ }
1888
+ owned2.consent = "revoked";
1889
+ }
1890
+ }
1891
+ /** Before the SDK has executed nothing needs retracting: remove everything so a late script finds no queue and does nothing. */
1892
+ discard(w) {
1893
+ const owned2 = this.owned;
1894
+ if (!owned2) return;
1895
+ owned2.script.onload = null;
1896
+ owned2.script.onerror = null;
1897
+ if (owned2.script.isConnected) owned2.script.remove();
1898
+ if (w.fbq === owned2.fbq) delete w.fbq;
1899
+ if (w._fbq === owned2.fbq) delete w._fbq;
1900
+ if (w.__SONOR_RESONANCE_META_V1__ === owned2) delete w.__SONOR_RESONANCE_META_V1__;
1901
+ this.owned = null;
1902
+ }
1903
+ withdraw() {
1904
+ this.generation++;
1905
+ const owned2 = this.owned;
1906
+ if (owned2) {
1907
+ if (!owned2.loaded) this.discard(window);
1908
+ else if (owned2.consent === "granted") {
1909
+ try {
1910
+ owned2.fbq("consent", "revoke");
1911
+ } catch {
1912
+ }
1913
+ owned2.consent = "revoked";
1914
+ }
1915
+ }
1916
+ if ((this.granted || owned2?.loaded) && !this.cookiesExpired) {
1917
+ expireMetaCookies();
1918
+ this.cookiesExpired = true;
1919
+ }
1920
+ this.granted = false;
1921
+ }
1922
+ stop() {
1923
+ this.withdraw();
1924
+ }
1925
+ };
1926
+
1927
+ // src/resonance/providers.ts
1928
+ var denied = { analytics_storage: "denied", ad_storage: "denied", ad_user_data: "denied", ad_personalization: "denied" };
1929
+ var ResonanceProviderRuntime = class {
1930
+ constructor(config, onBlockers, options = {}) {
1931
+ this.config = config;
1932
+ this.onBlockers = onBlockers;
1933
+ this.google = null;
1934
+ this.script = null;
1935
+ this.configured = /* @__PURE__ */ new Set();
1936
+ this.pending = null;
1937
+ this.generation = 0;
1938
+ this.active = false;
1939
+ this.loaded = false;
1940
+ this.finishLoad = null;
1941
+ this.blockers = /* @__PURE__ */ new Set();
1942
+ this.meta = new MetaPixelLoader(config, { allowed: () => this.allowed(), setBlocker: (code, present) => this.setBlocker(code, present), implementedRevisions: options.metaControlsRevisions });
1943
+ const meta2 = this.meta.inspect();
1944
+ if (meta2.destinations && !meta2.witnessed) this.block("meta_browser_collection_unverified");
1945
+ if (meta2.multiple) this.block("meta_multiple_destinations");
1946
+ }
1947
+ block(code) {
1948
+ this.setBlocker(code, true);
1949
+ }
1950
+ setBlocker(code, present) {
1951
+ if (this.blockers.has(code) === present) return;
1952
+ if (present) this.blockers.add(code);
1953
+ else this.blockers.delete(code);
1954
+ this.onBlockers?.([...this.blockers]);
1955
+ }
1956
+ allowed() {
1957
+ const state = measurementPolicySnapshot();
1958
+ return this.active && advertisingPurposesPermitted() && state.config?.projectId === this.config.projectId && state.config.site === this.config.site && state.config.configRevision === this.config.configRevision;
1959
+ }
1960
+ update() {
1961
+ if (!advertisingPurposesPermitted()) {
1962
+ this.withdraw();
1963
+ return;
1964
+ }
1965
+ this.active = true;
1966
+ const meta2 = this.meta.inspect();
1967
+ this.setBlocker("meta_browser_collection_unverified", meta2.destinations > 0 && !meta2.witnessed);
1968
+ this.meta.update();
1969
+ if (!this.pending && !this.google && !this.blockers.has("google_duplicate_installation") && !this.blockers.has("google_scope_change_requires_reload") && !this.blockers.has("google_load_failed")) {
1970
+ this.pending = this.loadGoogle().finally(() => {
1971
+ this.pending = null;
1972
+ });
1973
+ } else if (this.google) {
1974
+ this.applyConsent();
1975
+ if (!this.loaded && this.script && !this.script.isConnected && this.allowed()) document.head.appendChild(this.script);
1976
+ }
1977
+ }
1978
+ applyConsent() {
1979
+ if (!this.allowed() || !this.google) return;
1980
+ this.google("consent", "update", { ...measurementPolicySnapshot().choices });
1981
+ }
1982
+ async loadGoogle() {
1983
+ const destinations = this.config.destinations.filter((d) => d.provider === "google_ads");
1984
+ if (!destinations.length || !this.allowed() || typeof window === "undefined") return;
1985
+ const w = window;
1986
+ const owned2 = w.__SONOR_RESONANCE_GOOGLE_V1__;
1987
+ if (owned2) {
1988
+ const tags = [...new Set(destinations.map((d) => d.tagId))].sort();
1989
+ if (owned2.scope !== `${this.config.projectId}:${this.config.site}` || JSON.stringify(tags) !== JSON.stringify(owned2.tags)) {
1990
+ this.block("google_scope_change_requires_reload");
1991
+ return;
1992
+ }
1993
+ if (w.gtag !== owned2.command || w.dataLayer !== owned2.data) {
1994
+ this.block("google_duplicate_installation");
1995
+ return;
1996
+ }
1997
+ if (owned2.failed) {
1998
+ this.block("google_load_failed");
1999
+ return;
2000
+ }
2001
+ this.google = owned2.command;
2002
+ this.script = owned2.script;
2003
+ this.loaded = owned2.loaded;
2004
+ for (const tag of tags) this.configured.add(tag);
2005
+ this.applyConsent();
2006
+ if (!this.loaded) {
2007
+ const script2 = this.script;
2008
+ let done;
2009
+ const loaded = () => {
2010
+ this.loaded = true;
2011
+ done();
2012
+ };
2013
+ const failed = () => {
2014
+ this.block("google_load_failed");
2015
+ done();
2016
+ };
2017
+ await new Promise((resolve) => {
2018
+ done = resolve;
2019
+ this.finishLoad = resolve;
2020
+ script2.addEventListener("load", loaded, { once: true });
2021
+ script2.addEventListener("error", failed, { once: true });
2022
+ if (!script2.isConnected && this.allowed()) document.head.appendChild(script2);
2023
+ });
2024
+ script2.removeEventListener("load", loaded);
2025
+ script2.removeEventListener("error", failed);
2026
+ }
2027
+ return;
2028
+ }
2029
+ if (w.gtag || w.dataLayer || document.querySelector('script[src*="googletagmanager.com/gtag/js"],script[src*="googletagmanager.com/gtm.js"]')) {
2030
+ this.block("google_duplicate_installation");
2031
+ return;
2032
+ }
2033
+ const generation = this.generation;
2034
+ const data = [];
2035
+ const command2 = function(..._args) {
2036
+ data.push(arguments);
2037
+ };
2038
+ w.dataLayer = data;
2039
+ w.gtag = command2;
2040
+ this.google = command2;
2041
+ command2("consent", "default", denied);
2042
+ command2("set", "ads_data_redaction", true);
2043
+ command2("set", "url_passthrough", false);
2044
+ this.applyConsent();
2045
+ command2("js", /* @__PURE__ */ new Date());
2046
+ for (const d of destinations) {
2047
+ if (this.configured.has(d.tagId)) continue;
2048
+ command2("config", d.tagId, { send_page_view: false, allow_enhanced_conversions: false, page_location: `https://${this.config.site}/`, page_referrer: "", page_title: "" });
2049
+ this.configured.add(d.tagId);
2050
+ }
2051
+ const script = document.createElement("script");
2052
+ this.script = script;
2053
+ script.async = true;
2054
+ script.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(destinations[0].tagId)}`;
2055
+ script.referrerPolicy = "no-referrer";
2056
+ const ownership = { scope: `${this.config.projectId}:${this.config.site}`, tags: [...this.configured].sort(), command: command2, data, script, loaded: false, failed: false };
2057
+ w.__SONOR_RESONANCE_GOOGLE_V1__ = ownership;
2058
+ await new Promise((resolve) => {
2059
+ this.finishLoad = resolve;
2060
+ script.onload = () => {
2061
+ this.loaded = true;
2062
+ ownership.loaded = true;
2063
+ resolve();
2064
+ };
2065
+ script.onerror = () => {
2066
+ ownership.failed = true;
2067
+ this.block("google_load_failed");
2068
+ resolve();
2069
+ };
2070
+ if (generation !== this.generation || !this.allowed()) {
2071
+ resolve();
2072
+ return;
2073
+ }
2074
+ document.head.appendChild(script);
2075
+ });
2076
+ }
2077
+ async occurrence(event) {
2078
+ const generation = this.generation;
2079
+ if (!this.allowed() || event.kind === "experiment_exposure") return;
2080
+ if (this.pending) await this.pending;
2081
+ if (generation !== this.generation || !this.allowed() || !this.google || this.blockers.has("google_load_failed")) return;
2082
+ const rule = event.resourceRef ? this.config.resourceRules.find((r) => r.resource.id === event.resourceRef?.id && r.resource.revision === event.resourceRef.revision && r.resource.type === event.resourceRef.type) : null;
2083
+ for (const tag of this.configured) this.google("event", event.kind === "page_view" ? "page_view" : "sonor_content_interest", {
2084
+ send_to: tag,
2085
+ event_id: event.eventId,
2086
+ page_location: `https://${this.config.site}${rule?.path ?? "/"}`,
2087
+ page_referrer: "",
2088
+ page_title: "",
2089
+ ...rule ? { sonor_resource_id: rule.resource.id, sonor_interest_key: rule.interestKey } : {}
2090
+ });
2091
+ }
2092
+ /** Single-page navigation: Meta re-evaluates the new page's eligibility and URL hygiene. */
2093
+ navigate() {
2094
+ if (this.active) this.meta.navigate();
2095
+ }
2096
+ withdraw() {
2097
+ this.meta.withdraw();
2098
+ if (!this.active) return;
2099
+ this.active = false;
2100
+ this.generation++;
2101
+ this.google?.("consent", "update", denied);
2102
+ if (!this.loaded) this.script?.remove();
2103
+ this.finishLoad?.();
2104
+ this.finishLoad = null;
2105
+ }
2106
+ stop() {
2107
+ this.withdraw();
2108
+ }
2109
+ };
2110
+
2111
+ // src/resonance/runtime-core.ts
2112
+ function currentSourceMarker() {
2113
+ const values = new URLSearchParams(window.location.search).getAll(RESONANCE_SOURCE_MARKER_PARAMETER);
2114
+ if (values.length !== 1) return null;
2115
+ try {
2116
+ return parseResonanceSourceMarker(values[0]);
2117
+ } catch {
2118
+ return null;
2119
+ }
2120
+ }
2121
+ function mountResonanceRuntime({ site: site2, apiUrl, apiKey }) {
2122
+ if (!site2 || !resolveAnalyticsTarget({ apiUrl, apiKey })) return;
2123
+ let providers = null;
2124
+ let providerRevision = "";
2125
+ const runtime = new ResonanceController({
2126
+ site: site2,
2127
+ readClickIdentifiers(pathname) {
2128
+ if (typeof window === "undefined" || window.location.pathname !== pathname) return null;
2129
+ const parameters = new URLSearchParams(window.location.search);
2130
+ const entries = ["gclid", "gbraid", "wbraid"].flatMap((key) => parameters.getAll(key).map((value) => [key, value]));
2131
+ if (entries.length !== 1) return null;
2132
+ try {
2133
+ return parseResonanceClickIdentifiers(Object.fromEntries(entries));
2134
+ } catch {
2135
+ return null;
2136
+ }
2137
+ },
2138
+ readMetaCookieObservation(pathname) {
2139
+ if (typeof window === "undefined" || typeof document === "undefined" || window.location.pathname !== pathname) return null;
2140
+ const pages = measurementPolicySnapshot().config?.resourceRules.filter((rule) => rule.path === pathname && rule.resource.type === "page") ?? [];
2141
+ if (pages.length !== 1 || pages[0].metaEligible !== true) return null;
2142
+ try {
2143
+ const cookies = document.cookie.split(";").map((part) => {
2144
+ const separator = part.indexOf("=");
2145
+ return separator < 0 ? null : [part.slice(0, separator).trim(), part.slice(separator + 1)];
2146
+ });
2147
+ if (window.location.pathname !== pathname) return null;
2148
+ const observed = {};
2149
+ for (const field3 of RESONANCE_WEBSITE_VISIT_PROFILE.matchingFields) {
2150
+ const matches = cookies.filter((cookie) => cookie?.[0] === `_${field3}`);
2151
+ if (matches.length === 1 && matches[0][1]) observed[field3] = matches[0][1];
2152
+ }
2153
+ return Object.keys(observed).length ? { source: RESONANCE_WEBSITE_VISIT_PROFILE.source, ...observed } : null;
2154
+ } catch {
2155
+ return null;
2156
+ }
2157
+ },
2158
+ transport: { async request(path2, init, stillAllowed) {
2159
+ const response = await sonorFetch(`${apiUrl.replace(/\/+$/, "")}/api/public/resonance/measurement${path2}`, init, {
2160
+ apiKey,
2161
+ retries: 0,
2162
+ shouldSend: () => !!resolveAnalyticsTarget({ apiUrl, apiKey }) && (stillAllowed?.() ?? true)
2163
+ });
2164
+ return { status: response.status, body: await response.json().catch(() => null) };
2165
+ } },
2166
+ onOccurrence(event) {
2167
+ void providers?.occurrence(event);
2168
+ }
2169
+ });
2170
+ const unregister = registerResonanceController(runtime);
2171
+ if (!unregister) return;
2172
+ const updateProviders = () => {
2173
+ const config = measurementPolicySnapshot().config;
2174
+ const revision2 = config ? `${config.projectId}:${config.site}:${config.configRevision}:${config.policyRevision}` : "";
2175
+ if (revision2 !== providerRevision) {
2176
+ providers?.stop();
2177
+ providers = null;
2178
+ providerRevision = revision2;
2179
+ }
2180
+ if (config && !providers) providers = new ResonanceProviderRuntime(config, setProviderBlockers);
2181
+ providers?.update();
2182
+ setProviderBlockers(providers ? [...providers.blockers] : []);
2183
+ };
2184
+ const unpolicy = subscribeMeasurementPolicy(updateProviders);
2185
+ const adapter = () => runtime.setAdapter(getConsentAdapter());
2186
+ const unadapter = subscribeConsentAdapter(adapter);
2187
+ adapter();
2188
+ void runtime.start();
2189
+ runtime.navigate(window.location.pathname, currentSourceMarker());
2190
+ const online = () => {
2191
+ void runtime.recover();
2192
+ };
2193
+ window.addEventListener("online", online);
2194
+ const navigation = () => {
2195
+ runtime.navigate(window.location.pathname, currentSourceMarker());
2196
+ providers?.navigate();
2197
+ };
2198
+ window.addEventListener("popstate", navigation);
2199
+ return { navigate(pathname) {
2200
+ runtime.navigate(pathname, currentSourceMarker());
2201
+ providers?.navigate();
2202
+ }, stop() {
2203
+ window.removeEventListener("online", online);
2204
+ window.removeEventListener("popstate", navigation);
2205
+ unadapter();
2206
+ unpolicy();
2207
+ unregister();
2208
+ runtime.stop();
2209
+ providers?.stop();
2210
+ } };
2211
+ }
2212
+
2213
+ export { mountResonanceRuntime };