{"version":3,"file":"utils.mjs","names":[],"sources":["../../../src/common/data/utils.ts"],"sourcesContent":["import type { Json } from '../types'\nimport { jsonStringifySafe } from './json'\n\n/**\n * Call a create function if key does not yet exist on an object. Returns the found or created object. Example:\n *\n * ```js\n * function createRoom(room, rooms) { return new Room() }\n * ensureKey(rooms, room, createRoom).enter()\n * ```\n */\nexport function ensureKey<T>(\n  obj: Record<string, T>,\n  key: string,\n  createFn: (key?: string, obj?: Record<string, T>) => T,\n): T {\n  let value = obj[key]\n  if (value === undefined) {\n    value = createFn(key, obj)\n    obj[key] = value\n  }\n  return value\n}\n\n/**\n * Call a create function if key does not yet exist on an object. Returns the found or created object. Example:\n *\n * ```js\n * async function fetchItem(id, cache) { ... }\n * let data = await ensureKey(cache, id, fetchItem)\n * ```\n */\nexport async function ensureKeyAsync<T>(\n  obj: Record<string, T>,\n  key: string,\n  createFn: (key?: string, obj?: Record<string, T>) => Promise<T>,\n): Promise<T> {\n  let value = obj[key]\n  if (value === undefined) {\n    value = await createFn(key, obj)\n    obj[key] = value\n  }\n  return value\n}\n\n/**\n * Returns the size of an object, array, string or similar. For example:\n */\nexport function size(obj: any) {\n  if (obj != null) {\n    if (obj.size != null)\n      return obj.size\n\n    if (obj.length != null)\n      return obj.length\n\n    return Object.keys(obj).length\n  }\n  return 0\n}\n\nexport function first<T>(array?: T[]): T | undefined {\n  return (array != null && array.length > 0) ? array[0] : undefined\n}\n\nexport function last<T>(array?: T[]): T | undefined {\n  return (array != null && array.length > 0) ? array[array.length - 1] : undefined\n}\n\n// True for [], {}, \"\", Map(), Set() and all primitives\nexport function empty(value: any): boolean {\n  try {\n    if (value != null) {\n      if (Array.isArray(value))\n        return value.length <= 0\n      else if (typeof value === 'string')\n        return value.length <= 0\n      else if (value?.size != null)\n        return value.size <= 0\n      else\n        return Object.keys(value).length <= 0\n    }\n  }\n  catch (err) {\n    console.warn('Failed to check if empty for', value, err)\n  }\n  return true\n}\n\n// Also see common/data/deep.ts\nexport function cloneObject<T>(obj: T): T {\n  // Primitives are immutable anyway\n  if (Object(obj) !== obj)\n    return obj\n\n  // Rude but very efficient way to clone\n  return JSON.parse(jsonStringifySafe(obj))\n}\n\n// export const cloneObject = typeof structuredClone !== 'undefined' ? structuredClone : _cloneObject\n\n// Also see common/data/deep.ts\nexport function cloneJsonObject<T = Json>(obj: T): T {\n  // Primitives are immutable anyway\n  if (Object(obj) !== obj)\n    return obj\n\n  // Rude but very efficient way to clone\n  return JSON.parse(jsonStringifySafe(obj))\n}\n\n// export function cloneStructuredObject<T>(obj: T): T {\n//   // Primitives are immutable anyway\n//   if (Object(obj) !== obj) return obj\n\n//   // https://developer.mozilla.org/en-US/docs/Web/API/structuredClone\n//   // @ts-ignore\n//   return typeof structuredClone !== \"undefined\"\n//     ? // @ts-ignore\n//       structuredClone(obj)\n//     : // Rude but very efficient way to clone\n//       JSON.parse(JSON.stringify(obj))\n// }\n\n/**\n * Cache result of a function. Same arguments have to always return the same result in order to get expected optimization!\n *\n * ```\n * const square = memoize((value) => value * value)`\n * square(2) // == 2\n * ```\n */\nexport function memoize<In, Out>(fn: (arg: In) => Out): (arg: In) => Out {\n  const cache = new Map<In, Out>()\n  return (n: In): Out => {\n    if (cache.has(n))\n      return cache.get(n)!\n    const result = fn(n)\n    cache.set(n, result)\n    return result\n  }\n}\n\nexport function memoizeAsync<In extends any[], Out extends Promise<any>>(fn: (...arg: In) => Out): (...arg: In) => Promise<Out> {\n  const cache = new Map<string, Out>()\n  return async (...n: In): Promise<Out> => {\n    const key = jsonStringifySafe(n)\n    if (cache.has(key))\n      return cache.get(key)!\n\n    const result = await fn(...n)\n    cache.set(key, result)\n    return result\n  }\n}\n\n// let cacheMemoizeDomain: any\n\n// /**\n//  * Same as `memoize` but does not require global const.\n//  */\n// export function memoizeDomain<In, Out>(domain: string, fn: (arg: In) => Out): (arg: In) => Out {\n//   if (cacheMemoizeDomain == null)\n//     cacheMemoizeDomain = new Map<string, Map<In, Out>>()\n//   if (!cacheMemoizeDomain.has(domain))\n//     cacheMemoizeDomain.set(domain, new Map())\n//   const cache = cacheMemoizeDomain.get(domain)\n//   return (n: In): Out => {\n//     if (cache.has(n))\n//       return cache.get(n)!\n//     const result = fn(n)\n//     cache.set(n, result)\n//     return result\n//   }\n// }\n\n// export default function memoizeMultiArguments<T extends Function>(func: T) {\n//   const cache = new Map()\n\n//   const memoized = function (...args: any[]) {\n//     let innerCache = cache\n\n//     // first layer of the map is the arguments length\n//     // if two calls have different number of arguments\n//     // then they cannot be the same call\n//     if (!innerCache.has(args.length))\n//       innerCache.set(args.length, new Map())\n\n//     innerCache = innerCache.get(args.length)\n\n//     // using args.length because func.length is 0 for variadic functions\n//     for (let i = 0; i < args.length - 1; i++) {\n//       const key = args[i]\n//       if (!innerCache.has(key))\n//         innerCache.set(key, new Map())\n\n//       innerCache = innerCache.get(key)\n//     }\n\n//     const key = args[args.length - 1]\n//     if (innerCache.has(key))\n//       return innerCache.get(key)\n\n//     const result = func(...args)\n//     innerCache.set(key, result)\n//     return result\n//   }\n\n//   return memoized\n// }\n\n/** Repeat `count` times. Starts with `0` */\nexport function forTimes<T = undefined>(\n  count: number,\n  fn: (i: number, count: number) => T,\n): T[] {\n  const result = []\n  for (let i = 0; i < count; i++)\n    result.push(fn(i, count))\n\n  return result\n}\n"],"mappings":";;;;;;;;;;;AAWA,SAAgB,UACd,KACA,KACA,UACG;CACH,IAAI,QAAQ,IAAI;AAChB,KAAI,UAAU,QAAW;AACvB,UAAQ,SAAS,KAAK,IAAI;AAC1B,MAAI,OAAO;;AAEb,QAAO;;;;;;;;;;AAWT,eAAsB,eACpB,KACA,KACA,UACY;CACZ,IAAI,QAAQ,IAAI;AAChB,KAAI,UAAU,QAAW;AACvB,UAAQ,MAAM,SAAS,KAAK,IAAI;AAChC,MAAI,OAAO;;AAEb,QAAO;;;;;AAMT,SAAgB,KAAK,KAAU;AAC7B,KAAI,OAAO,MAAM;AACf,MAAI,IAAI,QAAQ,KACd,QAAO,IAAI;AAEb,MAAI,IAAI,UAAU,KAChB,QAAO,IAAI;AAEb,SAAO,OAAO,KAAK,IAAI,CAAC;;AAE1B,QAAO;;AAGT,SAAgB,MAAS,OAA4B;AACnD,QAAQ,SAAS,QAAQ,MAAM,SAAS,IAAK,MAAM,KAAK;;AAG1D,SAAgB,KAAQ,OAA4B;AAClD,QAAQ,SAAS,QAAQ,MAAM,SAAS,IAAK,MAAM,MAAM,SAAS,KAAK;;AAIzE,SAAgB,MAAM,OAAqB;AACzC,KAAI;AACF,MAAI,SAAS,KACX,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,UAAU;WAChB,OAAO,UAAU,SACxB,QAAO,MAAM,UAAU;WAChB,OAAO,QAAQ,KACtB,QAAO,MAAM,QAAQ;MAErB,QAAO,OAAO,KAAK,MAAM,CAAC,UAAU;UAGnC,KAAK;AACV,UAAQ,KAAK,gCAAgC,OAAO,IAAI;;AAE1D,QAAO;;AAIT,SAAgB,YAAe,KAAW;AAExC,KAAI,OAAO,IAAI,KAAK,IAClB,QAAO;AAGT,QAAO,KAAK,MAAM,kBAAkB,IAAI,CAAC;;AAM3C,SAAgB,gBAA0B,KAAW;AAEnD,KAAI,OAAO,IAAI,KAAK,IAClB,QAAO;AAGT,QAAO,KAAK,MAAM,kBAAkB,IAAI,CAAC;;;;;;;;;;AAwB3C,SAAgB,QAAiB,IAAwC;CACvE,MAAM,wBAAQ,IAAI,KAAc;AAChC,SAAQ,MAAe;AACrB,MAAI,MAAM,IAAI,EAAE,CACd,QAAO,MAAM,IAAI,EAAE;EACrB,MAAM,SAAS,GAAG,EAAE;AACpB,QAAM,IAAI,GAAG,OAAO;AACpB,SAAO;;;AAIX,SAAgB,aAAyD,IAAuD;CAC9H,MAAM,wBAAQ,IAAI,KAAkB;AACpC,QAAO,OAAO,GAAG,MAAwB;EACvC,MAAM,MAAM,kBAAkB,EAAE;AAChC,MAAI,MAAM,IAAI,IAAI,CAChB,QAAO,MAAM,IAAI,IAAI;EAEvB,MAAM,SAAS,MAAM,GAAG,GAAG,EAAE;AAC7B,QAAM,IAAI,KAAK,OAAO;AACtB,SAAO;;;;AA4DX,SAAgB,SACd,OACA,IACK;CACL,MAAM,SAAS,EAAE;AACjB,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,IACzB,QAAO,KAAK,GAAG,GAAG,MAAM,CAAC;AAE3B,QAAO"}