superjs-core 0.3.2 → 0.3.4

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/dist/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/core/index.ts
2
9
  function isPlainObject(value) {
3
10
  if (value === null || typeof value !== "object") return false;
@@ -302,7 +309,8 @@ function div(a, b) {
302
309
  }
303
310
  function round(value, precision = 0) {
304
311
  const factor = Math.pow(10, precision);
305
- return Math.round(value * factor) / factor;
312
+ const shifted = Number((value * factor).toPrecision(15));
313
+ return Math.round(shifted) / factor;
306
314
  }
307
315
  function floor(value, precision = 0) {
308
316
  const factor = Math.pow(10, precision);
@@ -444,23 +452,20 @@ function parseDate(input) {
444
452
  }
445
453
  const dmyMatch = trimmed.match(/^(\d{1,2})[\/-](\d{1,2})[\/-](\d{4})$/);
446
454
  if (dmyMatch) {
447
- const d = new Date(
448
- parseInt(dmyMatch[3], 10),
449
- parseInt(dmyMatch[2], 10) - 1,
450
- parseInt(dmyMatch[1], 10)
451
- );
452
- if (!isNaN(d.getTime())) return d;
455
+ const year = parseInt(dmyMatch[3], 10);
456
+ const month = parseInt(dmyMatch[2], 10) - 1;
457
+ const day = parseInt(dmyMatch[1], 10);
458
+ const d = new Date(year, month, day);
459
+ if (!isNaN(d.getTime()) && d.getMonth() === month && d.getDate() === day) return d;
453
460
  }
454
461
  const textMatch = trimmed.match(/^(\d{1,2})\s+([a-zA-Z]+)\s+(\d{4})$/);
455
462
  if (textMatch) {
456
463
  const monthIndex = MONTH_MAP[textMatch[2].toLowerCase()];
457
464
  if (monthIndex !== void 0) {
458
- const d = new Date(
459
- parseInt(textMatch[3], 10),
460
- monthIndex,
461
- parseInt(textMatch[1], 10)
462
- );
463
- if (!isNaN(d.getTime())) return d;
465
+ const year = parseInt(textMatch[3], 10);
466
+ const day = parseInt(textMatch[1], 10);
467
+ const d = new Date(year, monthIndex, day);
468
+ if (!isNaN(d.getTime()) && d.getMonth() === monthIndex && d.getDate() === day) return d;
464
469
  }
465
470
  }
466
471
  const numMatch = trimmed.match(/^-?\d+$/);
@@ -1798,11 +1803,496 @@ async function analyzeUsage(projectPath, packageName) {
1798
1803
  importLocations
1799
1804
  };
1800
1805
  }
1806
+
1807
+ // src/validation/isNIK.ts
1808
+ function isNIK(value) {
1809
+ const digits = value.replace(/\D/g, "");
1810
+ if (digits.length !== 16) return false;
1811
+ const rawDay = Number.parseInt(digits.slice(6, 8), 10);
1812
+ const month = Number.parseInt(digits.slice(8, 10), 10);
1813
+ const year = Number.parseInt(digits.slice(10, 12), 10);
1814
+ if (rawDay < 1 || rawDay > 71) return false;
1815
+ if (month < 1 || month > 12) return false;
1816
+ let day = rawDay;
1817
+ if (day >= 41) day -= 40;
1818
+ const fullYear = year < 70 ? 2e3 + year : 1900 + year;
1819
+ const date = new Date(fullYear, month - 1, day);
1820
+ return date.getFullYear() === fullYear && date.getMonth() === month - 1 && date.getDate() === day;
1821
+ }
1822
+
1823
+ // src/validation/isNPWP.ts
1824
+ function isNPWP(value) {
1825
+ const digits = value.replace(/\D/g, "");
1826
+ if (digits.length !== 15 && digits.length !== 16) return false;
1827
+ const nums = [];
1828
+ for (let i = 0; i < digits.length; i++) {
1829
+ nums.push(Number.parseInt(digits[i], 10));
1830
+ }
1831
+ const checkDigit = nums[nums.length - 1];
1832
+ let sum2 = 0;
1833
+ for (let i = 0; i < nums.length - 1; i++) {
1834
+ sum2 += nums[i] * [3, 7, 1][i % 3];
1835
+ }
1836
+ const computed = (11 - sum2 % 11) % 10;
1837
+ return computed === checkDigit;
1838
+ }
1839
+
1840
+ // src/validation/isPhone.ts
1841
+ var INDONESIAN_PREFIXES = [
1842
+ [11, 19],
1843
+ [21, 29],
1844
+ [51, 59],
1845
+ [77, 79],
1846
+ [95, 99]
1847
+ ];
1848
+ function isValidIndonesianPrefix(prefix) {
1849
+ for (const [min, max] of INDONESIAN_PREFIXES) {
1850
+ if (prefix >= min && prefix <= max) return true;
1851
+ }
1852
+ return false;
1853
+ }
1854
+ function isPhone(value, country = "id") {
1855
+ const digits = value.replace(/\D/g, "");
1856
+ if (country === "any") {
1857
+ return digits.length >= 10 && digits.length <= 15;
1858
+ }
1859
+ if (digits.length < 10) return false;
1860
+ let normalized;
1861
+ if (digits.startsWith("62")) {
1862
+ normalized = digits.slice(2);
1863
+ } else if (digits.startsWith("0")) {
1864
+ normalized = digits.slice(1);
1865
+ } else {
1866
+ normalized = digits;
1867
+ }
1868
+ if (normalized.length < 10 || normalized.length > 13) return false;
1869
+ if (!normalized.startsWith("8")) return false;
1870
+ const prefix = Number.parseInt(normalized.slice(1, 3), 10);
1871
+ return isValidIndonesianPrefix(prefix);
1872
+ }
1873
+
1874
+ // src/validation/isEmail.ts
1875
+ var LOCAL_SPECIAL = "!#$%&'*+/=?^_`{|}~-";
1876
+ function isQuotedLocalPart(local) {
1877
+ if (local.length < 2) return false;
1878
+ let i = 1;
1879
+ while (i < local.length - 1) {
1880
+ const ch = local[i];
1881
+ if (ch === "\\") {
1882
+ i++;
1883
+ if (i >= local.length - 1) return false;
1884
+ } else if (ch === '"') {
1885
+ return false;
1886
+ }
1887
+ i++;
1888
+ }
1889
+ return true;
1890
+ }
1891
+ function isUnquotedLocalPart(local) {
1892
+ if (local.length === 0 || local.startsWith(".") || local.endsWith(".")) return false;
1893
+ for (let i = 0; i < local.length; i++) {
1894
+ const ch = local[i];
1895
+ if (ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "." || LOCAL_SPECIAL.includes(ch)) {
1896
+ continue;
1897
+ }
1898
+ return false;
1899
+ }
1900
+ for (let i = 1; i < local.length; i++) {
1901
+ if (local[i] === "." && local[i - 1] === ".") return false;
1902
+ }
1903
+ return true;
1904
+ }
1905
+ function isValidDomain(domain) {
1906
+ if (domain.length === 0 || domain.startsWith(".") || domain.endsWith(".")) return false;
1907
+ const labels = domain.split(".");
1908
+ if (labels.length < 2) return false;
1909
+ for (const label of labels) {
1910
+ if (label.length === 0 || label.length > 63) return false;
1911
+ if (label.startsWith("-") || label.endsWith("-")) return false;
1912
+ for (let i = 0; i < label.length; i++) {
1913
+ const ch = label[i];
1914
+ if (!(ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "-")) {
1915
+ return false;
1916
+ }
1917
+ }
1918
+ }
1919
+ return true;
1920
+ }
1921
+ function isEmail(value) {
1922
+ if (value.length > 254) return false;
1923
+ const atIndex = value.lastIndexOf("@");
1924
+ if (atIndex < 1 || atIndex === value.length - 1) return false;
1925
+ const localPart = value.slice(0, atIndex);
1926
+ const domainPart = value.slice(atIndex + 1);
1927
+ if (localPart.length > 64) return false;
1928
+ if (domainPart.length > 255) return false;
1929
+ if (localPart.startsWith('"') && localPart.endsWith('"')) {
1930
+ if (!isQuotedLocalPart(localPart)) return false;
1931
+ } else {
1932
+ if (!isUnquotedLocalPart(localPart)) return false;
1933
+ }
1934
+ return isValidDomain(domainPart);
1935
+ }
1936
+
1937
+ // src/validation/isURL.ts
1938
+ var IPV4_OCTET = /^(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
1939
+ function isValidIPv4(hostname) {
1940
+ const octets = hostname.split(".");
1941
+ if (octets.length !== 4) return false;
1942
+ return octets.every((octet) => IPV4_OCTET.test(octet));
1943
+ }
1944
+ function isValidDNSHostname(hostname) {
1945
+ if (hostname.startsWith(".") || hostname.endsWith(".")) return false;
1946
+ const labels = hostname.split(".");
1947
+ if (labels.length < 2) return false;
1948
+ for (const label of labels) {
1949
+ if (label.length === 0 || label.length > 63) return false;
1950
+ if (label.startsWith("-") || label.endsWith("-")) return false;
1951
+ for (let i = 0; i < label.length; i++) {
1952
+ const ch = label[i];
1953
+ if (!(ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "-")) {
1954
+ return false;
1955
+ }
1956
+ }
1957
+ }
1958
+ return true;
1959
+ }
1960
+ function isValidHostname(hostname) {
1961
+ if (hostname.length === 0) return false;
1962
+ if (hostname.startsWith("[") && hostname.endsWith("]")) {
1963
+ return hostname.length > 2;
1964
+ }
1965
+ if (/^\d/.test(hostname) || /\d$/.test(hostname)) {
1966
+ if (isValidIPv4(hostname)) return true;
1967
+ }
1968
+ if (hostname === "localhost") return true;
1969
+ return isValidDNSHostname(hostname);
1970
+ }
1971
+ function isURL(value) {
1972
+ try {
1973
+ const url = new URL(value);
1974
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
1975
+ return false;
1976
+ }
1977
+ return isValidHostname(url.hostname);
1978
+ } catch {
1979
+ return false;
1980
+ }
1981
+ }
1982
+
1983
+ // src/error/createError.ts
1984
+ var defaultStatus = {
1985
+ "BAD_REQUEST": 400,
1986
+ "UNAUTHORIZED": 401,
1987
+ "FORBIDDEN": 403,
1988
+ "NOT_FOUND": 404,
1989
+ "CONFLICT": 409,
1990
+ "VALIDATION_ERROR": 422,
1991
+ "TOO_MANY": 429,
1992
+ "INTERNAL": 500,
1993
+ "BAD_GATEWAY": 502,
1994
+ "UNAVAILABLE": 503
1995
+ };
1996
+ var TypedError = class extends Error {
1997
+ code;
1998
+ status;
1999
+ details;
2000
+ constructor(code, message, options) {
2001
+ super(message, { cause: options?.cause });
2002
+ this.name = "TypedError";
2003
+ this.code = code;
2004
+ this.status = options?.status ?? defaultStatus[code] ?? 500;
2005
+ this.details = options?.details;
2006
+ Object.setPrototypeOf(this, new.target.prototype);
2007
+ }
2008
+ /**
2009
+ * Serialize the error to a plain JSON-safe object.
2010
+ */
2011
+ toJSON() {
2012
+ return {
2013
+ name: this.name,
2014
+ message: this.message,
2015
+ code: this.code,
2016
+ status: this.status,
2017
+ details: this.details,
2018
+ cause: this.cause,
2019
+ ...this.stack ? { stack: this.stack } : {}
2020
+ };
2021
+ }
2022
+ toString() {
2023
+ return `${this.name} [${this.code}]: ${this.message}`;
2024
+ }
2025
+ };
2026
+ function createError(code, message, options) {
2027
+ return new TypedError(code, message, options);
2028
+ }
2029
+ function isTypedError(error) {
2030
+ return error instanceof TypedError;
2031
+ }
2032
+
2033
+ // src/error/MultiError.ts
2034
+ var MultiError = class extends Error {
2035
+ errors;
2036
+ constructor(errors, message) {
2037
+ const joined = errors.map((e) => e.message).join("; ");
2038
+ super(message ?? joined);
2039
+ this.name = "MultiError";
2040
+ this.errors = [...errors];
2041
+ Object.setPrototypeOf(this, new.target.prototype);
2042
+ }
2043
+ /** Number of collected errors. */
2044
+ get length() {
2045
+ return this.errors.length;
2046
+ }
2047
+ /**
2048
+ * Check if any collected error satisfies `predicate`.
2049
+ *
2050
+ * @example
2051
+ * ```ts
2052
+ * if (err.some(e => e.message.includes('timeout'))) { … }
2053
+ * ```
2054
+ */
2055
+ some(predicate) {
2056
+ return this.errors.some(predicate);
2057
+ }
2058
+ /** Array of all error messages. */
2059
+ get messages() {
2060
+ return this.errors.map((e) => e.message);
2061
+ }
2062
+ /**
2063
+ * Serialize to a plain JSON-safe object.
2064
+ */
2065
+ toJSON() {
2066
+ return {
2067
+ name: this.name,
2068
+ message: this.message,
2069
+ errors: this.errors.map((e) => ({
2070
+ name: e.name,
2071
+ message: e.message,
2072
+ ...e.stack ? { stack: e.stack } : {}
2073
+ })),
2074
+ ...this.stack ? { stack: this.stack } : {}
2075
+ };
2076
+ }
2077
+ toString() {
2078
+ return `${this.name}: ${this.message}`;
2079
+ }
2080
+ };
2081
+ function collectErrors(fn) {
2082
+ try {
2083
+ return { result: fn(), errors: [] };
2084
+ } catch (err) {
2085
+ return {
2086
+ errors: [err instanceof Error ? err : new Error(String(err))]
2087
+ };
2088
+ }
2089
+ }
2090
+
2091
+ // src/logger/logger.ts
2092
+ var LEVEL_ORDER = { debug: 0, info: 1, warn: 2, error: 3 };
2093
+ var Logger = class _Logger {
2094
+ _level;
2095
+ _name;
2096
+ _transport;
2097
+ _extraMeta;
2098
+ constructor(options) {
2099
+ this._level = options?.level ?? "info";
2100
+ this._name = options?.name;
2101
+ this._transport = options?.transport ?? consoleTransport;
2102
+ this._extraMeta = /* @__PURE__ */ Object.create(null);
2103
+ }
2104
+ _shouldLog(target) {
2105
+ return LEVEL_ORDER[this._level] <= LEVEL_ORDER[target];
2106
+ }
2107
+ _log(level, message, meta) {
2108
+ if (level !== "error" && !this._shouldLog(level)) return;
2109
+ const merged = /* @__PURE__ */ Object.create(null);
2110
+ for (const key of Object.keys(this._extraMeta)) {
2111
+ merged[key] = this._extraMeta[key];
2112
+ }
2113
+ if (meta !== void 0) {
2114
+ for (const key of Object.keys(meta)) {
2115
+ merged[key] = meta[key];
2116
+ }
2117
+ }
2118
+ const label = this._name !== void 0 ? `[${this._name}] ${message}` : message;
2119
+ const finalMeta = Object.keys(merged).length > 0 ? merged : void 0;
2120
+ this._transport.log(level, label, finalMeta);
2121
+ }
2122
+ /** Log at `debug` level. Only emitted when the current level is `'debug'`. */
2123
+ debug = (message, meta) => this._log("debug", message, meta);
2124
+ /** Log at `info` level. Emitted when level is `'debug'` or `'info'`. */
2125
+ info = (message, meta) => this._log("info", message, meta);
2126
+ /** Log at `warn` level. Emitted when level is `'debug'`, `'info'`, or `'warn'`. */
2127
+ warn = (message, meta) => this._log("warn", message, meta);
2128
+ /** Log at `error` level. Always emitted regardless of current level. */
2129
+ error = (message, meta) => this._log("error", message, meta);
2130
+ /**
2131
+ * Creates a child logger that inherits the parent's level, name, and transport,
2132
+ * but merges `extraMeta` into every log call. Child metadata is shallow-merged
2133
+ * on top of the parent's inherited metadata.
2134
+ *
2135
+ * @param extraMeta - Additional context to include in every log entry.
2136
+ */
2137
+ child(extraMeta) {
2138
+ const child = new _Logger({
2139
+ level: this._level,
2140
+ name: this._name,
2141
+ transport: this._transport
2142
+ });
2143
+ const inherited = /* @__PURE__ */ Object.create(null);
2144
+ for (const key of Object.keys(this._extraMeta)) {
2145
+ inherited[key] = this._extraMeta[key];
2146
+ }
2147
+ for (const key of Object.keys(extraMeta)) {
2148
+ inherited[key] = extraMeta[key];
2149
+ }
2150
+ child._extraMeta = inherited;
2151
+ return child;
2152
+ }
2153
+ /** Updates the minimum log level for this instance. */
2154
+ setLevel(level) {
2155
+ this._level = level;
2156
+ }
2157
+ /** Returns the current minimum log level. */
2158
+ getLevel() {
2159
+ return this._level;
2160
+ }
2161
+ /**
2162
+ * Creates a new named Logger.
2163
+ * Convenience shorthand for `new Logger({ ...options, name })`.
2164
+ *
2165
+ * @param name - The name tag shown in log output.
2166
+ * @param options - Additional configuration.
2167
+ */
2168
+ static create(name, options) {
2169
+ return new _Logger({ ...options, name });
2170
+ }
2171
+ };
2172
+ var consoleTransport = {
2173
+ log(level, message, meta) {
2174
+ const metaStr = meta !== void 0 && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
2175
+ console.log(`[${level.toUpperCase()}] ${message}${metaStr}`);
2176
+ }
2177
+ };
2178
+ var logger = new Logger();
2179
+
2180
+ // src/logger/transports.ts
2181
+ var LEVEL_COLORS = {
2182
+ debug: "\x1B[90m",
2183
+ info: "\x1B[34m",
2184
+ warn: "\x1B[33m",
2185
+ error: "\x1B[31m"
2186
+ };
2187
+ var RESET = "\x1B[0m";
2188
+ function createConsoleTransport(options) {
2189
+ const useColors = options?.colors !== false;
2190
+ const showTimestamp = options?.timestamp ?? false;
2191
+ return {
2192
+ log(level, message, meta) {
2193
+ const parts = [];
2194
+ if (showTimestamp) {
2195
+ parts.push((/* @__PURE__ */ new Date()).toISOString());
2196
+ }
2197
+ if (useColors) {
2198
+ const color = LEVEL_COLORS[level];
2199
+ parts.push(`${color}[${level.toUpperCase()}]${RESET}`);
2200
+ } else {
2201
+ parts.push(`[${level.toUpperCase()}]`);
2202
+ }
2203
+ parts.push(message);
2204
+ if (meta !== void 0 && Object.keys(meta).length > 0) {
2205
+ parts.push(JSON.stringify(meta));
2206
+ }
2207
+ console.log(parts.join(" "));
2208
+ }
2209
+ };
2210
+ }
2211
+ function createJsonTransport(options) {
2212
+ const writeStream = options?.stream ?? (typeof process !== "undefined" && typeof process.stdout !== "undefined" && typeof process.stdout.write === "function" ? process.stdout : void 0);
2213
+ return {
2214
+ log(level, message, meta) {
2215
+ const entry = {
2216
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2217
+ level,
2218
+ message
2219
+ };
2220
+ if (meta !== void 0 && Object.keys(meta).length > 0) {
2221
+ entry.meta = meta;
2222
+ }
2223
+ const line = JSON.stringify(entry);
2224
+ if (writeStream !== void 0) {
2225
+ writeStream.write(line + "\n");
2226
+ } else {
2227
+ console.log(line);
2228
+ }
2229
+ }
2230
+ };
2231
+ }
2232
+ function createFileTransport(filename, _options) {
2233
+ let fs = null;
2234
+ try {
2235
+ if (typeof process !== "undefined" && process.versions?.node) {
2236
+ const m = __require("fs");
2237
+ fs = m;
2238
+ }
2239
+ } catch {
2240
+ }
2241
+ return {
2242
+ log(level, message, meta) {
2243
+ if (fs === null) return;
2244
+ const metaStr = meta !== void 0 && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
2245
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${level.toUpperCase()}] ${message}${metaStr}
2246
+ `;
2247
+ try {
2248
+ fs.appendFileSync(filename, line);
2249
+ } catch {
2250
+ }
2251
+ }
2252
+ };
2253
+ }
2254
+ function createBufferedTransport(transport, options) {
2255
+ const maxSize = options?.maxSize ?? 100;
2256
+ const flushIntervalMs = options?.flushIntervalMs ?? 5e3;
2257
+ const buffer = [];
2258
+ let timer;
2259
+ function flush() {
2260
+ if (timer !== void 0) {
2261
+ clearTimeout(timer);
2262
+ timer = void 0;
2263
+ }
2264
+ for (let i = 0; i < buffer.length; i++) {
2265
+ const entry = buffer[i];
2266
+ transport.log(entry.level, entry.message, entry.meta);
2267
+ }
2268
+ buffer.length = 0;
2269
+ }
2270
+ function scheduleFlush() {
2271
+ if (timer !== void 0 || flushIntervalMs <= 0) return;
2272
+ timer = setTimeout(() => {
2273
+ timer = void 0;
2274
+ flush();
2275
+ }, flushIntervalMs);
2276
+ }
2277
+ return {
2278
+ log(level, message, meta) {
2279
+ buffer.push({ level, message, meta });
2280
+ if (buffer.length >= maxSize) {
2281
+ flush();
2282
+ } else {
2283
+ scheduleFlush();
2284
+ }
2285
+ }
2286
+ };
2287
+ }
1801
2288
  export {
1802
2289
  DivisionByZeroError,
1803
2290
  InvalidDateError,
1804
2291
  KNOWN_CVES,
1805
2292
  KNOWN_MAPPINGS,
2293
+ Logger,
2294
+ MultiError,
2295
+ TypedError,
1806
2296
  add,
1807
2297
  addBusinessDays,
1808
2298
  addDays,
@@ -1825,8 +2315,15 @@ export {
1825
2315
  checksum,
1826
2316
  chunk,
1827
2317
  clamp,
2318
+ collectErrors,
2319
+ consoleTransport,
1828
2320
  constantTimeEqual,
1829
2321
  countOccurrences,
2322
+ createBufferedTransport,
2323
+ createConsoleTransport,
2324
+ createError,
2325
+ createFileTransport,
2326
+ createJsonTransport,
1830
2327
  dateDiff,
1831
2328
  debounce,
1832
2329
  deepClone,
@@ -1864,24 +2361,31 @@ export {
1864
2361
  isBoolean,
1865
2362
  isBusinessDay,
1866
2363
  isDate,
2364
+ isEmail,
1867
2365
  isEmpty,
1868
2366
  isFunction,
1869
2367
  isLeapYear,
1870
2368
  isMap,
2369
+ isNIK,
2370
+ isNPWP,
1871
2371
  isNil,
1872
2372
  isNull,
1873
2373
  isNumber,
1874
2374
  isObject,
2375
+ isPhone,
1875
2376
  isPromise,
1876
2377
  isRegExp,
1877
2378
  isSet,
1878
2379
  isString,
2380
+ isTypedError,
2381
+ isURL,
1879
2382
  isUndefined,
1880
2383
  isWeekend,
1881
2384
  join,
1882
2385
  kebabCase,
1883
2386
  keyBy,
1884
2387
  last,
2388
+ logger,
1885
2389
  memoize,
1886
2390
  mul,
1887
2391
  nanoid,