@sonordev/site-kit 3.3.0 → 3.3.1

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 (156) hide show
  1. package/agent-manifest.json +1 -1
  2. package/dist/AnalyticsProvider-3MZ3O5OB.mjs +8 -0
  3. package/dist/{AnalyticsProvider-AMHG4DIE.mjs.map → AnalyticsProvider-3MZ3O5OB.mjs.map} +1 -1
  4. package/dist/AnalyticsProvider-PLGTU56K.js +29 -0
  5. package/dist/{AnalyticsProvider-26VS3CQR.js.map → AnalyticsProvider-PLGTU56K.js.map} +1 -1
  6. package/dist/ChatWidget-7JHVRNVE.mjs +8 -0
  7. package/dist/{ChatWidget-UKPEQEHB.mjs.map → ChatWidget-7JHVRNVE.mjs.map} +1 -1
  8. package/dist/ChatWidget-FQARJMGU.js +17 -0
  9. package/dist/{ChatWidget-NIYNSMLU.js.map → ChatWidget-FQARJMGU.js.map} +1 -1
  10. package/dist/EngageWidget-DQ62NQPU.js +16 -0
  11. package/dist/{EngageWidget-33QCFWQW.js.map → EngageWidget-DQ62NQPU.js.map} +1 -1
  12. package/dist/EngageWidget-VXQOZOYA.mjs +7 -0
  13. package/dist/{EngageWidget-S76ZMDEF.mjs.map → EngageWidget-VXQOZOYA.mjs.map} +1 -1
  14. package/dist/{SignalCore-2UXXM4FF.js → SignalCore-3SG3LFQL.js} +8 -8
  15. package/dist/{SignalCore-2UXXM4FF.js.map → SignalCore-3SG3LFQL.js.map} +1 -1
  16. package/dist/{SignalCore-2AUGAOQT.mjs → SignalCore-BKG66B4C.mjs} +4 -4
  17. package/dist/{SignalCore-2AUGAOQT.mjs.map → SignalCore-BKG66B4C.mjs.map} +1 -1
  18. package/dist/SitemapSync-DWX7I6MX.js +16 -0
  19. package/dist/{SitemapSync-WQJMHJNY.js.map → SitemapSync-DWX7I6MX.js.map} +1 -1
  20. package/dist/SitemapSync-IO2VOR23.mjs +7 -0
  21. package/dist/{SitemapSync-RBHXPQAQ.mjs.map → SitemapSync-IO2VOR23.mjs.map} +1 -1
  22. package/dist/analytics/index.js +11 -11
  23. package/dist/analytics/index.mjs +4 -4
  24. package/dist/{chunk-WEHJRE3H.mjs → chunk-25YPXQBA.mjs} +3 -3
  25. package/dist/{chunk-WEHJRE3H.mjs.map → chunk-25YPXQBA.mjs.map} +1 -1
  26. package/dist/{chunk-NNPF4KRZ.mjs → chunk-3AG6O3IA.mjs} +3 -3
  27. package/dist/{chunk-NNPF4KRZ.mjs.map → chunk-3AG6O3IA.mjs.map} +1 -1
  28. package/dist/{chunk-EL6ZMYD2.js → chunk-3WRE7ZDC.js} +6 -6
  29. package/dist/{chunk-EL6ZMYD2.js.map → chunk-3WRE7ZDC.js.map} +1 -1
  30. package/dist/chunk-3X2YPJU5.js +8 -0
  31. package/dist/{chunk-EOENN46G.js.map → chunk-3X2YPJU5.js.map} +1 -1
  32. package/dist/{chunk-BOTWTNHW.js → chunk-4JMXBIUC.js} +12 -12
  33. package/dist/{chunk-BOTWTNHW.js.map → chunk-4JMXBIUC.js.map} +1 -1
  34. package/dist/{chunk-XOCQANYF.js → chunk-4ROFY6PM.js} +13 -13
  35. package/dist/{chunk-XOCQANYF.js.map → chunk-4ROFY6PM.js.map} +1 -1
  36. package/dist/{chunk-PLQ4CUNI.mjs → chunk-7OROUYZH.mjs} +4 -4
  37. package/dist/{chunk-PLQ4CUNI.mjs.map → chunk-7OROUYZH.mjs.map} +1 -1
  38. package/dist/{chunk-WAYG3LS2.js → chunk-BOWR5QEV.js} +14 -14
  39. package/dist/{chunk-WAYG3LS2.js.map → chunk-BOWR5QEV.js.map} +1 -1
  40. package/dist/{chunk-P3QZRG2Z.js → chunk-C4JAMON3.js} +15 -15
  41. package/dist/{chunk-P3QZRG2Z.js.map → chunk-C4JAMON3.js.map} +1 -1
  42. package/dist/{chunk-PUGRSMAV.mjs → chunk-DIXWP4BO.mjs} +3 -3
  43. package/dist/{chunk-PUGRSMAV.mjs.map → chunk-DIXWP4BO.mjs.map} +1 -1
  44. package/dist/{chunk-BPKDRTEA.js → chunk-DNW7FTJD.js} +9 -9
  45. package/dist/{chunk-BPKDRTEA.js.map → chunk-DNW7FTJD.js.map} +1 -1
  46. package/dist/{chunk-6UK5JIKE.js → chunk-FMQHK6MM.js} +3 -3
  47. package/dist/{chunk-6UK5JIKE.js.map → chunk-FMQHK6MM.js.map} +1 -1
  48. package/dist/{chunk-G4FJJK6J.js → chunk-FSYXHIAI.js} +14 -14
  49. package/dist/{chunk-G4FJJK6J.js.map → chunk-FSYXHIAI.js.map} +1 -1
  50. package/dist/{chunk-FO6Y27AH.mjs → chunk-GUU3AEFM.mjs} +3 -3
  51. package/dist/{chunk-FO6Y27AH.mjs.map → chunk-GUU3AEFM.mjs.map} +1 -1
  52. package/dist/{chunk-5KVT66R6.mjs → chunk-JD6LV6JZ.mjs} +3 -3
  53. package/dist/{chunk-5KVT66R6.mjs.map → chunk-JD6LV6JZ.mjs.map} +1 -1
  54. package/dist/chunk-JEOKDUPQ.js +45 -0
  55. package/dist/chunk-JEOKDUPQ.js.map +1 -0
  56. package/dist/chunk-K6YGF2KJ.mjs +6 -0
  57. package/dist/{chunk-S4FJIG6Y.mjs.map → chunk-K6YGF2KJ.mjs.map} +1 -1
  58. package/dist/{chunk-M2EGCGIT.js → chunk-KPKZBIAQ.js} +7 -7
  59. package/dist/{chunk-M2EGCGIT.js.map → chunk-KPKZBIAQ.js.map} +1 -1
  60. package/dist/chunk-LOS5D6B3.js +72 -0
  61. package/dist/chunk-LOS5D6B3.js.map +1 -0
  62. package/dist/{chunk-6BDXN4X3.mjs → chunk-MLD3EGM2.mjs} +3 -3
  63. package/dist/{chunk-6BDXN4X3.mjs.map → chunk-MLD3EGM2.mjs.map} +1 -1
  64. package/dist/{chunk-AXFM4MFK.mjs → chunk-NQ3K4NWX.mjs} +8 -8
  65. package/dist/{chunk-AXFM4MFK.mjs.map → chunk-NQ3K4NWX.mjs.map} +1 -1
  66. package/dist/{chunk-ILLOVVVP.mjs → chunk-OC64AHKN.mjs} +3 -3
  67. package/dist/{chunk-ILLOVVVP.mjs.map → chunk-OC64AHKN.mjs.map} +1 -1
  68. package/dist/{chunk-I2M533CW.js → chunk-PCDAVHVX.js} +8 -8
  69. package/dist/{chunk-I2M533CW.js.map → chunk-PCDAVHVX.js.map} +1 -1
  70. package/dist/{chunk-QOFC3BYG.js → chunk-PIV54R2O.js} +9 -28
  71. package/dist/chunk-PIV54R2O.js.map +1 -0
  72. package/dist/{chunk-ZCZEKWFW.mjs → chunk-QFWBAGBT.mjs} +3 -3
  73. package/dist/{chunk-ZCZEKWFW.mjs.map → chunk-QFWBAGBT.mjs.map} +1 -1
  74. package/dist/{chunk-DLJZYWP3.mjs → chunk-QGV7FEUI.mjs} +7 -43
  75. package/dist/chunk-QGV7FEUI.mjs.map +1 -0
  76. package/dist/chunk-QJU6HBSK.mjs +39 -0
  77. package/dist/chunk-QJU6HBSK.mjs.map +1 -0
  78. package/dist/{chunk-FX3VR4BF.js → chunk-QRIGY6EM.js} +7 -7
  79. package/dist/{chunk-FX3VR4BF.js.map → chunk-QRIGY6EM.js.map} +1 -1
  80. package/dist/{chunk-M4HLDK62.mjs → chunk-QW3T6WH2.mjs} +6 -25
  81. package/dist/chunk-QW3T6WH2.mjs.map +1 -0
  82. package/dist/{chunk-2RLG4KBJ.mjs → chunk-S2N3NSC7.mjs} +4 -4
  83. package/dist/{chunk-2RLG4KBJ.mjs.map → chunk-S2N3NSC7.mjs.map} +1 -1
  84. package/dist/{chunk-EM4DFSC5.js → chunk-TXEOLHMC.js} +4 -4
  85. package/dist/{chunk-EM4DFSC5.js.map → chunk-TXEOLHMC.js.map} +1 -1
  86. package/dist/{chunk-Z26CMPBX.js → chunk-VMVEL4WU.js} +7 -43
  87. package/dist/chunk-VMVEL4WU.js.map +1 -0
  88. package/dist/chunk-XIJ4F6IE.mjs +63 -0
  89. package/dist/chunk-XIJ4F6IE.mjs.map +1 -0
  90. package/dist/{chunk-K2P7KPEQ.js → chunk-YFYMOQAX.js} +10 -10
  91. package/dist/{chunk-K2P7KPEQ.js.map → chunk-YFYMOQAX.js.map} +1 -1
  92. package/dist/{chunk-X72PFC4X.mjs → chunk-ZFF3BKNE.mjs} +4 -4
  93. package/dist/{chunk-X72PFC4X.mjs.map → chunk-ZFF3BKNE.mjs.map} +1 -1
  94. package/dist/{chunk-SLOZWRWU.mjs → chunk-ZYIONE7A.mjs} +4 -4
  95. package/dist/{chunk-SLOZWRWU.mjs.map → chunk-ZYIONE7A.mjs.map} +1 -1
  96. package/dist/cli/index.js +2 -2
  97. package/dist/cli/index.mjs +1 -1
  98. package/dist/commerce/index.js +50 -50
  99. package/dist/commerce/index.mjs +5 -5
  100. package/dist/discover-Q5RWC27Y.js +21 -0
  101. package/dist/discover-Q5RWC27Y.js.map +1 -0
  102. package/dist/discover-ROPVYJJP.mjs +4 -0
  103. package/dist/discover-ROPVYJJP.mjs.map +1 -0
  104. package/dist/engage/index.js +8 -8
  105. package/dist/engage/index.mjs +5 -5
  106. package/dist/fleet/index.js +7 -7
  107. package/dist/fleet/index.mjs +3 -3
  108. package/dist/forms/index.js +12 -12
  109. package/dist/forms/index.mjs +4 -4
  110. package/dist/images/index.js +10 -10
  111. package/dist/images/index.mjs +3 -3
  112. package/dist/index.js +63 -63
  113. package/dist/index.mjs +9 -9
  114. package/dist/layout/client.js +6 -6
  115. package/dist/layout/client.mjs +5 -5
  116. package/dist/layout/index.js +7 -7
  117. package/dist/layout/index.mjs +5 -5
  118. package/dist/maps/index.js +4 -4
  119. package/dist/maps/index.mjs +2 -2
  120. package/dist/seo/client.js +5 -4
  121. package/dist/seo/client.mjs +4 -3
  122. package/dist/seo/index.js +14 -13
  123. package/dist/seo/index.js.map +1 -1
  124. package/dist/seo/index.mjs +6 -5
  125. package/dist/seo/index.mjs.map +1 -1
  126. package/dist/seo/register-sitemap-cli.js +64 -96
  127. package/dist/seo/register-sitemap-cli.js.map +1 -1
  128. package/dist/seo/register-sitemap-cli.mjs +62 -94
  129. package/dist/seo/register-sitemap-cli.mjs.map +1 -1
  130. package/dist/seo/server.js +5 -5
  131. package/dist/seo/server.mjs +1 -1
  132. package/dist/signal/index.js +11 -11
  133. package/dist/signal/index.mjs +2 -2
  134. package/dist/sitemap/index.d.mts +15 -17
  135. package/dist/sitemap/index.d.ts +15 -17
  136. package/dist/sitemap/index.js +14 -37
  137. package/dist/sitemap/index.js.map +1 -1
  138. package/dist/sitemap/index.mjs +11 -34
  139. package/dist/sitemap/index.mjs.map +1 -1
  140. package/dist/sync/index.js +15 -15
  141. package/dist/sync/index.mjs +3 -3
  142. package/package.json +1 -1
  143. package/dist/AnalyticsProvider-26VS3CQR.js +0 -29
  144. package/dist/AnalyticsProvider-AMHG4DIE.mjs +0 -8
  145. package/dist/ChatWidget-NIYNSMLU.js +0 -17
  146. package/dist/ChatWidget-UKPEQEHB.mjs +0 -8
  147. package/dist/EngageWidget-33QCFWQW.js +0 -16
  148. package/dist/EngageWidget-S76ZMDEF.mjs +0 -7
  149. package/dist/SitemapSync-RBHXPQAQ.mjs +0 -6
  150. package/dist/SitemapSync-WQJMHJNY.js +0 -15
  151. package/dist/chunk-DLJZYWP3.mjs.map +0 -1
  152. package/dist/chunk-EOENN46G.js +0 -8
  153. package/dist/chunk-M4HLDK62.mjs.map +0 -1
  154. package/dist/chunk-QOFC3BYG.js.map +0 -1
  155. package/dist/chunk-S4FJIG6Y.mjs +0 -6
  156. package/dist/chunk-Z26CMPBX.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { SITE_KIT_VERSION } from './chunk-S4FJIG6Y.mjs';
1
+ import { SITE_KIT_VERSION } from './chunk-K6YGF2KJ.mjs';
2
2
 
3
3
  // src/shared/siteToken.ts
4
4
  var SITE_TOKEN_PREFIX = "st1.";
@@ -255,5 +255,5 @@ function sonorBeacon(url, payload, apiKey) {
255
255
  }
256
256
 
257
257
  export { configureSiteCredential, currentCredential, sonorBeacon, sonorFetch };
