@strivacity/sdk-react 4.0.0-beta.0 → 4.0.0-beta.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.
- package/dist/hooks.cjs +1 -1
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.mjs +1 -1
- package/dist/hooks.mjs.map +1 -1
- package/package.json +2 -2
- package/testing/tests/hooks.spec.tsx +1 -1
package/dist/hooks.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs");let t=require("@strivacity/sdk-core/utils"),n=require("react"),r=require("react/jsx-runtime");var i=(0,n.createContext)(null),a=new Set,o={loading:!0,forms:{},messages:{},state:{},triggerFallback:()=>void 0,triggerClose:()=>void 0,submitForm:async()=>Promise.resolve(void 0),setFormValue:()=>void 0,setMessage:()=>void 0},s=o,c={subscribe:e=>(a.add(e),()=>{a.delete(e)}),notify:()=>{a.forEach(e=>e())},get:()=>s,set:e=>{s=e}},l=()=>(0,n.useSyncExternalStore)(c.subscribe,c.get),u=()=>{let e=(0,n.useContext)(i);if(!e)throw Error(`Missing SDK context`);return e};function d(r={}){let{sdk:i}=u(),a=(0,n.useRef)(r),s=(0,n.useRef)({}),[l,d]=(0,n.useState)(!0),[f,p]=(0,n.useState)({}),[m,h]=(0,n.useState)({}),[g,_]=(0,n.useState)({});(0,n.useEffect)(()=>{a.current=r},[r]),(0,n.useEffect)(()=>{s.current=g},[g]);let v=(0,n.useCallback)(async e=>{if(i.session)return await a.current.onLogin?.(i.session);let
|
|
1
|
+
"use client";"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs");let t=require("@strivacity/sdk-core/utils"),n=require("react"),r=require("react/jsx-runtime");var i=(0,n.createContext)(null),a=new Set,o={loading:!0,forms:{},messages:{},state:{},triggerFallback:()=>void 0,triggerClose:()=>void 0,submitForm:async()=>Promise.resolve(void 0),setFormValue:()=>void 0,setMessage:()=>void 0},s=o,c={subscribe:e=>(a.add(e),()=>{a.delete(e)}),notify:()=>{a.forEach(e=>e())},get:()=>s,set:e=>{s=e}},l=()=>(0,n.useSyncExternalStore)(c.subscribe,c.get),u=()=>{let e=(0,n.useContext)(i);if(!e)throw Error(`Missing SDK context`);return e};function d(r={}){let{sdk:i}=u(),a=(0,n.useRef)(r),s=(0,n.useRef)({}),[l,d]=(0,n.useState)(!0),[f,p]=(0,n.useState)({}),[m,h]=(0,n.useState)({}),[g,_]=(0,n.useState)({});(0,n.useEffect)(()=>{a.current=r},[r]),(0,n.useEffect)(()=>{s.current=g},[g]);let v=(0,n.useCallback)(async e=>{if(i.session&&!(0,t.isSessionExpired)(i.session))return await a.current.onLogin?.(i.session);let n={hostedUrl:e?.hostedUrl??s.current.hostedUrl,finalizeUrl:e?.finalizeUrl??s.current.finalizeUrl,screen:e?.screen??s.current.screen,forms:e?.forms??s.current.forms,layout:e?.layout??s.current.layout,messages:e?.messages??{},branding:e?.branding??s.current.branding};if(n.screen!==s.current.screen){let e={},t={};for(let r of n.forms??[])e[r.id]={},t[r.id]={};p(e),h(t)}else i.logging?.info(`Updating screen: ${n.screen}`);h(e=>{let t={...e};return Object.keys(n.messages??{}).forEach(e=>{e===`global`?a.current.onGlobalMessage?.(n.messages?.global):t[e]=n.messages[e]??{}}),t}),_(n),n.finalizeUrl||d(!1)},[i]),y=(0,n.useCallback)(async(t={})=>{try{d(!0);let e=await i.startSession(t);e&&await v(e)}catch(t){t instanceof e.t.FallbackError?(i.logging?.error(`Fallback error occurred`,t),a.current.onFallback?.(t)):(i.logging?.error(`Error starting session`,t),a.current.onError?.(t))}},[v,i]),b=(0,n.useCallback)(t=>{if(i.logging?.warn(t?`Triggering fallback due to: ${t}`:`Triggering fallback`),!s.current.hostedUrl){let e=Error(`No hosted URL provided`);throw i.logging?.error(`Fallback error`,e),e}a.current.onFallback?.(new e.t.FallbackError(new URL(s.current.hostedUrl)))},[i]),x=(0,n.useCallback)(()=>{i.logging?.debug(`Triggering close`),a.current.onClose?.()},[i]),S=(0,n.useCallback)(async(n,r)=>{try{d(!0);let e=await i.submitForm(n,r??(0,t.unflattenObject)(f[n]??{}));e&&await v(e)}catch(t){t instanceof e.t.FallbackError?(i.logging?.error(`Fallback error occurred`,t),a.current.onFallback?.(t)):(i.logging?.error(`Error submitting form`,t),a.current.onError?.(t))}},[f,v,i]),C=(0,n.useCallback)((e,t,n)=>{p(r=>({...r,[e]:{...r[e]??{},[t]:n===``?null:n}}))},[]),w=(0,n.useCallback)((e,t,n)=>{h(r=>({...r,[e]:{...r[e]??{},[t]:n}}))},[]);(0,n.useEffect)(()=>{if(!i)return;let e=new AbortController;return i.init().then(()=>{y(a.current.params).catch(e=>{e instanceof DOMException&&e.name===`AbortError`||(i.logging?.error(`Error fetching authorization URI`,e),a.current.onError?.(e),d(!1))})}).catch(e=>{i.logging?.error(`Error initializing SDK`,e),a.current.onError?.(e),d(!1)}),()=>{e.abort()}},[i,y]);let T=(0,n.useMemo)(()=>({loading:l,forms:f,messages:m,state:g,triggerFallback:b,triggerClose:x,submitForm:S,setFormValue:C,setMessage:w}),[l,f,m,g,b,x,S,C,w]);return c.set(T),(0,n.useEffect)(()=>(c.set(T),c.notify(),()=>{c.set(o),c.notify()}),[T]),T}function f(e,t){return function(i){let{loading:a,sdk:o}=u(),[s,c]=(0,n.useState)(null);return(0,n.useEffect)(()=>{if(a)return;let e=!1;return o.checkAuthentication().then(n=>{e||(c(n),n||(t??={},t.loginUri??=`/login`,globalThis.location.href=t.loginUri))}),()=>{e=!0}},[a,o]),a||!s?t?.onLoading?.()??null:(0,r.jsx)(e,{...i})}}exports.STRIVACITY_SDK=i,exports.useNativeLogin=d,exports.useNativeLoginContext=l,exports.useStrivacity=u,exports.withAuthGuard=f;
|
|
2
2
|
//# sourceMappingURL=hooks.cjs.map
|
package/dist/hooks.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.cjs","names":[],"sources":["../src/hooks.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentType } from 'react';\nimport type {\n\tNativeFlowState,\n\tNativeFlowMessage,\n\tRedirectFlow,\n\tPopupFlow,\n\tEmbeddedFlow,\n\tNativeFlow,\n\tNativeParams,\n\tLoginContext,\n\tSDKContext,\n\tUseNativeLoginOptions,\n\tWithAuthGuardOptions,\n} from './types';\nimport { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport { unflattenObject } from '@strivacity/sdk-core/utils';\nimport { FallbackError } from './errors';\n\nexport const STRIVACITY_SDK = createContext<SDKContext<RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow>>(null!);\n\nconst loginContextListeners = new Set<() => void>();\nconst defaultLoginContext: LoginContext = {\n\tloading: true,\n\tforms: {},\n\tmessages: {},\n\tstate: {},\n\ttriggerFallback: () => undefined,\n\ttriggerClose: () => undefined,\n\tsubmitForm: async () => Promise.resolve(undefined),\n\tsetFormValue: () => undefined,\n\tsetMessage: () => undefined,\n};\nlet currentLoginContext: LoginContext = defaultLoginContext;\n\nconst loginContextStore = {\n\tsubscribe: (listener: () => void) => {\n\t\tloginContextListeners.add(listener);\n\n\t\treturn () => {\n\t\t\tloginContextListeners.delete(listener);\n\t\t};\n\t},\n\tnotify: () => {\n\t\tloginContextListeners.forEach((listener) => listener());\n\t},\n\tget: () => currentLoginContext,\n\tset: (context: LoginContext) => {\n\t\tcurrentLoginContext = context;\n\t},\n};\n\n/**\n * A custom hook to access the native login context.\n *\n * @returns {LoginContext} The native login context.\n */\nexport const useNativeLoginContext = (): LoginContext => {\n\treturn useSyncExternalStore(loginContextStore.subscribe, loginContextStore.get);\n};\n\n/**\n * A custom hook to access the Strivacity SDK context.\n *\n * @template T - The type of the Strivacity SDK flow (RedirectFlow, PopupFlow, NativeFlow, or EmbeddedFlow).\n * @returns {SDKContext<T>} The Strivacity SDK context.\n */\nexport const useStrivacity = <\n\tT extends RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow = RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow,\n>(): SDKContext<T> => {\n\tconst context = useContext(STRIVACITY_SDK);\n\n\tif (!context) {\n\t\tthrow new Error('Missing SDK context');\n\t}\n\n\treturn context as SDKContext<T>;\n};\n\n/**\n * A custom hook that manages a native login flow session.\n *\n * @param {UseNativeLoginOptions} [options] - Optional options for the login session.\n * @returns {LoginContext} The native login context.\n */\nexport function useNativeLogin(options: UseNativeLoginOptions = {}): ReturnType<typeof useNativeLoginContext> {\n\tconst { sdk } = useStrivacity<NativeFlow>();\n\tconst optionsRef = useRef(options);\n\tconst stateRef = useRef<Partial<NativeFlowState>>({});\n\n\tconst [loading, setLoading] = useState<boolean>(true);\n\tconst [forms, setForms] = useState<Record<string, Record<string, unknown>>>({});\n\tconst [messages, setMessages] = useState<Record<string, Record<string, NativeFlowMessage>>>({});\n\tconst [state, setState] = useState<Partial<NativeFlowState>>({});\n\n\tuseEffect(() => {\n\t\toptionsRef.current = options;\n\t}, [options]);\n\n\tuseEffect(() => {\n\t\tstateRef.current = state;\n\t}, [state]);\n\n\tconst handleResponse = useCallback(\n\t\tasync (nextState: Partial<NativeFlowState>) => {\n\t\t\tif (sdk.session) {\n\t\t\t\treturn await optionsRef.current.onLogin?.(sdk.session);\n\t\t\t}\n\n\t\t\tconst newState: NativeFlowState = {\n\t\t\t\thostedUrl: nextState?.hostedUrl ?? stateRef.current.hostedUrl,\n\t\t\t\tfinalizeUrl: nextState?.finalizeUrl ?? stateRef.current.finalizeUrl,\n\t\t\t\tscreen: nextState?.screen ?? stateRef.current.screen,\n\t\t\t\tforms: nextState?.forms ?? stateRef.current.forms,\n\t\t\t\tlayout: nextState?.layout ?? stateRef.current.layout,\n\t\t\t\tmessages: nextState?.messages ?? {},\n\t\t\t\tbranding: nextState?.branding ?? stateRef.current.branding,\n\t\t\t};\n\n\t\t\tconst hasScreenChanged = newState.screen !== stateRef.current.screen;\n\n\t\t\tif (hasScreenChanged) {\n\t\t\t\tconst nextForms: Record<string, Record<string, unknown>> = {};\n\t\t\t\tconst nextMessages: Record<string, Record<string, NativeFlowMessage>> = {};\n\n\t\t\t\tfor (const form of newState.forms ?? []) {\n\t\t\t\t\tnextForms[form.id] = {};\n\t\t\t\t\tnextMessages[form.id] = {};\n\t\t\t\t}\n\n\t\t\t\tsetForms(nextForms);\n\t\t\t\tsetMessages(nextMessages);\n\t\t\t} else {\n\t\t\t\tsdk.logging?.info(`Updating screen: ${newState.screen}`);\n\t\t\t}\n\n\t\t\tsetMessages((previousMessages) => {\n\t\t\t\tconst nextMessageMap = { ...previousMessages };\n\n\t\t\t\tObject.keys(newState.messages ?? {}).forEach((formId) => {\n\t\t\t\t\tif (formId === 'global') {\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain\n\t\t\t\t\t\tvoid optionsRef.current.onGlobalMessage?.(newState.messages?.global!);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextMessageMap[formId] = newState.messages![formId] ?? {};\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn nextMessageMap;\n\t\t\t});\n\t\t\tsetState(newState);\n\n\t\t\tif (!newState.finalizeUrl) {\n\t\t\t\tsetLoading(false);\n\t\t\t}\n\t\t},\n\t\t[sdk],\n\t);\n\n\tconst startSession = useCallback(\n\t\tasync (loginParams: NativeParams = {}) => {\n\t\t\ttry {\n\t\t\t\tsetLoading(true);\n\n\t\t\t\tconst nextState = await sdk.startSession(loginParams);\n\n\t\t\t\tif (nextState) {\n\t\t\t\t\tawait handleResponse(nextState);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof FallbackError) {\n\t\t\t\t\tsdk.logging?.error('Fallback error occurred', error);\n\t\t\t\t\tvoid optionsRef.current.onFallback?.(error);\n\t\t\t\t} else {\n\t\t\t\t\tsdk.logging?.error('Error starting session', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[handleResponse, sdk],\n\t);\n\n\tconst triggerFallback = useCallback(\n\t\t(message?: string) => {\n\t\t\tsdk.logging?.warn(message ? `Triggering fallback due to: ${message}` : 'Triggering fallback');\n\n\t\t\tif (!stateRef.current.hostedUrl) {\n\t\t\t\tconst error = new Error('No hosted URL provided');\n\t\t\t\tsdk.logging?.error('Fallback error', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tvoid optionsRef.current.onFallback?.(new FallbackError(new URL(stateRef.current.hostedUrl)));\n\t\t},\n\t\t[sdk],\n\t);\n\n\tconst triggerClose = useCallback(() => {\n\t\tsdk.logging?.debug('Triggering close');\n\t\tvoid optionsRef.current.onClose?.();\n\t}, [sdk]);\n\n\tconst submitForm = useCallback(\n\t\tasync (formId: string, customBody?: Record<string, unknown>) => {\n\t\t\ttry {\n\t\t\t\tsetLoading(true);\n\n\t\t\t\tconst nextState = await sdk.submitForm(formId, customBody ?? unflattenObject(forms[formId] ?? {}));\n\n\t\t\t\tif (nextState) {\n\t\t\t\t\tawait handleResponse(nextState);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof FallbackError) {\n\t\t\t\t\tsdk.logging?.error('Fallback error occurred', error);\n\t\t\t\t\tvoid optionsRef.current.onFallback?.(error);\n\t\t\t\t} else {\n\t\t\t\t\tsdk.logging?.error('Error submitting form', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[forms, handleResponse, sdk],\n\t);\n\n\tconst setFormValue = useCallback((formId: string, widgetId: string, value: unknown) => {\n\t\tsetForms((previousForms) => ({\n\t\t\t...previousForms,\n\t\t\t[formId]: {\n\t\t\t\t...(previousForms[formId] ?? {}),\n\t\t\t\t[widgetId]: value === '' ? null : value,\n\t\t\t},\n\t\t}));\n\t}, []);\n\n\tconst setMessage = useCallback((formId: string, widgetId: string, value: NativeFlowMessage) => {\n\t\tsetMessages((previousMessages) => ({\n\t\t\t...previousMessages,\n\t\t\t[formId]: {\n\t\t\t\t...(previousMessages[formId] ?? {}),\n\t\t\t\t[widgetId]: value,\n\t\t\t},\n\t\t}));\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (!sdk) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\n\t\tsdk\n\t\t\t.init()\n\t\t\t.then(() => {\n\t\t\t\tstartSession(optionsRef.current.params).catch((error) => {\n\t\t\t\t\tif (error instanceof DOMException && error.name === 'AbortError') {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tsdk.logging?.error('Error fetching authorization URI', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t});\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tsdk.logging?.error('Error initializing SDK', error);\n\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\tsetLoading(false);\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tabortController.abort();\n\t\t};\n\t}, [sdk, startSession]);\n\n\tconst ctx = useMemo(\n\t\t() => ({\n\t\t\tloading,\n\t\t\tforms,\n\t\t\tmessages,\n\t\t\tstate,\n\t\t\ttriggerFallback,\n\t\t\ttriggerClose,\n\t\t\tsubmitForm,\n\t\t\tsetFormValue,\n\t\t\tsetMessage,\n\t\t}),\n\t\t[loading, forms, messages, state, triggerFallback, triggerClose, submitForm, setFormValue, setMessage],\n\t);\n\n\t// Publish synchronously during render (not only in an effect): WidgetRenderer/widgets are descendants rendered in this same synchronous pass and read this store via useNativeLoginContext();\n\t// If we only wrote in an effect, they'd read the previous render's (stale) value, since effects run after the whole tree has rendered.\n\tloginContextStore.set(ctx);\n\n\tuseEffect(() => {\n\t\t// In React StrictMode (dev) effects run mount -> cleanup -> mount again without a new render in between. The cleanup below resets the store to defaultLoginContext.\n\t\t// Without this line the second \"mount\" would leave the store stuck on that empty default instead of the real ctx.\n\t\tloginContextStore.set(ctx);\n\t\tloginContextStore.notify();\n\n\t\treturn () => {\n\t\t\tloginContextStore.set(defaultLoginContext);\n\t\t\tloginContextStore.notify();\n\t\t};\n\t}, [ctx]);\n\n\treturn ctx;\n}\n\n/**\n * A higher-order component that guards a component with authentication.\n * Waits for the SDK to finish loading, then redirects to the login page if the user is not authenticated.\n *\n * @param {ComponentType<P>} WrappedComponent - The component to guard.\n * @param {WithAuthGuardOptions} [options] - Optional options for the auth guard.\n * @param {string} [opts.loginUri='/login'] - The URI to redirect to if the user is not authenticated.\n * @returns {ComponentType<P>} The guarded component.\n */\nexport function withAuthGuard<P extends object>(WrappedComponent: ComponentType<P>, options?: WithAuthGuardOptions): ComponentType<P> {\n\treturn function AuthGuard(props: P) {\n\t\tconst { loading, sdk } = useStrivacity();\n\t\tconst [authenticated, setAuthenticated] = useState<boolean | null>(null);\n\n\t\tuseEffect(() => {\n\t\t\tif (loading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet disposed = false;\n\n\t\t\tvoid sdk.checkAuthentication().then((result) => {\n\t\t\t\tif (disposed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tsetAuthenticated(result);\n\n\t\t\t\tif (!result) {\n\t\t\t\t\toptions ??= {};\n\t\t\t\t\toptions.loginUri ??= '/login';\n\t\t\t\t\tglobalThis.location.href = options.loginUri;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tdisposed = true;\n\t\t\t};\n\t\t}, [loading, sdk]);\n\n\t\tif (loading || !authenticated) {\n\t\t\treturn options?.onLoading?.() ?? null;\n\t\t}\n\n\t\treturn <WrappedComponent {...props} />;\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hooks.cjs","names":[],"sources":["../src/hooks.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentType } from 'react';\nimport type {\n\tNativeFlowState,\n\tNativeFlowMessage,\n\tRedirectFlow,\n\tPopupFlow,\n\tEmbeddedFlow,\n\tNativeFlow,\n\tNativeParams,\n\tLoginContext,\n\tSDKContext,\n\tUseNativeLoginOptions,\n\tWithAuthGuardOptions,\n} from './types';\nimport { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport { isSessionExpired, unflattenObject } from '@strivacity/sdk-core/utils';\nimport { FallbackError } from './errors';\n\nexport const STRIVACITY_SDK = createContext<SDKContext<RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow>>(null!);\n\nconst loginContextListeners = new Set<() => void>();\nconst defaultLoginContext: LoginContext = {\n\tloading: true,\n\tforms: {},\n\tmessages: {},\n\tstate: {},\n\ttriggerFallback: () => undefined,\n\ttriggerClose: () => undefined,\n\tsubmitForm: async () => Promise.resolve(undefined),\n\tsetFormValue: () => undefined,\n\tsetMessage: () => undefined,\n};\nlet currentLoginContext: LoginContext = defaultLoginContext;\n\nconst loginContextStore = {\n\tsubscribe: (listener: () => void) => {\n\t\tloginContextListeners.add(listener);\n\n\t\treturn () => {\n\t\t\tloginContextListeners.delete(listener);\n\t\t};\n\t},\n\tnotify: () => {\n\t\tloginContextListeners.forEach((listener) => listener());\n\t},\n\tget: () => currentLoginContext,\n\tset: (context: LoginContext) => {\n\t\tcurrentLoginContext = context;\n\t},\n};\n\n/**\n * A custom hook to access the native login context.\n *\n * @returns {LoginContext} The native login context.\n */\nexport const useNativeLoginContext = (): LoginContext => {\n\treturn useSyncExternalStore(loginContextStore.subscribe, loginContextStore.get);\n};\n\n/**\n * A custom hook to access the Strivacity SDK context.\n *\n * @template T - The type of the Strivacity SDK flow (RedirectFlow, PopupFlow, NativeFlow, or EmbeddedFlow).\n * @returns {SDKContext<T>} The Strivacity SDK context.\n */\nexport const useStrivacity = <\n\tT extends RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow = RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow,\n>(): SDKContext<T> => {\n\tconst context = useContext(STRIVACITY_SDK);\n\n\tif (!context) {\n\t\tthrow new Error('Missing SDK context');\n\t}\n\n\treturn context as SDKContext<T>;\n};\n\n/**\n * A custom hook that manages a native login flow session.\n *\n * @param {UseNativeLoginOptions} [options] - Optional options for the login session.\n * @returns {LoginContext} The native login context.\n */\nexport function useNativeLogin(options: UseNativeLoginOptions = {}): ReturnType<typeof useNativeLoginContext> {\n\tconst { sdk } = useStrivacity<NativeFlow>();\n\tconst optionsRef = useRef(options);\n\tconst stateRef = useRef<Partial<NativeFlowState>>({});\n\n\tconst [loading, setLoading] = useState<boolean>(true);\n\tconst [forms, setForms] = useState<Record<string, Record<string, unknown>>>({});\n\tconst [messages, setMessages] = useState<Record<string, Record<string, NativeFlowMessage>>>({});\n\tconst [state, setState] = useState<Partial<NativeFlowState>>({});\n\n\tuseEffect(() => {\n\t\toptionsRef.current = options;\n\t}, [options]);\n\n\tuseEffect(() => {\n\t\tstateRef.current = state;\n\t}, [state]);\n\n\tconst handleResponse = useCallback(\n\t\tasync (nextState: Partial<NativeFlowState>) => {\n\t\t\tif (sdk.session && !isSessionExpired(sdk.session)) {\n\t\t\t\treturn await optionsRef.current.onLogin?.(sdk.session);\n\t\t\t}\n\n\t\t\tconst newState: NativeFlowState = {\n\t\t\t\thostedUrl: nextState?.hostedUrl ?? stateRef.current.hostedUrl,\n\t\t\t\tfinalizeUrl: nextState?.finalizeUrl ?? stateRef.current.finalizeUrl,\n\t\t\t\tscreen: nextState?.screen ?? stateRef.current.screen,\n\t\t\t\tforms: nextState?.forms ?? stateRef.current.forms,\n\t\t\t\tlayout: nextState?.layout ?? stateRef.current.layout,\n\t\t\t\tmessages: nextState?.messages ?? {},\n\t\t\t\tbranding: nextState?.branding ?? stateRef.current.branding,\n\t\t\t};\n\n\t\t\tconst hasScreenChanged = newState.screen !== stateRef.current.screen;\n\n\t\t\tif (hasScreenChanged) {\n\t\t\t\tconst nextForms: Record<string, Record<string, unknown>> = {};\n\t\t\t\tconst nextMessages: Record<string, Record<string, NativeFlowMessage>> = {};\n\n\t\t\t\tfor (const form of newState.forms ?? []) {\n\t\t\t\t\tnextForms[form.id] = {};\n\t\t\t\t\tnextMessages[form.id] = {};\n\t\t\t\t}\n\n\t\t\t\tsetForms(nextForms);\n\t\t\t\tsetMessages(nextMessages);\n\t\t\t} else {\n\t\t\t\tsdk.logging?.info(`Updating screen: ${newState.screen}`);\n\t\t\t}\n\n\t\t\tsetMessages((previousMessages) => {\n\t\t\t\tconst nextMessageMap = { ...previousMessages };\n\n\t\t\t\tObject.keys(newState.messages ?? {}).forEach((formId) => {\n\t\t\t\t\tif (formId === 'global') {\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain\n\t\t\t\t\t\tvoid optionsRef.current.onGlobalMessage?.(newState.messages?.global!);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextMessageMap[formId] = newState.messages![formId] ?? {};\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn nextMessageMap;\n\t\t\t});\n\t\t\tsetState(newState);\n\n\t\t\tif (!newState.finalizeUrl) {\n\t\t\t\tsetLoading(false);\n\t\t\t}\n\t\t},\n\t\t[sdk],\n\t);\n\n\tconst startSession = useCallback(\n\t\tasync (loginParams: NativeParams = {}) => {\n\t\t\ttry {\n\t\t\t\tsetLoading(true);\n\n\t\t\t\tconst nextState = await sdk.startSession(loginParams);\n\n\t\t\t\tif (nextState) {\n\t\t\t\t\tawait handleResponse(nextState);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof FallbackError) {\n\t\t\t\t\tsdk.logging?.error('Fallback error occurred', error);\n\t\t\t\t\tvoid optionsRef.current.onFallback?.(error);\n\t\t\t\t} else {\n\t\t\t\t\tsdk.logging?.error('Error starting session', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[handleResponse, sdk],\n\t);\n\n\tconst triggerFallback = useCallback(\n\t\t(message?: string) => {\n\t\t\tsdk.logging?.warn(message ? `Triggering fallback due to: ${message}` : 'Triggering fallback');\n\n\t\t\tif (!stateRef.current.hostedUrl) {\n\t\t\t\tconst error = new Error('No hosted URL provided');\n\t\t\t\tsdk.logging?.error('Fallback error', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tvoid optionsRef.current.onFallback?.(new FallbackError(new URL(stateRef.current.hostedUrl)));\n\t\t},\n\t\t[sdk],\n\t);\n\n\tconst triggerClose = useCallback(() => {\n\t\tsdk.logging?.debug('Triggering close');\n\t\tvoid optionsRef.current.onClose?.();\n\t}, [sdk]);\n\n\tconst submitForm = useCallback(\n\t\tasync (formId: string, customBody?: Record<string, unknown>) => {\n\t\t\ttry {\n\t\t\t\tsetLoading(true);\n\n\t\t\t\tconst nextState = await sdk.submitForm(formId, customBody ?? unflattenObject(forms[formId] ?? {}));\n\n\t\t\t\tif (nextState) {\n\t\t\t\t\tawait handleResponse(nextState);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof FallbackError) {\n\t\t\t\t\tsdk.logging?.error('Fallback error occurred', error);\n\t\t\t\t\tvoid optionsRef.current.onFallback?.(error);\n\t\t\t\t} else {\n\t\t\t\t\tsdk.logging?.error('Error submitting form', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[forms, handleResponse, sdk],\n\t);\n\n\tconst setFormValue = useCallback((formId: string, widgetId: string, value: unknown) => {\n\t\tsetForms((previousForms) => ({\n\t\t\t...previousForms,\n\t\t\t[formId]: {\n\t\t\t\t...(previousForms[formId] ?? {}),\n\t\t\t\t[widgetId]: value === '' ? null : value,\n\t\t\t},\n\t\t}));\n\t}, []);\n\n\tconst setMessage = useCallback((formId: string, widgetId: string, value: NativeFlowMessage) => {\n\t\tsetMessages((previousMessages) => ({\n\t\t\t...previousMessages,\n\t\t\t[formId]: {\n\t\t\t\t...(previousMessages[formId] ?? {}),\n\t\t\t\t[widgetId]: value,\n\t\t\t},\n\t\t}));\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (!sdk) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\n\t\tsdk\n\t\t\t.init()\n\t\t\t.then(() => {\n\t\t\t\tstartSession(optionsRef.current.params).catch((error) => {\n\t\t\t\t\tif (error instanceof DOMException && error.name === 'AbortError') {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tsdk.logging?.error('Error fetching authorization URI', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t});\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tsdk.logging?.error('Error initializing SDK', error);\n\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\tsetLoading(false);\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tabortController.abort();\n\t\t};\n\t}, [sdk, startSession]);\n\n\tconst ctx = useMemo(\n\t\t() => ({\n\t\t\tloading,\n\t\t\tforms,\n\t\t\tmessages,\n\t\t\tstate,\n\t\t\ttriggerFallback,\n\t\t\ttriggerClose,\n\t\t\tsubmitForm,\n\t\t\tsetFormValue,\n\t\t\tsetMessage,\n\t\t}),\n\t\t[loading, forms, messages, state, triggerFallback, triggerClose, submitForm, setFormValue, setMessage],\n\t);\n\n\t// Publish synchronously during render (not only in an effect): WidgetRenderer/widgets are descendants rendered in this same synchronous pass and read this store via useNativeLoginContext();\n\t// If we only wrote in an effect, they'd read the previous render's (stale) value, since effects run after the whole tree has rendered.\n\tloginContextStore.set(ctx);\n\n\tuseEffect(() => {\n\t\t// In React StrictMode (dev) effects run mount -> cleanup -> mount again without a new render in between. The cleanup below resets the store to defaultLoginContext.\n\t\t// Without this line the second \"mount\" would leave the store stuck on that empty default instead of the real ctx.\n\t\tloginContextStore.set(ctx);\n\t\tloginContextStore.notify();\n\n\t\treturn () => {\n\t\t\tloginContextStore.set(defaultLoginContext);\n\t\t\tloginContextStore.notify();\n\t\t};\n\t}, [ctx]);\n\n\treturn ctx;\n}\n\n/**\n * A higher-order component that guards a component with authentication.\n * Waits for the SDK to finish loading, then redirects to the login page if the user is not authenticated.\n *\n * @param {ComponentType<P>} WrappedComponent - The component to guard.\n * @param {WithAuthGuardOptions} [options] - Optional options for the auth guard.\n * @param {string} [opts.loginUri='/login'] - The URI to redirect to if the user is not authenticated.\n * @returns {ComponentType<P>} The guarded component.\n */\nexport function withAuthGuard<P extends object>(WrappedComponent: ComponentType<P>, options?: WithAuthGuardOptions): ComponentType<P> {\n\treturn function AuthGuard(props: P) {\n\t\tconst { loading, sdk } = useStrivacity();\n\t\tconst [authenticated, setAuthenticated] = useState<boolean | null>(null);\n\n\t\tuseEffect(() => {\n\t\t\tif (loading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet disposed = false;\n\n\t\t\tvoid sdk.checkAuthentication().then((result) => {\n\t\t\t\tif (disposed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tsetAuthenticated(result);\n\n\t\t\t\tif (!result) {\n\t\t\t\t\toptions ??= {};\n\t\t\t\t\toptions.loginUri ??= '/login';\n\t\t\t\t\tglobalThis.location.href = options.loginUri;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tdisposed = true;\n\t\t\t};\n\t\t}, [loading, sdk]);\n\n\t\tif (loading || !authenticated) {\n\t\t\treturn options?.onLoading?.() ?? null;\n\t\t}\n\n\t\treturn <WrappedComponent {...props} />;\n\t};\n}\n"],"mappings":"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"}
|
package/dist/hooks.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";"use client";import{t as e}from"./errors.mjs";import{unflattenObject as
|
|
1
|
+
"use client";"use client";import{t as e}from"./errors.mjs";import{isSessionExpired as t,unflattenObject as n}from"@strivacity/sdk-core/utils";import{createContext as r,useCallback as i,useContext as a,useEffect as o,useMemo as s,useRef as c,useState as l,useSyncExternalStore as u}from"react";import{jsx as d}from"react/jsx-runtime";var f=r(null),p=new Set,m={loading:!0,forms:{},messages:{},state:{},triggerFallback:()=>void 0,triggerClose:()=>void 0,submitForm:async()=>Promise.resolve(void 0),setFormValue:()=>void 0,setMessage:()=>void 0},h=m,g={subscribe:e=>(p.add(e),()=>{p.delete(e)}),notify:()=>{p.forEach(e=>e())},get:()=>h,set:e=>{h=e}},_=()=>u(g.subscribe,g.get),v=()=>{let e=a(f);if(!e)throw Error(`Missing SDK context`);return e};function y(r={}){let{sdk:a}=v(),u=c(r),d=c({}),[f,p]=l(!0),[h,_]=l({}),[y,b]=l({}),[x,S]=l({});o(()=>{u.current=r},[r]),o(()=>{d.current=x},[x]);let C=i(async e=>{if(a.session&&!t(a.session))return await u.current.onLogin?.(a.session);let n={hostedUrl:e?.hostedUrl??d.current.hostedUrl,finalizeUrl:e?.finalizeUrl??d.current.finalizeUrl,screen:e?.screen??d.current.screen,forms:e?.forms??d.current.forms,layout:e?.layout??d.current.layout,messages:e?.messages??{},branding:e?.branding??d.current.branding};if(n.screen!==d.current.screen){let e={},t={};for(let r of n.forms??[])e[r.id]={},t[r.id]={};_(e),b(t)}else a.logging?.info(`Updating screen: ${n.screen}`);b(e=>{let t={...e};return Object.keys(n.messages??{}).forEach(e=>{e===`global`?u.current.onGlobalMessage?.(n.messages?.global):t[e]=n.messages[e]??{}}),t}),S(n),n.finalizeUrl||p(!1)},[a]),w=i(async(t={})=>{try{p(!0);let e=await a.startSession(t);e&&await C(e)}catch(t){t instanceof e.FallbackError?(a.logging?.error(`Fallback error occurred`,t),u.current.onFallback?.(t)):(a.logging?.error(`Error starting session`,t),u.current.onError?.(t))}},[C,a]),T=i(t=>{if(a.logging?.warn(t?`Triggering fallback due to: ${t}`:`Triggering fallback`),!d.current.hostedUrl){let e=Error(`No hosted URL provided`);throw a.logging?.error(`Fallback error`,e),e}u.current.onFallback?.(new e.FallbackError(new URL(d.current.hostedUrl)))},[a]),E=i(()=>{a.logging?.debug(`Triggering close`),u.current.onClose?.()},[a]),D=i(async(t,r)=>{try{p(!0);let e=await a.submitForm(t,r??n(h[t]??{}));e&&await C(e)}catch(t){t instanceof e.FallbackError?(a.logging?.error(`Fallback error occurred`,t),u.current.onFallback?.(t)):(a.logging?.error(`Error submitting form`,t),u.current.onError?.(t))}},[h,C,a]),O=i((e,t,n)=>{_(r=>({...r,[e]:{...r[e]??{},[t]:n===``?null:n}}))},[]),k=i((e,t,n)=>{b(r=>({...r,[e]:{...r[e]??{},[t]:n}}))},[]);o(()=>{if(!a)return;let e=new AbortController;return a.init().then(()=>{w(u.current.params).catch(e=>{e instanceof DOMException&&e.name===`AbortError`||(a.logging?.error(`Error fetching authorization URI`,e),u.current.onError?.(e),p(!1))})}).catch(e=>{a.logging?.error(`Error initializing SDK`,e),u.current.onError?.(e),p(!1)}),()=>{e.abort()}},[a,w]);let A=s(()=>({loading:f,forms:h,messages:y,state:x,triggerFallback:T,triggerClose:E,submitForm:D,setFormValue:O,setMessage:k}),[f,h,y,x,T,E,D,O,k]);return g.set(A),o(()=>(g.set(A),g.notify(),()=>{g.set(m),g.notify()}),[A]),A}function b(e,t){return function(n){let{loading:r,sdk:i}=v(),[a,s]=l(null);return o(()=>{if(r)return;let e=!1;return i.checkAuthentication().then(n=>{e||(s(n),n||(t??={},t.loginUri??=`/login`,globalThis.location.href=t.loginUri))}),()=>{e=!0}},[r,i]),r||!a?t?.onLoading?.()??null:d(e,{...n})}}export{f as STRIVACITY_SDK,y as useNativeLogin,_ as useNativeLoginContext,v as useStrivacity,b as withAuthGuard};
|
|
2
2
|
//# sourceMappingURL=hooks.mjs.map
|
package/dist/hooks.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.mjs","names":[],"sources":["../src/hooks.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentType } from 'react';\nimport type {\n\tNativeFlowState,\n\tNativeFlowMessage,\n\tRedirectFlow,\n\tPopupFlow,\n\tEmbeddedFlow,\n\tNativeFlow,\n\tNativeParams,\n\tLoginContext,\n\tSDKContext,\n\tUseNativeLoginOptions,\n\tWithAuthGuardOptions,\n} from './types';\nimport { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport { unflattenObject } from '@strivacity/sdk-core/utils';\nimport { FallbackError } from './errors';\n\nexport const STRIVACITY_SDK = createContext<SDKContext<RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow>>(null!);\n\nconst loginContextListeners = new Set<() => void>();\nconst defaultLoginContext: LoginContext = {\n\tloading: true,\n\tforms: {},\n\tmessages: {},\n\tstate: {},\n\ttriggerFallback: () => undefined,\n\ttriggerClose: () => undefined,\n\tsubmitForm: async () => Promise.resolve(undefined),\n\tsetFormValue: () => undefined,\n\tsetMessage: () => undefined,\n};\nlet currentLoginContext: LoginContext = defaultLoginContext;\n\nconst loginContextStore = {\n\tsubscribe: (listener: () => void) => {\n\t\tloginContextListeners.add(listener);\n\n\t\treturn () => {\n\t\t\tloginContextListeners.delete(listener);\n\t\t};\n\t},\n\tnotify: () => {\n\t\tloginContextListeners.forEach((listener) => listener());\n\t},\n\tget: () => currentLoginContext,\n\tset: (context: LoginContext) => {\n\t\tcurrentLoginContext = context;\n\t},\n};\n\n/**\n * A custom hook to access the native login context.\n *\n * @returns {LoginContext} The native login context.\n */\nexport const useNativeLoginContext = (): LoginContext => {\n\treturn useSyncExternalStore(loginContextStore.subscribe, loginContextStore.get);\n};\n\n/**\n * A custom hook to access the Strivacity SDK context.\n *\n * @template T - The type of the Strivacity SDK flow (RedirectFlow, PopupFlow, NativeFlow, or EmbeddedFlow).\n * @returns {SDKContext<T>} The Strivacity SDK context.\n */\nexport const useStrivacity = <\n\tT extends RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow = RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow,\n>(): SDKContext<T> => {\n\tconst context = useContext(STRIVACITY_SDK);\n\n\tif (!context) {\n\t\tthrow new Error('Missing SDK context');\n\t}\n\n\treturn context as SDKContext<T>;\n};\n\n/**\n * A custom hook that manages a native login flow session.\n *\n * @param {UseNativeLoginOptions} [options] - Optional options for the login session.\n * @returns {LoginContext} The native login context.\n */\nexport function useNativeLogin(options: UseNativeLoginOptions = {}): ReturnType<typeof useNativeLoginContext> {\n\tconst { sdk } = useStrivacity<NativeFlow>();\n\tconst optionsRef = useRef(options);\n\tconst stateRef = useRef<Partial<NativeFlowState>>({});\n\n\tconst [loading, setLoading] = useState<boolean>(true);\n\tconst [forms, setForms] = useState<Record<string, Record<string, unknown>>>({});\n\tconst [messages, setMessages] = useState<Record<string, Record<string, NativeFlowMessage>>>({});\n\tconst [state, setState] = useState<Partial<NativeFlowState>>({});\n\n\tuseEffect(() => {\n\t\toptionsRef.current = options;\n\t}, [options]);\n\n\tuseEffect(() => {\n\t\tstateRef.current = state;\n\t}, [state]);\n\n\tconst handleResponse = useCallback(\n\t\tasync (nextState: Partial<NativeFlowState>) => {\n\t\t\tif (sdk.session) {\n\t\t\t\treturn await optionsRef.current.onLogin?.(sdk.session);\n\t\t\t}\n\n\t\t\tconst newState: NativeFlowState = {\n\t\t\t\thostedUrl: nextState?.hostedUrl ?? stateRef.current.hostedUrl,\n\t\t\t\tfinalizeUrl: nextState?.finalizeUrl ?? stateRef.current.finalizeUrl,\n\t\t\t\tscreen: nextState?.screen ?? stateRef.current.screen,\n\t\t\t\tforms: nextState?.forms ?? stateRef.current.forms,\n\t\t\t\tlayout: nextState?.layout ?? stateRef.current.layout,\n\t\t\t\tmessages: nextState?.messages ?? {},\n\t\t\t\tbranding: nextState?.branding ?? stateRef.current.branding,\n\t\t\t};\n\n\t\t\tconst hasScreenChanged = newState.screen !== stateRef.current.screen;\n\n\t\t\tif (hasScreenChanged) {\n\t\t\t\tconst nextForms: Record<string, Record<string, unknown>> = {};\n\t\t\t\tconst nextMessages: Record<string, Record<string, NativeFlowMessage>> = {};\n\n\t\t\t\tfor (const form of newState.forms ?? []) {\n\t\t\t\t\tnextForms[form.id] = {};\n\t\t\t\t\tnextMessages[form.id] = {};\n\t\t\t\t}\n\n\t\t\t\tsetForms(nextForms);\n\t\t\t\tsetMessages(nextMessages);\n\t\t\t} else {\n\t\t\t\tsdk.logging?.info(`Updating screen: ${newState.screen}`);\n\t\t\t}\n\n\t\t\tsetMessages((previousMessages) => {\n\t\t\t\tconst nextMessageMap = { ...previousMessages };\n\n\t\t\t\tObject.keys(newState.messages ?? {}).forEach((formId) => {\n\t\t\t\t\tif (formId === 'global') {\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain\n\t\t\t\t\t\tvoid optionsRef.current.onGlobalMessage?.(newState.messages?.global!);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextMessageMap[formId] = newState.messages![formId] ?? {};\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn nextMessageMap;\n\t\t\t});\n\t\t\tsetState(newState);\n\n\t\t\tif (!newState.finalizeUrl) {\n\t\t\t\tsetLoading(false);\n\t\t\t}\n\t\t},\n\t\t[sdk],\n\t);\n\n\tconst startSession = useCallback(\n\t\tasync (loginParams: NativeParams = {}) => {\n\t\t\ttry {\n\t\t\t\tsetLoading(true);\n\n\t\t\t\tconst nextState = await sdk.startSession(loginParams);\n\n\t\t\t\tif (nextState) {\n\t\t\t\t\tawait handleResponse(nextState);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof FallbackError) {\n\t\t\t\t\tsdk.logging?.error('Fallback error occurred', error);\n\t\t\t\t\tvoid optionsRef.current.onFallback?.(error);\n\t\t\t\t} else {\n\t\t\t\t\tsdk.logging?.error('Error starting session', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[handleResponse, sdk],\n\t);\n\n\tconst triggerFallback = useCallback(\n\t\t(message?: string) => {\n\t\t\tsdk.logging?.warn(message ? `Triggering fallback due to: ${message}` : 'Triggering fallback');\n\n\t\t\tif (!stateRef.current.hostedUrl) {\n\t\t\t\tconst error = new Error('No hosted URL provided');\n\t\t\t\tsdk.logging?.error('Fallback error', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tvoid optionsRef.current.onFallback?.(new FallbackError(new URL(stateRef.current.hostedUrl)));\n\t\t},\n\t\t[sdk],\n\t);\n\n\tconst triggerClose = useCallback(() => {\n\t\tsdk.logging?.debug('Triggering close');\n\t\tvoid optionsRef.current.onClose?.();\n\t}, [sdk]);\n\n\tconst submitForm = useCallback(\n\t\tasync (formId: string, customBody?: Record<string, unknown>) => {\n\t\t\ttry {\n\t\t\t\tsetLoading(true);\n\n\t\t\t\tconst nextState = await sdk.submitForm(formId, customBody ?? unflattenObject(forms[formId] ?? {}));\n\n\t\t\t\tif (nextState) {\n\t\t\t\t\tawait handleResponse(nextState);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof FallbackError) {\n\t\t\t\t\tsdk.logging?.error('Fallback error occurred', error);\n\t\t\t\t\tvoid optionsRef.current.onFallback?.(error);\n\t\t\t\t} else {\n\t\t\t\t\tsdk.logging?.error('Error submitting form', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[forms, handleResponse, sdk],\n\t);\n\n\tconst setFormValue = useCallback((formId: string, widgetId: string, value: unknown) => {\n\t\tsetForms((previousForms) => ({\n\t\t\t...previousForms,\n\t\t\t[formId]: {\n\t\t\t\t...(previousForms[formId] ?? {}),\n\t\t\t\t[widgetId]: value === '' ? null : value,\n\t\t\t},\n\t\t}));\n\t}, []);\n\n\tconst setMessage = useCallback((formId: string, widgetId: string, value: NativeFlowMessage) => {\n\t\tsetMessages((previousMessages) => ({\n\t\t\t...previousMessages,\n\t\t\t[formId]: {\n\t\t\t\t...(previousMessages[formId] ?? {}),\n\t\t\t\t[widgetId]: value,\n\t\t\t},\n\t\t}));\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (!sdk) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\n\t\tsdk\n\t\t\t.init()\n\t\t\t.then(() => {\n\t\t\t\tstartSession(optionsRef.current.params).catch((error) => {\n\t\t\t\t\tif (error instanceof DOMException && error.name === 'AbortError') {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tsdk.logging?.error('Error fetching authorization URI', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t});\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tsdk.logging?.error('Error initializing SDK', error);\n\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\tsetLoading(false);\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tabortController.abort();\n\t\t};\n\t}, [sdk, startSession]);\n\n\tconst ctx = useMemo(\n\t\t() => ({\n\t\t\tloading,\n\t\t\tforms,\n\t\t\tmessages,\n\t\t\tstate,\n\t\t\ttriggerFallback,\n\t\t\ttriggerClose,\n\t\t\tsubmitForm,\n\t\t\tsetFormValue,\n\t\t\tsetMessage,\n\t\t}),\n\t\t[loading, forms, messages, state, triggerFallback, triggerClose, submitForm, setFormValue, setMessage],\n\t);\n\n\t// Publish synchronously during render (not only in an effect): WidgetRenderer/widgets are descendants rendered in this same synchronous pass and read this store via useNativeLoginContext();\n\t// If we only wrote in an effect, they'd read the previous render's (stale) value, since effects run after the whole tree has rendered.\n\tloginContextStore.set(ctx);\n\n\tuseEffect(() => {\n\t\t// In React StrictMode (dev) effects run mount -> cleanup -> mount again without a new render in between. The cleanup below resets the store to defaultLoginContext.\n\t\t// Without this line the second \"mount\" would leave the store stuck on that empty default instead of the real ctx.\n\t\tloginContextStore.set(ctx);\n\t\tloginContextStore.notify();\n\n\t\treturn () => {\n\t\t\tloginContextStore.set(defaultLoginContext);\n\t\t\tloginContextStore.notify();\n\t\t};\n\t}, [ctx]);\n\n\treturn ctx;\n}\n\n/**\n * A higher-order component that guards a component with authentication.\n * Waits for the SDK to finish loading, then redirects to the login page if the user is not authenticated.\n *\n * @param {ComponentType<P>} WrappedComponent - The component to guard.\n * @param {WithAuthGuardOptions} [options] - Optional options for the auth guard.\n * @param {string} [opts.loginUri='/login'] - The URI to redirect to if the user is not authenticated.\n * @returns {ComponentType<P>} The guarded component.\n */\nexport function withAuthGuard<P extends object>(WrappedComponent: ComponentType<P>, options?: WithAuthGuardOptions): ComponentType<P> {\n\treturn function AuthGuard(props: P) {\n\t\tconst { loading, sdk } = useStrivacity();\n\t\tconst [authenticated, setAuthenticated] = useState<boolean | null>(null);\n\n\t\tuseEffect(() => {\n\t\t\tif (loading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet disposed = false;\n\n\t\t\tvoid sdk.checkAuthentication().then((result) => {\n\t\t\t\tif (disposed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tsetAuthenticated(result);\n\n\t\t\t\tif (!result) {\n\t\t\t\t\toptions ??= {};\n\t\t\t\t\toptions.loginUri ??= '/login';\n\t\t\t\t\tglobalThis.location.href = options.loginUri;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tdisposed = true;\n\t\t\t};\n\t\t}, [loading, sdk]);\n\n\t\tif (loading || !authenticated) {\n\t\t\treturn options?.onLoading?.() ?? null;\n\t\t}\n\n\t\treturn <WrappedComponent {...props} />;\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hooks.mjs","names":[],"sources":["../src/hooks.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentType } from 'react';\nimport type {\n\tNativeFlowState,\n\tNativeFlowMessage,\n\tRedirectFlow,\n\tPopupFlow,\n\tEmbeddedFlow,\n\tNativeFlow,\n\tNativeParams,\n\tLoginContext,\n\tSDKContext,\n\tUseNativeLoginOptions,\n\tWithAuthGuardOptions,\n} from './types';\nimport { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport { isSessionExpired, unflattenObject } from '@strivacity/sdk-core/utils';\nimport { FallbackError } from './errors';\n\nexport const STRIVACITY_SDK = createContext<SDKContext<RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow>>(null!);\n\nconst loginContextListeners = new Set<() => void>();\nconst defaultLoginContext: LoginContext = {\n\tloading: true,\n\tforms: {},\n\tmessages: {},\n\tstate: {},\n\ttriggerFallback: () => undefined,\n\ttriggerClose: () => undefined,\n\tsubmitForm: async () => Promise.resolve(undefined),\n\tsetFormValue: () => undefined,\n\tsetMessage: () => undefined,\n};\nlet currentLoginContext: LoginContext = defaultLoginContext;\n\nconst loginContextStore = {\n\tsubscribe: (listener: () => void) => {\n\t\tloginContextListeners.add(listener);\n\n\t\treturn () => {\n\t\t\tloginContextListeners.delete(listener);\n\t\t};\n\t},\n\tnotify: () => {\n\t\tloginContextListeners.forEach((listener) => listener());\n\t},\n\tget: () => currentLoginContext,\n\tset: (context: LoginContext) => {\n\t\tcurrentLoginContext = context;\n\t},\n};\n\n/**\n * A custom hook to access the native login context.\n *\n * @returns {LoginContext} The native login context.\n */\nexport const useNativeLoginContext = (): LoginContext => {\n\treturn useSyncExternalStore(loginContextStore.subscribe, loginContextStore.get);\n};\n\n/**\n * A custom hook to access the Strivacity SDK context.\n *\n * @template T - The type of the Strivacity SDK flow (RedirectFlow, PopupFlow, NativeFlow, or EmbeddedFlow).\n * @returns {SDKContext<T>} The Strivacity SDK context.\n */\nexport const useStrivacity = <\n\tT extends RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow = RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow,\n>(): SDKContext<T> => {\n\tconst context = useContext(STRIVACITY_SDK);\n\n\tif (!context) {\n\t\tthrow new Error('Missing SDK context');\n\t}\n\n\treturn context as SDKContext<T>;\n};\n\n/**\n * A custom hook that manages a native login flow session.\n *\n * @param {UseNativeLoginOptions} [options] - Optional options for the login session.\n * @returns {LoginContext} The native login context.\n */\nexport function useNativeLogin(options: UseNativeLoginOptions = {}): ReturnType<typeof useNativeLoginContext> {\n\tconst { sdk } = useStrivacity<NativeFlow>();\n\tconst optionsRef = useRef(options);\n\tconst stateRef = useRef<Partial<NativeFlowState>>({});\n\n\tconst [loading, setLoading] = useState<boolean>(true);\n\tconst [forms, setForms] = useState<Record<string, Record<string, unknown>>>({});\n\tconst [messages, setMessages] = useState<Record<string, Record<string, NativeFlowMessage>>>({});\n\tconst [state, setState] = useState<Partial<NativeFlowState>>({});\n\n\tuseEffect(() => {\n\t\toptionsRef.current = options;\n\t}, [options]);\n\n\tuseEffect(() => {\n\t\tstateRef.current = state;\n\t}, [state]);\n\n\tconst handleResponse = useCallback(\n\t\tasync (nextState: Partial<NativeFlowState>) => {\n\t\t\tif (sdk.session && !isSessionExpired(sdk.session)) {\n\t\t\t\treturn await optionsRef.current.onLogin?.(sdk.session);\n\t\t\t}\n\n\t\t\tconst newState: NativeFlowState = {\n\t\t\t\thostedUrl: nextState?.hostedUrl ?? stateRef.current.hostedUrl,\n\t\t\t\tfinalizeUrl: nextState?.finalizeUrl ?? stateRef.current.finalizeUrl,\n\t\t\t\tscreen: nextState?.screen ?? stateRef.current.screen,\n\t\t\t\tforms: nextState?.forms ?? stateRef.current.forms,\n\t\t\t\tlayout: nextState?.layout ?? stateRef.current.layout,\n\t\t\t\tmessages: nextState?.messages ?? {},\n\t\t\t\tbranding: nextState?.branding ?? stateRef.current.branding,\n\t\t\t};\n\n\t\t\tconst hasScreenChanged = newState.screen !== stateRef.current.screen;\n\n\t\t\tif (hasScreenChanged) {\n\t\t\t\tconst nextForms: Record<string, Record<string, unknown>> = {};\n\t\t\t\tconst nextMessages: Record<string, Record<string, NativeFlowMessage>> = {};\n\n\t\t\t\tfor (const form of newState.forms ?? []) {\n\t\t\t\t\tnextForms[form.id] = {};\n\t\t\t\t\tnextMessages[form.id] = {};\n\t\t\t\t}\n\n\t\t\t\tsetForms(nextForms);\n\t\t\t\tsetMessages(nextMessages);\n\t\t\t} else {\n\t\t\t\tsdk.logging?.info(`Updating screen: ${newState.screen}`);\n\t\t\t}\n\n\t\t\tsetMessages((previousMessages) => {\n\t\t\t\tconst nextMessageMap = { ...previousMessages };\n\n\t\t\t\tObject.keys(newState.messages ?? {}).forEach((formId) => {\n\t\t\t\t\tif (formId === 'global') {\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain\n\t\t\t\t\t\tvoid optionsRef.current.onGlobalMessage?.(newState.messages?.global!);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextMessageMap[formId] = newState.messages![formId] ?? {};\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn nextMessageMap;\n\t\t\t});\n\t\t\tsetState(newState);\n\n\t\t\tif (!newState.finalizeUrl) {\n\t\t\t\tsetLoading(false);\n\t\t\t}\n\t\t},\n\t\t[sdk],\n\t);\n\n\tconst startSession = useCallback(\n\t\tasync (loginParams: NativeParams = {}) => {\n\t\t\ttry {\n\t\t\t\tsetLoading(true);\n\n\t\t\t\tconst nextState = await sdk.startSession(loginParams);\n\n\t\t\t\tif (nextState) {\n\t\t\t\t\tawait handleResponse(nextState);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof FallbackError) {\n\t\t\t\t\tsdk.logging?.error('Fallback error occurred', error);\n\t\t\t\t\tvoid optionsRef.current.onFallback?.(error);\n\t\t\t\t} else {\n\t\t\t\t\tsdk.logging?.error('Error starting session', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[handleResponse, sdk],\n\t);\n\n\tconst triggerFallback = useCallback(\n\t\t(message?: string) => {\n\t\t\tsdk.logging?.warn(message ? `Triggering fallback due to: ${message}` : 'Triggering fallback');\n\n\t\t\tif (!stateRef.current.hostedUrl) {\n\t\t\t\tconst error = new Error('No hosted URL provided');\n\t\t\t\tsdk.logging?.error('Fallback error', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tvoid optionsRef.current.onFallback?.(new FallbackError(new URL(stateRef.current.hostedUrl)));\n\t\t},\n\t\t[sdk],\n\t);\n\n\tconst triggerClose = useCallback(() => {\n\t\tsdk.logging?.debug('Triggering close');\n\t\tvoid optionsRef.current.onClose?.();\n\t}, [sdk]);\n\n\tconst submitForm = useCallback(\n\t\tasync (formId: string, customBody?: Record<string, unknown>) => {\n\t\t\ttry {\n\t\t\t\tsetLoading(true);\n\n\t\t\t\tconst nextState = await sdk.submitForm(formId, customBody ?? unflattenObject(forms[formId] ?? {}));\n\n\t\t\t\tif (nextState) {\n\t\t\t\t\tawait handleResponse(nextState);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof FallbackError) {\n\t\t\t\t\tsdk.logging?.error('Fallback error occurred', error);\n\t\t\t\t\tvoid optionsRef.current.onFallback?.(error);\n\t\t\t\t} else {\n\t\t\t\t\tsdk.logging?.error('Error submitting form', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[forms, handleResponse, sdk],\n\t);\n\n\tconst setFormValue = useCallback((formId: string, widgetId: string, value: unknown) => {\n\t\tsetForms((previousForms) => ({\n\t\t\t...previousForms,\n\t\t\t[formId]: {\n\t\t\t\t...(previousForms[formId] ?? {}),\n\t\t\t\t[widgetId]: value === '' ? null : value,\n\t\t\t},\n\t\t}));\n\t}, []);\n\n\tconst setMessage = useCallback((formId: string, widgetId: string, value: NativeFlowMessage) => {\n\t\tsetMessages((previousMessages) => ({\n\t\t\t...previousMessages,\n\t\t\t[formId]: {\n\t\t\t\t...(previousMessages[formId] ?? {}),\n\t\t\t\t[widgetId]: value,\n\t\t\t},\n\t\t}));\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (!sdk) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\n\t\tsdk\n\t\t\t.init()\n\t\t\t.then(() => {\n\t\t\t\tstartSession(optionsRef.current.params).catch((error) => {\n\t\t\t\t\tif (error instanceof DOMException && error.name === 'AbortError') {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tsdk.logging?.error('Error fetching authorization URI', error);\n\t\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t});\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tsdk.logging?.error('Error initializing SDK', error);\n\t\t\t\tvoid optionsRef.current.onError?.(error as Error);\n\t\t\t\tsetLoading(false);\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tabortController.abort();\n\t\t};\n\t}, [sdk, startSession]);\n\n\tconst ctx = useMemo(\n\t\t() => ({\n\t\t\tloading,\n\t\t\tforms,\n\t\t\tmessages,\n\t\t\tstate,\n\t\t\ttriggerFallback,\n\t\t\ttriggerClose,\n\t\t\tsubmitForm,\n\t\t\tsetFormValue,\n\t\t\tsetMessage,\n\t\t}),\n\t\t[loading, forms, messages, state, triggerFallback, triggerClose, submitForm, setFormValue, setMessage],\n\t);\n\n\t// Publish synchronously during render (not only in an effect): WidgetRenderer/widgets are descendants rendered in this same synchronous pass and read this store via useNativeLoginContext();\n\t// If we only wrote in an effect, they'd read the previous render's (stale) value, since effects run after the whole tree has rendered.\n\tloginContextStore.set(ctx);\n\n\tuseEffect(() => {\n\t\t// In React StrictMode (dev) effects run mount -> cleanup -> mount again without a new render in between. The cleanup below resets the store to defaultLoginContext.\n\t\t// Without this line the second \"mount\" would leave the store stuck on that empty default instead of the real ctx.\n\t\tloginContextStore.set(ctx);\n\t\tloginContextStore.notify();\n\n\t\treturn () => {\n\t\t\tloginContextStore.set(defaultLoginContext);\n\t\t\tloginContextStore.notify();\n\t\t};\n\t}, [ctx]);\n\n\treturn ctx;\n}\n\n/**\n * A higher-order component that guards a component with authentication.\n * Waits for the SDK to finish loading, then redirects to the login page if the user is not authenticated.\n *\n * @param {ComponentType<P>} WrappedComponent - The component to guard.\n * @param {WithAuthGuardOptions} [options] - Optional options for the auth guard.\n * @param {string} [opts.loginUri='/login'] - The URI to redirect to if the user is not authenticated.\n * @returns {ComponentType<P>} The guarded component.\n */\nexport function withAuthGuard<P extends object>(WrappedComponent: ComponentType<P>, options?: WithAuthGuardOptions): ComponentType<P> {\n\treturn function AuthGuard(props: P) {\n\t\tconst { loading, sdk } = useStrivacity();\n\t\tconst [authenticated, setAuthenticated] = useState<boolean | null>(null);\n\n\t\tuseEffect(() => {\n\t\t\tif (loading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet disposed = false;\n\n\t\t\tvoid sdk.checkAuthentication().then((result) => {\n\t\t\t\tif (disposed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tsetAuthenticated(result);\n\n\t\t\t\tif (!result) {\n\t\t\t\t\toptions ??= {};\n\t\t\t\t\toptions.loginUri ??= '/login';\n\t\t\t\t\tglobalThis.location.href = options.loginUri;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tdisposed = true;\n\t\t\t};\n\t\t}, [loading, sdk]);\n\n\t\tif (loading || !authenticated) {\n\t\t\treturn options?.onLoading?.() ?? null;\n\t\t}\n\n\t\treturn <WrappedComponent {...props} />;\n\t};\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strivacity/sdk-react",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.1",
|
|
4
4
|
"description": "Strivacity React SDK client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "strivacity <opensource@strivacity.com>",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@strivacity/sdk-core": "4.0.0-beta.
|
|
59
|
+
"@strivacity/sdk-core": "4.0.0-beta.1"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"react": ">=18"
|
|
@@ -232,7 +232,7 @@ describe('useNativeLogin', () => {
|
|
|
232
232
|
});
|
|
233
233
|
|
|
234
234
|
test('calls onLogin and leaves the tracked state untouched when the sdk already has a session', async () => {
|
|
235
|
-
const session = { access_token: 'access-token' } as unknown as SessionData;
|
|
235
|
+
const session = { access_token: 'access-token', expires_at: Math.floor(Date.now() / 1000) + 3600 } as unknown as SessionData;
|
|
236
236
|
const sdk = createMockFlow<NativeFlow>({ session });
|
|
237
237
|
vi.mocked(sdk.startSession).mockResolvedValue({ screen: 'identifier' });
|
|
238
238
|
const onLogin = vi.fn();
|