@sonordev/site-kit 3.0.4 → 3.2.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 (227) hide show
  1. package/AGENTS.md +107 -0
  2. package/agent-manifest.json +377 -0
  3. package/dist/AnalyticsProvider-CD635CHQ.mjs +8 -0
  4. package/dist/{AnalyticsProvider-MLT26JRP.mjs.map → AnalyticsProvider-CD635CHQ.mjs.map} +1 -1
  5. package/dist/AnalyticsProvider-WK2Z6AIN.js +29 -0
  6. package/dist/{AnalyticsProvider-WTOGN7LV.js.map → AnalyticsProvider-WK2Z6AIN.js.map} +1 -1
  7. package/dist/BlogPostFaqSection-4AD2NIGG.js +13 -0
  8. package/dist/{BlogPostFaqSection-H556RV62.js.map → BlogPostFaqSection-4AD2NIGG.js.map} +1 -1
  9. package/dist/{BlogPostFaqSection-VSHOKKUD.mjs → BlogPostFaqSection-TU6YEC7P.mjs} +3 -3
  10. package/dist/{BlogPostFaqSection-VSHOKKUD.mjs.map → BlogPostFaqSection-TU6YEC7P.mjs.map} +1 -1
  11. package/dist/ChatWidget-AIOOZY6H.js +17 -0
  12. package/dist/{ChatWidget-MAL7FB4M.js.map → ChatWidget-AIOOZY6H.js.map} +1 -1
  13. package/dist/ChatWidget-UAAWDBNS.mjs +8 -0
  14. package/dist/{ChatWidget-QPZTPSPE.mjs.map → ChatWidget-UAAWDBNS.mjs.map} +1 -1
  15. package/dist/EngageWidget-EZ5T6H3K.mjs +7 -0
  16. package/dist/{EngageWidget-IPBUA4DW.mjs.map → EngageWidget-EZ5T6H3K.mjs.map} +1 -1
  17. package/dist/EngageWidget-RWVWW56C.js +16 -0
  18. package/dist/{EngageWidget-N3CABK3L.js.map → EngageWidget-RWVWW56C.js.map} +1 -1
  19. package/dist/{SignalCore-4MDIJUAP.mjs → SignalCore-4B4RNYLV.mjs} +60 -47
  20. package/dist/SignalCore-4B4RNYLV.mjs.map +1 -0
  21. package/dist/{SignalCore-ZMFUE3N4.js → SignalCore-AECW2JFP.js} +60 -47
  22. package/dist/SignalCore-AECW2JFP.js.map +1 -0
  23. package/dist/SitemapSync-AICU4SSV.js +15 -0
  24. package/dist/{SitemapSync-FUCH6Y6J.js.map → SitemapSync-AICU4SSV.js.map} +1 -1
  25. package/dist/SitemapSync-CIZCAZXK.mjs +6 -0
  26. package/dist/{SitemapSync-LCSPRAWU.mjs.map → SitemapSync-CIZCAZXK.mjs.map} +1 -1
  27. package/dist/analytics/index.js +11 -9
  28. package/dist/analytics/index.mjs +4 -2
  29. package/dist/blog/index.js +12 -12
  30. package/dist/blog/index.mjs +2 -2
  31. package/dist/blog/server-ui.js +5 -5
  32. package/dist/blog/server-ui.mjs +1 -1
  33. package/dist/brand.css +8 -3
  34. package/dist/{chunk-QTWA44S3.mjs → chunk-2WYEJOGA.mjs} +7 -8
  35. package/dist/chunk-2WYEJOGA.mjs.map +1 -0
  36. package/dist/{chunk-AN4ATCYQ.mjs → chunk-45QHDA7Q.mjs} +3 -3
  37. package/dist/{chunk-AN4ATCYQ.mjs.map → chunk-45QHDA7Q.mjs.map} +1 -1
  38. package/dist/{chunk-RI6JZUAE.js → chunk-4FBV5BJW.js} +7 -8
  39. package/dist/chunk-4FBV5BJW.js.map +1 -0
  40. package/dist/{chunk-YJWKHCOB.js → chunk-4NTMIU6V.js} +13 -11
  41. package/dist/chunk-4NTMIU6V.js.map +1 -0
  42. package/dist/{chunk-YFGKIKXA.mjs → chunk-57QV6NIY.mjs} +3 -3
  43. package/dist/{chunk-YFGKIKXA.mjs.map → chunk-57QV6NIY.mjs.map} +1 -1
  44. package/dist/chunk-5OWZUNNE.js +102 -0
  45. package/dist/chunk-5OWZUNNE.js.map +1 -0
  46. package/dist/{chunk-EUDTPXUI.mjs → chunk-6HPNFWSC.mjs} +3 -3
  47. package/dist/{chunk-EUDTPXUI.mjs.map → chunk-6HPNFWSC.mjs.map} +1 -1
  48. package/dist/{chunk-ZM2ALSBM.js → chunk-7TX5BKVA.js} +18 -7
  49. package/dist/chunk-7TX5BKVA.js.map +1 -0
  50. package/dist/{chunk-FT5JNCD4.mjs → chunk-AB2VYIYU.mjs} +11 -11
  51. package/dist/{chunk-FT5JNCD4.mjs.map → chunk-AB2VYIYU.mjs.map} +1 -1
  52. package/dist/{chunk-I5TJIESQ.js → chunk-BMKZHXWQ.js} +11 -11
  53. package/dist/{chunk-I5TJIESQ.js.map → chunk-BMKZHXWQ.js.map} +1 -1
  54. package/dist/{chunk-KHFNPPOX.mjs → chunk-DSRPKUQI.mjs} +6 -6
  55. package/dist/{chunk-KHFNPPOX.mjs.map → chunk-DSRPKUQI.mjs.map} +1 -1
  56. package/dist/chunk-EAASMIWX.js +258 -0
  57. package/dist/chunk-EAASMIWX.js.map +1 -0
  58. package/dist/{chunk-T4QBSRA4.mjs → chunk-EIWRPGOK.mjs} +37 -60
  59. package/dist/chunk-EIWRPGOK.mjs.map +1 -0
  60. package/dist/{chunk-O7E6QVK3.js → chunk-GCDVKOSM.js} +3 -3
  61. package/dist/{chunk-O7E6QVK3.js.map → chunk-GCDVKOSM.js.map} +1 -1
  62. package/dist/{chunk-KMZ527EM.mjs → chunk-GN62H6EG.mjs} +34 -8
  63. package/dist/chunk-GN62H6EG.mjs.map +1 -0
  64. package/dist/{chunk-UJQYSVYV.js → chunk-GQF6QAQS.js} +4 -4
  65. package/dist/{chunk-UJQYSVYV.js.map → chunk-GQF6QAQS.js.map} +1 -1
  66. package/dist/{chunk-MLJXELPA.js → chunk-HAZN43I6.js} +60 -94
  67. package/dist/chunk-HAZN43I6.js.map +1 -0
  68. package/dist/chunk-LZSCCJ4O.mjs +253 -0
  69. package/dist/chunk-LZSCCJ4O.mjs.map +1 -0
  70. package/dist/{chunk-TCVHI3JP.mjs → chunk-N2HR2E4Z.mjs} +60 -92
  71. package/dist/chunk-N2HR2E4Z.mjs.map +1 -0
  72. package/dist/{chunk-VCWSSGCU.js → chunk-NAVDN4AE.js} +7 -7
  73. package/dist/{chunk-VCWSSGCU.js.map → chunk-NAVDN4AE.js.map} +1 -1
  74. package/dist/chunk-NDHFU4HV.mjs +97 -0
  75. package/dist/chunk-NDHFU4HV.mjs.map +1 -0
  76. package/dist/{chunk-PST3NOEM.mjs → chunk-O237KKCX.mjs} +4 -4
  77. package/dist/{chunk-PST3NOEM.mjs.map → chunk-O237KKCX.mjs.map} +1 -1
  78. package/dist/{chunk-5AESOLXS.mjs → chunk-OTY7EWET.mjs} +13 -11
  79. package/dist/chunk-OTY7EWET.mjs.map +1 -0
  80. package/dist/chunk-Q2NBD54V.js +69 -0
  81. package/dist/chunk-Q2NBD54V.js.map +1 -0
  82. package/dist/{chunk-DGHPOWLU.js → chunk-R5A64DCX.js} +8 -8
  83. package/dist/{chunk-DGHPOWLU.js.map → chunk-R5A64DCX.js.map} +1 -1
  84. package/dist/{chunk-3YQCPLGP.mjs → chunk-RFIWVH3D.mjs} +41 -79
  85. package/dist/chunk-RFIWVH3D.mjs.map +1 -0
  86. package/dist/{chunk-NNZDHLXN.js → chunk-S3YQGTBF.js} +35 -9
  87. package/dist/chunk-S3YQGTBF.js.map +1 -0
  88. package/dist/{chunk-IBDVZ6BF.js → chunk-SLWTG2LJ.js} +69 -74
  89. package/dist/chunk-SLWTG2LJ.js.map +1 -0
  90. package/dist/chunk-SN2O72UF.mjs +6 -0
  91. package/dist/chunk-SN2O72UF.mjs.map +1 -0
  92. package/dist/{chunk-65SMJBHW.mjs → chunk-T2DEEKVQ.mjs} +69 -74
  93. package/dist/chunk-T2DEEKVQ.mjs.map +1 -0
  94. package/dist/{chunk-3GCEFS73.js → chunk-TE3W5REY.js} +39 -62
  95. package/dist/chunk-TE3W5REY.js.map +1 -0
  96. package/dist/{chunk-IFVMZFNT.js → chunk-TVCCZ7NR.js} +41 -79
  97. package/dist/chunk-TVCCZ7NR.js.map +1 -0
  98. package/dist/{chunk-EBJ4S5DU.js → chunk-UT2CF4FC.js} +4 -4
  99. package/dist/{chunk-EBJ4S5DU.js.map → chunk-UT2CF4FC.js.map} +1 -1
  100. package/dist/chunk-V6NVBFEP.js +54 -0
  101. package/dist/chunk-V6NVBFEP.js.map +1 -0
  102. package/dist/{chunk-6FWQMKVO.mjs → chunk-WN5A3KSY.mjs} +18 -7
  103. package/dist/chunk-WN5A3KSY.mjs.map +1 -0
  104. package/dist/chunk-WRZQS7O4.js +8 -0
  105. package/dist/chunk-WRZQS7O4.js.map +1 -0
  106. package/dist/chunk-WZHWWOUC.js +51 -0
  107. package/dist/chunk-WZHWWOUC.js.map +1 -0
  108. package/dist/chunk-X3S6KXAP.mjs +47 -0
  109. package/dist/chunk-X3S6KXAP.mjs.map +1 -0
  110. package/dist/chunk-X4OPM3SK.mjs +43 -0
  111. package/dist/chunk-X4OPM3SK.mjs.map +1 -0
  112. package/dist/chunk-ZPLN537Q.mjs +57 -0
  113. package/dist/chunk-ZPLN537Q.mjs.map +1 -0
  114. package/dist/cli/index.js +8986 -1276
  115. package/dist/cli/index.js.map +1 -1
  116. package/dist/cli/index.mjs +8972 -1280
  117. package/dist/cli/index.mjs.map +1 -1
  118. package/dist/commerce/index.js +50 -48
  119. package/dist/commerce/index.mjs +5 -3
  120. package/dist/engage/index.js +11 -8
  121. package/dist/engage/index.js.map +1 -1
  122. package/dist/engage/index.mjs +8 -5
  123. package/dist/engage/index.mjs.map +1 -1
  124. package/dist/fleet/contract.d.mts +99 -0
  125. package/dist/fleet/contract.d.ts +99 -0
  126. package/dist/fleet/contract.js +53 -0
  127. package/dist/fleet/contract.js.map +1 -0
  128. package/dist/fleet/contract.mjs +4 -0
  129. package/dist/fleet/contract.mjs.map +1 -0
  130. package/dist/fleet/index.d.mts +89 -0
  131. package/dist/fleet/index.d.ts +89 -0
  132. package/dist/fleet/index.js +74 -0
  133. package/dist/fleet/index.js.map +1 -0
  134. package/dist/fleet/index.mjs +9 -0
  135. package/dist/fleet/index.mjs.map +1 -0
  136. package/dist/forms/contract.d.mts +69 -0
  137. package/dist/forms/contract.d.ts +69 -0
  138. package/dist/forms/contract.js +33 -0
  139. package/dist/forms/contract.js.map +1 -0
  140. package/dist/forms/contract.mjs +4 -0
  141. package/dist/forms/contract.mjs.map +1 -0
  142. package/dist/forms/index.js +21 -17
  143. package/dist/forms/index.js.map +1 -1
  144. package/dist/forms/index.mjs +14 -10
  145. package/dist/forms/index.mjs.map +1 -1
  146. package/dist/forms/styles.css +10 -8
  147. package/dist/images/index.js +10 -8
  148. package/dist/images/index.mjs +3 -1
  149. package/dist/index.js +98 -96
  150. package/dist/index.js.map +1 -1
  151. package/dist/index.mjs +9 -7
  152. package/dist/index.mjs.map +1 -1
  153. package/dist/layout/client.d.mts +8 -1
  154. package/dist/layout/client.d.ts +8 -1
  155. package/dist/layout/client.js +8 -3
  156. package/dist/layout/client.mjs +7 -2
  157. package/dist/layout/index.d.mts +7 -1
  158. package/dist/layout/index.d.ts +7 -1
  159. package/dist/layout/index.js +57 -4
  160. package/dist/layout/index.js.map +1 -1
  161. package/dist/layout/index.mjs +56 -3
  162. package/dist/layout/index.mjs.map +1 -1
  163. package/dist/llms/index.d.mts +4 -0
  164. package/dist/llms/index.d.ts +4 -0
  165. package/dist/llms/index.js +12 -12
  166. package/dist/llms/index.mjs +2 -2
  167. package/dist/maps/index.js +9 -18
  168. package/dist/maps/index.js.map +1 -1
  169. package/dist/maps/index.mjs +9 -18
  170. package/dist/maps/index.mjs.map +1 -1
  171. package/dist/portfolio/contract.d.mts +61 -0
  172. package/dist/portfolio/contract.d.ts +61 -0
  173. package/dist/portfolio/contract.js +43 -0
  174. package/dist/portfolio/contract.js.map +1 -0
  175. package/dist/portfolio/contract.mjs +38 -0
  176. package/dist/portfolio/contract.mjs.map +1 -0
  177. package/dist/seo/client.js +4 -2
  178. package/dist/seo/client.mjs +3 -1
  179. package/dist/seo/index.js +4 -2
  180. package/dist/seo/index.js.map +1 -1
  181. package/dist/seo/index.mjs +3 -1
  182. package/dist/seo/index.mjs.map +1 -1
  183. package/dist/seo-pages/contract.d.mts +62 -0
  184. package/dist/seo-pages/contract.d.ts +62 -0
  185. package/dist/seo-pages/contract.js +48 -0
  186. package/dist/seo-pages/contract.js.map +1 -0
  187. package/dist/seo-pages/contract.mjs +44 -0
  188. package/dist/seo-pages/contract.mjs.map +1 -0
  189. package/dist/signal/index.js +11 -11
  190. package/dist/signal/index.mjs +2 -2
  191. package/dist/sitemap/index.js +2 -2
  192. package/dist/sitemap/index.mjs +1 -1
  193. package/dist/sites/contract.d.mts +57 -0
  194. package/dist/sites/contract.d.ts +57 -0
  195. package/dist/sites/contract.js +37 -0
  196. package/dist/sites/contract.js.map +1 -0
  197. package/dist/sites/contract.mjs +4 -0
  198. package/dist/sites/contract.mjs.map +1 -0
  199. package/package.json +44 -4
  200. package/skills/site-kit/SKILL.md +66 -0
  201. package/dist/AnalyticsProvider-MLT26JRP.mjs +0 -6
  202. package/dist/AnalyticsProvider-WTOGN7LV.js +0 -27
  203. package/dist/BlogPostFaqSection-H556RV62.js +0 -13
  204. package/dist/ChatWidget-MAL7FB4M.js +0 -14
  205. package/dist/ChatWidget-QPZTPSPE.mjs +0 -5
  206. package/dist/EngageWidget-IPBUA4DW.mjs +0 -5
  207. package/dist/EngageWidget-N3CABK3L.js +0 -14
  208. package/dist/SignalCore-4MDIJUAP.mjs.map +0 -1
  209. package/dist/SignalCore-ZMFUE3N4.js.map +0 -1
  210. package/dist/SitemapSync-FUCH6Y6J.js +0 -13
  211. package/dist/SitemapSync-LCSPRAWU.mjs +0 -4
  212. package/dist/chunk-3GCEFS73.js.map +0 -1
  213. package/dist/chunk-3YQCPLGP.mjs.map +0 -1
  214. package/dist/chunk-5AESOLXS.mjs.map +0 -1
  215. package/dist/chunk-65SMJBHW.mjs.map +0 -1
  216. package/dist/chunk-6FWQMKVO.mjs.map +0 -1
  217. package/dist/chunk-IBDVZ6BF.js.map +0 -1
  218. package/dist/chunk-IFVMZFNT.js.map +0 -1
  219. package/dist/chunk-KMZ527EM.mjs.map +0 -1
  220. package/dist/chunk-MLJXELPA.js.map +0 -1
  221. package/dist/chunk-NNZDHLXN.js.map +0 -1
  222. package/dist/chunk-QTWA44S3.mjs.map +0 -1
  223. package/dist/chunk-RI6JZUAE.js.map +0 -1
  224. package/dist/chunk-T4QBSRA4.mjs.map +0 -1
  225. package/dist/chunk-TCVHI3JP.mjs.map +0 -1
  226. package/dist/chunk-YJWKHCOB.js.map +0 -1
  227. package/dist/chunk-ZM2ALSBM.js.map +0 -1
