@knocklabs/react-core 0.13.12 → 0.13.14

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.14
4
+
5
+ ### Patch Changes
6
+
7
+ - e6ac8cc: chore(deps): bump swr from 2.4.0 to 2.4.1
8
+ - abac967: Fix auth button cross-contamination when SlackKit and TeamsKit are rendered simultaneously. The shared `useAuthPostMessageListener` hook now checks whether its own popup is open before processing `authComplete` messages, preventing one integration's OAuth completion from incorrectly updating the other's connection state.
9
+ - Updated dependencies [454f947]
10
+ - @knocklabs/client@0.21.13
11
+
12
+ ## 0.13.13
13
+
14
+ ### Patch Changes
15
+
16
+ - ffa7bd2: [Guide] Remove guide toolbar v1 support
17
+ - Updated dependencies [ffa7bd2]
18
+ - @knocklabs/client@0.21.12
19
+
3
20
  ## 0.13.12
4
21
 
5
22
  ### Patch Changes
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("react");function y(e){return typeof e=="object"&&e!==null&&"type"in e?e.type:e}function p(e){if(typeof e=="object"&&e!==null&&"nonce"in e){const o=e.nonce;return typeof o=="string"?o:void 0}}function a(e){const{knockHost:o,popupWindowRef:r,setConnectionStatus:t,onAuthenticationComplete:s,nonceStorageKey:n}=e;d.useEffect(()=>{const i=()=>{r.current&&!r.current.closed&&r.current.close(),r.current=null},f=u=>{if(u.origin!==o)return;const c=y(u.data);if(c==="authComplete"){if(n){const g=p(u.data),l=sessionStorage.getItem(n);if(sessionStorage.removeItem(n),!g||l&&l!==g){t("error"),s==null||s("authFailed"),i();return}}t("connected"),s==null||s(c),i()}else c==="authFailed"&&(n&&sessionStorage.removeItem(n),t("error"),s==null||s(c),r.current=null)};return window.addEventListener("message",f,!1),()=>window.removeEventListener("message",f)},[o,s,t,r,n])}exports.useAuthPostMessageListener=a;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("react");function y(e){return typeof e=="object"&&e!==null&&"type"in e?e.type:e}function p(e){if(typeof e=="object"&&e!==null&&"nonce"in e){const t=e.nonce;return typeof t=="string"?t:void 0}}function a(e){const{knockHost:t,popupWindowRef:s,setConnectionStatus:o,onAuthenticationComplete:r,nonceStorageKey:n}=e;d.useEffect(()=>{const i=()=>{s.current&&!s.current.closed&&s.current.close(),s.current=null},f=u=>{if(u.origin!==t||!s.current)return;const c=y(u.data);if(c==="authComplete"){if(n){const g=p(u.data),l=sessionStorage.getItem(n);if(sessionStorage.removeItem(n),!g||l&&l!==g){o("error"),r==null||r("authFailed"),i();return}}o("connected"),r==null||r(c),i()}else c==="authFailed"&&(n&&sessionStorage.removeItem(n),o("error"),r==null||r(c),s.current=null)};return window.addEventListener("message",f,!1),()=>window.removeEventListener("message",f)},[t,r,o,s,n])}exports.useAuthPostMessageListener=a;
2
2
  //# sourceMappingURL=useAuthPostMessageListener.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAuthPostMessageListener.js","sources":["../../../../../src/modules/core/hooks/useAuthPostMessageListener.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { ConnectionStatus } from \"../types\";\n\nexport interface UseAuthPostMessageListenerOptions {\n knockHost: string;\n popupWindowRef: React.MutableRefObject<Window | null>;\n setConnectionStatus: (status: ConnectionStatus) => void;\n onAuthenticationComplete?: (authenticationResp: string) => void;\n /**\n * The sessionStorage key where the CSRF nonce was stored when the auth URL\n * was built. When provided, the listener will verify the nonce returned in\n * the postMessage payload matches the stored value.\n */\n nonceStorageKey?: string;\n}\n\n/**\n * Extracts the message type from a postMessage event data payload.\n * Supports both the legacy string format (\"authComplete\") and the new\n * structured format ({ type: \"authComplete\", nonce: \"...\" }).\n */\nfunction getMessageType(data: unknown): string {\n if (typeof data === \"object\" && data !== null && \"type\" in data) {\n return (data as { type: string }).type;\n }\n return data as string;\n}\n\n/**\n * Extracts the nonce from a structured postMessage event data payload.\n * Returns undefined for legacy string-format messages.\n */\nfunction getMessageNonce(data: unknown): string | undefined {\n if (typeof data === \"object\" && data !== null && \"nonce\" in data) {\n const nonce = (data as { nonce: unknown }).nonce;\n return typeof nonce === \"string\" ? nonce : undefined;\n }\n return undefined;\n}\n\n/**\n * Hook that listens for postMessage events from OAuth popup windows.\n *\n * Handles \"authComplete\" and \"authFailed\" messages sent from the OAuth flow popup,\n * validates the message origin, optionally verifies the CSRF nonce, updates\n * connection status, and closes the popup.\n *\n * @param options - Configuration options for the postMessage listener\n *\n * @example\n * ```tsx\n * useAuthPostMessageListener({\n * knockHost: knock.host,\n * popupWindowRef,\n * setConnectionStatus,\n * onAuthenticationComplete,\n * nonceStorageKey: \"knock:slack-auth-nonce:channel_123:user_1\",\n * });\n * ```\n */\nexport function useAuthPostMessageListener(\n options: UseAuthPostMessageListenerOptions,\n): void {\n const {\n knockHost,\n popupWindowRef,\n setConnectionStatus,\n onAuthenticationComplete,\n nonceStorageKey,\n } = options;\n\n useEffect(() => {\n const closePopup = () => {\n if (popupWindowRef.current && !popupWindowRef.current.closed) {\n popupWindowRef.current.close();\n }\n popupWindowRef.current = null;\n };\n\n const receiveMessage = (event: MessageEvent) => {\n // Validate message origin for security\n if (event.origin !== knockHost) {\n return;\n }\n\n const messageType = getMessageType(event.data);\n\n if (messageType === \"authComplete\") {\n // Verify CSRF nonce when a nonceStorageKey is configured.\n if (nonceStorageKey) {\n const returnedNonce = getMessageNonce(event.data);\n const storedNonce = sessionStorage.getItem(nonceStorageKey);\n sessionStorage.removeItem(nonceStorageKey);\n\n // If nonce already consumed by a prior handler invocation, then bail\n // out from checking again.\n if (\n !returnedNonce ||\n (storedNonce && storedNonce !== returnedNonce)\n ) {\n setConnectionStatus(\"error\");\n onAuthenticationComplete?.(\"authFailed\");\n closePopup();\n return;\n }\n }\n\n setConnectionStatus(\"connected\");\n onAuthenticationComplete?.(messageType);\n closePopup();\n } else if (messageType === \"authFailed\") {\n if (nonceStorageKey) {\n sessionStorage.removeItem(nonceStorageKey);\n }\n setConnectionStatus(\"error\");\n onAuthenticationComplete?.(messageType);\n popupWindowRef.current = null;\n }\n };\n\n window.addEventListener(\"message\", receiveMessage, false);\n return () => window.removeEventListener(\"message\", receiveMessage);\n }, [\n knockHost,\n onAuthenticationComplete,\n setConnectionStatus,\n popupWindowRef,\n nonceStorageKey,\n ]);\n}\n"],"names":["getMessageType","data","type","getMessageNonce","nonce","undefined","useAuthPostMessageListener","options","knockHost","popupWindowRef","setConnectionStatus","onAuthenticationComplete","nonceStorageKey","useEffect","closePopup","current","closed","close","receiveMessage","event","origin","messageType","returnedNonce","storedNonce","sessionStorage","getItem","removeItem","addEventListener","window","removeEventListener"],"mappings":"yGAsBA,SAASA,EAAeC,EAAuB,CAC7C,OAAI,OAAOA,GAAS,UAAYA,IAAS,MAAQ,SAAUA,EACjDA,EAA0BC,KAE7BD,CACT,CAMA,SAASE,EAAgBF,EAAmC,CAC1D,GAAI,OAAOA,GAAS,UAAYA,IAAS,MAAQ,UAAWA,EAAM,CAChE,MAAMG,EAASH,EAA4BG,MACpC,OAAA,OAAOA,GAAU,SAAWA,EAAQC,MAAAA,CAG/C,CAsBO,SAASC,EACdC,EACM,CACA,KAAA,CACJC,UAAAA,EACAC,eAAAA,EACAC,oBAAAA,EACAC,yBAAAA,EACAC,gBAAAA,CAAAA,EACEL,EAEJM,EAAAA,UAAU,IAAM,CACd,MAAMC,EAAaA,IAAM,CACnBL,EAAeM,SAAW,CAACN,EAAeM,QAAQC,QACpDP,EAAeM,QAAQE,MAAM,EAE/BR,EAAeM,QAAU,IAC3B,EAEMG,EAAkBC,GAAwB,CAE1CA,GAAAA,EAAMC,SAAWZ,EACnB,OAGIa,MAAAA,EAAcrB,EAAemB,EAAMlB,IAAI,EAE7C,GAAIoB,IAAgB,eAAgB,CAElC,GAAIT,EAAiB,CACbU,MAAAA,EAAgBnB,EAAgBgB,EAAMlB,IAAI,EAC1CsB,EAAcC,eAAeC,QAAQb,CAAe,EAK1D,GAJAY,eAAeE,WAAWd,CAAe,EAKvC,CAACU,GACAC,GAAeA,IAAgBD,EAChC,CACAZ,EAAoB,OAAO,EAC3BC,GAAAA,MAAAA,EAA2B,cAChBG,EAAA,EACX,MAAA,CACF,CAGFJ,EAAoB,WAAW,EAC/BC,GAAAA,MAAAA,EAA2BU,GAChBP,EAAA,CAAA,MACFO,IAAgB,eACrBT,GACFY,eAAeE,WAAWd,CAAe,EAE3CF,EAAoB,OAAO,EAC3BC,GAAAA,MAAAA,EAA2BU,GAC3BZ,EAAeM,QAAU,KAE7B,EAEOY,cAAAA,iBAAiB,UAAWT,EAAgB,EAAK,EACjD,IAAMU,OAAOC,oBAAoB,UAAWX,CAAc,CAAA,EAChE,CACDV,EACAG,EACAD,EACAD,EACAG,CAAe,CAChB,CACH"}
