@andrew_l/toolkit 0.3.5 → 0.3.7

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.mjs CHANGED
@@ -821,7 +821,7 @@ function uniqBy(array, comparator) {
821
821
  });
822
822
  }
823
823
 
824
- class BrowserAssertionError extends Error {
824
+ class AssertionError extends Error {
825
825
  /**
826
826
  * Set to the `actual` argument for methods such as {@link assert.strictEqual()}.
827
827
  */
@@ -852,17 +852,6 @@ class BrowserAssertionError extends Error {
852
852
  }
853
853
  }
854
854
 
855
- let AssertionError = BrowserAssertionError;
856
- if (isNode()) {
857
- try {
858
- const assert = require("node:assert");
859
- if (assert.AssertionError) {
860
- AssertionError = assert.AssertionError;
861
- }
862
- } catch {
863
- }
864
- }
865
-
866
855
  function ok(value, message) {
867
856
  if (!value) {
868
857
  throw toError$1(
@@ -1583,8 +1572,12 @@ class Base64Encoding {
1583
1572
  }
1584
1573
  this.alphabet = alphabet;
1585
1574
  this.padding = options?.padding ?? "=";
1586
- if (this.alphabet.includes(this.padding) || this.padding.length !== 1) {
1587
- throw new Error("Invalid padding");
1575
+ if (this.padding) {
1576
+ ok(
1577
+ !this.alphabet.includes(this.padding),
1578
+ "Padding cannot be a part of alphabet"
1579
+ );
1580
+ ok(this.padding.length === 1, "Padding length must be a 1");
1588
1581
  }
1589
1582
  for (let i = 0; i < alphabet.length; i++) {
1590
1583
  this.decodeMap.set(alphabet[i], i);
@@ -1603,26 +1596,24 @@ class Base64Encoding {
1603
1596
  * ```
1604
1597
  */
1605
1598
  encode(data, options) {
1599
+ const includePadding = options?.includePadding ?? true;
1606
1600
  let result = "";
1607
1601
  let buffer = 0;
1608
1602
  let shift = 0;
1609
- for (let i = 0; i < data.length; i++) {
1610
- buffer = buffer << 8 | data[i];
1603
+ for (const byte of data) {
1604
+ buffer = buffer << 8 | byte;
1611
1605
  shift += 8;
1612
1606
  while (shift >= 6) {
1613
- shift += -6;
1607
+ shift -= 6;
1614
1608
  result += this.alphabet[buffer >> shift & 63];
1615
1609
  }
1616
1610
  }
1617
1611
  if (shift > 0) {
1618
1612
  result += this.alphabet[buffer << 6 - shift & 63];
1619
1613
  }
1620
- const includePadding = options?.includePadding ?? true;
1621
- if (includePadding) {
1614
+ if (includePadding && this.padding) {
1622
1615
  const padCount = (4 - result.length % 4) % 4;
1623
- for (let i = 0; i < padCount; i++) {
1624
- result += "=";
1625
- }
1616
+ result += "=".repeat(padCount);
1626
1617
  }
1627
1618
  return result;
1628
1619
  }
@@ -1641,39 +1632,23 @@ class Base64Encoding {
1641
1632
  */
1642
1633
  decode(data, options) {
1643
1634
  const strict = options?.strict ?? true;
1644
- const chunkCount = Math.ceil(data.length / 4);
1645
1635
  const result = [];
1646
- for (let i = 0; i < chunkCount; i++) {
1647
- let padCount = 0;
1648
- let buffer = 0;
1649
- for (let j = 0; j < 4; j++) {
1650
- const encoded = data[i * 4 + j];
1651
- if (encoded === "=") {
1652
- if (i + 1 !== chunkCount) {
1653
- throw new Error(`Invalid character: ${encoded}`);
1654
- }
1655
- padCount += 1;
1656
- continue;
1657
- }
1658
- if (encoded === void 0) {
1659
- if (strict) {
1660
- throw new Error("Invalid data");
1661
- }
1662
- padCount += 1;
1663
- continue;
1664
- }
1665
- const value = this.decodeMap.get(encoded) ?? null;
1666
- if (value === null) {
1667
- throw new Error(`Invalid character: ${encoded}`);
1668
- }
1669
- buffer += value << 6 * (3 - j);
1670
- }
1671
- result.push(buffer >> 16 & 255);
1672
- if (padCount < 2) {
1673
- result.push(buffer >> 8 & 255);
1636
+ let buffer = 0;
1637
+ let bitsCollected = 0;
1638
+ if (this.padding && strict) {
1639
+ ok(data.length % 4 === 0, "Invalid Base64 data");
1640
+ }
1641
+ for (const char of data) {
1642
+ if (char === this.padding) break;
1643
+ const value = this.decodeMap.get(char);
1644
+ if (value === void 0) {
1645
+ throw new Error(`Invalid Base64 character: ${char}`);
1674
1646
  }
1675
- if (padCount < 1) {
1676
- result.push(buffer & 255);
1647
+ buffer = buffer << 6 | value;
1648
+ bitsCollected += 6;
1649
+ if (bitsCollected >= 8) {
1650
+ bitsCollected -= 8;
1651
+ result.push(buffer >> bitsCollected & 255);
1677
1652
  }
1678
1653
  }
1679
1654
  return Uint8Array.from(result);
@@ -1687,10 +1662,22 @@ const base64url = new Base64Encoding(
1687
1662
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
1688
1663
  );
1689
1664
 
1690
- function base64ToBytes(data, { encoding = "base64", strict } = {}) {
1665
+ function base64ToBytes(data, { encoding = "base64", strict, native = true } = {}) {
1691
1666
  if (encoding === "base64") {
1667
+ if (native && isFunction(Uint8Array.fromBase64)) {
1668
+ return Uint8Array.fromBase64(data, {
1669
+ alphabet: "base64",
1670
+ lastChunkHandling: strict ?? true ? "strict" : "loose"
1671
+ });
1672
+ }
1692
1673
  return base64.decode(data, { strict: strict ?? true });
1693
1674
  } else if (encoding === "base64url") {
1675
+ if (native && isFunction(Uint8Array.fromBase64)) {
1676
+ return Uint8Array.fromBase64(data, {
1677
+ alphabet: "base64url",
1678
+ lastChunkHandling: strict ?? false ? "strict" : "loose"
1679
+ });
1680
+ }
1694
1681
  return base64url.decode(data, { strict: strict ?? false });
1695
1682
  }
1696
1683
  ok(false, "Invalid encoding options: " + encoding);
@@ -2154,10 +2141,22 @@ function buildFieldsInfo(fields, totalBits) {
2154
2141
  return result;
2155
2142
  }
2156
2143
 
2157
- function bytesToBase64(data, { encoding = "base64", padding } = {}) {
2144
+ function bytesToBase64(data, { encoding = "base64", padding, native = true } = {}) {
2158
2145
  if (encoding === "base64") {
2146
+ if (native && isFunction(data.toBase64)) {
2147
+ return data.toBase64({
2148
+ alphabet: "base64",
2149
+ omitPadding: !(padding ?? true)
2150
+ });
2151
+ }
2159
2152
  return base64.encode(data, { includePadding: padding ?? true });
2160
2153
  } else if (encoding === "base64url") {
2154
+ if (native && isFunction(data.toBase64)) {
2155
+ return data.toBase64({
2156
+ alphabet: "base64url",
2157
+ omitPadding: !(padding ?? false)
2158
+ });
2159
+ }
2161
2160
  return base64url.encode(data, { includePadding: padding ?? false });
2162
2161
  }
2163
2162
  ok(false, "Invalid encoding options: " + encoding);
@@ -2182,6 +2181,47 @@ function concatenateBytes(a, b) {
2182
2181
  return result;
2183
2182
  }
2184
2183
 
2184
+ function rleDecode(buf, value = 0) {
2185
+ const result = [];
2186
+ let i = 0;
2187
+ while (i < buf.length) {
2188
+ if (buf[i] === value) {
2189
+ const count = buf[i + 1];
2190
+ for (let j = 0; j < count; j++) {
2191
+ result.push(value);
2192
+ }
2193
+ i += 2;
2194
+ } else {
2195
+ result.push(buf[i]);
2196
+ i++;
2197
+ }
2198
+ }
2199
+ return new Uint8Array(result);
2200
+ }
2201
+
2202
+ function rleEncode(buf, value = 0) {
2203
+ const result = [];
2204
+ let i = 0;
2205
+ while (i < buf.length) {
2206
+ if (buf[i] === value) {
2207
+ const runStart = i;
2208
+ while (i < buf.length && buf[i] === value) {
2209
+ i++;
2210
+ }
2211
+ let runLength = i - runStart;
2212
+ while (runLength > 0) {
2213
+ const chunk = Math.min(runLength, 255);
2214
+ result.push(value, chunk);
2215
+ runLength -= chunk;
2216
+ }
2217
+ } else {
2218
+ result.push(buf[i]);
2219
+ i++;
2220
+ }
2221
+ }
2222
+ return new Uint8Array(result);
2223
+ }
2224
+
2185
2225
  function uint16ToUint8(value) {
2186
2226
  const uint8Array = new Uint8Array(value.length * 2);
2187
2227
  for (let i = 0; i < value.length; i++) {
@@ -5167,17 +5207,28 @@ function throttle(func, throttleMs, { signal, edges = ["leading", "trailing"] }
5167
5207
  return throttled;
5168
5208
  }
5169
5209
 
5170
- const LEVEL_NUM = {
5210
+ const LEVEL_NAME_TO_NUM = {
5171
5211
  debug: 0,
5172
5212
  log: 1,
5173
5213
  info: 2,
5174
5214
  warn: 3,
5175
5215
  error: 4
5176
5216
  };
5177
- let currentLogLevel = LEVEL_NUM.log;
5178
- const loggerSetLevel = (level) => {
5179
- number$1(LEVEL_NUM[level], `Invalid log level: ${level}`);
5180
- currentLogLevel = LEVEL_NUM[level];
5217
+ const LEVEL_NUM_TO_NAME = {
5218
+ 0: "debug",
5219
+ 1: "log",
5220
+ 2: "info",
5221
+ 3: "warn",
5222
+ 4: "error"
5223
+ };
5224
+ const LOG_LEVEL = env.string("LOG_LEVEL", "info");
5225
+ let currentLogLevel = LOG_LEVEL in LEVEL_NAME_TO_NUM ? LEVEL_NAME_TO_NUM[LOG_LEVEL] : LEVEL_NAME_TO_NUM.log;
5226
+ const setLoggerLevel = (level) => {
5227
+ number$1(LEVEL_NAME_TO_NUM[level], `Invalid log level: ${level}`);
5228
+ currentLogLevel = LEVEL_NAME_TO_NUM[level];
5229
+ };
5230
+ const getLoggerLevel = () => {
5231
+ return LEVEL_NUM_TO_NAME[currentLogLevel];
5181
5232
  };
5182
5233
  const logger = (...baseArgs) => {
5183
5234
  if (isString(baseArgs[0]?.url)) {
@@ -5187,7 +5238,7 @@ const logger = (...baseArgs) => {
5187
5238
  baseArgs[0] = `[${baseArgs[0].split("/").at(-1).split("?", 1)[0]}]`;
5188
5239
  }
5189
5240
  const writeLog = (level, ...[pattern, ...args]) => {
5190
- const levelNum = LEVEL_NUM[level];
5241
+ const levelNum = LEVEL_NAME_TO_NUM[level];
5191
5242
  if (levelNum < currentLogLevel) {
5192
5243
  return;
5193
5244
  }
@@ -5915,6 +5966,135 @@ class ResourcePool extends SimpleEventEmitter {
5915
5966
  }
5916
5967
  }
5917
5968
 
5969
+ const SCHEDULER_JOB_FLAGS = {
5970
+ QUEUED: 1 << 0,
5971
+ ALLOW_RECURSE: 1 << 2,
5972
+ DISPOSED: 1 << 3
5973
+ };
5974
+ function createScheduler() {
5975
+ return new Scheduler();
5976
+ }
5977
+ class Scheduler {
5978
+ queue = [];
5979
+ flushIndex = -1;
5980
+ resolvedPromise = Promise.resolve();
5981
+ currentFlushPromise = null;
5982
+ onJobCbs = [];
5983
+ onJobStartCbs = [];
5984
+ onJobCompleteCbs = [];
5985
+ constructor() {
5986
+ for (const methodName of Object.getOwnPropertyNames(
5987
+ Object.getPrototypeOf(this)
5988
+ )) {
5989
+ this[methodName] = this[methodName].bind(this);
5990
+ }
5991
+ }
5992
+ onJob(fn) {
5993
+ this.onJobCbs.push(fn);
5994
+ }
5995
+ onJobStart(fn) {
5996
+ this.onJobStartCbs.push(fn);
5997
+ }
5998
+ onJobComplete(fn) {
5999
+ this.onJobCompleteCbs.push(fn);
6000
+ }
6001
+ nextTick(fn) {
6002
+ const p = this.currentFlushPromise || this.resolvedPromise;
6003
+ return fn ? p.then(this ? fn.bind(this) : fn) : p;
6004
+ }
6005
+ queueJob(job, opts) {
6006
+ if (opts) {
6007
+ Object.assign(job, opts);
6008
+ }
6009
+ if (!(job.flags & SCHEDULER_JOB_FLAGS.QUEUED)) {
6010
+ const jobId = getId(job);
6011
+ const lastJob = this.queue[this.queue.length - 1];
6012
+ if (!lastJob || jobId >= getId(lastJob)) {
6013
+ this.queue.push(job);
6014
+ } else {
6015
+ this.queue.splice(this.findInsertionIndex(jobId), 0, job);
6016
+ }
6017
+ job.flags |= SCHEDULER_JOB_FLAGS.QUEUED;
6018
+ this.queueFlush();
6019
+ for (const hookFn of this.onJobCbs) {
6020
+ hookFn(job);
6021
+ }
6022
+ }
6023
+ }
6024
+ queueJobWait(job, opts) {
6025
+ return new CancellablePromise((resolve, reject, onCancel) => {
6026
+ onCancel(() => {
6027
+ job.flags |= SCHEDULER_JOB_FLAGS.DISPOSED;
6028
+ reject(new Error("Canceled"));
6029
+ });
6030
+ const fn = () => {
6031
+ return Promise.resolve().then(() => job()).then(resolve).catch(reject);
6032
+ };
6033
+ fn.id = job.id;
6034
+ fn.flags = job.flags;
6035
+ this.queueJob(fn, opts);
6036
+ });
6037
+ }
6038
+ findInsertionIndex(id) {
6039
+ let start = this.flushIndex + 1;
6040
+ let end = this.queue.length;
6041
+ while (start < end) {
6042
+ const middle = start + end >>> 1;
6043
+ const middleJob = this.queue[middle];
6044
+ const middleJobId = getId(middleJob);
6045
+ if (middleJobId < id) {
6046
+ start = middle + 1;
6047
+ } else {
6048
+ end = middle;
6049
+ }
6050
+ }
6051
+ return start;
6052
+ }
6053
+ async flushJobs() {
6054
+ try {
6055
+ for (this.flushIndex = 0; this.flushIndex < this.queue.length; this.flushIndex++) {
6056
+ const job = this.queue[this.flushIndex];
6057
+ if (job && !(job.flags & SCHEDULER_JOB_FLAGS.DISPOSED)) {
6058
+ if (job.flags & SCHEDULER_JOB_FLAGS.ALLOW_RECURSE) {
6059
+ job.flags &= ~SCHEDULER_JOB_FLAGS.QUEUED;
6060
+ }
6061
+ for (const hookFn of this.onJobStartCbs) {
6062
+ hookFn(job);
6063
+ }
6064
+ await job();
6065
+ for (const hookFn of this.onJobCompleteCbs) {
6066
+ hookFn(job);
6067
+ }
6068
+ if (!(job.flags & SCHEDULER_JOB_FLAGS.ALLOW_RECURSE)) {
6069
+ job.flags &= ~SCHEDULER_JOB_FLAGS.QUEUED;
6070
+ }
6071
+ }
6072
+ }
6073
+ } finally {
6074
+ for (; this.flushIndex < this.queue.length; this.flushIndex++) {
6075
+ const job = this.queue[this.flushIndex];
6076
+ if (job) {
6077
+ job.flags &= ~SCHEDULER_JOB_FLAGS.QUEUED;
6078
+ }
6079
+ }
6080
+ this.flushIndex = -1;
6081
+ this.queue.length = 0;
6082
+ this.currentFlushPromise = null;
6083
+ if (this.queue.length) {
6084
+ return this.flushJobs();
6085
+ }
6086
+ }
6087
+ }
6088
+ queueFlush() {
6089
+ if (!this.currentFlushPromise) {
6090
+ this.currentFlushPromise = this.resolvedPromise.then(this.flushJobs);
6091
+ }
6092
+ }
6093
+ }
6094
+ function getId(job) {
6095
+ return job.id == null ? Infinity : job.id;
6096
+ }
6097
+
5918
6098
  function timeout(ms, promiseOrCallback, timeoutError = new AppError(408)) {
5919
6099
  const abortController = new AbortController();
5920
6100
  let taskResult;
@@ -5999,5 +6179,5 @@ function withResolve(fn, getCacheKey) {
5999
6179
  }
6000
6180
  }
6001
6181
 
6002
- export { AppError, AsyncIterableQueue, BrowserAssertionError, CancellablePromise, ColorParser, instance as EJSON, EJSON as EJSONInstance, EJSONStream, FindMean, FixedMap, FixedWeakMap, LruCache, Queue, ResourcePool, SimpleEventEmitter, SortedArray, TWEMOJI_REGEX, TimeBucket, TimeSpan, alpha, arrayable, assert, asyncFilter, asyncFind, asyncForEach, asyncMap, avg, avgCircular, base62, base62Fast, base64, base64ToBytes, base64url, basex, bigIntBytes, bigIntFromBytes, bitPack, bitUnpack, blendColors, buildCssColor, bytesToBase64, cache, cacheBucket, cacheFixed, cacheLRU, camelCase, capitalize, captureStackTrace, catchError, channelsToHSL, channelsToHex, channelsToRGB, checkBitmask, chunk, chunkSeries, clamp, cleanEmpty, cleanObject, colorToChannels, compareBytes, concatenateBytes, contrastRatio, convertToUnit, crc32, createCustomizer, createCustomizerFactory, createDateObject, createDeepCloneWith, createEJSON, createEJSONStream, createEnvParser, createFunction, createRandomizer, createSecureCustomizer, createTimeObject, createTimeSpan, createWithCache, cssVariable, dateInDays, dateInSeconds, debounce, deepAssign, deepClone, deepCloneWith, deepDefaults, deepFreeze, def, defer, delay, difference, dropCache, env, escapeHtml, escapeNumeric, escapeRegExp, fastIdle, fastIdlePromise, fastRaf, findMean, flagsToMap, flatten, formatMoney, formatNumber, get, getFileExtension, getFileName, getInitials, getMostSpecificPaths, getRandomInt, getRandomTime, getWords, groupBy, has, hasOwn, hasProtocol, hex, hexToChannels, hmToSeconds, hslToChannels, humanFileSize, humanize, interpolateColor, intersection, intersectionBy, isBigInt, isBoolean, isCached, isClient, isColorChannels, isCustomizerFactory, isDate, isDateObject, isDef, isEmpty, isEqual, isError, isFunction, isInfinity, isMap, isNode, isNullOrUndefined, isNumber, isObject, isOneEmoji, isPlainObject, isPrimitive, isPromise, isRegExp, isSet, isSkip, isString, isSuccess, isSymbol, isTimeObject, isTimeString, isTimeValue, isValidWeekDay, isWeakMap, isWeakSet, isWithCache, isoToFlagEmoji, kebabCase, keyBy, logger, loggerSetLevel, lowerCase, luminance, maskingEmail, maskingPhone, maskingWords, nextTickIteration, noop, objectId, omit, omitPrefixed, orderBy, parseAllNumbers, parseAlpha, parsePercentage, percentOf, pick, pickPrefixed, qs, rafPromise, randomString, removeVS16s, retryOnError, rgbToChannels, round2digits, secondsToHm, set, shuffle, snakeCase, sprintf, startCase, strAssign, sum, textDecoder, textEncoder, throttle, timeFromMinutes, timeStringify, timeToMinutes, timeout, timestamp, timestampMs, timestampToDate, tintedTextColor, toError, toMap, truncate, typeOf, uint16ToUint8, uint32ToUint8, uint8ToUint16, uint8ToUint32, unflatten, union, uniq, uniqBy, unset, weeksInYear, weightedRoundRobin, withCache, withCacheBucket, withCacheBucketBatch, withCacheFixed, withCacheLRU, withDeepClone, withPointerCache, withResolve, wrapText };
6182
+ export { AppError, AssertionError, AsyncIterableQueue, CancellablePromise, ColorParser, instance as EJSON, EJSON as EJSONInstance, EJSONStream, FindMean, FixedMap, FixedWeakMap, LruCache, Queue, ResourcePool, SCHEDULER_JOB_FLAGS, Scheduler, SimpleEventEmitter, SortedArray, TWEMOJI_REGEX, TimeBucket, TimeSpan, alpha, arrayable, assert, asyncFilter, asyncFind, asyncForEach, asyncMap, avg, avgCircular, base62, base62Fast, base64, base64ToBytes, base64url, basex, bigIntBytes, bigIntFromBytes, bitPack, bitUnpack, blendColors, buildCssColor, bytesToBase64, cache, cacheBucket, cacheFixed, cacheLRU, camelCase, capitalize, captureStackTrace, catchError, channelsToHSL, channelsToHex, channelsToRGB, checkBitmask, chunk, chunkSeries, clamp, cleanEmpty, cleanObject, colorToChannels, compareBytes, concatenateBytes, contrastRatio, convertToUnit, crc32, createCustomizer, createCustomizerFactory, createDateObject, createDeepCloneWith, createEJSON, createEJSONStream, createEnvParser, createFunction, createRandomizer, createScheduler, createSecureCustomizer, createTimeObject, createTimeSpan, createWithCache, cssVariable, dateInDays, dateInSeconds, debounce, deepAssign, deepClone, deepCloneWith, deepDefaults, deepFreeze, def, defer, delay, difference, dropCache, env, escapeHtml, escapeNumeric, escapeRegExp, fastIdle, fastIdlePromise, fastRaf, findMean, flagsToMap, flatten, formatMoney, formatNumber, get, getFileExtension, getFileName, getInitials, getLoggerLevel, getMostSpecificPaths, getRandomInt, getRandomTime, getWords, groupBy, has, hasOwn, hasProtocol, hex, hexToChannels, hmToSeconds, hslToChannels, humanFileSize, humanize, interpolateColor, intersection, intersectionBy, isBigInt, isBoolean, isCached, isClient, isColorChannels, isCustomizerFactory, isDate, isDateObject, isDef, isEmpty, isEqual, isError, isFunction, isInfinity, isMap, isNode, isNullOrUndefined, isNumber, isObject, isOneEmoji, isPlainObject, isPrimitive, isPromise, isRegExp, isSet, isSkip, isString, isSuccess, isSymbol, isTimeObject, isTimeString, isTimeValue, isValidWeekDay, isWeakMap, isWeakSet, isWithCache, isoToFlagEmoji, kebabCase, keyBy, logger, lowerCase, luminance, maskingEmail, maskingPhone, maskingWords, nextTickIteration, noop, objectId, omit, omitPrefixed, orderBy, parseAllNumbers, parseAlpha, parsePercentage, percentOf, pick, pickPrefixed, qs, rafPromise, randomString, removeVS16s, retryOnError, rgbToChannels, rleDecode, rleEncode, round2digits, secondsToHm, set, setLoggerLevel, shuffle, snakeCase, sprintf, startCase, strAssign, sum, textDecoder, textEncoder, throttle, timeFromMinutes, timeStringify, timeToMinutes, timeout, timestamp, timestampMs, timestampToDate, tintedTextColor, toError, toMap, truncate, typeOf, uint16ToUint8, uint32ToUint8, uint8ToUint16, uint8ToUint32, unflatten, union, uniq, uniqBy, unset, weeksInYear, weightedRoundRobin, withCache, withCacheBucket, withCacheBucketBatch, withCacheFixed, withCacheLRU, withDeepClone, withPointerCache, withResolve, wrapText };
6003
6183
  //# sourceMappingURL=index.mjs.map