@sonordev/site-kit 6.3.0 → 6.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 (198) hide show
  1. package/agent-manifest.json +1 -1
  2. package/dist/{AnalyticsProvider-JKWJGIH2.js → AnalyticsProvider-G46LCH3V.js} +10 -10
  3. package/dist/{AnalyticsProvider-JKWJGIH2.js.map → AnalyticsProvider-G46LCH3V.js.map} +1 -1
  4. package/dist/{AnalyticsProvider-T4UBUUV4.mjs → AnalyticsProvider-IYGKHMVM.mjs} +6 -6
  5. package/dist/{AnalyticsProvider-T4UBUUV4.mjs.map → AnalyticsProvider-IYGKHMVM.mjs.map} +1 -1
  6. package/dist/{ArticleViewTracker-F5IP734Z.js → ArticleViewTracker-3USIHB4O.js} +6 -6
  7. package/dist/{ArticleViewTracker-F5IP734Z.js.map → ArticleViewTracker-3USIHB4O.js.map} +1 -1
  8. package/dist/{ArticleViewTracker-AYYSXNJS.mjs → ArticleViewTracker-7GEI37VK.mjs} +5 -5
  9. package/dist/{ArticleViewTracker-AYYSXNJS.mjs.map → ArticleViewTracker-7GEI37VK.mjs.map} +1 -1
  10. package/dist/ChatWidget-36IDUB53.mjs +17 -0
  11. package/dist/{ChatWidget-3RVBHZCS.mjs.map → ChatWidget-36IDUB53.mjs.map} +1 -1
  12. package/dist/{ChatWidget-RFSI2XNU.js → ChatWidget-VP3QQJA6.js} +11 -11
  13. package/dist/{ChatWidget-RFSI2XNU.js.map → ChatWidget-VP3QQJA6.js.map} +1 -1
  14. package/dist/EngageWidget-ILRTLGBB.mjs +12 -0
  15. package/dist/{EngageWidget-TE6S5KYT.mjs.map → EngageWidget-ILRTLGBB.mjs.map} +1 -1
  16. package/dist/EngageWidget-YHMSB4YE.js +21 -0
  17. package/dist/{EngageWidget-QO5S6YV5.js.map → EngageWidget-YHMSB4YE.js.map} +1 -1
  18. package/dist/{FileField-OXZXB3UW.mjs → FileField-DXGPA4OB.mjs} +5 -5
  19. package/dist/{FileField-OXZXB3UW.mjs.map → FileField-DXGPA4OB.mjs.map} +1 -1
  20. package/dist/{FileField-JQSRKWXW.js → FileField-LMSLNNRX.js} +6 -6
  21. package/dist/{FileField-JQSRKWXW.js.map → FileField-LMSLNNRX.js.map} +1 -1
  22. package/dist/{FormSpotlight-H2A3JIFI.js → FormSpotlight-5BQCAXIO.js} +9 -9
  23. package/dist/{FormSpotlight-H2A3JIFI.js.map → FormSpotlight-5BQCAXIO.js.map} +1 -1
  24. package/dist/{FormSpotlight-PZ5RLZRV.mjs → FormSpotlight-SEJ4NJHG.mjs} +3 -3
  25. package/dist/{FormSpotlight-PZ5RLZRV.mjs.map → FormSpotlight-SEJ4NJHG.mjs.map} +1 -1
  26. package/dist/{FormStage-CIHL7OBA.mjs → FormStage-6OLDPLYU.mjs} +3 -3
  27. package/dist/{FormStage-CIHL7OBA.mjs.map → FormStage-6OLDPLYU.mjs.map} +1 -1
  28. package/dist/{FormStage-ZA2FABYQ.js → FormStage-ZRXENSYN.js} +9 -9
  29. package/dist/{FormStage-ZA2FABYQ.js.map → FormStage-ZRXENSYN.js.map} +1 -1
  30. package/dist/ManagedForm-AIPXF5QY.mjs +16 -0
  31. package/dist/{ManagedForm-PTCWSTC5.mjs.map → ManagedForm-AIPXF5QY.mjs.map} +1 -1
  32. package/dist/ManagedForm-T464AA5B.js +25 -0
  33. package/dist/{ManagedForm-BVLS2QZR.js.map → ManagedForm-T464AA5B.js.map} +1 -1
  34. package/dist/{ManagedNewsletterForm-IB5VD43M.js → ManagedNewsletterForm-HKNXIKDV.js} +8 -8
  35. package/dist/{ManagedNewsletterForm-IB5VD43M.js.map → ManagedNewsletterForm-HKNXIKDV.js.map} +1 -1
  36. package/dist/{ManagedNewsletterForm-WMCRFDL7.mjs → ManagedNewsletterForm-UGDUBYR3.mjs} +7 -7
  37. package/dist/{ManagedNewsletterForm-WMCRFDL7.mjs.map → ManagedNewsletterForm-UGDUBYR3.mjs.map} +1 -1
  38. package/dist/{SignalCore-R44AWPYN.mjs → SignalCore-O3GEJZCN.mjs} +5 -5
  39. package/dist/{SignalCore-R44AWPYN.mjs.map → SignalCore-O3GEJZCN.mjs.map} +1 -1
  40. package/dist/{SignalCore-Q57NY7EG.js → SignalCore-OOQHKIUZ.js} +11 -11
  41. package/dist/{SignalCore-Q57NY7EG.js.map → SignalCore-OOQHKIUZ.js.map} +1 -1
  42. package/dist/SitemapSync-MT3I4W3B.js +18 -0
  43. package/dist/{SitemapSync-ZP4PKZZ3.js.map → SitemapSync-MT3I4W3B.js.map} +1 -1
  44. package/dist/SitemapSync-RG554AJR.mjs +9 -0
  45. package/dist/{SitemapSync-NICUUMQF.mjs.map → SitemapSync-RG554AJR.mjs.map} +1 -1
  46. package/dist/admin-auth/index.d.ts +8 -0
  47. package/dist/admin-auth/index.d.ts.map +1 -1
  48. package/dist/admin-auth/index.js +0 -0
  49. package/dist/admin-auth/index.js.map +1 -1
  50. package/dist/admin-auth/index.mjs +0 -0
  51. package/dist/admin-auth/index.mjs.map +1 -1
  52. package/dist/analytics/index.js +9 -9
  53. package/dist/analytics/index.mjs +4 -4
  54. package/dist/articles/index.js +1 -1
  55. package/dist/articles/index.mjs +1 -1
  56. package/dist/articles/server-ui.js +1 -1
  57. package/dist/articles/server-ui.mjs +1 -1
  58. package/dist/{chunk-72WO3AE5.js → chunk-2LX7LHAO.js} +5 -5
  59. package/dist/{chunk-72WO3AE5.js.map → chunk-2LX7LHAO.js.map} +1 -1
  60. package/dist/{chunk-YZCRYK5M.mjs → chunk-3EOUKWHB.mjs} +3 -3
  61. package/dist/{chunk-YZCRYK5M.mjs.map → chunk-3EOUKWHB.mjs.map} +1 -1
  62. package/dist/{chunk-IHG7W5JH.mjs → chunk-4EXBEFIV.mjs} +4 -4
  63. package/dist/{chunk-IHG7W5JH.mjs.map → chunk-4EXBEFIV.mjs.map} +1 -1
  64. package/dist/{chunk-6SH6HLWS.mjs → chunk-4HSZPHF7.mjs} +3 -3
  65. package/dist/{chunk-6SH6HLWS.mjs.map → chunk-4HSZPHF7.mjs.map} +1 -1
  66. package/dist/{chunk-I5XK32OJ.js → chunk-4KSE642V.js} +18 -18
  67. package/dist/{chunk-I5XK32OJ.js.map → chunk-4KSE642V.js.map} +1 -1
  68. package/dist/{chunk-F4DZDIR4.js → chunk-6FASIYCV.js} +4 -4
  69. package/dist/{chunk-F4DZDIR4.js.map → chunk-6FASIYCV.js.map} +1 -1
  70. package/dist/{chunk-4BQQGOHC.js → chunk-6TSYWYYR.js} +4 -4
  71. package/dist/{chunk-4BQQGOHC.js.map → chunk-6TSYWYYR.js.map} +1 -1
  72. package/dist/{chunk-OZLYJSFE.js → chunk-6WWGH6E3.js} +14 -14
  73. package/dist/{chunk-OZLYJSFE.js.map → chunk-6WWGH6E3.js.map} +1 -1
  74. package/dist/{chunk-BSTV3T6P.mjs → chunk-7YFUX2CL.mjs} +3 -3
  75. package/dist/{chunk-BSTV3T6P.mjs.map → chunk-7YFUX2CL.mjs.map} +1 -1
  76. package/dist/{chunk-6AIXHYFO.js → chunk-A3QAZJXC.js} +5 -5
  77. package/dist/{chunk-6AIXHYFO.js.map → chunk-A3QAZJXC.js.map} +1 -1
  78. package/dist/{chunk-2JZFTPPE.js → chunk-A4WYSHIX.js} +3 -3
  79. package/dist/{chunk-2JZFTPPE.js.map → chunk-A4WYSHIX.js.map} +1 -1
  80. package/dist/{chunk-REGHZJQF.mjs → chunk-A5R62CXI.mjs} +3 -3
  81. package/dist/{chunk-REGHZJQF.mjs.map → chunk-A5R62CXI.mjs.map} +1 -1
  82. package/dist/chunk-C654VCWV.mjs +6 -0
  83. package/dist/{chunk-COCYQHK3.mjs.map → chunk-C654VCWV.mjs.map} +1 -1
  84. package/dist/{chunk-KLKUI3US.mjs → chunk-DUFU27R2.mjs} +3 -3
  85. package/dist/{chunk-KLKUI3US.mjs.map → chunk-DUFU27R2.mjs.map} +1 -1
  86. package/dist/{chunk-ZKM6ELMZ.js → chunk-FVATDBFX.js} +10 -10
  87. package/dist/{chunk-ZKM6ELMZ.js.map → chunk-FVATDBFX.js.map} +1 -1
  88. package/dist/{chunk-FFE6P6PQ.js → chunk-GE3CGR2T.js} +14 -14
  89. package/dist/{chunk-FFE6P6PQ.js.map → chunk-GE3CGR2T.js.map} +1 -1
  90. package/dist/{chunk-QUMXVJ2C.mjs → chunk-HIG7COWF.mjs} +3 -3
  91. package/dist/{chunk-QUMXVJ2C.mjs.map → chunk-HIG7COWF.mjs.map} +1 -1
  92. package/dist/{chunk-OMYR2ORG.js → chunk-HY6EXWLZ.js} +8 -8
  93. package/dist/{chunk-OMYR2ORG.js.map → chunk-HY6EXWLZ.js.map} +1 -1
  94. package/dist/{chunk-NCURAYKL.mjs → chunk-IDBA3YZ6.mjs} +4 -4
  95. package/dist/{chunk-NCURAYKL.mjs.map → chunk-IDBA3YZ6.mjs.map} +1 -1
  96. package/dist/{chunk-C3MRX7NL.js → chunk-JPXT5LY2.js} +12 -12
  97. package/dist/{chunk-C3MRX7NL.js.map → chunk-JPXT5LY2.js.map} +1 -1
  98. package/dist/{chunk-P2VWGMQU.mjs → chunk-JS265UKZ.mjs} +3 -3
  99. package/dist/{chunk-P2VWGMQU.mjs.map → chunk-JS265UKZ.mjs.map} +1 -1
  100. package/dist/{chunk-DFAW62AX.mjs → chunk-KAJT3MOM.mjs} +4 -4
  101. package/dist/{chunk-DFAW62AX.mjs.map → chunk-KAJT3MOM.mjs.map} +1 -1
  102. package/dist/{chunk-22MXW53O.mjs → chunk-KUDSEKV7.mjs} +4 -4
  103. package/dist/{chunk-22MXW53O.mjs.map → chunk-KUDSEKV7.mjs.map} +1 -1
  104. package/dist/{chunk-DN6BLVU7.mjs → chunk-MWIIHMYS.mjs} +3 -3
  105. package/dist/{chunk-DN6BLVU7.mjs.map → chunk-MWIIHMYS.mjs.map} +1 -1
  106. package/dist/{chunk-PXN7LIYZ.js → chunk-NKBPEZCX.js} +15 -15
  107. package/dist/{chunk-PXN7LIYZ.js.map → chunk-NKBPEZCX.js.map} +1 -1
  108. package/dist/{chunk-HQCWWQAI.mjs → chunk-NO4JQ2G3.mjs} +7 -7
  109. package/dist/{chunk-HQCWWQAI.mjs.map → chunk-NO4JQ2G3.mjs.map} +1 -1
  110. package/dist/{chunk-QSCE66KQ.mjs → chunk-OUM77VZ4.mjs} +3 -3
  111. package/dist/{chunk-QSCE66KQ.mjs.map → chunk-OUM77VZ4.mjs.map} +1 -1
  112. package/dist/{chunk-GBOSAWBR.js → chunk-QRL3QDUE.js} +4 -4
  113. package/dist/{chunk-GBOSAWBR.js.map → chunk-QRL3QDUE.js.map} +1 -1
  114. package/dist/{chunk-LIWPIG7U.js → chunk-QRN6HQOX.js} +15 -15
  115. package/dist/{chunk-LIWPIG7U.js.map → chunk-QRN6HQOX.js.map} +1 -1
  116. package/dist/{chunk-M436JMD4.mjs → chunk-RH46WZRN.mjs} +3 -3
  117. package/dist/{chunk-M436JMD4.mjs.map → chunk-RH46WZRN.mjs.map} +1 -1
  118. package/dist/{chunk-P35JIP3J.js → chunk-RZUUPX2H.js} +6 -6
  119. package/dist/{chunk-P35JIP3J.js.map → chunk-RZUUPX2H.js.map} +1 -1
  120. package/dist/{chunk-XRUKYQ43.js → chunk-S4DTX5E7.js} +4 -4
  121. package/dist/{chunk-XRUKYQ43.js.map → chunk-S4DTX5E7.js.map} +1 -1
  122. package/dist/{chunk-RDKICSVU.js → chunk-S7S6ZZ3R.js} +7 -7
  123. package/dist/{chunk-RDKICSVU.js.map → chunk-S7S6ZZ3R.js.map} +1 -1
  124. package/dist/{chunk-5XLPWHQC.mjs → chunk-TDJZFBHI.mjs} +4 -4
  125. package/dist/{chunk-5XLPWHQC.mjs.map → chunk-TDJZFBHI.mjs.map} +1 -1
  126. package/dist/{chunk-6AAT724D.js → chunk-U274XAMD.js} +11 -11
  127. package/dist/{chunk-6AAT724D.js.map → chunk-U274XAMD.js.map} +1 -1
  128. package/dist/{chunk-UJFRMHUN.js → chunk-UFG2TUBG.js} +3 -3
  129. package/dist/{chunk-UJFRMHUN.js.map → chunk-UFG2TUBG.js.map} +1 -1
  130. package/dist/{chunk-RNEPZD2I.js → chunk-W4NTPOBD.js} +5 -5
  131. package/dist/{chunk-RNEPZD2I.js.map → chunk-W4NTPOBD.js.map} +1 -1
  132. package/dist/{chunk-CBZJNMYS.mjs → chunk-W5MHX6SO.mjs} +3 -3
  133. package/dist/{chunk-CBZJNMYS.mjs.map → chunk-W5MHX6SO.mjs.map} +1 -1
  134. package/dist/{chunk-X5WJ5XPZ.mjs → chunk-WIRGZMM5.mjs} +3 -3
  135. package/dist/{chunk-X5WJ5XPZ.mjs.map → chunk-WIRGZMM5.mjs.map} +1 -1
  136. package/dist/{chunk-RSP6RDR2.mjs → chunk-XNZ2WFXR.mjs} +4 -4
  137. package/dist/{chunk-RSP6RDR2.mjs.map → chunk-XNZ2WFXR.mjs.map} +1 -1
  138. package/dist/{chunk-W7HSYABM.mjs → chunk-XV2FFSJU.mjs} +8 -8
  139. package/dist/{chunk-W7HSYABM.mjs.map → chunk-XV2FFSJU.mjs.map} +1 -1
  140. package/dist/{chunk-L6XXUNJB.js → chunk-YCE6HRWW.js} +8 -8
  141. package/dist/{chunk-L6XXUNJB.js.map → chunk-YCE6HRWW.js.map} +1 -1
  142. package/dist/{chunk-ED5GOAF6.mjs → chunk-YOS4A3QE.mjs} +3 -3
  143. package/dist/{chunk-ED5GOAF6.mjs.map → chunk-YOS4A3QE.mjs.map} +1 -1
  144. package/dist/{chunk-RB53MKRX.mjs → chunk-YUCKBIRE.mjs} +4 -4
  145. package/dist/{chunk-RB53MKRX.mjs.map → chunk-YUCKBIRE.mjs.map} +1 -1
  146. package/dist/{chunk-JCBENABK.js → chunk-YYOGUTLV.js} +3 -3
  147. package/dist/{chunk-JCBENABK.js.map → chunk-YYOGUTLV.js.map} +1 -1
  148. package/dist/chunk-ZKOQCKOR.js +8 -0
  149. package/dist/{chunk-JLIL5EFG.js.map → chunk-ZKOQCKOR.js.map} +1 -1
  150. package/dist/cli/index.js +3 -3
  151. package/dist/cli/index.mjs +1 -1
  152. package/dist/client/index.js +4 -4
  153. package/dist/client/index.mjs +3 -3
  154. package/dist/commerce/index.js +57 -57
  155. package/dist/commerce/index.mjs +5 -5
  156. package/dist/engage/index.js +10 -10
  157. package/dist/engage/index.mjs +7 -7
  158. package/dist/fleet/index.js +8 -8
  159. package/dist/fleet/index.mjs +4 -4
  160. package/dist/forms/index.js +16 -16
  161. package/dist/forms/index.mjs +9 -9
  162. package/dist/forms/server.js +10 -10
  163. package/dist/forms/server.mjs +8 -8
  164. package/dist/forms/static.js +7 -7
  165. package/dist/forms/static.mjs +6 -6
  166. package/dist/images/index.js +11 -11
  167. package/dist/images/index.mjs +4 -4
  168. package/dist/index.js +63 -63
  169. package/dist/index.mjs +9 -9
  170. package/dist/layout/client.js +8 -8
  171. package/dist/layout/client.mjs +7 -7
  172. package/dist/layout/index.js +10 -10
  173. package/dist/layout/index.mjs +8 -8
  174. package/dist/maps/index.js +5 -5
  175. package/dist/maps/index.mjs +3 -3
  176. package/dist/search/index.js +4 -4
  177. package/dist/search/index.mjs +3 -3
  178. package/dist/seo/client.js +5 -5
  179. package/dist/seo/client.mjs +4 -4
  180. package/dist/seo/index.js +5 -5
  181. package/dist/seo/index.mjs +4 -4
  182. package/dist/server/index.js +3 -3
  183. package/dist/server/index.mjs +2 -2
  184. package/dist/shared/version.d.ts +1 -1
  185. package/dist/signal/index.js +11 -11
  186. package/dist/signal/index.mjs +2 -2
  187. package/dist/sync/index.js +17 -17
  188. package/dist/sync/index.mjs +4 -4
  189. package/package.json +1 -1
  190. package/dist/ChatWidget-3RVBHZCS.mjs +0 -17
  191. package/dist/EngageWidget-QO5S6YV5.js +0 -21
  192. package/dist/EngageWidget-TE6S5KYT.mjs +0 -12
  193. package/dist/ManagedForm-BVLS2QZR.js +0 -25
  194. package/dist/ManagedForm-PTCWSTC5.mjs +0 -16
  195. package/dist/SitemapSync-NICUUMQF.mjs +0 -9
  196. package/dist/SitemapSync-ZP4PKZZ3.js +0 -18
  197. package/dist/chunk-COCYQHK3.mjs +0 -6
  198. package/dist/chunk-JLIL5EFG.js +0 -8
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/signal/SignalCore.tsx"],"names":["useState","useRef","getOrCreateVisitorId","getOrCreateSessionId","useEffect","useCallback","sonorFetch","getFreshCredential","isSiteToken","onPageLeave"],"mappings":"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","file":"SignalCore-Q57NY7EG.js","sourcesContent":["/**\n * @sonordev/site-kit/signal - SignalCore\n *\n * The heavy half of SignalBridge: config fetching, SSE streaming, experiment\n * assignment, event queue/flush, and behavioral tracking. Mounted by the\n * SignalBridge shell as a CHILDLESS lazy sibling — it never wraps page\n * content, so the signal machinery stays out of the server-render path and\n * off the critical hydration chunk. It reports state up to the shell via\n * `onState` and registers its imperative API via `onRegister`.\n */\n\n'use client'\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n// Single source of truth for visitor/session IDs (storage keys _sk_vid/_sk_sid)\n// — shared with Analytics and Engage. Do NOT re-fork ID logic into this module.\nimport { getOrCreateVisitorId, getOrCreateSessionId } from '../shared/identity'\n// Canonical client transport — the same wrapper Analytics/Forms/Engage route\n// through (see shared/sonorFetch header). It gives every request a per-attempt\n// AbortSignal timeout, so a hung API aborts cleanly instead of being left to log\n// net::ERR_TIMED_OUT — the console error that fails Lighthouse best-practices.\n// It also sets x-api-key from the `apiKey` option, so callers drop the header.\nimport { sonorFetch } from '../shared/sonorFetch'\nimport { getFreshCredential, isSiteToken } from '../shared/siteToken'\nimport { onPageLeave } from '../shared/page-leave'\nimport type {\n SignalConfig,\n SignalEvent,\n SignalOutcome,\n ExperimentAssignment,\n} from './types'\n\n// ============================================\n// Shell ↔ core contract\n// ============================================\n\nexport interface SignalCoreApi {\n trackEvent: (event: Partial<SignalEvent>) => void\n trackOutcome: (outcome: SignalOutcome) => void\n getExperiment: (experimentId: string) => ExperimentAssignment | null\n refreshConfig: () => Promise<void>\n}\n\nexport interface SignalCoreState {\n config: SignalConfig | null\n loading: boolean\n error: Error | null\n}\n\nexport interface SignalCoreProps {\n enabled: boolean\n realtime: boolean\n experiments: boolean\n behaviorTracking: boolean\n externalVisitorId?: string\n externalSessionId?: string\n pageMetadataRef?: React.RefObject<Record<string, unknown> | null>\n /** Called with the imperative API on mount, and with null on unmount. */\n onRegister: (api: SignalCoreApi | null) => void\n /** Called whenever config/loading/error change, so the shell can re-render consumers. */\n onState: (state: SignalCoreState) => void\n}\n\n// ============================================\n// Utility Functions\n// ============================================\n\nfunction getApiConfig() {\n const apiUrl = typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n : 'https://api.sonor.io'\n const apiKey = typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_KEY__\n : undefined\n return { apiUrl, apiKey }\n}\n\n// A visitor's console must stay clean (Lighthouse \"errors logged to the console\"\n// best-practice) — Signal's diagnostics only surface when the site opts into\n// debug via SiteKitClientProviders' __SITE_KIT_DEBUG__ global, mirroring how\n// AnalyticsProvider gates every one of its logs behind its `debug` prop.\nfunction isDebug(): boolean {\n return typeof window !== 'undefined' && !!(window as any).__SITE_KIT_DEBUG__\n}\n\nfunction getDeviceType(): 'desktop' | 'mobile' | 'tablet' {\n if (typeof window === 'undefined') return 'desktop'\n const ua = navigator.userAgent\n if (/tablet|ipad|playbook|silk/i.test(ua)) return 'tablet'\n if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return 'mobile'\n return 'desktop'\n}\n\nfunction getBrowser(): string {\n if (typeof window === 'undefined') return 'unknown'\n const ua = navigator.userAgent\n if (ua.includes('Firefox')) return 'Firefox'\n if (ua.includes('Edg')) return 'Edge'\n if (ua.includes('Chrome')) return 'Chrome'\n if (ua.includes('Safari')) return 'Safari'\n return 'Other'\n}\n\nfunction getOS(): string {\n if (typeof window === 'undefined') return 'unknown'\n const ua = navigator.userAgent\n if (ua.includes('Windows')) return 'Windows'\n if (ua.includes('Mac')) return 'macOS'\n if (ua.includes('iPhone') || ua.includes('iPad')) return 'iOS'\n if (ua.includes('Android')) return 'Android'\n if (ua.includes('Linux')) return 'Linux'\n return 'Other'\n}\n\n// ============================================\n// SignalCore Component (childless)\n// ============================================\n\nexport function SignalCore({\n enabled,\n realtime,\n experiments,\n behaviorTracking,\n externalVisitorId,\n externalSessionId,\n pageMetadataRef,\n onRegister,\n onState,\n}: SignalCoreProps) {\n const [config, setConfig] = useState<SignalConfig | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n // Refs for SSE and tracking\n const eventSourceRef = useRef<EventSource | null>(null)\n const eventQueueRef = useRef<Partial<SignalEvent>[]>([])\n const flushTimeoutRef = useRef<NodeJS.Timeout | null>(null)\n const assignmentsRef = useRef<Map<string, ExperimentAssignment>>(new Map())\n\n // Behavioral tracking state\n const pageLoadTimeRef = useRef<number>(Date.now())\n const scrollDepthRef = useRef<number>(0)\n const clickCountRef = useRef<number>(0)\n\n const { apiUrl, apiKey } = getApiConfig()\n\n // Use external IDs from shared identity, fall back to the shared generators\n const resolvedVisitorId = externalVisitorId || getOrCreateVisitorId()\n const resolvedSessionId = externalSessionId || getOrCreateSessionId()\n\n // Mirror state up to the shell so context consumers re-render\n useEffect(() => {\n onState({ config, loading, error })\n }, [config, loading, error, onState])\n\n // ============================================\n // Experiment Assignment\n // ============================================\n\n const loadExperimentAssignment = useCallback(async (experimentId: string): Promise<ExperimentAssignment | null> => {\n // Check localStorage first\n const storageKey = `_signal_exp_${experimentId}`\n const stored = localStorage.getItem(storageKey)\n\n if (stored) {\n try {\n const assignment = JSON.parse(stored) as ExperimentAssignment\n if (assignment.expires > Date.now()) {\n assignmentsRef.current.set(experimentId, assignment)\n return assignment\n }\n } catch {\n // Invalid stored data, continue to fetch\n }\n }\n\n // Fetch from API — idempotent read, so the transport's default retries apply.\n try {\n const response = await sonorFetch(\n `${apiUrl}/api/public/signal/experiment/${experimentId}`,\n {\n headers: {\n 'x-visitor-id': resolvedVisitorId,\n },\n },\n { apiKey },\n )\n\n if (!response.ok) return null\n\n const assignment = await response.json() as ExperimentAssignment\n\n // Store assignment\n localStorage.setItem(storageKey, JSON.stringify(assignment))\n assignmentsRef.current.set(experimentId, assignment)\n\n return assignment\n } catch (err) {\n if (isDebug()) console.error('[Signal] Experiment assignment error:', err)\n return null\n }\n }, [apiUrl, apiKey])\n\n const getExperiment = useCallback((experimentId: string): ExperimentAssignment | null => {\n return assignmentsRef.current.get(experimentId) || null\n }, [])\n\n // ============================================\n // Config Fetching\n // ============================================\n\n const fetchConfig = useCallback(async () => {\n if (!apiKey || !enabled) {\n setLoading(false)\n return\n }\n\n try {\n const response = await sonorFetch(\n `${apiUrl}/api/public/signal/config`,\n {\n headers: {\n 'x-visitor-id': resolvedVisitorId,\n },\n },\n { apiKey },\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch Signal config: ${response.statusText}`)\n }\n\n const data = await response.json()\n setConfig(data.config)\n setError(null)\n\n // Load experiment assignments\n if (experiments && data.config?.experiments) {\n for (const exp of data.config.experiments) {\n if (exp.status === 'running') {\n await loadExperimentAssignment(exp.id)\n }\n }\n }\n } catch (err) {\n if (isDebug()) console.error('[Signal] Config fetch error:', err)\n setError(err as Error)\n } finally {\n setLoading(false)\n }\n }, [apiUrl, apiKey, enabled, experiments, resolvedVisitorId, loadExperimentAssignment])\n\n // ============================================\n // SSE Real-time Updates\n // ============================================\n\n const connectSSE = useCallback(() => {\n if (!apiKey || !enabled || !realtime) return\n\n // Close existing connection\n if (eventSourceRef.current) {\n eventSourceRef.current.close()\n eventSourceRef.current = null\n }\n\n // EventSource cannot send headers, so the credential rides in the URL —\n // which server/CDN access logs capture. Per the transport contract in\n // sonorFetch.ts, the raw API key NEVER goes in a URL: only the\n // short-lived minted site token may (bounded exposure, unforgeable HMAC).\n // Raw-key mode (2.x layouts, mint-failure fallback) skips the stream —\n // config still loads via POST, only live push is lost.\n void (async () => {\n const credential = await getFreshCredential(apiKey)\n if (!credential || !isSiteToken(credential)) {\n if (isDebug()) {\n console.warn('[Signal] Realtime stream disabled: requires site-token mode (raw keys never go in URLs)')\n }\n return\n }\n if (eventSourceRef.current) return\n\n const url = `${apiUrl}/api/public/signal/stream?token=${encodeURIComponent(credential)}`\n connectEventSource(url)\n })()\n\n function connectEventSource(url: string) {\n const eventSource = new EventSource(url)\n\n eventSource.addEventListener('config_update', (e) => {\n try {\n const { config: newConfig, version } = JSON.parse(e.data)\n setConfig(prev => {\n if (prev?.version !== version) {\n if (isDebug()) console.log('[Signal] Config updated to version:', version)\n return newConfig\n }\n return prev\n })\n } catch (err) {\n if (isDebug()) console.error('[Signal] SSE parse error:', err)\n }\n })\n\n eventSource.addEventListener('experiment_update', (e) => {\n try {\n const { experiment_id, action } = JSON.parse(e.data)\n if (action === 'started' || action === 'updated') {\n loadExperimentAssignment(experiment_id)\n } else if (action === 'stopped') {\n assignmentsRef.current.delete(experiment_id)\n }\n } catch (err) {\n if (isDebug()) console.error('[Signal] Experiment update error:', err)\n }\n })\n\n eventSource.onerror = () => {\n if (isDebug()) console.warn('[Signal] SSE connection error, reconnecting...')\n eventSource.close()\n if (eventSourceRef.current === eventSource) {\n eventSourceRef.current = null\n }\n // Reconnect after 5 seconds (re-mints the token if it expired)\n setTimeout(connectSSE, 5000)\n }\n\n eventSourceRef.current = eventSource\n }\n }, [apiUrl, apiKey, enabled, realtime, loadExperimentAssignment])\n\n // ============================================\n // Event Tracking\n // ============================================\n\n const flushEvents = useCallback(async () => {\n if (eventQueueRef.current.length === 0) return\n\n const events = [...eventQueueRef.current]\n eventQueueRef.current = []\n\n try {\n // retries: 0 — fire-and-forget tracking; a retried flush after a lost\n // response would double-count events. The transport still aborts a hung\n // request at its timeout (no net::ERR_TIMED_OUT).\n await sonorFetch(\n `${apiUrl}/api/public/signal/events`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n visitor_id: resolvedVisitorId,\n session_id: resolvedSessionId,\n events,\n }),\n },\n { apiKey, retries: 0 },\n )\n } catch (err) {\n // Re-queue failed events\n eventQueueRef.current = [...events, ...eventQueueRef.current]\n if (isDebug()) console.error('[Signal] Event flush error:', err)\n }\n }, [apiUrl, apiKey])\n\n const trackEvent = useCallback((event: Partial<SignalEvent>) => {\n if (!enabled || !apiKey) return\n\n // Enrich event with context\n const enrichedEvent: Partial<SignalEvent> = {\n ...event,\n page_url: window.location.href,\n page_path: window.location.pathname,\n referrer: document.referrer,\n device_type: getDeviceType(),\n browser: getBrowser(),\n os: getOS(),\n viewport_width: window.innerWidth,\n viewport_height: window.innerHeight,\n time_on_page: Date.now() - pageLoadTimeRef.current,\n scroll_depth: scrollDepthRef.current,\n click_count: clickCountRef.current,\n experiments: Array.from(assignmentsRef.current.values()).map(a => ({\n experiment_id: a.experiment_id,\n variant_key: a.variant_key,\n })),\n // Merge page metadata from Analytics if available\n page_metadata: pageMetadataRef?.current ?? undefined,\n timestamp: new Date().toISOString(),\n }\n\n eventQueueRef.current.push(enrichedEvent)\n\n // Debounce flush\n if (flushTimeoutRef.current) {\n clearTimeout(flushTimeoutRef.current)\n }\n flushTimeoutRef.current = setTimeout(flushEvents, 1000)\n }, [enabled, apiKey, flushEvents])\n\n // ============================================\n // Outcome Tracking\n // ============================================\n\n const trackOutcome = useCallback(async (outcome: SignalOutcome) => {\n if (!enabled || !apiKey) return\n\n try {\n // retries: 0 — fire-and-forget conversion signal; never double-fire on a\n // lost response. Still aborts a hung request at the transport timeout.\n await sonorFetch(\n `${apiUrl}/api/public/signal/outcome`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...outcome,\n visitor_id: resolvedVisitorId,\n session_id: resolvedSessionId,\n experiments: Array.from(assignmentsRef.current.keys()),\n page_url: window.location.href,\n timestamp: new Date().toISOString(),\n }),\n },\n { apiKey, retries: 0 },\n )\n } catch (err) {\n if (isDebug()) console.error('[Signal] Outcome tracking error:', err)\n }\n }, [apiUrl, apiKey, enabled])\n\n // ============================================\n // Behavioral Tracking\n // ============================================\n\n useEffect(() => {\n if (!behaviorTracking || typeof window === 'undefined') return\n\n // Track scroll depth\n const handleScroll = () => {\n const scrollTop = window.scrollY\n const docHeight = document.documentElement.scrollHeight - window.innerHeight\n const depth = docHeight > 0 ? Math.round((scrollTop / docHeight) * 100) : 0\n scrollDepthRef.current = Math.max(scrollDepthRef.current, depth)\n }\n\n // Track clicks\n const handleClick = () => {\n clickCountRef.current++\n }\n\n window.addEventListener('scroll', handleScroll, { passive: true })\n window.addEventListener('click', handleClick)\n\n // Flush whenever the page is hidden or unloaded (shared/page-leave.ts).\n const stopLeaveListener = onPageLeave(flushEvents)\n\n return () => {\n window.removeEventListener('scroll', handleScroll)\n window.removeEventListener('click', handleClick)\n stopLeaveListener()\n }\n }, [behaviorTracking, flushEvents])\n\n // ============================================\n // Initialization + shell registration\n // ============================================\n\n useEffect(() => {\n fetchConfig()\n }, [fetchConfig])\n\n useEffect(() => {\n if (config && realtime) {\n connectSSE()\n }\n\n return () => {\n if (eventSourceRef.current) {\n eventSourceRef.current.close()\n }\n }\n }, [config, realtime, connectSSE])\n\n // Hand the imperative API to the shell; buffered calls drain on register.\n useEffect(() => {\n onRegister({\n trackEvent,\n trackOutcome: (outcome: SignalOutcome) => { void trackOutcome(outcome) },\n getExperiment,\n refreshConfig: fetchConfig,\n })\n return () => onRegister(null)\n }, [onRegister, trackEvent, trackOutcome, getExperiment, fetchConfig])\n\n return null\n}\n"]}
1
+ {"version":3,"sources":["../src/signal/SignalCore.tsx"],"names":["useState","useRef","getOrCreateVisitorId","getOrCreateSessionId","useEffect","useCallback","sonorFetch","getFreshCredential","isSiteToken","onPageLeave"],"mappings":"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","file":"SignalCore-OOQHKIUZ.js","sourcesContent":["/**\n * @sonordev/site-kit/signal - SignalCore\n *\n * The heavy half of SignalBridge: config fetching, SSE streaming, experiment\n * assignment, event queue/flush, and behavioral tracking. Mounted by the\n * SignalBridge shell as a CHILDLESS lazy sibling — it never wraps page\n * content, so the signal machinery stays out of the server-render path and\n * off the critical hydration chunk. It reports state up to the shell via\n * `onState` and registers its imperative API via `onRegister`.\n */\n\n'use client'\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n// Single source of truth for visitor/session IDs (storage keys _sk_vid/_sk_sid)\n// — shared with Analytics and Engage. Do NOT re-fork ID logic into this module.\nimport { getOrCreateVisitorId, getOrCreateSessionId } from '../shared/identity'\n// Canonical client transport — the same wrapper Analytics/Forms/Engage route\n// through (see shared/sonorFetch header). It gives every request a per-attempt\n// AbortSignal timeout, so a hung API aborts cleanly instead of being left to log\n// net::ERR_TIMED_OUT — the console error that fails Lighthouse best-practices.\n// It also sets x-api-key from the `apiKey` option, so callers drop the header.\nimport { sonorFetch } from '../shared/sonorFetch'\nimport { getFreshCredential, isSiteToken } from '../shared/siteToken'\nimport { onPageLeave } from '../shared/page-leave'\nimport type {\n SignalConfig,\n SignalEvent,\n SignalOutcome,\n ExperimentAssignment,\n} from './types'\n\n// ============================================\n// Shell ↔ core contract\n// ============================================\n\nexport interface SignalCoreApi {\n trackEvent: (event: Partial<SignalEvent>) => void\n trackOutcome: (outcome: SignalOutcome) => void\n getExperiment: (experimentId: string) => ExperimentAssignment | null\n refreshConfig: () => Promise<void>\n}\n\nexport interface SignalCoreState {\n config: SignalConfig | null\n loading: boolean\n error: Error | null\n}\n\nexport interface SignalCoreProps {\n enabled: boolean\n realtime: boolean\n experiments: boolean\n behaviorTracking: boolean\n externalVisitorId?: string\n externalSessionId?: string\n pageMetadataRef?: React.RefObject<Record<string, unknown> | null>\n /** Called with the imperative API on mount, and with null on unmount. */\n onRegister: (api: SignalCoreApi | null) => void\n /** Called whenever config/loading/error change, so the shell can re-render consumers. */\n onState: (state: SignalCoreState) => void\n}\n\n// ============================================\n// Utility Functions\n// ============================================\n\nfunction getApiConfig() {\n const apiUrl = typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n : 'https://api.sonor.io'\n const apiKey = typeof window !== 'undefined'\n ? (window as any).__SITE_KIT_API_KEY__\n : undefined\n return { apiUrl, apiKey }\n}\n\n// A visitor's console must stay clean (Lighthouse \"errors logged to the console\"\n// best-practice) — Signal's diagnostics only surface when the site opts into\n// debug via SiteKitClientProviders' __SITE_KIT_DEBUG__ global, mirroring how\n// AnalyticsProvider gates every one of its logs behind its `debug` prop.\nfunction isDebug(): boolean {\n return typeof window !== 'undefined' && !!(window as any).__SITE_KIT_DEBUG__\n}\n\nfunction getDeviceType(): 'desktop' | 'mobile' | 'tablet' {\n if (typeof window === 'undefined') return 'desktop'\n const ua = navigator.userAgent\n if (/tablet|ipad|playbook|silk/i.test(ua)) return 'tablet'\n if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return 'mobile'\n return 'desktop'\n}\n\nfunction getBrowser(): string {\n if (typeof window === 'undefined') return 'unknown'\n const ua = navigator.userAgent\n if (ua.includes('Firefox')) return 'Firefox'\n if (ua.includes('Edg')) return 'Edge'\n if (ua.includes('Chrome')) return 'Chrome'\n if (ua.includes('Safari')) return 'Safari'\n return 'Other'\n}\n\nfunction getOS(): string {\n if (typeof window === 'undefined') return 'unknown'\n const ua = navigator.userAgent\n if (ua.includes('Windows')) return 'Windows'\n if (ua.includes('Mac')) return 'macOS'\n if (ua.includes('iPhone') || ua.includes('iPad')) return 'iOS'\n if (ua.includes('Android')) return 'Android'\n if (ua.includes('Linux')) return 'Linux'\n return 'Other'\n}\n\n// ============================================\n// SignalCore Component (childless)\n// ============================================\n\nexport function SignalCore({\n enabled,\n realtime,\n experiments,\n behaviorTracking,\n externalVisitorId,\n externalSessionId,\n pageMetadataRef,\n onRegister,\n onState,\n}: SignalCoreProps) {\n const [config, setConfig] = useState<SignalConfig | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n // Refs for SSE and tracking\n const eventSourceRef = useRef<EventSource | null>(null)\n const eventQueueRef = useRef<Partial<SignalEvent>[]>([])\n const flushTimeoutRef = useRef<NodeJS.Timeout | null>(null)\n const assignmentsRef = useRef<Map<string, ExperimentAssignment>>(new Map())\n\n // Behavioral tracking state\n const pageLoadTimeRef = useRef<number>(Date.now())\n const scrollDepthRef = useRef<number>(0)\n const clickCountRef = useRef<number>(0)\n\n const { apiUrl, apiKey } = getApiConfig()\n\n // Use external IDs from shared identity, fall back to the shared generators\n const resolvedVisitorId = externalVisitorId || getOrCreateVisitorId()\n const resolvedSessionId = externalSessionId || getOrCreateSessionId()\n\n // Mirror state up to the shell so context consumers re-render\n useEffect(() => {\n onState({ config, loading, error })\n }, [config, loading, error, onState])\n\n // ============================================\n // Experiment Assignment\n // ============================================\n\n const loadExperimentAssignment = useCallback(async (experimentId: string): Promise<ExperimentAssignment | null> => {\n // Check localStorage first\n const storageKey = `_signal_exp_${experimentId}`\n const stored = localStorage.getItem(storageKey)\n\n if (stored) {\n try {\n const assignment = JSON.parse(stored) as ExperimentAssignment\n if (assignment.expires > Date.now()) {\n assignmentsRef.current.set(experimentId, assignment)\n return assignment\n }\n } catch {\n // Invalid stored data, continue to fetch\n }\n }\n\n // Fetch from API — idempotent read, so the transport's default retries apply.\n try {\n const response = await sonorFetch(\n `${apiUrl}/api/public/signal/experiment/${experimentId}`,\n {\n headers: {\n 'x-visitor-id': resolvedVisitorId,\n },\n },\n { apiKey },\n )\n\n if (!response.ok) return null\n\n const assignment = await response.json() as ExperimentAssignment\n\n // Store assignment\n localStorage.setItem(storageKey, JSON.stringify(assignment))\n assignmentsRef.current.set(experimentId, assignment)\n\n return assignment\n } catch (err) {\n if (isDebug()) console.error('[Signal] Experiment assignment error:', err)\n return null\n }\n }, [apiUrl, apiKey])\n\n const getExperiment = useCallback((experimentId: string): ExperimentAssignment | null => {\n return assignmentsRef.current.get(experimentId) || null\n }, [])\n\n // ============================================\n // Config Fetching\n // ============================================\n\n const fetchConfig = useCallback(async () => {\n if (!apiKey || !enabled) {\n setLoading(false)\n return\n }\n\n try {\n const response = await sonorFetch(\n `${apiUrl}/api/public/signal/config`,\n {\n headers: {\n 'x-visitor-id': resolvedVisitorId,\n },\n },\n { apiKey },\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch Signal config: ${response.statusText}`)\n }\n\n const data = await response.json()\n setConfig(data.config)\n setError(null)\n\n // Load experiment assignments\n if (experiments && data.config?.experiments) {\n for (const exp of data.config.experiments) {\n if (exp.status === 'running') {\n await loadExperimentAssignment(exp.id)\n }\n }\n }\n } catch (err) {\n if (isDebug()) console.error('[Signal] Config fetch error:', err)\n setError(err as Error)\n } finally {\n setLoading(false)\n }\n }, [apiUrl, apiKey, enabled, experiments, resolvedVisitorId, loadExperimentAssignment])\n\n // ============================================\n // SSE Real-time Updates\n // ============================================\n\n const connectSSE = useCallback(() => {\n if (!apiKey || !enabled || !realtime) return\n\n // Close existing connection\n if (eventSourceRef.current) {\n eventSourceRef.current.close()\n eventSourceRef.current = null\n }\n\n // EventSource cannot send headers, so the credential rides in the URL —\n // which server/CDN access logs capture. Per the transport contract in\n // sonorFetch.ts, the raw API key NEVER goes in a URL: only the\n // short-lived minted site token may (bounded exposure, unforgeable HMAC).\n // Raw-key mode (2.x layouts, mint-failure fallback) skips the stream —\n // config still loads via POST, only live push is lost.\n void (async () => {\n const credential = await getFreshCredential(apiKey)\n if (!credential || !isSiteToken(credential)) {\n if (isDebug()) {\n console.warn('[Signal] Realtime stream disabled: requires site-token mode (raw keys never go in URLs)')\n }\n return\n }\n if (eventSourceRef.current) return\n\n const url = `${apiUrl}/api/public/signal/stream?token=${encodeURIComponent(credential)}`\n connectEventSource(url)\n })()\n\n function connectEventSource(url: string) {\n const eventSource = new EventSource(url)\n\n eventSource.addEventListener('config_update', (e) => {\n try {\n const { config: newConfig, version } = JSON.parse(e.data)\n setConfig(prev => {\n if (prev?.version !== version) {\n if (isDebug()) console.log('[Signal] Config updated to version:', version)\n return newConfig\n }\n return prev\n })\n } catch (err) {\n if (isDebug()) console.error('[Signal] SSE parse error:', err)\n }\n })\n\n eventSource.addEventListener('experiment_update', (e) => {\n try {\n const { experiment_id, action } = JSON.parse(e.data)\n if (action === 'started' || action === 'updated') {\n loadExperimentAssignment(experiment_id)\n } else if (action === 'stopped') {\n assignmentsRef.current.delete(experiment_id)\n }\n } catch (err) {\n if (isDebug()) console.error('[Signal] Experiment update error:', err)\n }\n })\n\n eventSource.onerror = () => {\n if (isDebug()) console.warn('[Signal] SSE connection error, reconnecting...')\n eventSource.close()\n if (eventSourceRef.current === eventSource) {\n eventSourceRef.current = null\n }\n // Reconnect after 5 seconds (re-mints the token if it expired)\n setTimeout(connectSSE, 5000)\n }\n\n eventSourceRef.current = eventSource\n }\n }, [apiUrl, apiKey, enabled, realtime, loadExperimentAssignment])\n\n // ============================================\n // Event Tracking\n // ============================================\n\n const flushEvents = useCallback(async () => {\n if (eventQueueRef.current.length === 0) return\n\n const events = [...eventQueueRef.current]\n eventQueueRef.current = []\n\n try {\n // retries: 0 — fire-and-forget tracking; a retried flush after a lost\n // response would double-count events. The transport still aborts a hung\n // request at its timeout (no net::ERR_TIMED_OUT).\n await sonorFetch(\n `${apiUrl}/api/public/signal/events`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n visitor_id: resolvedVisitorId,\n session_id: resolvedSessionId,\n events,\n }),\n },\n { apiKey, retries: 0 },\n )\n } catch (err) {\n // Re-queue failed events\n eventQueueRef.current = [...events, ...eventQueueRef.current]\n if (isDebug()) console.error('[Signal] Event flush error:', err)\n }\n }, [apiUrl, apiKey])\n\n const trackEvent = useCallback((event: Partial<SignalEvent>) => {\n if (!enabled || !apiKey) return\n\n // Enrich event with context\n const enrichedEvent: Partial<SignalEvent> = {\n ...event,\n page_url: window.location.href,\n page_path: window.location.pathname,\n referrer: document.referrer,\n device_type: getDeviceType(),\n browser: getBrowser(),\n os: getOS(),\n viewport_width: window.innerWidth,\n viewport_height: window.innerHeight,\n time_on_page: Date.now() - pageLoadTimeRef.current,\n scroll_depth: scrollDepthRef.current,\n click_count: clickCountRef.current,\n experiments: Array.from(assignmentsRef.current.values()).map(a => ({\n experiment_id: a.experiment_id,\n variant_key: a.variant_key,\n })),\n // Merge page metadata from Analytics if available\n page_metadata: pageMetadataRef?.current ?? undefined,\n timestamp: new Date().toISOString(),\n }\n\n eventQueueRef.current.push(enrichedEvent)\n\n // Debounce flush\n if (flushTimeoutRef.current) {\n clearTimeout(flushTimeoutRef.current)\n }\n flushTimeoutRef.current = setTimeout(flushEvents, 1000)\n }, [enabled, apiKey, flushEvents])\n\n // ============================================\n // Outcome Tracking\n // ============================================\n\n const trackOutcome = useCallback(async (outcome: SignalOutcome) => {\n if (!enabled || !apiKey) return\n\n try {\n // retries: 0 — fire-and-forget conversion signal; never double-fire on a\n // lost response. Still aborts a hung request at the transport timeout.\n await sonorFetch(\n `${apiUrl}/api/public/signal/outcome`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...outcome,\n visitor_id: resolvedVisitorId,\n session_id: resolvedSessionId,\n experiments: Array.from(assignmentsRef.current.keys()),\n page_url: window.location.href,\n timestamp: new Date().toISOString(),\n }),\n },\n { apiKey, retries: 0 },\n )\n } catch (err) {\n if (isDebug()) console.error('[Signal] Outcome tracking error:', err)\n }\n }, [apiUrl, apiKey, enabled])\n\n // ============================================\n // Behavioral Tracking\n // ============================================\n\n useEffect(() => {\n if (!behaviorTracking || typeof window === 'undefined') return\n\n // Track scroll depth\n const handleScroll = () => {\n const scrollTop = window.scrollY\n const docHeight = document.documentElement.scrollHeight - window.innerHeight\n const depth = docHeight > 0 ? Math.round((scrollTop / docHeight) * 100) : 0\n scrollDepthRef.current = Math.max(scrollDepthRef.current, depth)\n }\n\n // Track clicks\n const handleClick = () => {\n clickCountRef.current++\n }\n\n window.addEventListener('scroll', handleScroll, { passive: true })\n window.addEventListener('click', handleClick)\n\n // Flush whenever the page is hidden or unloaded (shared/page-leave.ts).\n const stopLeaveListener = onPageLeave(flushEvents)\n\n return () => {\n window.removeEventListener('scroll', handleScroll)\n window.removeEventListener('click', handleClick)\n stopLeaveListener()\n }\n }, [behaviorTracking, flushEvents])\n\n // ============================================\n // Initialization + shell registration\n // ============================================\n\n useEffect(() => {\n fetchConfig()\n }, [fetchConfig])\n\n useEffect(() => {\n if (config && realtime) {\n connectSSE()\n }\n\n return () => {\n if (eventSourceRef.current) {\n eventSourceRef.current.close()\n }\n }\n }, [config, realtime, connectSSE])\n\n // Hand the imperative API to the shell; buffered calls drain on register.\n useEffect(() => {\n onRegister({\n trackEvent,\n trackOutcome: (outcome: SignalOutcome) => { void trackOutcome(outcome) },\n getExperiment,\n refreshConfig: fetchConfig,\n })\n return () => onRegister(null)\n }, [onRegister, trackEvent, trackOutcome, getExperiment, fetchConfig])\n\n return null\n}\n"]}
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ var chunk2LX7LHAO_js = require('./chunk-2LX7LHAO.js');
4
+ require('./chunk-JONWE26J.js');
5
+ require('./chunk-5RNAUYWE.js');
6
+ require('./chunk-NKBPEZCX.js');
7
+ require('./chunk-6TSYWYYR.js');
8
+ require('./chunk-ZKOQCKOR.js');
9
+ require('./chunk-SNAQBZPT.js');
10
+
11
+
12
+
13
+ Object.defineProperty(exports, "SitemapSync", {
14
+ enumerable: true,
15
+ get: function () { return chunk2LX7LHAO_js.SitemapSync; }
16
+ });
17
+ //# sourceMappingURL=SitemapSync-MT3I4W3B.js.map
18
+ //# sourceMappingURL=SitemapSync-MT3I4W3B.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"SitemapSync-ZP4PKZZ3.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"SitemapSync-MT3I4W3B.js"}
@@ -0,0 +1,9 @@
1
+ export { SitemapSync } from './chunk-DUFU27R2.mjs';
2
+ import './chunk-C2CTLLIO.mjs';
3
+ import './chunk-A6UYV475.mjs';
4
+ import './chunk-XNZ2WFXR.mjs';
5
+ import './chunk-OUM77VZ4.mjs';
6
+ import './chunk-C654VCWV.mjs';
7
+ import './chunk-BEXTDHO4.mjs';
8
+ //# sourceMappingURL=SitemapSync-RG554AJR.mjs.map
9
+ //# sourceMappingURL=SitemapSync-RG554AJR.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"SitemapSync-NICUUMQF.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"SitemapSync-RG554AJR.mjs"}
@@ -53,6 +53,13 @@ export interface SonorSsoConfig {
53
53
  loginPath?: string;
54
54
  /** Other gated paths reachable without a session. */
55
55
  publicPaths?: string[];
56
+ /**
57
+ * Gated API prefixes (e.g. `/api/studio`). Without a session they answer
58
+ * 401 JSON instead of redirecting to the login page. Only useful when the
59
+ * middleware matcher includes them; handlers must still check the session
60
+ * themselves.
61
+ */
62
+ apiPaths?: string[];
56
63
  /** Where the callback route lives. Default `/api/auth/callback`. */
57
64
  callbackPath?: string;
58
65
  /** Default `__Host-sonor-session`. Keep a site's old name to keep its users signed in. */
@@ -93,6 +100,7 @@ export declare const SSO_ERROR_MESSAGES: Record<string, string>;
93
100
  export declare function createSonorSso(config: SonorSsoConfig): {
94
101
  gate: (request: NextRequest) => Promise<NextResponse | undefined>;
95
102
  getSession: () => Promise<SonorIdentity | null>;
103
+ getRequestSession: (request: NextRequest) => Promise<SonorIdentity | null>;
96
104
  getLoginUrl: (options?: {
97
105
  returnTo?: string | null;
98
106
  siteUrl?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin-auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,WAAW,CAAA;AAE9E,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAS3D,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACxB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uBAAuB;IACvB,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,OAAO,EAAE,OAAO,CAAA;IAChB,mDAAmD;IACnD,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQrD,CAAA;AAaD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc;oBAoEtB,WAAW,KAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;sBAmB/C,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;4BAsCtB;QAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KAAQ,OAAO,CAAC,MAAM,CAAC;8BAuBlE,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC;4BA2D3C,WAAW,KAAG,YAAY;8BAhKxB,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,MAAM;gCAjChC,MAAM;;;;EAuN1C;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin-auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,WAAW,CAAA;AAE9E,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAS3D,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACxB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uBAAuB;IACvB,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,OAAO,EAAE,OAAO,CAAA;IAChB,mDAAmD;IACnD,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQrD,CAAA;AAaD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc;oBAsEtB,WAAW,KAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;sBAiC/C,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;iCAXhB,WAAW,KAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;4BAiDjD;QAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KAAQ,OAAO,CAAC,MAAM,CAAC;8BAuBlE,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC;4BA2D3C,WAAW,KAAG,YAAY;8BA9KxB,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,MAAM;gCAlChC,MAAM;;;;EAuO1C;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAA"}
Binary file
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/admin-auth/session.ts","../../src/admin-auth/index.ts"],"names":["NextResponse"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Signed session cookie for Sonor SSO-gated site areas.\n *\n * Format: `<base64url(payload)>.<base64url(HMAC-SHA256(payloadB64))>`, payload\n * `{ email, contactId, iat, exp }` in Unix seconds. This is byte-compatible with\n * the hand-rolled Web Crypto sessions legacy-clay-classic and the CCG builder\n * shipped before this module existed, so a site that passes its old cookie name\n * and secret keeps every signed-in user signed in across the switch.\n *\n * Web Crypto only (never Node `crypto`): these helpers run in Edge middleware\n * and in Node route handlers alike.\n */\n\nexport interface SonorSession {\n email: string\n contactId: string\n /** Unix seconds */\n iat: number\n /** Unix seconds */\n exp: number\n}\n\nexport const MIN_SECRET_LENGTH = 32\n\nconst encoder = new TextEncoder()\nconst keyCache = new Map<string, Promise<CryptoKey>>()\n\nfunction hmacKey(secret: string): Promise<CryptoKey> {\n let key = keyCache.get(secret)\n if (!key) {\n key = crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n )\n keyCache.set(secret, key)\n }\n return key\n}\n\nfunction bytesToB64url(bytes: Uint8Array): string {\n let bin = ''\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction b64urlToBytes(s: string): Uint8Array<ArrayBuffer> | null {\n if (!/^[A-Za-z0-9_-]*$/.test(s)) return null\n try {\n const bin = atob(s.replace(/-/g, '+').replace(/_/g, '/') + '==='.slice((s.length + 3) % 4))\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n return bytes\n } catch {\n return null\n }\n}\n\nfunction assertSecret(secret: string | undefined): string {\n if (!secret || secret.length < MIN_SECRET_LENGTH) {\n throw new Error(\n `@sonordev/site-kit/admin-auth: the session secret must be at least ${MIN_SECRET_LENGTH} characters (try \\`openssl rand -hex 32\\`)`,\n )\n }\n return secret\n}\n\nexport async function signSession(\n identity: { email: string; contactId: string },\n secret: string,\n lifetimeSeconds: number,\n now: number = Math.floor(Date.now() / 1000),\n): Promise<string> {\n const payload: SonorSession = {\n email: identity.email,\n contactId: identity.contactId,\n iat: now,\n exp: now + lifetimeSeconds,\n }\n const payloadB64 = bytesToB64url(encoder.encode(JSON.stringify(payload)))\n const key = await hmacKey(assertSecret(secret))\n const sig = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(payloadB64)))\n return `${payloadB64}.${bytesToB64url(sig)}`\n}\n\n/**\n * Returns the session, or null for anything short of a valid, unexpired,\n * correctly signed token — including a missing or short secret, so a\n * misconfigured deploy sends people to the login page instead of 500-ing.\n */\nexport async function verifySessionToken(\n token: string | null | undefined,\n secret: string | undefined,\n now: number = Math.floor(Date.now() / 1000),\n): Promise<SonorSession | null> {\n if (!token || typeof token !== 'string') return null\n const dot = token.indexOf('.')\n if (dot < 1 || dot !== token.lastIndexOf('.')) return null\n const payloadB64 = token.slice(0, dot)\n const sig = b64urlToBytes(token.slice(dot + 1))\n if (!sig) return null\n\n let key: CryptoKey\n try {\n key = await hmacKey(assertSecret(secret))\n } catch (err) {\n console.error(String(err instanceof Error ? err.message : err))\n return null\n }\n\n // subtle.verify is the constant-time comparison.\n const ok = await crypto.subtle.verify('HMAC', key, sig, encoder.encode(payloadB64))\n if (!ok) return null\n\n const raw = b64urlToBytes(payloadB64)\n if (!raw) return null\n try {\n const p = JSON.parse(new TextDecoder().decode(raw)) as Partial<SonorSession>\n if (\n typeof p.email !== 'string' ||\n typeof p.contactId !== 'string' ||\n typeof p.iat !== 'number' ||\n typeof p.exp !== 'number' ||\n p.exp < now\n ) {\n return null\n }\n return { email: p.email, contactId: p.contactId, iat: p.iat, exp: p.exp }\n } catch {\n return null\n }\n}\n","/**\n * @sonordev/site-kit/admin-auth — \"Sign in with Sonor\" for a site's own admin area\n *\n * SINGLE SOURCE OF TRUTH for the Sonor SSO handshake on client sites. Before\n * this module, 4m-lawn-care (/bid, /route), legacy-clay-classic (/admin) and the\n * CCG builder (/studio) each carried a hand-copied version, and the copies had\n * already drifted (Node vs Web Crypto, 7 vs 30 day sessions, roles in one only).\n * Do not re-fork it into a site.\n *\n * The handshake:\n * 1. `/admin/login` links to `app.sonor.io/sso/grant?project_id=…&return_to=<callback>`.\n * 2. Sonor checks the signed-in user can access the project (owner, member,\n * managing agency, or platform admin) and redirects to the callback with a\n * short-lived token.\n * 3. The callback posts the token to `/api/public/auth/verify-sso-token` with\n * the project API key; Sonor rejects tokens issued for any other project.\n * 4. The callback sets a signed, httpOnly session cookie (30 days default).\n *\n * Wiring (four files):\n *\n * // lib/sonor-sso.ts\n * export const sso = createSonorSso({ paths: '/admin' })\n *\n * // middleware.ts (or proxy.ts)\n * export default createProxy({ before: sso.gate, securityHeaders: true })\n *\n * // app/api/auth/callback/route.ts\n * export const GET = sso.handleCallback\n *\n * // app/api/auth/logout/route.ts\n * export const GET = sso.handleLogout\n * export const POST = sso.handleLogout\n *\n * Then `await sso.getLoginUrl()` in the login page, and `await sso.getSession()`\n * in every page, server action and API route that exposes admin data. The\n * middleware gate never sees `/api/*` (the standard matcher excludes it), so\n * API routes MUST check `getSession()` themselves.\n *\n * Env: SONOR_API_KEY (already set on every site), SONOR_SESSION_SECRET (>= 32\n * chars; rotating it signs everyone out), NEXT_PUBLIC_SITE_URL.\n */\n\nimport { NextResponse, type NextRequest } from 'next/server'\nimport { signSession, verifySessionToken, type SonorSession } from './session'\n\nexport type { SonorSession } from './session'\nexport { signSession, verifySessionToken } from './session'\n\nconst DEFAULT_APP_URL = 'https://app.sonor.io'\nconst DEFAULT_API_URL = 'https://api.sonor.io'\nconst DEFAULT_COOKIE_NAME = '__Host-sonor-session'\nconst DEFAULT_SECRET_ENV = 'SONOR_SESSION_SECRET'\nconst DEFAULT_CALLBACK_PATH = '/api/auth/callback'\nconst THIRTY_DAYS = 30 * 24 * 60 * 60\n\nexport interface SonorSsoConfig {\n /**\n * The gated area(s). Every path equal to or under one of these needs a\n * session. The first entry is where a sign-in lands by default.\n */\n paths: string | string[]\n /** Default `${paths[0]}/login`. Always reachable without a session. */\n loginPath?: string\n /** Other gated paths reachable without a session. */\n publicPaths?: string[]\n /** Where the callback route lives. Default `/api/auth/callback`. */\n callbackPath?: string\n /** Default `__Host-sonor-session`. Keep a site's old name to keep its users signed in. */\n cookieName?: string\n /** Default 30 days. */\n sessionLifetimeSeconds?: number\n /** Env var holding the signing secret. Default `SONOR_SESSION_SECRET`. */\n secretEnv?: string\n /**\n * Emails that get `isAdmin: true` on their session identity (comma string or\n * list, case-insensitive). Computed on every read from the verified cookie,\n * never stored in it. Everyone Sonor lets in still gets in; this only marks\n * who is an admin within the area.\n */\n adminEmails?: string | string[]\n /**\n * Outside production the gate lets everyone through as this identity, so\n * local dev works without a round trip to production Sonor. `false` makes\n * dev enforce the gate too. Default email: `SONOR_SSO_DEV_EMAIL`, else\n * `dev@localhost`.\n */\n devBypass?: false | { email?: string }\n /** Public origin for the callback URL. Default `NEXT_PUBLIC_SITE_URL`. */\n siteUrl?: string\n /** Default `NEXT_PUBLIC_SONOR_APP_URL`, else https://app.sonor.io. */\n appUrl?: string\n /** Default `SONOR_API_URL`, else https://api.sonor.io. */\n apiUrl?: string\n}\n\nexport interface SonorIdentity extends SonorSession {\n isAdmin: boolean\n /** True only for the non-production dev bypass. */\n isDevBypass: boolean\n}\n\nexport const SSO_ERROR_MESSAGES: Record<string, string> = {\n missing_token: 'The sign-in link was incomplete. Try again.',\n invalid_token: 'The sign-in link was invalid or expired. Try again.',\n invalid_or_expired: 'The sign-in link expired before it was used. Try again.',\n project_mismatch: 'That sign-in link was for a different site. Try again from the button below.',\n verify_failed: 'Sonor could not verify the sign-in link.',\n sonor_unreachable: 'Could not reach Sonor to verify sign-in. Check your connection and try again.',\n server_config: 'This site is missing its Sonor sign-in configuration.',\n}\n\nconst projectIdCache = new Map<string, Promise<string>>()\n\nfunction trimSlash(url: string): string {\n return url.replace(/\\/+$/, '')\n}\n\nfunction normalizePath(p: string): string {\n const withSlash = p.startsWith('/') ? p : `/${p}`\n return withSlash.length > 1 ? withSlash.replace(/\\/+$/, '') : withSlash\n}\n\nexport function createSonorSso(config: SonorSsoConfig) {\n const paths = (Array.isArray(config.paths) ? config.paths : [config.paths]).map(normalizePath)\n if (!paths.length || paths.some((p) => p === '/')) {\n throw new Error('@sonordev/site-kit/admin-auth: `paths` must name at least one area below the site root')\n }\n const home = paths[0]\n const loginPath = normalizePath(config.loginPath ?? `${home}/login`)\n const publicPaths = [loginPath, ...(config.publicPaths ?? []).map(normalizePath)]\n const callbackPath = normalizePath(config.callbackPath ?? DEFAULT_CALLBACK_PATH)\n const cookieName = config.cookieName ?? DEFAULT_COOKIE_NAME\n const lifetime = config.sessionLifetimeSeconds ?? THIRTY_DAYS\n const secretEnv = config.secretEnv ?? DEFAULT_SECRET_ENV\n\n const under = (pathname: string, base: string) => pathname === base || pathname.startsWith(`${base}/`)\n const isProtectedPath = (pathname: string) => paths.some((p) => under(pathname, p))\n const isPublicPath = (pathname: string) => publicPaths.some((p) => under(pathname, p))\n\n const secret = () => process.env[secretEnv]\n const apiUrl = () => trimSlash(config.apiUrl || process.env.SONOR_API_URL || DEFAULT_API_URL)\n const appUrl = () => trimSlash(config.appUrl || process.env.NEXT_PUBLIC_SONOR_APP_URL || DEFAULT_APP_URL)\n const isProduction = () => process.env.NODE_ENV === 'production'\n const devBypassEmail = () =>\n config.devBypass === false\n ? null\n : config.devBypass?.email || process.env.SONOR_SSO_DEV_EMAIL || 'dev@localhost'\n\n const adminSet = (): Set<string> => {\n const raw = config.adminEmails\n const list = Array.isArray(raw) ? raw : (raw ?? '').split(',')\n return new Set(list.map((e) => e.trim().toLowerCase()).filter(Boolean))\n }\n const toIdentity = (s: SonorSession, isDevBypass = false): SonorIdentity => ({\n ...s,\n isAdmin: adminSet().has(s.email.trim().toLowerCase()),\n isDevBypass,\n })\n const devIdentity = (): SonorIdentity | null => {\n const email = !isProduction() ? devBypassEmail() : null\n return email ? toIdentity({ email, contactId: `dev-${email}`, iat: 0, exp: 0 }, true) : null\n }\n\n /**\n * A safe post-sign-in destination: a same-origin path inside a gated area.\n * Anything else (absolute URLs, `//host`, backslashes, paths outside the\n * area) falls back to the area's home, so the callback can't be used as an\n * open redirect.\n */\n function sanitizeReturnTo(value: string | null | undefined): string {\n if (!value || typeof value !== 'string') return home\n if (!value.startsWith('/') || value.startsWith('//') || /[\\\\\u0000-\u001f]/.test(value)) return home\n const pathname = value.split(/[?#]/, 1)[0]\n return isProtectedPath(pathname) && !under(pathname, loginPath) ? value : home\n }\n\n function passThrough(request: NextRequest): NextResponse {\n // x-next-pathname on the REQUEST lets an admin layout know where it is.\n const headers = new Headers(request.headers)\n headers.set('x-next-pathname', request.nextUrl.pathname)\n const res = NextResponse.next({ request: { headers } })\n res.headers.set('X-Robots-Tag', 'noindex, nofollow')\n return res\n }\n\n /**\n * Middleware gate — pass as `createProxy({ before: sso.gate })`. Returns\n * undefined outside the gated area so the rest of the proxy (redirects, AI\n * discovery headers) still runs for public pages.\n */\n async function gate(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname, search } = request.nextUrl\n if (!isProtectedPath(pathname) && !isPublicPath(pathname)) return undefined\n if (isPublicPath(pathname) || devIdentity()) return passThrough(request)\n\n const session = await verifySessionToken(request.cookies.get(cookieName)?.value, secret())\n if (session) return passThrough(request)\n\n const login = new URL(loginPath, request.url)\n if (pathname !== home || search) login.searchParams.set('return_to', pathname + search)\n const res = NextResponse.redirect(login)\n res.headers.set('X-Robots-Tag', 'noindex, nofollow')\n return res\n }\n\n /**\n * The signed-in identity for this request, or null. Use it in every server\n * component, server action and API route that exposes gated data.\n */\n async function getSession(): Promise<SonorIdentity | null> {\n const dev = devIdentity()\n if (dev) return dev\n const { cookies } = await import('next/headers')\n const jar = await cookies()\n const session = await verifySessionToken(jar.get(cookieName)?.value, secret())\n return session ? toIdentity(session) : null\n }\n\n async function resolveProjectId(): Promise<string> {\n const apiKey = process.env.SONOR_API_KEY\n if (!apiKey) throw new Error('SONOR_API_KEY is not set')\n const cacheKey = `${apiUrl()}|${apiKey}`\n let pending = projectIdCache.get(cacheKey)\n if (!pending) {\n pending = (async () => {\n const res = await fetch(`${apiUrl()}/api/public/auth/verify`, {\n headers: { 'x-api-key': apiKey },\n cache: 'no-store',\n signal: AbortSignal.timeout(10_000),\n })\n if (!res.ok) throw new Error(`Sonor verify returned ${res.status}`)\n const data = (await res.json()) as { project_id?: string }\n if (!data.project_id) throw new Error('Sonor verify returned no project_id')\n return data.project_id\n })()\n projectIdCache.set(cacheKey, pending)\n pending.catch(() => projectIdCache.delete(cacheKey))\n }\n return pending\n }\n\n /**\n * The \"Sign in with Sonor\" URL for the login page. Pass the page's\n * `return_to` search param through; it's sanitized here. Throws when the\n * site can't identify its project (no API key, Sonor unreachable) — catch\n * it and show the error rather than a dead button.\n */\n async function getLoginUrl(options: { returnTo?: string | null; siteUrl?: string } = {}): Promise<string> {\n const site = options.siteUrl || config.siteUrl || process.env.NEXT_PUBLIC_SITE_URL\n if (!site) throw new Error('NEXT_PUBLIC_SITE_URL (or siteUrl) is required to build the Sonor sign-in link')\n const callback = new URL(callbackPath, site)\n if (options.returnTo) {\n const safe = sanitizeReturnTo(options.returnTo)\n if (safe !== home) callback.searchParams.set('return_to', safe)\n }\n const grant = new URL('/sso/grant', `${appUrl()}/`)\n grant.searchParams.set('project_id', await resolveProjectId())\n grant.searchParams.set('return_to', callback.toString())\n return grant.toString()\n }\n\n function loginRedirect(request: NextRequest, error?: string): NextResponse {\n const url = new URL(loginPath, request.url)\n if (error) url.searchParams.set('error', error)\n const res = NextResponse.redirect(url)\n res.headers.set('Cache-Control', 'no-store')\n return res\n }\n\n /** `export const GET = sso.handleCallback` in the callback route. */\n async function handleCallback(request: NextRequest): Promise<NextResponse> {\n const token = request.nextUrl.searchParams.get('token')\n if (!token) return loginRedirect(request, 'missing_token')\n\n const apiKey = process.env.SONOR_API_KEY\n if (!apiKey) {\n console.error('[sonor-sso] SONOR_API_KEY is not set; cannot verify the sign-in token')\n return loginRedirect(request, 'server_config')\n }\n\n let res: Response\n try {\n res = await fetch(`${apiUrl()}/api/public/auth/verify-sso-token`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', 'x-api-key': apiKey },\n body: JSON.stringify({ token }),\n cache: 'no-store',\n signal: AbortSignal.timeout(10_000),\n })\n } catch (err) {\n console.error('[sonor-sso] verify request failed:', err)\n return loginRedirect(request, 'sonor_unreachable')\n }\n if (!res.ok) {\n console.error(`[sonor-sso] verify returned ${res.status}`)\n return loginRedirect(request, 'verify_failed')\n }\n\n const data = (await res.json().catch(() => null)) as\n | { valid?: boolean; email?: string; contactId?: string; reason?: string }\n | null\n if (!data?.valid || !data.email || !data.contactId) {\n return loginRedirect(request, data?.reason || 'invalid_token')\n }\n\n let sessionToken: string\n try {\n sessionToken = await signSession({ email: data.email, contactId: data.contactId }, secret() ?? '', lifetime)\n } catch (err) {\n console.error(`[sonor-sso] could not sign the session (check ${secretEnv}):`, err)\n return loginRedirect(request, 'server_config')\n }\n\n const destination = sanitizeReturnTo(request.nextUrl.searchParams.get('return_to'))\n const out = NextResponse.redirect(new URL(destination, request.url))\n out.cookies.set(cookieName, sessionToken, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n path: '/',\n maxAge: lifetime,\n })\n // The token rode in on the URL: keep it out of Referer headers and caches.\n out.headers.set('Referrer-Policy', 'no-referrer')\n out.headers.set('Cache-Control', 'no-store')\n return out\n }\n\n /** `export const GET = sso.handleLogout` (and POST) in the logout route. */\n function handleLogout(request: NextRequest): NextResponse {\n const url = new URL(loginPath, request.url)\n url.searchParams.set('signed_out', '1')\n // 303 so a POST from a sign-out form lands as a GET on the login page.\n const res = NextResponse.redirect(url, 303)\n res.cookies.set(cookieName, '', { httpOnly: true, secure: true, sameSite: 'lax', path: '/', maxAge: 0 })\n res.headers.set('Cache-Control', 'no-store')\n return res\n }\n\n return {\n gate,\n getSession,\n getLoginUrl,\n handleCallback,\n handleLogout,\n sanitizeReturnTo,\n isProtectedPath,\n cookieName,\n loginPath,\n homePath: home,\n }\n}\n\nexport type SonorSso = ReturnType<typeof createSonorSso>\n"]}
1
+ {"version":3,"sources":["../../src/admin-auth/session.ts","../../src/admin-auth/index.ts"],"names":["NextResponse"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Signed session cookie for Sonor SSO-gated site areas.\n *\n * Format: `<base64url(payload)>.<base64url(HMAC-SHA256(payloadB64))>`, payload\n * `{ email, contactId, iat, exp }` in Unix seconds. This is byte-compatible with\n * the hand-rolled Web Crypto sessions legacy-clay-classic and the CCG builder\n * shipped before this module existed, so a site that passes its old cookie name\n * and secret keeps every signed-in user signed in across the switch.\n *\n * Web Crypto only (never Node `crypto`): these helpers run in Edge middleware\n * and in Node route handlers alike.\n */\n\nexport interface SonorSession {\n email: string\n contactId: string\n /** Unix seconds */\n iat: number\n /** Unix seconds */\n exp: number\n}\n\nexport const MIN_SECRET_LENGTH = 32\n\nconst encoder = new TextEncoder()\nconst keyCache = new Map<string, Promise<CryptoKey>>()\n\nfunction hmacKey(secret: string): Promise<CryptoKey> {\n let key = keyCache.get(secret)\n if (!key) {\n key = crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n )\n keyCache.set(secret, key)\n }\n return key\n}\n\nfunction bytesToB64url(bytes: Uint8Array): string {\n let bin = ''\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction b64urlToBytes(s: string): Uint8Array<ArrayBuffer> | null {\n if (!/^[A-Za-z0-9_-]*$/.test(s)) return null\n try {\n const bin = atob(s.replace(/-/g, '+').replace(/_/g, '/') + '==='.slice((s.length + 3) % 4))\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n return bytes\n } catch {\n return null\n }\n}\n\nfunction assertSecret(secret: string | undefined): string {\n if (!secret || secret.length < MIN_SECRET_LENGTH) {\n throw new Error(\n `@sonordev/site-kit/admin-auth: the session secret must be at least ${MIN_SECRET_LENGTH} characters (try \\`openssl rand -hex 32\\`)`,\n )\n }\n return secret\n}\n\nexport async function signSession(\n identity: { email: string; contactId: string },\n secret: string,\n lifetimeSeconds: number,\n now: number = Math.floor(Date.now() / 1000),\n): Promise<string> {\n const payload: SonorSession = {\n email: identity.email,\n contactId: identity.contactId,\n iat: now,\n exp: now + lifetimeSeconds,\n }\n const payloadB64 = bytesToB64url(encoder.encode(JSON.stringify(payload)))\n const key = await hmacKey(assertSecret(secret))\n const sig = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(payloadB64)))\n return `${payloadB64}.${bytesToB64url(sig)}`\n}\n\n/**\n * Returns the session, or null for anything short of a valid, unexpired,\n * correctly signed token — including a missing or short secret, so a\n * misconfigured deploy sends people to the login page instead of 500-ing.\n */\nexport async function verifySessionToken(\n token: string | null | undefined,\n secret: string | undefined,\n now: number = Math.floor(Date.now() / 1000),\n): Promise<SonorSession | null> {\n if (!token || typeof token !== 'string') return null\n const dot = token.indexOf('.')\n if (dot < 1 || dot !== token.lastIndexOf('.')) return null\n const payloadB64 = token.slice(0, dot)\n const sig = b64urlToBytes(token.slice(dot + 1))\n if (!sig) return null\n\n let key: CryptoKey\n try {\n key = await hmacKey(assertSecret(secret))\n } catch (err) {\n console.error(String(err instanceof Error ? err.message : err))\n return null\n }\n\n // subtle.verify is the constant-time comparison.\n const ok = await crypto.subtle.verify('HMAC', key, sig, encoder.encode(payloadB64))\n if (!ok) return null\n\n const raw = b64urlToBytes(payloadB64)\n if (!raw) return null\n try {\n const p = JSON.parse(new TextDecoder().decode(raw)) as Partial<SonorSession>\n if (\n typeof p.email !== 'string' ||\n typeof p.contactId !== 'string' ||\n typeof p.iat !== 'number' ||\n typeof p.exp !== 'number' ||\n p.exp < now\n ) {\n return null\n }\n return { email: p.email, contactId: p.contactId, iat: p.iat, exp: p.exp }\n } catch {\n return null\n }\n}\n","/**\n * @sonordev/site-kit/admin-auth — \"Sign in with Sonor\" for a site's own admin area\n *\n * SINGLE SOURCE OF TRUTH for the Sonor SSO handshake on client sites. Before\n * this module, 4m-lawn-care (/bid, /route), legacy-clay-classic (/admin) and the\n * CCG builder (/studio) each carried a hand-copied version, and the copies had\n * already drifted (Node vs Web Crypto, 7 vs 30 day sessions, roles in one only).\n * Do not re-fork it into a site.\n *\n * The handshake:\n * 1. `/admin/login` links to `app.sonor.io/sso/grant?project_id=…&return_to=<callback>`.\n * 2. Sonor checks the signed-in user can access the project (owner, member,\n * managing agency, or platform admin) and redirects to the callback with a\n * short-lived token.\n * 3. The callback posts the token to `/api/public/auth/verify-sso-token` with\n * the project API key; Sonor rejects tokens issued for any other project.\n * 4. The callback sets a signed, httpOnly session cookie (30 days default).\n *\n * Wiring (four files):\n *\n * // lib/sonor-sso.ts\n * export const sso = createSonorSso({ paths: '/admin' })\n *\n * // middleware.ts (or proxy.ts)\n * export default createProxy({ before: sso.gate, securityHeaders: true })\n *\n * // app/api/auth/callback/route.ts\n * export const GET = sso.handleCallback\n *\n * // app/api/auth/logout/route.ts\n * export const GET = sso.handleLogout\n * export const POST = sso.handleLogout\n *\n * Then `await sso.getLoginUrl()` in the login page, and `await sso.getSession()`\n * in every page, server action and API route that exposes admin data. The\n * middleware gate never sees `/api/*` (the standard matcher excludes it), so\n * API routes MUST check `getSession()` themselves.\n *\n * Env: SONOR_API_KEY (already set on every site), SONOR_SESSION_SECRET (>= 32\n * chars; rotating it signs everyone out), NEXT_PUBLIC_SITE_URL.\n */\n\nimport { NextResponse, type NextRequest } from 'next/server'\nimport { signSession, verifySessionToken, type SonorSession } from './session'\n\nexport type { SonorSession } from './session'\nexport { signSession, verifySessionToken } from './session'\n\nconst DEFAULT_APP_URL = 'https://app.sonor.io'\nconst DEFAULT_API_URL = 'https://api.sonor.io'\nconst DEFAULT_COOKIE_NAME = '__Host-sonor-session'\nconst DEFAULT_SECRET_ENV = 'SONOR_SESSION_SECRET'\nconst DEFAULT_CALLBACK_PATH = '/api/auth/callback'\nconst THIRTY_DAYS = 30 * 24 * 60 * 60\n\nexport interface SonorSsoConfig {\n /**\n * The gated area(s). Every path equal to or under one of these needs a\n * session. The first entry is where a sign-in lands by default.\n */\n paths: string | string[]\n /** Default `${paths[0]}/login`. Always reachable without a session. */\n loginPath?: string\n /** Other gated paths reachable without a session. */\n publicPaths?: string[]\n /**\n * Gated API prefixes (e.g. `/api/studio`). Without a session they answer\n * 401 JSON instead of redirecting to the login page. Only useful when the\n * middleware matcher includes them; handlers must still check the session\n * themselves.\n */\n apiPaths?: string[]\n /** Where the callback route lives. Default `/api/auth/callback`. */\n callbackPath?: string\n /** Default `__Host-sonor-session`. Keep a site's old name to keep its users signed in. */\n cookieName?: string\n /** Default 30 days. */\n sessionLifetimeSeconds?: number\n /** Env var holding the signing secret. Default `SONOR_SESSION_SECRET`. */\n secretEnv?: string\n /**\n * Emails that get `isAdmin: true` on their session identity (comma string or\n * list, case-insensitive). Computed on every read from the verified cookie,\n * never stored in it. Everyone Sonor lets in still gets in; this only marks\n * who is an admin within the area.\n */\n adminEmails?: string | string[]\n /**\n * Outside production the gate lets everyone through as this identity, so\n * local dev works without a round trip to production Sonor. `false` makes\n * dev enforce the gate too. Default email: `SONOR_SSO_DEV_EMAIL`, else\n * `dev@localhost`.\n */\n devBypass?: false | { email?: string }\n /** Public origin for the callback URL. Default `NEXT_PUBLIC_SITE_URL`. */\n siteUrl?: string\n /** Default `NEXT_PUBLIC_SONOR_APP_URL`, else https://app.sonor.io. */\n appUrl?: string\n /** Default `SONOR_API_URL`, else https://api.sonor.io. */\n apiUrl?: string\n}\n\nexport interface SonorIdentity extends SonorSession {\n isAdmin: boolean\n /** True only for the non-production dev bypass. */\n isDevBypass: boolean\n}\n\nexport const SSO_ERROR_MESSAGES: Record<string, string> = {\n missing_token: 'The sign-in link was incomplete. Try again.',\n invalid_token: 'The sign-in link was invalid or expired. Try again.',\n invalid_or_expired: 'The sign-in link expired before it was used. Try again.',\n project_mismatch: 'That sign-in link was for a different site. Try again from the button below.',\n verify_failed: 'Sonor could not verify the sign-in link.',\n sonor_unreachable: 'Could not reach Sonor to verify sign-in. Check your connection and try again.',\n server_config: 'This site is missing its Sonor sign-in configuration.',\n}\n\nconst projectIdCache = new Map<string, Promise<string>>()\n\nfunction trimSlash(url: string): string {\n return url.replace(/\\/+$/, '')\n}\n\nfunction normalizePath(p: string): string {\n const withSlash = p.startsWith('/') ? p : `/${p}`\n return withSlash.length > 1 ? withSlash.replace(/\\/+$/, '') : withSlash\n}\n\nexport function createSonorSso(config: SonorSsoConfig) {\n const paths = (Array.isArray(config.paths) ? config.paths : [config.paths]).map(normalizePath)\n if (!paths.length || paths.some((p) => p === '/')) {\n throw new Error('@sonordev/site-kit/admin-auth: `paths` must name at least one area below the site root')\n }\n const home = paths[0]\n const loginPath = normalizePath(config.loginPath ?? `${home}/login`)\n const publicPaths = [loginPath, ...(config.publicPaths ?? []).map(normalizePath)]\n const apiPaths = (config.apiPaths ?? []).map(normalizePath)\n const callbackPath = normalizePath(config.callbackPath ?? DEFAULT_CALLBACK_PATH)\n const cookieName = config.cookieName ?? DEFAULT_COOKIE_NAME\n const lifetime = config.sessionLifetimeSeconds ?? THIRTY_DAYS\n const secretEnv = config.secretEnv ?? DEFAULT_SECRET_ENV\n\n const under = (pathname: string, base: string) => pathname === base || pathname.startsWith(`${base}/`)\n const isProtectedPath = (pathname: string) => paths.some((p) => under(pathname, p))\n const isPublicPath = (pathname: string) => publicPaths.some((p) => under(pathname, p))\n const isApiPath = (pathname: string) => apiPaths.some((p) => under(pathname, p))\n\n const secret = () => process.env[secretEnv]\n const apiUrl = () => trimSlash(config.apiUrl || process.env.SONOR_API_URL || DEFAULT_API_URL)\n const appUrl = () => trimSlash(config.appUrl || process.env.NEXT_PUBLIC_SONOR_APP_URL || DEFAULT_APP_URL)\n const isProduction = () => process.env.NODE_ENV === 'production'\n const devBypassEmail = () =>\n config.devBypass === false\n ? null\n : config.devBypass?.email || process.env.SONOR_SSO_DEV_EMAIL || 'dev@localhost'\n\n const adminSet = (): Set<string> => {\n const raw = config.adminEmails\n const list = Array.isArray(raw) ? raw : (raw ?? '').split(',')\n return new Set(list.map((e) => e.trim().toLowerCase()).filter(Boolean))\n }\n const toIdentity = (s: SonorSession, isDevBypass = false): SonorIdentity => ({\n ...s,\n isAdmin: adminSet().has(s.email.trim().toLowerCase()),\n isDevBypass,\n })\n const devIdentity = (): SonorIdentity | null => {\n const email = !isProduction() ? devBypassEmail() : null\n return email ? toIdentity({ email, contactId: `dev-${email}`, iat: 0, exp: 0 }, true) : null\n }\n\n /**\n * A safe post-sign-in destination: a same-origin path inside a gated area.\n * Anything else (absolute URLs, `//host`, backslashes, paths outside the\n * area) falls back to the area's home, so the callback can't be used as an\n * open redirect.\n */\n function sanitizeReturnTo(value: string | null | undefined): string {\n if (!value || typeof value !== 'string') return home\n if (!value.startsWith('/') || value.startsWith('//') || /[\\\\\u0000-\u001f]/.test(value)) return home\n const pathname = value.split(/[?#]/, 1)[0]\n return isProtectedPath(pathname) && !under(pathname, loginPath) ? value : home\n }\n\n function passThrough(request: NextRequest): NextResponse {\n // x-next-pathname on the REQUEST lets an admin layout know where it is.\n const headers = new Headers(request.headers)\n headers.set('x-next-pathname', request.nextUrl.pathname)\n const res = NextResponse.next({ request: { headers } })\n res.headers.set('X-Robots-Tag', 'noindex, nofollow')\n return res\n }\n\n /**\n * Middleware gate — pass as `createProxy({ before: sso.gate })`. Returns\n * undefined outside the gated area so the rest of the proxy (redirects, AI\n * discovery headers) still runs for public pages.\n */\n async function gate(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname, search } = request.nextUrl\n if (isApiPath(pathname)) {\n if (devIdentity() || (await getRequestSession(request))) return NextResponse.next()\n return NextResponse.json({ error: 'unauthorized' }, { status: 401, headers: { 'Cache-Control': 'no-store' } })\n }\n if (!isProtectedPath(pathname) && !isPublicPath(pathname)) return undefined\n if (isPublicPath(pathname) || devIdentity()) return passThrough(request)\n\n if (await getRequestSession(request)) return passThrough(request)\n\n const login = new URL(loginPath, request.url)\n if (pathname !== home || search) login.searchParams.set('return_to', pathname + search)\n const res = NextResponse.redirect(login)\n res.headers.set('X-Robots-Tag', 'noindex, nofollow')\n return res\n }\n\n /**\n * The identity carried by a request's cookie, for code that has the request\n * in hand (middleware, route handlers). Includes the dev bypass.\n */\n async function getRequestSession(request: NextRequest): Promise<SonorIdentity | null> {\n const dev = devIdentity()\n if (dev) return dev\n const session = await verifySessionToken(request.cookies.get(cookieName)?.value, secret())\n return session ? toIdentity(session) : null\n }\n\n /**\n * The signed-in identity for this request, or null. Use it in every server\n * component, server action and API route that exposes gated data.\n */\n async function getSession(): Promise<SonorIdentity | null> {\n const dev = devIdentity()\n if (dev) return dev\n const { cookies } = await import('next/headers')\n const jar = await cookies()\n const session = await verifySessionToken(jar.get(cookieName)?.value, secret())\n return session ? toIdentity(session) : null\n }\n\n async function resolveProjectId(): Promise<string> {\n const apiKey = process.env.SONOR_API_KEY\n if (!apiKey) throw new Error('SONOR_API_KEY is not set')\n const cacheKey = `${apiUrl()}|${apiKey}`\n let pending = projectIdCache.get(cacheKey)\n if (!pending) {\n pending = (async () => {\n const res = await fetch(`${apiUrl()}/api/public/auth/verify`, {\n headers: { 'x-api-key': apiKey },\n cache: 'no-store',\n signal: AbortSignal.timeout(10_000),\n })\n if (!res.ok) throw new Error(`Sonor verify returned ${res.status}`)\n const data = (await res.json()) as { project_id?: string }\n if (!data.project_id) throw new Error('Sonor verify returned no project_id')\n return data.project_id\n })()\n projectIdCache.set(cacheKey, pending)\n pending.catch(() => projectIdCache.delete(cacheKey))\n }\n return pending\n }\n\n /**\n * The \"Sign in with Sonor\" URL for the login page. Pass the page's\n * `return_to` search param through; it's sanitized here. Throws when the\n * site can't identify its project (no API key, Sonor unreachable) — catch\n * it and show the error rather than a dead button.\n */\n async function getLoginUrl(options: { returnTo?: string | null; siteUrl?: string } = {}): Promise<string> {\n const site = options.siteUrl || config.siteUrl || process.env.NEXT_PUBLIC_SITE_URL\n if (!site) throw new Error('NEXT_PUBLIC_SITE_URL (or siteUrl) is required to build the Sonor sign-in link')\n const callback = new URL(callbackPath, site)\n if (options.returnTo) {\n const safe = sanitizeReturnTo(options.returnTo)\n if (safe !== home) callback.searchParams.set('return_to', safe)\n }\n const grant = new URL('/sso/grant', `${appUrl()}/`)\n grant.searchParams.set('project_id', await resolveProjectId())\n grant.searchParams.set('return_to', callback.toString())\n return grant.toString()\n }\n\n function loginRedirect(request: NextRequest, error?: string): NextResponse {\n const url = new URL(loginPath, request.url)\n if (error) url.searchParams.set('error', error)\n const res = NextResponse.redirect(url)\n res.headers.set('Cache-Control', 'no-store')\n return res\n }\n\n /** `export const GET = sso.handleCallback` in the callback route. */\n async function handleCallback(request: NextRequest): Promise<NextResponse> {\n const token = request.nextUrl.searchParams.get('token')\n if (!token) return loginRedirect(request, 'missing_token')\n\n const apiKey = process.env.SONOR_API_KEY\n if (!apiKey) {\n console.error('[sonor-sso] SONOR_API_KEY is not set; cannot verify the sign-in token')\n return loginRedirect(request, 'server_config')\n }\n\n let res: Response\n try {\n res = await fetch(`${apiUrl()}/api/public/auth/verify-sso-token`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', 'x-api-key': apiKey },\n body: JSON.stringify({ token }),\n cache: 'no-store',\n signal: AbortSignal.timeout(10_000),\n })\n } catch (err) {\n console.error('[sonor-sso] verify request failed:', err)\n return loginRedirect(request, 'sonor_unreachable')\n }\n if (!res.ok) {\n console.error(`[sonor-sso] verify returned ${res.status}`)\n return loginRedirect(request, 'verify_failed')\n }\n\n const data = (await res.json().catch(() => null)) as\n | { valid?: boolean; email?: string; contactId?: string; reason?: string }\n | null\n if (!data?.valid || !data.email || !data.contactId) {\n return loginRedirect(request, data?.reason || 'invalid_token')\n }\n\n let sessionToken: string\n try {\n sessionToken = await signSession({ email: data.email, contactId: data.contactId }, secret() ?? '', lifetime)\n } catch (err) {\n console.error(`[sonor-sso] could not sign the session (check ${secretEnv}):`, err)\n return loginRedirect(request, 'server_config')\n }\n\n const destination = sanitizeReturnTo(request.nextUrl.searchParams.get('return_to'))\n const out = NextResponse.redirect(new URL(destination, request.url))\n out.cookies.set(cookieName, sessionToken, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n path: '/',\n maxAge: lifetime,\n })\n // The token rode in on the URL: keep it out of Referer headers and caches.\n out.headers.set('Referrer-Policy', 'no-referrer')\n out.headers.set('Cache-Control', 'no-store')\n return out\n }\n\n /** `export const GET = sso.handleLogout` (and POST) in the logout route. */\n function handleLogout(request: NextRequest): NextResponse {\n const url = new URL(loginPath, request.url)\n url.searchParams.set('signed_out', '1')\n // 303 so a POST from a sign-out form lands as a GET on the login page.\n const res = NextResponse.redirect(url, 303)\n res.cookies.set(cookieName, '', { httpOnly: true, secure: true, sameSite: 'lax', path: '/', maxAge: 0 })\n res.headers.set('Cache-Control', 'no-store')\n return res\n }\n\n return {\n gate,\n getSession,\n getRequestSession,\n getLoginUrl,\n handleCallback,\n handleLogout,\n sanitizeReturnTo,\n isProtectedPath,\n cookieName,\n loginPath,\n homePath: home,\n }\n}\n\nexport type SonorSso = ReturnType<typeof createSonorSso>\n"]}
Binary file
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/admin-auth/session.ts","../../src/admin-auth/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Signed session cookie for Sonor SSO-gated site areas.\n *\n * Format: `<base64url(payload)>.<base64url(HMAC-SHA256(payloadB64))>`, payload\n * `{ email, contactId, iat, exp }` in Unix seconds. This is byte-compatible with\n * the hand-rolled Web Crypto sessions legacy-clay-classic and the CCG builder\n * shipped before this module existed, so a site that passes its old cookie name\n * and secret keeps every signed-in user signed in across the switch.\n *\n * Web Crypto only (never Node `crypto`): these helpers run in Edge middleware\n * and in Node route handlers alike.\n */\n\nexport interface SonorSession {\n email: string\n contactId: string\n /** Unix seconds */\n iat: number\n /** Unix seconds */\n exp: number\n}\n\nexport const MIN_SECRET_LENGTH = 32\n\nconst encoder = new TextEncoder()\nconst keyCache = new Map<string, Promise<CryptoKey>>()\n\nfunction hmacKey(secret: string): Promise<CryptoKey> {\n let key = keyCache.get(secret)\n if (!key) {\n key = crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n )\n keyCache.set(secret, key)\n }\n return key\n}\n\nfunction bytesToB64url(bytes: Uint8Array): string {\n let bin = ''\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction b64urlToBytes(s: string): Uint8Array<ArrayBuffer> | null {\n if (!/^[A-Za-z0-9_-]*$/.test(s)) return null\n try {\n const bin = atob(s.replace(/-/g, '+').replace(/_/g, '/') + '==='.slice((s.length + 3) % 4))\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n return bytes\n } catch {\n return null\n }\n}\n\nfunction assertSecret(secret: string | undefined): string {\n if (!secret || secret.length < MIN_SECRET_LENGTH) {\n throw new Error(\n `@sonordev/site-kit/admin-auth: the session secret must be at least ${MIN_SECRET_LENGTH} characters (try \\`openssl rand -hex 32\\`)`,\n )\n }\n return secret\n}\n\nexport async function signSession(\n identity: { email: string; contactId: string },\n secret: string,\n lifetimeSeconds: number,\n now: number = Math.floor(Date.now() / 1000),\n): Promise<string> {\n const payload: SonorSession = {\n email: identity.email,\n contactId: identity.contactId,\n iat: now,\n exp: now + lifetimeSeconds,\n }\n const payloadB64 = bytesToB64url(encoder.encode(JSON.stringify(payload)))\n const key = await hmacKey(assertSecret(secret))\n const sig = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(payloadB64)))\n return `${payloadB64}.${bytesToB64url(sig)}`\n}\n\n/**\n * Returns the session, or null for anything short of a valid, unexpired,\n * correctly signed token — including a missing or short secret, so a\n * misconfigured deploy sends people to the login page instead of 500-ing.\n */\nexport async function verifySessionToken(\n token: string | null | undefined,\n secret: string | undefined,\n now: number = Math.floor(Date.now() / 1000),\n): Promise<SonorSession | null> {\n if (!token || typeof token !== 'string') return null\n const dot = token.indexOf('.')\n if (dot < 1 || dot !== token.lastIndexOf('.')) return null\n const payloadB64 = token.slice(0, dot)\n const sig = b64urlToBytes(token.slice(dot + 1))\n if (!sig) return null\n\n let key: CryptoKey\n try {\n key = await hmacKey(assertSecret(secret))\n } catch (err) {\n console.error(String(err instanceof Error ? err.message : err))\n return null\n }\n\n // subtle.verify is the constant-time comparison.\n const ok = await crypto.subtle.verify('HMAC', key, sig, encoder.encode(payloadB64))\n if (!ok) return null\n\n const raw = b64urlToBytes(payloadB64)\n if (!raw) return null\n try {\n const p = JSON.parse(new TextDecoder().decode(raw)) as Partial<SonorSession>\n if (\n typeof p.email !== 'string' ||\n typeof p.contactId !== 'string' ||\n typeof p.iat !== 'number' ||\n typeof p.exp !== 'number' ||\n p.exp < now\n ) {\n return null\n }\n return { email: p.email, contactId: p.contactId, iat: p.iat, exp: p.exp }\n } catch {\n return null\n }\n}\n","/**\n * @sonordev/site-kit/admin-auth — \"Sign in with Sonor\" for a site's own admin area\n *\n * SINGLE SOURCE OF TRUTH for the Sonor SSO handshake on client sites. Before\n * this module, 4m-lawn-care (/bid, /route), legacy-clay-classic (/admin) and the\n * CCG builder (/studio) each carried a hand-copied version, and the copies had\n * already drifted (Node vs Web Crypto, 7 vs 30 day sessions, roles in one only).\n * Do not re-fork it into a site.\n *\n * The handshake:\n * 1. `/admin/login` links to `app.sonor.io/sso/grant?project_id=…&return_to=<callback>`.\n * 2. Sonor checks the signed-in user can access the project (owner, member,\n * managing agency, or platform admin) and redirects to the callback with a\n * short-lived token.\n * 3. The callback posts the token to `/api/public/auth/verify-sso-token` with\n * the project API key; Sonor rejects tokens issued for any other project.\n * 4. The callback sets a signed, httpOnly session cookie (30 days default).\n *\n * Wiring (four files):\n *\n * // lib/sonor-sso.ts\n * export const sso = createSonorSso({ paths: '/admin' })\n *\n * // middleware.ts (or proxy.ts)\n * export default createProxy({ before: sso.gate, securityHeaders: true })\n *\n * // app/api/auth/callback/route.ts\n * export const GET = sso.handleCallback\n *\n * // app/api/auth/logout/route.ts\n * export const GET = sso.handleLogout\n * export const POST = sso.handleLogout\n *\n * Then `await sso.getLoginUrl()` in the login page, and `await sso.getSession()`\n * in every page, server action and API route that exposes admin data. The\n * middleware gate never sees `/api/*` (the standard matcher excludes it), so\n * API routes MUST check `getSession()` themselves.\n *\n * Env: SONOR_API_KEY (already set on every site), SONOR_SESSION_SECRET (>= 32\n * chars; rotating it signs everyone out), NEXT_PUBLIC_SITE_URL.\n */\n\nimport { NextResponse, type NextRequest } from 'next/server'\nimport { signSession, verifySessionToken, type SonorSession } from './session'\n\nexport type { SonorSession } from './session'\nexport { signSession, verifySessionToken } from './session'\n\nconst DEFAULT_APP_URL = 'https://app.sonor.io'\nconst DEFAULT_API_URL = 'https://api.sonor.io'\nconst DEFAULT_COOKIE_NAME = '__Host-sonor-session'\nconst DEFAULT_SECRET_ENV = 'SONOR_SESSION_SECRET'\nconst DEFAULT_CALLBACK_PATH = '/api/auth/callback'\nconst THIRTY_DAYS = 30 * 24 * 60 * 60\n\nexport interface SonorSsoConfig {\n /**\n * The gated area(s). Every path equal to or under one of these needs a\n * session. The first entry is where a sign-in lands by default.\n */\n paths: string | string[]\n /** Default `${paths[0]}/login`. Always reachable without a session. */\n loginPath?: string\n /** Other gated paths reachable without a session. */\n publicPaths?: string[]\n /** Where the callback route lives. Default `/api/auth/callback`. */\n callbackPath?: string\n /** Default `__Host-sonor-session`. Keep a site's old name to keep its users signed in. */\n cookieName?: string\n /** Default 30 days. */\n sessionLifetimeSeconds?: number\n /** Env var holding the signing secret. Default `SONOR_SESSION_SECRET`. */\n secretEnv?: string\n /**\n * Emails that get `isAdmin: true` on their session identity (comma string or\n * list, case-insensitive). Computed on every read from the verified cookie,\n * never stored in it. Everyone Sonor lets in still gets in; this only marks\n * who is an admin within the area.\n */\n adminEmails?: string | string[]\n /**\n * Outside production the gate lets everyone through as this identity, so\n * local dev works without a round trip to production Sonor. `false` makes\n * dev enforce the gate too. Default email: `SONOR_SSO_DEV_EMAIL`, else\n * `dev@localhost`.\n */\n devBypass?: false | { email?: string }\n /** Public origin for the callback URL. Default `NEXT_PUBLIC_SITE_URL`. */\n siteUrl?: string\n /** Default `NEXT_PUBLIC_SONOR_APP_URL`, else https://app.sonor.io. */\n appUrl?: string\n /** Default `SONOR_API_URL`, else https://api.sonor.io. */\n apiUrl?: string\n}\n\nexport interface SonorIdentity extends SonorSession {\n isAdmin: boolean\n /** True only for the non-production dev bypass. */\n isDevBypass: boolean\n}\n\nexport const SSO_ERROR_MESSAGES: Record<string, string> = {\n missing_token: 'The sign-in link was incomplete. Try again.',\n invalid_token: 'The sign-in link was invalid or expired. Try again.',\n invalid_or_expired: 'The sign-in link expired before it was used. Try again.',\n project_mismatch: 'That sign-in link was for a different site. Try again from the button below.',\n verify_failed: 'Sonor could not verify the sign-in link.',\n sonor_unreachable: 'Could not reach Sonor to verify sign-in. Check your connection and try again.',\n server_config: 'This site is missing its Sonor sign-in configuration.',\n}\n\nconst projectIdCache = new Map<string, Promise<string>>()\n\nfunction trimSlash(url: string): string {\n return url.replace(/\\/+$/, '')\n}\n\nfunction normalizePath(p: string): string {\n const withSlash = p.startsWith('/') ? p : `/${p}`\n return withSlash.length > 1 ? withSlash.replace(/\\/+$/, '') : withSlash\n}\n\nexport function createSonorSso(config: SonorSsoConfig) {\n const paths = (Array.isArray(config.paths) ? config.paths : [config.paths]).map(normalizePath)\n if (!paths.length || paths.some((p) => p === '/')) {\n throw new Error('@sonordev/site-kit/admin-auth: `paths` must name at least one area below the site root')\n }\n const home = paths[0]\n const loginPath = normalizePath(config.loginPath ?? `${home}/login`)\n const publicPaths = [loginPath, ...(config.publicPaths ?? []).map(normalizePath)]\n const callbackPath = normalizePath(config.callbackPath ?? DEFAULT_CALLBACK_PATH)\n const cookieName = config.cookieName ?? DEFAULT_COOKIE_NAME\n const lifetime = config.sessionLifetimeSeconds ?? THIRTY_DAYS\n const secretEnv = config.secretEnv ?? DEFAULT_SECRET_ENV\n\n const under = (pathname: string, base: string) => pathname === base || pathname.startsWith(`${base}/`)\n const isProtectedPath = (pathname: string) => paths.some((p) => under(pathname, p))\n const isPublicPath = (pathname: string) => publicPaths.some((p) => under(pathname, p))\n\n const secret = () => process.env[secretEnv]\n const apiUrl = () => trimSlash(config.apiUrl || process.env.SONOR_API_URL || DEFAULT_API_URL)\n const appUrl = () => trimSlash(config.appUrl || process.env.NEXT_PUBLIC_SONOR_APP_URL || DEFAULT_APP_URL)\n const isProduction = () => process.env.NODE_ENV === 'production'\n const devBypassEmail = () =>\n config.devBypass === false\n ? null\n : config.devBypass?.email || process.env.SONOR_SSO_DEV_EMAIL || 'dev@localhost'\n\n const adminSet = (): Set<string> => {\n const raw = config.adminEmails\n const list = Array.isArray(raw) ? raw : (raw ?? '').split(',')\n return new Set(list.map((e) => e.trim().toLowerCase()).filter(Boolean))\n }\n const toIdentity = (s: SonorSession, isDevBypass = false): SonorIdentity => ({\n ...s,\n isAdmin: adminSet().has(s.email.trim().toLowerCase()),\n isDevBypass,\n })\n const devIdentity = (): SonorIdentity | null => {\n const email = !isProduction() ? devBypassEmail() : null\n return email ? toIdentity({ email, contactId: `dev-${email}`, iat: 0, exp: 0 }, true) : null\n }\n\n /**\n * A safe post-sign-in destination: a same-origin path inside a gated area.\n * Anything else (absolute URLs, `//host`, backslashes, paths outside the\n * area) falls back to the area's home, so the callback can't be used as an\n * open redirect.\n */\n function sanitizeReturnTo(value: string | null | undefined): string {\n if (!value || typeof value !== 'string') return home\n if (!value.startsWith('/') || value.startsWith('//') || /[\\\\\u0000-\u001f]/.test(value)) return home\n const pathname = value.split(/[?#]/, 1)[0]\n return isProtectedPath(pathname) && !under(pathname, loginPath) ? value : home\n }\n\n function passThrough(request: NextRequest): NextResponse {\n // x-next-pathname on the REQUEST lets an admin layout know where it is.\n const headers = new Headers(request.headers)\n headers.set('x-next-pathname', request.nextUrl.pathname)\n const res = NextResponse.next({ request: { headers } })\n res.headers.set('X-Robots-Tag', 'noindex, nofollow')\n return res\n }\n\n /**\n * Middleware gate — pass as `createProxy({ before: sso.gate })`. Returns\n * undefined outside the gated area so the rest of the proxy (redirects, AI\n * discovery headers) still runs for public pages.\n */\n async function gate(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname, search } = request.nextUrl\n if (!isProtectedPath(pathname) && !isPublicPath(pathname)) return undefined\n if (isPublicPath(pathname) || devIdentity()) return passThrough(request)\n\n const session = await verifySessionToken(request.cookies.get(cookieName)?.value, secret())\n if (session) return passThrough(request)\n\n const login = new URL(loginPath, request.url)\n if (pathname !== home || search) login.searchParams.set('return_to', pathname + search)\n const res = NextResponse.redirect(login)\n res.headers.set('X-Robots-Tag', 'noindex, nofollow')\n return res\n }\n\n /**\n * The signed-in identity for this request, or null. Use it in every server\n * component, server action and API route that exposes gated data.\n */\n async function getSession(): Promise<SonorIdentity | null> {\n const dev = devIdentity()\n if (dev) return dev\n const { cookies } = await import('next/headers')\n const jar = await cookies()\n const session = await verifySessionToken(jar.get(cookieName)?.value, secret())\n return session ? toIdentity(session) : null\n }\n\n async function resolveProjectId(): Promise<string> {\n const apiKey = process.env.SONOR_API_KEY\n if (!apiKey) throw new Error('SONOR_API_KEY is not set')\n const cacheKey = `${apiUrl()}|${apiKey}`\n let pending = projectIdCache.get(cacheKey)\n if (!pending) {\n pending = (async () => {\n const res = await fetch(`${apiUrl()}/api/public/auth/verify`, {\n headers: { 'x-api-key': apiKey },\n cache: 'no-store',\n signal: AbortSignal.timeout(10_000),\n })\n if (!res.ok) throw new Error(`Sonor verify returned ${res.status}`)\n const data = (await res.json()) as { project_id?: string }\n if (!data.project_id) throw new Error('Sonor verify returned no project_id')\n return data.project_id\n })()\n projectIdCache.set(cacheKey, pending)\n pending.catch(() => projectIdCache.delete(cacheKey))\n }\n return pending\n }\n\n /**\n * The \"Sign in with Sonor\" URL for the login page. Pass the page's\n * `return_to` search param through; it's sanitized here. Throws when the\n * site can't identify its project (no API key, Sonor unreachable) — catch\n * it and show the error rather than a dead button.\n */\n async function getLoginUrl(options: { returnTo?: string | null; siteUrl?: string } = {}): Promise<string> {\n const site = options.siteUrl || config.siteUrl || process.env.NEXT_PUBLIC_SITE_URL\n if (!site) throw new Error('NEXT_PUBLIC_SITE_URL (or siteUrl) is required to build the Sonor sign-in link')\n const callback = new URL(callbackPath, site)\n if (options.returnTo) {\n const safe = sanitizeReturnTo(options.returnTo)\n if (safe !== home) callback.searchParams.set('return_to', safe)\n }\n const grant = new URL('/sso/grant', `${appUrl()}/`)\n grant.searchParams.set('project_id', await resolveProjectId())\n grant.searchParams.set('return_to', callback.toString())\n return grant.toString()\n }\n\n function loginRedirect(request: NextRequest, error?: string): NextResponse {\n const url = new URL(loginPath, request.url)\n if (error) url.searchParams.set('error', error)\n const res = NextResponse.redirect(url)\n res.headers.set('Cache-Control', 'no-store')\n return res\n }\n\n /** `export const GET = sso.handleCallback` in the callback route. */\n async function handleCallback(request: NextRequest): Promise<NextResponse> {\n const token = request.nextUrl.searchParams.get('token')\n if (!token) return loginRedirect(request, 'missing_token')\n\n const apiKey = process.env.SONOR_API_KEY\n if (!apiKey) {\n console.error('[sonor-sso] SONOR_API_KEY is not set; cannot verify the sign-in token')\n return loginRedirect(request, 'server_config')\n }\n\n let res: Response\n try {\n res = await fetch(`${apiUrl()}/api/public/auth/verify-sso-token`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', 'x-api-key': apiKey },\n body: JSON.stringify({ token }),\n cache: 'no-store',\n signal: AbortSignal.timeout(10_000),\n })\n } catch (err) {\n console.error('[sonor-sso] verify request failed:', err)\n return loginRedirect(request, 'sonor_unreachable')\n }\n if (!res.ok) {\n console.error(`[sonor-sso] verify returned ${res.status}`)\n return loginRedirect(request, 'verify_failed')\n }\n\n const data = (await res.json().catch(() => null)) as\n | { valid?: boolean; email?: string; contactId?: string; reason?: string }\n | null\n if (!data?.valid || !data.email || !data.contactId) {\n return loginRedirect(request, data?.reason || 'invalid_token')\n }\n\n let sessionToken: string\n try {\n sessionToken = await signSession({ email: data.email, contactId: data.contactId }, secret() ?? '', lifetime)\n } catch (err) {\n console.error(`[sonor-sso] could not sign the session (check ${secretEnv}):`, err)\n return loginRedirect(request, 'server_config')\n }\n\n const destination = sanitizeReturnTo(request.nextUrl.searchParams.get('return_to'))\n const out = NextResponse.redirect(new URL(destination, request.url))\n out.cookies.set(cookieName, sessionToken, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n path: '/',\n maxAge: lifetime,\n })\n // The token rode in on the URL: keep it out of Referer headers and caches.\n out.headers.set('Referrer-Policy', 'no-referrer')\n out.headers.set('Cache-Control', 'no-store')\n return out\n }\n\n /** `export const GET = sso.handleLogout` (and POST) in the logout route. */\n function handleLogout(request: NextRequest): NextResponse {\n const url = new URL(loginPath, request.url)\n url.searchParams.set('signed_out', '1')\n // 303 so a POST from a sign-out form lands as a GET on the login page.\n const res = NextResponse.redirect(url, 303)\n res.cookies.set(cookieName, '', { httpOnly: true, secure: true, sameSite: 'lax', path: '/', maxAge: 0 })\n res.headers.set('Cache-Control', 'no-store')\n return res\n }\n\n return {\n gate,\n getSession,\n getLoginUrl,\n handleCallback,\n handleLogout,\n sanitizeReturnTo,\n isProtectedPath,\n cookieName,\n loginPath,\n homePath: home,\n }\n}\n\nexport type SonorSso = ReturnType<typeof createSonorSso>\n"]}
1
+ {"version":3,"sources":["../../src/admin-auth/session.ts","../../src/admin-auth/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Signed session cookie for Sonor SSO-gated site areas.\n *\n * Format: `<base64url(payload)>.<base64url(HMAC-SHA256(payloadB64))>`, payload\n * `{ email, contactId, iat, exp }` in Unix seconds. This is byte-compatible with\n * the hand-rolled Web Crypto sessions legacy-clay-classic and the CCG builder\n * shipped before this module existed, so a site that passes its old cookie name\n * and secret keeps every signed-in user signed in across the switch.\n *\n * Web Crypto only (never Node `crypto`): these helpers run in Edge middleware\n * and in Node route handlers alike.\n */\n\nexport interface SonorSession {\n email: string\n contactId: string\n /** Unix seconds */\n iat: number\n /** Unix seconds */\n exp: number\n}\n\nexport const MIN_SECRET_LENGTH = 32\n\nconst encoder = new TextEncoder()\nconst keyCache = new Map<string, Promise<CryptoKey>>()\n\nfunction hmacKey(secret: string): Promise<CryptoKey> {\n let key = keyCache.get(secret)\n if (!key) {\n key = crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n )\n keyCache.set(secret, key)\n }\n return key\n}\n\nfunction bytesToB64url(bytes: Uint8Array): string {\n let bin = ''\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction b64urlToBytes(s: string): Uint8Array<ArrayBuffer> | null {\n if (!/^[A-Za-z0-9_-]*$/.test(s)) return null\n try {\n const bin = atob(s.replace(/-/g, '+').replace(/_/g, '/') + '==='.slice((s.length + 3) % 4))\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n return bytes\n } catch {\n return null\n }\n}\n\nfunction assertSecret(secret: string | undefined): string {\n if (!secret || secret.length < MIN_SECRET_LENGTH) {\n throw new Error(\n `@sonordev/site-kit/admin-auth: the session secret must be at least ${MIN_SECRET_LENGTH} characters (try \\`openssl rand -hex 32\\`)`,\n )\n }\n return secret\n}\n\nexport async function signSession(\n identity: { email: string; contactId: string },\n secret: string,\n lifetimeSeconds: number,\n now: number = Math.floor(Date.now() / 1000),\n): Promise<string> {\n const payload: SonorSession = {\n email: identity.email,\n contactId: identity.contactId,\n iat: now,\n exp: now + lifetimeSeconds,\n }\n const payloadB64 = bytesToB64url(encoder.encode(JSON.stringify(payload)))\n const key = await hmacKey(assertSecret(secret))\n const sig = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(payloadB64)))\n return `${payloadB64}.${bytesToB64url(sig)}`\n}\n\n/**\n * Returns the session, or null for anything short of a valid, unexpired,\n * correctly signed token — including a missing or short secret, so a\n * misconfigured deploy sends people to the login page instead of 500-ing.\n */\nexport async function verifySessionToken(\n token: string | null | undefined,\n secret: string | undefined,\n now: number = Math.floor(Date.now() / 1000),\n): Promise<SonorSession | null> {\n if (!token || typeof token !== 'string') return null\n const dot = token.indexOf('.')\n if (dot < 1 || dot !== token.lastIndexOf('.')) return null\n const payloadB64 = token.slice(0, dot)\n const sig = b64urlToBytes(token.slice(dot + 1))\n if (!sig) return null\n\n let key: CryptoKey\n try {\n key = await hmacKey(assertSecret(secret))\n } catch (err) {\n console.error(String(err instanceof Error ? err.message : err))\n return null\n }\n\n // subtle.verify is the constant-time comparison.\n const ok = await crypto.subtle.verify('HMAC', key, sig, encoder.encode(payloadB64))\n if (!ok) return null\n\n const raw = b64urlToBytes(payloadB64)\n if (!raw) return null\n try {\n const p = JSON.parse(new TextDecoder().decode(raw)) as Partial<SonorSession>\n if (\n typeof p.email !== 'string' ||\n typeof p.contactId !== 'string' ||\n typeof p.iat !== 'number' ||\n typeof p.exp !== 'number' ||\n p.exp < now\n ) {\n return null\n }\n return { email: p.email, contactId: p.contactId, iat: p.iat, exp: p.exp }\n } catch {\n return null\n }\n}\n","/**\n * @sonordev/site-kit/admin-auth — \"Sign in with Sonor\" for a site's own admin area\n *\n * SINGLE SOURCE OF TRUTH for the Sonor SSO handshake on client sites. Before\n * this module, 4m-lawn-care (/bid, /route), legacy-clay-classic (/admin) and the\n * CCG builder (/studio) each carried a hand-copied version, and the copies had\n * already drifted (Node vs Web Crypto, 7 vs 30 day sessions, roles in one only).\n * Do not re-fork it into a site.\n *\n * The handshake:\n * 1. `/admin/login` links to `app.sonor.io/sso/grant?project_id=…&return_to=<callback>`.\n * 2. Sonor checks the signed-in user can access the project (owner, member,\n * managing agency, or platform admin) and redirects to the callback with a\n * short-lived token.\n * 3. The callback posts the token to `/api/public/auth/verify-sso-token` with\n * the project API key; Sonor rejects tokens issued for any other project.\n * 4. The callback sets a signed, httpOnly session cookie (30 days default).\n *\n * Wiring (four files):\n *\n * // lib/sonor-sso.ts\n * export const sso = createSonorSso({ paths: '/admin' })\n *\n * // middleware.ts (or proxy.ts)\n * export default createProxy({ before: sso.gate, securityHeaders: true })\n *\n * // app/api/auth/callback/route.ts\n * export const GET = sso.handleCallback\n *\n * // app/api/auth/logout/route.ts\n * export const GET = sso.handleLogout\n * export const POST = sso.handleLogout\n *\n * Then `await sso.getLoginUrl()` in the login page, and `await sso.getSession()`\n * in every page, server action and API route that exposes admin data. The\n * middleware gate never sees `/api/*` (the standard matcher excludes it), so\n * API routes MUST check `getSession()` themselves.\n *\n * Env: SONOR_API_KEY (already set on every site), SONOR_SESSION_SECRET (>= 32\n * chars; rotating it signs everyone out), NEXT_PUBLIC_SITE_URL.\n */\n\nimport { NextResponse, type NextRequest } from 'next/server'\nimport { signSession, verifySessionToken, type SonorSession } from './session'\n\nexport type { SonorSession } from './session'\nexport { signSession, verifySessionToken } from './session'\n\nconst DEFAULT_APP_URL = 'https://app.sonor.io'\nconst DEFAULT_API_URL = 'https://api.sonor.io'\nconst DEFAULT_COOKIE_NAME = '__Host-sonor-session'\nconst DEFAULT_SECRET_ENV = 'SONOR_SESSION_SECRET'\nconst DEFAULT_CALLBACK_PATH = '/api/auth/callback'\nconst THIRTY_DAYS = 30 * 24 * 60 * 60\n\nexport interface SonorSsoConfig {\n /**\n * The gated area(s). Every path equal to or under one of these needs a\n * session. The first entry is where a sign-in lands by default.\n */\n paths: string | string[]\n /** Default `${paths[0]}/login`. Always reachable without a session. */\n loginPath?: string\n /** Other gated paths reachable without a session. */\n publicPaths?: string[]\n /**\n * Gated API prefixes (e.g. `/api/studio`). Without a session they answer\n * 401 JSON instead of redirecting to the login page. Only useful when the\n * middleware matcher includes them; handlers must still check the session\n * themselves.\n */\n apiPaths?: string[]\n /** Where the callback route lives. Default `/api/auth/callback`. */\n callbackPath?: string\n /** Default `__Host-sonor-session`. Keep a site's old name to keep its users signed in. */\n cookieName?: string\n /** Default 30 days. */\n sessionLifetimeSeconds?: number\n /** Env var holding the signing secret. Default `SONOR_SESSION_SECRET`. */\n secretEnv?: string\n /**\n * Emails that get `isAdmin: true` on their session identity (comma string or\n * list, case-insensitive). Computed on every read from the verified cookie,\n * never stored in it. Everyone Sonor lets in still gets in; this only marks\n * who is an admin within the area.\n */\n adminEmails?: string | string[]\n /**\n * Outside production the gate lets everyone through as this identity, so\n * local dev works without a round trip to production Sonor. `false` makes\n * dev enforce the gate too. Default email: `SONOR_SSO_DEV_EMAIL`, else\n * `dev@localhost`.\n */\n devBypass?: false | { email?: string }\n /** Public origin for the callback URL. Default `NEXT_PUBLIC_SITE_URL`. */\n siteUrl?: string\n /** Default `NEXT_PUBLIC_SONOR_APP_URL`, else https://app.sonor.io. */\n appUrl?: string\n /** Default `SONOR_API_URL`, else https://api.sonor.io. */\n apiUrl?: string\n}\n\nexport interface SonorIdentity extends SonorSession {\n isAdmin: boolean\n /** True only for the non-production dev bypass. */\n isDevBypass: boolean\n}\n\nexport const SSO_ERROR_MESSAGES: Record<string, string> = {\n missing_token: 'The sign-in link was incomplete. Try again.',\n invalid_token: 'The sign-in link was invalid or expired. Try again.',\n invalid_or_expired: 'The sign-in link expired before it was used. Try again.',\n project_mismatch: 'That sign-in link was for a different site. Try again from the button below.',\n verify_failed: 'Sonor could not verify the sign-in link.',\n sonor_unreachable: 'Could not reach Sonor to verify sign-in. Check your connection and try again.',\n server_config: 'This site is missing its Sonor sign-in configuration.',\n}\n\nconst projectIdCache = new Map<string, Promise<string>>()\n\nfunction trimSlash(url: string): string {\n return url.replace(/\\/+$/, '')\n}\n\nfunction normalizePath(p: string): string {\n const withSlash = p.startsWith('/') ? p : `/${p}`\n return withSlash.length > 1 ? withSlash.replace(/\\/+$/, '') : withSlash\n}\n\nexport function createSonorSso(config: SonorSsoConfig) {\n const paths = (Array.isArray(config.paths) ? config.paths : [config.paths]).map(normalizePath)\n if (!paths.length || paths.some((p) => p === '/')) {\n throw new Error('@sonordev/site-kit/admin-auth: `paths` must name at least one area below the site root')\n }\n const home = paths[0]\n const loginPath = normalizePath(config.loginPath ?? `${home}/login`)\n const publicPaths = [loginPath, ...(config.publicPaths ?? []).map(normalizePath)]\n const apiPaths = (config.apiPaths ?? []).map(normalizePath)\n const callbackPath = normalizePath(config.callbackPath ?? DEFAULT_CALLBACK_PATH)\n const cookieName = config.cookieName ?? DEFAULT_COOKIE_NAME\n const lifetime = config.sessionLifetimeSeconds ?? THIRTY_DAYS\n const secretEnv = config.secretEnv ?? DEFAULT_SECRET_ENV\n\n const under = (pathname: string, base: string) => pathname === base || pathname.startsWith(`${base}/`)\n const isProtectedPath = (pathname: string) => paths.some((p) => under(pathname, p))\n const isPublicPath = (pathname: string) => publicPaths.some((p) => under(pathname, p))\n const isApiPath = (pathname: string) => apiPaths.some((p) => under(pathname, p))\n\n const secret = () => process.env[secretEnv]\n const apiUrl = () => trimSlash(config.apiUrl || process.env.SONOR_API_URL || DEFAULT_API_URL)\n const appUrl = () => trimSlash(config.appUrl || process.env.NEXT_PUBLIC_SONOR_APP_URL || DEFAULT_APP_URL)\n const isProduction = () => process.env.NODE_ENV === 'production'\n const devBypassEmail = () =>\n config.devBypass === false\n ? null\n : config.devBypass?.email || process.env.SONOR_SSO_DEV_EMAIL || 'dev@localhost'\n\n const adminSet = (): Set<string> => {\n const raw = config.adminEmails\n const list = Array.isArray(raw) ? raw : (raw ?? '').split(',')\n return new Set(list.map((e) => e.trim().toLowerCase()).filter(Boolean))\n }\n const toIdentity = (s: SonorSession, isDevBypass = false): SonorIdentity => ({\n ...s,\n isAdmin: adminSet().has(s.email.trim().toLowerCase()),\n isDevBypass,\n })\n const devIdentity = (): SonorIdentity | null => {\n const email = !isProduction() ? devBypassEmail() : null\n return email ? toIdentity({ email, contactId: `dev-${email}`, iat: 0, exp: 0 }, true) : null\n }\n\n /**\n * A safe post-sign-in destination: a same-origin path inside a gated area.\n * Anything else (absolute URLs, `//host`, backslashes, paths outside the\n * area) falls back to the area's home, so the callback can't be used as an\n * open redirect.\n */\n function sanitizeReturnTo(value: string | null | undefined): string {\n if (!value || typeof value !== 'string') return home\n if (!value.startsWith('/') || value.startsWith('//') || /[\\\\\u0000-\u001f]/.test(value)) return home\n const pathname = value.split(/[?#]/, 1)[0]\n return isProtectedPath(pathname) && !under(pathname, loginPath) ? value : home\n }\n\n function passThrough(request: NextRequest): NextResponse {\n // x-next-pathname on the REQUEST lets an admin layout know where it is.\n const headers = new Headers(request.headers)\n headers.set('x-next-pathname', request.nextUrl.pathname)\n const res = NextResponse.next({ request: { headers } })\n res.headers.set('X-Robots-Tag', 'noindex, nofollow')\n return res\n }\n\n /**\n * Middleware gate — pass as `createProxy({ before: sso.gate })`. Returns\n * undefined outside the gated area so the rest of the proxy (redirects, AI\n * discovery headers) still runs for public pages.\n */\n async function gate(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname, search } = request.nextUrl\n if (isApiPath(pathname)) {\n if (devIdentity() || (await getRequestSession(request))) return NextResponse.next()\n return NextResponse.json({ error: 'unauthorized' }, { status: 401, headers: { 'Cache-Control': 'no-store' } })\n }\n if (!isProtectedPath(pathname) && !isPublicPath(pathname)) return undefined\n if (isPublicPath(pathname) || devIdentity()) return passThrough(request)\n\n if (await getRequestSession(request)) return passThrough(request)\n\n const login = new URL(loginPath, request.url)\n if (pathname !== home || search) login.searchParams.set('return_to', pathname + search)\n const res = NextResponse.redirect(login)\n res.headers.set('X-Robots-Tag', 'noindex, nofollow')\n return res\n }\n\n /**\n * The identity carried by a request's cookie, for code that has the request\n * in hand (middleware, route handlers). Includes the dev bypass.\n */\n async function getRequestSession(request: NextRequest): Promise<SonorIdentity | null> {\n const dev = devIdentity()\n if (dev) return dev\n const session = await verifySessionToken(request.cookies.get(cookieName)?.value, secret())\n return session ? toIdentity(session) : null\n }\n\n /**\n * The signed-in identity for this request, or null. Use it in every server\n * component, server action and API route that exposes gated data.\n */\n async function getSession(): Promise<SonorIdentity | null> {\n const dev = devIdentity()\n if (dev) return dev\n const { cookies } = await import('next/headers')\n const jar = await cookies()\n const session = await verifySessionToken(jar.get(cookieName)?.value, secret())\n return session ? toIdentity(session) : null\n }\n\n async function resolveProjectId(): Promise<string> {\n const apiKey = process.env.SONOR_API_KEY\n if (!apiKey) throw new Error('SONOR_API_KEY is not set')\n const cacheKey = `${apiUrl()}|${apiKey}`\n let pending = projectIdCache.get(cacheKey)\n if (!pending) {\n pending = (async () => {\n const res = await fetch(`${apiUrl()}/api/public/auth/verify`, {\n headers: { 'x-api-key': apiKey },\n cache: 'no-store',\n signal: AbortSignal.timeout(10_000),\n })\n if (!res.ok) throw new Error(`Sonor verify returned ${res.status}`)\n const data = (await res.json()) as { project_id?: string }\n if (!data.project_id) throw new Error('Sonor verify returned no project_id')\n return data.project_id\n })()\n projectIdCache.set(cacheKey, pending)\n pending.catch(() => projectIdCache.delete(cacheKey))\n }\n return pending\n }\n\n /**\n * The \"Sign in with Sonor\" URL for the login page. Pass the page's\n * `return_to` search param through; it's sanitized here. Throws when the\n * site can't identify its project (no API key, Sonor unreachable) — catch\n * it and show the error rather than a dead button.\n */\n async function getLoginUrl(options: { returnTo?: string | null; siteUrl?: string } = {}): Promise<string> {\n const site = options.siteUrl || config.siteUrl || process.env.NEXT_PUBLIC_SITE_URL\n if (!site) throw new Error('NEXT_PUBLIC_SITE_URL (or siteUrl) is required to build the Sonor sign-in link')\n const callback = new URL(callbackPath, site)\n if (options.returnTo) {\n const safe = sanitizeReturnTo(options.returnTo)\n if (safe !== home) callback.searchParams.set('return_to', safe)\n }\n const grant = new URL('/sso/grant', `${appUrl()}/`)\n grant.searchParams.set('project_id', await resolveProjectId())\n grant.searchParams.set('return_to', callback.toString())\n return grant.toString()\n }\n\n function loginRedirect(request: NextRequest, error?: string): NextResponse {\n const url = new URL(loginPath, request.url)\n if (error) url.searchParams.set('error', error)\n const res = NextResponse.redirect(url)\n res.headers.set('Cache-Control', 'no-store')\n return res\n }\n\n /** `export const GET = sso.handleCallback` in the callback route. */\n async function handleCallback(request: NextRequest): Promise<NextResponse> {\n const token = request.nextUrl.searchParams.get('token')\n if (!token) return loginRedirect(request, 'missing_token')\n\n const apiKey = process.env.SONOR_API_KEY\n if (!apiKey) {\n console.error('[sonor-sso] SONOR_API_KEY is not set; cannot verify the sign-in token')\n return loginRedirect(request, 'server_config')\n }\n\n let res: Response\n try {\n res = await fetch(`${apiUrl()}/api/public/auth/verify-sso-token`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', 'x-api-key': apiKey },\n body: JSON.stringify({ token }),\n cache: 'no-store',\n signal: AbortSignal.timeout(10_000),\n })\n } catch (err) {\n console.error('[sonor-sso] verify request failed:', err)\n return loginRedirect(request, 'sonor_unreachable')\n }\n if (!res.ok) {\n console.error(`[sonor-sso] verify returned ${res.status}`)\n return loginRedirect(request, 'verify_failed')\n }\n\n const data = (await res.json().catch(() => null)) as\n | { valid?: boolean; email?: string; contactId?: string; reason?: string }\n | null\n if (!data?.valid || !data.email || !data.contactId) {\n return loginRedirect(request, data?.reason || 'invalid_token')\n }\n\n let sessionToken: string\n try {\n sessionToken = await signSession({ email: data.email, contactId: data.contactId }, secret() ?? '', lifetime)\n } catch (err) {\n console.error(`[sonor-sso] could not sign the session (check ${secretEnv}):`, err)\n return loginRedirect(request, 'server_config')\n }\n\n const destination = sanitizeReturnTo(request.nextUrl.searchParams.get('return_to'))\n const out = NextResponse.redirect(new URL(destination, request.url))\n out.cookies.set(cookieName, sessionToken, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n path: '/',\n maxAge: lifetime,\n })\n // The token rode in on the URL: keep it out of Referer headers and caches.\n out.headers.set('Referrer-Policy', 'no-referrer')\n out.headers.set('Cache-Control', 'no-store')\n return out\n }\n\n /** `export const GET = sso.handleLogout` (and POST) in the logout route. */\n function handleLogout(request: NextRequest): NextResponse {\n const url = new URL(loginPath, request.url)\n url.searchParams.set('signed_out', '1')\n // 303 so a POST from a sign-out form lands as a GET on the login page.\n const res = NextResponse.redirect(url, 303)\n res.cookies.set(cookieName, '', { httpOnly: true, secure: true, sameSite: 'lax', path: '/', maxAge: 0 })\n res.headers.set('Cache-Control', 'no-store')\n return res\n }\n\n return {\n gate,\n getSession,\n getRequestSession,\n getLoginUrl,\n handleCallback,\n handleLogout,\n sanitizeReturnTo,\n isProtectedPath,\n cookieName,\n loginPath,\n homePath: home,\n }\n}\n\nexport type SonorSso = ReturnType<typeof createSonorSso>\n"]}