@sonordev/site-kit 3.0.5 → 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 (215) 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-FT5JNCD4.mjs → chunk-AB2VYIYU.mjs} +11 -11
  49. package/dist/{chunk-FT5JNCD4.mjs.map → chunk-AB2VYIYU.mjs.map} +1 -1
  50. package/dist/{chunk-I5TJIESQ.js → chunk-BMKZHXWQ.js} +11 -11
  51. package/dist/{chunk-I5TJIESQ.js.map → chunk-BMKZHXWQ.js.map} +1 -1
  52. package/dist/{chunk-KHFNPPOX.mjs → chunk-DSRPKUQI.mjs} +6 -6
  53. package/dist/{chunk-KHFNPPOX.mjs.map → chunk-DSRPKUQI.mjs.map} +1 -1
  54. package/dist/chunk-EAASMIWX.js +258 -0
  55. package/dist/chunk-EAASMIWX.js.map +1 -0
  56. package/dist/{chunk-T4QBSRA4.mjs → chunk-EIWRPGOK.mjs} +37 -60
  57. package/dist/chunk-EIWRPGOK.mjs.map +1 -0
  58. package/dist/{chunk-O7E6QVK3.js → chunk-GCDVKOSM.js} +3 -3
  59. package/dist/{chunk-O7E6QVK3.js.map → chunk-GCDVKOSM.js.map} +1 -1
  60. package/dist/{chunk-KMZ527EM.mjs → chunk-GN62H6EG.mjs} +34 -8
  61. package/dist/chunk-GN62H6EG.mjs.map +1 -0
  62. package/dist/{chunk-UJQYSVYV.js → chunk-GQF6QAQS.js} +4 -4
  63. package/dist/{chunk-UJQYSVYV.js.map → chunk-GQF6QAQS.js.map} +1 -1
  64. package/dist/{chunk-MLJXELPA.js → chunk-HAZN43I6.js} +60 -94
  65. package/dist/chunk-HAZN43I6.js.map +1 -0
  66. package/dist/chunk-LZSCCJ4O.mjs +253 -0
  67. package/dist/chunk-LZSCCJ4O.mjs.map +1 -0
  68. package/dist/{chunk-TCVHI3JP.mjs → chunk-N2HR2E4Z.mjs} +60 -92
  69. package/dist/chunk-N2HR2E4Z.mjs.map +1 -0
  70. package/dist/{chunk-VCWSSGCU.js → chunk-NAVDN4AE.js} +7 -7
  71. package/dist/{chunk-VCWSSGCU.js.map → chunk-NAVDN4AE.js.map} +1 -1
  72. package/dist/chunk-NDHFU4HV.mjs +97 -0
  73. package/dist/chunk-NDHFU4HV.mjs.map +1 -0
  74. package/dist/{chunk-PST3NOEM.mjs → chunk-O237KKCX.mjs} +4 -4
  75. package/dist/{chunk-PST3NOEM.mjs.map → chunk-O237KKCX.mjs.map} +1 -1
  76. package/dist/{chunk-5AESOLXS.mjs → chunk-OTY7EWET.mjs} +13 -11
  77. package/dist/chunk-OTY7EWET.mjs.map +1 -0
  78. package/dist/chunk-Q2NBD54V.js +69 -0
  79. package/dist/chunk-Q2NBD54V.js.map +1 -0
  80. package/dist/{chunk-DGHPOWLU.js → chunk-R5A64DCX.js} +8 -8
  81. package/dist/{chunk-DGHPOWLU.js.map → chunk-R5A64DCX.js.map} +1 -1
  82. package/dist/{chunk-3YQCPLGP.mjs → chunk-RFIWVH3D.mjs} +41 -79
  83. package/dist/chunk-RFIWVH3D.mjs.map +1 -0
  84. package/dist/{chunk-NNZDHLXN.js → chunk-S3YQGTBF.js} +35 -9
  85. package/dist/chunk-S3YQGTBF.js.map +1 -0
  86. package/dist/{chunk-IBDVZ6BF.js → chunk-SLWTG2LJ.js} +69 -74
  87. package/dist/chunk-SLWTG2LJ.js.map +1 -0
  88. package/dist/chunk-SN2O72UF.mjs +6 -0
  89. package/dist/chunk-SN2O72UF.mjs.map +1 -0
  90. package/dist/{chunk-65SMJBHW.mjs → chunk-T2DEEKVQ.mjs} +69 -74
  91. package/dist/chunk-T2DEEKVQ.mjs.map +1 -0
  92. package/dist/{chunk-3GCEFS73.js → chunk-TE3W5REY.js} +39 -62
  93. package/dist/chunk-TE3W5REY.js.map +1 -0
  94. package/dist/{chunk-IFVMZFNT.js → chunk-TVCCZ7NR.js} +41 -79
  95. package/dist/chunk-TVCCZ7NR.js.map +1 -0
  96. package/dist/{chunk-EBJ4S5DU.js → chunk-UT2CF4FC.js} +4 -4
  97. package/dist/{chunk-EBJ4S5DU.js.map → chunk-UT2CF4FC.js.map} +1 -1
  98. package/dist/chunk-V6NVBFEP.js +54 -0
  99. package/dist/chunk-V6NVBFEP.js.map +1 -0
  100. package/dist/chunk-WRZQS7O4.js +8 -0
  101. package/dist/chunk-WRZQS7O4.js.map +1 -0
  102. package/dist/chunk-WZHWWOUC.js +51 -0
  103. package/dist/chunk-WZHWWOUC.js.map +1 -0
  104. package/dist/chunk-X3S6KXAP.mjs +47 -0
  105. package/dist/chunk-X3S6KXAP.mjs.map +1 -0
  106. package/dist/chunk-X4OPM3SK.mjs +43 -0
  107. package/dist/chunk-X4OPM3SK.mjs.map +1 -0
  108. package/dist/chunk-ZPLN537Q.mjs +57 -0
  109. package/dist/chunk-ZPLN537Q.mjs.map +1 -0
  110. package/dist/cli/index.js +8986 -1276
  111. package/dist/cli/index.js.map +1 -1
  112. package/dist/cli/index.mjs +8972 -1280
  113. package/dist/cli/index.mjs.map +1 -1
  114. package/dist/commerce/index.js +50 -48
  115. package/dist/commerce/index.mjs +5 -3
  116. package/dist/engage/index.js +11 -8
  117. package/dist/engage/index.js.map +1 -1
  118. package/dist/engage/index.mjs +8 -5
  119. package/dist/engage/index.mjs.map +1 -1
  120. package/dist/fleet/contract.d.mts +99 -0
  121. package/dist/fleet/contract.d.ts +99 -0
  122. package/dist/fleet/contract.js +53 -0
  123. package/dist/fleet/contract.js.map +1 -0
  124. package/dist/fleet/contract.mjs +4 -0
  125. package/dist/fleet/contract.mjs.map +1 -0
  126. package/dist/fleet/index.d.mts +89 -0
  127. package/dist/fleet/index.d.ts +89 -0
  128. package/dist/fleet/index.js +74 -0
  129. package/dist/fleet/index.js.map +1 -0
  130. package/dist/fleet/index.mjs +9 -0
  131. package/dist/fleet/index.mjs.map +1 -0
  132. package/dist/forms/contract.d.mts +69 -0
  133. package/dist/forms/contract.d.ts +69 -0
  134. package/dist/forms/contract.js +33 -0
  135. package/dist/forms/contract.js.map +1 -0
  136. package/dist/forms/contract.mjs +4 -0
  137. package/dist/forms/contract.mjs.map +1 -0
  138. package/dist/forms/index.js +21 -17
  139. package/dist/forms/index.js.map +1 -1
  140. package/dist/forms/index.mjs +14 -10
  141. package/dist/forms/index.mjs.map +1 -1
  142. package/dist/forms/styles.css +10 -8
  143. package/dist/images/index.js +10 -8
  144. package/dist/images/index.mjs +3 -1
  145. package/dist/index.js +98 -96
  146. package/dist/index.js.map +1 -1
  147. package/dist/index.mjs +9 -7
  148. package/dist/index.mjs.map +1 -1
  149. package/dist/layout/client.d.mts +8 -1
  150. package/dist/layout/client.d.ts +8 -1
  151. package/dist/layout/client.js +8 -3
  152. package/dist/layout/client.mjs +7 -2
  153. package/dist/layout/index.d.mts +7 -1
  154. package/dist/layout/index.d.ts +7 -1
  155. package/dist/layout/index.js +57 -4
  156. package/dist/layout/index.js.map +1 -1
  157. package/dist/layout/index.mjs +56 -3
  158. package/dist/layout/index.mjs.map +1 -1
  159. package/dist/maps/index.js +9 -18
  160. package/dist/maps/index.js.map +1 -1
  161. package/dist/maps/index.mjs +9 -18
  162. package/dist/maps/index.mjs.map +1 -1
  163. package/dist/portfolio/contract.d.mts +61 -0
  164. package/dist/portfolio/contract.d.ts +61 -0
  165. package/dist/portfolio/contract.js +43 -0
  166. package/dist/portfolio/contract.js.map +1 -0
  167. package/dist/portfolio/contract.mjs +38 -0
  168. package/dist/portfolio/contract.mjs.map +1 -0
  169. package/dist/seo/client.js +4 -2
  170. package/dist/seo/client.mjs +3 -1
  171. package/dist/seo/index.js +4 -2
  172. package/dist/seo/index.js.map +1 -1
  173. package/dist/seo/index.mjs +3 -1
  174. package/dist/seo/index.mjs.map +1 -1
  175. package/dist/seo-pages/contract.d.mts +62 -0
  176. package/dist/seo-pages/contract.d.ts +62 -0
  177. package/dist/seo-pages/contract.js +48 -0
  178. package/dist/seo-pages/contract.js.map +1 -0
  179. package/dist/seo-pages/contract.mjs +44 -0
  180. package/dist/seo-pages/contract.mjs.map +1 -0
  181. package/dist/signal/index.js +11 -11
  182. package/dist/signal/index.mjs +2 -2
  183. package/dist/sites/contract.d.mts +57 -0
  184. package/dist/sites/contract.d.ts +57 -0
  185. package/dist/sites/contract.js +37 -0
  186. package/dist/sites/contract.js.map +1 -0
  187. package/dist/sites/contract.mjs +4 -0
  188. package/dist/sites/contract.mjs.map +1 -0
  189. package/package.json +44 -4
  190. package/skills/site-kit/SKILL.md +66 -0
  191. package/dist/AnalyticsProvider-MLT26JRP.mjs +0 -6
  192. package/dist/AnalyticsProvider-WTOGN7LV.js +0 -27
  193. package/dist/BlogPostFaqSection-H556RV62.js +0 -13
  194. package/dist/ChatWidget-MAL7FB4M.js +0 -14
  195. package/dist/ChatWidget-QPZTPSPE.mjs +0 -5
  196. package/dist/EngageWidget-IPBUA4DW.mjs +0 -5
  197. package/dist/EngageWidget-N3CABK3L.js +0 -14
  198. package/dist/SignalCore-4MDIJUAP.mjs.map +0 -1
  199. package/dist/SignalCore-ZMFUE3N4.js.map +0 -1
  200. package/dist/SitemapSync-FUCH6Y6J.js +0 -13
  201. package/dist/SitemapSync-LCSPRAWU.mjs +0 -4
  202. package/dist/chunk-3GCEFS73.js.map +0 -1
  203. package/dist/chunk-3YQCPLGP.mjs.map +0 -1
  204. package/dist/chunk-5AESOLXS.mjs.map +0 -1
  205. package/dist/chunk-65SMJBHW.mjs.map +0 -1
  206. package/dist/chunk-IBDVZ6BF.js.map +0 -1
  207. package/dist/chunk-IFVMZFNT.js.map +0 -1
  208. package/dist/chunk-KMZ527EM.mjs.map +0 -1
  209. package/dist/chunk-MLJXELPA.js.map +0 -1
  210. package/dist/chunk-NNZDHLXN.js.map +0 -1
  211. package/dist/chunk-QTWA44S3.mjs.map +0 -1
  212. package/dist/chunk-RI6JZUAE.js.map +0 -1
  213. package/dist/chunk-T4QBSRA4.mjs.map +0 -1
  214. package/dist/chunk-TCVHI3JP.mjs.map +0 -1
  215. package/dist/chunk-YJWKHCOB.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use client';
