@sonordev/site-kit 6.3.5 → 6.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 (267) hide show
  1. package/agent-manifest.json +8 -2
  2. package/dist/{AnalyticsProvider-2A75HPK6.mjs → AnalyticsProvider-4K5T7OQ7.mjs} +6 -6
  3. package/dist/{AnalyticsProvider-2A75HPK6.mjs.map → AnalyticsProvider-4K5T7OQ7.mjs.map} +1 -1
  4. package/dist/{AnalyticsProvider-2H3POGSJ.js → AnalyticsProvider-QXZN5O6D.js} +10 -10
  5. package/dist/{AnalyticsProvider-2H3POGSJ.js.map → AnalyticsProvider-QXZN5O6D.js.map} +1 -1
  6. package/dist/{ArticleViewTracker-75BLDTHR.mjs → ArticleViewTracker-BUG2TOHS.mjs} +5 -5
  7. package/dist/{ArticleViewTracker-75BLDTHR.mjs.map → ArticleViewTracker-BUG2TOHS.mjs.map} +1 -1
  8. package/dist/{ArticleViewTracker-R3LNZ4A4.js → ArticleViewTracker-FFMHR7AY.js} +6 -6
  9. package/dist/{ArticleViewTracker-R3LNZ4A4.js.map → ArticleViewTracker-FFMHR7AY.js.map} +1 -1
  10. package/dist/{ChatWidget-F6UWIR7I.mjs → ChatWidget-CRQPBDEF.mjs} +7 -7
  11. package/dist/{ChatWidget-F6UWIR7I.mjs.map → ChatWidget-CRQPBDEF.mjs.map} +1 -1
  12. package/dist/{ChatWidget-BLZSLYN2.js → ChatWidget-XFYLVXAX.js} +10 -10
  13. package/dist/{ChatWidget-BLZSLYN2.js.map → ChatWidget-XFYLVXAX.js.map} +1 -1
  14. package/dist/EngageWidget-S234YAT2.js +21 -0
  15. package/dist/{EngageWidget-YONW3W3Z.js.map → EngageWidget-S234YAT2.js.map} +1 -1
  16. package/dist/EngageWidget-SQU4T7CI.mjs +12 -0
  17. package/dist/{EngageWidget-4WB74GQR.mjs.map → EngageWidget-SQU4T7CI.mjs.map} +1 -1
  18. package/dist/{FileField-GXNFYVSY.mjs → FileField-HNEBEBLB.mjs} +5 -5
  19. package/dist/{FileField-GXNFYVSY.mjs.map → FileField-HNEBEBLB.mjs.map} +1 -1
  20. package/dist/{FileField-KYQVN4XB.js → FileField-XG4EYAZW.js} +6 -6
  21. package/dist/{FileField-KYQVN4XB.js.map → FileField-XG4EYAZW.js.map} +1 -1
  22. package/dist/{FormSpotlight-ZOSI7MXH.js → FormSpotlight-IODREOPN.js} +9 -9
  23. package/dist/{FormSpotlight-ZOSI7MXH.js.map → FormSpotlight-IODREOPN.js.map} +1 -1
  24. package/dist/{FormSpotlight-F4YZOWHA.mjs → FormSpotlight-VKD774PZ.mjs} +3 -3
  25. package/dist/{FormSpotlight-F4YZOWHA.mjs.map → FormSpotlight-VKD774PZ.mjs.map} +1 -1
  26. package/dist/{FormStage-DICKOW7R.js → FormStage-37KKZQH4.js} +9 -9
  27. package/dist/{FormStage-DICKOW7R.js.map → FormStage-37KKZQH4.js.map} +1 -1
  28. package/dist/{FormStage-TIQ43MF6.mjs → FormStage-PRZNRYNC.mjs} +3 -3
  29. package/dist/{FormStage-TIQ43MF6.mjs.map → FormStage-PRZNRYNC.mjs.map} +1 -1
  30. package/dist/ManagedForm-4PZOCKCV.mjs +16 -0
  31. package/dist/{ManagedForm-KKJOIEFW.mjs.map → ManagedForm-4PZOCKCV.mjs.map} +1 -1
  32. package/dist/ManagedForm-JCMEE626.js +25 -0
  33. package/dist/{ManagedForm-ZUPV4DPQ.js.map → ManagedForm-JCMEE626.js.map} +1 -1
  34. package/dist/{ManagedNewsletterForm-NFV2JGNA.js → ManagedNewsletterForm-CTC33B2U.js} +7 -7
  35. package/dist/{ManagedNewsletterForm-NFV2JGNA.js.map → ManagedNewsletterForm-CTC33B2U.js.map} +1 -1
  36. package/dist/{ManagedNewsletterForm-JOTIWXLQ.mjs → ManagedNewsletterForm-KMZG24OE.mjs} +6 -6
  37. package/dist/{ManagedNewsletterForm-JOTIWXLQ.mjs.map → ManagedNewsletterForm-KMZG24OE.mjs.map} +1 -1
  38. package/dist/{SignalCore-DLRQTFKK.js → SignalCore-YHT7WPXV.js} +11 -11
  39. package/dist/{SignalCore-DLRQTFKK.js.map → SignalCore-YHT7WPXV.js.map} +1 -1
  40. package/dist/{SignalCore-MUN44S6K.mjs → SignalCore-YIPKKIUG.mjs} +5 -5
  41. package/dist/{SignalCore-MUN44S6K.mjs.map → SignalCore-YIPKKIUG.mjs.map} +1 -1
  42. package/dist/SitemapSync-GN4OXIM2.js +18 -0
  43. package/dist/{SitemapSync-5TKLHXFL.js.map → SitemapSync-GN4OXIM2.js.map} +1 -1
  44. package/dist/SitemapSync-NG763CGN.mjs +9 -0
  45. package/dist/{SitemapSync-BIWCTMRJ.mjs.map → SitemapSync-NG763CGN.mjs.map} +1 -1
  46. package/dist/analytics/AnalyticsProvider.d.ts +0 -6
  47. package/dist/analytics/AnalyticsProvider.d.ts.map +1 -1
  48. package/dist/analytics/WebVitals.d.ts +4 -1
  49. package/dist/analytics/WebVitals.d.ts.map +1 -1
  50. package/dist/analytics/index.js +9 -9
  51. package/dist/analytics/index.mjs +4 -4
  52. package/dist/analytics/measurement-context.d.ts +13 -0
  53. package/dist/analytics/measurement-context.d.ts.map +1 -0
  54. package/dist/analytics/page-observation.d.ts +39 -0
  55. package/dist/analytics/page-observation.d.ts.map +1 -0
  56. package/dist/articles/AuthorPage.d.ts +1 -1
  57. package/dist/articles/AuthorPage.d.ts.map +1 -1
  58. package/dist/articles/author-schema.d.ts +59 -13
  59. package/dist/articles/author-schema.d.ts.map +1 -1
  60. package/dist/articles/index.js +24 -23
  61. package/dist/articles/index.js.map +1 -1
  62. package/dist/articles/index.mjs +12 -11
  63. package/dist/articles/index.mjs.map +1 -1
  64. package/dist/articles/routes.d.ts +9 -0
  65. package/dist/articles/routes.d.ts.map +1 -1
  66. package/dist/articles/server-core.d.ts +5 -2
  67. package/dist/articles/server-core.d.ts.map +1 -1
  68. package/dist/articles/server-ui.js +45 -45
  69. package/dist/articles/server-ui.mjs +8 -8
  70. package/dist/articles/server.d.ts +5 -2
  71. package/dist/articles/server.d.ts.map +1 -1
  72. package/dist/articles/server.js +72 -56
  73. package/dist/articles/server.mjs +3 -3
  74. package/dist/articles/types.d.ts +17 -0
  75. package/dist/articles/types.d.ts.map +1 -1
  76. package/dist/{chunk-H6F7T5Z6.mjs → chunk-24LFAQEG.mjs} +5 -5
  77. package/dist/{chunk-H6F7T5Z6.mjs.map → chunk-24LFAQEG.mjs.map} +1 -1
  78. package/dist/chunk-3AOKYAZ2.js +135 -0
  79. package/dist/chunk-3AOKYAZ2.js.map +1 -0
  80. package/dist/{chunk-QII2TNKS.mjs → chunk-4C3INRHE.mjs} +72 -15
  81. package/dist/chunk-4C3INRHE.mjs.map +1 -0
  82. package/dist/{chunk-ZR43XFUM.js → chunk-55J2KE7D.js} +3 -3
  83. package/dist/chunk-55J2KE7D.js.map +1 -0
  84. package/dist/{chunk-SIQX3GL3.mjs → chunk-5BARLEAI.mjs} +3 -3
  85. package/dist/{chunk-SIQX3GL3.mjs.map → chunk-5BARLEAI.mjs.map} +1 -1
  86. package/dist/{chunk-HTCMZAXW.mjs → chunk-5T3J24K4.mjs} +4 -4
  87. package/dist/{chunk-HTCMZAXW.mjs.map → chunk-5T3J24K4.mjs.map} +1 -1
  88. package/dist/{chunk-RWERWLRM.js → chunk-5W4S7XQD.js} +3 -3
  89. package/dist/{chunk-RWERWLRM.js.map → chunk-5W4S7XQD.js.map} +1 -1
  90. package/dist/{chunk-6DCAMW62.mjs → chunk-6C47333H.mjs} +3 -3
  91. package/dist/chunk-6C47333H.mjs.map +1 -0
  92. package/dist/{chunk-2OA274JZ.mjs → chunk-6QPKC6XP.mjs} +3 -3
  93. package/dist/{chunk-2OA274JZ.mjs.map → chunk-6QPKC6XP.mjs.map} +1 -1
  94. package/dist/{chunk-R7XCEUEI.mjs → chunk-BI7DH42Z.mjs} +4 -4
  95. package/dist/{chunk-R7XCEUEI.mjs.map → chunk-BI7DH42Z.mjs.map} +1 -1
  96. package/dist/{chunk-ID4RGBQT.js → chunk-C7J4XLBN.js} +15 -15
  97. package/dist/{chunk-ID4RGBQT.js.map → chunk-C7J4XLBN.js.map} +1 -1
  98. package/dist/{chunk-LWCAZDY2.js → chunk-CDRQYJUT.js} +4 -4
  99. package/dist/{chunk-LWCAZDY2.js.map → chunk-CDRQYJUT.js.map} +1 -1
  100. package/dist/chunk-CIB3RHNZ.js +8 -0
  101. package/dist/{chunk-LSB3CWSB.js.map → chunk-CIB3RHNZ.js.map} +1 -1
  102. package/dist/{chunk-NCLF7RUL.js → chunk-CUZ3I6CO.js} +5 -5
  103. package/dist/{chunk-NCLF7RUL.js.map → chunk-CUZ3I6CO.js.map} +1 -1
  104. package/dist/{chunk-UEYZXWIP.js → chunk-EWDWLY74.js} +11 -11
  105. package/dist/{chunk-UEYZXWIP.js.map → chunk-EWDWLY74.js.map} +1 -1
  106. package/dist/{chunk-4EMMMKDR.js → chunk-F2GECCK7.js} +12 -12
  107. package/dist/{chunk-4EMMMKDR.js.map → chunk-F2GECCK7.js.map} +1 -1
  108. package/dist/chunk-GFSQMK35.mjs +127 -0
  109. package/dist/chunk-GFSQMK35.mjs.map +1 -0
  110. package/dist/{chunk-XP63NBUV.js → chunk-GGGX54ZE.js} +14 -14
  111. package/dist/{chunk-XP63NBUV.js.map → chunk-GGGX54ZE.js.map} +1 -1
  112. package/dist/{chunk-ZPITTAT5.mjs → chunk-GSU6VTCZ.mjs} +3 -3
  113. package/dist/{chunk-ZPITTAT5.mjs.map → chunk-GSU6VTCZ.mjs.map} +1 -1
  114. package/dist/{chunk-XQPNCDGF.js → chunk-GXSSFQ5D.js} +15 -15
  115. package/dist/{chunk-XQPNCDGF.js.map → chunk-GXSSFQ5D.js.map} +1 -1
  116. package/dist/{chunk-BJ4T65OK.mjs → chunk-H4TSADY3.mjs} +3 -3
  117. package/dist/{chunk-BJ4T65OK.mjs.map → chunk-H4TSADY3.mjs.map} +1 -1
  118. package/dist/{chunk-3UT3XR6M.js → chunk-HSLPN3QK.js} +7 -7
  119. package/dist/{chunk-3UT3XR6M.js.map → chunk-HSLPN3QK.js.map} +1 -1
  120. package/dist/{chunk-QAYB7NCH.js → chunk-HTCNRTN6.js} +129 -69
  121. package/dist/chunk-HTCNRTN6.js.map +1 -0
  122. package/dist/{chunk-Y3SYVAP6.mjs → chunk-JXRGIE3V.mjs} +4 -4
  123. package/dist/{chunk-Y3SYVAP6.mjs.map → chunk-JXRGIE3V.mjs.map} +1 -1
  124. package/dist/chunk-K3PVUXKB.mjs +6 -0
  125. package/dist/{chunk-7JGD5TUI.mjs.map → chunk-K3PVUXKB.mjs.map} +1 -1
  126. package/dist/{chunk-L2KN3GYW.mjs → chunk-KOPFFKG2.mjs} +3 -3
  127. package/dist/{chunk-L2KN3GYW.mjs.map → chunk-KOPFFKG2.mjs.map} +1 -1
  128. package/dist/{chunk-LP6KJPAL.js → chunk-KZBDH5A3.js} +4 -4
  129. package/dist/{chunk-LP6KJPAL.js.map → chunk-KZBDH5A3.js.map} +1 -1
  130. package/dist/{chunk-DJAJB6AZ.mjs → chunk-LPDXS2B2.mjs} +3 -3
  131. package/dist/{chunk-DJAJB6AZ.mjs.map → chunk-LPDXS2B2.mjs.map} +1 -1
  132. package/dist/{chunk-I5X7BX6I.mjs → chunk-M3JCHPK3.mjs} +127 -67
  133. package/dist/chunk-M3JCHPK3.mjs.map +1 -0
  134. package/dist/{chunk-RSAQ7WGF.mjs → chunk-M62L2N5A.mjs} +3 -3
  135. package/dist/{chunk-RSAQ7WGF.mjs.map → chunk-M62L2N5A.mjs.map} +1 -1
  136. package/dist/{chunk-G3L26XQ7.js → chunk-OIPG3EGM.js} +5 -5
  137. package/dist/{chunk-G3L26XQ7.js.map → chunk-OIPG3EGM.js.map} +1 -1
  138. package/dist/{chunk-X3MY4PL7.mjs → chunk-QAQTDHHT.mjs} +10 -14
  139. package/dist/chunk-QAQTDHHT.mjs.map +1 -0
  140. package/dist/{chunk-VFXLB5JU.mjs → chunk-QOKMH2TD.mjs} +3 -3
  141. package/dist/{chunk-VFXLB5JU.mjs.map → chunk-QOKMH2TD.mjs.map} +1 -1
  142. package/dist/{chunk-HXHRS3PX.mjs → chunk-RCB6HEHM.mjs} +3 -3
  143. package/dist/{chunk-HXHRS3PX.mjs.map → chunk-RCB6HEHM.mjs.map} +1 -1
  144. package/dist/{chunk-NAA5J2JM.js → chunk-RQROZLH3.js} +47 -51
  145. package/dist/chunk-RQROZLH3.js.map +1 -0
  146. package/dist/{chunk-PHE5MIW4.js → chunk-S5QKNXLQ.js} +10 -10
  147. package/dist/{chunk-PHE5MIW4.js.map → chunk-S5QKNXLQ.js.map} +1 -1
  148. package/dist/{chunk-MERNU3MY.mjs → chunk-SBTA6B6Y.mjs} +4 -4
  149. package/dist/{chunk-MERNU3MY.mjs.map → chunk-SBTA6B6Y.mjs.map} +1 -1
  150. package/dist/{chunk-CPRCBO6Y.mjs → chunk-TPDDSEL3.mjs} +3 -3
  151. package/dist/{chunk-CPRCBO6Y.mjs.map → chunk-TPDDSEL3.mjs.map} +1 -1
  152. package/dist/{chunk-C4JCJ66V.js → chunk-U4CYO4NG.js} +5 -5
  153. package/dist/{chunk-C4JCJ66V.js.map → chunk-U4CYO4NG.js.map} +1 -1
  154. package/dist/{chunk-BVSTYFFI.js → chunk-U5NFIHLY.js} +4 -4
  155. package/dist/{chunk-BVSTYFFI.js.map → chunk-U5NFIHLY.js.map} +1 -1
  156. package/dist/{chunk-4KTPBCB3.mjs → chunk-USQPRLSG.mjs} +8 -8
  157. package/dist/{chunk-4KTPBCB3.mjs.map → chunk-USQPRLSG.mjs.map} +1 -1
  158. package/dist/{chunk-RR6U3Z76.js → chunk-VDEQVXNV.js} +3 -3
  159. package/dist/{chunk-RR6U3Z76.js.map → chunk-VDEQVXNV.js.map} +1 -1
  160. package/dist/{chunk-WYUYAMFL.js → chunk-VMNPLCZB.js} +14 -14
  161. package/dist/{chunk-WYUYAMFL.js.map → chunk-VMNPLCZB.js.map} +1 -1
  162. package/dist/{chunk-5EVGWCUB.js → chunk-W7IAG5YD.js} +3 -3
  163. package/dist/{chunk-5EVGWCUB.js.map → chunk-W7IAG5YD.js.map} +1 -1
  164. package/dist/{chunk-K5Q7UYZM.mjs → chunk-WIWHM47B.mjs} +3 -3
  165. package/dist/{chunk-K5Q7UYZM.mjs.map → chunk-WIWHM47B.mjs.map} +1 -1
  166. package/dist/{chunk-KRU755E3.js → chunk-XZHR4E2D.js} +8 -8
  167. package/dist/{chunk-KRU755E3.js.map → chunk-XZHR4E2D.js.map} +1 -1
  168. package/dist/{chunk-EW2GKDDC.js → chunk-YACU35MY.js} +9 -9
  169. package/dist/{chunk-EW2GKDDC.js.map → chunk-YACU35MY.js.map} +1 -1
  170. package/dist/chunk-YMZNEDFP.js +39 -0
  171. package/dist/chunk-YMZNEDFP.js.map +1 -0
  172. package/dist/{chunk-QMVBKSTZ.mjs → chunk-YRKWJCML.mjs} +3 -3
  173. package/dist/{chunk-QMVBKSTZ.mjs.map → chunk-YRKWJCML.mjs.map} +1 -1
  174. package/dist/chunk-Z2URIOKG.mjs +36 -0
  175. package/dist/chunk-Z2URIOKG.mjs.map +1 -0
  176. package/dist/{chunk-S2LJ26WQ.mjs → chunk-ZEBOOUBP.mjs} +3 -3
  177. package/dist/{chunk-S2LJ26WQ.mjs.map → chunk-ZEBOOUBP.mjs.map} +1 -1
  178. package/dist/{chunk-QXD7AWGV.js → chunk-ZOBUA65N.js} +74 -17
  179. package/dist/chunk-ZOBUA65N.js.map +1 -0
  180. package/dist/cli/index.js +3 -3
  181. package/dist/cli/index.mjs +1 -1
  182. package/dist/client/index.js +4 -4
  183. package/dist/client/index.mjs +3 -3
  184. package/dist/commerce/index.js +57 -57
  185. package/dist/commerce/index.mjs +5 -5
  186. package/dist/engage/DesignRenderer.d.ts.map +1 -1
  187. package/dist/engage/EngageWidget.d.ts.map +1 -1
  188. package/dist/engage/element-rules.d.ts +39 -0
  189. package/dist/engage/element-rules.d.ts.map +1 -0
  190. package/dist/engage/index.js +9 -9
  191. package/dist/engage/index.mjs +6 -6
  192. package/dist/fleet/index.js +8 -8
  193. package/dist/fleet/index.mjs +4 -4
  194. package/dist/forms/index.js +13 -13
  195. package/dist/forms/index.mjs +7 -7
  196. package/dist/forms/server.js +2 -2
  197. package/dist/forms/server.mjs +1 -1
  198. package/dist/images/index.js +11 -11
  199. package/dist/images/index.mjs +4 -4
  200. package/dist/index.js +63 -63
  201. package/dist/index.mjs +9 -9
  202. package/dist/layout/client.js +8 -8
  203. package/dist/layout/client.mjs +7 -7
  204. package/dist/layout/index.js +10 -10
  205. package/dist/layout/index.mjs +8 -8
  206. package/dist/llms/index.d.ts +2 -0
  207. package/dist/llms/index.d.ts.map +1 -1
  208. package/dist/llms/index.js +129 -14
  209. package/dist/llms/index.js.map +1 -1
  210. package/dist/llms/index.mjs +115 -5
  211. package/dist/llms/index.mjs.map +1 -1
  212. package/dist/llms/revalidate.d.ts +3 -0
  213. package/dist/llms/revalidate.d.ts.map +1 -1
  214. package/dist/llms/seo-revalidate.d.ts +105 -0
  215. package/dist/llms/seo-revalidate.d.ts.map +1 -0
  216. package/dist/maps/index.js +5 -5
  217. package/dist/maps/index.mjs +3 -3
  218. package/dist/mcp/transport.d.ts +166 -0
  219. package/dist/mcp/transport.d.ts.map +1 -0
  220. package/dist/mcp/transport.js +137 -0
  221. package/dist/mcp/transport.js.map +1 -0
  222. package/dist/mcp/transport.mjs +123 -0
  223. package/dist/mcp/transport.mjs.map +1 -0
  224. package/dist/search/index.js +4 -4
  225. package/dist/search/index.mjs +3 -3
  226. package/dist/seo/client.js +5 -5
  227. package/dist/seo/client.mjs +4 -4
  228. package/dist/seo/index.js +5 -5
  229. package/dist/seo/index.mjs +4 -4
  230. package/dist/seo/register-sitemap-cli.js +1 -1
  231. package/dist/seo/register-sitemap-cli.mjs +1 -1
  232. package/dist/server/index.js +3 -3
  233. package/dist/server/index.mjs +2 -2
  234. package/dist/shared/build-entries.d.ts +7 -0
  235. package/dist/shared/build-entries.d.ts.map +1 -1
  236. package/dist/shared/request-guards.d.ts +27 -0
  237. package/dist/shared/request-guards.d.ts.map +1 -0
  238. package/dist/shared/version.d.ts +1 -1
  239. package/dist/signal/index.js +11 -11
  240. package/dist/signal/index.mjs +2 -2
  241. package/dist/sitemap/index.js +3 -3
  242. package/dist/sitemap/index.mjs +2 -2
  243. package/dist/sync/index.js +17 -17
  244. package/dist/sync/index.mjs +4 -4
  245. package/dist/{writeLLMsTxt-LYYC3ZSN.mjs → writeLLMsTxt-IYJILIU3.mjs} +3 -3
  246. package/dist/{writeLLMsTxt-XHLHJ5XY.js → writeLLMsTxt-SQG4B63X.js} +4 -4
  247. package/package.json +6 -1
  248. package/dist/EngageWidget-4WB74GQR.mjs +0 -12
  249. package/dist/EngageWidget-YONW3W3Z.js +0 -21
  250. package/dist/ManagedForm-KKJOIEFW.mjs +0 -16
  251. package/dist/ManagedForm-ZUPV4DPQ.js +0 -25
  252. package/dist/SitemapSync-5TKLHXFL.js +0 -18
  253. package/dist/SitemapSync-BIWCTMRJ.mjs +0 -9
  254. package/dist/chunk-6DCAMW62.mjs.map +0 -1
  255. package/dist/chunk-7JGD5TUI.mjs +0 -6
  256. package/dist/chunk-I5X7BX6I.mjs.map +0 -1
  257. package/dist/chunk-LSB3CWSB.js +0 -8
  258. package/dist/chunk-NAA5J2JM.js.map +0 -1
  259. package/dist/chunk-QAYB7NCH.js.map +0 -1
  260. package/dist/chunk-QII2TNKS.mjs.map +0 -1
  261. package/dist/chunk-QXD7AWGV.js.map +0 -1
  262. package/dist/chunk-X3MY4PL7.mjs.map +0 -1
  263. package/dist/chunk-YRCR7WY3.js +0 -65
  264. package/dist/chunk-YRCR7WY3.js.map +0 -1
  265. package/dist/chunk-ZL35JCDA.mjs +0 -61
  266. package/dist/chunk-ZL35JCDA.mjs.map +0 -1
  267. package/dist/chunk-ZR43XFUM.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/siteToken.ts"],"names":[],"mappings":"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","file":"chunk-QMVBKSTZ.mjs","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":[],"mappings":"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","file":"chunk-YRKWJCML.mjs","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"]}
