@d-zero/dealer 1.6.3 → 1.6.5

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
@@ -80,6 +80,23 @@ async function deal<T extends WeakKey>(
80
80
  - これはアイテム開始**後**、最初の出力の**前**に発生
81
81
  - 実際の処理が始まる(ユーザーコードからの最初の `update()` 呼び出し)
82
82
 
83
+ #### エラーハンドリング
84
+
85
+ ワーカー(`start()`関数)がエラーを投げた場合、残りのワーカーの処理は継続されます。すべてのワーカーが完了(成功または失敗)した後、1つ以上のエラーがあれば`AggregateError`でrejectされます。
86
+
87
+ ```ts
88
+ try {
89
+ await deal(items, setup, options);
90
+ } catch (error) {
91
+ if (error instanceof AggregateError) {
92
+ console.error(`${error.errors.length} workers failed:`);
93
+ for (const e of error.errors) {
94
+ console.error(e);
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
83
100
  ### DealOptions型
84
101
 
85
102
  ```ts
@@ -101,7 +118,7 @@ type DealOptions<T = unknown> = DealerOptions<T> &
101
118
  - `animations?: Animations`: アニメーション定義
102
119
  - `fps?: FPS`: フレームレート(12, 24, 30, 60)
103
120
  - `indent?: string`: ログのインデント文字列
104
- - `sort?: (a: [number, string], b: [number, string]) => number`: ログのソート関数
121
+ - `sort?: (a: readonly [number, string], b: readonly [number, string]) => number`: ログのソート関数
105
122
  - `verbose?: boolean`: 詳細ログモード
106
123
 
107
124
  ### DealHeader型
@@ -120,7 +137,7 @@ type DealHeader = (
120
137
  #### パラメータ
121
138
 
122
139
  - `progress`: 進捗率(0〜1)
123
- - `done`: 完了したアイテム数
140
+ - `done`: 処理済みアイテム数(エラーで失敗したアイテムを含む)
124
141
  - `total`: 総アイテム数
125
142
  - `limit`: 同時実行数制限
126
143
 
@@ -142,6 +159,19 @@ constructor(items: readonly T[], options?: DealerOptions<T>)
142
159
  - `options.limit`: 同時実行数の制限(デフォルト: 10)
143
160
  - `options.onPush`: `push()`時のフィルタ関数
144
161
 
162
+ #### プロパティ
163
+
164
+ ##### get errors(): ReadonlyArray<{ item: T; error: unknown }>
165
+
166
+ ワーカーの実行中に発生したエラーの一覧を返します。各エントリにはエラーを起こしたアイテムとエラーオブジェクトが含まれます。
167
+
168
+ ```ts
169
+ await runDealer(dealer);
170
+ for (const { item, error } of dealer.errors) {
171
+ console.error('Failed item:', item, error);
172
+ }
173
+ ```
174
+
145
175
  #### メソッド
146
176
 
147
177
  ##### debug(listener: (log: string) => void)
package/dist/deal.d.ts CHANGED
@@ -1,11 +1,28 @@
1
1
  import type { DealerOptions } from './dealer.js';
2
2
  import type { LanesOptions } from './lanes.js';
3
3
  import type { DelayOptions } from '@d-zero/shared/delay';
4
+ /**
5
+ * Configuration options for the {@link deal} function.
6
+ * Combines {@link DealerOptions} (concurrency), {@link LanesOptions} (display),
7
+ * and deal-specific options (header, debug, interval).
8
+ * @template T - The type of items being processed
9
+ */
4
10
  export type DealOptions<T = unknown> = DealerOptions<T> & LanesOptions & {
11
+ /** Function to generate the progress header string */
5
12
  readonly header?: DealHeader;
13
+ /** Whether to display debug log output */
6
14
  readonly debug?: boolean;
15
+ /** Delay between each worker start, in milliseconds or as a DelayOptions object */
7
16
  readonly interval?: number | DelayOptions;
8
17
  };
18
+ /**
19
+ * Function type that converts progress information into a header string for display.
20
+ * @param progress - Progress ratio from 0 to 1
21
+ * @param done - Number of processed items (including errors)
22
+ * @param total - Total number of items
23
+ * @param limit - Concurrency limit
24
+ * @returns Header string to display. May include animation variables like `%earth%`, `%dots%`.
25
+ */
9
26
  export type DealHeader = (progress: number, done: number, total: number, limit: number) => string;
10
27
  /**
11
28
  * Processes items in parallel with coordinated logging and optional interval delays.
@@ -36,9 +53,16 @@ export type DealHeader = (progress: number, done: number, total: number, limit:
36
53
  * - All wait logs (including interval delay) are output via `delay()` callback
37
54
  * - This ensures the determined interval (even for random delays) is used for countdown
38
55
  * - The `%countdown()` function displays remaining time based on the actual delay duration
56
+ * @template T - The type of items to process, must extend WeakKey
39
57
  * @param items - Collection of items to process
40
- * @param setup - Function that initializes each item and returns a start function
41
- * @param options - Configuration options including interval delay
42
- * @returns Promise that resolves when all items are processed
58
+ * @param setup - Function that initializes each item and returns a start function.
59
+ * Receives the item, an update callback for logging, the item index,
60
+ * a setLineHeader callback for log prefixes, and a push callback to add items dynamically.
61
+ * Must return a start function that performs the actual work.
62
+ * @param options - Configuration options including concurrency limit, interval delay, and display settings
63
+ * @returns Promise that resolves when all items are processed successfully
64
+ * @throws {AggregateError} When one or more workers throw errors. All workers run to
65
+ * completion regardless of individual failures. The AggregateError.errors array
66
+ * contains the original errors from each failed worker.
43
67
  */
44
68
  export declare function deal<T extends WeakKey>(items: readonly T[], setup: (process: T, update: (log: string) => void, index: number, setLineHeader: (lineHeader: string) => void, push: (...items: T[]) => Promise<void>) => Promise<() => void | Promise<void>> | (() => void | Promise<void>), options?: DealOptions<T>): Promise<void>;
package/dist/deal.js CHANGED
@@ -31,10 +31,17 @@ const DEBUG_ID = Number.MIN_SAFE_INTEGER;
31
31
  * - All wait logs (including interval delay) are output via `delay()` callback
32
32
  * - This ensures the determined interval (even for random delays) is used for countdown
33
33
  * - The `%countdown()` function displays remaining time based on the actual delay duration
34
+ * @template T - The type of items to process, must extend WeakKey
34
35
  * @param items - Collection of items to process
35
- * @param setup - Function that initializes each item and returns a start function
36
- * @param options - Configuration options including interval delay
37
- * @returns Promise that resolves when all items are processed
36
+ * @param setup - Function that initializes each item and returns a start function.
37
+ * Receives the item, an update callback for logging, the item index,
38
+ * a setLineHeader callback for log prefixes, and a push callback to add items dynamically.
39
+ * Must return a start function that performs the actual work.
40
+ * @param options - Configuration options including concurrency limit, interval delay, and display settings
41
+ * @returns Promise that resolves when all items are processed successfully
42
+ * @throws {AggregateError} When one or more workers throw errors. All workers run to
43
+ * completion regardless of individual failures. The AggregateError.errors array
44
+ * contains the original errors from each failed worker.
38
45
  */
39
46
  export async function deal(items, setup, options) {
40
47
  const dealer = new Dealer(items, options);
@@ -56,8 +63,12 @@ export async function deal(items, setup, options) {
56
63
  await delay(options?.interval ?? 0, (determinedInterval) => {
57
64
  update(`Waiting interval: %countdown(${determinedInterval},${index}_interval)%ms`);
58
65
  });
59
- await start();
60
- lanes.delete(index);
66
+ try {
67
+ await start();
68
+ }
69
+ finally {
70
+ lanes.delete(index);
71
+ }
61
72
  };
62
73
  });
63
74
  if (options?.debug) {
@@ -65,10 +76,15 @@ export async function deal(items, setup, options) {
65
76
  lanes.update(DEBUG_ID, `[DEBUG]: ${log}`);
66
77
  });
67
78
  }