2
- export { ExperimentConversion, SignalExperiment, useExperimentVariant } from '../chunk-EUDTPXUI.mjs';
3
- export { SignalBridge, useSignal, useSignalConfig, useSignalEvent, useSignalExperiment, useSignalOutcome } from '../chunk-YFGKIKXA.mjs';
2
+ export { ExperimentConversion, SignalExperiment, useExperimentVariant } from '../chunk-6HPNFWSC.mjs';
3
+ export { SignalBridge, useSignal, useSignalConfig, useSignalEvent, useSignalExperiment, useSignalOutcome } from '../chunk-57QV6NIY.mjs';
4
4
  import '../chunk-4XPGGLVP.mjs';
5
5
  //# sourceMappingURL=index.mjs.map
6
6
  //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @sonordev/site-kit/sites/contract — Multi-site "site" dimension host contract (v1)
3
+ *
4
+ * Single source of truth for normalizing and comparing sub-site host values
5
+ * (the multi-site `site` dimension, see CLAUDE.md). Same pattern as
6
+ * `@sonordev/site-kit/llms/contract` and `@sonordev/site-kit/slots/contract`:
7
+ * site-kit owns the canonical logic; the Nest APIs keep byte-identical inline
8
+ * mirrors (sonor-api `src/common/utils/site-host.ts`, signal-api
9
+ * `src/shared/utils/seo-page-pick.util.ts`) guarded by the shared drift-test
10
+ * vectors in `contract.test.ts` — so a divergent copy fails CI on either side.
11
+ *
12
+ * Pure string logic only (no Node built-ins) so it is safe in the browser:
13
+ * site-kit itself normalizes `__SITE_KIT_SITE__` with `normalizeSiteHost`.
14
+ */
15
+ /** Bump on breaking changes to the normalized host shape. */
16
+ declare const SITE_HOST_CONTRACT_VERSION: 1;
17
+ /**
18
+ * normalizeSiteHost — canonical normalizer for a sub-site host value before it
19
+ * is persisted or used as a filter.
20
+ *
21
+ * `https://OhioPowerStudies.com:3000/path` → `ohiopowerstudies.com`
22
+ *
23
+ * Lowercases, strips protocol, keeps host only (drops path + query), strips
24
+ * port (rollups span dev/prod). Returns null for empty/non-string input.
25
+ * Note: `www.` is intentionally preserved here — use `sameSite`/`stripWww`
26
+ * for www-insensitive equality.
27
+ */
28
+ declare function normalizeSiteHost(site?: string | null): string | null;
29
+ /** Strip a leading `www.` from a host ("www.tps.com" → "tps.com"). */
30
+ declare function stripWww(host: string): string;
31
+ /**
32
+ * www-insensitive sub-site host equality — `www.a.com` and `a.com` are the
33
+ * same site. Operands are expected already-normalized (lowercase host);
34
+ * returns false if either side is missing.
35
+ */
36
+ declare function sameSite(a: string | null | undefined, b: string | null | undefined): boolean;
37
+ /**
38
+ * Host of a full URL ("https://ohiopowerstudies.com/x" → "ohiopowerstudies.com"),
39
+ * null for site-relative paths ("/services") and unparseable input.
40
+ */
41
+ declare function hostFromUrl(value: string | null | undefined): string | null;
42
+ /** True when `value` is a non-empty normalized host (lowercase, no protocol/path/port). */
43
+ declare function isNormalizedSiteHost(value: unknown): value is string;
44
+ /**
45
+ * Agent-native contract: JSON Schema for a normalized `site` value, so services
46
+ * and agents can validate the dimension mechanically without importing the code.
47
+ */
48
+ declare const SITE_HOST_JSON_SCHEMA: {
49
+ readonly $schema: "http://json-schema.org/draft-07/schema#";
50
+ readonly $id: "https://sonor.io/schemas/site-host.v1.json";
51
+ readonly title: "NormalizedSiteHost";
52
+ readonly description: "A normalized sub-site host (the multi-site `site` dimension): lowercase, no protocol, no path, no port. null denotes a project-wide (unattributed) row.";
53
+ readonly type: readonly ["string", "null"];
54
+ readonly pattern: "^[a-z0-9.-]+$";
55
+ };
56
+
57
+ export { SITE_HOST_CONTRACT_VERSION, SITE_HOST_JSON_SCHEMA, hostFromUrl, isNormalizedSiteHost, normalizeSiteHost, sameSite, stripWww };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @sonordev/site-kit/sites/contract — Multi-site "site" dimension host contract (v1)
3
+ *
4
+ * Single source of truth for normalizing and comparing sub-site host values
5
+ * (the multi-site `site` dimension, see CLAUDE.md). Same pattern as
6
+ * `@sonordev/site-kit/llms/contract` and `@sonordev/site-kit/slots/contract`:
7
+ * site-kit owns the canonical logic; the Nest APIs keep byte-identical inline
8
+ * mirrors (sonor-api `src/common/utils/site-host.ts`, signal-api
9
+ * `src/shared/utils/seo-page-pick.util.ts`) guarded by the shared drift-test
10
+ * vectors in `contract.test.ts` — so a divergent copy fails CI on either side.
11
+ *
12
+ * Pure string logic only (no Node built-ins) so it is safe in the browser:
13
+ * site-kit itself normalizes `__SITE_KIT_SITE__` with `normalizeSiteHost`.
14
+ */
15
+ /** Bump on breaking changes to the normalized host shape. */
16
+ declare const SITE_HOST_CONTRACT_VERSION: 1;
17
+ /**
18
+ * normalizeSiteHost — canonical normalizer for a sub-site host value before it
19
+ * is persisted or used as a filter.
20
+ *
21
+ * `https://OhioPowerStudies.com:3000/path` → `ohiopowerstudies.com`
22
+ *
23
+ * Lowercases, strips protocol, keeps host only (drops path + query), strips
24
+ * port (rollups span dev/prod). Returns null for empty/non-string input.
25
+ * Note: `www.` is intentionally preserved here — use `sameSite`/`stripWww`
26
+ * for www-insensitive equality.
27
+ */
28
+ declare function normalizeSiteHost(site?: string | null): string | null;
29
+ /** Strip a leading `www.` from a host ("www.tps.com" → "tps.com"). */
30
+ declare function stripWww(host: string): string;
31
+ /**
32
+ * www-insensitive sub-site host equality — `www.a.com` and `a.com` are the
33
+ * same site. Operands are expected already-normalized (lowercase host);
34
+ * returns false if either side is missing.
35
+ */
36
+ declare function sameSite(a: string | null | undefined, b: string | null | undefined): boolean;
37
+ /**
38
+ * Host of a full URL ("https://ohiopowerstudies.com/x" → "ohiopowerstudies.com"),
39
+ * null for site-relative paths ("/services") and unparseable input.
40
+ */
41
+ declare function hostFromUrl(value: string | null | undefined): string | null;
42
+ /** True when `value` is a non-empty normalized host (lowercase, no protocol/path/port). */
43
+ declare function isNormalizedSiteHost(value: unknown): value is string;
44
+ /**
45
+ * Agent-native contract: JSON Schema for a normalized `site` value, so services
46
+ * and agents can validate the dimension mechanically without importing the code.
47
+ */
48
+ declare const SITE_HOST_JSON_SCHEMA: {
49
+ readonly $schema: "http://json-schema.org/draft-07/schema#";
50
+ readonly $id: "https://sonor.io/schemas/site-host.v1.json";
51
+ readonly title: "NormalizedSiteHost";
52
+ readonly description: "A normalized sub-site host (the multi-site `site` dimension): lowercase, no protocol, no path, no port. null denotes a project-wide (unattributed) row.";
53
+ readonly type: readonly ["string", "null"];
54
+ readonly pattern: "^[a-z0-9.-]+$";
55
+ };
56
+
57
+ export { SITE_HOST_CONTRACT_VERSION, SITE_HOST_JSON_SCHEMA, hostFromUrl, isNormalizedSiteHost, normalizeSiteHost, sameSite, stripWww };
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ var chunkWZHWWOUC_js = require('../chunk-WZHWWOUC.js');
4
+ require('../chunk-ZSMWDLMK.js');
5
+
6
+
7
+
8
+ Object.defineProperty(exports, "SITE_HOST_CONTRACT_VERSION", {
9
+ enumerable: true,
10
+ get: function () { return chunkWZHWWOUC_js.SITE_HOST_CONTRACT_VERSION; }
11
+ });
12
+ Object.defineProperty(exports, "SITE_HOST_JSON_SCHEMA", {
13
+ enumerable: true,
14
+ get: function () { return chunkWZHWWOUC_js.SITE_HOST_JSON_SCHEMA; }
15
+ });
16
+ Object.defineProperty(exports, "hostFromUrl", {
17
+ enumerable: true,
18
+ get: function () { return chunkWZHWWOUC_js.hostFromUrl; }
19
+ });
20
+ Object.defineProperty(exports, "isNormalizedSiteHost", {
21
+ enumerable: true,
22
+ get: function () { return chunkWZHWWOUC_js.isNormalizedSiteHost; }
23
+ });
24
+ Object.defineProperty(exports, "normalizeSiteHost", {
25
+ enumerable: true,
26
+ get: function () { return chunkWZHWWOUC_js.normalizeSiteHost; }
27
+ });
28
+ Object.defineProperty(exports, "sameSite", {
29
+ enumerable: true,
30
+ get: function () { return chunkWZHWWOUC_js.sameSite; }
31
+ });
32
+ Object.defineProperty(exports, "stripWww", {
33
+ enumerable: true,
34
+ get: function () { return chunkWZHWWOUC_js.stripWww; }
35
+ });
36
+ //# sourceMappingURL=contract.js.map
37
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"contract.js"}
@@ -0,0 +1,4 @@
1
+ export { SITE_HOST_CONTRACT_VERSION, SITE_HOST_JSON_SCHEMA, hostFromUrl, isNormalizedSiteHost, normalizeSiteHost, sameSite, stripWww } from '../chunk-X4OPM3SK.mjs';
2
+ import '../chunk-4XPGGLVP.mjs';
3
+ //# sourceMappingURL=contract.mjs.map
4
+ //# sourceMappingURL=contract.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"contract.mjs"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonordev/site-kit",
3
- "version": "3.0.5",
3
+ "version": "3.2.0",
4
4
  "description": "Complete client-side integration kit for Sonor - SEO, Analytics, Engage, Forms, Blog",