@@ -0,0 +1,36 @@
1
+ import { timingSafeEqual } from 'crypto';
2
+
3
+ // src/shared/request-guards.ts
4
+ function secretsMatch(supplied, expected) {
5
+ if (!expected || !supplied) return false;
6
+ const a = Buffer.from(supplied);
7
+ const b = Buffer.from(expected);
8
+ return a.length === b.length && timingSafeEqual(a, b);
9
+ }
10
+ async function readCappedBody(request, maxBytes) {
11
+ const reader = request.body?.getReader();
12
+ if (!reader) return new Uint8Array(0);
13
+ const chunks = [];
14
+ let size = 0;
15
+ for (; ; ) {
16
+ const { done, value } = await reader.read();
17
+ if (done) break;
18
+ size += value.byteLength;
19
+ if (size > maxBytes) {
20
+ await reader.cancel();
21
+ return null;
22
+ }
23
+ chunks.push(value);
24
+ }
25
+ const body = new Uint8Array(size);
26
+ let offset = 0;
27
+ for (const chunk of chunks) {
28
+ body.set(chunk, offset);
29
+ offset += chunk.byteLength;
30
+ }
31
+ return body;
32
+ }
33
+
34
+ export { readCappedBody, secretsMatch };
35
+ //# sourceMappingURL=chunk-Z2URIOKG.mjs.map
36
+ //# sourceMappingURL=chunk-Z2URIOKG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/request-guards.ts"],"names":[],"mappings":";;;AAoBO,SAAS,YAAA,CAAa,UAAqC,QAAA,EAA8C;AAC9G,EAAA,IAAI,CAAC,QAAA,IAAY,CAAC,QAAA,EAAU,OAAO,KAAA;AACnC,EAAA,MAAM,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA;AAC9B,EAAA,MAAM,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA;AAC9B,EAAA,OAAO,EAAE,MAAA,KAAW,CAAA,CAAE,MAAA,IAAU,eAAA,CAAgB,GAAG,CAAC,CAAA;AACtD;AASA,eAAsB,cAAA,CAAe,SAAkB,QAAA,EAA2D;AAChH,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,IAAA,EAAM,SAAA,EAAU;AACvC,EAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,IAAI,WAAW,CAAC,CAAA;AACpC,EAAA,MAAM,SAAuB,EAAC;AAC9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,WAAS;AACP,IAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,OAAO,IAAA,EAAK;AAC1C,IAAA,IAAI,IAAA,EAAM;AACV,IAAA,IAAA,IAAQ,KAAA,CAAM,UAAA;AACd,IAAA,IAAI,OAAO,QAAA,EAAU;AACnB,MAAA,MAAM,OAAO,MAAA,EAAO;AACpB,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA,EACnB;AACA,EAAA,MAAM,IAAA,GAAO,IAAI,UAAA,CAAW,IAAI,CAAA;AAChC,EAAA,IAAI,MAAA,GAAS,CAAA;AACb,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAA,CAAK,GAAA,CAAI,OAAO,MAAM,CAAA;AACtB,IAAA,MAAA,IAAU,KAAA,CAAM,UAAA;AAAA,EAClB;AACA,EAAA,OAAO,IAAA;AACT","file":"chunk-Z2URIOKG.mjs","sourcesContent":["/**\n * @sonordev/site-kit shared - Request guards for secret-bearing route handlers\n *\n * Node only (`crypto`). Single source of truth for the two checks every\n * webhook-style handler in the kit needs, so no module grows its own copy:\n *\n * - `secretsMatch`: constant-time secret comparison. Used by\n * `createLlmsRevalidateHandler` and `createSeoRevalidationHandler`.\n * - `readCappedBody`: read a request body without buffering past a cap. Used\n * by `createSeoRevalidationHandler` (16 KB) and the MCP transport relay\n * (64 KB).\n */\n\nimport { timingSafeEqual } from 'crypto'\n\n/**\n * Constant-time equality for a supplied secret against the expected one.\n * An empty expected secret never matches, so a misconfigured site (no env\n * var) refuses everything instead of accepting an empty token.\n */\nexport function secretsMatch(supplied: string | null | undefined, expected: string | null | undefined): boolean {\n if (!expected || !supplied) return false\n const a = Buffer.from(supplied)\n const b = Buffer.from(expected)\n return a.length === b.length && timingSafeEqual(a, b)\n}\n\n/**\n * Read a request body, giving up at the first chunk past `maxBytes`.\n *\n * Returns the bytes, or `null` when the body is larger than the cap (the\n * stream is cancelled, so nothing past the cap is held in memory). A request\n * with no body returns an empty array.\n */\nexport async function readCappedBody(request: Request, maxBytes: number): Promise<Uint8Array<ArrayBuffer> | null> {\n const reader = request.body?.getReader()\n if (!reader) return new Uint8Array(0)\n const chunks: Uint8Array[] = []\n let size = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n size += value.byteLength\n if (size > maxBytes) {\n await reader.cancel()\n return null\n }\n chunks.push(value)\n }\n const body = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) {\n body.set(chunk, offset)\n offset += chunk.byteLength\n }\n return body\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { resolveFetch } from './chunk-BQP6JIE2.mjs';
2
- import { SITE_KIT_VERSION } from './chunk-7JGD5TUI.mjs';
2
+ import { SITE_KIT_VERSION } from './chunk-K3PVUXKB.mjs';
3
3
  import 'server-only';
