@sonordev/site-kit 5.3.1 → 5.4.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 (336) hide show
  1. package/README.md +17 -0
  2. package/agent-manifest.json +5 -4
  3. package/dist/AnalyticsProvider-2XIOMRCN.mjs +11 -0
  4. package/dist/{AnalyticsProvider-NA7EMJCA.mjs.map → AnalyticsProvider-2XIOMRCN.mjs.map} +1 -1
  5. package/dist/AnalyticsProvider-Y2P2BPOI.js +32 -0
  6. package/dist/{AnalyticsProvider-XE3ADUUH.js.map → AnalyticsProvider-Y2P2BPOI.js.map} +1 -1
  7. package/dist/{BlogViewTracker-DVJHZ7CM.js → BlogViewTracker-6EGEDPP3.js} +6 -6
  8. package/dist/{BlogViewTracker-DVJHZ7CM.js.map → BlogViewTracker-6EGEDPP3.js.map} +1 -1
  9. package/dist/{BlogViewTracker-MKJYTTJQ.mjs → BlogViewTracker-R4YQ76WP.mjs} +5 -5
  10. package/dist/{BlogViewTracker-MKJYTTJQ.mjs.map → BlogViewTracker-R4YQ76WP.mjs.map} +1 -1
  11. package/dist/ChatWidget-2OFWPCWF.js +20 -0
  12. package/dist/{ChatWidget-BZ3KOSXG.js.map → ChatWidget-2OFWPCWF.js.map} +1 -1
  13. package/dist/ChatWidget-4AOGHYXQ.mjs +11 -0
  14. package/dist/{ChatWidget-BD4F4XLD.mjs.map → ChatWidget-4AOGHYXQ.mjs.map} +1 -1
  15. package/dist/EngageWidget-SFO2XRNG.mjs +9 -0
  16. package/dist/{EngageWidget-L7B6PCM3.mjs.map → EngageWidget-SFO2XRNG.mjs.map} +1 -1
  17. package/dist/EngageWidget-T4KAAD3G.js +18 -0
  18. package/dist/{EngageWidget-JPP6EDCJ.js.map → EngageWidget-T4KAAD3G.js.map} +1 -1
  19. package/dist/{FileField-7WPRONDS.mjs → FileField-7DF2SLPF.mjs} +5 -5
  20. package/dist/{FileField-7WPRONDS.mjs.map → FileField-7DF2SLPF.mjs.map} +1 -1
  21. package/dist/{FileField-GHT7444Y.js → FileField-CE2XNBBG.js} +6 -6
  22. package/dist/{FileField-GHT7444Y.js.map → FileField-CE2XNBBG.js.map} +1 -1
  23. package/dist/{FormSpotlight-WZVS6HHR.mjs → FormSpotlight-IRLKSZGV.mjs} +3 -3
  24. package/dist/{FormSpotlight-WZVS6HHR.mjs.map → FormSpotlight-IRLKSZGV.mjs.map} +1 -1
  25. package/dist/{FormSpotlight-BB2IB7IX.js → FormSpotlight-ZD3WR5BC.js} +15 -15
  26. package/dist/{FormSpotlight-BB2IB7IX.js.map → FormSpotlight-ZD3WR5BC.js.map} +1 -1
  27. package/dist/{FormStage-Y3NHHMV2.mjs → FormStage-2OSRNQJL.mjs} +3 -3
  28. package/dist/{FormStage-Y3NHHMV2.mjs.map → FormStage-2OSRNQJL.mjs.map} +1 -1
  29. package/dist/{FormStage-FLBLXENZ.js → FormStage-OSPXOUQE.js} +13 -13
  30. package/dist/{FormStage-FLBLXENZ.js.map → FormStage-OSPXOUQE.js.map} +1 -1
  31. package/dist/ManagedForm-5G37EDJT.mjs +14 -0
  32. package/dist/{ManagedForm-AJM6D4SK.mjs.map → ManagedForm-5G37EDJT.mjs.map} +1 -1
  33. package/dist/ManagedForm-JCHGBBQW.js +23 -0
  34. package/dist/{ManagedForm-2OPCHDNJ.js.map → ManagedForm-JCHGBBQW.js.map} +1 -1
  35. package/dist/{ManagedNewsletterForm-L2ZHJ7F4.js → ManagedNewsletterForm-4LVJZROZ.js} +8 -8
  36. package/dist/{ManagedNewsletterForm-L2ZHJ7F4.js.map → ManagedNewsletterForm-4LVJZROZ.js.map} +1 -1
  37. package/dist/{ManagedNewsletterForm-MJI2JXIB.mjs → ManagedNewsletterForm-QJSVH23S.mjs} +7 -7
  38. package/dist/{ManagedNewsletterForm-MJI2JXIB.mjs.map → ManagedNewsletterForm-QJSVH23S.mjs.map} +1 -1
  39. package/dist/{SignalCore-23YDDSWO.mjs → SignalCore-L6Z3YTCF.mjs} +5 -5
  40. package/dist/{SignalCore-23YDDSWO.mjs.map → SignalCore-L6Z3YTCF.mjs.map} +1 -1
  41. package/dist/{SignalCore-G2VM7NUP.js → SignalCore-NQ4YEOI7.js} +11 -11
  42. package/dist/{SignalCore-G2VM7NUP.js.map → SignalCore-NQ4YEOI7.js.map} +1 -1
  43. package/dist/SitemapSync-3LUZWBTB.js +18 -0
  44. package/dist/{SitemapSync-T2N3TCWF.js.map → SitemapSync-3LUZWBTB.js.map} +1 -1
  45. package/dist/SitemapSync-MYSTLJOM.mjs +9 -0
  46. package/dist/{SitemapSync-YLWMU6J3.mjs.map → SitemapSync-MYSTLJOM.mjs.map} +1 -1
  47. package/dist/SitemapSync.d.ts.map +1 -1
  48. package/dist/analytics/AnalyticsProvider.d.ts +11 -1
  49. package/dist/analytics/AnalyticsProvider.d.ts.map +1 -1
  50. package/dist/analytics/WebVitals.d.ts +7 -1
  51. package/dist/analytics/WebVitals.d.ts.map +1 -1
  52. package/dist/analytics/index.d.ts +1 -0
  53. package/dist/analytics/index.d.ts.map +1 -1
  54. package/dist/analytics/index.js +25 -12
  55. package/dist/analytics/index.mjs +6 -5
  56. package/dist/analytics/send-gate.d.ts +119 -0
  57. package/dist/analytics/send-gate.d.ts.map +1 -0
  58. package/dist/analytics/send-gate.test.d.ts +20 -0
  59. package/dist/analytics/send-gate.test.d.ts.map +1 -0
  60. package/dist/blog/AuthorPage.d.ts +1 -1
  61. package/dist/blog/AuthorPage.d.ts.map +1 -1
  62. package/dist/blog/BlogLayout.d.ts +3 -1
  63. package/dist/blog/BlogLayout.d.ts.map +1 -1
  64. package/dist/blog/BlogList.d.ts +3 -1
  65. package/dist/blog/BlogList.d.ts.map +1 -1
  66. package/dist/blog/BlogPost.d.ts +3 -1
  67. package/dist/blog/BlogPost.d.ts.map +1 -1
  68. package/dist/blog/BlogSidebar.d.ts +3 -1
  69. package/dist/blog/BlogSidebar.d.ts.map +1 -1
  70. package/dist/blog/ClusterLandingPage.d.ts +3 -1
  71. package/dist/blog/ClusterLandingPage.d.ts.map +1 -1
  72. package/dist/blog/ClusterNavigation.d.ts +3 -1
  73. package/dist/blog/ClusterNavigation.d.ts.map +1 -1
  74. package/dist/blog/RelatedPosts.d.ts +4 -1
  75. package/dist/blog/RelatedPosts.d.ts.map +1 -1
  76. package/dist/blog/artwork-components.test.d.ts +2 -0
  77. package/dist/blog/artwork-components.test.d.ts.map +1 -0
  78. package/dist/blog/artwork.d.ts +14 -0
  79. package/dist/blog/artwork.d.ts.map +1 -0
  80. package/dist/blog/artwork.test.d.ts +2 -0
  81. package/dist/blog/artwork.test.d.ts.map +1 -0
  82. package/dist/blog/blog-styles.d.ts.map +1 -1
  83. package/dist/blog/blog-tables.d.ts +13 -0
  84. package/dist/blog/blog-tables.d.ts.map +1 -0
  85. package/dist/blog/blog-tables.test.d.ts +2 -0
  86. package/dist/blog/blog-tables.test.d.ts.map +1 -0
  87. package/dist/blog/index.d.ts +3 -0
  88. package/dist/blog/index.d.ts.map +1 -1
  89. package/dist/blog/index.js +52 -30
  90. package/dist/blog/index.js.map +1 -1
  91. package/dist/blog/index.mjs +15 -12
  92. package/dist/blog/index.mjs.map +1 -1
  93. package/dist/blog/publication-render.test.d.ts +2 -0
  94. package/dist/blog/publication-render.test.d.ts.map +1 -0
  95. package/dist/blog/publication-routes.test.d.ts +2 -0
  96. package/dist/blog/publication-routes.test.d.ts.map +1 -0
  97. package/dist/blog/routes.d.ts +40 -0
  98. package/dist/blog/routes.d.ts.map +1 -0
  99. package/dist/blog/server-core.d.ts +28 -21
  100. package/dist/blog/server-core.d.ts.map +1 -1
  101. package/dist/blog/server-ui.d.ts +4 -0
  102. package/dist/blog/server-ui.d.ts.map +1 -1
  103. package/dist/blog/server-ui.js +49 -28
  104. package/dist/blog/server-ui.js.map +1 -1
  105. package/dist/blog/server-ui.mjs +9 -15
  106. package/dist/blog/server-ui.mjs.map +1 -1
  107. package/dist/blog/server.d.ts +28 -21
  108. package/dist/blog/server.d.ts.map +1 -1
  109. package/dist/blog/server.js +71 -42
  110. package/dist/blog/server.mjs +3 -2
  111. package/dist/blog/types.d.ts +11 -1
  112. package/dist/blog/types.d.ts.map +1 -1
  113. package/dist/blog-styles-6EY3BS5Y.js +595 -0
  114. package/dist/blog-styles-6EY3BS5Y.js.map +1 -0
  115. package/dist/blog-styles-7AKRUMV7.mjs +592 -0
  116. package/dist/blog-styles-7AKRUMV7.mjs.map +1 -0
  117. package/dist/blog-tables-DWDEWA3S.js +17 -0
  118. package/dist/blog-tables-DWDEWA3S.js.map +1 -0
  119. package/dist/blog-tables-QGIHOESZ.mjs +4 -0
  120. package/dist/blog-tables-QGIHOESZ.mjs.map +1 -0
  121. package/dist/{chunk-KP56C66Z.js → chunk-2KIGFOPM.js} +126 -188
  122. package/dist/chunk-2KIGFOPM.js.map +1 -0
  123. package/dist/{chunk-EDZWNCSE.js → chunk-3EPMDPWI.js} +2 -2
  124. package/dist/{chunk-EDZWNCSE.js.map → chunk-3EPMDPWI.js.map} +1 -1
  125. package/dist/chunk-4B4S56WJ.mjs +110 -0
  126. package/dist/chunk-4B4S56WJ.mjs.map +1 -0
  127. package/dist/{chunk-47566YF4.js → chunk-4WQB2ORG.js} +14 -14
  128. package/dist/{chunk-47566YF4.js.map → chunk-4WQB2ORG.js.map} +1 -1
  129. package/dist/{chunk-5TGSM5EQ.js → chunk-5737U6DI.js} +14 -14
  130. package/dist/{chunk-5TGSM5EQ.js.map → chunk-5737U6DI.js.map} +1 -1
  131. package/dist/{chunk-DN6A6KXH.mjs → chunk-6CUBSQMD.mjs} +3 -3
  132. package/dist/{chunk-DN6A6KXH.mjs.map → chunk-6CUBSQMD.mjs.map} +1 -1
  133. package/dist/{chunk-JBGRXICL.js → chunk-746RKQHA.js} +75 -70
  134. package/dist/chunk-746RKQHA.js.map +1 -0
  135. package/dist/{chunk-GTK3CGWF.js → chunk-7IPSQ5JR.js} +6 -6
  136. package/dist/{chunk-GTK3CGWF.js.map → chunk-7IPSQ5JR.js.map} +1 -1
  137. package/dist/{chunk-4S7BHFGT.js → chunk-7PLH3H3W.js} +3 -3
  138. package/dist/{chunk-4S7BHFGT.js.map → chunk-7PLH3H3W.js.map} +1 -1
  139. package/dist/chunk-7U7C6XRI.mjs +6 -0
  140. package/dist/{chunk-HRQ4367C.mjs.map → chunk-7U7C6XRI.mjs.map} +1 -1
  141. package/dist/{chunk-X4PL7R63.mjs → chunk-7Y2UPFSY.mjs} +3 -3
  142. package/dist/{chunk-X4PL7R63.mjs.map → chunk-7Y2UPFSY.mjs.map} +1 -1
  143. package/dist/{chunk-XR47TOGW.mjs → chunk-A2VLEORI.mjs} +3 -3
  144. package/dist/{chunk-XR47TOGW.mjs.map → chunk-A2VLEORI.mjs.map} +1 -1
  145. package/dist/{chunk-PA5ZVCW2.mjs → chunk-ACTLQ2RH.mjs} +3 -3
  146. package/dist/{chunk-PA5ZVCW2.mjs.map → chunk-ACTLQ2RH.mjs.map} +1 -1
  147. package/dist/{chunk-VLR2UYO4.js → chunk-AWFZIXCT.js} +12 -11
  148. package/dist/chunk-AWFZIXCT.js.map +1 -0
  149. package/dist/{chunk-COAVBUMS.mjs → chunk-B36PTS7B.mjs} +413 -94
  150. package/dist/chunk-B36PTS7B.mjs.map +1 -0
  151. package/dist/{chunk-LFJRMY4V.js → chunk-DPDV5Z4G.js} +12 -12
  152. package/dist/{chunk-LFJRMY4V.js.map → chunk-DPDV5Z4G.js.map} +1 -1
  153. package/dist/{chunk-W47J6ZZG.mjs → chunk-DQM5GR2P.mjs} +4 -4
  154. package/dist/{chunk-W47J6ZZG.mjs.map → chunk-DQM5GR2P.mjs.map} +1 -1
  155. package/dist/{chunk-VGX6G3TA.mjs → chunk-EBMPXFOP.mjs} +3 -3
  156. package/dist/{chunk-VGX6G3TA.mjs.map → chunk-EBMPXFOP.mjs.map} +1 -1
  157. package/dist/{chunk-DHXQBTXE.mjs → chunk-EEQYKMGH.mjs} +75 -70
  158. package/dist/chunk-EEQYKMGH.mjs.map +1 -0
  159. package/dist/{chunk-77HT6TYK.mjs → chunk-G5I3XEQS.mjs} +3 -3
  160. package/dist/{chunk-77HT6TYK.mjs.map → chunk-G5I3XEQS.mjs.map} +1 -1
  161. package/dist/chunk-GHECYDSZ.mjs +134 -0
  162. package/dist/chunk-GHECYDSZ.mjs.map +1 -0
  163. package/dist/{chunk-UJOEN4X6.js → chunk-GQ6CUOLA.js} +15 -15
  164. package/dist/{chunk-UJOEN4X6.js.map → chunk-GQ6CUOLA.js.map} +1 -1
  165. package/dist/{chunk-DTX4R45S.js → chunk-HBMF3M5F.js} +4 -4
  166. package/dist/{chunk-DTX4R45S.js.map → chunk-HBMF3M5F.js.map} +1 -1
  167. package/dist/{chunk-NW5CBNRU.mjs → chunk-HDDV63XJ.mjs} +3 -3
  168. package/dist/{chunk-NW5CBNRU.mjs.map → chunk-HDDV63XJ.mjs.map} +1 -1
  169. package/dist/{chunk-DBJRJ2Q2.mjs → chunk-HH5KGXTK.mjs} +4 -4
  170. package/dist/{chunk-DBJRJ2Q2.mjs.map → chunk-HH5KGXTK.mjs.map} +1 -1
  171. package/dist/{chunk-AKQA5ZYZ.mjs → chunk-I4PDSDVE.mjs} +3 -3
  172. package/dist/{chunk-AKQA5ZYZ.mjs.map → chunk-I4PDSDVE.mjs.map} +1 -1
  173. package/dist/{chunk-2MPOO6QJ.js → chunk-ITFZZ6YT.js} +3 -3
  174. package/dist/{chunk-2MPOO6QJ.js.map → chunk-ITFZZ6YT.js.map} +1 -1
  175. package/dist/{chunk-WZZYTFDV.mjs → chunk-JJ26JKBJ.mjs} +8 -3
  176. package/dist/chunk-JJ26JKBJ.mjs.map +1 -0
  177. package/dist/{chunk-Z33JXJTJ.js → chunk-KMGGAL2X.js} +2 -2
  178. package/dist/{chunk-Z33JXJTJ.js.map → chunk-KMGGAL2X.js.map} +1 -1
  179. package/dist/{chunk-4IMZ45YA.mjs → chunk-KQ3VAOAF.mjs} +4 -4
  180. package/dist/{chunk-4IMZ45YA.mjs.map → chunk-KQ3VAOAF.mjs.map} +1 -1
  181. package/dist/{chunk-GPH3VVLP.js → chunk-LBS4PRC7.js} +3 -3
  182. package/dist/{chunk-GPH3VVLP.js.map → chunk-LBS4PRC7.js.map} +1 -1
  183. package/dist/chunk-LC7AM4J5.js +118 -0
  184. package/dist/chunk-LC7AM4J5.js.map +1 -0
  185. package/dist/{chunk-5HMN3FXL.js → chunk-LF4L3ZP5.js} +8 -8
  186. package/dist/{chunk-5HMN3FXL.js.map → chunk-LF4L3ZP5.js.map} +1 -1
  187. package/dist/{chunk-LHD45PGP.js → chunk-LHYVFWHL.js} +8 -8
  188. package/dist/{chunk-LHD45PGP.js.map → chunk-LHYVFWHL.js.map} +1 -1
  189. package/dist/{chunk-I72AUITZ.mjs → chunk-LIYSLNEK.mjs} +96 -644
  190. package/dist/chunk-LIYSLNEK.mjs.map +1 -0
  191. package/dist/{chunk-HIV5YK5G.js → chunk-LUYUFILE.js} +4 -4
  192. package/dist/{chunk-HIV5YK5G.js.map → chunk-LUYUFILE.js.map} +1 -1
  193. package/dist/{chunk-4HL6CAZ7.mjs → chunk-MIFP6QEQ.mjs} +4 -4
  194. package/dist/{chunk-4HL6CAZ7.mjs.map → chunk-MIFP6QEQ.mjs.map} +1 -1
  195. package/dist/{chunk-SI7R3IJX.mjs → chunk-MTANHZKJ.mjs} +7 -7
  196. package/dist/{chunk-SI7R3IJX.mjs.map → chunk-MTANHZKJ.mjs.map} +1 -1
  197. package/dist/{chunk-R4PFWVGZ.mjs → chunk-N6AVWMIB.mjs} +4 -4
  198. package/dist/{chunk-R4PFWVGZ.mjs.map → chunk-N6AVWMIB.mjs.map} +1 -1
  199. package/dist/{chunk-WD57YWY5.mjs → chunk-N6X76QUF.mjs} +3 -3
  200. package/dist/{chunk-WD57YWY5.mjs.map → chunk-N6X76QUF.mjs.map} +1 -1
  201. package/dist/{chunk-7UPPYERC.mjs → chunk-NUH245XK.mjs} +4 -4
  202. package/dist/{chunk-7UPPYERC.mjs.map → chunk-NUH245XK.mjs.map} +1 -1
  203. package/dist/chunk-NVSWTDWU.mjs +25 -0
  204. package/dist/chunk-NVSWTDWU.mjs.map +1 -0
  205. package/dist/chunk-OL2QBWAU.js +29 -0
  206. package/dist/chunk-OL2QBWAU.js.map +1 -0
  207. package/dist/{chunk-UYR22S67.js → chunk-OXZYP32B.js} +7 -7
  208. package/dist/{chunk-UYR22S67.js.map → chunk-OXZYP32B.js.map} +1 -1
  209. package/dist/{chunk-QG7WHAGD.js → chunk-QWLL3B6U.js} +15 -15
  210. package/dist/{chunk-QG7WHAGD.js.map → chunk-QWLL3B6U.js.map} +1 -1
  211. package/dist/{chunk-26JXNW7B.mjs → chunk-RO55G3OA.mjs} +2 -2
  212. package/dist/{chunk-26JXNW7B.mjs.map → chunk-RO55G3OA.mjs.map} +1 -1
  213. package/dist/{chunk-FH4LLJFL.mjs → chunk-SJXGIUHP.mjs} +9 -8
  214. package/dist/chunk-SJXGIUHP.mjs.map +1 -0
  215. package/dist/{chunk-4TU246Q4.mjs → chunk-SZHKNGNR.mjs} +2 -2
  216. package/dist/{chunk-4TU246Q4.mjs.map → chunk-SZHKNGNR.mjs.map} +1 -1
  217. package/dist/{chunk-GQTSQR63.js → chunk-U53KN4SA.js} +10 -5
  218. package/dist/chunk-U53KN4SA.js.map +1 -0
  219. package/dist/{chunk-AJBJH3BA.mjs → chunk-UOLY4NXK.mjs} +127 -189
  220. package/dist/chunk-UOLY4NXK.mjs.map +1 -0
  221. package/dist/{chunk-AVPMSJCQ.js → chunk-URVGBWCP.js} +4 -4
  222. package/dist/{chunk-AVPMSJCQ.js.map → chunk-URVGBWCP.js.map} +1 -1
  223. package/dist/chunk-UTOHMK52.js +8 -0
  224. package/dist/{chunk-TMQKZ4DO.js.map → chunk-UTOHMK52.js.map} +1 -1
  225. package/dist/{chunk-SHZXSIRH.js → chunk-VA36CJRV.js} +4 -4
  226. package/dist/{chunk-SHZXSIRH.js.map → chunk-VA36CJRV.js.map} +1 -1
  227. package/dist/{chunk-ZMFJXFUY.js → chunk-VWDT7H6L.js} +96 -644
  228. package/dist/chunk-VWDT7H6L.js.map +1 -0
  229. package/dist/{chunk-2VQ5YXOD.js → chunk-WSUQ76LN.js} +18 -18
  230. package/dist/{chunk-2VQ5YXOD.js.map → chunk-WSUQ76LN.js.map} +1 -1
  231. package/dist/{chunk-GM5E6R5W.mjs → chunk-XWF7QPIP.mjs} +3 -3
  232. package/dist/{chunk-GM5E6R5W.mjs.map → chunk-XWF7QPIP.mjs.map} +1 -1
  233. package/dist/chunk-YQTE32QK.js +137 -0
  234. package/dist/chunk-YQTE32QK.js.map +1 -0
  235. package/dist/{chunk-ISKJLXB4.mjs → chunk-ZDFVYSKK.mjs} +4 -4
  236. package/dist/{chunk-ISKJLXB4.mjs.map → chunk-ZDFVYSKK.mjs.map} +1 -1
  237. package/dist/{chunk-APL44ZVK.js → chunk-ZLPXFV5Y.js} +5 -5
  238. package/dist/{chunk-APL44ZVK.js.map → chunk-ZLPXFV5Y.js.map} +1 -1
  239. package/dist/{chunk-TAIGHH5J.js → chunk-ZY5AVC4P.js} +418 -98
  240. package/dist/chunk-ZY5AVC4P.js.map +1 -0
  241. package/dist/cli/index.js +3 -3
  242. package/dist/cli/index.mjs +1 -1
  243. package/dist/client/index.js +4 -4
  244. package/dist/client/index.mjs +3 -3
  245. package/dist/commerce/index.js +58 -57
  246. package/dist/commerce/index.mjs +7 -6
  247. package/dist/engage/index.js +10 -10
  248. package/dist/engage/index.mjs +7 -7
  249. package/dist/fleet/index.js +8 -8
  250. package/dist/fleet/index.mjs +4 -4
  251. package/dist/forms/index.js +16 -16
  252. package/dist/forms/index.mjs +9 -9
  253. package/dist/forms/server.js +10 -10
  254. package/dist/forms/server.mjs +8 -8
  255. package/dist/forms/static.js +7 -7
  256. package/dist/forms/static.mjs +6 -6
  257. package/dist/images/index.js +11 -11
  258. package/dist/images/index.mjs +4 -4
  259. package/dist/index.js +65 -64
  260. package/dist/index.js.map +1 -1
  261. package/dist/index.mjs +11 -10
  262. package/dist/index.mjs.map +1 -1
  263. package/dist/layout/SiteKitClientProviders.d.ts.map +1 -1
  264. package/dist/layout/client.d.ts.map +1 -1
  265. package/dist/layout/client.js +8 -8
  266. package/dist/layout/client.mjs +7 -7
  267. package/dist/layout/index.js +10 -10
  268. package/dist/layout/index.mjs +8 -8
  269. package/dist/layout/types.d.ts +16 -0
  270. package/dist/layout/types.d.ts.map +1 -1
  271. package/dist/llms/index.js +12 -12
  272. package/dist/llms/index.mjs +2 -2
  273. package/dist/maps/index.js +5 -5
  274. package/dist/maps/index.mjs +3 -3
  275. package/dist/search/index.js +4 -4
  276. package/dist/search/index.mjs +3 -3
  277. package/dist/seo/client.js +6 -5
  278. package/dist/seo/client.mjs +5 -4
  279. package/dist/seo/index.js +6 -5
  280. package/dist/seo/index.js.map +1 -1
  281. package/dist/seo/index.mjs +5 -4
  282. package/dist/seo/index.mjs.map +1 -1
  283. package/dist/server/index.js +3 -3
  284. package/dist/server/index.mjs +2 -2
  285. package/dist/shared/frame.d.ts +71 -0
  286. package/dist/shared/frame.d.ts.map +1 -0
  287. package/dist/shared/version.d.ts +1 -1
  288. package/dist/signal/index.js +11 -11
  289. package/dist/signal/index.mjs +2 -2
  290. package/dist/sitemap/index.js +2 -2
  291. package/dist/sitemap/index.mjs +1 -1
  292. package/dist/sync/BookingWidget.d.ts +0 -23
  293. package/dist/sync/BookingWidget.d.ts.map +1 -1
  294. package/dist/sync/MeetingFormatStep.d.ts +13 -0
  295. package/dist/sync/MeetingFormatStep.d.ts.map +1 -0
  296. package/dist/sync/api.d.ts +9 -10
  297. package/dist/sync/api.d.ts.map +1 -1
  298. package/dist/sync/booking-flow.d.ts +7 -0
  299. package/dist/sync/booking-flow.d.ts.map +1 -0
  300. package/dist/sync/booking-flow.test.d.ts +2 -0
  301. package/dist/sync/booking-flow.test.d.ts.map +1 -0
  302. package/dist/sync/index.d.ts +2 -2
  303. package/dist/sync/index.d.ts.map +1 -1
  304. package/dist/sync/index.js +20 -16
  305. package/dist/sync/index.mjs +4 -4
  306. package/dist/sync/types.d.ts +34 -0
  307. package/dist/sync/types.d.ts.map +1 -1
  308. package/package.json +1 -1
  309. package/dist/AnalyticsProvider-NA7EMJCA.mjs +0 -10
  310. package/dist/AnalyticsProvider-XE3ADUUH.js +0 -31
  311. package/dist/ChatWidget-BD4F4XLD.mjs +0 -11
  312. package/dist/ChatWidget-BZ3KOSXG.js +0 -20
  313. package/dist/EngageWidget-JPP6EDCJ.js +0 -18
  314. package/dist/EngageWidget-L7B6PCM3.mjs +0 -9
  315. package/dist/ManagedForm-2OPCHDNJ.js +0 -23
  316. package/dist/ManagedForm-AJM6D4SK.mjs +0 -14
  317. package/dist/SitemapSync-T2N3TCWF.js +0 -17
  318. package/dist/SitemapSync-YLWMU6J3.mjs +0 -8
  319. package/dist/chunk-AJBJH3BA.mjs.map +0 -1
  320. package/dist/chunk-CGCYZJYX.mjs +0 -41
  321. package/dist/chunk-CGCYZJYX.mjs.map +0 -1
  322. package/dist/chunk-COAVBUMS.mjs.map +0 -1
  323. package/dist/chunk-DHXQBTXE.mjs.map +0 -1
  324. package/dist/chunk-FH4LLJFL.mjs.map +0 -1
  325. package/dist/chunk-GQTSQR63.js.map +0 -1
  326. package/dist/chunk-HRQ4367C.mjs +0 -6
  327. package/dist/chunk-I72AUITZ.mjs.map +0 -1
  328. package/dist/chunk-JBGRXICL.js.map +0 -1
  329. package/dist/chunk-KP56C66Z.js.map +0 -1
  330. package/dist/chunk-TAIGHH5J.js.map +0 -1
  331. package/dist/chunk-TMQKZ4DO.js +0 -8
  332. package/dist/chunk-TPL2IHFI.js +0 -43
  333. package/dist/chunk-TPL2IHFI.js.map +0 -1
  334. package/dist/chunk-VLR2UYO4.js.map +0 -1
  335. package/dist/chunk-WZZYTFDV.mjs.map +0 -1
  336. package/dist/chunk-ZMFJXFUY.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/analytics/send-gate.ts","../src/analytics/WebVitals.tsx","../src/analytics/AnalyticsProvider.tsx"],"names":["propApiUrl","propApiKey","propSite","allowInFrame","debug","wordCount","usePathname","useEffect","useRef"],"mappings":"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","file":"chunk-UOLY4NXK.mjs","sourcesContent":["/**\n * @sonordev/site-kit/analytics — the ONE analytics send decision\n *\n * SINGLE SOURCE OF TRUTH for \"may this document report analytics, and where\n * to\". Every phone-home in the analytics module — page views, journey /\n * session rows, scroll depth, heatmap clicks, web vitals, custom events,\n * conversions, and the sitemap-validation read — resolves through\n * {@link resolveAnalyticsTarget} and sends through {@link analyticsSend} /\n * {@link analyticsBeacon}. Do NOT call sonorFetch/sonorBeacon directly from\n * this module, and do not re-fork the credential resolution below; the\n * eight hand-rolled copies this replaced are exactly how a rule gets fixed\n * in one place and left broken in seven (see CLAUDE.md, \"Code Hygiene\").\n * `send-gate.test.ts` fails the build if a new direct call appears.\n *\n * It answers two questions, in this order:\n *\n * 1. IS THIS DOCUMENT SOMEONE ELSE'S EMBED? A site loaded in a cross-origin\n * iframe must not report traffic against its own Sonor project. The\n * visitor is not on that site; they are on whatever page framed it, and\n * every metric the frame produces is phantom traffic in the analytics its\n * owner reads (and, for an agency, reports to the client).\n *\n * 2. IS THERE A CREDENTIAL? Unchanged behavior — no key, no send.\n *\n * ─── Why this landed on 2026-09-08, and what was measured ──────────────────\n *\n * Upforge case studies embed each client's whole production site in three\n * eager iframes per study (agency-site-kit `DeviceTrifolio`), and\n * `securityHeaders`' DEFAULT_FRAME_ANCESTORS has allowed https://upforge.io\n * to frame every managed site since it was introduced. DeviceTrifolio's own\n * doc comment reasons that nobody is affected because its cross-origin load\n * detection can never succeed, so the screenshot overlay never fades. That is\n * true of the PIXELS and false of the JAVASCRIPT: the overlay is an opacity\n * layer over a live frame, so the embedded site loads, hydrates and runs its\n * analytics whether or not it is ever revealed.\n *\n * Measured in the Sonor analytics table before this change (2026-09-08), the\n * signature is unmistakable — from `analytics_page_views`, three views of `/`\n * sharing ONE session_id and ONE visitor_id, milliseconds apart, with\n * referrer `https://upforge.io/`:\n *\n * abbeyglenapts.com 20:02:13.852 / .900 / .921 (48ms, 21ms apart)\n * goldenmilenky.com 04:12:23.840 / .868 / 24.624\n * queencityriverboats 22:12:33.609 / .690 / .784\n *\n * Three loads of one page inside 70 milliseconds is not a person; it is the\n * desktop + tablet + mobile frames of one trifolio, sharing storage because\n * they share the embedded site's origin. 23 client sites carried this traffic\n * on the day of the fix, the oldest from 2026-06-11. The widening of\n * frame-ancestors to upforgelabs.com (upforge-nextjs 0c12be5) does not start\n * this problem — it multiplies one that was already live.\n *\n * ─── Same-origin frames are ALLOWED, deliberately ──────────────────────────\n *\n * The block is on CROSS-origin embedding, not on being framed at all. A site\n * embedding itself (a preview pane, a print view, an on-domain booking frame)\n * has a real visitor really on that site and no other tenant to pollute, so\n * it keeps reporting. How that is detected, and why `window.top` rather than\n * `window.parent`, lives with the primitive in `shared/frame.ts` — this file\n * owns the analytics POLICY, that file owns the frame FACT.\n */\n\nimport { isCrossOriginFrame } from '../shared/frame'\nimport { sonorBeacon, sonorFetch } from '../shared/sonorFetch'\n\n/** Resolved destination for an analytics send. */\nexport interface AnalyticsTarget {\n apiUrl: string\n apiKey: string\n}\n\nexport interface AnalyticsGateOptions {\n /** `apiUrl` prop, when the caller was given one. Falls back to the window global. */\n apiUrl?: string\n /** `apiKey` prop, when the caller was given one. Falls back to the window global. */\n apiKey?: string\n /**\n * Opt back in to reporting from inside a cross-origin frame. For a site\n * deliberately distributed as an embed (a widget, a partner-hosted\n * booking or menu page) the frame IS the product, and its traffic is real.\n * Explicit per-site config only — see AnalyticsConfig.allowInFrame. There\n * is deliberately no window global or env var for this: a silent switch\n * that turns cross-tenant tracking back on is the failure mode, not the\n * feature.\n */\n allowInFrame?: boolean\n /** Emit the one-time diagnostic when a send is suppressed. */\n debug?: boolean\n}\n\n// Re-exported so analytics consumers have one import for the whole decision.\nexport { isCrossOriginFrame, isFramed, isTopFrameSameOrigin } from '../shared/frame'\n\n/** Window globals published by SiteKitClientProviders / publishSiteCredential. */\nfunction windowConfig(): { apiUrl: string; apiKey?: string } {\n const apiUrl =\n (typeof window !== 'undefined' &&\n (window as { __SITE_KIT_API_URL__?: string }).__SITE_KIT_API_URL__) ||\n 'https://api.sonor.io'\n const apiKey =\n typeof window !== 'undefined'\n ? (window as { __SITE_KIT_API_KEY__?: string }).__SITE_KIT_API_KEY__\n : undefined\n return { apiUrl, apiKey }\n}\n\n/**\n * One `console.info` per document, not one per event. A framed page fires\n * page view + journey + vitals + a click's worth of heatmap rows; logging\n * each suppression would turn a correct decision into console spam (and\n * Lighthouse best-practices noise) in someone else's page.\n */\nlet announcedSuppression = false\n\n/** Test seam — resets the once-per-document log latch. */\nexport function __resetSendGate(): void {\n announcedSuppression = false\n}\n\n/**\n * Resolve where an analytics call should go, or `null` if it must not happen.\n *\n * Returning `null` is the ONLY \"do not send\" signal in this module. Callers\n * that set up listeners or scrape the DOM should resolve first and bail on\n * `null`, so a cross-origin frame never even attaches a scroll or click\n * listener — the guard saves the work, not just the request.\n */\nexport function resolveAnalyticsTarget(\n options: AnalyticsGateOptions = {},\n): AnalyticsTarget | null {\n const { apiUrl: globalApiUrl, apiKey: globalApiKey } = windowConfig()\n const apiUrl = options.apiUrl || globalApiUrl\n const apiKey = options.apiKey || globalApiKey\n\n if (!apiKey) return null\n\n if (!options.allowInFrame && isCrossOriginFrame()) {\n if (options.debug && !announcedSuppression) {\n announcedSuppression = true\n console.info(\n '[Analytics] This page is embedded in a cross-origin frame, so nothing is being reported. ' +\n 'Set analytics.allowInFrame if this site is meant to be tracked as an embed.',\n )\n }\n return null\n }\n\n return { apiUrl, apiKey }\n}\n\n/**\n * POST an analytics payload. Resolves the gate itself and no-ops when it\n * says no — so a caller that forgets its own early bail still cannot send\n * from a cross-origin frame. That belt-and-braces is the point: this is the\n * choke point, and it does not rely on every call site remembering.\n *\n * `retries: 0` by default — every analytics endpoint here is non-idempotent\n * from the client's side (a retried page view after a lost response\n * double-counts). Never resolves to a rejected promise; tracking must not\n * break a page.\n */\nexport async function analyticsSend(\n path: string,\n body: unknown,\n options: AnalyticsGateOptions & {\n retries?: number\n method?: string\n /** Let the request outlive the page (scroll depth flushed on unmount). */\n keepalive?: boolean\n } = {},\n): Promise<Response | null> {\n const target = resolveAnalyticsTarget(options)\n if (!target) return null\n try {\n return await sonorFetch(\n `${target.apiUrl}${path}`,\n {\n method: options.method || 'POST',\n headers: { 'Content-Type': 'application/json' },\n ...(options.keepalive ? { keepalive: true } : {}),\n ...(body === undefined ? {} : { body: JSON.stringify(body) }),\n },\n { apiKey: target.apiKey, retries: options.retries ?? 0 },\n )\n } catch {\n // Network failures are expected and non-fatal — callers log under debug.\n return null\n }\n}\n\n/**\n * Unload-time twin of {@link analyticsSend}, through the keepalive beacon so\n * the credential stays in a header. Gated identically.\n */\nexport function analyticsBeacon(\n path: string,\n body: unknown,\n options: AnalyticsGateOptions = {},\n): void {\n const target = resolveAnalyticsTarget(options)\n if (!target) return\n sonorBeacon(`${target.apiUrl}${path}`, body, target.apiKey)\n}\n","/**\n * @sonordev/site-kit/analytics - Web Vitals Component\n *\n * Reports Core Web Vitals to the Sonor API, per route and with attribution.\n *\n * Two things make this more than a number stream (web-vitals v6):\n *\n * 1. SOFT NAVIGATIONS. Vitals used to finalize once per document, so on an\n * App Router site every metric was attributed to the LANDING path — a\n * visitor who arrived on / and browsed to /services produced no data for\n * /services at all. With `reportSoftNavs`, the library re-measures across\n * in-session route changes and tells us which URL each metric belongs to\n * (`metric.navigationURL`), so per-page vitals finally reflect the journey\n * rather than the entry point.\n *\n * 2. ATTRIBUTION. The attribution build answers \"what was slow\": the LCP\n * element and its image URL, the element an interaction hit and which\n * phase dominated INP, the node responsible for the largest layout shift.\n * A value says a page is slow; this says what to fix.\n *\n * Ratings come from the library (`metric.rating`) rather than thresholds\n * copied into this file, so they can't drift from the official buckets.\n */\n\n'use client'\n\nimport { useEffect, useRef } from 'react'\nimport { usePathname } from 'next/navigation'\nimport type { MetricWithAttribution } from 'web-vitals/attribution'\n// Same send gate as the rest of the analytics module — credential resolution\n// and the cross-origin-frame guard. This file used to carry its own copy of\n// the credential resolution; it does not any more, and must not again.\nimport { analyticsBeacon, analyticsSend } from './send-gate'\n\ninterface WebVitalsProps {\n apiUrl?: string\n apiKey?: string\n /** Sub-site identifier (matches AnalyticsConfig.site). Tagged on every Web Vital event. */\n site?: string\n /**\n * Report vitals from inside a cross-origin frame. Default false — see\n * send-gate.ts. AnalyticsProvider passes its own `allowInFrame` down, so a\n * site that opts in gets vitals too; standalone mounts pass their own.\n */\n allowInFrame?: boolean\n debug?: boolean\n}\n\n/** Path a metric belongs to. Soft-nav metrics carry the URL they were\n measured for, which is NOT necessarily the route showing when they\n report — trusting the current pathname would misattribute them. */\nfunction pathForMetric(metric: MetricWithAttribution, fallback: string): string {\n const url = (metric as { navigationURL?: string }).navigationURL\n if (url) {\n try {\n return new URL(url, window.location.origin).pathname || fallback\n } catch {\n /* fall through */\n }\n }\n return fallback\n}\n\n/**\n * Trim the library's attribution to the fields that answer \"what do I fix?\".\n * Kept small on purpose: this rides on every metric report, and the raw\n * objects carry PerformanceEntry arrays we have no use for downstream.\n */\nfunction summarizeAttribution(metric: MetricWithAttribution): Record<string, unknown> | undefined {\n const a = metric.attribution as Record<string, any> | undefined\n if (!a) return undefined\n const pick = (keys: string[]) => {\n const out: Record<string, unknown> = {}\n for (const key of keys) {\n const value = a[key]\n if (value !== undefined && value !== null && value !== '') out[key] = value\n }\n return Object.keys(out).length ? out : undefined\n }\n switch (metric.name) {\n case 'LCP':\n // target = the element; url = its image; the rest is the load broken\n // into the four subparts you can actually act on.\n return pick([\n 'target',\n 'url',\n 'timeToFirstByte',\n 'resourceLoadDelay',\n 'resourceLoadDuration',\n 'elementRenderDelay',\n ])\n case 'INP':\n return pick([\n 'interactionTarget',\n 'interactionType',\n 'inputDelay',\n 'processingDuration',\n 'presentationDelay',\n 'loadState',\n ])\n case 'CLS':\n return pick(['largestShiftTarget', 'largestShiftValue', 'largestShiftTime', 'loadState'])\n case 'FCP':\n return pick(['timeToFirstByte', 'firstByteToFCP', 'loadState'])\n case 'TTFB':\n return pick(['waitingDuration', 'dnsDuration', 'connectionDuration', 'requestDuration'])\n default:\n return undefined\n }\n}\n\nexport function WebVitals({\n apiUrl: propApiUrl,\n apiKey: propApiKey,\n site: propSite,\n allowInFrame = false,\n debug = false,\n}: WebVitalsProps) {\n const pathname = usePathname()\n\n // Observers register exactly once (mount): re-registering per pathname\n // leaked listeners on every navigation and re-reported the landing page's\n // LCP under later routes. Soft-navigation support means one registration\n // now covers the whole session, and each metric says which URL it is for.\n const landingPathRef = useRef<string>(pathname || '/')\n const propsRef = useRef({ propApiUrl, propApiKey, propSite, allowInFrame, debug })\n propsRef.current = { propApiUrl, propApiKey, propSite, allowInFrame, debug }\n\n useEffect(() => {\n let cancelled = false\n let teardown: (() => void) | undefined\n\n // Dynamic import to avoid SSR issues; catch ChunkLoadError (e.g. Turbopack dev)\n import('web-vitals/attribution').then(({ onCLS, onLCP, onTTFB, onINP, onFCP }) => {\n if (cancelled) return\n const { propApiUrl, propApiKey, propSite, allowInFrame, debug } = propsRef.current\n\n // Metric instances already sent. Metrics can report repeatedly as a\n // page evolves (and once more at page hide); the id is stable per\n // instance, so this keeps the last value per instance without\n // double-counting.\n const sent = new Map<string, number>()\n\n const report = (metric: MetricWithAttribution, viaBeacon = false) => {\n if (sent.get(metric.id) === metric.value) return\n sent.set(metric.id, metric.value)\n\n const site =\n propSite ||\n (typeof window !== 'undefined'\n ? (window as { __SITE_KIT_SITE__?: string }).__SITE_KIT_SITE__ ||\n window.location.host\n : '')\n\n const data = {\n site,\n pagePath: pathForMetric(metric, landingPathRef.current),\n metricName: metric.name,\n metricValue: metric.value,\n // The library's own rating — no thresholds duplicated here.\n metricRating: metric.rating,\n metricDelta: metric.delta,\n metricId: metric.id,\n navigationType: metric.navigationType,\n attribution: summarizeAttribution(metric),\n }\n\n if (debug) console.log('[Analytics] Web Vital:', data)\n\n const endpoint = '/api/public/analytics/web-vitals'\n const gate = { apiUrl: propApiUrl, apiKey: propApiKey, allowInFrame, debug }\n if (viaBeacon) {\n // Page is going away: keepalive fetch keeps the credential in the\n // header (raw sendBeacon can't set one).\n analyticsBeacon(endpoint, data, gate)\n return\n }\n void analyticsSend(endpoint, data, gate)\n }\n\n // reportSoftNavs: measure across in-session route changes, not just the\n // initial document. Without it, everything below the entry page is dark.\n const opts = { reportSoftNavs: true }\n const pending: MetricWithAttribution[] = []\n const collect = (metric: MetricWithAttribution) => {\n pending.push(metric)\n report(metric)\n }\n\n onLCP(collect, opts)\n onCLS(collect, opts)\n onTTFB(collect, opts)\n onINP(collect, opts)\n onFCP(collect, opts)\n\n // At page hide, flush final values (CLS and INP in particular keep\n // changing until the very end) using the beacon path.\n const handleVisibilityChange = () => {\n if (document.visibilityState !== 'hidden') return\n for (const metric of pending) report(metric, true)\n }\n document.addEventListener('visibilitychange', handleVisibilityChange)\n\n teardown = () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n }\n }).catch((err) => {\n // ChunkLoadError in Turbopack/dev - Web Vitals are optional\n if (propsRef.current.debug) console.warn('[Analytics] Web Vitals import failed:', err)\n })\n\n return () => {\n cancelled = true\n teardown?.()\n }\n // Mount-once by design — see comment above. Props are read via propsRef.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n return null\n}\n","/**\n * @sonordev/site-kit/analytics - Analytics Provider\n * \n * Provides analytics context and automatic page view tracking.\n * All data goes through Portal API with API key auth - never Supabase directly.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useEffect, useRef, useCallback, useMemo, useState } from 'react'\nimport { usePathname } from 'next/navigation'\nimport type { AnalyticsContextValue, TrackEventOptions, TrackConversionOptions } from './types'\nimport { registerAnalyticsDispatcher } from './dispatch'\nimport { WebVitals } from './WebVitals'\n// Single source of truth for visitor/session IDs (storage keys _sk_vid/_sk_sid)\n// — shared with SignalBridge and EngageWidget. Includes the crypto.randomUUID\n// fallback for older browsers; do NOT re-fork ID logic into this module.\nimport { getOrCreateVisitorId, getOrCreateSessionId } from '../shared/identity'\n// THE single analytics send decision — credential resolution AND the\n// cross-origin-frame guard, in one place. Every phone-home below goes through\n// it; this module must never call sonorFetch/sonorBeacon directly (send-gate.ts\n// explains why, and send-gate.test.ts enforces it).\nimport {\n analyticsBeacon,\n analyticsSend,\n resolveAnalyticsTarget,\n} from './send-gate'\n\nconst AnalyticsContext = createContext<AnalyticsContextValue | null>(null)\n\n// ============================================\n// Utility Functions\n// ============================================\n\n/**\n * Schedule a callback to run during browser idle time.\n * Safari has no requestIdleCallback — use a short real delay there so the\n * work lands after the post-hydration burst instead of right inside it.\n */\nfunction scheduleIdleTask(callback: () => void, timeout = 2000): void {\n if (typeof window !== 'undefined' && 'requestIdleCallback' in window) {\n ;(window as typeof window & { requestIdleCallback: (cb: () => void, opts?: { timeout: number }) => number }).requestIdleCallback(\n callback,\n { timeout }\n )\n } else {\n setTimeout(callback, 300)\n }\n}\n\nfunction getDeviceType(): 'desktop' | 'mobile' | 'tablet' {\n if (typeof window === 'undefined') return 'desktop'\n \n const ua = navigator.userAgent\n if (/tablet|ipad|playbook|silk/i.test(ua)) return 'tablet'\n if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return 'mobile'\n return 'desktop'\n}\n\nfunction getBrowser(): string {\n if (typeof window === 'undefined') return 'unknown'\n \n const ua = navigator.userAgent\n if (ua.includes('Firefox')) return 'Firefox'\n if (ua.includes('Edg')) return 'Edge'\n if (ua.includes('Chrome')) return 'Chrome'\n if (ua.includes('Safari')) return 'Safari'\n if (ua.includes('Opera') || ua.includes('OPR')) return 'Opera'\n return 'Other'\n}\n\nfunction getOS(): string {\n if (typeof window === 'undefined') return 'unknown'\n \n const ua = navigator.userAgent\n if (ua.includes('Windows')) return 'Windows'\n if (ua.includes('Mac OS X') || ua.includes('Macintosh')) return 'macOS'\n if (ua.includes('iPhone') || ua.includes('iPad')) return 'iOS'\n if (ua.includes('Android')) return 'Android'\n if (ua.includes('Linux')) return 'Linux'\n if (ua.includes('CrOS')) return 'ChromeOS'\n return 'Other'\n}\n\nfunction getUserAgent(): string {\n if (typeof window === 'undefined') return ''\n return navigator.userAgent\n}\n\nfunction getUTMParams(): Record<string, string> {\n if (typeof window === 'undefined') return {}\n\n const params = new URLSearchParams(window.location.search)\n const utmParams: Record<string, string> = {}\n\n for (const key of ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']) {\n const value = params.get(key)\n if (value) utmParams[key] = value\n }\n\n return utmParams\n}\n\n/**\n * Ad click IDs (Google/Microsoft auto-tagging sends these with NO utm\n * params). The server classifies gclid/msclkid as paid search — without\n * them, ad traffic silently reports as organic/direct.\n */\nfunction getClickIds(): { gclid?: string; fbclid?: string; msclkid?: string } {\n if (typeof window === 'undefined') return {}\n const params = new URLSearchParams(window.location.search)\n const ids: { gclid?: string; fbclid?: string; msclkid?: string } = {}\n for (const key of ['gclid', 'fbclid', 'msclkid'] as const) {\n const value = params.get(key)\n if (value) ids[key] = value.slice(0, 200)\n }\n return ids\n}\n\n/**\n * Landing-page markers set per-route by <LandingPage campaign=\"...\">\n * (@sonordev/site-kit/landing). Read at SEND time so SPA navigations onto\n * and off a landing route tag exactly the right payloads.\n */\nfunction getLandingContext(): { pageKind?: string; landingCampaign?: string } {\n if (typeof window === 'undefined') return {}\n const w = window as { __SITE_KIT_PAGE_KIND__?: string; __SITE_KIT_CAMPAIGN__?: string }\n if (w.__SITE_KIT_PAGE_KIND__ !== 'landing' || !w.__SITE_KIT_CAMPAIGN__) return {}\n return { pageKind: 'landing', landingCampaign: w.__SITE_KIT_CAMPAIGN__ }\n}\n\n/** Browser locale + IANA timezone — coarse audience signals, no PII. */\nfunction getLocaleInfo(): { language?: string; timezone?: string } {\n if (typeof navigator === 'undefined') return {}\n let timezone: string | undefined\n try {\n timezone = Intl.DateTimeFormat().resolvedOptions().timeZone\n } catch {\n timezone = undefined\n }\n return { language: navigator.language || undefined, timezone }\n}\n\n/**\n * Promise wrapper around scheduleIdleTask so the metadata scrape can yield\n * back to the main thread between slices.\n */\nfunction nextIdleSlice(timeout = 1500): Promise<void> {\n return new Promise(resolve => scheduleIdleTask(resolve, timeout))\n}\n\n/**\n * Extract SEO metadata from the current page.\n * This enriches seo_pages with live data from the actual page.\n *\n * Split into three slices so no single idle callback runs the whole scrape as\n * one long task on throttled mobile CPUs (full-tree clones + textContent reads\n * are the expensive part):\n * 1. getLightPageMetadata — meta tags, canonical, H1 (cheap queries)\n * 2. getLinkPageMetadata — word count + link graph (whole-tree text + anchors)\n * 3. getHeavyPageMetadata — images w/ context, content clone, sections, FAQs\n * Callers yield via nextIdleSlice() between them and merge the results; the\n * merged payload shape is identical to the old monolithic getPageMetadata().\n */\nfunction getLightPageMetadata(): Record<string, any> {\n if (typeof document === 'undefined') return {}\n\n const getMeta = (name: string): string | null => {\n const el = document.querySelector(`meta[name=\"${name}\"], meta[property=\"${name}\"]`)\n return el?.getAttribute('content') || null\n }\n \n const getCanonical = (): string | null => {\n const el = document.querySelector('link[rel=\"canonical\"]')\n return el?.getAttribute('href') || null\n }\n \n const getRobots = (): string | null => {\n return getMeta('robots')\n }\n \n const getH1 = (): string | null => {\n const h1 = document.querySelector('h1')\n return h1?.textContent?.trim() || null\n }\n \n const getH1Count = (): number => {\n return document.querySelectorAll('h1').length\n }\n \n return {\n metaDescription: getMeta('description'),\n canonical: getCanonical(),\n robots: getRobots(),\n ogTitle: getMeta('og:title'),\n ogDescription: getMeta('og:description'),\n ogImage: getMeta('og:image'),\n h1: getH1(),\n h1Count: getH1Count(),\n }\n}\n\n/** Slice 2: image inventory — per-image ancestor walks + surrounding text. */\nfunction getMediaPageMetadata(): Record<string, any> {\n if (typeof document === 'undefined') return {}\n\n interface PageImageReport {\n src: string\n alt: string | null\n elementType: 'img' | 'Image' | 'ManagedImage' | 'picture' | 'background'\n slotId?: string\n position: 'hero' | 'content' | 'sidebar' | 'footer' | 'header' | 'unknown'\n width?: number\n height?: number\n surroundingText?: string\n }\n\n // Helper to determine element position in page\n const getElementPosition = (el: Element): PageImageReport['position'] => {\n let current: Element | null = el\n while (current) {\n const tag = current.tagName?.toLowerCase()\n const role = current.getAttribute('role')\n const className = current.className?.toString?.() || ''\n \n // Check for hero sections\n if (className.includes('hero') || current.id?.includes('hero')) return 'hero'\n if (tag === 'header' || role === 'banner') return 'header'\n if (tag === 'nav' || role === 'navigation') return 'header'\n if (tag === 'footer' || role === 'contentinfo') return 'footer'\n if (tag === 'aside' || role === 'complementary') return 'sidebar'\n if (tag === 'main' || tag === 'article' || role === 'main') return 'content'\n \n current = current.parentElement\n }\n return 'unknown'\n }\n\n // Get surrounding text context for an image\n const getSurroundingText = (el: Element): string => {\n // Find closest paragraph, figcaption, or container\n const container = el.closest('figure, p, div, section, article')\n if (!container) return ''\n \n // Get text excluding script/style content\n const text = container.textContent?.replace(/\\s+/g, ' ').trim() || ''\n return text.slice(0, 300) // Limit to 300 chars\n }\n\n const getImageDetails = (): PageImageReport[] => {\n const images = document.querySelectorAll('img, [data-managed-image]')\n const seen = new Set<string>()\n const results: PageImageReport[] = []\n \n images.forEach(el => {\n const img = el as HTMLImageElement\n const src = img.getAttribute('src') || img.getAttribute('data-src') || ''\n \n // Skip duplicates, data URIs, and empty src\n if (!src || src.startsWith('data:') || seen.has(src)) return\n seen.add(src)\n \n // Detect if it's a ManagedImage\n const slotId = img.getAttribute('data-slot-id') || undefined\n const isManagedImage = img.hasAttribute('data-managed-image') || !!slotId\n \n results.push({\n src,\n alt: img.alt || null,\n elementType: isManagedImage ? 'ManagedImage' : 'img',\n slotId,\n position: getElementPosition(img),\n width: img.naturalWidth || parseInt(img.getAttribute('width') || '0') || undefined,\n height: img.naturalHeight || parseInt(img.getAttribute('height') || '0') || undefined,\n surroundingText: getSurroundingText(img),\n })\n })\n \n return results\n }\n\n const getImageStats = (): { count: number; withoutAlt: number; images: PageImageReport[] } => {\n const images = getImageDetails()\n let withoutAlt = 0\n images.forEach(img => {\n if (!img.alt || img.alt.trim() === '') withoutAlt++\n })\n return { count: images.length, withoutAlt, images }\n }\n\n const imageStats = getImageStats()\n return {\n imagesCount: imageStats.count,\n imagesWithoutAlt: imageStats.withoutAlt,\n images: imageStats.images, // Full image details for SEO optimization\n }\n}\n\n/** Slice 3: word count, link graph, and content extraction for Signal AI. */\nfunction getContentPageMetadata(): Record<string, any> {\n if (typeof document === 'undefined') return {}\n\n const getWordCount = (): number => {\n // Get main content, fallback to body\n const main = document.querySelector('main, article, [role=\"main\"]') || document.body\n const text = main.textContent || ''\n return text.split(/\\s+/).filter(w => w.length > 0).length\n }\n\n interface InternalLink {\n targetPath: string\n anchorText: string\n position: 'header' | 'nav' | 'content' | 'footer' | 'sidebar' | 'unknown'\n isNofollow: boolean\n }\n \n const getLinkStats = (): { \n internal: number\n external: number\n internalLinks: InternalLink[]\n } => {\n const links = document.querySelectorAll('a[href]')\n const currentHost = window.location.host\n let internal = 0\n let external = 0\n const internalLinks: InternalLink[] = []\n \n // Helper to determine link position in page\n const getLinkPosition = (el: Element): InternalLink['position'] => {\n let current: Element | null = el\n while (current) {\n const tag = current.tagName?.toLowerCase()\n const role = current.getAttribute('role')\n \n if (tag === 'header' || role === 'banner') return 'header'\n if (tag === 'nav' || role === 'navigation') return 'nav'\n if (tag === 'footer' || role === 'contentinfo') return 'footer'\n if (tag === 'aside' || role === 'complementary') return 'sidebar'\n if (tag === 'main' || tag === 'article' || role === 'main') return 'content'\n \n current = current.parentElement\n }\n return 'unknown'\n }\n \n links.forEach(link => {\n const href = link.getAttribute('href') || ''\n const rel = link.getAttribute('rel') || ''\n const isNofollow = rel.includes('nofollow')\n \n let isInternal = false\n let targetPath = ''\n \n if (href.startsWith('/') && !href.startsWith('//')) {\n isInternal = true\n targetPath = href.split('?')[0].split('#')[0] // Remove query/hash\n } else if (href.startsWith('#')) {\n // Same page anchor - skip\n internal++\n return\n } else if (href.startsWith('http')) {\n try {\n const url = new URL(href)\n if (url.host === currentHost) {\n isInternal = true\n targetPath = url.pathname\n } else {\n external++\n }\n } catch {\n // Invalid URL\n }\n }\n \n if (isInternal && targetPath) {\n internal++\n // Normalize path\n if (!targetPath.startsWith('/')) targetPath = '/' + targetPath\n if (targetPath !== '/' && targetPath.endsWith('/')) {\n targetPath = targetPath.slice(0, -1)\n }\n \n internalLinks.push({\n targetPath,\n anchorText: (link.textContent || '').trim().slice(0, 200),\n position: getLinkPosition(link),\n isNofollow,\n })\n }\n })\n \n return { internal, external, internalLinks }\n }\n\n // ============================================\n // Content Extraction for Signal AI Analysis\n // ============================================\n \n interface HeadingInfo {\n level: number\n text: string\n id?: string\n }\n\n interface ContentSection {\n heading?: string\n headingLevel?: number\n text: string\n wordCount: number\n }\n\n interface FAQItem {\n question: string\n answer: string\n }\n\n const getHeadingStructure = (): HeadingInfo[] => {\n const headings: HeadingInfo[] = []\n const elements = document.querySelectorAll('h1, h2, h3, h4, h5, h6')\n \n elements.forEach(el => {\n const level = parseInt(el.tagName[1])\n const text = el.textContent?.trim() || ''\n if (text) {\n headings.push({\n level,\n text: text.slice(0, 200), // Limit length\n id: el.id || undefined\n })\n }\n })\n \n return headings\n }\n\n const getContentText = (): { text: string; hash: string } => {\n // Get main content area\n const main = document.querySelector('main, article, [role=\"main\"]') || document.body\n \n // Clone to avoid modifying the page\n const clone = main.cloneNode(true) as Element\n \n // Remove script, style, nav, header, footer from clone\n clone.querySelectorAll('script, style, nav, header, footer, [role=\"navigation\"], [role=\"banner\"], [role=\"contentinfo\"]').forEach(el => el.remove())\n \n // Get clean text\n let text = clone.textContent || ''\n text = text.replace(/\\s+/g, ' ').trim()\n \n // Limit to 10KB for storage\n text = text.slice(0, 10000)\n \n // Simple hash for change detection\n let hash = 0\n for (let i = 0; i < text.length; i++) {\n const chr = text.charCodeAt(i)\n hash = ((hash << 5) - hash) + chr\n hash |= 0\n }\n \n return { text, hash: hash.toString(16) }\n }\n\n const getContentSections = (): ContentSection[] => {\n const sections: ContentSection[] = []\n const main = document.querySelector('main, article, [role=\"main\"]') || document.body\n \n // Find all heading + content pairs\n const headings = main.querySelectorAll('h1, h2, h3')\n \n headings.forEach((heading, idx) => {\n const headingText = heading.textContent?.trim() || ''\n const level = parseInt(heading.tagName[1])\n \n // Get content between this heading and the next\n let content = ''\n let sibling = heading.nextElementSibling\n \n while (sibling && !['H1', 'H2', 'H3'].includes(sibling.tagName)) {\n if (sibling.tagName === 'P' || sibling.tagName === 'UL' || sibling.tagName === 'OL' || sibling.tagName === 'DIV') {\n content += (sibling.textContent || '') + ' '\n }\n sibling = sibling.nextElementSibling\n }\n \n content = content.replace(/\\s+/g, ' ').trim().slice(0, 1000)\n const wordCount = content.split(/\\s+/).filter(w => w.length > 0).length\n \n if (content && wordCount > 10) {\n sections.push({\n heading: headingText.slice(0, 200),\n headingLevel: level,\n text: content,\n wordCount\n })\n }\n })\n \n return sections.slice(0, 20) // Limit to 20 sections\n }\n\n const detectFAQContent = (): FAQItem[] => {\n const faqs: FAQItem[] = []\n \n // Look for FAQ schema\n const scripts = document.querySelectorAll('script[type=\"application/ld+json\"]')\n scripts.forEach(script => {\n try {\n const data = JSON.parse(script.textContent || '')\n if (data['@type'] === 'FAQPage' && data.mainEntity) {\n data.mainEntity.forEach((item: any) => {\n if (item['@type'] === 'Question') {\n faqs.push({\n question: item.name?.slice(0, 200) || '',\n answer: (item.acceptedAnswer?.text || '').slice(0, 500)\n })\n }\n })\n }\n } catch { /* ignore parse errors */ }\n })\n \n // Look for common FAQ patterns in HTML\n if (faqs.length === 0) {\n // details/summary pattern\n const details = document.querySelectorAll('details')\n details.forEach(detail => {\n const summary = detail.querySelector('summary')\n if (summary) {\n const question = summary.textContent?.trim() || ''\n const answer = detail.textContent?.replace(question, '').trim().slice(0, 500) || ''\n if (question && answer) {\n faqs.push({ question: question.slice(0, 200), answer })\n }\n }\n })\n \n // Accordion pattern (common class names)\n const accordionItems = document.querySelectorAll('[class*=\"accordion\"], [class*=\"faq\"], [data-faq]')\n accordionItems.forEach(item => {\n const questionEl = item.querySelector('[class*=\"question\"], [class*=\"title\"], button, h3, h4')\n const answerEl = item.querySelector('[class*=\"answer\"], [class*=\"content\"], [class*=\"panel\"], p')\n if (questionEl && answerEl) {\n const question = questionEl.textContent?.trim() || ''\n const answer = answerEl.textContent?.trim().slice(0, 500) || ''\n if (question && answer && question.includes('?')) {\n faqs.push({ question: question.slice(0, 200), answer })\n }\n }\n })\n }\n \n return faqs.slice(0, 20) // Limit to 20 FAQs\n }\n\n const detectLists = (): { type: 'ul' | 'ol'; items: string[] }[] => {\n const lists: { type: 'ul' | 'ol'; items: string[] }[] = []\n const main = document.querySelector('main, article, [role=\"main\"]') || document.body\n \n main.querySelectorAll('ul, ol').forEach(list => {\n const type = list.tagName.toLowerCase() as 'ul' | 'ol'\n const items: string[] = []\n \n list.querySelectorAll(':scope > li').forEach(li => {\n const text = li.textContent?.trim().slice(0, 200) || ''\n if (text) items.push(text)\n })\n \n // Only include substantial lists (3+ items)\n if (items.length >= 3) {\n lists.push({ type, items: items.slice(0, 10) })\n }\n })\n \n return lists.slice(0, 10) // Limit to 10 lists\n }\n\n const estimateReadingTime = (wordCount: number): number => {\n // Average reading speed: 200-250 words per minute\n return Math.ceil(wordCount / 225)\n }\n \n const linkStats = getLinkStats()\n const headingStructure = getHeadingStructure()\n const contentData = getContentText()\n const contentSections = getContentSections()\n const faqContent = detectFAQContent()\n const listContent = detectLists()\n const wordCount = getWordCount()\n\n return {\n wordCount,\n internalLinks: linkStats.internal,\n internalLinkTargets: linkStats.internalLinks, // Full link graph data\n externalLinks: linkStats.external,\n // Content analysis data\n content: {\n text: contentData.text,\n hash: contentData.hash,\n headings: headingStructure,\n sections: contentSections,\n faqs: faqContent.length > 0 ? faqContent : undefined,\n lists: listContent.length > 0 ? listContent : undefined,\n readingTime: estimateReadingTime(wordCount),\n },\n }\n}\n\n/**\n * Collect full page metadata across three idle slices so the scrape never\n * runs as one long main-thread task. `isStale` is checked between slices —\n * if the user SPA-navigates mid-scrape, later slices would read the NEW\n * page's DOM under the old path, so we stop and return only the slices\n * scraped while the page was still current (partial metadata beats corrupt\n * metadata, and the page view itself is still recorded).\n */\nasync function collectPageMetadata(isStale: () => boolean): Promise<Record<string, any>> {\n const light = getLightPageMetadata()\n await nextIdleSlice()\n if (isStale()) return light\n const media = getMediaPageMetadata()\n await nextIdleSlice()\n if (isStale()) return { ...light, ...media }\n const content = getContentPageMetadata()\n return { ...light, ...media, ...content }\n}\n\n// ============================================\n// Provider Component\n// ============================================\n\ninterface AnalyticsProviderProps {\n /**\n * Optional. AnalyticsProvider is normally mounted as a CHILDLESS sibling of\n * page content (see SiteKitClientProviders) — it tracks via effects and a\n * metadata ref, it does not need to enclose the page. When rendered\n * childless, the AnalyticsContext simply has no consumers; components that\n * need it should use `useAnalyticsOptional()`.\n */\n children?: React.ReactNode\n apiUrl?: string\n apiKey?: string\n trackPageViews?: boolean\n trackWebVitals?: boolean\n trackScrollDepth?: boolean\n trackClicks?: boolean\n trackJourneys?: boolean // NEW: Track user journey paths\n sessionTimeout?: number\n excludePaths?: string[]\n validateAgainstSitemap?: boolean // Log debug warnings for paths not in sitemap (informational only, never blocks tracking)\n debug?: boolean\n /** External visitor ID from shared identity (used by SiteKitLayout) */\n externalVisitorId?: string\n /** External session ID from shared identity (used by SiteKitLayout) */\n externalSessionId?: string\n /** Callback invoked with page metadata after each page view (used by Signal bridge) */\n onPageMetadata?: (metadata: Record<string, unknown>) => void\n /**\n * Sub-site identifier (e.g. `ohiopowerstudies.com`). Every event tagged with\n * this string so a single Sonor project can host many sites and the\n * dashboard can filter / roll up per site. Normally passed in by\n * SiteKitClientProviders; if omitted, falls back to\n * `window.__SITE_KIT_SITE__` then `window.location.host`.\n */\n site?: string\n /**\n * Report analytics even when this page is rendered inside a CROSS-ORIGIN\n * iframe. Default false: an embedded page's traffic belongs to whoever\n * framed it, not to this project (see send-gate.ts for the measurement that\n * prompted this). Turn it on only for a site deliberately distributed as an\n * embed, where the frame is the product.\n *\n * Same-origin frames are unaffected — they always report.\n */\n allowInFrame?: boolean\n}\n\nexport function AnalyticsProvider({\n children,\n apiUrl: propApiUrl,\n apiKey: propApiKey,\n trackPageViews = true,\n trackWebVitals = true,\n trackScrollDepth = true,\n trackClicks = true,\n trackJourneys = true, // NEW: Enable journey tracking by default\n sessionTimeout = 30,\n excludePaths = [],\n validateAgainstSitemap = false,\n debug = false,\n externalVisitorId,\n externalSessionId,\n onPageMetadata,\n site: propSite,\n allowInFrame = false,\n}: AnalyticsProviderProps) {\n const pathname = usePathname()\n // Use state to track query string changes instead of useSearchParams (avoids Suspense requirement)\n const [queryString, setQueryString] = useState('')\n\n // Sub-site identifier — every event body below carries this so a single\n // Sonor project can host many sites and the dashboard can filter / roll up\n // per site. Resolution: prop > window global (set by SiteKitClientProviders)\n // > window.location.host. Empty string is acceptable (older clients).\n const site =\n propSite ||\n (typeof window !== 'undefined'\n ? (window as { __SITE_KIT_SITE__?: string }).__SITE_KIT_SITE__ ||\n window.location.host\n : '')\n\n // Everything the send gate needs, resolved once. Every effect and callback\n // below passes THIS object to resolveAnalyticsTarget / analyticsSend /\n // analyticsBeacon — there is no second place that decides whether a send\n // may happen, and no second copy of the prop-over-global fallback.\n const gate = useMemo(\n () => ({ apiUrl: propApiUrl, apiKey: propApiKey, allowInFrame, debug }),\n [propApiUrl, propApiKey, allowInFrame, debug],\n )\n\n // Listen for URL changes including query string\n useEffect(() => {\n if (typeof window === 'undefined') return\n \n // Set initial query string\n setQueryString(window.location.search)\n \n // Listen for popstate (back/forward navigation)\n const handlePopState = () => {\n setQueryString(window.location.search)\n }\n \n window.addEventListener('popstate', handlePopState)\n return () => window.removeEventListener('popstate', handlePopState)\n }, [pathname]) // Re-check when pathname changes\n \n const visitorIdRef = useRef<string>('')\n const sessionIdRef = useRef<string>('')\n const lastPathRef = useRef<string>('')\n const validPathsRef = useRef<Set<string> | null>(null)\n const sitemapLoadedRef = useRef<boolean>(false)\n \n // Journey tracking state\n const journeyStartTimeRef = useRef<number>(0)\n const pageEnterTimeRef = useRef<number>(0)\n const currentScrollDepthRef = useRef<number>(0)\n \n // Initialize IDs — use external (shared) IDs when provided by SiteKitLayout\n useEffect(() => {\n visitorIdRef.current = externalVisitorId || getOrCreateVisitorId()\n sessionIdRef.current = externalSessionId || getOrCreateSessionId(sessionTimeout)\n }, [sessionTimeout, externalVisitorId, externalSessionId])\n \n // Fetch valid pages from seo_pages (populated by SitemapSync from SEO module)\n useEffect(() => {\n if (!validateAgainstSitemap) {\n if (debug) console.log('[Analytics] Page validation disabled')\n return\n }\n \n const fetchValidPages = async () => {\n try {\n // Fetch seo_pages which is the canonical source of truth\n // (populated by SitemapSync component from the SEO module).\n // Gated like every other call: a cross-origin frame should not spend\n // the project's rate limit on data it will never report against.\n const response = await analyticsSend(\n '/api/public/seo/pages',\n undefined,\n { ...gate, method: 'GET', retries: 2 },\n )\n if (!response) {\n if (debug) console.warn('[Analytics] Page validation skipped (no key, or embedded)')\n sitemapLoadedRef.current = true\n return\n }\n\n if (response.ok) {\n const data = await response.json()\n const pages = data?.pages || []\n validPathsRef.current = new Set(\n pages.map((p: any) => p.path).filter(Boolean)\n )\n sitemapLoadedRef.current = true\n\n if (debug) {\n console.log('[Analytics] Loaded', validPathsRef.current.size, 'valid pages from seo_pages')\n }\n } else {\n sitemapLoadedRef.current = true // Mark as loaded even on failure so we don't block\n if (debug) {\n console.error('[Analytics] Pages fetch failed:', response.statusText)\n }\n }\n } catch (error) {\n sitemapLoadedRef.current = true // Mark as loaded even on error so we don't block\n if (debug) console.error('[Analytics] Error fetching valid pages:', error)\n }\n }\n \n fetchValidPages()\n }, [gate, validateAgainstSitemap, debug])\n \n // Track page views\n useEffect(() => {\n if (!trackPageViews) return\n if (!pathname) return\n\n // Normalize path: strip trailing slashes (except root)\n const normalizedPath = pathname === '/' ? '/' : pathname.replace(/\\/+$/, '')\n\n if (excludePaths.some(p => normalizedPath.startsWith(p))) return\n if (normalizedPath === lastPathRef.current) return\n\n lastPathRef.current = normalizedPath\n \n const trackPageView = async () => {\n // Resolve BEFORE the metadata scrape: no point burning three idle\n // slices of the visitor's main thread on a DOM scrape whose result can\n // never be sent (no key, or a cross-origin embed).\n if (!resolveAnalyticsTarget(gate)) {\n if (debug) console.warn('[Analytics] Page view skipped (no key, or embedded)')\n return\n }\n\n const utmParams = getUTMParams()\n // Captured before the sliced scrape yields — the title must belong to\n // THIS page even if the user navigates while slices are pending.\n const pageTitle = document.title\n // Sliced scrape: yields to the main thread between phases; on SPA\n // navigation mid-scrape it returns only the slices read from this\n // page's DOM (the page view still records, with partial metadata).\n const pageMetadata = await collectPageMetadata(\n () => lastPathRef.current !== normalizedPath,\n )\n\n // Share page metadata with Signal bridge via callback\n if (onPageMetadata) {\n onPageMetadata(pageMetadata)\n }\n\n const clickIds = getClickIds()\n const locale = getLocaleInfo()\n const landing = getLandingContext()\n const pageView = {\n sessionId: sessionIdRef.current,\n visitorId: visitorIdRef.current,\n site,\n pageKind: landing.pageKind,\n landingCampaign: landing.landingCampaign,\n pagePath: normalizedPath,\n pageTitle,\n referrer: document.referrer || null,\n deviceType: getDeviceType(),\n browser: getBrowser(),\n os: getOS(),\n userAgent: getUserAgent(),\n language: locale.language,\n timezone: locale.timezone,\n utmSource: utmParams.utm_source,\n utmMedium: utmParams.utm_medium,\n utmCampaign: utmParams.utm_campaign,\n utmTerm: utmParams.utm_term,\n utmContent: utmParams.utm_content,\n gclid: clickIds.gclid,\n fbclid: clickIds.fbclid,\n msclkid: clickIds.msclkid,\n // SEO enrichment data - updates seo_pages\n seo: pageMetadata,\n }\n\n if (debug) {\n console.log('[Analytics] Page view:', pageView)\n }\n\n // retries: 0 (the gate's default) — a retried page-view after a lost\n // response would double-count.\n const response = await analyticsSend('/api/public/analytics/page-view', pageView, gate)\n\n if (debug && response) {\n console.log('[Analytics] Page view response:', response.status, response.statusText)\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n console.error('[Analytics] Error tracking page view:', response.status, response.statusText, text)\n }\n }\n\n // Informational sitemap validation (never blocks tracking)\n if (validateAgainstSitemap && debug) {\n if (sitemapLoadedRef.current && validPathsRef.current && validPathsRef.current.size > 0) {\n if (!validPathsRef.current.has(normalizedPath)) {\n console.log('[Analytics] Note: path not found in sitemap (tracked anyway):', normalizedPath)\n }\n } else if (!sitemapLoadedRef.current) {\n console.log('[Analytics] Sitemap not yet loaded, tracked anyway:', normalizedPath)\n }\n }\n }\n \n // Use requestIdleCallback to defer tracking until browser is idle\n scheduleIdleTask(() => trackPageView())\n }, [pathname, queryString, gate, trackPageViews, excludePaths, debug, validateAgainstSitemap])\n\n // ============================================\n // Journey Path Tracking\n // ============================================\n useEffect(() => {\n if (!trackJourneys) return\n if (!pathname) return\n if (typeof window === 'undefined') return\n\n // Resolve up front so an embedded page never registers the pagehide\n // listener below — the guard should save the work, not just the request.\n if (!resolveAnalyticsTarget(gate)) return\n\n const now = Date.now()\n const previousPath = lastPathRef.current\n const previousDuration = pageEnterTimeRef.current > 0 \n ? Math.round((now - pageEnterTimeRef.current) / 1000) \n : 0\n const previousScrollDepth = currentScrollDepthRef.current\n\n // Update tracking refs for next navigation\n pageEnterTimeRef.current = now\n currentScrollDepthRef.current = 0\n\n // Determine if this is a new session or continuation\n const isNewSession = !previousPath || journeyStartTimeRef.current === 0\n \n if (isNewSession) {\n journeyStartTimeRef.current = now\n }\n\n const trackJourneyStep = async () => {\n // Landing markers ride on EVERY session action (not just 'start'): a\n // visitor can enter elsewhere and reach the landing page mid-session —\n // the server stamps the campaign first-touch either way.\n const landing = getLandingContext()\n const sessionData: Record<string, any> = {\n sessionId: sessionIdRef.current,\n visitorId: visitorIdRef.current,\n site,\n action: isNewSession ? 'start' : 'update',\n lastPage: pathname,\n userAgent: getUserAgent(),\n pageKind: landing.pageKind,\n landingCampaign: landing.landingCampaign,\n // Journey step data\n journeyStep: {\n page: pathname,\n timestamp: new Date().toISOString(),\n },\n }\n\n // Include previous page duration when navigating\n if (!isNewSession && previousDuration > 0) {\n sessionData.previousPageDuration = previousDuration\n sessionData.previousPageScrollDepth = previousScrollDepth\n }\n\n // Include first page for new sessions\n if (isNewSession) {\n sessionData.firstPage = pathname\n const utmParams = getUTMParams()\n const clickIds = getClickIds()\n sessionData.referrer = document.referrer || null\n sessionData.utmSource = utmParams.utm_source\n sessionData.utmMedium = utmParams.utm_medium\n sessionData.utmCampaign = utmParams.utm_campaign\n sessionData.utmTerm = utmParams.utm_term\n sessionData.utmContent = utmParams.utm_content\n sessionData.gclid = clickIds.gclid\n sessionData.fbclid = clickIds.fbclid\n sessionData.msclkid = clickIds.msclkid\n sessionData.screenWidth = window.screen.width\n sessionData.screenHeight = window.screen.height\n }\n\n if (debug) {\n console.log('[Analytics] Journey step:', sessionData)\n }\n\n await analyticsSend('/api/public/analytics/session', sessionData, gate)\n }\n\n scheduleIdleTask(() => trackJourneyStep())\n\n // Track session end when the page is being hidden/unloaded. `pagehide`\n // (not `beforeunload`): beforeunload never fires on iOS Safari and makes\n // pages ineligible for the back/forward cache in Firefox.\n const handleUnload = () => {\n const duration = Math.round((Date.now() - journeyStartTimeRef.current) / 1000)\n // keepalive transport keeps the key in headers — never in the URL,\n // where server/CDN access logs would capture it.\n analyticsBeacon(\n '/api/public/analytics/session',\n {\n sessionId: sessionIdRef.current,\n site,\n action: 'end',\n duration,\n lastPage: pathname,\n previousPageDuration: Math.round((Date.now() - pageEnterTimeRef.current) / 1000),\n previousPageScrollDepth: currentScrollDepthRef.current,\n },\n gate,\n )\n }\n\n window.addEventListener('pagehide', handleUnload)\n\n return () => {\n window.removeEventListener('pagehide', handleUnload)\n }\n }, [pathname, gate, trackJourneys, debug])\n\n // ============================================\n // Scroll Depth Tracking\n // ============================================\n useEffect(() => {\n if (typeof window === 'undefined') return\n \n if (debug) console.log('[Analytics] Scroll tracking setup:', { trackScrollDepth, hasApiKey: !!propApiKey })\n \n if (!trackScrollDepth) return\n\n // Resolve before attaching the scroll listener: an embedded page should\n // not run scroll math on every frame for data it cannot report.\n if (!resolveAnalyticsTarget(gate)) {\n if (debug) console.warn('[Analytics] Scroll tracking disabled (no key, or embedded)')\n return\n }\n\n if (debug) console.log('[Analytics] Scroll tracking enabled for:', pathname)\n\n let maxDepth = 0\n let startTime = Date.now()\n let milestone25: number | null = null\n let milestone50: number | null = null\n let milestone75: number | null = null\n let milestone100: number | null = null\n let hasTracked = false\n\n const calculateScrollDepth = () => {\n const scrollTop = window.scrollY\n const docHeight = document.documentElement.scrollHeight\n const winHeight = window.innerHeight\n const scrollableHeight = docHeight - winHeight\n \n if (scrollableHeight <= 0) return 100\n return Math.min(100, Math.round((scrollTop / scrollableHeight) * 100))\n }\n\n const handleScroll = () => {\n const depth = calculateScrollDepth()\n const elapsed = (Date.now() - startTime) / 1000\n\n if (depth > maxDepth) {\n maxDepth = depth\n currentScrollDepthRef.current = depth // Update ref for journey tracking\n if (debug && depth % 25 === 0) console.log('[Analytics] Scroll milestone:', depth + '%')\n \n if (depth >= 25 && milestone25 === null) milestone25 = elapsed\n if (depth >= 50 && milestone50 === null) milestone50 = elapsed\n if (depth >= 75 && milestone75 === null) milestone75 = elapsed\n if (depth >= 100 && milestone100 === null) milestone100 = elapsed\n }\n }\n\n const sendScrollData = async (useBeacon = false) => {\n if (hasTracked || maxDepth === 0) return\n hasTracked = true\n\n const totalTime = (Date.now() - startTime) / 1000\n \n const payload = {\n sessionId: sessionIdRef.current,\n visitorId: visitorIdRef.current,\n site,\n pagePath: pathname,\n maxDepthPercent: maxDepth,\n timeTo25: milestone25,\n timeTo50: milestone50,\n timeTo75: milestone75,\n timeTo100: milestone100,\n totalTimeSeconds: totalTime,\n deviceType: getDeviceType(),\n }\n\n // Unload sends go through the beacon (keepalive fetch) so the key stays\n // in headers instead of the URL; regular sends use the shared transport.\n if (useBeacon) {\n analyticsBeacon('/api/public/analytics/scroll-depth', payload, gate)\n } else {\n // keepalive: this also runs on unmount (SPA navigation), where a\n // plain fetch would be cancelled with the outgoing page.\n await analyticsSend('/api/public/analytics/scroll-depth', payload, {\n ...gate,\n keepalive: true,\n })\n }\n if (debug) console.log('[Analytics] Scroll depth:', { maxDepth, totalTime, useBeacon })\n }\n\n // Reset on page change\n maxDepth = 0\n startTime = Date.now()\n milestone25 = null\n milestone50 = null\n milestone75 = null\n milestone100 = null\n hasTracked = false\n\n window.addEventListener('scroll', handleScroll, { passive: true })\n\n // Capture the current position immediately — the provider mounts deferred\n // (idle/first-interaction), so the user may have already scrolled.\n handleScroll()\n\n // Send on page hide/unload - use beacon for reliability. `pagehide` (not\n // `beforeunload`) — it fires on iOS Safari and is bfcache-safe.\n const handlePageHide = () => sendScrollData(true)\n const handleVisibilityChange = () => {\n if (document.visibilityState === 'hidden') sendScrollData(true)\n }\n\n window.addEventListener('pagehide', handlePageHide)\n document.addEventListener('visibilitychange', handleVisibilityChange)\n\n return () => {\n window.removeEventListener('scroll', handleScroll)\n window.removeEventListener('pagehide', handlePageHide)\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n sendScrollData(false) // Send when component unmounts (navigation) - can use fetch here\n }\n }, [pathname, gate, trackScrollDepth, debug])\n\n // ============================================\n // Click/Heatmap Tracking\n // ============================================\n useEffect(() => {\n if (!trackClicks) return\n if (typeof window === 'undefined') return\n\n // Resolve before attaching the document-wide click listener — an embedded\n // page should not run element inspection on every click for nothing.\n if (!resolveAnalyticsTarget(gate)) return\n\n const handleClick = async (e: MouseEvent) => {\n const target = e.target as HTMLElement\n if (!target) return\n\n const docHeight = document.documentElement.scrollHeight\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n // Calculate click position as percentage\n // Both axes in document space (pageX over the document's scrollWidth,\n // pageY over its scrollHeight) so heat positions reconstruct on the\n // page, not the viewport. pageX / viewportWidth overflowed 100% on\n // any horizontally-scrolled page.\n const docWidth = document.documentElement.scrollWidth || viewportWidth\n const xPercent = Math.round((e.pageX / docWidth) * 100)\n const yPercent = Math.round(((e.pageY) / docHeight) * 100)\n\n // Get element info\n const elementTag = target.tagName.toLowerCase()\n const elementId = target.id || null\n const elementClass = target.className && typeof target.className === 'string' \n ? target.className.split(' ').slice(0, 3).join(' ') \n : null\n const elementText = target.textContent?.slice(0, 50) || null\n\n const clickData = {\n sessionId: sessionIdRef.current,\n site,\n pagePath: pathname,\n xPercent,\n yPercent,\n xAbsolute: e.pageX,\n yAbsolute: e.pageY,\n viewportWidth,\n viewportHeight,\n pageHeight: docHeight,\n elementTag,\n elementId,\n elementClass,\n elementText,\n }\n\n if (debug) console.log('[Analytics] Click:', clickData)\n\n scheduleIdleTask(() => {\n void analyticsSend('/api/public/analytics/heatmap-click', clickData, gate)\n }, 500)\n }\n\n document.addEventListener('click', handleClick, { passive: true })\n\n return () => {\n document.removeEventListener('click', handleClick)\n }\n }, [pathname, gate, trackClicks, debug])\n\n // Track event function - uses idle callback for non-critical events\n const trackEvent = useCallback((options: TrackEventOptions) => {\n const doTrack = async () => {\n const event = {\n sessionId: sessionIdRef.current,\n visitorId: visitorIdRef.current,\n site,\n eventName: options.name,\n eventCategory: options.category,\n eventLabel: options.label,\n eventValue: options.value,\n properties: options.properties,\n pagePath: pathname,\n }\n \n if (debug) {\n console.log('[Analytics] Event:', event)\n }\n \n const response = await analyticsSend('/api/public/analytics/event', event, gate)\n if (response && !response.ok && debug) {\n console.error('[Analytics] Error tracking event:', response.statusText)\n }\n }\n\n // Defer event tracking to idle time\n scheduleIdleTask(doTrack, 1000)\n }, [gate, site, pathname, debug])\n \n // Track conversion function - runs immediately (conversions are high priority)\n const trackConversion = useCallback((options: TrackConversionOptions) => {\n const doTrack = async () => {\n const conversion = {\n sessionId: sessionIdRef.current,\n visitorId: visitorIdRef.current,\n site,\n conversionType: options.type,\n value: options.value,\n currency: options.currency,\n metadata: options.metadata,\n pagePath: pathname,\n referrer: document.referrer || null,\n deviceType: getDeviceType(),\n }\n \n if (debug) {\n console.log('[Analytics] Conversion:', conversion)\n }\n \n const response = await analyticsSend('/api/public/analytics/conversion', conversion, gate)\n if (response && !response.ok && debug) {\n console.error('[Analytics] Error tracking conversion:', response.statusText)\n }\n }\n\n // Conversions run immediately (not deferred) since they're high-value events\n doTrack()\n }, [gate, site, pathname, debug])\n \n // Bridge for code outside this provider's tree (the childless-sibling\n // mount pattern leaves the whole page un-provided): register the REAL\n // track functions with the standalone dispatch module, which queues any\n // calls made before this (idle-deferred) provider mounted.\n useEffect(() => {\n registerAnalyticsDispatcher({ trackEvent, trackConversion })\n return () => registerAnalyticsDispatcher(null)\n }, [trackEvent, trackConversion])\n\n const contextValue = useMemo<AnalyticsContextValue>(() => ({\n trackEvent,\n trackConversion,\n sessionId: sessionIdRef.current,\n visitorId: visitorIdRef.current,\n }), [trackEvent, trackConversion])\n \n return (\n <AnalyticsContext.Provider value={contextValue}>\n {trackWebVitals && (\n <WebVitals\n apiUrl={propApiUrl}\n apiKey={propApiKey}\n site={site}\n allowInFrame={allowInFrame}\n debug={debug}\n />\n )}\n {children}\n </AnalyticsContext.Provider>\n )\n}\n\n// ============================================\n// Hooks\n// ============================================\n\nexport function useAnalytics(): AnalyticsContextValue {\n const context = useContext(AnalyticsContext)\n if (!context) {\n throw new Error('useAnalytics must be used within an AnalyticsProvider')\n }\n return context\n}\n\n/**\n * Safe version of useAnalytics that returns null when used outside of AnalyticsProvider.\n * Use this in components that may be rendered without an AnalyticsProvider in the tree.\n */\nexport function useAnalyticsOptional(): AnalyticsContextValue | null {\n return useContext(AnalyticsContext)\n}\n\nexport function useTrackEvent() {\n const { trackEvent, trackConversion } = useAnalytics()\n return { trackEvent, trackConversion }\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkTMQKZ4DO_js = require('./chunk-TMQKZ4DO.js');
