inngest 4.1.2 → 4.2.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/CHANGELOG.md +6 -0
- package/api/api.cjs +30 -1
- package/api/api.cjs.map +1 -1
- package/api/api.d.cts +19 -0
- package/api/api.d.cts.map +1 -1
- package/api/api.d.ts +19 -0
- package/api/api.d.ts.map +1 -1
- package/api/api.js +30 -1
- package/api/api.js.map +1 -1
- package/components/InngestCommHandler.cjs +9 -1
- package/components/InngestCommHandler.cjs.map +1 -1
- package/components/InngestCommHandler.d.cts.map +1 -1
- package/components/InngestCommHandler.d.ts.map +1 -1
- package/components/InngestCommHandler.js +9 -1
- package/components/InngestCommHandler.js.map +1 -1
- package/components/StreamTools.cjs +241 -0
- package/components/StreamTools.cjs.map +1 -0
- package/components/StreamTools.d.cts +161 -0
- package/components/StreamTools.d.cts.map +1 -0
- package/components/StreamTools.d.ts +161 -0
- package/components/StreamTools.d.ts.map +1 -0
- package/components/StreamTools.js +240 -0
- package/components/StreamTools.js.map +1 -0
- package/components/createWebApiCommHandler.cjs +46 -0
- package/components/createWebApiCommHandler.cjs.map +1 -0
- package/components/createWebApiCommHandler.js +46 -0
- package/components/createWebApiCommHandler.js.map +1 -0
- package/components/execution/InngestExecution.cjs.map +1 -1
- package/components/execution/InngestExecution.d.cts +6 -0
- package/components/execution/InngestExecution.d.cts.map +1 -1
- package/components/execution/InngestExecution.d.ts +6 -0
- package/components/execution/InngestExecution.d.ts.map +1 -1
- package/components/execution/InngestExecution.js.map +1 -1
- package/components/execution/als.cjs.map +1 -1
- package/components/execution/als.d.cts +9 -1
- package/components/execution/als.d.cts.map +1 -1
- package/components/execution/als.d.ts +9 -1
- package/components/execution/als.d.ts.map +1 -1
- package/components/execution/als.js.map +1 -1
- package/components/execution/engine.cjs +334 -26
- package/components/execution/engine.cjs.map +1 -1
- package/components/execution/engine.d.cts +1 -0
- package/components/execution/engine.d.cts.map +1 -1
- package/components/execution/engine.d.ts +1 -0
- package/components/execution/engine.d.ts.map +1 -1
- package/components/execution/engine.js +334 -26
- package/components/execution/engine.js.map +1 -1
- package/components/execution/streaming.cjs +208 -0
- package/components/execution/streaming.cjs.map +1 -0
- package/components/execution/streaming.d.cts +12 -0
- package/components/execution/streaming.d.cts.map +1 -0
- package/components/execution/streaming.d.ts +12 -0
- package/components/execution/streaming.d.ts.map +1 -0
- package/components/execution/streaming.js +198 -0
- package/components/execution/streaming.js.map +1 -0
- package/edge.cjs +19 -32
- package/edge.cjs.map +1 -1
- package/edge.d.cts +1 -1
- package/edge.d.cts.map +1 -1
- package/edge.d.ts +1 -1
- package/edge.d.ts.map +1 -1
- package/edge.js +19 -32
- package/edge.js.map +1 -1
- package/experimental/durable-endpoints/client.cjs +114 -0
- package/experimental/durable-endpoints/client.cjs.map +1 -0
- package/experimental/durable-endpoints/client.d.cts +49 -0
- package/experimental/durable-endpoints/client.d.cts.map +1 -0
- package/experimental/durable-endpoints/client.d.ts +49 -0
- package/experimental/durable-endpoints/client.d.ts.map +1 -0
- package/experimental/durable-endpoints/client.js +114 -0
- package/experimental/durable-endpoints/client.js.map +1 -0
- package/experimental/durable-endpoints/index.cjs +3 -0
- package/experimental/durable-endpoints/index.d.cts +2 -0
- package/experimental/durable-endpoints/index.d.ts +2 -0
- package/experimental/durable-endpoints/index.js +3 -0
- package/helpers/promises.cjs.map +1 -1
- package/helpers/promises.js.map +1 -1
- package/node.cjs +97 -0
- package/node.cjs.map +1 -1
- package/node.d.cts +34 -2
- package/node.d.cts.map +1 -1
- package/node.d.ts +34 -2
- package/node.d.ts.map +1 -1
- package/node.js +95 -2
- package/node.js.map +1 -1
- package/package.json +17 -1
- package/react.d.cts.map +1 -1
- package/version.cjs +1 -1
- package/version.cjs.map +1 -1
- package/version.d.cts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.js.map +1 -1
|
@@ -5,6 +5,7 @@ const require_NonRetriableError = require('../NonRetriableError.cjs');
|
|
|
5
5
|
const require_errors = require('../../helpers/errors.cjs');
|
|
6
6
|
const require_types = require('../../types.cjs');
|
|
7
7
|
const require_InngestExecution = require('./InngestExecution.cjs');
|
|
8
|
+
const require_types$1 = require('../../helpers/types.cjs');
|
|
8
9
|
const require_functions = require('../../helpers/functions.cjs');
|
|
9
10
|
const require_promises = require('../../helpers/promises.cjs');
|
|
10
11
|
const require_als = require('./als.cjs');
|
|
@@ -16,6 +17,8 @@ const require_Inngest = require('../Inngest.cjs');
|
|
|
16
17
|
const require_InngestGroupTools = require('../InngestGroupTools.cjs');
|
|
17
18
|
const require_RetryAfterError = require('../RetryAfterError.cjs');
|
|
18
19
|
const require_StepError = require('../StepError.cjs');
|
|
20
|
+
const require_streaming = require('./streaming.cjs');
|
|
21
|
+
const require_StreamTools = require('../StreamTools.cjs');
|
|
19
22
|
const require_utils$1 = require('../triggers/utils.cjs');
|
|
20
23
|
const require_access = require('./otel/access.cjs');
|
|
21
24
|
let zod_v3 = require("zod/v3");
|
|
@@ -43,10 +46,43 @@ const CHECKPOINT_RETRY_OPTIONS = {
|
|
|
43
46
|
maxAttempts: 5,
|
|
44
47
|
baseDelay: 100
|
|
45
48
|
};
|
|
49
|
+
function errorMessage(error) {
|
|
50
|
+
if (error instanceof Error) return error.message;
|
|
51
|
+
if (require_types$1.isRecord(error) && typeof error.message === "string") return error.message;
|
|
52
|
+
return String(error);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Placeholder step ID used when completing a checkpointed run.
|
|
56
|
+
*/
|
|
57
|
+
const RUN_COMPLETE_STEP_ID = "complete";
|
|
46
58
|
const STEP_NOT_FOUND_MAX_FOUND_STEPS = 25;
|
|
47
59
|
const createExecutionEngine = (options) => {
|
|
48
60
|
return new InngestExecutionEngine(options);
|
|
49
61
|
};
|
|
62
|
+
function extractSseResponse(response, body) {
|
|
63
|
+
const headers = {};
|
|
64
|
+
response.headers.forEach((value, key) => {
|
|
65
|
+
headers[key] = value;
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
body,
|
|
69
|
+
statusCode: response.status,
|
|
70
|
+
headers
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function defaultSseResponse(data) {
|
|
74
|
+
return {
|
|
75
|
+
body: JSON.stringify(data),
|
|
76
|
+
statusCode: 200,
|
|
77
|
+
headers: { "content-type": "application/json" }
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function jsonResponse(data, status = 200) {
|
|
81
|
+
return new Response(JSON.stringify(data), {
|
|
82
|
+
status,
|
|
83
|
+
headers: { "Content-Type": "application/json" }
|
|
84
|
+
});
|
|
85
|
+
}
|
|
50
86
|
var InngestExecutionEngine = class extends require_InngestExecution.InngestExecution {
|
|
51
87
|
version = require_consts.ExecutionVersion.V2;
|
|
52
88
|
state;
|
|
@@ -57,6 +93,29 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
57
93
|
userFnToRun;
|
|
58
94
|
middlewareManager;
|
|
59
95
|
/**
|
|
96
|
+
* Close the stream via {@link streamCloseSucceeded}, {@link streamCloseFailed},
|
|
97
|
+
* or {@link streamEnd} — never call `streamTools.close*`/`end` directly, as
|
|
98
|
+
* the wrappers ensure the redirect event is flushed first.
|
|
99
|
+
*/
|
|
100
|
+
streamTools;
|
|
101
|
+
/**
|
|
102
|
+
* Resolved when `stream.push()`/`pipe()` is first called in sync mode,
|
|
103
|
+
* allowing `_start()` to return the SSE Response to the HTTP layer while
|
|
104
|
+
* the core loop continues executing steps in the background.
|
|
105
|
+
*/
|
|
106
|
+
earlyStreamResponse;
|
|
107
|
+
/**
|
|
108
|
+
* Whether the `inngest.redirect_info` SSE event has already been sent.
|
|
109
|
+
* Prevents duplicate redirect events.
|
|
110
|
+
*/
|
|
111
|
+
redirectSent = false;
|
|
112
|
+
/**
|
|
113
|
+
* Promise that resolves once the redirect event has been written (or the
|
|
114
|
+
* attempt completes). Stored so that `checkpointAndSwitchToAsync` can
|
|
115
|
+
* await it before closing the writer.
|
|
116
|
+
*/
|
|
117
|
+
redirectPromise = Promise.resolve();
|
|
118
|
+
/**
|
|
60
119
|
* If we're supposed to run a particular step via `requestedRunStep`, this
|
|
61
120
|
* will be a `Promise` that resolves after no steps have been found for
|
|
62
121
|
* `timeoutDuration` milliseconds.
|
|
@@ -91,6 +150,10 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
91
150
|
if (!this.options.createResponse) throw new Error("createResponse is required for sync step mode");
|
|
92
151
|
}
|
|
93
152
|
this.userFnToRun = this.getUserFnToRun();
|
|
153
|
+
this.streamTools = new require_StreamTools.Stream({
|
|
154
|
+
onActivated: () => this.handleStreamActivated(),
|
|
155
|
+
onWriteError: (err) => this.devDebug("stream write error (client may have disconnected):", err)
|
|
156
|
+
});
|
|
94
157
|
this.state = this.createExecutionState();
|
|
95
158
|
this.fnArg = this.createFnArg();
|
|
96
159
|
const mwInstances = this.options.middlewareInstances ?? (this.options.client.middleware || []).map((Cls) => {
|
|
@@ -115,7 +178,8 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
115
178
|
app: this.options.client,
|
|
116
179
|
execution: {
|
|
117
180
|
ctx: this.fnArg,
|
|
118
|
-
instance: this
|
|
181
|
+
instance: this,
|
|
182
|
+
stream: this.streamTools
|
|
119
183
|
}
|
|
120
184
|
}, async () => {
|
|
121
185
|
return tracer.startActiveSpan("inngest.execution", (span) => {
|
|
@@ -154,6 +218,21 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
154
218
|
* Starts execution of the user's function and the core loop.
|
|
155
219
|
*/
|
|
156
220
|
async _start() {
|
|
221
|
+
if (this.options.stepMode === require_types.StepMode.Sync && this.options.acceptsSse) this.earlyStreamResponse = require_promises.createDeferredPromise();
|
|
222
|
+
const coreLoop = this.runCoreLoop();
|
|
223
|
+
if (this.earlyStreamResponse) {
|
|
224
|
+
coreLoop.catch((err) => {
|
|
225
|
+
this.options.client[require_Inngest.internalLoggerSymbol].error({ err }, "Core loop rejected after early stream response was sent");
|
|
226
|
+
});
|
|
227
|
+
return Promise.race([this.earlyStreamResponse.promise, coreLoop]);
|
|
228
|
+
}
|
|
229
|
+
return coreLoop;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The core checkpoint loop: processes checkpoints until a handler returns
|
|
233
|
+
* a result.
|
|
234
|
+
*/
|
|
235
|
+
async runCoreLoop() {
|
|
157
236
|
try {
|
|
158
237
|
const allCheckpointHandler = this.getCheckpointHandler("");
|
|
159
238
|
await this.startExecution();
|
|
@@ -164,6 +243,12 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
164
243
|
if (result) return result;
|
|
165
244
|
}
|
|
166
245
|
} catch (error) {
|
|
246
|
+
if (this.earlyStreamResponse) {
|
|
247
|
+
await this.streamCloseFailed("Internal execution error");
|
|
248
|
+
const result = this.transformOutput({ error });
|
|
249
|
+
this.earlyStreamResponse.resolve(result);
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
167
252
|
return this.transformOutput({ error });
|
|
168
253
|
} finally {
|
|
169
254
|
this.state.loop.return();
|
|
@@ -186,8 +271,10 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
186
271
|
this.state.checkpointedRun = {
|
|
187
272
|
appId: res.data.app_id,
|
|
188
273
|
fnId: res.data.fn_id,
|
|
189
|
-
token: res.data.token
|
|
274
|
+
token: res.data.token,
|
|
275
|
+
realtimeToken: res.data.realtime_token
|
|
190
276
|
};
|
|
277
|
+
this.sendRedirectIfReady();
|
|
191
278
|
} else await require_promises.retryWithBackoff(() => this.options.client["inngestApi"].checkpointSteps({
|
|
192
279
|
appId: this.state.checkpointedRun.appId,
|
|
193
280
|
fnId: this.state.checkpointedRun.fnId,
|
|
@@ -195,34 +282,191 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
195
282
|
steps
|
|
196
283
|
}), CHECKPOINT_RETRY_OPTIONS);
|
|
197
284
|
else if (this.options.stepMode === require_types.StepMode.AsyncCheckpointing) {
|
|
198
|
-
|
|
199
|
-
if (!
|
|
285
|
+
const { internalFnId, queueItemId } = this.options;
|
|
286
|
+
if (!queueItemId) throw new Error("Missing queueItemId for async checkpointing. This is a bug in the Inngest SDK.");
|
|
287
|
+
if (!internalFnId) throw new Error("Missing internalFnId for async checkpointing. This is a bug in the Inngest SDK.");
|
|
200
288
|
await require_promises.retryWithBackoff(() => this.options.client["inngestApi"].checkpointStepsAsync({
|
|
201
289
|
runId: this.fnArg.runId,
|
|
202
|
-
fnId:
|
|
203
|
-
queueItemId
|
|
290
|
+
fnId: internalFnId,
|
|
291
|
+
queueItemId,
|
|
204
292
|
steps
|
|
205
293
|
}), CHECKPOINT_RETRY_OPTIONS);
|
|
206
294
|
} else throw new Error("Checkpointing is only supported in Sync and AsyncCheckpointing step modes. This is a bug in the Inngest SDK.");
|
|
207
295
|
}
|
|
208
|
-
async checkpointAndSwitchToAsync(steps) {
|
|
296
|
+
async checkpointAndSwitchToAsync(steps, stepError) {
|
|
209
297
|
await this.checkpoint(steps);
|
|
210
298
|
if (!this.state.checkpointedRun?.token) throw new Error("Failed to checkpoint and switch to async mode");
|
|
299
|
+
const token = this.state.checkpointedRun.token;
|
|
300
|
+
if (this.streamTools.activated) if (stepError && !this.retriability(stepError)) await this.streamCloseFailed(errorMessage(stepError));
|
|
301
|
+
else await this.streamEnd();
|
|
302
|
+
else if (this.options.acceptsSse) {
|
|
303
|
+
await this.streamEnd();
|
|
304
|
+
return {
|
|
305
|
+
type: "function-resolved",
|
|
306
|
+
ctx: this.fnArg,
|
|
307
|
+
ops: this.ops,
|
|
308
|
+
data: this.buildSyncSseResponse()
|
|
309
|
+
};
|
|
310
|
+
}
|
|
211
311
|
return {
|
|
212
312
|
type: "change-mode",
|
|
213
313
|
ctx: this.fnArg,
|
|
214
314
|
ops: this.ops,
|
|
215
315
|
to: require_types.StepMode.Async,
|
|
216
|
-
token
|
|
316
|
+
token
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Prepend the `inngest.metadata` SSE event to the stream's readable side.
|
|
321
|
+
* The returned stream can be used as a fetch body or Response body.
|
|
322
|
+
*
|
|
323
|
+
* NOTE: `this.streamTools.readable` can only be consumed once, so only one
|
|
324
|
+
* of `buildSyncSseResponse` or `postCheckpointStream` may be called per
|
|
325
|
+
* execution.
|
|
326
|
+
*/
|
|
327
|
+
buildMetadataPrefixedStream() {
|
|
328
|
+
const metadataEvent = require_streaming.buildSseMetadataEvent(this.fnArg.runId);
|
|
329
|
+
return require_streaming.prependToStream(new TextEncoder().encode(metadataEvent), this.streamTools.readable);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Build the initial SSE `Response` that marks the start of streaming to the
|
|
333
|
+
* client. Only used in sync mode. In async mode, the stream is POSTed to the
|
|
334
|
+
* Inngest Server via {@link postCheckpointStream} instead.
|
|
335
|
+
*
|
|
336
|
+
* The response body is the stream's readable side, prefixed with the
|
|
337
|
+
* `inngest.metadata` SSE event.
|
|
338
|
+
*/
|
|
339
|
+
buildSyncSseResponse() {
|
|
340
|
+
return new Response(this.buildMetadataPrefixedStream(), {
|
|
341
|
+
status: 200,
|
|
342
|
+
headers: {
|
|
343
|
+
"Content-Type": "text/event-stream",
|
|
344
|
+
"Cache-Control": "no-cache"
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Wraps a plain return value as an SSE Response.
|
|
350
|
+
*
|
|
351
|
+
* Used when the client sent `Accept: text/event-stream` but
|
|
352
|
+
* `stream.push()`/`pipe()` was NOT called during execution. The
|
|
353
|
+
* checkpointable data is the function's return value. The SSE events are just
|
|
354
|
+
* a delivery mechanism.
|
|
355
|
+
*/
|
|
356
|
+
async wrapResultAsSse(checkpoint, sseResponse) {
|
|
357
|
+
const resultData = checkpoint.data;
|
|
358
|
+
await this.streamCloseSucceeded(sseResponse);
|
|
359
|
+
const clientResponse = this.buildSyncSseResponse();
|
|
360
|
+
const streamingResult = {
|
|
361
|
+
...this.transformOutput({ data: resultData }),
|
|
362
|
+
data: clientResponse
|
|
217
363
|
};
|
|
364
|
+
this.checkpointReturnValue(resultData);
|
|
365
|
+
return streamingResult;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Called when `stream.push()`/`pipe()` is first invoked during sync
|
|
369
|
+
* execution. Resolves {@link earlyStreamResponse} so that `_start()` can
|
|
370
|
+
* return the SSE Response to the HTTP layer immediately, while the core
|
|
371
|
+
* checkpoint loop keeps running steps in the background.
|
|
372
|
+
*/
|
|
373
|
+
handleStreamActivated() {
|
|
374
|
+
if (this.earlyStreamResponse) {
|
|
375
|
+
this.earlyStreamResponse.resolve({
|
|
376
|
+
type: "function-resolved",
|
|
377
|
+
ctx: this.fnArg,
|
|
378
|
+
ops: this.ops,
|
|
379
|
+
data: this.buildSyncSseResponse()
|
|
380
|
+
});
|
|
381
|
+
this.sendRedirectIfReady();
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (this.options.stepMode !== require_types.StepMode.Sync) this.postCheckpointStream();
|
|
385
|
+
this.sendRedirectIfReady();
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Sends the `inngest.redirect_info` SSE event when both conditions are met:
|
|
389
|
+
* 1. The client accepts SSE (so there's a stream to write the event to)
|
|
390
|
+
* 2. We have a realtime token (first checkpoint has completed)
|
|
391
|
+
*
|
|
392
|
+
* Called after the first checkpoint AND on stream activation, whichever
|
|
393
|
+
* comes second, so the redirect is sent as early as possible.
|
|
394
|
+
*/
|
|
395
|
+
sendRedirectIfReady() {
|
|
396
|
+
if (this.redirectSent) return;
|
|
397
|
+
if (!this.options.acceptsSse) return;
|
|
398
|
+
if (!this.state.checkpointedRun) return;
|
|
399
|
+
this.redirectSent = true;
|
|
400
|
+
const { realtimeToken } = this.state.checkpointedRun;
|
|
401
|
+
this.redirectPromise = (async () => {
|
|
402
|
+
try {
|
|
403
|
+
const redirect = await this.options.client["inngestApi"].getRealtimeStreamRedirect(realtimeToken);
|
|
404
|
+
this.streamTools.sendRedirectInfo({
|
|
405
|
+
runId: this.fnArg.runId,
|
|
406
|
+
url: redirect.url
|
|
407
|
+
});
|
|
408
|
+
} catch (err) {
|
|
409
|
+
this.options.client[require_Inngest.internalLoggerSymbol].warn({ err }, "Failed to fetch realtime stream redirect URL");
|
|
410
|
+
}
|
|
411
|
+
})();
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Await the pending redirect-info fetch, then close the stream with a
|
|
415
|
+
* succeeded result. Awaiting first guarantees the redirect event is
|
|
416
|
+
* enqueued on the write chain before the close event.
|
|
417
|
+
*/
|
|
418
|
+
async streamCloseSucceeded(response) {
|
|
419
|
+
await this.redirectPromise;
|
|
420
|
+
this.streamTools.closeSucceeded(response);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Await the pending redirect-info fetch, then close the stream with a
|
|
424
|
+
* failed result.
|
|
425
|
+
*/
|
|
426
|
+
async streamCloseFailed(error) {
|
|
427
|
+
await this.redirectPromise;
|
|
428
|
+
this.streamTools.closeFailed(error);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Await the pending redirect-info fetch, then close the stream without
|
|
432
|
+
* a result event.
|
|
433
|
+
*/
|
|
434
|
+
async streamEnd() {
|
|
435
|
+
await this.redirectPromise;
|
|
436
|
+
this.streamTools.end();
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* POST stream data to the checkpoint stream ingest endpoint.
|
|
440
|
+
*
|
|
441
|
+
* Called eagerly from handleStreamActivated so chunks flow in
|
|
442
|
+
* real-time, or after completion if stream.push() was never called.
|
|
443
|
+
*/
|
|
444
|
+
postCheckpointStream() {
|
|
445
|
+
try {
|
|
446
|
+
this.options.client["inngestApi"].checkpointStream({
|
|
447
|
+
runId: this.fnArg.runId,
|
|
448
|
+
body: this.buildMetadataPrefixedStream()
|
|
449
|
+
}).catch((err) => {
|
|
450
|
+
this.devDebug("checkpoint stream POST error:", err);
|
|
451
|
+
});
|
|
452
|
+
} catch (err) {
|
|
453
|
+
this.devDebug("checkpoint stream POST error:", err);
|
|
454
|
+
}
|
|
218
455
|
}
|
|
219
456
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
457
|
+
* Checkpoints the return value of a function that was delivered via SSE.
|
|
458
|
+
* Runs in the background so it doesn't block the client stream.
|
|
222
459
|
*/
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
460
|
+
async checkpointReturnValue(data) {
|
|
461
|
+
try {
|
|
462
|
+
if (this.options.createResponse) await this.checkpoint([{
|
|
463
|
+
op: require_types.StepOpCode.RunComplete,
|
|
464
|
+
id: hashId(RUN_COMPLETE_STEP_ID),
|
|
465
|
+
data: await this.options.createResponse(jsonResponse(data))
|
|
466
|
+
}]);
|
|
467
|
+
} catch (err) {
|
|
468
|
+
this.devDebug("error during background checkpoint of SSE result, client stream unaffected:", err);
|
|
469
|
+
}
|
|
226
470
|
}
|
|
227
471
|
/**
|
|
228
472
|
* Creates a handler for every checkpoint type, defining what to do when we
|
|
@@ -313,19 +557,67 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
313
557
|
};
|
|
314
558
|
const syncHandlers = {
|
|
315
559
|
"": commonCheckpointHandler,
|
|
316
|
-
"function-resolved": async (checkpoint
|
|
317
|
-
const
|
|
560
|
+
"function-resolved": async (checkpoint) => {
|
|
561
|
+
const usingSseStream = !!this.earlyStreamResponse;
|
|
562
|
+
if (this.streamTools.activated) {
|
|
563
|
+
let resultData = checkpoint.data;
|
|
564
|
+
let sseResponse;
|
|
565
|
+
if (checkpoint.data instanceof Response) {
|
|
566
|
+
const body = await (usingSseStream ? checkpoint.data.text() : checkpoint.data.clone().text());
|
|
567
|
+
sseResponse = extractSseResponse(checkpoint.data, body);
|
|
568
|
+
resultData = body;
|
|
569
|
+
} else sseResponse = defaultSseResponse(resultData);
|
|
570
|
+
await this.streamCloseSucceeded(sseResponse);
|
|
571
|
+
if (usingSseStream) {
|
|
572
|
+
this.checkpointReturnValue(resultData);
|
|
573
|
+
return this.transformOutput({ data: resultData });
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (this.options.acceptsSse) {
|
|
577
|
+
let sseResponse;
|
|
578
|
+
if (checkpoint.data instanceof Response) {
|
|
579
|
+
const body = await checkpoint.data.text();
|
|
580
|
+
sseResponse = extractSseResponse(checkpoint.data, body);
|
|
581
|
+
checkpoint = {
|
|
582
|
+
...checkpoint,
|
|
583
|
+
data: body
|
|
584
|
+
};
|
|
585
|
+
} else sseResponse = defaultSseResponse(checkpoint.data);
|
|
586
|
+
return this.wrapResultAsSse(checkpoint, sseResponse);
|
|
587
|
+
}
|
|
588
|
+
if (checkpoint.data instanceof Response) {
|
|
589
|
+
this.checkpointReturnValue(null);
|
|
590
|
+
return this.transformOutput({ data: checkpoint.data });
|
|
591
|
+
}
|
|
318
592
|
await this.checkpoint([{
|
|
319
593
|
op: require_types.StepOpCode.RunComplete,
|
|
320
|
-
id:
|
|
321
|
-
data: await this.options.createResponse(
|
|
594
|
+
id: hashId(RUN_COMPLETE_STEP_ID),
|
|
595
|
+
data: await this.options.createResponse(jsonResponse(checkpoint.data))
|
|
322
596
|
}]);
|
|
323
|
-
return
|
|
597
|
+
return this.transformOutput({ data: checkpoint.data });
|
|
324
598
|
},
|
|
325
599
|
"function-rejected": async (checkpoint) => {
|
|
326
|
-
|
|
600
|
+
const usingSseStream = !!this.earlyStreamResponse;
|
|
601
|
+
const isFinal = !this.retriability(checkpoint.error);
|
|
602
|
+
if (this.streamTools.activated && usingSseStream) {
|
|
603
|
+
(async () => {
|
|
604
|
+
try {
|
|
605
|
+
await this.checkpoint([{
|
|
606
|
+
id: hashId(RUN_COMPLETE_STEP_ID),
|
|
607
|
+
op: isFinal ? require_types.StepOpCode.StepFailed : require_types.StepOpCode.StepError,
|
|
608
|
+
error: checkpoint.error
|
|
609
|
+
}]);
|
|
610
|
+
} catch (err) {
|
|
611
|
+
this.options.client[require_Inngest.internalLoggerSymbol].warn({ err }, "Failed to checkpoint function error");
|
|
612
|
+
}
|
|
613
|
+
if (isFinal) await this.streamCloseFailed(errorMessage(checkpoint.error));
|
|
614
|
+
else await this.streamEnd();
|
|
615
|
+
})();
|
|
616
|
+
return this.transformOutput({ error: checkpoint.error });
|
|
617
|
+
}
|
|
618
|
+
if (isFinal) return this.transformOutput({ error: checkpoint.error });
|
|
327
619
|
return this.checkpointAndSwitchToAsync([{
|
|
328
|
-
id:
|
|
620
|
+
id: hashId(RUN_COMPLETE_STEP_ID),
|
|
329
621
|
op: require_types.StepOpCode.StepError,
|
|
330
622
|
error: checkpoint.error
|
|
331
623
|
}]);
|
|
@@ -347,7 +639,7 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
347
639
|
const result = await this.executeStep(steps[0]);
|
|
348
640
|
const transformed = await stepRanHandler(result);
|
|
349
641
|
if (transformed.type !== "step-ran") throw new Error("Unexpected checkpoint handler result type after running step in sync mode");
|
|
350
|
-
if (result.error) return this.checkpointAndSwitchToAsync([transformed.step]);
|
|
642
|
+
if (result.error) return this.checkpointAndSwitchToAsync([transformed.step], result.error);
|
|
351
643
|
const stepToResume = this.resumeStepWithResult(result);
|
|
352
644
|
delete this.state.executingStep;
|
|
353
645
|
const stepForCheckpoint = {
|
|
@@ -369,13 +661,25 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
369
661
|
const asyncHandlers = {
|
|
370
662
|
"": commonCheckpointHandler,
|
|
371
663
|
"function-resolved": async ({ data }) => {
|
|
664
|
+
let resultData = data;
|
|
665
|
+
let sseResponse;
|
|
666
|
+
if (data instanceof Response) {
|
|
667
|
+
const body = await data.text();
|
|
668
|
+
sseResponse = extractSseResponse(data, body);
|
|
669
|
+
resultData = body;
|
|
670
|
+
} else sseResponse = defaultSseResponse(resultData);
|
|
372
671
|
const newStepsResult = await maybeReturnNewSteps();
|
|
373
672
|
if (newStepsResult) return newStepsResult;
|
|
374
|
-
|
|
375
|
-
|
|
673
|
+
await this.streamCloseSucceeded(sseResponse);
|
|
674
|
+
if (!this.streamTools.activated) this.postCheckpointStream();
|
|
675
|
+
if (this.options.createResponse) data = await this.options.createResponse(jsonResponse(resultData));
|
|
676
|
+
return this.transformOutput({ data });
|
|
376
677
|
},
|
|
377
678
|
"function-rejected": async (checkpoint) => {
|
|
378
|
-
|
|
679
|
+
if (!this.retriability(checkpoint.error)) await this.streamCloseFailed(errorMessage(checkpoint.error));
|
|
680
|
+
else await this.streamEnd();
|
|
681
|
+
if (!this.streamTools.activated) this.postCheckpointStream();
|
|
682
|
+
return this.transformOutput({ error: checkpoint.error });
|
|
379
683
|
},
|
|
380
684
|
"steps-found": async ({ steps }) => {
|
|
381
685
|
const stepResult = await this.tryExecuteStep(steps);
|
|
@@ -407,7 +711,7 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
407
711
|
if (output?.type === "function-resolved") {
|
|
408
712
|
const steps = this.state.checkpointingStepBuffer.concat({
|
|
409
713
|
op: require_types.StepOpCode.RunComplete,
|
|
410
|
-
id:
|
|
714
|
+
id: hashId(RUN_COMPLETE_STEP_ID),
|
|
411
715
|
data: output.data
|
|
412
716
|
});
|
|
413
717
|
if (isNonEmpty(steps)) return {
|
|
@@ -467,6 +771,7 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
467
771
|
}
|
|
468
772
|
},
|
|
469
773
|
"checkpointing-runtime-reached": async () => {
|
|
774
|
+
this.options.client[require_Inngest.internalLoggerSymbol].debug("Checkpointing runtime reached; sending discovery request");
|
|
470
775
|
return {
|
|
471
776
|
type: "steps-found",
|
|
472
777
|
ctx: this.fnArg,
|
|
@@ -550,7 +855,8 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
550
855
|
const store = await require_als.getAsyncCtx();
|
|
551
856
|
if (store?.execution) store.execution.executingStep = {
|
|
552
857
|
id,
|
|
553
|
-
name: displayName
|
|
858
|
+
name: displayName,
|
|
859
|
+
hashedId
|
|
554
860
|
};
|
|
555
861
|
this.devDebug(`executing step "${id}"`);
|
|
556
862
|
if (this.rootSpanId && this.options.checkpointingConfig) require_access.clientProcessorMap.get(this.options.client)?.declareStepExecution(this.rootSpanId, userland.id ?? "", userland.index ?? 0, hashedId, this.options.data?.attempt ?? 0);
|
|
@@ -576,6 +882,7 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
576
882
|
const metadata = this.state.metadata?.get(id);
|
|
577
883
|
const serverData = await resultPromise;
|
|
578
884
|
await this.middlewareManager.onStepComplete(stepInfo, serverData);
|
|
885
|
+
this.streamTools.commit(hashedId);
|
|
579
886
|
return {
|
|
580
887
|
...outgoingOp,
|
|
581
888
|
data: serverData,
|
|
@@ -650,6 +957,7 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
650
957
|
const isFinal = !this.retriability(error);
|
|
651
958
|
const metadata = this.state.metadata?.get(id);
|
|
652
959
|
await this.middlewareManager.onStepError(stepInfo, error instanceof Error ? error : new Error(String(error)), isFinal);
|
|
960
|
+
this.streamTools.rollback(outgoingOp.id);
|
|
653
961
|
const serialized = require_errors.serializeError(error);
|
|
654
962
|
return {
|
|
655
963
|
...outgoingOp,
|