{"version":3,"sources":["../src/index.ts","../src/arrayBuffer.ts","../src/toReadableString.ts","../src/capLength.ts","../src/concatIterators.ts","../src/dateTimeStr.ts","../src/assert.ts","../src/timeConstants.ts","../src/duration.ts","../src/heap.ts","../src/getEnv.ts","../src/asNumber.ts","../src/isPromise.ts","../src/sleep.ts","../src/http.ts","../src/isEmpty.ts","../src/kvStore.ts","../src/sum.ts","../src/mean.ts","../src/median.ts","../src/memoize.ts","../src/base64Url.ts","../src/lru.ts","../src/localStore.ts","../src/round.ts","../src/roundToString.ts","../src/safeBtoa.ts","../src/nonEmpty.ts","../src/sha256.ts","../src/nonNil.ts","../src/timer.ts"],"sourcesContent":["export * from \"./arrayBuffer\";\nexport * from \"./capLength\";\nexport * from \"./concatIterators\";\nexport * from \"./dateTimeStr\";\nexport * from \"./assert\";\nexport * from \"./duration\";\nexport * from \"./heap\";\nexport * from \"./getEnv\";\nexport * from \"./asNumber\";\nexport * from \"./isPromise\";\nexport * from \"./http\";\nexport * from \"./isEmpty\";\nexport * from \"./kvStore\";\nexport * from \"./mean\";\nexport * from \"./median\";\nexport * from \"./memoize\";\nexport * from \"./base64Url\";\nexport * from \"./lru\";\nexport * from \"./localStore\";\nexport * from \"./roundToString\";\nexport * from \"./safeBtoa\";\nexport * from \"./nonEmpty\";\nexport * from \"./sha256\";\nexport * from \"./nonNil\";\nexport * from \"./round\";\nexport * from \"./timeConstants\";\nexport * from \"./timer\";\nexport * from \"./sum\";\nexport * from \"./storageAdapter\";\nexport * from \"./toReadableString\";\nexport * from \"./sleep\";\n","/**\n * Encode an input ArrayBuffer into a hex string.\n */\nexport function arrayBufferToHex(buffer: ArrayBuffer): string {\n  // Create a Uint8Array view of the ArrayBuffer\n  const byteArray = new Uint8Array(buffer);\n\n  // Convert each byte to a two-character hexadecimal string\n  return Array.from(byteArray)\n    .map((byte) => byte.toString(16).padStart(2, \"0\"))\n    .join(\"\");\n}\n\n/**\n * Encode an input ArrayBuffer into a base64 string.\n */\nexport function arrayBufferToBase64(buffer: ArrayBuffer): string {\n  // Convert the ArrayBuffer to a Uint8Array\n  const byteArray = new Uint8Array(buffer);\n\n  // Create a binary string from the byte array\n  const binaryString = Array.from(byteArray)\n    .map((byte) => String.fromCodePoint(byte))\n    .join(\"\");\n\n  // Encode the binary string to base64. No need to use safeBtoa because we\n  // already simplified the binary input above.\n  return btoa(binaryString);\n}\n","/**\n * Make the given argument of unknown type into something human-readable.\n * For Error objects, you can specify options to make the string more verbose.\n */\nexport function toReadableString(\n  u: unknown,\n  options?: { includeStack?: boolean; includeErrorProps?: boolean },\n): string {\n  if (typeof u === \"string\") {\n    return u;\n  }\n\n  if (u === undefined) {\n    return \"undefined\";\n  }\n\n  if (u instanceof Error) {\n    const error = u as Error;\n    let result = \"\";\n\n    // Always include the name and message\n    const errorName = error.name || \"Error\";\n    const errorMessage =\n      error.message || \"An error occurred with no message provided.\";\n\n    result += `${errorName}: ${errorMessage}`;\n\n    // Optionally include the stack trace\n    if (options?.includeStack && error.stack) {\n      // Clean up the stack trace to start on a new line,\n      // removing potential duplicate header lines if the browser adds them.\n      const stack = error.stack\n        // Remove the first line if it duplicates the name/message\n        .replace(new RegExp(`^${errorName}:.*\\\\n?`), \"\")\n        .trim();\n\n      if (stack) {\n        result += `\\nStack Trace:\\n${stack}`;\n      }\n    }\n\n    // Add any potential custom error properties (e.g., HTTP status code)\n    if (options?.includeErrorProps) {\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      const customProps: { [key: string]: unknown } = error as any;\n\n      const additionalInfo = Object.keys(customProps)\n        .filter(\n          (key) =>\n            key !== \"name\" &&\n            key !== \"message\" &&\n            key !== \"stack\" &&\n            typeof customProps[key] !== \"function\" &&\n            typeof customProps[key] !== \"object\",\n        )\n        .map((key) => `\\n- ${key}: ${customProps[key]}`);\n\n      if (additionalInfo.length > 0) {\n        result += `\\nAdditional Data:${additionalInfo.join(\"\")}`;\n      }\n    }\n\n    return result;\n  }\n\n  // If the object has a custom toString(), then use it.\n  if (u && typeof u === \"object\" && u.toString !== Object.prototype.toString) {\n    return u.toString();\n  }\n\n  try {\n    // Attempt to JSON stringify the object for inspection.\n    return JSON.stringify(u);\n  } catch {\n    // Fallback if the object cannot be stringified (e.g., circular references).\n    return String(u);\n  }\n}\n","import { toReadableString } from \"./toReadableString\";\n\nexport function capLength(u: unknown, maxLength = 400): string {\n  const s = toReadableString(u);\n\n  if (s.length <= maxLength) {\n    return s;\n  }\n\n  return s.slice(0, maxLength) + ` ... (${s.length - maxLength} more)`;\n}\n","/** Memory-efficient way to concat two or more iterators. */\nexport function* concatIterators<T>(...iterators: Generator<T>[]) {\n  for (const iterator of iterators) {\n    yield* iterator;\n  }\n}\n","export type AnyDateTime = number | Date | string;\n\nfunction isAllDigits(str: string): boolean {\n  return /^\\d+$/.test(str);\n}\n\n/**\n * Convert a number (epoch seconds or milliseconds), string (parseable\n * date/time or epoch seconds or milliseconds), or Date object (no conversion)\n * into a Date object.\n */\nexport function toDate(ts: AnyDateTime): Date {\n  if (typeof ts === \"number\") {\n    // Handle timestamp in seconds (less than 12 digits long).\n    if (ts < 1_000_000_000_00) {\n      return new Date(ts * 1000);\n    }\n\n    // Handle timestamp in milliseconds.\n    return new Date(ts);\n  }\n\n  if (typeof ts === \"string\") {\n    if (isAllDigits(ts)) {\n      return toDate(Number(ts));\n    }\n\n    return new Date(ts);\n  }\n\n  return ts;\n}\n\n/**\n * Returns a date in yyyy-MM format. E.g. '2000-01'.\n *\n * @param dt Specify a date object or default to the current date.\n * @param separator Defaults to '-'.\n */\nexport function yyyyMm(dt = new Date(), separator = \"-\"): string {\n  const yr = dt.getFullYear();\n  const mth = dt.getMonth() + 1;\n\n  return yr + separator + (mth < 10 ? \"0\" + mth : mth);\n}\n\n/**\n * Returns a date in yyyy-MM-dd format. E.g. '2000-01-02'.\n *\n * @param dt Specify a date object or default to the current date.\n * @param separator Defaults to '-'.\n */\nexport function yyyyMmDd(dt = new Date(), separator = \"-\"): string {\n  const day = dt.getDate();\n\n  return yyyyMm(dt, separator) + separator + (day < 10 ? \"0\" + day : day);\n}\n\n/**\n * Returns a date in hh:mm format. E.g. '01:02'.\n *\n * @param dt Specify a date object or default to the current date/time.\n * @param separator Defaults to ':'.\n */\nexport function hhMm(dt = new Date(), separator = \":\"): string {\n  const hr = dt.getHours();\n  const min = dt.getMinutes();\n\n  return (hr < 10 ? \"0\" + hr : hr) + separator + (min < 10 ? \"0\" + min : min);\n}\n\n/**\n * Returns a date in hh:mm:ss format. E.g. '01:02:03'.\n *\n * @param dt Specify a date object or default to the current date/time.\n * @param separator Defaults to ':'.\n */\nexport function hhMmSs(dt = new Date(), separator = \":\"): string {\n  const sec = dt.getSeconds();\n\n  return hhMm(dt, separator) + separator + (sec < 10 ? \"0\" + sec : sec);\n}\n\n/**\n * Returns a date in hh:mm:ss.SSS format. E.g. '01:02:03.004'.\n *\n * @param dt Specify a date object or default to the current date/time.\n * @param timeSeparator Separator for hh/mm/ss. Defaults to ':'.\n * @param msSeparator Separator before SSS. Defaults to '.'.\n */\nexport function hhMmSsMs(\n  dt = new Date(),\n  timeSeparator = \":\",\n  msSeparator = \".\",\n): string {\n  const ms = dt.getMilliseconds();\n\n  return (\n    hhMmSs(dt, timeSeparator) +\n    msSeparator +\n    (ms < 10 ? \"00\" + ms : ms < 100 ? \"0\" + ms : ms)\n  );\n}\n\n/**\n * Returns the timezone string for the given date. E.g. '+8', '-3.5'.\n * Returns 'Z' for UTC.\n *\n * @param dt Specify a date object or default to the current date/time.\n */\nexport function tzShort(dt = new Date()): string {\n  if (dt.getTimezoneOffset() === 0) {\n    return \"Z\";\n  }\n\n  const tzHours = dt.getTimezoneOffset() / 60;\n  return tzHours >= 0 ? \"+\" + tzHours : String(tzHours);\n}\n\n/**\n * Returns the long month name, zero-indexed. E.g. 0 for 'January'.\n *\n * @param month Zero-indexed month.\n * @param locales Specify the locale, e.g. 'en-US', new Intl.Locale(\"en-US\").\n */\nexport function getLongMonthNameZeroIndexed(\n  month: number,\n  locales: Intl.LocalesArgument = \"default\",\n): string {\n  return new Date(2024, month, 15).toLocaleString(locales, {\n    month: \"long\",\n  });\n}\n\n/**\n * Returns the long month name, one-indexed. E.g. 1 for 'January'.\n *\n * @param month One-indexed month.\n * @param locales Specify the locale, e.g. 'en-US', new Intl.Locale(\"en-US\").\n */\nexport function getLongMonthNameOneIndexed(\n  month: number,\n  locales: Intl.LocalesArgument = \"default\",\n): string {\n  return getLongMonthNameZeroIndexed(month - 1, locales);\n}\n\n/**\n * Returns the short month name, zero-indexed. E.g. 0 for 'Jan'.\n *\n * @param month Zero-indexed month.\n * @param locales Specify the locale, e.g. 'en-US', new Intl.Locale(\"en-US\").\n */\nexport function getShortMonthNameZeroIndexed(\n  month: number,\n  locales: Intl.LocalesArgument = \"default\",\n): string {\n  return new Date(2000, month, 15).toLocaleString(locales, {\n    month: \"short\",\n  });\n}\n\n/**\n * Returns the short month name, one-indexed. E.g. 1 for 'Jan'.\n *\n * @param month One-indexed month.\n * @param locales Specify the locale, e.g. 'en-US', new Intl.Locale(\"en-US\").\n */\nexport function getShortMonthNameOneIndexed(\n  month: number,\n  locales: Intl.LocalesArgument = \"default\",\n): string {\n  return getShortMonthNameZeroIndexed(month - 1, locales);\n}\n\n/**\n * Returns a human-readable string date/time like '2025-01-01 22:31:16Z'.\n * Excludes the milliseconds assuming it is not necessary for display.\n */\nexport function getDisplayDateTime(ts: AnyDateTime) {\n  const iso = toDate(ts).toISOString();\n  const noMs = iso.slice(0, 19) + \"Z\";\n  return noMs.replace(\"T\", \" \");\n}\n","/**\n * Type asserts that `t` is truthy. Throws an error with `errorMessage` if\n * `t` is falsy.\n */\nexport function assert<T>(\n  t: T | null | undefined | \"\" | 0 | -0 | 0n | false | typeof NaN,\n  errorMessage?: string,\n): asserts t is T {\n  if (!t) {\n    throw new Error(\n      errorMessage || `Assertion failed: ${JSON.stringify(t)} is falsy`,\n    );\n  }\n}\n","/**\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file.\n */\n\nexport const MS_PER_SECOND = 1000;\nexport const MS_PER_MINUTE = 60_000;\nexport const MS_PER_HOUR = 3_600_000;\nexport const MS_PER_DAY = 86_400_000;\nexport const MS_PER_WEEK = 604_800_000;\n\nexport const SECONDS_PER_MINUTE = 60;\nexport const SECONDS_PER_HOUR = 3_600;\nexport const SECONDS_PER_DAY = 86_400;\nexport const SECONDS_PER_WEEK = 604_800;\n\nexport const MINUTES_PER_HOUR = 60;\nexport const MINUTES_PER_DAY = 1440;\nexport const MINUTES_PER_WEEK = 10_080;\n\nexport const HOURS_PER_DAY = 24;\nexport const HOURS_PER_WEEK = 168;\n","/**\n * Bunch of miscellaneous constants and utility functions related to handling\n * date and time durations.\n *\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file. Weeks have fixed durations, but are excluded because we\n * use days as the max duration supported.\n */\n\nimport {\n  HOURS_PER_DAY,\n  MINUTES_PER_HOUR,\n  MS_PER_DAY,\n  MS_PER_HOUR,\n  MS_PER_MINUTE,\n  MS_PER_SECOND,\n  SECONDS_PER_MINUTE,\n} from \"./timeConstants\";\n\nexport type Duration = {\n  days?: number;\n  hours?: number;\n  minutes?: number;\n  seconds?: number;\n  milliseconds?: number;\n};\n\n/**\n * One of: days, hours, minutes, seconds, milliseconds\n */\nexport type DurationType = keyof Duration;\n\n/**\n * Order in which the duration type appears in the duration string.\n */\nexport const DURATION_TYPE_SEQUENCE: DurationType[] = [\n  \"days\",\n  \"hours\",\n  \"minutes\",\n  \"seconds\",\n  \"milliseconds\",\n];\n\n/**\n * Follows the same format as Intl.DurationFormat.prototype.format().\n *\n * Short: 1 yr, 2 mths, 3 wks, 3 days, 4 hr, 5 min, 6 sec, 7 ms, 8 μs, 9 ns\n * Long: 1 year, 2 months, 3 weeks, 3 days, 4 hours, 5 minutes, 6 seconds,\n *       7 milliseconds, 8 microseconds, 9 nanoseconds\n * Narrow: 1y 2mo 3w 3d 4h 5m 6s 7ms 8μs 9ns\n */\nexport type DurationStyle = \"short\" | \"long\" | \"narrow\";\n\nexport type DurationSuffixMap = {\n  short: string;\n  shorts: string;\n  long: string;\n  longs: string;\n  narrow: string;\n};\n\nexport type DurationSuffixType = keyof DurationSuffixMap;\n\nexport const DURATION_STYLE_SUFFIX_MAP: Record<\n  DurationType,\n  DurationSuffixMap\n> = {\n  days: {\n    short: \"day\",\n    shorts: \"days\",\n    long: \"day\",\n    longs: \"days\",\n    narrow: \"d\",\n  },\n  hours: {\n    short: \"hr\",\n    shorts: \"hrs\",\n    long: \"hour\",\n    longs: \"hours\",\n    narrow: \"h\",\n  },\n  minutes: {\n    short: \"min\",\n    shorts: \"mins\",\n    long: \"minute\",\n    longs: \"minutes\",\n    narrow: \"m\",\n  },\n  seconds: {\n    short: \"sec\",\n    shorts: \"secs\",\n    long: \"second\",\n    longs: \"seconds\",\n    narrow: \"s\",\n  },\n  milliseconds: {\n    short: \"ms\",\n    shorts: \"ms\",\n    long: \"millisecond\",\n    longs: \"milliseconds\",\n    narrow: \"ms\",\n  },\n};\n\nfunction getDurationStyleForPlural(style: DurationStyle): DurationSuffixType {\n  return style == \"short\" ? \"shorts\" : style === \"long\" ? \"longs\" : style;\n}\n\nfunction getValueAndUnitSeparator(style: DurationStyle): string {\n  return style === \"narrow\" ? \"\" : \" \";\n}\n\nfunction getDurationTypeSeparator(style: DurationStyle): string {\n  return style === \"narrow\" ? \" \" : \", \";\n}\n\n/**\n * Convert a milliseconds duration into a Duration object. If the given ms is\n * zero, then return an object with a single field of zero with duration type\n * of durationTypeForZero.\n *\n * @param durationTypeForZero Defaults to 'milliseconds'\n */\nexport function msToDuration(\n  ms: number,\n  durationTypeForZero?: DurationType,\n): Duration {\n  if (ms === 0) {\n    durationTypeForZero = durationTypeForZero ?? \"milliseconds\";\n    return { [durationTypeForZero]: 0 };\n  }\n\n  const duration: Duration = {};\n\n  for (let i = 0; i < 1; i++) {\n    let seconds = Math.floor(ms / MS_PER_SECOND);\n    const millis = ms - seconds * MS_PER_SECOND;\n\n    if (millis > 0) {\n      duration[\"milliseconds\"] = millis;\n    }\n\n    if (seconds === 0) {\n      break;\n    }\n\n    let minutes = Math.floor(seconds / SECONDS_PER_MINUTE);\n    seconds -= minutes * SECONDS_PER_MINUTE;\n\n    if (seconds > 0) {\n      duration[\"seconds\"] = seconds;\n    }\n\n    if (minutes === 0) {\n      break;\n    }\n\n    let hours = Math.floor(minutes / MINUTES_PER_HOUR);\n    minutes -= hours * MINUTES_PER_HOUR;\n\n    if (minutes > 0) {\n      duration[\"minutes\"] = minutes;\n    }\n\n    if (hours === 0) {\n      break;\n    }\n\n    const days = Math.floor(hours / HOURS_PER_DAY);\n    hours -= days * HOURS_PER_DAY;\n\n    if (hours > 0) {\n      duration[\"hours\"] = hours;\n    }\n\n    if (days > 0) {\n      duration[\"days\"] = days;\n    }\n  }\n\n  return duration;\n}\n\n/**\n * Returns the number of milliseconds for the given duration.\n */\nexport function durationToMs(duration: Duration): number {\n  const daysMs = (duration.days ?? 0) * MS_PER_DAY;\n  const hoursMs = (duration.hours ?? 0) * MS_PER_HOUR;\n  const minsMs = (duration.minutes ?? 0) * MS_PER_MINUTE;\n  const secsMs = (duration.seconds ?? 0) * MS_PER_SECOND;\n  const msMs = duration.milliseconds ?? 0;\n\n  return daysMs + hoursMs + minsMs + secsMs + msMs;\n}\n\n/**\n * Convenience function to return a duration given an ms or Duration.\n */\nexport function durationOrMsToMs(duration: number | Duration): number {\n  return typeof duration === \"number\" ? duration : durationToMs(duration);\n}\n\n/**\n * Format a given Duration object into a string. If the object has no fields,\n * then returns an empty string.\n *\n * @param style Defaults to 'short'\n */\nexport function formatDuration(duration: Duration, style?: DurationStyle) {\n  style = style ?? \"short\";\n  const stylePlural = getDurationStyleForPlural(style);\n\n  const space = getValueAndUnitSeparator(style);\n\n  const a: string[] = [];\n\n  for (const unit of DURATION_TYPE_SEQUENCE) {\n    const value = duration[unit];\n    if (value === undefined) continue;\n\n    const suffixMap = DURATION_STYLE_SUFFIX_MAP[unit];\n    const suffix = value === 1 ? suffixMap[style] : suffixMap[stylePlural];\n    a.push(value + space + suffix);\n  }\n\n  const separator = getDurationTypeSeparator(style);\n  return a.join(separator);\n}\n\n/**\n * Convert a millisecond duration into a human-readable duration string.\n *\n * @param options.durationTypeForZero - Defaults to 'milliseconds'\n * @param options.style - Defaults to 'short'\n */\nexport function readableDuration(\n  ms: number,\n  options?: { durationTypeForZero?: DurationType; style?: DurationStyle },\n): string {\n  const duration = msToDuration(ms, options?.durationTypeForZero);\n\n  return formatDuration(duration, options?.style);\n}\n\n/** A shortened duration string useful for logging timings. */\nexport function elapsed(ms: number): string {\n  // Use long format for 1 minute or over.\n  if (ms > MS_PER_MINUTE) {\n    return readableDuration(ms);\n  }\n\n  // Use seconds format for over 100ms.\n  if (ms > 100) {\n    return `${(ms / 1000).toFixed(3)}s`;\n  }\n\n  // Use milliseconds format.\n  return ms + \"ms\";\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// Heap — a generic binary heap usable as a min-heap or max-heap.\n//\n// Comparator conventions (same as Array.prototype.sort):\n//   compare(a, b) < 0  →  a has higher priority than b  (a closer to top)\n//   compare(a, b) > 0  →  b has higher priority than a  (b closer to top)\n//   compare(a, b) = 0  →  equal priority\n//\n// Min-heap: (a, b) => a - b          top = smallest number\n// Max-heap: (a, b) => b - a          top = largest number\n// By field:  (a, b) => a.ts - b.ts   top = smallest .ts\n//\n// All core operations are O(log n) except peek and size which are O(1).\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type Comparator<T> = (a: T, b: T) => number;\n\nexport class Heap<T> {\n  private readonly data: T[];\n  private readonly compare: Comparator<T>;\n\n  /**\n   * @param compare - Comparator function. Return negative to place `a` above\n   *   `b` in the heap (i.e. closer to the top / higher priority).\n   * @param initial - Optional array of items to heapify in O(n) time.\n   *   The array is copied; the original is not modified.\n   */\n  public constructor(compare: Comparator<T>, initial: T[] = []) {\n    this.compare = compare;\n    this.data = [...initial];\n\n    // Floyd's algorithm: heapify in O(n) by sifting down from last parent.\n    for (let i = parent(this.data.length - 1); i >= 0; i--) {\n      this.siftDown(i);\n    }\n  }\n\n  // ── Accessors ──────────────────────────────────────────────────────────────\n\n  /** Number of items currently in the heap. */\n  public get size(): number {\n    return this.data.length;\n  }\n\n  /** True when the heap contains no items. */\n  public get isEmpty(): boolean {\n    return this.data.length === 0;\n  }\n\n  /** Return the top item without removing it. O(1). */\n  public peek(): T | undefined {\n    return this.data[0];\n  }\n\n  // ── Mutators ───────────────────────────────────────────────────────────────\n\n  /** Add an item. O(log n). */\n  public push(item: T): void {\n    this.data.push(item);\n    this.siftUp(this.data.length - 1);\n  }\n\n  /** Remove and return the top item. O(log n). */\n  public pop(): T | undefined {\n    if (this.data.length === 0) return undefined;\n\n    const top = this.data[0];\n    const last = this.data.pop()!;\n\n    if (this.data.length > 0) {\n      this.data[0] = last;\n      this.siftDown(0);\n    }\n\n    return top;\n  }\n\n  /**\n   * Push a new item and pop the top in one pass — more efficient than calling\n   * push() then pop() separately because it avoids an extra sift. O(log n).\n   */\n  public pushPop(item: T): T {\n    if (this.data.length === 0 || this.compare(item, this.data[0]) <= 0) {\n      // The new item would immediately be popped anyway.\n      return item;\n    }\n\n    const top = this.data[0];\n    this.data[0] = item;\n    this.siftDown(0);\n    return top;\n  }\n\n  /**\n   * Pop the top item and push a replacement in one pass — more efficient than\n   * pop() then push() separately. Throws if the heap is empty. O(log n).\n   */\n  public replace(item: T): T {\n    if (this.data.length === 0) {\n      throw new Error(\"Heap is empty\");\n    }\n\n    const top = this.data[0];\n    this.data[0] = item;\n    this.siftDown(0);\n    return top;\n  }\n\n  /**\n   * Remove the first item that satisfies the predicate.\n   * Returns the removed item, or undefined if not found.\n   *\n   * Finding the item is O(n). The removal itself is O(log n).\n   */\n  public remove(predicate: (item: T) => boolean): T | undefined {\n    const i = this.data.findIndex(predicate);\n    if (i === -1) return undefined;\n    return this.removeAt(i);\n  }\n\n  /**\n   * Remove all items that satisfy the predicate. Returns the removed items in\n   * the order they were found (not priority order).\n   *\n   * O(n) to scan + O(k log n) for k removals.\n   */\n  public removeAll(predicate: (item: T) => boolean): T[] {\n    const removed: T[] = [];\n\n    // Iterate backwards so that removeAt's swap of the last element\n    // doesn't cause us to skip or re-visit items.\n    for (let i = this.data.length - 1; i >= 0; i--) {\n      if (predicate(this.data[i])) {\n        removed.push(this.removeAt(i));\n      }\n    }\n\n    return removed;\n  }\n\n  /** Remove all items. */\n  public clear(): void {\n    this.data.length = 0;\n  }\n\n  // ── Bulk operations ────────────────────────────────────────────────────────\n\n  /**\n   * Add multiple items at once. More efficient than repeated push() calls\n   * when adding many items: uses heapify (O(n)) rather than O(n log n). */\n  public pushAll(items: Iterable<T>): void {\n    for (const item of items) {\n      this.data.push(item);\n    }\n\n    // Re-heapify from scratch.\n    for (let i = parent(this.data.length - 1); i >= 0; i--) {\n      this.siftDown(i);\n    }\n  }\n\n  /**\n   * Drain all items in priority order. The heap is empty afterward.\n   * Equivalent to calling pop() until empty, but expressed as a generator\n   * so callers can break early without popping everything. O(n log n) total.\n   */\n  public *drain(): Generator<T> {\n    while (this.data.length > 0) {\n      yield this.pop()!;\n    }\n  }\n\n  /**\n   * Return a sorted array of all items in priority order without mutating\n   * the heap. O(n log n).\n   */\n  public toSortedArray(): T[] {\n    // Clone into a temporary heap and drain it.\n    const tmp = new Heap<T>(this.compare, this.data);\n    return [...tmp.drain()];\n  }\n\n  // ── Private helpers ────────────────────────────────────────────────────────\n\n  private siftUp(i: number): void {\n    while (i > 0) {\n      const p = parent(i);\n      if (this.compare(this.data[i], this.data[p]) >= 0) break;\n      swap(this.data, i, p);\n      i = p;\n    }\n  }\n\n  private siftDown(i: number): void {\n    const n = this.data.length;\n\n    while (true) {\n      let top = i;\n      const l = leftChild(i);\n      const r = rightChild(i);\n\n      if (l < n && this.compare(this.data[l], this.data[top]) < 0) top = l;\n      if (r < n && this.compare(this.data[r], this.data[top]) < 0) top = r;\n      if (top === i) break;\n\n      swap(this.data, i, top);\n      i = top;\n    }\n  }\n\n  private removeAt(i: number): T {\n    const last = this.data.pop()!;\n\n    // If we just removed the last element, no fixup needed.\n    if (i === this.data.length) return last;\n\n    // Overwrite the target slot with the last element, then restore the\n    // heap invariant. We need to try both directions because the last\n    // element could be either larger or smaller than the removed item's\n    // neighbours.\n    const removed = this.data[i];\n    this.data[i] = last;\n    this.siftUp(i);\n    this.siftDown(i);\n    return removed;\n  }\n}\n\n// ── Index arithmetic (plain functions keep the class body clean) ─────────────\n\nfunction parent(i: number): number {\n  return (i - 1) >> 1;\n}\n\nfunction leftChild(i: number): number {\n  return 2 * i + 1;\n}\n\nfunction rightChild(i: number): number {\n  return 2 * i + 2;\n}\n\nfunction swap<T>(data: T[], i: number, j: number): void {\n  const tmp = data[i];\n  data[i] = data[j];\n  data[j] = tmp;\n}\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\ndeclare const process: any;\n\nexport class MissingEnvVarError extends Error {}\n\n/**\n * Get an environment variable by name. If a default value is not given, and\n * the variable is empty or missing, throw MissingEnvVarError.\n *\n * If an empty string is acceptable, pass `defaultValue` as \"\".\n */\nexport function getEnv(varName: string, defaultValue?: string | null): string {\n  try {\n    const value = process?.env?.[varName];\n\n    // This value should always be a string, unless the user sets it otherwise,\n    // which is an unusual case.\n    if (value) return value;\n  } catch {\n    // ignore\n  }\n\n  if (typeof defaultValue !== \"string\") {\n    throw new MissingEnvVarError(`Missing process.env.${varName}`);\n  }\n\n  return defaultValue;\n}\n","/**\n * Coerce `u` into a number if possible, otherwise just return 0.\n */\nexport function asNumber(u: unknown, defaultValue = 0): number {\n  // If u is a valid number, return it.\n  if (typeof u === \"number\") {\n    return isFinite(u) ? u : defaultValue;\n  }\n\n  // Try to make into a number if not already a number.\n  u = Number(u);\n\n  // If u is a valid number, return it.\n  if (typeof u === \"number\" && isFinite(u)) {\n    return u;\n  }\n\n  // Return `defaultValue` for everything else. This is usually ok if want to\n  // just ignore all other noise.\n  return defaultValue;\n}\n","/**\n * A type guard to check if an object is a Promise (or a \"thenable\"). It checks\n * if the object is not null, is an object, and has a callable .then method.\n *\n * Note that if the Promise expects a certain type like `Promise<T>`, there is\n * no way to validate the type of T unless we resolve the promise. This function\n * does not attempt to typecheck for T in any way.\n */\nexport function isPromise(obj: unknown): obj is Promise<unknown> {\n  // Check if the object is defined and not null.\n  if (!obj || (typeof obj !== \"object\" && typeof obj !== \"function\")) {\n    return false;\n  }\n\n  // Check if the .then property is a function (callable).\n  return \"then\" in obj && typeof obj.then === \"function\";\n}\n\n/**\n * A type guard to check if an object is a native Promise.\n *\n * Note that if the Promise expects a certain type like `Promise<T>`, there is\n * no way to validate the type of T unless we resolve the promise. This function\n * does not attempt to typecheck for T in any way.\n */\nexport function isNativePromise(obj: unknown): obj is Promise<unknown> {\n  return obj instanceof Promise;\n}\n","/**\n * Sleep for a given number of milliseconds. Note that this method is async,\n * so please remember to call it with await, like `await sleep(1000);`.\n */\nexport function sleep(ms: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// Simplified HTTP fetch interface\n//\n// Inspired by axios, but having just the basic functionality, which seems\n// sufficient for the vast majority of fetch use cases.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport { sleep } from \"./sleep\";\n\nexport class HttpError extends Error {\n  constructor(\n    public status: number,\n    message?: string,\n  ) {\n    super(message ?? `HTTP ${status} error`);\n  }\n}\n\n/** Consumes the response body. */\nexport async function safeGetJson(\n  response: Response,\n): Promise<unknown | undefined> {\n  try {\n    return await response.json();\n  } catch {\n    return undefined;\n  }\n}\n\n/** Consumes the response text. */\nexport async function safeGetText(\n  response: Response,\n): Promise<string | undefined> {\n  try {\n    return await response.text();\n  } catch {\n    return undefined;\n  }\n}\n\n/** Consumes the response body. */\nexport async function safeGetErrorMessage(response: Response): Promise<string> {\n  const s = `HTTP ${response.status}: ${response.statusText}`;\n  const text = await safeGetText(response);\n\n  return s + (text ? `: text=${text}` : \"\");\n}\n\nexport async function throwIfError(response: Response): Promise<Response> {\n  if (!response.ok) {\n    throw new HttpError(response.status, await safeGetErrorMessage(response));\n  }\n\n  return response;\n}\n\nexport type HttpBodyInit = BodyInit | object | null;\n\nexport type RequestInitNoBody = Omit<RequestInit, \"body\">;\n\nexport type HttpRequestInit = RequestInitNoBody & {\n  body?: HttpBodyInit;\n};\n\nexport function normalizeBody(body: HttpBodyInit | undefined): {\n  body?: BodyInit | null;\n  headers?: Record<string, string>;\n} {\n  if (\n    body &&\n    typeof body === \"object\" &&\n    !(\n      body instanceof Blob ||\n      body instanceof ArrayBuffer ||\n      body instanceof FormData ||\n      body instanceof URLSearchParams ||\n      body instanceof ReadableStream ||\n      ArrayBuffer.isView(body)\n    )\n  ) {\n    return {\n      body: JSON.stringify(body),\n      headers: { \"Content-Type\": \"application/json\" },\n    };\n  }\n\n  return { body: body as BodyInit | null | undefined };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type HttpMiddleware = (\n  request: Request,\n  next: (req: Request) => Promise<Response>,\n) => Promise<Response>;\n\n/** Throw on non-OK responses. */\nexport const throwOnError: HttpMiddleware = async (request, next) => {\n  const response = await next(request);\n  return await throwIfError(response);\n};\n\n/**\n * Add retries. If you also use throwOnError, the sequence should be\n * `http.use(throwOnError).use(retries())` so that throwOnError runs outside of\n * the retry loops.\n */\nexport const retries: (options?: {\n  maxTries?: number;\n  delayMs?: number;\n  backoffMultiplier?: number;\n}) => HttpMiddleware = (options?: {\n  maxTries?: number;\n  delayMs?: number;\n  backoffMultiplier?: number;\n}) => {\n  const maxTries = options?.maxTries ?? 4;\n  const delayMs = options?.delayMs ?? 1000;\n  const backoffMultiplier = options?.backoffMultiplier ?? 1.5;\n\n  if (maxTries < 1) {\n    throw new Error(`Invalid maxTries=${maxTries}`);\n  }\n\n  return async (\n    request: Request,\n    next: (req: Request) => Promise<Response>,\n  ) => {\n    let ms = delayMs;\n\n    for (let i = 1; i <= maxTries; i++, ms *= backoffMultiplier) {\n      // Don't catch errors. Once thrown, the retry loop will end.\n      const response = await next(request.clone());\n\n      if (i < maxTries && response.status >= 500 && response.status <= 599) {\n        const jitterMs = (Math.random() - 0.5) * 200;\n        await sleep(ms + jitterMs);\n        continue;\n      }\n\n      return response;\n    }\n\n    throw new Error(`Unreachable code in retries`);\n  };\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class HttpFetch {\n  public constructor(private readonly middlewares: HttpMiddleware[] = []) {}\n\n  /** Add a middleware to the chain. */\n  public use(middleware: HttpMiddleware): HttpFetch {\n    return new HttpFetch([...this.middlewares, middleware]);\n  }\n\n  /**\n   * Main fetch method with middleware support. Supports auto-conversion of\n   * JSON bodies to JSON strings, and adding the JSON Content-Type.\n   */\n  public async fetch(\n    input: RequestInfo | URL,\n    init?: HttpRequestInit,\n  ): Promise<Response> {\n    // This applies to every request and hence is not built as a middleware.\n    const { body, headers } = normalizeBody(init?.body);\n\n    // The caller-specified headers will take priority.\n    const newHeaders = new Headers(headers);\n    const callerHeaders = new Headers(init?.headers);\n\n    for (const [k, v] of callerHeaders.entries()) {\n      newHeaders.set(k, v);\n    }\n\n    const newInit: RequestInit = {\n      ...init,\n      body,\n      headers: newHeaders,\n    };\n\n    const request = new Request(input, newInit);\n\n    // Build middleware chain from last to first (so first added runs first)\n    let handler = async (req: Request): Promise<Response> => {\n      return fetch(req);\n    };\n\n    // Apply middlewares in reverse order\n    for (let i = this.middlewares.length - 1; i >= 0; i--) {\n      const currentMiddleware = this.middlewares[i];\n      const nextHandler = handler;\n      handler = (req) => currentMiddleware(req, nextHandler);\n    }\n\n    return handler(request);\n  }\n\n  // Convenience methods\n  public async get(\n    input: RequestInfo | URL,\n    init?: HttpRequestInit,\n  ): Promise<Response> {\n    return this.fetch(input, init);\n  }\n\n  public async post(\n    input: RequestInfo | URL,\n    // A POST request should almost always have a body.\n    body?: HttpBodyInit,\n    // Don't allow specifying the body in the RequestInit.\n    init?: RequestInitNoBody,\n  ): Promise<Response> {\n    return this.fetch(input, { ...init, method: \"POST\", body });\n  }\n\n  public async put(\n    input: RequestInfo | URL,\n    // A PUT request should almost always have a body.\n    body?: HttpBodyInit,\n    // Don't allow specifying the body in the RequestInit.\n    init?: RequestInitNoBody,\n  ): Promise<Response> {\n    return this.fetch(input, { ...init, method: \"PUT\", body });\n  }\n\n  public async patch(\n    input: RequestInfo | URL,\n    // A PATCH request should almost always have a body.\n    body?: HttpBodyInit,\n    // Don't allow specifying the body in the RequestInit.\n    init?: RequestInitNoBody,\n  ): Promise<Response> {\n    return this.fetch(input, { ...init, method: \"PATCH\", body });\n  }\n\n  public async delete(\n    input: RequestInfo | URL,\n    init?: HttpRequestInit,\n  ): Promise<Response> {\n    return this.fetch(input, { ...init, method: \"DELETE\" });\n  }\n\n  public async head(\n    input: RequestInfo | URL,\n    init?: HttpRequestInit,\n  ): Promise<Response> {\n    return this.fetch(input, { ...init, method: \"HEAD\" });\n  }\n}\n\n/**\n * Standard http instance that can be used for most purposes.\n * - To add retries, use `http.use(retries())`\n * - To throw on errors, use `http.use(throwOnError)`\n * - To do both, use `http.use(throwOnError).use(retries())`\n */\nexport const http = new HttpFetch();\n","/**\n * Returns true if `t` is empty.\n */\nexport function isEmpty(t: unknown): boolean {\n  // Anything falsy is considered empty.\n  if (!t) {\n    return true;\n  }\n\n  // Arrays are also of type `object`.\n  if (typeof t !== \"object\") {\n    return false;\n  }\n\n  // `length` includes arrays as well.\n  if (\"length\" in t) {\n    return t.length === 0;\n  }\n\n  // `size` is for Set, Map, Blob etc.\n  if (\"size\" in t) {\n    return t.size === 0;\n  }\n\n  // Super fast check for object emptiness.\n  // https://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object\n  for (const k in t) {\n    return false;\n  }\n\n  return true;\n}\n","/**\n * Indexed DB key-value store with support for auto-expirations.\n *\n * Why use this?\n * 1. Extremely simple interface to use indexed DBs.\n * 2. Auto-expirations with GC frees you from worrying about data clean-up.\n * 3. Any serializable data type can be stored (except undefined).\n *\n * How to use?\n * Just use the `kvStore` global constant like the local storage, but with\n * async interface functions as required by indexed DB.\n *\n * Why not use the indexed DB directly?\n * It will require you to write a lot of code to reinvent the wheel.\n */\n\nimport { Duration, durationOrMsToMs } from \"./duration\";\nimport {\n  FullStorageAdapter,\n  StorageAdapter,\n  StoredObject,\n} from \"./storageAdapter\";\nimport { MS_PER_DAY } from \"./timeConstants\";\n\n/** Global defaults can be updated directly. */\nexport const KvStoreConfig = {\n  /**\n   * Name of the DB in the indexed DB.\n   * Updating the DB name will cause all old entries to be gone.\n   */\n  dbName: \"KVStore\",\n\n  /**\n   * Version of the DB schema. Most likely you will never want to change this.\n   * Updating the version will cause all old entries to be gone.\n   */\n  dbVersion: 1,\n\n  /**\n   * Name of the store within the indexed DB. Each DB can have multiple stores.\n   * In practice, it doesn't matter what you name this to be.\n   */\n  storeName: \"kvStore\",\n\n  /** 30 days in ms. */\n  expiryMs: MS_PER_DAY * 30,\n\n  /** Do GC once per day. */\n  gcIntervalMs: MS_PER_DAY,\n};\n\nexport type KvStoreConfig = typeof KvStoreConfig;\n\n/** Convenience function to update global defaults. */\nexport function configureKvStore(config: Partial<KvStoreConfig>) {\n  Object.assign(KvStoreConfig, config);\n}\n\n/** Type to represent a full object with metadata stored in the store. */\nexport type KvStoredObject<T> = StoredObject<T> & {\n  // The key is required by the ObjectStore.\n  key: string;\n};\n\n/**\n * Parse a stored value string. Returns undefined if invalid or expired.\n * Throws an error if the string cannot be parsed as JSON.\n */\nfunction validateStoredObject<T>(\n  obj: KvStoredObject<T>,\n): KvStoredObject<T> | undefined {\n  if (\n    !obj ||\n    typeof obj !== \"object\" ||\n    typeof obj.key !== \"string\" ||\n    obj.value === undefined ||\n    typeof obj.storedMs !== \"number\" ||\n    typeof obj.expiryMs !== \"number\" ||\n    Date.now() >= obj.expiryMs\n  ) {\n    return undefined;\n  }\n\n  return obj;\n}\n\n/** Add an `onerror` handler to the request. */\nfunction withOnError<T extends IDBRequest | IDBTransaction>(\n  request: T,\n  reject: (reason?: unknown) => void,\n): T {\n  request.onerror = (event) => {\n    reject(event);\n  };\n\n  return request;\n}\n\n/**\n * You can create multiple KvStores if you want, but most likely you will only\n * need to use the default `kvStore` instance.\n */\nexport function createKvStore(\n  dbName: string,\n  options?: {\n    dbVersion?: number;\n    storeName?: string;\n    defaultExpiryMs?: number | Duration;\n    gcIntervalMs?: number | Duration;\n  },\n) {\n  /** We'll init the DB only on first use. */\n  let db: IDBDatabase | undefined;\n\n  const dbVersion = options?.dbVersion ?? KvStoreConfig.dbVersion;\n  const storeName = options?.storeName ?? KvStoreConfig.storeName;\n\n  const defaultExpiryMs = options?.defaultExpiryMs\n    ? durationOrMsToMs(options.defaultExpiryMs)\n    : KvStoreConfig.expiryMs;\n\n  const gcIntervalMs = options?.gcIntervalMs\n    ? durationOrMsToMs(options.gcIntervalMs)\n    : KvStoreConfig.gcIntervalMs;\n\n  const gcMsStorageKey = `__kvStore:lastGcMs:${dbName}:v${dbVersion}:${storeName}`;\n\n  async function getOrCreateDb() {\n    if (!db) {\n      db = await new Promise<IDBDatabase>((resolve, reject) => {\n        const request = withOnError(indexedDB.open(dbName, dbVersion), reject);\n\n        request.onupgradeneeded = (event) => {\n          const db = (event.target as unknown as { result: IDBDatabase })\n            .result;\n\n          // Create the store on DB init.\n          const objectStore = db.createObjectStore(storeName, {\n            keyPath: \"key\",\n          });\n\n          objectStore.createIndex(\"key\", \"key\", {\n            unique: true,\n          });\n        };\n\n        request.onsuccess = (event) => {\n          const db = (event.target as unknown as { result: IDBDatabase })\n            .result;\n          resolve(db);\n        };\n      });\n    }\n\n    return db;\n  }\n\n  async function transact<T>(\n    mode: IDBTransactionMode,\n    callback: (\n      objectStore: IDBObjectStore,\n      resolve: (t: T) => void,\n      reject: (reason?: unknown) => void,\n    ) => void,\n  ): Promise<T> {\n    const db = await getOrCreateDb();\n\n    return await new Promise<T>((resolve, reject) => {\n      const transaction = withOnError(db.transaction(storeName, mode), reject);\n\n      transaction.onabort = (event) => {\n        reject(event);\n      };\n\n      const objectStore = transaction.objectStore(storeName);\n\n      callback(objectStore, resolve, reject);\n    });\n  }\n\n  const obj = {\n    /** Input name for the DB. */\n    dbName,\n\n    /** Input version for the DB. */\n    dbVersion,\n\n    /** Input name for the DB store. */\n    storeName,\n\n    /** Default expiry to use if not specified in set(). */\n    defaultExpiryMs,\n\n    /** Time interval for when GC's occur. */\n    gcIntervalMs,\n\n    /** Local storage key name for the last GC completed timestamp. */\n    gcMsStorageKey,\n\n    /** Set a value in the store. */\n    async set<T>(\n      key: string,\n      value: T,\n      expiryDeltaMs?: number | Duration,\n    ): Promise<T> {\n      const nowMs = Date.now();\n      const stored: KvStoredObject<T> = {\n        key,\n        value,\n        storedMs: nowMs,\n        expiryMs: nowMs + durationOrMsToMs(expiryDeltaMs ?? defaultExpiryMs),\n      };\n\n      return await transact<T>(\"readwrite\", (objectStore, resolve, reject) => {\n        const request = withOnError(objectStore.put(stored), reject);\n\n        request.onsuccess = () => {\n          resolve(value);\n\n          obj.gc(); // check GC on every write\n        };\n      });\n    },\n\n    /** Delete one or multiple keys. */\n    async delete(key: string | string[]): Promise<void> {\n      return await transact<void>(\n        \"readwrite\",\n        (objectStore, resolve, reject) => {\n          objectStore.transaction.oncomplete = () => {\n            resolve();\n          };\n\n          if (typeof key === \"string\") {\n            withOnError(objectStore.delete(key), reject);\n          } else {\n            for (const k of key) {\n              withOnError(objectStore.delete(k), reject);\n            }\n          }\n        },\n      );\n    },\n\n    /** Mainly used to get the expiration timestamp of an object. */\n    async getStoredObject<T>(\n      key: string,\n    ): Promise<KvStoredObject<T> | undefined> {\n      const stored = await transact<KvStoredObject<T> | undefined>(\n        \"readonly\",\n        (objectStore, resolve, reject) => {\n          const request = withOnError(objectStore.get(key), reject);\n\n          request.onsuccess = () => {\n            resolve(request.result);\n          };\n        },\n      );\n\n      if (!stored) {\n        return undefined;\n      }\n\n      try {\n        const valid = validateStoredObject(stored);\n        if (!valid) {\n          await obj.delete(key);\n\n          obj.gc(); // check GC on every read of an expired key\n\n          return undefined;\n        }\n\n        return valid;\n      } catch (e) {\n        console.error(`Invalid kv value: ${key}=${JSON.stringify(stored)}:`, e);\n        await obj.delete(key);\n\n        obj.gc(); // check GC on every read of an invalid key\n\n        return undefined;\n      }\n    },\n\n    /** Get a value by key, or undefined if it does not exist. */\n    async get<T>(key: string): Promise<T | undefined> {\n      const stored = await obj.getStoredObject<T>(key);\n\n      return stored?.value;\n    },\n\n    /** Generic way to iterate through all entries. */\n    async forEach<T>(\n      callback: (\n        key: string,\n        value: T,\n        expiryMs: number,\n        storedMs: number,\n      ) => void | Promise<void>,\n    ): Promise<void> {\n      await transact<void>(\"readonly\", (objectStore, resolve, reject) => {\n        const request = withOnError(objectStore.openCursor(), reject);\n\n        request.onsuccess = async (event) => {\n          const cursor = (\n            event.target as unknown as { result: IDBCursorWithValue }\n          ).result;\n\n          if (cursor) {\n            if (cursor.key) {\n              const valid = validateStoredObject(cursor.value);\n              if (valid !== undefined) {\n                await callback(\n                  String(cursor.key),\n                  valid.value as T,\n                  valid.expiryMs,\n                  valid.storedMs,\n                );\n              }\n            }\n            cursor.continue();\n          } else {\n            resolve();\n          }\n        };\n      });\n    },\n\n    /**\n     * Returns the number of items in the store. Note that getting the size\n     * requires iterating through the entire store because the items could expire\n     * at any time, and hence the size is a dynamic number.\n     */\n    async size(): Promise<number> {\n      let count = 0;\n      await obj.forEach(() => {\n        count++;\n      });\n      return count;\n    },\n\n    /** Remove all items from the store. */\n    async clear(): Promise<void> {\n      await transact<void>(\"readwrite\", (objectStore, resolve, reject) => {\n        const request = withOnError(objectStore.clear(), reject);\n\n        request.onsuccess = () => {\n          resolve();\n        };\n      });\n    },\n\n    /**\n     * Returns all items as map of key to value, mainly used for debugging dumps.\n     * The type T is applied to all values, even though they might not be of type\n     * T (in the case when you store different data types in the same store).\n     */\n    async asMap<T>(): Promise<Map<string, StoredObject<T>>> {\n      const map = new Map<string, StoredObject<T>>();\n      await obj.forEach((key, value, expiryMs, storedMs) => {\n        map.set(key, { value: value as T, expiryMs, storedMs });\n      });\n      return map;\n    },\n\n    /** Returns the ms timestamp for the last GC (garbage collection). */\n    getLastGcMs(): number {\n      const lastGcMsStr = localStorage.getItem(gcMsStorageKey);\n      if (!lastGcMsStr) return 0;\n\n      const ms = Number(lastGcMsStr);\n      return isNaN(ms) ? 0 : ms;\n    },\n\n    /** Set the ms timestamp for the last GC (garbage collection). */\n    setLastGcMs(ms: number) {\n      localStorage.setItem(gcMsStorageKey, String(ms));\n    },\n\n    /** Perform garbage-collection if due, else do nothing. */\n    async gc(): Promise<void> {\n      const lastGcMs = obj.getLastGcMs();\n\n      // Set initial timestamp - no need GC now.\n      if (!lastGcMs) {\n        obj.setLastGcMs(Date.now());\n        return;\n      }\n\n      if (Date.now() < lastGcMs + gcIntervalMs) {\n        return; // not due for next GC yet\n      }\n\n      // GC is due now, so run it.\n      await obj.gcNow();\n    },\n\n    /**\n     * Perform garbage collection immediately without checking whether we are\n     * due for the next GC or not.\n     */\n    async gcNow(): Promise<void> {\n      console.log(`Starting kvStore GC on ${dbName} v${dbVersion}...`);\n\n      // Prevent concurrent GC runs.\n      obj.setLastGcMs(Date.now());\n\n      const keysToDelete: string[] = [];\n      await obj.forEach(\n        async (key: string, value: unknown, expiryMs: number) => {\n          if (value === undefined || Date.now() >= expiryMs) {\n            keysToDelete.push(key);\n          }\n        },\n      );\n\n      if (keysToDelete.length) {\n        await obj.delete(keysToDelete);\n      }\n\n      console.log(\n        `Finished kvStore GC on ${dbName} v${dbVersion} ` +\n          `- deleted ${keysToDelete.length} keys`,\n      );\n\n      // Mark the end time as last GC time.\n      obj.setLastGcMs(Date.now());\n    },\n\n    /** Returns `this` casted into a StorageAdapter<T>. */\n    asStorageAdapter<T>(): StorageAdapter<T> {\n      return obj as StorageAdapter<T>;\n    },\n  } as const;\n\n  // Using `any` because the store could store any type of data for each key,\n  // but the caller can specify a more specific type when calling each of the\n  // methods.\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  return obj satisfies FullStorageAdapter<any>;\n}\n\nexport type KvStore = ReturnType<typeof createKvStore>;\n\n/**\n * Default KV store ready for immediate use. You can create new instances if\n * you want, but most likely you will only need one store instance.\n */\nexport const kvStore = createKvStore(KvStoreConfig.dbName);\n\n/** Create a KV store item with a key and a default expiration. */\nexport function kvStoreItem<T>(\n  key: string,\n  expiryMs?: number | Duration,\n  store: KvStore = kvStore,\n) {\n  const defaultExpiryMs = expiryMs && durationOrMsToMs(expiryMs);\n\n  const obj = {\n    key,\n    defaultExpiryMs,\n    store,\n\n    /** Set a value in the store. */\n    async set(value: T, expiryDeltaMs?: number | undefined): Promise<void> {\n      await store.set(key, value, expiryDeltaMs ?? defaultExpiryMs);\n    },\n\n    /**\n     * Example usage:\n     *\n     *   const { value, storedMs, expiryMs, storedMs } =\n     *     await myKvItem.getStoredObject();\n     */\n    async getStoredObject(): Promise<KvStoredObject<T> | undefined> {\n      return await store.getStoredObject(key);\n    },\n\n    /** Get a value by key, or undefined if it does not exist. */\n    async get(): Promise<T | undefined> {\n      return await store.get(key);\n    },\n\n    /** Delete this key from the store. */\n    async delete(): Promise<void> {\n      await store.delete(key);\n    },\n  } as const;\n\n  return obj;\n}\n\n/** Class to represent one key in the store with a default expiration. */\nexport type KvStoreItem<T> = ReturnType<typeof kvStoreItem<T>>;\n","import { asNumber } from \"./asNumber\";\n\n/**\n * Add all the numbers together in the given array.\n * Non-numbers will be coerced into numbers if possible.\n */\nexport function sum(numbers: unknown[]): number {\n  return numbers.reduce((accumulated: number, current: unknown) => {\n    return accumulated + asNumber(current);\n  }, 0);\n}\n","import { sum } from \"./sum\";\n\n/**\n * Compute the mean (average) of all the numbers in the given array.\n * Non-numbers will be coerced into numbers if possible.\n */\nexport function mean(numbers: unknown[]): number {\n  return numbers.length > 0 ? sum(numbers) / numbers.length : 0;\n}\n","import { asNumber } from \"./asNumber\";\n\n/**\n * Compute the median (middle number) of all the numbers in the given array.\n * Non-numbers will be coerced into numbers if possible.\n */\nexport function median(numbers: unknown[]): number {\n  if (numbers.length === 0) {\n    return 0;\n  }\n\n  // Create a copy with slice() to avoid mutating the original array.\n  const sorted = numbers\n    .map(asNumber)\n    .slice()\n    .sort((a, b) => a - b);\n\n  const middleIndex = Math.floor(sorted.length / 2);\n\n  // Is odd length -> return middle number.\n  if (sorted.length % 2 === 1) {\n    return sorted[middleIndex];\n  }\n\n  // Is even length -> return mean of middle 2 numbers.\n  const value1 = sorted[middleIndex - 1];\n  const value2 = sorted[middleIndex];\n  return (value1 + value2) / 2;\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// Simple memoize an async-loaded result once.\n//\n// Interface design notes:\n// 1. Retry on errors - supported. If an error is thrown, then nothing is\n//    memoized.\n// 2. Cache invalidation - does not seem useful because all the user needs to\n//    do is to call memoize again.\n// 3. TTL - could be useful, but doesn't seem necessary to have. Niche use case.\n// 4. Memoize different results for different function arguments - Could be\n//    useful, but not the main use case we are targeting for.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Memoizes the result of an async loader function.\n *\n * - The loader is only executed once.\n * - Concurrent callers share the same in-flight promise.\n * - Subsequent calls return the cached value.\n *\n * Example usage:\n *\n *   const getSomeData = memoize(async () => {\n *     return await loadSomethingExpensive();\n *   });\n *\n * Or just use:\n *\n *   const getSomeData = memoize(loadSomethingExpensive);\n *   ...\n *   // Slow in the first call, immediate in subsequent calls.\n *   const data = await getSomeData();\n *\n */\nexport function memoize<T>(loader: () => Promise<T>): () => Promise<T> {\n  let promise: Promise<T> | undefined;\n\n  return (): Promise<T> => {\n    if (!promise) {\n      promise = loader().catch((error) => {\n        // Clear the cache so the next call retries\n        promise = undefined;\n        throw error;\n      });\n    }\n\n    return promise;\n  };\n}\n","/**\n * Convert a base64 string to a base64url string.\n */\nexport function base64ToBase64URL(base64: string): string {\n  return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\"); // Remove padding\n}\n\n/**\n * Convert a base64url string to a base64 string.\n */\nexport function base64UrlToBase64(base64Url: string): string {\n  if (!base64Url) return \"\";\n\n  // Replace URL-safe characters\n  const base64 = base64Url.replace(/-/g, \"+\").replace(/_/g, \"/\");\n\n  // Calculate and add padding\n  const padLength = (4 - (base64.length % 4)) % 4;\n  return base64 + \"=\".repeat(padLength);\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// LRU Cache with TTL, size limit, auto GC, and peek.\n//\n// Interface design notes:\n// 1. Event listeners - Not included as forseeable usage is very limited.\n// 2. Cache hit/miss stats - Not included as these do not seem useful enough.\n// 3. Peek - Included as there is no performance impact.\n// 4. Max size - Seems like a common config needed for LRU caches, hence\n//    included.\n// 5. TTL - Seems useful because data could get stale, and having this avoids\n//    requiring the user to implement this by themselves.\n// 6. GC - Included because the GC is super efficient using timers. Users can\n//    just ignore GC entirely and let it do its thing.\n// 7. Map implementation - Uses the default Map implementation in ES, no need\n//    to reinvent our own map here to save on a few nanoseconds (at best).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** All options are optional. */\nexport type LRUMapOptions = {\n  /**\n   * Maximum number of entries. Oldest entry is evicted when exceeded.\n   * Defaults to no limit. 0 means \"no max\", not \"no entries allowed\".\n   */\n  maxSize?: number;\n\n  /**\n   * Time-to-live in milliseconds. Entries expire after this duration.\n   * Defaults to no expiration. 0 means \"no expiration\", not\n   * \"expires immediately\".\n   */\n  ttlMs?: number;\n};\n\ntype Entry<K, V> = {\n  key: K;\n  value: V;\n  /** Absolute expiry timestamp (ms), or undefined if no TTL. */\n  expiryMs?: number;\n  expiryTimeout?: ReturnType<typeof setTimeout>;\n  prev?: Entry<K, V>;\n  next?: Entry<K, V>;\n};\n\n/**\n * Strongly-typed LRU cache that implements the Map interface. You can also use\n * this as a size-limited map by setting maxSize.\n *\n * Values can be null, but cannot be undefined.\n */\nexport class LRUMap<K, V> implements Map<K, V> {\n  private readonly options: LRUMapOptions;\n  private readonly map = new Map<K, Entry<K, V>>();\n\n  // Doubly-linked list — head.next = MRU, tail.prev = LRU\n  // Sentinel nodes simplify edge cases.\n  private readonly head = {} as Entry<K, V>;\n  private readonly tail = {} as Entry<K, V>;\n\n  public constructor(options?: LRUMapOptions) {\n    this.options = { ...options }; // clone the argument\n    this.head.next = this.tail;\n    this.tail.prev = this.head;\n  }\n\n  // ── Core API ───────────────────────────────────────────────────────────────\n\n  /**\n   * Store a value. Overwrites any existing entry for the key.\n   * Note: Disallow setting a custom TTL because that will require us to do a\n   * sorted insertion instead of an insert-at-front.\n   */\n  public set(key: K, value: V): this {\n    const existingEntry = this.map.get(key);\n\n    if (existingEntry) {\n      existingEntry.value = value;\n      this.setupExpiryTimeout(existingEntry);\n      this.moveToFront(existingEntry);\n    } else {\n      // Evict LRU entry if over capacity.\n      if (this.options.maxSize && this.map.size >= this.options.maxSize) {\n        this.evictOldestEntry();\n      }\n\n      const entry: Entry<K, V> = { key, value };\n      this.setupExpiryTimeout(entry);\n      this.map.set(key, entry);\n      this.insertAtFront(entry);\n    }\n\n    return this;\n  }\n\n  /**\n   * Retrieve a value and mark it as recently used.\n   * Returns `undefined` on miss or if the entry has expired.\n   */\n  public get(key: K): V | undefined {\n    const entry = this.map.get(key);\n    if (!entry || this.isExpired(entry)) {\n      if (entry) this.deleteEntry(entry);\n      return undefined;\n    }\n\n    // Don't renew expiration time on gets. If we do, stale entries might never\n    // expire if we keep on reading them.\n    this.moveToFront(entry);\n    return entry.value;\n  }\n\n  /**\n   * Read a value WITHOUT updating recency or hit/miss stats.\n   * Useful for inspection or monitoring without polluting cache order.\n   */\n  public peek(key: K): V | undefined {\n    return this.peekEntry(key)?.value;\n  }\n\n  /**\n   * Get the existing value, or return a default value. Will not set the value\n   * in the map.\n   */\n  public getOrDefault(key: K, defaultValue: V): V {\n    const existingValue = this.get(key);\n    if (existingValue !== undefined) {\n      return existingValue;\n    }\n\n    return defaultValue;\n  }\n\n  /** Required by Map interface. */\n  public getOrInsert(key: K, value: V): V {\n    const existingValue = this.get(key);\n    if (existingValue !== undefined) {\n      return existingValue;\n    }\n\n    this.set(key, value);\n    return value;\n  }\n\n  /** Required by Map interface. */\n  public getOrInsertComputed(key: K, callback: (key: K) => V): V {\n    const existingValue = this.get(key);\n    if (existingValue !== undefined) {\n      return existingValue;\n    }\n\n    const value = callback(key);\n    this.set(key, value);\n    return value;\n  }\n\n  /** Same as getOrInsertComputed, but async. */\n  public async getOrInsertLoaded(\n    key: K,\n    loader: (key: K) => Promise<V>,\n  ): Promise<V> {\n    const existingValue = this.get(key);\n    if (existingValue !== undefined) {\n      return existingValue;\n    }\n\n    const loadedValue = await loader(key);\n    this.set(key, loadedValue);\n    return loadedValue;\n  }\n\n  /** Returns true if the key exists and has not expired. */\n  public has(key: K): boolean {\n    const entry = this.map.get(key);\n    if (!entry) return false;\n    if (this.isExpired(entry)) {\n      this.deleteEntry(entry);\n      return false;\n    }\n    return true;\n  }\n\n  /** Remove a single entry. Returns true if the key existed. */\n  public delete(key: K): boolean {\n    const entry = this.map.get(key);\n    if (!entry) return false;\n    this.deleteEntry(entry);\n    return true;\n  }\n\n  /** Remove all entries. */\n  public clear(): void {\n    if (this.options.ttlMs) {\n      for (const entry of this.map.values()) {\n        if (entry.expiryTimeout) {\n          clearTimeout(entry.expiryTimeout);\n          delete entry.expiryTimeout;\n        }\n      }\n    }\n\n    this.map.clear();\n    this.head.next = this.tail;\n    this.tail.prev = this.head;\n  }\n\n  /** Number of entries currently in the cache (including expired ones). */\n  public get size(): number {\n    return this.map.size;\n  }\n\n  public get [Symbol.toStringTag](): string {\n    return `LRUMap(${this.size})`;\n  }\n\n  // ── Iteration ──────────────────────────────────────────────────────────────\n\n  /** Filter out expired entries. */\n  public keys(): MapIterator<K> {\n    return this.entries().map(([k]) => k);\n  }\n\n  /** Filter out expired entries. */\n  public values(): MapIterator<V> {\n    return this.entries().map(([, v]) => v);\n  }\n\n  /**\n   * Iterate over [key, value] pairs (in insertion order), skipping expired\n   * entries.\n   * NOTE: Do not delete any entries, otherwise it will break the LRU data.\n   */\n  public entries(): MapIterator<[K, V]> {\n    return this.map\n      .entries()\n      .filter(([, e]) => !this.isExpired(e))\n      .map(([k, e]) => [k, e.value]);\n  }\n\n  /** NOTE: Do not delete any entries, otherwise it will break the LRU data. */\n  public [Symbol.iterator](): MapIterator<[K, V]> {\n    return this.entries();\n  }\n\n  /**\n   * NOTE: Do not use the `map` argument as it will always be an empty Map.\n   * The actual underlying map has a different value type.\n   */\n  public forEach(\n    callbackFn: (value: V, key: K, map: Map<K, V>) => void,\n    thisArg?: unknown,\n  ): void {\n    const tempMap = new Map<K, V>();\n\n    this.map.forEach((entry, key) => {\n      if (!this.isExpired(entry)) {\n        callbackFn(entry.value, key, tempMap);\n      }\n    }, thisArg);\n  }\n\n  // ── Private Helpers ────────────────────────────────────────────────────────\n\n  private calcExpiry(): number | undefined {\n    // When ttlMs=0, it means \"no expiration\", not \"always expire\".\n    const ms = this.options.ttlMs;\n    return ms ? Date.now() + ms : undefined;\n  }\n\n  private isExpired(entry: Entry<K, V>): boolean {\n    // When expiryMs=0, it means \"no expiration\", not \"already expired\".\n    return !!entry.expiryMs && Date.now() >= entry.expiryMs;\n  }\n\n  private peekEntry(key: K): Entry<K, V> | undefined {\n    const entry = this.map.get(key);\n    if (!entry || this.isExpired(entry)) {\n      return undefined;\n    }\n    return entry;\n  }\n\n  private unrefTimer(t: ReturnType<typeof setTimeout>): void {\n    const timer = t as unknown;\n\n    if (\n      timer &&\n      typeof timer === \"object\" &&\n      \"unref\" in timer &&\n      typeof timer.unref === \"function\"\n    ) {\n      timer.unref();\n    }\n  }\n\n  private setupExpiryTimeout(entry: Entry<K, V>) {\n    if (!this.options.ttlMs) {\n      return;\n    }\n\n    const expiryMs = (entry.expiryMs = this.calcExpiry());\n\n    if (entry.expiryTimeout) {\n      clearTimeout(entry.expiryTimeout);\n    }\n\n    entry.expiryTimeout = setTimeout(() => {\n      if (entry.expiryMs === expiryMs) {\n        this.deleteEntry(entry);\n      }\n    }, this.options.ttlMs);\n\n    // NodeJS only: Allow process to exit before this timeout runs.\n    this.unrefTimer(entry.expiryTimeout);\n  }\n\n  private insertAtFront(entry: Entry<K, V>): void {\n    entry.prev = this.head;\n    entry.next = this.head.next;\n    this.head.next!.prev = entry;\n    this.head.next = entry;\n  }\n\n  private removeFromList(entry: Entry<K, V>): void {\n    entry.prev!.next = entry.next;\n    entry.next!.prev = entry.prev;\n  }\n\n  private moveToFront(entry: Entry<K, V>): void {\n    if (this.head.next === entry) return; // already MRU\n    this.removeFromList(entry);\n    this.insertAtFront(entry);\n  }\n\n  private evictOldestEntry(): void {\n    const oldestEntry = this.tail.prev!;\n    if (oldestEntry === this.head) return; // empty\n    this.deleteEntry(oldestEntry);\n  }\n\n  private deleteEntry(entry: Entry<K, V>): void {\n    if (entry.expiryTimeout) {\n      clearTimeout(entry.expiryTimeout);\n      delete entry.expiryTimeout;\n    }\n\n    this.removeFromList(entry);\n    this.map.delete(entry.key);\n  }\n}\n","/**\n * Local storage key-value store with support for auto-expirations.\n *\n * Why use this?\n * 1. Extremely simple interface to use local storage.\n * 2. Auto-expirations with GC frees you from worrying about data clean-up.\n * 3. Any serializable data type can be stored (except undefined).\n *\n * How to use?\n * Just use the `localStore` global constant like the local storage.\n *\n * Why not use the localStorage directly?\n * localStorage does not provide auto-expirations with GC. If you don't need\n * this (items never expire), then just use localStorage directly.\n */\n\nimport { Duration, durationOrMsToMs } from \"./duration\";\nimport {\n  FullStorageAdapter,\n  StorageAdapter,\n  StoredObject,\n} from \"./storageAdapter\";\nimport { MS_PER_DAY } from \"./timeConstants\";\n\n/** Global defaults can be updated directly. */\nexport const LocalStoreConfig = {\n  /** All items with the same store name will share the same storage space. */\n  storeName: \"ts-utils\",\n\n  /** 30 days in ms. */\n  expiryMs: MS_PER_DAY * 30,\n\n  /** Do GC once per day. */\n  gcIntervalMs: MS_PER_DAY,\n};\n\nexport type LocalStoreConfig = typeof LocalStoreConfig;\n\n/** Convenience function to update global defaults. */\nexport function configureLocalStore(config: Partial<LocalStoreConfig>) {\n  Object.assign(LocalStoreConfig, config);\n}\n\n/**\n * Parse a stored value string. Returns undefined if invalid or expired.\n * Throws an error if the string cannot be parsed as JSON.\n */\nfunction validateStoredObject<T>(\n  obj: StoredObject<T>,\n): StoredObject<T> | undefined {\n  if (\n    !obj ||\n    typeof obj !== \"object\" ||\n    obj.value === undefined ||\n    typeof obj.storedMs !== \"number\" ||\n    typeof obj.expiryMs !== \"number\" ||\n    Date.now() >= obj.expiryMs\n  ) {\n    return undefined;\n  }\n\n  return obj;\n}\n\n/**\n * You can create multiple LocalStores if you want, but most likely you will only\n * need to use the default `localStore` instance.\n */\n// Using `any` because the store could store any type of data for each key,\n// but the caller can specify a more specific type when calling each of the\n// methods.\n\nexport function createLocalStore(\n  storeName: string,\n  options?: {\n    defaultExpiryMs?: number | Duration;\n    gcIntervalMs?: number | Duration;\n  },\n) {\n  const keyPrefix = storeName + \":\";\n\n  const defaultExpiryMs = options?.defaultExpiryMs\n    ? durationOrMsToMs(options.defaultExpiryMs)\n    : LocalStoreConfig.expiryMs;\n\n  const gcIntervalMs = options?.gcIntervalMs\n    ? durationOrMsToMs(options.gcIntervalMs)\n    : LocalStoreConfig.gcIntervalMs;\n\n  const gcMsStorageKey = `__localStore:lastGcMs:${storeName}`;\n\n  const obj = {\n    /** Input name for the store. */\n    storeName,\n\n    /**\n     * The prefix string for the local storage key which identifies items\n     * belonging to this namespace.\n     */\n    keyPrefix,\n\n    /** Default expiry to use if not specified in set(). */\n    defaultExpiryMs,\n\n    /** Time interval for when GC's occur. */\n    gcIntervalMs,\n\n    /** Local storage key name for the last GC completed timestamp. */\n    gcMsStorageKey,\n\n    /** Set a value in the store. */\n    set<T>(key: string, value: T, expiryDeltaMs?: number | Duration): T {\n      const nowMs = Date.now();\n      const stored: StoredObject<T> = {\n        value,\n        storedMs: nowMs,\n        expiryMs: nowMs + durationOrMsToMs(expiryDeltaMs ?? defaultExpiryMs),\n      };\n\n      localStorage.setItem(keyPrefix + key, JSON.stringify(stored));\n\n      obj.gc(); // check GC on every write\n\n      return value;\n    },\n\n    /** Delete one or multiple keys. */\n    delete(key: string | string[]): void {\n      if (typeof key === \"string\") {\n        localStorage.removeItem(keyPrefix + key);\n      } else {\n        for (const k of key) {\n          localStorage.removeItem(keyPrefix + k);\n        }\n      }\n    },\n\n    /** Mainly used to get the expiration timestamp of an object. */\n    getStoredObject<T>(key: string): StoredObject<T> | undefined {\n      const k = keyPrefix + key;\n      const stored = localStorage.getItem(k);\n\n      if (!stored) {\n        return undefined;\n      }\n\n      try {\n        const parsed = JSON.parse(stored);\n        const valid = validateStoredObject(parsed);\n        if (!valid) {\n          obj.delete(k);\n\n          obj.gc(); // check GC on every read of an expired key\n\n          return undefined;\n        }\n\n        return valid as StoredObject<T>;\n      } catch (e) {\n        console.error(`Invalid local value: ${k}=${stored}:`, e);\n        obj.delete(k);\n\n        obj.gc(); // check GC on every read of an invalid key\n\n        return undefined;\n      }\n    },\n\n    /** Get a value by key, or undefined if it does not exist. */\n    get<T>(key: string): T | undefined {\n      const stored = obj.getStoredObject<T>(key);\n\n      return stored?.value;\n    },\n\n    /** Generic way to iterate through all entries. */\n    forEach<T>(\n      callback: (\n        key: string,\n        value: T,\n        expiryMs: number,\n        storedMs: number,\n      ) => void,\n    ): void {\n      for (const k of Object.keys(localStorage)) {\n        if (!k.startsWith(keyPrefix)) continue;\n\n        const key = k.slice(keyPrefix.length);\n        const stored = obj.getStoredObject(key);\n\n        if (!stored) continue;\n\n        callback(key, stored.value as T, stored.expiryMs, stored.storedMs);\n      }\n    },\n\n    /**\n     * Returns the number of items in the store. Note that getting the size\n     * requires iterating through the entire store because the items could expire\n     * at any time, and hence the size is a dynamic number.\n     */\n    size(): number {\n      let count = 0;\n      obj.forEach(() => {\n        count++;\n      });\n      return count;\n    },\n\n    /** Remove all items from the store. */\n    clear(): void {\n      // Note that we don't need to use obj.forEach() because we are just\n      // going to delete all the items without checking for expiration.\n      for (const key of Object.keys(localStorage)) {\n        if (key.startsWith(keyPrefix)) {\n          localStorage.removeItem(key);\n        }\n      }\n    },\n\n    /**\n     * Returns all items as map of key to value, mainly used for debugging dumps.\n     * The type T is applied to all values, even though they might not be of type\n     * T (in the case when you store different data types in the same store).\n     */\n    asMap<T>(): Map<string, StoredObject<T>> {\n      const map = new Map<string, StoredObject<T>>();\n      obj.forEach(\n        (key: string, value: T, expiryMs: number, storedMs: number) => {\n          map.set(key, { value: value as T, expiryMs, storedMs });\n        },\n      );\n      return map;\n    },\n\n    /** Returns the ms timestamp for the last GC (garbage collection). */\n    getLastGcMs(): number {\n      const lastGcMsStr = localStorage.getItem(gcMsStorageKey);\n      if (!lastGcMsStr) return 0;\n\n      const ms = Number(lastGcMsStr);\n      return isNaN(ms) ? 0 : ms;\n    },\n\n    /** Set the ms timestamp for the last GC (garbage collection). */\n    setLastGcMs(ms: number) {\n      localStorage.setItem(gcMsStorageKey, String(ms));\n    },\n\n    /** Perform garbage-collection if due, else do nothing. */\n    gc(): void {\n      const lastGcMs = obj.getLastGcMs();\n\n      // Set initial timestamp - no need GC now.\n      if (!lastGcMs) {\n        obj.setLastGcMs(Date.now());\n        return;\n      }\n\n      if (Date.now() < lastGcMs + gcIntervalMs) {\n        return; // not due for next GC yet\n      }\n\n      // GC is due now, so run it.\n      obj.gcNow();\n    },\n\n    /**\n     * Perform garbage collection immediately without checking whether we are\n     * due for the next GC or not.\n     */\n    gcNow(): void {\n      console.log(`Starting localStore GC on ${storeName}`);\n\n      // Prevent concurrent GC runs.\n      obj.setLastGcMs(Date.now());\n      let count = 0;\n\n      obj.forEach((key: string, value: unknown, expiryMs: number) => {\n        if (Date.now() >= expiryMs) {\n          obj.delete(key);\n          count++;\n        }\n      });\n\n      console.log(\n        `Finished localStore GC on ${storeName} - deleted ${count} keys`,\n      );\n\n      // Mark the end time as last GC time.\n      obj.setLastGcMs(Date.now());\n    },\n\n    /** Returns `this` casted into a StorageAdapter<T>. */\n    asStorageAdapter<T>(): StorageAdapter<T> {\n      return obj as StorageAdapter<T>;\n    },\n  } as const;\n\n  // Using `any` because the store could store any type of data for each key,\n  // but the caller can specify a more specific type when calling each of the\n  // methods.\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  return obj satisfies FullStorageAdapter<any>;\n}\n\nexport type LocalStore = ReturnType<typeof createLocalStore>;\n\n/**\n * Default local store ready for immediate use. You can create new instances if\n * you want, but most likely you will only need one store instance.\n */\nexport const localStore = createLocalStore(LocalStoreConfig.storeName);\n\n/** Create a local store item with a key and a default expiration. */\nexport function localStoreItem<T>(\n  key: string,\n  expiryMs?: number | Duration,\n  store: LocalStore = localStore,\n) {\n  const defaultExpiryMs = expiryMs && durationOrMsToMs(expiryMs);\n\n  const obj = {\n    key,\n    defaultExpiryMs,\n    store,\n\n    /** Set a value in the store. */\n    set(value: T, expiryDeltaMs?: number | undefined): void {\n      store.set(key, value, expiryDeltaMs ?? defaultExpiryMs);\n    },\n\n    /**\n     * Example usage:\n     *\n     *   const { value, storedMs, expiryMs, storedMs } =\n     *     await myLocalItem.getStoredObject();\n     */\n    getStoredObject(): StoredObject<T> | undefined {\n      return store.getStoredObject(key);\n    },\n\n    /** Get a value by key, or undefined if it does not exist. */\n    get(): T | undefined {\n      return store.get(key);\n    },\n\n    /** Delete this key from the store. */\n    delete(): void {\n      store.delete(key);\n    },\n  };\n\n  return obj;\n}\n\nexport type LocalStoreItem<T> = ReturnType<typeof localStoreItem<T>>;\n","/**\n * Simplifies rounding of floating point numbers. Note that if your number ends\n * with zeros, and you convert the number to string, you will lose the zeroes\n * at the end. To show the exact number of decimal places, you'll need to use\n * roundToString() or toFixed(). E.g. round(1.20, 2).toFixed(2).\n */\nexport function round(n: number, numDecimalPlaces = 0): number {\n  const multipler = Math.pow(10, numDecimalPlaces);\n  return Math.round(n * multipler) / multipler;\n}\n","import { round } from \"./round\";\n\n/**\n * Returns a string with the number in the exact number of decimal places\n * specified, in case the number ends with zeroes, and adding commas for each\n * group of 3 significant digits.\n */\nexport function roundToString(n: number, numDecimalPlaces = 0): string {\n  return round(n, numDecimalPlaces).toLocaleString(\"en-US\", {\n    minimumFractionDigits: numDecimalPlaces,\n    maximumFractionDigits: numDecimalPlaces,\n  });\n}\n","/**\n * Base 64 encode the given input string, but safely.\n *\n * Why using btoa() directly might not always work:\n * btoa() expects a \"binary string\" where each character is represented by a\n * single byte (0-255). Modern JavaScript strings, however, are encoded in\n * UTF-16 and can contain characters that require more than one byte (i.e.,\n * characters outside the Latin-1 range, such as those with code points greater\n * than 255).\n */\nexport function safeBtoa(input: string): string {\n  // Convert the string to a UTF-8 encoded binary-safe string\n  const utf8Bytes = new TextEncoder().encode(input);\n\n  // Convert the binary data to a string for btoa\n  const binaryString = Array.from(utf8Bytes)\n    .map((byte) => String.fromCodePoint(byte))\n    .join(\"\");\n\n  // Use btoa to encode the binary-safe string\n  return btoa(binaryString);\n}\n","import { isEmpty } from \"./isEmpty\";\n\n/**\n * Type asserts that `t` is truthy.\n * Throws an error if `t` is null or undefined.\n *\n * @param varName The variable name to include in the error to throw when t is\n *   empty. Defaults to 'value'.\n */\nexport function nonEmpty<T>(\n  t: T | null | undefined | \"\" | 0 | -0 | 0n | false | typeof NaN,\n  varName = \"value\",\n): T {\n  if (isEmpty(t)) {\n    throw new Error(`Empty ${varName}: ${t}`);\n  }\n  return t as T;\n}\n","/**\n * SHA-256 hash an input string into an ArrayBuffer.\n */\nexport async function sha256(input: string): Promise<ArrayBuffer> {\n  // Encode the input string as a Uint8Array\n  const encoder = new TextEncoder();\n  const uint8Array = encoder.encode(input);\n\n  // Compute the SHA-256 hash using the SubtleCrypto API\n  const arrayBuffer = await crypto.subtle.digest(\"SHA-256\", uint8Array);\n\n  return arrayBuffer;\n}\n","/**\n * Type asserts that `t` is neither null nor undefined.\n * Throws an error if `t` is null or undefined.\n *\n * @param varName The variable name to include in the error to throw when t is\n *   nil. Defaults to 'value'.\n */\nexport function nonNil<T>(t: T | null | undefined, varName = \"value\"): T {\n  if (t === null || t === undefined) {\n    throw new Error(`Missing ${varName}: ${t}`);\n  }\n  return t;\n}\n","import { elapsed } from \"./duration\";\n\n/**\n * Create a new timer and starts the timing right away. Returns a closed object\n * instead of a class to make sure the variables are bound correctly.\n */\nexport function timer() {\n  const obj = {\n    startMs: Date.now(),\n    endMs: 0,\n\n    stop(): void {\n      obj.endMs = Date.now();\n    },\n\n    restart(): void {\n      obj.endMs = 0;\n      obj.startMs = Date.now();\n    },\n\n    elapsedMs(): number {\n      const stopMs = obj.endMs || Date.now();\n      return stopMs - obj.startMs;\n    },\n\n    toString(): string {\n      return elapsed(obj.elapsedMs());\n    },\n  };\n\n  return obj;\n}\n\n/** Defines the type of the Timer object. */\nexport type Timer = ReturnType<typeof timer>;\n"],"mappings":"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","names":["index_exports","__export","DURATION_STYLE_SUFFIX_MAP","DURATION_TYPE_SEQUENCE","HOURS_PER_DAY","HOURS_PER_WEEK","Heap","HttpError","HttpFetch","KvStoreConfig","LRUMap","LocalStoreConfig","MINUTES_PER_DAY","MINUTES_PER_HOUR","MINUTES_PER_WEEK","MS_PER_DAY","MS_PER_HOUR","MS_PER_MINUTE","MS_PER_SECOND","MS_PER_WEEK","MissingEnvVarError","SECONDS_PER_DAY","SECONDS_PER_HOUR","SECONDS_PER_MINUTE","SECONDS_PER_WEEK","arrayBufferToBase64","arrayBufferToHex","asNumber","assert","base64ToBase64URL","base64UrlToBase64","capLength","concatIterators","configureKvStore","configureLocalStore","createKvStore","createLocalStore","durationOrMsToMs","durationToMs","elapsed","formatDuration","getDisplayDateTime","getEnv","getLongMonthNameOneIndexed","getLongMonthNameZeroIndexed","getShortMonthNameOneIndexed","getShortMonthNameZeroIndexed","hhMm","hhMmSs","hhMmSsMs","http","isEmpty","isNativePromise","isPromise","kvStore","kvStoreItem","localStore","localStoreItem","mean","median","memoize","msToDuration","nonEmpty","nonNil","normalizeBody","readableDuration","retries","round","roundToString","safeBtoa","safeGetErrorMessage","safeGetJson","safeGetText","sha256","sleep","sum","throwIfError","throwOnError","timer","toDate","toReadableString","tzShort","yyyyMm","yyyyMmDd","__toCommonJS","arrayBufferToHex","buffer","byteArray","byte","arrayBufferToBase64","binaryString","toReadableString","u","options","error","result","errorName","errorMessage","stack","customProps","additionalInfo","key","capLength","u","maxLength","s","toReadableString","concatIterators","iterators","iterator","isAllDigits","str","toDate","ts","yyyyMm","dt","separator","yr","mth","yyyyMmDd","day","hhMm","hr","min","hhMmSs","sec","hhMmSsMs","timeSeparator","msSeparator","ms","tzShort","tzHours","getLongMonthNameZeroIndexed","month","locales","getLongMonthNameOneIndexed","getShortMonthNameZeroIndexed","getShortMonthNameOneIndexed","getDisplayDateTime","assert","t","errorMessage","MS_PER_SECOND","MS_PER_MINUTE","MS_PER_HOUR","MS_PER_DAY","MS_PER_WEEK","SECONDS_PER_MINUTE","SECONDS_PER_HOUR","SECONDS_PER_DAY","SECONDS_PER_WEEK","MINUTES_PER_HOUR","MINUTES_PER_DAY","MINUTES_PER_WEEK","HOURS_PER_DAY","HOURS_PER_WEEK","DURATION_TYPE_SEQUENCE","DURATION_STYLE_SUFFIX_MAP","getDurationStyleForPlural","style","getValueAndUnitSeparator","getDurationTypeSeparator","msToDuration","ms","durationTypeForZero","duration","i","seconds","millis","minutes","hours","days","durationToMs","daysMs","hoursMs","minsMs","secsMs","msMs","durationOrMsToMs","formatDuration","stylePlural","space","a","unit","value","suffixMap","suffix","separator","readableDuration","options","elapsed","Heap","_Heap","compare","initial","i","parent","item","top","last","predicate","removed","items","p","swap","n","l","leftChild","r","rightChild","data","j","tmp","MissingEnvVarError","getEnv","varName","defaultValue","value","asNumber","u","defaultValue","isPromise","obj","isNativePromise","sleep","ms","resolve","HttpError","status","message","safeGetJson","response","safeGetText","safeGetErrorMessage","s","text","throwIfError","normalizeBody","body","throwOnError","request","next","retries","options","maxTries","delayMs","backoffMultiplier","ms","i","jitterMs","sleep","HttpFetch","_HttpFetch","middlewares","middleware","input","init","headers","newHeaders","callerHeaders","k","v","newInit","handler","req","currentMiddleware","nextHandler","http","isEmpty","t","k","KvStoreConfig","configureKvStore","config","validateStoredObject","obj","withOnError","request","reject","event","createKvStore","dbName","options","db","dbVersion","storeName","defaultExpiryMs","durationOrMsToMs","gcIntervalMs","gcMsStorageKey","getOrCreateDb","resolve","transact","mode","callback","transaction","objectStore","key","value","expiryDeltaMs","nowMs","stored","k","valid","e","cursor","count","map","expiryMs","storedMs","lastGcMsStr","ms","lastGcMs","keysToDelete","kvStore","kvStoreItem","store","sum","numbers","accumulated","current","asNumber","mean","numbers","sum","median","numbers","sorted","asNumber","a","b","middleIndex","value1","value2","memoize","loader","promise","error","base64ToBase64URL","base64","base64UrlToBase64","base64Url","padLength","LRUMap","options","key","value","existingEntry","entry","defaultValue","existingValue","callback","loader","loadedValue","k","v","e","callbackFn","thisArg","tempMap","ms","timer","expiryMs","oldestEntry","LocalStoreConfig","configureLocalStore","config","validateStoredObject","obj","createLocalStore","storeName","options","keyPrefix","defaultExpiryMs","durationOrMsToMs","gcIntervalMs","gcMsStorageKey","key","value","expiryDeltaMs","nowMs","stored","k","parsed","valid","e","callback","count","map","expiryMs","storedMs","lastGcMsStr","ms","lastGcMs","localStore","localStoreItem","store","round","n","numDecimalPlaces","multipler","roundToString","n","numDecimalPlaces","round","safeBtoa","input","utf8Bytes","binaryString","byte","nonEmpty","t","varName","isEmpty","sha256","input","uint8Array","nonNil","t","varName","timer","obj","elapsed"]}