3
+ var chunkUTOHMK52_js = require('./chunk-UTOHMK52.js');
4
4
  require('server-only');
5
5
 
6
6
  var MINT_PATH = "/api/public/site-token";
@@ -80,7 +80,7 @@ async function mintWithRetry(rawKey, apiUrl) {
80
80
  headers: {
81
81
  "content-type": "application/json",
82
82
  "x-api-key": rawKey,
83
- "x-sitekit-version": chunkTMQKZ4DO_js.SITE_KIT_VERSION
83
+ "x-sitekit-version": chunkUTOHMK52_js.SITE_KIT_VERSION
84
84
  },
85
85
  // Only meaningful on the fallback path, where a future Next stopped
86
86
  // exposing `_nextOriginalFetch` and we're back on the patched fetch.
@@ -132,5 +132,5 @@ async function mintWithRetry(rawKey, apiUrl) {
132
132
  }
133
133
 
134
134
  exports.resolveClientCredential = resolveClientCredential;
135
- //# sourceMappingURL=chunk-AVPMSJCQ.js.map
136
- //# sourceMappingURL=chunk-AVPMSJCQ.js.map
135
+ //# sourceMappingURL=chunk-URVGBWCP.js.map
136
+ //# sourceMappingURL=chunk-URVGBWCP.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/mint-site-token.ts"],"names":["SITE_KIT_VERSION"],"mappings":"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","file":"chunk-AVPMSJCQ.js","sourcesContent":["/**\n * @sonordev/site-kit/server — Client-token minting (server-only)\n *\n * SiteKitLayout holds the raw `SONOR_API_KEY` server-side but must NOT ship it to\n * the browser. This mints a short-lived, project-scoped token (server-to-server,\n * authenticated with the raw key, no browser Origin so it passes domain-binding)\n * and returns THAT for the layout to inject instead.\n *\n * `import 'server-only'` makes importing this into a client bundle a hard build\n * error — the raw key stays on the server.\n *\n * FAILS CLOSED. If minting genuinely fails, this throws and the build fails —\n * it does NOT publish the raw key. A failed deploy is loud and recoverable; a\n * leaked project secret in public HTML is neither. The only paths that still\n * return the raw key are a 404 (an older API that predates the endpoint, an\n * expected state during rollout) and the explicit `SONOR_SITE_TOKEN_FALLBACK=raw`\n * escape hatch, for when shipping beats being blocked.\n *\n * That posture is not academic. Through 3.5.0 a throttled mint (HTTP 429)\n * returned the raw key AND cached it for 60s, so every page rendered in that\n * window shipped the secret: one real build came out with 180 of 272 prerendered\n * pages carrying it. The build \"succeeded\" and nothing downstream noticed.\n * Rate limiting is transient by definition — the fix is to wait and retry, not\n * to downgrade to publishing the key.\n *\n * Two things keep the request count low enough that throttling is rare:\n * - a module-level memo, so a worker mints once and every page reuses the seed;\n * - an in-flight map, because that memo is only populated AFTER the await. Next\n * renders pages concurrently, so without coalescing the first N concurrent\n * renders in a process all miss the cache and all fire a request — a\n * stampede per worker, multiplied by one worker per core. That is how a\n * single site exhausted a 60/min budget.\n *\n * SEED FRESHNESS: a static page's seed carries `iat` = build time, and the API\n * refuses to refresh a seed older than SITE_TOKEN_MAX_REFRESH_AGE_SECONDS\n * (sonor-api common/site-auth/site-token.util.ts — currently 400 days, raised\n * from 30 alongside this fix). A site not rebuilt within that window serves a\n * seed the client cannot refresh, and its forms/analytics/engage start failing\n * for new visitors. Keep that limit comfortably longer than the fleet's\n * realistic redeploy gap; marketing sites routinely go many months untouched.\n */\nimport 'server-only'\nimport { SITE_KIT_VERSION } from '../shared/version'\n\nconst MINT_PATH = '/api/public/site-token'\nconst MINT_TIMEOUT_MS = 4_000\nconst SITE_TOKEN_PREFIX = 'st1.'\n\n/**\n * Retry budget for transient failures (429 / 5xx / network). Rate limiting is\n * measured per minute, so a few seconds of backoff clears it; the whole ladder\n * costs at most ~20s once per build worker, and only when something is wrong.\n */\nconst MINT_MAX_ATTEMPTS = 4\nconst MINT_BACKOFF_BASE_MS = 500\nconst MINT_BACKOFF_CAP_MS = 8_000\n\ninterface CacheEntry {\n credential: string\n expiresAt: number\n}\nconst cache = new Map<string, CacheEntry>()\n\n/**\n * Requests already in flight, keyed by raw key. The `cache` above is only\n * written after the response lands, so concurrent renders would otherwise each\n * start their own mint. See the module header.\n */\nconst inflight = new Map<string, Promise<string>>()\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms))\n\n/**\n * Honour `Retry-After` when the server sends it (delta-seconds or HTTP date),\n * otherwise exponential backoff. Capped so a misconfigured server cannot stall\n * a build indefinitely.\n */\nfunction retryDelayMs(res: Response | null, attempt: number): number {\n const header = res?.headers?.get?.('retry-after')\n if (header) {\n const seconds = Number(header)\n const ms = Number.isFinite(seconds)\n ? seconds * 1000\n : Date.parse(header) - Date.now()\n if (Number.isFinite(ms) && ms > 0) return Math.min(ms, MINT_BACKOFF_CAP_MS)\n }\n return Math.min(MINT_BACKOFF_BASE_MS * 2 ** attempt, MINT_BACKOFF_CAP_MS)\n}\n\n/** 429 and 5xx are transient; waiting is the correct response to both. */\nconst isRetryableStatus = (status: number) => status === 429 || status >= 500\n\n/**\n * The escape hatch, for when a broken mint must not block a release. Opt-in and\n * loud: it re-enables publishing the project secret in page HTML.\n */\nfunction rawFallbackAllowed(): boolean {\n return (\n typeof process !== 'undefined' &&\n process.env?.SONOR_SITE_TOKEN_FALLBACK === 'raw'\n )\n}\n\nfunction failClosed(reason: string, rawKey: string, now: number): string {\n if (rawFallbackAllowed()) {\n console.warn(\n `[SiteKitLayout] Token mint failed (${reason}) and SONOR_SITE_TOKEN_FALLBACK=raw is set; ` +\n 'injecting the RAW API KEY into page HTML. The key is publicly readable wherever ' +\n 'this page is served — unset the escape hatch once the mint is healthy.',\n )\n cache.set(rawKey, { credential: rawKey, expiresAt: now + 60_000 })\n return rawKey\n }\n throw new Error(\n `site-kit: could not mint a site token (${reason}). Refusing to publish the raw ` +\n 'SONOR_API_KEY in page HTML, so this build fails instead. Check that the mint ' +\n 'endpoint is reachable and the key is valid, then rebuild. To ship anyway — ' +\n 'which publishes the project secret in public HTML — set SONOR_SITE_TOKEN_FALLBACK=raw.',\n )\n}\n\ntype FetchLike = typeof fetch\n\n/**\n * Next.js monkey-patches `globalThis.fetch` so every call participates in its\n * Data Cache and static-generation tracking. This mint must NOT participate.\n *\n * `cache: 'no-store'` marks the calling scope dynamic, so during `next build`\n * Next throws DynamicServerError *before the request goes out*. On a statically\n * prerendered route the mint therefore never ran at all — the catch below\n * swallowed the bailout and every page shipped the raw key. (Verified against\n * Next 16.2.9: the mint endpoint received zero requests for a static build.)\n *\n * The two obvious repairs are both wrong:\n * - `next: { revalidate: N }` LOWERS the route's revalidate window\n * (patch-fetch.js: `finalRevalidate < revalidateStore.revalidate`), quietly\n * converting fully-static pages into ISR across the whole fleet.\n * - `next: { revalidate: false }` keeps pages static but writes the token into\n * the Data Cache, which hosts persist across builds — a fresh deploy could\n * re-bake a months-old seed, which is the exact failure we're removing.\n *\n * A credential mint is not page data and has no business in the page data cache.\n * So we call the original fetch Next saves on the patched one and stay invisible\n * to the framework: no bailout, no cache entry, no effect on staticness. The\n * module-level memo above is the only cache we want — one mint per build\n * process, and a build-fresh seed on every deploy.\n *\n * @internal Exported for unit tests; not part of the package's public API.\n */\nexport function resolveFetch(): { fn: FetchLike; unpatched: boolean } {\n const current = globalThis.fetch as\n | (FetchLike & { _nextOriginalFetch?: FetchLike })\n | undefined\n if (!current) throw new Error('site-kit: global fetch is unavailable')\n const original = current._nextOriginalFetch\n if (typeof original === 'function') return { fn: original, unpatched: true }\n return { fn: current, unpatched: false }\n}\n\n/**\n * Next signals \"this render must go dynamic\" by THROWING (DynamicServerError,\n * static-gen bailout, redirect/notFound digests, React's postpone). Those are\n * the framework's control flow, not a mint failure. Swallowing one makes Next\n * believe the render succeeded statically — which is precisely how the raw key\n * ended up stranded in prerendered HTML with no error anywhere. Let them\n * propagate; only real network/HTTP failures fall back to the raw key.\n *\n * @internal Exported for unit tests; not part of the package's public API.\n */\nexport function isFrameworkSignal(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n const e = err as { digest?: unknown; code?: unknown; $$typeof?: unknown }\n if (e.digest === 'DYNAMIC_SERVER_USAGE') return true\n if (e.code === 'NEXT_STATIC_GEN_BAILOUT') return true\n if (typeof e.digest === 'string' && e.digest.startsWith('NEXT_')) return true\n if (e.$$typeof === Symbol.for('react.postpone')) return true\n return false\n}\n\nexport async function resolveClientCredential(\n rawKey: string,\n apiUrl: string,\n): Promise<string> {\n if (!rawKey) return ''\n // Env should hold a raw key; if it already looks like a token, pass it through.\n if (rawKey.startsWith(SITE_TOKEN_PREFIX)) return rawKey\n\n const cached = cache.get(rawKey)\n if (cached && cached.expiresAt > Date.now()) return cached.credential\n\n // Collapse concurrent renders onto one request rather than a stampede.\n const pending = inflight.get(rawKey)\n if (pending) return pending\n\n const request = mintWithRetry(rawKey, apiUrl)\n inflight.set(rawKey, request)\n try {\n return await request\n } finally {\n inflight.delete(rawKey)\n }\n}\n\nasync function mintWithRetry(rawKey: string, apiUrl: string): Promise<string> {\n const base = (apiUrl || 'https://api.sonor.io').replace(/\\/+$/, '')\n let lastReason = 'unknown error'\n\n for (let attempt = 0; attempt < MINT_MAX_ATTEMPTS; attempt++) {\n let res: Response | null = null\n try {\n const { fn: fetchFn, unpatched } = resolveFetch()\n res = await fetchFn(`${base}${MINT_PATH}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-api-key': rawKey,\n 'x-sitekit-version': SITE_KIT_VERSION,\n },\n // Only meaningful on the fallback path, where a future Next stopped\n // exposing `_nextOriginalFetch` and we're back on the patched fetch.\n // `revalidate: false` is the one setting that neither bails out of static\n // generation nor downgrades the route to ISR; a Data Cache entry is the\n // lesser evil versus shipping the raw key again.\n ...(unpatched ? {} : ({ next: { revalidate: false } } as RequestInit)),\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(MINT_TIMEOUT_MS)\n : undefined,\n })\n } catch (err) {\n // Never swallow the framework's own control flow — see isFrameworkSignal.\n if (isFrameworkSignal(err)) throw err\n lastReason = err instanceof Error ? err.message : 'network error'\n if (attempt < MINT_MAX_ATTEMPTS - 1) {\n await sleep(retryDelayMs(null, attempt))\n continue\n }\n break\n }\n\n if (res.ok) {\n const data: any = await res.json().catch(() => null)\n const token = data?.token\n const expiresIn = typeof data?.expiresIn === 'number' ? data.expiresIn : 3600\n if (typeof token === 'string' && token.startsWith(SITE_TOKEN_PREFIX)) {\n cache.set(rawKey, {\n credential: token,\n expiresAt: Date.now() + Math.min((expiresIn * 1000) / 2, 30 * 60_000),\n })\n return token\n }\n lastReason = 'mint returned no usable token'\n break\n }\n\n // 404 = an API older than the endpoint. Expected during rollout, and the raw\n // key is exactly what such an API still expects, so this stays a fallback\n // rather than a build failure.\n if (res.status === 404) {\n cache.set(rawKey, { credential: rawKey, expiresAt: Date.now() + 60_000 })\n return rawKey\n }\n\n lastReason = `HTTP ${res.status}`\n if (isRetryableStatus(res.status) && attempt < MINT_MAX_ATTEMPTS - 1) {\n const delay = retryDelayMs(res, attempt)\n console.warn(\n `[SiteKitLayout] Token mint returned ${res.status}; retrying in ${Math.round(delay)}ms ` +\n `(attempt ${attempt + 2}/${MINT_MAX_ATTEMPTS}).`,\n )\n await sleep(delay)\n continue\n }\n break\n }\n\n return failClosed(lastReason, rawKey, Date.now())\n}\n"]}
1
+ {"version":3,"sources":["../src/server/mint-site-token.ts"],"names":["SITE_KIT_VERSION"],"mappings":"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","file":"chunk-URVGBWCP.js","sourcesContent":["/**\n * @sonordev/site-kit/server — Client-token minting (server-only)\n *\n * SiteKitLayout holds the raw `SONOR_API_KEY` server-side but must NOT ship it to\n * the browser. This mints a short-lived, project-scoped token (server-to-server,\n * authenticated with the raw key, no browser Origin so it passes domain-binding)\n * and returns THAT for the layout to inject instead.\n *\n * `import 'server-only'` makes importing this into a client bundle a hard build\n * error — the raw key stays on the server.\n *\n * FAILS CLOSED. If minting genuinely fails, this throws and the build fails —\n * it does NOT publish the raw key. A failed deploy is loud and recoverable; a\n * leaked project secret in public HTML is neither. The only paths that still\n * return the raw key are a 404 (an older API that predates the endpoint, an\n * expected state during rollout) and the explicit `SONOR_SITE_TOKEN_FALLBACK=raw`\n * escape hatch, for when shipping beats being blocked.\n *\n * That posture is not academic. Through 3.5.0 a throttled mint (HTTP 429)\n * returned the raw key AND cached it for 60s, so every page rendered in that\n * window shipped the secret: one real build came out with 180 of 272 prerendered\n * pages carrying it. The build \"succeeded\" and nothing downstream noticed.\n * Rate limiting is transient by definition — the fix is to wait and retry, not\n * to downgrade to publishing the key.\n *\n * Two things keep the request count low enough that throttling is rare:\n * - a module-level memo, so a worker mints once and every page reuses the seed;\n * - an in-flight map, because that memo is only populated AFTER the await. Next\n * renders pages concurrently, so without coalescing the first N concurrent\n * renders in a process all miss the cache and all fire a request — a\n * stampede per worker, multiplied by one worker per core. That is how a\n * single site exhausted a 60/min budget.\n *\n * SEED FRESHNESS: a static page's seed carries `iat` = build time, and the API\n * refuses to refresh a seed older than SITE_TOKEN_MAX_REFRESH_AGE_SECONDS\n * (sonor-api common/site-auth/site-token.util.ts — currently 400 days, raised\n * from 30 alongside this fix). A site not rebuilt within that window serves a\n * seed the client cannot refresh, and its forms/analytics/engage start failing\n * for new visitors. Keep that limit comfortably longer than the fleet's\n * realistic redeploy gap; marketing sites routinely go many months untouched.\n */\nimport 'server-only'\nimport { SITE_KIT_VERSION } from '../shared/version'\n\nconst MINT_PATH = '/api/public/site-token'\nconst MINT_TIMEOUT_MS = 4_000\nconst SITE_TOKEN_PREFIX = 'st1.'\n\n/**\n * Retry budget for transient failures (429 / 5xx / network). Rate limiting is\n * measured per minute, so a few seconds of backoff clears it; the whole ladder\n * costs at most ~20s once per build worker, and only when something is wrong.\n */\nconst MINT_MAX_ATTEMPTS = 4\nconst MINT_BACKOFF_BASE_MS = 500\nconst MINT_BACKOFF_CAP_MS = 8_000\n\ninterface CacheEntry {\n credential: string\n expiresAt: number\n}\nconst cache = new Map<string, CacheEntry>()\n\n/**\n * Requests already in flight, keyed by raw key. The `cache` above is only\n * written after the response lands, so concurrent renders would otherwise each\n * start their own mint. See the module header.\n */\nconst inflight = new Map<string, Promise<string>>()\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms))\n\n/**\n * Honour `Retry-After` when the server sends it (delta-seconds or HTTP date),\n * otherwise exponential backoff. Capped so a misconfigured server cannot stall\n * a build indefinitely.\n */\nfunction retryDelayMs(res: Response | null, attempt: number): number {\n const header = res?.headers?.get?.('retry-after')\n if (header) {\n const seconds = Number(header)\n const ms = Number.isFinite(seconds)\n ? seconds * 1000\n : Date.parse(header) - Date.now()\n if (Number.isFinite(ms) && ms > 0) return Math.min(ms, MINT_BACKOFF_CAP_MS)\n }\n return Math.min(MINT_BACKOFF_BASE_MS * 2 ** attempt, MINT_BACKOFF_CAP_MS)\n}\n\n/** 429 and 5xx are transient; waiting is the correct response to both. */\nconst isRetryableStatus = (status: number) => status === 429 || status >= 500\n\n/**\n * The escape hatch, for when a broken mint must not block a release. Opt-in and\n * loud: it re-enables publishing the project secret in page HTML.\n */\nfunction rawFallbackAllowed(): boolean {\n return (\n typeof process !== 'undefined' &&\n process.env?.SONOR_SITE_TOKEN_FALLBACK === 'raw'\n )\n}\n\nfunction failClosed(reason: string, rawKey: string, now: number): string {\n if (rawFallbackAllowed()) {\n console.warn(\n `[SiteKitLayout] Token mint failed (${reason}) and SONOR_SITE_TOKEN_FALLBACK=raw is set; ` +\n 'injecting the RAW API KEY into page HTML. The key is publicly readable wherever ' +\n 'this page is served — unset the escape hatch once the mint is healthy.',\n )\n cache.set(rawKey, { credential: rawKey, expiresAt: now + 60_000 })\n return rawKey\n }\n throw new Error(\n `site-kit: could not mint a site token (${reason}). Refusing to publish the raw ` +\n 'SONOR_API_KEY in page HTML, so this build fails instead. Check that the mint ' +\n 'endpoint is reachable and the key is valid, then rebuild. To ship anyway — ' +\n 'which publishes the project secret in public HTML — set SONOR_SITE_TOKEN_FALLBACK=raw.',\n )\n}\n\ntype FetchLike = typeof fetch\n\n/**\n * Next.js monkey-patches `globalThis.fetch` so every call participates in its\n * Data Cache and static-generation tracking. This mint must NOT participate.\n *\n * `cache: 'no-store'` marks the calling scope dynamic, so during `next build`\n * Next throws DynamicServerError *before the request goes out*. On a statically\n * prerendered route the mint therefore never ran at all — the catch below\n * swallowed the bailout and every page shipped the raw key. (Verified against\n * Next 16.2.9: the mint endpoint received zero requests for a static build.)\n *\n * The two obvious repairs are both wrong:\n * - `next: { revalidate: N }` LOWERS the route's revalidate window\n * (patch-fetch.js: `finalRevalidate < revalidateStore.revalidate`), quietly\n * converting fully-static pages into ISR across the whole fleet.\n * - `next: { revalidate: false }` keeps pages static but writes the token into\n * the Data Cache, which hosts persist across builds — a fresh deploy could\n * re-bake a months-old seed, which is the exact failure we're removing.\n *\n * A credential mint is not page data and has no business in the page data cache.\n * So we call the original fetch Next saves on the patched one and stay invisible\n * to the framework: no bailout, no cache entry, no effect on staticness. The\n * module-level memo above is the only cache we want — one mint per build\n * process, and a build-fresh seed on every deploy.\n *\n * @internal Exported for unit tests; not part of the package's public API.\n */\nexport function resolveFetch(): { fn: FetchLike; unpatched: boolean } {\n const current = globalThis.fetch as\n | (FetchLike & { _nextOriginalFetch?: FetchLike })\n | undefined\n if (!current) throw new Error('site-kit: global fetch is unavailable')\n const original = current._nextOriginalFetch\n if (typeof original === 'function') return { fn: original, unpatched: true }\n return { fn: current, unpatched: false }\n}\n\n/**\n * Next signals \"this render must go dynamic\" by THROWING (DynamicServerError,\n * static-gen bailout, redirect/notFound digests, React's postpone). Those are\n * the framework's control flow, not a mint failure. Swallowing one makes Next\n * believe the render succeeded statically — which is precisely how the raw key\n * ended up stranded in prerendered HTML with no error anywhere. Let them\n * propagate; only real network/HTTP failures fall back to the raw key.\n *\n * @internal Exported for unit tests; not part of the package's public API.\n */\nexport function isFrameworkSignal(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n const e = err as { digest?: unknown; code?: unknown; $$typeof?: unknown }\n if (e.digest === 'DYNAMIC_SERVER_USAGE') return true\n if (e.code === 'NEXT_STATIC_GEN_BAILOUT') return true\n if (typeof e.digest === 'string' && e.digest.startsWith('NEXT_')) return true\n if (e.$$typeof === Symbol.for('react.postpone')) return true\n return false\n}\n\nexport async function resolveClientCredential(\n rawKey: string,\n apiUrl: string,\n): Promise<string> {\n if (!rawKey) return ''\n // Env should hold a raw key; if it already looks like a token, pass it through.\n if (rawKey.startsWith(SITE_TOKEN_PREFIX)) return rawKey\n\n const cached = cache.get(rawKey)\n if (cached && cached.expiresAt > Date.now()) return cached.credential\n\n // Collapse concurrent renders onto one request rather than a stampede.\n const pending = inflight.get(rawKey)\n if (pending) return pending\n\n const request = mintWithRetry(rawKey, apiUrl)\n inflight.set(rawKey, request)\n try {\n return await request\n } finally {\n inflight.delete(rawKey)\n }\n}\n\nasync function mintWithRetry(rawKey: string, apiUrl: string): Promise<string> {\n const base = (apiUrl || 'https://api.sonor.io').replace(/\\/+$/, '')\n let lastReason = 'unknown error'\n\n for (let attempt = 0; attempt < MINT_MAX_ATTEMPTS; attempt++) {\n let res: Response | null = null\n try {\n const { fn: fetchFn, unpatched } = resolveFetch()\n res = await fetchFn(`${base}${MINT_PATH}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-api-key': rawKey,\n 'x-sitekit-version': SITE_KIT_VERSION,\n },\n // Only meaningful on the fallback path, where a future Next stopped\n // exposing `_nextOriginalFetch` and we're back on the patched fetch.\n // `revalidate: false` is the one setting that neither bails out of static\n // generation nor downgrades the route to ISR; a Data Cache entry is the\n // lesser evil versus shipping the raw key again.\n ...(unpatched ? {} : ({ next: { revalidate: false } } as RequestInit)),\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(MINT_TIMEOUT_MS)\n : undefined,\n })\n } catch (err) {\n // Never swallow the framework's own control flow — see isFrameworkSignal.\n if (isFrameworkSignal(err)) throw err\n lastReason = err instanceof Error ? err.message : 'network error'\n if (attempt < MINT_MAX_ATTEMPTS - 1) {\n await sleep(retryDelayMs(null, attempt))\n continue\n }\n break\n }\n\n if (res.ok) {\n const data: any = await res.json().catch(() => null)\n const token = data?.token\n const expiresIn = typeof data?.expiresIn === 'number' ? data.expiresIn : 3600\n if (typeof token === 'string' && token.startsWith(SITE_TOKEN_PREFIX)) {\n cache.set(rawKey, {\n credential: token,\n expiresAt: Date.now() + Math.min((expiresIn * 1000) / 2, 30 * 60_000),\n })\n return token\n }\n lastReason = 'mint returned no usable token'\n break\n }\n\n // 404 = an API older than the endpoint. Expected during rollout, and the raw\n // key is exactly what such an API still expects, so this stays a fallback\n // rather than a build failure.\n if (res.status === 404) {\n cache.set(rawKey, { credential: rawKey, expiresAt: Date.now() + 60_000 })\n return rawKey\n }\n\n lastReason = `HTTP ${res.status}`\n if (isRetryableStatus(res.status) && attempt < MINT_MAX_ATTEMPTS - 1) {\n const delay = retryDelayMs(res, attempt)\n console.warn(\n `[SiteKitLayout] Token mint returned ${res.status}; retrying in ${Math.round(delay)}ms ` +\n `(attempt ${attempt + 2}/${MINT_MAX_ATTEMPTS}).`,\n )\n await sleep(delay)\n continue\n }\n break\n }\n\n return failClosed(lastReason, rawKey, Date.now())\n}\n"]}
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ // src/shared/version.ts
4
+ var SITE_KIT_VERSION = "5.4.0";
5
+
6
+ exports.SITE_KIT_VERSION = SITE_KIT_VERSION;
7
+ //# sourceMappingURL=chunk-UTOHMK52.js.map
8
+ //# sourceMappingURL=chunk-UTOHMK52.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;AASO,IAAM,gBAAA,GAAmB","file":"chunk-TMQKZ4DO.js","sourcesContent":["/**\n * @sonordev/site-kit shared - Package version constant\n *\n * Single source of truth for the version the kit reports at runtime (the\n * x-sitekit-version request header set by sonorFetch). Kept as a plain const\n * so client bundles don't need JSON imports; scripts/verify-dts.cjs asserts\n * it matches package.json at publish time, and version.test.ts asserts it in\n * dev, so it can't silently drift.\n */\nexport const SITE_KIT_VERSION = '5.3.1'\n"]}
1
+ {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;AASO,IAAM,gBAAA,GAAmB","file":"chunk-UTOHMK52.js","sourcesContent":["/**\n * @sonordev/site-kit shared - Package version constant\n *\n * Single source of truth for the version the kit reports at runtime (the\n * x-sitekit-version request header set by sonorFetch). Kept as a plain const\n * so client bundles don't need JSON imports; scripts/verify-dts.cjs asserts\n * it matches package.json at publish time, and version.test.ts asserts it in\n * dev, so it can't silently drift.\n */\nexport const SITE_KIT_VERSION = '5.4.0'\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkTMQKZ4DO_js = require('./chunk-TMQKZ4DO.js');
3
+ var chunkUTOHMK52_js = require('./chunk-UTOHMK52.js');
4
4
 
5
5
  // src/shared/siteToken.ts
6
6
  var SITE_TOKEN_PREFIX = "st1.";
@@ -99,7 +99,7 @@ async function doRefresh() {
99
99
  method: "POST",
100
100
  headers: {
101
101
  "content-type": "application/json",
102
- "x-sitekit-version": chunkTMQKZ4DO_js.SITE_KIT_VERSION,
102
+ "x-sitekit-version": chunkUTOHMK52_js.SITE_KIT_VERSION,
103
103
  // Send the current token as x-api-key too, so the server's key-scoped
104
104
  // throttler buckets refreshes by token rather than by shared CDN IP.
105
105
  "x-api-key": current
@@ -154,5 +154,5 @@ exports.getFreshCredential = getFreshCredential;
154
154
  exports.isSiteToken = isSiteToken;
155
155
  exports.isTokenMode = isTokenMode;
156
156
  exports.refreshSiteToken = refreshSiteToken;
157
- //# sourceMappingURL=chunk-SHZXSIRH.js.map
158
- //# sourceMappingURL=chunk-SHZXSIRH.js.map
157
+ //# sourceMappingURL=chunk-VA36CJRV.js.map
158
+ //# sourceMappingURL=chunk-VA36CJRV.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/siteToken.ts"],"names":["SITE_KIT_VERSION"],"mappings":"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","file":"chunk-SHZXSIRH.js","sourcesContent":["/**\n * @sonordev/site-kit shared — Client token manager\n *\n * SiteKitLayout (a server component) no longer ships the raw `sonor_` API key to\n * the browser. Instead it mints a short-lived, project-scoped token server-side\n * and injects THAT. This module is the browser-side lifecycle for that token:\n *\n * - holds the current token (seeded from the injected value)\n * - a static page's seed can be days old and already expired on first view, so\n * it transparently REFRESHES via the public endpoint before first use\n * - dedupes concurrent refreshes (one in-flight request, awaited by all)\n * - persists the freshest token in sessionStorage so navigations within a\n * session don't each re-refresh\n *\n * Everything here is a no-op passthrough when the injected credential is a raw\n * key (a 2.x kit, or the mint-failed fallback) — legacy behavior is unchanged.\n * Fully SSR-safe: on the server there is no refresh, just the seed.\n *\n * The token payload is NOT secret (only its HMAC is unforgeable), so decoding it\n * client-side to read `exp`/`pid` is fine and needs no crypto.\n */\nimport { SITE_KIT_VERSION } from './version'\n\nexport const SITE_TOKEN_PREFIX = 'st1.'\n\n/** Refresh this many ms before expiry, so a request never races the deadline. */\nconst REFRESH_SKEW_MS = 5 * 60_000\nconst REFRESH_TIMEOUT_MS = 6_000\nconst STORAGE_KEY = '__sonor_site_token__'\n\nexport function isSiteToken(credential?: string | null): boolean {\n return typeof credential === 'string' && credential.startsWith(SITE_TOKEN_PREFIX)\n}\n\ninterface DecodedClaims {\n pid?: string\n iat?: number\n exp?: number\n}\n\n/** Decode the (non-secret) token payload. Returns null if not a token / malformed. */\nexport function decodeSiteTokenClaims(token?: string | null): DecodedClaims | null {\n if (!isSiteToken(token)) return null\n const seg = (token as string).split('.')[1]\n if (!seg) return null\n try {\n // base64url → JSON. atob handles standard base64; restore url-safe chars.\n const b64 = seg.replace(/-/g, '+').replace(/_/g, '/')\n const json =\n typeof atob === 'function'\n ? atob(b64)\n : Buffer.from(seg, 'base64url').toString('utf8')\n const claims = JSON.parse(json)\n return typeof claims === 'object' && claims ? claims : null\n } catch {\n return null\n }\n}\n\n/**\n * Stable id for the auth circuit breaker. Tokens rotate, so keying the breaker\n * by the raw value would reset it every refresh — key by the project id encoded\n * in the token instead. Raw keys use a safe prefix.\n */\nexport function credentialBreakerId(credential?: string | null): string {\n if (!credential) return '(anonymous)'\n const claims = decodeSiteTokenClaims(credential)\n if (claims?.pid) return `pid:${claims.pid}`\n return credential.slice(0, 16)\n}\n\ninterface TokenState {\n /** The credential injected by the layout (token or raw key). */\n seed: string | undefined\n /** Whether we're in token mode (seed is a token). */\n isToken: boolean\n /** Base API URL for the refresh endpoint. */\n apiUrl: string\n /** The freshest known token (>= seed). Null in raw-key mode. */\n token: string | null\n /** In-flight refresh, deduped. */\n refreshing: Promise<string | null> | null\n}\n\nfunction initialTokenState(): TokenState {\n return {\n seed: undefined,\n isToken: false,\n apiUrl: 'https://api.sonor.io',\n token: null,\n refreshing: null,\n }\n}\n\n/**\n * Page-wide singleton, keyed on window. Bundlers can (and in the field, do)\n * inline more than one copy of this module into a page — e.g. an app importing\n * site-kit directly while a transpiled component library bundles its own copy.\n * Module-scoped state then splits into per-copy brains: one copy gets\n * configured and refreshes fine while the other keeps sending the stale\n * prerendered seed, 401s, and trips its breaker (\"sonor-auth-paused\" with a\n * working network). Sharing the state object on window gives every copy the\n * same token, the same deduped refresh, and the same breaker. On the server\n * (no window) each module instance keeps private state — there's no refresh\n * lifecycle there anyway.\n */\nconst TOKEN_STATE_KEY = '__SONOR_SITE_TOKEN_STATE__'\n\nconst state: TokenState = (() => {\n if (typeof window === 'undefined') return initialTokenState()\n const w = window as unknown as Record<string, TokenState | undefined>\n return (w[TOKEN_STATE_KEY] ??= initialTokenState())\n})()\n\nfunction readStoredToken(): string | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n return typeof parsed?.token === 'string' ? parsed.token : null\n } catch {\n return null\n }\n}\n\nfunction storeToken(token: string): void {\n if (typeof window === 'undefined') return\n try {\n window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ token }))\n } catch {\n /* private mode / disabled storage — memory-only is fine */\n }\n try {\n ;(window as any).__SITE_KIT_API_KEY__ = token\n } catch {\n /* ignore */\n }\n}\n\nfunction tokenExp(token: string | null): number {\n const claims = decodeSiteTokenClaims(token)\n return typeof claims?.exp === 'number' ? claims.exp * 1000 : 0\n}\n\n/**\n * Configure the manager from the layout-injected values. Adopts a fresher token\n * from sessionStorage if a prior page in this session already refreshed.\n */\nexport function configureSiteCredential(opts: {\n credential?: string\n apiUrl?: string\n}): void {\n state.seed = opts.credential\n state.isToken = isSiteToken(opts.credential)\n if (opts.apiUrl) state.apiUrl = opts.apiUrl.replace(/\\/+$/, '')\n\n if (!state.isToken) {\n state.token = null\n return\n }\n\n // configure runs on every render — never downgrade. Keep the freshest of the\n // injected seed, a token stored by an earlier page this session, and whatever\n // an in-flight/prior refresh already put in memory.\n const prior = state.token\n let best = opts.credential ?? null\n const stored = readStoredToken()\n if (stored && tokenExp(stored) > tokenExp(best)) best = stored\n if (prior && tokenExp(prior) > tokenExp(best)) best = prior\n state.token = best\n}\n\nfunction needsRefresh(): boolean {\n if (!state.isToken) return false\n if (!state.token) return true\n return Date.now() >= tokenExp(state.token) - REFRESH_SKEW_MS\n}\n\nasync function doRefresh(): Promise<string | null> {\n const current = state.token\n if (!current) return null\n try {\n const res = await fetch(`${state.apiUrl}/api/public/site-token/refresh`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n // Send the current token as x-api-key too, so the server's key-scoped\n // throttler buckets refreshes by token rather than by shared CDN IP.\n 'x-api-key': current,\n },\n body: JSON.stringify({ token: current }),\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(REFRESH_TIMEOUT_MS)\n : undefined,\n })\n if (!res.ok) return null\n const data = await res.json().catch(() => null)\n const next = data?.token\n if (typeof next === 'string' && isSiteToken(next)) {\n state.token = next\n storeToken(next)\n return next\n }\n return null\n } catch {\n return null\n }\n}\n\n/** Refresh now (deduped). Returns the new token, or null on failure. */\nexport function refreshSiteToken(): Promise<string | null> {\n if (!state.isToken) return Promise.resolve(null)\n if (state.refreshing) return state.refreshing\n state.refreshing = doRefresh().finally(() => {\n state.refreshing = null\n })\n return state.refreshing\n}\n\n/**\n * The credential to put on the next request. In token mode, refreshes a stale\n * seed first. In raw-key mode, returns the seed unchanged (legacy passthrough).\n */\nexport async function getFreshCredential(\n seed?: string,\n): Promise<string | undefined> {\n // Unconfigured but handed a token (a component received the prerendered seed\n // as a prop before any provider configured the manager): adopt it so the\n // refresh machinery works instead of replaying a possibly-expired seed.\n if (!state.isToken && isSiteToken(seed)) {\n configureSiteCredential({ credential: seed })\n }\n // A raw key (or nothing configured) — no token machinery.\n if (!state.isToken) return seed ?? state.seed\n if (needsRefresh()) {\n const refreshed = await refreshSiteToken()\n if (refreshed) return refreshed\n // Refresh failed — fall through to whatever we have; the request may 401 and\n // trip the breaker with friendly copy.\n }\n return state.token ?? seed ?? state.seed\n}\n\n/** Whether the given credential is being managed as a token. */\nexport function isTokenMode(): boolean {\n return state.isToken\n}\n\n/**\n * Best-known credential WITHOUT refreshing — for synchronous unload paths\n * (sonorBeacon) that can't await. In token mode returns the freshest token held;\n * in raw-key mode returns the seed.\n */\nexport function currentCredential(seed?: string): string | undefined {\n if (!state.isToken) return seed ?? state.seed\n return state.token ?? seed ?? state.seed\n}\n"]}
1
+ {"version":3,"sources":["../src/shared/siteToken.ts"],"names":["SITE_KIT_VERSION"],"mappings":"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","file":"chunk-VA36CJRV.js","sourcesContent":["/**\n * @sonordev/site-kit shared — Client token manager\n *\n * SiteKitLayout (a server component) no longer ships the raw `sonor_` API key to\n * the browser. Instead it mints a short-lived, project-scoped token server-side\n * and injects THAT. This module is the browser-side lifecycle for that token:\n *\n * - holds the current token (seeded from the injected value)\n * - a static page's seed can be days old and already expired on first view, so\n * it transparently REFRESHES via the public endpoint before first use\n * - dedupes concurrent refreshes (one in-flight request, awaited by all)\n * - persists the freshest token in sessionStorage so navigations within a\n * session don't each re-refresh\n *\n * Everything here is a no-op passthrough when the injected credential is a raw\n * key (a 2.x kit, or the mint-failed fallback) — legacy behavior is unchanged.\n * Fully SSR-safe: on the server there is no refresh, just the seed.\n *\n * The token payload is NOT secret (only its HMAC is unforgeable), so decoding it\n * client-side to read `exp`/`pid` is fine and needs no crypto.\n */\nimport { SITE_KIT_VERSION } from './version'\n\nexport const SITE_TOKEN_PREFIX = 'st1.'\n\n/** Refresh this many ms before expiry, so a request never races the deadline. */\nconst REFRESH_SKEW_MS = 5 * 60_000\nconst REFRESH_TIMEOUT_MS = 6_000\nconst STORAGE_KEY = '__sonor_site_token__'\n\nexport function isSiteToken(credential?: string | null): boolean {\n return typeof credential === 'string' && credential.startsWith(SITE_TOKEN_PREFIX)\n}\n\ninterface DecodedClaims {\n pid?: string\n iat?: number\n exp?: number\n}\n\n/** Decode the (non-secret) token payload. Returns null if not a token / malformed. */\nexport function decodeSiteTokenClaims(token?: string | null): DecodedClaims | null {\n if (!isSiteToken(token)) return null\n const seg = (token as string).split('.')[1]\n if (!seg) return null\n try {\n // base64url → JSON. atob handles standard base64; restore url-safe chars.\n const b64 = seg.replace(/-/g, '+').replace(/_/g, '/')\n const json =\n typeof atob === 'function'\n ? atob(b64)\n : Buffer.from(seg, 'base64url').toString('utf8')\n const claims = JSON.parse(json)\n return typeof claims === 'object' && claims ? claims : null\n } catch {\n return null\n }\n}\n\n/**\n * Stable id for the auth circuit breaker. Tokens rotate, so keying the breaker\n * by the raw value would reset it every refresh — key by the project id encoded\n * in the token instead. Raw keys use a safe prefix.\n */\nexport function credentialBreakerId(credential?: string | null): string {\n if (!credential) return '(anonymous)'\n const claims = decodeSiteTokenClaims(credential)\n if (claims?.pid) return `pid:${claims.pid}`\n return credential.slice(0, 16)\n}\n\ninterface TokenState {\n /** The credential injected by the layout (token or raw key). */\n seed: string | undefined\n /** Whether we're in token mode (seed is a token). */\n isToken: boolean\n /** Base API URL for the refresh endpoint. */\n apiUrl: string\n /** The freshest known token (>= seed). Null in raw-key mode. */\n token: string | null\n /** In-flight refresh, deduped. */\n refreshing: Promise<string | null> | null\n}\n\nfunction initialTokenState(): TokenState {\n return {\n seed: undefined,\n isToken: false,\n apiUrl: 'https://api.sonor.io',\n token: null,\n refreshing: null,\n }\n}\n\n/**\n * Page-wide singleton, keyed on window. Bundlers can (and in the field, do)\n * inline more than one copy of this module into a page — e.g. an app importing\n * site-kit directly while a transpiled component library bundles its own copy.\n * Module-scoped state then splits into per-copy brains: one copy gets\n * configured and refreshes fine while the other keeps sending the stale\n * prerendered seed, 401s, and trips its breaker (\"sonor-auth-paused\" with a\n * working network). Sharing the state object on window gives every copy the\n * same token, the same deduped refresh, and the same breaker. On the server\n * (no window) each module instance keeps private state — there's no refresh\n * lifecycle there anyway.\n */\nconst TOKEN_STATE_KEY = '__SONOR_SITE_TOKEN_STATE__'\n\nconst state: TokenState = (() => {\n if (typeof window === 'undefined') return initialTokenState()\n const w = window as unknown as Record<string, TokenState | undefined>\n return (w[TOKEN_STATE_KEY] ??= initialTokenState())\n})()\n\nfunction readStoredToken(): string | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n return typeof parsed?.token === 'string' ? parsed.token : null\n } catch {\n return null\n }\n}\n\nfunction storeToken(token: string): void {\n if (typeof window === 'undefined') return\n try {\n window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ token }))\n } catch {\n /* private mode / disabled storage — memory-only is fine */\n }\n try {\n ;(window as any).__SITE_KIT_API_KEY__ = token\n } catch {\n /* ignore */\n }\n}\n\nfunction tokenExp(token: string | null): number {\n const claims = decodeSiteTokenClaims(token)\n return typeof claims?.exp === 'number' ? claims.exp * 1000 : 0\n}\n\n/**\n * Configure the manager from the layout-injected values. Adopts a fresher token\n * from sessionStorage if a prior page in this session already refreshed.\n */\nexport function configureSiteCredential(opts: {\n credential?: string\n apiUrl?: string\n}): void {\n state.seed = opts.credential\n state.isToken = isSiteToken(opts.credential)\n if (opts.apiUrl) state.apiUrl = opts.apiUrl.replace(/\\/+$/, '')\n\n if (!state.isToken) {\n state.token = null\n return\n }\n\n // configure runs on every render — never downgrade. Keep the freshest of the\n // injected seed, a token stored by an earlier page this session, and whatever\n // an in-flight/prior refresh already put in memory.\n const prior = state.token\n let best = opts.credential ?? null\n const stored = readStoredToken()\n if (stored && tokenExp(stored) > tokenExp(best)) best = stored\n if (prior && tokenExp(prior) > tokenExp(best)) best = prior\n state.token = best\n}\n\nfunction needsRefresh(): boolean {\n if (!state.isToken) return false\n if (!state.token) return true\n return Date.now() >= tokenExp(state.token) - REFRESH_SKEW_MS\n}\n\nasync function doRefresh(): Promise<string | null> {\n const current = state.token\n if (!current) return null\n try {\n const res = await fetch(`${state.apiUrl}/api/public/site-token/refresh`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n // Send the current token as x-api-key too, so the server's key-scoped\n // throttler buckets refreshes by token rather than by shared CDN IP.\n 'x-api-key': current,\n },\n body: JSON.stringify({ token: current }),\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(REFRESH_TIMEOUT_MS)\n : undefined,\n })\n if (!res.ok) return null\n const data = await res.json().catch(() => null)\n const next = data?.token\n if (typeof next === 'string' && isSiteToken(next)) {\n state.token = next\n storeToken(next)\n return next\n }\n return null\n } catch {\n return null\n }\n}\n\n/** Refresh now (deduped). Returns the new token, or null on failure. */\nexport function refreshSiteToken(): Promise<string | null> {\n if (!state.isToken) return Promise.resolve(null)\n if (state.refreshing) return state.refreshing\n state.refreshing = doRefresh().finally(() => {\n state.refreshing = null\n })\n return state.refreshing\n}\n\n/**\n * The credential to put on the next request. In token mode, refreshes a stale\n * seed first. In raw-key mode, returns the seed unchanged (legacy passthrough).\n */\nexport async function getFreshCredential(\n seed?: string,\n): Promise<string | undefined> {\n // Unconfigured but handed a token (a component received the prerendered seed\n // as a prop before any provider configured the manager): adopt it so the\n // refresh machinery works instead of replaying a possibly-expired seed.\n if (!state.isToken && isSiteToken(seed)) {\n configureSiteCredential({ credential: seed })\n }\n // A raw key (or nothing configured) — no token machinery.\n if (!state.isToken) return seed ?? state.seed\n if (needsRefresh()) {\n const refreshed = await refreshSiteToken()\n if (refreshed) return refreshed\n // Refresh failed — fall through to whatever we have; the request may 401 and\n // trip the breaker with friendly copy.\n }\n return state.token ?? seed ?? state.seed\n}\n\n/** Whether the given credential is being managed as a token. */\nexport function isTokenMode(): boolean {\n return state.isToken\n}\n\n/**\n * Best-known credential WITHOUT refreshing — for synchronous unload paths\n * (sonorBeacon) that can't await. In token mode returns the freshest token held;\n * in raw-key mode returns the seed.\n */\nexport function currentCredential(seed?: string): string | undefined {\n if (!state.isToken) return seed ?? state.seed\n return state.token ?? seed ?? state.seed\n}\n"]}