4
4
 
5
5
  var MINT_PATH = "/api/public/site-token";
@@ -124,5 +124,5 @@ async function mintWithRetry(rawKey, apiUrl) {
124
124
  }
125
125
 
126
126
  export { resolveClientCredential };
127
- //# sourceMappingURL=chunk-S2LJ26WQ.mjs.map
128
- //# sourceMappingURL=chunk-S2LJ26WQ.mjs.map
127
+ //# sourceMappingURL=chunk-ZEBOOUBP.mjs.map
128
+ //# sourceMappingURL=chunk-ZEBOOUBP.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/mint-site-token.ts"],"names":[],"mappings":"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","file":"chunk-S2LJ26WQ.mjs","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'\nimport { resolveFetch } from '../shared/fresh-fetch'\n\n// Re-exported for this module's unit tests; the helper lives in shared/fresh-fetch.\nexport { resolveFetch }\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\n/*\n * The mint uses Next's saved original fetch (resolveFetch, shared/fresh-fetch.ts,\n * which documents why). A credential is not page data: `cache: 'no-store'` would\n * bail a static route before the request goes out (verified on Next 16.2.9: the\n * mint endpoint received zero requests for a static build, and every page\n * shipped the raw key), and a Data Cache entry would let a fresh deploy re-bake a\n * months-old seed. The module-level memo above is the only cache we want: one\n * mint per build process, and a build-fresh seed on every deploy.\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":[],"mappings":"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","file":"chunk-ZEBOOUBP.mjs","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'\nimport { resolveFetch } from '../shared/fresh-fetch'\n\n// Re-exported for this module's unit tests; the helper lives in shared/fresh-fetch.\nexport { resolveFetch }\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\n/*\n * The mint uses Next's saved original fetch (resolveFetch, shared/fresh-fetch.ts,\n * which documents why). A credential is not page data: `cache: 'no-store'` would\n * bail a static route before the request goes out (verified on Next 16.2.9: the\n * mint endpoint received zero requests for a static build, and every page\n * shipped the raw key), and a Data Cache entry would let a fresh deploy re-bake a\n * months-old seed. The module-level memo above is the only cache we want: one\n * mint per build process, and a build-fresh seed on every deploy.\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"]}
@@ -5,17 +5,72 @@ var chunk3DWIYCQX_js = require('./chunk-3DWIYCQX.js');
5
5
  var chunkRC4WWQXR_js = require('./chunk-RC4WWQXR.js');
