runsheet 0.5.0 → 0.7.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 +291 -249
- package/dist/index.cjs +418 -520
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +365 -400
- package/dist/index.d.ts +365 -400
- package/dist/index.js +415 -513
- package/dist/index.js.map +1 -1
- package/llms.txt +93 -124
- package/package.json +2 -5
package/dist/index.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// src/define-step.ts
|
|
2
|
-
import { composable } from "composable-functions";
|
|
3
|
-
|
|
4
1
|
// src/errors.ts
|
|
5
2
|
var RunsheetError = class extends Error {
|
|
6
3
|
/** Discriminant code identifying the type of library error. */
|
|
@@ -69,12 +66,6 @@ var StrictOverlapError = class extends RunsheetError {
|
|
|
69
66
|
this.steps = steps;
|
|
70
67
|
}
|
|
71
68
|
};
|
|
72
|
-
var ChoiceNoMatchError = class extends RunsheetError {
|
|
73
|
-
constructor(message) {
|
|
74
|
-
super("CHOICE_NO_MATCH", message);
|
|
75
|
-
this.name = "ChoiceNoMatchError";
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
69
|
var UnknownError = class extends RunsheetError {
|
|
79
70
|
/** The original thrown value before stringification. */
|
|
80
71
|
originalValue;
|
|
@@ -85,24 +76,73 @@ var UnknownError = class extends RunsheetError {
|
|
|
85
76
|
}
|
|
86
77
|
};
|
|
87
78
|
var RollbackError = class extends RunsheetError {
|
|
88
|
-
|
|
79
|
+
/** The individual errors from each failed rollback handler. */
|
|
80
|
+
causes;
|
|
81
|
+
constructor(message, causes = []) {
|
|
89
82
|
super("ROLLBACK", message);
|
|
90
83
|
this.name = "RollbackError";
|
|
84
|
+
this.causes = causes;
|
|
85
|
+
if (causes.length === 1) this.cause = causes[0];
|
|
86
|
+
else if (causes.length > 1) this.cause = new AggregateError(causes, message);
|
|
91
87
|
}
|
|
92
88
|
};
|
|
93
89
|
|
|
94
|
-
// src/
|
|
95
|
-
function
|
|
96
|
-
|
|
90
|
+
// src/internal.ts
|
|
91
|
+
function toError(err) {
|
|
92
|
+
if (err instanceof Error) return err;
|
|
93
|
+
return new UnknownError(String(err), err);
|
|
94
|
+
}
|
|
95
|
+
var EMPTY_ROLLBACK = Object.freeze({
|
|
96
|
+
completed: Object.freeze([]),
|
|
97
|
+
failed: Object.freeze([])
|
|
98
|
+
});
|
|
99
|
+
function formatIssues(issues) {
|
|
100
|
+
return issues.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join(", ");
|
|
101
|
+
}
|
|
102
|
+
function collapseErrors(errors, message) {
|
|
103
|
+
return errors.length === 1 ? errors[0] : new AggregateError(errors, message);
|
|
104
|
+
}
|
|
105
|
+
function createStepObject(fields) {
|
|
106
|
+
return Object.freeze({
|
|
107
|
+
name: fields.name,
|
|
108
|
+
requires: fields.requires ?? void 0,
|
|
109
|
+
provides: fields.provides ?? void 0,
|
|
110
|
+
run: fields.run,
|
|
111
|
+
rollback: fields.rollback ?? void 0,
|
|
112
|
+
retry: fields.retry ?? void 0,
|
|
113
|
+
timeout: fields.timeout ?? void 0
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function baseMeta(name, args) {
|
|
117
|
+
return Object.freeze({ name, args });
|
|
118
|
+
}
|
|
119
|
+
function aggregateMeta(name, args, stepsExecuted) {
|
|
120
|
+
return Object.freeze({ name, args, stepsExecuted });
|
|
121
|
+
}
|
|
122
|
+
function stepSuccess(data, meta) {
|
|
123
|
+
return Object.freeze({ success: true, data, meta });
|
|
124
|
+
}
|
|
125
|
+
function stepFailure(error, meta, failedStep, rollback = EMPTY_ROLLBACK) {
|
|
126
|
+
return Object.freeze({ success: false, error, meta, failedStep, rollback });
|
|
127
|
+
}
|
|
128
|
+
function aggregateSuccess(data, meta) {
|
|
129
|
+
return Object.freeze({ success: true, data, meta });
|
|
130
|
+
}
|
|
131
|
+
function aggregateFailure(error, meta, failedStep, rollback = EMPTY_ROLLBACK) {
|
|
132
|
+
return Object.freeze({ success: false, error, meta, failedStep, rollback });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/step.ts
|
|
136
|
+
function withTimeout(fn, stepName, ms) {
|
|
137
|
+
return async () => {
|
|
97
138
|
let timer;
|
|
98
|
-
const timeout = new Promise((
|
|
139
|
+
const timeout = new Promise((_, reject) => {
|
|
99
140
|
timer = setTimeout(() => {
|
|
100
|
-
|
|
101
|
-
resolve({ success: false, errors: [error] });
|
|
141
|
+
reject(new TimeoutError(`${stepName} timed out after ${ms}ms`, ms));
|
|
102
142
|
}, ms);
|
|
103
143
|
});
|
|
104
144
|
try {
|
|
105
|
-
return await Promise.race([
|
|
145
|
+
return await Promise.race([Promise.resolve(fn()), timeout]);
|
|
106
146
|
} finally {
|
|
107
147
|
clearTimeout(timer);
|
|
108
148
|
}
|
|
@@ -114,303 +154,306 @@ function computeDelay(policy, attempt) {
|
|
|
114
154
|
const strategy = policy.backoff ?? "linear";
|
|
115
155
|
return strategy === "exponential" ? base * 2 ** (attempt - 1) : base * attempt;
|
|
116
156
|
}
|
|
117
|
-
function withRetry(
|
|
118
|
-
return async (
|
|
119
|
-
let
|
|
120
|
-
|
|
157
|
+
function withRetry(fn, stepName, policy) {
|
|
158
|
+
return async () => {
|
|
159
|
+
let lastError;
|
|
160
|
+
const errors = [];
|
|
161
|
+
try {
|
|
162
|
+
return await fn();
|
|
163
|
+
} catch (err) {
|
|
164
|
+
lastError = toError(err);
|
|
165
|
+
errors.push(lastError);
|
|
166
|
+
}
|
|
121
167
|
for (let attempt = 1; attempt <= policy.count; attempt++) {
|
|
122
|
-
if (policy.retryIf && !policy.retryIf(
|
|
168
|
+
if (policy.retryIf && !policy.retryIf(errors)) throw lastError;
|
|
123
169
|
const delay = computeDelay(policy, attempt);
|
|
124
|
-
if (delay > 0) await new Promise((r) => setTimeout(r, delay));
|
|
125
|
-
|
|
126
|
-
|
|
170
|
+
if (delay > 0) await new Promise((r) => setTimeout(() => r(void 0), delay));
|
|
171
|
+
try {
|
|
172
|
+
return await fn();
|
|
173
|
+
} catch (err) {
|
|
174
|
+
lastError = toError(err);
|
|
175
|
+
errors.push(lastError);
|
|
176
|
+
}
|
|
127
177
|
}
|
|
128
178
|
const error = new RetryExhaustedError(
|
|
129
179
|
`${stepName} failed after ${policy.count} retries`,
|
|
130
180
|
policy.count + 1
|
|
131
181
|
);
|
|
132
|
-
|
|
182
|
+
error.cause = errors.length === 1 ? errors[0] : new AggregateError(errors, error.message);
|
|
183
|
+
throw error;
|
|
133
184
|
};
|
|
134
185
|
}
|
|
135
|
-
function
|
|
136
|
-
let
|
|
137
|
-
if (timeout !== void 0)
|
|
138
|
-
|
|
139
|
-
|
|
186
|
+
function buildExecutor(config) {
|
|
187
|
+
let fn = (ctx) => Promise.resolve(config.run(ctx));
|
|
188
|
+
if (config.timeout !== void 0) {
|
|
189
|
+
const baseFn = fn;
|
|
190
|
+
const ms = config.timeout;
|
|
191
|
+
fn = (ctx) => withTimeout(() => baseFn(ctx), config.name, ms)();
|
|
192
|
+
}
|
|
193
|
+
if (config.retry !== void 0) {
|
|
194
|
+
const baseFn = fn;
|
|
195
|
+
const policy = config.retry;
|
|
196
|
+
fn = (ctx) => withRetry(() => baseFn(ctx), config.name, policy)();
|
|
197
|
+
}
|
|
198
|
+
return fn;
|
|
140
199
|
}
|
|
141
|
-
function
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
200
|
+
function step(config) {
|
|
201
|
+
const execute = buildExecutor(config);
|
|
202
|
+
const run = async (ctx) => {
|
|
203
|
+
const frozenCtx = Object.freeze({ ...ctx });
|
|
204
|
+
const meta = baseMeta(config.name, frozenCtx);
|
|
205
|
+
if (config.requires) {
|
|
206
|
+
const parsed = config.requires.safeParse(frozenCtx);
|
|
207
|
+
if (!parsed.success) {
|
|
208
|
+
const error = new RequiresValidationError(
|
|
209
|
+
`${config.name} requires: ${formatIssues(parsed.error.issues)}`
|
|
210
|
+
);
|
|
211
|
+
return stepFailure(error, meta, config.name);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
let data;
|
|
215
|
+
try {
|
|
216
|
+
data = await execute(frozenCtx);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
return stepFailure(toError(err), meta, config.name);
|
|
219
|
+
}
|
|
220
|
+
if (config.provides) {
|
|
221
|
+
const parsed = config.provides.safeParse(data);
|
|
222
|
+
if (!parsed.success) {
|
|
223
|
+
const error = new ProvidesValidationError(
|
|
224
|
+
`${config.name} provides: ${formatIssues(parsed.error.issues)}`
|
|
225
|
+
);
|
|
226
|
+
return stepFailure(error, meta, config.name);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return stepSuccess(data, meta);
|
|
230
|
+
};
|
|
231
|
+
return createStepObject({
|
|
145
232
|
name: config.name,
|
|
146
|
-
requires: config.requires
|
|
147
|
-
provides: config.provides
|
|
148
|
-
run
|
|
233
|
+
requires: config.requires,
|
|
234
|
+
provides: config.provides,
|
|
235
|
+
run,
|
|
149
236
|
rollback: config.rollback ? async (ctx, output) => {
|
|
150
237
|
await config.rollback(ctx, output);
|
|
151
238
|
} : void 0,
|
|
152
|
-
retry: config.retry
|
|
153
|
-
timeout: config.timeout
|
|
239
|
+
retry: config.retry,
|
|
240
|
+
timeout: config.timeout
|
|
154
241
|
});
|
|
155
242
|
}
|
|
156
243
|
|
|
157
|
-
// src/
|
|
158
|
-
function
|
|
159
|
-
return middlewares.reduceRight((next, mw) => mw(step, next), executor);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// src/when.ts
|
|
163
|
-
function when(predicate, step) {
|
|
244
|
+
// src/builder.ts
|
|
245
|
+
function makeBuilder(state) {
|
|
164
246
|
return Object.freeze({
|
|
165
|
-
|
|
166
|
-
|
|
247
|
+
step: (step2) => makeBuilder({
|
|
248
|
+
...state,
|
|
249
|
+
steps: [...state.steps, step2]
|
|
250
|
+
}),
|
|
251
|
+
use: (...middleware) => makeBuilder({
|
|
252
|
+
...state,
|
|
253
|
+
middleware: [...state.middleware, ...middleware]
|
|
254
|
+
}),
|
|
255
|
+
build: () => buildPipelineStep({
|
|
256
|
+
name: state.name,
|
|
257
|
+
steps: state.steps,
|
|
258
|
+
middleware: state.middleware.length > 0 ? state.middleware : void 0,
|
|
259
|
+
argsSchema: state.argsSchema,
|
|
260
|
+
strict: state.strict ? true : void 0
|
|
261
|
+
})
|
|
167
262
|
});
|
|
168
263
|
}
|
|
169
|
-
function isConditionalStep(step) {
|
|
170
|
-
return "predicate" in step && typeof step.predicate === "function";
|
|
171
|
-
}
|
|
172
264
|
|
|
173
|
-
// src/
|
|
174
|
-
function
|
|
175
|
-
|
|
176
|
-
return new UnknownError(String(err), err);
|
|
265
|
+
// src/middleware.ts
|
|
266
|
+
function applyMiddleware(middlewares, step2, executor) {
|
|
267
|
+
return middlewares.reduceRight((next, mw) => mw(step2, next), executor);
|
|
177
268
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
269
|
+
|
|
270
|
+
// src/when.ts
|
|
271
|
+
function when(predicate, step2) {
|
|
272
|
+
const name = step2.name;
|
|
273
|
+
const innerStep = step2;
|
|
274
|
+
const run = async (ctx) => {
|
|
275
|
+
const frozenCtx = Object.freeze({ ...ctx });
|
|
276
|
+
let shouldRun;
|
|
277
|
+
try {
|
|
278
|
+
shouldRun = predicate(frozenCtx);
|
|
279
|
+
} catch (err) {
|
|
280
|
+
const cause = toError(err);
|
|
281
|
+
const error = new PredicateError(`${name} predicate: ${cause.message}`);
|
|
282
|
+
error.cause = cause;
|
|
283
|
+
return aggregateFailure(error, aggregateMeta(name, frozenCtx, []), name);
|
|
284
|
+
}
|
|
285
|
+
if (!shouldRun) {
|
|
286
|
+
return aggregateSuccess({}, aggregateMeta(name, frozenCtx, []));
|
|
287
|
+
}
|
|
288
|
+
const result = await innerStep.run(frozenCtx);
|
|
289
|
+
if (!result.success) {
|
|
290
|
+
return aggregateFailure(result.error, aggregateMeta(name, frozenCtx, [name]), name);
|
|
291
|
+
}
|
|
292
|
+
return aggregateSuccess(result.data, aggregateMeta(name, frozenCtx, [name]));
|
|
293
|
+
};
|
|
294
|
+
return createStepObject({
|
|
295
|
+
name,
|
|
296
|
+
run,
|
|
297
|
+
rollback: innerStep.rollback,
|
|
298
|
+
requires: innerStep.requires,
|
|
299
|
+
provides: innerStep.provides
|
|
300
|
+
});
|
|
185
301
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
step.requires,
|
|
189
|
-
ctx,
|
|
190
|
-
`${step.name} requires`,
|
|
191
|
-
RequiresValidationError
|
|
192
|
-
);
|
|
193
|
-
if (requiresErrors) return { success: false, errors: requiresErrors };
|
|
194
|
-
const result = await step.run(ctx);
|
|
195
|
-
if (!result.success) return result;
|
|
196
|
-
const providesErrors = validateInnerSchema(
|
|
197
|
-
step.provides,
|
|
198
|
-
result.data,
|
|
199
|
-
`${step.name} provides`,
|
|
200
|
-
ProvidesValidationError
|
|
201
|
-
);
|
|
202
|
-
if (providesErrors) return { success: false, errors: providesErrors };
|
|
203
|
-
return result;
|
|
302
|
+
function wasSkipped(meta) {
|
|
303
|
+
return "stepsExecuted" in meta && meta.stepsExecuted.length === 0;
|
|
204
304
|
}
|
|
205
305
|
|
|
206
306
|
// src/pipeline.ts
|
|
207
307
|
function checkStrictOverlap(steps) {
|
|
208
308
|
const seen = /* @__PURE__ */ new Map();
|
|
209
|
-
for (const
|
|
210
|
-
if (!
|
|
211
|
-
const shape =
|
|
309
|
+
for (const step2 of steps) {
|
|
310
|
+
if (!step2.provides) continue;
|
|
311
|
+
const shape = step2.provides.shape;
|
|
212
312
|
if (!shape || typeof shape !== "object") continue;
|
|
213
313
|
for (const key of Object.keys(shape)) {
|
|
214
314
|
const existing = seen.get(key);
|
|
215
315
|
if (existing) {
|
|
216
316
|
throw new StrictOverlapError(
|
|
217
|
-
`strict mode: key "${key}" is provided by both "${existing}" and "${
|
|
317
|
+
`strict mode: key "${key}" is provided by both "${existing}" and "${step2.name}"`,
|
|
218
318
|
key,
|
|
219
|
-
[existing,
|
|
319
|
+
[existing, step2.name]
|
|
220
320
|
);
|
|
221
321
|
}
|
|
222
|
-
seen.set(key,
|
|
322
|
+
seen.set(key, step2.name);
|
|
223
323
|
}
|
|
224
324
|
}
|
|
225
325
|
}
|
|
226
|
-
function
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
);
|
|
233
|
-
return { success: false, errors };
|
|
326
|
+
function createExecutionState(args) {
|
|
327
|
+
return {
|
|
328
|
+
context: Object.freeze({ ...args }),
|
|
329
|
+
executed: [],
|
|
330
|
+
stepsExecuted: []
|
|
331
|
+
};
|
|
234
332
|
}
|
|
235
|
-
async function executeRollback(
|
|
333
|
+
async function executeRollback(executed) {
|
|
236
334
|
const completed = [];
|
|
237
335
|
const failed = [];
|
|
238
|
-
for (let i =
|
|
239
|
-
const
|
|
240
|
-
if (!step.rollback) continue;
|
|
336
|
+
for (let i = executed.length - 1; i >= 0; i--) {
|
|
337
|
+
const entry = executed[i];
|
|
338
|
+
if (!entry.step.rollback) continue;
|
|
241
339
|
try {
|
|
242
|
-
await step.rollback(
|
|
243
|
-
completed.push(step.name);
|
|
340
|
+
await entry.step.rollback(entry.snapshot, entry.output);
|
|
341
|
+
completed.push(entry.step.name);
|
|
244
342
|
} catch (err) {
|
|
245
|
-
failed.push({
|
|
246
|
-
step: step.name,
|
|
247
|
-
error: toError(err)
|
|
248
|
-
});
|
|
343
|
+
failed.push({ step: entry.step.name, error: toError(err) });
|
|
249
344
|
}
|
|
250
345
|
}
|
|
251
346
|
return Object.freeze({ completed, failed });
|
|
252
347
|
}
|
|
253
|
-
function createExecutionState(args) {
|
|
254
|
-
return {
|
|
255
|
-
context: Object.freeze({ ...args }),
|
|
256
|
-
snapshots: [],
|
|
257
|
-
outputs: [],
|
|
258
|
-
executedSteps: [],
|
|
259
|
-
stepsExecuted: [],
|
|
260
|
-
stepsSkipped: []
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
function pipelineFailure(pipelineName, args, state, failedStep, errors, rollback) {
|
|
264
|
-
return Object.freeze({
|
|
265
|
-
success: false,
|
|
266
|
-
errors,
|
|
267
|
-
meta: Object.freeze({
|
|
268
|
-
pipeline: pipelineName,
|
|
269
|
-
args,
|
|
270
|
-
stepsExecuted: state.stepsExecuted,
|
|
271
|
-
stepsSkipped: state.stepsSkipped
|
|
272
|
-
}),
|
|
273
|
-
failedStep,
|
|
274
|
-
rollback
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
function pipelineSuccess(pipelineName, args, state) {
|
|
278
|
-
return Object.freeze({
|
|
279
|
-
success: true,
|
|
280
|
-
data: state.context,
|
|
281
|
-
errors: [],
|
|
282
|
-
meta: Object.freeze({
|
|
283
|
-
pipeline: pipelineName,
|
|
284
|
-
args,
|
|
285
|
-
stepsExecuted: state.stepsExecuted,
|
|
286
|
-
stepsSkipped: state.stepsSkipped
|
|
287
|
-
})
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
function createStepExecutor(step) {
|
|
291
|
-
return async (ctx) => {
|
|
292
|
-
const requiresCheck = validateSchema(
|
|
293
|
-
step.requires,
|
|
294
|
-
ctx,
|
|
295
|
-
`${step.name} requires`,
|
|
296
|
-
RequiresValidationError
|
|
297
|
-
);
|
|
298
|
-
if (!requiresCheck.success) {
|
|
299
|
-
return { success: false, errors: requiresCheck.errors };
|
|
300
|
-
}
|
|
301
|
-
const result = await step.run(ctx);
|
|
302
|
-
if (!result.success) return result;
|
|
303
|
-
const providesCheck = validateSchema(
|
|
304
|
-
step.provides,
|
|
305
|
-
result.data,
|
|
306
|
-
`${step.name} provides`,
|
|
307
|
-
ProvidesValidationError
|
|
308
|
-
);
|
|
309
|
-
if (!providesCheck.success) {
|
|
310
|
-
return { success: false, errors: providesCheck.errors };
|
|
311
|
-
}
|
|
312
|
-
return {
|
|
313
|
-
success: true,
|
|
314
|
-
data: providesCheck.data,
|
|
315
|
-
errors: []
|
|
316
|
-
};
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
348
|
async function executePipeline(config, args) {
|
|
349
|
+
const frozenArgs = Object.freeze({ ...args });
|
|
320
350
|
if (config.argsSchema) {
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
ArgsValidationError
|
|
326
|
-
);
|
|
327
|
-
if (!argsCheck.success) {
|
|
328
|
-
const state2 = createExecutionState(args);
|
|
329
|
-
return pipelineFailure(
|
|
330
|
-
config.name,
|
|
331
|
-
args,
|
|
332
|
-
state2,
|
|
333
|
-
config.name,
|
|
334
|
-
argsCheck.errors,
|
|
335
|
-
Object.freeze({ completed: [], failed: [] })
|
|
351
|
+
const parsed = config.argsSchema.safeParse(frozenArgs);
|
|
352
|
+
if (!parsed.success) {
|
|
353
|
+
const error = new ArgsValidationError(
|
|
354
|
+
`${config.name} args: ${formatIssues(parsed.error.issues)}`
|
|
336
355
|
);
|
|
356
|
+
const meta2 = aggregateMeta(config.name, frozenArgs, []);
|
|
357
|
+
const state2 = createExecutionState(frozenArgs);
|
|
358
|
+
return { result: aggregateFailure(error, meta2, config.name), state: state2 };
|
|
337
359
|
}
|
|
338
360
|
}
|
|
339
|
-
const state = createExecutionState(
|
|
361
|
+
const state = createExecutionState(frozenArgs);
|
|
340
362
|
const middlewares = config.middleware ?? [];
|
|
341
|
-
for (const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
state.stepsSkipped.push(step.name);
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
} catch (err) {
|
|
348
|
-
const cause = toError(err);
|
|
349
|
-
const error = new PredicateError(`${step.name} predicate: ${cause.message}`);
|
|
350
|
-
error.cause = cause;
|
|
351
|
-
const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);
|
|
352
|
-
return pipelineFailure(config.name, args, state, step.name, [error], rollback);
|
|
353
|
-
}
|
|
354
|
-
state.snapshots.push(state.context);
|
|
355
|
-
const baseExecutor = createStepExecutor(step);
|
|
356
|
-
const executor = applyMiddleware(
|
|
363
|
+
for (const step2 of config.steps) {
|
|
364
|
+
const snapshot = state.context;
|
|
365
|
+
const executor = middlewares.length > 0 ? applyMiddleware(
|
|
357
366
|
middlewares,
|
|
358
|
-
{ name:
|
|
359
|
-
|
|
360
|
-
);
|
|
367
|
+
{ name: step2.name, requires: step2.requires, provides: step2.provides },
|
|
368
|
+
(ctx) => step2.run(ctx)
|
|
369
|
+
) : (ctx) => step2.run(ctx);
|
|
361
370
|
let result;
|
|
362
371
|
try {
|
|
363
372
|
result = await executor(state.context);
|
|
364
373
|
} catch (err) {
|
|
365
374
|
const error = toError(err);
|
|
366
|
-
state.
|
|
367
|
-
const
|
|
368
|
-
return
|
|
375
|
+
const rollback = await executeRollback(state.executed);
|
|
376
|
+
const meta2 = aggregateMeta(config.name, frozenArgs, [...state.stepsExecuted]);
|
|
377
|
+
return { result: aggregateFailure(error, meta2, step2.name, rollback), state };
|
|
369
378
|
}
|
|
370
379
|
if (!result.success) {
|
|
371
|
-
state.
|
|
372
|
-
const
|
|
373
|
-
return
|
|
380
|
+
const rollback = await executeRollback(state.executed);
|
|
381
|
+
const meta2 = aggregateMeta(config.name, frozenArgs, [...state.stepsExecuted]);
|
|
382
|
+
return { result: aggregateFailure(result.error, meta2, step2.name, rollback), state };
|
|
374
383
|
}
|
|
384
|
+
if (wasSkipped(result.meta)) continue;
|
|
375
385
|
const output = result.data;
|
|
376
|
-
state.
|
|
377
|
-
state.
|
|
378
|
-
state.stepsExecuted.push(step.name);
|
|
386
|
+
state.executed.push({ step: step2, snapshot, output });
|
|
387
|
+
state.stepsExecuted.push(step2.name);
|
|
379
388
|
state.context = Object.freeze({ ...state.context, ...output });
|
|
380
389
|
}
|
|
381
|
-
|
|
390
|
+
const meta = aggregateMeta(config.name, frozenArgs, [...state.stepsExecuted]);
|
|
391
|
+
return { result: aggregateSuccess(state.context, meta), state };
|
|
382
392
|
}
|
|
383
|
-
function
|
|
393
|
+
function buildPipelineStep(config) {
|
|
384
394
|
if (config.strict) checkStrictOverlap(config.steps);
|
|
385
|
-
|
|
395
|
+
const pipelineConfig = config;
|
|
396
|
+
const stateMap = /* @__PURE__ */ new WeakMap();
|
|
397
|
+
const run = async (ctx) => {
|
|
398
|
+
const outcome = await executePipeline(pipelineConfig, ctx);
|
|
399
|
+
if (outcome.result.success) {
|
|
400
|
+
stateMap.set(outcome.result.data, outcome.state);
|
|
401
|
+
}
|
|
402
|
+
return outcome.result;
|
|
403
|
+
};
|
|
404
|
+
const rollback = async (_ctx, output) => {
|
|
405
|
+
const state = stateMap.get(output);
|
|
406
|
+
if (!state) return;
|
|
407
|
+
stateMap.delete(output);
|
|
408
|
+
const report = await executeRollback(state.executed);
|
|
409
|
+
if (report.failed.length > 0) {
|
|
410
|
+
throw new RollbackError(
|
|
411
|
+
`${config.name}: ${report.failed.length} rollback(s) failed`,
|
|
412
|
+
report.failed.map((f) => f.error)
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
return createStepObject({
|
|
386
417
|
name: config.name,
|
|
387
|
-
|
|
418
|
+
requires: config.argsSchema,
|
|
419
|
+
run,
|
|
420
|
+
rollback
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
function pipeline(config) {
|
|
424
|
+
if (config.steps) {
|
|
425
|
+
return buildPipelineStep(
|
|
426
|
+
config
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
return makeBuilder({
|
|
430
|
+
name: config.name,
|
|
431
|
+
steps: [],
|
|
432
|
+
middleware: config.middleware ? [...config.middleware] : [],
|
|
433
|
+
argsSchema: config.argsSchema,
|
|
434
|
+
strict: config.strict ?? false
|
|
388
435
|
});
|
|
389
436
|
}
|
|
390
437
|
|
|
391
438
|
// src/parallel.ts
|
|
392
|
-
async function executeInner(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
} catch (err) {
|
|
398
|
-
const cause = toError(err);
|
|
399
|
-
const error = new PredicateError(`${step.name} predicate: ${cause.message}`);
|
|
400
|
-
error.cause = cause;
|
|
401
|
-
return { step, skipped: false, errors: [error] };
|
|
402
|
-
}
|
|
403
|
-
const result = await runInnerStep(step, ctx);
|
|
404
|
-
if (!result.success) return { step, skipped: false, errors: [...result.errors] };
|
|
405
|
-
return { step, skipped: false, output: result.data };
|
|
439
|
+
async function executeInner(step2, ctx) {
|
|
440
|
+
const result = await step2.run(ctx);
|
|
441
|
+
if (!result.success) return { step: step2, skipped: false, error: result.error };
|
|
442
|
+
if (wasSkipped(result.meta)) return { step: step2, skipped: true };
|
|
443
|
+
return { step: step2, skipped: false, output: result.data };
|
|
406
444
|
}
|
|
407
445
|
function parallel(...steps) {
|
|
408
446
|
const name = `parallel(${steps.map((s) => s.name).join(", ")})`;
|
|
409
447
|
const innerSteps = steps;
|
|
448
|
+
const executedMap = /* @__PURE__ */ new WeakMap();
|
|
410
449
|
const run = async (ctx) => {
|
|
411
|
-
const
|
|
450
|
+
const frozenCtx = Object.freeze({ ...ctx });
|
|
451
|
+
const settled = await Promise.allSettled(
|
|
452
|
+
innerSteps.map((step2) => executeInner(step2, frozenCtx))
|
|
453
|
+
);
|
|
412
454
|
const succeeded = [];
|
|
413
455
|
const allErrors = [];
|
|
456
|
+
const executed = [];
|
|
414
457
|
for (const s of settled) {
|
|
415
458
|
if (s.status === "rejected") {
|
|
416
459
|
allErrors.push(toError(s.reason));
|
|
@@ -419,54 +462,67 @@ function parallel(...steps) {
|
|
|
419
462
|
if (r.skipped) continue;
|
|
420
463
|
if (r.output) {
|
|
421
464
|
succeeded.push({ step: r.step, output: r.output });
|
|
422
|
-
|
|
423
|
-
|
|
465
|
+
executed.push(r.step.name);
|
|
466
|
+
} else if (r.error) {
|
|
467
|
+
allErrors.push(r.error);
|
|
424
468
|
}
|
|
425
469
|
}
|
|
426
470
|
}
|
|
427
471
|
if (allErrors.length > 0) {
|
|
472
|
+
const rollbackErrors = [];
|
|
428
473
|
for (let i = succeeded.length - 1; i >= 0; i--) {
|
|
429
|
-
const { step, output } = succeeded[i];
|
|
430
|
-
if (
|
|
474
|
+
const { step: step2, output } = succeeded[i];
|
|
475
|
+
if (step2.rollback) {
|
|
431
476
|
try {
|
|
432
|
-
await
|
|
433
|
-
} catch {
|
|
477
|
+
await step2.rollback(frozenCtx, output);
|
|
478
|
+
} catch (err) {
|
|
479
|
+
rollbackErrors.push(toError(err));
|
|
434
480
|
}
|
|
435
481
|
}
|
|
436
482
|
}
|
|
437
|
-
|
|
483
|
+
const error = collapseErrors(allErrors, `${name}: ${allErrors.length} step(s) failed`);
|
|
484
|
+
if (rollbackErrors.length > 0) {
|
|
485
|
+
error.cause = new RollbackError(
|
|
486
|
+
`${name}: ${rollbackErrors.length} partial-failure rollback(s) failed`,
|
|
487
|
+
rollbackErrors
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
const meta2 = aggregateMeta(name, frozenCtx, executed);
|
|
491
|
+
return aggregateFailure(error, meta2, name);
|
|
438
492
|
}
|
|
439
493
|
const merged = {};
|
|
440
494
|
for (const { output } of succeeded) {
|
|
441
495
|
Object.assign(merged, output);
|
|
442
496
|
}
|
|
443
|
-
|
|
497
|
+
executedMap.set(
|
|
498
|
+
merged,
|
|
499
|
+
succeeded.map((s) => ({ step: s.step, output: s.output }))
|
|
500
|
+
);
|
|
501
|
+
const meta = aggregateMeta(name, frozenCtx, executed);
|
|
502
|
+
return aggregateSuccess(merged, meta);
|
|
444
503
|
};
|
|
445
504
|
const rollback = async (ctx, mergedOutput) => {
|
|
505
|
+
const entries = executedMap.get(mergedOutput);
|
|
506
|
+
if (!entries) return;
|
|
507
|
+
executedMap.delete(mergedOutput);
|
|
446
508
|
const errors = [];
|
|
447
|
-
for (let i =
|
|
448
|
-
const step =
|
|
449
|
-
if (!
|
|
509
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
510
|
+
const { step: step2, output } = entries[i];
|
|
511
|
+
if (!step2.rollback) continue;
|
|
450
512
|
try {
|
|
451
|
-
await
|
|
513
|
+
await step2.rollback(ctx, output);
|
|
452
514
|
} catch (err) {
|
|
453
515
|
errors.push(toError(err));
|
|
454
516
|
}
|
|
455
517
|
}
|
|
456
518
|
if (errors.length > 0) {
|
|
457
|
-
|
|
458
|
-
error.cause = errors;
|
|
459
|
-
throw error;
|
|
519
|
+
throw new RollbackError(`${name}: ${errors.length} rollback(s) failed`, errors);
|
|
460
520
|
}
|
|
461
521
|
};
|
|
462
|
-
return
|
|
522
|
+
return createStepObject({
|
|
463
523
|
name,
|
|
464
|
-
requires: void 0,
|
|
465
|
-
provides: void 0,
|
|
466
524
|
run,
|
|
467
|
-
rollback
|
|
468
|
-
retry: void 0,
|
|
469
|
-
timeout: void 0
|
|
525
|
+
rollback
|
|
470
526
|
});
|
|
471
527
|
}
|
|
472
528
|
|
|
@@ -479,309 +535,158 @@ function normalizeBranches(args) {
|
|
|
479
535
|
}
|
|
480
536
|
function choice(...args) {
|
|
481
537
|
const innerBranches = normalizeBranches(args);
|
|
482
|
-
const name = `choice(${innerBranches.map(([,
|
|
538
|
+
const name = `choice(${innerBranches.map(([, step2]) => step2.name).join(", ")})`;
|
|
483
539
|
const branchMap = /* @__PURE__ */ new WeakMap();
|
|
484
540
|
const run = async (ctx) => {
|
|
541
|
+
const frozenCtx = Object.freeze({ ...ctx });
|
|
485
542
|
for (let i = 0; i < innerBranches.length; i++) {
|
|
486
|
-
const [predicate,
|
|
543
|
+
const [predicate, step2] = innerBranches[i];
|
|
487
544
|
let matches;
|
|
488
545
|
try {
|
|
489
|
-
matches = predicate(
|
|
546
|
+
matches = predicate(frozenCtx);
|
|
490
547
|
} catch (err) {
|
|
491
548
|
const cause = toError(err);
|
|
492
549
|
const error = new PredicateError(`${name} predicate: ${cause.message}`);
|
|
493
550
|
error.cause = cause;
|
|
494
|
-
|
|
551
|
+
const meta3 = aggregateMeta(name, frozenCtx, []);
|
|
552
|
+
return aggregateFailure(error, meta3, name);
|
|
495
553
|
}
|
|
496
554
|
if (!matches) continue;
|
|
497
|
-
const result = await
|
|
498
|
-
if (!result.success)
|
|
555
|
+
const result = await step2.run(frozenCtx);
|
|
556
|
+
if (!result.success) {
|
|
557
|
+
const meta3 = aggregateMeta(name, frozenCtx, [step2.name]);
|
|
558
|
+
return aggregateFailure(result.error, meta3, name);
|
|
559
|
+
}
|
|
499
560
|
branchMap.set(result.data, i);
|
|
500
|
-
|
|
561
|
+
const meta2 = aggregateMeta(name, frozenCtx, [step2.name]);
|
|
562
|
+
return aggregateSuccess(result.data, meta2);
|
|
501
563
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
errors: [new ChoiceNoMatchError(`${name}: no branch matched`)]
|
|
505
|
-
};
|
|
564
|
+
const meta = aggregateMeta(name, frozenCtx, []);
|
|
565
|
+
return aggregateSuccess({}, meta);
|
|
506
566
|
};
|
|
507
567
|
const rollback = async (ctx, output) => {
|
|
508
568
|
const branchIndex = branchMap.get(output);
|
|
509
569
|
if (branchIndex === void 0) return;
|
|
510
|
-
|
|
511
|
-
|
|
570
|
+
branchMap.delete(output);
|
|
571
|
+
const [, step2] = innerBranches[branchIndex];
|
|
572
|
+
if (step2.rollback) {
|
|
512
573
|
try {
|
|
513
|
-
await
|
|
574
|
+
await step2.rollback(ctx, output);
|
|
514
575
|
} catch (err) {
|
|
515
|
-
|
|
516
|
-
error.cause = [toError(err)];
|
|
517
|
-
throw error;
|
|
576
|
+
throw new RollbackError(`${name}: 1 rollback(s) failed`, [toError(err)]);
|
|
518
577
|
}
|
|
519
578
|
}
|
|
520
579
|
};
|
|
521
|
-
return
|
|
580
|
+
return createStepObject({
|
|
522
581
|
name,
|
|
523
|
-
requires: void 0,
|
|
524
|
-
provides: void 0,
|
|
525
582
|
run,
|
|
526
|
-
rollback
|
|
527
|
-
retry: void 0,
|
|
528
|
-
timeout: void 0
|
|
583
|
+
rollback
|
|
529
584
|
});
|
|
530
585
|
}
|
|
531
586
|
|
|
532
|
-
// src/
|
|
533
|
-
function
|
|
534
|
-
|
|
587
|
+
// src/distribute.ts
|
|
588
|
+
function crossProduct(mapping, ctx) {
|
|
589
|
+
const entries = Object.entries(mapping);
|
|
590
|
+
let combinations = [{}];
|
|
591
|
+
for (const [contextKey, stepKey] of entries) {
|
|
592
|
+
const items = ctx[contextKey];
|
|
593
|
+
if (!Array.isArray(items) || items.length === 0) return [];
|
|
594
|
+
const next = [];
|
|
595
|
+
for (const combo of combinations) {
|
|
596
|
+
for (const item of items) {
|
|
597
|
+
next.push({ ...combo, [stepKey]: item });
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
combinations = next;
|
|
601
|
+
}
|
|
602
|
+
return combinations;
|
|
535
603
|
}
|
|
536
|
-
function
|
|
537
|
-
const
|
|
538
|
-
const
|
|
604
|
+
function distribute(key, mapping, step2) {
|
|
605
|
+
const stepName = `distribute(${key}, ${step2.name})`;
|
|
606
|
+
const arrayKeys = new Set(Object.keys(mapping));
|
|
539
607
|
const executionMap = /* @__PURE__ */ new WeakMap();
|
|
540
608
|
const run = async (ctx) => {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
errors: [toError(err)]
|
|
548
|
-
};
|
|
609
|
+
const frozenCtx = Object.freeze({ ...ctx });
|
|
610
|
+
const meta = baseMeta(stepName, frozenCtx);
|
|
611
|
+
const combinations = crossProduct(mapping, frozenCtx);
|
|
612
|
+
const baseCtx = {};
|
|
613
|
+
for (const k of Object.keys(frozenCtx)) {
|
|
614
|
+
if (!arrayKeys.has(k)) baseCtx[k] = frozenCtx[k];
|
|
549
615
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
616
|
+
const settled = await Promise.allSettled(
|
|
617
|
+
combinations.map(async (combo) => {
|
|
618
|
+
const itemCtx = Object.freeze({ ...baseCtx, ...combo });
|
|
619
|
+
return step2.run(itemCtx);
|
|
620
|
+
})
|
|
621
|
+
);
|
|
622
|
+
const succeeded = [];
|
|
623
|
+
const allErrors = [];
|
|
624
|
+
for (let i = 0; i < settled.length; i++) {
|
|
625
|
+
const s = settled[i];
|
|
626
|
+
if (s.status === "rejected") {
|
|
627
|
+
allErrors.push(toError(s.reason));
|
|
628
|
+
} else if (!s.value.success) {
|
|
629
|
+
allErrors.push(s.value.error);
|
|
630
|
+
} else {
|
|
631
|
+
succeeded.push({ index: i, output: s.value.data });
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (allErrors.length > 0) {
|
|
635
|
+
const rollbackErrors = [];
|
|
636
|
+
if (step2.rollback) {
|
|
637
|
+
for (let i = succeeded.length - 1; i >= 0; i--) {
|
|
638
|
+
try {
|
|
639
|
+
const combo = combinations[succeeded[i].index];
|
|
640
|
+
const itemCtx = Object.freeze({ ...baseCtx, ...combo });
|
|
641
|
+
await step2.rollback(itemCtx, succeeded[i].output);
|
|
642
|
+
} catch (err) {
|
|
643
|
+
rollbackErrors.push(toError(err));
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const error = collapseErrors(allErrors, `${stepName}: ${allErrors.length} item(s) failed`);
|
|
648
|
+
if (rollbackErrors.length > 0) {
|
|
649
|
+
error.cause = new RollbackError(
|
|
650
|
+
`${stepName}: ${rollbackErrors.length} partial-failure rollback(s) failed`,
|
|
651
|
+
rollbackErrors
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
return stepFailure(error, meta, stepName);
|
|
559
655
|
}
|
|
656
|
+
const results = succeeded.map((s) => s.output);
|
|
657
|
+
const data = { [key]: results };
|
|
658
|
+
executionMap.set(data, { combinations, baseCtx });
|
|
659
|
+
return stepSuccess(data, meta);
|
|
560
660
|
};
|
|
561
|
-
const rollback =
|
|
562
|
-
const step = fnOrStep;
|
|
563
|
-
if (!step.rollback) return;
|
|
661
|
+
const rollback = step2.rollback ? async (_ctx, output) => {
|
|
564
662
|
const exec = executionMap.get(output);
|
|
565
663
|
if (!exec) return;
|
|
664
|
+
executionMap.delete(output);
|
|
566
665
|
const results = output[key];
|
|
567
666
|
const errors = [];
|
|
568
667
|
for (let i = results.length - 1; i >= 0; i--) {
|
|
569
668
|
try {
|
|
570
|
-
const itemCtx = {
|
|
571
|
-
|
|
669
|
+
const itemCtx = Object.freeze({
|
|
670
|
+
...exec.baseCtx,
|
|
671
|
+
...exec.combinations[i]
|
|
672
|
+
});
|
|
673
|
+
await step2.rollback(itemCtx, results[i]);
|
|
572
674
|
} catch (err) {
|
|
573
675
|
errors.push(toError(err));
|
|
574
676
|
}
|
|
575
677
|
}
|
|
576
678
|
if (errors.length > 0) {
|
|
577
|
-
|
|
578
|
-
error.cause = errors;
|
|
579
|
-
throw error;
|
|
679
|
+
throw new RollbackError(`${stepName}: ${errors.length} rollback(s) failed`, errors);
|
|
580
680
|
}
|
|
581
681
|
} : void 0;
|
|
582
|
-
return
|
|
583
|
-
name,
|
|
584
|
-
requires: void 0,
|
|
585
|
-
provides: void 0,
|
|
682
|
+
return createStepObject({
|
|
683
|
+
name: stepName,
|
|
586
684
|
run,
|
|
587
|
-
rollback
|
|
588
|
-
retry: void 0,
|
|
589
|
-
timeout: void 0
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
async function runStepMode(step, items, ctx, name, key, executionMap) {
|
|
593
|
-
const settled = await Promise.allSettled(
|
|
594
|
-
items.map(async (item) => {
|
|
595
|
-
const itemCtx = { ...ctx, ...item };
|
|
596
|
-
return runInnerStep(step, itemCtx);
|
|
597
|
-
})
|
|
598
|
-
);
|
|
599
|
-
const succeeded = [];
|
|
600
|
-
const allErrors = [];
|
|
601
|
-
for (let i = 0; i < settled.length; i++) {
|
|
602
|
-
const s = settled[i];
|
|
603
|
-
if (s.status === "rejected") {
|
|
604
|
-
allErrors.push(toError(s.reason));
|
|
605
|
-
} else if (!s.value.success) {
|
|
606
|
-
allErrors.push(...s.value.errors);
|
|
607
|
-
} else {
|
|
608
|
-
succeeded.push({ index: i, output: s.value.data });
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
if (allErrors.length > 0) {
|
|
612
|
-
if (step.rollback) {
|
|
613
|
-
for (let i = succeeded.length - 1; i >= 0; i--) {
|
|
614
|
-
try {
|
|
615
|
-
const itemCtx = { ...ctx, ...items[succeeded[i].index] };
|
|
616
|
-
await step.rollback(itemCtx, succeeded[i].output);
|
|
617
|
-
} catch {
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
return { success: false, errors: allErrors };
|
|
622
|
-
}
|
|
623
|
-
const results = succeeded.map((s) => s.output);
|
|
624
|
-
const data = { [key]: results };
|
|
625
|
-
executionMap.set(data, { items, ctx: { ...ctx } });
|
|
626
|
-
return { success: true, data, errors: [] };
|
|
627
|
-
}
|
|
628
|
-
async function runFunctionMode(fn, items, ctx, key) {
|
|
629
|
-
const settled = await Promise.allSettled(items.map(async (item) => fn(item, ctx)));
|
|
630
|
-
const results = [];
|
|
631
|
-
const allErrors = [];
|
|
632
|
-
for (const s of settled) {
|
|
633
|
-
if (s.status === "rejected") {
|
|
634
|
-
allErrors.push(toError(s.reason));
|
|
635
|
-
} else {
|
|
636
|
-
results.push(s.value);
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
if (allErrors.length > 0) {
|
|
640
|
-
return { success: false, errors: allErrors };
|
|
641
|
-
}
|
|
642
|
-
const data = { [key]: results };
|
|
643
|
-
return { success: true, data, errors: [] };
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// src/filter.ts
|
|
647
|
-
function filter(key, collection, predicate) {
|
|
648
|
-
const name = `filter(${key})`;
|
|
649
|
-
const run = async (ctx) => {
|
|
650
|
-
let items;
|
|
651
|
-
try {
|
|
652
|
-
items = collection(ctx);
|
|
653
|
-
} catch (err) {
|
|
654
|
-
return {
|
|
655
|
-
success: false,
|
|
656
|
-
errors: [toError(err)]
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
return runFilter(
|
|
660
|
-
items,
|
|
661
|
-
ctx,
|
|
662
|
-
predicate,
|
|
663
|
-
key
|
|
664
|
-
);
|
|
665
|
-
};
|
|
666
|
-
return Object.freeze({
|
|
667
|
-
name,
|
|
668
|
-
requires: void 0,
|
|
669
|
-
provides: void 0,
|
|
670
|
-
run,
|
|
671
|
-
rollback: void 0,
|
|
672
|
-
retry: void 0,
|
|
673
|
-
timeout: void 0
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
async function runFilter(items, ctx, predicate, key) {
|
|
677
|
-
const settled = await Promise.allSettled(items.map(async (item) => predicate(item, ctx)));
|
|
678
|
-
const results = [];
|
|
679
|
-
const allErrors = [];
|
|
680
|
-
for (let i = 0; i < settled.length; i++) {
|
|
681
|
-
const s = settled[i];
|
|
682
|
-
if (s.status === "rejected") {
|
|
683
|
-
allErrors.push(toError(s.reason));
|
|
684
|
-
} else if (s.value) {
|
|
685
|
-
results.push(items[i]);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
if (allErrors.length > 0) {
|
|
689
|
-
return { success: false, errors: allErrors };
|
|
690
|
-
}
|
|
691
|
-
const data = { [key]: results };
|
|
692
|
-
return { success: true, data, errors: [] };
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// src/flat-map.ts
|
|
696
|
-
function flatMap(key, collection, fn) {
|
|
697
|
-
const name = `flatMap(${key})`;
|
|
698
|
-
const run = async (ctx) => {
|
|
699
|
-
let items;
|
|
700
|
-
try {
|
|
701
|
-
items = collection(ctx);
|
|
702
|
-
} catch (err) {
|
|
703
|
-
return {
|
|
704
|
-
success: false,
|
|
705
|
-
errors: [toError(err)]
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
return runFlatMap(
|
|
709
|
-
items,
|
|
710
|
-
ctx,
|
|
711
|
-
fn,
|
|
712
|
-
key
|
|
713
|
-
);
|
|
714
|
-
};
|
|
715
|
-
return Object.freeze({
|
|
716
|
-
name,
|
|
717
|
-
requires: void 0,
|
|
718
|
-
provides: void 0,
|
|
719
|
-
run,
|
|
720
|
-
rollback: void 0,
|
|
721
|
-
retry: void 0,
|
|
722
|
-
timeout: void 0
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
async function runFlatMap(items, ctx, fn, key) {
|
|
726
|
-
const settled = await Promise.allSettled(items.map(async (item) => fn(item, ctx)));
|
|
727
|
-
const results = [];
|
|
728
|
-
const allErrors = [];
|
|
729
|
-
for (const s of settled) {
|
|
730
|
-
if (s.status === "rejected") {
|
|
731
|
-
allErrors.push(toError(s.reason));
|
|
732
|
-
} else {
|
|
733
|
-
results.push(...s.value);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
if (allErrors.length > 0) {
|
|
737
|
-
return { success: false, errors: allErrors };
|
|
738
|
-
}
|
|
739
|
-
const data = { [key]: results };
|
|
740
|
-
return { success: true, data, errors: [] };
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// src/builder.ts
|
|
744
|
-
function makeBuilder(state) {
|
|
745
|
-
return Object.freeze({
|
|
746
|
-
step: (step) => makeBuilder({
|
|
747
|
-
...state,
|
|
748
|
-
steps: [...state.steps, step]
|
|
749
|
-
}),
|
|
750
|
-
use: (...middleware) => makeBuilder({
|
|
751
|
-
...state,
|
|
752
|
-
middleware: [...state.middleware, ...middleware]
|
|
753
|
-
}),
|
|
754
|
-
build: () => buildPipeline({
|
|
755
|
-
name: state.name,
|
|
756
|
-
steps: state.steps,
|
|
757
|
-
middleware: state.middleware.length > 0 ? state.middleware : void 0,
|
|
758
|
-
argsSchema: state.argsSchema,
|
|
759
|
-
strict: state.strict || void 0
|
|
760
|
-
})
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
function createPipeline(name, schemaOrOptions, options) {
|
|
764
|
-
let argsSchema;
|
|
765
|
-
let strict = false;
|
|
766
|
-
if (schemaOrOptions != null) {
|
|
767
|
-
if ("safeParse" in schemaOrOptions) {
|
|
768
|
-
argsSchema = schemaOrOptions;
|
|
769
|
-
strict = options?.strict ?? false;
|
|
770
|
-
} else {
|
|
771
|
-
strict = schemaOrOptions.strict ?? false;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
return makeBuilder({
|
|
775
|
-
name,
|
|
776
|
-
steps: [],
|
|
777
|
-
middleware: [],
|
|
778
|
-
argsSchema,
|
|
779
|
-
strict
|
|
685
|
+
rollback
|
|
780
686
|
});
|
|
781
687
|
}
|
|
782
688
|
export {
|
|
783
689
|
ArgsValidationError,
|
|
784
|
-
ChoiceNoMatchError,
|
|
785
690
|
PredicateError,
|
|
786
691
|
ProvidesValidationError,
|
|
787
692
|
RequiresValidationError,
|
|
@@ -791,14 +696,11 @@ export {
|
|
|
791
696
|
StrictOverlapError,
|
|
792
697
|
TimeoutError,
|
|
793
698
|
UnknownError,
|
|
794
|
-
buildPipeline,
|
|
795
699
|
choice,
|
|
796
|
-
|
|
797
|
-
defineStep,
|
|
798
|
-
filter,
|
|
799
|
-
flatMap,
|
|
800
|
-
map,
|
|
700
|
+
distribute,
|
|
801
701
|
parallel,
|
|
702
|
+
pipeline,
|
|
703
|
+
step,
|
|
802
704
|
when
|
|
803
705
|
};
|
|
804
706
|
//# sourceMappingURL=index.js.map
|