@d-zero/dealer 1.5.4 → 1.6.1

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
@@ -15,13 +15,9 @@ npm install @d-zero/dealer
15
15
  ```ts
16
16
  import { deal } from '@d-zero/dealer';
17
17
 
18
- await deal(items, {
19
- limit: 30,
20
- header: (progress, done, total, limit) =>
21
- progress === 1
22
- ? 'HeaderMessage: Done!'
23
- : `HeaderMessage: %earth% %dots% %block% %propeller%`,
24
- setup: (item, update, index) => {
18
+ await deal(
19
+ items,
20
+ (item, update, index, setLineHeader, push) => {
25
21
  item.setup();
26
22
  item.addListeners((state) => {
27
23
  update(`item(${index}): ${state}`);
@@ -32,7 +28,14 @@ await deal(items, {
32
28
  item.cleanup();
33
29
  };
34
30
  },
35
- });
31
+ {
32
+ limit: 30,
33
+ header: (progress, done, total, limit) =>
34
+ progress === 1
35
+ ? 'HeaderMessage: Done!'
36
+ : `HeaderMessage: %earth% %dots% %block% %propeller%`,
37
+ },
38
+ );
36
39
  ```
37
40
 
38
41
  ### deal関数
@@ -49,8 +52,9 @@ async function deal<T extends WeakKey>(
49
52
  update: (log: string) => void,
50
53
  index: number,
51
54
  setLineHeader: (lineHeader: string) => void,
55
+ push: (...items: T[]) => Promise<void>,
52
56
  ) => Promise<() => void | Promise<void>> | (() => void | Promise<void>),
53
- options?: DealOptions,
57
+ options?: DealOptions<T>,
54
58
  ): Promise<void>;
55
59
  ```
56
60
 
@@ -62,6 +66,7 @@ async function deal<T extends WeakKey>(
62
66
  - `update`: ログを更新する関数
63
67
  - `index`: アイテムのインデックス
64
68
  - `setLineHeader`: ログの各行にプレフィックスを設定する関数
69
+ - `push`: 実行中にアイテムをキューに追加する関数
65
70
  - 戻り値: アイテムを開始する関数
66
71
  - `options`: 設定オプション
67
72
 
@@ -78,7 +83,7 @@ async function deal<T extends WeakKey>(
78
83
  ### DealOptions型
79
84
 
80
85
  ```ts