6
6
  var chunkP4Y75QSU_js = require('./chunk-P4Y75QSU.js');
7
7
  var chunk5RNAUYWE_js = require('./chunk-5RNAUYWE.js');
8
- var chunkID4RGBQT_js = require('./chunk-ID4RGBQT.js');
8
+ var chunkC7J4XLBN_js = require('./chunk-C7J4XLBN.js');
9
9
  var react = require('react');
10
10
  var navigation = require('next/navigation');
11
11
  var jsxRuntime = require('react/jsx-runtime');
12
12
 
13
+ // src/analytics/page-observation.ts
14
+ function collectPageObservation() {
15
+ const observedAt = (/* @__PURE__ */ new Date()).toISOString();
16
+ const observed = (value, source) => ({ state: "observed", value, source });
17
+ const unknown = { state: "not_checked", value: null, source: null };
18
+ const nav = performance.getEntriesByType("navigation")[0];
19
+ const samePage = nav && new URL(nav.name).pathname === location.pathname;
20
+ const status = samePage && nav.responseStatus >= 100 ? nav.responseStatus : null;
21
+ const schema = Array.from(document.querySelectorAll('script[type="application/ld+json"]')).map((el) => {
22
+ try {
23
+ return { validJson: true, value: JSON.parse(el.textContent || "") };
24
+ } catch {
25
+ return { validJson: false, value: null };
26
+ }
27
+ });
28
+ return {
29
+ version: 1,
30
+ observedAt,
31
+ pageUrl: location.origin + location.pathname,
32
+ source: "site-kit/browser",
33
+ fields: {
34
+ httpStatus: status === null ? unknown : observed(status, "navigation.responseStatus"),
35
+ canonical: observed(document.querySelector('link[rel="canonical"]')?.getAttribute("href") || null, "DOM link[rel=canonical]"),
36
+ robots: observed(document.querySelector('meta[name="robots"]')?.getAttribute("content") || null, "DOM meta[name=robots]"),
37
+ xRobotsTag: unknown,
38
+ schema: observed(schema, "DOM script[type=application/ld+json]")
39
+ }
40
+ };
41
+ }
42
+
43
+ // src/analytics/measurement-context.ts
44
+ function getDeviceType() {
45
+ if (typeof window === "undefined") return "desktop";
46
+ const ua = navigator.userAgent;
47
+ if (/tablet|ipad|playbook|silk/i.test(ua)) return "tablet";
48
+ if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return "mobile";
49
+ return "desktop";
50
+ }
51
+ function getMeasurementContext(options = {}) {
52
+ let visitorId = options.visitorId;
53
+ let sessionId = options.sessionId;
54
+ try {
55
+ visitorId || (visitorId = chunkRC4WWQXR_js.getOrCreateVisitorId());
56
+ sessionId || (sessionId = chunkRC4WWQXR_js.getOrCreateSessionId(options.sessionTimeout));
57
+ } catch {
58
+ }
59
+ const connection = typeof navigator === "undefined" ? void 0 : navigator.connection;
60
+ return {
61
+ visitorId: visitorId || void 0,
62
+ sessionId: sessionId || void 0,
63
+ deviceType: getDeviceType(),
64
+ connectionType: connection?.effectiveType || connection?.type || void 0
65
+ };
66
+ }
67
+
13
68
  // src/analytics/send-gate.ts
