index.mjs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. function clamp(n, min, max) {
  2. return Math.min(max, Math.max(min, n));
  3. }
  4. function sum(...args) {
  5. return flattenArrayable(args).reduce((a, b) => a + b, 0);
  6. }
  7. function toArray(array) {
  8. array = array || [];
  9. if (Array.isArray(array))
  10. return array;
  11. return [array];
  12. }
  13. function flattenArrayable(array) {
  14. return toArray(array).flat(1);
  15. }
  16. function mergeArrayable(...args) {
  17. return args.flatMap((i) => toArray(i));
  18. }
  19. function partition(array, ...filters) {
  20. const result = new Array(filters.length + 1).fill(null).map(() => []);
  21. array.forEach((e, idx, arr) => {
  22. let i = 0;
  23. for (const filter of filters) {
  24. if (filter(e, idx, arr)) {
  25. result[i].push(e);
  26. return;
  27. }
  28. i += 1;
  29. }
  30. result[i].push(e);
  31. });
  32. return result;
  33. }
  34. function uniq(array) {
  35. return Array.from(new Set(array));
  36. }
  37. function last(array) {
  38. return at(array, -1);
  39. }
  40. function remove(array, value) {
  41. if (!array)
  42. return false;
  43. const index = array.indexOf(value);
  44. if (index >= 0) {
  45. array.splice(index, 1);
  46. return true;
  47. }
  48. return false;
  49. }
  50. function at(array, index) {
  51. const len = array.length;
  52. if (!len)
  53. return void 0;
  54. if (index < 0)
  55. index += len;
  56. return array[index];
  57. }
  58. function range(...args) {
  59. let start, stop, step;
  60. if (args.length === 1) {
  61. start = 0;
  62. step = 1;
  63. [stop] = args;
  64. } else {
  65. [start, stop, step = 1] = args;
  66. }
  67. const arr = [];
  68. let current = start;
  69. while (current < stop) {
  70. arr.push(current);
  71. current += step || 1;
  72. }
  73. return arr;
  74. }
  75. function move(arr, from, to) {
  76. arr.splice(to, 0, arr.splice(from, 1)[0]);
  77. return arr;
  78. }
  79. function clampArrayRange(n, arr) {
  80. return clamp(n, 0, arr.length - 1);
  81. }
  82. function sample(arr, count) {
  83. return Array.from({ length: count }, (_) => arr[Math.round(Math.random() * (arr.length - 1))]);
  84. }
  85. function shuffle(array) {
  86. for (let i = array.length - 1; i > 0; i--) {
  87. const j = Math.floor(Math.random() * (i + 1));
  88. [array[i], array[j]] = [array[j], array[i]];
  89. }
  90. return array;
  91. }
  92. const assert = (condition, message) => {
  93. if (!condition)
  94. throw new Error(message);
  95. };
  96. const toString = (v) => Object.prototype.toString.call(v);
  97. const noop = () => {
  98. };
  99. function notNullish(v) {
  100. return v != null;
  101. }
  102. function noNull(v) {
  103. return v !== null;
  104. }
  105. function notUndefined(v) {
  106. return v !== void 0;
  107. }
  108. function isTruthy(v) {
  109. return Boolean(v);
  110. }
  111. const isDef = (val) => typeof val !== "undefined";
  112. const isBoolean = (val) => typeof val === "boolean";
  113. const isFunction = (val) => typeof val === "function";
  114. const isNumber = (val) => typeof val === "number";
  115. const isString = (val) => typeof val === "string";
  116. const isObject = (val) => toString(val) === "[object Object]";
  117. const isWindow = (val) => typeof window !== "undefined" && toString(val) === "[object Window]";
  118. const isBrowser = typeof window !== "undefined";
  119. function slash(str) {
  120. return str.replace(/\\/g, "/");
  121. }
  122. function ensurePrefix(prefix, str) {
  123. if (!str.startsWith(prefix))
  124. return prefix + str;
  125. return str;
  126. }
  127. function ensureSuffix(suffix, str) {
  128. if (!str.endsWith(suffix))
  129. return str + suffix;
  130. return str;
  131. }
  132. function template(str, ...args) {
  133. return str.replace(/{(\d+)}/g, (match, key) => {
  134. const index = Number(key);
  135. if (Number.isNaN(index))
  136. return match;
  137. return args[index];
  138. });
  139. }
  140. const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
  141. function randomStr(size = 16, dict = urlAlphabet) {
  142. let id = "";
  143. let i = size;
  144. const len = dict.length;
  145. while (i--)
  146. id += dict[Math.random() * len | 0];
  147. return id;
  148. }
  149. const timestamp = () => +Date.now();
  150. function batchInvoke(functions) {
  151. functions.forEach((fn) => fn && fn());
  152. }
  153. function invoke(fn) {
  154. return fn();
  155. }
  156. function tap(value, callback) {
  157. callback(value);
  158. return value;
  159. }
  160. function objectMap(obj, fn) {
  161. return Object.fromEntries(Object.entries(obj).map(([k, v]) => fn(k, v)).filter(notNullish));
  162. }
  163. function isKeyOf(obj, k) {
  164. return k in obj;
  165. }
  166. function objectKeys(obj) {
  167. return Object.keys(obj);
  168. }
  169. function objectEntries(obj) {
  170. return Object.entries(obj);
  171. }
  172. function deepMerge(target, ...sources) {
  173. if (!sources.length)
  174. return target;
  175. const source = sources.shift();
  176. if (source === void 0)
  177. return target;
  178. if (isMergableObject(target) && isMergableObject(source)) {
  179. objectKeys(source).forEach((key) => {
  180. if (isMergableObject(source[key])) {
  181. if (!target[key])
  182. target[key] = {};
  183. deepMerge(target[key], source[key]);
  184. } else {
  185. target[key] = source[key];
  186. }
  187. });
  188. }
  189. return deepMerge(target, ...sources);
  190. }
  191. function isMergableObject(item) {
  192. return isObject(item) && !Array.isArray(item);
  193. }
  194. function objectPick(obj, keys, omitUndefined = false) {
  195. return keys.reduce((n, k) => {
  196. if (k in obj) {
  197. if (!omitUndefined || obj[k] !== void 0)
  198. n[k] = obj[k];
  199. }
  200. return n;
  201. }, {});
  202. }
  203. function clearUndefined(obj) {
  204. Object.keys(obj).forEach((key) => obj[key] === void 0 ? delete obj[key] : {});
  205. return obj;
  206. }
  207. function hasOwnProperty(obj, v) {
  208. if (obj == null)
  209. return false;
  210. return Object.prototype.hasOwnProperty.call(obj, v);
  211. }
  212. function createSingletonPromise(fn) {
  213. let _promise;
  214. function wrapper() {
  215. if (!_promise)
  216. _promise = fn();
  217. return _promise;
  218. }
  219. wrapper.reset = async () => {
  220. const _prev = _promise;
  221. _promise = void 0;
  222. if (_prev)
  223. await _prev;
  224. };
  225. return wrapper;
  226. }
  227. function sleep(ms, callback) {
  228. return new Promise((resolve) => setTimeout(async () => {
  229. await (callback == null ? void 0 : callback());
  230. resolve();
  231. }, ms));
  232. }
  233. function createPromiseLock() {
  234. const locks = [];
  235. return {
  236. async run(fn) {
  237. const p = fn();
  238. locks.push(p);
  239. try {
  240. return await p;
  241. } finally {
  242. remove(locks, p);
  243. }
  244. },
  245. async wait() {
  246. await Promise.allSettled(locks);
  247. },
  248. isWaiting() {
  249. return Boolean(locks.length);
  250. },
  251. clear() {
  252. locks.length = 0;
  253. }
  254. };
  255. }
  256. function createControlledPromise() {
  257. let resolve, reject;
  258. const promise = new Promise((_resolve, _reject) => {
  259. resolve = _resolve;
  260. reject = _reject;
  261. });
  262. promise.resolve = resolve;
  263. promise.reject = reject;
  264. return promise;
  265. }
  266. /* eslint-disable no-undefined,no-param-reassign,no-shadow */
  267. /**
  268. * Throttle execution of a function. Especially useful for rate limiting
  269. * execution of handlers on events like resize and scroll.
  270. *
  271. * @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.
  272. * @param {boolean} [noTrailing] - Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds while the
  273. * throttled-function is being called. If noTrailing is false or unspecified, callback will be executed one final time
  274. * after the last throttled-function call. (After the throttled-function has not been called for `delay` milliseconds,
  275. * the internal counter is reset).
  276. * @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,
  277. * to `callback` when the throttled-function is executed.
  278. * @param {boolean} [debounceMode] - If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is false (at end),
  279. * schedule `callback` to execute after `delay` ms.
  280. *
  281. * @returns {Function} A new, throttled, function.
  282. */
  283. function throttle (delay, noTrailing, callback, debounceMode) {
  284. /*
  285. * After wrapper has stopped being called, this timeout ensures that
  286. * `callback` is executed at the proper times in `throttle` and `end`
  287. * debounce modes.
  288. */
  289. var timeoutID;
  290. var cancelled = false; // Keep track of the last time `callback` was executed.
  291. var lastExec = 0; // Function to clear existing timeout
  292. function clearExistingTimeout() {
  293. if (timeoutID) {
  294. clearTimeout(timeoutID);
  295. }
  296. } // Function to cancel next exec
  297. function cancel() {
  298. clearExistingTimeout();
  299. cancelled = true;
  300. } // `noTrailing` defaults to falsy.
  301. if (typeof noTrailing !== 'boolean') {
  302. debounceMode = callback;
  303. callback = noTrailing;
  304. noTrailing = undefined;
  305. }
  306. /*
  307. * The `wrapper` function encapsulates all of the throttling / debouncing
  308. * functionality and when executed will limit the rate at which `callback`
  309. * is executed.
  310. */
  311. function wrapper() {
  312. for (var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++) {
  313. arguments_[_key] = arguments[_key];
  314. }
  315. var self = this;
  316. var elapsed = Date.now() - lastExec;
  317. if (cancelled) {
  318. return;
  319. } // Execute `callback` and update the `lastExec` timestamp.
  320. function exec() {
  321. lastExec = Date.now();
  322. callback.apply(self, arguments_);
  323. }
  324. /*
  325. * If `debounceMode` is true (at begin) this is used to clear the flag
  326. * to allow future `callback` executions.
  327. */
  328. function clear() {
  329. timeoutID = undefined;
  330. }
  331. if (debounceMode && !timeoutID) {
  332. /*
  333. * Since `wrapper` is being called for the first time and
  334. * `debounceMode` is true (at begin), execute `callback`.
  335. */
  336. exec();
  337. }
  338. clearExistingTimeout();
  339. if (debounceMode === undefined && elapsed > delay) {
  340. /*
  341. * In throttle mode, if `delay` time has been exceeded, execute
  342. * `callback`.
  343. */
  344. exec();
  345. } else if (noTrailing !== true) {
  346. /*
  347. * In trailing throttle mode, since `delay` time has not been
  348. * exceeded, schedule `callback` to execute `delay` ms after most
  349. * recent execution.
  350. *
  351. * If `debounceMode` is true (at begin), schedule `clear` to execute
  352. * after `delay` ms.
  353. *
  354. * If `debounceMode` is false (at end), schedule `callback` to
  355. * execute after `delay` ms.
  356. */
  357. timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
  358. }
  359. }
  360. wrapper.cancel = cancel; // Return the wrapper function.
  361. return wrapper;
  362. }
  363. /* eslint-disable no-undefined */
  364. /**
  365. * Debounce execution of a function. Debouncing, unlike throttling,
  366. * guarantees that a function is only executed a single time, either at the
  367. * very beginning of a series of calls, or at the very end.
  368. *
  369. * @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.
  370. * @param {boolean} [atBegin] - Optional, defaults to false. If atBegin is false or unspecified, callback will only be executed `delay` milliseconds
  371. * after the last debounced-function call. If atBegin is true, callback will be executed only at the first debounced-function call.
  372. * (After the throttled-function has not been called for `delay` milliseconds, the internal counter is reset).
  373. * @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,
  374. * to `callback` when the debounced-function is executed.
  375. *
  376. * @returns {Function} A new, debounced function.
  377. */
  378. function debounce (delay, atBegin, callback) {
  379. return callback === undefined ? throttle(delay, atBegin, false) : throttle(delay, callback, atBegin !== false);
  380. }
  381. /*
  382. How it works:
  383. `this.#head` is an instance of `Node` which keeps track of its current value and nests another instance of `Node` that keeps the value that comes after it. When a value is provided to `.enqueue()`, the code needs to iterate through `this.#head`, going deeper and deeper to find the last value. However, iterating through every single item is slow. This problem is solved by saving a reference to the last value as `this.#tail` so that it can reference it to add a new value.
  384. */
  385. class Node {
  386. value;
  387. next;
  388. constructor(value) {
  389. this.value = value;
  390. }
  391. }
  392. class Queue {
  393. #head;
  394. #tail;
  395. #size;
  396. constructor() {
  397. this.clear();
  398. }
  399. enqueue(value) {
  400. const node = new Node(value);
  401. if (this.#head) {
  402. this.#tail.next = node;
  403. this.#tail = node;
  404. } else {
  405. this.#head = node;
  406. this.#tail = node;
  407. }
  408. this.#size++;
  409. }
  410. dequeue() {
  411. const current = this.#head;
  412. if (!current) {
  413. return;
  414. }
  415. this.#head = this.#head.next;
  416. this.#size--;
  417. return current.value;
  418. }
  419. clear() {
  420. this.#head = undefined;
  421. this.#tail = undefined;
  422. this.#size = 0;
  423. }
  424. get size() {
  425. return this.#size;
  426. }
  427. * [Symbol.iterator]() {
  428. let current = this.#head;
  429. while (current) {
  430. yield current.value;
  431. current = current.next;
  432. }
  433. }
  434. }
  435. function pLimit(concurrency) {
  436. if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
  437. throw new TypeError('Expected `concurrency` to be a number from 1 and up');
  438. }
  439. const queue = new Queue();
  440. let activeCount = 0;
  441. const next = () => {
  442. activeCount--;
  443. if (queue.size > 0) {
  444. queue.dequeue()();
  445. }
  446. };
  447. const run = async (fn, resolve, args) => {
  448. activeCount++;
  449. const result = (async () => fn(...args))();
  450. resolve(result);
  451. try {
  452. await result;
  453. } catch {}
  454. next();
  455. };
  456. const enqueue = (fn, resolve, args) => {
  457. queue.enqueue(run.bind(undefined, fn, resolve, args));
  458. (async () => {
  459. // This function needs to wait until the next microtask before comparing
  460. // `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
  461. // when the run function is dequeued and called. The comparison in the if-statement
  462. // needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
  463. await Promise.resolve();
  464. if (activeCount < concurrency && queue.size > 0) {
  465. queue.dequeue()();
  466. }
  467. })();
  468. };
  469. const generator = (fn, ...args) => new Promise(resolve => {
  470. enqueue(fn, resolve, args);
  471. });
  472. Object.defineProperties(generator, {
  473. activeCount: {
  474. get: () => activeCount,
  475. },
  476. pendingCount: {
  477. get: () => queue.size,
  478. },
  479. clearQueue: {
  480. value: () => {
  481. queue.clear();
  482. },
  483. },
  484. });
  485. return generator;
  486. }
  487. const VOID = Symbol("p-void");
  488. class PInstance extends Promise {
  489. constructor(items = [], options) {
  490. super(() => {
  491. });
  492. this.items = items;
  493. this.options = options;
  494. this.promises = /* @__PURE__ */ new Set();
  495. }
  496. get promise() {
  497. var _a;
  498. let batch;
  499. const items = [...Array.from(this.items), ...Array.from(this.promises)];
  500. if ((_a = this.options) == null ? void 0 : _a.concurrency) {
  501. const limit = pLimit(this.options.concurrency);
  502. batch = Promise.all(items.map((p2) => limit(() => p2)));
  503. } else {
  504. batch = Promise.all(items);
  505. }
  506. return batch.then((l) => l.filter((i) => i !== VOID));
  507. }
  508. add(...args) {
  509. args.forEach((i) => {
  510. this.promises.add(i);
  511. });
  512. }
  513. map(fn) {
  514. return new PInstance(Array.from(this.items).map(async (i, idx) => {
  515. const v = await i;
  516. if (v === VOID)
  517. return VOID;
  518. return fn(v, idx);
  519. }), this.options);
  520. }
  521. filter(fn) {
  522. return new PInstance(Array.from(this.items).map(async (i, idx) => {
  523. const v = await i;
  524. const r = await fn(v, idx);
  525. if (!r)
  526. return VOID;
  527. return v;
  528. }), this.options);
  529. }
  530. forEach(fn) {
  531. return this.map(fn).then();
  532. }
  533. reduce(fn, initialValue) {
  534. return this.promise.then((array) => array.reduce(fn, initialValue));
  535. }
  536. clear() {
  537. this.promises.clear();
  538. }
  539. then(fn) {
  540. const p2 = this.promise;
  541. if (fn)
  542. return p2.then(fn);
  543. else
  544. return p2;
  545. }
  546. catch(fn) {
  547. return this.promise.catch(fn);
  548. }
  549. finally(fn) {
  550. return this.promise.finally(fn);
  551. }
  552. }
  553. function p(items, options) {
  554. return new PInstance(items, options);
  555. }
  556. export { assert, at, batchInvoke, clamp, clampArrayRange, clearUndefined, createControlledPromise, createPromiseLock, createSingletonPromise, debounce, deepMerge, ensurePrefix, ensureSuffix, flattenArrayable, hasOwnProperty, invoke, isBoolean, isBrowser, isDef, isFunction, isKeyOf, isNumber, isObject, isString, isTruthy, isWindow, last, mergeArrayable, move, noNull, noop, notNullish, notUndefined, objectEntries, objectKeys, objectMap, objectPick, p, partition, randomStr, range, remove, sample, shuffle, slash, sleep, sum, tap, template, throttle, timestamp, toArray, toString, uniq };