@yorozu/utils 0.5.0 → 0.5.2

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/async/index.d.ts CHANGED
@@ -7,5 +7,6 @@ export * from './condition-variable';
7
7
  export * from './deferred';
8
8
  export * from './emitter';
9
9
  export * from './pool';
10
+ export * from './priority-work-queue';
10
11
  export * from './sleep';
11
12
  export { timers };
@@ -0,0 +1,25 @@
1
+ export type WorkPri = "visible" | "preload" | "background";
2
+ export type PriorityWorkJob = {
3
+ id: string;
4
+ pri: WorkPri;
5
+ run: (ctx: {
6
+ signal: AbortSignal;
7
+ }) => Promise<void>;
8
+ };
9
+ export type PriorityWorkQueueStats = {
10
+ active: number;
11
+ queued: number;
12
+ maxActive: number;
13
+ };
14
+ export type PriorityWorkQueueOptions = {
15
+ concurrency?: number;
16
+ onError?: (error: Error, id: string) => void;
17
+ };
18
+ export type PriorityWorkQueue = {
19
+ enqueue(job: PriorityWorkJob): boolean;
20
+ cancel(id: string): boolean;
21
+ cancelAll(): void;
22
+ isBusy(id: string): boolean;
23
+ get stats(): PriorityWorkQueueStats;
24
+ };
25
+ export declare function createPriorityWorkQueue(opts?: PriorityWorkQueueOptions): PriorityWorkQueue;
package/index.js CHANGED
@@ -1803,26 +1803,6 @@ async function parallelMap(iterable, executor, options = {}) {
1803
1803
  return result;
1804
1804
  }
1805
1805
  //#endregion
1806
- //#region src/async/sleep.ts
1807
- function sleep(ms, signal) {
1808
- if (ms < 0) throw new RangeError("sleep: ms must be a non-negative number");
1809
- return new Promise((resolve, reject) => {
1810
- if (signal?.aborted) {
1811
- reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError"));
1812
- return;
1813
- }
1814
- let onAbort = () => {
1815
- clearTimeout(timeoutId);
1816
- reject(signal?.reason ?? new DOMException("The operation was aborted.", "AbortError"));
1817
- };
1818
- let timeoutId = setTimeout(() => {
1819
- signal?.removeEventListener("abort", onAbort);
1820
- resolve();
1821
- }, ms);
1822
- signal?.addEventListener("abort", onAbort, { once: true });
1823
- });
1824
- }
1825
- //#endregion
1826
1806
  //#region src/types/error.ts
