@blokjs/runner 0.2.2 → 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/dist/Blok.js +32 -3
- package/dist/Blok.js.map +1 -1
- package/dist/Configuration.d.ts +59 -5
- package/dist/Configuration.js +366 -96
- package/dist/Configuration.js.map +1 -1
- package/dist/ForEachNode.d.ts +59 -0
- package/dist/ForEachNode.js +522 -0
- package/dist/ForEachNode.js.map +1 -0
- package/dist/LoopMaxIterationsError.d.ts +11 -0
- package/dist/LoopMaxIterationsError.js +18 -0
- package/dist/LoopMaxIterationsError.js.map +1 -0
- package/dist/LoopNode.d.ts +36 -0
- package/dist/LoopNode.js +182 -0
- package/dist/LoopNode.js.map +1 -0
- package/dist/PayloadTooLargeError.d.ts +19 -0
- package/dist/PayloadTooLargeError.js +29 -0
- package/dist/PayloadTooLargeError.js.map +1 -0
- package/dist/RunCancelledError.d.ts +17 -0
- package/dist/RunCancelledError.js +25 -0
- package/dist/RunCancelledError.js.map +1 -0
- package/dist/Runner.d.ts +11 -1
- package/dist/Runner.js +9 -2
- package/dist/Runner.js.map +1 -1
- package/dist/RunnerSteps.js +648 -44
- package/dist/RunnerSteps.js.map +1 -1
- package/dist/RuntimeAdapterNode.d.ts +2 -1
- package/dist/RuntimeAdapterNode.js +2 -2
- package/dist/RuntimeAdapterNode.js.map +1 -1
- package/dist/RuntimeRegistry.d.ts +23 -2
- package/dist/RuntimeRegistry.js +31 -2
- package/dist/RuntimeRegistry.js.map +1 -1
- package/dist/SubworkflowNode.d.ts +181 -0
- package/dist/SubworkflowNode.js +479 -0
- package/dist/SubworkflowNode.js.map +1 -0
- package/dist/SwitchNode.d.ts +37 -0
- package/dist/SwitchNode.js +153 -0
- package/dist/SwitchNode.js.map +1 -0
- package/dist/TriggerBase.d.ts +178 -0
- package/dist/TriggerBase.js +1032 -5
- package/dist/TriggerBase.js.map +1 -1
- package/dist/TryCatchNode.d.ts +32 -0
- package/dist/TryCatchNode.js +207 -0
- package/dist/TryCatchNode.js.map +1 -0
- package/dist/WaitDispatchRequest.d.ts +38 -0
- package/dist/WaitDispatchRequest.js +13 -0
- package/dist/WaitDispatchRequest.js.map +1 -0
- package/dist/WaitNode.d.ts +23 -0
- package/dist/WaitNode.js +26 -0
- package/dist/WaitNode.js.map +1 -0
- package/dist/adapters/grpc/GrpcCodec.js +2 -2
- package/dist/adapters/grpc/GrpcRuntimeAdapter.d.ts +6 -4
- package/dist/adapters/grpc/GrpcRuntimeAdapter.js +6 -4
- package/dist/adapters/grpc/GrpcRuntimeAdapter.js.map +1 -1
- package/dist/adapters/grpc/types.d.ts +7 -5
- package/dist/adapters/grpc/types.js.map +1 -1
- package/dist/adapters/transport.d.ts +12 -41
- package/dist/adapters/transport.js +21 -70
- package/dist/adapters/transport.js.map +1 -1
- package/dist/cache/NodeResultCache.js +7 -0
- package/dist/cache/NodeResultCache.js.map +1 -1
- package/dist/concurrency/ConcurrencyBackend.d.ts +61 -0
- package/dist/concurrency/ConcurrencyBackend.js +20 -0
- package/dist/concurrency/ConcurrencyBackend.js.map +1 -0
- package/dist/concurrency/ConcurrencyLimitError.d.ts +37 -0
- package/dist/concurrency/ConcurrencyLimitError.js +16 -0
- package/dist/concurrency/ConcurrencyLimitError.js.map +1 -0
- package/dist/concurrency/NatsKvConcurrencyBackend.d.ts +64 -0
- package/dist/concurrency/NatsKvConcurrencyBackend.js +310 -0
- package/dist/concurrency/NatsKvConcurrencyBackend.js.map +1 -0
- package/dist/concurrency/QueueExpiredError.d.ts +40 -0
- package/dist/concurrency/QueueExpiredError.js +15 -0
- package/dist/concurrency/QueueExpiredError.js.map +1 -0
- package/dist/concurrency/RedisConcurrencyBackend.d.ts +64 -0
- package/dist/concurrency/RedisConcurrencyBackend.js +374 -0
- package/dist/concurrency/RedisConcurrencyBackend.js.map +1 -0
- package/dist/concurrency/createConcurrencyBackend.d.ts +24 -0
- package/dist/concurrency/createConcurrencyBackend.js +38 -0
- package/dist/concurrency/createConcurrencyBackend.js.map +1 -0
- package/dist/concurrency/readConcurrencyConfig.d.ts +60 -0
- package/dist/concurrency/readConcurrencyConfig.js +60 -0
- package/dist/concurrency/readConcurrencyConfig.js.map +1 -0
- package/dist/defineNode.d.ts +8 -0
- package/dist/defineNode.js +25 -5
- package/dist/defineNode.js.map +1 -1
- package/dist/graphql/GraphQLSchemaGenerator.js +1 -1
- package/dist/graphql/GraphQLSchemaGenerator.js.map +1 -1
- package/dist/idempotency/resolveIdempotencyKey.d.ts +20 -0
- package/dist/idempotency/resolveIdempotencyKey.js +37 -0
- package/dist/idempotency/resolveIdempotencyKey.js.map +1 -0
- package/dist/index.d.ts +30 -6
- package/dist/index.js +55 -6
- package/dist/index.js.map +1 -1
- package/dist/marketplace/RuntimeCatalog.d.ts +6 -0
- package/dist/marketplace/RuntimeCatalog.js.map +1 -1
- package/dist/marketplace/RuntimeDiscovery.d.ts +2 -2
- package/dist/marketplace/RuntimeDiscovery.js +18 -6
- package/dist/marketplace/RuntimeDiscovery.js.map +1 -1
- package/dist/monitoring/ConcurrencyMetrics.d.ts +82 -0
- package/dist/monitoring/ConcurrencyMetrics.js +139 -0
- package/dist/monitoring/ConcurrencyMetrics.js.map +1 -0
- package/dist/monitoring/ForEachWaitMetrics.d.ts +22 -0
- package/dist/monitoring/ForEachWaitMetrics.js +36 -0
- package/dist/monitoring/ForEachWaitMetrics.js.map +1 -0
- package/dist/monitoring/JanitorMetrics.d.ts +27 -0
- package/dist/monitoring/JanitorMetrics.js +48 -0
- package/dist/monitoring/JanitorMetrics.js.map +1 -0
- package/dist/openapi/OpenAPIGenerator.js +7 -2
- package/dist/openapi/OpenAPIGenerator.js.map +1 -1
- package/dist/runtime/PrimitiveStack.d.ts +64 -0
- package/dist/runtime/PrimitiveStack.js +92 -0
- package/dist/runtime/PrimitiveStack.js.map +1 -0
- package/dist/scheduling/DebounceBackend.d.ts +108 -0
- package/dist/scheduling/DebounceBackend.js +23 -0
- package/dist/scheduling/DebounceBackend.js.map +1 -0
- package/dist/scheduling/DebounceCoordinator.d.ts +141 -0
- package/dist/scheduling/DebounceCoordinator.js +362 -0
- package/dist/scheduling/DebounceCoordinator.js.map +1 -0
- package/dist/scheduling/DeferredDispatchSignal.d.ts +50 -0
- package/dist/scheduling/DeferredDispatchSignal.js +14 -0
- package/dist/scheduling/DeferredDispatchSignal.js.map +1 -0
- package/dist/scheduling/DeferredRunScheduler.d.ts +96 -0
- package/dist/scheduling/DeferredRunScheduler.js +256 -0
- package/dist/scheduling/DeferredRunScheduler.js.map +1 -0
- package/dist/scheduling/NatsKvDebounceBackend.d.ts +53 -0
- package/dist/scheduling/NatsKvDebounceBackend.js +334 -0
- package/dist/scheduling/NatsKvDebounceBackend.js.map +1 -0
- package/dist/scheduling/RedisDebounceBackend.d.ts +49 -0
- package/dist/scheduling/RedisDebounceBackend.js +356 -0
- package/dist/scheduling/RedisDebounceBackend.js.map +1 -0
- package/dist/scheduling/createDebounceBackend.d.ts +25 -0
- package/dist/scheduling/createDebounceBackend.js +39 -0
- package/dist/scheduling/createDebounceBackend.js.map +1 -0
- package/dist/scheduling/readSchedulingConfig.d.ts +24 -0
- package/dist/scheduling/readSchedulingConfig.js +52 -0
- package/dist/scheduling/readSchedulingConfig.js.map +1 -0
- package/dist/security/AuditLogger.js +1 -1
- package/dist/security/AuditLogger.js.map +1 -1
- package/dist/security/AuthMiddleware.d.ts +19 -20
- package/dist/security/AuthMiddleware.js +35 -20
- package/dist/security/AuthMiddleware.js.map +1 -1
- package/dist/security/OAuthProvider.js +2 -2
- package/dist/security/OAuthProvider.js.map +1 -1
- package/dist/security/SecretManager.js +14 -13
- package/dist/security/SecretManager.js.map +1 -1
- package/dist/security/index.d.ts +3 -1
- package/dist/security/index.js +3 -1
- package/dist/security/index.js.map +1 -1
- package/dist/testing/TestHarness.d.ts +27 -12
- package/dist/testing/TestHarness.js +19 -3
- package/dist/testing/TestHarness.js.map +1 -1
- package/dist/testing/WorkflowTestRunner.js +0 -7
- package/dist/testing/WorkflowTestRunner.js.map +1 -1
- package/dist/timeouts/StepTimeoutError.d.ts +22 -0
- package/dist/timeouts/StepTimeoutError.js +31 -0
- package/dist/timeouts/StepTimeoutError.js.map +1 -0
- package/dist/tracing/InMemoryRunStore.d.ts +41 -1
- package/dist/tracing/InMemoryRunStore.js +239 -0
- package/dist/tracing/InMemoryRunStore.js.map +1 -1
- package/dist/tracing/Janitor.d.ts +70 -0
- package/dist/tracing/Janitor.js +150 -0
- package/dist/tracing/Janitor.js.map +1 -0
- package/dist/tracing/PostgresRunStore.d.ts +57 -1
- package/dist/tracing/PostgresRunStore.js +711 -6
- package/dist/tracing/PostgresRunStore.js.map +1 -1
- package/dist/tracing/RoutingDiagnostics.d.ts +55 -0
- package/dist/tracing/RoutingDiagnostics.js +50 -0
- package/dist/tracing/RoutingDiagnostics.js.map +1 -0
- package/dist/tracing/RunStore.d.ts +181 -1
- package/dist/tracing/RunTracker.d.ts +244 -9
- package/dist/tracing/RunTracker.js +594 -1
- package/dist/tracing/RunTracker.js.map +1 -1
- package/dist/tracing/SqliteRunStore.d.ts +79 -2
- package/dist/tracing/SqliteRunStore.js +775 -16
- package/dist/tracing/SqliteRunStore.js.map +1 -1
- package/dist/tracing/TraceRouter.d.ts +20 -2
- package/dist/tracing/TraceRouter.js +612 -6
- package/dist/tracing/TraceRouter.js.map +1 -1
- package/dist/tracing/createStore.js +14 -3
- package/dist/tracing/createStore.js.map +1 -1
- package/dist/tracing/metadataFilter.d.ts +63 -0
- package/dist/tracing/metadataFilter.js +224 -0
- package/dist/tracing/metadataFilter.js.map +1 -0
- package/dist/tracing/sanitize.d.ts +11 -0
- package/dist/tracing/sanitize.js +29 -0
- package/dist/tracing/sanitize.js.map +1 -1
- package/dist/tracing/types.d.ts +672 -2
- package/dist/utils/createChildContext.d.ts +32 -0
- package/dist/utils/createChildContext.js +113 -0
- package/dist/utils/createChildContext.js.map +1 -0
- package/dist/utils/envAllowlist.d.ts +35 -0
- package/dist/utils/envAllowlist.js +113 -0
- package/dist/utils/envAllowlist.js.map +1 -0
- package/dist/version/RuntimeVersionValidator.d.ts +38 -0
- package/dist/version/RuntimeVersionValidator.js +121 -0
- package/dist/version/RuntimeVersionValidator.js.map +1 -0
- package/dist/visualization/WorkflowVisualizer.js +4 -4
- package/dist/visualization/WorkflowVisualizer.js.map +1 -1
- package/dist/workflow/PersistenceHelper.d.ts +18 -10
- package/dist/workflow/PersistenceHelper.js +35 -9
- package/dist/workflow/PersistenceHelper.js.map +1 -1
- package/dist/workflow/WorkflowNormalizer.d.ts +48 -42
- package/dist/workflow/WorkflowNormalizer.js +650 -18
- package/dist/workflow/WorkflowNormalizer.js.map +1 -1
- package/dist/workflow/WorkflowRegistry.d.ts +186 -0
- package/dist/workflow/WorkflowRegistry.js +202 -0
- package/dist/workflow/WorkflowRegistry.js.map +1 -0
- package/dist/workflow/sampleBody.d.ts +54 -0
- package/dist/workflow/sampleBody.js +320 -0
- package/dist/workflow/sampleBody.js.map +1 -0
- package/package.json +3 -8
- package/dist/adapters/HttpRuntimeAdapter.d.ts +0 -79
- package/dist/adapters/HttpRuntimeAdapter.js +0 -233
- package/dist/adapters/HttpRuntimeAdapter.js.map +0 -1
package/dist/Configuration.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
// import { NodeBase } from "@blokjs/shared";
|
|
2
|
+
// import { z } from "zod";
|
|
3
|
+
import { sep as pathSep, resolve as resolvePath } from "node:path";
|
|
4
|
+
import { tryParseDuration } from "@blokjs/helper";
|
|
1
5
|
import ConfigurationResolver from "./ConfigurationResolver";
|
|
2
6
|
import RunnerNode from "./RunnerNode";
|
|
3
7
|
import { RuntimeAdapterNode } from "./RuntimeAdapterNode";
|
|
4
8
|
import { RuntimeRegistry } from "./RuntimeRegistry";
|
|
5
|
-
import { HttpRuntimeAdapter } from "./adapters/HttpRuntimeAdapter";
|
|
6
9
|
import { NodeJsRuntimeAdapter } from "./adapters/NodeJsRuntimeAdapter";
|
|
7
10
|
import { GrpcRuntimeAdapter } from "./adapters/grpc/GrpcRuntimeAdapter";
|
|
8
11
|
import { DEFAULT_GRPC_PORTS, GRPC_DEFAULTS } from "./adapters/grpc/types";
|
|
9
|
-
import { isLoopbackHost, isStreamLogsEnabled, isStrictTlsEnabled, loadTlsConfigForKind, resolveHealthCheckFailureThreshold, resolveHealthCheckIntervalMs,
|
|
12
|
+
import { assertGrpcOnlyTransport, isLoopbackHost, isStreamLogsEnabled, isStrictTlsEnabled, loadTlsConfigForKind, resolveHealthCheckFailureThreshold, resolveHealthCheckIntervalMs, } from "./adapters/transport";
|
|
13
|
+
import { RuntimeVersionValidator } from "./version/RuntimeVersionValidator";
|
|
10
14
|
export default class Configuration {
|
|
11
15
|
workflow = {};
|
|
12
16
|
name;
|
|
@@ -14,6 +18,14 @@ export default class Configuration {
|
|
|
14
18
|
steps;
|
|
15
19
|
nodes;
|
|
16
20
|
trigger;
|
|
21
|
+
/**
|
|
22
|
+
* v0.5.2 — workflow-level middleware chain. Populated from the
|
|
23
|
+
* normalized workflow's `appliedMiddleware` field. HTTP and Worker
|
|
24
|
+
* triggers prepend this list to their own `trigger.<kind>.middleware`
|
|
25
|
+
* before invoking the chain, so workflow-level entries run BEFORE
|
|
26
|
+
* trigger-level entries on every request.
|
|
27
|
+
*/
|
|
28
|
+
appliedMiddleware;
|
|
17
29
|
static loaded_nodes = {};
|
|
18
30
|
globalOptions;
|
|
19
31
|
constructor() {
|
|
@@ -22,87 +34,40 @@ export default class Configuration {
|
|
|
22
34
|
this.version = "";
|
|
23
35
|
this.name = "";
|
|
24
36
|
this.trigger = {};
|
|
37
|
+
this.appliedMiddleware = [];
|
|
25
38
|
this.initializeRuntimeRegistry();
|
|
26
39
|
}
|
|
27
40
|
/**
|
|
28
41
|
* Initialize the RuntimeRegistry with built-in adapters.
|
|
29
42
|
*
|
|
30
|
-
* Registers `NodeJsRuntimeAdapter` for in-process JS nodes, then
|
|
31
|
-
* SDK language
|
|
32
|
-
*
|
|
33
|
-
*
|
|
43
|
+
* Registers `NodeJsRuntimeAdapter` for in-process JS nodes, then a
|
|
44
|
+
* `GrpcRuntimeAdapter` per SDK language. gRPC is the sole transport
|
|
45
|
+
* since v0.5 — `assertGrpcOnlyTransport` throws if the operator still
|
|
46
|
+
* has `RUNTIME_TRANSPORT=http` set.
|
|
34
47
|
*/
|
|
35
48
|
initializeRuntimeRegistry() {
|
|
49
|
+
assertGrpcOnlyTransport();
|
|
36
50
|
const registry = RuntimeRegistry.getInstance();
|
|
37
51
|
if (!registry.has("nodejs")) {
|
|
38
52
|
registry.register(new NodeJsRuntimeAdapter());
|
|
39
53
|
}
|
|
40
54
|
const sdkLanguages = [
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
kind: "rust",
|
|
50
|
-
hostEnv: "RUNTIME_RUST_HOST",
|
|
51
|
-
httpPortEnv: "RUNTIME_RUST_PORT",
|
|
52
|
-
grpcPortEnv: "RUNTIME_RUST_GRPC_PORT",
|
|
53
|
-
defaultHttpPort: 9002,
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
kind: "java",
|
|
57
|
-
hostEnv: "RUNTIME_JAVA_HOST",
|
|
58
|
-
httpPortEnv: "RUNTIME_JAVA_PORT",
|
|
59
|
-
grpcPortEnv: "RUNTIME_JAVA_GRPC_PORT",
|
|
60
|
-
defaultHttpPort: 9003,
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
kind: "csharp",
|
|
64
|
-
hostEnv: "RUNTIME_CSHARP_HOST",
|
|
65
|
-
httpPortEnv: "RUNTIME_CSHARP_PORT",
|
|
66
|
-
grpcPortEnv: "RUNTIME_CSHARP_GRPC_PORT",
|
|
67
|
-
defaultHttpPort: 9004,
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
kind: "php",
|
|
71
|
-
hostEnv: "RUNTIME_PHP_HOST",
|
|
72
|
-
httpPortEnv: "RUNTIME_PHP_PORT",
|
|
73
|
-
grpcPortEnv: "RUNTIME_PHP_GRPC_PORT",
|
|
74
|
-
defaultHttpPort: 9005,
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
kind: "ruby",
|
|
78
|
-
hostEnv: "RUNTIME_RUBY_HOST",
|
|
79
|
-
httpPortEnv: "RUNTIME_RUBY_PORT",
|
|
80
|
-
grpcPortEnv: "RUNTIME_RUBY_GRPC_PORT",
|
|
81
|
-
defaultHttpPort: 9006,
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
kind: "python3",
|
|
85
|
-
hostEnv: "RUNTIME_PYTHON3_HOST",
|
|
86
|
-
httpPortEnv: "RUNTIME_PYTHON3_PORT",
|
|
87
|
-
grpcPortEnv: "RUNTIME_PYTHON3_GRPC_PORT",
|
|
88
|
-
defaultHttpPort: 9007,
|
|
89
|
-
},
|
|
55
|
+
{ kind: "go", hostEnv: "RUNTIME_GO_HOST", grpcPortEnv: "RUNTIME_GO_GRPC_PORT" },
|
|
56
|
+
{ kind: "rust", hostEnv: "RUNTIME_RUST_HOST", grpcPortEnv: "RUNTIME_RUST_GRPC_PORT" },
|
|
57
|
+
{ kind: "java", hostEnv: "RUNTIME_JAVA_HOST", grpcPortEnv: "RUNTIME_JAVA_GRPC_PORT" },
|
|
58
|
+
{ kind: "csharp", hostEnv: "RUNTIME_CSHARP_HOST", grpcPortEnv: "RUNTIME_CSHARP_GRPC_PORT" },
|
|
59
|
+
{ kind: "php", hostEnv: "RUNTIME_PHP_HOST", grpcPortEnv: "RUNTIME_PHP_GRPC_PORT" },
|
|
60
|
+
{ kind: "ruby", hostEnv: "RUNTIME_RUBY_HOST", grpcPortEnv: "RUNTIME_RUBY_GRPC_PORT" },
|
|
61
|
+
{ kind: "python3", hostEnv: "RUNTIME_PYTHON3_HOST", grpcPortEnv: "RUNTIME_PYTHON3_GRPC_PORT" },
|
|
90
62
|
];
|
|
91
63
|
for (const lang of sdkLanguages) {
|
|
92
64
|
if (registry.has(lang.kind))
|
|
93
65
|
continue;
|
|
94
|
-
const transport = resolveTransportForKind(lang.kind);
|
|
95
66
|
const host = process.env[lang.hostEnv] || "localhost";
|
|
96
|
-
const adapter =
|
|
97
|
-
? this.buildGrpcAdapter(lang.kind, host, lang.grpcPortEnv)
|
|
98
|
-
: this.buildHttpAdapter(lang.kind, host, lang.httpPortEnv, lang.defaultHttpPort);
|
|
67
|
+
const adapter = this.buildGrpcAdapter(lang.kind, host, lang.grpcPortEnv);
|
|
99
68
|
registry.register(adapter);
|
|
100
69
|
}
|
|
101
70
|
}
|
|
102
|
-
buildHttpAdapter(kind, host, portEnv, defaultPort) {
|
|
103
|
-
const port = process.env[portEnv] ? Number.parseInt(process.env[portEnv], 10) : defaultPort;
|
|
104
|
-
return new HttpRuntimeAdapter(kind, host, port);
|
|
105
|
-
}
|
|
106
71
|
buildGrpcAdapter(kind, host, portEnv) {
|
|
107
72
|
const defaultPort = DEFAULT_GRPC_PORTS[kind];
|
|
108
73
|
const port = process.env[portEnv] ? Number.parseInt(process.env[portEnv], 10) : defaultPort;
|
|
@@ -156,9 +121,17 @@ export default class Configuration {
|
|
|
156
121
|
if (preloaded !== undefined) {
|
|
157
122
|
// Boot-time scan path — workflow object already loaded, just
|
|
158
123
|
// normalize it through the same v1→v2 pipeline as disk-loaded
|
|
159
|
-
// workflows.
|
|
124
|
+
// workflows. **Deep-clone first** so per-request mutations
|
|
125
|
+
// (`NodeBase.blueprintMapper` → `mapper.replaceObjectStrings`
|
|
126
|
+
// resolves `js/...` expressions in place) don't bleed across
|
|
127
|
+
// requests by baking the first request's resolved values into
|
|
128
|
+
// the shared route-table workflow object. JSON-clone is safe:
|
|
129
|
+
// workflow definitions are pure data, and helper proxies like
|
|
130
|
+
// `$.req.body` serialize to their `js/...` string form via
|
|
131
|
+
// `Symbol.toPrimitive` / `toJSON`.
|
|
160
132
|
const { normalizeWorkflow } = await import("./workflow/WorkflowNormalizer");
|
|
161
|
-
|
|
133
|
+
const fresh = JSON.parse(JSON.stringify(preloaded));
|
|
134
|
+
this.workflow = normalizeWorkflow(fresh, workflowNameInPath);
|
|
162
135
|
}
|
|
163
136
|
else {
|
|
164
137
|
const resolver = new ConfigurationResolver(opts);
|
|
@@ -173,6 +146,11 @@ export default class Configuration {
|
|
|
173
146
|
this.version = this.workflow.version;
|
|
174
147
|
this.name = this.workflow.name;
|
|
175
148
|
this.trigger = this.workflow.trigger;
|
|
149
|
+
// Workflow-level middleware list (v0.5.2). Lives on the normalized
|
|
150
|
+
// workflow as `appliedMiddleware` — see WorkflowNormalizer for the
|
|
151
|
+
// schema overload (`middleware: string[]` at the top level).
|
|
152
|
+
const wfWithApplied = this.workflow;
|
|
153
|
+
this.appliedMiddleware = Array.isArray(wfWithApplied.appliedMiddleware) ? wfWithApplied.appliedMiddleware : [];
|
|
176
154
|
}
|
|
177
155
|
async getSteps(blueprint_steps) {
|
|
178
156
|
const nodes = [];
|
|
@@ -191,13 +169,6 @@ export default class Configuration {
|
|
|
191
169
|
node.name = step.name;
|
|
192
170
|
node.active = step.active !== undefined ? step.active : true;
|
|
193
171
|
node.stop = step.stop !== undefined ? step.stop : false;
|
|
194
|
-
// Pass `set_var` through verbatim — DO NOT default to `false`. The
|
|
195
|
-
// `false` value short-circuits PersistenceHelper.applyStepOutput
|
|
196
|
-
// and silently disables v2's default-store rule. Legacy v1
|
|
197
|
-
// workflows that explicitly set `set_var: false` are normalized
|
|
198
|
-
// to `ephemeral: true` upstream by WorkflowNormalizer.
|
|
199
|
-
if (step.set_var !== undefined)
|
|
200
|
-
node.set_var = step.set_var;
|
|
201
172
|
// V2 persistence knobs — read by PersistenceHelper.applyStepOutput.
|
|
202
173
|
// `as` renames the state key; `spread` flattens result.data into
|
|
203
174
|
// state; `ephemeral: true` skips persistence entirely. Default
|
|
@@ -205,6 +176,27 @@ export default class Configuration {
|
|
|
205
176
|
node.as = step.as;
|
|
206
177
|
node.spread = step.spread === true;
|
|
207
178
|
node.ephemeral = step.ephemeral === true;
|
|
179
|
+
// V2 idempotency cache + retry knobs — read by RunnerSteps before
|
|
180
|
+
// delegating to step.process(). Caching layers ABOVE
|
|
181
|
+
// PersistenceHelper; retry wraps the same call site.
|
|
182
|
+
const v2Idem = step;
|
|
183
|
+
if (v2Idem.idempotencyKey !== undefined)
|
|
184
|
+
node.idempotencyKey = v2Idem.idempotencyKey;
|
|
185
|
+
if (v2Idem.idempotencyKeyTTL !== undefined)
|
|
186
|
+
node.idempotencyKeyTTL = v2Idem.idempotencyKeyTTL;
|
|
187
|
+
if (v2Idem.retry !== undefined)
|
|
188
|
+
node.retry = v2Idem.retry;
|
|
189
|
+
// V2 sub-workflow knobs — read by SubworkflowNode at run time.
|
|
190
|
+
if (v2Idem.subworkflow !== undefined)
|
|
191
|
+
node.subworkflow = v2Idem.subworkflow;
|
|
192
|
+
if (v2Idem.wait !== undefined)
|
|
193
|
+
node.wait = v2Idem.wait;
|
|
194
|
+
// Tier 2 quick-wins — parse maxDuration string/number → ms.
|
|
195
|
+
if (v2Idem.maxDuration !== undefined) {
|
|
196
|
+
const parsed = tryParseDuration(v2Idem.maxDuration);
|
|
197
|
+
if (parsed !== null)
|
|
198
|
+
node.maxDurationMs = parsed;
|
|
199
|
+
}
|
|
208
200
|
nodes.push(node);
|
|
209
201
|
}
|
|
210
202
|
return nodes;
|
|
@@ -222,10 +214,17 @@ export default class Configuration {
|
|
|
222
214
|
const hasOutputs = currentNode.mapper !== undefined;
|
|
223
215
|
if (isFlowWithProperties) {
|
|
224
216
|
const steps = currentNode.steps;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
217
|
+
const flow = await this.getFlow(steps);
|
|
218
|
+
// Spread the metadata FIRST, then the resolved flow — this
|
|
219
|
+
// keeps the resolved NodeBase[] in `flow.steps` and lets
|
|
220
|
+
// the metadata (e.g. forEach's in/as/mode/concurrency,
|
|
221
|
+
// loop's while/maxIterations) survive on the merged config.
|
|
222
|
+
// The earlier code spread metadata AFTER flow with a
|
|
223
|
+
// `copyBlueprintNode.steps = []` reset, which clobbered
|
|
224
|
+
// the resolved steps array — broken for any node config
|
|
225
|
+
// that needed both inner steps AND sibling fields.
|
|
226
|
+
const { steps: _drop, ...metadata } = workflow_nodes[key];
|
|
227
|
+
nodes[key] = { ...metadata, ...flow };
|
|
229
228
|
}
|
|
230
229
|
else if (isFlow) {
|
|
231
230
|
const steps = currentNode.steps;
|
|
@@ -251,6 +250,47 @@ export default class Configuration {
|
|
|
251
250
|
catch: await this.getFlow(currentNode.catch.steps),
|
|
252
251
|
};
|
|
253
252
|
}
|
|
253
|
+
else if (typeof workflow_nodes[key] === "object" &&
|
|
254
|
+
Array.isArray(currentNode.try) &&
|
|
255
|
+
Array.isArray(currentNode.catch)) {
|
|
256
|
+
// v0.5 · tryCatch step. `try`, `catch`, and optional `finally`
|
|
257
|
+
// each carry their own inner-step array (set by
|
|
258
|
+
// `normalizeTryCatchStep`). Resolve each block as its own Flow
|
|
259
|
+
// so TryCatchNode.run() can dispatch them through child Runners.
|
|
260
|
+
const raw = workflow_nodes[key];
|
|
261
|
+
const merged = {
|
|
262
|
+
try: (await this.getFlow(raw.try)).steps,
|
|
263
|
+
catch: (await this.getFlow(raw.catch)).steps,
|
|
264
|
+
};
|
|
265
|
+
if (Array.isArray(raw.finally)) {
|
|
266
|
+
merged.finally = (await this.getFlow(raw.finally)).steps;
|
|
267
|
+
}
|
|
268
|
+
nodes[key] = merged;
|
|
269
|
+
}
|
|
270
|
+
else if (typeof workflow_nodes[key] === "object" &&
|
|
271
|
+
currentNode.cases !== undefined &&
|
|
272
|
+
Array.isArray(currentNode.cases)) {
|
|
273
|
+
// v0.5 · switch step. Each case carries its own inner-step
|
|
274
|
+
// list at `case.steps` (set by `normalizeSwitchStep`); resolve
|
|
275
|
+
// each independently via getFlow. Optional `default` is its
|
|
276
|
+
// own resolved Flow. The merged config preserves the `on`
|
|
277
|
+
// expression so the blueprint mapper can rewrite it before
|
|
278
|
+
// SwitchNode.run() reads ctx.config[name].on at run time.
|
|
279
|
+
const raw = workflow_nodes[key];
|
|
280
|
+
const rawCases = raw.cases;
|
|
281
|
+
const resolvedCases = await Promise.all(rawCases.map(async (c) => ({
|
|
282
|
+
when: c.when,
|
|
283
|
+
steps: (await this.getFlow(c.steps)).steps,
|
|
284
|
+
})));
|
|
285
|
+
const merged = {
|
|
286
|
+
on: raw.on,
|
|
287
|
+
cases: resolvedCases,
|
|
288
|
+
};
|
|
289
|
+
if (Array.isArray(raw.default)) {
|
|
290
|
+
merged.default = (await this.getFlow(raw.default)).steps;
|
|
291
|
+
}
|
|
292
|
+
nodes[key] = merged;
|
|
293
|
+
}
|
|
254
294
|
else {
|
|
255
295
|
nodes[key] = { ...workflow_nodes[key] };
|
|
256
296
|
}
|
|
@@ -276,13 +316,32 @@ export default class Configuration {
|
|
|
276
316
|
node.name = step.name;
|
|
277
317
|
node.active = step.active !== undefined ? step.active : true;
|
|
278
318
|
node.stop = step.stop !== undefined ? step.stop : false;
|
|
279
|
-
//
|
|
280
|
-
// `
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
if (
|
|
285
|
-
node.
|
|
319
|
+
// V2 persistence + idempotency + retry knobs flow through nested
|
|
320
|
+
// flow steps too. Without this, a `branch.then[0]` step with
|
|
321
|
+
// `idempotencyKey` set would NOT be cached on rerun. Mirrors the
|
|
322
|
+
// same trio Configuration.getSteps copies onto top-level steps.
|
|
323
|
+
const v2Flow = step;
|
|
324
|
+
if (v2Flow.as !== undefined)
|
|
325
|
+
node.as = v2Flow.as;
|
|
326
|
+
node.spread = v2Flow.spread === true;
|
|
327
|
+
node.ephemeral = v2Flow.ephemeral === true;
|
|
328
|
+
if (v2Flow.idempotencyKey !== undefined)
|
|
329
|
+
node.idempotencyKey = v2Flow.idempotencyKey;
|
|
330
|
+
if (v2Flow.idempotencyKeyTTL !== undefined)
|
|
331
|
+
node.idempotencyKeyTTL = v2Flow.idempotencyKeyTTL;
|
|
332
|
+
if (v2Flow.retry !== undefined)
|
|
333
|
+
node.retry = v2Flow.retry;
|
|
334
|
+
// V2 sub-workflow knobs — also flow through nested branches so a
|
|
335
|
+
// `branch.then[0]` step that invokes a sub-workflow works.
|
|
336
|
+
if (v2Flow.subworkflow !== undefined)
|
|
337
|
+
node.subworkflow = v2Flow.subworkflow;
|
|
338
|
+
if (v2Flow.wait !== undefined)
|
|
339
|
+
node.wait = v2Flow.wait;
|
|
340
|
+
if (v2Flow.maxDuration !== undefined) {
|
|
341
|
+
const parsed = tryParseDuration(v2Flow.maxDuration);
|
|
342
|
+
if (parsed !== null)
|
|
343
|
+
node.maxDurationMs = parsed;
|
|
344
|
+
}
|
|
286
345
|
// const validator = z.instanceof(NodeBase);
|
|
287
346
|
// validator.parse(node);
|
|
288
347
|
flows.steps.push(node);
|
|
@@ -325,6 +384,34 @@ export default class Configuration {
|
|
|
325
384
|
"runtime.ruby": {
|
|
326
385
|
resolver: async (node) => await this.runtimeResolver(node),
|
|
327
386
|
},
|
|
387
|
+
subworkflow: {
|
|
388
|
+
resolver: async (node) => await this.subworkflowResolver(node),
|
|
389
|
+
},
|
|
390
|
+
// PR 4 · `wait.for(duration)` / `wait.until(date)` step. Resolves
|
|
391
|
+
// to a stub node — RunnerSteps intercepts before step.process()
|
|
392
|
+
// runs, so the stub's `run()` should never fire in practice.
|
|
393
|
+
// Without this entry, getSteps() throws `Node type wait not found`
|
|
394
|
+
// at workflow load.
|
|
395
|
+
wait: {
|
|
396
|
+
resolver: async (node) => await this.waitResolver(node),
|
|
397
|
+
},
|
|
398
|
+
// v0.5 · `forEach({...})` step — iterate a collection running
|
|
399
|
+
// inner steps per item. Sequential or parallel-bounded.
|
|
400
|
+
forEach: {
|
|
401
|
+
resolver: async (node) => await this.forEachResolver(node),
|
|
402
|
+
},
|
|
403
|
+
// v0.5 · `loop({...})` step — while-loop with maxIterations cap.
|
|
404
|
+
loop: {
|
|
405
|
+
resolver: async (node) => await this.loopResolver(node),
|
|
406
|
+
},
|
|
407
|
+
// v0.5 · `switchOn({...})` step — N-way branch; first matching case wins.
|
|
408
|
+
switch: {
|
|
409
|
+
resolver: async (node) => await this.switchResolver(node),
|
|
410
|
+
},
|
|
411
|
+
// v0.5 · `tryCatch({...})` step — JS-like try/catch/finally semantics.
|
|
412
|
+
tryCatch: {
|
|
413
|
+
resolver: async (node) => await this.tryCatchResolver(node),
|
|
414
|
+
},
|
|
328
415
|
};
|
|
329
416
|
}
|
|
330
417
|
async runtimeResolver(node) {
|
|
@@ -354,21 +441,28 @@ export default class Configuration {
|
|
|
354
441
|
targetNode.runtime = runtimeKind;
|
|
355
442
|
targetNode.active = node.active !== undefined ? node.active : true;
|
|
356
443
|
targetNode.stop = node.stop !== undefined ? node.stop : false;
|
|
357
|
-
// Pass `set_var` through verbatim — DO NOT default to `false`. The v2
|
|
358
|
-
// default-store rule in PersistenceHelper persists `result.data` at
|
|
359
|
-
// `state[name]` unless `set_var === false` is explicit. Defaulting to
|
|
360
|
-
// `false` here silently disabled persistence for every SDK step,
|
|
361
|
-
// breaking `js/ctx.state['<id>']` reads in v2 workflows
|
|
362
|
-
// (cross-runtime-chain regressed: `state['go']` was undefined even
|
|
363
|
-
// though the GO step ran fine).
|
|
364
|
-
if (node.set_var !== undefined)
|
|
365
|
-
targetNode.set_var = node.set_var;
|
|
366
444
|
// V2 persistence knobs — flow through to PersistenceHelper.
|
|
367
445
|
const v2 = node;
|
|
368
446
|
if (v2.as !== undefined)
|
|
369
447
|
targetNode.as = v2.as;
|
|
370
448
|
targetNode.spread = v2.spread === true;
|
|
371
449
|
targetNode.ephemeral = v2.ephemeral === true;
|
|
450
|
+
// V2 idempotency cache + retry knobs — copied here so the targetNode
|
|
451
|
+
// surfaces them for any future code that inspects the inner SDK node
|
|
452
|
+
// directly. The OUTER RuntimeAdapterNode also carries them via
|
|
453
|
+
// getSteps/getFlow so RunnerSteps' cache-check + retry-loop wrapper
|
|
454
|
+
// works regardless of which side it reads.
|
|
455
|
+
if (v2.idempotencyKey !== undefined)
|
|
456
|
+
targetNode.idempotencyKey = v2.idempotencyKey;
|
|
457
|
+
if (v2.idempotencyKeyTTL !== undefined)
|
|
458
|
+
targetNode.idempotencyKeyTTL = v2.idempotencyKeyTTL;
|
|
459
|
+
if (v2.retry !== undefined)
|
|
460
|
+
targetNode.retry = v2.retry;
|
|
461
|
+
if (v2.maxDuration !== undefined) {
|
|
462
|
+
const parsed = tryParseDuration(v2.maxDuration);
|
|
463
|
+
if (parsed !== null)
|
|
464
|
+
targetNode.maxDurationMs = parsed;
|
|
465
|
+
}
|
|
372
466
|
// Wrap in RuntimeAdapterNode to integrate with existing Runner.
|
|
373
467
|
// Per-step `stream_logs: true|false` overrides the global
|
|
374
468
|
// `BLOK_STREAM_LOGS` env flag (master plan §17 Phase 5 follow-up).
|
|
@@ -379,11 +473,70 @@ export default class Configuration {
|
|
|
379
473
|
const streamLogs = stepStreamLogs !== undefined ? stepStreamLogs : isStreamLogsEnabled();
|
|
380
474
|
return new RuntimeAdapterNode(adapter, targetNode, { streamLogs });
|
|
381
475
|
}
|
|
476
|
+
/**
|
|
477
|
+
* Resolve a `subworkflow` step into a fully-wired `SubworkflowNode` —
|
|
478
|
+
* the dispatch class that looks up the named child workflow in the
|
|
479
|
+
* `WorkflowRegistry` and runs it inline with isolated state.
|
|
480
|
+
*
|
|
481
|
+
* The returned node carries the parent's `globalOptions` so the
|
|
482
|
+
* child `Configuration.init()` can resolve `module` step references
|
|
483
|
+
* against the same node registry.
|
|
484
|
+
*/
|
|
485
|
+
async subworkflowResolver(node) {
|
|
486
|
+
const v2 = node;
|
|
487
|
+
if (typeof v2.subworkflow !== "string" || v2.subworkflow.length === 0) {
|
|
488
|
+
throw new Error(`[blok] subworkflowResolver: step "${node.name}" is missing the \`subworkflow\` field after normalization.`);
|
|
489
|
+
}
|
|
490
|
+
// Lazy import to avoid a circular dep (SubworkflowNode imports
|
|
491
|
+
// Configuration to construct the child).
|
|
492
|
+
const { SubworkflowNode } = await import("./SubworkflowNode");
|
|
493
|
+
const subworkflowNode = new SubworkflowNode();
|
|
494
|
+
subworkflowNode.node = node.node;
|
|
495
|
+
subworkflowNode.name = node.name;
|
|
496
|
+
subworkflowNode.type = node.type;
|
|
497
|
+
subworkflowNode.active = node.active !== undefined ? node.active : true;
|
|
498
|
+
subworkflowNode.stop = node.stop !== undefined ? node.stop : false;
|
|
499
|
+
subworkflowNode.subworkflow = v2.subworkflow;
|
|
500
|
+
// `wait: false` triggers the fire-and-forget branch in SubworkflowNode.run.
|
|
501
|
+
// Default to `true` (synchronous) when unset.
|
|
502
|
+
subworkflowNode.wait = v2.wait !== false;
|
|
503
|
+
// v0.7 PR 4 — polymorphic sub-workflow dispatch carries the
|
|
504
|
+
// parent workflow's `trigger.webhook.namespace` so a resolved
|
|
505
|
+
// event-type name (e.g. `"invoice.paid"`) gets prefixed into a
|
|
506
|
+
// full registry name (e.g. `"stripe.invoice.paid"`). Static
|
|
507
|
+
// names are unaffected.
|
|
508
|
+
const triggerCfg = this.workflow?.trigger;
|
|
509
|
+
if (typeof triggerCfg?.webhook?.namespace === "string" && triggerCfg.webhook.namespace.length > 0) {
|
|
510
|
+
subworkflowNode.namespace = triggerCfg.webhook.namespace;
|
|
511
|
+
}
|
|
512
|
+
// G3 polymorphic dispatch — pass the per-step allowList (cleaned to
|
|
513
|
+
// non-empty strings by `normalizeSubworkflowStep`) onto the node so
|
|
514
|
+
// `resolveSubworkflowName` can reject unauthorized lookups at
|
|
515
|
+
// dispatch time without re-walking the workflow shape.
|
|
516
|
+
const allowListSource = node.allowList;
|
|
517
|
+
if (Array.isArray(allowListSource) && allowListSource.length > 0) {
|
|
518
|
+
subworkflowNode.allowList = Object.freeze(allowListSource.filter((s) => typeof s === "string" && s.length > 0));
|
|
519
|
+
}
|
|
520
|
+
// G2 (v0.6) — dispatch strategy. `in-process` (default) preserves
|
|
521
|
+
// the v0.5 behaviour; `http-self` routes the child via a fresh
|
|
522
|
+
// HTTP request to the deployment's own base URL so multi-process
|
|
523
|
+
// deployments can isolate child execution from the parent.
|
|
524
|
+
const dispatchRaw = node.dispatch;
|
|
525
|
+
if (dispatchRaw === "http-self" || dispatchRaw === "in-process") {
|
|
526
|
+
subworkflowNode.dispatch = dispatchRaw;
|
|
527
|
+
}
|
|
528
|
+
// `globalOptions` is the runner's node registry — child Configuration.init
|
|
529
|
+
// needs it for `module:` step resolution.
|
|
530
|
+
subworkflowNode.globalOptions = this.globalOptions;
|
|
531
|
+
return subworkflowNode;
|
|
532
|
+
}
|
|
382
533
|
async moduleResolver(node, opts) {
|
|
383
534
|
const nodeHandler = opts?.nodes?.getNode(node.node);
|
|
384
535
|
if (!nodeHandler) {
|
|
385
536
|
throw new Error(`Node ${node.node} not found`);
|
|
386
537
|
}
|
|
538
|
+
// Validate runtime requirements if the node declares them
|
|
539
|
+
this.validateNodeRuntimeRequirements(nodeHandler);
|
|
387
540
|
const clone = Object.assign(Object.create(Object.getPrototypeOf(nodeHandler)), nodeHandler);
|
|
388
541
|
// Copy step-level metadata from the workflow JSON onto the clone.
|
|
389
542
|
// Without this, `step.type` is undefined for module nodes and
|
|
@@ -398,13 +551,130 @@ export default class Configuration {
|
|
|
398
551
|
clone.active = node.active;
|
|
399
552
|
if (node.stop !== undefined)
|
|
400
553
|
clone.stop = node.stop;
|
|
401
|
-
if (node.set_var !== undefined)
|
|
402
|
-
clone.set_var = node.set_var;
|
|
403
554
|
return clone;
|
|
404
555
|
}
|
|
556
|
+
/**
|
|
557
|
+
* PR 4 · resolve a `wait` step to a stub node. The runner's wait
|
|
558
|
+
* primitive (`wait.for`/`wait.until`) is implemented at the
|
|
559
|
+
* RunnerSteps level — this resolver exists only to satisfy
|
|
560
|
+
* getSteps() at workflow load time so `Node type wait not found`
|
|
561
|
+
* doesn't fire on otherwise-valid wait steps.
|
|
562
|
+
*/
|
|
563
|
+
async waitResolver(node) {
|
|
564
|
+
const { WaitNode } = await import("./WaitNode");
|
|
565
|
+
const stub = new WaitNode();
|
|
566
|
+
stub.node = node.node;
|
|
567
|
+
stub.name = node.name;
|
|
568
|
+
stub.type = node.type;
|
|
569
|
+
stub.active = node.active !== undefined ? node.active : true;
|
|
570
|
+
stub.stop = node.stop !== undefined ? node.stop : false;
|
|
571
|
+
const v2 = node;
|
|
572
|
+
if (v2.waitForMs !== undefined)
|
|
573
|
+
stub.waitForMs = v2.waitForMs;
|
|
574
|
+
if (v2.waitUntil !== undefined)
|
|
575
|
+
stub.waitUntil = v2.waitUntil;
|
|
576
|
+
return stub;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* v0.5 · resolve a `forEach` step. The actual iteration logic lives
|
|
580
|
+
* in `ForEachNode.run()`; the inner `steps` array is pre-resolved by
|
|
581
|
+
* the existing isFlowWithProperties path in `getNodes()`.
|
|
582
|
+
*/
|
|
583
|
+
async forEachResolver(node) {
|
|
584
|
+
const { ForEachNode } = await import("./ForEachNode");
|
|
585
|
+
const n = new ForEachNode();
|
|
586
|
+
n.node = node.node;
|
|
587
|
+
n.name = node.name;
|
|
588
|
+
n.type = node.type;
|
|
589
|
+
n.active = node.active !== undefined ? node.active : true;
|
|
590
|
+
n.stop = node.stop !== undefined ? node.stop : false;
|
|
591
|
+
return n;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* v0.5 · resolve a `loop` step. While-loop semantics live in
|
|
595
|
+
* `LoopNode.run()`. Inner `steps` resolved by isFlowWithProperties.
|
|
596
|
+
*/
|
|
597
|
+
async loopResolver(node) {
|
|
598
|
+
const { LoopNode } = await import("./LoopNode");
|
|
599
|
+
const n = new LoopNode();
|
|
600
|
+
n.node = node.node;
|
|
601
|
+
n.name = node.name;
|
|
602
|
+
n.type = node.type;
|
|
603
|
+
n.active = node.active !== undefined ? node.active : true;
|
|
604
|
+
n.stop = node.stop !== undefined ? node.stop : false;
|
|
605
|
+
return n;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* v0.5 · resolve a `switch` step. The N-way match logic lives in
|
|
609
|
+
* `SwitchNode.run()`. Cases + default each carry their own resolved
|
|
610
|
+
* inner-step list — see the dedicated `cases` branch in `getNodes()`.
|
|
611
|
+
*/
|
|
612
|
+
async switchResolver(node) {
|
|
613
|
+
const { SwitchNode } = await import("./SwitchNode");
|
|
614
|
+
const n = new SwitchNode();
|
|
615
|
+
n.node = node.node;
|
|
616
|
+
n.name = node.name;
|
|
617
|
+
n.type = node.type;
|
|
618
|
+
n.active = node.active !== undefined ? node.active : true;
|
|
619
|
+
n.stop = node.stop !== undefined ? node.stop : false;
|
|
620
|
+
return n;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* v0.5 · resolve a `tryCatch` step. JS-like try/catch/finally semantics
|
|
624
|
+
* live in `TryCatchNode.run()`. Each block (try, catch, finally) is
|
|
625
|
+
* pre-resolved by the dedicated tryCatch branch in `getNodes()` so
|
|
626
|
+
* the runtime can dispatch them through child Runners on-demand.
|
|
627
|
+
*/
|
|
628
|
+
async tryCatchResolver(node) {
|
|
629
|
+
const { TryCatchNode } = await import("./TryCatchNode");
|
|
630
|
+
const n = new TryCatchNode();
|
|
631
|
+
n.node = node.node;
|
|
632
|
+
n.name = node.name;
|
|
633
|
+
n.type = node.type;
|
|
634
|
+
n.active = node.active !== undefined ? node.active : true;
|
|
635
|
+
n.stop = node.stop !== undefined ? node.stop : false;
|
|
636
|
+
return n;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Check if a resolved node has runtimeRequirements and validate them
|
|
640
|
+
* against the currently known runtime versions in the RuntimeRegistry.
|
|
641
|
+
*/
|
|
642
|
+
validateNodeRuntimeRequirements(node) {
|
|
643
|
+
const fnNode = node;
|
|
644
|
+
if (!fnNode.runtimeRequirements)
|
|
645
|
+
return;
|
|
646
|
+
const registry = RuntimeRegistry.getInstance();
|
|
647
|
+
const runtimeVersions = {};
|
|
648
|
+
for (const kind of registry.getRegisteredKinds()) {
|
|
649
|
+
const version = registry.getVersion(kind);
|
|
650
|
+
if (version) {
|
|
651
|
+
runtimeVersions[kind] = version;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
// If no runtime versions are known yet, skip validation
|
|
655
|
+
// (versions are populated when health checks succeed)
|
|
656
|
+
if (Object.keys(runtimeVersions).length === 0)
|
|
657
|
+
return;
|
|
658
|
+
const validator = new RuntimeVersionValidator(runtimeVersions);
|
|
659
|
+
const results = validator.validateNode({
|
|
660
|
+
name: fnNode.name || "unknown",
|
|
661
|
+
runtimeRequirements: fnNode.runtimeRequirements,
|
|
662
|
+
});
|
|
663
|
+
const failures = results.filter((r) => !r.valid);
|
|
664
|
+
if (failures.length > 0) {
|
|
665
|
+
throw new Error(RuntimeVersionValidator.formatErrors(failures));
|
|
666
|
+
}
|
|
667
|
+
}
|
|
405
668
|
async localResolver(node) {
|
|
406
|
-
|
|
407
|
-
|
|
669
|
+
// Security review FW-3 — canonicalize the resolved path against
|
|
670
|
+
// NODES_PATH so a node.node value like "../../malicious" can't
|
|
671
|
+
// walk the filesystem outside the configured directory.
|
|
672
|
+
const base = resolvePath(process.env.NODES_PATH || ".");
|
|
673
|
+
const target = resolvePath(base, node.node);
|
|
674
|
+
if (target !== base && !target.startsWith(base + pathSep)) {
|
|
675
|
+
throw new Error(`[blok] local node path escapes NODES_PATH: '${node.node}' resolves outside ${base}`);
|
|
676
|
+
}
|
|
677
|
+
return new (await import(target)).default();
|
|
408
678
|
}
|
|
409
679
|
}
|
|
410
680
|
//# sourceMappingURL=Configuration.js.map
|