@@ -1,17 +1,22 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var chunkNNZDHLXN_js = require('../chunk-NNZDHLXN.js');
4
+ var chunkS3YQGTBF_js = require('../chunk-S3YQGTBF.js');
5
+ require('../chunk-5OWZUNNE.js');
5
6
  require('../chunk-HGNSJXWT.js');
6
- require('../chunk-O7E6QVK3.js');
7
+ require('../chunk-Q2NBD54V.js');
8
+ require('../chunk-WZHWWOUC.js');
9
+ require('../chunk-GCDVKOSM.js');
7
10
  require('../chunk-3BNFUMLL.js');
11
+ require('../chunk-EAASMIWX.js');
12
+ require('../chunk-WRZQS7O4.js');
8
13
  require('../chunk-ZSMWDLMK.js');
9
14
 
10
15
 
11
16
 
12
17
  Object.defineProperty(exports, "SiteKitClientProviders", {
13
18
  enumerable: true,
14
- get: function () { return chunkNNZDHLXN_js.SiteKitClientProviders; }
19
+ get: function () { return chunkS3YQGTBF_js.SiteKitClientProviders; }
15
20
  });
16
21
  //# sourceMappingURL=client.js.map
17
22
  //# sourceMappingURL=client.js.map
@@ -1,8 +1,13 @@
1
1
  'use client';
