@upstash/workflow 0.2.7 → 0.2.8-rc-invoke

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.
@@ -41,7 +41,7 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
41
41
  var DEFAULT_CONTENT_TYPE = "application/json";
42
42
  var NO_CONCURRENCY = 1;
43
43
  var DEFAULT_RETRIES = 3;
44
- var VERSION = "v0.2.3";
44
+ var VERSION = "v0.2.7";
45
45
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
46
46
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
47
47
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -96,7 +96,8 @@ var StepTypes = [
96
96
  "SleepUntil",
97
97
  "Call",
98
98
  "Wait",
99
- "Notify"
99
+ "Notify",
100
+ "Invoke"
100
101
  ];
101
102
 
102
103
  // src/client/utils.ts
@@ -159,6 +160,31 @@ var getSteps = async (requester, workflowRunId, messageId, debug) => {
159
160
  }
160
161
  };
161
162
 
163
+ // src/utils.ts
164
+ var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
165
+ var NANOID_LENGTH = 21;
166
+ function getRandomInt() {
167
+ return Math.floor(Math.random() * NANOID_CHARS.length);
168
+ }
169
+ function nanoid() {
170
+ return Array.from({ length: NANOID_LENGTH }).map(() => NANOID_CHARS[getRandomInt()]).join("");
171
+ }
172
+ function getWorkflowRunId(id) {
173
+ return `wfr_${id ?? nanoid()}`;
174
+ }
175
+ function decodeBase64(base64) {
176
+ try {
177
+ const binString = atob(base64);
178
+ const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
179
+ return new TextDecoder().decode(intArray);
180
+ } catch (error) {
181
+ console.warn(
182
+ `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
183
+ );
184
+ return atob(base64);
185
+ }
186
+ }
187
+
162
188
  // src/context/steps.ts
163
189
  var BaseLazyStep = class {
164
190
  stepName;
@@ -339,6 +365,41 @@ var LazyNotifyStep = class extends LazyFunctionStep {
339
365
  });
340
366
  }
341
367
  };
368
+ var LazyInvokeStep = class extends BaseLazyStep {
369
+ stepType = "Invoke";
370
+ params;
371
+ constructor(stepName, { workflow, body, headers = {}, workflowRunId, retries }) {
372
+ super(stepName);
373
+ this.params = {
374
+ workflow,
375
+ body,
376
+ headers,
377
+ workflowRunId: getWorkflowRunId(workflowRunId),
378
+ retries
379
+ };
380
+ }
381
+ getPlanStep(concurrent, targetStep) {
382
+ return {
383
+ stepId: 0,
384
+ stepName: this.stepName,
385
+ stepType: this.stepType,
386
+ concurrent,
387
+ targetStep
388
+ };
389
+ }
390
+ /**
391
+ * won't be used as it's the server who will add the result step
392
+ * in Invoke step.
393
+ */
394
+ getResultStep(concurrent, stepId) {
395
+ return Promise.resolve({
396
+ stepId,
397
+ stepName: this.stepName,
398
+ stepType: this.stepType,
399
+ concurrent
400
+ });
401
+ }
402
+ };
342
403
 
343
404
  // node_modules/neverthrow/dist/index.es.js
344
405
  var defaultErrorConfig = {
@@ -816,8 +877,8 @@ var triggerRouteFunction = async ({
816
877
  debug
817
878
  }) => {
818
879
  try {
819
- await onStep();
820
- await onCleanup();
880
+ const result = await onStep();
881
+ await onCleanup(result);
821
882
  return ok("workflow-finished");
822
883
  } catch (error) {
823
884
  const error_ = error;
@@ -838,14 +899,15 @@ var triggerRouteFunction = async ({
838
899
  }
839
900
  }
840
901
  };
841
- var triggerWorkflowDelete = async (workflowContext, debug, cancel = false) => {
902
+ var triggerWorkflowDelete = async (workflowContext, result, debug, cancel = false) => {
842
903
  await debug?.log("SUBMIT", "SUBMIT_CLEANUP", {
843
904
  deletedWorkflowRunId: workflowContext.workflowRunId
844
905
  });
845
906
  await workflowContext.qstashClient.http.request({
846
907
  path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
847
908
  method: "DELETE",
848
- parseResponseAsJson: false
909
+ parseResponseAsJson: false,
910
+ body: JSON.stringify(result)
849
911
  });
850
912
  await debug?.log(
851
913
  "SUBMIT",
@@ -1000,11 +1062,14 @@ var getHeaders = ({
1000
1062
  callTimeout,
1001
1063
  telemetry
1002
1064
  }) => {
1065
+ const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1003
1066
  const baseHeaders = {
1004
1067
  [WORKFLOW_INIT_HEADER]: initHeaderValue,
1005
1068
  [WORKFLOW_ID_HEADER]: workflowRunId,
1006
1069
  [WORKFLOW_URL_HEADER]: workflowUrl,
1007
1070
  [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
1071
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1072
+ "content-type": contentType,
1008
1073
  ...telemetry ? getTelemetryHeaders(telemetry) : {}
1009
1074
  };
1010
1075
  if (!step?.callUrl) {
@@ -1019,6 +1084,9 @@ var getHeaders = ({
1019
1084
  baseHeaders["Upstash-Failure-Callback"] = failureUrl;
1020
1085
  }
1021
1086
  }
1087
+ if (step?.stepType === "Invoke") {
1088
+ baseHeaders["upstash-workflow-invoke"] = "true";
1089
+ }
1022
1090
  if (step?.callUrl) {
1023
1091
  baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
1024
1092
  baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
@@ -1040,7 +1108,6 @@ var getHeaders = ({
1040
1108
  baseHeaders[`Upstash-Failure-Callback-Forward-${header}`] = userHeaders.get(header);
1041
1109
  }
1042
1110
  }
1043
- const contentType = (userHeaders ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1044
1111
  if (step?.callHeaders) {
1045
1112
  const forwardedHeaders = Object.fromEntries(
1046
1113
  Object.entries(step.callHeaders).map(([header, value]) => [
@@ -1090,8 +1157,7 @@ var getHeaders = ({
1090
1157
  "Upstash-Workflow-Runid": [workflowRunId],
1091
1158
  [WORKFLOW_INIT_HEADER]: ["false"],
1092
1159
  [WORKFLOW_URL_HEADER]: [workflowUrl],
1093
- "Upstash-Workflow-CallType": ["step"],
1094
- "Content-Type": [contentType]
1160
+ "Upstash-Workflow-CallType": ["step"]
1095
1161
  }
1096
1162
  };
1097
1163
  }
@@ -1386,7 +1452,23 @@ var AutoExecutor = class _AutoExecutor {
1386
1452
  method: "POST",
1387
1453
  parseResponseAsJson: false
1388
1454
  });
1389
- throw new WorkflowAbort(steps[0].stepName, steps[0]);
1455
+ throw new WorkflowAbort(waitStep.stepName, waitStep);
1456
+ }
1457
+ if (steps.length === 1 && lazySteps[0] instanceof LazyInvokeStep) {
1458
+ const invokeStep = steps[0];
1459
+ const lazyInvokeStep = lazySteps[0];
1460
+ await lazyInvokeStep.params.workflow.callback(
1461
+ {
1462
+ body: lazyInvokeStep.params.body,
1463
+ headers: lazyInvokeStep.params.headers,
1464
+ workflowRunId: lazyInvokeStep.params.workflowRunId,
1465
+ workflow: lazyInvokeStep.params.workflow,
1466
+ retries: lazyInvokeStep.params.retries
1467
+ },
1468
+ invokeStep,
1469
+ this.context
1470
+ );
1471
+ throw new WorkflowAbort(invokeStep.stepName, invokeStep);
1390
1472
  }
1391
1473
  const result = await this.context.qstashClient.batchJSON(
1392
1474
  steps.map((singleStep, index) => {
@@ -2303,6 +2385,13 @@ var WorkflowContext = class {
2303
2385
  return result;
2304
2386
  }
2305
2387
  }
2388
+ async invoke(stepName, settings) {
2389
+ const result = await this.addStep(new LazyInvokeStep(stepName, settings));
2390
+ return {
2391
+ ...result,
2392
+ body: result.body ? JSON.parse(result.body) : void 0
2393
+ };
2394
+ }
2306
2395
  /**
2307
2396
  * Cancel the current workflow run
2308
2397
  *
@@ -2380,31 +2469,6 @@ var WorkflowLogger = class _WorkflowLogger {
2380
2469
  }
2381
2470
  };
2382
2471
 
2383
- // src/utils.ts
2384
- var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
2385
- var NANOID_LENGTH = 21;
2386
- function getRandomInt() {
2387
- return Math.floor(Math.random() * NANOID_CHARS.length);
2388
- }
2389
- function nanoid() {
2390
- return Array.from({ length: NANOID_LENGTH }).map(() => NANOID_CHARS[getRandomInt()]).join("");
2391
- }
2392
- function getWorkflowRunId(id) {
2393
- return `wfr_${id ?? nanoid()}`;
2394
- }
2395
- function decodeBase64(base64) {
2396
- try {
2397
- const binString = atob(base64);
2398
- const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
2399
- return new TextDecoder().decode(intArray);
2400
- } catch (error) {
2401
- console.warn(
2402
- `Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
2403
- );
2404
- return atob(base64);
2405
- }
2406
- }
2407
-
2408
2472
  // src/serve/authorization.ts