258
- //# sourceMappingURL=chunk-FO6Y27AH.mjs.map
259
- //# sourceMappingURL=chunk-FO6Y27AH.mjs.map
258
+ //# sourceMappingURL=chunk-GUU3AEFM.mjs.map
259
+ //# sourceMappingURL=chunk-GUU3AEFM.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/siteToken.ts","../src/shared/fetchWithRetry.ts","../src/shared/sonorFetch.ts"],"names":[],"mappings":"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","file":"chunk-FO6Y27AH.mjs","sourcesContent":["/**\n * @sonordev/site-kit shared — Client token manager\n *\n * SiteKitLayout (a server component) no longer ships the raw `sonor_` API key to\n * the browser. Instead it mints a short-lived, project-scoped token server-side\n * and injects THAT. This module is the browser-side lifecycle for that token:\n *\n * - holds the current token (seeded from the injected value)\n * - a static page's seed can be days old and already expired on first view, so\n * it transparently REFRESHES via the public endpoint before first use\n * - dedupes concurrent refreshes (one in-flight request, awaited by all)\n * - persists the freshest token in sessionStorage so navigations within a\n * session don't each re-refresh\n *\n * Everything here is a no-op passthrough when the injected credential is a raw\n * key (a 2.x kit, or the mint-failed fallback) — legacy behavior is unchanged.\n * Fully SSR-safe: on the server there is no refresh, just the seed.\n *\n * The token payload is NOT secret (only its HMAC is unforgeable), so decoding it\n * client-side to read `exp`/`pid` is fine and needs no crypto.\n */\nimport { SITE_KIT_VERSION } from './version'\n\nexport const SITE_TOKEN_PREFIX = 'st1.'\n\n/** Refresh this many ms before expiry, so a request never races the deadline. */\nconst REFRESH_SKEW_MS = 5 * 60_000\nconst REFRESH_TIMEOUT_MS = 6_000\nconst STORAGE_KEY = '__sonor_site_token__'\n\nexport function isSiteToken(credential?: string | null): boolean {\n return typeof credential === 'string' && credential.startsWith(SITE_TOKEN_PREFIX)\n}\n\ninterface DecodedClaims {\n pid?: string\n iat?: number\n exp?: number\n}\n\n/** Decode the (non-secret) token payload. Returns null if not a token / malformed. */\nexport function decodeSiteTokenClaims(token?: string | null): DecodedClaims | null {\n if (!isSiteToken(token)) return null\n const seg = (token as string).split('.')[1]\n if (!seg) return null\n try {\n // base64url → JSON. atob handles standard base64; restore url-safe chars.\n const b64 = seg.replace(/-/g, '+').replace(/_/g, '/')\n const json =\n typeof atob === 'function'\n ? atob(b64)\n : Buffer.from(seg, 'base64url').toString('utf8')\n const claims = JSON.parse(json)\n return typeof claims === 'object' && claims ? claims : null\n } catch {\n return null\n }\n}\n\n/**\n * Stable id for the auth circuit breaker. Tokens rotate, so keying the breaker\n * by the raw value would reset it every refresh — key by the project id encoded\n * in the token instead. Raw keys use a safe prefix.\n */\nexport function credentialBreakerId(credential?: string | null): string {\n if (!credential) return '(anonymous)'\n const claims = decodeSiteTokenClaims(credential)\n if (claims?.pid) return `pid:${claims.pid}`\n return credential.slice(0, 16)\n}\n\ninterface TokenState {\n /** The credential injected by the layout (token or raw key). */\n seed: string | undefined\n /** Whether we're in token mode (seed is a token). */\n isToken: boolean\n /** Base API URL for the refresh endpoint. */\n apiUrl: string\n /** The freshest known token (>= seed). Null in raw-key mode. */\n token: string | null\n /** In-flight refresh, deduped. */\n refreshing: Promise<string | null> | null\n}\n\nconst state: TokenState = {\n seed: undefined,\n isToken: false,\n apiUrl: 'https://api.sonor.io',\n token: null,\n refreshing: null,\n}\n\nfunction readStoredToken(): string | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n return typeof parsed?.token === 'string' ? parsed.token : null\n } catch {\n return null\n }\n}\n\nfunction storeToken(token: string): void {\n if (typeof window === 'undefined') return\n try {\n window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ token }))\n } catch {\n /* private mode / disabled storage — memory-only is fine */\n }\n try {\n ;(window as any).__SITE_KIT_API_KEY__ = token\n } catch {\n /* ignore */\n }\n}\n\nfunction tokenExp(token: string | null): number {\n const claims = decodeSiteTokenClaims(token)\n return typeof claims?.exp === 'number' ? claims.exp * 1000 : 0\n}\n\n/**\n * Configure the manager from the layout-injected values. Adopts a fresher token\n * from sessionStorage if a prior page in this session already refreshed.\n */\nexport function configureSiteCredential(opts: {\n credential?: string\n apiUrl?: string\n}): void {\n state.seed = opts.credential\n state.isToken = isSiteToken(opts.credential)\n if (opts.apiUrl) state.apiUrl = opts.apiUrl.replace(/\\/+$/, '')\n\n if (!state.isToken) {\n state.token = null\n return\n }\n\n // configure runs on every render — never downgrade. Keep the freshest of the\n // injected seed, a token stored by an earlier page this session, and whatever\n // an in-flight/prior refresh already put in memory.\n const prior = state.token\n let best = opts.credential ?? null\n const stored = readStoredToken()\n if (stored && tokenExp(stored) > tokenExp(best)) best = stored\n if (prior && tokenExp(prior) > tokenExp(best)) best = prior\n state.token = best\n}\n\nfunction needsRefresh(): boolean {\n if (!state.isToken) return false\n if (!state.token) return true\n return Date.now() >= tokenExp(state.token) - REFRESH_SKEW_MS\n}\n\nasync function doRefresh(): Promise<string | null> {\n const current = state.token\n if (!current) return null\n try {\n const res = await fetch(`${state.apiUrl}/api/public/site-token/refresh`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n // Send the current token as x-api-key too, so the server's key-scoped\n // throttler buckets refreshes by token rather than by shared CDN IP.\n 'x-api-key': current,\n },\n body: JSON.stringify({ token: current }),\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(REFRESH_TIMEOUT_MS)\n : undefined,\n })\n if (!res.ok) return null\n const data = await res.json().catch(() => null)\n const next = data?.token\n if (typeof next === 'string' && isSiteToken(next)) {\n state.token = next\n storeToken(next)\n return next\n }\n return null\n } catch {\n return null\n }\n}\n\n/** Refresh now (deduped). Returns the new token, or null on failure. */\nexport function refreshSiteToken(): Promise<string | null> {\n if (!state.isToken) return Promise.resolve(null)\n if (state.refreshing) return state.refreshing\n state.refreshing = doRefresh().finally(() => {\n state.refreshing = null\n })\n return state.refreshing\n}\n\n/**\n * The credential to put on the next request. In token mode, refreshes a stale\n * seed first. In raw-key mode, returns the seed unchanged (legacy passthrough).\n */\nexport async function getFreshCredential(\n seed?: string,\n): Promise<string | undefined> {\n // A raw key (or nothing configured) — no token machinery.\n if (!state.isToken) return seed ?? state.seed\n if (needsRefresh()) {\n const refreshed = await refreshSiteToken()\n if (refreshed) return refreshed\n // Refresh failed — fall through to whatever we have; the request may 401 and\n // trip the breaker with friendly copy.\n }\n return state.token ?? seed ?? state.seed\n}\n\n/** Whether the given credential is being managed as a token. */\nexport function isTokenMode(): boolean {\n return state.isToken\n}\n\n/**\n * Best-known credential WITHOUT refreshing — for synchronous unload paths\n * (sonorBeacon) that can't await. In token mode returns the freshest token held;\n * in raw-key mode returns the seed.\n */\nexport function currentCredential(seed?: string): string | undefined {\n if (!state.isToken) return seed ?? state.seed\n return state.token ?? seed ?? state.seed\n}\n","/**\n * @sonordev/site-kit shared - Resilient fetch\n *\n * Single source of truth for client-side resilient fetch (forms, SitemapSync).\n * Adds a per-attempt timeout and retries transient failures (network errors,\n * request timeouts, HTTP 429, and 5xx) with exponential backoff.\n *\n * A form submission must survive a brief API blip — a deploy, a cold start, a\n * dropped packet. A single un-retried fetch turning into \"Something went wrong\"\n * is how leads get lost. The explicit abort also matters on its own: a request\n * left to hang until the browser's own network timeout logs a console error\n * (net::ERR_TIMED_OUT) that fails Lighthouse best-practices; an aborted fetch\n * logs nothing.\n */\n\nexport interface FetchRetryOptions {\n /** Number of retries AFTER the first attempt (default 2 → up to 3 tries total). */\n retries?: number\n /** Per-attempt timeout in milliseconds (default 15000). */\n timeoutMs?: number\n}\n\nfunction backoffDelay(attempt: number, response?: Response): number {\n // Honor a Retry-After header on 429 responses when present.\n if (response?.status === 429) {\n const header = response.headers.get('retry-after')\n const seconds = header ? parseInt(header, 10) : NaN\n if (!Number.isNaN(seconds) && seconds > 0) return Math.min(seconds * 1000, 10000)\n }\n // Exponential backoff: 500ms, 1s, 2s, ... capped at 8s.\n return Math.min(8000, 500 * 2 ** attempt)\n}\n\n/**\n * fetch() with a timeout and automatic retries for transient failures.\n *\n * Non-transient responses (2xx, 3xx, and 4xx other than 429) are returned to\n * the caller as-is — only 429/5xx and thrown network/timeout errors are retried.\n * If every attempt fails with a thrown error, the last error is re-thrown.\n */\nexport async function fetchWithRetry(\n url: string,\n init: RequestInit,\n options: FetchRetryOptions = {},\n): Promise<Response> {\n const retries = options.retries ?? 2\n const timeoutMs = options.timeoutMs ?? 15000\n let lastError: unknown\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const response = await fetch(url, { ...init, signal: controller.signal })\n clearTimeout(timer)\n\n if ((response.status === 429 || response.status >= 500) && attempt < retries) {\n await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt, response)))\n continue\n }\n return response\n } catch (error) {\n clearTimeout(timer)\n lastError = error\n // Network failure or timeout (AbortController) — retry while attempts remain.\n if (attempt < retries) {\n await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt)))\n continue\n }\n }\n }\n\n throw lastError instanceof Error\n ? lastError\n : new Error('Network request failed after multiple attempts. Please try again.')\n}\n","/**\n * @sonordev/site-kit shared - Canonical client transport\n *\n * Single source of truth for how site-kit talks to Sonor from the browser.\n * Every client-side module (analytics, forms, commerce, engage, maps, images,\n * SitemapSync) routes through here instead of bare fetch(), which gives the\n * whole kit, in one place:\n *\n * - per-attempt timeout + retry with backoff (via fetchWithRetry) — a hung\n * request left to the browser's own network timeout logs a console error\n * (net::ERR_TIMED_OUT) that fails Lighthouse best-practices\n * - the x-sitekit-version header on every request, so the platform can see\n * which kit version each site actually runs from live traffic\n * - CREDENTIAL LIFECYCLE: sites on 3.2+ ship a short-lived minted token instead\n * of the raw key. This transport resolves the freshest token (refreshing a\n * stale seed transparently), and on a 401/403 for a token it refreshes once\n * and retries before giving up. A raw key (2.x kit, or the mint-failed\n * fallback) is a pure passthrough — legacy behavior is unchanged.\n * - an auth circuit breaker: after a 401/403 the credential is paused for a few\n * minutes and ONE friendly console.info is emitted — a site on a stale or\n * canceled key must not spam the API or the visitor's console. Keyed by the\n * project (from the token) so it survives token rotation.\n * - sonorBeacon for unload-time telemetry that keeps the credential in headers;\n * navigator.sendBeacon can't set headers and forced the key into the URL,\n * where server and CDN access logs capture it\n *\n * Non-idempotent endpoints (checkout, payment intents, uploads) must pass\n * { retries: 0 } — they still get the timeout, but never an automatic retry.\n */\nimport { fetchWithRetry, type FetchRetryOptions } from './fetchWithRetry'\nimport { SITE_KIT_VERSION } from './version'\nimport {\n credentialBreakerId,\n currentCredential,\n getFreshCredential,\n isSiteToken,\n isTokenMode,\n refreshSiteToken,\n} from './siteToken'\n\nexport interface SonorFetchOptions extends FetchRetryOptions {\n /** Project credential — the minted token (3.2+) or raw key. Sent as x-api-key. */\n apiKey?: string\n}\n\n/** How long a credential stays paused after an auth failure. */\nconst AUTH_BREAK_MS = 5 * 60_000\n\n// breaker id → epoch ms until which requests are paused. Module-scoped: the\n// breaker lives for the page's lifetime and never applies on the server.\nconst pausedKeys = new Map<string, number>()\nlet warnedThisPage = false\n\n/** True when a previous 401/403 has paused this credential (browser only). */\nexport function isAuthPaused(credential?: string): boolean {\n if (typeof window === 'undefined') return false\n const id = credentialBreakerId(credential)\n const until = pausedKeys.get(id)\n if (!until) return false\n if (Date.now() >= until) {\n pausedKeys.delete(id)\n return false\n }\n return true\n}\n\nfunction tripBreaker(credential?: string): void {\n pausedKeys.set(credentialBreakerId(credential), Date.now() + AUTH_BREAK_MS)\n if (!warnedThisPage) {\n warnedThisPage = true\n // info, not error: a visitor's console must stay clean (Lighthouse\n // errors-in-console), and copy never names env vars or key formats.\n console.info(\n \"[Sonor] This site couldn't authenticate with Sonor, so data features are paused for a few minutes. If this keeps happening, the site is likely running an outdated API key.\",\n )\n }\n}\n\nexport async function sonorFetch(\n url: string,\n init: RequestInit = {},\n options: SonorFetchOptions = {},\n): Promise<Response> {\n // Resolve the credential to send. In token mode this may transparently refresh\n // a stale seed before the first request; in raw-key mode it's a passthrough.\n let credential = options.apiKey\n if (credential !== undefined || isTokenMode()) {\n credential = await getFreshCredential(options.apiKey)\n }\n\n const headers = new Headers(init.headers || {})\n headers.set('x-sitekit-version', SITE_KIT_VERSION)\n if (credential) {\n if (isTokenMode()) {\n // Token mode: `credential` is the freshest token (refreshed just above if\n // the seed was stale). It MUST override any x-api-key a caller hand-set — a\n // stale build-time seed passed as a header would otherwise pin the request\n // and defeat refresh entirely. Also drop a caller-set Authorization:\n // extractApiKey() server-side prefers `Authorization: Bearer` over\n // x-api-key, so a stale Bearer seed would win even once we set a fresh\n // x-api-key. This is the single place the client credential is chosen — do\n // not re-attach it as a header at call sites.\n headers.set('x-api-key', credential)\n headers.delete('authorization')\n } else if (!headers.has('x-api-key')) {\n // Raw-key mode: legacy passthrough — respect a caller-supplied header.\n headers.set('x-api-key', credential)\n }\n }\n\n if (isAuthPaused(credential)) {\n // Synthetic response keeps every caller's !response.ok path working\n // without a network round trip while the breaker is open.\n return new Response(null, { status: 401, statusText: 'sonor-auth-paused' })\n }\n\n let response = await fetchWithRetry(url, { ...init, headers }, options)\n\n // A token can rotate or expire mid-session. On the first auth failure, try ONE\n // refresh + retry before tripping the breaker — only for token credentials\n // (raw keys have nothing to refresh).\n if (\n (response.status === 401 || response.status === 403) &&\n typeof window !== 'undefined' &&\n isSiteToken(credential)\n ) {\n const refreshed = await refreshSiteToken()\n if (refreshed && refreshed !== credential) {\n const retryHeaders = new Headers(init.headers || {})\n retryHeaders.set('x-sitekit-version', SITE_KIT_VERSION)\n retryHeaders.set('x-api-key', refreshed)\n // Same reason as the initial request: a caller-set stale Bearer would win\n // over our fresh x-api-key server-side, so strip it on the retry too.\n retryHeaders.delete('authorization')\n credential = refreshed\n response = await fetchWithRetry(url, { ...init, headers: retryHeaders }, options)\n }\n }\n\n if (\n (response.status === 401 || response.status === 403) &&\n typeof window !== 'undefined' &&\n credential\n ) {\n tripBreaker(credential)\n }\n\n return response\n}\n\n/**\n * Fire-and-forget telemetry that survives page unload.\n *\n * Uses fetch({ keepalive: true }) so auth travels in headers. Falls back to\n * navigator.sendBeacon (no auth headers possible — the server may reject it,\n * but we never put the key in the URL) only when keepalive fetch is missing.\n *\n * Cannot await a refresh at unload time, so it sends the freshest token already\n * held (currentCredential) rather than the possibly-stale seed the caller passed.\n */\nexport function sonorBeacon(url: string, payload: unknown, apiKey?: string): void {\n const credential = currentCredential(apiKey)\n const body = typeof payload === 'string' ? payload : JSON.stringify(payload)\n try {\n void fetch(url, {\n method: 'POST',\n keepalive: true,\n headers: {\n 'Content-Type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n ...(credential ? { 'x-api-key': credential } : {}),\n },\n body,\n }).catch(() => {\n /* page is closing — best effort */\n })\n } catch {\n try {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }))\n }\n } catch {\n /* nothing left to try */\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/shared/siteToken.ts","../src/shared/fetchWithRetry.ts","../src/shared/sonorFetch.ts"],"names":[],"mappings":"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","file":"chunk-GUU3AEFM.mjs","sourcesContent":["/**\n * @sonordev/site-kit shared — Client token manager\n *\n * SiteKitLayout (a server component) no longer ships the raw `sonor_` API key to\n * the browser. Instead it mints a short-lived, project-scoped token server-side\n * and injects THAT. This module is the browser-side lifecycle for that token:\n *\n * - holds the current token (seeded from the injected value)\n * - a static page's seed can be days old and already expired on first view, so\n * it transparently REFRESHES via the public endpoint before first use\n * - dedupes concurrent refreshes (one in-flight request, awaited by all)\n * - persists the freshest token in sessionStorage so navigations within a\n * session don't each re-refresh\n *\n * Everything here is a no-op passthrough when the injected credential is a raw\n * key (a 2.x kit, or the mint-failed fallback) — legacy behavior is unchanged.\n * Fully SSR-safe: on the server there is no refresh, just the seed.\n *\n * The token payload is NOT secret (only its HMAC is unforgeable), so decoding it\n * client-side to read `exp`/`pid` is fine and needs no crypto.\n */\nimport { SITE_KIT_VERSION } from './version'\n\nexport const SITE_TOKEN_PREFIX = 'st1.'\n\n/** Refresh this many ms before expiry, so a request never races the deadline. */\nconst REFRESH_SKEW_MS = 5 * 60_000\nconst REFRESH_TIMEOUT_MS = 6_000\nconst STORAGE_KEY = '__sonor_site_token__'\n\nexport function isSiteToken(credential?: string | null): boolean {\n return typeof credential === 'string' && credential.startsWith(SITE_TOKEN_PREFIX)\n}\n\ninterface DecodedClaims {\n pid?: string\n iat?: number\n exp?: number\n}\n\n/** Decode the (non-secret) token payload. Returns null if not a token / malformed. */\nexport function decodeSiteTokenClaims(token?: string | null): DecodedClaims | null {\n if (!isSiteToken(token)) return null\n const seg = (token as string).split('.')[1]\n if (!seg) return null\n try {\n // base64url → JSON. atob handles standard base64; restore url-safe chars.\n const b64 = seg.replace(/-/g, '+').replace(/_/g, '/')\n const json =\n typeof atob === 'function'\n ? atob(b64)\n : Buffer.from(seg, 'base64url').toString('utf8')\n const claims = JSON.parse(json)\n return typeof claims === 'object' && claims ? claims : null\n } catch {\n return null\n }\n}\n\n/**\n * Stable id for the auth circuit breaker. Tokens rotate, so keying the breaker\n * by the raw value would reset it every refresh — key by the project id encoded\n * in the token instead. Raw keys use a safe prefix.\n */\nexport function credentialBreakerId(credential?: string | null): string {\n if (!credential) return '(anonymous)'\n const claims = decodeSiteTokenClaims(credential)\n if (claims?.pid) return `pid:${claims.pid}`\n return credential.slice(0, 16)\n}\n\ninterface TokenState {\n /** The credential injected by the layout (token or raw key). */\n seed: string | undefined\n /** Whether we're in token mode (seed is a token). */\n isToken: boolean\n /** Base API URL for the refresh endpoint. */\n apiUrl: string\n /** The freshest known token (>= seed). Null in raw-key mode. */\n token: string | null\n /** In-flight refresh, deduped. */\n refreshing: Promise<string | null> | null\n}\n\nconst state: TokenState = {\n seed: undefined,\n isToken: false,\n apiUrl: 'https://api.sonor.io',\n token: null,\n refreshing: null,\n}\n\nfunction readStoredToken(): string | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n return typeof parsed?.token === 'string' ? parsed.token : null\n } catch {\n return null\n }\n}\n\nfunction storeToken(token: string): void {\n if (typeof window === 'undefined') return\n try {\n window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ token }))\n } catch {\n /* private mode / disabled storage — memory-only is fine */\n }\n try {\n ;(window as any).__SITE_KIT_API_KEY__ = token\n } catch {\n /* ignore */\n }\n}\n\nfunction tokenExp(token: string | null): number {\n const claims = decodeSiteTokenClaims(token)\n return typeof claims?.exp === 'number' ? claims.exp * 1000 : 0\n}\n\n/**\n * Configure the manager from the layout-injected values. Adopts a fresher token\n * from sessionStorage if a prior page in this session already refreshed.\n */\nexport function configureSiteCredential(opts: {\n credential?: string\n apiUrl?: string\n}): void {\n state.seed = opts.credential\n state.isToken = isSiteToken(opts.credential)\n if (opts.apiUrl) state.apiUrl = opts.apiUrl.replace(/\\/+$/, '')\n\n if (!state.isToken) {\n state.token = null\n return\n }\n\n // configure runs on every render — never downgrade. Keep the freshest of the\n // injected seed, a token stored by an earlier page this session, and whatever\n // an in-flight/prior refresh already put in memory.\n const prior = state.token\n let best = opts.credential ?? null\n const stored = readStoredToken()\n if (stored && tokenExp(stored) > tokenExp(best)) best = stored\n if (prior && tokenExp(prior) > tokenExp(best)) best = prior\n state.token = best\n}\n\nfunction needsRefresh(): boolean {\n if (!state.isToken) return false\n if (!state.token) return true\n return Date.now() >= tokenExp(state.token) - REFRESH_SKEW_MS\n}\n\nasync function doRefresh(): Promise<string | null> {\n const current = state.token\n if (!current) return null\n try {\n const res = await fetch(`${state.apiUrl}/api/public/site-token/refresh`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n // Send the current token as x-api-key too, so the server's key-scoped\n // throttler buckets refreshes by token rather than by shared CDN IP.\n 'x-api-key': current,\n },\n body: JSON.stringify({ token: current }),\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(REFRESH_TIMEOUT_MS)\n : undefined,\n })\n if (!res.ok) return null\n const data = await res.json().catch(() => null)\n const next = data?.token\n if (typeof next === 'string' && isSiteToken(next)) {\n state.token = next\n storeToken(next)\n return next\n }\n return null\n } catch {\n return null\n }\n}\n\n/** Refresh now (deduped). Returns the new token, or null on failure. */\nexport function refreshSiteToken(): Promise<string | null> {\n if (!state.isToken) return Promise.resolve(null)\n if (state.refreshing) return state.refreshing\n state.refreshing = doRefresh().finally(() => {\n state.refreshing = null\n })\n return state.refreshing\n}\n\n/**\n * The credential to put on the next request. In token mode, refreshes a stale\n * seed first. In raw-key mode, returns the seed unchanged (legacy passthrough).\n */\nexport async function getFreshCredential(\n seed?: string,\n): Promise<string | undefined> {\n // A raw key (or nothing configured) — no token machinery.\n if (!state.isToken) return seed ?? state.seed\n if (needsRefresh()) {\n const refreshed = await refreshSiteToken()\n if (refreshed) return refreshed\n // Refresh failed — fall through to whatever we have; the request may 401 and\n // trip the breaker with friendly copy.\n }\n return state.token ?? seed ?? state.seed\n}\n\n/** Whether the given credential is being managed as a token. */\nexport function isTokenMode(): boolean {\n return state.isToken\n}\n\n/**\n * Best-known credential WITHOUT refreshing — for synchronous unload paths\n * (sonorBeacon) that can't await. In token mode returns the freshest token held;\n * in raw-key mode returns the seed.\n */\nexport function currentCredential(seed?: string): string | undefined {\n if (!state.isToken) return seed ?? state.seed\n return state.token ?? seed ?? state.seed\n}\n","/**\n * @sonordev/site-kit shared - Resilient fetch\n *\n * Single source of truth for client-side resilient fetch (forms, SitemapSync).\n * Adds a per-attempt timeout and retries transient failures (network errors,\n * request timeouts, HTTP 429, and 5xx) with exponential backoff.\n *\n * A form submission must survive a brief API blip — a deploy, a cold start, a\n * dropped packet. A single un-retried fetch turning into \"Something went wrong\"\n * is how leads get lost. The explicit abort also matters on its own: a request\n * left to hang until the browser's own network timeout logs a console error\n * (net::ERR_TIMED_OUT) that fails Lighthouse best-practices; an aborted fetch\n * logs nothing.\n */\n\nexport interface FetchRetryOptions {\n /** Number of retries AFTER the first attempt (default 2 → up to 3 tries total). */\n retries?: number\n /** Per-attempt timeout in milliseconds (default 15000). */\n timeoutMs?: number\n}\n\nfunction backoffDelay(attempt: number, response?: Response): number {\n // Honor a Retry-After header on 429 responses when present.\n if (response?.status === 429) {\n const header = response.headers.get('retry-after')\n const seconds = header ? parseInt(header, 10) : NaN\n if (!Number.isNaN(seconds) && seconds > 0) return Math.min(seconds * 1000, 10000)\n }\n // Exponential backoff: 500ms, 1s, 2s, ... capped at 8s.\n return Math.min(8000, 500 * 2 ** attempt)\n}\n\n/**\n * fetch() with a timeout and automatic retries for transient failures.\n *\n * Non-transient responses (2xx, 3xx, and 4xx other than 429) are returned to\n * the caller as-is — only 429/5xx and thrown network/timeout errors are retried.\n * If every attempt fails with a thrown error, the last error is re-thrown.\n */\nexport async function fetchWithRetry(\n url: string,\n init: RequestInit,\n options: FetchRetryOptions = {},\n): Promise<Response> {\n const retries = options.retries ?? 2\n const timeoutMs = options.timeoutMs ?? 15000\n let lastError: unknown\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const response = await fetch(url, { ...init, signal: controller.signal })\n clearTimeout(timer)\n\n if ((response.status === 429 || response.status >= 500) && attempt < retries) {\n await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt, response)))\n continue\n }\n return response\n } catch (error) {\n clearTimeout(timer)\n lastError = error\n // Network failure or timeout (AbortController) — retry while attempts remain.\n if (attempt < retries) {\n await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt)))\n continue\n }\n }\n }\n\n throw lastError instanceof Error\n ? lastError\n : new Error('Network request failed after multiple attempts. Please try again.')\n}\n","/**\n * @sonordev/site-kit shared - Canonical client transport\n *\n * Single source of truth for how site-kit talks to Sonor from the browser.\n * Every client-side module (analytics, forms, commerce, engage, maps, images,\n * SitemapSync) routes through here instead of bare fetch(), which gives the\n * whole kit, in one place:\n *\n * - per-attempt timeout + retry with backoff (via fetchWithRetry) — a hung\n * request left to the browser's own network timeout logs a console error\n * (net::ERR_TIMED_OUT) that fails Lighthouse best-practices\n * - the x-sitekit-version header on every request, so the platform can see\n * which kit version each site actually runs from live traffic\n * - CREDENTIAL LIFECYCLE: sites on 3.2+ ship a short-lived minted token instead\n * of the raw key. This transport resolves the freshest token (refreshing a\n * stale seed transparently), and on a 401/403 for a token it refreshes once\n * and retries before giving up. A raw key (2.x kit, or the mint-failed\n * fallback) is a pure passthrough — legacy behavior is unchanged.\n * - an auth circuit breaker: after a 401/403 the credential is paused for a few\n * minutes and ONE friendly console.info is emitted — a site on a stale or\n * canceled key must not spam the API or the visitor's console. Keyed by the\n * project (from the token) so it survives token rotation.\n * - sonorBeacon for unload-time telemetry that keeps the credential in headers;\n * navigator.sendBeacon can't set headers and forced the key into the URL,\n * where server and CDN access logs capture it\n *\n * Non-idempotent endpoints (checkout, payment intents, uploads) must pass\n * { retries: 0 } — they still get the timeout, but never an automatic retry.\n */\nimport { fetchWithRetry, type FetchRetryOptions } from './fetchWithRetry'\nimport { SITE_KIT_VERSION } from './version'\nimport {\n credentialBreakerId,\n currentCredential,\n getFreshCredential,\n isSiteToken,\n isTokenMode,\n refreshSiteToken,\n} from './siteToken'\n\nexport interface SonorFetchOptions extends FetchRetryOptions {\n /** Project credential — the minted token (3.2+) or raw key. Sent as x-api-key. */\n apiKey?: string\n}\n\n/** How long a credential stays paused after an auth failure. */\nconst AUTH_BREAK_MS = 5 * 60_000\n\n// breaker id → epoch ms until which requests are paused. Module-scoped: the\n// breaker lives for the page's lifetime and never applies on the server.\nconst pausedKeys = new Map<string, number>()\nlet warnedThisPage = false\n\n/** True when a previous 401/403 has paused this credential (browser only). */\nexport function isAuthPaused(credential?: string): boolean {\n if (typeof window === 'undefined') return false\n const id = credentialBreakerId(credential)\n const until = pausedKeys.get(id)\n if (!until) return false\n if (Date.now() >= until) {\n pausedKeys.delete(id)\n return false\n }\n return true\n}\n\nfunction tripBreaker(credential?: string): void {\n pausedKeys.set(credentialBreakerId(credential), Date.now() + AUTH_BREAK_MS)\n if (!warnedThisPage) {\n warnedThisPage = true\n // info, not error: a visitor's console must stay clean (Lighthouse\n // errors-in-console), and copy never names env vars or key formats.\n console.info(\n \"[Sonor] This site couldn't authenticate with Sonor, so data features are paused for a few minutes. If this keeps happening, the site is likely running an outdated API key.\",\n )\n }\n}\n\nexport async function sonorFetch(\n url: string,\n init: RequestInit = {},\n options: SonorFetchOptions = {},\n): Promise<Response> {\n // Resolve the credential to send. In token mode this may transparently refresh\n // a stale seed before the first request; in raw-key mode it's a passthrough.\n let credential = options.apiKey\n if (credential !== undefined || isTokenMode()) {\n credential = await getFreshCredential(options.apiKey)\n }\n\n const headers = new Headers(init.headers || {})\n headers.set('x-sitekit-version', SITE_KIT_VERSION)\n if (credential) {\n if (isTokenMode()) {\n // Token mode: `credential` is the freshest token (refreshed just above if\n // the seed was stale). It MUST override any x-api-key a caller hand-set — a\n // stale build-time seed passed as a header would otherwise pin the request\n // and defeat refresh entirely. Also drop a caller-set Authorization:\n // extractApiKey() server-side prefers `Authorization: Bearer` over\n // x-api-key, so a stale Bearer seed would win even once we set a fresh\n // x-api-key. This is the single place the client credential is chosen — do\n // not re-attach it as a header at call sites.\n headers.set('x-api-key', credential)\n headers.delete('authorization')\n } else if (!headers.has('x-api-key')) {\n // Raw-key mode: legacy passthrough — respect a caller-supplied header.\n headers.set('x-api-key', credential)\n }\n }\n\n if (isAuthPaused(credential)) {\n // Synthetic response keeps every caller's !response.ok path working\n // without a network round trip while the breaker is open.\n return new Response(null, { status: 401, statusText: 'sonor-auth-paused' })\n }\n\n let response = await fetchWithRetry(url, { ...init, headers }, options)\n\n // A token can rotate or expire mid-session. On the first auth failure, try ONE\n // refresh + retry before tripping the breaker — only for token credentials\n // (raw keys have nothing to refresh).\n if (\n (response.status === 401 || response.status === 403) &&\n typeof window !== 'undefined' &&\n isSiteToken(credential)\n ) {\n const refreshed = await refreshSiteToken()\n if (refreshed && refreshed !== credential) {\n const retryHeaders = new Headers(init.headers || {})\n retryHeaders.set('x-sitekit-version', SITE_KIT_VERSION)\n retryHeaders.set('x-api-key', refreshed)\n // Same reason as the initial request: a caller-set stale Bearer would win\n // over our fresh x-api-key server-side, so strip it on the retry too.\n retryHeaders.delete('authorization')\n credential = refreshed\n response = await fetchWithRetry(url, { ...init, headers: retryHeaders }, options)\n }\n }\n\n if (\n (response.status === 401 || response.status === 403) &&\n typeof window !== 'undefined' &&\n credential\n ) {\n tripBreaker(credential)\n }\n\n return response\n}\n\n/**\n * Fire-and-forget telemetry that survives page unload.\n *\n * Uses fetch({ keepalive: true }) so auth travels in headers. Falls back to\n * navigator.sendBeacon (no auth headers possible — the server may reject it,\n * but we never put the key in the URL) only when keepalive fetch is missing.\n *\n * Cannot await a refresh at unload time, so it sends the freshest token already\n * held (currentCredential) rather than the possibly-stale seed the caller passed.\n */\nexport function sonorBeacon(url: string, payload: unknown, apiKey?: string): void {\n const credential = currentCredential(apiKey)\n const body = typeof payload === 'string' ? payload : JSON.stringify(payload)\n try {\n void fetch(url, {\n method: 'POST',\n keepalive: true,\n headers: {\n 'Content-Type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n ...(credential ? { 'x-api-key': credential } : {}),\n },\n body,\n }).catch(() => {\n /* page is closing — best effort */\n })\n } catch {\n try {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }))\n }\n } catch {\n /* nothing left to try */\n }\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { useSignalExperiment, useSignalEvent } from './chunk-ZCZEKWFW.mjs';
2
+ import { useSignalExperiment, useSignalEvent } from './chunk-QFWBAGBT.mjs';
3
3
  import React, { useEffect } from 'react';