68
- return new Promise((resolve) => {
79
+ return new Promise((resolve, reject) => {
69
80
  dealer.finish(() => {
70
81
  lanes.close();
71
- resolve();
82
+ if (dealer.errors.length > 0) {
83
+ reject(new AggregateError(dealer.errors.map((e) => e.error), `${dealer.errors.length} worker(s) failed`));
84
+ }
85
+ else {
86
+ resolve();
87
+ }
72
88
  });
73
89
  dealer.play();
74
90
  });
package/dist/dealer.d.ts CHANGED
@@ -1,15 +1,67 @@
1
1
  import type { ProcessInitializer } from './types.js';
2
+ /**
3
+ * Configuration options for the {@link Dealer} class.
4
+ * @template T - The type of items being processed
5
+ */
2
6
  export interface DealerOptions<T = unknown> {
7
+ /** Maximum number of concurrent workers (default: 10) */
3
8
  limit?: number;
9
+ /** Filter function called when items are added via {@link Dealer.push}. Return `false` to reject the item. */
4
10
  onPush?: (item: T) => boolean;
5
11
  }
12
+ /**
13
+ * Manages parallel processing of items with configurable concurrency limits.
14
+ *
15
+ * Items are dispatched to workers up to the configured limit. When a worker
16
+ * completes (successfully or with error), the next pending item is dispatched.
17
+ * Errors are collected and accessible via the `errors` property.
18
+ * @template T - The type of items to process, must extend WeakKey
19
+ */
6
20
  export declare class Dealer<T extends WeakKey> {
7
21
  #private;
22
+ /**
23
+ * Errors collected from failed workers during processing.
24
+ * @returns Array of objects containing the failed item and its error
25
+ */
26
+ get errors(): ReadonlyArray<{
27
+ item: T;
28
+ error: unknown;
29
+ }>;
30
+ /**
31
+ * @param items - Collection of items to process
32
+ * @param options - Configuration options
33
+ */
8
34
  constructor(items: readonly T[], options?: DealerOptions<T>);
35
+ /**
36
+ * Sets a listener for debug log messages.
37
+ * @param listener - Callback invoked with debug log strings
38
+ */
9
39
  debug(listener: (log: string) => void): void;
40
+ /**
41
+ * Sets a listener called when all items have been processed (including failures).
42
+ * @param listener - Callback invoked on completion
43
+ */
10
44
  finish(listener: () => void): void;
45
+ /**
46
+ * Starts dispatching items to workers.
47
+ */
11
48
  play(): void;
49
+ /**
50
+ * Sets a listener for progress updates, called each time a worker completes.
51
+ * @param listener - Callback receiving progress ratio (0–1), done count (including errors), total count, and concurrency limit
52
+ */
12
53
  progress(listener: (progress: number, done: number, total: number, limit: number) => void): void;
54
+ /**
55
+ * Adds items to the processing queue during execution.
56
+ * Added items are initialized using the same initializer set via {@link setup}.
57
+ * Calls after all processing has finished are silently ignored.
58
+ * @param items - Items to add. If `onPush` is configured, items returning `false` are skipped.
59
+ */
13
60
  push(...items: T[]): Promise<void>;
61
+ /**
62
+ * Registers the initializer function and prepares all items for processing.
63
+ * Must be called before {@link play}.
64
+ * @param initializer - Function that receives each item and its index, returning a start function
65
+ */
14
66
  setup(initializer: ProcessInitializer<T>): Promise<void>;
15
67
  }
package/dist/dealer.js CHANGED
@@ -1,7 +1,16 @@
1
+ /**
2
+ * Manages parallel processing of items with configurable concurrency limits.
3
+ *
4
+ * Items are dispatched to workers up to the configured limit. When a worker
5
+ * completes (successfully or with error), the next pending item is dispatched.
6
+ * Errors are collected and accessible via the `errors` property.
7
+ * @template T - The type of items to process, must extend WeakKey
8
+ */
1
9
  export class Dealer {
2
10
  #debug = () => { };
3
11
  #done = new WeakSet();
4
12
  #doneCount = 0;
13
+ #errors = [];
5
14
  #finish = () => { };
6
15
  #finished = false;
7
16
  #initializer = null;
@@ -13,23 +22,55 @@ export class Dealer {
13
22
  #progress = () => { };
14
23
  #starts = new WeakMap();
15
24
  #workers = new Set();
25
+ /**
26
+ * Errors collected from failed workers during processing.
27
+ * @returns Array of objects containing the failed item and its error
28
+ */
29
+ get errors() {
30
+ return this.#errors;
31
+ }
32
+ /**
33
+ * @param items - Collection of items to process
34
+ * @param options - Configuration options
35
+ */
16
36
  constructor(items, options) {
17
37
  this.#items = [...items];
18
38
  this.#limit = options?.limit ?? 10;
19
39
  this.#onPush = options?.onPush;
20
40
  }
41
+ /**
42
+ * Sets a listener for debug log messages.
43
+ * @param listener - Callback invoked with debug log strings
44
+ */
21
45
  debug(listener) {
22
46
  this.#debug = listener;
23
47
  }
48
+ /**
49
+ * Sets a listener called when all items have been processed (including failures).
50
+ * @param listener - Callback invoked on completion
51
+ */
24
52
  finish(listener) {
25
53
  this.#finish = listener;
26
54
  }
55
+ /**
56
+ * Starts dispatching items to workers.
57
+ */
27
58
  play() {
28
59
  this.#deal();
29
60
  }
61
+ /**
62
+ * Sets a listener for progress updates, called each time a worker completes.
63
+ * @param listener - Callback receiving progress ratio (0–1), done count (including errors), total count, and concurrency limit
64
+ */
30
65
  progress(listener) {
31
66
  this.#progress = listener;
32
67
  }
68
+ /**
69
+ * Adds items to the processing queue during execution.
70
+ * Added items are initialized using the same initializer set via {@link setup}.
71
+ * Calls after all processing has finished are silently ignored.
72
+ * @param items - Items to add. If `onPush` is configured, items returning `false` are skipped.
73
+ */
33
74
  async push(...items) {
34
75
  if (this.#finished) {
35
76
  return;
@@ -42,6 +83,11 @@ export class Dealer {
42
83
  await this.#initializeAndDispatch(item);
43
84
  }
44
85
  }
86
+ /**
87
+ * Registers the initializer function and prepares all items for processing.
88
+ * Must be called before {@link play}.
89
+ * @param initializer - Function that receives each item and its index, returning a start function
90
+ */
45
91
  async setup(initializer) {
46
92
  this.#initializer = initializer;
47
93
  for (const item of this.#items) {
@@ -51,6 +97,12 @@ export class Dealer {
51
97
  const total = this.#items.length;
52
98
  this.#progress(total === 0 ? 0 : this.#doneCount / total, this.#doneCount, total, this.#limit);
53
99
  }
100
+ #completeWorker(worker) {
101
+ this.#workers.delete(worker);
102
+ this.#done.add(worker);
103
+ this.#doneCount++;
104
+ this.#deal();
105
+ }
54
106
  #deal() {
55
107
  const total = this.#items.length;
56
108
  this.#debug(`Done: ${this.#doneCount}/${total} (Limit: ${this.#limit})`);
@@ -70,11 +122,13 @@ export class Dealer {
70
122
  if (!start) {
71
123
  throw new Error(`Didn't have a starting function`);
72
124
  }
73
- void start().then(() => {
74
- this.#workers.delete(worker);
75
- this.#done.add(worker);
76
- this.#doneCount++;
77
- this.#deal();
125
+ void start()
126
+ .then(() => {
127
+ this.#completeWorker(worker);
128
+ })
129
+ .catch((error) => {
130
+ this.#errors.push({ item: worker, error });
131
+ this.#completeWorker(worker);
78
132
  });
79
133
  }
80
134
  }
package/dist/lanes.d.ts CHANGED
@@ -1,23 +1,61 @@
1
1
  import type { Animations, FPS } from './types.js';
2
2
  type Log = readonly [id: number, message: string];
3
3
  type SortFunc = (a: Log, b: Log) => number;
4
+ /** Configuration options for the {@link Lanes} display manager. */
4
5
  export type LanesOptions = {
6
+ /** Custom animation definitions to override or extend built-in presets */
5
7
  readonly animations?: Animations;
8
+ /** Frame rate for display rendering */
6
9
  readonly fps?: FPS;
10
+ /** Indent string prepended to each log line */
7
11
  readonly indent?: string;
12
+ /** Sort function for ordering log entries by their ID */
8
13
  readonly sort?: SortFunc;
14
+ /** When true, logs are appended line-by-line to stdout instead of being redrawn in-place */
9
15
  readonly verbose?: boolean;
10
16
  };
17
+ /**
18
+ * Manages multiple concurrent log lines with ordered display output.
19
+ * Each log line is identified by a numeric ID and can be independently updated or deleted.
20
+ */
11
21
  export declare class Lanes {
12
22
  #private;
23
+ /**
24
+ * @param options - Display configuration options
25
+ */
13
26
  constructor(options?: LanesOptions);
27
+ /**
28
+ * Clears all log entries. No-op in verbose mode.
29
+ * @param options - Pass `{ header: true }` to also clear the header
30
+ * @param options.header
31
+ */
14
32
  clear(options?: {
15
33
  header?: boolean;
16
34
  }): void;
35
+ /**
36
+ * Closes the underlying display and releases resources.
37
+ */
17
38
  close(): void;
39
+ /**
40
+ * Removes the log entry with the given ID. No-op in verbose mode.
41
+ * @param id - The log entry ID to remove
42
+ */
18
43
  delete(id: number): void;
44
+ /**
45
+ * Sets the header text displayed above all log lines.
46
+ * @param text - Header text to display
47
+ */
19
48
  header(text: string): void;
49
+ /**
50
+ * Updates the log entry for the given ID. In verbose mode, immediately writes the header and log to stdout.
51
+ * @param id - The log entry ID
52
+ * @param log - The log message
53
+ */
20
54
  update(id: number, log: string): void;
55
+ /**
56
+ * Renders all current log entries to the display. No-op in verbose mode.
57
+ * Entries are sorted by the configured sort function before rendering.
58
+ */
21
59
  write(): void;
22
60
  }
23
61
  export {};
package/dist/lanes.js CHANGED
@@ -1,5 +1,9 @@
1
1
  import { Display } from './display.js';
2
2
  const RESET = '\u001B[0m';
3
+ /**
4
+ * Manages multiple concurrent log lines with ordered display output.
5
+ * Each log line is identified by a numeric ID and can be independently updated or deleted.
6
+ */
3
7
  export class Lanes {
4
8
  #display;
5
9
  #header;
@@ -7,6 +11,9 @@ export class Lanes {
7
11
  #logs = new Map();
8
12
  #sort = ([a], [b]) => a - b;
9
13
  #verbose;
14
+ /**
15
+ * @param options - Display configuration options
16
+ */
10
17
  constructor(options) {
11
18
  this.#display = new Display({
12
19
  animations: options?.animations,
@@ -17,6 +24,11 @@ export class Lanes {
17
24
  this.#sort = options?.sort ?? this.#sort;
18
25
  this.#verbose = options?.verbose ?? false;
19
26
  }
27
+ /**
28
+ * Clears all log entries. No-op in verbose mode.
29
+ * @param options - Pass `{ header: true }` to also clear the header
30
+ * @param options.header
31
+ */
20
32
  clear(options) {
21
33
  if (this.#verbose) {
22
34
  return;
@@ -27,9 +39,16 @@ export class Lanes {
27
39
  }
28
40
  this.write();
29
41
  }
42
+ /**
43
+ * Closes the underlying display and releases resources.
44
+ */
30
45
  close() {
31
46
  this.#display.close();
32
47
  }
48
+ /**
49
+ * Removes the log entry with the given ID. No-op in verbose mode.
50
+ * @param id - The log entry ID to remove
51
+ */
33
52
  delete(id) {
34
53
  if (this.#verbose) {
35
54
  return;
@@ -37,6 +56,10 @@ export class Lanes {
37
56
  this.#logs.delete(id);
38
57
  this.write();
39
58
  }
59
+ /**
60
+ * Sets the header text displayed above all log lines.
61
+ * @param text - Header text to display
62
+ */
40
63
  header(text) {
41
64
  this.#header = text;
42
65
  if (this.#verbose) {
@@ -44,6 +67,11 @@ export class Lanes {
44
67
  }
45
68
  this.write();
46
69
  }
70
+ /**
71
+ * Updates the log entry for the given ID. In verbose mode, immediately writes the header and log to stdout.
72
+ * @param id - The log entry ID
73
+ * @param log - The log message
74
+ */
47
75
  update(id, log) {
48
76
  if (this.#verbose) {
49
77
  this.#display.write(`${RESET}${this.#header}${RESET} ${log}`);
@@ -52,6 +80,10 @@ export class Lanes {
52
80
  this.#logs.set(id, log);
53
81
  this.write();
54
82
  }
83
+ /**
84
+ * Renders all current log entries to the display. No-op in verbose mode.
85
+ * Entries are sorted by the configured sort function before rendering.
86
+ */
55
87
  write() {
56
88
  if (this.#verbose) {
57
89
  return;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,14 @@
1
+ /** Mapping of animation names to their frame rate and sprite sequences. */
1
2
  export type Animations = Record<string, [fps: number, ...sprites: string[]]>;
3
+ /** Supported frame rates for display rendering. */
2
4
  export type FPS = 12 | 24 | 30 | 60;
5
+ /**
6
+ * Function that initializes an item and returns its start function.
7
+ * @template T - The type of item being initialized
8
+ * @param process - The item to initialize
9
+ * @param index - The sequential index assigned to the item
10
+ * @returns A start function that performs the actual processing
11
+ */
3
12
  export interface ProcessInitializer<T> {
4
13
  (process: T, index: number): Promise<() => Promise<void> | void>;
5
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d-zero/dealer",
3
- "version": "1.6.3",
3
+ "version": "1.6.5",
4
4
  "description": "A tool that provides an API and CLI for parallel processing of collections and sequential logging to standard output",
5
5
  "author": "D-ZERO",
6
6
  "license": "MIT",
@@ -23,8 +23,8 @@
23
23
  "clean": "tsc --build --clean"
24
24
  },
25
25
  "dependencies": {
26
- "@d-zero/shared": "0.20.0",
26
+ "@d-zero/shared": "0.20.1",
27
27
  "ansi-colors": "4.1.3"
28
28
  },
29
- "gitHead": "11ddaf830a1ac0fd8c7c3247a21c3d69d91d8fbb"
29
+ "gitHead": "31410767ae6beff5c5dbe21a824406a4e6716868"
30
30
  }