1
+ {"version":3,"file":"useAuthPostMessageListener.js","sources":["../../../../../src/modules/core/hooks/useAuthPostMessageListener.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { ConnectionStatus } from \"../types\";\n\nexport interface UseAuthPostMessageListenerOptions {\n knockHost: string;\n popupWindowRef: React.MutableRefObject<Window | null>;\n setConnectionStatus: (status: ConnectionStatus) => void;\n onAuthenticationComplete?: (authenticationResp: string) => void;\n /**\n * The sessionStorage key where the CSRF nonce was stored when the auth URL\n * was built. When provided, the listener will verify the nonce returned in\n * the postMessage payload matches the stored value.\n */\n nonceStorageKey?: string;\n}\n\n/**\n * Extracts the message type from a postMessage event data payload.\n * Supports both the legacy string format (\"authComplete\") and the new\n * structured format ({ type: \"authComplete\", nonce: \"...\" }).\n */\nfunction getMessageType(data: unknown): string {\n if (typeof data === \"object\" && data !== null && \"type\" in data) {\n return (data as { type: string }).type;\n }\n return data as string;\n}\n\n/**\n * Extracts the nonce from a structured postMessage event data payload.\n * Returns undefined for legacy string-format messages.\n */\nfunction getMessageNonce(data: unknown): string | undefined {\n if (typeof data === \"object\" && data !== null && \"nonce\" in data) {\n const nonce = (data as { nonce: unknown }).nonce;\n return typeof nonce === \"string\" ? nonce : undefined;\n }\n return undefined;\n}\n\n/**\n * Hook that listens for postMessage events from OAuth popup windows.\n *\n * Handles \"authComplete\" and \"authFailed\" messages sent from the OAuth flow popup,\n * validates the message origin, optionally verifies the CSRF nonce, updates\n * connection status, and closes the popup.\n *\n * @param options - Configuration options for the postMessage listener\n *\n * @example\n * ```tsx\n * useAuthPostMessageListener({\n * knockHost: knock.host,\n * popupWindowRef,\n * setConnectionStatus,\n * onAuthenticationComplete,\n * nonceStorageKey: \"knock:slack-auth-nonce:channel_123:user_1\",\n * });\n * ```\n */\nexport function useAuthPostMessageListener(\n options: UseAuthPostMessageListenerOptions,\n): void {\n const {\n knockHost,\n popupWindowRef,\n setConnectionStatus,\n onAuthenticationComplete,\n nonceStorageKey,\n } = options;\n\n useEffect(() => {\n const closePopup = () => {\n if (popupWindowRef.current && !popupWindowRef.current.closed) {\n popupWindowRef.current.close();\n }\n popupWindowRef.current = null;\n };\n\n const receiveMessage = (event: MessageEvent) => {\n // Validate message origin for security\n if (event.origin !== knockHost) {\n return;\n }\n\n // Ignore messages when this integration hasn't opened a popup\n if (!popupWindowRef.current) {\n return;\n }\n\n const messageType = getMessageType(event.data);\n\n if (messageType === \"authComplete\") {\n // Verify CSRF nonce when a nonceStorageKey is configured.\n if (nonceStorageKey) {\n const returnedNonce = getMessageNonce(event.data);\n const storedNonce = sessionStorage.getItem(nonceStorageKey);\n sessionStorage.removeItem(nonceStorageKey);\n\n // If nonce already consumed by a prior handler invocation, then bail\n // out from checking again.\n if (\n !returnedNonce ||\n (storedNonce && storedNonce !== returnedNonce)\n ) {\n setConnectionStatus(\"error\");\n onAuthenticationComplete?.(\"authFailed\");\n closePopup();\n return;\n }\n }\n\n setConnectionStatus(\"connected\");\n onAuthenticationComplete?.(messageType);\n closePopup();\n } else if (messageType === \"authFailed\") {\n if (nonceStorageKey) {\n sessionStorage.removeItem(nonceStorageKey);\n }\n setConnectionStatus(\"error\");\n onAuthenticationComplete?.(messageType);\n popupWindowRef.current = null;\n }\n };\n\n window.addEventListener(\"message\", receiveMessage, false);\n return () => window.removeEventListener(\"message\", receiveMessage);\n }, [\n knockHost,\n onAuthenticationComplete,\n setConnectionStatus,\n popupWindowRef,\n nonceStorageKey,\n ]);\n}\n"],"names":["getMessageType","data","type","getMessageNonce","nonce","undefined","useAuthPostMessageListener","options","knockHost","popupWindowRef","setConnectionStatus","onAuthenticationComplete","nonceStorageKey","useEffect","closePopup","current","closed","close","receiveMessage","event","origin","messageType","returnedNonce","storedNonce","sessionStorage","getItem","removeItem","addEventListener","window","removeEventListener"],"mappings":"yGAsBA,SAASA,EAAeC,EAAuB,CAC7C,OAAI,OAAOA,GAAS,UAAYA,IAAS,MAAQ,SAAUA,EACjDA,EAA0BC,KAE7BD,CACT,CAMA,SAASE,EAAgBF,EAAmC,CAC1D,GAAI,OAAOA,GAAS,UAAYA,IAAS,MAAQ,UAAWA,EAAM,CAChE,MAAMG,EAASH,EAA4BG,MACpC,OAAA,OAAOA,GAAU,SAAWA,EAAQC,MAAAA,CAG/C,CAsBO,SAASC,EACdC,EACM,CACA,KAAA,CACJC,UAAAA,EACAC,eAAAA,EACAC,oBAAAA,EACAC,yBAAAA,EACAC,gBAAAA,CAAAA,EACEL,EAEJM,EAAAA,UAAU,IAAM,CACd,MAAMC,EAAaA,IAAM,CACnBL,EAAeM,SAAW,CAACN,EAAeM,QAAQC,QACpDP,EAAeM,QAAQE,MAAM,EAE/BR,EAAeM,QAAU,IAC3B,EAEMG,EAAkBC,GAAwB,CAO1C,GALAA,EAAMC,SAAWZ,GAKjB,CAACC,EAAeM,QAClB,OAGIM,MAAAA,EAAcrB,EAAemB,EAAMlB,IAAI,EAE7C,GAAIoB,IAAgB,eAAgB,CAElC,GAAIT,EAAiB,CACbU,MAAAA,EAAgBnB,EAAgBgB,EAAMlB,IAAI,EAC1CsB,EAAcC,eAAeC,QAAQb,CAAe,EAK1D,GAJAY,eAAeE,WAAWd,CAAe,EAKvC,CAACU,GACAC,GAAeA,IAAgBD,EAChC,CACAZ,EAAoB,OAAO,EAC3BC,GAAAA,MAAAA,EAA2B,cAChBG,EAAA,EACX,MAAA,CACF,CAGFJ,EAAoB,WAAW,EAC/BC,GAAAA,MAAAA,EAA2BU,GAChBP,EAAA,CAAA,MACFO,IAAgB,eACrBT,GACFY,eAAeE,WAAWd,CAAe,EAE3CF,EAAoB,OAAO,EAC3BC,GAAAA,MAAAA,EAA2BU,GAC3BZ,EAAeM,QAAU,KAE7B,EAEOY,cAAAA,iBAAiB,UAAWT,EAAgB,EAAK,EACjD,IAAMU,OAAOC,oBAAoB,UAAWX,CAAc,CAAA,EAChE,CACDV,EACAG,EACAD,EACAD,EACAG,CAAe,CAChB,CACH"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=require("@knocklabs/client"),p=require("react"),m=require("../../core/context/KnockProvider.js"),v=require("../../core/hooks/useStableOptions.js");require("date-fns");function C(e){if(e&&typeof e=="object"&&"default"in e)return e;const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const t in e)if(t!=="default"){const o=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,o.get?o:{enumerable:!0,get:()=>e[t]})}}return n.default=e,Object.freeze(n)}const i=C(p),k=i.createContext(void 0),P=({channelId:e,readyToTarget:n,listenForUpdates:t=!0,colorMode:o="light",targetParams:K={},trackLocationFromWindow:u=!0,trackDebugParams:s=!1,orderResolutionDuration:l=0,throttleCheckInterval:f,children:g})=>{let c;try{c=m.useKnockClient()}catch{throw new Error("KnockGuideProvider must be used within a KnockProvider")}const a=v(K),r=i.useMemo(()=>new b.KnockGuideClient(c,e,a,{trackLocationFromWindow:u,trackDebugParams:s,orderResolutionDuration:l,throttleCheckInterval:f}),[c,e,a,u,s,l,f]);return i.useEffect(()=>{const d=b.KnockGuideClient.getToolbarRunConfigFromUrl();return n&&!d.isVisible&&(r.fetch(),t&&r.subscribe()),()=>r.cleanup()},[n,t,r]),i.createElement(k.Provider,{value:{client:r,colorMode:o}},g)};exports.KnockGuideContext=k;exports.KnockGuideProvider=P;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("@knocklabs/client"),K=require("react"),m=require("../../core/context/KnockProvider.js"),p=require("../../core/hooks/useStableOptions.js");require("date-fns");function P(e){if(e&&typeof e=="object"&&"default"in e)return e;const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const t in e)if(t!=="default"){const o=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,o.get?o:{enumerable:!0,get:()=>e[t]})}}return n.default=e,Object.freeze(n)}const c=P(K),b=c.createContext(void 0),v=({channelId:e,readyToTarget:n,listenForUpdates:t=!0,colorMode:o="light",targetParams:k={},trackLocationFromWindow:u=!0,orderResolutionDuration:s=0,throttleCheckInterval:l,children:g})=>{let i;try{i=m.useKnockClient()}catch{throw new Error("KnockGuideProvider must be used within a KnockProvider")}const a=p(k),r=c.useMemo(()=>new f.KnockGuideClient(i,e,a,{trackLocationFromWindow:u,orderResolutionDuration:s,throttleCheckInterval:l}),[i,e,a,u,s,l]);return c.useEffect(()=>{const d=f.KnockGuideClient.getToolbarRunConfigFromUrl();return n&&!d.isVisible&&(r.fetch(),t&&r.subscribe()),()=>r.cleanup()},[n,t,r]),c.createElement(b.Provider,{value:{client:r,colorMode:o}},g)};exports.KnockGuideContext=b;exports.KnockGuideProvider=v;
2
2
  //# sourceMappingURL=KnockGuideProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"KnockGuideProvider.js","sources":["../../../../../src/modules/guide/context/KnockGuideProvider.tsx"],"sourcesContent":["import Knock, {\n KnockGuideClient,\n KnockGuideTargetParams,\n} from \"@knocklabs/client\";\nimport * as React from \"react\";\n\nimport { useKnockClient, useStableOptions } from \"../../core\";\nimport { ColorMode } from \"../../core/constants\";\n\ntype KnockGuideProviderValue = {\n client: KnockGuideClient;\n colorMode: ColorMode;\n};\n\nexport const KnockGuideContext = React.createContext<\n KnockGuideProviderValue | undefined\n>(undefined);\n\nexport type KnockGuideProviderProps = {\n channelId: string;\n readyToTarget: boolean;\n listenForUpdates?: boolean;\n colorMode?: ColorMode;\n targetParams?: KnockGuideTargetParams;\n trackLocationFromWindow?: boolean;\n trackDebugParams?: boolean;\n orderResolutionDuration?: number; // in milliseconds\n throttleCheckInterval?: number; // in milliseconds\n};\n\nexport const KnockGuideProvider: React.FC<\n React.PropsWithChildren<KnockGuideProviderProps>\n> = ({\n channelId,\n readyToTarget,\n listenForUpdates = true,\n colorMode = \"light\",\n targetParams = {},\n trackLocationFromWindow = true,\n // Whether the guide client should look for debug params in url/local storage\n // to launch guide toolbar. Set to true if using toolbar v1.\n // TODO(KNO-11523): Remove this once we ship v2.\n trackDebugParams = false,\n // Default to 0 which works well for react apps as this \"yields\" to react for\n // one render cyle first and close the group stage.\n orderResolutionDuration = 0,\n throttleCheckInterval,\n children,\n}) => {\n let knock: Knock;\n\n try {\n knock = useKnockClient();\n } catch (_) {\n throw new Error(\"KnockGuideProvider must be used within a KnockProvider\");\n }\n\n const stableTargetParams = useStableOptions(targetParams);\n\n const knockGuideClient = React.useMemo(() => {\n return new KnockGuideClient(knock, channelId, stableTargetParams, {\n trackLocationFromWindow,\n trackDebugParams,\n orderResolutionDuration,\n throttleCheckInterval,\n });\n }, [\n knock,\n channelId,\n stableTargetParams,\n trackLocationFromWindow,\n trackDebugParams,\n orderResolutionDuration,\n throttleCheckInterval,\n ]);\n\n React.useEffect(() => {\n // When the toolbar v2 is visible, defer fetch/subscribe to the toolbar\n // so it can drive the debugging session lifecycle.\n const toolbarRunConfig = KnockGuideClient.getToolbarRunConfigFromUrl();\n\n if (readyToTarget && !toolbarRunConfig.isVisible) {\n knockGuideClient.fetch();\n if (listenForUpdates) knockGuideClient.subscribe();\n }\n\n return () => knockGuideClient.cleanup();\n }, [readyToTarget, listenForUpdates, knockGuideClient]);\n\n return (\n <KnockGuideContext.Provider\n value={{\n client: knockGuideClient,\n colorMode,\n }}\n >\n {children}\n </KnockGuideContext.Provider>\n );\n};\n"],"names":["KnockGuideContext","React","createContext","undefined","KnockGuideProvider","channelId","readyToTarget","listenForUpdates","colorMode","targetParams","trackLocationFromWindow","trackDebugParams","orderResolutionDuration","throttleCheckInterval","children","knock","useKnockClient","Error","stableTargetParams","useStableOptions","knockGuideClient","useMemo","KnockGuideClient","useEffect","toolbarRunConfig","getToolbarRunConfigFromUrl","isVisible","fetch","subscribe","cleanup","client"],"mappings":"0kBAcaA,EAAoBC,EAAMC,cAErCC,MAAS,EAcEC,EAETA,CAAC,CACHC,UAAAA,EACAC,cAAAA,EACAC,iBAAAA,EAAmB,GACnBC,UAAAA,EAAY,QACZC,aAAAA,EAAe,CAAC,EAChBC,wBAAAA,EAA0B,GAI1BC,iBAAAA,EAAmB,GAGnBC,wBAAAA,EAA0B,EAC1BC,sBAAAA,EACAC,SAAAA,CACF,IAAM,CACAC,IAAAA,EAEA,GAAA,CACFA,EAAQC,EAAAA,eAAe,OACb,CACJ,MAAA,IAAIC,MAAM,wDAAwD,CAAA,CAGpEC,MAAAA,EAAqBC,EAAiBV,CAAY,EAElDW,EAAmBnB,EAAMoB,QAAQ,IAC9B,IAAIC,EAAAA,iBAAiBP,EAAOV,EAAWa,EAAoB,CAChER,wBAAAA,EACAC,iBAAAA,EACAC,wBAAAA,EACAC,sBAAAA,CAAAA,CACD,EACA,CACDE,EACAV,EACAa,EACAR,EACAC,EACAC,EACAC,CAAqB,CACtB,EAEDZ,OAAAA,EAAMsB,UAAU,IAAM,CAGdC,MAAAA,EAAmBF,mBAAiBG,2BAA2B,EAEjEnB,OAAAA,GAAiB,CAACkB,EAAiBE,YACrCN,EAAiBO,MAAM,EACnBpB,KAAmCqB,UAAU,GAG5C,IAAMR,EAAiBS,QAAQ,CACrC,EAAA,CAACvB,EAAeC,EAAkBa,CAAgB,CAAC,EAGnDnB,EAAA,cAAAD,EAAkB,SAAlB,CACC,MAAO,CACL8B,OAAQV,EACRZ,UAAAA,IAGDM,CACH,CAEJ"}