81
- type DealOptions = DealerOptions &
86
+ type DealOptions<T = unknown> = DealerOptions<T> &
82
87
  LanesOptions & {
83
88
  readonly header?: DealHeader;
84
89
  readonly debug?: boolean;
@@ -89,6 +94,7 @@ type DealOptions = DealerOptions &
89
94
  #### プロパティ
90
95
 
91
96
  - `limit?: number`: 同時実行数の制限(デフォルト: 10)
97
+ - `onPush?: (item: T) => boolean`: `push()`時のフィルタ関数。`false`を返すとそのアイテムは拒否される(例: 重複排除)
92
98
  - `header?: DealHeader`: 進捗ヘッダーを生成する関数
93
99
  - `debug?: boolean`: デバッグログを表示するかどうか
94
100
  - `interval?: number | DelayOptions`: 各処理の間隔(ミリ秒またはDelayOptions)
@@ -129,11 +135,12 @@ type DealHeader = (
129
135
  #### コンストラクタ
130
136
 
131
137
  ```ts
132
- constructor(items: readonly T[], options?: DealerOptions)
138
+ constructor(items: readonly T[], options?: DealerOptions<T>)
133
139
  ```
134
140
 
135
141
  - `items`: 処理対象のアイテム
136
142
  - `options.limit`: 同時実行数の制限(デフォルト: 10)
143
+ - `options.onPush`: `push()`時のフィルタ関数
137
144
 
138
145
  #### メソッド
139
146
 
@@ -175,6 +182,16 @@ dealer.progress((progress, done, total, limit) => {
175
182
  });
176
183
  ```
177
184
 
185
+ ##### async push(...items: T[])
186
+
187
+ 実行中にアイテムをキューに追加します。追加されたアイテムには`setup()`で設定した初期化関数が自動適用されます。完了後の呼び出しは無視されます。
188
+
189
+ ```ts
190
+ await dealer.push(newItem1, newItem2);
191
+ ```
192
+
193
+ - `items`: 追加するアイテム。`onPush`が設定されている場合、`false`を返したアイテムはスキップされます。
194
+
178
195
  ##### async setup(initializer: ProcessInitializer<T>)
179
196
 
180
197
  各アイテムの初期化関数を設定します。
package/dist/deal.d.ts CHANGED
@@ -1,7 +1,7 @@
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
- export type DealOptions = DealerOptions & LanesOptions & {
4
+ export type DealOptions<T = unknown> = DealerOptions<T> & LanesOptions & {
5
5
  readonly header?: DealHeader;
6
6
  readonly debug?: boolean;
7
7
  readonly interval?: number | DelayOptions;
@@ -41,4 +41,4 @@ export type DealHeader = (progress: number, done: number, total: number, limit:
41
41
  * @param options - Configuration options including interval delay
42
42
  * @returns Promise that resolves when all items are processed
43
43
  */
44
- export declare function deal<T extends WeakKey>(items: readonly T[], setup: (process: T, update: (log: string) => void, index: number, setLineHeader: (lineHeader: string) => void) => Promise<() => void | Promise<void>> | (() => void | Promise<void>), options?: DealOptions): Promise<void>;
44
+ 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
@@ -50,7 +50,8 @@ export async function deal(items, setup, options) {
50
50
  lineHeader = header;
51
51
  };
52
52
  const update = (log) => lanes.update(index, lineHeader + log);
53
- const start = await setup(process, update, index, setLineHeader);
53
+ const push = (...newItems) => dealer.push(...newItems);
54
+ const start = await setup(process, update, index, setLineHeader, push);
54
55
  return async () => {
55
56
  await delay(options?.interval ?? 0, (determinedInterval) => {
56
57
  update(`Waiting interval: %countdown(${determinedInterval},${index}_interval)%ms`);
package/dist/dealer.d.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  import type { ProcessInitializer } from './types.js';
2
- export interface DealerOptions {
2
+ export interface DealerOptions<T = unknown> {
3
3
  limit?: number;
4
+ onPush?: (item: T) => boolean;
4
5
  }
5
6
  export declare class Dealer<T extends WeakKey> {
6
7
  #private;
7
- constructor(items: readonly T[], options?: DealerOptions);
8
+ constructor(items: readonly T[], options?: DealerOptions<T>);
8
9
  debug(listener: (log: string) => void): void;
9
10
  finish(listener: () => void): void;
10
11
  play(): void;
11
12
  progress(listener: (progress: number, done: number, total: number, limit: number) => void): void;
13
+ push(...items: T[]): Promise<void>;
12
14
  setup(initializer: ProcessInitializer<T>): Promise<void>;
13
15
  }
package/dist/dealer.js CHANGED
@@ -3,14 +3,20 @@ export class Dealer {
3
3
  #done = new WeakSet();
4
4
  #doneCount = 0;
5
5
  #finish = () => { };
6
+ #finished = false;
7
+ #initializer = null;
6
8
  #items;
7
9
  #limit;
10
+ #nextIndex = 0;
11
+ #onPush;
12
+ #pendingInitCount = 0;
8
13
  #progress = () => { };
9
14
  #starts = new WeakMap();
10
15
  #workers = new Set();
11
16
  constructor(items, options) {
12
- this.#items = items;
17
+ this.#items = [...items];
13
18
  this.#limit = options?.limit ?? 10;
19
+ this.#onPush = options?.onPush;
14
20
  }
15
21
  debug(listener) {
16
22
  this.#debug = listener;
@@ -24,18 +30,35 @@ export class Dealer {
24
30
  progress(listener) {
25
31
  this.#progress = listener;
26
32
  }
33
+ async push(...items) {
34
+ if (this.#finished) {
35
+ return;
36
+ }
37
+ for (const item of items) {
38
+ if (this.#onPush && !this.#onPush(item)) {
39
+ continue;
40
+ }
41
+ this.#pendingInitCount++;
42
+ await this.#initializeAndDispatch(item);
43
+ }
44
+ }
27
45
  async setup(initializer) {
28
- for (const [index, item] of this.#items.entries()) {
29
- const start = await initializer(item, index);
46
+ this.#initializer = initializer;
47
+ for (const item of this.#items) {
48
+ const start = await initializer(item, this.#nextIndex++);
30
49
  this.#starts.set(item, async () => await start());
31
50
  }
32
- this.#progress(this.#doneCount / this.#items.length, this.#doneCount, this.#items.length, this.#limit);
51
+ const total = this.#items.length;
52
+ this.#progress(total === 0 ? 0 : this.#doneCount / total, this.#doneCount, total, this.#limit);
33
53
  }
34
54
  #deal() {
35
- this.#debug(`Done: ${this.#doneCount}/${this.#items.length} (Limit: ${this.#limit})`);
36
- this.#progress(this.#doneCount / this.#items.length, this.#doneCount, this.#items.length, this.#limit);
37
- if (this.#doneCount === this.#items.length) {
55
+ const total = this.#items.length;
56
+ this.#debug(`Done: ${this.#doneCount}/${total} (Limit: ${this.#limit})`);
57
+ this.#progress(total === 0 ? 0 : this.#doneCount / total, this.#doneCount, total, this.#limit);
58
+ if (total > 0 && this.#doneCount === total && this.#pendingInitCount === 0) {
59
+ this.#finished = true;
38
60
  this.#finish();
61
+ return;
39
62
  }
40
63
  while (this.#workers.size < this.#limit) {
41
64
  const worker = this.#draw();
@@ -67,4 +90,14 @@ export class Dealer {
67
90
  }
68
91
  return null;
69
92
  }
93
+ async #initializeAndDispatch(item) {
94
+ if (!this.#initializer) {
95
+ throw new Error('setup() must be called before push()');
96
+ }
97
+ const start = await this.#initializer(item, this.#nextIndex++);
98
+ this.#starts.set(item, async () => await start());
99
+ this.#items.push(item);
100
+ this.#pendingInitCount--;
101
+ this.#deal();
102
+ }
70
103
  }
package/dist/display.js CHANGED
@@ -1,8 +1,10 @@
1
- import readline from 'node:readline';
2
1
  import c from 'ansi-colors';
3
2
  import { countDownFunctionParser } from './count-down-function-parser.js';
4
3
  import { riffle } from './riffle.js';
5
4
  const RESET = c.reset('');
5
+ const CURSOR_UP = (n) => `\u001B[${n}A`;
6
+ const CURSOR_TO_COL0 = '\u001B[G';
7
+ const ERASE_DOWN = '\u001B[0J';
6
8
  const animationPresets = {
7
9
  earth: [2, '🌏', '🌍', '🌎'],
8
10
  dots: [5, '. ', '.. ', '...'],
@@ -39,7 +41,9 @@ export class Display {
39
41
  this.#timer = null;
40
42
  }
41
43
  this.#write();
42
- process.stdin.setRawMode(false);
44
+ if (process.stdin.isTTY) {
45
+ process.stdin.setRawMode(false);
46
+ }
43
47
  this.#lastWroteLineNum = 0;
44
48
  this.#stack = null;
45
49
  }
@@ -59,16 +63,6 @@ export class Display {
59
63
  }
60
64
  this.#enterFrame();
61
65
  }
62
- #clear() {
63
- if (this.#verbose) {
64
- return;
65
- }
66
- for (let i = 0; i < this.#lastWroteLineNum; i++) {
67
- readline.moveCursor(process.stdout, 0, -1);
68
- readline.cursorTo(process.stdout, 0);
69
- readline.clearLine(process.stdout, 0);
70
- }
71
- }
72
66
  #countDown(text) {