1827
1807
  function unknownToError(err) {
1828
1808
  if (err instanceof Error) return err;
@@ -1845,6 +1825,144 @@ function throwUnreachable() {
1845
1825
  throw new Error("Unreachable");
1846
1826
  }
1847
1827
  //#endregion
1828
+ //#region src/async/priority-work-queue.ts
1829
+ var PRI_ORDER = [
1830
+ "visible",
1831
+ "preload",
1832
+ "background"
1833
+ ];
1834
+ function isAbortError(err) {
1835
+ return err instanceof Error && err.name === "AbortError";
1836
+ }
1837
+ function emptyLanes() {
1838
+ return {
1839
+ visible: [],
1840
+ preload: [],
1841
+ background: []
1842
+ };
1843
+ }
1844
+ function resolveConcurrency(value) {
1845
+ if (value === void 0) return 3;
1846
+ let n = Math.floor(value);
1847
+ if (!Number.isFinite(n) || n < 1) return 1;
1848
+ return n;
1849
+ }
1850
+ function createPriorityWorkQueue(opts) {
1851
+ let concurrency = resolveConcurrency(opts?.concurrency);
1852
+ let lanes = emptyLanes();
1853
+ let queuedPri = /* @__PURE__ */ new Map();
1854
+ let active = /* @__PURE__ */ new Map();
1855
+ let maxActive = 0;
1856
+ function queuedCount() {
1857
+ return queuedPri.size;
1858
+ }
1859
+ function removeQueued(id) {
1860
+ let pri = queuedPri.get(id);
1861
+ if (pri === void 0) return void 0;
1862
+ let lane = lanes[pri];
1863
+ let idx = lane.findIndex((job) => job.id === id);
1864
+ queuedPri.delete(id);
1865
+ if (idx < 0) return void 0;
1866
+ return lane.splice(idx, 1)[0];
1867
+ }
1868
+ function pickNext() {
1869
+ for (let pri of PRI_ORDER) {
1870
+ let lane = lanes[pri];
1871
+ while (lane.length > 0) {
1872
+ let job = lane.shift();
1873
+ queuedPri.delete(job.id);
1874
+ if (active.has(job.id)) continue;
1875
+ return job;
1876
+ }
1877
+ }
1878
+ }
1879
+ function pump() {
1880
+ while (active.size < concurrency) {
1881
+ let job = pickNext();
1882
+ if (!job) return;
1883
+ startJob(job);
1884
+ }
1885
+ }
1886
+ function startJob(job) {
1887
+ let controller = new AbortController();
1888
+ active.set(job.id, {
1889
+ id: job.id,
1890
+ controller
1891
+ });
1892
+ if (active.size > maxActive) maxActive = active.size;
1893
+ (async () => {
1894
+ let reported;
1895
+ try {
1896
+ await job.run({ signal: controller.signal });
1897
+ } catch (err) {
1898
+ if (!isAbortError(err)) reported = unknownToError(err);
1899
+ } finally {
1900
+ active.delete(job.id);
1901
+ }
1902
+ if (reported) try {
1903
+ opts?.onError?.(reported, job.id);
1904
+ } catch {}
1905
+ pump();
1906
+ })();
1907
+ }
1908
+ return {
1909
+ enqueue(job) {
1910
+ if (active.has(job.id)) return false;
1911
+ removeQueued(job.id);
1912
+ lanes[job.pri].push({
1913
+ id: job.id,
1914
+ pri: job.pri,
1915
+ run: job.run
1916
+ });
1917
+ queuedPri.set(job.id, job.pri);
1918
+ pump();
1919
+ return true;
1920
+ },
1921
+ cancel(id) {
1922
+ if (removeQueued(id)) return true;
1923
+ let running = active.get(id);
1924
+ if (!running) return false;
1925
+ running.controller.abort();
1926
+ return true;
1927
+ },
1928
+ cancelAll() {
1929
+ lanes = emptyLanes();
1930
+ queuedPri.clear();
1931
+ for (let running of active.values()) running.controller.abort();
1932
+ },
1933
+ isBusy(id) {
1934
+ return active.has(id) || queuedPri.has(id);
1935
+ },
1936
+ get stats() {
1937
+ return {
1938
+ active: active.size,
1939
+ queued: queuedCount(),
1940
+ maxActive
1941
+ };
1942
+ }
1943
+ };
1944
+ }
1945
+ //#endregion
1946
+ //#region src/async/sleep.ts
1947
+ function sleep(ms, signal) {
1948
+ if (ms < 0) throw new RangeError("sleep: ms must be a non-negative number");
1949
+ return new Promise((resolve, reject) => {
1950
+ if (signal?.aborted) {
1951
+ reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError"));
1952
+ return;
1953
+ }
1954
+ let onAbort = () => {
1955
+ clearTimeout(timeoutId);
1956
+ reject(signal?.reason ?? new DOMException("The operation was aborted.", "AbortError"));
1957
+ };
1958
+ let timeoutId = setTimeout(() => {
1959
+ signal?.removeEventListener("abort", onAbort);
1960
+ resolve();
1961
+ }, ms);
1962
+ signal?.addEventListener("abort", onAbort, { once: true });
1963
+ });
1964
+ }
1965
+ //#endregion
1848
1966
  //#region src/compress/checksum.ts
1849
1967
  var crcTable = new Int32Array(256);
1850
1968
  for (let i = 0; i < 256; i++) {
@@ -3180,4 +3298,4 @@ var decompress_exports = /* @__PURE__ */ __exportAll({
3180
3298
  zlib: () => decompress$1
3181
3299
  });
3182
3300
  //#endregion
3183
- export { Adler32, AggregateError, AsyncInterval, AsyncLock, AsyncQueue, AsyncResource, ChecksumMismatchError, ConditionVariable, Crc32, CustomMap, CustomSet, Deferred, DeferredTracked, Deque, Emitter, FlateError, InvalidBlockTypeError, InvalidDistanceError, InvalidHeaderError, InvalidLengthLiteralError, LruMap, LruSet, NotImplementedError, StreamFinishedError, UnexpectedEofError, abs, adler32, asNonNull, assert, assertEndsWith, assertEndsWith as assertsEndsWith, assertHashKey, assertMatches, assertNotNull, assertStartsWith, asyncPool, base64_exports as base64, bitLength, clearUndefinedInPlace, composeMiddlewares, compress_exports as compress, crc32, decompress_exports as decompress, deepMerge, enumerate, euclideanGcd, fromBytes, hex_exports as hex, isBigInt, isBoolean, isFalsy, isFunction, isNotNull, isNotUndefined, isNumber, isObject, isString, isSymbol, isTruthy, max, max2, min, min2, modInv, modPowBinary, noop, objectEntries, objectKeys, parallelMap, sleep, splitOnce, throwNotImplemented, throwUnreachable, timers_exports as timers, toBytes, twoMultiplicity, typed_exports as typed, u8_exports as u8, unknownToError, unsafeCastType, utf8_exports as utf8 };
3301
+ export { Adler32, AggregateError, AsyncInterval, AsyncLock, AsyncQueue, AsyncResource, ChecksumMismatchError, ConditionVariable, Crc32, CustomMap, CustomSet, Deferred, DeferredTracked, Deque, Emitter, FlateError, InvalidBlockTypeError, InvalidDistanceError, InvalidHeaderError, InvalidLengthLiteralError, LruMap, LruSet, NotImplementedError, StreamFinishedError, UnexpectedEofError, abs, adler32, asNonNull, assert, assertEndsWith, assertEndsWith as assertsEndsWith, assertHashKey, assertMatches, assertNotNull, assertStartsWith, asyncPool, base64_exports as base64, bitLength, clearUndefinedInPlace, composeMiddlewares, compress_exports as compress, crc32, createPriorityWorkQueue, decompress_exports as decompress, deepMerge, enumerate, euclideanGcd, fromBytes, hex_exports as hex, isBigInt, isBoolean, isFalsy, isFunction, isNotNull, isNotUndefined, isNumber, isObject, isString, isSymbol, isTruthy, max, max2, min, min2, modInv, modPowBinary, noop, objectEntries, objectKeys, parallelMap, sleep, splitOnce, throwNotImplemented, throwUnreachable, timers_exports as timers, toBytes, twoMultiplicity, typed_exports as typed, u8_exports as u8, unknownToError, unsafeCastType, utf8_exports as utf8 };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yorozu/utils",
3
3
  "type": "module",
4
- "version": "0.5.0",
4
+ "version": "0.5.2",
5
5
  "description": "quality of life",
6
6
  "license": "MIT",
7
7
  "exports": {