14
69
  async function analyticsSend(path, body, options = {}) {
15
70
  const target = chunk5RNAUYWE_js.resolveAnalyticsTarget(options);
16
71
  if (!target) return null;
17
72
  try {
18
- return await chunkID4RGBQT_js.sonorFetch(
73
+ return await chunkC7J4XLBN_js.sonorFetch(
19
74
  `${target.apiUrl}${path}`,
20
75
  {
21
76
  method: options.method || "POST",
@@ -32,7 +87,7 @@ async function analyticsSend(path, body, options = {}) {
32
87
  function analyticsBeacon(path, body, options = {}) {
33
88
  const target = chunk5RNAUYWE_js.resolveAnalyticsTarget(options);
34
89
  if (!target) return;
35
- chunkID4RGBQT_js.sonorBeacon(`${target.apiUrl}${path}`, body, target.apiKey);
90
+ chunkC7J4XLBN_js.sonorBeacon(`${target.apiUrl}${path}`, body, target.apiKey);
36
91
  }
37
92
 
38
93
  // src/analytics/WebVitals.tsx
@@ -92,24 +147,29 @@ function WebVitals({
92
147
  site: propSite,
93
148
  allowInFrame = false,
94
149
  allowLocalhost = false,
95
- debug = false
150
+ debug = false,
151
+ visitorId,
152
+ sessionId,
153
+ sessionTimeout
96
154
  }) {
97
155
  const pathname = navigation.usePathname();
98
156
  const landingPathRef = react.useRef(pathname || "/");
99
- const propsRef = react.useRef({ propApiUrl, propApiKey, propSite, allowInFrame, allowLocalhost, debug });
100
- propsRef.current = { propApiUrl, propApiKey, propSite, allowInFrame, allowLocalhost, debug };
157
+ const propsRef = react.useRef({ propApiUrl, propApiKey, propSite, allowInFrame, allowLocalhost, debug, visitorId, sessionId, sessionTimeout });
158
+ propsRef.current = { propApiUrl, propApiKey, propSite, allowInFrame, allowLocalhost, debug, visitorId, sessionId, sessionTimeout };
101
159
  react.useEffect(() => {
102
160
  let cancelled = false;
103
161
  let teardown;
104
162
  import('./web-vitals.attribution-SQVZNHN3.js').then(({ onCLS, onLCP, onTTFB, onINP, onFCP }) => {
105
163
  if (cancelled) return;
106
- const { propApiUrl: propApiUrl2, propApiKey: propApiKey2, propSite: propSite2, allowInFrame: allowInFrame2, allowLocalhost: allowLocalhost2, debug: debug2 } = propsRef.current;
164
+ const { propApiUrl: propApiUrl2, propApiKey: propApiKey2, propSite: propSite2, allowInFrame: allowInFrame2, allowLocalhost: allowLocalhost2, debug: debug2, visitorId: visitorId2, sessionId: sessionId2, sessionTimeout: sessionTimeout2 } = propsRef.current;
107
165
  const sent = /* @__PURE__ */ new Map();
108
166
  const report = (metric, viaBeacon = false) => {
167
+ if (!chunk5RNAUYWE_js.resolveAnalyticsTarget({ apiUrl: propApiUrl2, apiKey: propApiKey2, allowInFrame: allowInFrame2, allowLocalhost: allowLocalhost2, debug: debug2 })) return;
109
168
  if (sent.get(metric.id) === metric.value) return;
110
169
  sent.set(metric.id, metric.value);
111
170
  const site = propSite2 || (typeof window !== "undefined" ? window.__SITE_KIT_SITE__ || window.location.host : "");
112
171
  const data = {
172
+ ...getMeasurementContext(propsRef.current),
113
173
  site,
114
174
  pagePath: pathForMetric(metric, landingPathRef.current),
115
175
  metricName: metric.name,
@@ -123,7 +183,7 @@ function WebVitals({
123
183
  };
124
184
  if (debug2) console.log("[Analytics] Web Vital:", data);
125
185
  const endpoint = "/api/public/analytics/web-vitals";
126
- const gate = { apiUrl: propApiUrl2, apiKey: propApiKey2, allowInFrame: allowInFrame2, allowLocalhost: allowLocalhost2, debug: debug2 };
186
+ const gate = { apiUrl: propApiUrl2, apiKey: propApiKey2, allowInFrame: allowInFrame2, allowLocalhost: allowLocalhost2, debug: debug2, visitorId: visitorId2, sessionId: sessionId2, sessionTimeout: sessionTimeout2 };
127
187
  if (viaBeacon) {
128
188
  analyticsBeacon(endpoint, data, gate);
129
189
  return;
@@ -170,13 +230,6 @@ function scheduleIdleTask(callback, timeout = 2e3) {
170
230
  setTimeout(callback, 300);
171
231
  }
172
232
  }
173
- function getDeviceType() {
174
- if (typeof window === "undefined") return "desktop";
175
- const ua = navigator.userAgent;
176
- if (/tablet|ipad|playbook|silk/i.test(ua)) return "tablet";
177
- if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return "mobile";
178
- return "desktop";
179
- }
180
233
  function getBrowser() {
181
234
  if (typeof window === "undefined") return "unknown";
182
235
  const ua = navigator.userAgent;
@@ -262,6 +315,7 @@ function getLightPageMetadata() {
262
315
  return document.querySelectorAll("h1").length;
263
316
  };
264
317
  return {
318
+ observation: collectPageObservation(),
265
319
  metaDescription: getMeta("description"),
266
320
  canonical: getCanonical(),
267
321
  robots: getRobots(),
@@ -990,6 +1044,9 @@ function AnalyticsProvider({
990
1044
  apiUrl: propApiUrl,
991
1045
  apiKey: propApiKey,
992
1046
  site,
1047
+ visitorId: externalVisitorId,
1048
+ sessionId: externalSessionId,
1049
+ sessionTimeout,
993
1050
  allowInFrame,
994
1051
  allowLocalhost,
995
1052
  debug
@@ -1018,5 +1075,5 @@ exports.WebVitals = WebVitals;
1018
1075
  exports.useAnalytics = useAnalytics;
1019
1076
  exports.useAnalyticsOptional = useAnalyticsOptional;
1020
1077
  exports.useTrackEvent = useTrackEvent;
1021
- //# sourceMappingURL=chunk-QXD7AWGV.js.map
1022
- //# sourceMappingURL=chunk-QXD7AWGV.js.map
1078
+ //# sourceMappingURL=chunk-ZOBUA65N.js.map
1079
+ //# sourceMappingURL=chunk-ZOBUA65N.js.map