5
5
  "license": "MIT",
6
6
  "sideEffects": [
@@ -60,6 +60,11 @@
60
60
  "import": "./dist/forms/index.mjs",
61
61
  "require": "./dist/forms/index.js"
62
62
  },
63
+ "./forms/contract": {
64
+ "types": "./dist/forms/contract.d.ts",
65
+ "import": "./dist/forms/contract.mjs",
66
+ "require": "./dist/forms/contract.js"
67
+ },
63
68
  "./blog": {
64
69
  "types": "./dist/blog/index.d.ts",
65
70
  "import": "./dist/blog/index.mjs",
@@ -145,6 +150,31 @@
145
150
  "import": "./dist/slots/contract.mjs",
146
151
  "require": "./dist/slots/contract.js"
147
152
  },
153
+ "./sites/contract": {
154
+ "types": "./dist/sites/contract.d.ts",
155
+ "import": "./dist/sites/contract.mjs",
156
+ "require": "./dist/sites/contract.js"
157
+ },
158
+ "./seo-pages/contract": {
159
+ "types": "./dist/seo-pages/contract.d.ts",
160
+ "import": "./dist/seo-pages/contract.mjs",
161
+ "require": "./dist/seo-pages/contract.js"
162
+ },
163
+ "./portfolio/contract": {
164
+ "types": "./dist/portfolio/contract.d.ts",
165
+ "import": "./dist/portfolio/contract.mjs",
166
+ "require": "./dist/portfolio/contract.js"
167
+ },
168
+ "./fleet": {
169
+ "types": "./dist/fleet/index.d.ts",
170
+ "import": "./dist/fleet/index.mjs",
171
+ "require": "./dist/fleet/index.js"
172
+ },
173
+ "./fleet/contract": {
174
+ "types": "./dist/fleet/contract.d.ts",
175
+ "import": "./dist/fleet/contract.mjs",
176
+ "require": "./dist/fleet/contract.js"
177
+ },
148
178
  "./robots": {
149
179
  "types": "./dist/robots/index.d.ts",
150
180
  "import": "./dist/robots/index.mjs",
@@ -201,16 +231,24 @@
201
231
  "files": [
202
232
  "dist",
203
233
  "scripts/postinstall.cjs",
204
- "README.md"
234
+ "README.md",
235
+ "AGENTS.md",
236
+ "agent-manifest.json",
237
+ "skills"
205
238
  ],
206
239
  "scripts": {
207
240
  "postinstall": "node ./scripts/postinstall.cjs",
208
- "build": "tsup",
241
+ "build": "NODE_OPTIONS=--max-old-space-size=8192 tsup && node scripts/gen-agent-manifest.cjs",
209
242
  "dev": "tsup --watch",
243
+ "manifest:gen": "node scripts/gen-agent-manifest.cjs",
210
244
  "test": "vitest run",
245
+ "test:integration": "node test/integration/run.mjs",
246
+ "test:integration:baseline": "node test/integration/run.mjs --update-baseline",
247
+ "test:integration:selfcheck": "node test/integration/checks/selfcheck.mjs",
248
+ "test:integration:setup": "playwright install chromium",
211
249
  "clean": "rm -rf dist",
212
250
  "typecheck": "tsc --noEmit",
213
- "prepublishOnly": "rm -rf dist && tsup && node scripts/verify-dts.cjs"
251
+ "prepublishOnly": "rm -rf dist && NODE_OPTIONS=--max-old-space-size=8192 tsup && node scripts/gen-agent-manifest.cjs && node scripts/verify-dts.cjs && node scripts/prepublish-integration.cjs"
214
252
  },
215
253
  "peerDependencies": {
216
254
  "@vis.gl/react-google-maps": ">=1.0.0",
@@ -240,6 +278,8 @@
240
278
  "socket.io-client": "^4.8.3"
241
279
  },
242
280
  "devDependencies": {
281
+ "@axe-core/playwright": "^4.10.2",
282
+ "playwright": "^1.56.0",
243
283
  "@babel/parser": "^7.23.0",
244
284
  "@babel/traverse": "^7.23.0",
245
285
  "@babel/types": "^7.28.6",
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: site-kit
3
+ description: Integrate, upgrade, or debug a Next.js site's Sonor integration via @sonordev/site-kit. Use when wiring a site to Sonor, migrating a 2.x integration to 3.x, or diagnosing SSR/analytics/key issues on a site that installs @sonordev/site-kit. Trigger on "wire up Sonor", "connect this site to Sonor", "site-kit", "sonor-setup", "SiteKitLayout", "analytics bailout / CSR", or a failing `sonor-setup verify`.
4
+ ---
5
+
6
+ # @sonordev/site-kit — integration skill
7
+
8
+ You are integrating a Next.js site with Sonor using `@sonordev/site-kit`. The
9
+ package ships its own machine-readable contract; use it instead of guessing.
10
+
11
+ ## Golden rules
12
+
13
+ 1. **`verify` exit 0 is the definition of done.** Not "the command ran." Loop
14
+ `sonor-setup verify --json` until it exits 0.
15
+ 2. **Drive the CLI in `--json` mode.** Every agent-native command
16
+ (`verify`, `doctor`, `codemod`, `manifest`, `init`, `install`, `upgrade`)
17
+ prints ONE JSON envelope to stdout. Parse it; branch on `exitCode`/`checks`.
18
+ 3. **Never let a command prompt.** Pass `--yes` (and `--api-key` to `init`) so it
19
+ runs non-interactively. Exit `3` means you're missing a flag — the error's
20
+ `fix` names it.
21
+ 4. **One env var: `SONOR_API_KEY`** (server-side, no `NEXT_PUBLIC_` prefix). Never
22
+ add `UPTRADE_*`, `NEXT_PUBLIC_SONOR_API_KEY`, or `SONOR_PROJECT_ID`.
23
+
24
+ ## First step — discover, don't assume
25
+
26
+ ```bash
27
+ cat node_modules/@sonordev/site-kit/AGENTS.md # human-skimmable guide
28
+ npx sonor-setup manifest --json # modules, env, patterns, failure modes
29
+ ```
30
+
31
+ ## Wire a bare site
32
+
33
+ ```bash
34
+ npx sonor-setup init --api-key "$SONOR_API_KEY" --yes --json
35
+ npx sonor-setup scaffold --yes --json
36
+ next build
37
+ npx sonor-setup verify --json
38
+ ```
39
+
40
+ ## Migrate a 2.x site
41
+
42
+ ```bash
43
+ npx sonor-setup codemod --check --json # exit 1 ⇒ work pending
44
+ npx sonor-setup codemod --write --json # apply (idempotent; writes .bak). Review the diff.
45
+ npx sonor-setup verify --json
46
+ ```
47
+
48
+ Codemod transforms: `provider-to-layout` (SiteKitProvider→SiteKitLayout),
49
+ `uptrade-to-sonor` (package/env/token remnants), `analytics-sibling`
50
+ (detects the SSR-bailout wrapper and flags the fix — apply it by hand).
51
+
52
+ ## Debug a red integration
53
+
54
+ Run `npx sonor-setup doctor --online --json` (or `verify`). Each `checks[]`
55
+ finding has a stable `id`, a `fix`, and often a `fixCommand`. Highest-signal ids:
56
+
57
+ - `ssr.render` — page bailed to client rendering. Fix: `SiteKitLayout` with
58
+ `analytics={false}` + a deferred, childless analytics sibling. Confirm with
59
+ `verify --url <url>`.
60
+ - `key.valid` — key rejected/stale. Put the current `sonor_` key in `.env.local`
61
+ and redeploy (hosts don't pick up env changes without a redeploy).
62
+ - `middleware.netlify` — `runtime:'nodejs'` middleware silently doesn't run on
63
+ Netlify. Gate the logic in a server component.
64
+ - `layout.sitekit` — deprecated `SiteKitProvider`; run the codemod.
65
+
66
+ Apply fixes, then re-run `verify --json`. Done when it exits 0.
@@ -1,6 +0,0 @@
1
- export { AnalyticsProvider, useAnalytics, useAnalyticsOptional, useTrackEvent } from './chunk-65SMJBHW.mjs';
2
- import './chunk-3DPXOZAU.mjs';
3
- import './chunk-EE6SFAZP.mjs';
4
- import './chunk-4XPGGLVP.mjs';
5
- //# sourceMappingURL=AnalyticsProvider-MLT26JRP.mjs.map
6
- //# sourceMappingURL=AnalyticsProvider-MLT26JRP.mjs.map
@@ -1,27 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkIBDVZ6BF_js = require('./chunk-IBDVZ6BF.js');
4
- require('./chunk-HMFFAUCD.js');
5
- require('./chunk-3BNFUMLL.js');
6
- require('./chunk-ZSMWDLMK.js');
7
-
8
-
9
-
10
- Object.defineProperty(exports, "AnalyticsProvider", {
11
- enumerable: true,
12
- get: function () { return chunkIBDVZ6BF_js.AnalyticsProvider; }
13
- });
14
- Object.defineProperty(exports, "useAnalytics", {
15
- enumerable: true,
16
- get: function () { return chunkIBDVZ6BF_js.useAnalytics; }
17
- });
18
- Object.defineProperty(exports, "useAnalyticsOptional", {
19
- enumerable: true,
20
- get: function () { return chunkIBDVZ6BF_js.useAnalyticsOptional; }
21
- });
22
- Object.defineProperty(exports, "useTrackEvent", {
23
- enumerable: true,
24
- get: function () { return chunkIBDVZ6BF_js.useTrackEvent; }
25
- });
26
- //# sourceMappingURL=AnalyticsProvider-WTOGN7LV.js.map
27
- //# sourceMappingURL=AnalyticsProvider-WTOGN7LV.js.map
@@ -1,13 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkUJQYSVYV_js = require('./chunk-UJQYSVYV.js');
4
- require('./chunk-ZSMWDLMK.js');
5
- var jsxRuntime = require('react/jsx-runtime');
6
-
7
- function BlogPostFaqSection(props) {
8
- return /* @__PURE__ */ jsxRuntime.jsx(chunkUJQYSVYV_js.BlogFAQ, { ...props });
9
- }
10
-
11
- exports.BlogPostFaqSection = BlogPostFaqSection;
12
- //# sourceMappingURL=BlogPostFaqSection-H556RV62.js.map
13
- //# sourceMappingURL=BlogPostFaqSection-H556RV62.js.map
@@ -1,14 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkVCWSSGCU_js = require('./chunk-VCWSSGCU.js');
4
- require('./chunk-MLJXELPA.js');
5
- require('./chunk-ZSMWDLMK.js');
6
-
7
-
8
-
9
- Object.defineProperty(exports, "ChatWidget", {
10
- enumerable: true,
11
- get: function () { return chunkVCWSSGCU_js.ChatWidget; }
12
- });
13
- //# sourceMappingURL=ChatWidget-MAL7FB4M.js.map
14
- //# sourceMappingURL=ChatWidget-MAL7FB4M.js.map
@@ -1,5 +0,0 @@
1
- export { ChatWidget } from './chunk-KHFNPPOX.mjs';
2
- import './chunk-TCVHI3JP.mjs';
3
- import './chunk-4XPGGLVP.mjs';
4
- //# sourceMappingURL=ChatWidget-QPZTPSPE.mjs.map
5
- //# sourceMappingURL=ChatWidget-QPZTPSPE.mjs.map
@@ -1,5 +0,0 @@
1
- export { EngageWidget } from './chunk-5AESOLXS.mjs';
2
- import './chunk-EE6SFAZP.mjs';
3
- import './chunk-4XPGGLVP.mjs';
4
- //# sourceMappingURL=EngageWidget-IPBUA4DW.mjs.map
5
- //# sourceMappingURL=EngageWidget-IPBUA4DW.mjs.map
@@ -1,14 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkYJWKHCOB_js = require('./chunk-YJWKHCOB.js');
4
- require('./chunk-3BNFUMLL.js');
5
- require('./chunk-ZSMWDLMK.js');
6
-
7
-
8
-
9
- Object.defineProperty(exports, "EngageWidget", {
10
- enumerable: true,
11
- get: function () { return chunkYJWKHCOB_js.EngageWidget; }
12
- });
13
- //# sourceMappingURL=EngageWidget-N3CABK3L.js.map
14
- //# sourceMappingURL=EngageWidget-N3CABK3L.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/signal/SignalCore.tsx"],"names":[],"mappings":"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","file":"SignalCore-4MDIJUAP.mjs","sourcesContent":["/**\n * @sonordev/site-kit/signal - SignalCore\n *\n * The heavy half of SignalBridge: config fetching, SSE streaming, experiment\n * assignment, event queue/flush, and behavioral tracking. Mounted by the\n * SignalBridge shell as a CHILDLESS lazy sibling — it never wraps page\n * content, so the signal machinery stays out of the server-render path and\n * off the critical hydration chunk. It reports state up to the shell via\n * `onState` and registers its imperative API via `onRegister`.\n */\n\n'use client'\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n// Single source of truth for visitor/session IDs (storage keys _sk_vid/_sk_sid)\n// — shared with Analytics and Engage. Do NOT re-fork ID logic into this module.\nimport { getOrCreateVisitorId, getOrCreateSessionId } from '../shared/identity'\nimport type {\n SignalConfig,\n SignalEvent,\n SignalOutcome,\n ExperimentAssignment,\n} from './types'\n\n// ============================================\n// Shell ↔ core contract\n// ============================================\n\nexport interface SignalCoreApi {\n trackEvent: (event: Partial<SignalEvent>) => void\n trackOutcome: (outcome: SignalOutcome) => void\n getExperiment: (experimentId: string) => ExperimentAssignment | null\n refreshConfig: () => Promise<void>\n}\n\nexport interface SignalCoreState {\n config: SignalConfig | null\n loading: boolean\n error: Error | null\n}\n\nexport interface SignalCoreProps {\n enabled: boolean\n realtime: boolean\n experiments: boolean\n behaviorTracking: boolean\n externalVisitorId?: string\n externalSessionId?: string\n pageMetadataRef?: React.RefObject<Record<string, unknown> | null>\n /** Called with the imperative API on mount, and with null on unmount. */\n onRegister: (api: SignalCoreApi | null) => void\n /** Called whenever config/loading/error change, so the shell can re-render consumers. */\n onState: (state: SignalCoreState) => void\n}\n\n// ============================================\n// Utility Functions\n// ============================================\n\nfunction getApiConfig() {\n const apiUrl = typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n : 'https://api.sonor.io'\n const apiKey = typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_KEY__\n : undefined\n return { apiUrl, apiKey }\n}\n\nfunction getDeviceType(): 'desktop' | 'mobile' | 'tablet' {\n if (typeof window === 'undefined') return 'desktop'\n const ua = navigator.userAgent\n if (/tablet|ipad|playbook|silk/i.test(ua)) return 'tablet'\n if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return 'mobile'\n return 'desktop'\n}\n\nfunction getBrowser(): string {\n if (typeof window === 'undefined') return 'unknown'\n const ua = navigator.userAgent\n if (ua.includes('Firefox')) return 'Firefox'\n if (ua.includes('Edg')) return 'Edge'\n if (ua.includes('Chrome')) return 'Chrome'\n if (ua.includes('Safari')) return 'Safari'\n return 'Other'\n}\n\nfunction getOS(): string {\n if (typeof window === 'undefined') return 'unknown'\n const ua = navigator.userAgent\n if (ua.includes('Windows')) return 'Windows'\n if (ua.includes('Mac')) return 'macOS'\n if (ua.includes('iPhone') || ua.includes('iPad')) return 'iOS'\n if (ua.includes('Android')) return 'Android'\n if (ua.includes('Linux')) return 'Linux'\n return 'Other'\n}\n\n// ============================================\n// SignalCore Component (childless)\n// ============================================\n\nexport function SignalCore({\n enabled,\n realtime,\n experiments,\n behaviorTracking,\n externalVisitorId,\n externalSessionId,\n pageMetadataRef,\n onRegister,\n onState,\n}: SignalCoreProps) {\n const [config, setConfig] = useState<SignalConfig | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n // Refs for SSE and tracking\n const eventSourceRef = useRef<EventSource | null>(null)\n const eventQueueRef = useRef<Partial<SignalEvent>[]>([])\n const flushTimeoutRef = useRef<NodeJS.Timeout | null>(null)\n const assignmentsRef = useRef<Map<string, ExperimentAssignment>>(new Map())\n\n // Behavioral tracking state\n const pageLoadTimeRef = useRef<number>(Date.now())\n const scrollDepthRef = useRef<number>(0)\n const clickCountRef = useRef<number>(0)\n\n const { apiUrl, apiKey } = getApiConfig()\n\n // Use external IDs from shared identity, fall back to the shared generators\n const resolvedVisitorId = externalVisitorId || getOrCreateVisitorId()\n const resolvedSessionId = externalSessionId || getOrCreateSessionId()\n\n // Mirror state up to the shell so context consumers re-render\n useEffect(() => {\n onState({ config, loading, error })\n }, [config, loading, error, onState])\n\n // ============================================\n // Experiment Assignment\n // ============================================\n\n const loadExperimentAssignment = useCallback(async (experimentId: string): Promise<ExperimentAssignment | null> => {\n // Check localStorage first\n const storageKey = `_signal_exp_${experimentId}`\n const stored = localStorage.getItem(storageKey)\n\n if (stored) {\n try {\n const assignment = JSON.parse(stored) as ExperimentAssignment\n if (assignment.expires > Date.now()) {\n assignmentsRef.current.set(experimentId, assignment)\n return assignment\n }\n } catch {\n // Invalid stored data, continue to fetch\n }\n }\n\n // Fetch from API\n try {\n const response = await fetch(`${apiUrl}/api/public/signal/experiment/${experimentId}`, {\n headers: {\n 'x-api-key': apiKey!,\n 'x-visitor-id': resolvedVisitorId,\n },\n })\n\n if (!response.ok) return null\n\n const assignment = await response.json() as ExperimentAssignment\n\n // Store assignment\n localStorage.setItem(storageKey, JSON.stringify(assignment))\n assignmentsRef.current.set(experimentId, assignment)\n\n return assignment\n } catch (err) {\n console.error('[Signal] Experiment assignment error:', err)\n return null\n }\n }, [apiUrl, apiKey])\n\n const getExperiment = useCallback((experimentId: string): ExperimentAssignment | null => {\n return assignmentsRef.current.get(experimentId) || null\n }, [])\n\n // ============================================\n // Config Fetching\n // ============================================\n\n const fetchConfig = useCallback(async () => {\n if (!apiKey || !enabled) {\n setLoading(false)\n return\n }\n\n try {\n const response = await fetch(`${apiUrl}/api/public/signal/config`, {\n headers: {\n 'x-api-key': apiKey,\n 'x-visitor-id': resolvedVisitorId,\n },\n })\n\n if (!response.ok) {\n throw new Error(`Failed to fetch Signal config: ${response.statusText}`)\n }\n\n const data = await response.json()\n setConfig(data.config)\n setError(null)\n\n // Load experiment assignments\n if (experiments && data.config?.experiments) {\n for (const exp of data.config.experiments) {\n if (exp.status === 'running') {\n await loadExperimentAssignment(exp.id)\n }\n }\n }\n } catch (err) {\n console.error('[Signal] Config fetch error:', err)\n setError(err as Error)\n } finally {\n setLoading(false)\n }\n }, [apiUrl, apiKey, enabled, experiments, resolvedVisitorId, loadExperimentAssignment])\n\n // ============================================\n // SSE Real-time Updates\n // ============================================\n\n const connectSSE = useCallback(() => {\n if (!apiKey || !enabled || !realtime) return\n\n // Close existing connection\n if (eventSourceRef.current) {\n eventSourceRef.current.close()\n }\n\n const url = `${apiUrl}/api/public/signal/stream?key=${apiKey}`\n const eventSource = new EventSource(url)\n\n eventSource.addEventListener('config_update', (e) => {\n try {\n const { config: newConfig, version } = JSON.parse(e.data)\n setConfig(prev => {\n if (prev?.version !== version) {\n console.log('[Signal] Config updated to version:', version)\n return newConfig\n }\n return prev\n })\n } catch (err) {\n console.error('[Signal] SSE parse error:', err)\n }\n })\n\n eventSource.addEventListener('experiment_update', (e) => {\n try {\n const { experiment_id, action } = JSON.parse(e.data)\n if (action === 'started' || action === 'updated') {\n loadExperimentAssignment(experiment_id)\n } else if (action === 'stopped') {\n assignmentsRef.current.delete(experiment_id)\n }\n } catch (err) {\n console.error('[Signal] Experiment update error:', err)\n }\n })\n\n eventSource.onerror = () => {\n console.warn('[Signal] SSE connection error, reconnecting...')\n eventSource.close()\n // Reconnect after 5 seconds\n setTimeout(connectSSE, 5000)\n }\n\n eventSourceRef.current = eventSource\n }, [apiUrl, apiKey, enabled, realtime, loadExperimentAssignment])\n\n // ============================================\n // Event Tracking\n // ============================================\n\n const flushEvents = useCallback(async () => {\n if (eventQueueRef.current.length === 0) return\n\n const events = [...eventQueueRef.current]\n eventQueueRef.current = []\n\n try {\n await fetch(`${apiUrl}/api/public/signal/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey!,\n },\n body: JSON.stringify({\n visitor_id: resolvedVisitorId,\n session_id: resolvedSessionId,\n events,\n }),\n })\n } catch (err) {\n // Re-queue failed events\n eventQueueRef.current = [...events, ...eventQueueRef.current]\n console.error('[Signal] Event flush error:', err)\n }\n }, [apiUrl, apiKey])\n\n const trackEvent = useCallback((event: Partial<SignalEvent>) => {\n if (!enabled || !apiKey) return\n\n // Enrich event with context\n const enrichedEvent: Partial<SignalEvent> = {\n ...event,\n page_url: window.location.href,\n page_path: window.location.pathname,\n referrer: document.referrer,\n device_type: getDeviceType(),\n browser: getBrowser(),\n os: getOS(),\n viewport_width: window.innerWidth,\n viewport_height: window.innerHeight,\n time_on_page: Date.now() - pageLoadTimeRef.current,\n scroll_depth: scrollDepthRef.current,\n click_count: clickCountRef.current,\n experiments: Array.from(assignmentsRef.current.values()).map(a => ({\n experiment_id: a.experiment_id,\n variant_key: a.variant_key,\n })),\n // Merge page metadata from Analytics if available\n page_metadata: pageMetadataRef?.current ?? undefined,\n timestamp: new Date().toISOString(),\n }\n\n eventQueueRef.current.push(enrichedEvent)\n\n // Debounce flush\n if (flushTimeoutRef.current) {\n clearTimeout(flushTimeoutRef.current)\n }\n flushTimeoutRef.current = setTimeout(flushEvents, 1000)\n }, [enabled, apiKey, flushEvents])\n\n // ============================================\n // Outcome Tracking\n // ============================================\n\n const trackOutcome = useCallback(async (outcome: SignalOutcome) => {\n if (!enabled || !apiKey) return\n\n try {\n await fetch(`${apiUrl}/api/public/signal/outcome`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({\n ...outcome,\n visitor_id: resolvedVisitorId,\n session_id: resolvedSessionId,\n experiments: Array.from(assignmentsRef.current.keys()),\n page_url: window.location.href,\n timestamp: new Date().toISOString(),\n }),\n })\n } catch (err) {\n console.error('[Signal] Outcome tracking error:', err)\n }\n }, [apiUrl, apiKey, enabled])\n\n // ============================================\n // Behavioral Tracking\n // ============================================\n\n useEffect(() => {\n if (!behaviorTracking || typeof window === 'undefined') return\n\n // Track scroll depth\n const handleScroll = () => {\n const scrollTop = window.scrollY\n const docHeight = document.documentElement.scrollHeight - window.innerHeight\n const depth = docHeight > 0 ? Math.round((scrollTop / docHeight) * 100) : 0\n scrollDepthRef.current = Math.max(scrollDepthRef.current, depth)\n }\n\n // Track clicks\n const handleClick = () => {\n clickCountRef.current++\n }\n\n window.addEventListener('scroll', handleScroll, { passive: true })\n window.addEventListener('click', handleClick)\n\n // Flush on visibility change or page hide.\n // `pagehide` (not `beforeunload`): fires on iOS Safari, bfcache-safe.\n const handleVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n flushEvents()\n }\n }\n\n document.addEventListener('visibilitychange', handleVisibilityChange)\n window.addEventListener('pagehide', flushEvents)\n\n return () => {\n window.removeEventListener('scroll', handleScroll)\n window.removeEventListener('click', handleClick)\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n window.removeEventListener('pagehide', flushEvents)\n }\n }, [behaviorTracking, flushEvents])\n\n // ============================================\n // Initialization + shell registration\n // ============================================\n\n useEffect(() => {\n fetchConfig()\n }, [fetchConfig])\n\n useEffect(() => {\n if (config && realtime) {\n connectSSE()\n }\n\n return () => {\n if (eventSourceRef.current) {\n eventSourceRef.current.close()\n }\n }\n }, [config, realtime, connectSSE])\n\n // Hand the imperative API to the shell; buffered calls drain on register.\n useEffect(() => {\n onRegister({\n trackEvent,\n trackOutcome: (outcome: SignalOutcome) => { void trackOutcome(outcome) },\n getExperiment,\n refreshConfig: fetchConfig,\n })\n return () => onRegister(null)\n }, [onRegister, trackEvent, trackOutcome, getExperiment, fetchConfig])\n\n return null\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/signal/SignalCore.tsx"],"names":["useState","useRef","getOrCreateVisitorId","getOrCreateSessionId","useEffect","useCallback"],"mappings":"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","file":"SignalCore-ZMFUE3N4.js","sourcesContent":["/**\n * @sonordev/site-kit/signal - SignalCore\n *\n * The heavy half of SignalBridge: config fetching, SSE streaming, experiment\n * assignment, event queue/flush, and behavioral tracking. Mounted by the\n * SignalBridge shell as a CHILDLESS lazy sibling — it never wraps page\n * content, so the signal machinery stays out of the server-render path and\n * off the critical hydration chunk. It reports state up to the shell via\n * `onState` and registers its imperative API via `onRegister`.\n */\n\n'use client'\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n// Single source of truth for visitor/session IDs (storage keys _sk_vid/_sk_sid)\n// — shared with Analytics and Engage. Do NOT re-fork ID logic into this module.\nimport { getOrCreateVisitorId, getOrCreateSessionId } from '../shared/identity'\nimport type {\n SignalConfig,\n SignalEvent,\n SignalOutcome,\n ExperimentAssignment,\n} from './types'\n\n// ============================================\n// Shell ↔ core contract\n// ============================================\n\nexport interface SignalCoreApi {\n trackEvent: (event: Partial<SignalEvent>) => void\n trackOutcome: (outcome: SignalOutcome) => void\n getExperiment: (experimentId: string) => ExperimentAssignment | null\n refreshConfig: () => Promise<void>\n}\n\nexport interface SignalCoreState {\n config: SignalConfig | null\n loading: boolean\n error: Error | null\n}\n\nexport interface SignalCoreProps {\n enabled: boolean\n realtime: boolean\n experiments: boolean\n behaviorTracking: boolean\n externalVisitorId?: string\n externalSessionId?: string\n pageMetadataRef?: React.RefObject<Record<string, unknown> | null>\n /** Called with the imperative API on mount, and with null on unmount. */\n onRegister: (api: SignalCoreApi | null) => void\n /** Called whenever config/loading/error change, so the shell can re-render consumers. */\n onState: (state: SignalCoreState) => void\n}\n\n// ============================================\n// Utility Functions\n// ============================================\n\nfunction getApiConfig() {\n const apiUrl = typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n : 'https://api.sonor.io'\n const apiKey = typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_KEY__\n : undefined\n return { apiUrl, apiKey }\n}\n\nfunction getDeviceType(): 'desktop' | 'mobile' | 'tablet' {\n if (typeof window === 'undefined') return 'desktop'\n const ua = navigator.userAgent\n if (/tablet|ipad|playbook|silk/i.test(ua)) return 'tablet'\n if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return 'mobile'\n return 'desktop'\n}\n\nfunction getBrowser(): string {\n if (typeof window === 'undefined') return 'unknown'\n const ua = navigator.userAgent\n if (ua.includes('Firefox')) return 'Firefox'\n if (ua.includes('Edg')) return 'Edge'\n if (ua.includes('Chrome')) return 'Chrome'\n if (ua.includes('Safari')) return 'Safari'\n return 'Other'\n}\n\nfunction getOS(): string {\n if (typeof window === 'undefined') return 'unknown'\n const ua = navigator.userAgent\n if (ua.includes('Windows')) return 'Windows'\n if (ua.includes('Mac')) return 'macOS'\n if (ua.includes('iPhone') || ua.includes('iPad')) return 'iOS'\n if (ua.includes('Android')) return 'Android'\n if (ua.includes('Linux')) return 'Linux'\n return 'Other'\n}\n\n// ============================================\n// SignalCore Component (childless)\n// ============================================\n\nexport function SignalCore({\n enabled,\n realtime,\n experiments,\n behaviorTracking,\n externalVisitorId,\n externalSessionId,\n pageMetadataRef,\n onRegister,\n onState,\n}: SignalCoreProps) {\n const [config, setConfig] = useState<SignalConfig | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n // Refs for SSE and tracking\n const eventSourceRef = useRef<EventSource | null>(null)\n const eventQueueRef = useRef<Partial<SignalEvent>[]>([])\n const flushTimeoutRef = useRef<NodeJS.Timeout | null>(null)\n const assignmentsRef = useRef<Map<string, ExperimentAssignment>>(new Map())\n\n // Behavioral tracking state\n const pageLoadTimeRef = useRef<number>(Date.now())\n const scrollDepthRef = useRef<number>(0)\n const clickCountRef = useRef<number>(0)\n\n const { apiUrl, apiKey } = getApiConfig()\n\n // Use external IDs from shared identity, fall back to the shared generators\n const resolvedVisitorId = externalVisitorId || getOrCreateVisitorId()\n const resolvedSessionId = externalSessionId || getOrCreateSessionId()\n\n // Mirror state up to the shell so context consumers re-render\n useEffect(() => {\n onState({ config, loading, error })\n }, [config, loading, error, onState])\n\n // ============================================\n // Experiment Assignment\n // ============================================\n\n const loadExperimentAssignment = useCallback(async (experimentId: string): Promise<ExperimentAssignment | null> => {\n // Check localStorage first\n const storageKey = `_signal_exp_${experimentId}`\n const stored = localStorage.getItem(storageKey)\n\n if (stored) {\n try {\n const assignment = JSON.parse(stored) as ExperimentAssignment\n if (assignment.expires > Date.now()) {\n assignmentsRef.current.set(experimentId, assignment)\n return assignment\n }\n } catch {\n // Invalid stored data, continue to fetch\n }\n }\n\n // Fetch from API\n try {\n const response = await fetch(`${apiUrl}/api/public/signal/experiment/${experimentId}`, {\n headers: {\n 'x-api-key': apiKey!,\n 'x-visitor-id': resolvedVisitorId,\n },\n })\n\n if (!response.ok) return null\n\n const assignment = await response.json() as ExperimentAssignment\n\n // Store assignment\n localStorage.setItem(storageKey, JSON.stringify(assignment))\n assignmentsRef.current.set(experimentId, assignment)\n\n return assignment\n } catch (err) {\n console.error('[Signal] Experiment assignment error:', err)\n return null\n }\n }, [apiUrl, apiKey])\n\n const getExperiment = useCallback((experimentId: string): ExperimentAssignment | null => {\n return assignmentsRef.current.get(experimentId) || null\n }, [])\n\n // ============================================\n // Config Fetching\n // ============================================\n\n const fetchConfig = useCallback(async () => {\n if (!apiKey || !enabled) {\n setLoading(false)\n return\n }\n\n try {\n const response = await fetch(`${apiUrl}/api/public/signal/config`, {\n headers: {\n 'x-api-key': apiKey,\n 'x-visitor-id': resolvedVisitorId,\n },\n })\n\n if (!response.ok) {\n throw new Error(`Failed to fetch Signal config: ${response.statusText}`)\n }\n\n const data = await response.json()\n setConfig(data.config)\n setError(null)\n\n // Load experiment assignments\n if (experiments && data.config?.experiments) {\n for (const exp of data.config.experiments) {\n if (exp.status === 'running') {\n await loadExperimentAssignment(exp.id)\n }\n }\n }\n } catch (err) {\n console.error('[Signal] Config fetch error:', err)\n setError(err as Error)\n } finally {\n setLoading(false)\n }\n }, [apiUrl, apiKey, enabled, experiments, resolvedVisitorId, loadExperimentAssignment])\n\n // ============================================\n // SSE Real-time Updates\n // ============================================\n\n const connectSSE = useCallback(() => {\n if (!apiKey || !enabled || !realtime) return\n\n // Close existing connection\n if (eventSourceRef.current) {\n eventSourceRef.current.close()\n }\n\n const url = `${apiUrl}/api/public/signal/stream?key=${apiKey}`\n const eventSource = new EventSource(url)\n\n eventSource.addEventListener('config_update', (e) => {\n try {\n const { config: newConfig, version } = JSON.parse(e.data)\n setConfig(prev => {\n if (prev?.version !== version) {\n console.log('[Signal] Config updated to version:', version)\n return newConfig\n }\n return prev\n })\n } catch (err) {\n console.error('[Signal] SSE parse error:', err)\n }\n })\n\n eventSource.addEventListener('experiment_update', (e) => {\n try {\n const { experiment_id, action } = JSON.parse(e.data)\n if (action === 'started' || action === 'updated') {\n loadExperimentAssignment(experiment_id)\n } else if (action === 'stopped') {\n assignmentsRef.current.delete(experiment_id)\n }\n } catch (err) {\n console.error('[Signal] Experiment update error:', err)\n }\n })\n\n eventSource.onerror = () => {\n console.warn('[Signal] SSE connection error, reconnecting...')\n eventSource.close()\n // Reconnect after 5 seconds\n setTimeout(connectSSE, 5000)\n }\n\n eventSourceRef.current = eventSource\n }, [apiUrl, apiKey, enabled, realtime, loadExperimentAssignment])\n\n // ============================================\n // Event Tracking\n // ============================================\n\n const flushEvents = useCallback(async () => {\n if (eventQueueRef.current.length === 0) return\n\n const events = [...eventQueueRef.current]\n eventQueueRef.current = []\n\n try {\n await fetch(`${apiUrl}/api/public/signal/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey!,\n },\n body: JSON.stringify({\n visitor_id: resolvedVisitorId,\n session_id: resolvedSessionId,\n events,\n }),\n })\n } catch (err) {\n // Re-queue failed events\n eventQueueRef.current = [...events, ...eventQueueRef.current]\n console.error('[Signal] Event flush error:', err)\n }\n }, [apiUrl, apiKey])\n\n const trackEvent = useCallback((event: Partial<SignalEvent>) => {\n if (!enabled || !apiKey) return\n\n // Enrich event with context\n const enrichedEvent: Partial<SignalEvent> = {\n ...event,\n page_url: window.location.href,\n page_path: window.location.pathname,\n referrer: document.referrer,\n device_type: getDeviceType(),\n browser: getBrowser(),\n os: getOS(),\n viewport_width: window.innerWidth,\n viewport_height: window.innerHeight,\n time_on_page: Date.now() - pageLoadTimeRef.current,\n scroll_depth: scrollDepthRef.current,\n click_count: clickCountRef.current,\n experiments: Array.from(assignmentsRef.current.values()).map(a => ({\n experiment_id: a.experiment_id,\n variant_key: a.variant_key,\n })),\n // Merge page metadata from Analytics if available\n page_metadata: pageMetadataRef?.current ?? undefined,\n timestamp: new Date().toISOString(),\n }\n\n eventQueueRef.current.push(enrichedEvent)\n\n // Debounce flush\n if (flushTimeoutRef.current) {\n clearTimeout(flushTimeoutRef.current)\n }\n flushTimeoutRef.current = setTimeout(flushEvents, 1000)\n }, [enabled, apiKey, flushEvents])\n\n // ============================================\n // Outcome Tracking\n // ============================================\n\n const trackOutcome = useCallback(async (outcome: SignalOutcome) => {\n if (!enabled || !apiKey) return\n\n try {\n await fetch(`${apiUrl}/api/public/signal/outcome`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({\n ...outcome,\n visitor_id: resolvedVisitorId,\n session_id: resolvedSessionId,\n experiments: Array.from(assignmentsRef.current.keys()),\n page_url: window.location.href,\n timestamp: new Date().toISOString(),\n }),\n })\n } catch (err) {\n console.error('[Signal] Outcome tracking error:', err)\n }\n }, [apiUrl, apiKey, enabled])\n\n // ============================================\n // Behavioral Tracking\n // ============================================\n\n useEffect(() => {\n if (!behaviorTracking || typeof window === 'undefined') return\n\n // Track scroll depth\n const handleScroll = () => {\n const scrollTop = window.scrollY\n const docHeight = document.documentElement.scrollHeight - window.innerHeight\n const depth = docHeight > 0 ? Math.round((scrollTop / docHeight) * 100) : 0\n scrollDepthRef.current = Math.max(scrollDepthRef.current, depth)\n }\n\n // Track clicks\n const handleClick = () => {\n clickCountRef.current++\n }\n\n window.addEventListener('scroll', handleScroll, { passive: true })\n window.addEventListener('click', handleClick)\n\n // Flush on visibility change or page hide.\n // `pagehide` (not `beforeunload`): fires on iOS Safari, bfcache-safe.\n const handleVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n flushEvents()\n }\n }\n\n document.addEventListener('visibilitychange', handleVisibilityChange)\n window.addEventListener('pagehide', flushEvents)\n\n return () => {\n window.removeEventListener('scroll', handleScroll)\n window.removeEventListener('click', handleClick)\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n window.removeEventListener('pagehide', flushEvents)\n }\n }, [behaviorTracking, flushEvents])\n\n // ============================================\n // Initialization + shell registration\n // ============================================\n\n useEffect(() => {\n fetchConfig()\n }, [fetchConfig])\n\n useEffect(() => {\n if (config && realtime) {\n connectSSE()\n }\n\n return () => {\n if (eventSourceRef.current) {\n eventSourceRef.current.close()\n }\n }\n }, [config, realtime, connectSSE])\n\n // Hand the imperative API to the shell; buffered calls drain on register.\n useEffect(() => {\n onRegister({\n trackEvent,\n trackOutcome: (outcome: SignalOutcome) => { void trackOutcome(outcome) },\n getExperiment,\n refreshConfig: fetchConfig,\n })\n return () => onRegister(null)\n }, [onRegister, trackEvent, trackOutcome, getExperiment, fetchConfig])\n\n return null\n}\n"]}
@@ -1,13 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkRI6JZUAE_js = require('./chunk-RI6JZUAE.js');
4
- require('./chunk-ZSMWDLMK.js');
5
-
6
-
7
-
8
- Object.defineProperty(exports, "SitemapSync", {
9
- enumerable: true,
10
- get: function () { return chunkRI6JZUAE_js.SitemapSync; }
11
- });
12
- //# sourceMappingURL=SitemapSync-FUCH6Y6J.js.map
13
- //# sourceMappingURL=SitemapSync-FUCH6Y6J.js.map
@@ -1,4 +0,0 @@
1
- export { SitemapSync } from './chunk-QTWA44S3.mjs';
2
- import './chunk-4XPGGLVP.mjs';
3
- //# sourceMappingURL=SitemapSync-LCSPRAWU.mjs.map
4
- //# sourceMappingURL=SitemapSync-LCSPRAWU.mjs.map