@ls-stack/utils 3.9.0 → 3.10.0

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/README.md CHANGED
@@ -1 +1,82 @@
1
- # Generic typescript utils
1
+ # @ls-stack/utils
2
+
3
+ Generic TypeScript utilities for modern JavaScript/TypeScript projects.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @ls-stack/utils
9
+ # or
10
+ pnpm add @ls-stack/utils
11
+ # or
12
+ yarn add @ls-stack/utils
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ Import specific utilities from their modules:
18
+
19
+ ```typescript
20
+ import { createAsyncQueue } from '@ls-stack/utils/asyncQueue';
21
+ import { deepEqual } from '@ls-stack/utils/deepEqual';
22
+ import { debounce } from '@ls-stack/utils/debounce';
23
+ ```
24
+
25
+ ## Available Utilities
26
+
27
+ This package includes a wide range of utilities for:
28
+
29
+ - **Array manipulation** (`arrayUtils`) - sorting, grouping, filtering
30
+ - **Async operations** (`asyncQueue`, `parallelAsyncCalls`, `promiseUtils`) - queue management and promise utilities
31
+ - **Type assertions** (`assertions`) - runtime type checking
32
+ - **Caching** (`cache`) - efficient caching with TTL support
33
+ - **Concurrency control** (`concurrentCalls`, `createThrottleController`) - rate limiting and throttling
34
+ - **Object utilities** (`objUtils`) - deep operations on objects
35
+ - **String utilities** (`stringUtils`) - string manipulation and formatting
36
+ - **Math utilities** (`mathUtils`) - mathematical operations and calculations
37
+ - **And many more...**
38
+
39
+ ## Documentation
40
+
41
+ Comprehensive API documentation is available in the [`docs/`](docs/) folder. Start with the [modules overview](docs/modules.md) to explore all available utilities.
42
+
43
+ ### Generating Documentation
44
+
45
+ To regenerate the documentation after making changes:
46
+
47
+ ```bash
48
+ pnpm docs
49
+ ```
50
+
51
+ For continuous updates during development:
52
+
53
+ ```bash
54
+ pnpm docs:watch
55
+ ```
56
+
57
+ ## Development
58
+
59
+ ```bash
60
+ # Install dependencies
61
+ pnpm install
62
+
63
+ # Run tests
64
+ pnpm test
65
+
66
+ # Run tests with UI
67
+ pnpm test:ui
68
+
69
+ # Build the library
70
+ pnpm build
71
+
72
+ # Lint code
73
+ pnpm lint
74
+ ```
75
+
76
+ ## License
77
+
78
+ MIT
79
+
80
+ ## Repository
81
+
82
+ [github:lucasols/utils](https://github.com/lucasols/utils)
@@ -21,23 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var asyncQueue_exports = {};
22
22
  __export(asyncQueue_exports, {
23
23
  createAsyncQueue: () => createAsyncQueue,
24
- createAsyncQueueWithId: () => createAsyncQueueWithId
24
+ createAsyncQueueWithMeta: () => createAsyncQueueWithMeta
25
25
  });
26
26
  module.exports = __toCommonJS(asyncQueue_exports);
27
27
  var import_evtmitter = require("evtmitter");
28
28
  var import_t_result = require("t-result");
29
29
 
30
- // src/assertions.ts
31
- function isObject(value) {
32
- return typeof value === "object" && value !== null && !Array.isArray(value);
33
- }
34
- function isFunction(value) {
35
- return typeof value === "function";
36
- }
37
- function isPromise(value) {
38
- return isObject(value) && "then" in value && isFunction(value.then);
39
- }
40
-
41
30
  // src/promiseUtils.ts
42
31
  function defer() {
43
32
  let resolve;
@@ -74,36 +63,48 @@ var AsyncQueue = class {
74
63
  this.#queue.push(task);
75
64
  this.#size++;
76
65
  }
77
- add(fn, options) {
66
+ async add(fn, options) {
67
+ if (this.#signal?.aborted) {
68
+ return import_t_result.Result.err(
69
+ this.#signal.reason instanceof Error ? this.#signal.reason : new DOMException("Queue aborted", "AbortError")
70
+ );
71
+ }
78
72
  const deferred = defer();
79
73
  const taskTimeout = this.#taskTimeout ?? options?.timeout;
80
74
  const task = {
81
75
  run: async (ctx) => {
82
- if (isPromise(fn)) {
83
- return fn;
84
- }
85
- return await fn(ctx);
76
+ return fn(ctx);
86
77
  },
87
78
  resolve: deferred.resolve,
88
79
  reject: deferred.reject,
89
80
  signal: options?.signal,
90
- id: options?.id,
81
+ meta: options?.meta,
91
82
  timeout: taskTimeout
92
83
  };
93
84
  this.#enqueue(task);
94
85
  this.#processQueue();
95
- return deferred.promise;
86
+ const r = await deferred.promise;
87
+ if (options?.onComplete) {
88
+ r.onOk(options.onComplete);
89
+ }
90
+ if (options?.onError) {
91
+ r.onErr(options.onError);
92
+ }
93
+ return r;
96
94
  }
97
95
  resultifyAdd(fn, options) {
98
- const cb = async (ctx) => {
99
- if (isPromise(fn)) {
100
- return await fn;
101
- }
102
- return fn(ctx);
103
- };
104
- return this.add((ctx) => (0, import_t_result.resultify)(cb(ctx)), options);
96
+ return this.add(
97
+ (ctx) => (0, import_t_result.resultify)(async () => {
98
+ return fn(ctx);
99
+ }),
100
+ options
101
+ );
105
102
  }
106
103
  async #processQueue() {
104
+ if (this.#signal?.aborted) {
105
+ this.clear();
106
+ return;
107
+ }
107
108
  if (this.#pending >= this.#concurrency || this.#queue.length === 0) {
108
109
  return;
109
110
  }
@@ -125,45 +126,54 @@ var AsyncQueue = class {
125
126
  }
126
127
  const signal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
127
128
  let abortListener;
128
- const signalAbortPromise = new Promise((_, reject) => {
129
- if (signal) {
130
- const error = signal.reason instanceof Error ? signal.reason : new DOMException("Aborted", "AbortError");
131
- if (signal.aborted) {
132
- reject(error);
133
- return;
134
- }
135
- abortListener = () => {
136
- reject(error);
137
- };
138
- signal.addEventListener("abort", abortListener, { once: true });
139
- }
140
- });
141
129
  try {
142
- const taskRunPromise = task.run({ signal, id: task.id });
143
- this.events.emit("start", { id: task.id });
130
+ if (signal?.aborted) {
131
+ const error = signal.reason instanceof Error ? signal.reason : new DOMException("This operation was aborted", "AbortError");
132
+ throw error;
133
+ }
134
+ const signalAbortPromise = new Promise((_, reject) => {
135
+ if (signal) {
136
+ const error = signal.reason instanceof Error ? signal.reason : new DOMException("This operation was aborted", "AbortError");
137
+ abortListener = () => {
138
+ reject(error);
139
+ };
140
+ signal.addEventListener("abort", abortListener, { once: true });
141
+ }
142
+ });
143
+ const taskRunPromise = task.run({ signal, meta: task.meta });
144
+ this.events.emit("start", { meta: task.meta });
144
145
  const result = await Promise.race([taskRunPromise, signalAbortPromise]);
145
146
  if ((0, import_t_result.isResult)(result)) {
146
147
  task.resolve(result);
147
148
  if (result.error) {
148
149
  this.#failed++;
149
- this.events.emit("error", { id: task.id, error: result.error });
150
+ this.events.emit("error", {
151
+ meta: task.meta,
152
+ error: result.error
153
+ });
150
154
  } else {
151
155
  this.#completed++;
152
- this.events.emit("complete", { id: task.id, value: result.value });
156
+ this.events.emit("complete", {
157
+ meta: task.meta,
158
+ value: result.value
159
+ });
153
160
  }
154
161
  } else {
155
162
  const error = new Error("Response not a Result");
156
163
  task.resolve(import_t_result.Result.err(error));
157
164
  this.#failed++;
158
165
  this.events.emit("error", {
159
- id: task.id,
160
- error: (0, import_t_result.unknownToError)(error)
166
+ meta: task.meta,
167
+ error
161
168
  });
162
169
  }
163
170
  } catch (error) {
164
171
  task.resolve(import_t_result.Result.err(error));
165
172
  this.#failed++;
166
- this.events.emit("error", { id: task.id, error: (0, import_t_result.unknownToError)(error) });
173
+ this.events.emit("error", {
174
+ meta: task.meta,
175
+ error: (0, import_t_result.unknownToError)(error)
176
+ });
167
177
  } finally {
168
178
  if (signal && abortListener) {
169
179
  signal.removeEventListener("abort", abortListener);
@@ -191,13 +201,9 @@ var AsyncQueue = class {
191
201
  this.#idleResolvers.push(resolve);
192
202
  });
193
203
  }
194
- clear({ resetCounters = true } = {}) {
204
+ clear() {
195
205
  this.#queue = [];
196
206
  this.#size = 0;
197
- if (resetCounters) {
198
- this.#completed = 0;
199
- this.#failed = 0;
200
- }
201
207
  if (this.#pending === 0) {
202
208
  this.#resolveIdleWaiters();
203
209
  }
@@ -215,7 +221,7 @@ var AsyncQueue = class {
215
221
  return this.#size;
216
222
  }
217
223
  };
218
- var AsyncQueueWithId = class extends AsyncQueue {
224
+ var AsyncQueueWithMeta = class extends AsyncQueue {
219
225
  constructor(options) {
220
226
  super(options);
221
227
  }
@@ -229,11 +235,11 @@ var AsyncQueueWithId = class extends AsyncQueue {
229
235
  function createAsyncQueue(options) {
230
236
  return new AsyncQueue(options);
231
237
  }
232
- function createAsyncQueueWithId(options) {
233
- return new AsyncQueueWithId(options);
238
+ function createAsyncQueueWithMeta(options) {
239
+ return new AsyncQueueWithMeta(options);
234
240
  }
235
241
  // Annotate the CommonJS export names for ESM import in node:
236
242
  0 && (module.exports = {
237
243
  createAsyncQueue,
238
- createAsyncQueueWithId
244
+ createAsyncQueueWithMeta
239
245
  });
@@ -6,51 +6,51 @@ type AsyncQueueOptions = {
6
6
  signal?: AbortSignal;
7
7
  timeout?: number;
8
8
  };
9
- type AddOptions<I> = {
9
+ type AddOptions<I, T, E extends ResultValidErrors> = {
10
10
  signal?: AbortSignal;
11
11
  timeout?: number;
12
- id?: I;
12
+ meta?: I;
13
+ onComplete?: (value: T) => void;
14
+ onError?: (error: E | Error) => void;
13
15
  };
14
16
  type RunCtx<I> = {
15
17
  signal?: AbortSignal;
16
- id?: I;
18
+ meta?: I;
17
19
  };
18
- declare class AsyncQueue<T, E extends ResultValidErrors = Error, I = undefined> {
20
+ declare class AsyncQueue<T, E extends ResultValidErrors = Error, I = unknown> {
19
21
  #private;
20
22
  events: evtmitter.Emitter<{
21
23
  error: {
22
- id: I;
24
+ meta: I;
23
25
  error: E | Error;
24
26
  };
25
27
  complete: {
26
- id: I;
28
+ meta: I;
27
29
  value: T;
28
30
  };
29
31
  start: {
30
- id: I;
32
+ meta: I;
31
33
  };
32
34
  }>;
33
35
  constructor({ concurrency, signal, timeout: taskTimeout, }?: AsyncQueueOptions);
34
- add(fn: ((ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>) | Promise<Result<T, E>>, options?: AddOptions<I>): Promise<Result<T, E | Error>>;
35
- resultifyAdd(fn: ((ctx: RunCtx<I>) => Promise<T> | T) | Promise<T>, options?: AddOptions<I>): Promise<Result<T, E | Error>>;
36
+ add(fn: (ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>, options?: AddOptions<I, T, E>): Promise<Result<T, E | Error>>;
37
+ resultifyAdd(fn: (ctx: RunCtx<I>) => Promise<T> | T, options?: AddOptions<I, T, E>): Promise<Result<T, E | Error>>;
36
38
  onIdle(): Promise<void>;
37
- clear({ resetCounters }?: {
38
- resetCounters?: boolean;
39
- }): void;
39
+ clear(): void;
40
40
  get completed(): number;
41
41
  get failed(): number;
42
42
  get pending(): number;
43
43
  get size(): number;
44
44
  }
45
- type AddOptionsWithId<I> = Omit<AddOptions<I>, 'id'> & {
46
- id: I;
45
+ type AddOptionsWithId<I, T, E extends ResultValidErrors> = Omit<AddOptions<I, T, E>, 'meta'> & {
46
+ meta: I;
47
47
  };
48
- declare class AsyncQueueWithId<T, I, E extends ResultValidErrors = Error> extends AsyncQueue<T, E, I> {
48
+ declare class AsyncQueueWithMeta<T, I, E extends ResultValidErrors = Error> extends AsyncQueue<T, E, I> {
49
49
  constructor(options?: AsyncQueueOptions);
50
- add(fn: ((ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>) | Promise<Result<T, E>>, options?: AddOptionsWithId<I>): Promise<Result<T, E | Error>>;
51
- resultifyAdd(fn: ((ctx: RunCtx<I>) => Promise<T> | T) | Promise<T>, options?: AddOptionsWithId<I>): Promise<Result<T, E | Error>>;
50
+ add(fn: (ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>, options?: AddOptionsWithId<I, T, E>): Promise<Result<T, E | Error>>;
51
+ resultifyAdd(fn: (ctx: RunCtx<I>) => Promise<T> | T, options?: AddOptionsWithId<I, T, E>): Promise<Result<T, E | Error>>;
52
52
  }
53
- declare function createAsyncQueue<T, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueue<T, E, undefined>;
54
- declare function createAsyncQueueWithId<T, I, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueueWithId<T, I, E>;
53
+ declare function createAsyncQueue<T, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueue<T, E>;
54
+ declare function createAsyncQueueWithMeta<T, I, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueueWithMeta<T, I, E>;
55
55
 
56
- export { createAsyncQueue, createAsyncQueueWithId };
56
+ export { createAsyncQueue, createAsyncQueueWithMeta };
@@ -6,51 +6,51 @@ type AsyncQueueOptions = {
6
6
  signal?: AbortSignal;
7
7
  timeout?: number;
8
8
  };
9
- type AddOptions<I> = {
9
+ type AddOptions<I, T, E extends ResultValidErrors> = {
10
10
  signal?: AbortSignal;
11
11
  timeout?: number;
12
- id?: I;
12
+ meta?: I;
13
+ onComplete?: (value: T) => void;
14
+ onError?: (error: E | Error) => void;
13
15
  };
14
16
  type RunCtx<I> = {
15
17
  signal?: AbortSignal;
16
- id?: I;
18
+ meta?: I;
17
19
  };
18
- declare class AsyncQueue<T, E extends ResultValidErrors = Error, I = undefined> {
20
+ declare class AsyncQueue<T, E extends ResultValidErrors = Error, I = unknown> {
19
21
  #private;
20
22
  events: evtmitter.Emitter<{
21
23
  error: {
22
- id: I;
24
+ meta: I;
23
25
  error: E | Error;
24
26
  };
25
27
  complete: {
26
- id: I;
28
+ meta: I;
27
29
  value: T;
28
30
  };
29
31
  start: {
30
- id: I;
32
+ meta: I;
31
33
  };
32
34
  }>;
33
35
  constructor({ concurrency, signal, timeout: taskTimeout, }?: AsyncQueueOptions);
34
- add(fn: ((ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>) | Promise<Result<T, E>>, options?: AddOptions<I>): Promise<Result<T, E | Error>>;
35
- resultifyAdd(fn: ((ctx: RunCtx<I>) => Promise<T> | T) | Promise<T>, options?: AddOptions<I>): Promise<Result<T, E | Error>>;
36
+ add(fn: (ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>, options?: AddOptions<I, T, E>): Promise<Result<T, E | Error>>;
37
+ resultifyAdd(fn: (ctx: RunCtx<I>) => Promise<T> | T, options?: AddOptions<I, T, E>): Promise<Result<T, E | Error>>;
36
38
  onIdle(): Promise<void>;
37
- clear({ resetCounters }?: {
38
- resetCounters?: boolean;
39
- }): void;
39
+ clear(): void;
40
40
  get completed(): number;
41
41
  get failed(): number;
42
42
  get pending(): number;
43
43
  get size(): number;
44
44
  }
45
- type AddOptionsWithId<I> = Omit<AddOptions<I>, 'id'> & {
46
- id: I;
45
+ type AddOptionsWithId<I, T, E extends ResultValidErrors> = Omit<AddOptions<I, T, E>, 'meta'> & {
46
+ meta: I;
47
47
  };
48
- declare class AsyncQueueWithId<T, I, E extends ResultValidErrors = Error> extends AsyncQueue<T, E, I> {
48
+ declare class AsyncQueueWithMeta<T, I, E extends ResultValidErrors = Error> extends AsyncQueue<T, E, I> {
49
49
  constructor(options?: AsyncQueueOptions);
50
- add(fn: ((ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>) | Promise<Result<T, E>>, options?: AddOptionsWithId<I>): Promise<Result<T, E | Error>>;
51
- resultifyAdd(fn: ((ctx: RunCtx<I>) => Promise<T> | T) | Promise<T>, options?: AddOptionsWithId<I>): Promise<Result<T, E | Error>>;
50
+ add(fn: (ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>, options?: AddOptionsWithId<I, T, E>): Promise<Result<T, E | Error>>;
51
+ resultifyAdd(fn: (ctx: RunCtx<I>) => Promise<T> | T, options?: AddOptionsWithId<I, T, E>): Promise<Result<T, E | Error>>;
52
52
  }
53
- declare function createAsyncQueue<T, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueue<T, E, undefined>;
54
- declare function createAsyncQueueWithId<T, I, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueueWithId<T, I, E>;
53
+ declare function createAsyncQueue<T, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueue<T, E>;
54
+ declare function createAsyncQueueWithMeta<T, I, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueueWithMeta<T, I, E>;
55
55
 
56
- export { createAsyncQueue, createAsyncQueueWithId };
56
+ export { createAsyncQueue, createAsyncQueueWithMeta };
package/lib/asyncQueue.js CHANGED
@@ -1,9 +1,6 @@
1
1
  import {
2
2
  defer
3
3
  } from "./chunk-DFXNVEH6.js";
4
- import {
5
- isPromise
6
- } from "./chunk-3XCS7FVO.js";
7
4
 
8
5
  // src/asyncQueue.ts
9
6
  import { evtmitter } from "evtmitter";
@@ -37,36 +34,48 @@ var AsyncQueue = class {
37
34
  this.#queue.push(task);
38
35
  this.#size++;
39
36
  }
40
- add(fn, options) {
37
+ async add(fn, options) {
38
+ if (this.#signal?.aborted) {
39
+ return Result.err(
40
+ this.#signal.reason instanceof Error ? this.#signal.reason : new DOMException("Queue aborted", "AbortError")
41
+ );
42
+ }
41
43
  const deferred = defer();
42
44
  const taskTimeout = this.#taskTimeout ?? options?.timeout;
43
45
  const task = {
44
46
  run: async (ctx) => {
45
- if (isPromise(fn)) {
46
- return fn;
47
- }
48
- return await fn(ctx);
47
+ return fn(ctx);
49
48
  },
50
49
  resolve: deferred.resolve,
51
50
  reject: deferred.reject,
52
51
  signal: options?.signal,
53
- id: options?.id,
52
+ meta: options?.meta,
54
53
  timeout: taskTimeout
55
54
  };
56
55
  this.#enqueue(task);
57
56
  this.#processQueue();
58
- return deferred.promise;
57
+ const r = await deferred.promise;
58
+ if (options?.onComplete) {
59
+ r.onOk(options.onComplete);
60
+ }
61
+ if (options?.onError) {
62
+ r.onErr(options.onError);
63
+ }
64
+ return r;
59
65
  }
60
66
  resultifyAdd(fn, options) {
61
- const cb = async (ctx) => {
62
- if (isPromise(fn)) {
63
- return await fn;
64
- }
65
- return fn(ctx);
66
- };
67
- return this.add((ctx) => resultify(cb(ctx)), options);
67
+ return this.add(
68
+ (ctx) => resultify(async () => {
69
+ return fn(ctx);
70
+ }),
71
+ options
72
+ );
68
73
  }
69
74
  async #processQueue() {
75
+ if (this.#signal?.aborted) {
76
+ this.clear();
77
+ return;
78
+ }
70
79
  if (this.#pending >= this.#concurrency || this.#queue.length === 0) {
71
80
  return;
72
81
  }
@@ -88,45 +97,54 @@ var AsyncQueue = class {
88
97
  }
89
98
  const signal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
90
99
  let abortListener;
91
- const signalAbortPromise = new Promise((_, reject) => {
92
- if (signal) {
93
- const error = signal.reason instanceof Error ? signal.reason : new DOMException("Aborted", "AbortError");
94
- if (signal.aborted) {
95
- reject(error);
96
- return;
97
- }
98
- abortListener = () => {
99
- reject(error);
100
- };
101
- signal.addEventListener("abort", abortListener, { once: true });
102
- }
103
- });
104
100
  try {
105
- const taskRunPromise = task.run({ signal, id: task.id });
106
- this.events.emit("start", { id: task.id });
101
+ if (signal?.aborted) {
102
+ const error = signal.reason instanceof Error ? signal.reason : new DOMException("This operation was aborted", "AbortError");
103
+ throw error;
104
+ }
105
+ const signalAbortPromise = new Promise((_, reject) => {
106
+ if (signal) {
107
+ const error = signal.reason instanceof Error ? signal.reason : new DOMException("This operation was aborted", "AbortError");
108
+ abortListener = () => {
109
+ reject(error);
110
+ };
111
+ signal.addEventListener("abort", abortListener, { once: true });
112
+ }
113
+ });
114
+ const taskRunPromise = task.run({ signal, meta: task.meta });
115
+ this.events.emit("start", { meta: task.meta });
107
116
  const result = await Promise.race([taskRunPromise, signalAbortPromise]);
108
117
  if (isResult(result)) {
109
118
  task.resolve(result);
110
119
  if (result.error) {
111
120
  this.#failed++;
112
- this.events.emit("error", { id: task.id, error: result.error });
121
+ this.events.emit("error", {
122
+ meta: task.meta,
123
+ error: result.error
124
+ });
113
125
  } else {
114
126
  this.#completed++;
115
- this.events.emit("complete", { id: task.id, value: result.value });
127
+ this.events.emit("complete", {
128
+ meta: task.meta,
129
+ value: result.value
130
+ });
116
131
  }
117
132
  } else {
118
133
  const error = new Error("Response not a Result");
119
134
  task.resolve(Result.err(error));
120
135
  this.#failed++;
121
136
  this.events.emit("error", {
122
- id: task.id,
123
- error: unknownToError(error)
137
+ meta: task.meta,
138
+ error
124
139
  });
125
140
  }
126
141
  } catch (error) {
127
142
  task.resolve(Result.err(error));
128
143
  this.#failed++;
129
- this.events.emit("error", { id: task.id, error: unknownToError(error) });
144
+ this.events.emit("error", {
145
+ meta: task.meta,
146
+ error: unknownToError(error)
147
+ });
130
148
  } finally {
131
149
  if (signal && abortListener) {
132
150
  signal.removeEventListener("abort", abortListener);
@@ -154,13 +172,9 @@ var AsyncQueue = class {
154
172
  this.#idleResolvers.push(resolve);
155
173
  });
156
174
  }
157
- clear({ resetCounters = true } = {}) {
175
+ clear() {
158
176
  this.#queue = [];
159
177
  this.#size = 0;
160
- if (resetCounters) {
161
- this.#completed = 0;
162
- this.#failed = 0;
163
- }
164
178
  if (this.#pending === 0) {
165
179
  this.#resolveIdleWaiters();
166
180
  }
@@ -178,7 +192,7 @@ var AsyncQueue = class {
178
192
  return this.#size;
179
193
  }
180
194
  };
181
- var AsyncQueueWithId = class extends AsyncQueue {
195
+ var AsyncQueueWithMeta = class extends AsyncQueue {
182
196
  constructor(options) {
183
197
  super(options);
184
198
  }
@@ -192,10 +206,10 @@ var AsyncQueueWithId = class extends AsyncQueue {
192
206
  function createAsyncQueue(options) {
193
207
  return new AsyncQueue(options);
194
208
  }
195
- function createAsyncQueueWithId(options) {
196
- return new AsyncQueueWithId(options);
209
+ function createAsyncQueueWithMeta(options) {
210
+ return new AsyncQueueWithMeta(options);
197
211
  }
198
212
  export {
199
213
  createAsyncQueue,
200
- createAsyncQueueWithId
214
+ createAsyncQueueWithMeta
201
215
  };
@@ -288,7 +288,7 @@ var ConcurrentCallsWithMetadata = class {
288
288
  `${failedProcessing.length}/${total} calls failed: ${truncateArray(
289
289
  failedProcessing.map((f) => truncateString(f.error.message, 20)),
290
290
  5,
291
- (count) => `+${count} more`
291
+ "..."
292
292
  ).join("; ")}`
293
293
  ) : null;
294
294
  return {
@@ -235,7 +235,7 @@ var ConcurrentCallsWithMetadata = class {
235
235
  `${failedProcessing.length}/${total} calls failed: ${truncateArray(
236
236
  failedProcessing.map((f) => truncateString(f.error.message, 20)),
237
237
  5,
238
- (count) => `+${count} more`
238
+ "..."
239
239
  ).join("; ")}`
240
240
  ) : null;
241
241
  return {
package/lib/testUtils.cjs CHANGED
@@ -21,7 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var testUtils_exports = {};
22
22
  __export(testUtils_exports, {
23
23
  createLoggerStore: () => createLoggerStore,
24
- getResultFn: () => getResultFn
24
+ getResultFn: () => getResultFn,
25
+ waitController: () => waitController
25
26
  });
26
27
  module.exports = __toCommonJS(testUtils_exports);
27
28
 
@@ -141,6 +142,17 @@ function omit(obj, keys) {
141
142
  return result;
142
143
  }
143
144
 
145
+ // src/promiseUtils.ts
146
+ function defer() {
147
+ let resolve;
148
+ let reject;
149
+ const promise = new Promise((_resolve, _reject) => {
150
+ resolve = _resolve;
151
+ reject = _reject;
152
+ });
153
+ return { resolve, reject, promise };
154
+ }
155
+
144
156
  // src/testUtils.ts
145
157
  function createLoggerStore({
146
158
  filterKeys: defaultFilterKeys,
@@ -353,8 +365,23 @@ function getResultFn(fnGetter, wrapper) {
353
365
  }
354
366
  };
355
367
  }
368
+ function waitController() {
369
+ const { promise, resolve } = defer();
370
+ return {
371
+ wait: promise,
372
+ stopWaiting: () => {
373
+ resolve();
374
+ },
375
+ stopWaitingAfter: (ms) => {
376
+ setTimeout(() => {
377
+ resolve();
378
+ }, ms);
379
+ }
380
+ };
381
+ }
356
382
  // Annotate the CommonJS export names for ESM import in node:
357
383
  0 && (module.exports = {
358
384
  createLoggerStore,
359
- getResultFn
385
+ getResultFn,
386
+ waitController
360
387
  });
@@ -34,5 +34,10 @@ declare function createLoggerStore({ filterKeys: defaultFilterKeys, rejectKeys:
34
34
  addMark: (label: string) => void;
35
35
  };
36
36
  declare function getResultFn<T extends (...args: any[]) => any>(fnGetter: () => T, wrapper?: (...args: any[]) => any): T;
37
+ declare function waitController(): {
38
+ wait: Promise<void>;
39
+ stopWaiting: () => void;
40
+ stopWaitingAfter: (ms: number) => void;
41
+ };
37
42
 
38
- export { createLoggerStore, getResultFn };
43
+ export { createLoggerStore, getResultFn, waitController };
@@ -34,5 +34,10 @@ declare function createLoggerStore({ filterKeys: defaultFilterKeys, rejectKeys:
34
34
  addMark: (label: string) => void;
35
35
  };
36
36
  declare function getResultFn<T extends (...args: any[]) => any>(fnGetter: () => T, wrapper?: (...args: any[]) => any): T;
37
+ declare function waitController(): {
38
+ wait: Promise<void>;
39
+ stopWaiting: () => void;
40
+ stopWaitingAfter: (ms: number) => void;
41
+ };
37
42
 
38
- export { createLoggerStore, getResultFn };
43
+ export { createLoggerStore, getResultFn, waitController };
package/lib/testUtils.js CHANGED
@@ -5,6 +5,9 @@ import {
5
5
  import {
6
6
  deepEqual
7
7
  } from "./chunk-JQFUKJU5.js";
8
+ import {
9
+ defer
10
+ } from "./chunk-DFXNVEH6.js";
8
11
  import {
9
12
  clampMin
10
13
  } from "./chunk-HTCYUMDR.js";
@@ -228,7 +231,22 @@ function getResultFn(fnGetter, wrapper) {
228
231
  }
229
232
  };
230
233
  }
234
+ function waitController() {
235
+ const { promise, resolve } = defer();
236
+ return {
237
+ wait: promise,
238
+ stopWaiting: () => {
239
+ resolve();
240
+ },
241
+ stopWaitingAfter: (ms) => {
242
+ setTimeout(() => {
243
+ resolve();
244
+ }, ms);
245
+ }
246
+ };
247
+ }
231
248
  export {
232
249
  createLoggerStore,
233
- getResultFn
250
+ getResultFn,
251
+ waitController
234
252
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ls-stack/utils",
3
3
  "description": "Typescript utils",
4
- "version": "3.9.0",
4
+ "version": "3.10.0",
5
5
  "license": "MIT",
6
6
  "files": [
7
7
  "lib"
@@ -179,6 +179,9 @@
179
179
  "prettier-plugin-organize-imports": "^4.1.0",
180
180
  "tsm": "^2.3.0",
181
181
  "tsup": "^8.3.5",
182
+ "typedoc": "^0.28.4",
183
+ "typedoc-plugin-markdown": "^4.6.3",
184
+ "typedoc-plugin-missing-exports": "^4.0.0",
182
185
  "typescript": "^5.7.3",
183
186
  "typescript-eslint": "^8.21.0",
184
187
  "vite": "^6.0.11",
@@ -195,12 +198,15 @@
195
198
  "tsc": "tsc -p tsconfig.prod.json",
196
199
  "tsc:watch": "tsc -p tsconfig.prod.json --watch",
197
200
  "eslint": "CI=true eslint src/ scripts/ --color --max-warnings=0",
198
- "build": "pnpm test && pnpm lint && pnpm build:no-test && pnpm build:update-exports",
201
+ "build": "pnpm test && pnpm lint && pnpm build:no-test && pnpm run docs && pnpm run docs:commit && pnpm build:update-exports",
199
202
  "build:no-test": "tsup",
200
203
  "build:update-exports": "tsm --no-warnings scripts/updatePackageExports.ts",
201
204
  "build-test": "tsup --config tsup.test.config.ts",
202
205
  "pre-publish": "./scripts/check-if-is-sync.sh && pnpm build",
203
206
  "test:console-fmt": "tsm --no-warnings scripts/testConsoleFmt.ts",
204
- "bench:deepEqual": "tsm --no-warnings benchmarks/deepEqual.ts"
207
+ "bench:deepEqual": "tsm --no-warnings benchmarks/deepEqual.ts",
208
+ "docs": "typedoc",
209
+ "docs:watch": "typedoc --watch",
210
+ "docs:commit": "git add docs && git diff --cached --quiet docs || git commit -m 'docs: update docs'"
205
211
  }
206
212
  }