{"version":3,"file":"auth-keep-alive.mjs","sources":["../../../src/frame/auth-keep-alive.ts"],"sourcesContent":["import type { AuthData } from '../types/auth'\nimport type { LoggerInterface } from '../logger'\n\n/**\n * Opt-in keep-alive for the frame access token (#532).\n *\n * The SDK's automatic refresh hangs off the request path: `_ensureAuth` before\n * a call and `_makeRequestWithAuthRetry` on a 401. An app that reads the token\n * with `auth.getAuthData()` and hands it to its OWN backend makes no `$b24`\n * calls at all, so neither path ever fires and the token quietly dies in an\n * idle tab — the app then looks broken although nothing changed.\n *\n * This module is the timer that closes that gap. It costs no REST: refreshing\n * the frame token is a `postMessage` to the parent window.\n */\n\n/**\n * Refresh once less than this remains of the token's lifetime.\n *\n * @default 5 minutes\n */\nexport const FRAME_REFRESH_MARGIN_MS = 5 * 60_000\n\n/**\n * Hard lower bound on the sleep between checks, and the default. Bitrix warns\n * that refreshing too often risks an application being auto-blocked, so this\n * one is a floor rather than a default: `minDelayMs` can only raise it. A\n * one-character slip (`minDelayMs: 30`, meaning seconds) would otherwise be a\n * thousand `postMessage` round-trips a second.\n *\n * @default 30 seconds\n */\nexport const FRAME_PULSE_MIN_MS = 30_000\n\n/**\n * Upper bound on the sleep between checks. It keeps the pulse from sleeping\n * past a token whose lifetime shrank under it, at the cost of one wake-up per\n * 10 minutes in the worst case.\n *\n * @default 10 minutes\n */\nexport const FRAME_PULSE_MAX_MS = 10 * 60_000\n\n/**\n * Tunables for {@link frameTokenDue} / {@link frameTokenDelayMs}. All three are\n * optional; each falls back to the constant of the same name.\n */\nexport type KeepAuthFreshParams = {\n  /**\n   * How long before expiry a refresh becomes due, in milliseconds.\n   * @default 300000 (5 minutes)\n   */\n  marginMs?: number\n  /**\n   * Shortest sleep between checks, in milliseconds. Values below\n   * {@link FRAME_PULSE_MIN_MS} are raised to it — this knob can only slow the\n   * pulse down, never speed it past the safety floor.\n   * @default 30000 (30 seconds)\n   */\n  minDelayMs?: number\n  /**\n   * Longest sleep between checks, in milliseconds.\n   * @default 600000 (10 minutes)\n   */\n  maxDelayMs?: number\n}\n\ntype ResolvedParams = Required<KeepAuthFreshParams>\n\n/**\n * Normalises {@link KeepAuthFreshParams}, dropping values that cannot describe\n * a delay (non-finite, zero, negative) in favour of the defaults, and ordering\n * the bounds so `minDelayMs <= maxDelayMs` holds whatever the caller passed.\n */\nexport function resolveKeepAuthFreshParams(params?: KeepAuthFreshParams): ResolvedParams {\n  const pick = (value: undefined | number, fallback: number): number => {\n    return Number.isFinite(value) && (value as number) > 0\n      ? (value as number)\n      : fallback\n  }\n\n  const marginMs = pick(params?.marginMs, FRAME_REFRESH_MARGIN_MS)\n  // The floor is not negotiable — see FRAME_PULSE_MIN_MS.\n  const minDelayMs = Math.max(FRAME_PULSE_MIN_MS, pick(params?.minDelayMs, FRAME_PULSE_MIN_MS))\n  const maxDelayMs = pick(params?.maxDelayMs, FRAME_PULSE_MAX_MS)\n\n  return {\n    marginMs,\n    minDelayMs,\n    // A caller that inverts the bounds gets the wider of the two as the\n    // ceiling rather than a `Math.min(max, Math.max(min, …))` that silently\n    // collapses to `min` for every input.\n    maxDelayMs: Math.max(minDelayMs, maxDelayMs)\n  }\n}\n\n/**\n * Is the token due for a refresh?\n *\n * An unreadable `expires` counts as due: the cost of one extra `postMessage`\n * is nothing next to the cost of missing the only chance to refresh.\n *\n * @param expiresSec expiry as a UNIX timestamp in **seconds** — the unit\n *   `AuthData.expires` uses.\n * @param nowMs current time in milliseconds (`Date.now()`).\n */\nexport function frameTokenDue(\n  expiresSec: undefined | number,\n  nowMs: number,\n  params?: KeepAuthFreshParams\n): boolean {\n  if (!Number.isFinite(expiresSec) || (expiresSec as number) <= 0) {\n    return true\n  }\n\n  const { marginMs } = resolveKeepAuthFreshParams(params)\n  return (expiresSec as number) * 1_000 - nowMs <= marginMs\n}\n\n/**\n * How long to sleep before the next check, clamped to the configured bounds.\n *\n * Aimed at \"expiry minus margin\", so the pulse sleeps through the part of the\n * token's life where there is nothing to do rather than waking on a fixed tick.\n * The clamp still applies: with the defaults, anything further out than 15\n * minutes sleeps the 10-minute maximum.\n *\n * @param expiresSec expiry as a UNIX timestamp in **seconds**.\n * @param nowMs current time in milliseconds (`Date.now()`).\n */\nexport function frameTokenDelayMs(\n  expiresSec: undefined | number,\n  nowMs: number,\n  params?: KeepAuthFreshParams\n): number {\n  const { marginMs, minDelayMs, maxDelayMs } = resolveKeepAuthFreshParams(params)\n\n  if (!Number.isFinite(expiresSec) || (expiresSec as number) <= 0) {\n    return minDelayMs\n  }\n\n  const untilRefresh = (expiresSec as number) * 1_000 - nowMs - marginMs\n  return Math.min(maxDelayMs, Math.max(minDelayMs, untilRefresh))\n}\n\n/**\n * The slice of `AuthActions` the pulse needs. Structural on purpose: it keeps\n * this module independent of `AuthManager` and makes the driver testable with a\n * two-method stub.\n */\nexport type KeepAuthFreshActions = {\n  getAuthData: () => false | AuthData\n  refreshAuth: () => Promise<AuthData>\n}\n\n/**\n * The timer itself: wakes up, refreshes the token if it is due, and schedules\n * the next wake-up from the new expiry.\n *\n * Two properties matter more than the schedule:\n *\n * - **It never throws.** A refusal from the portal is a state, not an exception\n *   for the app: a failed tick is logged through the SDK logger (which is the\n *   null logger unless the app wired one, so production stays silent) and the\n *   pulse backs off — see `#backoffDelayMs`: 30s, 1m, 2m, 4m, 8m, then every\n *   10m, reset by the first check that ends with a usable token.\n * - **It listens to `visibilitychange`.** A background tab's timers are\n *   throttled and a frozen tab's are not run at all, so a timer alone would\n *   come back to a dead token. On return the pulse re-arms from the time the\n *   next check was due: if that moment has passed it checks at once, and if it\n *   has not, a tab switch does not get to walk past the backoff.\n */\nexport class AuthKeepAlive {\n  readonly #actions: KeepAuthFreshActions\n  readonly #params: ResolvedParams\n  readonly #getLogger: () => LoggerInterface\n\n  #timer: null | ReturnType<typeof setTimeout> = null\n  #isRunning: boolean = false\n  // The run whose tick is currently in flight, or `null`. Run-scoped rather\n  // than a plain flag: a tick left over from an earlier run must not block a\n  // newer run's tick, or the pulse strands itself with nothing scheduled.\n  #tickingRunId: null | number = null\n  #onVisibilityChange: null | (() => void) = null\n\n  // Identifies the current start()..stop() run. A tick or timer from an earlier\n  // run must not schedule work for a later one, and a bare ticking flag cannot\n  // tell them apart because `stop()` cannot cancel a refresh already in flight.\n  #runId: number = 0\n  // Consecutive ticks that did not end with a usable token. Drives the backoff.\n  #failures: number = 0\n  // When the next check is due. Everything that wants to check early — the\n  // visibility handler above all — defers to this, so a tab switch cannot walk\n  // past the backoff and turn a refusing parent back into a 30-second retry.\n  #nextCheckAtMs: number = 0\n\n  constructor(\n    actions: KeepAuthFreshActions,\n    getLogger: () => LoggerInterface,\n    params?: KeepAuthFreshParams\n  ) {\n    this.#actions = actions\n    this.#getLogger = getLogger\n    this.#params = resolveKeepAuthFreshParams(params)\n  }\n\n  /**\n   * Starts the pulse. Idempotent, and a no-op outside a browser (SSR): there is\n   * no parent window to postMessage to, and no `document` to watch.\n   */\n  public start(): void {\n    if (this.#isRunning || typeof window === 'undefined') {\n      return\n    }\n\n    this.#isRunning = true\n    this.#failures = 0\n\n    // The run id is bumped by `stop()`, which is the only thing that needs to\n    // invalidate one. Bumping here as well would be a second mechanism doing\n    // the same job, and neither would be observable on its own.\n    const runId = this.#runId\n\n    if (typeof document !== 'undefined') {\n      this.#onVisibilityChange = () => {\n        if (document.visibilityState !== 'visible') {\n          return\n        }\n\n        // Coming back to the tab is the one moment a throttled timer is\n        // certainly stale: a frozen tab's timers do not run at all, so the\n        // check that was due while we were away never happened. Re-arming from\n        // the deadline handles both halves of that — if the deadline has\n        // passed, check now; if it has not, the timer we would have replaced\n        // was not late after all.\n        //\n        // Gating on the deadline rather than on the floor is what keeps a tab\n        // switch from walking past the backoff: fifty alt-tabs against a\n        // refusing parent must not become fifty messages.\n        const currentRunId = this.#runId\n        const waitMs = this.#nextCheckAtMs - Date.now()\n\n        if (waitMs > 0) {\n          this.#schedule(waitMs, currentRunId)\n          return\n        }\n\n        void this.#tick(currentRunId)\n      }\n      document.addEventListener('visibilitychange', this.#onVisibilityChange)\n    }\n\n    if (this.#tickingRunId !== null) {\n      // A tick from the previous run is still awaiting its refresh. It belongs\n      // to a stale run and will not schedule anything, so this run arms its own\n      // timer rather than waiting on it.\n      //\n      // That briefly leaves two refreshes in flight across the two runs, which\n      // is fine: `AuthManager.refreshAuth()` coalesces them into one message to\n      // the parent window.\n      this.#schedule(this.#params.minDelayMs, runId)\n      return\n    }\n\n    void this.#tick(runId)\n  }\n\n  /**\n   * Stops the pulse and detaches the listener. Idempotent; safe to call on a\n   * pulse that never started.\n   */\n  public stop(): void {\n    this.#isRunning = false\n    // Invalidate the run so a refresh already in flight cannot re-arm the timer\n    // when it settles — `stop()` cannot cancel it, only disown it.\n    this.#runId += 1\n    this.#clearTimer()\n\n    if (this.#onVisibilityChange !== null && typeof document !== 'undefined') {\n      document.removeEventListener('visibilitychange', this.#onVisibilityChange)\n    }\n\n    this.#onVisibilityChange = null\n  }\n\n  /**\n   * True between `start()` and `stop()`. Internal: the class does not leave the\n   * package (only {@link KeepAuthFreshParams} is exported), so this is for the\n   * owning `B24Frame` and for tests.\n   */\n  public get isRunning(): boolean {\n    return this.#isRunning\n  }\n\n  async #tick(runId: number): Promise<void> {\n    // `#tickingRunId === runId` is the re-entrancy guard: one check at a time\n    // within a run, so a visibility event cannot send a second refresh on top\n    // of one already in flight. A tick from an OLDER run is not a reason to\n    // skip this one — it has been disowned and will schedule nothing.\n    //\n    // There is deliberately no `#runId !== runId` check here. `#schedule` is\n    // the gate that disowns a stale run, and it is the only way a tick is ever\n    // armed: every caller passes the run id it read a statement earlier, and\n    // `stop()` clears the pending timer. A check here would be unreachable, and\n    // an unreachable guard is a claim no test can keep honest.\n    if (!this.#isRunning || this.#tickingRunId === runId) {\n      return\n    }\n\n    this.#tickingRunId = runId\n\n    let delay = this.#params.minDelayMs\n\n    try {\n      const authData = this.#actions.getAuthData()\n      // `getAuthData()` returns `false` once the token has already expired —\n      // that is `due` too, and the reason the pulse exists is to get ahead of it.\n      const expires = authData === false ? undefined : authData.expires\n\n      if (!frameTokenDue(expires, Date.now(), this.#params)) {\n        this.#failures = 0\n        delay = frameTokenDelayMs(expires, Date.now(), this.#params)\n      } else {\n        const refreshed = await this.#actions.refreshAuth()\n        // Not `refreshed?.expires`: optional chaining does not short-circuit on\n        // `false`, and a frame refresh can resolve falsy.\n        const freshExpires = refreshed ? refreshed.expires : undefined\n\n        if (frameTokenDue(freshExpires, Date.now(), this.#params)) {\n          // The refresh succeeded and bought no headroom: the token's whole\n          // life is shorter than the margin, or the portal answered with an\n          // expiry we cannot use. Ticking again at the floor would mean a\n          // `postMessage` every 30 seconds for as long as the tab is open —\n          // the auto-block pattern. Back off instead.\n          this.#failures += 1\n          delay = this.#backoffDelayMs()\n        } else {\n          this.#failures = 0\n          delay = frameTokenDelayMs(freshExpires, Date.now(), this.#params)\n        }\n      }\n    } catch (error) {\n      // Never surface to the app. The logger is the null logger by default, so\n      // this is silent in production unless the app wired a sink itself.\n      //\n      // Only `error.message` — never the error object or the payload, which on\n      // this path carries the tokens. (#43)\n      this.#failures += 1\n      delay = this.#backoffDelayMs()\n\n      try {\n        this.#getLogger().warning('keepAuthFresh: refresh failed, will retry', {\n          error: error instanceof Error ? error.message : String(error),\n          failures: this.#failures,\n          retryInMs: delay\n        }).catch(() => {})\n      } catch {\n        // A logger that throws on the way in is still not the app's problem.\n      }\n    } finally {\n      if (this.#tickingRunId === runId) {\n        this.#tickingRunId = null\n      }\n\n      this.#schedule(delay, runId)\n    }\n  }\n\n  /**\n   * Exponential backoff over consecutive failed (or useless) refreshes, from\n   * the floor up to the ceiling: 30s, 1m, 2m, 4m, 8m, then 10m forever.\n   *\n   * A parent window that is navigated away, blocked or refusing is the case\n   * `REFRESH_AUTH_TIMEOUT` exists for, and each attempt costs a message plus a\n   * 10-second reject timer. Retrying it at a flat 30 seconds for the life of\n   * the tab is the behaviour this exists to prevent.\n   */\n  #backoffDelayMs(): number {\n    // Capped before the shift so a long-lived tab cannot overflow the exponent.\n    const steps = Math.min(Math.max(this.#failures - 1, 0), 10)\n    return Math.min(this.#params.maxDelayMs, this.#params.minDelayMs * 2 ** steps)\n  }\n\n  #schedule(delayMs: number, runId: number): void {\n    if (!this.#isRunning || this.#runId !== runId) {\n      return\n    }\n\n    this.#clearTimer()\n    this.#nextCheckAtMs = Date.now() + delayMs\n    this.#timer = setTimeout(() => {\n      this.#timer = null\n      void this.#tick(runId)\n    }, delayMs)\n\n    // Node keeps the process alive for a pending timer; a keep-alive must not.\n    // Browsers have no `unref`, hence the guard.\n    const timer = this.#timer as unknown as { unref?: () => void }\n    if (typeof timer?.unref === 'function') {\n      timer.unref()\n    }\n  }\n\n  #clearTimer(): void {\n    if (this.#timer !== null) {\n      clearTimeout(this.#timer)\n      this.#timer = null\n    }\n  }\n}\n"],"names":[],"mappings":"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"}