@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.
Files changed (213) hide show
  1. package/dist/Blok.js +32 -3
  2. package/dist/Blok.js.map +1 -1
  3. package/dist/Configuration.d.ts +59 -5
  4. package/dist/Configuration.js +366 -96
  5. package/dist/Configuration.js.map +1 -1
  6. package/dist/ForEachNode.d.ts +59 -0
  7. package/dist/ForEachNode.js +522 -0
  8. package/dist/ForEachNode.js.map +1 -0
  9. package/dist/LoopMaxIterationsError.d.ts +11 -0
  10. package/dist/LoopMaxIterationsError.js +18 -0
  11. package/dist/LoopMaxIterationsError.js.map +1 -0
  12. package/dist/LoopNode.d.ts +36 -0
  13. package/dist/LoopNode.js +182 -0
  14. package/dist/LoopNode.js.map +1 -0
  15. package/dist/PayloadTooLargeError.d.ts +19 -0
  16. package/dist/PayloadTooLargeError.js +29 -0
  17. package/dist/PayloadTooLargeError.js.map +1 -0
  18. package/dist/RunCancelledError.d.ts +17 -0
  19. package/dist/RunCancelledError.js +25 -0
  20. package/dist/RunCancelledError.js.map +1 -0
  21. package/dist/Runner.d.ts +11 -1
  22. package/dist/Runner.js +9 -2
  23. package/dist/Runner.js.map +1 -1
  24. package/dist/RunnerSteps.js +648 -44
  25. package/dist/RunnerSteps.js.map +1 -1
  26. package/dist/RuntimeAdapterNode.d.ts +2 -1
  27. package/dist/RuntimeAdapterNode.js +2 -2
  28. package/dist/RuntimeAdapterNode.js.map +1 -1
  29. package/dist/RuntimeRegistry.d.ts +23 -2
  30. package/dist/RuntimeRegistry.js +31 -2
  31. package/dist/RuntimeRegistry.js.map +1 -1
  32. package/dist/SubworkflowNode.d.ts +181 -0
  33. package/dist/SubworkflowNode.js +479 -0
  34. package/dist/SubworkflowNode.js.map +1 -0
  35. package/dist/SwitchNode.d.ts +37 -0
  36. package/dist/SwitchNode.js +153 -0
  37. package/dist/SwitchNode.js.map +1 -0
  38. package/dist/TriggerBase.d.ts +178 -0
  39. package/dist/TriggerBase.js +1032 -5
  40. package/dist/TriggerBase.js.map +1 -1
  41. package/dist/TryCatchNode.d.ts +32 -0
  42. package/dist/TryCatchNode.js +207 -0
  43. package/dist/TryCatchNode.js.map +1 -0
  44. package/dist/WaitDispatchRequest.d.ts +38 -0
  45. package/dist/WaitDispatchRequest.js +13 -0
  46. package/dist/WaitDispatchRequest.js.map +1 -0
  47. package/dist/WaitNode.d.ts +23 -0
  48. package/dist/WaitNode.js +26 -0
  49. package/dist/WaitNode.js.map +1 -0
  50. package/dist/adapters/grpc/GrpcCodec.js +2 -2
  51. package/dist/adapters/grpc/GrpcRuntimeAdapter.d.ts +6 -4
  52. package/dist/adapters/grpc/GrpcRuntimeAdapter.js +6 -4
  53. package/dist/adapters/grpc/GrpcRuntimeAdapter.js.map +1 -1
  54. package/dist/adapters/grpc/types.d.ts +7 -5
  55. package/dist/adapters/grpc/types.js.map +1 -1
  56. package/dist/adapters/transport.d.ts +12 -41
  57. package/dist/adapters/transport.js +21 -70
  58. package/dist/adapters/transport.js.map +1 -1
  59. package/dist/cache/NodeResultCache.js +7 -0
  60. package/dist/cache/NodeResultCache.js.map +1 -1
  61. package/dist/concurrency/ConcurrencyBackend.d.ts +61 -0
  62. package/dist/concurrency/ConcurrencyBackend.js +20 -0
  63. package/dist/concurrency/ConcurrencyBackend.js.map +1 -0
  64. package/dist/concurrency/ConcurrencyLimitError.d.ts +37 -0
  65. package/dist/concurrency/ConcurrencyLimitError.js +16 -0
  66. package/dist/concurrency/ConcurrencyLimitError.js.map +1 -0
  67. package/dist/concurrency/NatsKvConcurrencyBackend.d.ts +64 -0
  68. package/dist/concurrency/NatsKvConcurrencyBackend.js +310 -0
  69. package/dist/concurrency/NatsKvConcurrencyBackend.js.map +1 -0
  70. package/dist/concurrency/QueueExpiredError.d.ts +40 -0
  71. package/dist/concurrency/QueueExpiredError.js +15 -0
  72. package/dist/concurrency/QueueExpiredError.js.map +1 -0
  73. package/dist/concurrency/RedisConcurrencyBackend.d.ts +64 -0
  74. package/dist/concurrency/RedisConcurrencyBackend.js +374 -0
  75. package/dist/concurrency/RedisConcurrencyBackend.js.map +1 -0
  76. package/dist/concurrency/createConcurrencyBackend.d.ts +24 -0
  77. package/dist/concurrency/createConcurrencyBackend.js +38 -0
  78. package/dist/concurrency/createConcurrencyBackend.js.map +1 -0
  79. package/dist/concurrency/readConcurrencyConfig.d.ts +60 -0
  80. package/dist/concurrency/readConcurrencyConfig.js +60 -0
  81. package/dist/concurrency/readConcurrencyConfig.js.map +1 -0
  82. package/dist/defineNode.d.ts +8 -0
  83. package/dist/defineNode.js +25 -5
  84. package/dist/defineNode.js.map +1 -1
  85. package/dist/graphql/GraphQLSchemaGenerator.js +1 -1
  86. package/dist/graphql/GraphQLSchemaGenerator.js.map +1 -1
  87. package/dist/idempotency/resolveIdempotencyKey.d.ts +20 -0
  88. package/dist/idempotency/resolveIdempotencyKey.js +37 -0
  89. package/dist/idempotency/resolveIdempotencyKey.js.map +1 -0
  90. package/dist/index.d.ts +30 -6
  91. package/dist/index.js +55 -6
  92. package/dist/index.js.map +1 -1
  93. package/dist/marketplace/RuntimeCatalog.d.ts +6 -0
  94. package/dist/marketplace/RuntimeCatalog.js.map +1 -1
  95. package/dist/marketplace/RuntimeDiscovery.d.ts +2 -2
  96. package/dist/marketplace/RuntimeDiscovery.js +18 -6
  97. package/dist/marketplace/RuntimeDiscovery.js.map +1 -1
  98. package/dist/monitoring/ConcurrencyMetrics.d.ts +82 -0
  99. package/dist/monitoring/ConcurrencyMetrics.js +139 -0
  100. package/dist/monitoring/ConcurrencyMetrics.js.map +1 -0
  101. package/dist/monitoring/ForEachWaitMetrics.d.ts +22 -0
  102. package/dist/monitoring/ForEachWaitMetrics.js +36 -0
  103. package/dist/monitoring/ForEachWaitMetrics.js.map +1 -0
  104. package/dist/monitoring/JanitorMetrics.d.ts +27 -0
  105. package/dist/monitoring/JanitorMetrics.js +48 -0
  106. package/dist/monitoring/JanitorMetrics.js.map +1 -0
  107. package/dist/openapi/OpenAPIGenerator.js +7 -2
  108. package/dist/openapi/OpenAPIGenerator.js.map +1 -1
  109. package/dist/runtime/PrimitiveStack.d.ts +64 -0
  110. package/dist/runtime/PrimitiveStack.js +92 -0
  111. package/dist/runtime/PrimitiveStack.js.map +1 -0
  112. package/dist/scheduling/DebounceBackend.d.ts +108 -0
  113. package/dist/scheduling/DebounceBackend.js +23 -0
  114. package/dist/scheduling/DebounceBackend.js.map +1 -0
  115. package/dist/scheduling/DebounceCoordinator.d.ts +141 -0
  116. package/dist/scheduling/DebounceCoordinator.js +362 -0
  117. package/dist/scheduling/DebounceCoordinator.js.map +1 -0
  118. package/dist/scheduling/DeferredDispatchSignal.d.ts +50 -0
  119. package/dist/scheduling/DeferredDispatchSignal.js +14 -0
  120. package/dist/scheduling/DeferredDispatchSignal.js.map +1 -0
  121. package/dist/scheduling/DeferredRunScheduler.d.ts +96 -0
  122. package/dist/scheduling/DeferredRunScheduler.js +256 -0
  123. package/dist/scheduling/DeferredRunScheduler.js.map +1 -0
  124. package/dist/scheduling/NatsKvDebounceBackend.d.ts +53 -0
  125. package/dist/scheduling/NatsKvDebounceBackend.js +334 -0
  126. package/dist/scheduling/NatsKvDebounceBackend.js.map +1 -0
  127. package/dist/scheduling/RedisDebounceBackend.d.ts +49 -0
  128. package/dist/scheduling/RedisDebounceBackend.js +356 -0
  129. package/dist/scheduling/RedisDebounceBackend.js.map +1 -0
  130. package/dist/scheduling/createDebounceBackend.d.ts +25 -0
  131. package/dist/scheduling/createDebounceBackend.js +39 -0
  132. package/dist/scheduling/createDebounceBackend.js.map +1 -0
  133. package/dist/scheduling/readSchedulingConfig.d.ts +24 -0
  134. package/dist/scheduling/readSchedulingConfig.js +52 -0
  135. package/dist/scheduling/readSchedulingConfig.js.map +1 -0
  136. package/dist/security/AuditLogger.js +1 -1
  137. package/dist/security/AuditLogger.js.map +1 -1
  138. package/dist/security/AuthMiddleware.d.ts +19 -20
  139. package/dist/security/AuthMiddleware.js +35 -20
  140. package/dist/security/AuthMiddleware.js.map +1 -1
  141. package/dist/security/OAuthProvider.js +2 -2
  142. package/dist/security/OAuthProvider.js.map +1 -1
  143. package/dist/security/SecretManager.js +14 -13
  144. package/dist/security/SecretManager.js.map +1 -1
  145. package/dist/security/index.d.ts +3 -1
  146. package/dist/security/index.js +3 -1
  147. package/dist/security/index.js.map +1 -1
  148. package/dist/testing/TestHarness.d.ts +27 -12
  149. package/dist/testing/TestHarness.js +19 -3
  150. package/dist/testing/TestHarness.js.map +1 -1
  151. package/dist/testing/WorkflowTestRunner.js +0 -7
  152. package/dist/testing/WorkflowTestRunner.js.map +1 -1
  153. package/dist/timeouts/StepTimeoutError.d.ts +22 -0
  154. package/dist/timeouts/StepTimeoutError.js +31 -0
  155. package/dist/timeouts/StepTimeoutError.js.map +1 -0
  156. package/dist/tracing/InMemoryRunStore.d.ts +41 -1
  157. package/dist/tracing/InMemoryRunStore.js +239 -0
  158. package/dist/tracing/InMemoryRunStore.js.map +1 -1
  159. package/dist/tracing/Janitor.d.ts +70 -0
  160. package/dist/tracing/Janitor.js +150 -0
  161. package/dist/tracing/Janitor.js.map +1 -0
  162. package/dist/tracing/PostgresRunStore.d.ts +57 -1
  163. package/dist/tracing/PostgresRunStore.js +711 -6
  164. package/dist/tracing/PostgresRunStore.js.map +1 -1
  165. package/dist/tracing/RoutingDiagnostics.d.ts +55 -0
  166. package/dist/tracing/RoutingDiagnostics.js +50 -0
  167. package/dist/tracing/RoutingDiagnostics.js.map +1 -0
  168. package/dist/tracing/RunStore.d.ts +181 -1
  169. package/dist/tracing/RunTracker.d.ts +244 -9
  170. package/dist/tracing/RunTracker.js +594 -1
  171. package/dist/tracing/RunTracker.js.map +1 -1
  172. package/dist/tracing/SqliteRunStore.d.ts +79 -2
  173. package/dist/tracing/SqliteRunStore.js +775 -16
  174. package/dist/tracing/SqliteRunStore.js.map +1 -1
  175. package/dist/tracing/TraceRouter.d.ts +20 -2
  176. package/dist/tracing/TraceRouter.js +612 -6
  177. package/dist/tracing/TraceRouter.js.map +1 -1
  178. package/dist/tracing/createStore.js +14 -3
  179. package/dist/tracing/createStore.js.map +1 -1
  180. package/dist/tracing/metadataFilter.d.ts +63 -0
  181. package/dist/tracing/metadataFilter.js +224 -0
  182. package/dist/tracing/metadataFilter.js.map +1 -0
  183. package/dist/tracing/sanitize.d.ts +11 -0
  184. package/dist/tracing/sanitize.js +29 -0
  185. package/dist/tracing/sanitize.js.map +1 -1
  186. package/dist/tracing/types.d.ts +672 -2
  187. package/dist/utils/createChildContext.d.ts +32 -0
  188. package/dist/utils/createChildContext.js +113 -0
  189. package/dist/utils/createChildContext.js.map +1 -0
  190. package/dist/utils/envAllowlist.d.ts +35 -0
  191. package/dist/utils/envAllowlist.js +113 -0
  192. package/dist/utils/envAllowlist.js.map +1 -0
  193. package/dist/version/RuntimeVersionValidator.d.ts +38 -0
  194. package/dist/version/RuntimeVersionValidator.js +121 -0
  195. package/dist/version/RuntimeVersionValidator.js.map +1 -0
  196. package/dist/visualization/WorkflowVisualizer.js +4 -4
  197. package/dist/visualization/WorkflowVisualizer.js.map +1 -1
  198. package/dist/workflow/PersistenceHelper.d.ts +18 -10
  199. package/dist/workflow/PersistenceHelper.js +35 -9
  200. package/dist/workflow/PersistenceHelper.js.map +1 -1
  201. package/dist/workflow/WorkflowNormalizer.d.ts +48 -42
  202. package/dist/workflow/WorkflowNormalizer.js +650 -18
  203. package/dist/workflow/WorkflowNormalizer.js.map +1 -1
  204. package/dist/workflow/WorkflowRegistry.d.ts +186 -0
  205. package/dist/workflow/WorkflowRegistry.js +202 -0
  206. package/dist/workflow/WorkflowRegistry.js.map +1 -0
  207. package/dist/workflow/sampleBody.d.ts +54 -0
  208. package/dist/workflow/sampleBody.js +320 -0
  209. package/dist/workflow/sampleBody.js.map +1 -0
  210. package/package.json +3 -8
  211. package/dist/adapters/HttpRuntimeAdapter.d.ts +0 -79
  212. package/dist/adapters/HttpRuntimeAdapter.js +0 -233
  213. package/dist/adapters/HttpRuntimeAdapter.js.map +0 -1
