{"version":3,"file":"refreshSession-B_E2FLUs.cjs","names":[],"sources":["../src/session/refreshSession.ts"],"sourcesContent":["import type { Client as AuthClient, ErrorResponse } from '../auth';\nimport type { FetchResponse } from '../fetch';\nimport type { StoredSession } from './session';\nimport type { SessionStorage } from './storage';\n\nclass DummyLock implements Lock {\n  async request(\n    _name: string,\n    _options: { mode: 'exclusive' | 'shared' },\n    // biome-ignore lint/suspicious/noExplicitAny: any\n    callback: () => Promise<any>,\n  ) {\n    return callback();\n  }\n}\n\ninterface Lock {\n  request: (\n    name: string,\n    options: { mode: 'exclusive' | 'shared' },\n    // biome-ignore lint/suspicious/noExplicitAny: blah\n    callback: () => Promise<any>,\n    // biome-ignore lint/suspicious/noExplicitAny: blah\n  ) => Promise<any>;\n}\n\nconst lock: Lock =\n  typeof navigator !== 'undefined' && navigator.locks\n    ? navigator.locks\n    : new DummyLock();\n\n/**\n * Refreshes the authentication session if needed\n *\n * This function checks if the current session needs to be refreshed based on\n * the access token expiration time. If a refresh is needed, it will attempt to\n * refresh the token using the provided auth client.\n *\n * @param auth - The authentication client to use for token refresh\n * @param storage - The session storage implementation\n * @param marginSeconds - The number of seconds before the token expiration to refresh the session. If the token is still valid for this duration, it will not be refreshed. Set to 0 to force the refresh.\n * @returns A promise that resolves to the current session (refreshed if needed) or null if no session exists\n */\nexport const refreshSession = async (\n  auth: AuthClient,\n  storage: SessionStorage,\n  marginSeconds = 60,\n): Promise<StoredSession | null> => {\n  try {\n    return await _refreshSession(auth, storage, marginSeconds);\n  } catch (error) {\n    try {\n      // we retry the refresh token in case of transient error\n      // or race conditions\n      console.warn('error refreshing session, retrying:', error);\n      return await _refreshSession(auth, storage, marginSeconds);\n    } catch (error) {\n      const errResponse = error as FetchResponse<ErrorResponse>;\n      if (errResponse?.status === 401) {\n        // this probably means the refresh token is invalid\n        console.error('session probably expired');\n        storage.remove();\n      }\n      return null;\n    }\n  }\n};\n\n/**\n * Internal implementation of the refresh session logic\n *\n * @param auth - The authentication client to use for token refresh\n * @param storage - The session storage implementation\n * @param marginSeconds - How many seconds before expiration to trigger a refresh\n * @returns A promise that resolves to the current session (refreshed if needed) or null if no session exists\n * @private\n */\nconst _refreshSession = async (\n  auth: AuthClient,\n  storage: SessionStorage,\n  marginSeconds = 60,\n): Promise<StoredSession | null> => {\n  const {\n    session,\n    needsRefresh,\n  }: { session: StoredSession | null; needsRefresh: boolean } =\n    await lock.request('nhostSessionLock', { mode: 'shared' }, async () => {\n      return _needsRefresh(storage, marginSeconds);\n    });\n\n  if (!session) {\n    return null; // No session found\n  }\n\n  if (!needsRefresh) {\n    return session; // No need to refresh\n  }\n\n  const refreshedSession: StoredSession | null = await lock.request(\n    'nhostSessionLock',\n    { mode: 'exclusive' },\n    async () => {\n      const { session, needsRefresh, sessionExpired } = _needsRefresh(\n        storage,\n        marginSeconds,\n      );\n\n      if (!session) {\n        return null; // No session found\n      }\n\n      if (!needsRefresh) {\n        return session; // No need to refresh\n      }\n\n      try {\n        const response = await auth.refreshToken({\n          refreshToken: session.refreshToken,\n        });\n        storage.set(response.body);\n\n        return response.body;\n      } catch (error) {\n        if (!sessionExpired) {\n          return session;\n        }\n\n        throw error;\n      }\n    },\n  );\n\n  return refreshedSession;\n};\n\n/**\n * Checks if the current session needs to be refreshed based on token expiration\n *\n * @param storage - The session storage implementation\n * @param marginSeconds - How many seconds before expiration to trigger a refresh\n * @returns An object containing the session, whether it needs refreshing, and whether it has expired\n * @private\n */\nconst _needsRefresh = (storage: SessionStorage, marginSeconds = 60) => {\n  const session = storage.get();\n  if (!session) {\n    return { session: null, needsRefresh: false, sessionExpired: false };\n  }\n\n  if (!session.decodedToken?.exp) {\n    // if the session does not have a valid decoded token, treat it as expired\n    // as we can't determine its validity\n    return { session, needsRefresh: true, sessionExpired: true };\n  }\n\n  // Force refresh if marginSeconds is 0\n  if (marginSeconds === 0) {\n    return { session, needsRefresh: true, sessionExpired: false };\n  }\n\n  const currentTime = Date.now();\n  if (session.decodedToken.exp - currentTime > marginSeconds * 1000) {\n    return { session, needsRefresh: false, sessionExpired: false };\n  }\n\n  return {\n    session,\n    needsRefresh: true,\n    sessionExpired: session.decodedToken.exp < currentTime,\n  };\n};\n"],"mappings":"AAKA,IAqBM,EACiB,oBAAd,WAA6B,UAAU,MAC1C,UAAU,MACV,IAxBN,MACE,aAAM,CACJ,EACA,EAEA,GAEA,OAAO,GACT,GA8BW,EAAiB,MAC5B,EACA,EACA,EAAgB,MAEhB,IACE,aAAa,EAAgB,EAAM,EAAS,EAC9C,CAAA,MAAS,GACP,IAIE,OADA,QAAQ,KAAK,sCAAuC,SACvC,EAAgB,EAAM,EAAS,EAC9C,CAAA,MAAS,GAOP,OAL4B,MAAxB,GAAa,SAEf,QAAQ,MAAM,4BACd,EAAQ,UAEH,IACT,CACF,GAYI,EAAkB,MACtB,EACA,EACA,EAAgB,MAEhB,MAAM,QACJ,EAAA,aACA,SAEM,EAAK,QAAQ,mBAAoB,CAAE,KAAM,WAAY,SAClD,EAAc,EAAS,KAGlC,OAAK,EAIA,QAIgD,EAAK,QACxD,mBACA,CAAE,KAAM,cACR,UACE,MAAM,QAAE,EAAA,aAAS,EAAA,eAAc,GAAmB,EAChD,EACA,GAGF,IAAK,EACH,OAAO,KAGT,IAAK,EACH,OAAO,EAGT,IACE,MAAM,QAAiB,EAAK,aAAa,CACvC,aAAc,EAAQ,eAIxB,OAFA,EAAQ,IAAI,EAAS,MAEd,EAAS,IAClB,CAAA,MAAS,GACP,IAAK,EACH,OAAO,EAGT,MAAM,CACR,KAjCK,EAJA,IAuCT,EAaI,EAAA,CAAiB,EAAyB,EAAgB,MAC9D,MAAM,EAAU,EAAQ,MACxB,IAAK,EACH,MAAO,CAAE,QAAS,KAAM,cAAc,EAAO,gBAAgB,GAG/D,IAAK,EAAQ,cAAc,IAGzB,MAAO,CAAE,UAAS,cAAc,EAAM,gBAAgB,GAIxD,GAAsB,IAAlB,EACF,MAAO,CAAE,UAAS,cAAc,EAAM,gBAAgB,GAGxD,MAAM,EAAc,KAAK,MACzB,OAAI,EAAQ,aAAa,IAAM,EAA8B,IAAhB,EACpC,CAAE,UAAS,cAAc,EAAO,gBAAgB,GAGlD,CACL,UACA,cAAc,EACd,eAAgB,EAAQ,aAAa,IAAM,EAC7C"}