1
+ {"version":3,"file":"KnockGuideProvider.js","sources":["../../../../../src/modules/guide/context/KnockGuideProvider.tsx"],"sourcesContent":["import Knock, {\n KnockGuideClient,\n KnockGuideTargetParams,\n} from \"@knocklabs/client\";\nimport * as React from \"react\";\n\nimport { useKnockClient, useStableOptions } from \"../../core\";\nimport { ColorMode } from \"../../core/constants\";\n\ntype KnockGuideProviderValue = {\n client: KnockGuideClient;\n colorMode: ColorMode;\n};\n\nexport const KnockGuideContext = React.createContext<\n KnockGuideProviderValue | undefined\n>(undefined);\n\nexport type KnockGuideProviderProps = {\n channelId: string;\n readyToTarget: boolean;\n listenForUpdates?: boolean;\n colorMode?: ColorMode;\n targetParams?: KnockGuideTargetParams;\n trackLocationFromWindow?: boolean;\n orderResolutionDuration?: number; // in milliseconds\n throttleCheckInterval?: number; // in milliseconds\n};\n\nexport const KnockGuideProvider: React.FC<\n React.PropsWithChildren<KnockGuideProviderProps>\n> = ({\n channelId,\n readyToTarget,\n listenForUpdates = true,\n colorMode = \"light\",\n targetParams = {},\n trackLocationFromWindow = true,\n // Default to 0 which works well for react apps as this \"yields\" to react for\n // one render cyle first and close the group stage.\n orderResolutionDuration = 0,\n throttleCheckInterval,\n children,\n}) => {\n let knock: Knock;\n\n try {\n knock = useKnockClient();\n } catch (_) {\n throw new Error(\"KnockGuideProvider must be used within a KnockProvider\");\n }\n\n const stableTargetParams = useStableOptions(targetParams);\n\n const knockGuideClient = React.useMemo(() => {\n return new KnockGuideClient(knock, channelId, stableTargetParams, {\n trackLocationFromWindow,\n orderResolutionDuration,\n throttleCheckInterval,\n });\n }, [\n knock,\n channelId,\n stableTargetParams,\n trackLocationFromWindow,\n orderResolutionDuration,\n throttleCheckInterval,\n ]);\n\n React.useEffect(() => {\n // When the toolbar v2 is visible, defer fetch/subscribe to the toolbar\n // so it can drive the debugging session lifecycle.\n const toolbarRunConfig = KnockGuideClient.getToolbarRunConfigFromUrl();\n\n if (readyToTarget && !toolbarRunConfig.isVisible) {\n knockGuideClient.fetch();\n if (listenForUpdates) knockGuideClient.subscribe();\n }\n\n return () => knockGuideClient.cleanup();\n }, [readyToTarget, listenForUpdates, knockGuideClient]);\n\n return (\n <KnockGuideContext.Provider\n value={{\n client: knockGuideClient,\n colorMode,\n }}\n >\n {children}\n </KnockGuideContext.Provider>\n );\n};\n"],"names":["KnockGuideContext","React","createContext","undefined","KnockGuideProvider","channelId","readyToTarget","listenForUpdates","colorMode","targetParams","trackLocationFromWindow","orderResolutionDuration","throttleCheckInterval","children","knock","useKnockClient","Error","stableTargetParams","useStableOptions","knockGuideClient","useMemo","KnockGuideClient","useEffect","toolbarRunConfig","getToolbarRunConfigFromUrl","isVisible","fetch","subscribe","cleanup","client"],"mappings":"0kBAcaA,EAAoBC,EAAMC,cAErCC,MAAS,EAaEC,EAETA,CAAC,CACHC,UAAAA,EACAC,cAAAA,EACAC,iBAAAA,EAAmB,GACnBC,UAAAA,EAAY,QACZC,aAAAA,EAAe,CAAC,EAChBC,wBAAAA,EAA0B,GAG1BC,wBAAAA,EAA0B,EAC1BC,sBAAAA,EACAC,SAAAA,CACF,IAAM,CACAC,IAAAA,EAEA,GAAA,CACFA,EAAQC,EAAAA,eAAe,OACb,CACJ,MAAA,IAAIC,MAAM,wDAAwD,CAAA,CAGpEC,MAAAA,EAAqBC,EAAiBT,CAAY,EAElDU,EAAmBlB,EAAMmB,QAAQ,IAC9B,IAAIC,EAAAA,iBAAiBP,EAAOT,EAAWY,EAAoB,CAChEP,wBAAAA,EACAC,wBAAAA,EACAC,sBAAAA,CAAAA,CACD,EACA,CACDE,EACAT,EACAY,EACAP,EACAC,EACAC,CAAqB,CACtB,EAEDX,OAAAA,EAAMqB,UAAU,IAAM,CAGdC,MAAAA,EAAmBF,mBAAiBG,2BAA2B,EAEjElB,OAAAA,GAAiB,CAACiB,EAAiBE,YACrCN,EAAiBO,MAAM,EACnBnB,KAAmCoB,UAAU,GAG5C,IAAMR,EAAiBS,QAAQ,CACrC,EAAA,CAACtB,EAAeC,EAAkBY,CAAgB,CAAC,EAGnDlB,EAAA,cAAAD,EAAkB,SAAlB,CACC,MAAO,CACL6B,OAAQV,EACRX,UAAAA,IAGDK,CACH,CAEJ"}
@@ -11,31 +11,31 @@ function y(e) {
11
11
  function v(e) {
12
12
  const {
13
13
  knockHost: o,
14
- popupWindowRef: s,
14
+ popupWindowRef: n,
15
15
  setConnectionStatus: t,
16
16
  onAuthenticationComplete: r,
17
- nonceStorageKey: n
17
+ nonceStorageKey: s
18
18
  } = e;
19
19
  p(() => {
20
20
  const i = () => {
21
- s.current && !s.current.closed && s.current.close(), s.current = null;
21
+ n.current && !n.current.closed && n.current.close(), n.current = null;
22
22
  }, f = (u) => {
23
- if (u.origin !== o)
23
+ if (u.origin !== o || !n.current)
24
24
  return;
25
25
  const c = d(u.data);
26
26
  if (c === "authComplete") {
27
- if (n) {
28
- const g = y(u.data), l = sessionStorage.getItem(n);
29
- if (sessionStorage.removeItem(n), !g || l && l !== g) {
27
+ if (s) {
28
+ const g = y(u.data), l = sessionStorage.getItem(s);
29
+ if (sessionStorage.removeItem(s), !g || l && l !== g) {
30
30
  t("error"), r == null || r("authFailed"), i();
31
31
  return;
32
32
  }
33
33
  }
34
34
  t("connected"), r == null || r(c), i();
35
- } else c === "authFailed" && (n && sessionStorage.removeItem(n), t("error"), r == null || r(c), s.current = null);
35
+ } else c === "authFailed" && (s && sessionStorage.removeItem(s), t("error"), r == null || r(c), n.current = null);
36
36
  };
37
37
  return window.addEventListener("message", f, !1), () => window.removeEventListener("message", f);
38
- }, [o, r, t, s, n]);
38
+ }, [o, r, t, n, s]);
39
39
  }