2409
2473
  import { Client as Client2 } from "@upstash/qstash";
2410
2474
  var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowContext {
@@ -2850,8 +2914,8 @@ var serveBase = (routeFunction, telemetry, options) => {
2850
2914
  } else if (callReturnCheck.value === "continue-workflow") {
2851
2915
  const result = isFirstInvocation ? await triggerFirstInvocation({ workflowContext, useJSONContent, telemetry, debug }) : await triggerRouteFunction({
2852
2916
  onStep: async () => routeFunction(workflowContext),
2853
- onCleanup: async () => {
2854
- await triggerWorkflowDelete(workflowContext, debug);
2917
+ onCleanup: async (result2) => {
2918
+ await triggerWorkflowDelete(workflowContext, result2, debug);
2855
2919
  },
2856
2920
  onCancel: async () => {
2857
2921
  await makeCancelRequest(workflowContext.qstashClient.http, workflowRunId);
@@ -2902,11 +2966,12 @@ export {
2902
2966
  SDK_TELEMETRY,
2903
2967
  WorkflowError,
2904
2968
  WorkflowAbort,
2969
+ getWorkflowRunId,
2905
2970
  StepTypes,
2906
2971
  triggerFirstInvocation,
2972
+ getHeaders,
2907
2973
  WorkflowContext,
2908
2974
  WorkflowLogger,
2909
- getWorkflowRunId,
2910
2975
  serveBase,
2911
2976
  serve
2912
2977
  };
@@ -0,0 +1,95 @@
1
+ import {
2
+ WorkflowError,
3
+ getHeaders,
4
+ getWorkflowRunId
5
+ } from "./chunk-IWAW7GIG.mjs";
6
+
7
+ // src/serve/serve-many.ts
8
+ var serveManyBase = ({
9
+ workflows,
10
+ getWorkflowId
11
+ }) => {
12
+ const workflowIds = [];
13
+ const workflowMap = Object.fromEntries(
14
+ Object.entries(workflows).map((workflow) => {
15
+ const workflowId = workflow[0];
16
+ if (workflowIds.includes(workflowId)) {
17
+ throw new WorkflowError(
18
+ `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
19
+ );
20
+ }
21
+ if (workflowId.includes("/")) {
22
+ throw new WorkflowError(
23
+ `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
24
+ );
25
+ }
26
+ workflowIds.push(workflowId);
27
+ workflow[1].workflowId = workflowId;
28
+ return [workflowId, workflow[1].handler];
29
+ })
30
+ );
31
+ return {
32
+ handler: async (...params) => {
33
+ const pickedWorkflowId = getWorkflowId(...params);
34
+ if (!pickedWorkflowId) {
35
+ throw new WorkflowError(`Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`);
36
+ }
37
+ const workflow = workflowMap[pickedWorkflowId];
38
+ if (!workflow) {
39
+ throw new WorkflowError(`No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`);
40
+ }
41
+ return await workflow(...params);
42
+ }
43
+ };
44
+ };
45
+ var createInvokeCallback = (telemetry) => {
46
+ const invokeCallback = async (settings, invokeStep, context) => {
47
+ const { body, workflow, headers = {}, workflowRunId = getWorkflowRunId(), retries } = settings;
48
+ const { workflowId } = workflow;
49
+ if (!workflowId) {
50
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
51
+ }
52
+ const { headers: invokerHeaders } = getHeaders({
53
+ initHeaderValue: "false",
54
+ workflowRunId: context.workflowRunId,
55
+ workflowUrl: context.url,
56
+ userHeaders: context.headers,
57
+ failureUrl: context.failureUrl,
58
+ retries: context.retries,
59
+ telemetry
60
+ });
61
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
62
+ const newUrl = context.url.replace(/[^/]+$/, workflowId);
63
+ const { headers: triggerHeaders } = getHeaders({
64
+ initHeaderValue: "true",
65
+ workflowRunId,
66
+ workflowUrl: newUrl,
67
+ userHeaders: new Headers(headers),
68
+ retries,
69
+ telemetry
70
+ });
71
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
72
+ const request = {
73
+ body: JSON.stringify(body),
74
+ headers: Object.fromEntries(
75
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
76
+ ),
77
+ workflowRunId,
78
+ workflowUrl: context.url,
79
+ step: invokeStep
80
+ };
81
+ await context.qstashClient.publish({
82
+ headers: triggerHeaders,
83
+ method: "POST",
84
+ body: JSON.stringify(request),
85
+ url: newUrl
86
+ });
87
+ return void 0;
88
+ };
89
+ return invokeCallback;
90
+ };
91
+
92
+ export {
93
+ serveManyBase,
94
+ createInvokeCallback
95
+ };
package/cloudflare.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { R as RouteFunction, j as PublicServeOptions } from './types-Cuqlx2Cr.mjs';
1
+ import { R as RouteFunction, j as PublicServeOptions, t as InvokableWorkflow } from './types-C7Y7WUQd.mjs';
2
+ import { s as serveManyBase } from './serve-many-BlBvXfBS.mjs';
2
3
  import '@upstash/qstash';
3
4
  import 'zod';
4
5
  import 'ai';
@@ -23,7 +24,7 @@ type PagesHandlerArgs = [{
23
24
  */
24
25
  type WorkersHandlerArgs = [Request, Record<string, string | undefined>];
25
26
  /**
26
- * Serve method to serve a Upstash Workflow in a Nextjs project
27
+ * Serve method to serve a Upstash Workflow in a Cloudflare project
27
28
  *
28
29
  * See for options https://upstash.com/docs/qstash/workflows/basics/serve
29
30
  *
@@ -31,8 +32,12 @@ type WorkersHandlerArgs = [Request, Record<string, string | undefined>];
31
32
  * @param options workflow options
32
33
  * @returns
33
34
  */
34
- declare const serve: <TInitialPayload = unknown>(routeFunction: RouteFunction<TInitialPayload>, options?: PublicServeOptions<TInitialPayload>) => {
35
+ declare const serve: <TInitialPayload = unknown, TResult = unknown>(routeFunction: RouteFunction<TInitialPayload, TResult>, options?: PublicServeOptions<TInitialPayload>) => {
35
36
  fetch: (...args: PagesHandlerArgs | WorkersHandlerArgs) => Promise<Response>;
36
37
  };
38
+ declare const createWorkflow: <TInitialPayload, TResult>(routeFunction: RouteFunction<TInitialPayload, TResult>, options?: PublicServeOptions<TInitialPayload> | undefined) => InvokableWorkflow<TInitialPayload, TResult, Parameters<ReturnType<typeof serve<TInitialPayload, TResult>>["fetch"]>>;
39
+ declare const serveMany: (workflows: Parameters<typeof serveManyBase>[0]["workflows"]) => {
40
+ fetch: (...params: PagesHandlerArgs | WorkersHandlerArgs) => Promise<any>;
41
+ };
37
42
 
38
- export { type PagesHandlerArgs, type WorkersHandlerArgs, type WorkflowBindings, serve };
43
+ export { type PagesHandlerArgs, type WorkersHandlerArgs, type WorkflowBindings, createWorkflow, serve, serveMany };
package/cloudflare.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { R as RouteFunction, j as PublicServeOptions } from './types-Cuqlx2Cr.js';
1
+ import { R as RouteFunction, j as PublicServeOptions, t as InvokableWorkflow } from './types-C7Y7WUQd.js';
2
+ import { s as serveManyBase } from './serve-many-Dw-UUnH6.js';
2
3
  import '@upstash/qstash';
3
4
  import 'zod';
4
5
  import 'ai';
@@ -23,7 +24,7 @@ type PagesHandlerArgs = [{
23
24
  */
24
25
  type WorkersHandlerArgs = [Request, Record<string, string | undefined>];
25
26
  /**
26
- * Serve method to serve a Upstash Workflow in a Nextjs project
27
+ * Serve method to serve a Upstash Workflow in a Cloudflare project
27
28
  *
28
29
  * See for options https://upstash.com/docs/qstash/workflows/basics/serve
29
30
  *
@@ -31,8 +32,12 @@ type WorkersHandlerArgs = [Request, Record<string, string | undefined>];
31
32
  * @param options workflow options
32
33
  * @returns
33
34
  */
34
- declare const serve: <TInitialPayload = unknown>(routeFunction: RouteFunction<TInitialPayload>, options?: PublicServeOptions<TInitialPayload>) => {
35
+ declare const serve: <TInitialPayload = unknown, TResult = unknown>(routeFunction: RouteFunction<TInitialPayload, TResult>, options?: PublicServeOptions<TInitialPayload>) => {
35
36
  fetch: (...args: PagesHandlerArgs | WorkersHandlerArgs) => Promise<Response>;
36
37
  };
38
+ declare const createWorkflow: <TInitialPayload, TResult>(routeFunction: RouteFunction<TInitialPayload, TResult>, options?: PublicServeOptions<TInitialPayload> | undefined) => InvokableWorkflow<TInitialPayload, TResult, Parameters<ReturnType<typeof serve<TInitialPayload, TResult>>["fetch"]>>;
39
+ declare const serveMany: (workflows: Parameters<typeof serveManyBase>[0]["workflows"]) => {
40
+ fetch: (...params: PagesHandlerArgs | WorkersHandlerArgs) => Promise<any>;
41
+ };
37
42
 
38
- export { type PagesHandlerArgs, type WorkersHandlerArgs, type WorkflowBindings, serve };
43
+ export { type PagesHandlerArgs, type WorkersHandlerArgs, type WorkflowBindings, createWorkflow, serve, serveMany };