73
67
  const parsed = countDownFunctionParser(text);
74
68
  if (!parsed) {
@@ -114,12 +108,17 @@ export class Display {
114
108
  if (!this.#stack) {
115
109
  return;
116
110
  }
117
- this.#clear();
118
111
  const outputBuffer = [];
119
112
  for (const stack of this.#stack) {
120
113
  outputBuffer.push(this.#text(stack));
121
114
  }
122
- process.stdout.write(outputBuffer.join('\n') + '\n');
115
+ const content = outputBuffer.join('\n') + '\n';
116
+ let output = '';
117
+ if (this.#lastWroteLineNum > 0) {
118
+ output += CURSOR_UP(this.#lastWroteLineNum) + CURSOR_TO_COL0 + ERASE_DOWN;
119
+ }
120
+ output += content;
121
+ process.stdout.write(output);
123
122
  this.#lastWroteLineNum = this.#stack.length;
124
123
  }
125
124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d-zero/dealer",
3
- "version": "1.5.4",
3
+ "version": "1.6.1",
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.18.0",
26
+ "@d-zero/shared": "0.19.0",
27
27
  "ansi-colors": "4.1.3"
28
28
  },
29
- "gitHead": "008ea25926579b061e36c1f5f4f4fc7e15cb782e"
29
+ "gitHead": "7f90e8c637c40b9abee652eac927a3ca257ef29f"
30
30
  }