2
- export { SiteKitClientProviders } from '../chunk-KMZ527EM.mjs';
2
+ export { SiteKitClientProviders } from '../chunk-GN62H6EG.mjs';
3
+ import '../chunk-NDHFU4HV.mjs';
3
4
  import '../chunk-ZVBLXXNJ.mjs';
4
- import '../chunk-YFGKIKXA.mjs';
5
+ import '../chunk-ZPLN537Q.mjs';
6
+ import '../chunk-X4OPM3SK.mjs';
7
+ import '../chunk-57QV6NIY.mjs';
5
8
  import '../chunk-EE6SFAZP.mjs';
9
+ import '../chunk-LZSCCJ4O.mjs';
10
+ import '../chunk-SN2O72UF.mjs';
6
11
  import '../chunk-4XPGGLVP.mjs';
7
12
  //# sourceMappingURL=client.mjs.map
8
13
  //# sourceMappingURL=client.mjs.map
@@ -18,6 +18,12 @@ interface SiteKitLayoutProps {
18
18
  signal?: boolean | SignalConfig;
19
19
  /** SitemapSync client component. Default: true */
20
20
  sitemapSync?: boolean;
21
+ /**
22
+ * Fleet heartbeat — reports this site's kit version + active modules to Sonor
23
+ * once per session so the platform can track version drift. Default: true.
24
+ * Deferred like Analytics; never in the hydration path.
25
+ */
26
+ fleet?: boolean;
21
27
  /**
22
28
  * Defer client modules (Analytics, Engage, SitemapSync) until window load +
23
29
  * idle or the first user interaction, keeping their JS and mount work out of
@@ -37,6 +43,6 @@ interface SiteKitLayoutProps {
37
43
  */
38
44
  showLlmsTxtFooterLink?: boolean;
39
45
  }
40
- declare function SiteKitLayout({ children, apiKey, apiUrl, projectId, analytics, engage, signal, sitemapSync, defer, favicon, managedScripts, debug, showLlmsTxtFooterLink, }: SiteKitLayoutProps): Promise<react_jsx_runtime.JSX.Element>;
46
+ declare function SiteKitLayout({ children, apiKey, apiUrl, projectId, analytics, engage, signal, sitemapSync, fleet, defer, favicon, managedScripts, debug, showLlmsTxtFooterLink, }: SiteKitLayoutProps): Promise<react_jsx_runtime.JSX.Element>;
41
47
 
42
48
  export { AnalyticsConfig, EngageConfig, SignalConfig, SiteKitLayout, type SiteKitLayoutProps };
@@ -18,6 +18,12 @@ interface SiteKitLayoutProps {
18
18
  signal?: boolean | SignalConfig;
19
19
  /** SitemapSync client component. Default: true */
20
20
  sitemapSync?: boolean;
21
+ /**
22
+ * Fleet heartbeat — reports this site's kit version + active modules to Sonor
23
+ * once per session so the platform can track version drift. Default: true.
24
+ * Deferred like Analytics; never in the hydration path.
25
+ */
26
+ fleet?: boolean;
21
27
  /**
22
28
  * Defer client modules (Analytics, Engage, SitemapSync) until window load +
23
29
  * idle or the first user interaction, keeping their JS and mount work out of
@@ -37,6 +43,6 @@ interface SiteKitLayoutProps {
37
43
  */
38
44
  showLlmsTxtFooterLink?: boolean;
39
45
  }
40
- declare function SiteKitLayout({ children, apiKey, apiUrl, projectId, analytics, engage, signal, sitemapSync, defer, favicon, managedScripts, debug, showLlmsTxtFooterLink, }: SiteKitLayoutProps): Promise<react_jsx_runtime.JSX.Element>;
46
+ declare function SiteKitLayout({ children, apiKey, apiUrl, projectId, analytics, engage, signal, sitemapSync, fleet, defer, favicon, managedScripts, debug, showLlmsTxtFooterLink, }: SiteKitLayoutProps): Promise<react_jsx_runtime.JSX.Element>;
41
47
 
42
48
  export { AnalyticsConfig, EngageConfig, SignalConfig, SiteKitLayout, type SiteKitLayoutProps };
@@ -1,15 +1,65 @@
1
1
  'use strict';
2
2
 
3
- var chunkNNZDHLXN_js = require('../chunk-NNZDHLXN.js');
3
+ var chunkS3YQGTBF_js = require('../chunk-S3YQGTBF.js');
4
+ require('../chunk-5OWZUNNE.js');
4
5
  require('../chunk-HGNSJXWT.js');
6
+ require('../chunk-Q2NBD54V.js');
7
+ require('../chunk-WZHWWOUC.js');
5
8
  var chunkGCPPXUAI_js = require('../chunk-GCPPXUAI.js');
6
- require('../chunk-O7E6QVK3.js');
9
+ require('../chunk-GCDVKOSM.js');
7
10
  var chunkDV2LJYFD_js = require('../chunk-DV2LJYFD.js');
8
11
  var chunkQPIKPWAZ_js = require('../chunk-QPIKPWAZ.js');
9
12
  require('../chunk-3BNFUMLL.js');
13
+ require('../chunk-EAASMIWX.js');
14
+ var chunkWRZQS7O4_js = require('../chunk-WRZQS7O4.js');
10
15
  require('../chunk-ZSMWDLMK.js');
16
+ require('server-only');
11
17
  var jsxRuntime = require('react/jsx-runtime');
12
18
 
19
+ var MINT_PATH = "/api/public/site-token";
20
+ var MINT_TIMEOUT_MS = 4e3;
21
+ var SITE_TOKEN_PREFIX = "st1.";
22
+ var cache = /* @__PURE__ */ new Map();
23
+ async function resolveClientCredential(rawKey, apiUrl) {
24
+ if (!rawKey) return "";
25
+ if (rawKey.startsWith(SITE_TOKEN_PREFIX)) return rawKey;
26
+ const now = Date.now();
27
+ const cached = cache.get(rawKey);
28
+ if (cached && cached.expiresAt > now) return cached.credential;
29
+ const base = (apiUrl || "https://api.sonor.io").replace(/\/+$/, "");
30
+ try {
31
+ const res = await fetch(`${base}${MINT_PATH}`, {
32
+ method: "POST",
33
+ headers: {
34
+ "content-type": "application/json",
35
+ "x-api-key": rawKey,
36
+ "x-sitekit-version": chunkWRZQS7O4_js.SITE_KIT_VERSION
37
+ },
38
+ cache: "no-store",
39
+ signal: typeof AbortSignal !== "undefined" && AbortSignal.timeout ? AbortSignal.timeout(MINT_TIMEOUT_MS) : void 0
40
+ });
41
+ if (res.ok) {
42
+ const data = await res.json().catch(() => null);
43
+ const token = data?.token;
44
+ const expiresIn = typeof data?.expiresIn === "number" ? data.expiresIn : 3600;
45
+ if (typeof token === "string" && token.startsWith(SITE_TOKEN_PREFIX)) {
46
+ cache.set(rawKey, {
47
+ credential: token,
48
+ expiresAt: now + Math.min(expiresIn * 1e3 / 2, 30 * 6e4)
49
+ });
50
+ return token;
51
+ }
52
+ } else if (res.status !== 404) {
53
+ console.warn(
54
+ `[SiteKitLayout] Token mint returned ${res.status}; injecting raw key for this render.`
55
+ );
56
+ }
57
+ } catch {
58
+ console.warn("[SiteKitLayout] Token mint unavailable; injecting raw key for this render.");
59
+ }
60
+ cache.set(rawKey, { credential: rawKey, expiresAt: now + 6e4 });
61
+ return rawKey;
62
+ }
13
63
  async function SiteKitLayout({
14
64
  children,
15
65
  apiKey,
@@ -19,6 +69,7 @@ async function SiteKitLayout({
19
69
  engage = true,
20
70
  signal = false,
21
71
  sitemapSync = true,
72
+ fleet = true,
22
73
  defer = true,
23
74
  favicon = true,
24
75
  managedScripts = true,
@@ -33,21 +84,23 @@ async function SiteKitLayout({
33
84
  );
34
85
  }
35
86
  const apiOrigin = chunkQPIKPWAZ_js.resolveApiOrigin(resolvedApiUrl);
87
+ const clientCredential = await resolveClientCredential(resolvedApiKey, resolvedApiUrl);
36
88
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
37
89
  /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "preconnect", href: apiOrigin, crossOrigin: "anonymous" }),
38
90
  /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "dns-prefetch", href: apiOrigin }),
39
91
  favicon && /* @__PURE__ */ jsxRuntime.jsx(chunkGCPPXUAI_js.ManagedFavicon, { apiKey: resolvedApiKey, apiUrl: resolvedApiUrl }),
40
92
  managedScripts && /* @__PURE__ */ jsxRuntime.jsx(chunkDV2LJYFD_js.ManagedScripts, { position: "head" }),
