rest-pipeline-js 1.0.5 → 1.0.6

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,5 +1,8 @@
1
+ ## Утсновка
1
2
 
2
-
3
+ ```sh
4
+ npm i rest-pipeline-js
5
+ ```
3
6
 
4
7
  ## Возможности и API
5
8
 
@@ -1,31 +1,127 @@
1
- import type { PipelineConfig, PipelineResult } from './types';
1
+ import type { PipelineConfig } from './types';
2
+ /**
3
+ * Событие шага pipeline (для хуков)
4
+ */
5
+ export type PipelineStepEvent = {
6
+ /** Индекс шага */
7
+ stepIndex: number;
8
+ /** Ключ шага */
9
+ stepKey: string;
10
+ /** Статус шага */
11
+ status: import('./types').PipelineStepStatus;
12
+ /** Данные результата (если успех) */
13
+ data?: any;
14
+ /** Ошибка (если error) */
15
+ error?: import('./types').ApiError;
16
+ /** Снимок всех результатов на момент события */
17
+ stageResults: Record<string, import('./types').PipelineStepResult>;
18
+ };
19
+ /**
20
+ * Callback для подписки на события этапов pipeline
21
+ */
22
+ export type PipelineStepEventHandler = (event: PipelineStepEvent) => void | Promise<void>;
2
23
  export declare class PipelineOrchestrator {
3
- private config;
4
24
  private progress;
5
25
  private errorHandler;
6
26
  private executor;
7
27
  private sharedData;
8
- constructor(config: PipelineConfig, httpConfig: import('./types').HttpConfig, sharedData?: Record<string, unknown>);
28
+ private onStepStartHandlers;
29
+ private onStepFinishHandlers;
30
+ private onStepErrorHandlers;
31
+ /** Универсальные подписчики событий: ключ — имя события */
32
+ private eventHandlers;
33
+ /** Встроенные логи */
34
+ private logs;
35
+ private stageResults;
36
+ private stageResultsListeners;
37
+ private autoReset;
38
+ /** AbortController для отмены пайплайна */
39
+ private abortController;
40
+ private config;
41
+ constructor(config: PipelineConfig, httpConfig: import('./types').HttpConfig, sharedData?: Record<string, unknown>, options?: {
42
+ autoReset?: boolean;
43
+ });
44
+ /**
45
+ * Подписка на изменения stageResults (реактивно)
46
+ */
47
+ subscribeStageResults(listener: (results: Record<string, import('./types').PipelineStepResult>) => void): () => void;
48
+ /**
49
+ * Универсальная подписка на события: step:<key>, progress, log и др.
50
+ */
51
+ on(event: string, handler: (...args: any[]) => void | Promise<void>): () => void;
52
+ /**
53
+ * Вызов всех обработчиков события
54
+ */
55
+ private emit;
56
+ /**
57
+ * Получить логи пайплайна
58
+ */
59
+ getLogs(): {
60
+ type: string;
61
+ message: string;
62
+ data?: any;
63
+ timestamp: Date;
64
+ }[];
65
+ private notifyStageResults;
66
+ /**
67
+ * Повторно выполнить только один шаг пайплайна (без полного рестарта)
68
+ * @param stepKey ключ шага
69
+ * @param options дополнительные опции (например, onStepPause, externalSignal)
70
+ */
71
+ rerunStep(stepKey: string, options?: {
72
+ onStepPause?: (stepIndex: number, stepResult: unknown, stageResults: Record<string, import('./types').PipelineStepResult>) => Promise<unknown> | unknown;
73
+ externalSignal?: AbortSignal;
74
+ }): Promise<import('./types').PipelineStepResult | undefined>;
75
+ /**
76
+ * Отменить выполнение пайплайна (вызывает ошибку AbortError)
77
+ */
78
+ abort(): void;
79
+ /**
80
+ * Проверить, был ли пайплайн отменён
81
+ */
82
+ isAborted(): boolean;
83
+ /**
84
+ * Подписка на событие начала шага
85
+ */
86
+ onStepStart(handler: PipelineStepEventHandler): () => void;
87
+ /**
88
+ * Подписка на событие успешного завершения шага
89
+ */
90
+ onStepFinish(handler: PipelineStepEventHandler): () => void;
91
+ /**
92
+ * Подписка на событие ошибки шага
93
+ */
94
+ onStepError(handler: PipelineStepEventHandler): () => void;
95
+ private emitStepStart;
96
+ private emitStepFinish;
97
+ private emitStepError;
9
98
  /**
10
99
  * Подписаться на изменения прогресса выполнения pipeline
11
100
  * @param listener функция-обработчик изменений
12
101
  * @returns функция для отписки
13
102
  */
14
103
  subscribeProgress(listener: (progress: import('./types').PipelineProgress) => void): () => void;
104
+ /**
105
+ * Подписка на прогресс с фильтрацией по этапу (stepKey) или общий
106
+ */
107
+ subscribeStepProgress(stepKey: string, listener: (status: import('./types').PipelineStepStatus) => void): () => void;
15
108
  /**
16
109
  * Получить текущий прогресс выполнения pipeline (snapshot, не реактивный)
17
110
  */
18
111
  getProgress(): {
19
112
  currentStage: number;
20
113
  totalStages: number;
21
- stageStatuses: Array<"pending" | "in-progress" | "success" | "error" | "skipped">;
114
+ stageStatuses: Array<import("./types").PipelineStepStatus>;
22
115
  };
23
116
  /**
24
- * @param onStepPause
25
- * Необязательный callback, вызывается после каждого шага (до перехода к следующему).
26
- * Позволяет приостановить выполнение, запросить подтверждение пользователя или изменить результат шага.
27
- * Должен вернуть (optionally изменённый) результат шага или промис с ним.
28
- * Если не передан — пайплайн работает как раньше.
117
+ * Возвращает текущий снимок состояния прогресса (не реактивный).
118
+ * Для отслеживания изменений используйте subscribeProgress.
119
+ */
120
+ getProgressRef(): import("./types").PipelineProgress;
121
+ /**
122
+ * Запустить выполнение пайплайна
123
+ * @param onStepPause callback для пользовательской паузы между шагами
124
+ * @param externalSignal внешний AbortSignal (опционально)
29
125
  */
30
- run(onStepPause?: (stepIndex: number, stepResult: unknown, results: unknown[]) => Promise<unknown> | unknown): Promise<PipelineResult>;
126
+ run(onStepPause?: (stepIndex: number, stepResult: unknown, stageResults: Record<string, import('./types').PipelineStepResult>) => Promise<unknown> | unknown, externalSignal?: AbortSignal): Promise<import('./types').PipelineResult>;
31
127
  }
