@xyne/workflow-sdk 3.2.36 → 3.2.38
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/dist/agents/agent-step.d.ts +2 -2
- package/dist/agents/host-agent-step.d.ts +1 -1
- package/dist/builder/index.d.ts +2 -2
- package/dist/builder/index.d.ts.map +1 -1
- package/dist/builder/index.js +1 -1
- package/dist/builder/index.js.map +1 -1
- package/dist/client/events-client.d.ts +57 -0
- package/dist/client/events-client.d.ts.map +1 -0
- package/dist/client/events-client.js +107 -0
- package/dist/client/events-client.js.map +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/sse.d.ts +30 -0
- package/dist/client/sse.d.ts.map +1 -0
- package/dist/client/sse.js +76 -0
- package/dist/client/sse.js.map +1 -0
- package/dist/client/types.d.ts +31 -6
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/url.d.ts +11 -0
- package/dist/client/url.d.ts.map +1 -0
- package/dist/client/url.js +28 -0
- package/dist/client/url.js.map +1 -0
- package/dist/client/workflow-client.d.ts +1 -1
- package/dist/client/workflow-client.d.ts.map +1 -1
- package/dist/client/workflow-client.js +32 -20
- package/dist/client/workflow-client.js.map +1 -1
- package/dist/common/citation-ref.d.ts +4 -4
- package/dist/common/credentials.d.ts +107 -0
- package/dist/common/credentials.d.ts.map +1 -1
- package/dist/common/credentials.js +67 -0
- package/dist/common/credentials.js.map +1 -1
- package/dist/common/index.d.ts +2 -2
- package/dist/common/index.d.ts.map +1 -1
- package/dist/common/index.js +2 -2
- package/dist/common/index.js.map +1 -1
- package/dist/connectors/builtin/builtin-connector.d.ts +32 -0
- package/dist/connectors/builtin/builtin-connector.d.ts.map +1 -0
- package/dist/connectors/builtin/builtin-connector.js +62 -0
- package/dist/connectors/builtin/builtin-connector.js.map +1 -0
- package/dist/connectors/builtin/credentials.d.ts +18 -0
- package/dist/connectors/builtin/credentials.d.ts.map +1 -0
- package/dist/connectors/builtin/credentials.js +50 -0
- package/dist/connectors/builtin/credentials.js.map +1 -0
- package/dist/connectors/builtin/steps/code.step.d.ts +99 -0
- package/dist/connectors/builtin/steps/code.step.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/code.step.js +152 -0
- package/dist/connectors/builtin/steps/code.step.js.map +1 -0
- package/dist/connectors/builtin/steps/conditional.step.d.ts +182 -0
- package/dist/connectors/builtin/steps/conditional.step.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/conditional.step.js +82 -0
- package/dist/connectors/builtin/steps/conditional.step.js.map +1 -0
- package/dist/connectors/builtin/steps/dedup.step.d.ts +65 -0
- package/dist/connectors/builtin/steps/dedup.step.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/dedup.step.js +61 -0
- package/dist/connectors/builtin/steps/dedup.step.js.map +1 -0
- package/dist/connectors/builtin/steps/http-request.step.d.ts +1027 -0
- package/dist/connectors/builtin/steps/http-request.step.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/http-request.step.js +497 -0
- package/dist/connectors/builtin/steps/http-request.step.js.map +1 -0
- package/dist/connectors/builtin/steps/loop.step.d.ts +100 -0
- package/dist/connectors/builtin/steps/loop.step.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/loop.step.js +73 -0
- package/dist/connectors/builtin/steps/loop.step.js.map +1 -0
- package/dist/connectors/builtin/steps/map.step.d.ts +148 -0
- package/dist/connectors/builtin/steps/map.step.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/map.step.js +111 -0
- package/dist/connectors/builtin/steps/map.step.js.map +1 -0
- package/dist/connectors/builtin/steps/parallel.step.d.ts +246 -0
- package/dist/connectors/builtin/steps/parallel.step.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/parallel.step.js +175 -0
- package/dist/connectors/builtin/steps/parallel.step.js.map +1 -0
- package/dist/connectors/builtin/steps/ssrf-guard.d.ts +9 -0
- package/dist/connectors/builtin/steps/ssrf-guard.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/ssrf-guard.js +115 -0
- package/dist/connectors/builtin/steps/ssrf-guard.js.map +1 -0
- package/dist/connectors/builtin/steps/switch.step.d.ts +235 -0
- package/dist/connectors/builtin/steps/switch.step.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/switch.step.js +101 -0
- package/dist/connectors/builtin/steps/switch.step.js.map +1 -0
- package/dist/connectors/builtin/steps/wait.step.d.ts +1072 -0
- package/dist/connectors/builtin/steps/wait.step.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/wait.step.js +254 -0
- package/dist/connectors/builtin/steps/wait.step.js.map +1 -0
- package/dist/connectors/builtin/triggers/default-cron-trigger.d.ts +44 -0
- package/dist/connectors/builtin/triggers/default-cron-trigger.d.ts.map +1 -0
- package/dist/connectors/builtin/triggers/default-cron-trigger.js +31 -0
- package/dist/connectors/builtin/triggers/default-cron-trigger.js.map +1 -0
- package/dist/connectors/builtin/triggers/default-event-trigger.d.ts +32 -0
- package/dist/connectors/builtin/triggers/default-event-trigger.d.ts.map +1 -0
- package/dist/connectors/builtin/triggers/default-event-trigger.js +21 -0
- package/dist/connectors/builtin/triggers/default-event-trigger.js.map +1 -0
- package/dist/connectors/builtin/triggers/default-manual-trigger.d.ts +45 -0
- package/dist/connectors/builtin/triggers/default-manual-trigger.d.ts.map +1 -0
- package/dist/connectors/builtin/triggers/default-manual-trigger.js +51 -0
- package/dist/connectors/builtin/triggers/default-manual-trigger.js.map +1 -0
- package/dist/connectors/builtin/triggers/default-webhook-v2-trigger.d.ts +31 -0
- package/dist/connectors/builtin/triggers/default-webhook-v2-trigger.d.ts.map +1 -0
- package/dist/connectors/builtin/triggers/default-webhook-v2-trigger.js +40 -0
- package/dist/connectors/builtin/triggers/default-webhook-v2-trigger.js.map +1 -0
- package/dist/connectors/core/base-connector.d.ts +45 -0
- package/dist/connectors/core/base-connector.d.ts.map +1 -0
- package/dist/connectors/core/base-connector.js +3 -0
- package/dist/connectors/core/base-connector.js.map +1 -0
- package/dist/connectors/core/connector-registry.d.ts +130 -0
- package/dist/connectors/core/connector-registry.d.ts.map +1 -0
- package/dist/connectors/core/connector-registry.js +357 -0
- package/dist/connectors/core/connector-registry.js.map +1 -0
- package/dist/connectors/core/credentials.d.ts +104 -0
- package/dist/connectors/core/credentials.d.ts.map +1 -0
- package/dist/connectors/core/credentials.js +67 -0
- package/dist/connectors/core/credentials.js.map +1 -0
- package/dist/connectors/core/index.d.ts +7 -0
- package/dist/connectors/core/index.d.ts.map +1 -0
- package/dist/connectors/core/index.js +5 -0
- package/dist/connectors/core/index.js.map +1 -0
- package/dist/engine/available-context.d.ts +3 -4
- package/dist/engine/available-context.d.ts.map +1 -1
- package/dist/engine/available-context.js +17 -13
- package/dist/engine/available-context.js.map +1 -1
- package/dist/engine/config-validator.d.ts +13 -5
- package/dist/engine/config-validator.d.ts.map +1 -1
- package/dist/engine/config-validator.js +89 -11
- package/dist/engine/config-validator.js.map +1 -1
- package/dist/engine/credential-resolver.d.ts +42 -0
- package/dist/engine/credential-resolver.d.ts.map +1 -0
- package/dist/engine/credential-resolver.js +63 -0
- package/dist/engine/credential-resolver.js.map +1 -0
- package/dist/engine/workflow-executor.d.ts +15 -9
- package/dist/engine/workflow-executor.d.ts.map +1 -1
- package/dist/engine/workflow-executor.js +31 -40
- package/dist/engine/workflow-executor.js.map +1 -1
- package/dist/events/in-memory-adapter.d.ts +15 -0
- package/dist/events/in-memory-adapter.d.ts.map +1 -0
- package/dist/events/in-memory-adapter.js +50 -0
- package/dist/events/in-memory-adapter.js.map +1 -0
- package/dist/events/types.d.ts +154 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/events/types.js +34 -0
- package/dist/events/types.js.map +1 -0
- package/dist/index.d.ts +50 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -16
- package/dist/index.js.map +1 -1
- package/dist/ingest/body.d.ts +18 -0
- package/dist/ingest/body.d.ts.map +1 -0
- package/dist/ingest/body.js +47 -0
- package/dist/ingest/body.js.map +1 -0
- package/dist/ingest/filter.d.ts +11 -0
- package/dist/ingest/filter.d.ts.map +1 -0
- package/dist/ingest/filter.js +23 -0
- package/dist/ingest/filter.js.map +1 -0
- package/dist/ingest/headers.d.ts +3 -0
- package/dist/ingest/headers.d.ts.map +1 -0
- package/dist/ingest/headers.js +11 -0
- package/dist/ingest/headers.js.map +1 -0
- package/dist/ingest/types.d.ts +50 -0
- package/dist/ingest/types.d.ts.map +1 -0
- package/dist/ingest/types.js +9 -0
- package/dist/ingest/types.js.map +1 -0
- package/dist/persistence/in-memory-adapter.d.ts +4 -8
- package/dist/persistence/in-memory-adapter.d.ts.map +1 -1
- package/dist/persistence/in-memory-adapter.js +8 -25
- package/dist/persistence/in-memory-adapter.js.map +1 -1
- package/dist/persistence/types.d.ts +5 -19
- package/dist/persistence/types.d.ts.map +1 -1
- package/dist/router/types.d.ts +31 -3
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/workflow-router.d.ts +4 -3
- package/dist/router/workflow-router.d.ts.map +1 -1
- package/dist/router/workflow-router.js +258 -63
- package/dist/router/workflow-router.js.map +1 -1
- package/dist/runtime/types.d.ts +36 -56
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/workflow-runtime.d.ts +70 -46
- package/dist/runtime/workflow-runtime.d.ts.map +1 -1
- package/dist/runtime/workflow-runtime.js +217 -141
- package/dist/runtime/workflow-runtime.js.map +1 -1
- package/dist/steps/base-step.d.ts +23 -0
- package/dist/steps/base-step.d.ts.map +1 -1
- package/dist/steps/base-step.js +22 -0
- package/dist/steps/base-step.js.map +1 -1
- package/dist/steps/step-descriptor.d.ts +52 -0
- package/dist/steps/step-descriptor.d.ts.map +1 -0
- package/dist/steps/step-descriptor.js +32 -0
- package/dist/steps/step-descriptor.js.map +1 -0
- package/dist/testing/authz-conformance.d.ts.map +1 -1
- package/dist/testing/authz-conformance.js +6 -7
- package/dist/testing/authz-conformance.js.map +1 -1
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock-step-context.d.ts +1 -0
- package/dist/testing/mock-step-context.d.ts.map +1 -1
- package/dist/testing/mock-step-context.js +1 -0
- package/dist/testing/mock-step-context.js.map +1 -1
- package/dist/testing/test-connector.d.ts +40 -0
- package/dist/testing/test-connector.d.ts.map +1 -0
- package/dist/testing/test-connector.js +39 -0
- package/dist/testing/test-connector.js.map +1 -0
- package/dist/triggers/api-trigger.d.ts +15 -0
- package/dist/triggers/api-trigger.d.ts.map +1 -0
- package/dist/triggers/api-trigger.js +14 -0
- package/dist/triggers/api-trigger.js.map +1 -0
- package/dist/triggers/base-trigger.d.ts +29 -0
- package/dist/triggers/base-trigger.d.ts.map +1 -1
- package/dist/triggers/base-trigger.js +16 -0
- package/dist/triggers/base-trigger.js.map +1 -1
- package/dist/triggers/trigger-descriptor.d.ts +42 -0
- package/dist/triggers/trigger-descriptor.d.ts.map +1 -0
- package/dist/triggers/trigger-descriptor.js +29 -0
- package/dist/triggers/trigger-descriptor.js.map +1 -0
- package/dist/triggers/webhook-trigger.d.ts +31 -38
- package/dist/triggers/webhook-trigger.d.ts.map +1 -1
- package/dist/triggers/webhook-trigger.js +19 -6
- package/dist/triggers/webhook-trigger.js.map +1 -1
- package/dist/types/known-types.d.ts +0 -1
- package/dist/types/known-types.d.ts.map +1 -1
- package/dist/types/known-types.js +0 -1
- package/dist/types/known-types.js.map +1 -1
- package/dist/types/validation.d.ts +3 -1
- package/dist/types/validation.d.ts.map +1 -1
- package/dist/types/validation.js +2 -0
- package/dist/types/validation.js.map +1 -1
- package/dist/types/workflow-config.d.ts +23 -0
- package/dist/types/workflow-config.d.ts.map +1 -1
- package/dist/types/workflow-config.js +8 -0
- package/dist/types/workflow-config.js.map +1 -1
- package/dist/util/credential-slot-schema.d.ts +20 -0
- package/dist/util/credential-slot-schema.d.ts.map +1 -0
- package/dist/util/credential-slot-schema.js +30 -0
- package/dist/util/credential-slot-schema.js.map +1 -0
- package/dist/util/event-channel.d.ts +25 -0
- package/dist/util/event-channel.d.ts.map +1 -0
- package/dist/util/event-channel.js +61 -0
- package/dist/util/event-channel.js.map +1 -0
- package/package.json +1 -1
- package/dist/common/expression-eval.d.ts +0 -35
- package/dist/common/expression-eval.d.ts.map +0 -1
- package/dist/common/expression-eval.js +0 -214
- package/dist/common/expression-eval.js.map +0 -1
- package/dist/common/principal.d.ts +0 -45
- package/dist/common/principal.d.ts.map +0 -1
- package/dist/common/principal.js +0 -9
- package/dist/common/principal.js.map +0 -1
- package/dist/steps/builtin/transform.step.d.ts +0 -247
- package/dist/steps/builtin/transform.step.d.ts.map +0 -1
- package/dist/steps/builtin/transform.step.js +0 -135
- package/dist/steps/builtin/transform.step.js.map +0 -1
- package/dist/types/attachment.d.ts +0 -23
- package/dist/types/attachment.d.ts.map +0 -1
- package/dist/types/attachment.js +0 -2
- package/dist/types/attachment.js.map +0 -1
- package/dist/types/resume-payload.d.ts +0 -34
- package/dist/types/resume-payload.d.ts.map +0 -1
- package/dist/types/resume-payload.js +0 -12
- package/dist/types/resume-payload.js.map +0 -1
- package/dist/util/executable-check.d.ts +0 -42
- package/dist/util/executable-check.d.ts.map +0 -1
- package/dist/util/executable-check.js +0 -115
- package/dist/util/executable-check.js.map +0 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PARALLEL step — fan-out N named branches concurrently and collect results.
|
|
3
|
+
*
|
|
4
|
+
* BaseControlFlowStep with N dynamic branches. Runs all in parallel via
|
|
5
|
+
* Promise.allSettled or Promise.race depending on `merge` mode.
|
|
6
|
+
*
|
|
7
|
+
* Each branch gets an isolated copy of the workflow context so concurrent
|
|
8
|
+
* writes to `context.steps` don't conflict. After completion, the last
|
|
9
|
+
* step's output from each branch is collected into the parallel step's output.
|
|
10
|
+
*
|
|
11
|
+
* ## Pause handling (allSettled)
|
|
12
|
+
*
|
|
13
|
+
* Each branch runs in its own scope and persists its steps under node-path keys
|
|
14
|
+
* (`parallel_x:<branch>#0/<stepId>`). When a branch parks, its `walkBranch`
|
|
15
|
+
* throws `PauseStep`; PARALLEL collects which branches paused and, if any did,
|
|
16
|
+
* throws a single `PauseStep` so the whole step parks. There is no internal
|
|
17
|
+
* saved state — the per-branch step records ARE the durable state. On resume
|
|
18
|
+
* the executor re-enters PARALLEL, re-runs every branch via `walkBranch`:
|
|
19
|
+
* completed branch steps memo-skip from their records, the parked one(s) resume
|
|
20
|
+
* from theirs. Multiple branches can be paused simultaneously; each `resume()`
|
|
21
|
+
* targets one branch's gate by node-path. When the last paused branch
|
|
22
|
+
* completes, PARALLEL merges everything and returns.
|
|
23
|
+
*
|
|
24
|
+
* ## Output
|
|
25
|
+
*
|
|
26
|
+
* Discriminated union on `mode` so consumers (including the UI)
|
|
27
|
+
* can switch on `mode` and get full type safety for each case:
|
|
28
|
+
*
|
|
29
|
+
* allSettled: { mode: 'allSettled', branches: { [name]: { status, value?, reason? } } }
|
|
30
|
+
* race: { mode: 'race', winner: string, result: { output } }
|
|
31
|
+
*/
|
|
32
|
+
import { z } from 'zod';
|
|
33
|
+
import { BaseControlFlowStep, collectBranchSteps, cloneScope } from '../../../steps/base-step.js';
|
|
34
|
+
import { BuiltinStepType } from '../../../types/known-types.js';
|
|
35
|
+
import { PauseStep } from '../../../engine/pause-step.js';
|
|
36
|
+
// ─── Config ───
|
|
37
|
+
export const ParallelStepConfigSchema = z.object({
|
|
38
|
+
branches: z.record(z.string(), z.array(z.object({ id: z.string(), type: z.string() }).passthrough())).describe('Named branches that execute concurrently. Each key is a branch name, value is the steps to run'),
|
|
39
|
+
merge: z.enum(['allSettled', 'race']).default('allSettled').describe('How to combine branch results: allSettled waits for all, race returns first completed'),
|
|
40
|
+
});
|
|
41
|
+
// ─── Output Zod schema (for validation / introspection) ───
|
|
42
|
+
const StepOutputMapSchema = z.record(z.object({ output: z.record(z.unknown()) }));
|
|
43
|
+
const ParallelSettledBranchResultSchema = z.discriminatedUnion('status', [
|
|
44
|
+
z.object({
|
|
45
|
+
status: z.literal('fulfilled'),
|
|
46
|
+
steps: StepOutputMapSchema,
|
|
47
|
+
}),
|
|
48
|
+
z.object({
|
|
49
|
+
status: z.literal('rejected'),
|
|
50
|
+
reason: z.string(),
|
|
51
|
+
}),
|
|
52
|
+
]);
|
|
53
|
+
const ParallelStepOutputSchema = z.discriminatedUnion('mode', [
|
|
54
|
+
z.object({
|
|
55
|
+
mode: z.literal('allSettled'),
|
|
56
|
+
branches: z.record(ParallelSettledBranchResultSchema),
|
|
57
|
+
}),
|
|
58
|
+
z.object({
|
|
59
|
+
mode: z.literal('race'),
|
|
60
|
+
branch: z.string(),
|
|
61
|
+
branches: z.record(ParallelSettledBranchResultSchema),
|
|
62
|
+
}),
|
|
63
|
+
]);
|
|
64
|
+
// ─── Step ───
|
|
65
|
+
export class ParallelStep extends BaseControlFlowStep {
|
|
66
|
+
type = BuiltinStepType.PARALLEL;
|
|
67
|
+
name = 'Parallel';
|
|
68
|
+
description = 'Run multiple branches concurrently and collect results';
|
|
69
|
+
configSchema = ParallelStepConfigSchema;
|
|
70
|
+
outputSchema = ParallelStepOutputSchema;
|
|
71
|
+
category = 'control';
|
|
72
|
+
icon = 'columns';
|
|
73
|
+
/**
|
|
74
|
+
* Declared outputs are dynamic — one per branch key.
|
|
75
|
+
* Since branch keys are user-defined, we return empty here and let
|
|
76
|
+
* `getBranches()` handle discovery for validation.
|
|
77
|
+
*/
|
|
78
|
+
declaredOutputs = [];
|
|
79
|
+
getBranches(config) {
|
|
80
|
+
const branches = (config['branches'] ?? {});
|
|
81
|
+
const result = {};
|
|
82
|
+
for (const [key, steps] of Object.entries(branches)) {
|
|
83
|
+
result[key] = steps;
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Collapse the static `mode` discriminated union to the *configured* mode, so
|
|
89
|
+
* the variable picker sees a concrete object (not an `anyOf`) it can drill.
|
|
90
|
+
* Only the mode-dependent meta is declared here; the harness
|
|
91
|
+
* (`resolveOutputJsonSchemaOf`) splices the typed `branches` from `getBranches`.
|
|
92
|
+
*/
|
|
93
|
+
resolveOutputJsonSchema(config) {
|
|
94
|
+
const mode = config['merge'] === 'race' ? 'race' : 'allSettled';
|
|
95
|
+
const properties = { mode: { type: 'string', const: mode } };
|
|
96
|
+
if (mode === 'race')
|
|
97
|
+
properties['branch'] = { type: 'string' };
|
|
98
|
+
return { type: 'object', properties };
|
|
99
|
+
}
|
|
100
|
+
async execute(config, ctx) {
|
|
101
|
+
const branchEntries = Object.entries(config.branches);
|
|
102
|
+
if (branchEntries.length === 0) {
|
|
103
|
+
return { mode: config.merge, branches: {} };
|
|
104
|
+
}
|
|
105
|
+
if (config.merge === 'allSettled') {
|
|
106
|
+
return this.executeAllSettled(branchEntries, ctx);
|
|
107
|
+
}
|
|
108
|
+
// race
|
|
109
|
+
return this.executeRace(branchEntries, ctx);
|
|
110
|
+
}
|
|
111
|
+
// ─── Merge: allSettled ───
|
|
112
|
+
async executeAllSettled(branchEntries, ctx) {
|
|
113
|
+
// Run every branch concurrently, each in its own scope. Completed branch
|
|
114
|
+
// steps memo-skip on resume; the parked one(s) resume — handled entirely by
|
|
115
|
+
// the executor's node-path records, so no saved state lives here.
|
|
116
|
+
const settled = await Promise.allSettled(branchEntries.map(async ([name, steps]) => {
|
|
117
|
+
const branchCtx = cloneScope(ctx.workflow);
|
|
118
|
+
await ctx.walkBranch(steps, branchCtx, name);
|
|
119
|
+
return {
|
|
120
|
+
steps: collectBranchSteps(steps, branchCtx),
|
|
121
|
+
};
|
|
122
|
+
}));
|
|
123
|
+
const branches = {};
|
|
124
|
+
let pausedCount = 0;
|
|
125
|
+
let firstExternalRef;
|
|
126
|
+
for (let i = 0; i < branchEntries.length; i++) {
|
|
127
|
+
const name = branchEntries[i][0];
|
|
128
|
+
const entry = settled[i];
|
|
129
|
+
if (entry.status === 'fulfilled') {
|
|
130
|
+
branches[name] = { status: 'fulfilled', steps: entry.value.steps };
|
|
131
|
+
}
|
|
132
|
+
else if (PauseStep.is(entry.reason)) {
|
|
133
|
+
pausedCount++;
|
|
134
|
+
const pe = entry.reason;
|
|
135
|
+
if (firstExternalRef === undefined && pe.externalRef !== undefined) {
|
|
136
|
+
firstExternalRef = pe.externalRef;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
branches[name] = {
|
|
141
|
+
status: 'rejected',
|
|
142
|
+
reason: entry.reason instanceof Error
|
|
143
|
+
? entry.reason.message
|
|
144
|
+
: String(entry.reason),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Any paused branch parks the whole step. The executor records PARALLEL as
|
|
149
|
+
// EXTERNAL_WAIT and re-enters it on resume; this throw discards the partial
|
|
150
|
+
// `branches` (the completed branches' outputs are durable in their records).
|
|
151
|
+
if (pausedCount > 0) {
|
|
152
|
+
const reason = pausedCount === 1 ? 'branch awaiting resume' : `${String(pausedCount)} branches paused`;
|
|
153
|
+
throw new PauseStep(reason, firstExternalRef !== undefined ? { externalRef: firstExternalRef } : undefined);
|
|
154
|
+
}
|
|
155
|
+
return { mode: 'allSettled', branches };
|
|
156
|
+
}
|
|
157
|
+
// ─── Merge: race ───
|
|
158
|
+
async executeRace(branchEntries, ctx) {
|
|
159
|
+
const promises = branchEntries.map(async ([name, steps]) => {
|
|
160
|
+
const branchCtx = cloneScope(ctx.workflow);
|
|
161
|
+
await ctx.walkBranch(steps, branchCtx, name);
|
|
162
|
+
return {
|
|
163
|
+
name,
|
|
164
|
+
steps: collectBranchSteps(steps, branchCtx),
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
const winner = await Promise.race(promises);
|
|
168
|
+
return {
|
|
169
|
+
mode: 'race',
|
|
170
|
+
branch: winner.name,
|
|
171
|
+
branches: { [winner.name]: { status: 'fulfilled', steps: winner.steps } },
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=parallel.step.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parallel.step.js","sourceRoot":"","sources":["../../../../src/connectors/builtin/steps/parallel.step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAElG,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE1D,iBAAiB;AAEjB,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,CAChB,CAAC,CAAC,MAAM,EAAE,EACV,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CACtE,CAAC,QAAQ,CAAC,gGAAgG,CAAC;IAC5G,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,uFAAuF,CAAC;CAC9J,CAAC,CAAC;AAgCH,6DAA6D;AAE7D,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AAElF,MAAM,iCAAiC,GAAG,CAAC,CAAC,kBAAkB,CAAC,QAAQ,EAAE;IACvE,CAAC,CAAC,MAAM,CAAC;QACP,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;QAC9B,KAAK,EAAE,mBAAmB;KAC3B,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;QAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;KACnB,CAAC;CACH,CAAC,CAAC;AAEH,MAAM,wBAAwB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAC5D,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;QAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,iCAAiC,CAAC;KACtD,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACvB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,iCAAiC,CAAC;KACtD,CAAC;CACH,CAAC,CAAC;AAEH,eAAe;AAEf,MAAM,OAAO,YAAa,SAAQ,mBAGjC;IACmB,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC;IAChC,IAAI,GAAG,UAAU,CAAC;IAClB,WAAW,GAC3B,wDAAwD,CAAC;IACzC,YAAY,GAAG,wBAAwB,CAAC;IACxC,YAAY,GAAG,wBAAwB,CAAC;IACxC,QAAQ,GAAG,SAAkB,CAAC;IAC9B,IAAI,GAAG,SAAS,CAAC;IAEnC;;;;OAIG;IACe,eAAe,GAAG,EAAW,CAAC;IAEvC,WAAW,CAClB,MAA+B;QAE/B,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAGzC,CAAC;QACF,MAAM,MAAM,GAAyC,EAAE,CAAC;QACxD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAwC,CAAC;QACzD,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACM,uBAAuB,CAC9B,MAA+B;QAE/B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC;QAChE,MAAM,UAAU,GAA4B,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QACtF,IAAI,IAAI,KAAK,MAAM;YAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC/D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACxC,CAAC;IAEQ,KAAK,CAAC,OAAO,CACpB,MAA0B,EAC1B,GAAgC;QAEhC,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAEtD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAwB,CAAC;QACpE,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,iBAAiB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QACpD,CAAC;QACD,OAAO;QACP,OAAO,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED,4BAA4B;IAEpB,KAAK,CAAC,iBAAiB,CAC7B,aAAoD,EACpD,GAAgC;QAEhC,yEAAyE;QACzE,4EAA4E;QAC5E,kEAAkE;QAClE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE;YACxC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,GAAG,CAAC,UAAU,CAClB,KAAwC,EACxC,SAAS,EACT,IAAI,CACL,CAAC;YACF,OAAO;gBACL,KAAK,EAAE,kBAAkB,CAAC,KAAwC,EAAE,SAAS,CAAC;aAC/E,CAAC;QACJ,CAAC,CAAC,CACH,CAAC;QAEF,MAAM,QAAQ,GAAgD,EAAE,CAAC;QACjE,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,gBAAoC,CAAC;QAEzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC;YAClC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBACjC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACrE,CAAC;iBAAM,IAAI,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtC,WAAW,EAAE,CAAC;gBACd,MAAM,EAAE,GAAG,KAAK,CAAC,MAAmB,CAAC;gBACrC,IAAI,gBAAgB,KAAK,SAAS,IAAI,EAAE,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;oBACnE,gBAAgB,GAAG,EAAE,CAAC,WAAW,CAAC;gBACpC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,IAAI,CAAC,GAAG;oBACf,MAAM,EAAE,UAAU;oBAClB,MAAM,EACJ,KAAK,CAAC,MAAM,YAAY,KAAK;wBAC3B,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO;wBACtB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;iBAC3B,CAAC;YACJ,CAAC;QACH,CAAC;QAED,2EAA2E;QAC3E,4EAA4E;QAC5E,6EAA6E;QAC7E,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,MAAM,GACV,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC;YAC1F,MAAM,IAAI,SAAS,CACjB,MAAM,EACN,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,SAAS,CAC/E,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;IAC1C,CAAC;IAED,sBAAsB;IAEd,KAAK,CAAC,WAAW,CACvB,aAAoD,EACpD,GAAgC;QAEhC,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE;YACzD,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,GAAG,CAAC,UAAU,CAClB,KAAwC,EACxC,SAAS,EACT,IAAI,CACL,CAAC;YACF,OAAO;gBACL,IAAI;gBACJ,KAAK,EAAE,kBAAkB,CAAC,KAAwC,EAAE,SAAS,CAAC;aAC/E,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE5C,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,MAAM,CAAC,IAAI;YACnB,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE;SAC1E,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** True if `ip` (a literal IPv4/IPv6) is in a blocked range. Non-IPs are blocked. */
|
|
2
|
+
export declare function isBlockedIp(ip: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Throw if `rawUrl` is not a safe public http(s) destination. Resolves DNS and
|
|
5
|
+
* checks every returned address so a hostname pointing at a private/metadata IP
|
|
6
|
+
* is rejected too. Call this for the initial URL AND every redirect target.
|
|
7
|
+
*/
|
|
8
|
+
export declare function assertPublicHttpUrl(rawUrl: string): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=ssrf-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssrf-guard.d.ts","sourceRoot":"","sources":["../../../../src/connectors/builtin/steps/ssrf-guard.ts"],"names":[],"mappings":"AAkEA,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAK/C;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BvE"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF guard for outbound HTTP (the HTTP_REQUEST step).
|
|
3
|
+
*
|
|
4
|
+
* A workflow author controls the request URL, so without validation the server
|
|
5
|
+
* can be turned into a proxy for internal services or — on a cloud VM — the
|
|
6
|
+
* instance metadata endpoint (169.254.169.254 / metadata.google.internal),
|
|
7
|
+
* which hands out service-account credentials. This module resolves the target
|
|
8
|
+
* host and refuses loopback / private / link-local / cloud-metadata addresses.
|
|
9
|
+
*
|
|
10
|
+
* Only http/https are allowed. Literal-IP and DNS-resolved addresses are both
|
|
11
|
+
* checked (basic resistance to a hostname that points at a private IP), and the
|
|
12
|
+
* step re-validates every redirect hop.
|
|
13
|
+
*/
|
|
14
|
+
import { lookup } from 'node:dns/promises';
|
|
15
|
+
import { isIP } from 'node:net';
|
|
16
|
+
const BLOCKED_HOSTNAMES = new Set(['metadata.google.internal', 'metadata', 'localhost']);
|
|
17
|
+
function ipv4ToInt(ip) {
|
|
18
|
+
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(ip);
|
|
19
|
+
if (!m)
|
|
20
|
+
return null;
|
|
21
|
+
let n = 0;
|
|
22
|
+
for (let i = 1; i <= 4; i += 1) {
|
|
23
|
+
const octet = Number(m[i]);
|
|
24
|
+
if (!Number.isInteger(octet) || octet < 0 || octet > 255)
|
|
25
|
+
return null;
|
|
26
|
+
n = n * 256 + octet;
|
|
27
|
+
}
|
|
28
|
+
return n >>> 0;
|
|
29
|
+
}
|
|
30
|
+
function inCidr(n, base, bits) {
|
|
31
|
+
const b = ipv4ToInt(base);
|
|
32
|
+
if (b === null)
|
|
33
|
+
return false;
|
|
34
|
+
const mask = bits === 0 ? 0 : (~0 << (32 - bits)) >>> 0;
|
|
35
|
+
return (n & mask) === (b & mask);
|
|
36
|
+
}
|
|
37
|
+
function isBlockedIpv4(ip) {
|
|
38
|
+
const n = ipv4ToInt(ip);
|
|
39
|
+
if (n === null)
|
|
40
|
+
return true; // unparseable → block
|
|
41
|
+
return (inCidr(n, '0.0.0.0', 8) || // "this" network
|
|
42
|
+
inCidr(n, '10.0.0.0', 8) || // private
|
|
43
|
+
inCidr(n, '100.64.0.0', 10) || // CGNAT
|
|
44
|
+
inCidr(n, '127.0.0.0', 8) || // loopback
|
|
45
|
+
inCidr(n, '169.254.0.0', 16) || // link-local incl. 169.254.169.254 metadata
|
|
46
|
+
inCidr(n, '172.16.0.0', 12) || // private
|
|
47
|
+
inCidr(n, '192.0.0.0', 24) || // IETF protocol assignments
|
|
48
|
+
inCidr(n, '192.168.0.0', 16) || // private
|
|
49
|
+
inCidr(n, '198.18.0.0', 15) || // benchmarking
|
|
50
|
+
inCidr(n, '224.0.0.0', 4) || // multicast
|
|
51
|
+
inCidr(n, '240.0.0.0', 4) // reserved
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
function isBlockedIpv6(ip) {
|
|
55
|
+
const addr = (ip.split('%')[0] ?? '').toLowerCase(); // strip zone id
|
|
56
|
+
if (addr === '::1' || addr === '::')
|
|
57
|
+
return true; // loopback / unspecified
|
|
58
|
+
if (addr.startsWith('fc') || addr.startsWith('fd'))
|
|
59
|
+
return true; // unique-local fc00::/7
|
|
60
|
+
// link-local fe80::/10 → first hextet fe80..febf
|
|
61
|
+
if (/^fe[89ab]/.test(addr))
|
|
62
|
+
return true;
|
|
63
|
+
const mapped = addr.match(/(?:^|:)(?:ffff:)?(\d+\.\d+\.\d+\.\d+)$/); // ::ffff:a.b.c.d
|
|
64
|
+
if (mapped && mapped[1])
|
|
65
|
+
return isBlockedIpv4(mapped[1]);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
/** True if `ip` (a literal IPv4/IPv6) is in a blocked range. Non-IPs are blocked. */
|
|
69
|
+
export function isBlockedIp(ip) {
|
|
70
|
+
const v = isIP(ip);
|
|
71
|
+
if (v === 4)
|
|
72
|
+
return isBlockedIpv4(ip);
|
|
73
|
+
if (v === 6)
|
|
74
|
+
return isBlockedIpv6(ip);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Throw if `rawUrl` is not a safe public http(s) destination. Resolves DNS and
|
|
79
|
+
* checks every returned address so a hostname pointing at a private/metadata IP
|
|
80
|
+
* is rejected too. Call this for the initial URL AND every redirect target.
|
|
81
|
+
*/
|
|
82
|
+
export async function assertPublicHttpUrl(rawUrl) {
|
|
83
|
+
let url;
|
|
84
|
+
try {
|
|
85
|
+
url = new URL(rawUrl);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
throw new Error(`SSRF guard: invalid URL: ${rawUrl}`);
|
|
89
|
+
}
|
|
90
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
91
|
+
throw new Error(`SSRF guard: blocked scheme "${url.protocol}" (only http/https allowed)`);
|
|
92
|
+
}
|
|
93
|
+
const host = url.hostname.replace(/^\[|\]$/g, ''); // strip IPv6 brackets
|
|
94
|
+
if (BLOCKED_HOSTNAMES.has(host.toLowerCase())) {
|
|
95
|
+
throw new Error(`SSRF guard: blocked host "${host}"`);
|
|
96
|
+
}
|
|
97
|
+
if (isIP(host)) {
|
|
98
|
+
if (isBlockedIp(host))
|
|
99
|
+
throw new Error(`SSRF guard: blocked address ${host}`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
let addrs;
|
|
103
|
+
try {
|
|
104
|
+
addrs = await lookup(host, { all: true });
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
throw new Error(`SSRF guard: could not resolve host "${host}"`);
|
|
108
|
+
}
|
|
109
|
+
for (const a of addrs) {
|
|
110
|
+
if (isBlockedIp(a.address)) {
|
|
111
|
+
throw new Error(`SSRF guard: host "${host}" resolves to blocked address ${a.address}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=ssrf-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssrf-guard.js","sourceRoot":"","sources":["../../../../src/connectors/builtin/steps/ssrf-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SWITCH step — first-match-wins over an ordered list of conditional cases,
|
|
3
|
+
* with a guaranteed `default` fallback.
|
|
4
|
+
*
|
|
5
|
+
* Semantics (no fall-through):
|
|
6
|
+
*
|
|
7
|
+
* for (const [i, c] of cases.entries())
|
|
8
|
+
* if (evaluator.evaluate(c.condition, ctx.workflow)) {
|
|
9
|
+
* walkBranch(c.steps, `case_${i}`)
|
|
10
|
+
* return { branch: `case_${i}`, caseIndex: i, label: c.label ?? null }
|
|
11
|
+
* }
|
|
12
|
+
* walkBranch(default, 'default')
|
|
13
|
+
* return { branch: 'default', caseIndex: null, label: null }
|
|
14
|
+
*
|
|
15
|
+
* Branch keys are positional (`case_0`, `case_1`, ..., `default`) so a
|
|
16
|
+
* paused execution's PausePath survives label edits.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { BaseControlFlowStep } from '../../../steps/base-step.js';
|
|
20
|
+
import type { ControlFlowExecutionContext, BranchStepOutputs } from '../../../steps/base-step.js';
|
|
21
|
+
import { BuiltinStepType } from '../../../types/known-types.js';
|
|
22
|
+
import type { WorkflowStepConfig } from '../../../types/workflow-config.js';
|
|
23
|
+
export declare const SwitchStepConfigSchema: z.ZodObject<{
|
|
24
|
+
cases: z.ZodArray<z.ZodObject<{
|
|
25
|
+
condition: z.ZodType<import("../../../types/workflow-config.js").Condition, z.ZodTypeDef, import("../../../types/workflow-config.js").Condition>;
|
|
26
|
+
label: z.ZodOptional<z.ZodString>;
|
|
27
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
28
|
+
id: z.ZodString;
|
|
29
|
+
type: z.ZodString;
|
|
30
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
31
|
+
id: z.ZodString;
|
|
32
|
+
type: z.ZodString;
|
|
33
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
34
|
+
id: z.ZodString;
|
|
35
|
+
type: z.ZodString;
|
|
36
|
+
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
37
|
+
}, "strip", z.ZodTypeAny, {
|
|
38
|
+
steps: z.objectOutputType<{
|
|
39
|
+
id: z.ZodString;
|
|
40
|
+
type: z.ZodString;
|
|
41
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
42
|
+
condition: import("../../../types/workflow-config.js").Condition;
|
|
43
|
+
label?: string | undefined;
|
|
44
|
+
}, {
|
|
45
|
+
steps: z.objectInputType<{
|
|
46
|
+
id: z.ZodString;
|
|
47
|
+
type: z.ZodString;
|
|
48
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
49
|
+
condition: import("../../../types/workflow-config.js").Condition;
|
|
50
|
+
label?: string | undefined;
|
|
51
|
+
}>, "many">;
|
|
52
|
+
default: z.ZodArray<z.ZodObject<{
|
|
53
|
+
id: z.ZodString;
|
|
54
|
+
type: z.ZodString;
|
|
55
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
56
|
+
id: z.ZodString;
|
|
57
|
+
type: z.ZodString;
|
|
58
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
59
|
+
id: z.ZodString;
|
|
60
|
+
type: z.ZodString;
|
|
61
|
+
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
62
|
+
}, "strip", z.ZodTypeAny, {
|
|
63
|
+
cases: {
|
|
64
|
+
steps: z.objectOutputType<{
|
|
65
|
+
id: z.ZodString;
|
|
66
|
+
type: z.ZodString;
|
|
67
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
68
|
+
condition: import("../../../types/workflow-config.js").Condition;
|
|
69
|
+
label?: string | undefined;
|
|
70
|
+
}[];
|
|
71
|
+
default: z.objectOutputType<{
|
|
72
|
+
id: z.ZodString;
|
|
73
|
+
type: z.ZodString;
|
|
74
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
75
|
+
}, {
|
|
76
|
+
cases: {
|
|
77
|
+
steps: z.objectInputType<{
|
|
78
|
+
id: z.ZodString;
|
|
79
|
+
type: z.ZodString;
|
|
80
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
81
|
+
condition: import("../../../types/workflow-config.js").Condition;
|
|
82
|
+
label?: string | undefined;
|
|
83
|
+
}[];
|
|
84
|
+
default: z.objectInputType<{
|
|
85
|
+
id: z.ZodString;
|
|
86
|
+
type: z.ZodString;
|
|
87
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
88
|
+
}>;
|
|
89
|
+
export type SwitchStepConfig = z.infer<typeof SwitchStepConfigSchema>;
|
|
90
|
+
export interface SwitchStepOutput extends Record<string, unknown> {
|
|
91
|
+
/** Which branch executed. `'default'` when no case matched. `case_<i>` (i = caseIndex) otherwise. */
|
|
92
|
+
branch: string;
|
|
93
|
+
/** Index of the matched case, or `null` when `default` ran. */
|
|
94
|
+
caseIndex: number | null;
|
|
95
|
+
/** Label of the matched case, or `null` when `default` ran (or case had no label). */
|
|
96
|
+
label: string | null;
|
|
97
|
+
/**
|
|
98
|
+
* The taken branch's top-level steps, keyed by branch key (`case_<i>`/`default`)
|
|
99
|
+
* then step id — referenced as `…output.branches.<key>.steps.<id>.output.<field>`.
|
|
100
|
+
* Only the taken branch is populated. The harness types all branches from `getBranches`.
|
|
101
|
+
*/
|
|
102
|
+
branches: Record<string, {
|
|
103
|
+
steps: BranchStepOutputs;
|
|
104
|
+
}>;
|
|
105
|
+
}
|
|
106
|
+
export declare class SwitchStep extends BaseControlFlowStep<typeof SwitchStepConfigSchema, SwitchStepOutput> {
|
|
107
|
+
readonly type = BuiltinStepType.SWITCH;
|
|
108
|
+
readonly name = "Switch";
|
|
109
|
+
readonly description = "Route to one of N branches; first matching case wins, otherwise default";
|
|
110
|
+
readonly configSchema: z.ZodObject<{
|
|
111
|
+
cases: z.ZodArray<z.ZodObject<{
|
|
112
|
+
condition: z.ZodType<import("../../../types/workflow-config.js").Condition, z.ZodTypeDef, import("../../../types/workflow-config.js").Condition>;
|
|
113
|
+
label: z.ZodOptional<z.ZodString>;
|
|
114
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
115
|
+
id: z.ZodString;
|
|
116
|
+
type: z.ZodString;
|
|
117
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
118
|
+
id: z.ZodString;
|
|
119
|
+
type: z.ZodString;
|
|
120
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
121
|
+
id: z.ZodString;
|
|
122
|
+
type: z.ZodString;
|
|
123
|
+
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
124
|
+
}, "strip", z.ZodTypeAny, {
|
|
125
|
+
steps: z.objectOutputType<{
|
|
126
|
+
id: z.ZodString;
|
|
127
|
+
type: z.ZodString;
|
|
128
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
129
|
+
condition: import("../../../types/workflow-config.js").Condition;
|
|
130
|
+
label?: string | undefined;
|
|
131
|
+
}, {
|
|
132
|
+
steps: z.objectInputType<{
|
|
133
|
+
id: z.ZodString;
|
|
134
|
+
type: z.ZodString;
|
|
135
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
136
|
+
condition: import("../../../types/workflow-config.js").Condition;
|
|
137
|
+
label?: string | undefined;
|
|
138
|
+
}>, "many">;
|
|
139
|
+
default: z.ZodArray<z.ZodObject<{
|
|
140
|
+
id: z.ZodString;
|
|
141
|
+
type: z.ZodString;
|
|
142
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
143
|
+
id: z.ZodString;
|
|
144
|
+
type: z.ZodString;
|
|
145
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
146
|
+
id: z.ZodString;
|
|
147
|
+
type: z.ZodString;
|
|
148
|
+
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
149
|
+
}, "strip", z.ZodTypeAny, {
|
|
150
|
+
cases: {
|
|
151
|
+
steps: z.objectOutputType<{
|
|
152
|
+
id: z.ZodString;
|
|
153
|
+
type: z.ZodString;
|
|
154
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
155
|
+
condition: import("../../../types/workflow-config.js").Condition;
|
|
156
|
+
label?: string | undefined;
|
|
157
|
+
}[];
|
|
158
|
+
default: z.objectOutputType<{
|
|
159
|
+
id: z.ZodString;
|
|
160
|
+
type: z.ZodString;
|
|
161
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
162
|
+
}, {
|
|
163
|
+
cases: {
|
|
164
|
+
steps: z.objectInputType<{
|
|
165
|
+
id: z.ZodString;
|
|
166
|
+
type: z.ZodString;
|
|
167
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
168
|
+
condition: import("../../../types/workflow-config.js").Condition;
|
|
169
|
+
label?: string | undefined;
|
|
170
|
+
}[];
|
|
171
|
+
default: z.objectInputType<{
|
|
172
|
+
id: z.ZodString;
|
|
173
|
+
type: z.ZodString;
|
|
174
|
+
}, z.ZodTypeAny, "passthrough">[];
|
|
175
|
+
}>;
|
|
176
|
+
readonly outputSchema: z.ZodObject<{
|
|
177
|
+
branch: z.ZodString;
|
|
178
|
+
caseIndex: z.ZodNullable<z.ZodNumber>;
|
|
179
|
+
label: z.ZodNullable<z.ZodString>;
|
|
180
|
+
branches: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
181
|
+
steps: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
182
|
+
output: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
183
|
+
}, "strip", z.ZodTypeAny, {
|
|
184
|
+
output: Record<string, unknown>;
|
|
185
|
+
}, {
|
|
186
|
+
output: Record<string, unknown>;
|
|
187
|
+
}>>;
|
|
188
|
+
}, "strip", z.ZodTypeAny, {
|
|
189
|
+
steps: Record<string, {
|
|
190
|
+
output: Record<string, unknown>;
|
|
191
|
+
}>;
|
|
192
|
+
}, {
|
|
193
|
+
steps: Record<string, {
|
|
194
|
+
output: Record<string, unknown>;
|
|
195
|
+
}>;
|
|
196
|
+
}>>;
|
|
197
|
+
}, "strip", z.ZodTypeAny, {
|
|
198
|
+
label: string | null;
|
|
199
|
+
branch: string;
|
|
200
|
+
branches: Record<string, {
|
|
201
|
+
steps: Record<string, {
|
|
202
|
+
output: Record<string, unknown>;
|
|
203
|
+
}>;
|
|
204
|
+
}>;
|
|
205
|
+
caseIndex: number | null;
|
|
206
|
+
}, {
|
|
207
|
+
label: string | null;
|
|
208
|
+
branch: string;
|
|
209
|
+
branches: Record<string, {
|
|
210
|
+
steps: Record<string, {
|
|
211
|
+
output: Record<string, unknown>;
|
|
212
|
+
}>;
|
|
213
|
+
}>;
|
|
214
|
+
caseIndex: number | null;
|
|
215
|
+
}>;
|
|
216
|
+
readonly category: "control";
|
|
217
|
+
readonly icon = "switch";
|
|
218
|
+
readonly declaredOutputs: readonly [{
|
|
219
|
+
readonly key: "branch";
|
|
220
|
+
readonly label: "Matched branch key";
|
|
221
|
+
}, {
|
|
222
|
+
readonly key: "caseIndex";
|
|
223
|
+
readonly label: "Matched case index";
|
|
224
|
+
}, {
|
|
225
|
+
readonly key: "label";
|
|
226
|
+
readonly label: "Matched case label";
|
|
227
|
+
}];
|
|
228
|
+
/**
|
|
229
|
+
* Expose every branch (each case + default) so the config validator can
|
|
230
|
+
* walk forward references inside each one independently.
|
|
231
|
+
*/
|
|
232
|
+
getBranches(config: Record<string, unknown>): Record<string, WorkflowStepConfig[]>;
|
|
233
|
+
execute(config: SwitchStepConfig, ctx: ControlFlowExecutionContext): Promise<SwitchStepOutput>;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=switch.step.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"switch.step.d.ts","sourceRoot":"","sources":["../../../../src/connectors/builtin/steps/switch.step.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAsB,MAAM,6BAA6B,CAAC;AACtF,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAClG,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAgB5E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE,MAAM,WAAW,gBAAiB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/D,qGAAqG;IACrG,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,sFAAsF;IACtF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,iBAAiB,CAAA;KAAE,CAAC,CAAC;CACxD;AAcD,qBAAa,UAAW,SAAQ,mBAAmB,CACjD,OAAO,sBAAsB,EAC7B,gBAAgB,CACjB;IACC,SAAkB,IAAI,0BAA0B;IAChD,SAAkB,IAAI,YAAY;IAClC,SAAkB,WAAW,6EAA6E;IAC1G,SAAkB,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAA0B;IACxD,SAAkB,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAA0B;IACxD,SAAkB,QAAQ,EAAG,SAAS,CAAU;IAChD,SAAkB,IAAI,YAAY;IAClC,SAAkB,eAAe;;;;;;;;;OAItB;IAEX;;;OAGG;IACM,WAAW,CAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAUxB,OAAO,CACpB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,2BAA2B,GAC/B,OAAO,CAAC,gBAAgB,CAAC;CA+B7B"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SWITCH step — first-match-wins over an ordered list of conditional cases,
|
|
3
|
+
* with a guaranteed `default` fallback.
|
|
4
|
+
*
|
|
5
|
+
* Semantics (no fall-through):
|
|
6
|
+
*
|
|
7
|
+
* for (const [i, c] of cases.entries())
|
|
8
|
+
* if (evaluator.evaluate(c.condition, ctx.workflow)) {
|
|
9
|
+
* walkBranch(c.steps, `case_${i}`)
|
|
10
|
+
* return { branch: `case_${i}`, caseIndex: i, label: c.label ?? null }
|
|
11
|
+
* }
|
|
12
|
+
* walkBranch(default, 'default')
|
|
13
|
+
* return { branch: 'default', caseIndex: null, label: null }
|
|
14
|
+
*
|
|
15
|
+
* Branch keys are positional (`case_0`, `case_1`, ..., `default`) so a
|
|
16
|
+
* paused execution's PausePath survives label edits.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { BaseControlFlowStep, collectBranchSteps } from '../../../steps/base-step.js';
|
|
20
|
+
import { BuiltinStepType } from '../../../types/known-types.js';
|
|
21
|
+
import { ConditionSchema } from '../../../types/workflow-config.js';
|
|
22
|
+
import { ConditionEvaluator } from '../../../engine/condition-evaluator.js';
|
|
23
|
+
// ─── Config ───
|
|
24
|
+
const SwitchCaseSchema = z.object({
|
|
25
|
+
condition: ConditionSchema
|
|
26
|
+
.describe('Recursive condition tree (leaf or all/any composite). First case whose condition is true wins.'),
|
|
27
|
+
label: z.string().optional()
|
|
28
|
+
.describe('Optional UI label for this case. Not used for routing — branch keys are positional.'),
|
|
29
|
+
steps: z.array(z.object({ id: z.string(), type: z.string() }).passthrough()).describe('Steps to execute when this case wins.'),
|
|
30
|
+
});
|
|
31
|
+
export const SwitchStepConfigSchema = z.object({
|
|
32
|
+
cases: z.array(SwitchCaseSchema)
|
|
33
|
+
.describe('Ordered cases. Evaluated top-to-bottom; first match wins, no fall-through.'),
|
|
34
|
+
default: z.array(z.object({ id: z.string(), type: z.string() }).passthrough()).describe('Fallback branch. Runs when no case matches. Use `[]` to no-op the fallback.'),
|
|
35
|
+
});
|
|
36
|
+
const SwitchStepOutputSchema = z.object({
|
|
37
|
+
branch: z.string(),
|
|
38
|
+
caseIndex: z.number().int().nullable(),
|
|
39
|
+
label: z.string().nullable(),
|
|
40
|
+
branches: z.record(z.object({ steps: z.record(z.object({ output: z.record(z.unknown()) })) })),
|
|
41
|
+
});
|
|
42
|
+
// ─── Step ───
|
|
43
|
+
const DEFAULT_BRANCH_KEY = 'default';
|
|
44
|
+
const caseBranchKey = (i) => `case_${i}`;
|
|
45
|
+
export class SwitchStep extends BaseControlFlowStep {
|
|
46
|
+
type = BuiltinStepType.SWITCH;
|
|
47
|
+
name = 'Switch';
|
|
48
|
+
description = 'Route to one of N branches; first matching case wins, otherwise default';
|
|
49
|
+
configSchema = SwitchStepConfigSchema;
|
|
50
|
+
outputSchema = SwitchStepOutputSchema;
|
|
51
|
+
category = 'control';
|
|
52
|
+
icon = 'switch';
|
|
53
|
+
declaredOutputs = [
|
|
54
|
+
{ key: 'branch', label: 'Matched branch key' },
|
|
55
|
+
{ key: 'caseIndex', label: 'Matched case index' },
|
|
56
|
+
{ key: 'label', label: 'Matched case label' },
|
|
57
|
+
];
|
|
58
|
+
/**
|
|
59
|
+
* Expose every branch (each case + default) so the config validator can
|
|
60
|
+
* walk forward references inside each one independently.
|
|
61
|
+
*/
|
|
62
|
+
getBranches(config) {
|
|
63
|
+
const branches = {};
|
|
64
|
+
const cases = (config['cases'] ?? []);
|
|
65
|
+
for (let i = 0; i < cases.length; i++) {
|
|
66
|
+
branches[caseBranchKey(i)] = (cases[i]?.steps ?? []);
|
|
67
|
+
}
|
|
68
|
+
branches[DEFAULT_BRANCH_KEY] = (config['default'] ?? []);
|
|
69
|
+
return branches;
|
|
70
|
+
}
|
|
71
|
+
async execute(config, ctx) {
|
|
72
|
+
const evaluator = new ConditionEvaluator();
|
|
73
|
+
for (let i = 0; i < config.cases.length; i++) {
|
|
74
|
+
const entry = config.cases[i];
|
|
75
|
+
if (evaluator.evaluate(entry.condition, ctx.workflow)) {
|
|
76
|
+
const branchKey = caseBranchKey(i);
|
|
77
|
+
const branchSteps = entry.steps;
|
|
78
|
+
if (branchSteps.length > 0) {
|
|
79
|
+
await ctx.walkBranch(branchSteps, ctx.workflow, branchKey);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
branch: branchKey,
|
|
83
|
+
caseIndex: i,
|
|
84
|
+
label: entry.label ?? null,
|
|
85
|
+
branches: { [branchKey]: { steps: collectBranchSteps(branchSteps, ctx.workflow) } },
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const defaultSteps = config.default;
|
|
90
|
+
if (defaultSteps.length > 0) {
|
|
91
|
+
await ctx.walkBranch(defaultSteps, ctx.workflow, DEFAULT_BRANCH_KEY);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
branch: DEFAULT_BRANCH_KEY,
|
|
95
|
+
caseIndex: null,
|
|
96
|
+
label: null,
|
|
97
|
+
branches: { [DEFAULT_BRANCH_KEY]: { steps: collectBranchSteps(defaultSteps, ctx.workflow) } },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=switch.step.js.map
|