@xyne/workflow-sdk 3.2.37 → 3.2.39
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/types.d.ts +7 -5
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/workflow-client.d.ts.map +1 -1
- package/dist/client/workflow-client.js +7 -0
- 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 +33 -0
- package/dist/connectors/builtin/builtin-connector.d.ts.map +1 -0
- package/dist/connectors/builtin/builtin-connector.js +63 -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/{steps/builtin → connectors/builtin/steps}/code.step.d.ts +4 -4
- package/dist/connectors/builtin/steps/code.step.d.ts.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/code.step.js +3 -3
- package/dist/connectors/builtin/steps/code.step.js.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/conditional.step.d.ts +10 -10
- package/dist/connectors/builtin/steps/conditional.step.d.ts.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/conditional.step.js +4 -4
- package/dist/connectors/builtin/steps/conditional.step.js.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/dedup.step.d.ts +4 -4
- package/dist/connectors/builtin/steps/dedup.step.d.ts.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/dedup.step.js +3 -3
- package/dist/connectors/builtin/steps/dedup.step.js.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/http-request.step.d.ts +29 -22
- package/dist/connectors/builtin/steps/http-request.step.d.ts.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/http-request.step.js +15 -9
- package/dist/connectors/builtin/steps/http-request.step.js.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/loop.step.d.ts +4 -4
- package/dist/connectors/builtin/steps/loop.step.d.ts.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/loop.step.js +3 -3
- package/dist/connectors/builtin/steps/loop.step.js.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/map.step.d.ts +4 -4
- package/dist/connectors/builtin/steps/map.step.d.ts.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/map.step.js +4 -4
- package/dist/connectors/builtin/steps/map.step.js.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/parallel.step.d.ts +4 -4
- package/dist/connectors/builtin/steps/parallel.step.d.ts.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/parallel.step.js +3 -3
- package/dist/connectors/builtin/steps/parallel.step.js.map +1 -0
- package/dist/connectors/builtin/steps/ssrf-guard.d.ts.map +1 -0
- package/dist/connectors/builtin/steps/ssrf-guard.js.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/switch.step.d.ts +14 -14
- package/dist/connectors/builtin/steps/switch.step.d.ts.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/switch.step.js +4 -4
- package/dist/connectors/builtin/steps/switch.step.js.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/wait.step.d.ts +4 -4
- package/dist/connectors/builtin/steps/wait.step.d.ts.map +1 -0
- package/dist/{steps/builtin → connectors/builtin/steps}/wait.step.js +4 -4
- package/dist/connectors/builtin/steps/wait.step.js.map +1 -0
- package/dist/{triggers/builtin → connectors/builtin/triggers}/default-cron-trigger.d.ts +4 -4
- package/dist/connectors/builtin/triggers/default-cron-trigger.d.ts.map +1 -0
- package/dist/{triggers/builtin → connectors/builtin/triggers}/default-cron-trigger.js +3 -3
- package/dist/connectors/builtin/triggers/default-cron-trigger.js.map +1 -0
- package/dist/{triggers/builtin → connectors/builtin/triggers}/default-event-trigger.d.ts +3 -3
- package/dist/connectors/builtin/triggers/default-event-trigger.d.ts.map +1 -0
- package/dist/{triggers/builtin → connectors/builtin/triggers}/default-event-trigger.js +3 -3
- package/dist/connectors/builtin/triggers/default-event-trigger.js.map +1 -0
- package/dist/{triggers/builtin → connectors/builtin/triggers}/default-manual-trigger.d.ts +3 -3
- package/dist/connectors/builtin/triggers/default-manual-trigger.d.ts.map +1 -0
- package/dist/{triggers/builtin → connectors/builtin/triggers}/default-manual-trigger.js +3 -3
- package/dist/connectors/builtin/triggers/default-manual-trigger.js.map +1 -0
- package/dist/{triggers/builtin → connectors/builtin/triggers}/default-webhook-v2-trigger.d.ts +10 -8
- package/dist/connectors/builtin/triggers/default-webhook-v2-trigger.d.ts.map +1 -0
- package/dist/{triggers/builtin → connectors/builtin/triggers}/default-webhook-v2-trigger.js +10 -8
- package/dist/connectors/builtin/triggers/default-webhook-v2-trigger.js.map +1 -0
- package/dist/connectors/core/base-connector.d.ts +50 -0
- package/dist/connectors/core/base-connector.d.ts.map +1 -0
- package/dist/connectors/core/base-connector.js +8 -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 +353 -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 +4 -5
- package/dist/engine/workflow-executor.d.ts.map +1 -1
- package/dist/engine/workflow-executor.js +16 -38
- package/dist/engine/workflow-executor.js.map +1 -1
- package/dist/index.d.ts +43 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -15
- 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 +84 -41
- package/dist/router/workflow-router.js.map +1 -1
- package/dist/runtime/types.d.ts +6 -4
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/workflow-runtime.d.ts +46 -37
- package/dist/runtime/workflow-runtime.d.ts.map +1 -1
- package/dist/runtime/workflow-runtime.js +173 -133
- package/dist/runtime/workflow-runtime.js.map +1 -1
- package/dist/steps/base-step.d.ts +24 -0
- package/dist/steps/base-step.d.ts.map +1 -1
- package/dist/steps/base-step.js +23 -0
- package/dist/steps/base-step.js.map +1 -1
- package/dist/steps/{step-registry.d.ts → step-descriptor.d.ts} +24 -32
- package/dist/steps/step-descriptor.d.ts.map +1 -0
- package/dist/steps/step-descriptor.js +33 -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 +30 -0
- package/dist/triggers/base-trigger.d.ts.map +1 -1
- package/dist/triggers/base-trigger.js +17 -0
- package/dist/triggers/base-trigger.js.map +1 -1
- package/dist/triggers/trigger-descriptor.d.ts +44 -0
- package/dist/triggers/trigger-descriptor.d.ts.map +1 -0
- package/dist/triggers/trigger-descriptor.js +30 -0
- package/dist/triggers/trigger-descriptor.js.map +1 -0
- package/dist/triggers/webhook-trigger.d.ts +48 -36
- package/dist/triggers/webhook-trigger.d.ts.map +1 -1
- package/dist/triggers/webhook-trigger.js +40 -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/package.json +1 -1
- package/dist/runtime/execution-event-bus.d.ts +0 -38
- package/dist/runtime/execution-event-bus.d.ts.map +0 -1
- package/dist/runtime/execution-event-bus.js +0 -87
- package/dist/runtime/execution-event-bus.js.map +0 -1
- package/dist/steps/builtin/code.step.d.ts.map +0 -1
- package/dist/steps/builtin/code.step.js.map +0 -1
- package/dist/steps/builtin/conditional.step.d.ts.map +0 -1
- package/dist/steps/builtin/conditional.step.js.map +0 -1
- package/dist/steps/builtin/dedup.step.d.ts.map +0 -1
- package/dist/steps/builtin/dedup.step.js.map +0 -1
- package/dist/steps/builtin/http-request.step.d.ts.map +0 -1
- package/dist/steps/builtin/http-request.step.js.map +0 -1
- package/dist/steps/builtin/loop.step.d.ts.map +0 -1
- package/dist/steps/builtin/loop.step.js.map +0 -1
- package/dist/steps/builtin/map.step.d.ts.map +0 -1
- package/dist/steps/builtin/map.step.js.map +0 -1
- package/dist/steps/builtin/parallel.step.d.ts.map +0 -1
- package/dist/steps/builtin/parallel.step.js.map +0 -1
- package/dist/steps/builtin/ssrf-guard.d.ts.map +0 -1
- package/dist/steps/builtin/ssrf-guard.js.map +0 -1
- package/dist/steps/builtin/switch.step.d.ts.map +0 -1
- package/dist/steps/builtin/switch.step.js.map +0 -1
- package/dist/steps/builtin/wait.step.d.ts.map +0 -1
- package/dist/steps/builtin/wait.step.js.map +0 -1
- package/dist/steps/step-registry.d.ts.map +0 -1
- package/dist/steps/step-registry.js +0 -103
- package/dist/steps/step-registry.js.map +0 -1
- package/dist/triggers/builtin/default-cron-trigger.d.ts.map +0 -1
- package/dist/triggers/builtin/default-cron-trigger.js.map +0 -1
- package/dist/triggers/builtin/default-event-trigger.d.ts.map +0 -1
- package/dist/triggers/builtin/default-event-trigger.js.map +0 -1
- package/dist/triggers/builtin/default-manual-trigger.d.ts.map +0 -1
- package/dist/triggers/builtin/default-manual-trigger.js.map +0 -1
- package/dist/triggers/builtin/default-webhook-trigger.d.ts +0 -72
- package/dist/triggers/builtin/default-webhook-trigger.d.ts.map +0 -1
- package/dist/triggers/builtin/default-webhook-trigger.js +0 -91
- package/dist/triggers/builtin/default-webhook-trigger.js.map +0 -1
- package/dist/triggers/builtin/default-webhook-v2-trigger.d.ts.map +0 -1
- package/dist/triggers/builtin/default-webhook-v2-trigger.js.map +0 -1
- package/dist/triggers/trigger-registry.d.ts +0 -48
- package/dist/triggers/trigger-registry.d.ts.map +0 -1
- package/dist/triggers/trigger-registry.js +0 -83
- package/dist/triggers/trigger-registry.js.map +0 -1
- /package/dist/{steps/builtin → connectors/builtin/steps}/ssrf-guard.d.ts +0 -0
- /package/dist/{steps/builtin → connectors/builtin/steps}/ssrf-guard.js +0 -0
|
@@ -18,10 +18,18 @@ import { validateTriggerPayload } from '../common/validate-payload.js';
|
|
|
18
18
|
import { AccessDeniedError, ResourceNotFoundError } from '../authz/default.js';
|
|
19
19
|
import { EventTrigger } from '../triggers/event-trigger.js';
|
|
20
20
|
import { WebhookTrigger } from '../triggers/webhook-trigger.js';
|
|
21
|
+
import { ApiTrigger } from '../triggers/api-trigger.js';
|
|
22
|
+
import { CredentialResolver } from '../engine/credential-resolver.js';
|
|
23
|
+
import { headerValue } from '../ingest/headers.js';
|
|
24
|
+
import { parseInboundBody } from '../ingest/body.js';
|
|
25
|
+
import { matchesTriggerFilter } from '../ingest/filter.js';
|
|
21
26
|
import { CronTrigger } from '../triggers/cron-trigger.js';
|
|
22
27
|
import { PauseStep } from '../engine/pause-step.js';
|
|
23
28
|
import { leafStepId } from '../engine/node-path.js';
|
|
24
29
|
// ─── Helpers ───
|
|
30
|
+
function messageOf(err) {
|
|
31
|
+
return err instanceof Error ? err.message : String(err);
|
|
32
|
+
}
|
|
25
33
|
const noopLogger = {
|
|
26
34
|
info: () => { },
|
|
27
35
|
warn: () => { },
|
|
@@ -32,6 +40,13 @@ function generateSecret() {
|
|
|
32
40
|
crypto.getRandomValues(bytes);
|
|
33
41
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
34
42
|
}
|
|
43
|
+
/** The trigger's author-supplied input schema, when it has one. */
|
|
44
|
+
function inputSchemaOf(config) {
|
|
45
|
+
const inputSchema = (config.trigger.config ?? {})['inputSchema'];
|
|
46
|
+
return inputSchema && typeof inputSchema === 'object' && !Array.isArray(inputSchema)
|
|
47
|
+
? inputSchema
|
|
48
|
+
: undefined;
|
|
49
|
+
}
|
|
35
50
|
function parseWorkflowConfig(raw) {
|
|
36
51
|
return JSON.parse(raw ?? '{}');
|
|
37
52
|
}
|
|
@@ -69,9 +84,9 @@ export class WorkflowRuntime {
|
|
|
69
84
|
queue;
|
|
70
85
|
scheduler;
|
|
71
86
|
services;
|
|
72
|
-
|
|
73
|
-
triggers;
|
|
87
|
+
connectors;
|
|
74
88
|
executor;
|
|
89
|
+
credentialResolver;
|
|
75
90
|
log;
|
|
76
91
|
baseUrl;
|
|
77
92
|
/** Pub/sub transport for live execution events. Exposed for the router's SSE stream. */
|
|
@@ -95,9 +110,9 @@ export class WorkflowRuntime {
|
|
|
95
110
|
this.queue = opts.queue;
|
|
96
111
|
this.scheduler = opts.scheduler;
|
|
97
112
|
this.services = opts.services;
|
|
98
|
-
this.
|
|
99
|
-
this.triggers = opts.triggers;
|
|
113
|
+
this.connectors = opts.connectors;
|
|
100
114
|
this.executor = opts.executor;
|
|
115
|
+
this.credentialResolver = new CredentialResolver(opts.persistence);
|
|
101
116
|
this.log = opts.logger ?? noopLogger;
|
|
102
117
|
this.eventBus = opts.eventBus;
|
|
103
118
|
this.storage = opts.storage;
|
|
@@ -247,23 +262,26 @@ export class WorkflowRuntime {
|
|
|
247
262
|
* can render inline in the workflow builder.
|
|
248
263
|
*/
|
|
249
264
|
validateConfig(config) {
|
|
250
|
-
const validator = new ConfigValidator(this.
|
|
265
|
+
const validator = new ConfigValidator(this.connectors);
|
|
251
266
|
return validator.validate(config);
|
|
252
267
|
}
|
|
253
268
|
// ─── Workflow Lifecycle ───
|
|
254
269
|
/**
|
|
255
270
|
* Activate a workflow's trigger.
|
|
256
271
|
*
|
|
257
|
-
* -
|
|
272
|
+
* - Every trigger: its credential slots must resolve to accepted credentials
|
|
258
273
|
* - CronTrigger: registers with the scheduler
|
|
259
|
-
*
|
|
274
|
+
*
|
|
275
|
+
* Only then is the workflow marked ACTIVE. Webhook ingest and event dispatch
|
|
276
|
+
* both key off that status, so it is what actually turns a workflow on.
|
|
260
277
|
*/
|
|
261
278
|
async activateWorkflow(ctx, workflowId, opts) {
|
|
262
279
|
// Guard up front, before any external side-effects (webhook/scheduler).
|
|
263
280
|
const { record } = await this.authorizedWorkflow(ctx, workflowId, 'workflow:activate');
|
|
264
281
|
await this.activateInternal(record);
|
|
265
|
-
|
|
266
|
-
//
|
|
282
|
+
await this.persistence.updateWorkflow(workflowId, { status: 'ACTIVE' });
|
|
283
|
+
// After the side-effects, never before: an activation that failed is not a
|
|
284
|
+
// change worth telling anyone about.
|
|
267
285
|
this.publishWorkflowChange(workflowId, 'activated', opts?.origin);
|
|
268
286
|
}
|
|
269
287
|
/**
|
|
@@ -274,20 +292,16 @@ export class WorkflowRuntime {
|
|
|
274
292
|
async activateInternal(workflow) {
|
|
275
293
|
const workflowId = workflow.id;
|
|
276
294
|
const config = parseWorkflowConfig(workflow.config);
|
|
277
|
-
if (!this.
|
|
295
|
+
if (!this.connectors.hasTrigger(config.trigger.type)) {
|
|
278
296
|
throw new Error(`Trigger type "${config.trigger.type}" is not registered`);
|
|
279
297
|
}
|
|
280
|
-
const trigger = this.
|
|
298
|
+
const trigger = this.connectors.getTrigger(config.trigger.type);
|
|
299
|
+
// A missing, revoked or wrong-type credential fails here, naming the slot,
|
|
300
|
+
// rather than on the first event.
|
|
301
|
+
if (trigger.credentialSlots?.length) {
|
|
302
|
+
await this.credentialResolver.resolve(workflow.attributes, trigger, config.trigger.config);
|
|
303
|
+
}
|
|
281
304
|
if (trigger instanceof WebhookTrigger) {
|
|
282
|
-
const secret = generateSecret();
|
|
283
|
-
const path = `/webhooks/${workflowId}/${secret}`;
|
|
284
|
-
await this.persistence.storeWebhookPath(workflowId, path, secret);
|
|
285
|
-
if (trigger.onActivate) {
|
|
286
|
-
await trigger.onActivate(config.trigger.config, {
|
|
287
|
-
webhookUrl: this.baseUrl + path,
|
|
288
|
-
credentials: {},
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
305
|
this.log.info(`activated webhook trigger for workflow ${workflowId}`);
|
|
292
306
|
}
|
|
293
307
|
else if (trigger instanceof CronTrigger) {
|
|
@@ -310,24 +324,16 @@ export class WorkflowRuntime {
|
|
|
310
324
|
// Falls through rather than returning early on an unregistered trigger type:
|
|
311
325
|
// the call still succeeded, and every successful call must leave by the same
|
|
312
326
|
// door so that none of them can quietly skip the notification below.
|
|
313
|
-
const trigger = this.
|
|
314
|
-
? this.
|
|
327
|
+
const trigger = this.connectors.hasTrigger(config.trigger.type)
|
|
328
|
+
? this.connectors.getTrigger(config.trigger.type)
|
|
315
329
|
: null;
|
|
316
|
-
if (trigger instanceof
|
|
317
|
-
if (trigger.onDeactivate) {
|
|
318
|
-
await trigger.onDeactivate(config.trigger.config, {
|
|
319
|
-
webhookUrl: '',
|
|
320
|
-
credentials: {},
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
await this.persistence.removeWebhookPath(workflowId);
|
|
324
|
-
this.log.info(`deactivated webhook trigger for workflow ${workflowId}`);
|
|
325
|
-
}
|
|
326
|
-
else if (trigger instanceof CronTrigger) {
|
|
330
|
+
if (trigger instanceof CronTrigger) {
|
|
327
331
|
await this.scheduler.unschedule(workflowId);
|
|
328
332
|
this.log.info(`deactivated cron trigger for workflow ${workflowId}`);
|
|
329
333
|
}
|
|
330
|
-
//
|
|
334
|
+
// Webhook, event and manual triggers need no teardown: webhook ingest and
|
|
335
|
+
// event dispatch only reach ACTIVE workflows, so the status below stops them.
|
|
336
|
+
await this.persistence.updateWorkflow(workflowId, { status: 'DISABLED' });
|
|
331
337
|
this.publishWorkflowChange(workflowId, 'deactivated', opts?.origin);
|
|
332
338
|
}
|
|
333
339
|
// ─── Workflow callback secret ───
|
|
@@ -379,9 +385,9 @@ export class WorkflowRuntime {
|
|
|
379
385
|
const executionIds = [];
|
|
380
386
|
for (const workflow of workflows) {
|
|
381
387
|
const config = parseWorkflowConfig(workflow.config);
|
|
382
|
-
if (!this.
|
|
388
|
+
if (!this.connectors.hasTrigger(config.trigger.type))
|
|
383
389
|
continue;
|
|
384
|
-
const trigger = this.
|
|
390
|
+
const trigger = this.connectors.getTrigger(config.trigger.type);
|
|
385
391
|
if (!(trigger instanceof EventTrigger))
|
|
386
392
|
continue;
|
|
387
393
|
// Hydrate lightweight payload → full context
|
|
@@ -399,6 +405,10 @@ export class WorkflowRuntime {
|
|
|
399
405
|
if (!trigger.matchFilters(config.trigger.config, payload)) {
|
|
400
406
|
continue;
|
|
401
407
|
}
|
|
408
|
+
// And the author's filter, before a run exists.
|
|
409
|
+
if (!matchesTriggerFilter(config.trigger.filter, payload, config.variables)) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
402
412
|
const execId = await this.createAndEnqueue(workflow, {
|
|
403
413
|
type: event.type,
|
|
404
414
|
...payload,
|
|
@@ -408,43 +418,95 @@ export class WorkflowRuntime {
|
|
|
408
418
|
return executionIds;
|
|
409
419
|
}
|
|
410
420
|
/**
|
|
411
|
-
*
|
|
421
|
+
* Ingest a request to a workflow's webhook URL, `<baseUrl>/webhooks/<workflowId>`.
|
|
422
|
+
*
|
|
423
|
+
* The order is fixed, and the trigger's own code runs only after the sender is
|
|
424
|
+
* authenticated:
|
|
412
425
|
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
426
|
+
* 1. The workflow exists, is ACTIVE and uses a webhook trigger — else 404, the
|
|
427
|
+
* same answer for each so workflow ids cannot be probed.
|
|
428
|
+
* 2. The trigger's credentials resolve — else 401, before any connector code runs.
|
|
429
|
+
* 3. The trigger's `handshake` may answer the request outright. It is the one
|
|
430
|
+
* connector hook that sees an unauthenticated request, so it can only reply,
|
|
431
|
+
* never start a run. A GET it does not answer gets 404: GET reaches nothing else.
|
|
432
|
+
* 4. The trigger's own `verify` accepts the request — else 401.
|
|
433
|
+
* 5. The body is parsed from the raw bytes — else 400.
|
|
434
|
+
* 6. The trigger turns the request into events and a reply.
|
|
435
|
+
* 7. Each event must pass the workflow's trigger filter, or it is dropped.
|
|
436
|
+
* 8. Survivors are enqueued before the reply is returned.
|
|
437
|
+
*
|
|
438
|
+
* Reasons for a rejection go to the log; responses stay generic.
|
|
415
439
|
*/
|
|
416
|
-
async handleWebhook(
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}
|
|
440
|
+
async handleWebhook(workflowId, request) {
|
|
441
|
+
const notFound = {
|
|
442
|
+
reply: { status: 404, body: { error: 'Webhook not found' } },
|
|
443
|
+
executionIds: [],
|
|
444
|
+
};
|
|
445
|
+
const unauthorized = {
|
|
446
|
+
reply: { status: 401, body: { error: 'Unauthorized' } },
|
|
447
|
+
executionIds: [],
|
|
448
|
+
};
|
|
449
|
+
const workflow = await this.persistence.getWorkflow(workflowId);
|
|
450
|
+
if (!workflow || workflow.status !== 'ACTIVE')
|
|
451
|
+
return notFound;
|
|
452
|
+
const config = parseWorkflowConfig(workflow.config);
|
|
453
|
+
const trigger = this.connectors.findTrigger(config.trigger.type)?.trigger;
|
|
454
|
+
if (!(trigger instanceof WebhookTrigger))
|
|
455
|
+
return notFound;
|
|
456
|
+
const attributes = workflow.attributes;
|
|
457
|
+
const triggerConfig = config.trigger.config;
|
|
458
|
+
let ctx;
|
|
459
|
+
try {
|
|
460
|
+
const credentials = await this.credentialResolver.resolve(attributes, trigger, triggerConfig);
|
|
461
|
+
ctx = { credentials, attributes, baseUrl: this.baseUrl };
|
|
423
462
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
httpResponse: { status: 404, body: { error: 'Workflow not active' } },
|
|
429
|
-
};
|
|
463
|
+
catch (err) {
|
|
464
|
+
// Fail closed: verification never runs without the credentials it needs.
|
|
465
|
+
this.log.warn(`webhook ${workflowId}: rejected, ${messageOf(err)}`);
|
|
466
|
+
return unauthorized;
|
|
430
467
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
return {
|
|
435
|
-
executionId: '',
|
|
436
|
-
httpResponse: {
|
|
437
|
-
status: 400,
|
|
438
|
-
body: { error: 'Workflow does not use a webhook trigger' },
|
|
439
|
-
},
|
|
440
|
-
};
|
|
468
|
+
let handshakeReply;
|
|
469
|
+
try {
|
|
470
|
+
handshakeReply = await trigger.handshake(request, triggerConfig, ctx);
|
|
441
471
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
472
|
+
catch (err) {
|
|
473
|
+
// A broken handshake must not block signed deliveries: carry on to verification.
|
|
474
|
+
this.log.warn(`webhook ${workflowId}: handshake threw: ${messageOf(err)}`);
|
|
475
|
+
}
|
|
476
|
+
if (handshakeReply)
|
|
477
|
+
return { reply: handshakeReply, executionIds: [] };
|
|
478
|
+
if (request.method !== 'POST')
|
|
479
|
+
return notFound;
|
|
480
|
+
let verified = false;
|
|
481
|
+
try {
|
|
482
|
+
verified = await trigger.verify(request, triggerConfig, ctx);
|
|
483
|
+
}
|
|
484
|
+
catch (err) {
|
|
485
|
+
this.log.warn(`webhook ${workflowId}: verification threw: ${messageOf(err)}`);
|
|
486
|
+
}
|
|
487
|
+
if (!verified) {
|
|
488
|
+
this.log.warn(`webhook ${workflowId}: rejected by ${trigger.type} verification`);
|
|
489
|
+
return unauthorized;
|
|
490
|
+
}
|
|
491
|
+
let body;
|
|
492
|
+
try {
|
|
493
|
+
body = parseInboundBody(request.rawBody, headerValue(request.headers, 'content-type'));
|
|
494
|
+
}
|
|
495
|
+
catch (err) {
|
|
496
|
+
this.log.warn(`webhook ${workflowId}: rejected, ${messageOf(err)}`);
|
|
497
|
+
return { reply: { status: 400, body: { error: 'Malformed request body' } }, executionIds: [] };
|
|
498
|
+
}
|
|
499
|
+
const translation = await trigger.handleRequest({ ...request, body }, triggerConfig, ctx);
|
|
500
|
+
const executionIds = [];
|
|
501
|
+
for (const event of translation.events) {
|
|
502
|
+
if (!matchesTriggerFilter(config.trigger.filter, event.payload, config.variables))
|
|
503
|
+
continue;
|
|
504
|
+
executionIds.push(await this.createAndEnqueue(workflow, { type: event.type, ...event.payload }, { publicTrigger: true }));
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
reply: translation.reply ?? { status: 200, body: { ok: true } },
|
|
508
|
+
executionIds,
|
|
509
|
+
};
|
|
448
510
|
}
|
|
449
511
|
/**
|
|
450
512
|
* Handle an incoming wait-callback at `POST /wait/callback/<secret>/<executionId>`.
|
|
@@ -524,91 +586,67 @@ export class WorkflowRuntime {
|
|
|
524
586
|
return this.createAndEnqueue(workflow, triggerPayload);
|
|
525
587
|
}
|
|
526
588
|
/**
|
|
527
|
-
*
|
|
589
|
+
* Start a workflow from the API trigger route.
|
|
590
|
+
*
|
|
591
|
+
* No caller reaches the SDK here: the route has no session, and the SDK does
|
|
592
|
+
* not authorize whoever calls it. A host that exposes the route guards it
|
|
593
|
+
* itself, for example with an API key. The workflow must use an API trigger,
|
|
594
|
+
* and the payload must satisfy its input schema.
|
|
528
595
|
*
|
|
529
|
-
*
|
|
530
|
-
* the route publicly. The workflow itself must opt in by using WEBHOOK_V2,
|
|
531
|
-
* and the execution is marked as public so privileged steps can enforce their
|
|
532
|
-
* own public-trigger restrictions.
|
|
596
|
+
* Runs are marked as public triggers, so privileged steps refuse them.
|
|
533
597
|
*/
|
|
534
|
-
async
|
|
535
|
-
const workflow = await this.
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const
|
|
540
|
-
if (config.trigger.type !== 'WEBHOOK_V2') {
|
|
541
|
-
throw new Error(`Workflow ${workflowId} does not use a WEBHOOK_V2 trigger`);
|
|
542
|
-
}
|
|
543
|
-
const triggerCfg = (config.trigger.config ?? {});
|
|
544
|
-
const inputSchema = triggerCfg['inputSchema'];
|
|
545
|
-
const webhookV2InputSchema = inputSchema && Array.isArray(inputSchema['required'])
|
|
598
|
+
async triggerViaApi(workflowId, payload) {
|
|
599
|
+
const { workflow, config } = await this.apiTriggerWorkflow(workflowId);
|
|
600
|
+
const inputSchema = inputSchemaOf(config);
|
|
601
|
+
// Uploaded documents are optional on this route even when the schema lists
|
|
602
|
+
// them as required.
|
|
603
|
+
const apiInputSchema = inputSchema && Array.isArray(inputSchema['required'])
|
|
546
604
|
? {
|
|
547
605
|
...inputSchema,
|
|
548
606
|
required: inputSchema['required'].filter((field) => field !== 'documents'),
|
|
549
607
|
}
|
|
550
608
|
: inputSchema;
|
|
551
|
-
const errors = validateTriggerPayload(
|
|
609
|
+
const errors = validateTriggerPayload(apiInputSchema, payload);
|
|
552
610
|
if (errors.length > 0) {
|
|
553
611
|
throw new Error(`Invalid trigger payload: ${errors.map((e) => e.message).join('; ')}`);
|
|
554
612
|
}
|
|
555
|
-
return this.createAndEnqueue(workflow, {
|
|
556
|
-
type: config.trigger.type,
|
|
557
|
-
...payload,
|
|
558
|
-
}, { publicTrigger: true });
|
|
613
|
+
return this.createAndEnqueue(workflow, { type: config.trigger.type, ...payload }, { publicTrigger: true });
|
|
559
614
|
}
|
|
560
615
|
/**
|
|
561
|
-
*
|
|
562
|
-
*
|
|
616
|
+
* What the API trigger route needs before it builds a payload: the trigger's
|
|
617
|
+
* input schema, and the workflow's scope for storing uploaded files. Checked
|
|
618
|
+
* the same way as {@link triggerViaApi}.
|
|
563
619
|
*/
|
|
564
|
-
async
|
|
565
|
-
const workflow = await this.
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
const config = parseWorkflowConfig(workflow.config);
|
|
571
|
-
const triggerCfg = (config.trigger.config ?? {});
|
|
572
|
-
const inputSchema = triggerCfg['inputSchema'];
|
|
573
|
-
return inputSchema && typeof inputSchema === 'object' && !Array.isArray(inputSchema)
|
|
574
|
-
? inputSchema
|
|
575
|
-
: undefined;
|
|
620
|
+
async getApiTriggerTarget(workflowId) {
|
|
621
|
+
const { workflow, config } = await this.apiTriggerWorkflow(workflowId);
|
|
622
|
+
return {
|
|
623
|
+
inputSchema: inputSchemaOf(config),
|
|
624
|
+
storageScope: workflow.attributes,
|
|
625
|
+
};
|
|
576
626
|
}
|
|
577
|
-
/**
|
|
578
|
-
|
|
579
|
-
*
|
|
580
|
-
* Unlike getWorkflowTriggerInputSchema(), this does not authorize a caller;
|
|
581
|
-
* it only verifies the workflow exists and opted into the expected public
|
|
582
|
-
* trigger type.
|
|
583
|
-
*/
|
|
584
|
-
async getWorkflowTriggerInputSchemaPublic(workflowId, expectedTriggerType) {
|
|
627
|
+
/** The workflow exists and uses an API trigger. */
|
|
628
|
+
async apiTriggerWorkflow(workflowId) {
|
|
585
629
|
const workflow = await this.persistence.getWorkflow(workflowId);
|
|
586
|
-
if (!workflow)
|
|
587
|
-
throw new
|
|
588
|
-
}
|
|
630
|
+
if (!workflow)
|
|
631
|
+
throw new ResourceNotFoundError('Workflow', workflowId);
|
|
589
632
|
const config = parseWorkflowConfig(workflow.config);
|
|
590
|
-
|
|
591
|
-
|
|
633
|
+
const trigger = this.connectors.findTrigger(config.trigger.type)?.trigger;
|
|
634
|
+
if (!(trigger instanceof ApiTrigger)) {
|
|
635
|
+
throw new Error(`Workflow ${workflowId} does not use an API trigger`);
|
|
592
636
|
}
|
|
593
|
-
|
|
594
|
-
const inputSchema = triggerCfg['inputSchema'];
|
|
595
|
-
return inputSchema && typeof inputSchema === 'object' && !Array.isArray(inputSchema)
|
|
596
|
-
? inputSchema
|
|
597
|
-
: undefined;
|
|
637
|
+
return { workflow, config };
|
|
598
638
|
}
|
|
599
639
|
/**
|
|
600
|
-
* Return the
|
|
640
|
+
* Return the configured trigger input schema for routes that need to translate
|
|
641
|
+
* transport-level payloads before calling `triggerManual()`.
|
|
601
642
|
*/
|
|
602
|
-
async
|
|
643
|
+
async getWorkflowTriggerInputSchema(ctx, workflowId) {
|
|
603
644
|
const workflow = await this.persistence.getWorkflow(workflowId);
|
|
604
645
|
if (!workflow) {
|
|
605
646
|
throw new Error(`Workflow ${workflowId} not found`);
|
|
606
647
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
throw new Error(`Workflow ${workflowId} does not use a ${expectedTriggerType} trigger`);
|
|
610
|
-
}
|
|
611
|
-
return workflow.attributes;
|
|
648
|
+
await this.require(ctx, 'workflow:run', this.workflowRef(workflow));
|
|
649
|
+
return inputSchemaOf(parseWorkflowConfig(workflow.config));
|
|
612
650
|
}
|
|
613
651
|
/**
|
|
614
652
|
* Resume a paused execution. Guards `execution:approve`, then does the work.
|
|
@@ -766,8 +804,10 @@ export class WorkflowRuntime {
|
|
|
766
804
|
// Internal post-create activation — already authorized by the create above.
|
|
767
805
|
await this.activateInternal(record);
|
|
768
806
|
}
|
|
769
|
-
catch {
|
|
770
|
-
// Non-fatal — workflow is created
|
|
807
|
+
catch (err) {
|
|
808
|
+
// Non-fatal — the workflow is created, and its trigger can be activated
|
|
809
|
+
// once whatever failed is fixed.
|
|
810
|
+
this.log.warn(`created workflow ${id} but could not activate its trigger: ${messageOf(err)}`);
|
|
771
811
|
}
|
|
772
812
|
}
|
|
773
813
|
this.log.info(`created workflow ${id} (${input.name})`);
|
|
@@ -1208,9 +1248,9 @@ export class WorkflowRuntime {
|
|
|
1208
1248
|
if (!workflow || workflow.status !== 'ACTIVE')
|
|
1209
1249
|
return null;
|
|
1210
1250
|
const config = parseWorkflowConfig(workflow.config);
|
|
1211
|
-
if (!this.
|
|
1251
|
+
if (!this.connectors.hasTrigger(config.trigger.type))
|
|
1212
1252
|
return null;
|
|
1213
|
-
const trigger = this.
|
|
1253
|
+
const trigger = this.connectors.getTrigger(config.trigger.type);
|
|
1214
1254
|
if (!(trigger instanceof CronTrigger))
|
|
1215
1255
|
return null;
|
|
1216
1256
|
const metadata = JSON.parse(workflow.metadata ?? '{}');
|