@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.cjs CHANGED
@@ -832,7 +832,7 @@ function uniqBy(array, comparator) {
832
832
  });
833
833
  }
834
834
 
835
- class BrowserAssertionError extends Error {
835
+ class AssertionError extends Error {
836
836
  /**
837
837
  * Set to the `actual` argument for methods such as {@link assert.strictEqual()}.
838
838
  */
@@ -863,17 +863,6 @@ class BrowserAssertionError extends Error {
863
863
  }
864
864
  }
865
865
 
866
- let AssertionError = BrowserAssertionError;
867
- if (isNode()) {
868
- try {
869
- const assert = require("node:assert");
870
- if (assert.AssertionError) {
871
- AssertionError = assert.AssertionError;
872
- }
873
- } catch {
874
- }
875
- }
876
-
877
866
  function ok(value, message) {
878
867
  if (!value) {
879
868
  throw toError$1(
@@ -1594,8 +1583,12 @@ class Base64Encoding {
1594
1583
  }
1595
1584
  this.alphabet = alphabet;
1596
1585
  this.padding = options?.padding ?? "=";
1597
- if (this.alphabet.includes(this.padding) || this.padding.length !== 1) {
1598
- throw new Error("Invalid padding");
1586
+ if (this.padding) {
1587
+ ok(
1588
+ !this.alphabet.includes(this.padding),
1589
+ "Padding cannot be a part of alphabet"
1590
+ );
1591
+ ok(this.padding.length === 1, "Padding length must be a 1");
1599
1592
  }
1600
1593
  for (let i = 0; i < alphabet.length; i++) {
1601
1594
  this.decodeMap.set(alphabet[i], i);
@@ -1614,26 +1607,24 @@ class Base64Encoding {
1614
1607
  * ```
1615
1608
  */
1616
1609
  encode(data, options) {
1610
+ const includePadding = options?.includePadding ?? true;
1617
1611
  let result = "";
1618
1612
  let buffer = 0;
1619
1613
  let shift = 0;
1620
- for (let i = 0; i < data.length; i++) {
1621
- buffer = buffer << 8 | data[i];
1614
+ for (const byte of data) {
1615
+ buffer = buffer << 8 | byte;
1622
1616
  shift += 8;
1623
1617
  while (shift >= 6) {
1624
- shift += -6;
1618
+ shift -= 6;
1625
1619
  result += this.alphabet[buffer >> shift & 63];
1626
1620
  }
1627
1621
  }
1628
1622
  if (shift > 0) {
1629
1623
  result += this.alphabet[buffer << 6 - shift & 63];
1630
1624
  }
1631
- const includePadding = options?.includePadding ?? true;
1632
- if (includePadding) {
1625
+ if (includePadding && this.padding) {
1633
1626
  const padCount = (4 - result.length % 4) % 4;
1634
- for (let i = 0; i < padCount; i++) {
1635
- result += "=";
1636
- }
1627
+ result += "=".repeat(padCount);
1637
1628
  }
1638
1629
  return result;
1639
1630
  }
@@ -1652,39 +1643,23 @@ class Base64Encoding {
1652
1643
  */
1653
1644
  decode(data, options) {
1654
1645
  const strict = options?.strict ?? true;
1655
- const chunkCount = Math.ceil(data.length / 4);
1656
1646
  const result = [];
1657
- for (let i = 0; i < chunkCount; i++) {
1658
- let padCount = 0;
1659
- let buffer = 0;
1660
- for (let j = 0; j < 4; j++) {
1661
- const encoded = data[i * 4 + j];
1662
- if (encoded === "=") {
1663
- if (i + 1 !== chunkCount) {
1664
- throw new Error(`Invalid character: ${encoded}`);
1665
- }
1666
- padCount += 1;
1667
- continue;
1668
- }
1669
- if (encoded === void 0) {
1670
- if (strict) {
1671
- throw new Error("Invalid data");
1672
- }
1673
- padCount += 1;
1674
- continue;
1675
- }
1676
- const value = this.decodeMap.get(encoded) ?? null;
1677
- if (value === null) {
1678
- throw new Error(`Invalid character: ${encoded}`);
1679
- }
1680
- buffer += value << 6 * (3 - j);
1681
- }
1682
- result.push(buffer >> 16 & 255);
1683
- if (padCount < 2) {
1684
- result.push(buffer >> 8 & 255);
1647
+ let buffer = 0;
1648
+ let bitsCollected = 0;
1649
+ if (this.padding && strict) {
1650
+ ok(data.length % 4 === 0, "Invalid Base64 data");
1651
+ }
1652
+ for (const char of data) {
1653
+ if (char === this.padding) break;
1654
+ const value = this.decodeMap.get(char);
1655
+ if (value === void 0) {
1656
+ throw new Error(`Invalid Base64 character: ${char}`);
1685
1657
  }
1686
- if (padCount < 1) {
1687
- result.push(buffer & 255);
1658
+ buffer = buffer << 6 | value;
1659
+ bitsCollected += 6;
1660
+ if (bitsCollected >= 8) {
1661
+ bitsCollected -= 8;
1662
+ result.push(buffer >> bitsCollected & 255);
1688
1663
  }
1689
1664
  }
1690
1665
  return Uint8Array.from(result);
@@ -1698,10 +1673,22 @@ const base64url = new Base64Encoding(
1698
1673
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
1699
1674
  );
1700
1675
 
1701
- function base64ToBytes(data, { encoding = "base64", strict } = {}) {
1676
+ function base64ToBytes(data, { encoding = "base64", strict, native = true } = {}) {
1702
1677
  if (encoding === "base64") {
1678
+ if (native && isFunction(Uint8Array.fromBase64)) {
1679
+ return Uint8Array.fromBase64(data, {
1680
+ alphabet: "base64",
1681
+ lastChunkHandling: strict ?? true ? "strict" : "loose"
1682
+ });
1683
+ }
1703
1684
  return base64.decode(data, { strict: strict ?? true });
1704
1685
  } else if (encoding === "base64url") {
1686
+ if (native && isFunction(Uint8Array.fromBase64)) {
1687
+ return Uint8Array.fromBase64(data, {
1688
+ alphabet: "base64url",
1689
+ lastChunkHandling: strict ?? false ? "strict" : "loose"
1690
+ });
1691
+ }
1705
1692
  return base64url.decode(data, { strict: strict ?? false });
1706
1693
  }
1707
1694
  ok(false, "Invalid encoding options: " + encoding);
@@ -2165,10 +2152,22 @@ function buildFieldsInfo(fields, totalBits) {
2165
2152
  return result;
2166
2153
  }
2167
2154
 
2168
- function bytesToBase64(data, { encoding = "base64", padding } = {}) {
2155
+ function bytesToBase64(data, { encoding = "base64", padding, native = true } = {}) {
2169
2156
  if (encoding === "base64") {
2157
+ if (native && isFunction(data.toBase64)) {
2158
+ return data.toBase64({
2159
+ alphabet: "base64",
2160
+ omitPadding: !(padding ?? true)
2161
+ });
2162
+ }
2170
2163
  return base64.encode(data, { includePadding: padding ?? true });
2171
2164
  } else if (encoding === "base64url") {
2165
+ if (native && isFunction(data.toBase64)) {
2166
+ return data.toBase64({
2167
+ alphabet: "base64url",
2168
+ omitPadding: !(padding ?? false)
2169
+ });
2170
+ }
2172
2171
  return base64url.encode(data, { includePadding: padding ?? false });
2173
2172
  }
2174
2173
  ok(false, "Invalid encoding options: " + encoding);
@@ -2193,6 +2192,47 @@ function concatenateBytes(a, b) {
2193
2192
  return result;
2194
2193
  }
2195
2194
 
2195
+ function rleDecode(buf, value = 0) {
2196
+ const result = [];
2197
+ let i = 0;
2198
+ while (i < buf.length) {
2199
+ if (buf[i] === value) {
2200
+ const count = buf[i + 1];
2201
+ for (let j = 0; j < count; j++) {
2202
+ result.push(value);
2203
+ }
2204
+ i += 2;
2205
+ } else {
2206
+ result.push(buf[i]);
2207
+ i++;
2208
+ }
2209
+ }
2210
+ return new Uint8Array(result);
2211
+ }
2212
+
2213
+ function rleEncode(buf, value = 0) {
2214
+ const result = [];
2215
+ let i = 0;
2216
+ while (i < buf.length) {
2217
+ if (buf[i] === value) {
2218
+ const runStart = i;
2219
+ while (i < buf.length && buf[i] === value) {
2220
+ i++;
2221
+ }
2222
+ let runLength = i - runStart;
2223
+ while (runLength > 0) {
2224
+ const chunk = Math.min(runLength, 255);
2225
+ result.push(value, chunk);
2226
+ runLength -= chunk;
2227
+ }
2228
+ } else {
2229
+ result.push(buf[i]);
2230
+ i++;
2231
+ }
2232
+ }
2233
+ return new Uint8Array(result);
2234
+ }
2235
+
2196
2236
  function uint16ToUint8(value) {
2197
2237
  const uint8Array = new Uint8Array(value.length * 2);
2198
2238
  for (let i = 0; i < value.length; i++) {
@@ -5178,17 +5218,28 @@ function throttle(func, throttleMs, { signal, edges = ["leading", "trailing"] }
5178
5218
  return throttled;
5179
5219
  }
5180
5220
 
5181
- const LEVEL_NUM = {
5221
+ const LEVEL_NAME_TO_NUM = {
5182
5222
  debug: 0,
5183
5223
  log: 1,
5184
5224
  info: 2,
5185
5225
  warn: 3,
5186
5226
  error: 4
5187
5227
  };
5188
- let currentLogLevel = LEVEL_NUM.log;
5189
- const loggerSetLevel = (level) => {
5190
- number$1(LEVEL_NUM[level], `Invalid log level: ${level}`);
5191
- currentLogLevel = LEVEL_NUM[level];
5228
+ const LEVEL_NUM_TO_NAME = {
5229
+ 0: "debug",
5230
+ 1: "log",
5231
+ 2: "info",
5232
+ 3: "warn",
5233
+ 4: "error"
5234
+ };
5235
+ const LOG_LEVEL = env.string("LOG_LEVEL", "info");
5236
+ let currentLogLevel = LOG_LEVEL in LEVEL_NAME_TO_NUM ? LEVEL_NAME_TO_NUM[LOG_LEVEL] : LEVEL_NAME_TO_NUM.log;
5237
+ const setLoggerLevel = (level) => {
5238
+ number$1(LEVEL_NAME_TO_NUM[level], `Invalid log level: ${level}`);
5239
+ currentLogLevel = LEVEL_NAME_TO_NUM[level];
5240
+ };
5241
+ const getLoggerLevel = () => {
5242
+ return LEVEL_NUM_TO_NAME[currentLogLevel];
5192
5243
  };
5193
5244
  const logger = (...baseArgs) => {
5194
5245
  if (isString(baseArgs[0]?.url)) {
@@ -5198,7 +5249,7 @@ const logger = (...baseArgs) => {
5198
5249
  baseArgs[0] = `[${baseArgs[0].split("/").at(-1).split("?", 1)[0]}]`;
5199
5250
  }
5200
5251
  const writeLog = (level, ...[pattern, ...args]) => {
5201
- const levelNum = LEVEL_NUM[level];
5252
+ const levelNum = LEVEL_NAME_TO_NUM[level];
5202
5253
  if (levelNum < currentLogLevel) {
5203
5254
  return;
5204
5255
  }
@@ -5926,6 +5977,135 @@ class ResourcePool extends SimpleEventEmitter {
5926
5977
  }
5927
5978
  }
5928
5979
 
5980
+ const SCHEDULER_JOB_FLAGS = {
5981
+ QUEUED: 1 << 0,
5982
+ ALLOW_RECURSE: 1 << 2,
5983
+ DISPOSED: 1 << 3
5984
+ };
5985
+ function createScheduler() {
5986
+ return new Scheduler();
5987
+ }
5988
+ class Scheduler {
5989
+ queue = [];
5990
+ flushIndex = -1;
5991
+ resolvedPromise = Promise.resolve();
5992
+ currentFlushPromise = null;
5993
+ onJobCbs = [];
5994
+ onJobStartCbs = [];
5995
+ onJobCompleteCbs = [];
5996
+ constructor() {
5997
+ for (const methodName of Object.getOwnPropertyNames(
5998
+ Object.getPrototypeOf(this)
5999
+ )) {
6000
+ this[methodName] = this[methodName].bind(this);
6001
+ }
6002
+ }
6003
+ onJob(fn) {
6004
+ this.onJobCbs.push(fn);
6005
+ }
6006
+ onJobStart(fn) {
6007
+ this.onJobStartCbs.push(fn);
6008
+ }
6009
+ onJobComplete(fn) {
6010
+ this.onJobCompleteCbs.push(fn);
6011
+ }
6012
+ nextTick(fn) {
6013
+ const p = this.currentFlushPromise || this.resolvedPromise;
6014
+ return fn ? p.then(this ? fn.bind(this) : fn) : p;
6015
+ }
6016
+ queueJob(job, opts) {
6017
+ if (opts) {
6018
+ Object.assign(job, opts);
6019
+ }
6020
+ if (!(job.flags & SCHEDULER_JOB_FLAGS.QUEUED)) {
6021
+ const jobId = getId(job);
6022
+ const lastJob = this.queue[this.queue.length - 1];
6023
+ if (!lastJob || jobId >= getId(lastJob)) {
6024
+ this.queue.push(job);
6025
+ } else {
6026
+ this.queue.splice(this.findInsertionIndex(jobId), 0, job);
6027
+ }
6028
+ job.flags |= SCHEDULER_JOB_FLAGS.QUEUED;
6029
+ this.queueFlush();
6030
+ for (const hookFn of this.onJobCbs) {
6031
+ hookFn(job);
6032
+ }
6033
+ }
6034
+ }
6035
+ queueJobWait(job, opts) {
6036
+ return new CancellablePromise((resolve, reject, onCancel) => {
6037
+ onCancel(() => {
6038
+ job.flags |= SCHEDULER_JOB_FLAGS.DISPOSED;
6039
+ reject(new Error("Canceled"));
6040
+ });
6041
+ const fn = () => {
6042
+ return Promise.resolve().then(() => job()).then(resolve).catch(reject);
6043
+ };
6044
+ fn.id = job.id;
6045
+ fn.flags = job.flags;
6046
+ this.queueJob(fn, opts);
6047
+ });
6048
+ }
6049
+ findInsertionIndex(id) {
6050
+ let start = this.flushIndex + 1;
6051
+ let end = this.queue.length;
6052
+ while (start < end) {
6053
+ const middle = start + end >>> 1;
6054
+ const middleJob = this.queue[middle];
6055
+ const middleJobId = getId(middleJob);
6056
+ if (middleJobId < id) {
6057
+ start = middle + 1;
6058
+ } else {
6059
+ end = middle;
6060
+ }
6061
+ }
6062
+ return start;
6063
+ }
6064
+ async flushJobs() {
6065
+ try {
6066
+ for (this.flushIndex = 0; this.flushIndex < this.queue.length; this.flushIndex++) {
6067
+ const job = this.queue[this.flushIndex];
6068
+ if (job && !(job.flags & SCHEDULER_JOB_FLAGS.DISPOSED)) {
6069
+ if (job.flags & SCHEDULER_JOB_FLAGS.ALLOW_RECURSE) {
6070
+ job.flags &= ~SCHEDULER_JOB_FLAGS.QUEUED;
6071
+ }
6072
+ for (const hookFn of this.onJobStartCbs) {
6073
+ hookFn(job);
6074
+ }
6075
+ await job();
6076
+ for (const hookFn of this.onJobCompleteCbs) {
6077
+ hookFn(job);
6078
+ }
6079
+ if (!(job.flags & SCHEDULER_JOB_FLAGS.ALLOW_RECURSE)) {
6080
+ job.flags &= ~SCHEDULER_JOB_FLAGS.QUEUED;
6081
+ }
6082
+ }
6083
+ }
6084
+ } finally {
6085
+ for (; this.flushIndex < this.queue.length; this.flushIndex++) {
6086
+ const job = this.queue[this.flushIndex];
6087
+ if (job) {
6088
+ job.flags &= ~SCHEDULER_JOB_FLAGS.QUEUED;
6089
+ }
6090
+ }
6091
+ this.flushIndex = -1;
6092
+ this.queue.length = 0;
6093
+ this.currentFlushPromise = null;
6094
+ if (this.queue.length) {
6095
+ return this.flushJobs();
6096
+ }
6097
+ }
6098
+ }
6099
+ queueFlush() {
6100
+ if (!this.currentFlushPromise) {
6101
+ this.currentFlushPromise = this.resolvedPromise.then(this.flushJobs);
6102
+ }
6103
+ }
6104
+ }
6105
+ function getId(job) {
6106
+ return job.id == null ? Infinity : job.id;
6107
+ }
6108
+
5929
6109
  function timeout(ms, promiseOrCallback, timeoutError = new AppError(408)) {
5930
6110
  const abortController = new AbortController();
5931
6111
  let taskResult;
@@ -6011,8 +6191,8 @@ function withResolve(fn, getCacheKey) {
6011
6191
  }
6012
6192
 
6013
6193
  exports.AppError = AppError;
6194
+ exports.AssertionError = AssertionError;
6014
6195
  exports.AsyncIterableQueue = AsyncIterableQueue;
6015
- exports.BrowserAssertionError = BrowserAssertionError;
6016
6196
  exports.CancellablePromise = CancellablePromise;
6017
6197
  exports.ColorParser = ColorParser;
6018
6198
  exports.EJSON = instance;
@@ -6024,6 +6204,8 @@ exports.FixedWeakMap = FixedWeakMap;
6024
6204
  exports.LruCache = LruCache;
6025
6205
  exports.Queue = Queue;
6026
6206
  exports.ResourcePool = ResourcePool;
6207
+ exports.SCHEDULER_JOB_FLAGS = SCHEDULER_JOB_FLAGS;
6208
+ exports.Scheduler = Scheduler;
6027
6209
  exports.SimpleEventEmitter = SimpleEventEmitter;
6028
6210
  exports.SortedArray = SortedArray;
6029
6211
  exports.TWEMOJI_REGEX = TWEMOJI_REGEX;
@@ -6083,6 +6265,7 @@ exports.createEJSONStream = createEJSONStream;
6083
6265
  exports.createEnvParser = createEnvParser;
6084
6266
  exports.createFunction = createFunction;
6085
6267
  exports.createRandomizer = createRandomizer;
6268
+ exports.createScheduler = createScheduler;
6086
6269
  exports.createSecureCustomizer = createSecureCustomizer;
6087
6270
  exports.createTimeObject = createTimeObject;
6088
6271
  exports.createTimeSpan = createTimeSpan;
@@ -6117,6 +6300,7 @@ exports.get = get;
6117
6300
  exports.getFileExtension = getFileExtension;
6118
6301
  exports.getFileName = getFileName;
6119
6302
  exports.getInitials = getInitials;
6303
+ exports.getLoggerLevel = getLoggerLevel;
6120
6304
  exports.getMostSpecificPaths = getMostSpecificPaths;
6121
6305
  exports.getRandomInt = getRandomInt;
6122
6306
  exports.getRandomTime = getRandomTime;
@@ -6174,7 +6358,6 @@ exports.isoToFlagEmoji = isoToFlagEmoji;
6174
6358
  exports.kebabCase = kebabCase;
6175
6359
  exports.keyBy = keyBy;
6176
6360
  exports.logger = logger;
6177
- exports.loggerSetLevel = loggerSetLevel;
6178
6361
  exports.lowerCase = lowerCase;
6179
6362
  exports.luminance = luminance;
6180
6363
  exports.maskingEmail = maskingEmail;
@@ -6198,9 +6381,12 @@ exports.randomString = randomString;
6198
6381
  exports.removeVS16s = removeVS16s;
6199
6382
  exports.retryOnError = retryOnError;
6200
6383
  exports.rgbToChannels = rgbToChannels;
6384
+ exports.rleDecode = rleDecode;
6385
+ exports.rleEncode = rleEncode;
6201
6386
  exports.round2digits = round2digits;
6202
6387
  exports.secondsToHm = secondsToHm;
6203
6388
  exports.set = set;
6389
+ exports.setLoggerLevel = setLoggerLevel;
6204
6390
  exports.shuffle = shuffle;
6205
6391
  exports.snakeCase = snakeCase;
6206
6392
  exports.sprintf = sprintf;