@@ -4,14 +4,204 @@ exports.PipelineOrchestrator = void 0;
4
4
  const error_handler_1 = require("./error-handler");
5
5
  const progress_tracker_1 = require("./progress-tracker");
6
6
  const request_executor_1 = require("./request-executor");
7
- // import { metricsBus } from '@/core/metrics/metrics-bus';
8
7
  class PipelineOrchestrator {
9
- constructor(config, httpConfig, sharedData = {}) {
8
+ constructor(config, httpConfig, sharedData = {}, options = {}) {
9
+ var _a;
10
+ this.onStepStartHandlers = [];
11
+ this.onStepFinishHandlers = [];
12
+ this.onStepErrorHandlers = [];
13
+ /** Универсальные подписчики событий: ключ — имя события */
14
+ this.eventHandlers = {};
15
+ /** Встроенные логи */
16
+ this.logs = [];
17
+ this.stageResults = {};
18
+ this.stageResultsListeners = [];
19
+ /** AbortController для отмены пайплайна */
20
+ this.abortController = null;
10
21
  this.config = config;
11
22
  this.progress = new progress_tracker_1.ProgressTracker(config.stages.length);
12
23
  this.errorHandler = new error_handler_1.ErrorHandler();
13
24
  this.executor = new request_executor_1.RequestExecutor(httpConfig);
14
25
  this.sharedData = sharedData;
26
+ this.autoReset = (_a = options.autoReset) !== null && _a !== void 0 ? _a : false;
27
+ }
28
+ /**
29
+ * Подписка на изменения stageResults (реактивно)
30
+ */
31
+ subscribeStageResults(listener) {
32
+ this.stageResultsListeners.push(listener);
33
+ // Немедленно уведомляем нового подписчика о текущем состоянии
34
+ listener({ ...this.stageResults });
35
+ return () => {
36
+ this.stageResultsListeners = this.stageResultsListeners.filter(l => l !== listener);
37
+ };
38
+ }
39
+ /**
40
+ * Универсальная подписка на события: step:<key>, progress, log и др.
41
+ */
42
+ on(event, handler) {
43
+ if (!this.eventHandlers[event])
44
+ this.eventHandlers[event] = [];
45
+ this.eventHandlers[event].push(handler);
46
+ return () => {
47
+ this.eventHandlers[event] = this.eventHandlers[event].filter(h => h !== handler);
48
+ };
49
+ }
50
+ /**
51
+ * Вызов всех обработчиков события
52
+ */
53
+ async emit(event, ...args) {
54
+ if (this.eventHandlers[event]) {
55
+ for (const handler of this.eventHandlers[event]) {
56
+ await handler(...args);
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Получить логи пайплайна
62
+ */
63
+ getLogs() {
64
+ return [...this.logs];
65
+ }
66
+ notifyStageResults() {
67
+ for (const listener of this.stageResultsListeners) {
68
+ listener({ ...this.stageResults });
69
+ }
70
+ }
71
+ /**
72
+ * Повторно выполнить только один шаг пайплайна (без полного рестарта)
73
+ * @param stepKey ключ шага
74
+ * @param options дополнительные опции (например, onStepPause, externalSignal)
75
+ */
76
+ async rerunStep(stepKey, options) {
77
+ var _a;
78
+ const i = this.config.stages.findIndex(s => s.key === stepKey);
79
+ if (i === -1)
80
+ return undefined;
81
+ const stage = this.config.stages[i];
82
+ const key = stage.key;
83
+ const signal = options === null || options === void 0 ? void 0 : options.externalSignal;
84
+ this.logs.push({ type: 'log', message: `rerunStep:${key}:start`, timestamp: new Date(), data: { stepIndex: i } });
85
+ await this.emit('log', { type: 'rerunStep:start', stepKey: key, stepIndex: i });
86
+ this.stageResults[key] = { status: 'pending' };
87
+ this.notifyStageResults();
88
+ this.progress.updateStage(i, 'loading');
89
+ await this.emitStepStart({ stepIndex: i, stepKey: key, status: 'loading', stageResults: { ...this.stageResults } });
90
+ await this.emit(`step:${key}:start`, { stepIndex: i, stepKey: key, status: 'loading', stageResults: { ...this.stageResults } });
91
+ try {
92
+ let stepResult;
93
+ if (typeof stage.request === 'function') {
94
+ stepResult = await stage.request(i > 0 ? (_a = this.stageResults[this.config.stages[i - 1].key]) === null || _a === void 0 ? void 0 : _a.data : undefined, this.stageResults);
95
+ }
96
+ else {
97
+ const res = await this.executor.execute(stage.key, undefined, stage.retryCount, stage.timeoutMs);
98
+ stepResult = res.data;
99
+ }
100
+ if (options === null || options === void 0 ? void 0 : options.onStepPause) {
101
+ stepResult = await options.onStepPause(i, stepResult, this.stageResults);
102
+ }
103
+ this.stageResults[key] = { status: 'success', data: stepResult };
104
+ this.notifyStageResults();
105
+ this.progress.updateStage(i, 'success');
106
+ await this.emitStepFinish({ stepIndex: i, stepKey: key, status: 'success', data: stepResult, stageResults: { ...this.stageResults } });
107
+ await this.emit(`step:${key}:success`, { stepIndex: i, stepKey: key, status: 'success', data: stepResult, stageResults: { ...this.stageResults } });
108
+ this.logs.push({ type: 'log', message: `rerunStep:${key}:success`, timestamp: new Date(), data: { stepIndex: i, data: stepResult } });
109
+ await this.emit('log', { type: 'rerunStep:success', stepKey: key, stepIndex: i, data: stepResult });
110
+ return this.stageResults[key];
111
+ }
112
+ catch (err) {
113
+ let handled;
114
+ if (stage && typeof stage.errorHandler === 'function') {
115
+ handled = stage.errorHandler(err, stage.key, this.sharedData);
116
+ }
117
+ else if (stage) {
118
+ handled = this.errorHandler.handle(err, stage.key);
119
+ }
120
+ else {
121
+ handled = this.errorHandler.handle(err, 'unknown');
122
+ }
123
+ if (!handled && stage) {
124
+ handled = this.errorHandler.handle(err, stage.key);
125
+ }
126
+ const { toApiError } = await import('./rest-client.js');
127
+ const apiError = toApiError(handled !== null && handled !== void 0 ? handled : err);
128
+ this.stageResults[key] = { status: 'error', error: apiError };
129
+ this.notifyStageResults();
130
+ this.progress.updateStage(i, 'error');
131
+ await this.emitStepError({ stepIndex: i, stepKey: key, status: 'error', error: apiError, stageResults: { ...this.stageResults } });
132
+ await this.emit(`step:${key}:error`, { stepIndex: i, stepKey: key, status: 'error', error: apiError, stageResults: { ...this.stageResults } });
133
+ this.logs.push({ type: 'error', message: `rerunStep:${key}:error`, timestamp: new Date(), data: { stepIndex: i, error: apiError } });
134
+ await this.emit('log', { type: 'rerunStep:error', stepKey: key, stepIndex: i, error: apiError });
135
+ return this.stageResults[key];
136
+ }
137
+ }
138
+ /**
139
+ * Отменить выполнение пайплайна (вызывает ошибку AbortError)
140
+ */
141
+ abort() {
142
+ if (this.abortController) {
143
+ this.abortController.abort();
144
+ }
145
+ }
146
+ /**
147
+ * Проверить, был ли пайплайн отменён
148
+ */
149
+ isAborted() {
150
+ var _a, _b;
151
+ return (_b = (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.signal.aborted) !== null && _b !== void 0 ? _b : false;
152
+ }
153
+ /**
154
+ * Подписка на событие начала шага
155
+ */
156
+ onStepStart(handler) {
157
+ this.onStepStartHandlers.push(handler);
158
+ return () => {
159
+ this.onStepStartHandlers = this.onStepStartHandlers.filter(h => h !== handler);
160
+ };
161
+ }
162
+ /**
163
+ * Подписка на событие успешного завершения шага
164
+ */
165
+ onStepFinish(handler) {
166
+ this.onStepFinishHandlers.push(handler);
167
+ return () => {
168
+ this.onStepFinishHandlers = this.onStepFinishHandlers.filter(h => h !== handler);
169
+ };
170
+ }
171
+ /**
172
+ * Подписка на событие ошибки шага
173
+ */
174
+ onStepError(handler) {
175
+ this.onStepErrorHandlers.push(handler);
176
+ return () => {
177
+ this.onStepErrorHandlers = this.onStepErrorHandlers.filter(h => h !== handler);
178
+ };
179
+ }
180
+ async emitStepStart(event) {
181
+ for (const handler of this.onStepStartHandlers) {
182
+ await handler(event);
183
+ }
184
+ // Гибкая подписка на step:<key>:start
185
+ await this.emit(`step:${event.stepKey}:start`, event);
186
+ // Логирование
187
+ this.logs.push({ type: 'log', message: `step:${event.stepKey}:start`, timestamp: new Date(), data: event });
188
+ await this.emit('log', { type: 'step:start', ...event });
189
+ }
190
+ async emitStepFinish(event) {
191
+ for (const handler of this.onStepFinishHandlers) {
192
+ await handler(event);
193
+ }
194
+ await this.emit(`step:${event.stepKey}:success`, event);
195
+ this.logs.push({ type: 'log', message: `step:${event.stepKey}:success`, timestamp: new Date(), data: event });
196
+ await this.emit('log', { type: 'step:success', ...event });
197
+ }
198
+ async emitStepError(event) {
199
+ for (const handler of this.onStepErrorHandlers) {
200
+ await handler(event);
201
+ }
202
+ await this.emit(`step:${event.stepKey}:error`, event);
203
+ this.logs.push({ type: 'error', message: `step:${event.stepKey}:error`, timestamp: new Date(), data: event });
204
+ await this.emit('log', { type: 'step:error', ...event });
15
205
  }
16
206
  /**
17
207
  * Подписаться на изменения прогресса выполнения pipeline
@@ -21,6 +211,12 @@ class PipelineOrchestrator {
21
211
  subscribeProgress(listener) {
22
212
  return this.progress.subscribe(listener);
23
213
  }
214
+ /**
215
+ * Подписка на прогресс с фильтрацией по этапу (stepKey) или общий
216
+ */
217
+ subscribeStepProgress(stepKey, listener) {
218
+ return this.on(`step:${stepKey}:progress`, listener);
219
+ }
24
220
  /**
25
221
  * Получить текущий прогресс выполнения pipeline (snapshot, не реактивный)
26
222
  */
@@ -28,35 +224,82 @@ class PipelineOrchestrator {
28
224
  return this.progress.getProgress();
29
225
  }
30
226
  /**
31
- * @param onStepPause
32
- * Необязательный callback, вызывается после каждого шага (до перехода к следующему).
33
- * Позволяет приостановить выполнение, запросить подтверждение пользователя или изменить результат шага.
34
- * Должен вернуть (optionally изменённый) результат шага или промис с ним.
35
- * Если не передан — пайплайн работает как раньше.
227
+ * Возвращает текущий снимок состояния прогресса (не реактивный).
228
+ * Для отслеживания изменений используйте subscribeProgress.
36
229
  */
37
- async run(onStepPause) {
38
- const results = [];
39
- const errors = [];
230
+ getProgressRef() {
231
+ return this.progress.getProgressRef();
232
+ }
233
+ /**
234
+ * Запустить выполнение пайплайна
235
+ * @param onStepPause callback для пользовательской паузы между шагами
236
+ * @param externalSignal внешний AbortSignal (опционально)
237
+ */
238
+ async run(onStepPause, externalSignal) {
239
+ var _a, _b, _c;
240
+ if (this.autoReset) {
241
+ this.stageResults = {};
242
+ this.notifyStageResults();
243
+ }
40
244
  let success = true;
245
+ // Создаём новый AbortController для этого запуска
246
+ this.abortController = new AbortController();
247
+ const signal = externalSignal !== null && externalSignal !== void 0 ? externalSignal : this.abortController.signal;
41
248
  for (let i = 0; i < this.config.stages.length; i++) {
249
+ if (signal.aborted) {
250
+ // Прерываем выполнение, если был вызван abort
251
+ const { toApiError } = await import('./rest-client.js');
252
+ const apiError = toApiError({ message: 'Pipeline aborted', code: 'ABORTED' });
253
+ const key = ((_a = this.config.stages[i]) === null || _a === void 0 ? void 0 : _a.key) || `stage${i}`;
254
+ this.stageResults[key] = { status: 'error', error: apiError };
255
+ this.notifyStageResults();
256
+ this.progress.updateStage(i, 'error');
257
+ this.logs.push({ type: 'error', message: `abort:${key}`, timestamp: new Date(), data: { stepIndex: i, error: apiError } });
258
+ await this.emit('log', { type: 'abort', stepKey: key, stepIndex: i, error: apiError });
259
+ await this.emitStepError({
260
+ stepIndex: i,
261
+ stepKey: key,
262
+ status: 'error',
263
+ error: apiError,
264
+ stageResults: { ...this.stageResults },
265
+ });
266
+ success = false;
267
+ break;
268
+ }
42
269
  const stage = this.config.stages[i];
43
- this.progress.updateStage(i, 'in-progress');
270
+ const key = (stage === null || stage === void 0 ? void 0 : stage.key) || `stage${i}`;
271
+ this.stageResults[key] = { status: 'pending' };
272
+ this.notifyStageResults();
273
+ this.progress.updateStage(i, 'loading');
274
+ // Гибкая подписка на прогресс шага
275
+ await this.emit(`step:${key}:progress`, 'loading');
276
+ // emit step start
277
+ await this.emitStepStart({
278
+ stepIndex: i,
279
+ stepKey: key,
280
+ status: 'loading',
281
+ stageResults: { ...this.stageResults },
282
+ });
44
283
  if (!stage) {
45
284
  this.progress.updateStage(i, 'skipped');
46
- results.push(undefined);
285
+ this.stageResults[key] = { status: 'skipped' };
286
+ this.notifyStageResults();
287
+ await this.emit(`step:${key}:progress`, 'skipped');
47
288
  continue;
48
289
  }
49
290
  // Проверка условия выполнения этапа
50
- if (stage.condition && !stage.condition(results[i - 1], results, this.sharedData)) {
291
+ if (stage.condition && !stage.condition(i > 0 ? (_b = this.stageResults[this.config.stages[i - 1].key]) === null || _b === void 0 ? void 0 : _b.data : undefined, this.stageResults, this.sharedData)) {
51
292
  this.progress.updateStage(i, 'skipped');
52
- results.push(undefined);
293
+ this.stageResults[key] = { status: 'skipped' };
294
+ this.notifyStageResults();
295
+ await this.emit(`step:${key}:progress`, 'skipped');
53
296
  continue;
54
297
  }
55
298
  try {
56
299
  let stepResult;
57
300
  // Всегда передаём (prev, allResults) в request — best practice для pipeline
58
301
  if (typeof stage.request === 'function') {
59
- stepResult = await stage.request(results[i - 1], results);
302
+ stepResult = await stage.request(i > 0 ? (_c = this.stageResults[this.config.stages[i - 1].key]) === null || _c === void 0 ? void 0 : _c.data : undefined, this.stageResults);
60
303
  }
61
304
  else if (stage.key) {
62
305
  const res = await this.executor.execute(stage.key, undefined, stage.retryCount, stage.timeoutMs);
@@ -67,11 +310,20 @@ class PipelineOrchestrator {
67
310
  }
68
311
  // --- Пользовательская пауза/подтверждение/изменение результата ---
69
312
  if (onStepPause) {
70
- stepResult = await onStepPause(i, stepResult, results);
313
+ stepResult = await onStepPause(i, stepResult, this.stageResults);
71
314
  }
72
- results.push(stepResult);
315
+ this.stageResults[key] = { status: 'success', data: stepResult };
316
+ this.notifyStageResults();
73
317
  this.progress.updateStage(i, 'success');
74
- // ...existing code...
318
+ await this.emit(`step:${key}:progress`, 'success');
319
+ // emit step finish
320
+ await this.emitStepFinish({
321
+ stepIndex: i,
322
+ stepKey: key,
323
+ status: 'success',
324
+ data: stepResult,
325
+ stageResults: { ...this.stageResults },
326
+ });
75
327
  }
76
328
  catch (err) {
77
329
  let handled;
@@ -87,13 +339,26 @@ class PipelineOrchestrator {
87
339
  if (!handled && stage) {
88
340
  handled = this.errorHandler.handle(err, stage.key);
89
341
  }
90
- errors.push(handled);
342
+ // Унификация: всегда ApiError
343
+ const { toApiError } = await import('./rest-client.js');
344
+ const apiError = toApiError(handled !== null && handled !== void 0 ? handled : err);
345
+ this.stageResults[key] = { status: 'error', error: apiError };
346
+ this.notifyStageResults();
91
347
  this.progress.updateStage(i, 'error');
348
+ await this.emit(`step:${key}:progress`, 'error');
349
+ // emit step error
350
+ await this.emitStepError({
351
+ stepIndex: i,
352
+ stepKey: key,
353
+ status: 'error',
354
+ error: apiError,
355
+ stageResults: { ...this.stageResults },
356
+ });
92
357
  success = false;
93
358
  break;
94
359
  }
95
360
  }
96
- return { results, errors, success };
361
+ return { stageResults: { ...this.stageResults }, success };
97
362
  }
98
363
  }
99
364
  exports.PipelineOrchestrator = PipelineOrchestrator;
@@ -4,11 +4,16 @@ export declare class ProgressTracker {
4
4
  private progress;
5
5
  private listeners;
6
6
  constructor(totalStages: number);
7
+ /**
8
+ * Возвращает текущий снимок состояния прогресса (не реактивный).
9
+ * Для отслеживания изменений используйте subscribeProgress.
10
+ */
11
+ getProgressRef(): PipelineProgress;
7
12
  updateStage(stage: number, status: PipelineProgress['stageStatuses'][number]): void;
8
13
  getProgress(): {
9
14
  currentStage: number;
10
15
  totalStages: number;
11
- stageStatuses: Array<"pending" | "in-progress" | "success" | "error" | "skipped">;
16
+ stageStatuses: Array<import("./types").PipelineStepStatus>;
12
17
  };
13
18
  subscribe(listener: ProgressListener): () => void;
14
19
  private notify;
@@ -10,6 +10,13 @@ class ProgressTracker {
10
10
  stageStatuses: Array(totalStages).fill('pending'),
11
11
  };
12
12
  }
13
+ /**
14
+ * Возвращает текущий снимок состояния прогресса (не реактивный).
15
+ * Для отслеживания изменений используйте subscribeProgress.
16
+ */
17
+ getProgressRef() {
18
+ return this.progress;
19
+ }
13
20
  updateStage(stage, status) {
14
21
  this.progress.stageStatuses[stage] = status;
15
22
  this.progress.currentStage = stage;
package/dist/types.d.ts CHANGED
@@ -63,24 +63,64 @@ export type RestRequestConfig = import('axios').AxiosRequestConfig & {
63
63
  skipRateLimit?: boolean;
64
64
  requestId?: string;
65
65
  };
66
- export type PipelineStageConfig<Input, Output> = {
66
+ /**
67
+ * Конфиг одного шага (этапа) pipeline
68
+ * @template Input Тип входных данных шага
69
+ * @template Output Тип результата шага
70
+ */
71
+ export type PipelineStageConfig<Input = any, Output = any> = {
72
+ /** Уникальный ключ шага */
67
73
  key: string;
68
- request: (input: Input, allResults?: any) => Promise<Output>;
69
- condition?: (input: Input, prevResults: any, sharedData?: Record<string, any>) => boolean;
74
+ /** Асинхронная функция-запрос шага */
75
+ request: (input: Input, allResults?: Record<string, PipelineStepResult>) => Promise<Output>;
76
+ /** Условие выполнения шага */
77
+ condition?: (input: Input, prevResults: Record<string, PipelineStepResult>, sharedData?: Record<string, any>) => boolean;
78
+ /** Количество попыток при ошибке */
70
79
  retryCount?: number;
80
+ /** Таймаут шага (мс) */
71
81
  timeoutMs?: number;
82
+ /** Обработчик ошибок шага */
72
83
  errorHandler?: (error: any, stageKey: string, sharedData?: Record<string, any>) => any;
73
84
  };
85
+ /**
86
+ * Статус выполнения шага pipeline
87
+ */
88
+ export type PipelineStepStatus = 'pending' | 'loading' | 'success' | 'error' | 'skipped';
89
+ /**
90
+ * Результат выполнения шага pipeline
91
+ */
92
+ export type PipelineStepResult = {
93
+ /** Статус шага */
94
+ status: PipelineStepStatus;
95
+ /** Данные результата (если успех) */
96
+ data?: any;
97
+ /** Ошибка (если error) */
98
+ error?: import('./types').ApiError;
99
+ };
100
+ /**
101
+ * Конфиг всего pipeline (массив этапов)
102
+ */
74
103
  export type PipelineConfig = {
75
- stages: PipelineStageConfig<any, any>[];
104
+ stages: PipelineStageConfig[];
76
105
  };
106
+ /**
107
+ * Прогресс выполнения pipeline
108
+ */
77
109
  export type PipelineProgress = {
78
110
  currentStage: number;
79
111
  totalStages: number;
80
- stageStatuses: Array<'pending' | 'in-progress' | 'success' | 'error' | 'skipped'>;
112
+ stageStatuses: Array<PipelineStepStatus>;
81
113
  };
114
+ /**
115
+ * Результаты всех шагов pipeline (ключ — имя шага)
116
+ */
117
+ export type PipelineStageResults = Record<string, PipelineStepResult>;
118
+ /**
119
+ * Итоговый результат выполнения pipeline
120
+ */
82
121
  export type PipelineResult = {
83
- results: any[];
84
- errors: any[];
122
+ /** Результаты по шагам */
123
+ stageResults: PipelineStageResults;
124
+ /** true, если pipeline завершился успешно */
85
125
  success: boolean;
86
126
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rest-pipeline-js",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Pipeline Orchestration Utilities for JavaScript REST API Clients",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",