{"version":3,"sources":["../src/duration.ts","../src/localStore.ts"],"sourcesContent":["/**\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 * 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"],"mappings":"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","names":["durationToMs","duration","daysMs","hoursMs","minsMs","secsMs","msMs","durationOrMsToMs","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"]}