41
93
  /* @__PURE__ */ jsxRuntime.jsxs(
42
- chunkNNZDHLXN_js.SiteKitClientProviders,
94
+ chunkS3YQGTBF_js.SiteKitClientProviders,
43
95
  {
44
- apiKey: resolvedApiKey,
96
+ apiKey: clientCredential,
45
97
  apiUrl: resolvedApiUrl,
46
98
  projectId,
47
99
  analytics,
48
100
  engage,
49
101
  signal,
50
102
  sitemapSync,
103
+ fleet,
51
104
  defer,
52
105
  debug,
53
106
  children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/layout/SiteKitLayout.tsx"],"names":["resolveApiOrigin","jsxs","Fragment","jsx","ManagedFavicon","ManagedScripts","SiteKitClientProviders"],"mappings":";;;;;;;;;;;;AA2FA,eAAsB,aAAA,CAAc;AAAA,EAClC,QAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA,GAAY,IAAA;AAAA,EACZ,MAAA,GAAS,IAAA;AAAA,EACT,MAAA,GAAS,KAAA;AAAA,EACT,WAAA,GAAc,IAAA;AAAA,EACd,KAAA,GAAQ,IAAA;AAAA,EACR,OAAA,GAAU,IAAA;AAAA,EACV,cAAA,GAAiB,IAAA;AAAA,EACjB,KAAA,GAAQ,KAAA;AAAA,EACR,qBAAA,GAAwB;AAC1B,CAAA,EAAuB;AAErB,EAAA,MAAM,cAAA,GACJ,MAAA,IACA,OAAA,CAAQ,GAAA,CAAI,aAAA,IACZ,EAAA;AACF,EAAA,MAAM,cAAA,GACJ,MAAA,IACA,OAAA,CAAQ,GAAA,CAAI,aAAA,IACZ,sBAAA;AAEF,EAAA,IAAI,CAAC,kBAAkB,KAAA,EAAO;AAC5B,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAAYA,kCAAiB,cAAc,CAAA;AAEjD,EAAA,uBACEC,eAAA,CAAAC,mBAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAAC,cAAA,CAAC,UAAK,GAAA,EAAI,YAAA,EAAa,IAAA,EAAM,SAAA,EAAW,aAAY,WAAA,EAAY,CAAA;AAAA,oBAChEA,cAAA,CAAC,MAAA,EAAA,EAAK,GAAA,EAAI,cAAA,EAAe,MAAM,SAAA,EAAW,CAAA;AAAA,IAEzC,2BAAWA,cAAA,CAACC,+BAAA,EAAA,EAAe,MAAA,EAAQ,cAAA,EAAgB,QAAQ,cAAA,EAAgB,CAAA;AAAA,IAC3E,cAAA,oBAAkBD,cAAA,CAACE,+BAAA,EAAA,EAAe,QAAA,EAAS,MAAA,EAAO,CAAA;AAAA,oBAGnDJ,eAAA;AAAA,MAACK,uCAAA;AAAA,MAAA;AAAA,QACC,MAAA,EAAQ,cAAA;AAAA,QACR,MAAA,EAAQ,cAAA;AAAA,QACR,SAAA;AAAA,QACA,SAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,WAAA;AAAA,QACA,KAAA;AAAA,QACA,KAAA;AAAA,QAEC,QAAA,EAAA;AAAA,UAAA,QAAA;AAAA,UACA,qBAAA,mBACCH,cAAA,CAAC,QAAA,EAAA,EAAO,SAAA,EAAU,SAAA,EAAU,aAAA,EAAY,MAAA,EACtC,QAAA,kBAAAA,cAAA,CAAC,GAAA,EAAA,EAAE,IAAA,EAAK,WAAA,EAAY,QAAA,EAAA,UAAA,EAAQ,GAC9B,CAAA,GACE;AAAA;AAAA;AAAA,KACN;AAAA,IAGC,cAAA,oBAAkBA,cAAA,CAACE,+BAAA,EAAA,EAAe,QAAA,EAAS,UAAA,EAAW;AAAA,GAAA,EACzD,CAAA;AAEJ","file":"index.js","sourcesContent":["/**\n * @sonordev/site-kit/layout — SiteKitLayout\n *\n * The master layout component. A single RSC that auto-composes every\n * site-kit feature your layout needs:\n *\n * Server-side (RSC):\n * - ManagedFavicon (link tags from Portal logo)\n * - ManagedScripts position=\"head\" (tracking pixels, analytics tags)\n * - ManagedScripts position=\"body-end\"\n *\n * Client-side (island):\n * - AnalyticsProvider (page views, scroll depth, heatmap, web vitals)\n * - EngageWidget (popups, nudges, chat)\n * - SignalBridge (A/B experiments, behavior tracking)\n * - SitemapSync (keeps Portal seo_pages in sync)\n *\n * @example\n * ```tsx\n * // app/layout.tsx\n * import { SiteKitLayout } from '@sonordev/site-kit/layout'\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * <SiteKitLayout>\n * <Header />\n * <main>{children}</main>\n * <Footer />\n * </SiteKitLayout>\n * </body>\n * </html>\n * )\n * }\n * ```\n *\n * @example Opt out of specific features:\n * ```tsx\n * <SiteKitLayout engage={false} signal={false}>\n * {children}\n * </SiteKitLayout>\n * ```\n */\n\nimport * as React from 'react'\nimport { ManagedFavicon } from '../images/ManagedFavicon'\nimport { ManagedScripts } from '../seo/ManagedScripts'\nimport { resolveApiOrigin } from '../server/server-fetch'\nimport { SiteKitClientProviders } from './SiteKitClientProviders'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\nexport interface SiteKitLayoutProps {\n children: React.ReactNode\n\n /** Portal API key. Defaults to SONOR_API_KEY env var. */\n apiKey?: string\n /** Portal API URL. Defaults to env or https://api.sonor.io */\n apiUrl?: string\n /** Project ID (for Engage chat room routing). Auto-resolved if not provided. */\n projectId?: string\n\n /** Analytics tracking. true (default) | false | config object */\n analytics?: boolean | AnalyticsConfig\n /** Engage widgets (popups, chat). true (default) | false | config object */\n engage?: boolean | EngageConfig\n /** Signal AI (experiments, behavior). false (default) | true | config object */\n signal?: boolean | SignalConfig\n /** SitemapSync client component. Default: true */\n sitemapSync?: boolean\n /**\n * Defer client modules (Analytics, Engage, SitemapSync) until window load +\n * idle or the first user interaction, keeping their JS and mount work out of\n * the critical hydration path. Default: true. Pass false to restore legacy\n * eager mounting. SignalBridge is never deferred.\n */\n defer?: boolean\n /** ManagedFavicon in <head>. Default: true */\n favicon?: boolean\n /** ManagedScripts (head + body-end). Default: true */\n managedScripts?: boolean\n\n /** Debug mode — logs to console. Default: false */\n debug?: boolean\n /**\n * When true, append a screen-reader-only link to `/llms.txt` after main content.\n * Default false — prefer `buildAiDiscoveryHeaders()` for discovery.\n */\n showLlmsTxtFooterLink?: boolean\n}\n\nexport async function SiteKitLayout({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n defer = true,\n favicon = true,\n managedScripts = true,\n debug = false,\n showLlmsTxtFooterLink = false,\n}: SiteKitLayoutProps) {\n // Resolve API config from env (server-side, safe)\n const resolvedApiKey =\n apiKey ??\n process.env.SONOR_API_KEY ??\n ''\n const resolvedApiUrl =\n apiUrl ??\n process.env.SONOR_API_URL ??\n 'https://api.sonor.io'\n\n if (!resolvedApiKey && debug) {\n console.warn(\n '[SiteKitLayout] No API key found. Set SONOR_API_KEY.',\n )\n }\n\n const apiOrigin = resolveApiOrigin(resolvedApiUrl)\n\n return (\n <>\n <link rel=\"preconnect\" href={apiOrigin} crossOrigin=\"anonymous\" />\n <link rel=\"dns-prefetch\" href={apiOrigin} />\n {/* Server-rendered head components */}\n {favicon && <ManagedFavicon apiKey={resolvedApiKey} apiUrl={resolvedApiUrl} />}\n {managedScripts && <ManagedScripts position=\"head\" />}\n\n {/* Client-side providers island */}\n <SiteKitClientProviders\n apiKey={resolvedApiKey}\n apiUrl={resolvedApiUrl}\n projectId={projectId}\n analytics={analytics}\n engage={engage}\n signal={signal}\n sitemapSync={sitemapSync}\n defer={defer}\n debug={debug}\n >\n {children}\n {showLlmsTxtFooterLink ? (\n <footer className=\"sr-only\" aria-hidden=\"true\">\n <a href=\"/llms.txt\">llms.txt</a>\n </footer>\n ) : null}\n </SiteKitClientProviders>\n\n {/* Server-rendered body-end scripts */}\n {managedScripts && <ManagedScripts position=\"body-end\" />}\n </>\n )\n}\n"]}
1
+ {"version":3,"sources":["../../src/server/mint-site-token.ts","../../src/layout/SiteKitLayout.tsx"],"names":["SITE_KIT_VERSION","resolveApiOrigin","jsxs","Fragment","jsx","ManagedFavicon","ManagedScripts","SiteKitClientProviders"],"mappings":"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","file":"index.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 * Resilience: if minting is unavailable (older API without the endpoint, token\n * auth not configured, transient outage) it returns the RAW KEY, so a build never\n * breaks. Domain-binding is log-only by default, so the raw key still works —\n * worst case is exactly pre-3.2 behavior. Memoized per raw key for the process,\n * so an entire `next build` mints ~once and every static page reuses one seed\n * (the client refreshes on the far side as needed).\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\ninterface CacheEntry {\n credential: string\n expiresAt: number\n}\nconst cache = new Map<string, CacheEntry>()\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 now = Date.now()\n const cached = cache.get(rawKey)\n if (cached && cached.expiresAt > now) return cached.credential\n\n const base = (apiUrl || 'https://api.sonor.io').replace(/\\/+$/, '')\n try {\n const res = await fetch(`${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 cache: 'no-store',\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(MINT_TIMEOUT_MS)\n : undefined,\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: now + Math.min((expiresIn * 1000) / 2, 30 * 60_000),\n })\n return token\n }\n } else if (res.status !== 404) {\n // 404 = older API without the endpoint (expected during rollout). Anything\n // else is worth a server-side note. Never logs the key value.\n console.warn(\n `[SiteKitLayout] Token mint returned ${res.status}; injecting raw key for this render.`,\n )\n }\n } catch {\n console.warn('[SiteKitLayout] Token mint unavailable; injecting raw key for this render.')\n }\n\n // Fallback: the raw key (pre-3.2 behavior). Short cache so a transient outage\n // retries soon rather than pinning the raw key for the whole build.\n cache.set(rawKey, { credential: rawKey, expiresAt: now + 60_000 })\n return rawKey\n}\n","/**\n * @sonordev/site-kit/layout — SiteKitLayout\n *\n * The master layout component. A single RSC that auto-composes every\n * site-kit feature your layout needs:\n *\n * Server-side (RSC):\n * - ManagedFavicon (link tags from Portal logo)\n * - ManagedScripts position=\"head\" (tracking pixels, analytics tags)\n * - ManagedScripts position=\"body-end\"\n *\n * Client-side (island):\n * - AnalyticsProvider (page views, scroll depth, heatmap, web vitals)\n * - EngageWidget (popups, nudges, chat)\n * - SignalBridge (A/B experiments, behavior tracking)\n * - SitemapSync (keeps Portal seo_pages in sync)\n *\n * @example\n * ```tsx\n * // app/layout.tsx\n * import { SiteKitLayout } from '@sonordev/site-kit/layout'\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * <SiteKitLayout>\n * <Header />\n * <main>{children}</main>\n * <Footer />\n * </SiteKitLayout>\n * </body>\n * </html>\n * )\n * }\n * ```\n *\n * @example Opt out of specific features:\n * ```tsx\n * <SiteKitLayout engage={false} signal={false}>\n * {children}\n * </SiteKitLayout>\n * ```\n */\n\nimport * as React from 'react'\nimport { ManagedFavicon } from '../images/ManagedFavicon'\nimport { ManagedScripts } from '../seo/ManagedScripts'\nimport { resolveApiOrigin } from '../server/server-fetch'\nimport { resolveClientCredential } from '../server/mint-site-token'\nimport { SiteKitClientProviders } from './SiteKitClientProviders'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\nexport interface SiteKitLayoutProps {\n children: React.ReactNode\n\n /** Portal API key. Defaults to SONOR_API_KEY env var. */\n apiKey?: string\n /** Portal API URL. Defaults to env or https://api.sonor.io */\n apiUrl?: string\n /** Project ID (for Engage chat room routing). Auto-resolved if not provided. */\n projectId?: string\n\n /** Analytics tracking. true (default) | false | config object */\n analytics?: boolean | AnalyticsConfig\n /** Engage widgets (popups, chat). true (default) | false | config object */\n engage?: boolean | EngageConfig\n /** Signal AI (experiments, behavior). false (default) | true | config object */\n signal?: boolean | SignalConfig\n /** SitemapSync client component. Default: true */\n sitemapSync?: boolean\n /**\n * Fleet heartbeat — reports this site's kit version + active modules to Sonor\n * once per session so the platform can track version drift. Default: true.\n * Deferred like Analytics; never in the hydration path.\n */\n fleet?: boolean\n /**\n * Defer client modules (Analytics, Engage, SitemapSync) until window load +\n * idle or the first user interaction, keeping their JS and mount work out of\n * the critical hydration path. Default: true. Pass false to restore legacy\n * eager mounting. SignalBridge is never deferred.\n */\n defer?: boolean\n /** ManagedFavicon in <head>. Default: true */\n favicon?: boolean\n /** ManagedScripts (head + body-end). Default: true */\n managedScripts?: boolean\n\n /** Debug mode — logs to console. Default: false */\n debug?: boolean\n /**\n * When true, append a screen-reader-only link to `/llms.txt` after main content.\n * Default false — prefer `buildAiDiscoveryHeaders()` for discovery.\n */\n showLlmsTxtFooterLink?: boolean\n}\n\nexport async function SiteKitLayout({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n fleet = true,\n defer = true,\n favicon = true,\n managedScripts = true,\n debug = false,\n showLlmsTxtFooterLink = false,\n}: SiteKitLayoutProps) {\n // Resolve API config from env (server-side, safe)\n const resolvedApiKey =\n apiKey ??\n process.env.SONOR_API_KEY ??\n ''\n const resolvedApiUrl =\n apiUrl ??\n process.env.SONOR_API_URL ??\n 'https://api.sonor.io'\n\n if (!resolvedApiKey && debug) {\n console.warn(\n '[SiteKitLayout] No API key found. Set SONOR_API_KEY.',\n )\n }\n\n const apiOrigin = resolveApiOrigin(resolvedApiUrl)\n\n // Mint a short-lived client token so the raw key never reaches the browser.\n // Falls back to the raw key if minting is unavailable (older API / transient\n // outage) — the build never breaks. The client refreshes the token as needed.\n const clientCredential = await resolveClientCredential(resolvedApiKey, resolvedApiUrl)\n\n return (\n <>\n <link rel=\"preconnect\" href={apiOrigin} crossOrigin=\"anonymous\" />\n <link rel=\"dns-prefetch\" href={apiOrigin} />\n {/* Server-rendered head components */}\n {favicon && <ManagedFavicon apiKey={resolvedApiKey} apiUrl={resolvedApiUrl} />}\n {managedScripts && <ManagedScripts position=\"head\" />}\n\n {/* Client-side providers island — receives the minted token (or raw-key\n fallback), never the raw key when minting succeeds. */}\n <SiteKitClientProviders\n apiKey={clientCredential}\n apiUrl={resolvedApiUrl}\n projectId={projectId}\n analytics={analytics}\n engage={engage}\n signal={signal}\n sitemapSync={sitemapSync}\n fleet={fleet}\n defer={defer}\n debug={debug}\n >\n {children}\n {showLlmsTxtFooterLink ? (\n <footer className=\"sr-only\" aria-hidden=\"true\">\n <a href=\"/llms.txt\">llms.txt</a>\n </footer>\n ) : null}\n </SiteKitClientProviders>\n\n {/* Server-rendered body-end scripts */}\n {managedScripts && <ManagedScripts position=\"body-end\" />}\n </>\n )\n}\n"]}
@@ -1,13 +1,63 @@
1
- import { SiteKitClientProviders } from '../chunk-KMZ527EM.mjs';
1
+ import { SiteKitClientProviders } from '../chunk-GN62H6EG.mjs';
2
+ import '../chunk-NDHFU4HV.mjs';
2
3
  import '../chunk-ZVBLXXNJ.mjs';
4
+ import '../chunk-ZPLN537Q.mjs';
5
+ import '../chunk-X4OPM3SK.mjs';
3
6
  import { ManagedFavicon } from '../chunk-CU6OGBKK.mjs';
4
- import '../chunk-YFGKIKXA.mjs';
7
+ import '../chunk-57QV6NIY.mjs';
5
8
  import { ManagedScripts } from '../chunk-6PJTLBRR.mjs';
6
9
  import { resolveApiOrigin } from '../chunk-UDBUK5ZJ.mjs';
7
10
  import '../chunk-EE6SFAZP.mjs';
11
+ import '../chunk-LZSCCJ4O.mjs';
12
+ import { SITE_KIT_VERSION } from '../chunk-SN2O72UF.mjs';
8
13
  import '../chunk-4XPGGLVP.mjs';
14
+ import 'server-only';
9
15
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
10
16
 
17
+ var MINT_PATH = "/api/public/site-token";
18
+ var MINT_TIMEOUT_MS = 4e3;
19
+ var SITE_TOKEN_PREFIX = "st1.";
20
+ var cache = /* @__PURE__ */ new Map();
21
+ async function resolveClientCredential(rawKey, apiUrl) {
22
+ if (!rawKey) return "";
23
+ if (rawKey.startsWith(SITE_TOKEN_PREFIX)) return rawKey;
24
+ const now = Date.now();
25
+ const cached = cache.get(rawKey);
26
+ if (cached && cached.expiresAt > now) return cached.credential;
27
+ const base = (apiUrl || "https://api.sonor.io").replace(/\/+$/, "");
28
+ try {
29
+ const res = await fetch(`${base}${MINT_PATH}`, {
30
+ method: "POST",
31
+ headers: {
32
+ "content-type": "application/json",
33
+ "x-api-key": rawKey,
34
+ "x-sitekit-version": SITE_KIT_VERSION
35
+ },
36
+ cache: "no-store",
37
+ signal: typeof AbortSignal !== "undefined" && AbortSignal.timeout ? AbortSignal.timeout(MINT_TIMEOUT_MS) : void 0
38
+ });
39
+ if (res.ok) {
40
+ const data = await res.json().catch(() => null);
41
+ const token = data?.token;
42
+ const expiresIn = typeof data?.expiresIn === "number" ? data.expiresIn : 3600;
43
+ if (typeof token === "string" && token.startsWith(SITE_TOKEN_PREFIX)) {
44
+ cache.set(rawKey, {
45
+ credential: token,
46
+ expiresAt: now + Math.min(expiresIn * 1e3 / 2, 30 * 6e4)
47
+ });
48
+ return token;
49
+ }
50
+ } else if (res.status !== 404) {
51
+ console.warn(
52
+ `[SiteKitLayout] Token mint returned ${res.status}; injecting raw key for this render.`
53
+ );
54
+ }
55
+ } catch {
56
+ console.warn("[SiteKitLayout] Token mint unavailable; injecting raw key for this render.");
57
+ }
58
+ cache.set(rawKey, { credential: rawKey, expiresAt: now + 6e4 });
59
+ return rawKey;
60
+ }
11
61
  async function SiteKitLayout({
12
62
  children,
13
63
  apiKey,
@@ -17,6 +67,7 @@ async function SiteKitLayout({
17
67
  engage = true,
18
68
  signal = false,
19
69
  sitemapSync = true,
70
+ fleet = true,
20
71
  defer = true,
21
72
  favicon = true,
22
73
  managedScripts = true,
@@ -31,6 +82,7 @@ async function SiteKitLayout({
31
82
  );
32
83
  }
33
84
  const apiOrigin = resolveApiOrigin(resolvedApiUrl);
85
+ const clientCredential = await resolveClientCredential(resolvedApiKey, resolvedApiUrl);
34
86
  return /* @__PURE__ */ jsxs(Fragment, { children: [
35
87
  /* @__PURE__ */ jsx("link", { rel: "preconnect", href: apiOrigin, crossOrigin: "anonymous" }),
36
88
  /* @__PURE__ */ jsx("link", { rel: "dns-prefetch", href: apiOrigin }),
@@ -39,13 +91,14 @@ async function SiteKitLayout({
39
91
  /* @__PURE__ */ jsxs(
40
92
  SiteKitClientProviders,
41
93
  {
42
- apiKey: resolvedApiKey,
94
+ apiKey: clientCredential,
43
95
  apiUrl: resolvedApiUrl,
44
96
  projectId,
45
97
  analytics,
46
98
  engage,
47
99
  signal,
48
100
  sitemapSync,
101
+ fleet,
49
102
  defer,
50
103
  debug,
51
104
  children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/layout/SiteKitLayout.tsx"],"names":[],"mappings":";;;;;;;;;;AA2FA,eAAsB,aAAA,CAAc;AAAA,EAClC,QAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA,GAAY,IAAA;AAAA,EACZ,MAAA,GAAS,IAAA;AAAA,EACT,MAAA,GAAS,KAAA;AAAA,EACT,WAAA,GAAc,IAAA;AAAA,EACd,KAAA,GAAQ,IAAA;AAAA,EACR,OAAA,GAAU,IAAA;AAAA,EACV,cAAA,GAAiB,IAAA;AAAA,EACjB,KAAA,GAAQ,KAAA;AAAA,EACR,qBAAA,GAAwB;AAC1B,CAAA,EAAuB;AAErB,EAAA,MAAM,cAAA,GACJ,MAAA,IACA,OAAA,CAAQ,GAAA,CAAI,aAAA,IACZ,EAAA;AACF,EAAA,MAAM,cAAA,GACJ,MAAA,IACA,OAAA,CAAQ,GAAA,CAAI,aAAA,IACZ,sBAAA;AAEF,EAAA,IAAI,CAAC,kBAAkB,KAAA,EAAO;AAC5B,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAAY,iBAAiB,cAAc,CAAA;AAEjD,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,UAAK,GAAA,EAAI,YAAA,EAAa,IAAA,EAAM,SAAA,EAAW,aAAY,WAAA,EAAY,CAAA;AAAA,oBAChE,GAAA,CAAC,MAAA,EAAA,EAAK,GAAA,EAAI,cAAA,EAAe,MAAM,SAAA,EAAW,CAAA;AAAA,IAEzC,2BAAW,GAAA,CAAC,cAAA,EAAA,EAAe,MAAA,EAAQ,cAAA,EAAgB,QAAQ,cAAA,EAAgB,CAAA;AAAA,IAC3E,cAAA,oBAAkB,GAAA,CAAC,cAAA,EAAA,EAAe,QAAA,EAAS,MAAA,EAAO,CAAA;AAAA,oBAGnD,IAAA;AAAA,MAAC,sBAAA;AAAA,MAAA;AAAA,QACC,MAAA,EAAQ,cAAA;AAAA,QACR,MAAA,EAAQ,cAAA;AAAA,QACR,SAAA;AAAA,QACA,SAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,WAAA;AAAA,QACA,KAAA;AAAA,QACA,KAAA;AAAA,QAEC,QAAA,EAAA;AAAA,UAAA,QAAA;AAAA,UACA,qBAAA,mBACC,GAAA,CAAC,QAAA,EAAA,EAAO,SAAA,EAAU,SAAA,EAAU,aAAA,EAAY,MAAA,EACtC,QAAA,kBAAA,GAAA,CAAC,GAAA,EAAA,EAAE,IAAA,EAAK,WAAA,EAAY,QAAA,EAAA,UAAA,EAAQ,GAC9B,CAAA,GACE;AAAA;AAAA;AAAA,KACN;AAAA,IAGC,cAAA,oBAAkB,GAAA,CAAC,cAAA,EAAA,EAAe,QAAA,EAAS,UAAA,EAAW;AAAA,GAAA,EACzD,CAAA;AAEJ","file":"index.mjs","sourcesContent":["/**\n * @sonordev/site-kit/layout — SiteKitLayout\n *\n * The master layout component. A single RSC that auto-composes every\n * site-kit feature your layout needs:\n *\n * Server-side (RSC):\n * - ManagedFavicon (link tags from Portal logo)\n * - ManagedScripts position=\"head\" (tracking pixels, analytics tags)\n * - ManagedScripts position=\"body-end\"\n *\n * Client-side (island):\n * - AnalyticsProvider (page views, scroll depth, heatmap, web vitals)\n * - EngageWidget (popups, nudges, chat)\n * - SignalBridge (A/B experiments, behavior tracking)\n * - SitemapSync (keeps Portal seo_pages in sync)\n *\n * @example\n * ```tsx\n * // app/layout.tsx\n * import { SiteKitLayout } from '@sonordev/site-kit/layout'\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * <SiteKitLayout>\n * <Header />\n * <main>{children}</main>\n * <Footer />\n * </SiteKitLayout>\n * </body>\n * </html>\n * )\n * }\n * ```\n *\n * @example Opt out of specific features:\n * ```tsx\n * <SiteKitLayout engage={false} signal={false}>\n * {children}\n * </SiteKitLayout>\n * ```\n */\n\nimport * as React from 'react'\nimport { ManagedFavicon } from '../images/ManagedFavicon'\nimport { ManagedScripts } from '../seo/ManagedScripts'\nimport { resolveApiOrigin } from '../server/server-fetch'\nimport { SiteKitClientProviders } from './SiteKitClientProviders'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\nexport interface SiteKitLayoutProps {\n children: React.ReactNode\n\n /** Portal API key. Defaults to SONOR_API_KEY env var. */\n apiKey?: string\n /** Portal API URL. Defaults to env or https://api.sonor.io */\n apiUrl?: string\n /** Project ID (for Engage chat room routing). Auto-resolved if not provided. */\n projectId?: string\n\n /** Analytics tracking. true (default) | false | config object */\n analytics?: boolean | AnalyticsConfig\n /** Engage widgets (popups, chat). true (default) | false | config object */\n engage?: boolean | EngageConfig\n /** Signal AI (experiments, behavior). false (default) | true | config object */\n signal?: boolean | SignalConfig\n /** SitemapSync client component. Default: true */\n sitemapSync?: boolean\n /**\n * Defer client modules (Analytics, Engage, SitemapSync) until window load +\n * idle or the first user interaction, keeping their JS and mount work out of\n * the critical hydration path. Default: true. Pass false to restore legacy\n * eager mounting. SignalBridge is never deferred.\n */\n defer?: boolean\n /** ManagedFavicon in <head>. Default: true */\n favicon?: boolean\n /** ManagedScripts (head + body-end). Default: true */\n managedScripts?: boolean\n\n /** Debug mode — logs to console. Default: false */\n debug?: boolean\n /**\n * When true, append a screen-reader-only link to `/llms.txt` after main content.\n * Default false — prefer `buildAiDiscoveryHeaders()` for discovery.\n */\n showLlmsTxtFooterLink?: boolean\n}\n\nexport async function SiteKitLayout({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n defer = true,\n favicon = true,\n managedScripts = true,\n debug = false,\n showLlmsTxtFooterLink = false,\n}: SiteKitLayoutProps) {\n // Resolve API config from env (server-side, safe)\n const resolvedApiKey =\n apiKey ??\n process.env.SONOR_API_KEY ??\n ''\n const resolvedApiUrl =\n apiUrl ??\n process.env.SONOR_API_URL ??\n 'https://api.sonor.io'\n\n if (!resolvedApiKey && debug) {\n console.warn(\n '[SiteKitLayout] No API key found. Set SONOR_API_KEY.',\n )\n }\n\n const apiOrigin = resolveApiOrigin(resolvedApiUrl)\n\n return (\n <>\n <link rel=\"preconnect\" href={apiOrigin} crossOrigin=\"anonymous\" />\n <link rel=\"dns-prefetch\" href={apiOrigin} />\n {/* Server-rendered head components */}\n {favicon && <ManagedFavicon apiKey={resolvedApiKey} apiUrl={resolvedApiUrl} />}\n {managedScripts && <ManagedScripts position=\"head\" />}\n\n {/* Client-side providers island */}\n <SiteKitClientProviders\n apiKey={resolvedApiKey}\n apiUrl={resolvedApiUrl}\n projectId={projectId}\n analytics={analytics}\n engage={engage}\n signal={signal}\n sitemapSync={sitemapSync}\n defer={defer}\n debug={debug}\n >\n {children}\n {showLlmsTxtFooterLink ? (\n <footer className=\"sr-only\" aria-hidden=\"true\">\n <a href=\"/llms.txt\">llms.txt</a>\n </footer>\n ) : null}\n </SiteKitClientProviders>\n\n {/* Server-rendered body-end scripts */}\n {managedScripts && <ManagedScripts position=\"body-end\" />}\n </>\n )\n}\n"]}
1
+ {"version":3,"sources":["../../src/server/mint-site-token.ts","../../src/layout/SiteKitLayout.tsx"],"names":[],"mappings":"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","file":"index.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 * Resilience: if minting is unavailable (older API without the endpoint, token\n * auth not configured, transient outage) it returns the RAW KEY, so a build never\n * breaks. Domain-binding is log-only by default, so the raw key still works —\n * worst case is exactly pre-3.2 behavior. Memoized per raw key for the process,\n * so an entire `next build` mints ~once and every static page reuses one seed\n * (the client refreshes on the far side as needed).\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\ninterface CacheEntry {\n credential: string\n expiresAt: number\n}\nconst cache = new Map<string, CacheEntry>()\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 now = Date.now()\n const cached = cache.get(rawKey)\n if (cached && cached.expiresAt > now) return cached.credential\n\n const base = (apiUrl || 'https://api.sonor.io').replace(/\\/+$/, '')\n try {\n const res = await fetch(`${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 cache: 'no-store',\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(MINT_TIMEOUT_MS)\n : undefined,\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: now + Math.min((expiresIn * 1000) / 2, 30 * 60_000),\n })\n return token\n }\n } else if (res.status !== 404) {\n // 404 = older API without the endpoint (expected during rollout). Anything\n // else is worth a server-side note. Never logs the key value.\n console.warn(\n `[SiteKitLayout] Token mint returned ${res.status}; injecting raw key for this render.`,\n )\n }\n } catch {\n console.warn('[SiteKitLayout] Token mint unavailable; injecting raw key for this render.')\n }\n\n // Fallback: the raw key (pre-3.2 behavior). Short cache so a transient outage\n // retries soon rather than pinning the raw key for the whole build.\n cache.set(rawKey, { credential: rawKey, expiresAt: now + 60_000 })\n return rawKey\n}\n","/**\n * @sonordev/site-kit/layout — SiteKitLayout\n *\n * The master layout component. A single RSC that auto-composes every\n * site-kit feature your layout needs:\n *\n * Server-side (RSC):\n * - ManagedFavicon (link tags from Portal logo)\n * - ManagedScripts position=\"head\" (tracking pixels, analytics tags)\n * - ManagedScripts position=\"body-end\"\n *\n * Client-side (island):\n * - AnalyticsProvider (page views, scroll depth, heatmap, web vitals)\n * - EngageWidget (popups, nudges, chat)\n * - SignalBridge (A/B experiments, behavior tracking)\n * - SitemapSync (keeps Portal seo_pages in sync)\n *\n * @example\n * ```tsx\n * // app/layout.tsx\n * import { SiteKitLayout } from '@sonordev/site-kit/layout'\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * <SiteKitLayout>\n * <Header />\n * <main>{children}</main>\n * <Footer />\n * </SiteKitLayout>\n * </body>\n * </html>\n * )\n * }\n * ```\n *\n * @example Opt out of specific features:\n * ```tsx\n * <SiteKitLayout engage={false} signal={false}>\n * {children}\n * </SiteKitLayout>\n * ```\n */\n\nimport * as React from 'react'\nimport { ManagedFavicon } from '../images/ManagedFavicon'\nimport { ManagedScripts } from '../seo/ManagedScripts'\nimport { resolveApiOrigin } from '../server/server-fetch'\nimport { resolveClientCredential } from '../server/mint-site-token'\nimport { SiteKitClientProviders } from './SiteKitClientProviders'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\nexport interface SiteKitLayoutProps {\n children: React.ReactNode\n\n /** Portal API key. Defaults to SONOR_API_KEY env var. */\n apiKey?: string\n /** Portal API URL. Defaults to env or https://api.sonor.io */\n apiUrl?: string\n /** Project ID (for Engage chat room routing). Auto-resolved if not provided. */\n projectId?: string\n\n /** Analytics tracking. true (default) | false | config object */\n analytics?: boolean | AnalyticsConfig\n /** Engage widgets (popups, chat). true (default) | false | config object */\n engage?: boolean | EngageConfig\n /** Signal AI (experiments, behavior). false (default) | true | config object */\n signal?: boolean | SignalConfig\n /** SitemapSync client component. Default: true */\n sitemapSync?: boolean\n /**\n * Fleet heartbeat — reports this site's kit version + active modules to Sonor\n * once per session so the platform can track version drift. Default: true.\n * Deferred like Analytics; never in the hydration path.\n */\n fleet?: boolean\n /**\n * Defer client modules (Analytics, Engage, SitemapSync) until window load +\n * idle or the first user interaction, keeping their JS and mount work out of\n * the critical hydration path. Default: true. Pass false to restore legacy\n * eager mounting. SignalBridge is never deferred.\n */\n defer?: boolean\n /** ManagedFavicon in <head>. Default: true */\n favicon?: boolean\n /** ManagedScripts (head + body-end). Default: true */\n managedScripts?: boolean\n\n /** Debug mode — logs to console. Default: false */\n debug?: boolean\n /**\n * When true, append a screen-reader-only link to `/llms.txt` after main content.\n * Default false — prefer `buildAiDiscoveryHeaders()` for discovery.\n */\n showLlmsTxtFooterLink?: boolean\n}\n\nexport async function SiteKitLayout({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n fleet = true,\n defer = true,\n favicon = true,\n managedScripts = true,\n debug = false,\n showLlmsTxtFooterLink = false,\n}: SiteKitLayoutProps) {\n // Resolve API config from env (server-side, safe)\n const resolvedApiKey =\n apiKey ??\n process.env.SONOR_API_KEY ??\n ''\n const resolvedApiUrl =\n apiUrl ??\n process.env.SONOR_API_URL ??\n 'https://api.sonor.io'\n\n if (!resolvedApiKey && debug) {\n console.warn(\n '[SiteKitLayout] No API key found. Set SONOR_API_KEY.',\n )\n }\n\n const apiOrigin = resolveApiOrigin(resolvedApiUrl)\n\n // Mint a short-lived client token so the raw key never reaches the browser.\n // Falls back to the raw key if minting is unavailable (older API / transient\n // outage) — the build never breaks. The client refreshes the token as needed.\n const clientCredential = await resolveClientCredential(resolvedApiKey, resolvedApiUrl)\n\n return (\n <>\n <link rel=\"preconnect\" href={apiOrigin} crossOrigin=\"anonymous\" />\n <link rel=\"dns-prefetch\" href={apiOrigin} />\n {/* Server-rendered head components */}\n {favicon && <ManagedFavicon apiKey={resolvedApiKey} apiUrl={resolvedApiUrl} />}\n {managedScripts && <ManagedScripts position=\"head\" />}\n\n {/* Client-side providers island — receives the minted token (or raw-key\n fallback), never the raw key when minting succeeds. */}\n <SiteKitClientProviders\n apiKey={clientCredential}\n apiUrl={resolvedApiUrl}\n projectId={projectId}\n analytics={analytics}\n engage={engage}\n signal={signal}\n sitemapSync={sitemapSync}\n fleet={fleet}\n defer={defer}\n debug={debug}\n >\n {children}\n {showLlmsTxtFooterLink ? (\n <footer className=\"sr-only\" aria-hidden=\"true\">\n <a href=\"/llms.txt\">llms.txt</a>\n </footer>\n ) : null}\n </SiteKitClientProviders>\n\n {/* Server-rendered body-end scripts */}\n {managedScripts && <ManagedScripts position=\"body-end\" />}\n </>\n )\n}\n"]}
@@ -488,6 +488,10 @@ interface WriteLLMsTxtOptions {
488
488
  /**
489
489
  * Fetch optimized llms.txt from Portal and write to public/llms.txt
490
490
  * Called at build time after sitemap sync (when optimizedLLMsTxt is enabled)
491
+ *
492
+ * Never persists a failure stub: when neither the Portal API nor local data
493
+ * yields real content, the write is skipped entirely so a previously
494
+ * generated (or committed) file keeps serving.
491
495
  */
492
496
  declare function writeLLMsTxtToPublic(options?: WriteLLMsTxtOptions): Promise<{
493
497
  success: boolean;
@@ -488,6 +488,10 @@ interface WriteLLMsTxtOptions {
488
488
  /**
489
489
  * Fetch optimized llms.txt from Portal and write to public/llms.txt
490
490
  * Called at build time after sitemap sync (when optimizedLLMsTxt is enabled)
491
+ *
492
+ * Never persists a failure stub: when neither the Portal API nor local data
493
+ * yields real content, the write is skipped entirely so a previously
494
+ * generated (or committed) file keeps serving.
491
495
  */
492
496
  declare function writeLLMsTxtToPublic(options?: WriteLLMsTxtOptions): Promise<{
493
497
  success: boolean;
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunk5B4FABFK_js = require('../chunk-5B4FABFK.js');
4
- var chunkZM2ALSBM_js = require('../chunk-ZM2ALSBM.js');
4
+ var chunk7TX5BKVA_js = require('../chunk-7TX5BKVA.js');
5
5
  var chunkATG4FJY6_js = require('../chunk-ATG4FJY6.js');
6
6
  var chunkJ7QI3GDX_js = require('../chunk-J7QI3GDX.js');
7
7
  require('../chunk-ZSMWDLMK.js');
@@ -37,7 +37,7 @@ function createLLMsTxtHandler(options = {}) {
37
37
  });
38
38
  }
39
39
  }
40
- const { markdown, metadata } = await chunkZM2ALSBM_js.generateLLMsTxt(generateOptions);
40
+ const { markdown, metadata } = await chunk7TX5BKVA_js.generateLLMsTxt(generateOptions);
41
41
  return new Response(markdown, {
42
42
  status: 200,
43
43
  headers: llmsResponseHeaders(markdown, {
@@ -71,7 +71,7 @@ function createLLMsFullTxtHandler(options = {}) {
71
71
  });
72
72
  }
73
73
  }
74
- const { markdown, metadata } = await chunkZM2ALSBM_js.generateLLMsFullTxt(generateOptions);
74
+ const { markdown, metadata } = await chunk7TX5BKVA_js.generateLLMsFullTxt(generateOptions);
75
75
  return new Response(markdown, {
76
76
  status: 200,
77
77
  headers: llmsResponseHeaders(markdown, {
@@ -586,39 +586,39 @@ Object.defineProperty(exports, "buildAiDiscoveryHeaders", {
586
586
  });
587
587
  Object.defineProperty(exports, "generateLLMsFullTxt", {
588
588
  enumerable: true,
589
- get: function () { return chunkZM2ALSBM_js.generateLLMsFullTxt; }
589
+ get: function () { return chunk7TX5BKVA_js.generateLLMsFullTxt; }
590
590
  });
591
591
  Object.defineProperty(exports, "generateLLMsTxt", {
592
592
  enumerable: true,
593
- get: function () { return chunkZM2ALSBM_js.generateLLMsTxt; }
593
+ get: function () { return chunk7TX5BKVA_js.generateLLMsTxt; }
594
594
  });
595
595
  Object.defineProperty(exports, "getBusinessInfo", {
596
596
  enumerable: true,
597
- get: function () { return chunkZM2ALSBM_js.getBusinessInfo; }
597
+ get: function () { return chunk7TX5BKVA_js.getBusinessInfo; }
598
598
  });
599
599
  Object.defineProperty(exports, "getFAQItems", {
600
600
  enumerable: true,
601
- get: function () { return chunkZM2ALSBM_js.getFAQItems; }
601
+ get: function () { return chunk7TX5BKVA_js.getFAQItems; }
602
602
  });
603
603
  Object.defineProperty(exports, "getLLMsData", {
604
604
  enumerable: true,
605
- get: function () { return chunkZM2ALSBM_js.getLLMsData; }
605
+ get: function () { return chunk7TX5BKVA_js.getLLMsData; }
606
606
  });
607
607
  Object.defineProperty(exports, "getOptimizedLLMsTxt", {
608
608
  enumerable: true,
609
- get: function () { return chunkZM2ALSBM_js.getOptimizedLLMsTxt; }
609
+ get: function () { return chunk7TX5BKVA_js.getOptimizedLLMsTxt; }
610
610
  });
611
611
  Object.defineProperty(exports, "getPageSummaries", {
612
612
  enumerable: true,
613
- get: function () { return chunkZM2ALSBM_js.getPageSummaries; }
613
+ get: function () { return chunk7TX5BKVA_js.getPageSummaries; }
614
614
  });
615
615
  Object.defineProperty(exports, "getServices", {
616
616
  enumerable: true,
617
- get: function () { return chunkZM2ALSBM_js.getServices; }
617
+ get: function () { return chunk7TX5BKVA_js.getServices; }
618
618
  });
619
619
  Object.defineProperty(exports, "writeLLMsTxtToPublic", {
620
620
  enumerable: true,
621
- get: function () { return chunkZM2ALSBM_js.writeLLMsTxtToPublic; }
621
+ get: function () { return chunk7TX5BKVA_js.writeLLMsTxtToPublic; }
622
622
  });
623
623
  Object.defineProperty(exports, "LLMS_DISCLAIMER_MAX_LENGTH", {
624
624
  enumerable: true,
@@ -1,6 +1,6 @@
1
1
  export { buildAiDiscoveryHeaders } from '../chunk-H4OBGC43.mjs';
2
- import { generateLLMsTxt, generateLLMsFullTxt } from '../chunk-6FWQMKVO.mjs';
3
- export { generateLLMsFullTxt, generateLLMsTxt, getBusinessInfo, getFAQItems, getLLMsData, getOptimizedLLMsTxt, getPageSummaries, getServices, writeLLMsTxtToPublic } from '../chunk-6FWQMKVO.mjs';
2
+ import { generateLLMsTxt, generateLLMsFullTxt } from '../chunk-WN5A3KSY.mjs';
3
+ export { generateLLMsFullTxt, generateLLMsTxt, getBusinessInfo, getFAQItems, getLLMsData, getOptimizedLLMsTxt, getPageSummaries, getServices, writeLLMsTxtToPublic } from '../chunk-WN5A3KSY.mjs';
4
4
  export { LLMS_DISCLAIMER_MAX_LENGTH, LLMS_PUBLIC_SUMMARY_MAX_LENGTH, LLM_GEO_CONTRACT_VERSION, MANAGED_LLM_SCHEMA_KNOWN_KEYS, pickManagedLlmSchemaForJsonLd, sanitizeLlmsDisclaimerLine, sanitizeLlmsPublicSummary, sanitizePrimaryLanguageTag } from '../chunk-H23ZT2I2.mjs';
5
5
  import { safeJsonLd } from '../chunk-662YHKWW.mjs';
6
6
  import '../chunk-4XPGGLVP.mjs';
@@ -1,6 +1,8 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
+ var chunkEAASMIWX_js = require('../chunk-EAASMIWX.js');
5
+ require('../chunk-WRZQS7O4.js');
4
6
  require('../chunk-ZSMWDLMK.js');
5
7
  var react = require('react');
6
8
  var reactGoogleMaps = require('@vis.gl/react-google-maps');
@@ -126,19 +128,6 @@ function getApiConfig() {
126
128
  const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ || "" : process.env.SONOR_API_KEY || "";
127
129
  return { apiUrl, apiKey };
128
130
  }
129
- async function fetchWithRetry(url, headers, retries = 2) {
130
- for (let attempt = 0; attempt <= retries; attempt++) {
131
- const response = await fetch(url, { headers });
132
- if (response.ok) return response;
133
- if (response.status === 429 && attempt < retries) {
134
- const delay = Math.pow(2, attempt + 1) * 1e3;
135
- await new Promise((r) => setTimeout(r, delay));
136
- continue;
137
- }
138
- return response;
139
- }
140
- throw new Error("fetchWithRetry exhausted");
141
- }
142
131
  var configCache = null;
143
132
  var CONFIG_CACHE_TTL = 3e5;
144
133
  var placesCache = /* @__PURE__ */ new Map();
@@ -150,9 +139,10 @@ async function fetchMapsConfig() {
150
139
  const { apiUrl, apiKey } = getApiConfig();
151
140
  if (apiKey) {
152
141
  try {
153
- const res = await fetchWithRetry(
142
+ const res = await chunkEAASMIWX_js.sonorFetch(
154
143
  `${apiUrl}/api/public/maps/config`,
155
- { "Content-Type": "application/json", "x-api-key": apiKey }
144
+ { headers: { "Content-Type": "application/json" } },
145
+ { apiKey }
156
146
  );
157
147
  if (res.ok) {
158
148
  const data = await res.json();
@@ -189,9 +179,10 @@ async function fetchNearbyPlaces(params) {
189
179
  });
190
180
  if (params.radius) searchParams.set("radius", String(params.radius));
191
181
  if (params.limit) searchParams.set("limit", String(params.limit));
192
- const res = await fetchWithRetry(
182
+ const res = await chunkEAASMIWX_js.sonorFetch(
193
183
  `${apiUrl}/api/public/maps/nearby-places?${searchParams}`,
194
- { "Content-Type": "application/json", "x-api-key": apiKey }
184
+ { headers: { "Content-Type": "application/json" } },
185
+ { apiKey }
195
186
  );
196
187
  if (!res.ok) return [];
197
188
  const data = await res.json();
@@ -199,7 +190,7 @@ async function fetchNearbyPlaces(params) {
199
190
  placesCache.set(cacheKey, { data: places, timestamp: Date.now() });
200
191
  return places;
201
192
  } catch (err) {
202
- console.error("[site-kit/maps] Failed to fetch nearby places:", err);
193
+ console.warn("[site-kit/maps] Failed to fetch nearby places:", err);
203
194
  return [];
204
195
  }
205
196
  }