{"version":3,"sources":["../src/workerRpc.ts","../src/collections.ts","../src/strings.ts","../src/errors.ts"],"sourcesContent":["import { MessagePort, MessageChannel } from 'worker_threads';\nimport { Awaitable } from './types';\nimport { errorFrom, serializeError } from './errors';\n\n/**\n * Helpers that are intended to set up rpc between a Node.js worker thread and the main thread.\n * Create the worker and pass a port in the workerData.\n *\n * On the main thread:\n *\n *     const rpcChannel = new MessageChannel()\n *     const worker = new Worker('./myWorker.js', {\n *       workerData: { rpcPort: rpcChannel.port1 },\n *       transferList: [rpcChannel.port1]\n *     })\n *\n *     // Depending of the direction of communication, either\n *     const client = createRpcClient(rpcChannel.port2)\n *     // or\n *     serveRpc(rpcChannel.port2, {\n *       myMethod\n *     })\n *\n * On the worker thread:\n *\n *     // Depending of the direction of communication, either\n *     const client = createRpcClient(workerData.rpcPort)\n *     // or\n *     serveRpc(workerData.rpcPort, {\n *       myMethod\n *     })\n *\n * Use multiple channels for bidirectional communication.\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type Methods = Record<string, (...args: any[]) => Awaitable<any>>;\n\ntype MessageRequest = {\n  method: string;\n  args: unknown[];\n  port: MessagePort;\n};\n\ninterface MsgResponse<T = unknown> {\n  error?: unknown;\n  result?: T;\n}\n\ninterface CreateRpcClientOptions {\n  timeout?: number;\n}\n\nexport function createRpcClient<M extends Methods>(\n  port: MessagePort,\n  { timeout = 30000 }: CreateRpcClientOptions = {},\n): M {\n  return new Proxy({} as M, {\n    get: (target, prop) => {\n      if (typeof prop !== 'string') {\n        return Reflect.get(target, prop);\n      }\n      return (...args: unknown[]) => {\n        return new Promise((resolve, reject) => {\n          const { port1, port2 } = new MessageChannel();\n\n          const timeoutId = setTimeout(() => {\n            port1.close();\n          }, timeout);\n\n          port1.on('message', (msg: MsgResponse) => {\n            clearTimeout(timeoutId);\n            if (msg.error) {\n              reject(msg.error);\n            } else {\n              resolve(msg.result);\n            }\n          });\n\n          port1.start();\n\n          port.postMessage(\n            {\n              method: prop,\n              args,\n              port: port2,\n            } satisfies MessageRequest,\n            [port2],\n          );\n        });\n      };\n    },\n  });\n}\n\nexport function serveRpc<M extends Methods>(port: MessagePort, methods: M) {\n  const methodMap = new Map(Object.entries(methods));\n  port.on('message', async (msg: MessageRequest) => {\n    const method = methodMap.get(msg.method);\n    if (method) {\n      try {\n        const result = await method(...msg.args);\n        msg.port.postMessage({ result } satisfies MsgResponse);\n      } catch (rawError) {\n        msg.port.postMessage({ error: serializeError(errorFrom(rawError)) } satisfies MsgResponse);\n      }\n    } else {\n      msg.port.postMessage({\n        error: new Error(`Method \"${msg.method}\" not found`),\n      } satisfies MsgResponse);\n    }\n  });\n  port.start();\n}\n","export function asArray<T>(maybeArray: T | T[]): T[] {\n  return Array.isArray(maybeArray) ? maybeArray : [maybeArray];\n}\n\ntype PropertiesOf<P> = Extract<keyof P, string>;\n\ntype Require<T, K extends keyof T> = T & { [P in K]-?: T[P] };\n\ntype Ensure<U, K extends PropertyKey> = K extends keyof U ? Require<U, K> : U & Record<K, unknown>;\n\n/**\n * Type aware version of Object.protoype.hasOwnProperty.\n * See https://fettblog.eu/typescript-hasownproperty/\n */\nexport function hasOwnProperty<X extends {}, Y extends PropertyKey>(\n  obj: X,\n  prop: Y,\n): obj is Ensure<X, Y> {\n  return obj.hasOwnProperty(prop);\n}\n\n/**\n * Maps `obj` to a new object. The `mapper` function receives an entry array of key and value and\n * is allowed to manipulate both. It can also return `null` to omit a property from the result.\n */\nexport function mapProperties<P, L extends PropertyKey, U>(\n  obj: P,\n  mapper: <K extends PropertiesOf<P>>(old: [K, P[K]]) => [L, U] | null,\n): Record<L, U>;\nexport function mapProperties<U, V>(\n  obj: Record<string, U>,\n  mapper: (old: [string, U]) => [string, V] | null,\n): Record<string, V> {\n  return Object.fromEntries(\n    Object.entries(obj).flatMap((entry) => {\n      const mapped = mapper(entry);\n      return mapped ? [mapped] : [];\n    }),\n  );\n}\n\n/**\n * Maps an objects' property keys. The result is a new object with the mapped keys, but the same values.\n */\nexport function mapKeys<U>(\n  obj: Record<string, U>,\n  mapper: (old: string) => string,\n): Record<string, U> {\n  return mapProperties(obj, ([key, value]) => [mapper(key), value]);\n}\n\n/**\n * Maps an objects' property values. The result is a new object with the same keys, but mapped values.\n */\nexport function mapValues<P, V>(\n  obj: P,\n  mapper: (old: P[PropertiesOf<P>], key: PropertiesOf<P>) => V,\n): Record<PropertiesOf<P>, V> {\n  return mapProperties(obj, ([key, value]) => [key, mapper(value, key)]);\n}\n/**\n * Filters an objects' property values. Similar to `array.filter` but for objects. The result is a new\n * object with all the properties removed for which `filter` returned `false`.\n */\nexport function filterValues<K extends PropertyKey, P, Q extends P>(\n  obj: Record<K, P>,\n  filter: (old: P) => old is Q,\n): Record<K, Q>;\nexport function filterValues<P>(obj: P, filter: (old: P[keyof P]) => boolean): Partial<P>;\nexport function filterValues<U>(\n  obj: Record<string, U>,\n  filter: (old: U) => boolean,\n): Record<string, U>;\nexport function filterValues<U>(\n  obj: Record<string, U>,\n  filter: (old: U) => boolean,\n): Record<string, U> {\n  return mapProperties(obj, ([key, value]) => (filter(value) ? [key, value] : null));\n}\n\n/**\n * Filters an objects' property keys. Similar to `array.filter` but for objects. The result is a new\n * object with all the properties removed for which `filter` returned `false`.\n */\nexport function filterKeys<P>(obj: P, filter: (old: keyof P) => boolean): Partial<P>;\nexport function filterKeys<U>(\n  obj: Record<string, U>,\n  filter: (old: string) => boolean,\n): Record<string, U>;\nexport function filterKeys<U>(\n  obj: Record<string, U>,\n  filter: (old: string) => boolean,\n): Record<string, U> {\n  return mapProperties(obj, ([key, value]) => (filter(key) ? [key, value] : null));\n}\n\n/**\n * Compares the properties of two objects. Returns `true` if all properties are strictly equal, `false`\n * otherwise.\n * Pass a subset of properties to only compare those.\n */\nexport function equalProperties<P extends object>(obj1: P, obj2: P, subset?: (keyof P)[]): boolean {\n  const keysToCheck = new Set(\n    subset ?? ([...Object.keys(obj1), ...Object.keys(obj2)] as (keyof P)[]),\n  );\n  return Array.from(keysToCheck).every((key) => Object.is(obj1[key], obj2[key]));\n}\n","import title from 'title';\n\n/**\n * Makes the first letter of [str] uppercase.\n * Not locale aware.\n */\nexport function uncapitalize(str: string): string {\n  return str.length > 0 ? str[0].toLowerCase() + str.slice(1) : '';\n}\n\n/**\n * Makes the first letter of [str] lowercase.\n * Not locale aware.\n */\nexport function capitalize(str: string): string {\n  return str.length > 0 ? str[0].toUpperCase() + str.slice(1) : '';\n}\n\n/**\n * Capitalizes and joins all [parts].\n */\nexport function pascalCase(...parts: string[]): string {\n  return parts.map((part) => capitalize(part.toLowerCase())).join('');\n}\n\n/**\n * Joins all [parts] and camelcases the result\n */\nexport function camelCase(...parts: string[]): string {\n  if (parts.length > 0) {\n    const [first, ...rest] = parts;\n    return uncapitalize(first) + pascalCase(...rest);\n  }\n  return '';\n}\n\n/**\n * Turns a kebab-case string into a constant case string.\n */\nexport function kebabToConstant(input: string): string {\n  return input\n    .split('-')\n    .map((part) => part.toUpperCase())\n    .join('_');\n}\n\n/**\n * Turns a kebab-case string into a PascalCase string.\n */\nexport function kebabToPascal(input: string): string {\n  return input\n    .split('-')\n    .map((part) => capitalize(part))\n    .join('');\n}\n\n/**\n * Generates a string for `base` by add a number until it's unique amongst a set of predefined names.\n */\nexport function generateUniqueString(base: string, existingNames: Set<string>) {\n  let i = 1;\n  if (!existingNames.has(base)) {\n    return base;\n  }\n  const newBase = base.replace(/\\d+$/, '');\n  let suggestion = newBase;\n  while (existingNames.has(suggestion)) {\n    suggestion = newBase + String(i);\n    i += 1;\n  }\n  return suggestion;\n}\n\n/**\n * Escape string for use in HTML.\n */\nexport function escapeHtml(unsafe: string): string {\n  return unsafe\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&#039;');\n}\n\n/**\n * Normalizes and removes all diacritics from a javascript string.\n *\n * See https://stackoverflow.com/a/37511463\n */\nexport function removeDiacritics(input: string): string {\n  return input.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n}\n\nexport function isAbsoluteUrl(maybeUrl: string) {\n  try {\n    return !!new URL(maybeUrl);\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Removes a prefix from a string if it starts with it.\n */\nexport function removePrefix(input: string, prefix: string): string {\n  return input.startsWith(prefix) ? input.slice(prefix.length) : input;\n}\n\n/**\n * Removes a suffix from a string if it ends with it.\n */\nexport function removeSuffix(input: string, suffix: string): string {\n  return input.endsWith(suffix) ? input.slice(0, -suffix.length) : input;\n}\n\n/**\n * Adds a prefix to a string if it doesn't start with it.\n */\nexport function ensurePrefix(input: string, prefix: string): string {\n  return input.startsWith(prefix) ? input : prefix + input;\n}\n\n/**\n * Adds a suffix to a string if it doesn't end with it.\n */\nexport function ensureSuffix(input: string, suffix: string): string {\n  return input.endsWith(suffix) ? input : input + suffix;\n}\n\n/**\n * Regex to statically find all static import statements\n *\n * Tested against:\n *   import {\n *     Component\n *   } from '@angular2/core';\n *   import defaultMember from \"module-name\";\n *   import   *    as name from \"module-name  \";\n *   import   {  member }   from \"  module-name\";\n *   import { member as alias } from \"module-name\";\n *   import { member1 ,\n *   member2 } from \"module-name\";\n *   import { member1 , member2 as alias2 , member3 as alias3 } from \"module-name\";\n *   import defaultMember, { member, member } from \"module-name\";\n *   import defaultMember, * as name from \"module-name\";\n *   import \"module-name\";\n *   import * from './smdn';\n */\nconst IMPORT_STATEMENT_REGEX =\n  /^\\s*import(?:[\"'\\s]*([\\w*{}\\n, ]+)from\\s*)?[\"'\\s]*([^\"']+)[\"'\\s].*/gm;\n\n/**\n * Statically analyses a javascript source code for import statements and return the specifiers.\n *\n * NOTE: This function does a best effort without parsing the code. The result may contain false\n *       positives\n */\nexport function findImports(src: string): string[] {\n  return Array.from(src.matchAll(IMPORT_STATEMENT_REGEX), (match) => match[2]);\n}\n\n/**\n * Limits the length of a string and adds ellipsis if necessary.\n */\nexport function truncate(str: string, maxLength: number, dots: string = '...') {\n  if (str.length <= maxLength) {\n    return str;\n  }\n  return str.slice(0, maxLength) + dots;\n}\n\n/**\n * Prepend a prefix to each line in the text\n */\nexport function prependLines(text: string, prefix: string): string {\n  return text\n    .split('\\n')\n    .map((line) => prefix + line)\n    .join('\\n');\n}\n\n/**\n * Indent the text with [length] number of spaces\n */\nexport function indent(text: string, length = 2): string {\n  return prependLines(text, ' '.repeat(length));\n}\n\n/**\n * Returns true if the string is a valid javascript identifier\n */\nexport function isValidJsIdentifier(base: string): boolean {\n  return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(base);\n}\n\nexport function guessTitle(str: string): string {\n  // Replace snake_case with space\n  str = str.replace(/[_-]/g, ' ');\n  // Split camelCase\n  str = str.replace(/([a-z0-9])([A-Z])/g, '$1 $2');\n  // Split acronyms\n  str = str.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2');\n  // Split numbers\n  str = str.replace(/([a-zA-Z])(\\d+)/g, '$1 $2');\n  str = str.replace(/(\\d+)([a-zA-Z])/g, '$1 $2');\n\n  return title(str);\n}\n","import { hasOwnProperty } from './collections';\nimport { truncate } from './strings';\n\ndeclare global {\n  interface Error {\n    code?: unknown;\n  }\n}\n\nexport type PlainObject = Record<string, unknown>;\n\nexport interface SerializedError extends PlainObject {\n  message: string;\n  name: string;\n  stack?: string;\n  code?: unknown;\n}\n\nexport function serializeError(error: Error): SerializedError {\n  const { message, name, stack, code } = error;\n  return { message, name, stack, code };\n}\n\n/**\n * Creates a javascript `Error` from an unknown value if it's not already an error.\n * Does a best effort at inferring a message. Intended to be used typically in `catch`\n * blocks, as there is no way to enforce only `Error` objects being thrown.\n *\n * ```\n * try {\n *   // ...\n * } catch (rawError) {\n *   const error = errorFrom(rawError);\n *   console.assert(error instanceof Error);\n * }\n * ```\n */\nexport function errorFrom(maybeError: unknown): Error {\n  if (maybeError instanceof Error) {\n    return maybeError;\n  }\n\n  if (\n    typeof maybeError === 'object' &&\n    maybeError &&\n    hasOwnProperty(maybeError, 'message') &&\n    typeof maybeError.message === 'string'\n  ) {\n    return new Error(maybeError.message, { cause: maybeError });\n  }\n\n  if (typeof maybeError === 'string') {\n    return new Error(maybeError, { cause: maybeError });\n  }\n\n  const message = truncate(String(JSON.stringify(maybeError)), 500);\n  return new Error(message, { cause: maybeError });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAA4C;;;ACcrC,SAAS,eACd,KACA,MACqB;AACrB,SAAO,IAAI,eAAe,IAAI;AAChC;;;ACnBA,mBAAkB;AAqKX,SAAS,SAAS,KAAa,WAAmB,OAAe,OAAO;AAC7E,MAAI,IAAI,UAAU,WAAW;AAC3B,WAAO;AAAA,EACT;AACA,SAAO,IAAI,MAAM,GAAG,SAAS,IAAI;AACnC;;;ACxJO,SAAS,eAAe,OAA+B;AAC5D,QAAM,EAAE,SAAS,MAAM,OAAO,KAAK,IAAI;AACvC,SAAO,EAAE,SAAS,MAAM,OAAO,KAAK;AACtC;AAgBO,SAAS,UAAU,YAA4B;AACpD,MAAI,sBAAsB,OAAO;AAC/B,WAAO;AAAA,EACT;AAEA,MACE,OAAO,eAAe,YACtB,cACA,eAAe,YAAY,SAAS,KACpC,OAAO,WAAW,YAAY,UAC9B;AACA,WAAO,IAAI,MAAM,WAAW,SAAS,EAAE,OAAO,WAAW,CAAC;AAAA,EAC5D;AAEA,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO,IAAI,MAAM,YAAY,EAAE,OAAO,WAAW,CAAC;AAAA,EACpD;AAEA,QAAM,UAAU,SAAS,OAAO,KAAK,UAAU,UAAU,CAAC,GAAG,GAAG;AAChE,SAAO,IAAI,MAAM,SAAS,EAAE,OAAO,WAAW,CAAC;AACjD;;;AHJO,SAAS,gBACd,MACA,EAAE,UAAU,IAAM,IAA4B,CAAC,GAC5C;AACH,SAAO,IAAI,MAAM,CAAC,GAAQ;AAAA,IACxB,KAAK,CAAC,QAAQ,SAAS;AACrB,UAAI,OAAO,SAAS,UAAU;AAC5B,eAAO,QAAQ,IAAI,QAAQ,IAAI;AAAA,MACjC;AACA,aAAO,IAAI,SAAoB;AAC7B,eAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAM,EAAE,OAAO,MAAM,IAAI,IAAI,qCAAe;AAE5C,gBAAM,YAAY,WAAW,MAAM;AACjC,kBAAM,MAAM;AAAA,UACd,GAAG,OAAO;AAEV,gBAAM,GAAG,WAAW,CAAC,QAAqB;AACxC,yBAAa,SAAS;AACtB,gBAAI,IAAI,OAAO;AACb,qBAAO,IAAI,KAAK;AAAA,YAClB,OAAO;AACL,sBAAQ,IAAI,MAAM;AAAA,YACpB;AAAA,UACF,CAAC;AAED,gBAAM,MAAM;AAEZ,eAAK;AAAA,YACH;AAAA,cACE,QAAQ;AAAA,cACR;AAAA,cACA,MAAM;AAAA,YACR;AAAA,YACA,CAAC,KAAK;AAAA,UACR;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEO,SAAS,SAA4B,MAAmB,SAAY;AACzE,QAAM,YAAY,IAAI,IAAI,OAAO,QAAQ,OAAO,CAAC;AACjD,OAAK,GAAG,WAAW,OAAO,QAAwB;AAChD,UAAM,SAAS,UAAU,IAAI,IAAI,MAAM;AACvC,QAAI,QAAQ;AACV,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,GAAG,IAAI,IAAI;AACvC,YAAI,KAAK,YAAY,EAAE,OAAO,CAAuB;AAAA,MACvD,SAAS,UAAU;AACjB,YAAI,KAAK,YAAY,EAAE,OAAO,eAAe,UAAU,QAAQ,CAAC,EAAE,CAAuB;AAAA,MAC3F;AAAA,IACF,OAAO;AACL,UAAI,KAAK,YAAY;AAAA,QACnB,OAAO,IAAI,MAAM,WAAW,IAAI,MAAM,aAAa;AAAA,MACrD,CAAuB;AAAA,IACzB;AAAA,EACF,CAAC;AACD,OAAK,MAAM;AACb;","names":[]}