@@ -0,0 +1,479 @@
1
+ import Configuration from "./Configuration";
2
+ import RunnerNode from "./RunnerNode";
3
+ import { RunTracker } from "./tracing/RunTracker";
4
+ import { createChildContext } from "./utils/createChildContext";
5
+ import { applyStepOutput } from "./workflow/PersistenceHelper";
6
+ import { WorkflowRegistry } from "./workflow/WorkflowRegistry";
7
+ /**
8
+ * Hard cap on `parent → child → grandchild → …` recursion. Bounds the
9
+ * blast radius of an accidental cycle (workflow A calls B calls A) or
10
+ * a legitimate-but-pathological deep nesting. Tunable via
11
+ * `BLOK_MAX_SUBWORKFLOW_DEPTH` env var; falls back to 10.
12
+ */
13
+ function getMaxDepth() {
14
+ const raw = process.env.BLOK_MAX_SUBWORKFLOW_DEPTH;
15
+ if (typeof raw === "string" && raw.length > 0) {
16
+ const parsed = Number.parseInt(raw, 10);
17
+ if (Number.isInteger(parsed) && parsed > 0)
18
+ return parsed;
19
+ }
20
+ return 10;
21
+ }
22
+ /**
23
+ * G2 — resolve the deployment's self base URL for HTTP self-call
24
+ * dispatch. Reads `BLOK_SELF_BASE_URL` when set (recommended in
25
+ * containerized deployments where `localhost` is just the pod);
26
+ * otherwise defaults to `http://localhost:${PORT || 4000}` which
27
+ * works out-of-box for the dev orchestrator. Always trims a trailing
28
+ * slash so callers can `${base}${path}` without doubling.
29
+ *
30
+ * Exported for tests; production callers go through
31
+ * `SubworkflowNode.dispatchHttpSelf`.
32
+ */
33
+ export function getSelfBaseUrl() {
34
+ const fromEnv = process.env.BLOK_SELF_BASE_URL;
35
+ if (typeof fromEnv === "string" && fromEnv.length > 0) {
36
+ return fromEnv.endsWith("/") ? fromEnv.slice(0, -1) : fromEnv;
37
+ }
38
+ const port = process.env.PORT && process.env.PORT.length > 0 ? process.env.PORT : "4000";
39
+ return `http://localhost:${port}`;
40
+ }
41
+ /**
42
+ * Internal ctx field that carries the current sub-workflow depth.
43
+ * Incremented by `SubworkflowNode.run` before invoking the child;
44
+ * read on entry to enforce the cap.
45
+ */
46
+ const SUBWORKFLOW_DEPTH_KEY = "_subworkflowDepth";
47
+ /**
48
+ * `SubworkflowNode` — the runner-side dispatch primitive that powers
49
+ * the v2 `subworkflow:` step shape. Looks up the named child workflow
50
+ * in the `WorkflowRegistry`, materializes a child `Configuration` +
51
+ * `Runner`, runs the child to completion in its own isolated `Context`,
52
+ * and returns the child's `ctx.response` as this step's `model.data`.
53
+ *
54
+ * **Composition with Tier 1**:
55
+ * - Parent step's `idempotencyKey` is consulted by `RunnerSteps` BEFORE
56
+ * `SubworkflowNode.run` is even called — cache hit short-circuits the
57
+ * entire sub-workflow (no child invocation, no side effects fire).
58
+ * This is the headline pattern AND the documented footgun.
59
+ * - Parent step's `retry` retries the whole sub-workflow on failure;
60
+ * each retry creates a fresh child run record under the same parent.
61
+ * - Replay re-creates fresh sub-run lineage automatically — the new
62
+ * parent run invokes the sub-workflow fresh.
63
+ *
64
+ * **Lineage**: child's `WorkflowRun.parentRunId` and
65
+ * `WorkflowRun.parentNodeRunId` carry the parent run + step that
66
+ * invoked it. Studio renders a "called from #..." breadcrumb on the
67
+ * child and a "Sub-runs" list on the parent.
68
+ *
69
+ * **Recursion guard**: `BLOK_MAX_SUBWORKFLOW_DEPTH` (default 10) bounds
70
+ * cycle / deep-nesting blast radius. Throws a clear error past the cap.
71
+ */
72
+ export class SubworkflowNode extends RunnerNode {
73
+ /**
74
+ * v0.7 — optional namespace prefix prepended to the resolved
75
+ * polymorphic name. Used by the Webhook trigger:
76
+ * `namespace: "stripe"` + `subworkflow: "js/ctx.req.body.type"`
77
+ * resolving to `"invoice.paid"` looks up `"stripe.invoice.paid"`.
78
+ * Static names are unaffected.
79
+ */
80
+ namespace;
81
+ /**
82
+ * G3 (v0.5) — exact-match allow-list for the workflow name dispatched
83
+ * by this step. When set, the **final** resolved name (after any
84
+ * polymorphic `js/...` evaluation AND the `namespace` prefix) must be
85
+ * in this array; otherwise the dispatch is rejected at run time with
86
+ * a structured error. Strongly recommended when `subworkflow` is an
87
+ * expression so a malicious or buggy ctx value can't escalate the
88
+ * workflow surface accessible to a request.
89
+ *
90
+ * `undefined` preserves pre-G3 behaviour (no constraint). Authors
91
+ * with literal `subworkflow:` strings have no reason to set this —
92
+ * the registry lookup already gates dispatch on the literal name.
93
+ */
94
+ allowList;
95
+ /**
96
+ * G2 (v0.6) — dispatch strategy.
97
+ *
98
+ * - `"in-process"` (default): the child workflow runs in the SAME
99
+ * Node process — synchronous when `wait: true`, `setImmediate`-
100
+ * based when `wait: false`. Cheapest, no extra hops; the historic
101
+ * behaviour.
102
+ * - `"http-self"`: the child is dispatched as a fresh HTTP request
103
+ * to the deployment's own base URL (resolved from
104
+ * `BLOK_SELF_BASE_URL`, defaulting to `http://localhost:${PORT}`).
105
+ * Use when each child run should land on a different process in
106
+ * a horizontally-scaled deployment, or to fully isolate child
107
+ * execution from the parent's call stack. The child MUST have an
108
+ * HTTP trigger — a runtime error is thrown otherwise. Lineage
109
+ * (parentRunId / parentNodeRunId / depth) is preserved across the
110
+ * HTTP hop via `X-Blok-Parent-Run-Id` / `X-Blok-Parent-Node-Run-Id`
111
+ * / `X-Blok-Subworkflow-Depth` headers that the receiving
112
+ * HttpTrigger reads + threads into `tracker.startRun(...)`.
113
+ *
114
+ * Both modes integrate with `wait` and `idempotencyKey` identically
115
+ * (the cache lookup happens BEFORE `SubworkflowNode.run`).
116
+ */
117
+ dispatch;
118
+ /**
119
+ * Runner-wide options (carries the `nodes` registry that the child
120
+ * Configuration needs for `module` step resolution). Set by
121
+ * `Configuration.subworkflowResolver` before this node runs.
122
+ */
123
+ globalOptions;
124
+ async run(ctx) {
125
+ // === 1. Recursion guard ===
126
+ const depth = (ctx[SUBWORKFLOW_DEPTH_KEY] ?? 0) + 1;
127
+ const maxDepth = getMaxDepth();
128
+ if (depth > maxDepth) {
129
+ throw new Error(`[blok] Sub-workflow recursion limit exceeded (depth ${depth} > ${maxDepth}). Likely a cycle: workflow "${ctx.workflow_name}" called sub-workflow "${this.subworkflow}" too deep. Bump via BLOK_MAX_SUBWORKFLOW_DEPTH if intentional.`);
130
+ }
131
+ // === 2. Resolve the child workflow name (polymorphic v0.7) ===
132
+ // Static names ("send-receipt-email") are looked up directly.
133
+ // Mapper-expression names ("js/ctx.req.body.type", "$.req.body.type")
134
+ // are evaluated against the live ctx — the cleanest way to dispatch
135
+ // many handlers from one webhook / event router workflow without a
136
+ // big switch statement. Resolved names go through the same registry
137
+ // lookup as static names.
138
+ const resolvedName = await this.resolveSubworkflowName(ctx);
139
+ const registry = WorkflowRegistry.getInstance();
140
+ const entry = registry.get(resolvedName);
141
+ if (!entry) {
142
+ const known = registry.list().map((w) => w.name);
143
+ const knownStr = known.length > 0 ? known.join(", ") : "(none registered yet)";
144
+ throw new Error(`[blok] Sub-workflow "${resolvedName}" not found in WorkflowRegistry. Available: ${knownStr}. Workflows are registered automatically by the HTTP trigger at boot — make sure the child workflow file is in the scanned directory and has \`name: "${resolvedName}"\`.`);
145
+ }
146
+ // === 2.5. Registry-level authorization ===
147
+ // Default-allow when no hook is installed (preserves pre-v0.4.1
148
+ // behavior). Operators install a hook via
149
+ // WorkflowRegistry.getInstance().setAuthorizeFn(...) for
150
+ // multi-tenant access control. Throws on denial; the parent
151
+ // step's retry loop (if any) will retry — author should pin
152
+ // `retry: undefined` on sub-workflow steps where denial is
153
+ // permanent.
154
+ const allowed = await registry.authorize(ctx.workflow_name ?? "<unknown>", resolvedName, ctx);
155
+ if (!allowed) {
156
+ throw new Error(`[blok] Sub-workflow access denied: workflow "${ctx.workflow_name}" is not authorized to invoke "${resolvedName}". This denial came from the registry-level authorize hook (WorkflowRegistry.setAuthorizeFn). Adjust the hook to allow this composition, or remove the gate.`);
157
+ }
158
+ // === 2.6. G2 — HTTP self-call dispatch ===
159
+ // When `dispatch: "http-self"`, skip the in-process Configuration
160
+ // + Runner materialization entirely. The child workflow runs on
161
+ // the OTHER side of an HTTP request that goes through the
162
+ // deployment's own base URL, hitting whichever process picks it
163
+ // up. The receiving HttpTrigger registers the child run record;
164
+ // this side just makes the HTTP call.
165
+ if (this.dispatch === "http-self") {
166
+ return this.dispatchHttpSelf(ctx, entry, resolvedName, depth);
167
+ }
168
+ // === 3. Materialize child Configuration + Runner ===
169
+ // `preloaded` = entry.workflow skips the disk re-read; the
170
+ // normalizer still runs so v1→v2 conversion happens for legacy
171
+ // child workflows.
172
+ const childConfig = new Configuration();
173
+ await childConfig.init(entry.name, this.globalOptions, entry.workflow);
174
+ // Lazy import of Runner to avoid a circular dep
175
+ // (Configuration → RunnerNode → ... — Runner has its own chain).
176
+ const { default: Runner } = await import("./Runner");
177
+ const childRunner = new Runner(childConfig.steps);
178
+ // === 4. Build the child Context ===
179
+ // Parent step's resolved inputs (from blueprint mapper) live on
180
+ // `ctx.config[this.name].inputs` — the blueprint mapper has
181
+ // mutated the wrapper in place, so `js/...` and `$.<path>`
182
+ // expressions are now concrete values. These become the child's
183
+ // `request.body` so the child reads them via `$.req.body.<key>`
184
+ // exactly as if HTTP-triggered (function-call semantics).
185
+ const parentNodeConfig = ctx.config?.[this.name];
186
+ const parentInputs = parentNodeConfig?.inputs ?? {};
187
+ const childCtx = createChildContext(ctx, {
188
+ workflowName: entry.name,
189
+ workflowPath: entry.source,
190
+ body: parentInputs,
191
+ config: childConfig.nodes,
192
+ });
193
+ // Carry the depth counter forward so nested sub-workflows hit the cap.
194
+ childCtx[SUBWORKFLOW_DEPTH_KEY] = depth;
195
+ // === 5. Tracing — child gets its own run record + lineage ===
196
+ const tracker = RunTracker.getInstance();
197
+ const parentRunId = ctx._traceRunId;
198
+ const parentNodeRunId = ctx._traceNodeId;
199
+ const childTriggerSummary = `${ctx.workflow_name ?? "?"} → ${entry.name}`;
200
+ let childRunId;
201
+ if (tracker.active) {
202
+ const childRun = tracker.startRun({
203
+ workflowName: entry.name,
204
+ workflowPath: entry.source,
205
+ triggerType: "subworkflow",
206
+ triggerSummary: childTriggerSummary,
207
+ nodeCount: childConfig.steps.length,
208
+ parentRunId,
209
+ parentNodeRunId,
210
+ });
211
+ childRunId = childRun.id;
212
+ childCtx._traceRunId = childRun.id;
213
+ }
214
+ // === 6. Dispatch — sync or fire-and-forget based on `this.wait` ===
215
+ if (this.wait === false) {
216
+ return this.dispatchAsync(ctx, childRunner, childCtx, childRunId, entry.name);
217
+ }
218
+ // === 6a. Synchronous dispatch (wait: true / default) ===
219
+ try {
220
+ await childRunner.run(childCtx);
221
+ if (childRunId)
222
+ tracker.completeRun(childRunId, childCtx.response);
223
+ }
224
+ catch (err) {
225
+ if (childRunId)
226
+ tracker.failRun(childRunId, err);
227
+ throw err;
228
+ }
229
+ finally {
230
+ // PR 1 follow-up · A3 fix. Abort the listener-cleanup signal so
231
+ // the parent.signal listener (registered in createChildContext)
232
+ // auto-removes. Without this, listeners accumulate on long-lived
233
+ // parents that fire many sub-workflows.
234
+ const childPrivate = childCtx._PRIVATE_;
235
+ if (childPrivate?.listenerCleanup && !childPrivate.listenerCleanup.signal.aborted) {
236
+ childPrivate.listenerCleanup.abort();
237
+ }
238
+ }
239
+ // === 7. Apply parent persistence + return child's response ===
240
+ // Mirrors HTTP function-call semantics: parent reads child output
241
+ // at `$.state[<this.name>]`. Child author controls the shape via
242
+ // `@blokjs/respond` (or the last step's natural output).
243
+ //
244
+ // Persistence-helper call mirrors the RuntimeAdapterNode pattern
245
+ // (RuntimeAdapterNode.ts:100). The parent step's `as` / `spread`
246
+ // / `ephemeral` knobs apply identically here — sub-workflow
247
+ // output is just data, persistence rules are uniform.
248
+ const result = { success: !childCtx.response?.error, data: childCtx.response };
249
+ applyStepOutput(ctx, this, result);
250
+ return {
251
+ success: result.success,
252
+ data: childCtx.response,
253
+ error: childCtx.response?.error ?? null,
254
+ };
255
+ }
256
+ /**
257
+ * Fire-and-forget dispatch (Tier 2 #4 follow-up — `wait: false`).
258
+ *
259
+ * Schedules the child runner via `setImmediate` so the parent step
260
+ * can return immediately. Child errors are caught and routed to
261
+ * `tracker.failRun(childRunId, err)` — visible in Studio, NOT
262
+ * propagated to the parent step (which has already returned). Also
263
+ * logged via `console.error` for ops visibility.
264
+ *
265
+ * Parent step's output is the dispatch metadata `{runId,
266
+ * workflowName, scheduledAt}` — NOT the child's response (which
267
+ * doesn't exist yet). Caller polls `GET /__blok/runs/<runId>` for
268
+ * the actual outcome.
269
+ */
270
+ /**
271
+ * v0.7 PR 4 — resolve a (possibly polymorphic) sub-workflow name to
272
+ * the actual workflow name in the registry.
273
+ *
274
+ * - Static names ("send-receipt-email") pass through unchanged.
275
+ * - `js/...` expressions are evaluated against the live ctx —
276
+ * `subworkflow: "js/ctx.req.body.type"` becomes `"invoice.paid"`
277
+ * on a request with that body.
278
+ * - `$.<path>` / `${...}` strings go through the same Mapper code
279
+ * path as step inputs (the TS DSL compiles `$` to `"js/ctx..."`
280
+ * by the time the workflow hits Configuration; this branch
281
+ * handles authors who hand-wrote `$` in JSON).
282
+ * - When `this.namespace` is set (from the parent workflow's
283
+ * `trigger.webhook.namespace`), the resolved name is prefixed
284
+ * as `"<namespace>.<resolvedName>"` — only when polymorphic
285
+ * resolution fired AND the resolved name isn't already prefixed.
286
+ *
287
+ * Throws if the expression evaluates to anything other than a
288
+ * non-empty string — operators should see a clear "polymorphic
289
+ * subworkflow name resolved to <T>" error rather than a confusing
290
+ * "workflow not found" downstream.
291
+ */
292
+ async resolveSubworkflowName(ctx) {
293
+ const raw = this.subworkflow;
294
+ const isExpression = typeof raw === "string" && (raw.startsWith("js/") || raw.startsWith("$.") || raw.startsWith("${"));
295
+ let resolvedName;
296
+ if (!isExpression) {
297
+ resolvedName = raw;
298
+ }
299
+ else {
300
+ // Lazy import keeps the static dep graph between runner and
301
+ // shared minimal — most steps don't dispatch sub-workflows.
302
+ const { mapper } = await import("@blokjs/shared");
303
+ // Normalise `$.<path>` → `js/ctx.<path>` so Mapper.replaceString
304
+ // evaluates it. Authors who wrote `js/...` go straight through.
305
+ let expr = raw;
306
+ if (expr.startsWith("$.")) {
307
+ expr = `js/ctx.${expr.slice(2)}`;
308
+ }
309
+ else if (expr.startsWith("$")) {
310
+ expr = `js/${expr.slice(1)}`;
311
+ }
312
+ const resolved = mapper.replaceString(expr, ctx, {});
313
+ if (typeof resolved !== "string" || resolved.length === 0) {
314
+ throw new Error(`[blok] Polymorphic sub-workflow name "${raw}" resolved to ${JSON.stringify(resolved)} (expected a non-empty string). Check the expression and the runtime value of ctx.`);
315
+ }
316
+ resolvedName = resolved;
317
+ }
318
+ // Namespace prefix — applies to polymorphic resolutions when set on
319
+ // the parent workflow's trigger (today: webhook namespace). Static
320
+ // names skip prefixing so they stay routable by their literal name.
321
+ if (isExpression && this.namespace && this.namespace.length > 0 && !resolvedName.startsWith(`${this.namespace}.`)) {
322
+ resolvedName = `${this.namespace}.${resolvedName}`;
323
+ }
324
+ // G3 allow-list enforcement. Checked after namespace prefixing so
325
+ // the value an author writes in the list matches the registry name
326
+ // they intend to permit. Polymorphic dispatch without an allow-list
327
+ // is still allowed (matches pre-G3 behaviour); the schema's
328
+ // describe() recommends pairing them in production.
329
+ if (this.allowList && this.allowList.length > 0 && !this.allowList.includes(resolvedName)) {
330
+ throw new Error(`[blok] Sub-workflow dispatch blocked: resolved name "${resolvedName}" is not in the step's \`allowList\` [${this.allowList.map((n) => `"${n}"`).join(", ")}]. Add it to the list if the dispatch is intended, or audit the workflow whose ctx produced this name.`);
331
+ }
332
+ return resolvedName;
333
+ }
334
+ /**
335
+ * G2 (v0.6) — HTTP self-call dispatch.
336
+ *
337
+ * Replaces the in-process child Configuration / Runner / Context
338
+ * with an HTTP request to the deployment's own base URL. The
339
+ * receiving HttpTrigger materializes the child as if it were a
340
+ * fresh request — registers its own run record, runs the workflow,
341
+ * returns the response.
342
+ *
343
+ * - `wait: true` (default) — `fetch` is awaited. The HTTP response
344
+ * body becomes this step's `model.data`. A non-2xx response is
345
+ * treated as failure (mirrors the in-process error propagation).
346
+ * - `wait: false` — `fetch` is fired-and-forgotten. The promise's
347
+ * rejection is caught + logged. Parent step's output is
348
+ * `{runId: null, workflowName, scheduledAt}` — the child's runId
349
+ * isn't known on this side until the receiver actually creates
350
+ * the run record. Studio's Sub-runs strip surfaces the child
351
+ * once it lands.
352
+ *
353
+ * Lineage (parentRunId / parentNodeRunId / depth) crosses the HTTP
354
+ * boundary via headers that the receiving HttpTrigger reads + threads
355
+ * into `tracker.startRun(...)`. Same end-result as the in-process
356
+ * path: child's `WorkflowRun` carries the parent ids so Studio renders
357
+ * the breadcrumbs.
358
+ */
359
+ async dispatchHttpSelf(parentCtx, entry, resolvedName, depth) {
360
+ // === 1. Validate the child has an HTTP trigger ===
361
+ const childWorkflow = entry.workflow;
362
+ const httpTrigger = childWorkflow?.trigger?.http;
363
+ if (!httpTrigger || typeof httpTrigger.path !== "string" || httpTrigger.path.length === 0) {
364
+ throw new Error(`[blok] Sub-workflow dispatch failed: \`dispatch: "http-self"\` requires the child workflow "${resolvedName}" to have an HTTP trigger with an explicit \`trigger.http.path\`. Switch the step to \`dispatch: "in-process"\` (or omit the field) for non-HTTP children, or add an HTTP trigger to the child.`);
365
+ }
366
+ const method = (httpTrigger.method ?? "POST").toUpperCase();
367
+ const path = httpTrigger.path.startsWith("/") ? httpTrigger.path : `/${httpTrigger.path}`;
368
+ // === 2. Resolve the deployment's self base URL ===
369
+ const baseUrl = getSelfBaseUrl();
370
+ const url = `${baseUrl}${path}`;
371
+ // === 3. Build lineage headers ===
372
+ const parentRunId = parentCtx._traceRunId;
373
+ const parentNodeRunId = parentCtx._traceNodeId;
374
+ const headers = {
375
+ "content-type": "application/json",
376
+ // Receiving HttpTrigger reads these in `runWorkflowExecution`
377
+ // and threads them into `tracker.startRun({parentRunId, ...})`.
378
+ // Same shape as the existing `X-Blok-Replay-Of` plumbing.
379
+ "X-Blok-Subworkflow-Depth": String(depth),
380
+ };
381
+ if (parentRunId)
382
+ headers["X-Blok-Parent-Run-Id"] = parentRunId;
383
+ if (parentNodeRunId)
384
+ headers["X-Blok-Parent-Node-Run-Id"] = parentNodeRunId;
385
+ // === 4. Parent step's resolved inputs become the request body ===
386
+ const parentNodeConfig = parentCtx.config?.[this.name];
387
+ const parentInputs = parentNodeConfig?.inputs ?? {};
388
+ const body = JSON.stringify(parentInputs);
389
+ // === 5. Fire-and-forget (wait: false) ===
390
+ if (this.wait === false) {
391
+ const scheduledAt = Date.now();
392
+ fetch(url, { method, headers, body }).catch((err) => {
393
+ console.error(`[blok][subworkflow] http-self dispatch to ${url} failed (wait:false):`, err instanceof Error ? err.stack || err.message : err);
394
+ });
395
+ const dispatchData = {
396
+ runId: null, // unknown on this side — receiving trigger creates the record
397
+ workflowName: entry.name,
398
+ scheduledAt,
399
+ dispatch: "http-self",
400
+ url,
401
+ };
402
+ const result = { success: true, data: dispatchData };
403
+ applyStepOutput(parentCtx, this, result);
404
+ return {
405
+ success: true,
406
+ data: dispatchData,
407
+ error: null,
408
+ };
409
+ }
410
+ // === 6. Synchronous (wait: true) ===
411
+ let response;
412
+ try {
413
+ response = await fetch(url, { method, headers, body });
414
+ }
415
+ catch (err) {
416
+ // Network-level failure: connection refused, DNS, etc.
417
+ throw new Error(`[blok] Sub-workflow http-self dispatch to ${url} failed: ${err instanceof Error ? err.message : String(err)}. The deployment's self base URL is "${baseUrl}" (set via BLOK_SELF_BASE_URL or defaulted from PORT). Make sure the trigger is listening + the URL is reachable from THIS process.`);
418
+ }
419
+ const responseText = await response.text();
420
+ let responseBody;
421
+ try {
422
+ responseBody = responseText.length > 0 ? JSON.parse(responseText) : undefined;
423
+ }
424
+ catch {
425
+ responseBody = responseText;
426
+ }
427
+ if (!response.ok) {
428
+ throw new Error(`[blok] Sub-workflow http-self dispatch returned ${response.status} ${response.statusText} from ${url}. Body: ${typeof responseBody === "string" ? responseBody.slice(0, 500) : JSON.stringify(responseBody).slice(0, 500)}`);
429
+ }
430
+ const resp = { success: true, data: responseBody, error: null };
431
+ const result = { success: true, data: responseBody };
432
+ applyStepOutput(parentCtx, this, result);
433
+ return resp;
434
+ }
435
+ dispatchAsync(parentCtx, childRunner, childCtx, childRunId, childWorkflowName) {
436
+ const scheduledAt = Date.now();
437
+ const tracker = RunTracker.getInstance();
438
+ setImmediate(() => {
439
+ void (async () => {
440
+ try {
441
+ await childRunner.run(childCtx);
442
+ if (childRunId)
443
+ tracker.completeRun(childRunId, childCtx.response);
444
+ }
445
+ catch (err) {
446
+ if (childRunId) {
447
+ tracker.failRun(childRunId, err instanceof Error ? err : new Error(String(err)));
448
+ }
449
+ console.error(`[blok][subworkflow] async child '${childWorkflowName}' (run ${childRunId ?? "?"}) failed:`, err instanceof Error ? err.stack || err.message : err);
450
+ }
451
+ finally {
452
+ // PR 1 follow-up · A3 fix. Same listener-cleanup hook as the
453
+ // sync path so async sub-workflows also auto-remove the
454
+ // parent.signal listener on completion.
455
+ const childPrivate = childCtx._PRIVATE_;
456
+ if (childPrivate?.listenerCleanup && !childPrivate.listenerCleanup.signal.aborted) {
457
+ childPrivate.listenerCleanup.abort();
458
+ }
459
+ }
460
+ })();
461
+ });
462
+ // Parent step's output: dispatch metadata (the runId is the
463
+ // canonical handle for at-most-once dispatch deduplication when
464
+ // combined with `idempotencyKey`).
465
+ const dispatchData = {
466
+ runId: childRunId ?? null,
467
+ workflowName: childWorkflowName,
468
+ scheduledAt,
469
+ };
470
+ const result = { success: true, data: dispatchData };
471
+ applyStepOutput(parentCtx, this, result);
472
+ return {
473
+ success: true,
474
+ data: dispatchData,
475
+ error: null,
476
+ };
477
+ }
478
+ }
479
+ //# sourceMappingURL=SubworkflowNode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SubworkflowNode.js","sourceRoot":"","sources":["../src/SubworkflowNode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * SwitchNode — v0.5 primitive. N-way branch keyed on a value. First
3
+ * matching case wins; an optional `default` block runs when no case
4
+ * matches.
5
+ *
6
+ * The runtime config is read from `ctx.config[this.name]`:
7
+ * {
8
+ * on: unknown, // resolved by mapper
9
+ * cases: [{ when, steps: NodeBase[] }], // pre-resolved by Configuration
10
+ * default?: NodeBase[], // pre-resolved
11
+ * }
12
+ *
13
+ * Match semantics:
14
+ * - `when` is a literal scalar (string/number/boolean) → match if `on === when`.
15
+ * - `when` is an array → match if `array.includes(on)` (group related cases).
16
+ *
17
+ * Mutations to state inside the matched case's sub-pipeline DO carry
18
+ * forward to subsequent top-level steps — switch is a passthrough flow,
19
+ * NOT an isolation boundary like forEach. The matched case's last step
20
+ * output becomes the switch step's `response.data`.
21
+ *
22
+ * If no case matches and there's no default, the step is a no-op
23
+ * (success, data: null).
24
+ *
25
+ * v0.6 Phase 4 — switch + wait support. The cursor schema
26
+ * (`SwitchIterationContext`) records `caseIndex` (or -1 for `default`)
27
+ * plus `innerStepIndex` within that arm. On re-entry after a wait,
28
+ * SwitchNode reads its cursor from the rehydrated map (keyed by its
29
+ * own NodeRun id), walks back into the matched arm at the right step,
30
+ * and ignores the `on` value entirely — we already committed to a
31
+ * specific arm in the first pass.
32
+ */
33
+ import type { Context, ResponseContext } from "@blokjs/shared";
34
+ import RunnerNode from "./RunnerNode";
35
+ export declare class SwitchNode extends RunnerNode {
36
+ run(ctx: Context): Promise<ResponseContext>;
37
+ }