40
40
  export {
41
41
  v as useAuthPostMessageListener
@@ -1 +1 @@
1
- {"version":3,"file":"useAuthPostMessageListener.mjs","sources":["../../../../../src/modules/core/hooks/useAuthPostMessageListener.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { ConnectionStatus } from \"../types\";\n\nexport interface UseAuthPostMessageListenerOptions {\n knockHost: string;\n popupWindowRef: React.MutableRefObject<Window | null>;\n setConnectionStatus: (status: ConnectionStatus) => void;\n onAuthenticationComplete?: (authenticationResp: string) => void;\n /**\n * The sessionStorage key where the CSRF nonce was stored when the auth URL\n * was built. When provided, the listener will verify the nonce returned in\n * the postMessage payload matches the stored value.\n */\n nonceStorageKey?: string;\n}\n\n/**\n * Extracts the message type from a postMessage event data payload.\n * Supports both the legacy string format (\"authComplete\") and the new\n * structured format ({ type: \"authComplete\", nonce: \"...\" }).\n */\nfunction getMessageType(data: unknown): string {\n if (typeof data === \"object\" && data !== null && \"type\" in data) {\n return (data as { type: string }).type;\n }\n return data as string;\n}\n\n/**\n * Extracts the nonce from a structured postMessage event data payload.\n * Returns undefined for legacy string-format messages.\n */\nfunction getMessageNonce(data: unknown): string | undefined {\n if (typeof data === \"object\" && data !== null && \"nonce\" in data) {\n const nonce = (data as { nonce: unknown }).nonce;\n return typeof nonce === \"string\" ? nonce : undefined;\n }\n return undefined;\n}\n\n/**\n * Hook that listens for postMessage events from OAuth popup windows.\n *\n * Handles \"authComplete\" and \"authFailed\" messages sent from the OAuth flow popup,\n * validates the message origin, optionally verifies the CSRF nonce, updates\n * connection status, and closes the popup.\n *\n * @param options - Configuration options for the postMessage listener\n *\n * @example\n * ```tsx\n * useAuthPostMessageListener({\n * knockHost: knock.host,\n * popupWindowRef,\n * setConnectionStatus,\n * onAuthenticationComplete,\n * nonceStorageKey: \"knock:slack-auth-nonce:channel_123:user_1\",\n * });\n * ```\n */\nexport function useAuthPostMessageListener(\n options: UseAuthPostMessageListenerOptions,\n): void {\n const {\n knockHost,\n popupWindowRef,\n setConnectionStatus,\n onAuthenticationComplete,\n nonceStorageKey,\n } = options;\n\n useEffect(() => {\n const closePopup = () => {\n if (popupWindowRef.current && !popupWindowRef.current.closed) {\n popupWindowRef.current.close();\n }\n popupWindowRef.current = null;\n };\n\n const receiveMessage = (event: MessageEvent) => {\n // Validate message origin for security\n if (event.origin !== knockHost) {\n return;\n }\n\n const messageType = getMessageType(event.data);\n\n if (messageType === \"authComplete\") {\n // Verify CSRF nonce when a nonceStorageKey is configured.\n if (nonceStorageKey) {\n const returnedNonce = getMessageNonce(event.data);\n const storedNonce = sessionStorage.getItem(nonceStorageKey);\n sessionStorage.removeItem(nonceStorageKey);\n\n // If nonce already consumed by a prior handler invocation, then bail\n // out from checking again.\n if (\n !returnedNonce ||\n (storedNonce && storedNonce !== returnedNonce)\n ) {\n setConnectionStatus(\"error\");\n onAuthenticationComplete?.(\"authFailed\");\n closePopup();\n return;\n }\n }\n\n setConnectionStatus(\"connected\");\n onAuthenticationComplete?.(messageType);\n closePopup();\n } else if (messageType === \"authFailed\") {\n if (nonceStorageKey) {\n sessionStorage.removeItem(nonceStorageKey);\n }\n setConnectionStatus(\"error\");\n onAuthenticationComplete?.(messageType);\n popupWindowRef.current = null;\n }\n };\n\n window.addEventListener(\"message\", receiveMessage, false);\n return () => window.removeEventListener(\"message\", receiveMessage);\n }, [\n knockHost,\n onAuthenticationComplete,\n setConnectionStatus,\n popupWindowRef,\n nonceStorageKey,\n ]);\n}\n"],"names":["getMessageType","data","type","getMessageNonce","nonce","undefined","useAuthPostMessageListener","options","knockHost","popupWindowRef","setConnectionStatus","onAuthenticationComplete","nonceStorageKey","useEffect","closePopup","current","closed","close","receiveMessage","event","origin","messageType","returnedNonce","storedNonce","sessionStorage","getItem","removeItem","addEventListener","window","removeEventListener"],"mappings":";AAsBA,SAASA,EAAeC,GAAuB;AAC7C,SAAI,OAAOA,KAAS,YAAYA,MAAS,QAAQ,UAAUA,IACjDA,EAA0BC,OAE7BD;AACT;AAMA,SAASE,EAAgBF,GAAmC;AAC1D,MAAI,OAAOA,KAAS,YAAYA,MAAS,QAAQ,WAAWA,GAAM;AAChE,UAAMG,IAASH,EAA4BG;AACpC,WAAA,OAAOA,KAAU,WAAWA,IAAQC;AAAAA,EAAAA;AAG/C;AAsBO,SAASC,EACdC,GACM;AACA,QAAA;AAAA,IACJC,WAAAA;AAAAA,IACAC,gBAAAA;AAAAA,IACAC,qBAAAA;AAAAA,IACAC,0BAAAA;AAAAA,IACAC,iBAAAA;AAAAA,EAAAA,IACEL;AAEJM,EAAAA,EAAU,MAAM;AACd,UAAMC,IAAaA,MAAM;AACvB,MAAIL,EAAeM,WAAW,CAACN,EAAeM,QAAQC,UACpDP,EAAeM,QAAQE,MAAM,GAE/BR,EAAeM,UAAU;AAAA,IAC3B,GAEMG,IAAiBA,CAACC,MAAwB;AAE1CA,UAAAA,EAAMC,WAAWZ;AACnB;AAGIa,YAAAA,IAAcrB,EAAemB,EAAMlB,IAAI;AAE7C,UAAIoB,MAAgB,gBAAgB;AAElC,YAAIT,GAAiB;AACbU,gBAAAA,IAAgBnB,EAAgBgB,EAAMlB,IAAI,GAC1CsB,IAAcC,eAAeC,QAAQb,CAAe;AAK1D,cAJAY,eAAeE,WAAWd,CAAe,GAKvC,CAACU,KACAC,KAAeA,MAAgBD,GAChC;AACAZ,YAAAA,EAAoB,OAAO,GAC3BC,KAAAA,QAAAA,EAA2B,eAChBG,EAAA;AACX;AAAA,UAAA;AAAA,QACF;AAGFJ,QAAAA,EAAoB,WAAW,GAC/BC,KAAAA,QAAAA,EAA2BU,IAChBP,EAAA;AAAA,MAAA,MACb,CAAWO,MAAgB,iBACrBT,KACFY,eAAeE,WAAWd,CAAe,GAE3CF,EAAoB,OAAO,GAC3BC,KAAAA,QAAAA,EAA2BU,IAC3BZ,EAAeM,UAAU;AAAA,IAE7B;AAEOY,kBAAAA,iBAAiB,WAAWT,GAAgB,EAAK,GACjD,MAAMU,OAAOC,oBAAoB,WAAWX,CAAc;AAAA,EAAA,GAChE,CACDV,GACAG,GACAD,GACAD,GACAG,CAAe,CAChB;AACH;"}
1
+ {"version":3,"file":"useAuthPostMessageListener.mjs","sources":["../../../../../src/modules/core/hooks/useAuthPostMessageListener.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { ConnectionStatus } from \"../types\";\n\nexport interface UseAuthPostMessageListenerOptions {\n knockHost: string;\n popupWindowRef: React.MutableRefObject<Window | null>;\n setConnectionStatus: (status: ConnectionStatus) => void;\n onAuthenticationComplete?: (authenticationResp: string) => void;\n /**\n * The sessionStorage key where the CSRF nonce was stored when the auth URL\n * was built. When provided, the listener will verify the nonce returned in\n * the postMessage payload matches the stored value.\n */\n nonceStorageKey?: string;\n}\n\n/**\n * Extracts the message type from a postMessage event data payload.\n * Supports both the legacy string format (\"authComplete\") and the new\n * structured format ({ type: \"authComplete\", nonce: \"...\" }).\n */\nfunction getMessageType(data: unknown): string {\n if (typeof data === \"object\" && data !== null && \"type\" in data) {\n return (data as { type: string }).type;\n }\n return data as string;\n}\n\n/**\n * Extracts the nonce from a structured postMessage event data payload.\n * Returns undefined for legacy string-format messages.\n */\nfunction getMessageNonce(data: unknown): string | undefined {\n if (typeof data === \"object\" && data !== null && \"nonce\" in data) {\n const nonce = (data as { nonce: unknown }).nonce;\n return typeof nonce === \"string\" ? nonce : undefined;\n }\n return undefined;\n}\n\n/**\n * Hook that listens for postMessage events from OAuth popup windows.\n *\n * Handles \"authComplete\" and \"authFailed\" messages sent from the OAuth flow popup,\n * validates the message origin, optionally verifies the CSRF nonce, updates\n * connection status, and closes the popup.\n *\n * @param options - Configuration options for the postMessage listener\n *\n * @example\n * ```tsx\n * useAuthPostMessageListener({\n * knockHost: knock.host,\n * popupWindowRef,\n * setConnectionStatus,\n * onAuthenticationComplete,\n * nonceStorageKey: \"knock:slack-auth-nonce:channel_123:user_1\",\n * });\n * ```\n */\nexport function useAuthPostMessageListener(\n options: UseAuthPostMessageListenerOptions,\n): void {\n const {\n knockHost,\n popupWindowRef,\n setConnectionStatus,\n onAuthenticationComplete,\n nonceStorageKey,\n } = options;\n\n useEffect(() => {\n const closePopup = () => {\n if (popupWindowRef.current && !popupWindowRef.current.closed) {\n popupWindowRef.current.close();\n }\n popupWindowRef.current = null;\n };\n\n const receiveMessage = (event: MessageEvent) => {\n // Validate message origin for security\n if (event.origin !== knockHost) {\n return;\n }\n\n // Ignore messages when this integration hasn't opened a popup\n if (!popupWindowRef.current) {\n return;\n }\n\n const messageType = getMessageType(event.data);\n\n if (messageType === \"authComplete\") {\n // Verify CSRF nonce when a nonceStorageKey is configured.\n if (nonceStorageKey) {\n const returnedNonce = getMessageNonce(event.data);\n const storedNonce = sessionStorage.getItem(nonceStorageKey);\n sessionStorage.removeItem(nonceStorageKey);\n\n // If nonce already consumed by a prior handler invocation, then bail\n // out from checking again.\n if (\n !returnedNonce ||\n (storedNonce && storedNonce !== returnedNonce)\n ) {\n setConnectionStatus(\"error\");\n onAuthenticationComplete?.(\"authFailed\");\n closePopup();\n return;\n }\n }\n\n setConnectionStatus(\"connected\");\n onAuthenticationComplete?.(messageType);\n closePopup();\n } else if (messageType === \"authFailed\") {\n if (nonceStorageKey) {\n sessionStorage.removeItem(nonceStorageKey);\n }\n setConnectionStatus(\"error\");\n onAuthenticationComplete?.(messageType);\n popupWindowRef.current = null;\n }\n };\n\n window.addEventListener(\"message\", receiveMessage, false);\n return () => window.removeEventListener(\"message\", receiveMessage);\n }, [\n knockHost,\n onAuthenticationComplete,\n setConnectionStatus,\n popupWindowRef,\n nonceStorageKey,\n ]);\n}\n"],"names":["getMessageType","data","type","getMessageNonce","nonce","undefined","useAuthPostMessageListener","options","knockHost","popupWindowRef","setConnectionStatus","onAuthenticationComplete","nonceStorageKey","useEffect","closePopup","current","closed","close","receiveMessage","event","origin","messageType","returnedNonce","storedNonce","sessionStorage","getItem","removeItem","addEventListener","window","removeEventListener"],"mappings":";AAsBA,SAASA,EAAeC,GAAuB;AAC7C,SAAI,OAAOA,KAAS,YAAYA,MAAS,QAAQ,UAAUA,IACjDA,EAA0BC,OAE7BD;AACT;AAMA,SAASE,EAAgBF,GAAmC;AAC1D,MAAI,OAAOA,KAAS,YAAYA,MAAS,QAAQ,WAAWA,GAAM;AAChE,UAAMG,IAASH,EAA4BG;AACpC,WAAA,OAAOA,KAAU,WAAWA,IAAQC;AAAAA,EAAAA;AAG/C;AAsBO,SAASC,EACdC,GACM;AACA,QAAA;AAAA,IACJC,WAAAA;AAAAA,IACAC,gBAAAA;AAAAA,IACAC,qBAAAA;AAAAA,IACAC,0BAAAA;AAAAA,IACAC,iBAAAA;AAAAA,EAAAA,IACEL;AAEJM,EAAAA,EAAU,MAAM;AACd,UAAMC,IAAaA,MAAM;AACvB,MAAIL,EAAeM,WAAW,CAACN,EAAeM,QAAQC,UACpDP,EAAeM,QAAQE,MAAM,GAE/BR,EAAeM,UAAU;AAAA,IAC3B,GAEMG,IAAiBA,CAACC,MAAwB;AAO1C,UALAA,EAAMC,WAAWZ,KAKjB,CAACC,EAAeM;AAClB;AAGIM,YAAAA,IAAcrB,EAAemB,EAAMlB,IAAI;AAE7C,UAAIoB,MAAgB,gBAAgB;AAElC,YAAIT,GAAiB;AACbU,gBAAAA,IAAgBnB,EAAgBgB,EAAMlB,IAAI,GAC1CsB,IAAcC,eAAeC,QAAQb,CAAe;AAK1D,cAJAY,eAAeE,WAAWd,CAAe,GAKvC,CAACU,KACAC,KAAeA,MAAgBD,GAChC;AACAZ,YAAAA,EAAoB,OAAO,GAC3BC,KAAAA,QAAAA,EAA2B,eAChBG,EAAA;AACX;AAAA,UAAA;AAAA,QACF;AAGFJ,QAAAA,EAAoB,WAAW,GAC/BC,KAAAA,QAAAA,EAA2BU,IAChBP,EAAA;AAAA,MAAA,MACb,CAAWO,MAAgB,iBACrBT,KACFY,eAAeE,WAAWd,CAAe,GAE3CF,EAAoB,OAAO,GAC3BC,KAAAA,QAAAA,EAA2BU,IAC3BZ,EAAeM,UAAU;AAAA,IAE7B;AAEOY,kBAAAA,iBAAiB,WAAWT,GAAgB,EAAK,GACjD,MAAMU,OAAOC,oBAAoB,WAAWX,CAAc;AAAA,EAAA,GAChE,CACDV,GACAG,GACAD,GACAD,GACAG,CAAe,CAChB;AACH;"}
@@ -1,47 +1,42 @@
1
- import { KnockGuideClient as b } from "@knocklabs/client";
1
+ import { KnockGuideClient as l } from "@knocklabs/client";
2
2
  import * as t from "react";
3
- import { useKnockClient as C } from "../../core/context/KnockProvider.mjs";
4
- import K from "../../core/hooks/useStableOptions.mjs";
3
+ import { useKnockClient as p } from "../../core/context/KnockProvider.mjs";
4
+ import d from "../../core/hooks/useStableOptions.mjs";
5
5
  import "date-fns";
6
- const a = t.createContext(void 0), w = ({
6
+ const C = t.createContext(void 0), g = ({
7
7
  channelId: r,
8
8
  readyToTarget: i,
9
9
  listenForUpdates: n = !0,
10
- colorMode: k = "light",
11
- targetParams: p = {},
10
+ colorMode: b = "light",
11
+ targetParams: k = {},
12
12
  trackLocationFromWindow: c = !0,
13
- // Whether the guide client should look for debug params in url/local storage
14
- // to launch guide toolbar. Set to true if using toolbar v1.
15
- // TODO(KNO-11523): Remove this once we ship v2.
16
- trackDebugParams: u = !1,
17
13
  // Default to 0 which works well for react apps as this "yields" to react for
18
14
  // one render cyle first and close the group stage.
19
- orderResolutionDuration: s = 0,
20
- throttleCheckInterval: f,
21
- children: d
15
+ orderResolutionDuration: u = 0,
16
+ throttleCheckInterval: s,
17
+ children: a
22
18
  }) => {
23
19
  let o;
24
20
  try {
25
- o = C();
21
+ o = p();
26
22
  } catch {
27
23
  throw new Error("KnockGuideProvider must be used within a KnockProvider");
28
24
  }
29
- const m = K(p), e = t.useMemo(() => new b(o, r, m, {
25
+ const m = d(k), e = t.useMemo(() => new l(o, r, m, {
30
26
  trackLocationFromWindow: c,
31
- trackDebugParams: u,
32
- orderResolutionDuration: s,
33
- throttleCheckInterval: f
34
- }), [o, r, m, c, u, s, f]);
27
+ orderResolutionDuration: u,
28
+ throttleCheckInterval: s
29
+ }), [o, r, m, c, u, s]);
35
30
  return t.useEffect(() => {
36
- const l = b.getToolbarRunConfigFromUrl();
37
- return i && !l.isVisible && (e.fetch(), n && e.subscribe()), () => e.cleanup();
38
- }, [i, n, e]), /* @__PURE__ */ t.createElement(a.Provider, { value: {
31
+ const f = l.getToolbarRunConfigFromUrl();
32
+ return i && !f.isVisible && (e.fetch(), n && e.subscribe()), () => e.cleanup();
33
+ }, [i, n, e]), /* @__PURE__ */ t.createElement(C.Provider, { value: {
39
34
  client: e,
40
- colorMode: k
41
- } }, d);
35
+ colorMode: b
36
+ } }, a);
42
37
  };
43
38
  export {
44
- a as KnockGuideContext,
45
- w as KnockGuideProvider
39
+ C as KnockGuideContext,
40
+ g as KnockGuideProvider
46
41
  };
47
42
  //# sourceMappingURL=KnockGuideProvider.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"KnockGuideProvider.mjs","sources":["../../../../../src/modules/guide/context/KnockGuideProvider.tsx"],"sourcesContent":["import Knock, {\n KnockGuideClient,\n KnockGuideTargetParams,\n} from \"@knocklabs/client\";\nimport * as React from \"react\";\n\nimport { useKnockClient, useStableOptions } from \"../../core\";\nimport { ColorMode } from \"../../core/constants\";\n\ntype KnockGuideProviderValue = {\n client: KnockGuideClient;\n colorMode: ColorMode;\n};\n\nexport const KnockGuideContext = React.createContext<\n KnockGuideProviderValue | undefined\n>(undefined);\n\nexport type KnockGuideProviderProps = {\n channelId: string;\n readyToTarget: boolean;\n listenForUpdates?: boolean;\n colorMode?: ColorMode;\n targetParams?: KnockGuideTargetParams;\n trackLocationFromWindow?: boolean;\n trackDebugParams?: boolean;\n orderResolutionDuration?: number; // in milliseconds\n throttleCheckInterval?: number; // in milliseconds\n};\n\nexport const KnockGuideProvider: React.FC<\n React.PropsWithChildren<KnockGuideProviderProps>\n> = ({\n channelId,\n readyToTarget,\n listenForUpdates = true,\n colorMode = \"light\",\n targetParams = {},\n trackLocationFromWindow = true,\n // Whether the guide client should look for debug params in url/local storage\n // to launch guide toolbar. Set to true if using toolbar v1.\n // TODO(KNO-11523): Remove this once we ship v2.\n trackDebugParams = false,\n // Default to 0 which works well for react apps as this \"yields\" to react for\n // one render cyle first and close the group stage.\n orderResolutionDuration = 0,\n throttleCheckInterval,\n children,\n}) => {\n let knock: Knock;\n\n try {\n knock = useKnockClient();\n } catch (_) {\n throw new Error(\"KnockGuideProvider must be used within a KnockProvider\");\n }\n\n const stableTargetParams = useStableOptions(targetParams);\n\n const knockGuideClient = React.useMemo(() => {\n return new KnockGuideClient(knock, channelId, stableTargetParams, {\n trackLocationFromWindow,\n trackDebugParams,\n orderResolutionDuration,\n throttleCheckInterval,\n });\n }, [\n knock,\n channelId,\n stableTargetParams,\n trackLocationFromWindow,\n trackDebugParams,\n orderResolutionDuration,\n throttleCheckInterval,\n ]);\n\n React.useEffect(() => {\n // When the toolbar v2 is visible, defer fetch/subscribe to the toolbar\n // so it can drive the debugging session lifecycle.\n const toolbarRunConfig = KnockGuideClient.getToolbarRunConfigFromUrl();\n\n if (readyToTarget && !toolbarRunConfig.isVisible) {\n knockGuideClient.fetch();\n if (listenForUpdates) knockGuideClient.subscribe();\n }\n\n return () => knockGuideClient.cleanup();\n }, [readyToTarget, listenForUpdates, knockGuideClient]);\n\n return (\n <KnockGuideContext.Provider\n value={{\n client: knockGuideClient,\n colorMode,\n }}\n >\n {children}\n </KnockGuideContext.Provider>\n );\n};\n"],"names":["KnockGuideContext","React","createContext","undefined","KnockGuideProvider","channelId","readyToTarget","listenForUpdates","colorMode","targetParams","trackLocationFromWindow","trackDebugParams","orderResolutionDuration","throttleCheckInterval","children","knock","useKnockClient","Error","stableTargetParams","useStableOptions","knockGuideClient","useMemo","KnockGuideClient","useEffect","toolbarRunConfig","getToolbarRunConfigFromUrl","isVisible","fetch","subscribe","cleanup","client"],"mappings":";;;;;AAcaA,MAAAA,IAAoBC,EAAMC,cAErCC,MAAS,GAcEC,IAETA,CAAC;AAAA,EACHC,WAAAA;AAAAA,EACAC,eAAAA;AAAAA,EACAC,kBAAAA,IAAmB;AAAA,EACnBC,WAAAA,IAAY;AAAA,EACZC,cAAAA,IAAe,CAAC;AAAA,EAChBC,yBAAAA,IAA0B;AAAA;AAAA;AAAA;AAAA,EAI1BC,kBAAAA,IAAmB;AAAA;AAAA;AAAA,EAGnBC,yBAAAA,IAA0B;AAAA,EAC1BC,uBAAAA;AAAAA,EACAC,UAAAA;AACF,MAAM;AACAC,MAAAA;AAEA,MAAA;AACFA,IAAAA,IAAQC,EAAe;AAAA,UACb;AACJ,UAAA,IAAIC,MAAM,wDAAwD;AAAA,EAAA;AAGpEC,QAAAA,IAAqBC,EAAiBV,CAAY,GAElDW,IAAmBnB,EAAMoB,QAAQ,MAC9B,IAAIC,EAAiBP,GAAOV,GAAWa,GAAoB;AAAA,IAChER,yBAAAA;AAAAA,IACAC,kBAAAA;AAAAA,IACAC,yBAAAA;AAAAA,IACAC,uBAAAA;AAAAA,EAAAA,CACD,GACA,CACDE,GACAV,GACAa,GACAR,GACAC,GACAC,GACAC,CAAqB,CACtB;AAEDZ,SAAAA,EAAMsB,UAAU,MAAM;AAGdC,UAAAA,IAAmBF,EAAiBG,2BAA2B;AAEjEnB,WAAAA,KAAiB,CAACkB,EAAiBE,cACrCN,EAAiBO,MAAM,GACnBpB,OAAmCqB,UAAU,IAG5C,MAAMR,EAAiBS,QAAQ;AAAA,EACrC,GAAA,CAACvB,GAAeC,GAAkBa,CAAgB,CAAC,GAGnD,gBAAAnB,EAAA,cAAAD,EAAkB,UAAlB,EACC,OAAO;AAAA,IACL8B,QAAQV;AAAAA,IACRZ,WAAAA;AAAAA,OAGDM,CACH;AAEJ;"}
1
+ {"version":3,"file":"KnockGuideProvider.mjs","sources":["../../../../../src/modules/guide/context/KnockGuideProvider.tsx"],"sourcesContent":["import Knock, {\n KnockGuideClient,\n KnockGuideTargetParams,\n} from \"@knocklabs/client\";\nimport * as React from \"react\";\n\nimport { useKnockClient, useStableOptions } from \"../../core\";\nimport { ColorMode } from \"../../core/constants\";\n\ntype KnockGuideProviderValue = {\n client: KnockGuideClient;\n colorMode: ColorMode;\n};\n\nexport const KnockGuideContext = React.createContext<\n KnockGuideProviderValue | undefined\n>(undefined);\n\nexport type KnockGuideProviderProps = {\n channelId: string;\n readyToTarget: boolean;\n listenForUpdates?: boolean;\n colorMode?: ColorMode;\n targetParams?: KnockGuideTargetParams;\n trackLocationFromWindow?: boolean;\n orderResolutionDuration?: number; // in milliseconds\n throttleCheckInterval?: number; // in milliseconds\n};\n\nexport const KnockGuideProvider: React.FC<\n React.PropsWithChildren<KnockGuideProviderProps>\n> = ({\n channelId,\n readyToTarget,\n listenForUpdates = true,\n colorMode = \"light\",\n targetParams = {},\n trackLocationFromWindow = true,\n // Default to 0 which works well for react apps as this \"yields\" to react for\n // one render cyle first and close the group stage.\n orderResolutionDuration = 0,\n throttleCheckInterval,\n children,\n}) => {\n let knock: Knock;\n\n try {\n knock = useKnockClient();\n } catch (_) {\n throw new Error(\"KnockGuideProvider must be used within a KnockProvider\");\n }\n\n const stableTargetParams = useStableOptions(targetParams);\n\n const knockGuideClient = React.useMemo(() => {\n return new KnockGuideClient(knock, channelId, stableTargetParams, {\n trackLocationFromWindow,\n orderResolutionDuration,\n throttleCheckInterval,\n });\n }, [\n knock,\n channelId,\n stableTargetParams,\n trackLocationFromWindow,\n orderResolutionDuration,\n throttleCheckInterval,\n ]);\n\n React.useEffect(() => {\n // When the toolbar v2 is visible, defer fetch/subscribe to the toolbar\n // so it can drive the debugging session lifecycle.\n const toolbarRunConfig = KnockGuideClient.getToolbarRunConfigFromUrl();\n\n if (readyToTarget && !toolbarRunConfig.isVisible) {\n knockGuideClient.fetch();\n if (listenForUpdates) knockGuideClient.subscribe();\n }\n\n return () => knockGuideClient.cleanup();\n }, [readyToTarget, listenForUpdates, knockGuideClient]);\n\n return (\n <KnockGuideContext.Provider\n value={{\n client: knockGuideClient,\n colorMode,\n }}\n >\n {children}\n </KnockGuideContext.Provider>\n );\n};\n"],"names":["KnockGuideContext","React","createContext","undefined","KnockGuideProvider","channelId","readyToTarget","listenForUpdates","colorMode","targetParams","trackLocationFromWindow","orderResolutionDuration","throttleCheckInterval","children","knock","useKnockClient","Error","stableTargetParams","useStableOptions","knockGuideClient","useMemo","KnockGuideClient","useEffect","toolbarRunConfig","getToolbarRunConfigFromUrl","isVisible","fetch","subscribe","cleanup","client"],"mappings":";;;;;AAcaA,MAAAA,IAAoBC,EAAMC,cAErCC,MAAS,GAaEC,IAETA,CAAC;AAAA,EACHC,WAAAA;AAAAA,EACAC,eAAAA;AAAAA,EACAC,kBAAAA,IAAmB;AAAA,EACnBC,WAAAA,IAAY;AAAA,EACZC,cAAAA,IAAe,CAAC;AAAA,EAChBC,yBAAAA,IAA0B;AAAA;AAAA;AAAA,EAG1BC,yBAAAA,IAA0B;AAAA,EAC1BC,uBAAAA;AAAAA,EACAC,UAAAA;AACF,MAAM;AACAC,MAAAA;AAEA,MAAA;AACFA,IAAAA,IAAQC,EAAe;AAAA,UACb;AACJ,UAAA,IAAIC,MAAM,wDAAwD;AAAA,EAAA;AAGpEC,QAAAA,IAAqBC,EAAiBT,CAAY,GAElDU,IAAmBlB,EAAMmB,QAAQ,MAC9B,IAAIC,EAAiBP,GAAOT,GAAWY,GAAoB;AAAA,IAChEP,yBAAAA;AAAAA,IACAC,yBAAAA;AAAAA,IACAC,uBAAAA;AAAAA,EAAAA,CACD,GACA,CACDE,GACAT,GACAY,GACAP,GACAC,GACAC,CAAqB,CACtB;AAEDX,SAAAA,EAAMqB,UAAU,MAAM;AAGdC,UAAAA,IAAmBF,EAAiBG,2BAA2B;AAEjElB,WAAAA,KAAiB,CAACiB,EAAiBE,cACrCN,EAAiBO,MAAM,GACnBnB,OAAmCoB,UAAU,IAG5C,MAAMR,EAAiBS,QAAQ;AAAA,EACrC,GAAA,CAACtB,GAAeC,GAAkBY,CAAgB,CAAC,GAGnD,gBAAAlB,EAAA,cAAAD,EAAkB,UAAlB,EACC,OAAO;AAAA,IACL6B,QAAQV;AAAAA,IACRX,WAAAA;AAAAA,OAGDK,CACH;AAEJ;"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAuthPostMessageListener.d.ts","sourceRoot":"","sources":["../../../../../src/modules/core/hooks/useAuthPostMessageListener.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,WAAW,iCAAiC;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtD,mBAAmB,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACxD,wBAAwB,CAAC,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA0BD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iCAAiC,GACzC,IAAI,CAmEN"}
1
+ {"version":3,"file":"useAuthPostMessageListener.d.ts","sourceRoot":"","sources":["../../../../../src/modules/core/hooks/useAuthPostMessageListener.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,WAAW,iCAAiC;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtD,mBAAmB,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACxD,wBAAwB,CAAC,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA0BD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iCAAiC,GACzC,IAAI,CAwEN"}
@@ -13,7 +13,6 @@ export type KnockGuideProviderProps = {
13
13
  colorMode?: ColorMode;
14
14
  targetParams?: KnockGuideTargetParams;
15
15
  trackLocationFromWindow?: boolean;
16
- trackDebugParams?: boolean;
17
16
  orderResolutionDuration?: number;
18
17
  throttleCheckInterval?: number;
19
18
  };
@@ -1 +1 @@
1
- {"version":3,"file":"KnockGuideProvider.d.ts","sourceRoot":"","sources":["../../../../../src/modules/guide/context/KnockGuideProvider.tsx"],"names":[],"mappings":"AAAA,OAAc,EACZ,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,KAAK,uBAAuB,GAAG;IAC7B,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,iBAAiB,oDAElB,CAAC;AAEb,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,YAAY,CAAC,EAAE,sBAAsB,CAAC;IACtC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CACvC,KAAK,CAAC,iBAAiB,CAAC,uBAAuB,CAAC,CAoEjD,CAAC"}
1
+ {"version":3,"file":"KnockGuideProvider.d.ts","sourceRoot":"","sources":["../../../../../src/modules/guide/context/KnockGuideProvider.tsx"],"names":[],"mappings":"AAAA,OAAc,EACZ,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,KAAK,uBAAuB,GAAG;IAC7B,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,iBAAiB,oDAElB,CAAC;AAEb,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,YAAY,CAAC,EAAE,sBAAsB,CAAC;IACtC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CACvC,KAAK,CAAC,iBAAiB,CAAC,uBAAuB,CAAC,CA8DjD,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@knocklabs/react-core",
3
3
  "description": "A set of React components to build notification experiences powered by Knock",
4
4
  "author": "@knocklabs",
5
- "version": "0.13.12",
5
+ "version": "0.13.14",
6
6
  "license": "MIT",
7
7
  "main": "dist/cjs/index.js",
8
8
  "module": "dist/esm/index.mjs",
@@ -47,14 +47,14 @@
47
47
  "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
48
48
  },
49
49
  "dependencies": {
50
- "@knocklabs/client": "^0.21.11",
50
+ "@knocklabs/client": "^0.21.13",
51
51
  "@tanstack/react-store": "^0.7.3",
52
52
  "date-fns": "^4.0.0",
53
53
  "fast-deep-equal": "^3.1.3",
54
- "swr": "^2.4.0"
54
+ "swr": "^2.4.1"
55
55
  },
56
56
  "devDependencies": {
57
- "@codecov/vite-plugin": "^1.9.1",
57
+ "@codecov/vite-plugin": "^2.0.1",
58
58
  "@testing-library/dom": "^10.4.1",
59
59
  "@testing-library/react": "^16.3.2",
60
60
  "@types/react": "^19.2.14",
@@ -66,7 +66,7 @@
66
66
  "eslint": "^8.56.0",
67
67
  "eslint-plugin-react-hooks": "^5.2.0",
68
68
  "eslint-plugin-react-refresh": "^0.5.2",
69
- "jsdom": "^27.1.0",
69
+ "jsdom": "^29.1.0",
70
70
  "react": "^19.2.5",
71
71
  "react-dom": "^19.2.5",
72
72
  "rimraf": "^6.0.1",
@@ -84,6 +84,11 @@ export function useAuthPostMessageListener(
84
84
  return;
85
85
  }
86
86
 
87
+ // Ignore messages when this integration hasn't opened a popup
88
+ if (!popupWindowRef.current) {
89
+ return;
90
+ }
91
+
87
92
  const messageType = getMessageType(event.data);
88
93
 
89
94
  if (messageType === "authComplete") {
@@ -23,7 +23,6 @@ export type KnockGuideProviderProps = {
23
23
  colorMode?: ColorMode;
24
24
  targetParams?: KnockGuideTargetParams;
25
25
  trackLocationFromWindow?: boolean;
26
- trackDebugParams?: boolean;
27
26
  orderResolutionDuration?: number; // in milliseconds
28
27
  throttleCheckInterval?: number; // in milliseconds
29
28
  };
@@ -37,10 +36,6 @@ export const KnockGuideProvider: React.FC<
37
36
  colorMode = "light",
38
37
  targetParams = {},
39
38
  trackLocationFromWindow = true,
40
- // Whether the guide client should look for debug params in url/local storage
41
- // to launch guide toolbar. Set to true if using toolbar v1.
42
- // TODO(KNO-11523): Remove this once we ship v2.
43
- trackDebugParams = false,
44
39
  // Default to 0 which works well for react apps as this "yields" to react for
45
40
  // one render cyle first and close the group stage.
46
41
  orderResolutionDuration = 0,
@@ -60,7 +55,6 @@ export const KnockGuideProvider: React.FC<
60
55
  const knockGuideClient = React.useMemo(() => {
61
56
  return new KnockGuideClient(knock, channelId, stableTargetParams, {
62
57
  trackLocationFromWindow,
63
- trackDebugParams,
64
58
  orderResolutionDuration,
65
59
  throttleCheckInterval,
66
60
  });
@@ -69,7 +63,6 @@ export const KnockGuideProvider: React.FC<
69
63
  channelId,
70
64
  stableTargetParams,
71
65
  trackLocationFromWindow,
72
- trackDebugParams,
73
66
  orderResolutionDuration,
74
67
  throttleCheckInterval,
75
68
  ]);