4
4
  import { jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -71,5 +71,5 @@ function ExperimentConversion({
71
71
  }
72
72
 
73
73
  export { ExperimentConversion, SignalExperiment, useExperimentVariant };
74
- //# sourceMappingURL=chunk-5KVT66R6.mjs.map
75
- //# sourceMappingURL=chunk-5KVT66R6.mjs.map
74
+ //# sourceMappingURL=chunk-JD6LV6JZ.mjs.map
75
+ //# sourceMappingURL=chunk-JD6LV6JZ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/signal/SignalExperiment.tsx"],"names":[],"mappings":";;;;AAgDO,SAAS,gBAAA,CAAiB;AAAA,EAC/B,YAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA,GAAkB,IAAA;AAAA,EAClB;AACF,CAAA,EAA0B;AACxB,EAAA,MAAM,EAAE,UAAA,EAAY,OAAA,EAAS,SAAA,EAAU,GAAI,oBAAoB,YAAY,CAAA;AAC3E,EAAA,MAAM,aAAa,cAAA,EAAe;AAGlC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,mBAAmB,OAAA,EAAS;AAC9B,MAAA,UAAA,CAAW;AAAA,QACT,UAAA,EAAY,YAAA;AAAA,QACZ,UAAA,EAAY,YAAA;AAAA,QACZ,UAAA,EAAY;AAAA,UACV,aAAA,EAAe,YAAA;AAAA,UACf,WAAA,EAAa;AAAA;AACf,OACD,CAAA;AAAA,IACH;AAAA,EACF,GAAG,CAAC,YAAA,EAAc,OAAA,EAAS,eAAA,EAAiB,UAAU,CAAC,CAAA;AAGvD,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,uBAAO,GAAA,CAAA,QAAA,EAAA,EAAG,mBAAS,EAAE,OAAA,EAAS,WAAW,SAAA,EAAW,SAAA,EAAW,CAAA,EAAE,CAAA;AAAA,EACnE;AAGA,EAAA,MAAM,kBAAkB,OAAA,IAAW,SAAA;AACnC,EAAA,MAAM,OAAA,GAAU,SAAS,eAAe,CAAA;AAExC,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,uCAAU,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,EACpB;AAGA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,uCAAU,QAAA,EAAA,QAAA,EAAS,CAAA;AAAA,EACrB;AAEA,EAAA,uBAAO,GAAA,CAAA,QAAA,EAAA,EAAG,QAAA,EAAA,QAAA,CAAS,OAAA,IAAW,IAAA,EAAK,CAAA;AACrC;AAYO,SAAS,qBAAqB,YAAA,EAAsB;AACzD,EAAA,OAAO,oBAAoB,YAAY,CAAA;AACzC;AAoBO,SAAS,oBAAA,CAAqB;AAAA,EACnC,YAAA;AAAA,EACA,WAAA,GAAc,OAAA;AAAA,EACd,KAAA;AAAA,EACA;AACF,CAAA,EAKG;AACD,EAAA,MAAM,aAAa,cAAA,EAAe;AAClC,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,mBAAA,CAAoB,YAAY,CAAA;AAEpD,EAAA,MAAM,oBAAoB,MAAM;AAC9B,IAAA,UAAA,CAAW;AAAA,MACT,UAAA,EAAY,YAAA;AAAA,MACZ,UAAA,EAAY,YAAA;AAAA,MACZ,UAAA,EAAY;AAAA,QACV,aAAA,EAAe,YAAA;AAAA,QACf,WAAA,EAAa,OAAA;AAAA,QACb,YAAA,EAAc,WAAA;AAAA,QACd;AAAA;AACF,KACD,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,OAAO,KAAA,CAAM,aAAa,QAAA,EAAU;AAAA,IAClC,OAAA,EAAS,CAAC,CAAA,KAAwB;AAChC,MAAA,iBAAA,EAAkB;AAElB,MAAA,IAAK,QAAA,CAAS,MAAsD,OAAA,EAAS;AAC3E,QAAC,QAAA,CAAS,KAAA,CAAsD,OAAA,CAAS,CAAC,CAAA;AAAA,MAC5E;AAAA,IACF;AAAA,GACD,CAAA;AACH","file":"chunk-5KVT66R6.mjs","sourcesContent":["/**\n * @sonordev/site-kit/signal - SignalExperiment\n * \n * A/B test component that renders different variants based on Signal assignment.\n * Uses Thompson Sampling (Bayesian bandit) for optimization.\n */\n\n'use client'\n\nimport React, { useMemo, useEffect } from 'react'\nimport { useSignalExperiment, useSignalEvent } from './SignalBridge'\n\ninterface SignalExperimentProps {\n /** Unique experiment ID */\n experimentId: string\n \n /** Variants to test - keys are variant names, values are React nodes */\n variants: Record<string, React.ReactNode>\n \n /** Fallback content if no assignment (defaults to 'control' variant) */\n fallback?: React.ReactNode\n \n /** Track impression automatically */\n trackImpression?: boolean\n \n /** Children function for more control */\n children?: (props: { variant: string; isControl: boolean }) => React.ReactNode\n}\n\n/**\n * Renders different content variants based on Signal's A/B assignment.\n * \n * @example\n * // Simple variant switching\n * <SignalExperiment experimentId=\"hero-cta\" variants={{\n * control: <Button>Get Started</Button>,\n * variant_a: <Button variant=\"primary\">Start Free Trial</Button>,\n * variant_b: <Button variant=\"secondary\">Book a Demo</Button>,\n * }} />\n * \n * @example\n * // With render prop for more control\n * <SignalExperiment experimentId=\"pricing-layout\">\n * {({ variant, isControl }) => (\n * <PricingSection layout={isControl ? 'cards' : 'table'} />\n * )}\n * </SignalExperiment>\n */\nexport function SignalExperiment({\n experimentId,\n variants,\n fallback,\n trackImpression = true,\n children,\n}: SignalExperimentProps) {\n const { assignment, variant, isControl } = useSignalExperiment(experimentId)\n const trackEvent = useSignalEvent()\n \n // Track impression when variant is rendered\n useEffect(() => {\n if (trackImpression && variant) {\n trackEvent({\n event_type: 'experiment',\n event_name: 'impression',\n event_data: {\n experiment_id: experimentId,\n variant_key: variant,\n },\n })\n }\n }, [experimentId, variant, trackImpression, trackEvent])\n \n // If using render prop\n if (children) {\n return <>{children({ variant: variant || 'control', isControl })}</>\n }\n \n // Determine what to render\n const selectedVariant = variant || 'control'\n const content = variants[selectedVariant]\n \n if (content !== undefined) {\n return <>{content}</>\n }\n \n // Fallback to provided fallback or control\n if (fallback) {\n return <>{fallback}</>\n }\n \n return <>{variants.control || null}</>\n}\n\n/**\n * Hook for conditional experiment logic\n * \n * @example\n * const { variant, isControl } = useExperimentVariant('pricing-test')\n * \n * return isControl \n * ? <OldPricing />\n * : <NewPricing showAnnual={variant === 'annual_first'} />\n */\nexport function useExperimentVariant(experimentId: string) {\n return useSignalExperiment(experimentId)\n}\n\n/**\n * Component for tracking experiment conversions\n * Wraps interactive elements to track when they convert\n * \n * @example\n * <SignalExperiment experimentId=\"signup-button\" variants={{\n * control: (\n * <ExperimentConversion experimentId=\"signup-button\" outcomeType=\"click\">\n * <Button>Sign Up</Button>\n * </ExperimentConversion>\n * ),\n * variant_a: (\n * <ExperimentConversion experimentId=\"signup-button\" outcomeType=\"click\">\n * <Button variant=\"large\">Join Now - It's Free!</Button>\n * </ExperimentConversion>\n * ),\n * }} />\n */\nexport function ExperimentConversion({\n experimentId,\n outcomeType = 'click',\n value,\n children,\n}: {\n experimentId: string\n outcomeType?: string\n value?: number\n children: React.ReactElement<{ onClick?: (e: React.MouseEvent) => void }>\n}) {\n const trackEvent = useSignalEvent()\n const { variant } = useSignalExperiment(experimentId)\n\n const handleInteraction = () => {\n trackEvent({\n event_type: 'experiment',\n event_name: 'conversion',\n event_data: {\n experiment_id: experimentId,\n variant_key: variant,\n outcome_type: outcomeType,\n value,\n },\n })\n }\n\n return React.cloneElement(children, {\n onClick: (e: React.MouseEvent) => {\n handleInteraction()\n // Call original onClick if exists\n if ((children.props as { onClick?: (e: React.MouseEvent) => void }).onClick) {\n (children.props as { onClick?: (e: React.MouseEvent) => void }).onClick!(e)\n }\n },\n })\n}\n"]}
1
+ {"version":3,"sources":["../src/signal/SignalExperiment.tsx"],"names":[],"mappings":";;;;AAgDO,SAAS,gBAAA,CAAiB;AAAA,EAC/B,YAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA,GAAkB,IAAA;AAAA,EAClB;AACF,CAAA,EAA0B;AACxB,EAAA,MAAM,EAAE,UAAA,EAAY,OAAA,EAAS,SAAA,EAAU,GAAI,oBAAoB,YAAY,CAAA;AAC3E,EAAA,MAAM,aAAa,cAAA,EAAe;AAGlC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,mBAAmB,OAAA,EAAS;AAC9B,MAAA,UAAA,CAAW;AAAA,QACT,UAAA,EAAY,YAAA;AAAA,QACZ,UAAA,EAAY,YAAA;AAAA,QACZ,UAAA,EAAY;AAAA,UACV,aAAA,EAAe,YAAA;AAAA,UACf,WAAA,EAAa;AAAA;AACf,OACD,CAAA;AAAA,IACH;AAAA,EACF,GAAG,CAAC,YAAA,EAAc,OAAA,EAAS,eAAA,EAAiB,UAAU,CAAC,CAAA;AAGvD,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,uBAAO,GAAA,CAAA,QAAA,EAAA,EAAG,mBAAS,EAAE,OAAA,EAAS,WAAW,SAAA,EAAW,SAAA,EAAW,CAAA,EAAE,CAAA;AAAA,EACnE;AAGA,EAAA,MAAM,kBAAkB,OAAA,IAAW,SAAA;AACnC,EAAA,MAAM,OAAA,GAAU,SAAS,eAAe,CAAA;AAExC,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,uCAAU,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,EACpB;AAGA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,uCAAU,QAAA,EAAA,QAAA,EAAS,CAAA;AAAA,EACrB;AAEA,EAAA,uBAAO,GAAA,CAAA,QAAA,EAAA,EAAG,QAAA,EAAA,QAAA,CAAS,OAAA,IAAW,IAAA,EAAK,CAAA;AACrC;AAYO,SAAS,qBAAqB,YAAA,EAAsB;AACzD,EAAA,OAAO,oBAAoB,YAAY,CAAA;AACzC;AAoBO,SAAS,oBAAA,CAAqB;AAAA,EACnC,YAAA;AAAA,EACA,WAAA,GAAc,OAAA;AAAA,EACd,KAAA;AAAA,EACA;AACF,CAAA,EAKG;AACD,EAAA,MAAM,aAAa,cAAA,EAAe;AAClC,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,mBAAA,CAAoB,YAAY,CAAA;AAEpD,EAAA,MAAM,oBAAoB,MAAM;AAC9B,IAAA,UAAA,CAAW;AAAA,MACT,UAAA,EAAY,YAAA;AAAA,MACZ,UAAA,EAAY,YAAA;AAAA,MACZ,UAAA,EAAY;AAAA,QACV,aAAA,EAAe,YAAA;AAAA,QACf,WAAA,EAAa,OAAA;AAAA,QACb,YAAA,EAAc,WAAA;AAAA,QACd;AAAA;AACF,KACD,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,OAAO,KAAA,CAAM,aAAa,QAAA,EAAU;AAAA,IAClC,OAAA,EAAS,CAAC,CAAA,KAAwB;AAChC,MAAA,iBAAA,EAAkB;AAElB,MAAA,IAAK,QAAA,CAAS,MAAsD,OAAA,EAAS;AAC3E,QAAC,QAAA,CAAS,KAAA,CAAsD,OAAA,CAAS,CAAC,CAAA;AAAA,MAC5E;AAAA,IACF;AAAA,GACD,CAAA;AACH","file":"chunk-JD6LV6JZ.mjs","sourcesContent":["/**\n * @sonordev/site-kit/signal - SignalExperiment\n * \n * A/B test component that renders different variants based on Signal assignment.\n * Uses Thompson Sampling (Bayesian bandit) for optimization.\n */\n\n'use client'\n\nimport React, { useMemo, useEffect } from 'react'\nimport { useSignalExperiment, useSignalEvent } from './SignalBridge'\n\ninterface SignalExperimentProps {\n /** Unique experiment ID */\n experimentId: string\n \n /** Variants to test - keys are variant names, values are React nodes */\n variants: Record<string, React.ReactNode>\n \n /** Fallback content if no assignment (defaults to 'control' variant) */\n fallback?: React.ReactNode\n \n /** Track impression automatically */\n trackImpression?: boolean\n \n /** Children function for more control */\n children?: (props: { variant: string; isControl: boolean }) => React.ReactNode\n}\n\n/**\n * Renders different content variants based on Signal's A/B assignment.\n * \n * @example\n * // Simple variant switching\n * <SignalExperiment experimentId=\"hero-cta\" variants={{\n * control: <Button>Get Started</Button>,\n * variant_a: <Button variant=\"primary\">Start Free Trial</Button>,\n * variant_b: <Button variant=\"secondary\">Book a Demo</Button>,\n * }} />\n * \n * @example\n * // With render prop for more control\n * <SignalExperiment experimentId=\"pricing-layout\">\n * {({ variant, isControl }) => (\n * <PricingSection layout={isControl ? 'cards' : 'table'} />\n * )}\n * </SignalExperiment>\n */\nexport function SignalExperiment({\n experimentId,\n variants,\n fallback,\n trackImpression = true,\n children,\n}: SignalExperimentProps) {\n const { assignment, variant, isControl } = useSignalExperiment(experimentId)\n const trackEvent = useSignalEvent()\n \n // Track impression when variant is rendered\n useEffect(() => {\n if (trackImpression && variant) {\n trackEvent({\n event_type: 'experiment',\n event_name: 'impression',\n event_data: {\n experiment_id: experimentId,\n variant_key: variant,\n },\n })\n }\n }, [experimentId, variant, trackImpression, trackEvent])\n \n // If using render prop\n if (children) {\n return <>{children({ variant: variant || 'control', isControl })}</>\n }\n \n // Determine what to render\n const selectedVariant = variant || 'control'\n const content = variants[selectedVariant]\n \n if (content !== undefined) {\n return <>{content}</>\n }\n \n // Fallback to provided fallback or control\n if (fallback) {\n return <>{fallback}</>\n }\n \n return <>{variants.control || null}</>\n}\n\n/**\n * Hook for conditional experiment logic\n * \n * @example\n * const { variant, isControl } = useExperimentVariant('pricing-test')\n * \n * return isControl \n * ? <OldPricing />\n * : <NewPricing showAnnual={variant === 'annual_first'} />\n */\nexport function useExperimentVariant(experimentId: string) {\n return useSignalExperiment(experimentId)\n}\n\n/**\n * Component for tracking experiment conversions\n * Wraps interactive elements to track when they convert\n * \n * @example\n * <SignalExperiment experimentId=\"signup-button\" variants={{\n * control: (\n * <ExperimentConversion experimentId=\"signup-button\" outcomeType=\"click\">\n * <Button>Sign Up</Button>\n * </ExperimentConversion>\n * ),\n * variant_a: (\n * <ExperimentConversion experimentId=\"signup-button\" outcomeType=\"click\">\n * <Button variant=\"large\">Join Now - It's Free!</Button>\n * </ExperimentConversion>\n * ),\n * }} />\n */\nexport function ExperimentConversion({\n experimentId,\n outcomeType = 'click',\n value,\n children,\n}: {\n experimentId: string\n outcomeType?: string\n value?: number\n children: React.ReactElement<{ onClick?: (e: React.MouseEvent) => void }>\n}) {\n const trackEvent = useSignalEvent()\n const { variant } = useSignalExperiment(experimentId)\n\n const handleInteraction = () => {\n trackEvent({\n event_type: 'experiment',\n event_name: 'conversion',\n event_data: {\n experiment_id: experimentId,\n variant_key: variant,\n outcome_type: outcomeType,\n value,\n },\n })\n }\n\n return React.cloneElement(children, {\n onClick: (e: React.MouseEvent) => {\n handleInteraction()\n // Call original onClick if exists\n if ((children.props as { onClick?: (e: React.MouseEvent) => void }).onClick) {\n (children.props as { onClick?: (e: React.MouseEvent) => void }).onClick!(e)\n }\n },\n })\n}\n"]}
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ // src/sitemap/shared.ts
4
+ function inferPageType(urlPath) {
5
+ if (urlPath === "/" || urlPath === "") return "homepage";
6
+ if (/^\/services(\/|$)/i.test(urlPath)) return "service";
7
+ if (/^\/compare(\/|$)/i.test(urlPath)) return "comparison";
8
+ if (/^\/(insights|blog)(\/|$)/i.test(urlPath)) return "blog";
9
+ if (/^\/industries(\/|$)/i.test(urlPath)) return "industry";
10
+ if (/^\/work(\/|$)/i.test(urlPath)) return "portfolio";
11
+ if (/^\/faq(\/|$)/i.test(urlPath)) return "faq";
12
+ if (/^\/pricing(\/|$)/i.test(urlPath)) return "pricing";
13
+ return "page";
14
+ }
15
+ function stripTrailingSlash(path) {
16
+ if (path !== "/" && path.endsWith("/")) {
17
+ return path.slice(0, -1);
18
+ }
19
+ return path;
20
+ }
21
+ function locToPath(loc) {
22
+ let path;
23
+ try {
24
+ path = new URL(loc).pathname;
25
+ } catch {
26
+ path = loc.startsWith("/") ? loc : `/${loc}`;
27
+ }
28
+ return stripTrailingSlash(path);
29
+ }
30
+ var EMBEDDED_DOMAIN_PREFIX = /^\/{1,2}[a-z0-9.-]+\.[a-z]{2,}(\/|$)/i;
31
+ var EMBEDDED_DOMAIN_ANYWHERE = /\/\/[a-z0-9.-]+\.[a-z]{2,}/i;
32
+ function stripEmbeddedDomain(path) {
33
+ return path.replace(EMBEDDED_DOMAIN_PREFIX, "/");
34
+ }
35
+ function isMalformedSitemapPath(path) {
36
+ return path.includes("://") || EMBEDDED_DOMAIN_ANYWHERE.test(path);
37
+ }
38
+
39
+ exports.inferPageType = inferPageType;
40
+ exports.isMalformedSitemapPath = isMalformedSitemapPath;
41
+ exports.locToPath = locToPath;
42
+ exports.stripEmbeddedDomain = stripEmbeddedDomain;
43
+ exports.stripTrailingSlash = stripTrailingSlash;
44
+ //# sourceMappingURL=chunk-JEOKDUPQ.js.map
45
+ //# sourceMappingURL=chunk-JEOKDUPQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sitemap/shared.ts"],"names":[],"mappings":";;;AAoCO,SAAS,cAAc,OAAA,EAA2B;AACvD,EAAA,IAAI,OAAA,KAAY,GAAA,IAAO,OAAA,KAAY,EAAA,EAAI,OAAO,UAAA;AAC9C,EAAA,IAAI,oBAAA,CAAqB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,SAAA;AAC/C,EAAA,IAAI,mBAAA,CAAoB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,YAAA;AAC9C,EAAA,IAAI,2BAAA,CAA4B,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,MAAA;AACtD,EAAA,IAAI,sBAAA,CAAuB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,UAAA;AACjD,EAAA,IAAI,gBAAA,CAAiB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,WAAA;AAC3C,EAAA,IAAI,eAAA,CAAgB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,KAAA;AAC1C,EAAA,IAAI,mBAAA,CAAoB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,SAAA;AAC9C,EAAA,OAAO,MAAA;AACT;AAMO,SAAS,mBAAmB,IAAA,EAAsB;AACvD,EAAA,IAAI,IAAA,KAAS,GAAA,IAAO,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AACtC,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAAA,EACzB;AACA,EAAA,OAAO,IAAA;AACT;AAUO,SAAS,UAAU,GAAA,EAAqB;AAC7C,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI;AACF,IAAA,IAAA,GAAO,IAAI,GAAA,CAAI,GAAG,CAAA,CAAE,QAAA;AAAA,EACtB,CAAA,CAAA,MAAQ;AACN,IAAA,IAAA,GAAO,IAAI,UAAA,CAAW,GAAG,CAAA,GAAI,GAAA,GAAM,IAAI,GAAG,CAAA,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,mBAAmB,IAAI,CAAA;AAChC;AAMA,IAAM,sBAAA,GAAyB,uCAAA;AAG/B,IAAM,wBAAA,GAA2B,6BAAA;AAO1B,SAAS,oBAAoB,IAAA,EAAsB;AACxD,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,sBAAA,EAAwB,GAAG,CAAA;AACjD;AAMO,SAAS,uBAAuB,IAAA,EAAuB;AAC5D,EAAA,OAAO,KAAK,QAAA,CAAS,KAAK,CAAA,IAAK,wBAAA,CAAyB,KAAK,IAAI,CAAA;AACnE","file":"chunk-JEOKDUPQ.js","sourcesContent":["/**\n * @sonordev/site-kit — shared sitemap page classification + path normalization\n *\n * Single source of truth for two bits of logic that used to be copy-pasted in\n * three places (createSitemap in `sitemap/index.ts`, the runtime `SitemapSync`\n * component, and the `sonor-register-sitemap` CLI):\n *\n * 1. inferPageType — path → Signal page-type hint\n * 2. path normalization — turning a sitemap `<loc>` / entry URL into a clean,\n * site-relative path, and rejecting/repairing malformed embedded-domain paths\n *\n * The copies had already drifted (one treated `''` as the homepage, the others\n * didn't; one stripped embedded domains, another only rejected them). Keeping\n * one implementation is the CLAUDE.md \"consolidate duplicated logic\" rule.\n *\n * Pure string logic only — NO Node built-ins, NO React — so this file is safe to\n * import from the browser `SitemapSync` client component AND from the Node CLI.\n */\n\n/** Signal page-type hint attached to each sitemap entry. */\nexport type PageType =\n | 'homepage'\n | 'service'\n | 'comparison'\n | 'blog'\n | 'industry'\n | 'portfolio'\n | 'faq'\n | 'pricing'\n | 'page'\n\n/**\n * Infer a page type from a URL path, to give Signal context about what each\n * page covers. Accepts both `'/'` and `''` as the homepage (callers pass either\n * depending on how they normalized the path).\n */\nexport function inferPageType(urlPath: string): PageType {\n if (urlPath === '/' || urlPath === '') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\n/**\n * Strip a trailing slash from a path, preserving the `'/'` root. Used so that\n * `/about` and `/about/` are treated as the same page everywhere.\n */\nexport function stripTrailingSlash(path: string): string {\n if (path !== '/' && path.endsWith('/')) {\n return path.slice(0, -1)\n }\n return path\n}\n\n/**\n * Turn a sitemap `<loc>` value (a full URL) or an already-relative path into a\n * clean, site-relative path. Falls back gracefully when the value isn't a\n * parseable absolute URL, and always strips the trailing slash.\n *\n * `https://example.com/about/` → `/about`\n * `about` → `/about`\n */\nexport function locToPath(loc: string): string {\n let path: string\n try {\n path = new URL(loc).pathname\n } catch {\n path = loc.startsWith('/') ? loc : `/${loc}`\n }\n return stripTrailingSlash(path)\n}\n\n/**\n * Matches a leading embedded domain in a malformed path, e.g. a crawler or bad\n * generator producing `//example.com/services` instead of `/services`.\n */\nconst EMBEDDED_DOMAIN_PREFIX = /^\\/{1,2}[a-z0-9.-]+\\.[a-z]{2,}(\\/|$)/i\n\n/** Matches an embedded protocol or domain anywhere in a path (unrepairable). */\nconst EMBEDDED_DOMAIN_ANYWHERE = /\\/\\/[a-z0-9.-]+\\.[a-z]{2,}/i\n\n/**\n * Repair a path whose leading segment is actually a domain\n * (`//example.com/x` → `/x`). Only touches the leading embedded domain; a path\n * that's malformed deeper in is left for `isMalformedSitemapPath` to reject.\n */\nexport function stripEmbeddedDomain(path: string): string {\n return path.replace(EMBEDDED_DOMAIN_PREFIX, '/')\n}\n\n/**\n * True when a path still contains an embedded protocol or domain after\n * normalization — such a \"path\" isn't a real page path and must not be synced.\n */\nexport function isMalformedSitemapPath(path: string): boolean {\n return path.includes('://') || EMBEDDED_DOMAIN_ANYWHERE.test(path)\n}\n"]}
@@ -0,0 +1,6 @@
1
+ // src/shared/version.ts
2
+ var SITE_KIT_VERSION = "3.3.1";
3
+
4
+ export { SITE_KIT_VERSION };
5
+ //# sourceMappingURL=chunk-K6YGF2KJ.mjs.map
6
+ //# sourceMappingURL=chunk-K6YGF2KJ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";AASO,IAAM,gBAAA,GAAmB","file":"chunk-S4FJIG6Y.mjs","sourcesContent":["/**\n * @sonordev/site-kit shared - Package version constant\n *\n * Single source of truth for the version the kit reports at runtime (the\n * x-sitekit-version request header set by sonorFetch). Kept as a plain const\n * so client bundles don't need JSON imports; scripts/verify-dts.cjs asserts\n * it matches package.json at publish time, and version.test.ts asserts it in\n * dev, so it can't silently drift.\n */\nexport const SITE_KIT_VERSION = '3.3.0'\n"]}
1
+ {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";AASO,IAAM,gBAAA,GAAmB","file":"chunk-K6YGF2KJ.mjs","sourcesContent":["/**\n * @sonordev/site-kit shared - Package version constant\n *\n * Single source of truth for the version the kit reports at runtime (the\n * x-sitekit-version request header set by sonorFetch). Kept as a plain const\n * so client bundles don't need JSON imports; scripts/verify-dts.cjs asserts\n * it matches package.json at publish time, and version.test.ts asserts it in\n * dev, so it can't silently drift.\n */\nexport const SITE_KIT_VERSION = '3.3.1'\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkEOENN46G_js = require('./chunk-EOENN46G.js');
3
+ var chunk3X2YPJU5_js = require('./chunk-3X2YPJU5.js');
4
4
 
5
5
  // src/shared/siteToken.ts
6
6
  var SITE_TOKEN_PREFIX = "st1.";
@@ -91,7 +91,7 @@ async function doRefresh() {
91
91
  method: "POST",
92
92
  headers: {
93
93
  "content-type": "application/json",
94
- "x-sitekit-version": chunkEOENN46G_js.SITE_KIT_VERSION,
94
+ "x-sitekit-version": chunk3X2YPJU5_js.SITE_KIT_VERSION,
95
95
  // Send the current token as x-api-key too, so the server's key-scoped
96
96
  // throttler buckets refreshes by token rather than by shared CDN IP.
97
97
  "x-api-key": current
@@ -202,7 +202,7 @@ async function sonorFetch(url, init = {}, options = {}) {
202
202
  credential = await getFreshCredential(options.apiKey);
203
203
  }
204
204
  const headers = new Headers(init.headers || {});
205
- headers.set("x-sitekit-version", chunkEOENN46G_js.SITE_KIT_VERSION);
205
+ headers.set("x-sitekit-version", chunk3X2YPJU5_js.SITE_KIT_VERSION);
206
206
  if (credential) {
207
207
  if (isTokenMode()) {
208
208
  headers.set("x-api-key", credential);
@@ -219,7 +219,7 @@ async function sonorFetch(url, init = {}, options = {}) {
219
219
  const refreshed = await refreshSiteToken();
220
220
  if (refreshed && refreshed !== credential) {
221
221
  const retryHeaders = new Headers(init.headers || {});
222
- retryHeaders.set("x-sitekit-version", chunkEOENN46G_js.SITE_KIT_VERSION);
222
+ retryHeaders.set("x-sitekit-version", chunk3X2YPJU5_js.SITE_KIT_VERSION);
223
223
  retryHeaders.set("x-api-key", refreshed);
224
224
  retryHeaders.delete("authorization");
225
225
  credential = refreshed;
@@ -240,7 +240,7 @@ function sonorBeacon(url, payload, apiKey) {
240
240
  keepalive: true,
241
241
  headers: {
242
242
  "Content-Type": "application/json",
243
- "x-sitekit-version": chunkEOENN46G_js.SITE_KIT_VERSION,
243
+ "x-sitekit-version": chunk3X2YPJU5_js.SITE_KIT_VERSION,
244
244
  ...credential ? { "x-api-key": credential } : {}
245
245
  },
246
246
  body
@@ -260,5 +260,5 @@ exports.configureSiteCredential = configureSiteCredential;
260
260
  exports.currentCredential = currentCredential;
261
261
  exports.sonorBeacon = sonorBeacon;
262
262
  exports.sonorFetch = sonorFetch;
263
- //# sourceMappingURL=chunk-M2EGCGIT.js.map
264
- //# sourceMappingURL=chunk-M2EGCGIT.js.map
263
+ //# sourceMappingURL=chunk-KPKZBIAQ.js.map
264
+ //# sourceMappingURL=chunk-KPKZBIAQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/siteToken.ts","../src/shared/fetchWithRetry.ts","../src/shared/sonorFetch.ts"],"names":["SITE_KIT_VERSION"],"mappings":"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","file":"chunk-M2EGCGIT.js","sourcesContent":["/**\n * @sonordev/site-kit shared — Client token manager\n *\n * SiteKitLayout (a server component) no longer ships the raw `sonor_` API key to\n * the browser. Instead it mints a short-lived, project-scoped token server-side\n * and injects THAT. This module is the browser-side lifecycle for that token:\n *\n * - holds the current token (seeded from the injected value)\n * - a static page's seed can be days old and already expired on first view, so\n * it transparently REFRESHES via the public endpoint before first use\n * - dedupes concurrent refreshes (one in-flight request, awaited by all)\n * - persists the freshest token in sessionStorage so navigations within a\n * session don't each re-refresh\n *\n * Everything here is a no-op passthrough when the injected credential is a raw\n * key (a 2.x kit, or the mint-failed fallback) — legacy behavior is unchanged.\n * Fully SSR-safe: on the server there is no refresh, just the seed.\n *\n * The token payload is NOT secret (only its HMAC is unforgeable), so decoding it\n * client-side to read `exp`/`pid` is fine and needs no crypto.\n */\nimport { SITE_KIT_VERSION } from './version'\n\nexport const SITE_TOKEN_PREFIX = 'st1.'\n\n/** Refresh this many ms before expiry, so a request never races the deadline. */\nconst REFRESH_SKEW_MS = 5 * 60_000\nconst REFRESH_TIMEOUT_MS = 6_000\nconst STORAGE_KEY = '__sonor_site_token__'\n\nexport function isSiteToken(credential?: string | null): boolean {\n return typeof credential === 'string' && credential.startsWith(SITE_TOKEN_PREFIX)\n}\n\ninterface DecodedClaims {\n pid?: string\n iat?: number\n exp?: number\n}\n\n/** Decode the (non-secret) token payload. Returns null if not a token / malformed. */\nexport function decodeSiteTokenClaims(token?: string | null): DecodedClaims | null {\n if (!isSiteToken(token)) return null\n const seg = (token as string).split('.')[1]\n if (!seg) return null\n try {\n // base64url → JSON. atob handles standard base64; restore url-safe chars.\n const b64 = seg.replace(/-/g, '+').replace(/_/g, '/')\n const json =\n typeof atob === 'function'\n ? atob(b64)\n : Buffer.from(seg, 'base64url').toString('utf8')\n const claims = JSON.parse(json)\n return typeof claims === 'object' && claims ? claims : null\n } catch {\n return null\n }\n}\n\n/**\n * Stable id for the auth circuit breaker. Tokens rotate, so keying the breaker\n * by the raw value would reset it every refresh — key by the project id encoded\n * in the token instead. Raw keys use a safe prefix.\n */\nexport function credentialBreakerId(credential?: string | null): string {\n if (!credential) return '(anonymous)'\n const claims = decodeSiteTokenClaims(credential)\n if (claims?.pid) return `pid:${claims.pid}`\n return credential.slice(0, 16)\n}\n\ninterface TokenState {\n /** The credential injected by the layout (token or raw key). */\n seed: string | undefined\n /** Whether we're in token mode (seed is a token). */\n isToken: boolean\n /** Base API URL for the refresh endpoint. */\n apiUrl: string\n /** The freshest known token (>= seed). Null in raw-key mode. */\n token: string | null\n /** In-flight refresh, deduped. */\n refreshing: Promise<string | null> | null\n}\n\nconst state: TokenState = {\n seed: undefined,\n isToken: false,\n apiUrl: 'https://api.sonor.io',\n token: null,\n refreshing: null,\n}\n\nfunction readStoredToken(): string | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n return typeof parsed?.token === 'string' ? parsed.token : null\n } catch {\n return null\n }\n}\n\nfunction storeToken(token: string): void {\n if (typeof window === 'undefined') return\n try {\n window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ token }))\n } catch {\n /* private mode / disabled storage — memory-only is fine */\n }\n try {\n ;(window as any).__SITE_KIT_API_KEY__ = token\n } catch {\n /* ignore */\n }\n}\n\nfunction tokenExp(token: string | null): number {\n const claims = decodeSiteTokenClaims(token)\n return typeof claims?.exp === 'number' ? claims.exp * 1000 : 0\n}\n\n/**\n * Configure the manager from the layout-injected values. Adopts a fresher token\n * from sessionStorage if a prior page in this session already refreshed.\n */\nexport function configureSiteCredential(opts: {\n credential?: string\n apiUrl?: string\n}): void {\n state.seed = opts.credential\n state.isToken = isSiteToken(opts.credential)\n if (opts.apiUrl) state.apiUrl = opts.apiUrl.replace(/\\/+$/, '')\n\n if (!state.isToken) {\n state.token = null\n return\n }\n\n // configure runs on every render — never downgrade. Keep the freshest of the\n // injected seed, a token stored by an earlier page this session, and whatever\n // an in-flight/prior refresh already put in memory.\n const prior = state.token\n let best = opts.credential ?? null\n const stored = readStoredToken()\n if (stored && tokenExp(stored) > tokenExp(best)) best = stored\n if (prior && tokenExp(prior) > tokenExp(best)) best = prior\n state.token = best\n}\n\nfunction needsRefresh(): boolean {\n if (!state.isToken) return false\n if (!state.token) return true\n return Date.now() >= tokenExp(state.token) - REFRESH_SKEW_MS\n}\n\nasync function doRefresh(): Promise<string | null> {\n const current = state.token\n if (!current) return null\n try {\n const res = await fetch(`${state.apiUrl}/api/public/site-token/refresh`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n // Send the current token as x-api-key too, so the server's key-scoped\n // throttler buckets refreshes by token rather than by shared CDN IP.\n 'x-api-key': current,\n },\n body: JSON.stringify({ token: current }),\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(REFRESH_TIMEOUT_MS)\n : undefined,\n })\n if (!res.ok) return null\n const data = await res.json().catch(() => null)\n const next = data?.token\n if (typeof next === 'string' && isSiteToken(next)) {\n state.token = next\n storeToken(next)\n return next\n }\n return null\n } catch {\n return null\n }\n}\n\n/** Refresh now (deduped). Returns the new token, or null on failure. */\nexport function refreshSiteToken(): Promise<string | null> {\n if (!state.isToken) return Promise.resolve(null)\n if (state.refreshing) return state.refreshing\n state.refreshing = doRefresh().finally(() => {\n state.refreshing = null\n })\n return state.refreshing\n}\n\n/**\n * The credential to put on the next request. In token mode, refreshes a stale\n * seed first. In raw-key mode, returns the seed unchanged (legacy passthrough).\n */\nexport async function getFreshCredential(\n seed?: string,\n): Promise<string | undefined> {\n // A raw key (or nothing configured) — no token machinery.\n if (!state.isToken) return seed ?? state.seed\n if (needsRefresh()) {\n const refreshed = await refreshSiteToken()\n if (refreshed) return refreshed\n // Refresh failed — fall through to whatever we have; the request may 401 and\n // trip the breaker with friendly copy.\n }\n return state.token ?? seed ?? state.seed\n}\n\n/** Whether the given credential is being managed as a token. */\nexport function isTokenMode(): boolean {\n return state.isToken\n}\n\n/**\n * Best-known credential WITHOUT refreshing — for synchronous unload paths\n * (sonorBeacon) that can't await. In token mode returns the freshest token held;\n * in raw-key mode returns the seed.\n */\nexport function currentCredential(seed?: string): string | undefined {\n if (!state.isToken) return seed ?? state.seed\n return state.token ?? seed ?? state.seed\n}\n","/**\n * @sonordev/site-kit shared - Resilient fetch\n *\n * Single source of truth for client-side resilient fetch (forms, SitemapSync).\n * Adds a per-attempt timeout and retries transient failures (network errors,\n * request timeouts, HTTP 429, and 5xx) with exponential backoff.\n *\n * A form submission must survive a brief API blip — a deploy, a cold start, a\n * dropped packet. A single un-retried fetch turning into \"Something went wrong\"\n * is how leads get lost. The explicit abort also matters on its own: a request\n * left to hang until the browser's own network timeout logs a console error\n * (net::ERR_TIMED_OUT) that fails Lighthouse best-practices; an aborted fetch\n * logs nothing.\n */\n\nexport interface FetchRetryOptions {\n /** Number of retries AFTER the first attempt (default 2 → up to 3 tries total). */\n retries?: number\n /** Per-attempt timeout in milliseconds (default 15000). */\n timeoutMs?: number\n}\n\nfunction backoffDelay(attempt: number, response?: Response): number {\n // Honor a Retry-After header on 429 responses when present.\n if (response?.status === 429) {\n const header = response.headers.get('retry-after')\n const seconds = header ? parseInt(header, 10) : NaN\n if (!Number.isNaN(seconds) && seconds > 0) return Math.min(seconds * 1000, 10000)\n }\n // Exponential backoff: 500ms, 1s, 2s, ... capped at 8s.\n return Math.min(8000, 500 * 2 ** attempt)\n}\n\n/**\n * fetch() with a timeout and automatic retries for transient failures.\n *\n * Non-transient responses (2xx, 3xx, and 4xx other than 429) are returned to\n * the caller as-is — only 429/5xx and thrown network/timeout errors are retried.\n * If every attempt fails with a thrown error, the last error is re-thrown.\n */\nexport async function fetchWithRetry(\n url: string,\n init: RequestInit,\n options: FetchRetryOptions = {},\n): Promise<Response> {\n const retries = options.retries ?? 2\n const timeoutMs = options.timeoutMs ?? 15000\n let lastError: unknown\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const response = await fetch(url, { ...init, signal: controller.signal })\n clearTimeout(timer)\n\n if ((response.status === 429 || response.status >= 500) && attempt < retries) {\n await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt, response)))\n continue\n }\n return response\n } catch (error) {\n clearTimeout(timer)\n lastError = error\n // Network failure or timeout (AbortController) — retry while attempts remain.\n if (attempt < retries) {\n await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt)))\n continue\n }\n }\n }\n\n throw lastError instanceof Error\n ? lastError\n : new Error('Network request failed after multiple attempts. Please try again.')\n}\n","/**\n * @sonordev/site-kit shared - Canonical client transport\n *\n * Single source of truth for how site-kit talks to Sonor from the browser.\n * Every client-side module (analytics, forms, commerce, engage, maps, images,\n * SitemapSync) routes through here instead of bare fetch(), which gives the\n * whole kit, in one place:\n *\n * - per-attempt timeout + retry with backoff (via fetchWithRetry) — a hung\n * request left to the browser's own network timeout logs a console error\n * (net::ERR_TIMED_OUT) that fails Lighthouse best-practices\n * - the x-sitekit-version header on every request, so the platform can see\n * which kit version each site actually runs from live traffic\n * - CREDENTIAL LIFECYCLE: sites on 3.2+ ship a short-lived minted token instead\n * of the raw key. This transport resolves the freshest token (refreshing a\n * stale seed transparently), and on a 401/403 for a token it refreshes once\n * and retries before giving up. A raw key (2.x kit, or the mint-failed\n * fallback) is a pure passthrough — legacy behavior is unchanged.\n * - an auth circuit breaker: after a 401/403 the credential is paused for a few\n * minutes and ONE friendly console.info is emitted — a site on a stale or\n * canceled key must not spam the API or the visitor's console. Keyed by the\n * project (from the token) so it survives token rotation.\n * - sonorBeacon for unload-time telemetry that keeps the credential in headers;\n * navigator.sendBeacon can't set headers and forced the key into the URL,\n * where server and CDN access logs capture it\n *\n * Non-idempotent endpoints (checkout, payment intents, uploads) must pass\n * { retries: 0 } — they still get the timeout, but never an automatic retry.\n */\nimport { fetchWithRetry, type FetchRetryOptions } from './fetchWithRetry'\nimport { SITE_KIT_VERSION } from './version'\nimport {\n credentialBreakerId,\n currentCredential,\n getFreshCredential,\n isSiteToken,\n isTokenMode,\n refreshSiteToken,\n} from './siteToken'\n\nexport interface SonorFetchOptions extends FetchRetryOptions {\n /** Project credential — the minted token (3.2+) or raw key. Sent as x-api-key. */\n apiKey?: string\n}\n\n/** How long a credential stays paused after an auth failure. */\nconst AUTH_BREAK_MS = 5 * 60_000\n\n// breaker id → epoch ms until which requests are paused. Module-scoped: the\n// breaker lives for the page's lifetime and never applies on the server.\nconst pausedKeys = new Map<string, number>()\nlet warnedThisPage = false\n\n/** True when a previous 401/403 has paused this credential (browser only). */\nexport function isAuthPaused(credential?: string): boolean {\n if (typeof window === 'undefined') return false\n const id = credentialBreakerId(credential)\n const until = pausedKeys.get(id)\n if (!until) return false\n if (Date.now() >= until) {\n pausedKeys.delete(id)\n return false\n }\n return true\n}\n\nfunction tripBreaker(credential?: string): void {\n pausedKeys.set(credentialBreakerId(credential), Date.now() + AUTH_BREAK_MS)\n if (!warnedThisPage) {\n warnedThisPage = true\n // info, not error: a visitor's console must stay clean (Lighthouse\n // errors-in-console), and copy never names env vars or key formats.\n console.info(\n \"[Sonor] This site couldn't authenticate with Sonor, so data features are paused for a few minutes. If this keeps happening, the site is likely running an outdated API key.\",\n )\n }\n}\n\nexport async function sonorFetch(\n url: string,\n init: RequestInit = {},\n options: SonorFetchOptions = {},\n): Promise<Response> {\n // Resolve the credential to send. In token mode this may transparently refresh\n // a stale seed before the first request; in raw-key mode it's a passthrough.\n let credential = options.apiKey\n if (credential !== undefined || isTokenMode()) {\n credential = await getFreshCredential(options.apiKey)\n }\n\n const headers = new Headers(init.headers || {})\n headers.set('x-sitekit-version', SITE_KIT_VERSION)\n if (credential) {\n if (isTokenMode()) {\n // Token mode: `credential` is the freshest token (refreshed just above if\n // the seed was stale). It MUST override any x-api-key a caller hand-set — a\n // stale build-time seed passed as a header would otherwise pin the request\n // and defeat refresh entirely. Also drop a caller-set Authorization:\n // extractApiKey() server-side prefers `Authorization: Bearer` over\n // x-api-key, so a stale Bearer seed would win even once we set a fresh\n // x-api-key. This is the single place the client credential is chosen — do\n // not re-attach it as a header at call sites.\n headers.set('x-api-key', credential)\n headers.delete('authorization')\n } else if (!headers.has('x-api-key')) {\n // Raw-key mode: legacy passthrough — respect a caller-supplied header.\n headers.set('x-api-key', credential)\n }\n }\n\n if (isAuthPaused(credential)) {\n // Synthetic response keeps every caller's !response.ok path working\n // without a network round trip while the breaker is open.\n return new Response(null, { status: 401, statusText: 'sonor-auth-paused' })\n }\n\n let response = await fetchWithRetry(url, { ...init, headers }, options)\n\n // A token can rotate or expire mid-session. On the first auth failure, try ONE\n // refresh + retry before tripping the breaker — only for token credentials\n // (raw keys have nothing to refresh).\n if (\n (response.status === 401 || response.status === 403) &&\n typeof window !== 'undefined' &&\n isSiteToken(credential)\n ) {\n const refreshed = await refreshSiteToken()\n if (refreshed && refreshed !== credential) {\n const retryHeaders = new Headers(init.headers || {})\n retryHeaders.set('x-sitekit-version', SITE_KIT_VERSION)\n retryHeaders.set('x-api-key', refreshed)\n // Same reason as the initial request: a caller-set stale Bearer would win\n // over our fresh x-api-key server-side, so strip it on the retry too.\n retryHeaders.delete('authorization')\n credential = refreshed\n response = await fetchWithRetry(url, { ...init, headers: retryHeaders }, options)\n }\n }\n\n if (\n (response.status === 401 || response.status === 403) &&\n typeof window !== 'undefined' &&\n credential\n ) {\n tripBreaker(credential)\n }\n\n return response\n}\n\n/**\n * Fire-and-forget telemetry that survives page unload.\n *\n * Uses fetch({ keepalive: true }) so auth travels in headers. Falls back to\n * navigator.sendBeacon (no auth headers possible — the server may reject it,\n * but we never put the key in the URL) only when keepalive fetch is missing.\n *\n * Cannot await a refresh at unload time, so it sends the freshest token already\n * held (currentCredential) rather than the possibly-stale seed the caller passed.\n */\nexport function sonorBeacon(url: string, payload: unknown, apiKey?: string): void {\n const credential = currentCredential(apiKey)\n const body = typeof payload === 'string' ? payload : JSON.stringify(payload)\n try {\n void fetch(url, {\n method: 'POST',\n keepalive: true,\n headers: {\n 'Content-Type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n ...(credential ? { 'x-api-key': credential } : {}),\n },\n body,\n }).catch(() => {\n /* page is closing — best effort */\n })\n } catch {\n try {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }))\n }\n } catch {\n /* nothing left to try */\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/shared/siteToken.ts","../src/shared/fetchWithRetry.ts","../src/shared/sonorFetch.ts"],"names":["SITE_KIT_VERSION"],"mappings":"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","file":"chunk-KPKZBIAQ.js","sourcesContent":["/**\n * @sonordev/site-kit shared — Client token manager\n *\n * SiteKitLayout (a server component) no longer ships the raw `sonor_` API key to\n * the browser. Instead it mints a short-lived, project-scoped token server-side\n * and injects THAT. This module is the browser-side lifecycle for that token:\n *\n * - holds the current token (seeded from the injected value)\n * - a static page's seed can be days old and already expired on first view, so\n * it transparently REFRESHES via the public endpoint before first use\n * - dedupes concurrent refreshes (one in-flight request, awaited by all)\n * - persists the freshest token in sessionStorage so navigations within a\n * session don't each re-refresh\n *\n * Everything here is a no-op passthrough when the injected credential is a raw\n * key (a 2.x kit, or the mint-failed fallback) — legacy behavior is unchanged.\n * Fully SSR-safe: on the server there is no refresh, just the seed.\n *\n * The token payload is NOT secret (only its HMAC is unforgeable), so decoding it\n * client-side to read `exp`/`pid` is fine and needs no crypto.\n */\nimport { SITE_KIT_VERSION } from './version'\n\nexport const SITE_TOKEN_PREFIX = 'st1.'\n\n/** Refresh this many ms before expiry, so a request never races the deadline. */\nconst REFRESH_SKEW_MS = 5 * 60_000\nconst REFRESH_TIMEOUT_MS = 6_000\nconst STORAGE_KEY = '__sonor_site_token__'\n\nexport function isSiteToken(credential?: string | null): boolean {\n return typeof credential === 'string' && credential.startsWith(SITE_TOKEN_PREFIX)\n}\n\ninterface DecodedClaims {\n pid?: string\n iat?: number\n exp?: number\n}\n\n/** Decode the (non-secret) token payload. Returns null if not a token / malformed. */\nexport function decodeSiteTokenClaims(token?: string | null): DecodedClaims | null {\n if (!isSiteToken(token)) return null\n const seg = (token as string).split('.')[1]\n if (!seg) return null\n try {\n // base64url → JSON. atob handles standard base64; restore url-safe chars.\n const b64 = seg.replace(/-/g, '+').replace(/_/g, '/')\n const json =\n typeof atob === 'function'\n ? atob(b64)\n : Buffer.from(seg, 'base64url').toString('utf8')\n const claims = JSON.parse(json)\n return typeof claims === 'object' && claims ? claims : null\n } catch {\n return null\n }\n}\n\n/**\n * Stable id for the auth circuit breaker. Tokens rotate, so keying the breaker\n * by the raw value would reset it every refresh — key by the project id encoded\n * in the token instead. Raw keys use a safe prefix.\n */\nexport function credentialBreakerId(credential?: string | null): string {\n if (!credential) return '(anonymous)'\n const claims = decodeSiteTokenClaims(credential)\n if (claims?.pid) return `pid:${claims.pid}`\n return credential.slice(0, 16)\n}\n\ninterface TokenState {\n /** The credential injected by the layout (token or raw key). */\n seed: string | undefined\n /** Whether we're in token mode (seed is a token). */\n isToken: boolean\n /** Base API URL for the refresh endpoint. */\n apiUrl: string\n /** The freshest known token (>= seed). Null in raw-key mode. */\n token: string | null\n /** In-flight refresh, deduped. */\n refreshing: Promise<string | null> | null\n}\n\nconst state: TokenState = {\n seed: undefined,\n isToken: false,\n apiUrl: 'https://api.sonor.io',\n token: null,\n refreshing: null,\n}\n\nfunction readStoredToken(): string | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n return typeof parsed?.token === 'string' ? parsed.token : null\n } catch {\n return null\n }\n}\n\nfunction storeToken(token: string): void {\n if (typeof window === 'undefined') return\n try {\n window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ token }))\n } catch {\n /* private mode / disabled storage — memory-only is fine */\n }\n try {\n ;(window as any).__SITE_KIT_API_KEY__ = token\n } catch {\n /* ignore */\n }\n}\n\nfunction tokenExp(token: string | null): number {\n const claims = decodeSiteTokenClaims(token)\n return typeof claims?.exp === 'number' ? claims.exp * 1000 : 0\n}\n\n/**\n * Configure the manager from the layout-injected values. Adopts a fresher token\n * from sessionStorage if a prior page in this session already refreshed.\n */\nexport function configureSiteCredential(opts: {\n credential?: string\n apiUrl?: string\n}): void {\n state.seed = opts.credential\n state.isToken = isSiteToken(opts.credential)\n if (opts.apiUrl) state.apiUrl = opts.apiUrl.replace(/\\/+$/, '')\n\n if (!state.isToken) {\n state.token = null\n return\n }\n\n // configure runs on every render — never downgrade. Keep the freshest of the\n // injected seed, a token stored by an earlier page this session, and whatever\n // an in-flight/prior refresh already put in memory.\n const prior = state.token\n let best = opts.credential ?? null\n const stored = readStoredToken()\n if (stored && tokenExp(stored) > tokenExp(best)) best = stored\n if (prior && tokenExp(prior) > tokenExp(best)) best = prior\n state.token = best\n}\n\nfunction needsRefresh(): boolean {\n if (!state.isToken) return false\n if (!state.token) return true\n return Date.now() >= tokenExp(state.token) - REFRESH_SKEW_MS\n}\n\nasync function doRefresh(): Promise<string | null> {\n const current = state.token\n if (!current) return null\n try {\n const res = await fetch(`${state.apiUrl}/api/public/site-token/refresh`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n // Send the current token as x-api-key too, so the server's key-scoped\n // throttler buckets refreshes by token rather than by shared CDN IP.\n 'x-api-key': current,\n },\n body: JSON.stringify({ token: current }),\n signal:\n typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(REFRESH_TIMEOUT_MS)\n : undefined,\n })\n if (!res.ok) return null\n const data = await res.json().catch(() => null)\n const next = data?.token\n if (typeof next === 'string' && isSiteToken(next)) {\n state.token = next\n storeToken(next)\n return next\n }\n return null\n } catch {\n return null\n }\n}\n\n/** Refresh now (deduped). Returns the new token, or null on failure. */\nexport function refreshSiteToken(): Promise<string | null> {\n if (!state.isToken) return Promise.resolve(null)\n if (state.refreshing) return state.refreshing\n state.refreshing = doRefresh().finally(() => {\n state.refreshing = null\n })\n return state.refreshing\n}\n\n/**\n * The credential to put on the next request. In token mode, refreshes a stale\n * seed first. In raw-key mode, returns the seed unchanged (legacy passthrough).\n */\nexport async function getFreshCredential(\n seed?: string,\n): Promise<string | undefined> {\n // A raw key (or nothing configured) — no token machinery.\n if (!state.isToken) return seed ?? state.seed\n if (needsRefresh()) {\n const refreshed = await refreshSiteToken()\n if (refreshed) return refreshed\n // Refresh failed — fall through to whatever we have; the request may 401 and\n // trip the breaker with friendly copy.\n }\n return state.token ?? seed ?? state.seed\n}\n\n/** Whether the given credential is being managed as a token. */\nexport function isTokenMode(): boolean {\n return state.isToken\n}\n\n/**\n * Best-known credential WITHOUT refreshing — for synchronous unload paths\n * (sonorBeacon) that can't await. In token mode returns the freshest token held;\n * in raw-key mode returns the seed.\n */\nexport function currentCredential(seed?: string): string | undefined {\n if (!state.isToken) return seed ?? state.seed\n return state.token ?? seed ?? state.seed\n}\n","/**\n * @sonordev/site-kit shared - Resilient fetch\n *\n * Single source of truth for client-side resilient fetch (forms, SitemapSync).\n * Adds a per-attempt timeout and retries transient failures (network errors,\n * request timeouts, HTTP 429, and 5xx) with exponential backoff.\n *\n * A form submission must survive a brief API blip — a deploy, a cold start, a\n * dropped packet. A single un-retried fetch turning into \"Something went wrong\"\n * is how leads get lost. The explicit abort also matters on its own: a request\n * left to hang until the browser's own network timeout logs a console error\n * (net::ERR_TIMED_OUT) that fails Lighthouse best-practices; an aborted fetch\n * logs nothing.\n */\n\nexport interface FetchRetryOptions {\n /** Number of retries AFTER the first attempt (default 2 → up to 3 tries total). */\n retries?: number\n /** Per-attempt timeout in milliseconds (default 15000). */\n timeoutMs?: number\n}\n\nfunction backoffDelay(attempt: number, response?: Response): number {\n // Honor a Retry-After header on 429 responses when present.\n if (response?.status === 429) {\n const header = response.headers.get('retry-after')\n const seconds = header ? parseInt(header, 10) : NaN\n if (!Number.isNaN(seconds) && seconds > 0) return Math.min(seconds * 1000, 10000)\n }\n // Exponential backoff: 500ms, 1s, 2s, ... capped at 8s.\n return Math.min(8000, 500 * 2 ** attempt)\n}\n\n/**\n * fetch() with a timeout and automatic retries for transient failures.\n *\n * Non-transient responses (2xx, 3xx, and 4xx other than 429) are returned to\n * the caller as-is — only 429/5xx and thrown network/timeout errors are retried.\n * If every attempt fails with a thrown error, the last error is re-thrown.\n */\nexport async function fetchWithRetry(\n url: string,\n init: RequestInit,\n options: FetchRetryOptions = {},\n): Promise<Response> {\n const retries = options.retries ?? 2\n const timeoutMs = options.timeoutMs ?? 15000\n let lastError: unknown\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const response = await fetch(url, { ...init, signal: controller.signal })\n clearTimeout(timer)\n\n if ((response.status === 429 || response.status >= 500) && attempt < retries) {\n await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt, response)))\n continue\n }\n return response\n } catch (error) {\n clearTimeout(timer)\n lastError = error\n // Network failure or timeout (AbortController) — retry while attempts remain.\n if (attempt < retries) {\n await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt)))\n continue\n }\n }\n }\n\n throw lastError instanceof Error\n ? lastError\n : new Error('Network request failed after multiple attempts. Please try again.')\n}\n","/**\n * @sonordev/site-kit shared - Canonical client transport\n *\n * Single source of truth for how site-kit talks to Sonor from the browser.\n * Every client-side module (analytics, forms, commerce, engage, maps, images,\n * SitemapSync) routes through here instead of bare fetch(), which gives the\n * whole kit, in one place:\n *\n * - per-attempt timeout + retry with backoff (via fetchWithRetry) — a hung\n * request left to the browser's own network timeout logs a console error\n * (net::ERR_TIMED_OUT) that fails Lighthouse best-practices\n * - the x-sitekit-version header on every request, so the platform can see\n * which kit version each site actually runs from live traffic\n * - CREDENTIAL LIFECYCLE: sites on 3.2+ ship a short-lived minted token instead\n * of the raw key. This transport resolves the freshest token (refreshing a\n * stale seed transparently), and on a 401/403 for a token it refreshes once\n * and retries before giving up. A raw key (2.x kit, or the mint-failed\n * fallback) is a pure passthrough — legacy behavior is unchanged.\n * - an auth circuit breaker: after a 401/403 the credential is paused for a few\n * minutes and ONE friendly console.info is emitted — a site on a stale or\n * canceled key must not spam the API or the visitor's console. Keyed by the\n * project (from the token) so it survives token rotation.\n * - sonorBeacon for unload-time telemetry that keeps the credential in headers;\n * navigator.sendBeacon can't set headers and forced the key into the URL,\n * where server and CDN access logs capture it\n *\n * Non-idempotent endpoints (checkout, payment intents, uploads) must pass\n * { retries: 0 } — they still get the timeout, but never an automatic retry.\n */\nimport { fetchWithRetry, type FetchRetryOptions } from './fetchWithRetry'\nimport { SITE_KIT_VERSION } from './version'\nimport {\n credentialBreakerId,\n currentCredential,\n getFreshCredential,\n isSiteToken,\n isTokenMode,\n refreshSiteToken,\n} from './siteToken'\n\nexport interface SonorFetchOptions extends FetchRetryOptions {\n /** Project credential — the minted token (3.2+) or raw key. Sent as x-api-key. */\n apiKey?: string\n}\n\n/** How long a credential stays paused after an auth failure. */\nconst AUTH_BREAK_MS = 5 * 60_000\n\n// breaker id → epoch ms until which requests are paused. Module-scoped: the\n// breaker lives for the page's lifetime and never applies on the server.\nconst pausedKeys = new Map<string, number>()\nlet warnedThisPage = false\n\n/** True when a previous 401/403 has paused this credential (browser only). */\nexport function isAuthPaused(credential?: string): boolean {\n if (typeof window === 'undefined') return false\n const id = credentialBreakerId(credential)\n const until = pausedKeys.get(id)\n if (!until) return false\n if (Date.now() >= until) {\n pausedKeys.delete(id)\n return false\n }\n return true\n}\n\nfunction tripBreaker(credential?: string): void {\n pausedKeys.set(credentialBreakerId(credential), Date.now() + AUTH_BREAK_MS)\n if (!warnedThisPage) {\n warnedThisPage = true\n // info, not error: a visitor's console must stay clean (Lighthouse\n // errors-in-console), and copy never names env vars or key formats.\n console.info(\n \"[Sonor] This site couldn't authenticate with Sonor, so data features are paused for a few minutes. If this keeps happening, the site is likely running an outdated API key.\",\n )\n }\n}\n\nexport async function sonorFetch(\n url: string,\n init: RequestInit = {},\n options: SonorFetchOptions = {},\n): Promise<Response> {\n // Resolve the credential to send. In token mode this may transparently refresh\n // a stale seed before the first request; in raw-key mode it's a passthrough.\n let credential = options.apiKey\n if (credential !== undefined || isTokenMode()) {\n credential = await getFreshCredential(options.apiKey)\n }\n\n const headers = new Headers(init.headers || {})\n headers.set('x-sitekit-version', SITE_KIT_VERSION)\n if (credential) {\n if (isTokenMode()) {\n // Token mode: `credential` is the freshest token (refreshed just above if\n // the seed was stale). It MUST override any x-api-key a caller hand-set — a\n // stale build-time seed passed as a header would otherwise pin the request\n // and defeat refresh entirely. Also drop a caller-set Authorization:\n // extractApiKey() server-side prefers `Authorization: Bearer` over\n // x-api-key, so a stale Bearer seed would win even once we set a fresh\n // x-api-key. This is the single place the client credential is chosen — do\n // not re-attach it as a header at call sites.\n headers.set('x-api-key', credential)\n headers.delete('authorization')\n } else if (!headers.has('x-api-key')) {\n // Raw-key mode: legacy passthrough — respect a caller-supplied header.\n headers.set('x-api-key', credential)\n }\n }\n\n if (isAuthPaused(credential)) {\n // Synthetic response keeps every caller's !response.ok path working\n // without a network round trip while the breaker is open.\n return new Response(null, { status: 401, statusText: 'sonor-auth-paused' })\n }\n\n let response = await fetchWithRetry(url, { ...init, headers }, options)\n\n // A token can rotate or expire mid-session. On the first auth failure, try ONE\n // refresh + retry before tripping the breaker — only for token credentials\n // (raw keys have nothing to refresh).\n if (\n (response.status === 401 || response.status === 403) &&\n typeof window !== 'undefined' &&\n isSiteToken(credential)\n ) {\n const refreshed = await refreshSiteToken()\n if (refreshed && refreshed !== credential) {\n const retryHeaders = new Headers(init.headers || {})\n retryHeaders.set('x-sitekit-version', SITE_KIT_VERSION)\n retryHeaders.set('x-api-key', refreshed)\n // Same reason as the initial request: a caller-set stale Bearer would win\n // over our fresh x-api-key server-side, so strip it on the retry too.\n retryHeaders.delete('authorization')\n credential = refreshed\n response = await fetchWithRetry(url, { ...init, headers: retryHeaders }, options)\n }\n }\n\n if (\n (response.status === 401 || response.status === 403) &&\n typeof window !== 'undefined' &&\n credential\n ) {\n tripBreaker(credential)\n }\n\n return response\n}\n\n/**\n * Fire-and-forget telemetry that survives page unload.\n *\n * Uses fetch({ keepalive: true }) so auth travels in headers. Falls back to\n * navigator.sendBeacon (no auth headers possible — the server may reject it,\n * but we never put the key in the URL) only when keepalive fetch is missing.\n *\n * Cannot await a refresh at unload time, so it sends the freshest token already\n * held (currentCredential) rather than the possibly-stale seed the caller passed.\n */\nexport function sonorBeacon(url: string, payload: unknown, apiKey?: string): void {\n const credential = currentCredential(apiKey)\n const body = typeof payload === 'string' ? payload : JSON.stringify(payload)\n try {\n void fetch(url, {\n method: 'POST',\n keepalive: true,\n headers: {\n 'Content-Type': 'application/json',\n 'x-sitekit-version': SITE_KIT_VERSION,\n ...(credential ? { 'x-api-key': credential } : {}),\n },\n body,\n }).catch(() => {\n /* page is closing — best effort */\n })\n } catch {\n try {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }))\n }\n } catch {\n /* nothing left to try */\n }\n }\n}\n"]}
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
9
+ var path__default = /*#__PURE__*/_interopDefault(path);
10
+
11
+ // src/sitemap/discover.ts
12
+ var APP_DIR_CANDIDATES = ["app", path__default.default.join("src", "app")];
13
+ var SITEMAP_ROUTE_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mjs"];
14
+ function findAppDirForDiscovery(cwd = process.cwd()) {
15
+ for (const candidate of APP_DIR_CANDIDATES) {
16
+ const abs = path__default.default.join(cwd, candidate);
17
+ if (fs__default.default.existsSync(abs)) return abs;
18
+ }
19
+ return null;
20
+ }
21
+ function findSitemapRoute(cwd = process.cwd()) {
22
+ for (const dir of APP_DIR_CANDIDATES) {
23
+ for (const ext of SITEMAP_ROUTE_EXTENSIONS) {
24
+ const rel = path__default.default.join(dir, `sitemap.${ext}`);
25
+ if (fs__default.default.existsSync(path__default.default.join(cwd, rel))) return rel;
26
+ }
27
+ }
28
+ return null;
29
+ }
30
+ function discoverNextJsRoutes(appDir, basePath = "") {
31
+ const entries = [];
32
+ const items = fs__default.default.readdirSync(appDir, { withFileTypes: true });
33
+ for (const item of items) {
34
+ if (item.name.startsWith("_") || item.name.startsWith(".")) continue;
35
+ if (item.name === "api") continue;
36
+ if (item.name === "node_modules") continue;
37
+ const itemPath = path__default.default.join(appDir, item.name);
38
+ if (item.isDirectory()) {
39
+ const hasPage = fs__default.default.existsSync(path__default.default.join(itemPath, "page.tsx")) || fs__default.default.existsSync(path__default.default.join(itemPath, "page.js")) || fs__default.default.existsSync(path__default.default.join(itemPath, "page.jsx"));
40
+ const isRouteGroup = item.name.startsWith("(") && item.name.endsWith(")");
41
+ const isDynamic = item.name.startsWith("[") && item.name.endsWith("]");
42
+ let routePath = basePath;
43
+ if (!isRouteGroup && !isDynamic) {
44
+ routePath = `${basePath}/${item.name}`;
45
+ }
46
+ if (hasPage && !isDynamic) {
47
+ const priority = routePath === "" ? 1 : 0.8;
48
+ entries.push({ path: routePath || "/", priority });
49
+ }
50
+ if (!isDynamic) {
51
+ const subEntries = discoverNextJsRoutes(
52
+ itemPath,
53
+ isRouteGroup ? basePath : routePath
54
+ );
55
+ entries.push(...subEntries);
56
+ }
57
+ }
58
+ }
59
+ if (basePath === "") {
60
+ const hasRootPage = fs__default.default.existsSync(path__default.default.join(appDir, "page.tsx")) || fs__default.default.existsSync(path__default.default.join(appDir, "page.js")) || fs__default.default.existsSync(path__default.default.join(appDir, "page.jsx"));
61
+ if (hasRootPage) {
62
+ entries.unshift({ path: "/", priority: 1 });
63
+ }
64
+ }
65
+ return entries;
66
+ }
67
+
68
+ exports.discoverNextJsRoutes = discoverNextJsRoutes;
69
+ exports.findAppDirForDiscovery = findAppDirForDiscovery;
70
+ exports.findSitemapRoute = findSitemapRoute;
71
+ //# sourceMappingURL=chunk-LOS5D6B3.js.map
72
+ //# sourceMappingURL=chunk-LOS5D6B3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sitemap/discover.ts"],"names":["path","fs"],"mappings":"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","file":"chunk-LOS5D6B3.js","sourcesContent":["/**\n * @sonordev/site-kit — Node-only filesystem discovery for the sitemap CLI\n *\n * Single source of truth for the build-time filesystem scan used by\n * `registerLocalSitemap` (both the standalone CLI impl and the server routing\n * helper used to keep their own byte-drifting copies of `discoverNextJsRoutes`).\n *\n * Plus `findSitemapRoute` — the guard that makes `--auto-discover` safe to leave\n * in any site's `postbuild`.\n *\n * IMPORTANT — why a filesystem scan must never drive a `full-replace`:\n * a scan of `app/` can only see STATIC route segments. It structurally cannot\n * enumerate the params of a dynamic route (`/work/[slug]`, `/blog/[cat]/[id]`),\n * and it ignores the app's sitemap `exclude` config. So the route set it\n * produces is always a strict, incomplete SUBSET of the real page set. Feeding\n * that incomplete set to a `full-replace` sync tells the API to DELETE every\n * page that isn't in it — i.e. every dynamic page, plus anything the app\n * intentionally excluded. When a site also has an `app/sitemap.{ts,js}` (which\n * `createSitemap` syncs as an authoritative full-replace), the two syncs delete\n * and recreate each other's rows on every build (the sitemap-oscillation bug),\n * destroying managed metadata, Signal optimizations, LLM schemas, and GSC\n * history on real pages. Hence: auto-discover is additive by default, and skips\n * entirely when a sitemap route already exists.\n *\n * Node built-ins (`fs`/`path`) only — never import this from browser/client code.\n */\n\nimport fs from 'fs'\nimport path from 'path'\n\n/** App-directory candidates, in resolution order (Next.js `app/` or `src/app/`). */\nconst APP_DIR_CANDIDATES = ['app', path.join('src', 'app')]\n\n/** Extensions a Next.js `sitemap` route file can use. */\nconst SITEMAP_ROUTE_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx', 'mjs']\n\n/**\n * Return the app directory (absolute path) that exists under `cwd`, or null.\n * Prefers `app/`, then `src/app/`.\n */\nexport function findAppDirForDiscovery(cwd: string = process.cwd()): string | null {\n for (const candidate of APP_DIR_CANDIDATES) {\n const abs = path.join(cwd, candidate)\n if (fs.existsSync(abs)) return abs\n }\n return null\n}\n\n/**\n * Detect a Next.js `sitemap` route file (`app/sitemap.{ts,tsx,js,jsx,mjs}` or\n * `src/app/sitemap.*`). Returns the project-relative path if found, else null.\n *\n * Presence of this file means the framework already emits `/sitemap.xml` — the\n * authoritative page set — and site-kit's runtime `SitemapSync` syncs that real\n * sitemap (dynamic routes and all) additively. In that world an `--auto-discover`\n * filesystem scan is both inferior and dangerous (see the module header), so the\n * CLI skips.\n */\nexport function findSitemapRoute(cwd: string = process.cwd()): string | null {\n for (const dir of APP_DIR_CANDIDATES) {\n for (const ext of SITEMAP_ROUTE_EXTENSIONS) {\n const rel = path.join(dir, `sitemap.${ext}`)\n if (fs.existsSync(path.join(cwd, rel))) return rel\n }\n }\n return null\n}\n\n/**\n * Recursively discover STATIC Next.js routes from an app directory.\n *\n * Intentionally skips dynamic segments (`[slug]`) — they can't be enumerated\n * from disk — which is exactly why the result is incomplete and must only ever\n * drive an additive sync. Route groups `(marketing)` are folded out of the URL.\n */\nexport function discoverNextJsRoutes(\n appDir: string,\n basePath: string = '',\n): Array<{ path: string; priority: number }> {\n const entries: Array<{ path: string; priority: number }> = []\n\n const items = fs.readdirSync(appDir, { withFileTypes: true })\n\n for (const item of items) {\n if (item.name.startsWith('_') || item.name.startsWith('.')) continue\n if (item.name === 'api') continue\n if (item.name === 'node_modules') continue\n\n const itemPath = path.join(appDir, item.name)\n\n if (item.isDirectory()) {\n const hasPage =\n fs.existsSync(path.join(itemPath, 'page.tsx')) ||\n fs.existsSync(path.join(itemPath, 'page.js')) ||\n fs.existsSync(path.join(itemPath, 'page.jsx'))\n\n const isRouteGroup = item.name.startsWith('(') && item.name.endsWith(')')\n const isDynamic = item.name.startsWith('[') && item.name.endsWith(']')\n\n let routePath = basePath\n if (!isRouteGroup && !isDynamic) {\n routePath = `${basePath}/${item.name}`\n }\n\n if (hasPage && !isDynamic) {\n const priority = routePath === '' ? 1.0 : 0.8\n entries.push({ path: routePath || '/', priority })\n }\n\n if (!isDynamic) {\n const subEntries = discoverNextJsRoutes(\n itemPath,\n isRouteGroup ? basePath : routePath,\n )\n entries.push(...subEntries)\n }\n }\n }\n\n if (basePath === '') {\n const hasRootPage =\n fs.existsSync(path.join(appDir, 'page.tsx')) ||\n fs.existsSync(path.join(appDir, 'page.js')) ||\n fs.existsSync(path.join(appDir, 'page.jsx'))\n if (hasRootPage) {\n entries.unshift({ path: '/', priority: 1.0 })\n }\n }\n\n return entries\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { sonorFetch } from './chunk-FO6Y27AH.mjs';
2
+ import { sonorFetch } from './chunk-GUU3AEFM.mjs';
3
3
  import { useState, useCallback, useEffect } from 'react';
4
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
5
 
@@ -521,5 +521,5 @@ async function clearImageSlot(config, slotId, pagePath) {
521
521
  }
522
522
 
523
523
  export { ManagedImage, assignImageToSlot, clearImageSlot, fetchManagedImage, fetchManagedImages, listImageFiles, uploadImage };
524
- //# sourceMappingURL=chunk-6BDXN4X3.mjs.map
525
- //# sourceMappingURL=chunk-6BDXN4X3.mjs.map
524
+ //# sourceMappingURL=chunk-MLD3EGM2.mjs.map
525
+ //# sourceMappingURL=chunk-MLD3EGM2.mjs.map