UNPKG

@teamhanko/hanko-frontend-sdk

Version:

A package for simplifying UI integration with the Hanko API. It is meant for use in browsers only.

1 lines 161 kB
{"version":3,"file":"sdk.cjs","sources":["../src/lib/Throttle.ts","../src/lib/events/CustomEvents.ts","../src/lib/events/Listener.ts","../src/lib/events/Dispatcher.ts","../src/lib/Errors.ts","../../node_modules/js-cookie/dist/js.cookie.mjs","../src/lib/Cookie.ts","../src/lib/SessionStorage.ts","../src/lib/client/HttpClient.ts","../src/lib/client/Client.ts","../src/lib/client/SessionClient.ts","../src/lib/events/SessionState.ts","../src/lib/events/WindowActivityManager.ts","../src/lib/events/Scheduler.ts","../src/lib/events/SessionChannel.ts","../src/lib/events/Relay.ts","../src/lib/WebauthnSupport.ts","../../node_modules/@github/webauthn-json/dist/esm/webauthn-json.js","../src/lib/flow-api/WebauthnManager.ts","../src/lib/Pkce.ts","../src/lib/flow-api/auto-steps.ts","../src/lib/flow-api/passkey-autofill-activation.ts","../src/lib/flow-api/State.ts","../src/lib/client/UserClient.ts","../src/Hanko.ts"],"sourcesContent":["/**\n * @interface\n * @category SDK\n * @subcategory Internal\n * @property {boolean=} leading - Whether to allow the function to be called on the leading edge of the wait timeout.\n * @property {boolean=} trailing - Whether to allow the function to be called on the trailing edge of the wait timeout.\n */\ninterface ThrottleOptions {\n leading?: boolean;\n trailing?: boolean;\n}\n\n// eslint-disable-next-line no-unused-vars\ntype ThrottledFunction<T extends (...args: any[]) => any> = (\n // eslint-disable-next-line no-unused-vars\n ...args: Parameters<T>\n) => void;\n\n/**\n * Provides throttle functionality.\n *\n * @hideconstructor\n * @category SDK\n * @subcategory Internal\n */\nexport class Throttle {\n /**\n * Throttles a function, ensuring that it can only be called once per `wait` milliseconds.\n *\n * @static\n * @param {function} func - The function to throttle.\n * @param {number} wait - The number of milliseconds to wait between function invocations.\n * @param {ThrottleOptions} options - Optional configuration for the throttle.\n * @returns {function} A throttled version of the original function.\n */\n // eslint-disable-next-line no-unused-vars,require-jsdoc\n static throttle<T extends (...args: any[]) => any>(\n func: T,\n wait: number,\n options: ThrottleOptions = {},\n ): ThrottledFunction<T> {\n const { leading = true, trailing = true } = options;\n let context: any;\n let args: any;\n let timeoutID: number;\n let previous = 0;\n\n // This function is used to invoke the original function.\n const executeThrottledFunction = () => {\n // If 'leading' is false and this is not the first invocation of the throttled function, set 'previous' to 0 to\n // ensure that the function is not called immediately.\n previous = leading === false ? 0 : Date.now();\n timeoutID = null;\n // Invoke the original function.\n func.apply(context, args);\n };\n\n // This is the throttled function that will be returned.\n const throttled = function (...funcArgs: Parameters<T>) {\n const now = Date.now();\n\n // If this is the first time the throttled function is being called, and 'leading' is false,\n // set 'previous' to the current time to ensure that the function is not called immediately.\n if (!previous && leading === false) previous = now;\n\n // The remaining wait time.\n const remaining = wait - (now - previous);\n\n // Save the context and arguments of the function call.\n // eslint-disable-next-line no-invalid-this\n context = this;\n args = funcArgs;\n\n // Check whether it's time to call the function immediately based on the leading and trailing options. If leading\n // is enabled and there was no previous invocation, or if trailing is enabled and the wait time has already passed,\n // the function will be invoked immediately.\n if (remaining <= 0 || remaining > wait) {\n // If there is a pending timeout, clear it.\n if (timeoutID) {\n window.clearTimeout(timeoutID);\n timeoutID = null;\n }\n\n // Invoke the original function and update the previous timestamp.\n previous = now;\n func.apply(context, args);\n } else if (!timeoutID && trailing !== false) {\n // If there is no pending timeout and trailing is allowed, start a new timeout.\n timeoutID = window.setTimeout(executeThrottledFunction, remaining);\n }\n };\n\n return throttled;\n }\n}\n","import { Claims } from \"../Dto\";\nimport { AnyState } from \"../flow-api/types/flow\";\n\n/**\n * The type of the `hanko-session-created` event.\n * @typedef {string} sessionCreatedType\n * @memberOf Listener\n */\nexport const sessionCreatedType: \"hanko-session-created\" =\n \"hanko-session-created\";\n\n/**\n * The type of the `hanko-session-expired` event.\n * @typedef {string} sessionExpiredType\n * @memberOf Listener\n */\nexport const sessionExpiredType: \"hanko-session-expired\" =\n \"hanko-session-expired\";\n\n/**\n * The type of the `hanko-user-logged-out` event.\n * @typedef {string} userLoggedOutType\n * @memberOf Listener\n */\nexport const userLoggedOutType: \"hanko-user-logged-out\" =\n \"hanko-user-logged-out\";\n\n/**\n * The type of the `hanko-user-deleted` event.\n * @typedef {string} userDeletedType\n * @memberOf Listener\n */\nexport const userDeletedType: \"hanko-user-deleted\" = \"hanko-user-deleted\";\n\n/**\n * The type of the `hanko-user-logged-in` event.\n * @typedef {string} userLoggedInType\n * @memberOf Listener\n */\nexport const userLoggedInType: \"hanko-user-logged-in\" = \"hanko-user-logged-in\";\n\n/**\n * The type of the `hanko-user-created` event.\n * @typedef {string} userCreatedType\n * @memberOf Listener\n */\nexport const userCreatedType: \"hanko-user-created\" = \"hanko-user-created\";\n\n/**\n * The type of the `hanko-after-state-change` event.\n * @typedef {string} flowAfterStateChangeType\n * @memberOf Listener\n */\nexport const flowAfterStateChangeType: \"hanko-after-state-change\" =\n \"hanko-after-state-change\";\n\n/**\n * The type of the `hanko-before-state-change` event.\n * @typedef {string} flowBeforeStateChangeType\n * @memberOf Listener\n */\nexport const flowBeforeStateChangeType: \"hanko-before-state-change\" =\n \"hanko-before-state-change\";\n\n/**\n * The type of the `hanko-flow-error` event.\n * @typedef {string} flowErrorType\n * @memberOf Listener\n */\nexport const flowErrorType: \"hanko-flow-error\" = \"hanko-flow-error\";\n\n/**\n * The data passed in the `hanko-session-created` or `hanko-session-resumed` event.\n *\n * @interface\n * @category SDK\n * @subcategory Events\n * @property {number} expirationSeconds - This property is deprecated. The number of seconds until the JWT expires.\n * @property {Claims} claims - The JSON web token associated with the session. Only present when the Hanko-API allows the JWT to be accessible client-side.\n */\nexport interface SessionDetail {\n claims: Claims;\n expirationSeconds: number; // deprecated\n}\n\nexport interface FlowErrorDetail {\n error: Error;\n}\n\nexport interface FlowDetail {\n state: AnyState;\n}\n\n/**\n * A custom event that includes a detail object.\n *\n * @category SDK\n * @subcategory Events\n * @extends CustomEvent\n * @ignore\n * @param {string} type - The type of the event.\n * @param {T} detail - The detail object to include in the event.\n */\nexport class CustomEventWithDetail<T> extends CustomEvent<T> {\n // eslint-disable-next-line require-jsdoc\n constructor(type: string, detail: T) {\n super(type, { detail });\n }\n}\n","import { Throttle } from \"../Throttle\";\nimport {\n CustomEventWithDetail,\n SessionDetail,\n FlowDetail,\n sessionCreatedType,\n sessionExpiredType,\n userDeletedType,\n userLoggedOutType,\n flowAfterStateChangeType,\n flowBeforeStateChangeType,\n flowErrorType,\n FlowErrorDetail,\n} from \"./CustomEvents\";\n\n/**\n * A callback function to be executed when an event is triggered.\n *\n * @alias CallbackFunc\n * @typedef {function} CallbackFunc\n * @memberOf Listener\n */\n// eslint-disable-next-line no-unused-vars\ntype CallbackFunc<T> = (detail: T) => any;\n\n/**\n * A wrapped callback function that will execute the original callback.\n *\n * @ignore\n * @param {T} event - The event object passed in the event.\n */\n// eslint-disable-next-line no-unused-vars\ntype WrappedCallback<T> = (event: CustomEventWithDetail<T>) => void;\n\n/**\n * A function returned when adding an event listener. The function can be called to remove the corresponding event\n * listener.\n *\n * @alias CleanupFunc\n * @typedef {function} CleanupFunc\n * @memberOf Listener\n */\ntype CleanupFunc = () => void;\n\n/**\n * @interface\n * @ignore\n * @property {Function} callback - The function to be executed.\n * @property {boolean=} once - Whether the event listener should be removed after being called once.\n */\ninterface EventListenerParams<T> {\n callback: CallbackFunc<T>;\n once?: boolean;\n}\n\n/**\n * @interface\n * @ignore\n * @extends {EventListenerParams<T>}\n * @property {string} type - The type of the event.\n * @property {boolean=} throttle - Whether the event listener should be throttled.\n */\ninterface EventListenerWithTypeParams<T> extends EventListenerParams<T> {\n type: string;\n throttle?: boolean;\n}\n\n/**\n * A class to bind event listener for custom events.\n *\n * @category SDK\n * @subcategory Events\n */\nexport class Listener {\n public throttleLimit = 1000;\n _addEventListener = document.addEventListener.bind(document);\n _removeEventListener = document.removeEventListener.bind(document);\n _throttle = Throttle.throttle;\n\n /**\n * Wraps the given callback.\n *\n * @param callback\n * @param throttle\n * @private\n * @return {WrappedCallback}\n */\n private wrapCallback<T>(\n callback: CallbackFunc<T>,\n throttle: boolean,\n ): WrappedCallback<T> {\n // The function that will be called when the event is triggered.\n const wrappedCallback = (event: CustomEventWithDetail<T>) => {\n callback(event.detail);\n };\n\n // Throttle the listener if multiple SDK instances could trigger the same event at the same time,\n // but the callback function should only be executed once.\n if (throttle) {\n return this._throttle(wrappedCallback, this.throttleLimit, {\n leading: true,\n trailing: false,\n });\n }\n\n return wrappedCallback;\n }\n\n /**\n * Adds an event listener with the specified type, callback function, and options.\n *\n * @private\n * @param {EventListenerWithTypeParams<T>} params - The parameters for the event listener.\n * @returns {CleanupFunc} This function can be called to remove the event listener.\n */\n private addEventListenerWithType<T>({\n type,\n callback,\n once = false,\n throttle = false,\n }: EventListenerWithTypeParams<T>): CleanupFunc {\n const wrappedCallback = this.wrapCallback(callback, throttle);\n this._addEventListener(type, wrappedCallback, { once });\n return () => this._removeEventListener(type, wrappedCallback);\n }\n\n /**\n * Maps the parameters for an event listener to the `EventListenerWithTypeParams` interface.\n *\n * @static\n * @private\n * @param {string} type - The type of the event.\n * @param {EventListenerParams<T>} params - The parameters for the event listener.\n * @param {boolean} [throttle=false] - Whether the event listener should be throttled.\n * @returns {EventListenerWithTypeParams<T>}\n **/\n private static mapAddEventListenerParams<T>(\n type: string,\n { once, callback }: EventListenerParams<T>,\n throttle?: boolean,\n ): EventListenerWithTypeParams<T> {\n return {\n type,\n callback,\n once,\n throttle,\n };\n }\n\n /**\n * Adds an event listener with the specified type, callback function, and options.\n *\n * @private\n * @param {string} type - The type of the event.\n * @param {EventListenerParams<T>} params - The parameters for the event listener.\n * @param {boolean=} throttle - Whether the event listener should be throttled.\n * @returns {CleanupFunc} This function can be called to remove the event listener.\n */\n private addEventListener<T>(\n type: string,\n params: EventListenerParams<T>,\n throttle?: boolean,\n ) {\n return this.addEventListenerWithType(\n Listener.mapAddEventListenerParams(type, params, throttle),\n );\n }\n\n /**\n * Adds an event listener for \"hanko-session-created\" events. Will be triggered across all browser windows, when the user\n * logs in, or when the page has been loaded or refreshed and there is a valid session.\n *\n * @param {CallbackFunc<SessionDetail>} callback - The function to be called when the event is triggered.\n * @param {boolean=} once - Whether the event listener should be removed after being called once.\n * @returns {CleanupFunc} This function can be called to remove the event listener.\n */\n public onSessionCreated(\n callback: CallbackFunc<SessionDetail>,\n once?: boolean,\n ): CleanupFunc {\n return this.addEventListener(sessionCreatedType, { callback, once }, true);\n }\n\n /**\n * Adds an event listener for \"hanko-session-expired\" events. The event will be triggered across all browser windows\n * as soon as the current JWT expires or the user logs out. It also triggers, when the user deletes the account in\n * another window.\n *\n * @param {CallbackFunc<null>} callback - The function to be called when the event is triggered.\n * @param {boolean=} once - Whether the event listener should be removed after being called once.\n * @returns {CleanupFunc} This function can be called to remove the event listener.\n */\n public onSessionExpired(\n callback: CallbackFunc<null>,\n once?: boolean,\n ): CleanupFunc {\n return this.addEventListener(sessionExpiredType, { callback, once }, true);\n }\n\n /**\n * Adds an event listener for hanko-user-deleted events. The event triggers, when the user has deleted the account in\n * the browser window where the deletion happened.\n *\n * @param {CallbackFunc<null>} callback - The function to be called when the event is triggered.\n * @param {boolean=} once - Whether the event listener should be removed after being called once.\n * @returns {CleanupFunc} This function can be called to remove the event listener.\n */\n public onUserLoggedOut(\n callback: CallbackFunc<null>,\n once?: boolean,\n ): CleanupFunc {\n return this.addEventListener(userLoggedOutType, { callback, once });\n }\n\n /**\n * Adds an event listener for hanko-user-deleted events. The event triggers, when the user has deleted the account.\n *\n * @param {CallbackFunc<null>} callback - The function to be called when the event is triggered.\n * @param {boolean=} once - Whether the event listener should be removed after being called once.\n * @returns {CleanupFunc} This function can be called to remove the event listener.\n */\n public onUserDeleted(\n callback: CallbackFunc<null>,\n once?: boolean,\n ): CleanupFunc {\n return this.addEventListener(userDeletedType, { callback, once });\n }\n\n public onAfterStateChange(\n callback: CallbackFunc<FlowDetail>,\n once?: boolean,\n ): CleanupFunc {\n return this.addEventListener(\n flowAfterStateChangeType,\n { callback, once },\n false,\n );\n }\n\n public onBeforeStateChange(\n callback: CallbackFunc<FlowDetail>,\n once?: boolean,\n ): CleanupFunc {\n return this.addEventListener(\n flowBeforeStateChangeType,\n { callback, once },\n false,\n );\n }\n}\n","import {\n SessionDetail,\n CustomEventWithDetail,\n sessionCreatedType,\n sessionExpiredType,\n userDeletedType,\n userLoggedOutType,\n flowAfterStateChangeType,\n FlowDetail,\n flowBeforeStateChangeType,\n} from \"./CustomEvents\";\n\n/**\n * A class that dispatches custom events.\n *\n * @category SDK\n * @subcategory Internal\n */\nexport class Dispatcher {\n _dispatchEvent = document.dispatchEvent.bind(document);\n\n /**\n * Dispatches a custom event.\n *\n * @param {string} type\n * @param {T} detail\n * @private\n */\n private dispatch<T>(type: string, detail: T) {\n this._dispatchEvent(new CustomEventWithDetail(type, detail));\n }\n\n /**\n * Dispatches a \"hanko-session-created\" event to the document with the specified detail.\n *\n * @param {SessionDetail} detail - The event detail.\n */\n public dispatchSessionCreatedEvent(detail: SessionDetail) {\n this.dispatch(sessionCreatedType, detail);\n }\n\n /**\n * Dispatches a \"hanko-session-expired\" event to the document.\n */\n public dispatchSessionExpiredEvent() {\n this.dispatch(sessionExpiredType, null);\n }\n\n /**\n * Dispatches a \"hanko-user-logged-out\" event to the document.\n */\n public dispatchUserLoggedOutEvent() {\n this.dispatch(userLoggedOutType, null);\n }\n\n /**\n * Dispatches a \"hanko-user-deleted\" event to the document.\n */\n public dispatchUserDeletedEvent() {\n this.dispatch(userDeletedType, null);\n }\n\n /**\n * Dispatches a \"hanko-after-state-change\" event to the document.\n */\n public dispatchAfterStateChangeEvent(detail: FlowDetail) {\n this.dispatch(flowAfterStateChangeType, detail);\n }\n\n /**\n * Dispatches a \"hanko-before-state-change\" event to the document.\n */\n public dispatchBeforeStateChangeEvent(detail: FlowDetail) {\n this.dispatch(flowBeforeStateChangeType, detail);\n }\n}\n","/**\n * Every error thrown in the SDK is an instance of 'HankoError'. The value of the 'code' property is eligible to\n * translate the error into an error message.\n *\n * @extends {Error}\n * @category SDK\n * @subcategory Errors\n * @param code {string} - An error code that refers to the error instance.\n * @param cause {Error=} - The original error\n */\nabstract class HankoError extends Error {\n code: string;\n cause?: Error;\n\n // eslint-disable-next-line require-jsdoc\n protected constructor(message: string, code: string, cause?: Error) {\n super(message);\n /**\n * @public\n * @type {string}\n */\n this.code = code;\n /**\n * @public\n * @type {Error=}\n */\n this.cause = cause;\n Object.setPrototypeOf(this, HankoError.prototype);\n }\n}\n\n/**\n * Every error that doesn't need to be handled in a special way is a 'TechnicalError'. Whenever you catch one, there is\n * usually nothing you can do but present an error to the user, e.g. \"Something went wrong\".\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass TechnicalError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"Technical error\", \"somethingWentWrong\", cause);\n Object.setPrototypeOf(this, TechnicalError.prototype);\n }\n}\n\n/**\n * Attempting to create a resource that already exists results in a 'ConflictError'.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass ConflictError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(userID?: string, cause?: Error) {\n super(\"Conflict error\", \"conflict\", cause);\n Object.setPrototypeOf(this, ConflictError.prototype);\n }\n}\n\n/**\n * A 'RequestTimeoutError' occurs when the specified timeout has been reached.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass RequestTimeoutError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"Request timed out error\", \"requestTimeout\", cause);\n Object.setPrototypeOf(this, RequestTimeoutError.prototype);\n }\n}\n\n/**\n * A 'WebauthnRequestCancelledError' occurs during WebAuthn authentication or registration, when the WebAuthn API throws\n * an error. In most cases, this happens when the user cancels the browser's WebAuthn dialog.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass WebauthnRequestCancelledError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"Request cancelled error\", \"requestCancelled\", cause);\n Object.setPrototypeOf(this, WebauthnRequestCancelledError.prototype);\n }\n}\n\n/**\n * An 'InvalidPasswordError' occurs when invalid credentials are provided when logging in with a password.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass InvalidPasswordError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"Invalid password error\", \"invalidPassword\", cause);\n Object.setPrototypeOf(this, InvalidPasswordError.prototype);\n }\n}\n\n/**\n * An 'InvalidPasswordError' occurs when an incorrect code is entered when logging in with a passcode.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass InvalidPasscodeError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"Invalid Passcode error\", \"invalidPasscode\", cause);\n Object.setPrototypeOf(this, InvalidPasscodeError.prototype);\n }\n}\n\n/**\n * An 'InvalidWebauthnCredentialError' occurs if invalid credentials were used when logging in with WebAuthn.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass InvalidWebauthnCredentialError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\n \"Invalid WebAuthn credential error\",\n \"invalidWebauthnCredential\",\n cause,\n );\n Object.setPrototypeOf(this, InvalidWebauthnCredentialError.prototype);\n }\n}\n\n/**\n * A 'PasscodeExpiredError' occurs when the passcode has expired.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass PasscodeExpiredError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"Passcode expired error\", \"passcodeExpired\", cause);\n Object.setPrototypeOf(this, PasscodeExpiredError.prototype);\n }\n}\n\n/**\n * A 'MaxNumOfPasscodeAttemptsReachedError' occurs when an incorrect passcode is provided too many times.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass MaxNumOfPasscodeAttemptsReachedError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\n \"Maximum number of Passcode attempts reached error\",\n \"passcodeAttemptsReached\",\n cause,\n );\n Object.setPrototypeOf(this, MaxNumOfPasscodeAttemptsReachedError.prototype);\n }\n}\n\n/**\n * A 'NotFoundError' occurs when the requested resource was not found.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass NotFoundError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"Not found error\", \"notFound\", cause);\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n\n/**\n * A 'TooManyRequestsError' occurs due to rate limiting when too many requests are made.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass TooManyRequestsError extends HankoError {\n retryAfter?: number;\n // eslint-disable-next-line require-jsdoc\n constructor(retryAfter?: number, cause?: Error) {\n super(\"Too many requests error\", \"tooManyRequests\", cause);\n this.retryAfter = retryAfter;\n Object.setPrototypeOf(this, TooManyRequestsError.prototype);\n }\n}\n\n/**\n * An 'UnauthorizedError' occurs when the user is not authorized to access the resource.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass UnauthorizedError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"Unauthorized error\", \"unauthorized\", cause);\n Object.setPrototypeOf(this, UnauthorizedError.prototype);\n }\n}\n\n/**\n * A 'ForbiddenError' occurs when the user is not allowed to perform the requested action.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass ForbiddenError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"Forbidden error\", \"forbidden\", cause);\n Object.setPrototypeOf(this, ForbiddenError.prototype);\n }\n}\n\n/**\n * A 'UserVerificationError' occurs when the user verification requirements\n * for a WebAuthn ceremony are not met.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass UserVerificationError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\"User verification error\", \"userVerification\", cause);\n Object.setPrototypeOf(this, UserVerificationError.prototype);\n }\n}\n\n/**\n * A 'MaxNumOfEmailAddressesReachedError' occurs when the user tries to add a new email address while the maximum number\n * of email addresses (see backend configuration) equals the number of email addresses already registered.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass MaxNumOfEmailAddressesReachedError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\n \"Maximum number of email addresses reached error\",\n \"maxNumOfEmailAddressesReached\",\n cause,\n );\n Object.setPrototypeOf(this, MaxNumOfEmailAddressesReachedError.prototype);\n }\n}\n\n/**\n * An 'EmailAddressAlreadyExistsError' occurs when the user tries to add a new email address which already exists.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass EmailAddressAlreadyExistsError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(cause?: Error) {\n super(\n \"The email address already exists\",\n \"emailAddressAlreadyExistsError\",\n cause,\n );\n Object.setPrototypeOf(this, EmailAddressAlreadyExistsError.prototype);\n }\n}\n\n/**\n * A `ThirdPartyError` may occur during a sign in/sign up with a third party\n * provider.\n *\n * @category SDK\n * @subcategory Errors\n * @extends {HankoError}\n */\nclass ThirdPartyError extends HankoError {\n // eslint-disable-next-line require-jsdoc\n constructor(code: string, cause?: Error) {\n super(\"An error occurred during third party sign up/sign in\", code, cause);\n Object.setPrototypeOf(this, ThirdPartyError.prototype);\n }\n}\n\nexport {\n HankoError,\n TechnicalError,\n ConflictError,\n RequestTimeoutError,\n WebauthnRequestCancelledError,\n InvalidPasswordError,\n InvalidPasscodeError,\n InvalidWebauthnCredentialError,\n PasscodeExpiredError,\n MaxNumOfPasscodeAttemptsReachedError,\n NotFoundError,\n TooManyRequestsError,\n UnauthorizedError,\n ForbiddenError,\n UserVerificationError,\n MaxNumOfEmailAddressesReachedError,\n EmailAddressAlreadyExistsError,\n ThirdPartyError,\n};\n","/*! js-cookie v3.0.8 | MIT */\nfunction assign (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n if (key === '__proto__') continue\n target[key] = source[key];\n }\n }\n return target\n}\n\nvar defaultConverter = {\n read: function (value) {\n if (value[0] === '\"') {\n value = value.slice(1, -1);\n }\n return value.replace(/(%[\\dA-F]{2})+/gi, decodeURIComponent)\n },\n write: function (value) {\n return encodeURIComponent(value).replace(\n /%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,\n decodeURIComponent\n )\n }\n};\n\nfunction init(converter, defaultAttributes) {\n function set(name, value, attributes) {\n if (typeof document === 'undefined') {\n return\n }\n\n attributes = assign({}, defaultAttributes, attributes);\n\n if (typeof attributes.expires === 'number') {\n attributes.expires = new Date(Date.now() + attributes.expires * 864e5);\n }\n if (attributes.expires) {\n attributes.expires = attributes.expires.toUTCString();\n }\n\n name = encodeURIComponent(name)\n .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)\n .replace(/[()]/g, escape);\n\n var stringifiedAttributes = '';\n for (var attributeName in attributes) {\n if (!attributes[attributeName]) {\n continue\n }\n\n stringifiedAttributes += '; ' + attributeName;\n\n if (attributes[attributeName] === true) {\n continue\n }\n\n // Considers RFC 6265 section 5.2:\n // ...\n // 3. If the remaining unparsed-attributes contains a %x3B (\";\")\n // character:\n // Consume the characters of the unparsed-attributes up to,\n // not including, the first %x3B (\";\") character.\n // ...\n stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];\n }\n\n return (document.cookie =\n name + '=' + converter.write(value, name) + stringifiedAttributes)\n }\n\n function get(name) {\n if (typeof document === 'undefined' || (arguments.length && !name)) {\n return\n }\n\n // To prevent the for loop in the first place assign an empty array\n // in case there are no cookies at all.\n var cookies = document.cookie ? document.cookie.split('; ') : [];\n var jar = {};\n for (var i = 0; i < cookies.length; i++) {\n var parts = cookies[i].split('=');\n var value = parts.slice(1).join('=');\n\n try {\n var found = decodeURIComponent(parts[0]);\n if (!(found in jar)) jar[found] = converter.read(value, found);\n if (name === found) {\n break\n }\n } catch (_e) {\n // Do nothing...\n }\n }\n\n return name ? jar[name] : jar\n }\n\n return Object.create(\n {\n set: set,\n get: get,\n remove: function (name, attributes) {\n set(\n name,\n '',\n assign({}, attributes, {\n expires: -1\n })\n );\n },\n withAttributes: function (attributes) {\n return init(this.converter, assign({}, this.attributes, attributes))\n },\n withConverter: function (converter) {\n return init(assign({}, this.converter, converter), this.attributes)\n }\n },\n {\n attributes: { value: Object.freeze(defaultAttributes) },\n converter: { value: Object.freeze(converter) }\n }\n )\n}\n\nvar api = init(defaultConverter, { path: '/' });\n\nexport { api as default };\n","import JSCookie, { CookieAttributes } from \"js-cookie\";\nimport { TechnicalError } from \"./Errors\";\n\n/**\n * Options for Cookie\n *\n * @category SDK\n * @subcategory Internal\n * @property {string=} cookieName - The name of the session cookie set from the SDK. Defaults to \"hanko\".\n * @property {string=} cookieDomain - The domain where the cookie set from the SDK is available. Defaults to the domain of the page where the cookie was created.\n * @property {string=} cookieSameSite -Specify whether/when cookies are sent with cross-site requests. Defaults to \"lax\".\n */\ninterface CookieOptions {\n cookieName?: string;\n cookieDomain?: string;\n cookieSameSite?: CookieSameSite;\n}\n\nexport type CookieSameSite =\n | \"strict\"\n | \"Strict\"\n | \"lax\"\n | \"Lax\"\n | \"none\"\n | \"None\";\n\n/**\n * A class to manage cookies.\n *\n * @category SDK\n * @subcategory Internal\n * @param {CookieOptions} options - The options that can be used\n */\nexport class Cookie {\n authCookieName: string;\n authCookieDomain?: string;\n authCookieSameSite: CookieSameSite;\n\n // eslint-disable-next-line require-jsdoc\n constructor(options: CookieOptions) {\n this.authCookieName = options.cookieName ?? \"hanko\";\n this.authCookieDomain = options.cookieDomain;\n this.authCookieSameSite = options.cookieSameSite ?? \"lax\";\n }\n\n /**\n * Returns the authentication token that was stored in the cookie.\n *\n * @return {string}\n */\n getAuthCookie(): string {\n return JSCookie.get(this.authCookieName);\n }\n\n /**\n * Stores the authentication token to the cookie.\n *\n * @param {string} token - The authentication token to be stored.\n * @param {CookieAttributes} options - Options for setting the auth cookie.\n */\n setAuthCookie(token: string, options?: CookieAttributes) {\n const defaults: CookieAttributes = {\n secure: true,\n sameSite: this.authCookieSameSite,\n };\n\n if (this.authCookieDomain !== undefined) {\n defaults.domain = this.authCookieDomain;\n }\n\n const o: CookieAttributes = { ...defaults, ...options };\n\n if (\n (o.sameSite === \"none\" || o.sameSite === \"None\") &&\n o.secure === false\n ) {\n throw new TechnicalError(\n new Error(\"Secure attribute must be set when SameSite=None\"),\n );\n }\n\n JSCookie.set(this.authCookieName, token, o);\n }\n\n /**\n * Removes the cookie used for authentication.\n */\n removeAuthCookie() {\n JSCookie.remove(this.authCookieName);\n }\n}\n","/**\n * Options for SessionStorage\n *\n * @category SDK\n * @subcategory Internal\n * @property {string} keyName - The name of the sessionStorage session token entry set from the SDK.\n */\ninterface SessionStorageOptions {\n keyName: string;\n}\n\n/**\n * A class to manage sessionStorage.\n *\n * @category SDK\n * @subcategory Internal\n * @param {SessionStorageOptions} options - The options that can be used.\n */\nexport class SessionStorage {\n keyName: string;\n\n // eslint-disable-next-line require-jsdoc\n constructor(options: SessionStorageOptions) {\n this.keyName = options.keyName;\n }\n\n /**\n * Return the session token that was stored in the sessionStorage.\n *\n * @return {string}\n */\n getSessionToken(): string {\n return sessionStorage.getItem(this.keyName);\n }\n\n /**\n * Stores the session token in the sessionStorage.\n *\n * @param {string} token - The session token to be stored.\n */\n setSessionToken(token: string) {\n sessionStorage.setItem(this.keyName, token);\n }\n\n /**\n * Removes the session token used for authentication.\n */\n removeSessionToken() {\n sessionStorage.removeItem(this.keyName);\n }\n}\n","import { RequestTimeoutError, TechnicalError } from \"../Errors\";\nimport { Dispatcher } from \"../events/Dispatcher\";\nimport { Cookie } from \"../Cookie\";\nimport { SessionStorage } from \"../SessionStorage\";\nimport { CookieAttributes } from \"js-cookie\";\nimport { HankoOptions } from \"../../Hanko\";\n\nexport type SessionTokenLocation = \"cookie\" | \"sessionStorage\";\n\n/**\n * This class wraps an XMLHttpRequest to maintain compatibility with the fetch API.\n *\n * @category SDK\n * @subcategory Internal\n * @param {XMLHttpRequest} xhr - The request to be wrapped.\n * @see HttpClient\n */\nclass Headers {\n _xhr: XMLHttpRequest;\n\n // eslint-disable-next-line require-jsdoc\n constructor(xhr: XMLHttpRequest) {\n this._xhr = xhr;\n }\n\n /**\n * Returns the response header with the given name.\n *\n * @param {string} name\n * @return {string}\n */\n getResponseHeader(name: string) {\n return this._xhr.getResponseHeader(name);\n }\n}\n\n/**\n * This class wraps an XMLHttpRequest to maintain compatibility with the fetch API.\n *\n * @category SDK\n * @subcategory Internal\n * @param {XMLHttpRequest} xhr - The request to be wrapped.\n * @see HttpClient\n */\nclass Response {\n headers: Headers;\n ok: boolean;\n status: number;\n statusText: string;\n url: string;\n _decodedJSON: any;\n xhr: XMLHttpRequest;\n\n // eslint-disable-next-line require-jsdoc\n constructor(xhr: XMLHttpRequest) {\n /**\n * @public\n * @type {Headers}\n */\n this.headers = new Headers(xhr);\n /**\n * @public\n * @type {boolean}\n */\n this.ok = xhr.status >= 200 && xhr.status <= 299;\n /**\n * @public\n * @type {number}\n */\n this.status = xhr.status;\n /**\n * @public\n * @type {string}\n */\n this.statusText = xhr.statusText;\n /**\n * @public\n * @type {string}\n */\n this.url = xhr.responseURL;\n /**\n * @private\n * @type {XMLHttpRequest}\n */\n this.xhr = xhr;\n }\n\n /**\n * Returns the JSON decoded response.\n *\n * @return {any}\n */\n json() {\n if (!this._decodedJSON) {\n this._decodedJSON = JSON.parse(this.xhr.response);\n }\n return this._decodedJSON;\n }\n\n /**\n * Returns the response header value with the given `name` as a number. When the value is not a number the return\n * value will be 0.\n *\n * @param {string} name - The name of the header field\n * @return {number}\n */\n parseNumericHeader(name: string): number {\n const result = parseInt(this.headers.getResponseHeader(name), 10);\n return isNaN(result) ? 0 : result;\n }\n}\n\n/**\n * Options for the HttpClient\n *\n * @category SDK\n * @subcategory Internal\n * @property {number=} timeout - The http request timeout in milliseconds.\n * @property {string} cookieName - The name of the session cookie set from the SDK.\n * @property {string=} cookieDomain - The domain where cookie set from the SDK is available. Defaults to the domain of the page where the cookie was created.\n * @property {string?} lang - The language used by the client(s) to convey to the Hanko API the language to use -\n * e.g. for translating outgoing emails - in a custom header (X-Language).\n */\nexport interface HttpClientOptions {\n timeout?: number;\n cookieName?: string;\n cookieDomain?: string;\n lang?: string;\n sessionTokenLocation?: SessionTokenLocation;\n}\n\n/**\n * Internally used for communication with the Hanko API. It also handles authorization tokens to enable authorized\n * requests.\n *\n * Currently, there is an issue with Safari and on iOS 15 devices where decoding a JSON response via the fetch API\n * breaks the user gesture and the user is not able to use the authenticator. Therefore, this class uses XMLHttpRequests\n * instead of the fetch API, but maintains compatibility by wrapping the XMLHttpRequests. So, if the issues are fixed,\n * we can easily return to the fetch API.\n *\n * @category SDK\n * @subcategory Internal\n * @param {string} api - The URL of your Hanko API instance\n * @param {HttpClientOptions} options - The options the HttpClient must be provided\n */\nclass HttpClient {\n timeout: number;\n api: string;\n dispatcher: Dispatcher;\n cookie: Cookie;\n sessionTokenStorage: SessionStorage;\n lang: string;\n sessionTokenLocation: SessionTokenLocation;\n\n // eslint-disable-next-line require-jsdoc\n constructor(api: string, options: HankoOptions) {\n this.api = api;\n this.timeout = options.timeout ?? 13000;\n this.dispatcher = new Dispatcher();\n this.cookie = new Cookie({ ...options });\n this.sessionTokenStorage = new SessionStorage({\n keyName: options.cookieName,\n });\n this.lang = options.lang;\n this.sessionTokenLocation = options.sessionTokenLocation;\n }\n\n // eslint-disable-next-line require-jsdoc\n _fetch(path: string, options: RequestInit, xhr = new XMLHttpRequest()) {\n const self = this;\n const url = this.api + path;\n const timeout = this.timeout;\n const bearerToken = this.getAuthToken();\n const lang = this.lang;\n\n return new Promise<Response>(function (resolve, reject) {\n xhr.open(options.method, url, true);\n xhr.setRequestHeader(\"Accept\", \"application/json\");\n xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n xhr.setRequestHeader(\"X-Language\", lang);\n\n if (bearerToken) {\n xhr.setRequestHeader(\"Authorization\", `Bearer ${bearerToken}`);\n }\n\n xhr.timeout = timeout;\n xhr.withCredentials = true;\n xhr.onload = () => {\n self.processHeaders(xhr);\n resolve(new Response(xhr));\n };\n\n xhr.onerror = () => {\n reject(new TechnicalError());\n };\n\n xhr.ontimeout = () => {\n reject(new RequestTimeoutError());\n };\n\n xhr.send(options.body ? options.body.toString() : null);\n });\n }\n\n /**\n * Processes the response headers on login and extracts the JWT and expiration time.\n *\n * @param {XMLHttpRequest} xhr - The xhr object.\n */\n processHeaders(xhr: XMLHttpRequest) {\n let jwt = \"\";\n let expirationSeconds = 0;\n let retention = \"\";\n\n xhr\n .getAllResponseHeaders()\n .split(\"\\r\\n\")\n .forEach((h) => {\n const header = h.toLowerCase();\n if (header.startsWith(\"x-auth-token\")) {\n jwt = xhr.getResponseHeader(\"X-Auth-Token\");\n } else if (header.startsWith(\"x-session-lifetime\")) {\n expirationSeconds = parseInt(\n xhr.getResponseHeader(\"X-Session-Lifetime\"),\n 10,\n );\n } else if (header.startsWith(\"x-session-retention\")) {\n retention = xhr.getResponseHeader(\"X-Session-Retention\");\n }\n });\n\n if (jwt) {\n const https = new RegExp(\"^https://\");\n const secure =\n !!this.api.match(https) && !!window.location.href.match(https);\n\n const expires =\n retention === \"session\"\n ? undefined\n : new Date(new Date().getTime() + expirationSeconds * 1000);\n\n this.setAuthToken(jwt, { secure, expires });\n }\n }\n\n /**\n * Performs a GET request.\n *\n * @param {string} path - The path to the requested resource.\n * @return {Promise<Response>}\n * @throws {RequestTimeoutError}\n * @throws {TechnicalError}\n */\n get(path: string) {\n return this._fetch(path, { method: \"GET\" });\n }\n\n /**\n * Performs a POST request.\n *\n * @param {string} path - The path to the requested resource.\n * @param {any=} body - The request body.\n * @return {Promise<Response>}\n * @throws {RequestTimeoutError}\n * @throws {TechnicalError}\n */\n post(path: string, body?: any) {\n return this._fetch(path, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n }\n\n /**\n * Performs a PUT request.\n *\n * @param {string} path - The path to the requested resource.\n * @param {any=} body - The request body.\n * @return {Promise<Response>}\n * @throws {RequestTimeoutError}\n * @throws {TechnicalError}\n */\n put(path: string, body?: any) {\n return this._fetch(path, {\n method: \"PUT\",\n body: JSON.stringify(body),\n });\n }\n\n /**\n * Performs a PATCH request.\n *\n * @param {string} path - The path to the requested resource.\n * @param {any=} body - The request body.\n * @return {Promise<Response>}\n * @throws {RequestTimeoutError}\n * @throws {TechnicalError}\n */\n patch(path: string, body?: any) {\n return this._fetch(path, {\n method: \"PATCH\",\n body: JSON.stringify(body),\n });\n }\n\n /**\n * Performs a DELETE request.\n *\n * @param {string} path - The path to the requested resource.\n * @return {Promise<Response>}\n * @throws {RequestTimeoutError}\n * @throws {TechnicalError}\n */\n delete(path: string) {\n return this._fetch(path, {\n method: \"DELETE\",\n });\n }\n\n /**\n * Returns the session token either from the cookie or the sessionStorage.\n * @private\n * @return {string}\n */\n private getAuthToken(): string {\n let token = \"\";\n switch (this.sessionTokenLocation) {\n case \"cookie\":\n token = this.cookie.getAuthCookie();\n break;\n case \"sessionStorage\":\n token = this.sessionTokenStorage.getSessionToken();\n break;\n default:\n token = this.cookie.getAuthCookie();\n break;\n }\n return token;\n }\n\n /**\n * Stores the session token either in a cookie or in the sessionStorage depending on the configuration.\n * @param {string} token - The session token to be stored.\n * @param {CookieAttributes} options - Options for setting the auth cookie.\n * @private\n */\n private setAuthToken(token: string, options: CookieAttributes) {\n switch (this.sessionTokenLocation) {\n case \"cookie\":\n return this.cookie.setAuthCookie(token, options);\n case \"sessionStorage\":\n return this.sessionTokenStorage.setSessionToken(token);\n default:\n return this.cookie.setAuthCookie(token, options);\n }\n }\n}\n\nexport { Headers, Response, HttpClient };\n","import { HttpClient, HttpClientOptions } from \"./HttpClient\";\n\n/**\n * A class to be extended by the other client classes.\n *\n * @abstract\n * @category SDK\n * @subcategory Internal\n * @param {string} api - The URL of your Hanko API instance\n * @param {HttpClientOptions} options - The options that can be used\n */\nabstract class Client {\n client: HttpClient;\n\n // eslint-disable-next-line require-jsdoc\n constructor(api: string, options: HttpClientOptions) {\n /**\n * @public\n * @type {HttpClient}\n */\n this.client = new HttpClient(api, options);\n }\n}\n\nexport { Client };\n","import { Client } from \"./Client\";\nimport { SessionCheckResponse } from \"../Dto\";\nimport { TechnicalError } from \"../Errors\";\n\n/**\n * A class that handles communication with the Hanko API for the purposes\n * of sessions.\n *\n * @constructor\n * @category SDK\n * @subcategory Clients\n * @extends {Client}\n */\nexport class SessionClient extends Client {\n /**\n * Checks if the current session is still valid.\n *\n * @return {Promise<SessionCheckResponse>}\n * @throws {TechnicalError}\n */\n async validate(): Promise<SessionCheckResponse> {\n const response = await this.client.get(\"/sessions/validate\");\n\n if (!response.ok) {\n throw new TechnicalError();\n }\n\n return await response.json();\n }\n}\n","/**\n * Represents the session state with expiration and last check timestamps.\n *\n * @category SDK\n * @subcategory Internal\n */\nexport interface State {\n expiration: number; // Timestamp (in milliseconds) when the session expires.\n lastCheck: number; // Timestamp (in milliseconds) of the last session check.\n}\n\n/**\n * Manages session state persistence using localStorage.\n *\n * @category SDK\n * @subcategory Internal\n */\nexport class SessionState {\n private readonly storageKey: string;\n private readonly defaultState: State = {\n expiration: 0,\n lastCheck: 0,\n };\n\n /**\n * Creates an instance of SessionState.\n *\n * @param {string} storageKey - The key used to store session state in localStorage.\n */\n constructor(storageKey: string) {\n this.storageKey = storageKey;\n }\n\n /**\n * Loads the current session state from localStorage.\n *\n * @returns {State} The parsed session state or a default state if not found.\n */\n load(): State {\n const item = window.localStorage.getItem(this.storageKey);\n return item == null ? this.defaultState : JSON.parse(item);\n }\n\n /**\n * Saves the session state to localStorage.\n *\n * @param {State | null} session - The session state to save. If null, the default state is used.\n */\n save(session: State | null): void {\n window.localStorage.setItem(\n this.storageKey,\n JSON.stringify(session ? session : this.defaultState),\n );\n }\n}\n","// Callback type for handling window activity changes.\ntype Callback = () => void;\n\n/**\n * Manages window focus and blur events.\n *\n * @class\n * @category SDK\n * @subcategory Internal\n * @param {Callback} onActivityCallback - Callback to invoke when the window gains focus.\n * @param {Callback} onInactivityCallback - Callback to invoke when the window loses focus.\n */\nexport class WindowActivityManager {\n private readonly onActivityCallback: Callback; // Callback for when the window or tab gains focus.\n private readonly onInactivityCallback: Callback; // Callback for when the window or tab loses focus.\n\n // eslint-disable-next-line require-jsdoc\n constructor(onActivityCallback: Callback, onInactivityCallback: Callback) {\n this.onActivityCallback = onActivityCallback;\n this.onInactivityCallback = onInactivityCallback;\n\n // Attach event listeners for focus and blur\n window.addEventListener(\"focus\", this.handleFocus);\n window.addEventListener(\"blur\", this.handleBlur);\n document.addEventListener(\"visibilitychange\", this.handleVisibilityChange);\n }\n\n /**\n * Handles the focus event and invokes the activity callback.\n * @private\n */\n private handleFocus = (): void => {\n this.onActivityCallback();\n };\n\n /**\n * Handles the blur event and invokes the inactivity callback.\n * @private\n */\n private handleBlur = (): void => {\n this.onInactivityCallback();\n };\n\n /**\n * Handles the visibility change event and invokes appropriate callbacks.\n * @private\n */\n private handleVisibilityChange = (): void => {\n if (document.visibilityState === \"visible\") {\n this.onActivityCallback();\n } else {\n this.onInactivityCallback();\n }\n };\n\n /**\n * Checks if the current window has focus.\n * @returns {boolean} True if the window has focus; otherwise, false.\n */\n hasFocus = (): boolean => {\n return document.hasFocus();\n };\n}\n","import { SessionCheckResponse } from \"../Dto\";\n\n// Type representing data returned by the session check callback.\nexport type SessionCheckResult =\n | (Omit