@workflow/core 4.0.1-beta.35 → 4.0.1-beta.37

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.
@@ -0,0 +1,82 @@
1
+ import { type PromiseWithResolvers } from '@workflow/utils';
2
+ /**
3
+ * Polling interval (in ms) for lock release detection.
4
+ *
5
+ * The Web Streams API does not expose an event for "lock released but stream
6
+ * still open"; we can only distinguish that state by periodically attempting
7
+ * to acquire a reader/writer. For that reason we use polling instead of a
8
+ * fully event-driven approach here.
9
+ *
10
+ * 100ms is a compromise between:
11
+ * - Latency: how quickly we notice that the user has released their lock, and
12
+ * - Cost/CPU usage: how often timers fire, especially with many concurrent
13
+ * streams or in serverless environments where billed time matters.
14
+ *
15
+ * This value should only be changed with care, as decreasing it will
16
+ * increase polling frequency (and thus potential cost), while increasing it
17
+ * will add worst-case delay before the `done` promise resolves after a lock
18
+ * is released.
19
+ */
20
+ export declare const LOCK_POLL_INTERVAL_MS = 100;
21
+ /**
22
+ * State tracker for flushable stream operations.
23
+ * Resolves when either:
24
+ * 1. Stream completes (close/error), OR
25
+ * 2. Lock is released AND all pending operations are flushed
26
+ *
27
+ * Note: `doneResolved` and `streamEnded` are separate:
28
+ * - `doneResolved`: The `done` promise has been resolved (step can complete)
29
+ * - `streamEnded`: The underlying stream has actually closed/errored
30
+ *
31
+ * Once `doneResolved` is set to true, the `done` promise will not resolve
32
+ * again. Re-acquiring locks after release is not supported as a way to
33
+ * trigger additional completion signaling.
34
+ */
35
+ export interface FlushableStreamState extends PromiseWithResolvers<void> {
36
+ /** Number of write operations currently in flight to the server */
37
+ pendingOps: number;
38
+ /** Whether the `done` promise has been resolved */
39
+ doneResolved: boolean;
40
+ /** Whether the underlying stream has actually closed/errored */
41
+ streamEnded: boolean;
42
+ /** Interval ID for writable lock polling (if active) */
43
+ writablePollingInterval?: ReturnType<typeof setInterval>;
44
+ /** Interval ID for readable lock polling (if active) */
45
+ readablePollingInterval?: ReturnType<typeof setInterval>;
46
+ }
47
+ export declare function createFlushableState(): FlushableStreamState;
48
+ /**
49
+ * Polls a WritableStream to check if the user has released their lock.
50
+ * Resolves the done promise when lock is released and no pending ops remain.
51
+ *
52
+ * Note: Only resolves if stream is unlocked but NOT closed. If the user closes
53
+ * the stream, the pump will handle resolution via the stream ending naturally.
54
+ *
55
+ * Protection: If polling is already active on this state, the existing interval
56
+ * is used to avoid creating multiple simultaneous polling operations.
57
+ */
58
+ export declare function pollWritableLock(writable: WritableStream, state: FlushableStreamState): void;
59
+ /**
60
+ * Polls a ReadableStream to check if the user has released their lock.
61
+ * Resolves the done promise when lock is released and no pending ops remain.
62
+ *
63
+ * Note: Only resolves if stream is unlocked but NOT closed. If the user closes
64
+ * the stream, the pump will handle resolution via the stream ending naturally.
65
+ *
66
+ * Protection: If polling is already active on this state, the existing interval
67
+ * is used to avoid creating multiple simultaneous polling operations.
68
+ */
69
+ export declare function pollReadableLock(readable: ReadableStream, state: FlushableStreamState): void;
70
+ /**
71
+ * Creates a flushable pipe from a ReadableStream to a WritableStream.
72
+ * Unlike pipeTo(), this resolves when:
73
+ * 1. The source stream completes (close/error), OR
74
+ * 2. The user releases their lock on userStream AND all pending writes are flushed
75
+ *
76
+ * @param source - The readable stream to read from (e.g., transform's readable)
77
+ * @param sink - The writable stream to write to (e.g., server writable)
78
+ * @param state - The flushable state tracker
79
+ * @returns Promise that resolves when stream ends (not when done promise resolves)
80
+ */
81
+ export declare function flushablePipe(source: ReadableStream, sink: WritableStream, state: FlushableStreamState): Promise<void>;
82
+ //# sourceMappingURL=flushable-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flushable-stream.d.ts","sourceRoot":"","sources":["../src/flushable-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,oBAAoB,EAAiB,MAAM,iBAAiB,CAAC;AAE3E;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB,CAAC,IAAI,CAAC;IACtE,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAC;IACtB,gEAAgE;IAChE,WAAW,EAAE,OAAO,CAAC;IACrB,wDAAwD;IACxD,uBAAuB,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IACzD,wDAAwD;IACxD,uBAAuB,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;CAC1D;AAED,wBAAgB,oBAAoB,IAAI,oBAAoB,CAO3D;AA2DD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,oBAAoB,GAC1B,IAAI,CAwBN;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,oBAAoB,GAC1B,IAAI,CAwBN;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAwDf"}
@@ -0,0 +1,214 @@
1
+ import { withResolvers } from '@workflow/utils';
2
+ /**
3
+ * Polling interval (in ms) for lock release detection.
4
+ *
5
+ * The Web Streams API does not expose an event for "lock released but stream
6
+ * still open"; we can only distinguish that state by periodically attempting
7
+ * to acquire a reader/writer. For that reason we use polling instead of a
8
+ * fully event-driven approach here.
9
+ *
10
+ * 100ms is a compromise between:
11
+ * - Latency: how quickly we notice that the user has released their lock, and
12
+ * - Cost/CPU usage: how often timers fire, especially with many concurrent
13
+ * streams or in serverless environments where billed time matters.
14
+ *
15
+ * This value should only be changed with care, as decreasing it will
16
+ * increase polling frequency (and thus potential cost), while increasing it
17
+ * will add worst-case delay before the `done` promise resolves after a lock
18
+ * is released.
19
+ */
20
+ export const LOCK_POLL_INTERVAL_MS = 100;
21
+ export function createFlushableState() {
22
+ return {
23
+ ...withResolvers(),
24
+ pendingOps: 0,
25
+ doneResolved: false,
26
+ streamEnded: false,
27
+ };
28
+ }
29
+ /**
30
+ * Checks if a WritableStream is unlocked (user released lock) vs closed.
31
+ * When a stream is closed, .locked is false but getWriter() throws.
32
+ * We only want to resolve via polling when the stream is unlocked, not closed.
33
+ * If closed, the pump will handle resolution via the stream ending naturally.
34
+ */
35
+ function isWritableUnlockedNotClosed(writable) {
36
+ if (writable.locked)
37
+ return false;
38
+ let writer;
39
+ try {
40
+ // Try to acquire writer - if successful, stream is unlocked (not closed)
41
+ writer = writable.getWriter();
42
+ }
43
+ catch {
44
+ // getWriter() throws if stream is closed/errored - let pump handle it
45
+ return false;
46
+ }
47
+ try {
48
+ writer.releaseLock();
49
+ }
50
+ catch {
51
+ // If releaseLock() throws for any reason, conservatively treat the
52
+ // stream as closed/errored so callers don't assume it's safe to use.
53
+ // The pump will observe the failure via the stream's end state.
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+ /**
59
+ * Checks if a ReadableStream is unlocked (user released lock) vs closed.
60
+ */
61
+ function isReadableUnlockedNotClosed(readable) {
62
+ if (readable.locked)
63
+ return false;
64
+ let reader;
65
+ try {
66
+ // Try to acquire reader - if successful, stream is unlocked (not closed)
67
+ reader = readable.getReader();
68
+ }
69
+ catch {
70
+ // getReader() throws if stream is closed/errored - let pump handle it
71
+ return false;
72
+ }
73
+ try {
74
+ reader.releaseLock();
75
+ }
76
+ catch {
77
+ // If releaseLock() throws for any reason, conservatively treat the
78
+ // stream as closed/errored so callers don't assume it's safe to use.
79
+ // The pump will observe the failure via the stream's end state.
80
+ return false;
81
+ }
82
+ return true;
83
+ }
84
+ /**
85
+ * Polls a WritableStream to check if the user has released their lock.
86
+ * Resolves the done promise when lock is released and no pending ops remain.
87
+ *
88
+ * Note: Only resolves if stream is unlocked but NOT closed. If the user closes
89
+ * the stream, the pump will handle resolution via the stream ending naturally.
90
+ *
91
+ * Protection: If polling is already active on this state, the existing interval
92
+ * is used to avoid creating multiple simultaneous polling operations.
93
+ */
94
+ export function pollWritableLock(writable, state) {
95
+ // Prevent multiple simultaneous polling on the same state
96
+ if (state.writablePollingInterval !== undefined) {
97
+ return;
98
+ }
99
+ const intervalId = setInterval(() => {
100
+ // Stop polling if already resolved or stream ended
101
+ if (state.doneResolved || state.streamEnded) {
102
+ clearInterval(intervalId);
103
+ state.writablePollingInterval = undefined;
104
+ return;
105
+ }
106
+ // Check if lock is released (not closed) and no pending ops
107
+ if (isWritableUnlockedNotClosed(writable) && state.pendingOps === 0) {
108
+ state.doneResolved = true;
109
+ state.resolve();
110
+ clearInterval(intervalId);
111
+ state.writablePollingInterval = undefined;
112
+ }
113
+ }, LOCK_POLL_INTERVAL_MS);
114
+ state.writablePollingInterval = intervalId;
115
+ }
116
+ /**
117
+ * Polls a ReadableStream to check if the user has released their lock.
118
+ * Resolves the done promise when lock is released and no pending ops remain.
119
+ *
120
+ * Note: Only resolves if stream is unlocked but NOT closed. If the user closes
121
+ * the stream, the pump will handle resolution via the stream ending naturally.
122
+ *
123
+ * Protection: If polling is already active on this state, the existing interval
124
+ * is used to avoid creating multiple simultaneous polling operations.
125
+ */
126
+ export function pollReadableLock(readable, state) {
127
+ // Prevent multiple simultaneous polling on the same state
128
+ if (state.readablePollingInterval !== undefined) {
129
+ return;
130
+ }
131
+ const intervalId = setInterval(() => {
132
+ // Stop polling if already resolved or stream ended
133
+ if (state.doneResolved || state.streamEnded) {
134
+ clearInterval(intervalId);
135
+ state.readablePollingInterval = undefined;
136
+ return;
137
+ }
138
+ // Check if lock is released (not closed) and no pending ops
139
+ if (isReadableUnlockedNotClosed(readable) && state.pendingOps === 0) {
140
+ state.doneResolved = true;
141
+ state.resolve();
142
+ clearInterval(intervalId);
143
+ state.readablePollingInterval = undefined;
144
+ }
145
+ }, LOCK_POLL_INTERVAL_MS);
146
+ state.readablePollingInterval = intervalId;
147
+ }
148
+ /**
149
+ * Creates a flushable pipe from a ReadableStream to a WritableStream.
150
+ * Unlike pipeTo(), this resolves when:
151
+ * 1. The source stream completes (close/error), OR
152
+ * 2. The user releases their lock on userStream AND all pending writes are flushed
153
+ *
154
+ * @param source - The readable stream to read from (e.g., transform's readable)
155
+ * @param sink - The writable stream to write to (e.g., server writable)
156
+ * @param state - The flushable state tracker
157
+ * @returns Promise that resolves when stream ends (not when done promise resolves)
158
+ */
159
+ export async function flushablePipe(source, sink, state) {
160
+ const reader = source.getReader();
161
+ const writer = sink.getWriter();
162
+ try {
163
+ while (true) {
164
+ // Check if stream has ended
165
+ if (state.streamEnded) {
166
+ return;
167
+ }
168
+ // Read from source - don't count as pending op since we're just waiting for data
169
+ // The important ops are writes to the sink (server)
170
+ const readResult = await reader.read();
171
+ // Check if stream has ended (e.g., due to error in another path) before processing
172
+ if (state.streamEnded) {
173
+ return;
174
+ }
175
+ if (readResult.done) {
176
+ // Source stream completed - close sink and resolve
177
+ state.streamEnded = true;
178
+ await writer.close();
179
+ // Resolve done promise if not already resolved
180
+ if (!state.doneResolved) {
181
+ state.doneResolved = true;
182
+ state.resolve();
183
+ }
184
+ return;
185
+ }
186
+ // Count write as a pending op - this is what we need to flush
187
+ state.pendingOps++;
188
+ try {
189
+ await writer.write(readResult.value);
190
+ }
191
+ finally {
192
+ state.pendingOps--;
193
+ }
194
+ }
195
+ }
196
+ catch (err) {
197
+ state.streamEnded = true;
198
+ if (!state.doneResolved) {
199
+ state.doneResolved = true;
200
+ state.reject(err);
201
+ }
202
+ // Propagate error through flushablePipe's own promise as well.
203
+ // Callers that rely on the FlushableStreamState should use `state.promise`,
204
+ // while other callers may depend on this rejection. Some known callers
205
+ // explicitly ignore this rejection (`.catch(() => {})`) and rely solely
206
+ // on `state.reject(err)` for error handling.
207
+ throw err;
208
+ }
209
+ finally {
210
+ reader.releaseLock();
211
+ writer.releaseLock();
212
+ }
213
+ }
214
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmx1c2hhYmxlLXN0cmVhbS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9mbHVzaGFibGUtc3RyZWFtLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBNkIsYUFBYSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFM0U7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0gsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsR0FBRyxDQUFDO0FBNkJ6QyxNQUFNLFVBQVUsb0JBQW9CO0lBQ2xDLE9BQU87UUFDTCxHQUFHLGFBQWEsRUFBUTtRQUN4QixVQUFVLEVBQUUsQ0FBQztRQUNiLFlBQVksRUFBRSxLQUFLO1FBQ25CLFdBQVcsRUFBRSxLQUFLO0tBQ25CLENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLDJCQUEyQixDQUFDLFFBQXdCO0lBQzNELElBQUksUUFBUSxDQUFDLE1BQU07UUFBRSxPQUFPLEtBQUssQ0FBQztJQUVsQyxJQUFJLE1BQStDLENBQUM7SUFDcEQsSUFBSSxDQUFDO1FBQ0gseUVBQXlFO1FBQ3pFLE1BQU0sR0FBRyxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUM7SUFDaEMsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLHNFQUFzRTtRQUN0RSxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxJQUFJLENBQUM7UUFDSCxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDdkIsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLG1FQUFtRTtRQUNuRSxxRUFBcUU7UUFDckUsZ0VBQWdFO1FBQ2hFLE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBUywyQkFBMkIsQ0FBQyxRQUF3QjtJQUMzRCxJQUFJLFFBQVEsQ0FBQyxNQUFNO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFFbEMsSUFBSSxNQUErQyxDQUFDO0lB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
@@ -1,4 +1,43 @@
1
- import type { Event, World } from '@workflow/world';
1
+ import type { Event, HealthCheckPayload, World } from '@workflow/world';
2
+ /**
3
+ * Result of a health check operation.
4
+ */
5
+ export interface HealthCheckResult {
6
+ healthy: boolean;
7
+ /** Error message if health check failed */
8
+ error?: string;
9
+ }
10
+ /**
11
+ * Checks if the given message is a health check payload.
12
+ * If so, returns the parsed payload. Otherwise returns undefined.
13
+ */
14
+ export declare function parseHealthCheckPayload(message: unknown): HealthCheckPayload | undefined;
15
+ /**
16
+ * Handles a health check message by writing the result to the world's stream.
17
+ * The caller can listen to this stream to get the health check response.
18
+ *
19
+ * @param healthCheck - The parsed health check payload
20
+ * @param endpoint - Which endpoint is responding ('workflow' or 'step')
21
+ */
22
+ export declare function handleHealthCheckMessage(healthCheck: HealthCheckPayload, endpoint: 'workflow' | 'step'): Promise<void>;
23
+ export type HealthCheckEndpoint = 'workflow' | 'step';
24
+ export interface HealthCheckOptions {
25
+ /** Timeout in milliseconds to wait for health check response. Default: 30000 (30s) */
26
+ timeout?: number;
27
+ }
28
+ /**
29
+ * Performs a health check by sending a message through the queue pipeline
30
+ * and verifying it is processed by the specified endpoint.
31
+ *
32
+ * This function bypasses Deployment Protection on Vercel because it goes
33
+ * through the queue infrastructure rather than direct HTTP.
34
+ *
35
+ * @param world - The World instance to use for the health check
36
+ * @param endpoint - Which endpoint to health check: 'workflow' or 'step'
37
+ * @param options - Optional configuration for the health check
38
+ * @returns Promise resolving to health check result
39
+ */
40
+ export declare function healthCheck(world: World, endpoint: HealthCheckEndpoint, options?: HealthCheckOptions): Promise<HealthCheckResult>;
2
41
  /**
3
42
  * Loads all workflow run events by iterating through all pages of paginated results.
4
43
  * This ensures that *all* events are loaded into memory before running the workflow.
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/runtime/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAKpD;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAwB7E;AAYD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,GAC3C,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAyBrC;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,KAAK,EACZ,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,KAAK,CAAC,iBAcxC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,WAAW,CAAC,EAAE,IAAI,CAAA;CAAE;;cAS/D"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/runtime/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,kBAAkB,EAElB,KAAK,EACN,MAAM,iBAAiB,CAAC;AAmBzB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,GACf,kBAAkB,GAAG,SAAS,CAMhC;AAWD;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,kBAAkB,EAC/B,QAAQ,EAAE,UAAU,GAAG,MAAM,GAC5B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IACjC,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAkG5B;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAwB7E;AAYD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,GAC3C,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAyBrC;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,KAAK,EACZ,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,KAAK,CAAC,iBAcxC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,WAAW,CAAC,EAAE,IAAI,CAAA;CAAE;;cAS/D"}
@@ -1,6 +1,156 @@
1
+ import { HealthCheckPayloadSchema } from '@workflow/world';
2
+ import { monotonicFactory } from 'ulid';
1
3
  import * as Attribute from '../telemetry/semantic-conventions.js';
2
4
  import { getSpanKind, trace } from '../telemetry.js';
3
5
  import { getWorld } from './world.js';
6
+ /** Default timeout for health checks in milliseconds */
7
+ const DEFAULT_HEALTH_CHECK_TIMEOUT = 30_000;
8
+ const generateId = monotonicFactory();
9
+ /**
10
+ * Returns the stream name for a health check with the given correlation ID.
11
+ */
12
+ function getHealthCheckStreamName(correlationId) {
13
+ return `__health_check__${correlationId}`;
14
+ }
15
+ /**
16
+ * Checks if the given message is a health check payload.
17
+ * If so, returns the parsed payload. Otherwise returns undefined.
18
+ */
19
+ export function parseHealthCheckPayload(message) {
20
+ const result = HealthCheckPayloadSchema.safeParse(message);
21
+ if (result.success) {
22
+ return result.data;
23
+ }
24
+ return undefined;
25
+ }
26
+ /**
27
+ * Generates a fake runId for health check streams.
28
+ * This runId passes server validation but is not associated with a real run.
29
+ * The server skips run validation for streams starting with `__health_check__`.
30
+ */
31
+ function generateHealthCheckRunId() {
32
+ return `wrun_${generateId()}`;
33
+ }
34
+ /**
35
+ * Handles a health check message by writing the result to the world's stream.
36
+ * The caller can listen to this stream to get the health check response.
37
+ *
38
+ * @param healthCheck - The parsed health check payload
39
+ * @param endpoint - Which endpoint is responding ('workflow' or 'step')
40
+ */
41
+ export async function handleHealthCheckMessage(healthCheck, endpoint) {
42
+ const world = getWorld();
43
+ const streamName = getHealthCheckStreamName(healthCheck.correlationId);
44
+ const response = JSON.stringify({
45
+ healthy: true,
46
+ endpoint,
47
+ correlationId: healthCheck.correlationId,
48
+ timestamp: Date.now(),
49
+ });
50
+ // Use a fake runId that passes validation.
51
+ // The stream name includes the correlationId for identification.
52
+ // The server skips run validation for health check streams.
53
+ const fakeRunId = generateHealthCheckRunId();
54
+ await world.writeToStream(streamName, fakeRunId, response);
55
+ await world.closeStream(streamName, fakeRunId);
56
+ }
57
+ /**
58
+ * Performs a health check by sending a message through the queue pipeline
59
+ * and verifying it is processed by the specified endpoint.
60
+ *
61
+ * This function bypasses Deployment Protection on Vercel because it goes
62
+ * through the queue infrastructure rather than direct HTTP.
63
+ *
64
+ * @param world - The World instance to use for the health check
65
+ * @param endpoint - Which endpoint to health check: 'workflow' or 'step'
66
+ * @param options - Optional configuration for the health check
67
+ * @returns Promise resolving to health check result
68
+ */
69
+ export async function healthCheck(world, endpoint, options) {
70
+ const timeout = options?.timeout ?? DEFAULT_HEALTH_CHECK_TIMEOUT;
71
+ const correlationId = `hc_${generateId()}`;
72
+ const streamName = getHealthCheckStreamName(correlationId);
73
+ // Determine which queue to use based on endpoint
74
+ const queueName = endpoint === 'workflow'
75
+ ? '__wkf_workflow_health_check'
76
+ : '__wkf_step_health_check';
77
+ const startTime = Date.now();
78
+ try {
79
+ // Send the health check message through the queue first
80
+ await world.queue(queueName, {
81
+ __healthCheck: true,
82
+ correlationId,
83
+ });
84
+ // Poll for the stream response with retries
85
+ // The stream may not exist immediately after queueing on Vercel
86
+ const pollInterval = 100; // ms between retries
87
+ while (Date.now() - startTime < timeout) {
88
+ try {
89
+ // Try to read from the stream by name (includes correlationId)
90
+ const stream = await world.readFromStream(streamName);
91
+ const reader = stream.getReader();
92
+ const chunks = [];
93
+ let done = false;
94
+ while (!done) {
95
+ const result = await reader.read();
96
+ done = result.done;
97
+ if (result.value)
98
+ chunks.push(result.value);
99
+ }
100
+ // If we got no data, the stream might not have been written yet
101
+ if (chunks.length === 0) {
102
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
103
+ continue;
104
+ }
105
+ // Parse the response
106
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
107
+ const combined = new Uint8Array(totalLength);
108
+ let offset = 0;
109
+ for (const chunk of chunks) {
110
+ combined.set(chunk, offset);
111
+ offset += chunk.length;
112
+ }
113
+ const responseText = new TextDecoder().decode(combined);
114
+ let response;
115
+ try {
116
+ response = JSON.parse(responseText);
117
+ }
118
+ catch {
119
+ // Response might not be valid JSON yet, retry
120
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
121
+ continue;
122
+ }
123
+ // Type guard: ensure response has the expected structure
124
+ if (typeof response !== 'object' ||
125
+ response === null ||
126
+ !('healthy' in response) ||
127
+ typeof response.healthy !== 'boolean') {
128
+ // Invalid structure, retry
129
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
130
+ continue;
131
+ }
132
+ return {
133
+ healthy: response.healthy,
134
+ };
135
+ }
136
+ catch {
137
+ // Stream might not exist yet, retry after a delay
138
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
139
+ }
140
+ }
141
+ // Timeout reached
142
+ return {
143
+ healthy: false,
144
+ error: `Health check timed out after ${timeout}ms`,
145
+ };
146
+ }
147
+ catch (error) {
148
+ return {
149
+ healthy: false,
150
+ error: error instanceof Error ? error.message : String(error),
151
+ };
152
+ }
153
+ }
4
154
  /**
5
155
  * Loads all workflow run events by iterating through all pages of paginated results.
6
156
  * This ensures that *all* events are loaded into memory before running the workflow.
@@ -90,4 +240,4 @@ export function getQueueOverhead(message) {
90
240
  return;
91
241
  }
92
242
  }
93
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL2hlbHBlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLFNBQVMsTUFBTSxzQ0FBc0MsQ0FBQztBQUNsRSxPQUFPLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFdEM7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsdUJBQXVCLENBQUMsS0FBYTtJQUN6RCxNQUFNLFNBQVMsR0FBWSxFQUFFLENBQUM7SUFDOUIsSUFBSSxNQUFNLEdBQWtCLElBQUksQ0FBQztJQUNqQyxJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUM7SUFFbkIsTUFBTSxLQUFLLEdBQUcsUUFBUSxFQUFFLENBQUM7SUFDekIsT0FBTyxPQUFPLEVBQUUsQ0FBQztRQUNmLCtGQUErRjtRQUMvRixxR0FBcUc7UUFDckcsbUNBQW1DO1FBQ25DLE1BQU0sUUFBUSxHQUFHLE1BQU0sS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7WUFDdkMsS0FBSztZQUNMLFVBQVUsRUFBRTtnQkFDVixTQUFTLEVBQUUsS0FBSyxFQUFFLDZEQUE2RDtnQkFDL0UsTUFBTSxFQUFFLE1BQU0sSUFBSSxTQUFTO2FBQzVCO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsU0FBUyxDQUFDLElBQUksQ0FBQyxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNqQyxPQUFPLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQztRQUMzQixNQUFNLEdBQUcsUUFBUSxDQUFDLE1BQU0sQ0FBQztJQUMzQixDQUFDO0lBRUQsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0seUJBQXlCLEdBQUc7SUFDaEMsNkJBQTZCLEVBQUUsR0FBRztJQUNsQyw4QkFBOEIsRUFBRSwwQkFBMEI7SUFDMUQsOEJBQThCLEVBQUUsY0FBYztDQUMvQyxDQUFDO0FBRUY7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLGVBQWUsQ0FDN0IsT0FBNEM7SUFFNUMsT0FBTyxLQUFLLEVBQUUsR0FBWSxFQUFFLEVBQUU7UUFDNUIsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzdCLE1BQU0sYUFBYSxHQUFHLEdBQUcsQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3ZELElBQUksYUFBYSxFQUFFLENBQUM7WUFDbEIseUNBQXlDO1lBQ3pDLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDN0IsT0FBTyxJQUFJLFFBQVEsQ0FBQyxJQUFJLEVBQUU7b0JBQ3hCLE1BQU0sRUFBRSxHQUFHO29CQUNYLE9BQU8sRUFBRSx5QkFBeUI7aUJBQ25DLENBQUMsQ0FBQztZQUNMLENBQUM7WUFDRCxPQUFPLElBQUksUUFBUSxDQUNqQixvQkFBb0IsR0FBRyxDQUFDLFFBQVEsdUJBQXVCLEVBQ3ZEO2dCQUNFLE1BQU0sRUFBRSxHQUFHO2dCQUNYLE9BQU8sRUFBRTtvQkFDUCxjQUFjLEVBQUUsWUFBWTtvQkFDNUIsR0FBRyx5QkFBeUI7aUJBQzdCO2FBQ0YsQ0FDRixDQUFDO1FBQ0osQ0FBQztRQUNELE9BQU8sTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDNUIsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxZQUFZLENBQ2hDLEtBQVksRUFDWixHQUFHLElBQW9DO0lBRXZDLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxQixNQUFNLEtBQUssQ0FDVCxjQUFjLEVBQ2Q7UUFDRSxVQUFVLEVBQUUsU0FBUyxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUM7UUFDMUMsSUFBSSxFQUFFLE1BQU0sV0FBVyxDQUFDLFVBQVUsQ0FBQztLQUNwQyxFQUNELEtBQUssRUFBRSxJQUFJLEVBQUUsRUFBRTtRQUNiLE1BQU0sRUFBRSxTQUFTLEVBQUUsR0FBRyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQztRQUNqRCxJQUFJLEVBQUUsYUFBYSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztJQUMzRCxDQUFDLENBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxPQUErQjtJQUM5RCxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVc7UUFBRSxPQUFPO0lBQ2pDLElBQUksQ0FBQztRQUNILE9BQU8sU0FBUyxDQUFDLGVBQWUsQ0FDOUIsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUMsT0FBTyxFQUFFLENBQzNDLENBQUM7SUFDSixDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsT0FBTztJQUNULENBQUM7QUFDSCxDQUFDIn0=
243
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL2hlbHBlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBTUEsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0QsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3hDLE9BQU8sS0FBSyxTQUFTLE1BQU0sc0NBQXNDLENBQUM7QUFDbEUsT0FBTyxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRXRDLHdEQUF3RDtBQUN4RCxNQUFNLDRCQUE0QixHQUFHLE1BQU0sQ0FBQztBQUU1QyxNQUFNLFVBQVUsR0FBRyxnQkFBZ0IsRUFBRSxDQUFDO0FBRXRDOztHQUVHO0FBQ0gsU0FBUyx3QkFBd0IsQ0FBQyxhQUFxQjtJQUNyRCxPQUFPLG1CQUFtQixhQUFhLEVBQUUsQ0FBQztBQUM1QyxDQUFDO0FBV0Q7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLHVCQUF1QixDQUNyQyxPQUFnQjtJQUVoQixNQUFNLE1BQU0sR0FBRyx3QkFBd0IsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDM0QsSUFBSSxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDbkIsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBQ3JCLENBQUM7SUFDRCxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQVMsd0JBQXdCO0lBQy9CLE9BQU8sUUFBUSxVQUFVLEVBQUUsRUFBRSxDQUFDO0FBQ2hDLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLHdCQUF3QixDQUM1QyxXQUErQixFQUMvQixRQUE2QjtJQUU3QixNQUFNLEtBQUssR0FBRyxRQUFRLEVBQUUsQ0FBQztJ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
@@ -13,7 +13,7 @@ export declare function getHookByToken(token: string): Promise<Hook>;
13
13
  * This function is called externally (e.g., from an API route or server action)
14
14
  * to send data to a hook and resume the associated workflow run.
15
15
  *
16
- * @param token - The unique token identifying the hook
16
+ * @param tokenOrHook - The unique token identifying the hook, or the hook object itself
17
17
  * @param payload - The data payload to send to the hook
18
18
  * @returns Promise resolving to the hook
19
19
  * @throws Error if the hook is not found or if there's an error during the process
@@ -36,7 +36,7 @@ export declare function getHookByToken(token: string): Promise<Hook>;
36
36
  * }
37
37
  * ```
38
38
  */
39
- export declare function resumeHook<T = any>(token: string, payload: T): Promise<Hook>;
39
+ export declare function resumeHook<T = any>(tokenOrHook: string | Hook, payload: T): Promise<Hook>;
40
40
  /**
41
41
  * Resumes a webhook by sending a {@link https://developer.mozilla.org/en-US/docs/Web/API/Request | Request}
42
42
  * object to a hook identified by its token.
@@ -1 +1 @@
1
- {"version":3,"file":"resume-hook.d.ts","sourceRoot":"","sources":["../../src/runtime/resume-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAyB,MAAM,iBAAiB,CAAC;AAWnE;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,GACT,OAAO,CAAC,IAAI,CAAC,CA6Ef;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,QAAQ,CAAC,CAkDnB"}
1
+ {"version":3,"file":"resume-hook.d.ts","sourceRoot":"","sources":["../../src/runtime/resume-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAyB,MAAM,iBAAiB,CAAC;AAWnE;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,OAAO,EAAE,CAAC,GACT,OAAO,CAAC,IAAI,CAAC,CAkFf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,QAAQ,CAAC,CAkDnB"}
@@ -27,7 +27,7 @@ export async function getHookByToken(token) {
27
27
  * This function is called externally (e.g., from an API route or server action)
28
28
  * to send data to a hook and resume the associated workflow run.
29
29
  *
30
- * @param token - The unique token identifying the hook
30
+ * @param tokenOrHook - The unique token identifying the hook, or the hook object itself
31
31
  * @param payload - The data payload to send to the hook
32
32
  * @returns Promise resolving to the hook
33
33
  * @throws Error if the hook is not found or if there's an error during the process
@@ -50,14 +50,16 @@ export async function getHookByToken(token) {
50
50
  * }
51
51
  * ```
52
52
  */
53
- export async function resumeHook(token, payload) {
53
+ export async function resumeHook(tokenOrHook, payload) {
54
54
  return await waitedUntil(() => {
55
55
  return trace('HOOK.resume', async (span) => {
56
56
  const world = getWorld();
57
57
  try {
58
- const hook = await getHookByToken(token);
58
+ const hook = typeof tokenOrHook === 'string'
59
+ ? await getHookByToken(tokenOrHook)
60
+ : tokenOrHook;
59
61
  span?.setAttributes({
60
- ...Attribute.HookToken(token),
62
+ ...Attribute.HookToken(hook.token),
61
63
  ...Attribute.HookId(hook.hookId),
62
64
  ...Attribute.WorkflowRunId(hook.runId),
63
65
  });
@@ -101,7 +103,7 @@ export async function resumeHook(token, payload) {
101
103
  }
102
104
  catch (err) {
103
105
  span?.setAttributes({
104
- ...Attribute.HookToken(token),
106
+ ...Attribute.HookToken(typeof tokenOrHook === 'string' ? tokenOrHook : tokenOrHook.token),
105
107
  ...Attribute.HookFound(false),
106
108
  });
107
109
  throw err;
@@ -169,7 +171,7 @@ export async function resumeWebhook(token, request) {
169
171
  // No `respondWith` value implies the default behavior of returning a 202
170
172
  response = new Response(null, { status: 202 });
171
173
  }
172
- await resumeHook(hook.token, request);
174
+ await resumeHook(hook, request);
173
175
  if (responseReadable) {
174
176
  // Wait for the readable stream to emit one chunk,
175
177
  // which is the `Response` object
@@ -187,4 +189,4 @@ export async function resumeWebhook(token, request) {
187
189
  }
188
190
  return response;
189
191
  }
190
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzdW1lLWhvb2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcnVudGltZS9yZXN1bWUtaG9vay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDOUMsT0FBTyxFQUFFLFdBQVcsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRXJFLE9BQU8sRUFDTCx3QkFBd0IsRUFDeEIsb0JBQW9CLEdBQ3JCLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFELE9BQU8sS0FBSyxTQUFTLE1BQU0sc0NBQXNDLENBQUM7QUFDbEUsT0FBTyxFQUFFLDZCQUE2QixFQUFFLEtBQUssRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDekMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUV0Qzs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGNBQWMsQ0FBQyxLQUFhO0lBQ2hELE1BQU0sS0FBSyxHQUFHLFFBQVEsRUFBRSxDQUFDO0lBQ3pCLE1BQU0sSUFBSSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDakQsSUFBSSxPQUFPLElBQUksQ0FBQyxRQUFRLEtBQUssV0FBVyxFQUFFLENBQUM7UUFDekMsSUFBSSxDQUFDLFFBQVEsR0FBRyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsUUFBZSxFQUFFLEVBQUUsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDN0UsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBNEJHO0FBQ0gsTUFBTSxDQUFDLEtB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
192
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzdW1lLWhvb2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcnVudGltZS9yZXN1bWUtaG9vay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDOUMsT0FBTyxFQUFFLFdBQVcsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRXJFLE9BQU8sRUFDTCx3QkFBd0IsRUFDeEIsb0JBQW9CLEdBQ3JCLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFELE9BQU8sS0FBSyxTQUFTLE1BQU0sc0NBQXNDLENBQUM7QUFDbEUsT0FBTyxFQUFFLDZCQUE2QixFQUFFLEtBQUssRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDekMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUV0Qzs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGNBQWMsQ0FBQyxLQUFhO0lBQ2hELE1BQU0sS0FBSyxHQUFHLFFBQVEsRUFBRSxDQUFDO0lBQ3pCLE1BQU0sSUFBSSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDakQsSUFBSSxPQUFPLElBQUksQ0FBQyxRQUFRLEtBQUssV0FBVyxFQUFFLENBQUM7UUFDekMsSUFBSSxDQUFDLFFBQVEsR0FBRyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsUUFBZSxFQUFFLEVBQUUsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDN0UsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBNEJHO0FBQ0gsTUFBTSxDQUFDLEtB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
@@ -1 +1 @@
1
- {"version":3,"file":"step-handler.d.ts","sourceRoot":"","sources":["../../src/runtime/step-handler.ts"],"names":[],"mappings":"AA6aA;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CACjB,CAAC"}
1
+ {"version":3,"file":"step-handler.d.ts","sourceRoot":"","sources":["../../src/runtime/step-handler.ts"],"names":[],"mappings":"AA6bA;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CACjB,CAAC"}