@naturalcycles/internal-web-lib 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/analyticsClient.js +1334 -0
- package/bundle/analyticsClient.js.map +1 -0
- package/dist/analytics/analyticsClient.d.ts +356 -0
- package/dist/analytics/analyticsClient.js +828 -0
- package/dist/analytics/index.d.ts +1 -0
- package/dist/analytics/index.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +54 -0
- package/readme.md +81 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyticsClient.js","names":[],"sources":["../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/is.util.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/abort.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/types.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/object/object.util.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/env.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/string/json.util.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/string/string.util.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/error/error.util.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/string/safeJsonStringify.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/string/stringify.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/error/assert.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/promise/pDelay.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/datetime/time.util.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/number/number.util.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/analytics/analytics.model.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/array/array.util.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/promise/pTimeout.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/string/url.util.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/http/http.model.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/http/fetcher.js","../../../node_modules/.pnpm/@naturalcycles+js-lib@15.87.0_undici@8.10.2/node_modules/@naturalcycles/js-lib/dist/nanoid.js","../src/analytics/analyticsClient.ts"],"sourcesContent":["export const _isNull = (v) => v === null;\nexport const _isUndefined = (v) => v === undefined;\nexport const _isNullish = (v) => v === undefined || v === null;\nexport const _isNotNullish = (v) => v !== undefined && v !== null;\n/**\n * Same as Boolean, but with correct type output.\n * Related:\n * https://github.com/microsoft/TypeScript/issues/16655\n * https://www.karltarvas.com/2021/03/11/typescript-array-filter-boolean.html\n *\n * @example\n *\n * [1, 2, undefined].filter(_isTruthy)\n * // => [1, 2]\n */\nexport const _isTruthy = (v) => !!v;\nexport const _isFalsy = (v) => !v;\n/**\n * Returns true if item is Object, not null and not Array.\n *\n * Currently treats RegEx as Object too, e.g _isObject(/some/) === true\n */\nexport function _isObject(obj) {\n return (typeof obj === 'object' && obj !== null && !Array.isArray(obj)) || false;\n}\nexport function _isPrimitive(v) {\n return (v === null ||\n v === undefined ||\n typeof v === 'number' ||\n typeof v === 'boolean' ||\n typeof v === 'string' ||\n typeof v === 'bigint' ||\n typeof v === 'symbol');\n}\nexport function _isEmptyObject(obj) {\n // for...in with early return avoids allocating the full Object.keys array.\n // Object.hasOwn matches Object.keys() semantics (own enumerable keys only).\n for (const k in obj) {\n if (Object.hasOwn(obj, k))\n return false;\n }\n return true;\n}\nexport function _isNotEmptyObject(obj) {\n for (const k in obj) {\n if (Object.hasOwn(obj, k))\n return true;\n }\n return false;\n}\n/**\n * Object is considered empty if it's one of:\n * undefined\n * null\n * '' (empty string)\n * [] (empty array)\n * {} (empty object)\n * new Map() (empty Map)\n * new Set() (empty Set)\n */\nexport function _isEmpty(obj) {\n if (obj === undefined || obj === null)\n return true;\n if (typeof obj === 'string' || Array.isArray(obj)) {\n return obj.length === 0;\n }\n if (obj instanceof Map || obj instanceof Set) {\n return obj.size === 0;\n }\n if (typeof obj === 'object') {\n for (const k in obj) {\n if (Object.hasOwn(obj, k))\n return false;\n }\n return true;\n }\n return false;\n}\n/**\n * @see _isEmpty\n */\nexport function _isNotEmpty(obj) {\n return !_isEmpty(obj);\n}\n","import { _isTruthy } from './is.util.js';\n/**\n * Creates AbortableSignal,\n * which is like AbortSignal, but can \"abort itself\" with `.abort()` method.\n *\n * @experimental\n */\nexport function createAbortableSignal() {\n const ac = new AbortController();\n return Object.assign(ac.signal, {\n abort: ac.abort.bind(ac),\n });\n}\n/**\n * Returns AbortSignal if ms is defined.\n * Otherwise returns undefined.\n */\nexport function abortSignalTimeoutOrUndefined(ms) {\n return ms ? abortSignalTimeout(ms) : undefined;\n}\n/**\n * Returns an AbortSignal that aborts after the given number of milliseconds.\n * Uses native `AbortSignal.timeout()` when available, falls back to a polyfill.\n *\n * The abort reason is a DOMException with name \"TimeoutError\".\n */\nexport function abortSignalTimeout(ms) {\n return typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(ms)\n : polyfilledAbortSignalTimeout(ms);\n}\nexport function polyfilledAbortSignalTimeout(ms) {\n const ac = new AbortController();\n setTimeout(() => {\n ac.abort(new DOMException('The operation was aborted due to timeout', 'TimeoutError'));\n }, ms);\n return ac.signal;\n}\n/**\n * Returns AbortSignal.any(signals) is the array (after filtering undefined inputs) is not empty,\n * otherwise undefined.\n */\nexport function abortSignalAnyOrUndefined(signals) {\n const filtered = signals.filter(_isTruthy);\n return filtered.length ? abortSignalAny(filtered) : undefined;\n}\n/**\n * Returns an AbortSignal that aborts when any of the given signals abort.\n * Uses native `AbortSignal.any()` when available, falls back to a polyfill.\n *\n * The abort reason is taken from the first signal that aborts.\n * If any input signal is already aborted, the returned signal is immediately aborted.\n *\n * If only 1 signal is passed in the input array - that Signal is returned as-is.\n */\nexport function abortSignalAny(signals) {\n if (signals.length === 1) {\n return signals[0];\n }\n return typeof AbortSignal.any === 'function'\n ? AbortSignal.any(signals)\n : polyfilledAbortSignalAny(signals);\n}\nexport function polyfilledAbortSignalAny(signals) {\n const ac = new AbortController();\n for (const signal of signals) {\n if (signal.aborted) {\n ac.abort(signal.reason);\n return ac.signal;\n }\n }\n for (const signal of signals) {\n signal.addEventListener('abort', () => ac.abort(signal.reason), {\n once: true,\n signal: ac.signal,\n });\n }\n return ac.signal;\n}\n","/**\n * Symbol to indicate END of Sequence.\n */\nexport const END = Symbol('END');\n/**\n * Symbol to indicate SKIP of item (e.g in AbortableMapper)\n */\nexport const SKIP = Symbol('SKIP');\n/**\n * Symbol to indicate cache miss.\n * To distinguish from cache returning `undefined` or `null`.\n */\nexport const MISS = Symbol('MISS');\nexport const _passthroughMapper = item => item;\nexport const _passUndefinedMapper = () => undefined;\n/**\n * Function that does nothings and returns `undefined`.\n */\nexport const _noop = (..._args) => undefined;\nexport const _passthroughPredicate = () => true;\nexport const _passNothingPredicate = () => false;\nexport const JWT_REGEX = /^[\\w-]+\\.[\\w-]+\\.[\\w-]+$/;\n/**\n * Needed due to https://github.com/microsoft/TypeScript/issues/13778\n * Only affects typings, no runtime effect.\n */\nexport const _stringMapValues = Object.values;\n/**\n * Needed due to https://github.com/microsoft/TypeScript/issues/13778\n * Only affects typings, no runtime effect.\n */\nexport const _stringMapEntries = Object.entries;\n/**\n * Alias of `Object.keys`, but returns keys typed as `keyof T`, not as just `string`.\n * This is how TypeScript should work, actually.\n *\n * Object.keys always returns strings, so numeric keys are stringified via `${K}`.\n * Symbol keys are excluded (Object.keys does not return symbols).\n */\nexport const _objectKeys = Object.keys;\n/**\n * Alias of `Object.entries`, but returns better-typed output.\n *\n * Difference with _stringMapEntries?\n * Use _stringMapEntries when the object is a StringMap<T> - it'll correctly infer T being not undefined.\n * If the object is not a StringMap - use _objectEntries - it'll correctly infer object keys, which can be typed as Enum.\n *\n * So e.g you can use _objectEntries(obj).map([k, v] => {})\n * and `k` will be `keyof obj` instead of generic `string`.\n */\nexport const _objectEntries = Object.entries;\n/**\n * Utility function that helps to cast *existing variable* to needed type T.\n *\n * @example\n * try {} catch (err) {\n * // err is unknown here\n * _typeCast<AppError>(err)\n * // now err is of type AppError\n * err.data = {} // can be done, because it was casted\n * }\n */\nexport function _typeCast(_v) { }\n/**\n * Type-safe Object.assign that checks that part is indeed a Partial<T>\n */\nexport const _objectAssign = Object.assign;\n","import { _isEmpty, _isObject } from '../is.util.js';\nimport { _objectEntries, SKIP } from '../types.js';\n/**\n * Returns clone of `obj` with only `props` preserved.\n * Opposite of Omit.\n */\nexport function _pick(obj, props, opt = {}) {\n if (opt.mutate) {\n // Start as original object (mutable), DELETE properties that are not whitelisted\n for (const k of Object.keys(obj)) {\n if (!props.includes(k))\n delete obj[k];\n }\n return obj;\n }\n // Start as empty object, pick/add needed properties\n const r = {};\n for (const k of props) {\n if (k in obj)\n r[k] = obj[k];\n }\n return r;\n}\n/**\n * Sets all properties of an object except passed ones to `undefined`.\n * This is a more performant alternative to `_pick` that does picking/deleting.\n */\nexport function _pickWithUndefined(obj, props, opt = {}) {\n const r = opt.mutate ? obj : { ...obj };\n for (const k of Object.keys(r)) {\n if (!props.includes(k)) {\n r[k] = undefined;\n }\n }\n return r;\n}\n/**\n * Returns clone of `obj` with `props` omitted.\n * Opposite of Pick.\n */\nexport function _omit(obj, props, opt = {}) {\n if (opt.mutate) {\n for (const k of props) {\n delete obj[k];\n }\n return obj;\n }\n const r = {};\n for (const k of Object.keys(obj)) {\n if (!props.includes(k))\n r[k] = obj[k];\n }\n return r;\n}\n/**\n * Sets all passed properties of an object to `undefined`.\n * This is a more performant alternative to `_omit` that does picking/deleting.\n */\nexport function _omitWithUndefined(obj, props, opt = {}) {\n const r = opt.mutate ? obj : { ...obj };\n for (const k of props) {\n r[k] = undefined;\n }\n return r;\n}\n/**\n * Returns object with filtered keys from `props` array.\n * E.g:\n * _mask({...}, [\n * 'account.id',\n * 'account.updated',\n * ])\n */\nexport function _mask(obj, props, opt = {}) {\n const r = opt.mutate ? obj : _deepCopy(obj);\n for (const k of props) {\n _unset(r, k);\n }\n return r;\n}\n/**\n * Removes \"falsy\" values from the object.\n */\nexport function _filterFalsyValues(obj, opt = {}) {\n return _filterObject(obj, (_k, v) => !!v, opt);\n}\n/**\n * Removes values from the object that are `null` or `undefined`.\n */\nexport function _filterNullishValues(obj, opt = {}) {\n return _filterObject(obj, (_k, v) => v !== undefined && v !== null, opt);\n}\n/**\n * Removes values from the object that are `undefined`.\n * Only `undefined` values are removed. `null` values are kept!\n */\nexport function _filterUndefinedValues(obj, opt = {}) {\n return _filterObject(obj, (_k, v) => v !== undefined, opt);\n}\nexport function _filterEmptyArrays(obj, opt = {}) {\n return _filterObject(obj, (_k, v) => !Array.isArray(v) || v.length > 0, opt);\n}\n/**\n * Returns clone of `obj` without properties that does not pass `predicate`.\n * Allows filtering by both key and value.\n */\nexport function _filterObject(obj, predicate, opt = {}) {\n if (opt.mutate) {\n for (const [k, v] of _objectEntries(obj)) {\n if (!predicate(k, v, obj)) {\n delete obj[k];\n }\n }\n return obj;\n }\n // Not vulnerable to prototype pollution: writes to a new {}, where __proto__\n // assignment only changes the new object's prototype, not Object.prototype.\n const r = {};\n for (const [k, v] of _objectEntries(obj)) {\n if (predicate(k, v, obj)) {\n r[k] = v;\n }\n }\n return r;\n}\n/**\n * var users = {\n * 'fred': { 'user': 'fred', 'age': 40 },\n * 'pebbles': { 'user': 'pebbles', 'age': 1 }\n * }\n *\n * _mapValues(users, (_key, value) => value.age)\n * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)\n *\n * To skip some key-value pairs - use _mapObject instead.\n */\nexport function _mapValues(obj, mapper, opt = {}) {\n // Not vulnerable to prototype pollution: writes to a new {} (or mutates the\n // source in-place where own data properties shadow the __proto__ accessor).\n const map = opt.mutate ? obj : {};\n for (const [k, v] of Object.entries(obj)) {\n map[k] = mapper(k, v, obj);\n }\n return map;\n}\n/**\n * _.mapKeys({ 'a': 1, 'b': 2 }, (key, value) => key + value)\n * // => { 'a1': 1, 'b2': 2 }\n *\n * Does not support `mutate` flag.\n *\n * To skip some key-value pairs - use _mapObject instead.\n */\nexport function _mapKeys(obj, mapper) {\n // Not vulnerable to prototype pollution: writes to a new {}, where __proto__\n // assignment only changes the new object's prototype, not Object.prototype.\n const map = {};\n for (const [k, v] of Object.entries(obj)) {\n map[mapper(k, v, obj)] = v;\n }\n return map;\n}\n/**\n * Maps object through predicate - a function that receives (k, v, obj)\n * k - key\n * v - value\n * obj - whole object\n *\n * Order of arguments in the predicate is different form _mapValues / _mapKeys!\n *\n * Predicate should return a _tuple_ [0, 1], where:\n * 0 - key of returned object (string)\n * 1 - value of returned object (any)\n *\n * If predicate returns SKIP symbol - such key/value pair is ignored (filtered out).\n *\n * Non-string keys are passed via String(...)\n */\nexport function _mapObject(obj, mapper) {\n // Not vulnerable to prototype pollution: writes to a new {}, where __proto__\n // assignment only changes the new object's prototype, not Object.prototype.\n const map = {};\n for (const [k, v] of Object.entries(obj)) {\n const r = mapper(k, v, obj);\n if (r === SKIP)\n continue;\n map[r[0]] = r[1];\n }\n return map;\n}\nexport function _findKeyByValue(obj, v) {\n return Object.entries(obj).find(([_, value]) => value === v)?.[0];\n}\n/**\n * Returns the first key of a non-empty object.\n * Throws if the object is empty.\n *\n * Performance-optimised: uses `for...in` with an early return to avoid\n * allocating the full `Object.keys(obj)` array. The `Object.hasOwn` filter\n * matches `Object.keys()` semantics (own enumerable string keys only) and\n * satisfies the `guard-for-in` lint rule; cost is a single check before\n * the early return. Iteration order matches `Object.keys()` for plain\n * objects (integer-like keys ascending first, then string keys in\n * insertion order).\n */\nexport function _firstKey(obj) {\n for (const k in obj) {\n if (Object.hasOwn(obj, k))\n return k;\n }\n throw new Error('_firstKey called on empty object');\n}\n/**\n * Returns the first key of the object (or undefined if the object is empty).\n * See `_firstKey` for the iteration-order contract.\n */\nexport function _firstKeyOrUndefined(obj) {\n for (const k in obj) {\n if (Object.hasOwn(obj, k))\n return k;\n }\n return undefined;\n}\n/**\n * Returns the first value of a non-empty object.\n * Throws if the object is empty.\n * See `_firstKey` for the iteration-order contract.\n */\nexport function _firstValue(obj) {\n for (const k in obj) {\n if (Object.hasOwn(obj, k))\n return obj[k];\n }\n throw new Error('_firstValue called on empty object');\n}\n/**\n * Returns the first value of the object (or undefined if the object is empty).\n * See `_firstKey` for the iteration-order contract.\n */\nexport function _firstValueOrUndefined(obj) {\n for (const k in obj) {\n if (Object.hasOwn(obj, k))\n return obj[k];\n }\n return undefined;\n}\n/**\n * Returns the first [key, value] tuple of a non-empty object.\n * Throws if the object is empty.\n * See `_firstKey` for the iteration-order contract.\n */\nexport function _firstEntry(obj) {\n for (const k in obj) {\n if (Object.hasOwn(obj, k))\n return [k, obj[k]];\n }\n throw new Error('_firstEntry called on empty object');\n}\n/**\n * Returns the first [key, value] tuple of the object (or undefined if the object is empty).\n * See `_firstKey` for the iteration-order contract.\n */\nexport function _firstEntryOrUndefined(obj) {\n for (const k in obj) {\n if (Object.hasOwn(obj, k))\n return [k, obj[k]];\n }\n return undefined;\n}\nexport function _objectNullValuesToUndefined(obj, opt = {}) {\n return _mapValues(obj, (_k, v) => (v === null ? undefined : v), opt);\n}\n/**\n * Deep copy object (by json parse/stringify, since it has unbeatable performance+simplicity combo).\n */\nexport function _deepCopy(o, reviver) {\n return JSON.parse(JSON.stringify(o), reviver);\n}\n/**\n * Performance-optimized implementation of merging two objects\n * without mutating any of them.\n * (if you are allowed to mutate - there can be a faster implementation).\n *\n * Gives ~40% speedup with map sizes between 10 and 100k items,\n * compared to {...obj1, ...obj2} or Object.assign({}, obj1, obj2).\n *\n * Only use it in hot paths that are known to be performance bottlenecks,\n * otherwise it's not worth it (use normal object spread then).\n */\nexport function _mergeObjects(obj1, obj2) {\n // Not vulnerable to prototype pollution: writes to a new {}, where __proto__\n // assignment only changes the new object's prototype, not Object.prototype.\n const map = {};\n for (const k of Object.keys(obj1))\n map[k] = obj1[k];\n for (const k of Object.keys(obj2))\n map[k] = obj2[k];\n return map;\n}\n/**\n * Returns `undefined` if it's empty (according to `_isEmpty()` specification),\n * otherwise returns the original object.\n */\nexport function _undefinedIfEmpty(obj) {\n return _isEmpty(obj) ? undefined : obj;\n}\n/**\n * Filters the object by removing all key-value pairs where Value is Empty (according to _isEmpty() specification).\n */\nexport function _filterEmptyValues(obj, opt = {}) {\n return _filterObject(obj, (_k, v) => !_isEmpty(v), opt);\n}\n/**\n * Recursively merges own and inherited enumerable properties of source\n * objects into the destination object, skipping source properties that resolve\n * to `undefined`. Array and plain object properties are merged recursively.\n * Other objects and value types are overridden by assignment. Source objects\n * are applied from left to right. Subsequent sources overwrite property\n * assignments of previous sources.\n *\n * Works as \"recursive Object.assign\".\n *\n * **Note:** This method mutates `object`.\n *\n * @param target The destination object.\n * @param sources The source objects.\n * @returns Returns `object`.\n * @example\n *\n * var users = {\n * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]\n * };\n *\n * var ages = {\n * 'data': [{ 'age': 36 }, { 'age': 40 }]\n * };\n *\n * _.merge(users, ages);\n * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }\n *\n * Based on: https://gist.github.com/Salakar/1d7137de9cb8b704e48a\n */\nexport function _merge(target, ...sources) {\n for (const source of sources) {\n if (!_isObject(source))\n continue;\n for (const key of Object.keys(source)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype')\n continue;\n if (_isObject(source[key])) {\n ;\n target[key] ||= {};\n _merge(target[key], source[key]);\n }\n else {\n ;\n target[key] = source[key];\n }\n }\n }\n return target;\n}\n/**\n * Trims all object VALUES deeply.\n * Doesn't touch object KEYS.\n * Mutates.\n */\nexport function _deepTrim(o) {\n if (!o)\n return o;\n if (typeof o === 'string') {\n return o.trim();\n }\n if (typeof o === 'object') {\n // Not vulnerable to prototype pollution: mutates in-place on the same object.\n // If __proto__ is an own data property (e.g. from JSON.parse), reads and writes\n // go through the own property, not the Object.prototype accessor.\n for (const k of Object.keys(o)) {\n o[k] = _deepTrim(o[k]);\n }\n }\n return o;\n}\n// from: https://github.com/jonschlinkert/unset-value\n// mutates obj\nexport function _unset(obj, prop) {\n if (!_isObject(obj)) {\n return;\n }\n if (obj.hasOwnProperty(prop)) {\n delete obj[prop];\n return;\n }\n const segs = prop.split('.');\n // Prevent prototype pollution\n if (segs.includes('__proto__') || segs.includes('constructor'))\n return;\n let last = segs.pop();\n while (segs.length && segs[segs.length - 1].endsWith('\\\\')) {\n last = segs.pop().slice(0, -1) + '.' + last;\n }\n while (segs.length && _isObject(obj)) {\n const k = segs.shift();\n obj = obj[k];\n }\n if (!_isObject(obj))\n return;\n delete obj[last];\n}\nexport function _invert(o) {\n const inv = {};\n Object.keys(o).forEach(k => {\n inv[o[k]] = k;\n });\n return inv;\n}\nexport function _invertMap(m) {\n const inv = new Map();\n m.forEach((v, k) => inv.set(v, k));\n return inv;\n}\n/**\n * Gets the property value at path of object.\n *\n * @example\n * const obj = {a: 'a', b: 'b', c: { cc: 'cc' }}\n * _get(obj, 'a') // 'a'\n * _get(obj, 'c.cc') // 'cc'\n * _get(obj, 'c[cc]') // 'cc'\n * _get(obj, 'unknown.path') // undefined\n */\nexport function _get(obj = {}, path = '') {\n return (path\n .replaceAll(/\\[([^\\]]+)]/g, '.$1')\n .split('.')\n // oxlint-disable-next-line unicorn/no-array-reduce\n .reduce((o, p) => o?.[p], obj));\n}\n/**\n * Sets the value at path of object. If a portion of path doesn’t exist it’s created. Arrays are created for\n * missing index properties while objects are created for all other missing properties.\n *\n * @param obj The object to modify.\n * @param path The path of the property to set.\n * @param value The value to set.\n * @returns Returns object.\n *\n * Based on: https://stackoverflow.com/a/54733755/4919972\n */\nexport function _set(obj, path, value) {\n if (!obj || Object(obj) !== obj || !path)\n return obj; // When obj is not an object\n // If not yet an array, get the keys from the string-path\n if (!Array.isArray(path)) {\n path = String(path).match(/[^.[\\]]+/g) || [];\n }\n else if (!path.length) {\n return obj;\n }\n // Prevent prototype pollution\n if (path.includes('__proto__') || path.includes('constructor')) {\n return obj;\n }\n // oxlint-disable-next-line unicorn/no-array-reduce\n ;\n path.slice(0, -1).reduce((a, c, i) => Object(a[c]) === a[c] // Does the key exist and is its value an object?\n ? // Yes: then follow that path\n a[c]\n : // No: create the key. Is the next key a potential array-index?\n (a[c] =\n // oxlint-disable-next-line no-bitwise, no-implicit-coercion, unicorn/prefer-math-trunc\n Math.abs(path[i + 1]) >> 0 === +path[i + 1]\n ? [] // Yes: assign a new array object\n : {}), // No: assign a new plain object\n obj)[path[path.length - 1]] = value; // Finally assign the value to the last key\n return obj; // allow chaining\n}\n/**\n * Checks if `path` is a direct property of `object` (not null, not undefined).\n *\n * @param obj The object to query.\n * @param path The path to check.\n * @returns Returns `true` if `path` exists, else `false`.\n * @example\n *\n * var object = { 'a': { 'b': { 'c': 3 } } };\n * var other = _.create({ 'a': _.create({ 'b': _.create({ 'c': 3 }) }) });\n *\n * _.has(object, 'a');\n * // => true\n *\n * _.has(object, 'a.b.c');\n * // => true\n *\n * _.has(object, ['a', 'b', 'c']);\n * // => true\n *\n * _.has(other, 'a');\n * // => false\n */\nexport function _has(obj, path) {\n const v = _get(obj, path);\n return v !== undefined && v !== null;\n}\n/**\n * Does Object.freeze recursively for given object.\n *\n * Based on: https://github.com/substack/deep-freeze/blob/master/index.js\n */\nexport function _deepFreeze(o) {\n // Not vulnerable to prototype pollution: read-only traversal, only calls\n // Object.freeze — never assigns properties from one object to another.\n Object.freeze(o);\n Object.getOwnPropertyNames(o).forEach(prop => {\n if (o.hasOwnProperty(prop) &&\n o[prop] !== null &&\n (typeof o[prop] === 'object' || typeof o[prop] === 'function') &&\n !Object.isFrozen(o[prop])) {\n _deepFreeze(o[prop]);\n }\n });\n}\n/**\n * let target: T = { a: 'a', n: 1}\n * let source: T = { a: 'a2', b: 'b' }\n *\n * _objectAssignExact(target, source)\n *\n * Does the same as `target = source`,\n * except that it mutates the target to make it exactly the same as source,\n * while keeping the reference to the same object.\n *\n * This way it can \"propagate deletions\".\n * E.g source doesn't have the `n` property, so it'll be deleted from target.\n * With normal Object.assign - it'll override the keys that `source` has, but not the\n * \"missing/deleted keys\".\n *\n * To make mutation extra clear - function returns void (unlike Object.assign).\n */\nexport function _objectAssignExact(target, source) {\n Object.assign(target, source);\n for (const k of Object.keys(target)) {\n if (!(k in source)) {\n // consider setting it to undefined maybe?\n delete target[k];\n }\n }\n}\n/**\n * type MyObj = { a?: string, b?: string }\n *\n * const collection: MyObj[] = [...]\n *\n * const collectionA = collection.filter(_hasProp('a'))\n * --> collectionA is now RequiredProp<MyObj, 'a'>[], i.e. { a: string, b?: string }[]\n */\nexport function _hasProp(prop) {\n return function (object) {\n return typeof object[prop] !== 'undefined';\n };\n}\n","/**\n * Use it to detect SSR/Node.js environment.\n *\n * Will return `true` in Node.js.\n * Will return `false` in the Browser.\n */\nexport function isServerSide() {\n return !isClientSide();\n}\n/**\n * Use it to detect Browser (not SSR/Node) environment.\n *\n * Will return `true` in the Browser.\n * Will return `false` in Node.js.\n */\nexport function isClientSide() {\n // oxlint-disable-next-line unicorn/prefer-global-this\n return typeof window !== 'undefined' && !!window?.document;\n}\n/**\n * Almost the same as isServerSide()\n * (isServerSide should return true for Node),\n * but detects Node specifically (not Deno, not Bun, etc).\n */\nexport function isNode() {\n return typeof process !== 'undefined' && process?.release?.name === 'node';\n}\n","// oxlint-disable-next-line import/no-cycle -- intentional cycle\nimport { JsonParseError } from '../error/error.util.js';\n// const possibleJsonStartTokens = ['{', '[', '\"']\nconst DETECT_JSON = /^\\s*[{[\"\\-\\d]/;\n/**\n * Attempts to parse object as JSON.\n * Returns original object if JSON parse failed (silently).\n */\nexport function _jsonParseIfPossible(obj, reviver) {\n // Optimization: only try to parse if it looks like JSON: starts with a json possible character\n if (typeof obj === 'string' && obj && DETECT_JSON.test(obj)) {\n try {\n return JSON.parse(obj, reviver);\n }\n catch { }\n }\n return obj;\n}\n/**\n * Convenience function that does JSON.parse, but doesn't throw on error,\n * instead - safely returns `undefined`.\n */\nexport function _jsonParseOrUndefined(obj, reviver) {\n // Optimization: only try to parse if it looks like JSON: starts with a json possible character\n if (typeof obj === 'string' && obj && DETECT_JSON.test(obj)) {\n try {\n return JSON.parse(obj, reviver);\n }\n catch { }\n }\n}\n/**\n * Same as JSON.parse, but throws JsonParseError:\n *\n * 1. It's message includes a piece of source text (truncated)\n * 2. It's data.text contains full source text\n */\nexport function _jsonParse(s, reviver) {\n try {\n return JSON.parse(s, reviver);\n }\n catch {\n throw new JsonParseError({\n text: s,\n });\n }\n}\n","/**\n * Converts the first character of string to upper case and the remaining to lower case.\n * Returns a type-safe capitalized string.\n */\nexport function _capitalize(s = '') {\n return (s.charAt(0).toUpperCase() + s.slice(1).toLowerCase());\n}\n/**\n * Convert a string to a type-safe uppercase string.\n */\nexport function _toUpperCase(s) {\n return s.toUpperCase();\n}\n/**\n * Convert a string to a type-safe lowercase string.\n */\nexport function _toLowercase(s) {\n return s.toLowerCase();\n}\nexport function _upperFirst(s = '') {\n return (s.charAt(0).toUpperCase() + s.slice(1));\n}\nexport function _lowerFirst(s) {\n return (s.charAt(0).toLowerCase() + s.slice(1));\n}\n/**\n * Like String.split(), but with limit, returning the tail together with last element.\n *\n * @returns Returns the new array of string segments.\n */\nexport function _split(str, separator, limit) {\n const parts = str.split(separator);\n if (parts.length <= limit)\n return parts;\n return [...parts.slice(0, limit - 1), parts.slice(limit - 1).join(separator)];\n}\nexport function _removeWhitespace(s) {\n return s.replaceAll(/\\s/g, '');\n}\n/**\n * _.truncate('hi-diddly-ho there, neighborino')\n * // => 'hi-diddly-ho there, neighbo...'\n */\nexport function _truncate(s, maxLen, omission = '...') {\n if (!s || s.length <= maxLen)\n return s;\n if (maxLen <= omission.length)\n return omission;\n return s.slice(0, maxLen - omission.length) + omission;\n}\n/**\n * _.truncateMiddle('abcdefghijklmnopqrstuvwxyz', 10)\n * // => 'abcd...xyz'\n */\nexport function _truncateMiddle(s, maxLen, omission = '...') {\n if (!s || s.length <= maxLen)\n return s;\n if (maxLen <= omission.length)\n return omission;\n const mark1 = Math.round((maxLen - omission.length) / 2);\n const mark2 = s.length - Math.floor((maxLen - omission.length) / 2);\n return s.slice(0, mark1) + omission + s.slice(mark2);\n}\n// These functions are modeled after Kotlin's String API\nexport function _substringBefore(s, delimiter) {\n const pos = s.indexOf(delimiter);\n return s.slice(0, pos !== -1 ? pos : undefined);\n}\nexport function _substringBeforeLast(s, delimiter) {\n const pos = s.lastIndexOf(delimiter);\n return s.slice(0, pos !== -1 ? pos : undefined);\n}\nexport function _substringAfter(s, delimiter) {\n const pos = s.indexOf(delimiter);\n return pos !== -1 ? s.slice(pos + delimiter.length) : s;\n}\nexport function _substringAfterLast(s, delimiter) {\n const pos = s.lastIndexOf(delimiter);\n return pos !== -1 ? s.slice(pos + delimiter.length) : s;\n}\n/**\n * Returns the substring between LAST `leftDelimiter` and then FIRST `rightDelimiter`.\n *\n * @example\n *\n * const s = '/Users/lalala/someFile.test.ts'\n * _substringBetweenLast(s, '/', '.')\n * // `someFile`\n */\nexport function _substringBetweenLast(s, leftDelimiter, rightDelimiter) {\n return _substringBefore(_substringAfterLast(s, leftDelimiter), rightDelimiter);\n}\n/**\n * Converts `\\n` (aka new-line) to `<br>`, to be presented in HTML.\n * Keeps `\\n`, so if it's printed in non-HTML environment it still looks ok-ish.\n */\nexport function _nl2br(s) {\n return s.replaceAll('\\n', '<br>\\n');\n}\n","import { isServerSide } from '../env.js';\n// oxlint-disable-next-line import/no-cycle -- intentional cycle\nimport { _jsonParseIfPossible } from '../string/json.util.js';\nimport { _truncate, _truncateMiddle } from '../string/string.util.js';\n// oxlint-disable-next-line import/no-cycle -- intentional cycle\nimport { _stringify } from '../string/stringify.js';\n/**\n * Useful to ensure that error in `catch (err) { ... }`\n * is indeed an Error (and not e.g `string` or `undefined`).\n * 99% of the cases it will be Error already.\n * Becomes more useful since TypeScript 4.4 made `err` of type `unknown` by default.\n *\n * Alternatively, if you're sure it's Error - you can use `_assertIsError(err)`.\n */\nexport function _anyToError(o, errorClass = Error, errorData) {\n let e;\n if (o instanceof errorClass) {\n e = o;\n }\n else {\n // If it's an instance of Error, but ErrorClass is something else (e.g AppError) - it'll be \"repacked\" into AppError\n const errorObject = _anyToErrorObject(o);\n e = _errorObjectToError(errorObject, errorClass);\n }\n if (errorData) {\n ;\n e.data ||= {};\n // Using Object.assign instead of ...data to not override err.data's non-enumerable properties\n Object.assign(e.data, errorData);\n }\n return e;\n}\n/**\n * Converts \"anything\" to ErrorObject.\n * Detects if it's HttpErrorResponse, HttpErrorObject, ErrorObject, Error, etc..\n * If object is Error - Error.message will be used.\n * Objects (not Errors) get converted to prettified JSON string (via `_stringify`).\n */\nexport function _anyToErrorObject(o, errorData) {\n let eo;\n if (_isErrorLike(o)) {\n eo = _errorLikeToErrorObject(o);\n }\n else {\n o = _jsonParseIfPossible(o);\n if (_isBackendErrorResponseObject(o)) {\n eo = o.error;\n }\n else if (_isErrorObject(o)) {\n eo = o;\n }\n else if (_isErrorLike(o)) {\n eo = _errorLikeToErrorObject(o);\n }\n else {\n // Here we are sure it has no `data` property,\n // so, fair to return `data: {}` in the end\n // Also we're sure it includes no \"error name\", e.g no `Error: ...`,\n // so, fair to include `name: 'Error'`\n const message = _stringify(o);\n eo = {\n name: 'Error',\n message,\n data: {}, // empty\n };\n }\n }\n Object.assign(eo.data, errorData);\n return eo;\n}\nexport function _errorLikeToErrorObject(e) {\n // If it's already an ErrorObject - just return it\n // AppError satisfies ErrorObject interface\n // Error does not satisfy (lacks `data`)\n // UPD: no, we expect a \"plain object\" here as an output,\n // because Error classes sometimes have non-enumerable properties (e.g data)\n if (!(e instanceof Error) && _isErrorObject(e)) {\n return e;\n }\n const obj = {\n name: e.name,\n message: e.message,\n data: { ...e.data }, // empty by default\n };\n if (e.stack)\n obj.stack = e.stack;\n if (e.cause) {\n obj.cause = _anyToErrorObject(e.cause);\n }\n return obj;\n}\nexport function _errorObjectToError(o, errorClass = Error) {\n if (o instanceof errorClass)\n return o;\n // Here we pass constructor values assuming it's AppError or sub-class of it\n // If not - will be checked at the next step\n // We cannot check `if (errorClass instanceof AppError)`, only `err instanceof AppError`\n const { name, cause } = o;\n const err = new errorClass(o.message, o.data, { name, cause });\n // name: err.name, // cannot be assigned to a readonly property like this\n // stack: o.stack, // also readonly e.g in Firefox\n if (o.stack) {\n Object.defineProperty(err, 'stack', {\n value: o.stack,\n });\n }\n if (!(err instanceof AppError)) {\n // Following actions are only needed for non-AppError-like errors\n Object.defineProperties(err, {\n name: {\n value: name,\n configurable: true,\n writable: true,\n },\n data: {\n value: o.data,\n writable: true,\n configurable: true,\n enumerable: false,\n },\n cause: {\n value: cause,\n writable: true,\n configurable: true,\n enumerable: true,\n },\n });\n Object.defineProperty(err.constructor, 'name', {\n value: name,\n configurable: true,\n writable: true,\n });\n }\n return err;\n}\n// These \"common\" error classes will not be printed as part of the Error snippet\nconst commonErrorClasses = new Set([\n 'Error',\n 'AppError',\n 'AssertionError',\n 'HttpRequestError',\n 'JoiValidationError',\n]);\n/**\n * Provides a short semi-user-friendly error message snippet,\n * that would allow to give a hint to the user what went wrong,\n * also to developers and CS to distinguish between different errors.\n *\n * It's not supposed to have full information about the error, just a small extract from it.\n */\nexport function _errorSnippet(err, opt = {}) {\n const { maxLineLength = 60, maxLines = 3 } = opt;\n const e = _anyToErrorObject(err);\n const lines = [errorObjectToSnippet(e)];\n let { cause } = e;\n while (cause && lines.length < maxLines) {\n lines.push('Caused by ' + errorObjectToSnippet(cause));\n cause = cause.cause; // insert DiCaprio Inception meme\n }\n return lines.map(line => _truncate(line, maxLineLength)).join('\\n');\n function errorObjectToSnippet(e) {\n // Return snippet if it was already prepared\n if (e.data.snippet)\n return e.data.snippet;\n // Code already serves the purpose of the snippet, so we can just return it\n if (e.data.code)\n return e.data.code;\n return [\n !commonErrorClasses.has(e.name) && e.name,\n // replace \"1+ white space characters\" with a single space\n e.message.replaceAll(/\\s+/gm, ' ').trim(),\n ]\n .filter(Boolean)\n .join(': ');\n }\n}\n// These duck-typing checks read properties of an arbitrary object, which may invoke its\n// property getters, which may throw (e.g a stateful getter, a Proxy trap).\n// A type guard should never throw - an object whose properties cannot even be read safely\n// is, by definition, not a well-behaved Error-like - hence try/catch returning false.\n// try/catch is free in V8 unless it actually catches, so there's no performance penalty.\nexport function _isBackendErrorResponseObject(o) {\n try {\n return _isErrorObject(o?.error);\n }\n catch {\n return false;\n }\n}\nexport function _isHttpRequestErrorObject(o) {\n try {\n return !!o && o.name === 'HttpRequestError' && typeof o.data?.requestUrl === 'string';\n }\n catch {\n return false;\n }\n}\n/**\n * Note: any instance of AppError is also automatically an ErrorObject\n */\nexport function _isErrorObject(o) {\n try {\n return (!!o &&\n typeof o === 'object' &&\n typeof o.name === 'string' &&\n typeof o.message === 'string' &&\n typeof o.data === 'object');\n }\n catch {\n return false;\n }\n}\nexport function _isErrorLike(o) {\n try {\n return (!!o && typeof o === 'object' && typeof o.name === 'string' && typeof o.message === 'string');\n }\n catch {\n return false;\n }\n}\n/**\n * Convenience function to safely add properties to Error's `data` object\n * (even if it wasn't previously existing).\n * Mutates err.\n * Returns err for convenience, so you can re-throw it directly.\n *\n * @example\n *\n * try {} catch (err) {\n * throw _errorDataAppend(err, {\n * backendResponseStatusCode: 401,\n * })\n * }\n */\nexport function _errorDataAppend(err, data) {\n if (!data)\n return err;\n return _anyToError(err, undefined, data);\n}\n/**\n * Base class for all our (not system) errors.\n *\n * message - \"technical\" message. Frontend decides to show it or not.\n * data - optional \"any\" payload.\n * data.userFriendly - if present, will be displayed to the User as is.\n *\n * Based on: `https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801`\n */\nexport class AppError extends Error {\n data;\n /**\n * `cause` here is normalized to be an ErrorObject\n */\n cause;\n /**\n * Experimental alternative static constructor.\n */\n static of(opt) {\n return new AppError(opt.message, opt.data, {\n name: opt.name,\n cause: opt.cause,\n });\n }\n constructor(message, data = {}, opt = {}) {\n super(message);\n // Here we default to `this.constructor.name` on Node, but to 'AppError' on the Frontend\n // because Frontend tends to minify class names, so `constructor.name` is not reliable\n const { name = isServerSide() ? this.constructor.name : 'AppError', cause } = opt;\n Object.defineProperties(this, {\n name: {\n value: name,\n configurable: true,\n writable: true,\n },\n data: {\n value: data,\n writable: true,\n configurable: true,\n enumerable: false,\n },\n });\n if (cause) {\n Object.defineProperty(this, 'cause', {\n value: _anyToErrorObject(cause),\n writable: true,\n configurable: true,\n enumerable: true, // unlike standard - setting it to true for \"visibility\"\n });\n }\n else {\n delete this.cause; // otherwise it's printed as `cause: undefined`\n }\n // this is to allow changing this.constuctor.name to a non-minified version\n Object.defineProperty(this.constructor, 'name', {\n value: name,\n configurable: true,\n writable: true,\n });\n // todo: check if it's needed at all!\n // if (Error.captureStackTrace) {\n // Error.captureStackTrace(this, this.constructor)\n // } else {\n // Object.defineProperty(this, 'stack', {\n // value: new Error().stack, // eslint-disable-line unicorn/error-message\n // writable: true,\n // configurable: true,\n // })\n // }\n }\n}\n/**\n * Error that is thrown when Http Request was made and returned an error.\n * Thrown by, for example, Fetcher.\n *\n * On the Frontend this Error class represents the error when calling the API,\n * contains all the necessary request and response information.\n *\n * On the Backend, similarly, it represents the error when calling some 3rd-party API\n * (backend-to-backend call).\n * On the Backend it often propagates all the way to the Backend error handler,\n * where it would be wrapped in BackendErrorResponseObject.\n *\n * Please note that `ErrorData.backendResponseStatusCode` is NOT exactly the same as\n * `HttpRequestErrorData.responseStatusCode`.\n * E.g 3rd-party call may return 401, but our Backend will still wrap it into an 500 error\n * (by default).\n */\nexport class HttpRequestError extends AppError {\n constructor(message, data, opt) {\n if (data.response) {\n Object.defineProperty(data, 'response', {\n enumerable: false,\n });\n }\n super(message, data, { ...opt, name: 'HttpRequestError' });\n }\n}\nexport class AssertionError extends AppError {\n constructor(message, data) {\n super(message, data, { name: 'AssertionError' });\n }\n}\nexport class JsonParseError extends AppError {\n constructor(data) {\n const message = ['Failed to parse', data.text && _truncateMiddle(data.text, 200)]\n .filter(Boolean)\n .join(': ');\n super(message, data, { name: 'JsonParseError' });\n }\n}\nexport class TimeoutError extends AppError {\n constructor(message, data, opt) {\n super(message, data, { ...opt, name: 'TimeoutError' });\n }\n}\n/**\n * It is thrown when Error was expected, but didn't happen\n * (\"pass\" happened instead).\n * \"Pass\" means \"no error\".\n */\nexport class UnexpectedPassError extends AppError {\n constructor(message) {\n super(message || 'expected error was not thrown', {}, {\n name: 'UnexpectedPassError',\n });\n }\n}\n","/**\n * JSON.stringify that avoids circular references, prints them as [Circular ~]\n *\n * Based on: https://github.com/moll/json-stringify-safe/\n */\nexport function _safeJsonStringify(obj, replacer, spaces, cycleReplacer) {\n try {\n // Try native first (as it's ~3 times faster)\n return JSON.stringify(obj, replacer, spaces);\n }\n catch {\n // Native failed - resort to the \"safe\" serializer\n return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces);\n }\n}\nfunction serializer(replacer, cycleReplacer) {\n const stack = [];\n const keys = [];\n cycleReplacer ??= (_key, value) => {\n if (stack[0] === value)\n return '[Circular ~]';\n return '[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']';\n };\n return function (key, value) {\n if (stack.length > 0) {\n const thisPos = stack.indexOf(this);\n if (thisPos !== -1) {\n stack.splice(thisPos + 1);\n keys.splice(thisPos, Infinity, key);\n }\n else {\n stack.push(this);\n keys.push(key);\n }\n if (stack.includes(value)) {\n value = cycleReplacer.call(this, key, value);\n }\n }\n else {\n stack.push(value);\n }\n return replacer == null ? value : replacer.call(this, key, value);\n };\n}\n","// oxlint-disable-next-line import/no-cycle -- intentional cycle\nimport { _isBackendErrorResponseObject, _isErrorLike, _isErrorObject } from '../error/error.util.js';\n// oxlint-disable-next-line import/no-cycle -- intentional cycle\nimport { _jsonParseIfPossible } from './json.util.js';\nimport { _safeJsonStringify } from './safeJsonStringify.js';\nimport { _truncateMiddle } from './string.util.js';\nconst supportsAggregateError = typeof globalThis.AggregateError === 'function';\nlet globalStringifyFunction = _safeJsonStringify;\n/**\n * Allows to set Global \"stringifyFunction\" that will be used to \"pretty-print\" objects\n * in various cases.\n *\n * Used, for example, by _stringify() to pretty-print objects/arrays.\n *\n * Defaults to _safeJsonStringify.\n *\n * Node.js project can set it to _inspect, which allows to use `util.inspect`\n * as pretty-printing function.\n *\n * It's recommended that this function is circular-reference-safe.\n */\nexport function setGlobalStringifyFunction(fn) {\n globalStringifyFunction = fn;\n}\nexport function resetGlobalStringifyFunction() {\n globalStringifyFunction = _safeJsonStringify;\n}\n/**\n * Inspired by `_inspect` from nodejs-lib, which is based on util.inpect that is not available in the Browser.\n * Potentially can do this (with extra 2Kb gz size): https://github.com/deecewan/browser-util-inspect\n *\n * Transforms ANY to human-readable string (via JSON.stringify pretty).\n * Safe (no error throwing).\n *\n * Correctly prints Errors, AppErrors, ErrorObjects: error.message + \\n + _stringify(error.data)\n *\n * Enforces max length (default to 1000, pass 0 to skip it).\n *\n * Logs numbers as-is, e.g: `6`.\n * Logs strings as-is (without single quotes around, unlike default util.inspect behavior).\n * Otherwise - just uses JSON.stringify().\n *\n * Returns 'empty_string' if empty string is passed.\n * Returns 'undefined' if undefined is passed (default util.inspect behavior).\n */\nexport function _stringify(obj, opt = {}) {\n if (obj === undefined)\n return 'undefined';\n if (obj === null)\n return 'null';\n if (typeof obj === 'function')\n return 'function';\n if (typeof obj === 'symbol')\n return obj.toString();\n let s;\n // Parse JSON string, if possible\n obj = _jsonParseIfPossible(obj); // in case it's e.g non-pretty JSON, or even a stringified ErrorObject\n //\n // HttpErrorResponse\n //\n if (_isBackendErrorResponseObject(obj)) {\n return _stringify(obj.error, opt);\n }\n if (obj instanceof Error || _isErrorLike(obj)) {\n s = stringifyErrorLike(obj, opt);\n }\n else if (typeof obj === 'string') {\n s = obj.trim() || 'empty_string';\n // todo: think about it more\n // Stringifying it like a JSON would.\n // To highlight that it's a String (and not a Number) - using double-quotes, JSON-like.\n // s = `\"${obj}\"`\n }\n else if (typeof obj === 'number') {\n s = String(obj);\n // todo: support RegExp and Date, when split between Browser and Node stringification is implemented\n // } else if (obj instanceof RegExp) {\n // s = String(obj)\n // } else if (obj instanceof Date) {\n // s = `Date (${obj.toISOString()})`\n }\n else {\n //\n // Other\n //\n if (obj instanceof Map) {\n // todo: double-check it, maybe Node's inspect has good built-in stringification\n obj = Object.fromEntries(obj);\n }\n else if (obj instanceof Set) {\n obj = Array.from(obj);\n }\n try {\n const { stringifyFn = globalStringifyFunction } = opt;\n s = stringifyFn(obj, undefined, 2);\n }\n catch {\n s = String(obj); // fallback\n }\n }\n // Shouldn't happen, but some weird input parameters may return this\n if (s === undefined)\n return 'undefined';\n // Handle maxLen\n const { maxLen = 10_000 } = opt;\n if (maxLen && s.length > maxLen) {\n return _truncateMiddle(s, maxLen, `\\n... ${Math.ceil(s.length / 1024)} Kb message truncated ...\\n`);\n }\n return s;\n}\nfunction stringifyErrorLike(obj, opt) {\n const { includeErrorCause = true } = opt;\n let s = [obj.name, obj.message].filter(Boolean).join(': ');\n if (typeof obj.code === 'string') {\n // Error that has no `data`, but has `code` property\n s += `\\ncode: ${obj.code}`;\n }\n if (opt.includeErrorData && _isErrorObject(obj) && Object.keys(obj.data).length) {\n s += '\\n' + _stringify(obj.data, opt);\n }\n if (opt.includeErrorStack && obj.stack) {\n // Here we're using the previously-generated \"title line\" (e.g \"Error: some_message\"),\n // concatenating it with the Stack (but without the title line of the Stack)\n // This is to fix the rare error (happened with Got) where `err.message` was changed,\n // but err.stack had \"old\" err.message\n // This should \"fix\" that\n const sLines = s.split('\\n').length;\n s = [s, ...obj.stack.split('\\n').slice(sLines)].join('\\n');\n }\n if (supportsAggregateError && obj instanceof AggregateError && obj.errors.length) {\n s = [\n s,\n `${obj.errors.length} error(s):`,\n ...obj.errors.map((err, i) => `${i + 1}. ${_stringify(err, opt)}`),\n ].join('\\n');\n }\n if (obj.cause && includeErrorCause) {\n s = s + '\\nCaused by: ' + _stringify(obj.cause, opt);\n }\n return s;\n}\n","import { _deepEquals } from '../object/deepEquals.js';\nimport { _stringify } from '../string/stringify.js';\nimport { _isBackendErrorResponseObject, _isErrorObject, AssertionError } from './error.util.js';\n/**\n * Evaluates the `condition` (casts it to Boolean).\n * Expects it to be truthy, otherwise throws AppError.\n *\n * Should be used NOT for \"expected\" / user-facing errors, but\n * vice-versa - for completely unexpected and 100% buggy \"should never happen\" cases.\n *\n * It'll result in http 500 on the server (cause that's the right code for \"unexpected\" errors).\n * Pass { backendResponseStatusCode: x } at errorData argument to override the http code (will be picked up by backend-lib).\n *\n * API is similar to Node's assert(), except:\n * 1. Throws js-lib's AppError\n * 2. Has a default message, if not provided\n *\n * Since 2024-07-10 it no longer sets `userFriendly: true` by default.\n */\nexport function _assert(condition, // will be evaluated as Boolean\nmessage, errorData) {\n if (!condition) {\n throw new AssertionError(message || 'condition failed', {\n ...errorData,\n });\n }\n}\n/**\n * Like _assert(), but prints more helpful error message.\n * API is similar to Node's assert.equals().\n *\n * Does SHALLOW, but strict equality (===), use _assertDeepEquals() for deep equality.\n */\nexport function _assertEquals(actual, expected, message, errorData) {\n if (actual !== expected) {\n const msg = message ||\n ['not equal', `expected: ${_stringify(expected)}`, `got : ${_stringify(actual)}`]\n .filter(Boolean)\n .join('\\n');\n throw new AssertionError(msg, {\n ...errorData,\n });\n }\n}\n/**\n * Like _assert(), but prints more helpful error message.\n * API is similar to Node's assert.deepEquals().\n *\n * Does DEEP equality via _deepEquals()\n */\nexport function _assertDeepEquals(actual, expected, message, errorData) {\n if (!_deepEquals(actual, expected)) {\n const msg = message ||\n ['not deeply equal', `expected: ${_stringify(expected)}`, `got : ${_stringify(actual)}`]\n .filter(Boolean)\n .join('\\n');\n throw new AssertionError(msg, {\n ...errorData,\n });\n }\n}\nexport function _assertIsError(err, errorClass = Error) {\n if (!(err instanceof errorClass)) {\n throw new AssertionError(`Expected to be instanceof ${errorClass.name}, actual typeof: ${typeof err}`);\n }\n}\n/**\n * Asserts that passed object is indeed an Error of defined ErrorClass.\n * If yes - returns peacefully (with TypeScript assertion).\n * In not - throws (re-throws) that error up.\n */\nexport function _assertErrorClassOrRethrow(err, errorClass) {\n if (!(err instanceof errorClass)) {\n // re-throw\n throw err;\n }\n}\nexport function _assertIsErrorObject(obj) {\n if (!_isErrorObject(obj)) {\n throw new AssertionError(`Expected to be ErrorObject, actual typeof: ${typeof obj}`);\n }\n}\nexport function _assertIsBackendErrorResponseObject(obj) {\n if (!_isBackendErrorResponseObject(obj)) {\n throw new AssertionError(`Expected to be BackendErrorResponseObject, actual typeof: ${typeof obj}`);\n }\n}\nexport function _assertIsString(v, message) {\n _assertTypeOf(v, 'string', message);\n}\nexport function _assertIsNumber(v, message) {\n _assertTypeOf(v, 'number', message);\n}\nexport function _assertTypeOf(v, expectedType, message) {\n if (typeof v !== expectedType) {\n const msg = message || `Expected typeof ${expectedType}, actual typeof: ${typeof v}`;\n throw new AssertionError(msg);\n }\n}\n/**\n * Casts an arbitrary number as UnixTimestamp.\n * Right now does not perform any validation (unlike `asUnixTimestamp2000`),\n * but only type casting.\n */\nexport function asUnixTimestamp(n) {\n return n;\n}\nconst TS_2500 = 16725225600; // 2500-01-01\nconst TS_2000 = 946684800; // 2000-01-01\n/**\n * Casts an arbitrary number as UnixTimestamp2000.\n * Throws if the number is not inside 2000-01-01 and 2500-01-01 time interval,\n * which would indicate a bug.\n */\nexport function asUnixTimestamp2000(n) {\n if (!n || n < TS_2000 || n > TS_2500) {\n throw new AssertionError(`Number is not a valid UnixTimestamp2000: ${n}`, {\n fingerprint: 'asUnixTimestamp2000',\n });\n }\n return n;\n}\n","import { pDefer } from './pDefer.js';\n/**\n * Promisified version of setTimeout.\n *\n * Can return a value.\n * If value is instanceof Error - rejects the Promise instead of resolving.\n */\nexport async function pDelay(ms = 0, value) {\n return await new Promise((resolve, reject) => setTimeout(value instanceof Error ? reject : resolve, ms, value));\n}\n/**\n * Like pDelay, but also resolves early (without an error) as soon as\n * the passed AbortSignal aborts.\n */\nexport async function pDelaySignal(ms, signal) {\n if (signal?.aborted)\n return;\n await new Promise(resolve => {\n const timer = setTimeout(done, ms);\n function done() {\n clearTimeout(timer);\n signal?.removeEventListener('abort', done);\n resolve();\n }\n signal?.addEventListener('abort', done);\n });\n}\n/* oxlint-disable @typescript-eslint/promise-function-async */\n/**\n * Promisified version of setTimeout.\n *\n * Wraps the passed function with try/catch,\n * catch will propagate to pDelayFn rejection,\n * otherwise pDelayFn will resolve with returned value.\n *\n * On abort() - clears the Timeout and immediately resolves the Promise with void.\n */\nexport function pDelayFn(ms, fn) {\n const p = pDefer();\n const timer = setTimeout(async () => {\n try {\n p.resolve(await fn());\n }\n catch (err) {\n p.reject(err);\n }\n }, ms);\n p.abort = () => {\n clearTimeout(timer);\n // p.rejectAborted(reason) // nope\n p.resolve();\n };\n return p;\n}\n","/**\n * using _ = blockTimer()\n * // will log \"took 1.234 sec\" on dispose\n *\n * using _ = blockTimer('named')\n * // will log \"named took 1.234 sec\" on dispose\n *\n * @experimental\n */\nexport function _blockTimer(name) {\n const started = Date.now();\n return {\n [Symbol.dispose]() {\n console.log(`${name ? name + ' ' : ''}took ${_since(started)}`);\n },\n };\n}\n/**\n * Returns time passed since `from` until `until` (default to Date.now())\n */\nexport function _since(from, until = Date.now()) {\n return _ms(until - from);\n}\n/**\n * Returns, e.g:\n * 125 ms\n * 1.125 sec\n * 11 sec\n * 1m12s\n * 59m2s\n * 1h3m12s\n */\nexport function _ms(millis) {\n // <1 sec\n if (millis < 1000)\n return `${Math.round(millis)} ms`;\n // < 10 sec\n if (millis < 10_000) {\n const s = millis / 1000;\n return `${s.toFixed(2)} sec`;\n }\n const sec = Math.floor(millis / 1000) % 60;\n const min = Math.floor(millis / (60 * 1000)) % 60;\n const hrs = Math.floor(millis / (3600 * 1000));\n // <1 hr\n if (hrs === 0) {\n // <1 min\n if (min === 0)\n return `${sec} sec`;\n return `${min}m${sec}s`;\n }\n if (hrs < 24) {\n return `${hrs}h${min}m`;\n }\n if (hrs < 48) {\n return `${Math.round(hrs + min / 60)}h`;\n }\n // >= 48 hours\n const days = Math.floor(hrs / 24);\n return `${days} days`;\n}\n","export function _randomInt(minIncl, maxIncl) {\n return Math.floor(Math.random() * (maxIncl - minIncl + 1) + minIncl);\n}\n/**\n * Returns random item from an array.\n * Should be used on non-empty arrays! (otherwise will return undefined,\n * which is not reflected in the output type)\n */\nexport function _randomArrayItem(array) {\n return array[_randomInt(0, array.length - 1)];\n}\n/**\n * Convenience function to \"throttle\" some code - run it less often.\n *\n * @example\n *\n * if (_runLessOften(10)) {\n * // this code will run only 10% of the time\n * }\n */\nexport function _runLessOften(percent) {\n return Math.random() * 100 < percent;\n}\n// todo: _.random to support floats\n/**\n * _isBetween(-10, 1, 5) // false\n * _isBetween(1, 1, 5) // true\n * _isBetween(3, 1, 5) // true\n * _isBetween(5, 1, 5) // false\n * _isBetween(7, 1, 5) // false\n *\n * Also works with strings:\n * _isBetween('2020-01-03', '2020-01-01', '2020-01-05') // true\n */\nexport function _isBetween(x, min, max, incl) {\n if (x < min || x > max)\n return false;\n if (x === max && incl === '[)')\n return false;\n return true;\n}\nexport function _clamp(x, minIncl, maxIncl) {\n if (x <= minIncl)\n return minIncl;\n if (x >= maxIncl)\n return maxIncl;\n return x;\n}\n/**\n * Same as .toFixed(), but conveniently casts the output to Number.\n *\n * @example\n *\n * _toFixed(1.2345, 2)\n * // 1.23\n *\n * _toFixed(1.10, 2)\n * // 1.1\n */\nexport function _toFixed(n, fractionDigits) {\n return Number(n.toFixed(fractionDigits));\n}\n/**\n * Same as .toPrecision(), but conveniently casts the output to Number.\n *\n * @example\n *\n * _toPrecision(1634.56, 1)\n * // 2000\n *\n * _toPrecision(1634.56, 2)\n * // 1600\n */\nexport function _toPrecision(n, precision) {\n return Number(n.toPrecision(precision));\n}\n/**\n * @example\n *\n * _round(1634, 1000) // 2000\n * _round(1634, 500) // 1500\n * _round(1634, 100) // 1600\n * _round(1634, 10) // 1630\n * _round(1634, 1) // 1634\n * _round(1634.5678, 0.1) // 1634.6\n * _round(1634.5678, 0.01) // 1634.57\n */\nexport function _round(n, precisionUnit) {\n if (precisionUnit >= 1) {\n return Math.round(n / precisionUnit) * precisionUnit;\n }\n const v = Math.floor(n) + Math.round((n % 1) / precisionUnit) * precisionUnit;\n return Number(v.toFixed(String(precisionUnit).length - 2)); // '0.\n}\n","/** Emitted when the identity changes, naming the anonymous id the events so far belong to. */\nexport const ANALYTICS_IDENTIFY_EVENT_NAME = 'identify';\n/** Cap applied by `truncateAnalyticsProperty`. Event properties are passed through as given. */\nexport const MAX_ANALYTICS_PROPERTY_LENGTH = 255;\n/**\n * Caps a string property at the limit. Counts code points, because a plain\n * slice(0, N) counts UTF-16 code units and can cut a surrogate pair in half.\n */\nexport function truncateAnalyticsProperty(value) {\n if (value.length <= MAX_ANALYTICS_PROPERTY_LENGTH)\n return value;\n return Array.from(value).slice(0, MAX_ANALYTICS_PROPERTY_LENGTH).join('');\n}\n/** Canonical utm parameters. */\nexport const CANONICAL_UTM_PARAMS = [\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_content',\n 'utm_term',\n 'utm_id',\n 'utm_source_platform',\n 'utm_campaign_id',\n 'utm_creative_format',\n 'utm_marketing_tactic',\n];\n","import { _assert } from '../error/assert.js';\nimport { END } from '../types.js';\n/**\n * Creates an array of elements split into groups the length of size. If collection can’t be split evenly, the\n * final chunk will be the remaining elements.\n *\n * @param array The array to process.\n * @param size The length of each chunk.\n * @returns Returns the new array containing chunks.\n *\n * https://lodash.com/docs#chunk\n *\n * Based on: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_chunk\n */\nexport function _chunk(array, size = 1) {\n const a = [];\n for (let i = 0; i < array.length; i += size) {\n a.push(array.slice(i, i + size));\n }\n return a;\n}\n/**\n * Removes duplicates from given array.\n */\nexport function _uniq(a) {\n return Array.from(new Set(a));\n}\n/**\n * Pushes an item to an array if it's not already there.\n * Mutates the array (same as normal `push`) and also returns it for chaining convenience.\n *\n * _pushUniq([1, 2, 3], 2) // => [1, 2, 3]\n *\n * Shortcut for:\n * if (!a.includes(item)) a.push(item)\n * // or\n * a = [...new Set(a).add(item)]\n * // or\n * a = _uniq([...a, item])\n */\nexport function _pushUniq(a, ...items) {\n for (const item of items) {\n if (!a.includes(item))\n a.push(item);\n }\n return a;\n}\n/**\n * Like _pushUniq but uses a mapper to determine uniqueness (like _uniqBy).\n * Mutates the array (same as normal `push`).\n */\nexport function _pushUniqBy(a, mapper, ...items) {\n const mappedSet = new Set(a.map(mapper));\n for (const item of items) {\n const mapped = mapper(item);\n if (!mappedSet.has(mapped)) {\n a.push(item);\n mappedSet.add(mapped);\n }\n }\n return a;\n}\n/**\n * This method is like `_.uniq` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * uniqueness is computed. The iteratee is invoked with one argument: (value).\n *\n * @returns Returns the new duplicate free array.\n * @example\n *\n * _.uniqBy([2.1, 1.2, 2.3], Math.floor);\n * // => [2.1, 1.2]\n *\n * // using the `_.property` iteratee shorthand\n * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }, { 'x': 2 }]\n *\n * Based on: https://stackoverflow.com/a/40808569/4919972\n */\nexport function _uniqBy(arr, mapper) {\n const map = new Map();\n for (const item of arr) {\n const key = item === undefined || item === null ? item : mapper(item);\n if (!map.has(key))\n map.set(key, item);\n }\n return Array.from(map.values());\n}\n/**\n * const a = [\n * {id: 'id1', a: 'a1'},\n * {id: 'id2', b: 'b1'},\n * ]\n *\n * _by(a, r => r.id)\n * // => {\n * id1: {id: 'id1', a: 'a1'},\n * id2: {id: 'id2', b: 'b1'},\n * }\n *\n * _by(a, r => r.id.toUpperCase())\n * // => {\n * ID1: {id: 'id1', a: 'a1'},\n * ID2: {id: 'id2', b: 'b1'},\n * }\n *\n * Returning `undefined` from the Mapper will EXCLUDE the item.\n */\nexport function _by(items, mapper) {\n const map = {};\n for (const v of items) {\n const k = mapper(v);\n if (k !== undefined) {\n map[k] = v;\n }\n }\n return map;\n}\n/**\n * Map an array of items by a key, that is calculated by a Mapper.\n */\nexport function _mapBy(items, mapper) {\n const map = new Map();\n for (const item of items) {\n const key = mapper(item);\n if (key !== undefined) {\n map.set(key, item);\n }\n }\n return map;\n}\n/**\n * const a = [1, 2, 3, 4, 5]\n *\n * _groupBy(a, r => r % 2 ? 'even' : 'odd')\n * // => {\n * odd: [1, 3, 5],\n * even: [2, 4],\n * }\n *\n * Returning `undefined` from the Mapper will EXCLUDE the item.\n */\nexport function _groupBy(items, mapper) {\n const map = {};\n for (const item of items) {\n const key = mapper(item);\n if (key !== undefined) {\n ;\n (map[key] ||= []).push(item);\n }\n }\n return map;\n}\n/**\n * Similar to `Array.find`, but the `predicate` may return `END` to stop the iteration early.\n *\n * Use `Array.find` if you don't need to stop the iteration early.\n */\nexport function _find(items, predicate) {\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n const result = predicate(item, i);\n if (result === END)\n return;\n if (result)\n return item;\n }\n}\n/**\n * Similar to `Array.findLast`, but the `predicate` may return `END` to stop the iteration early.\n *\n * Use `Array.findLast` if you don't need to stop the iteration early, which is supported:\n * - in Node since 18+\n * - in iOS Safari since 15.4\n */\nexport function _findLast(items, predicate) {\n return _find(items.toReversed(), predicate);\n}\nexport function _takeWhile(items, predicate) {\n let proceed = true;\n return items.filter((v, index) => (proceed &&= predicate(v, index)));\n}\nexport function _takeRightWhile(items, predicate) {\n let proceed = true;\n return items.toReversed().filter((v, index) => (proceed &&= predicate(v, index)));\n}\nexport function _dropWhile(items, predicate) {\n let proceed = false;\n return items.filter((v, index) => (proceed ||= !predicate(v, index)));\n}\nexport function _dropRightWhile(items, predicate) {\n let proceed = false;\n return items\n .toReversed()\n .filter((v, index) => (proceed ||= !predicate(v, index)))\n .reverse();\n}\n/**\n * Returns true if the _count >= limit.\n * _count counts how many times the Predicate returns true, and stops\n * when it reaches the limit.\n */\nexport function _countAtLeast(items, predicate, limit) {\n return _count(items, predicate, limit) >= limit;\n}\n/**\n * Returns true if the _count <> limit.\n * _count counts how many times the Predicate returns true, and stops\n * when it reaches the limit.\n */\nexport function _countLessThan(items, predicate, limit) {\n return _count(items, predicate, limit) < limit;\n}\n/**\n * Counts how many items match the predicate.\n *\n * `limit` allows to exit early when limit count is reached, skipping further iterations (perf optimization).\n */\nexport function _count(items, predicate, limit) {\n if (limit === 0)\n return 0;\n let count = 0;\n let i = 0;\n for (const item of items) {\n const r = predicate(item, i++);\n if (r === END)\n break;\n if (r) {\n count++;\n if (limit && count >= limit)\n break;\n }\n }\n return count;\n}\nexport function _countBy(items, mapper) {\n const map = {};\n for (const item of items) {\n const key = mapper(item);\n map[key] = (map[key] || 0) + 1;\n }\n return map;\n}\n// investigate: _groupBy\n/**\n * Returns an intersection between 2 arrays.\n *\n * Intersecion means an array of items that are present in both of the arrays.\n *\n * It's more performant to pass a Set as a second argument.\n *\n * @example\n * _intersection([2, 1], [2, 3])\n * // [2]\n */\nexport function _intersection(a1, a2) {\n const a2set = a2 instanceof Set ? a2 : new Set(a2);\n return a1.filter(v => a2set.has(v));\n}\n/**\n * Returns true if there is at least 1 item common between 2 arrays.\n * Otherwise returns false.\n *\n * It's more performant to use that versus `_intersection(a1, a2).length > 0`.\n *\n * Passing second array as Set is more performant (it'll skip turning the array into Set in-place).\n */\nexport function _intersectsWith(a1, a2) {\n const a2set = a2 instanceof Set ? a2 : new Set(a2);\n return a1.some(v => a2set.has(v));\n}\n/**\n * Returns array1 minus array2.\n *\n * @example\n * _difference([2, 1], [2, 3])\n * // [1]\n *\n * Passing second array as Set is more performant (it'll skip turning the array into Set in-place).\n */\nexport function _difference(a1, a2) {\n const a2set = a2 instanceof Set ? a2 : new Set(a2);\n return a1.filter(v => !a2set.has(v));\n}\n/**\n * Does NOT mutate the array, returns a filtered array instead.\n */\nexport function _arrayRemove(a, itemToRemove) {\n return a.filter(r => r !== itemToRemove);\n}\n/**\n * \"Toggles\" an item to be present or absent in the array,\n * based on the predicate. Respects uniqueness.\n *\n * If predicate==false - item gets removed from the array.\n * If predicate==true - item gets pushed to the array (unless it was already present).\n *\n * Pushing the item DOES MUTATE the array, same if you would do array.push manually.\n */\nexport function _arrayPushOrRemove(a, item, predicate) {\n if (predicate) {\n if (!a.includes(item)) {\n a.push(item);\n }\n return a;\n }\n return a.filter(r => r !== item);\n}\n/**\n * Returns the sum of items, or 0 for empty array.\n */\nexport function _sum(items) {\n let sum = 0;\n for (const n of items) {\n sum = (sum + n);\n }\n return sum;\n}\nexport function _sumBy(items, mapper) {\n let sum = 0;\n for (const n of items) {\n const v = mapper(n);\n if (typeof v === 'number') {\n // count only numbers, nothing else\n sum = (sum + v);\n }\n }\n return sum;\n}\n/**\n * Map an array of T to a StringMap<V>,\n * by returning a tuple of [key, value] from a mapper function.\n * Return undefined/null/false/0/void to filter out (not include) a value.\n *\n * @example\n *\n * _mapToObject([1, 2, 3], n => [n, n * 2])\n * // { '1': 2, '2': 4, '3': 6 }\n *\n * _mapToObject([1, 2, 3], n => [n, `id${n}`])\n * // { '1': 'id1, '2': 'id2', '3': 'id3' }\n */\nexport function _mapToObject(array, mapper) {\n const m = {};\n for (const item of array) {\n const r = mapper(item);\n if (!r)\n continue; // filtering\n m[r[0]] = r[1];\n }\n return m;\n}\n/**\n * Randomly shuffle an array values.\n * Fisher–Yates algorithm.\n * Based on: https://stackoverflow.com/a/12646864/4919972\n */\nexport function _shuffle(array, opt = {}) {\n const a = opt.mutate ? array : array.slice();\n for (let i = a.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [a[i], a[j]] = [a[j], a[i]];\n }\n return a;\n}\nexport function _firstLast(array) {\n if (!array.length)\n throw new Error('_firstLast called on empty array');\n return [array[0], array[array.length - 1]];\n}\nexport function _firstLastOrUndefined(array) {\n if (!array.length)\n return;\n return [array[0], array[array.length - 1]];\n}\n/**\n * Returns last item of non-empty array.\n * Throws if array is empty.\n */\nexport function _last(array) {\n if (!array.length)\n throw new Error('_last called on empty array');\n return array[array.length - 1];\n}\n/**\n * Returns last item of the array (or undefined if array is empty).\n */\nexport function _lastOrUndefined(array) {\n return array[array.length - 1];\n}\n/**\n * Returns the first item of non-empty array.\n * Throws if array is empty.\n */\nexport function _first(array) {\n if (!array.length)\n throw new Error('_first called on empty array');\n return array[0];\n}\n/**\n * Returns first item of the array (or undefined if array is empty).\n */\nexport function _firstOrUndefined(array) {\n return array[0];\n}\n/**\n * Returns the first item of a non-empty iterable (Set, Map.values(), generator, etc.).\n * Throws if the iterable is empty.\n *\n * Avoids the `Array.from(iter)[0]` pattern that materialises the entire iterable.\n * `for...of` with an early return advances the iterator exactly once; if the\n * iterator implements `return()` (generators, etc.), it is invoked for cleanup.\n */\nexport function _firstFromIterable(iter) {\n for (const item of iter)\n return item;\n throw new Error('_firstFromIterable called on empty iterable');\n}\n/**\n * Returns the first item of an iterable (or undefined if the iterable is empty).\n * See `_firstFromIterable` for the iteration semantics.\n */\nexport function _firstOrUndefinedFromIterable(iter) {\n for (const item of iter)\n return item;\n return undefined;\n}\nexport function _minOrUndefined(array) {\n let min;\n for (const item of array) {\n if (item === undefined || item === null)\n continue;\n if (min === undefined || item < min) {\n min = item;\n }\n }\n return min;\n}\n/**\n * Filters out nullish values (undefined and null).\n */\nexport function _min(array) {\n const min = _minOrUndefined(array);\n _assert(min !== undefined, '_min called on empty array');\n return min;\n}\nexport function _maxOrUndefined(array) {\n let max;\n for (const item of array) {\n if (item === undefined || item === null)\n continue;\n if (max === undefined || item > max) {\n max = item;\n }\n }\n return max;\n}\n/**\n * Filters out nullish values (undefined and null).\n */\nexport function _max(array) {\n const max = _maxOrUndefined(array);\n _assert(max !== undefined, '_max called on empty array');\n return max;\n}\nexport function _maxBy(array, mapper) {\n const max = _maxByOrUndefined(array, mapper);\n _assert(max !== undefined, '_maxBy returned undefined');\n return max;\n}\nexport function _minBy(array, mapper) {\n const min = _minByOrUndefined(array, mapper);\n _assert(min !== undefined, '_minBy returned undefined');\n return min;\n}\nexport function _minMax(array) {\n if (!array.length)\n throw new Error('_minMax called on empty array');\n const result = _minMaxOrUndefined(array);\n _assert(result !== undefined, '_minBy returned undefined');\n return result;\n}\nexport function _minMaxOrUndefined(array) {\n if (!array.length)\n return;\n let min;\n let max;\n for (const item of array) {\n if (item === undefined || item === null)\n continue;\n if (min === undefined)\n min = item;\n if (max === undefined)\n max = item;\n if (item < min)\n min = item;\n if (item > max)\n max = item;\n }\n if (min === undefined || max === undefined || min === null || max === null)\n return;\n return [min, max];\n}\nexport function _minMaxBy(array, mapper) {\n if (!array.length)\n throw new Error('_minMaxBy called on empty array');\n const result = _minMaxByOrUndefined(array, mapper);\n _assert(result !== undefined, '_minMaxBy returned undefined');\n return result;\n}\nexport function _minMaxByOrUndefined(array, mapper) {\n if (!array.length)\n return;\n let min;\n let minItem;\n let max;\n let maxItem;\n for (const item of array) {\n if (item === undefined || item === null)\n continue;\n const value = mapper(item);\n if (!value)\n continue;\n if (min === undefined) {\n min = value;\n minItem = item;\n }\n if (max === undefined) {\n max = value;\n maxItem = item;\n }\n if (value < min) {\n min = value;\n minItem = item;\n }\n if (value > max) {\n max = value;\n maxItem = item;\n }\n }\n if (minItem === undefined || maxItem === undefined || minItem === null || maxItem === null)\n return;\n return [minItem, maxItem];\n}\n// todo: looks like it _maxByOrUndefined/_minByOrUndefined can be DRYer\nexport function _maxByOrUndefined(array, mapper) {\n if (!array.length)\n return;\n let maxItem;\n let max;\n for (const item of array) {\n const v = mapper(item);\n if (v !== undefined && (max === undefined || v > max)) {\n maxItem = item;\n max = v;\n }\n }\n return maxItem;\n}\nexport function _minByOrUndefined(array, mapper) {\n if (!array.length)\n return;\n let minItem;\n let min;\n for (const item of array) {\n const v = mapper(item);\n if (v !== undefined && (min === undefined || v < min)) {\n minItem = item;\n min = v;\n }\n }\n return minItem;\n}\nexport function _zip(array1, array2) {\n const len = Math.min(array1.length, array2.length);\n const res = [];\n for (let i = 0; i < len; i++) {\n res.push([array1[i], array2[i]]);\n }\n return res;\n}\n","import { _errorDataAppend, TimeoutError } from '../error/error.util.js';\nimport { _typeCast } from '../types.js';\n/**\n * Decorates a Function with a timeout.\n * Returns a decorated Function.\n *\n * Throws an Error if the Function is not resolved in a certain time.\n * If the Function rejects - passes this rejection further.\n */\nexport function pTimeoutFn(fn, opt) {\n opt.name ||= fn.name;\n if (!opt.timeout) {\n return fn;\n }\n return async function pTimeoutInternalFn(...args) {\n return await pTimeout(() => fn.apply(this, args), opt);\n };\n}\n/**\n * Decorates a Function with a timeout and immediately calls it.\n *\n * Throws an Error if the Function is not resolved in a certain time.\n * If the Function rejects - passes this rejection further.\n */\nexport async function pTimeout(fn, opt) {\n const ac = new AbortController();\n const { signal } = ac;\n if (!opt.timeout) {\n // short-circuit to direct execution if 0 timeout is passed\n return await fn(signal);\n }\n const { timeout, name = fn.name || 'pTimeout function', onTimeout } = opt;\n const fakeError = opt.fakeError || new Error('TimeoutError');\n return await new Promise(async (resolve, reject) => {\n // Prepare the timeout timer\n const timer = setTimeout(() => {\n const err = new TimeoutError(`\"${name}\" timed out after ${timeout} ms`, opt.errorData);\n // keep original stack\n err.stack = fakeError.stack.replace('Error: TimeoutError', 'TimeoutError: ' + err.message);\n if (onTimeout) {\n try {\n resolve(onTimeout(err));\n }\n catch (err) {\n _typeCast(err);\n // keep original stack\n err.stack = fakeError.stack.replace('Error: TimeoutError', err.name + ': ' + err.message);\n // oxlint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n reject(_errorDataAppend(err, opt.errorData));\n }\n ac.abort(err);\n return;\n }\n reject(err);\n ac.abort(err);\n }, timeout);\n // Execute the Function\n try {\n resolve(await fn(signal));\n }\n catch (err) {\n // oxlint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n reject(err);\n }\n finally {\n clearTimeout(timer);\n }\n });\n}\n","/**\n * Parses `location.search` string (e.g `?a=1&b=2`) into a StringMap, e.g:\n * `{ a: '1', b: '2' }`\n *\n * Pass `location.search` to it in the Frontend, or any other string on the Backend (where `location.search` is not available).\n *\n * Works both with and without leading `?` character.\n *\n * Yes, there's `URLSearchParams` existing in the Frontend (not in Node yet), but it's API is not\n * as convenient. And the implementation here is super-small.\n *\n * Goal of this function is to produce exactly same output as URLSearchParams would.\n */\nexport function _parseQueryString(search) {\n const qs = {};\n search\n .slice(search.startsWith('?') ? 1 : 0)\n .split('&')\n .forEach(p => {\n const [k, v] = p.split('=');\n if (!k)\n return;\n qs[decodeURIComponent(k)] = decodeURIComponent(v || '');\n });\n return qs;\n}\n/**\n * A wrapper around `new URL(href)`, but it returns `null` instead of throwing an error.\n * While `URL.parse` exists, and behaves similarly, it's not widely supported.\n *\n * `null` was chosen instead of `undefined` in the return type union to make it easier to move to `URL.parse` if it ever becomes widely supported.\n */\nexport function _toUrlOrNull(url, base) {\n if (typeof url !== 'string')\n return null;\n try {\n return new URL(url, base || undefined);\n }\n catch {\n return null;\n }\n}\n","export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'];\n","/// <reference lib=\"es2023\" preserve=\"true\" />\n/// <reference lib=\"dom\" preserve=\"true\" />\n/// <reference lib=\"dom.iterable\" preserve=\"true\" />\nimport { abortSignalAnyOrUndefined, abortSignalTimeoutOrUndefined } from '../abort.js';\nimport { _ms, _since } from '../datetime/time.util.js';\nimport { isServerSide } from '../env.js';\nimport { _assertErrorClassOrRethrow, _assertIsError } from '../error/assert.js';\nimport { _anyToError, _anyToErrorObject, _errorDataAppend, _errorLikeToErrorObject, HttpRequestError, UnexpectedPassError, } from '../error/error.util.js';\nimport { _clamp } from '../number/number.util.js';\nimport { _filterFalsyValues, _filterNullishValues, _filterUndefinedValues, _mapKeys, _merge, _omit, _pick, } from '../object/object.util.js';\nimport { pDelaySignal } from '../promise/pDelay.js';\nimport { pTimeout } from '../promise/pTimeout.js';\nimport { _toUrlOrNull } from '../string/index.js';\nimport { _jsonParse, _jsonParseIfPossible } from '../string/json.util.js';\nimport { _stringify } from '../string/stringify.js';\nimport { HTTP_METHODS } from './http.model.js';\n/**\n * Experimental wrapper around Fetch.\n * Works in both Browser and Node, using `globalThis.fetch`.\n */\nexport class Fetcher {\n /**\n * Included in UserAgent when run in Node.\n * In the browser it's not included, as we want \"browser own\" UserAgent to be included instead.\n *\n * Version is to be incremented every time a difference in behaviour (or a bugfix) is done.\n */\n static VERSION = 5;\n /**\n * userAgent is statically exposed as Fetcher.userAgent.\n * It can be modified globally, and will be used (read) at the start of every request.\n */\n static userAgent = isServerSide() ? `fetcher/${this.VERSION}` : undefined;\n constructor(cfg = {}) {\n if (typeof globalThis.fetch !== 'function') {\n throw new TypeError('globalThis.fetch is not available');\n }\n this.cfg = this.normalizeCfg(cfg);\n // Dynamically create all helper methods\n for (const method of HTTP_METHODS) {\n const m = method.toLowerCase();\n this[`${m}Void`] = async (url, opt) => {\n return await this.fetch({\n url,\n method,\n responseType: 'void',\n ...opt,\n });\n };\n if (method === 'HEAD') {\n continue; // responseType=text\n }\n ;\n this[`${m}Text`] = async (url, opt) => {\n return await this.fetch({\n url,\n method,\n responseType: 'text',\n ...opt,\n });\n };\n this[m] = async (url, opt) => {\n return await this.fetch({\n url,\n method,\n responseType: 'json',\n ...opt,\n });\n };\n }\n }\n /**\n * Add BeforeRequest hook at the end of the hooks list.\n */\n onBeforeRequest(hook) {\n ;\n (this.cfg.hooks.beforeRequest ||= []).push(hook);\n return this;\n }\n onAfterResponse(hook) {\n ;\n (this.cfg.hooks.afterResponse ||= []).push(hook);\n return this;\n }\n onBeforeRetry(hook) {\n ;\n (this.cfg.hooks.beforeRetry ||= []).push(hook);\n return this;\n }\n onError(hook) {\n ;\n (this.cfg.hooks.onError ||= []).push(hook);\n return this;\n }\n /**\n * Init hooks run lazily, once per Fetcher instance, before the first request.\n * See FetcherInitHook docs.\n */\n onInit(hook) {\n ;\n (this.cfg.hooks.init ||= []).push(hook);\n return this;\n }\n /**\n * Clears the cached result of the init hooks, so they re-run before the next request.\n * Useful when the state acquired during init (e.g an auth token) becomes stale.\n * See also `cfg.hooks.shouldReinit`, which calls this automatically.\n */\n resetInit() {\n this.initPromise = undefined;\n this.initGeneration++;\n }\n cfg;\n initPromise;\n initGeneration = 0;\n static create(cfg = {}) {\n return new Fetcher(cfg);\n }\n // These methods are generated dynamically in the constructor\n // These default methods use responseType=json\n get;\n post;\n put;\n patch;\n delete;\n // responseType=text\n getText;\n postText;\n putText;\n patchText;\n deleteText;\n // responseType=void (no body fetching/parsing)\n getVoid;\n postVoid;\n putVoid;\n patchVoid;\n deleteVoid;\n headVoid;\n /**\n * Small convenience wrapper that allows to issue GraphQL queries.\n * In practice, all it does is:\n * - Defines convenience `query` input option\n * - Unwraps `response.data`\n * - Unwraps `response.errors` and throws, if it's defined (as GQL famously returns http 200 even for errors)\n *\n * Currently it only unwraps and uses the first error from the `errors` array, for simplicity.\n */\n async queryGraphQL(opt) {\n opt = { ...opt }; // avoid mutating the input options\n opt.method ||= this.cfg.init.method; // defaults to GET\n const payload = _filterFalsyValues({\n query: opt.query,\n variables: opt.variables,\n });\n // Checking the query length, and not allowing to use GET if above 1900\n if (opt.method === 'GET' && opt.query.length > 1900) {\n opt.method = 'POST';\n }\n if (opt.method === 'GET') {\n opt.searchParams = {\n ...opt.searchParams,\n ...payload,\n };\n }\n else {\n opt.json = payload;\n }\n const res = await this.doFetch(opt);\n if (res.err) {\n throw res.err;\n }\n if (res.body.errors) {\n // unwrap errors and throw\n const err = res.body.errors[0];\n // todo: consider creating a new GraphQLError class for this\n throw new HttpRequestError(err.message, {\n ...payload, // query and variables\n errors: res.body.errors, // full errors payload returned\n response: res.fetchResponse,\n responseStatusCode: res.statusCode,\n requestUrl: res.req.fullUrl,\n requestBaseUrl: this.cfg.baseUrl,\n requestMethod: res.req.init.method,\n requestSignature: res.signature,\n requestName: res.req.requestName,\n fetcherName: this.cfg.name,\n requestDuration: Date.now() - res.req.started,\n });\n }\n const { data } = res.body;\n if (opt.unwrapObject) {\n return data[opt.unwrapObject];\n }\n return data;\n }\n // responseType=bytes\n /**\n * Returns response body as Uint8Array.\n */\n async getBytes(url, opt) {\n return await this.fetch({\n url,\n responseType: 'bytes',\n ...opt,\n });\n }\n // responseType=readableStream\n /**\n * Returns raw fetchResponse.body, which is a ReadableStream<Uint8Array>\n *\n * More on streams and Node interop:\n * https://css-tricks.com/web-streams-everywhere-and-fetch-for-node-js/\n */\n async getReadableStream(url, opt) {\n return await this.fetch({\n url,\n responseType: 'readableStream',\n ...opt,\n });\n }\n async fetch(opt) {\n const res = await this.doFetch(opt);\n if (res.err && (res.req.throwHttpErrors || res.fetchResponse?.ok !== false)) {\n // With throwHttpErrors=false only http errors (response received, but !ok) are returned instead of thrown.\n // Other errors (network failure, timeout, body parsing) are still thrown.\n throw res.err;\n }\n return res.body;\n }\n /**\n * Like `fetch`, but returns the whole FetcherSuccessResponse, not just the body.\n * Allows to access response metadata, e.g `fetchResponse.headers` and `statusCode`,\n * while still throwing on errors (unlike `doFetch`).\n *\n * Note: `throwHttpErrors: false` is ignored here, http errors are always thrown -\n * otherwise the returned FetcherSuccessResponse type would lie.\n * Use `doFetch` if you don't want throwing.\n */\n async fetchWithMeta(opt) {\n const res = await this.doFetch(opt);\n if (res.err) {\n throw res.err;\n }\n return res;\n }\n /**\n * Execute fetch and expect/assert it to return an Error (which will be wrapped in\n * HttpRequestError as it normally would).\n * If fetch succeeds, which is unexpected, it'll throw an UnexpectedPass error.\n * Useful in unit testing.\n */\n async expectError(opt) {\n const res = await this.doFetch(opt);\n if (!res.err) {\n throw new UnexpectedPassError('Fetch was expected to error');\n }\n _assertIsError(res.err, HttpRequestError);\n return res.err;\n }\n /**\n * Like pTry - returns a [err, data] tuple (aka ErrorDataTuple).\n * err, if defined, is strictly HttpRequestError.\n * UPD: actually not, err is typed as Error, as it feels unsafe to guarantee error type.\n * UPD: actually yes - it will return HttpRequestError, and throw if there's an error\n * of any other type.\n */\n async tryFetch(opt) {\n const res = await this.doFetch(opt);\n if (res.err) {\n _assertErrorClassOrRethrow(res.err, HttpRequestError);\n return [res.err, null];\n }\n return [null, res.body];\n }\n /**\n * Returns FetcherResponse.\n * Never throws, returns `err` property in the response instead.\n * (Exception: errors thrown from init/beforeRequest hooks are re-thrown as-is.)\n * Use this method instead of `throwHttpErrors: false` or try-catching.\n *\n * Note: responseType defaults to the Fetcher's cfg responseType (`json`, unless overridden).\n */\n async doFetch(opt) {\n let initGeneration = 0;\n if (this.cfg.hooks.init) {\n try {\n await (this.initPromise ??= this.runInitHooks());\n }\n catch (err) {\n // Reset, so init hooks are re-attempted on the next request\n this.resetInit();\n throw err;\n }\n initGeneration = this.initGeneration;\n }\n const req = this.normalizeOptions(opt);\n const { logger } = this.cfg;\n const { init: { method }, } = req;\n const timeoutMillis = req.timeoutSeconds ? req.timeoutSeconds * 1000 : undefined;\n for (const hook of this.cfg.hooks.beforeRequest || []) {\n await hook(req);\n }\n const isFullUrl = req.fullUrl.includes('://');\n const fullUrl = isFullUrl ? new URL(req.fullUrl) : undefined;\n const shortUrl = fullUrl ? this.getShortUrl(fullUrl) : req.fullUrl;\n const signature = [method, shortUrl].join(' ');\n const res = {\n req,\n retryStatus: {\n retryAttempt: 0,\n retryStopped: false,\n retryTimeout: req.retry.timeout,\n },\n signature,\n };\n while (!res.retryStatus.retryStopped) {\n req.started = Date.now();\n res.body = undefined;\n req.init.signal = abortSignalAnyOrUndefined([\n abortSignalTimeoutOrUndefined(timeoutMillis),\n opt.signal,\n ]);\n if (req.logRequest) {\n const { retryAttempt } = res.retryStatus;\n logger.log([' >>', signature, retryAttempt && `try#${retryAttempt + 1}/${req.retry.count + 1}`]\n .filter(Boolean)\n .join(' '));\n if (req.logRequestBody && req.init.body) {\n logger.log(req.init.body); // todo: check if we can _inspect it\n }\n }\n try {\n res.fetchResponse = await (req.overrideFetchFn || Fetcher.callNativeFetch)(req.fullUrl, req.init, req.fetchFn);\n res.ok = res.fetchResponse.ok;\n // important to set it to undefined, otherwise it can keep the previous value (from previous try)\n res.err = undefined;\n }\n catch (err) {\n // For example, CORS error would result in \"TypeError: failed to fetch\" here\n // or, `fetch failed` with the cause of `unexpected redirect`\n // AbortSignal.timeout() throws a DOMException with name \"TimeoutError\"\n res.err = _anyToError(err);\n res.ok = false;\n // important to set it to undefined, otherwise it can keep the previous value (from previous try)\n res.fetchResponse = undefined;\n }\n res.statusFamily = this.getStatusFamily(res);\n res.statusCode = res.fetchResponse?.status;\n if (res.fetchResponse?.ok) {\n try {\n // We are applying a separate Timeout (as long as original Timeout for now) to \"download and parse the body\"\n await pTimeout(async () => await this.onOkResponse(res), {\n // 0 means \"no timeout\" for pTimeout\n timeout: timeoutMillis ?? 0,\n name: 'Fetcher.downloadBody',\n });\n }\n catch (err) {\n // Important to cancel the original request to not keep it running (and occupying resources)\n // UPD: no, we probably don't need to, because \"request\" has already completed, it's just the \"body\" is pending\n // if (err instanceof TimeoutError) {}\n // onOkResponse can still fail, e.g when loading/parsing json, text or doing other response manipulation\n res.err = _anyToError(err);\n res.ok = false;\n await this.onNotOkResponse(res);\n }\n }\n else {\n // !res.ok\n await this.onNotOkResponse(res);\n }\n }\n if (res.err) {\n _errorDataAppend(res.err, req.errorData);\n req.onError?.(res.err);\n for (const hook of this.cfg.hooks.onError || []) {\n await hook(res.err);\n }\n }\n for (const hook of this.cfg.hooks.afterResponse || []) {\n await hook(res);\n }\n if (res.err && (await this.detectStaleInit(opt, res, initGeneration))) {\n // Stale init (e.g expired auth token) was detected and reset - retry the request once\n return await this.doFetch({ ...opt, [reinitAttempted]: true });\n }\n return res;\n }\n /**\n * Consults cfg.hooks.shouldReinit to detect \"stale init\" (e.g expired auth token).\n * If detected - resets the init (so it re-runs) and returns true,\n * telling the caller to retry the request. At most once per request.\n */\n async detectStaleInit(opt, res, initGeneration) {\n const { shouldReinit, init } = this.cfg.hooks;\n if (!shouldReinit || !init)\n return false;\n if (opt[reinitAttempted])\n return false;\n if (!(await shouldReinit(res)))\n return false;\n // Generation check prevents concurrent stale requests from resetting the already-fresh init\n if (initGeneration === this.initGeneration) {\n this.resetInit();\n }\n return true;\n }\n async runInitHooks() {\n for (const hook of this.cfg.hooks.init || []) {\n await hook(this.cfg);\n }\n }\n async onOkResponse(res) {\n const { req } = res;\n const { responseType } = res.req;\n // This function is subject to a separate timeout to \"download and parse the data\"\n if (responseType === 'json') {\n if (res.fetchResponse.body) {\n const text = await res.fetchResponse.text();\n if (text) {\n res.body = text;\n res.body = _jsonParse(text, req.jsonReviver);\n // Error while parsing json can happen - it'll be handled upstream\n }\n else {\n // Body had a '' (empty string)\n res.body = {};\n }\n }\n else {\n // if no body: set responseBody as {}\n // do not throw a \"cannot parse null as Json\" error\n res.body = {};\n }\n }\n else if (responseType === 'text') {\n res.body = res.fetchResponse.body ? await res.fetchResponse.text() : '';\n }\n else if (responseType === 'arrayBuffer') {\n res.body = res.fetchResponse.body ? await res.fetchResponse.arrayBuffer() : new ArrayBuffer(0);\n }\n else if (responseType === 'bytes') {\n // Not using fetchResponse.bytes(), as it's unavailable in older browsers (e.g Safari <18.4)\n res.body = res.fetchResponse.body\n ? new Uint8Array(await res.fetchResponse.arrayBuffer())\n : new Uint8Array();\n }\n else if (responseType === 'blob') {\n res.body = res.fetchResponse.body ? await res.fetchResponse.blob() : new Blob();\n }\n else if (responseType === 'void') {\n // Cancel the body (without downloading it), to free the underlying connection for reuse\n await res.fetchResponse.body?.cancel();\n }\n else if (responseType === 'readableStream') {\n res.body = res.fetchResponse.body;\n if (res.body === null) {\n // Error is to be handled upstream\n throw new Error('fetchResponse.body is null');\n }\n }\n res.retryStatus.retryStopped = true;\n if (req.logResponse) {\n const { retryAttempt } = res.retryStatus;\n const { logger } = this.cfg;\n logger.log([\n ' <<',\n res.fetchResponse.status,\n res.signature,\n retryAttempt && `try#${retryAttempt + 1}/${req.retry.count + 1}`,\n _since(res.req.started),\n ]\n .filter(Boolean)\n .join(' '));\n if (req.logResponseBody && res.body !== undefined) {\n logger.log(res.body);\n }\n }\n }\n /**\n * This method exists to be able to easily mock it.\n * It is static, so mocking applies to ALL instances (even future ones) of Fetcher at once.\n */\n static async callNativeFetch(url, init, fetchFn) {\n return await (fetchFn || globalThis.fetch)(url, init);\n }\n async onNotOkResponse(res) {\n let cause;\n // Try to fetch body and attach to res.body\n // (but don't fail if it doesn't work)\n if (!res.body && res.fetchResponse) {\n try {\n res.body = _jsonParseIfPossible(await res.fetchResponse.text());\n }\n catch {\n // ignore body fetching/parsing errors at this point\n }\n }\n if (res.err) {\n // This is only possible on JSON.parse error, or CORS error,\n // or `unexpected redirect`\n // This check should go first, to avoid calling .text() twice (which will fail)\n cause = _errorLikeToErrorObject(res.err);\n }\n else if (res.body) {\n cause = _anyToErrorObject(res.body);\n }\n else {\n cause = {\n name: 'Error',\n message: 'Fetch failed',\n data: {},\n };\n }\n let responseStatusCode = res.fetchResponse?.status || 0;\n if (res.statusFamily === 2) {\n // important to reset responseStatusCode to 0 in this case, as status 2xx can be misleading\n res.statusFamily = undefined;\n res.statusCode = undefined;\n responseStatusCode = 0;\n }\n const message = [res.statusCode, res.signature].filter(Boolean).join(' ');\n res.err = new HttpRequestError(message, _filterNullishValues({\n response: res.fetchResponse,\n responseStatusCode,\n // These properties are provided to be used in e.g custom Sentry error grouping\n // Actually, disabled now, to avoid unnecessary error printing when both msg and data are printed\n // Enabled, cause `data` is not printed by default when error is HttpError\n // method: req.method,\n // tryCount: req.tryCount,\n requestUrl: res.req.fullUrl,\n requestBaseUrl: this.cfg.baseUrl || undefined,\n requestMethod: res.req.init.method,\n requestSignature: res.signature,\n requestName: res.req.requestName,\n fetcherName: this.cfg.name,\n requestDuration: Date.now() - res.req.started,\n }), {\n cause,\n });\n await this.processRetry(res);\n }\n async processRetry(res) {\n const { retryStatus } = res;\n if (!this.shouldRetry(res)) {\n retryStatus.retryStopped = true;\n }\n for (const hook of this.cfg.hooks.beforeRetry || []) {\n await hook(res);\n }\n const { count, timeoutMultiplier, timeoutMax } = res.req.retry;\n if (retryStatus.retryAttempt >= count) {\n retryStatus.retryStopped = true;\n }\n // We don't log \"last error\", because it will be thrown and logged by consumer,\n // but we should log all previous errors, otherwise they are lost.\n // Here is the right place where we know it's not the \"last error\".\n // lastError = retryStatus.retryStopped\n // We need to log the response \"anyway\" if logResponse is true\n if (res.err && (!retryStatus.retryStopped || res.req.logResponse)) {\n this.cfg.logger.error([\n ' <<',\n res.fetchResponse?.status || 0,\n res.signature,\n count &&\n (retryStatus.retryAttempt || !retryStatus.retryStopped) &&\n `try#${retryStatus.retryAttempt + 1}/${count + 1}`,\n _since(res.req.started),\n ]\n .filter(Boolean)\n .join(' ') + '\\n', \n // We're stringifying the error here, otherwise Sentry shows it as [object Object]\n _stringify(res.err.cause || res.err));\n }\n if (retryStatus.retryStopped)\n return;\n retryStatus.retryAttempt++;\n const timeout = this.getRetryTimeout(res);\n if (timeout === null) {\n this.cfg.logger.warn(`${res.signature} server-indicated delay exceeds maxRetryAfter, will not retry`);\n retryStatus.retryStopped = true;\n return;\n }\n // Increase the timeout for the next possible retry\n retryStatus.retryTimeout = _clamp(retryStatus.retryTimeout * timeoutMultiplier, 0, timeoutMax);\n if (res.req.debug) {\n this.cfg.logger.log(` .. ${res.signature} waiting ${_ms(timeout)}`);\n }\n await pDelaySignal(timeout, res.req.signal);\n if (res.req.signal?.aborted) {\n // Aborted while waiting for the retry - stop, without issuing another request\n retryStatus.retryStopped = true;\n }\n }\n /**\n * Returns the delay before the next retry attempt.\n * Returns null if the server-indicated delay exceeds `retry.maxRetryAfter`,\n * meaning the retry should not be attempted at all.\n */\n getRetryTimeout(res) {\n let timeout = 0;\n // Handling http 429 with specific retry headers\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After\n if (res.fetchResponse && [429, 503].includes(res.fetchResponse.status)) {\n const retryAfterStr = res.fetchResponse.headers.get('retry-after') ??\n res.fetchResponse.headers.get('x-ratelimit-reset');\n if (retryAfterStr) {\n const retryAfterNumber = Number(retryAfterStr);\n if (retryAfterNumber) {\n if (retryAfterNumber > 10 ** 9) {\n // Value is too large to be a \"seconds from now\" delay,\n // treat it as a UnixTimestamp instead (e.g GitHub sends `x-ratelimit-reset` like that)\n timeout = retryAfterNumber * 1000 - Date.now();\n }\n else {\n timeout = retryAfterNumber * 1000;\n }\n }\n else {\n const date = new Date(retryAfterStr);\n if (!Number.isNaN(date.getTime())) {\n timeout = date.getTime() - Date.now();\n }\n }\n this.cfg.logger.log(`retry-after: ${retryAfterStr}`);\n if (!timeout) {\n this.cfg.logger.warn('retry-after could not be parsed');\n }\n }\n }\n if (timeout) {\n if (timeout > res.req.retry.maxRetryAfter) {\n return null;\n }\n // Server-indicated delay is honored as-is (a negative value means \"retry now\")\n return Math.max(0, timeout);\n }\n const noise = Math.random() * 500;\n return res.retryStatus.retryTimeout + noise;\n }\n /**\n * Default is yes,\n * unless there's reason not to (e.g method is POST).\n *\n * statusCode of 0 (or absense of it) will BE retried.\n */\n shouldRetry(res) {\n // Don't retry if the input AbortSignal was aborted\n if (res.req.signal?.aborted)\n return false;\n const { retryPost, retry3xx, retry4xx, retry5xx } = res.req;\n const { method } = res.req.init;\n if (method === 'POST' && !retryPost)\n return false;\n const { statusFamily } = res;\n const statusCode = res.fetchResponse?.status || 0;\n if (statusFamily === 5 && !retry5xx)\n return false;\n if ([408, 429].includes(statusCode)) {\n // these codes are always retried\n return true;\n }\n if (statusFamily === 4 && !retry4xx)\n return false;\n if (statusFamily === 3 && !retry3xx)\n return false;\n // should not retry on `unexpected redirect` in error.cause.cause\n if (res.err?.cause?.cause?.message?.includes('unexpected redirect')) {\n return false;\n }\n return true; // default is true\n }\n getStatusFamily(res) {\n const status = res.fetchResponse?.status;\n if (!status)\n return;\n if (status >= 500)\n return 5;\n if (status >= 400)\n return 4;\n if (status >= 300)\n return 3;\n if (status >= 200)\n return 2;\n if (status >= 100)\n return 1;\n }\n /**\n * Returns url without baseUrl and before ?queryString\n */\n getShortUrl(url) {\n const { baseUrl } = this.cfg;\n if (url.password) {\n url = new URL(url.toString()); // prevent original url mutation\n url.password = '[redacted]';\n }\n let shortUrl = url.toString();\n if (!this.cfg.logWithSearchParams) {\n shortUrl = shortUrl.split('?')[0];\n }\n if (!this.cfg.logWithBaseUrl && baseUrl && shortUrl.startsWith(baseUrl)) {\n shortUrl = shortUrl.slice(baseUrl.length);\n }\n return shortUrl;\n }\n normalizeCfg(cfg) {\n const { debug = false, logger = console } = cfg;\n if (cfg.baseUrl?.endsWith('/')) {\n logger.warn(`Fetcher: baseUrl should not end with slash: ${cfg.baseUrl}`);\n cfg.baseUrl = cfg.baseUrl.slice(0, cfg.baseUrl.length - 1);\n }\n const norm = _merge({\n baseUrl: '',\n name: this.getFetcherName(cfg),\n inputUrl: '',\n responseType: 'json',\n searchParams: {},\n timeoutSeconds: 30,\n retryPost: false,\n retry3xx: false,\n retry4xx: false,\n retry5xx: true,\n logger,\n debug,\n logRequest: debug,\n logRequestBody: debug,\n logResponse: debug,\n logResponseBody: debug,\n logWithBaseUrl: isServerSide(),\n logWithSearchParams: true,\n retry: { ...defaultRetryOptions },\n init: {\n method: cfg.method || 'GET',\n headers: _filterNullishValues({\n 'user-agent': Fetcher.userAgent,\n ...cfg.headers,\n }),\n credentials: cfg.credentials,\n redirect: cfg.redirect,\n dispatcher: cfg.dispatcher,\n keepalive: cfg.keepalive,\n },\n hooks: {},\n throwHttpErrors: true,\n errorData: {},\n }, _omit(cfg, ['method', 'credentials', 'headers', 'redirect', 'logger', 'name', 'keepalive']));\n norm.init.headers = _mapKeys(norm.init.headers, k => k.toLowerCase());\n return norm;\n }\n getFetcherName(cfg) {\n let { name } = cfg;\n if (!name && cfg.baseUrl) {\n // derive FetcherName from baseUrl\n const url = _toUrlOrNull(cfg.baseUrl);\n if (url) {\n name = url.hostname;\n }\n }\n return name;\n }\n normalizeOptions(opt) {\n const req = {\n ..._pick(this.cfg, [\n 'timeoutSeconds',\n 'retryPost',\n 'retry3xx',\n 'retry4xx',\n 'retry5xx',\n 'responseType',\n 'jsonReviver',\n 'logRequest',\n 'logRequestBody',\n 'logResponse',\n 'logResponseBody',\n 'debug',\n 'throwHttpErrors',\n 'errorData',\n 'fetchFn',\n 'overrideFetchFn',\n ]),\n started: Date.now(),\n ..._omit(opt, ['method', 'headers', 'credentials']),\n inputUrl: opt.url || '',\n fullUrl: opt.url || '',\n retry: {\n ...this.cfg.retry,\n ..._filterUndefinedValues(opt.retry || {}),\n },\n init: _merge({\n ...this.cfg.init,\n headers: {\n ...this.cfg.init.headers, // this avoids mutation\n 'user-agent': Fetcher.userAgent, // re-load it here, to support setting it globally post-fetcher-creation\n },\n method: opt.method || this.cfg.init.method,\n credentials: opt.credentials || this.cfg.init.credentials,\n redirect: opt.redirect || this.cfg.init.redirect || 'follow',\n keepalive: opt.keepalive ?? this.cfg.init.keepalive,\n }, {\n headers: _mapKeys(opt.headers || {}, k => k.toLowerCase()),\n }),\n };\n // Because all header values are stringified, so `a: undefined` becomes `undefined` as a string\n _filterNullishValues(req.init.headers, { mutate: true });\n // setup url\n const baseUrl = opt.baseUrl || this.cfg.baseUrl;\n if (baseUrl) {\n let { inputUrl } = req;\n if (inputUrl.startsWith('/')) {\n this.cfg.logger.warn('Fetcher: url should not start with / when baseUrl is specified');\n inputUrl = inputUrl.slice(1);\n }\n req.fullUrl = `${baseUrl}/${inputUrl}`;\n }\n const searchParams = _filterUndefinedValues({\n ...this.cfg.searchParams,\n ...opt.searchParams,\n });\n if (Object.keys(searchParams).length) {\n const qs = new URLSearchParams(searchParams).toString();\n req.fullUrl += (req.fullUrl.includes('?') ? '&' : '?') + qs;\n }\n // setup request body\n // Unless it's a well-defined input type (json, text) - content-type is set automatically by the native fetch\n // Explicitly passed `content-type` header always wins\n if (opt.json !== undefined) {\n req.init.body = JSON.stringify(opt.json);\n req.init.headers['content-type'] ||= 'application/json';\n }\n else if (opt.text !== undefined) {\n req.init.body = opt.text;\n req.init.headers['content-type'] ||= 'text/plain';\n }\n else if (opt.form) {\n if (opt.form instanceof URLSearchParams || opt.form instanceof FormData) {\n req.init.body = opt.form;\n }\n else {\n req.init.body = new URLSearchParams(opt.form);\n req.init.headers['content-type'] ||= 'application/x-www-form-urlencoded';\n }\n }\n else if (opt.body !== undefined) {\n req.init.body = opt.body;\n }\n // Unless `accept` header was already set - set it based on responseType\n req.init.headers['accept'] ||= acceptByResponseType[req.responseType];\n return req;\n }\n}\nexport function getFetcher(cfg = {}) {\n return Fetcher.create(cfg);\n}\n// Marks FetcherOptions of a request that was already retried due to a reinit,\n// to guarantee at most one reinit per request\nconst reinitAttempted = Symbol('reinitAttempted');\nconst acceptByResponseType = {\n text: 'text/plain',\n json: 'application/json',\n void: '*/*',\n readableStream: 'application/octet-stream',\n arrayBuffer: 'application/octet-stream',\n bytes: 'application/octet-stream',\n blob: 'application/octet-stream',\n};\nconst defaultRetryOptions = {\n count: 2,\n timeout: 1000,\n timeoutMax: 30_000,\n timeoutMultiplier: 2,\n maxRetryAfter: 600_000, // 10 minutes\n};\n","// Vendored from https://github.com/ai/nanoid/blob/main/index.browser.js\n// All credit to nanoid authors: https://github.com/ai/nanoid\n// Reason for vendoring: (still) cannot import esm, and Nanoid went ESM-only since 4.0\n/// <reference lib=\"dom\" preserve=\"true\" />\n// \"0-9a-zA-Z-_\", same as base64url alphabet\nconst urlAlphabet = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict';\n// oxlint-disable no-bitwise -- NanoID uses bit operations to build compact IDs\nexport function nanoidBrowser(length = 21) {\n let id = '';\n const bytes = globalThis.crypto.getRandomValues(new Uint8Array(length));\n while (length--) {\n // Using the bitwise AND operator to \"cap\" the value of\n // the random byte from 255 to 63, in that way we can make sure\n // that the value will be a valid index for the \"chars\" string.\n id += urlAlphabet[bytes[length] & 63];\n }\n return id;\n}\nconst defaultRandomFunction = (bytes) => globalThis.crypto.getRandomValues(new Uint8Array(bytes));\nexport function nanoidBrowserCustomAlphabet(alphabet, length = 21) {\n return customRandom(alphabet, length, defaultRandomFunction);\n}\nfunction customRandom(alphabet, defaultSize, getRandom) {\n // First, a bitmask is necessary to generate the ID. The bitmask makes bytes\n // values closer to the alphabet size. The bitmask calculates the closest\n // `2^31 - 1` number, which exceeds the alphabet size.\n // For example, the bitmask for the alphabet size 30 is 31 (00011111).\n // `Math.clz32` is not used, because it is not available in browsers.\n const mask = (2 << Math.log2(alphabet.length - 1)) - 1;\n // Though, the bitmask solution is not perfect since the bytes exceeding\n // the alphabet size are refused. Therefore, to reliably generate the ID,\n // the random bytes redundancy has to be satisfied.\n // Note: every hardware random generator call is performance expensive,\n // because the system call for entropy collection takes a lot of time.\n // So, to avoid additional system calls, extra bytes are requested in advance.\n // Next, a step determines how many random bytes to generate.\n // The number of random bytes gets decided upon the ID size, mask,\n // alphabet size, and magic number 1.6 (using 1.6 peaks at performance\n // according to benchmarks).\n // `-~f => Math.ceil(f)` if f is a float\n // `-~i => i + 1` if i is an integer\n const step = -~((1.6 * mask * defaultSize) / alphabet.length);\n return (size = defaultSize) => {\n let id = '';\n while (true) {\n const bytes = getRandom(step);\n // A compact alternative for `for (var i = 0; i < step; i++)`.\n let j = step;\n while (j--) {\n // Adding `|| ''` refuses a random byte that exceeds the alphabet size.\n id += alphabet[bytes[j] & mask] || '';\n if (id.length === size)\n return id;\n }\n }\n };\n}\n","import { _isEmptyObject, isServerSide } from '@naturalcycles/js-lib'\nimport type { FirstTouchUtms, SetOnceUserProperties } from '@naturalcycles/js-lib/analytics'\nimport {\n CANONICAL_UTM_PARAMS,\n ANALYTICS_IDENTIFY_EVENT_NAME,\n truncateAnalyticsProperty,\n} from '@naturalcycles/js-lib/analytics'\nimport { _mapToObject } from '@naturalcycles/js-lib/array'\nimport { _errorDataAppend, AppError } from '@naturalcycles/js-lib/error'\nimport type { ErrorData } from '@naturalcycles/js-lib/error'\nimport { getFetcher } from '@naturalcycles/js-lib/http'\nimport type { Fetcher } from '@naturalcycles/js-lib/http'\nimport type { CommonLogger } from '@naturalcycles/js-lib/log'\nimport { nanoidBrowser, nanoidBrowserCustomAlphabet } from '@naturalcycles/js-lib/nanoid'\nimport { _filterNullishValues, _filterObject } from '@naturalcycles/js-lib/object'\nimport { _safeJsonStringify } from '@naturalcycles/js-lib/string'\nimport { _noop } from '@naturalcycles/js-lib/types'\nimport type {\n AnyObject,\n NumberOfMilliseconds,\n PositiveInteger,\n UnixTimestampMillis,\n} from '@naturalcycles/js-lib/types'\nimport type {\n AnalyticsBatchInput,\n AnalyticsClientEvent,\n AnalyticsEventListener,\n ClientId,\n FirstTouchInput,\n MixpanelDistinctId,\n} from '@naturalcycles/shared'\n\nexport type { AnalyticsEventListener } from '@naturalcycles/shared'\n\n/**\n * Browsers drop a cookie over 4096 bytes, and the identity goes with it. The 3800 budget also\n * has to cover the `; expires=`, `; path=`, `; domain=` and `; secure` attributes that setCookie\n * appends (roughly 75 bytes), which cookieLength below does not measure. Keep that in mind\n * before raising this toward 4096.\n */\nconst MAX_COOKIE_LENGTH = 3800\n\nconst UTM_QUERY_PARAM_PREFIX = 'utm_'\nconst CLICK_QUERY_PARAMS = [\n 'dclid',\n 'fbclid',\n 'gclid',\n 'ko_click_id',\n 'li_fat_id',\n 'msclkid',\n 'sccid',\n 'ttclid',\n 'twclid',\n 'wbraid',\n]\n\nconst generateEventId = nanoidBrowserCustomAlphabet(\n '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',\n 16,\n)\n\n/**\n * Self-hosted analytics event client.\n *\n * Captures client-side analytics events, adds browser page properties, batches them, retries\n * transient failures and delivers them to our own Backend via a single REST endpoint.\n */\nexport class AnalyticsClient implements AnalyticsClientApi {\n constructor(cfg: AnalyticsClientCfg) {\n const localStorageKeyPrefix = cfg.localStorageKeyPrefix || 'nca'\n this.cfg = {\n onError: _noop,\n firstTouchUrl: '',\n isEnabled: () => true,\n getCommonProps: () => ({}),\n flushInterval: 5000,\n maxBatchSize: 50,\n maxBatchBytes: 60_000,\n maxQueueSize: 1000,\n maxRetryBackoff: 600_000,\n requestTimeout: 30_000,\n persistQueue: true,\n maxPersistedAge: 24 * 3_600_000,\n localStorageKeyPrefix,\n logger: console,\n debug: false,\n ...cfg,\n }\n this.identity = new AnalyticsIdentity(this.cfg.identity)\n // Unlike events, we only fire firstTouch once per pageload, so\n // we care about and can tolerate retries\n this.firstTouchFetcher = getFetcher({\n logger: this.cfg.logger,\n retryPost: true,\n timeoutSeconds: 10,\n })\n if (isServerSide()) return\n this.restoreOrphanedQueues()\n globalThis.addEventListener('pagehide', this.handlePageHide)\n document.addEventListener('visibilitychange', this.handleVisibilityChange)\n }\n\n private cfg: Required<AnalyticsClientCfg>\n private firstTouchFetcher: Fetcher\n /**\n * The built-in identity, constructed from `cfg.identity`. Also readable by application code,\n * e.g to feed the distinctId or acquisition props to other integrations.\n */\n readonly identity: AnalyticsIdentity\n /**\n * Random id of this AnalyticsClient instance (in practice - of this tab/pageload),\n * used to namespace the localStorage queue snapshot.\n */\n private tabId = nanoidBrowser(10)\n private queue: AnalyticsClientEvent[] = []\n private flushTimer?: NodeJS.Timeout\n private isFlushing = false\n private consecutiveFailures = 0\n /**\n * Requested by the server via `Retry-After` header (already converted to ms).\n */\n private retryAfter: NumberOfMilliseconds = 0\n private hasUpdatedAcquisitionProps = false\n private readonly eventListeners = new Set<AnalyticsEventListener>()\n\n private handlePageHide = (): void => {\n this.flushNow()\n }\n\n private handleVisibilityChange = (): void => {\n // Fires on mobile when the tab is backgrounded (where pagehide may never fire) -\n // the last reliable moment to hand events over to the browser\n if (document.visibilityState === 'hidden') this.flushNow()\n }\n\n /**\n * Optional eager bootstrapping, to call once at app boot: registers the acquisition props\n * into the identity entry while the landing url/referrer are still current - the moment\n * an SPA navigation can strip the utm params off the url before the first event fires. Gated by cfg.isEnabled, like the events themselves.\n * Without it the registration happens lazily before the first enabled event.\n */\n init(): void {\n if (!this.canTrack()) return\n this.ensureAcquisitionProps()\n this.replayFromStub()\n }\n\n track(name: string, props?: AnyObject): void {\n const event = this.enqueue(name, props)\n if (event) this.notifyEventListeners(event)\n }\n\n /**\n * Replays the calls a stub recorded on the page before this client loaded, under the timestamps\n * they were made at. Tracked in order, so calls made before the stub's `identify()` keep the\n * anonymous identity.\n */\n private replayFromStub(): void {\n const stub = globalThis.analyticsClient\n if (!stub || !('q' in stub)) return\n\n for (const call of stub.q) {\n try {\n if (call.method === 'identify') {\n this.identify(call.args[0])\n } else {\n // Not track(), so the call keeps the timestamp it was originally made at\n const event = this.enqueue(call.args[0], call.args[1], call.ts)\n if (event) this.notifyEventListeners(event)\n }\n } catch (err) {\n this.cfg.logger.warn('[analytics] could not replay a stubbed call', err)\n }\n }\n stub.q.length = 0\n }\n\n /**\n * Registers a listener called synchronously for every delivered event, past the gates in\n * `track()`. Returns an unsubscribe function. Listeners receive a snapshot and cannot affect\n * delivery, and a throwing listener never breaks tracking.\n */\n onEvent(listener: AnalyticsEventListener): () => void {\n this.eventListeners.add(listener)\n return () => this.eventListeners.delete(listener)\n }\n\n private notifyEventListeners(event: AnalyticsClientEvent): void {\n if (!this.eventListeners.size) return\n const distinctId = event.userId || this.identity.getDistinctId()\n // Snapshot so a listener cannot mutate the event already queued for delivery. Shallow because\n // props may hold circular references (see track()) that a deep JSON copy would throw on.\n const snapshot: AnalyticsClientEvent = { ...event, props: { ...event.props } }\n for (const listener of this.eventListeners) {\n try {\n listener(snapshot, distinctId)\n } catch (err) {\n this.cfg.logger.warn('[analytics] onEvent listener threw', err)\n }\n }\n }\n\n /**\n * Sets the distinctId going forward (persisted), e.g after a successful signup.\n * Pending events are kept, each under the identity it was tracked with.\n */\n identify(userId: string): void {\n if (!this.canTrack()) return\n const previousDistinctId = this.identity.getDistinctId()\n this.identity.identify(userId)\n const distinctId = userId as MixpanelDistinctId\n if (previousDistinctId === distinctId) return\n this.enqueue(ANALYTICS_IDENTIFY_EVENT_NAME, { anon_distinct_id: previousDistinctId })\n void this.flush()\n // The destination merges the event streams of the two ids, but not their profiles\n void this.sendFirstTouch()\n }\n\n private enqueue(\n name: string,\n props?: AnyObject,\n ts?: UnixTimestampMillis,\n ): AnalyticsClientEvent | undefined {\n if (!this.canTrack()) return\n this.ensureAcquisitionProps()\n\n // Deliberately no client-side normalization: the server owns length limits, while\n // circular references are handled at serialization time by _safeJsonStringify.\n const event: AnalyticsClientEvent = {\n id: generateEventId(),\n name,\n ts: ts || (Date.now() as UnixTimestampMillis),\n props: {\n ...this.getDefaultProps(),\n ...this.cfg.getCommonProps(),\n ...props,\n },\n userId: this.identity.getDistinctId(),\n }\n\n if (this.cfg.debug) this.cfg.logger.log(`[analytics] ${event.name}`, event.props)\n this.queue.push(event)\n if (this.queue.length > this.cfg.maxQueueSize) {\n this.queue.shift()\n this.cfg.logger.warn('[analytics] queue overflow, dropped the oldest event')\n }\n this.persistQueue()\n if (this.queue.length >= this.cfg.maxBatchSize && !this.consecutiveFailures) {\n void this.flush()\n } else {\n this.scheduleFlush()\n }\n return event\n }\n\n private canTrack(): boolean {\n return !isServerSide() && this.cfg.isEnabled()\n }\n\n /**\n * Clears pending events and the persisted identity entry, including its acquisition\n * properties. A new identity is generated on next use.\n */\n reset(): void {\n this.clearPendingEvents()\n this.identity.reset()\n // The entry was wiped - re-register the acquisition props on the next tracked event\n this.hasUpdatedAcquisitionProps = false\n }\n\n /**\n * Drains the queue, one batch per request.\n * Called automatically (flush interval / batch size / pagehide) - public for manual flushing.\n */\n async flush(): Promise<void> {\n if (isServerSide() || this.isFlushing) return\n this.isFlushing = true\n this.clearFlushTimer()\n try {\n // The outer loop picks up events tracked while a batch was in-flight\n while (this.queue.length) {\n for (const batch of this.prepareBatches()) {\n const result = await this.sendBatch(batch)\n if (result === 'retry') {\n this.consecutiveFailures++\n this.scheduleFlush()\n return\n }\n // 'ok' or 'drop' - the batch is done either way\n this.removeFromQueue(batch.events)\n this.consecutiveFailures = 0\n this.retryAfter = 0\n }\n }\n } finally {\n this.isFlushing = false\n }\n }\n\n /**\n * Immediate flush: hands queued events over to the browser via keepalive fetch.\n * Runs automatically on pagehide / visibilitychange:hidden. Public so that app code can\n * hand events over right away: events tracked in its own page-lifecycle handlers (which\n * run after this client's own), and events tracked right before a navigation (e.g a\n * cta click) - unload-time delivery is best-effort and can be lost, while a\n * keepalive request from a still-alive page survives the navigation.\n * The persisted queue remains untouched because keepalive requests cannot reliably process\n * their response; a later regular flush re-sends and deduplicates the events.\n */\n flushNow(): void {\n if (!this.queue.length) return\n this.clearFlushTimer()\n for (const batch of this.prepareBatches()) {\n void this.postBatch(batch, true).catch(err => {\n this.cfg.logger.warn('[analytics] lifecycle batch failed to send; retained for retry', err)\n })\n }\n // visibilitychange can fire without unloading the page, so retain normal retry behavior.\n this.scheduleFlush()\n }\n\n /**\n * Removes listeners and pending timers. Only needed when an instance is discarded\n * (e.g in tests or HMR) - the app-wide singleton never needs it.\n */\n destroy(): void {\n this.clearFlushTimer()\n if (isServerSide()) return\n globalThis.removeEventListener('pagehide', this.handlePageHide)\n document.removeEventListener('visibilitychange', this.handleVisibilityChange)\n }\n\n /**\n * Registers the acquisition props into the identity entry once per pageload, like\n * eagerly from init(), or lazily before the first\n * enabled event. Either path is gated by cfg.isEnabled, so bot/e2e/consent gating\n * applies to the persistence write too.\n */\n private ensureAcquisitionProps(): void {\n if (this.hasUpdatedAcquisitionProps) return\n this.hasUpdatedAcquisitionProps = true\n this.identity.updateAcquisitionProps()\n void this.sendFirstTouch()\n }\n\n /**\n * Posts the first-touch props to their own endpoint, once per pageload. Fire-and-forget:\n * `$set_once` ignores every write after the first, so a lost or repeated call costs nothing.\n */\n private async sendFirstTouch(): Promise<void> {\n const { firstTouchUrl } = this.cfg\n if (!firstTouchUrl) return\n const props = this.identity.getFirstTouchProps()\n if (!props) return\n\n const input: FirstTouchInput = {\n clientId: this.cfg.clientId,\n userId: this.identity.getDistinctId(),\n props,\n }\n const res = await this.firstTouchFetcher.doFetch({\n url: firstTouchUrl,\n method: 'POST',\n text: _safeJsonStringify(input),\n responseType: 'void',\n })\n if (!res.err) return\n try {\n this.cfg.onError(_errorDataAppend(res.err, { firstTouch: true }))\n } catch (err) {\n this.cfg.logger.warn('[analytics] onError hook threw', err)\n }\n }\n\n private getDefaultProps(): AnyObject {\n const referrer = document.referrer\n const url = new URL(globalThis.location.href)\n return {\n ...(referrer && { referrer }),\n current_url: globalThis.location.href,\n screen_height: globalThis.screen.height,\n screen_width: globalThis.screen.width,\n // First-touch initial_referrer and last-touch utm_* come from the identity entry -\n // the single acquisition-props store (cross-subdomain when cookie-persisted)\n ...this.identity.getAcquisitionProps(),\n // Utms of the current url win over the persisted last-touch values\n ...getLastTouchUtms(url),\n // Click ids are read from the current url only, never persisted\n ...getQueryProperties(url, CLICK_QUERY_PARAMS),\n }\n }\n\n private clearPendingEvents(): void {\n this.clearFlushTimer()\n this.queue = []\n this.consecutiveFailures = 0\n this.retryAfter = 0\n this.persistQueue()\n }\n\n private async sendBatch(batch: PreparedBatch): Promise<SendBatchResult> {\n let res: Response\n try {\n // Optional chaining: AbortSignal.timeout is missing in older Safari (<16)\n res = await this.postBatch(batch, false, AbortSignal.timeout?.(this.cfg.requestTimeout))\n } catch (err) {\n // Network error or timeout - eligible for retry\n this.cfg.logger.warn('[analytics] batch failed to send, will retry', err)\n return 'retry'\n }\n if (res.ok) return 'ok'\n if (res.status === 429 || res.status >= 500) {\n const retryAfter = Number(res.headers.get('retry-after'))\n if (retryAfter) this.retryAfter = retryAfter * 1000\n this.cfg.logger.warn(`[analytics] batch rejected with ${res.status}, will retry`)\n return 'retry'\n }\n // Non-retryable 4xx - drop the batch to avoid a poison-pill retry loop\n this.cfg.logger.error(\n `[analytics] batch rejected with ${res.status}, dropping ${batch.events.length} event(s)`,\n )\n try {\n this.cfg.onError(\n new AppError('batch dropped on a non-retryable status', {\n status: res.status,\n eventCount: batch.events.length,\n }),\n )\n } catch (err) {\n this.cfg.logger.warn('[analytics] onError hook threw', err)\n }\n return 'drop'\n }\n\n private async postBatch(\n batch: PreparedBatch,\n isLifecycleFlush: boolean,\n signal?: AbortSignal,\n ): Promise<Response> {\n return fetch(this.cfg.url, {\n method: 'POST',\n // `text/plain` (a CORS-safelisted content-type) avoids a preflight OPTIONS round-trip\n // on every batch and keeps pagehide requests deliverable. The body is still a JSON string.\n headers: { 'content-type': 'text/plain' },\n body: batch.body,\n // Regular flushes use ordinary fetch; keepalive is reserved for page lifecycle delivery.\n ...(isLifecycleFlush && { keepalive: true }),\n signal,\n })\n }\n\n /**\n * Splits the whole queue into request-ready batches of at most maxBatchSize events\n * and maxBatchBytes serialized bytes each.\n */\n private prepareBatches(): PreparedBatch[] {\n const batches: PreparedBatch[] = []\n let current: PendingBatch | undefined\n const userId = this.identity.getDistinctId()\n\n for (const event of this.queue) {\n // _safeJsonStringify (here and wherever events are serialized): circular references in\n // props degrade to '[Circular ~]' markers instead of throwing - tracking must never\n // break the app. Non-circular events take its native JSON.stringify fast path.\n const eventBytes = getUtf8ByteLength(_safeJsonStringify(event))\n const hasReachedCount = current?.events.length === this.cfg.maxBatchSize\n const separatorBytes = current?.events.length ? 1 : 0\n const hasReachedBytes =\n !!current && current.bodyBytes + separatorBytes + eventBytes > this.cfg.maxBatchBytes\n\n if (current && (hasReachedCount || hasReachedBytes)) {\n batches.push(this.finalizeBatch(current))\n current = undefined\n }\n\n current ||= this.createPendingBatch(userId)\n const nextSeparatorBytes = current.events.length ? 1 : 0\n current.events.push(event)\n current.bodyBytes += nextSeparatorBytes + eventBytes\n }\n\n if (current?.events.length) {\n batches.push(this.finalizeBatch(current))\n }\n return batches\n }\n\n private createPendingBatch(userId: MixpanelDistinctId): PendingBatch {\n const sentAt = Date.now() as UnixTimestampMillis\n const emptyBody: AnalyticsBatchInput = {\n sentAt,\n clientId: this.cfg.clientId,\n userId,\n events: [],\n }\n return {\n sentAt,\n userId,\n events: [],\n bodyBytes: getUtf8ByteLength(JSON.stringify(emptyBody)),\n }\n }\n\n private finalizeBatch(batch: PendingBatch): PreparedBatch {\n const input: AnalyticsBatchInput = {\n sentAt: batch.sentAt,\n clientId: this.cfg.clientId,\n userId: batch.userId,\n events: batch.events,\n }\n return {\n events: batch.events,\n body: _safeJsonStringify(input),\n }\n }\n\n /**\n * Adopts queue snapshots persisted by previous pageloads (crashed/killed tabs) and re-sends them.\n * A snapshot of a still-alive tab may be adopted too - the resulting duplicate delivery\n * is deduped by the Backend via event ids.\n */\n private restoreOrphanedQueues(): void {\n if (!this.cfg.persistQueue) return\n try {\n const prefix = `${this.cfg.localStorageKeyPrefix}.q.`\n const keys: string[] = []\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i)\n if (key?.startsWith(prefix)) keys.push(key)\n }\n if (!keys.length) return\n const minTs = Date.now() - this.cfg.maxPersistedAge\n for (const key of keys) {\n try {\n const events: AnalyticsClientEvent[] = JSON.parse(localStorage.getItem(key) || '[]')\n localStorage.removeItem(key)\n this.queue.push(...events.filter(event => event.ts >= minTs))\n } catch {\n // Corrupted snapshot - discard only this key and continue restoring the others.\n localStorage.removeItem(key)\n }\n }\n if (this.queue.length > this.cfg.maxQueueSize) {\n this.queue.splice(0, this.queue.length - this.cfg.maxQueueSize)\n }\n if (this.queue.length) {\n this.persistQueue()\n this.scheduleFlush()\n }\n } catch {\n // localStorage unavailable - pending in-memory events continue normally\n }\n }\n\n private scheduleFlush(): void {\n if (this.flushTimer || isServerSide()) return\n let delayMs = this.cfg.flushInterval\n if (this.consecutiveFailures) {\n delayMs = Math.min(\n this.cfg.flushInterval * 2 ** this.consecutiveFailures,\n this.cfg.maxRetryBackoff,\n )\n delayMs = Math.max(delayMs, this.retryAfter)\n }\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined\n void this.flush()\n }, delayMs)\n }\n\n private clearFlushTimer(): void {\n if (!this.flushTimer) return\n clearTimeout(this.flushTimer)\n this.flushTimer = undefined\n }\n\n private removeFromQueue(batch: AnalyticsClientEvent[]): void {\n const ids = new Set(batch.map(event => event.id))\n this.queue = this.queue.filter(event => !ids.has(event.id))\n this.persistQueue()\n }\n\n private persistQueue(): void {\n if (!this.cfg.persistQueue) return\n try {\n if (this.queue.length) {\n localStorage.setItem(this.queueKey, _safeJsonStringify(this.queue))\n } else {\n localStorage.removeItem(this.queueKey)\n }\n } catch {\n // localStorage unavailable/full - analytics must never break the app\n }\n }\n\n private get queueKey(): string {\n return `${this.cfg.localStorageKeyPrefix}.q.${this.tabId}`\n }\n}\n\n/**\n * Distinct-id generation and persistence:\n *\n * - a new identity is a UUID v4 device id, stored as `$device_id`,\n * with `distinct_id` derived from it by `generateDistinctId`\n * - `identify(userId)` sets `distinct_id` and `user_id`, keeping `$device_id` (same person)\n * - `reset()` clears the whole entry and generates a fresh anonymous identity (unrelated person)\n * - the identity is stored as a JSON object under a single cookie / localStorage name\n *\n * Apps sharing a persistenceName and cookie domain read and write the same identity:\n * whichever writes first, the others adopt it. Properties they keep in the same entry\n * are preserved on write, never interpreted.\n *\n * Storage failures (SSR, blocked cookies/localStorage) degrade to an in-memory session-scoped\n * identity - analytics must never break the app.\n */\nexport class AnalyticsIdentity {\n constructor(cfg: AnalyticsIdentityCfg) {\n this.cfg = {\n onError: _noop,\n cookieDomain: '',\n expireDays: 365,\n secureCookie: false,\n // TODO: make it emit something else by default, like the device id itself\n generateDistinctId: deviceId => `$device:${deviceId}`,\n ...cfg,\n }\n }\n\n private cfg: Required<AnalyticsIdentityCfg>\n /**\n * Fallback identity for when storage is unavailable, and the last-known-good copy\n * if storage becomes unreadable later.\n */\n private memoryEntry: PersistedIdentity = {}\n private hasRefreshedExpiry = false\n\n getDistinctId(): MixpanelDistinctId {\n return this.ensureIdentity().distinct_id\n }\n\n /**\n * The bare (unprefixed) device UUID. Undefined for legacy identities persisted before\n * a device id was stored (their `distinct_id` has no device prefix either).\n */\n getDeviceId(): string | undefined {\n return this.ensureIdentity().$device_id\n }\n\n /**\n * Reads any property of the persisted entry: the acquisition props maintained by\n * updateAcquisitionProps(), or props written by another app sharing the entry.\n */\n getProperty(key: string): unknown {\n return this.loadEntry()[key]\n }\n\n /**\n * The acquisition props that can't be derived from event payloads, and are stored separately.\n */\n getAcquisitionProps(): AnyObject {\n const entry = this.loadEntry()\n const props: AnyObject = _filterObject(entry, k => String(k).startsWith(UTM_QUERY_PARAM_PREFIX))\n const { firstTouch } = entry\n if (firstTouch) {\n props['initial_referrer'] = firstTouch.referrer\n }\n return props\n }\n\n /** The captured first touch, as the props to `$set_once` on the profile. */\n getFirstTouchProps(): SetOnceUserProperties | undefined {\n const { firstTouch } = this.loadEntry()\n if (!firstTouch) return\n\n return { ...firstTouch.utms, initial_referrer: firstTouch.referrer }\n }\n\n /**\n * Sets the identity going forward, e.g after a successful signup/login.\n * `$device_id` is kept, so the destination can merge the pre-identify\n * anonymous events into the same user.\n */\n identify(userId: string): void {\n const entry = this.ensureIdentity()\n // Identities persisted before a device id was stored adopt the previous\n // distinct_id as their device id\n entry.$device_id ||= entry.distinct_id\n entry.user_id = userId as MixpanelDistinctId\n entry.distinct_id = entry.user_id\n this.saveEntry(entry)\n }\n\n /**\n * Clears the whole persisted entry and generates a fresh anonymous identity.\n * Call only when switching to an UNRELATED identity (e.g logout): everything else stored\n * in the entry may belong to the previous user, so it goes too.\n */\n reset(): void {\n const deviceId = crypto.randomUUID()\n this.saveEntry({\n distinct_id: this.cfg.generateDistinctId(deviceId) as MixpanelDistinctId,\n $device_id: deviceId,\n })\n }\n\n /** Collects properties of the user that can be used to attribute traffic. */\n updateAcquisitionProps(): void {\n if (isServerSide()) return\n const entry = this.loadEntry()\n const url = new URL(globalThis.location.href)\n Object.assign(entry, getLastTouchUtms(url))\n entry.firstTouch ||= { referrer: getReferrer() }\n entry.firstTouch.utms ||= getFirstTouchUtms(url)\n this.saveEntry(entry)\n }\n\n private ensureIdentity(): PersistedIdentity & { distinct_id: MixpanelDistinctId } {\n const entry = this.loadEntry()\n if (entry.distinct_id) {\n this.refreshExpiry(entry)\n } else {\n const deviceId = crypto.randomUUID()\n entry.distinct_id = this.cfg.generateDistinctId(deviceId) as MixpanelDistinctId\n entry.$device_id = deviceId\n this.saveEntry(entry)\n }\n return entry as PersistedIdentity & { distinct_id: MixpanelDistinctId }\n }\n\n /**\n * The cookie expiration window slides: expireDays counts from the LAST visit, not the\n * first. Re-saved once per instance (in practice - once per pageload).\n */\n private refreshExpiry(entry: PersistedIdentity): void {\n if (this.hasRefreshedExpiry || this.cfg.persistence !== 'cookie') return\n this.saveEntry(entry)\n }\n\n private loadEntry(): PersistedIdentity {\n try {\n const raw =\n this.cfg.persistence === 'cookie'\n ? getCookie(this.cfg.persistenceKey)\n : localStorage.getItem(this.cfg.persistenceKey)\n if (raw) this.memoryEntry = JSON.parse(raw)\n } catch {\n // Storage unavailable (SSR, blocked) or corrupted JSON - keep the in-memory copy\n }\n return this.memoryEntry\n }\n\n private saveEntry(entry: PersistedIdentity): void {\n this.memoryEntry = entry\n this.hasRefreshedExpiry = true\n // Stays 0 when the failure came before serializing, which distinguishes it from a rejected write\n let bytes = 0\n try {\n const value = JSON.stringify(entry)\n bytes = value.length\n if (this.cfg.persistence === 'cookie') {\n bytes = this.cookieLength(entry)\n if (bytes > MAX_COOKIE_LENGTH) {\n // Writing it would make the browser drop the cookie, and the persisted identity\n // with it. The last good cookie is kept instead, and this entry stays in memory.\n this.reportError(new AnalyticsClientError('cookie exceeded the length limit', { bytes }))\n return\n }\n setCookie(\n this.cfg.persistenceKey,\n value,\n this.cfg.expireDays,\n this.cfg.cookieDomain,\n this.cfg.secureCookie,\n )\n // `document.cookie` swallows a rejected write (cookies disabled, a domain the page is\n // not allowed to set, ITP), so reading it back is the only way to notice. A leftover\n // cookie of the same name can shadow the one just written, so any match counts.\n const persisted = getCookieValues(this.cfg.persistenceKey)\n if (!persisted.includes(value)) {\n this.reportError(\n new AnalyticsClientError('cookie write did not persist', {\n bytes,\n hadPreviousCookie: persisted.length > 0,\n }),\n )\n }\n } else {\n localStorage.setItem(this.cfg.persistenceKey, value)\n }\n } catch (err) {\n // The identity stays session-scoped in memory\n this.reportError(_errorDataAppend(err, { bytes }))\n }\n }\n\n private cookieLength(entry: PersistedIdentity): number {\n return this.cfg.persistenceKey.length + encodeURIComponent(JSON.stringify(entry)).length\n }\n\n private reportError(err: unknown): void {\n try {\n this.cfg.onError(err)\n } catch {\n // A consumer hook must not break identity updates\n }\n }\n}\n\n/** The non-empty canonical `utm_*` params of the given url. */\nfunction getLastTouchUtms(url: URL): AnyObject {\n const props: AnyObject = {}\n for (const param of CANONICAL_UTM_PARAMS) {\n const value = url.searchParams.get(param)\n if (value) props[param] = truncateAnalyticsProperty(value)\n }\n return props\n}\n\n/** The referrer of this pageload, or null when it has none (a direct visit). */\nfunction getReferrer(): string | null {\n return truncateAnalyticsProperty(document.referrer) || null\n}\n\n/** Captures all utms if any are set, otherwise none */\nfunction getFirstTouchUtms(url: URL): FirstTouchUtms | undefined {\n // The absent ones are stored as null, so a later campaign cannot fill in the gaps\n const utms = _mapToObject(CANONICAL_UTM_PARAMS, param => {\n const value = url.searchParams.get(param)\n return [`initial_${param}`, value ? truncateAnalyticsProperty(value) : null]\n })\n\n if (_isEmptyObject(_filterNullishValues(utms))) return\n return utms\n}\n\nfunction getQueryProperties(url: URL, keys: string[]): AnyObject {\n return Object.fromEntries(\n keys.flatMap(key => {\n const value = url.searchParams.get(key)\n return value ? [[key, value]] : []\n }),\n )\n}\n\n// Shared instance: runs once per event per batch preparation (not per property).\n// we measure to stay under the fetch keepalive body quota.\nconst textEncoder = new TextEncoder()\n\nfunction getUtf8ByteLength(value: string): number {\n return textEncoder.encode(value).byteLength\n}\n\n/** The first visible value under this name, or null. */\nexport function getCookie(name: string): string | null {\n if (!name) return null\n return getCookieValues(name)[0] ?? null\n}\n\n/**\n * Every visible value under this name, in `document.cookie` order. A host-only and a\n * parent-domain cookie with the same name and path coexist as separate cookies.\n */\nfunction getCookieValues(name: string): string[] {\n const nameEq = `${name}=`\n const values: string[] = []\n for (let c of document.cookie.split(';')) {\n while (c.startsWith(' ')) c = c.slice(1)\n if (!c.startsWith(nameEq)) continue\n values.push(decodeURIComponent(c.slice(nameEq.length)))\n }\n return values\n}\n\n/**\n * Writes the cookie and returns what was written, which the tests assert on.\n * `domain` is explicit, e.g `.example.com` to share it across subdomains.\n */\nexport function setCookie(\n name: string,\n value: string,\n days: PositiveInteger,\n domain: string,\n isSecure: boolean,\n): string {\n const attrs = [`${name}=${encodeURIComponent(value)}`]\n if (days) {\n attrs.push(`expires=${new Date(Date.now() + days * 24 * 3600 * 1000).toUTCString()}`)\n }\n attrs.push('path=/')\n if (domain) attrs.push(`domain=${domain}`)\n if (isSecure) attrs.push('secure')\n\n const cookie = attrs.join('; ')\n // The Cookie Store API is async and missing in Safari, so this stays synchronous\n // oxlint-disable-next-line unicorn/no-document-cookie\n document.cookie = cookie\n return cookie\n}\n\nexport class AnalyticsClientError extends AppError {\n constructor(message: string, data?: ErrorData) {\n super(message, data, { name: 'AnalyticsClientError' })\n }\n}\n\n/** Listener called for every delivered event. */\n/**\n * A call recorded by an inline stub before the client loaded, e.g\n * `{ method: 'track', args: ['Click', { element: 'cta' }], ts: Date.now() }`.\n */\ndeclare global {\n var analyticsClient: AnalyticsClient | AnalyticsClientStub | undefined\n}\n\n/** The stub a page assigns to `globalThis.analyticsClient` before the client loads. */\nexport interface AnalyticsClientStub extends AnalyticsClientApi {\n q: StubbedCall[]\n}\n\n/** What a page can call, on the loaded client or on a stub standing in for it. */\nexport interface AnalyticsClientApi {\n init: () => void\n track: (name: string, props?: AnyObject) => void\n identify: (userId: string) => void\n onEvent: (listener: AnalyticsEventListener) => () => void\n reset: () => void\n flushNow: () => void\n destroy: () => void\n}\n\nexport type StubbedCall =\n | { method: 'track'; args: [name: string, props?: AnyObject]; ts: UnixTimestampMillis }\n | { method: 'identify'; args: [userId: string]; ts: UnixTimestampMillis }\n\ntype SendBatchResult = 'ok' | 'retry' | 'drop'\n\nexport interface AnalyticsClientCfg {\n /**\n * Full url of the ingestion endpoint, e.g `https://api.example.com/web/e`.\n */\n url: string\n /**\n * Full url of the first-touch endpoint, e.g `https://api.example.com/web/ft`.\n * Omit to not send first-touch profile props at all.\n */\n firstTouchUrl?: string\n clientId: ClientId\n /**\n * Evaluated on every init(), track() and identify() call; return false to gate them all.\n * Use it for bot/e2e/consent gating.\n * Defaults to always-enabled (tracking is still client-side only).\n */\n isEnabled?: () => boolean\n /**\n * Props merged into every event, evaluated at track() time. Per-call props win.\n */\n getCommonProps?: () => AnyObject\n /**\n * Cfg of the built-in identity (see AnalyticsIdentity), constructed by the client and\n * exposed as `client.identity`.\n * Use cookie persistence to share the identity across subdomains.\n */\n identity: AnalyticsIdentityCfg\n /**\n * How often the queue is flushed.\n * Default 5000.\n */\n flushInterval?: NumberOfMilliseconds\n /**\n * Max events per request. The queue also flushes early when it's reached.\n * Default 50.\n */\n maxBatchSize?: PositiveInteger\n /**\n * Max serialized request body size. Default 60_000, leaving headroom below\n * the browser keepalive request limit of 65_536 bytes.\n */\n maxBatchBytes?: PositiveInteger\n /**\n * Max events held in the queue while the endpoint is unreachable.\n * Oldest events are dropped beyond it. Default 1000.\n */\n maxQueueSize?: PositiveInteger\n /**\n * Cap for the exponential retry backoff. Default 10 minutes.\n */\n maxRetryBackoff?: NumberOfMilliseconds\n /**\n * Per-request timeout. Default 30_000.\n */\n requestTimeout?: NumberOfMilliseconds\n /**\n * Persist unsent events in localStorage and restore them on the next page load,\n * so events survive crashes/killed tabs. There is no cross-tab locking - dedupe by\n * event id makes duplicates harmless.\n * Default true.\n */\n persistQueue?: boolean\n /**\n * Max age of persisted events to restore; older ones are discarded. Default 24 hours.\n */\n maxPersistedAge?: NumberOfMilliseconds\n /**\n * localStorage key prefix for the queue snapshots. Default 'nca'.\n */\n localStorageKeyPrefix?: string\n /**\n * Default `console`.\n */\n logger?: CommonLogger\n /**\n * Log every tracked event. Default false.\n */\n debug?: boolean\n /** Called on errors related to tracking events. */\n onError?: (err: unknown) => void\n // TODO: make onEvent a cfg callback? A subscription loses events tracked before it attaches.\n}\n\nexport interface AnalyticsIdentityCfg {\n /**\n * Where the identity is persisted:\n * 'cookie' - shareable across subdomains via `cookieDomain`,\n * 'localStorage' - per-origin, for when cross-(sub)domain sharing is not needed.\n */\n persistence: 'cookie' | 'localStorage'\n /**\n * Cookie name / localStorage key. Apps sharing an identity must use the same one,\n * and must rename in lockstep, otherwise their identities diverge on the next\n * identify()/reset().\n */\n persistenceKey: string\n /**\n * Explicit cookie domain, e.g `.example.com` to share the identity across subdomains.\n * Default '' - a host-only cookie.\n */\n cookieDomain?: string\n /**\n * Cookie lifetime in days, sliding: re-saved on first use of each pageload. Default 365.\n */\n expireDays?: PositiveInteger\n /**\n * Sets the `secure` cookie attribute. Default false.\n */\n secureCookie?: boolean\n /**\n * Builds the distinct id of a new anonymous identity from its generated device id.\n * Apps sharing an identity must use the same one, otherwise their identities diverge.\n */\n generateDistinctId?: (deviceId: string) => string\n /** Called on errors related to persisting the identity. */\n onError?: (err: unknown) => void\n}\n\n/**\n * The persisted entry. Unknown keys are preserved on write.\n */\ninterface PersistedIdentity {\n distinct_id?: MixpanelDistinctId\n /**\n * The bare device UUID that `distinct_id` was derived from.\n */\n $device_id?: string\n /**\n * Set by identify() - the identified (external) user id.\n */\n user_id?: MixpanelDistinctId\n /** The first-touch referrer and utms, each captured once. */\n firstTouch?: FirstTouch\n /**\n * Preserved on write, never interpreted.\n */\n [key: string]: unknown\n}\n\n/** Analytics properties we only persist once and don't set again. */\nexport interface FirstTouch {\n referrer: string | null\n utms?: FirstTouchUtms\n}\n\ninterface PreparedBatch {\n events: AnalyticsClientEvent[]\n body: string\n}\n\ninterface PendingBatch {\n sentAt: UnixTimestampMillis\n userId: MixpanelDistinctId\n events: AnalyticsClientEvent[]\n bodyBytes: number\n}\n"],"x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],"mappings":"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"}
|