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