@upstash/workflow 1.3.3 → 1.4.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/react-router.js CHANGED
@@ -51,6 +51,9 @@ var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
51
51
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
52
52
  var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
53
53
 
54
+ // src/serve/index.ts
55
+ var import_qstash12 = require("@upstash/qstash");
56
+
54
57
  // src/client/utils.ts
55
58
  var import_qstash2 = require("@upstash/qstash");
56
59
 
@@ -781,6 +784,10 @@ var triggerFirstInvocation = async (params) => {
781
784
  workflowConfig: {
782
785
  workflowRunId: workflowContext.workflowRunId,
783
786
  workflowUrl: workflowContext.url,
787
+ // the run doesn't exist yet, so the trigger time is sent when the creation time
788
+ // is not known. serve and client.trigger set it on the context already, so a
789
+ // retry of this request carries the same value and is deduplicated.
790
+ workflowRunCreatedAt: workflowContext.workflowRunCreatedAt || Date.now(),
784
791
  failureUrl,
785
792
  retries,
786
793
  retryDelay,
@@ -884,7 +891,10 @@ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, disp
884
891
  path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
885
892
  method: "DELETE",
886
893
  parseResponseAsJson: false,
887
- body: JSON.stringify(result)
894
+ body: JSON.stringify(result),
895
+ // with the creation time, QStash deletes the run only if it is the same run.
896
+ // a late delete of a previous run can't delete a new run with the same id.
897
+ headers: workflowContext.workflowRunCreatedAt ? { [WORKFLOW_CREATED_AT_HEADER]: workflowContext.workflowRunCreatedAt.toString() } : void 0
888
898
  });
889
899
  await dispatchDebug?.("onInfo", {
890
900
  info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
@@ -961,6 +971,7 @@ ${atob(callbackMessage.body ?? "")}`
961
971
  const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
962
972
  const contentType = request.headers.get("Upstash-Workflow-ContentType");
963
973
  const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
974
+ const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
964
975
  if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
965
976
  throw new Error(
966
977
  `Missing info in callback message source header: ${JSON.stringify({
@@ -978,6 +989,7 @@ ${atob(callbackMessage.body ?? "")}`
978
989
  workflowConfig: {
979
990
  workflowRunId,
980
991
  workflowUrl,
992
+ workflowRunCreatedAt: Number(workflowRunCreatedAt ?? "0"),
981
993
  telemetry: telemetry2
982
994
  },
983
995
  invokeCount: Number(invokeCount)
@@ -1125,6 +1137,7 @@ var BaseLazyStep = class _BaseLazyStep {
1125
1137
  workflowConfig: {
1126
1138
  workflowRunId: context.workflowRunId,
1127
1139
  workflowUrl: context.url,
1140
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1128
1141
  useJSONContent: false,
1129
1142
  telemetry: telemetry2
1130
1143
  },
@@ -1452,6 +1465,8 @@ var LazyWaitEventStep = class extends BaseLazyStep {
1452
1465
  ) : {},
1453
1466
  // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
1454
1467
  "Upstash-Workflow-Runid": [context.workflowRunId],
1468
+ // same for WORKFLOW_CREATED_AT_HEADER, because Createdat -> CreatedAt:
1469
+ ...context.workflowRunCreatedAt ? { "Upstash-Workflow-Createdat": [context.workflowRunCreatedAt.toString()] } : {},
1455
1470
  [WORKFLOW_INIT_HEADER]: ["false"],
1456
1471
  [WORKFLOW_URL_HEADER]: [context.url],
1457
1472
  "Upstash-Workflow-CallType": ["step"]
@@ -1554,6 +1569,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1554
1569
  workflowConfig: {
1555
1570
  workflowRunId: context.workflowRunId,
1556
1571
  workflowUrl: context.url,
1572
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1557
1573
  telemetry: telemetry2,
1558
1574
  useJSONContent: false
1559
1575
  },
@@ -1563,6 +1579,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1563
1579
  invokerHeaders[key] = value;
1564
1580
  });
1565
1581
  invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1582
+ if (context.workflowRunCreatedAt) {
1583
+ invokerHeaders["Upstash-Workflow-Createdat"] = context.workflowRunCreatedAt.toString();
1584
+ }
1566
1585
  const request = {
1567
1586
  body: stringifyBody(this.params.body),
1568
1587
  headers: Object.fromEntries(
@@ -1591,6 +1610,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1591
1610
  workflowConfig: {
1592
1611
  workflowRunId: getWorkflowRunId(workflowRunId),
1593
1612
  workflowUrl: newUrl,
1613
+ // the invoked run doesn't exist yet, so the creation time of the invoker run is sent.
1614
+ // it separates the invocations made by different runs of the invoker.
1615
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1594
1616
  retries,
1595
1617
  retryDelay,
1596
1618
  telemetry: telemetry2,
@@ -1755,6 +1777,9 @@ var WorkflowHeaders = class {
1755
1777
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1756
1778
  ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
1757
1779
  };
1780
+ if (this.workflowConfig.workflowRunCreatedAt) {
1781
+ this.headers.rawHeaders[WORKFLOW_CREATED_AT_HEADER] = this.workflowConfig.workflowRunCreatedAt.toString();
1782
+ }
1758
1783
  if (this.stepInfo?.lazyStep.stepType !== "Call") {
1759
1784
  this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1760
1785
  }
@@ -1914,6 +1939,7 @@ var submitParallelSteps = async ({
1914
1939
  workflowConfig: {
1915
1940
  workflowRunId: context.workflowRunId,
1916
1941
  workflowUrl: context.url,
1942
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1917
1943
  telemetry: telemetry2
1918
1944
  },
1919
1945
  invokeCount
@@ -3415,7 +3441,13 @@ var createRegionalHandler = (environment, receiverConfig, region, clientOptions)
3415
3441
  const client = new import_qstash11.Client({
3416
3442
  ...clientOptions,
3417
3443
  baseUrl: clientEnv.QSTASH_URL,
3418
- token: clientEnv.QSTASH_TOKEN
3444
+ token: clientEnv.QSTASH_TOKEN,
3445
+ // Mirror the receiver's dev-mode decision so a single QSTASH_DEV=true points
3446
+ // the client at the local dev server too. Without this the QStash Client
3447
+ // re-derives dev mode from its own process.env, which is blind to the
3448
+ // Cloudflare worker `env` binding that actually carries QSTASH_DEV there.
3449
+ // An explicit devMode in the user's client config still wins.
3450
+ devMode: clientOptions?.devMode ?? isQStashDevModeEnabled(environment)
3419
3451
  });
3420
3452
  const receiver = getReceiver(environment, receiverConfig, region);
3421
3453
  return { client, receiver };
@@ -3465,7 +3497,9 @@ var getQStashHandlers = ({
3465
3497
  client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash11.Client({
3466
3498
  ...qstashClientOption,
3467
3499
  baseUrl: environment.QSTASH_URL,
3468
- token: environment.QSTASH_TOKEN
3500
+ token: environment.QSTASH_TOKEN,
3501
+ // Mirror the receiver's dev-mode decision (see createRegionalHandler).
3502
+ devMode: qstashClientOption?.devMode ?? isQStashDevModeEnabled(environment)
3469
3503
  }),
3470
3504
  receiver: getReceiver(environment, receiverConfig)
3471
3505
  }
@@ -3804,7 +3838,8 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3804
3838
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3805
3839
  const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
3806
3840
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3807
- const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
3841
+ const workflowRunCreatedAtHeader = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
3842
+ const workflowRunCreatedAt = workflowRunCreatedAtHeader ? Number(workflowRunCreatedAtHeader) : isFirstInvocation ? Date.now() : 0;
3808
3843
  const workflowContext = new WorkflowContext({
3809
3844
  qstashClient: regionalClient,
3810
3845
  workflowRunId,
@@ -3817,7 +3852,7 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3817
3852
  invokeCount,
3818
3853
  label,
3819
3854
  retried,
3820
- workflowRunCreatedAt: Number(workflowRunCreatedAt),
3855
+ workflowRunCreatedAt,
3821
3856
  middlewareManager
3822
3857
  });
3823
3858
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
@@ -3940,12 +3975,14 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3940
3975
  };
3941
3976
 
3942
3977
  // platforms/react-router.ts
3978
+ var import_qstash13 = require("@upstash/qstash");
3943
3979
  var telemetry = {
3944
3980
  sdk: SDK_TELEMETRY,
3945
3981
  framework: "react-router",
3946
3982
  runtime: process.versions.bun ? `bun@${process.versions.bun}/node@${process.version}` : `node@${process.version}`
3947
3983
  };
3948
3984
  var serve = (routeFunction, options) => {
3985
+ void (0, import_qstash13.startDevServer)();
3949
3986
  const { handler: serveHandler } = serveBase(
3950
3987
  routeFunction,
3951
3988
  telemetry,
package/react-router.mjs CHANGED
@@ -2,15 +2,17 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-WEQGCISK.mjs";
5
+ } from "./chunk-DRHEHWMS.mjs";
6
6
 
7
7
  // platforms/react-router.ts
8
+ import { startDevServer } from "@upstash/qstash";
8
9
  var telemetry = {
9
10
  sdk: SDK_TELEMETRY,
10
11
  framework: "react-router",
11
12
  runtime: process.versions.bun ? `bun@${process.versions.bun}/node@${process.version}` : `node@${process.version}`
12
13
  };
13
14
  var serve = (routeFunction, options) => {
15
+ void startDevServer();
14
16
  const { handler: serveHandler } = serveBase(
15
17
  routeFunction,
16
18
  telemetry,
package/solidjs.js CHANGED
@@ -24,6 +24,9 @@ __export(solidjs_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(solidjs_exports);
26
26
 
27
+ // src/serve/index.ts
28
+ var import_qstash12 = require("@upstash/qstash");
29
+
27
30
  // src/client/utils.ts
28
31
  var import_qstash2 = require("@upstash/qstash");
29
32
 
@@ -779,6 +782,10 @@ var triggerFirstInvocation = async (params) => {
779
782
  workflowConfig: {
780
783
  workflowRunId: workflowContext.workflowRunId,
781
784
  workflowUrl: workflowContext.url,
785
+ // the run doesn't exist yet, so the trigger time is sent when the creation time
786
+ // is not known. serve and client.trigger set it on the context already, so a
787
+ // retry of this request carries the same value and is deduplicated.
788
+ workflowRunCreatedAt: workflowContext.workflowRunCreatedAt || Date.now(),
782
789
  failureUrl,
783
790
  retries,
784
791
  retryDelay,
@@ -882,7 +889,10 @@ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, disp
882
889
  path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
883
890
  method: "DELETE",
884
891
  parseResponseAsJson: false,
885
- body: JSON.stringify(result)
892
+ body: JSON.stringify(result),
893
+ // with the creation time, QStash deletes the run only if it is the same run.
894
+ // a late delete of a previous run can't delete a new run with the same id.
895
+ headers: workflowContext.workflowRunCreatedAt ? { [WORKFLOW_CREATED_AT_HEADER]: workflowContext.workflowRunCreatedAt.toString() } : void 0
886
896
  });
887
897
  await dispatchDebug?.("onInfo", {
888
898
  info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
@@ -959,6 +969,7 @@ ${atob(callbackMessage.body ?? "")}`
959
969
  const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
960
970
  const contentType = request.headers.get("Upstash-Workflow-ContentType");
961
971
  const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
972
+ const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
962
973
  if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
963
974
  throw new Error(
964
975
  `Missing info in callback message source header: ${JSON.stringify({
@@ -976,6 +987,7 @@ ${atob(callbackMessage.body ?? "")}`
976
987
  workflowConfig: {
977
988
  workflowRunId,
978
989
  workflowUrl,
990
+ workflowRunCreatedAt: Number(workflowRunCreatedAt ?? "0"),
979
991
  telemetry
980
992
  },
981
993
  invokeCount: Number(invokeCount)
@@ -1123,6 +1135,7 @@ var BaseLazyStep = class _BaseLazyStep {
1123
1135
  workflowConfig: {
1124
1136
  workflowRunId: context.workflowRunId,
1125
1137
  workflowUrl: context.url,
1138
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1126
1139
  useJSONContent: false,
1127
1140
  telemetry
1128
1141
  },
@@ -1450,6 +1463,8 @@ var LazyWaitEventStep = class extends BaseLazyStep {
1450
1463
  ) : {},
1451
1464
  // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
1452
1465
  "Upstash-Workflow-Runid": [context.workflowRunId],
1466
+ // same for WORKFLOW_CREATED_AT_HEADER, because Createdat -> CreatedAt:
1467
+ ...context.workflowRunCreatedAt ? { "Upstash-Workflow-Createdat": [context.workflowRunCreatedAt.toString()] } : {},
1453
1468
  [WORKFLOW_INIT_HEADER]: ["false"],
1454
1469
  [WORKFLOW_URL_HEADER]: [context.url],
1455
1470
  "Upstash-Workflow-CallType": ["step"]
@@ -1552,6 +1567,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1552
1567
  workflowConfig: {
1553
1568
  workflowRunId: context.workflowRunId,
1554
1569
  workflowUrl: context.url,
1570
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1555
1571
  telemetry,
1556
1572
  useJSONContent: false
1557
1573
  },
@@ -1561,6 +1577,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1561
1577
  invokerHeaders[key] = value;
1562
1578
  });
1563
1579
  invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1580
+ if (context.workflowRunCreatedAt) {
1581
+ invokerHeaders["Upstash-Workflow-Createdat"] = context.workflowRunCreatedAt.toString();
1582
+ }
1564
1583
  const request = {
1565
1584
  body: stringifyBody(this.params.body),
1566
1585
  headers: Object.fromEntries(
@@ -1589,6 +1608,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1589
1608
  workflowConfig: {
1590
1609
  workflowRunId: getWorkflowRunId(workflowRunId),
1591
1610
  workflowUrl: newUrl,
1611
+ // the invoked run doesn't exist yet, so the creation time of the invoker run is sent.
1612
+ // it separates the invocations made by different runs of the invoker.
1613
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1592
1614
  retries,
1593
1615
  retryDelay,
1594
1616
  telemetry,
@@ -1753,6 +1775,9 @@ var WorkflowHeaders = class {
1753
1775
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1754
1776
  ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
1755
1777
  };
1778
+ if (this.workflowConfig.workflowRunCreatedAt) {
1779
+ this.headers.rawHeaders[WORKFLOW_CREATED_AT_HEADER] = this.workflowConfig.workflowRunCreatedAt.toString();
1780
+ }
1756
1781
  if (this.stepInfo?.lazyStep.stepType !== "Call") {
1757
1782
  this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1758
1783
  }
@@ -1912,6 +1937,7 @@ var submitParallelSteps = async ({
1912
1937
  workflowConfig: {
1913
1938
  workflowRunId: context.workflowRunId,
1914
1939
  workflowUrl: context.url,
1940
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1915
1941
  telemetry
1916
1942
  },
1917
1943
  invokeCount
@@ -3346,7 +3372,13 @@ var createRegionalHandler = (environment, receiverConfig, region, clientOptions)
3346
3372
  const client = new import_qstash11.Client({
3347
3373
  ...clientOptions,
3348
3374
  baseUrl: clientEnv.QSTASH_URL,
3349
- token: clientEnv.QSTASH_TOKEN
3375
+ token: clientEnv.QSTASH_TOKEN,
3376
+ // Mirror the receiver's dev-mode decision so a single QSTASH_DEV=true points
3377
+ // the client at the local dev server too. Without this the QStash Client
3378
+ // re-derives dev mode from its own process.env, which is blind to the
3379
+ // Cloudflare worker `env` binding that actually carries QSTASH_DEV there.
3380
+ // An explicit devMode in the user's client config still wins.
3381
+ devMode: clientOptions?.devMode ?? isQStashDevModeEnabled(environment)
3350
3382
  });
3351
3383
  const receiver = getReceiver(environment, receiverConfig, region);
3352
3384
  return { client, receiver };
@@ -3396,7 +3428,9 @@ var getQStashHandlers = ({
3396
3428
  client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash11.Client({
3397
3429
  ...qstashClientOption,
3398
3430
  baseUrl: environment.QSTASH_URL,
3399
- token: environment.QSTASH_TOKEN
3431
+ token: environment.QSTASH_TOKEN,
3432
+ // Mirror the receiver's dev-mode decision (see createRegionalHandler).
3433
+ devMode: qstashClientOption?.devMode ?? isQStashDevModeEnabled(environment)
3400
3434
  }),
3401
3435
  receiver: getReceiver(environment, receiverConfig)
3402
3436
  }
@@ -3735,7 +3769,8 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3735
3769
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3736
3770
  const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
3737
3771
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3738
- const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
3772
+ const workflowRunCreatedAtHeader = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
3773
+ const workflowRunCreatedAt = workflowRunCreatedAtHeader ? Number(workflowRunCreatedAtHeader) : isFirstInvocation ? Date.now() : 0;
3739
3774
  const workflowContext = new WorkflowContext({
3740
3775
  qstashClient: regionalClient,
3741
3776
  workflowRunId,
@@ -3748,7 +3783,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3748
3783
  invokeCount,
3749
3784
  label,
3750
3785
  retried,
3751
- workflowRunCreatedAt: Number(workflowRunCreatedAt),
3786
+ workflowRunCreatedAt,
3752
3787
  middlewareManager
3753
3788
  });
3754
3789
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
@@ -3871,7 +3906,9 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3871
3906
  };
3872
3907
 
3873
3908
  // platforms/solidjs.ts
3909
+ var import_qstash13 = require("@upstash/qstash");
3874
3910
  var serve = (routeFunction, options) => {
3911
+ void (0, import_qstash13.startDevServer)();
3875
3912
  const telemetry = {
3876
3913
  sdk: SDK_TELEMETRY,
3877
3914
  framework: "solidjs",
package/solidjs.mjs CHANGED
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase
4
- } from "./chunk-WEQGCISK.mjs";
4
+ } from "./chunk-DRHEHWMS.mjs";
5
5
 
6
6
  // platforms/solidjs.ts
7
+ import { startDevServer } from "@upstash/qstash";
7
8
  var serve = (routeFunction, options) => {
9
+ void startDevServer();
8
10
  const telemetry = {
9
11
  sdk: SDK_TELEMETRY,
10
12
  framework: "solidjs",
package/svelte.js CHANGED
@@ -26,6 +26,9 @@ __export(svelte_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(svelte_exports);
28
28
 
29
+ // src/serve/index.ts
30
+ var import_qstash12 = require("@upstash/qstash");
31
+
29
32
  // src/client/utils.ts
30
33
  var import_qstash2 = require("@upstash/qstash");
31
34
 
@@ -781,6 +784,10 @@ var triggerFirstInvocation = async (params) => {
781
784
  workflowConfig: {
782
785
  workflowRunId: workflowContext.workflowRunId,
783
786
  workflowUrl: workflowContext.url,
787
+ // the run doesn't exist yet, so the trigger time is sent when the creation time
788
+ // is not known. serve and client.trigger set it on the context already, so a
789
+ // retry of this request carries the same value and is deduplicated.
790
+ workflowRunCreatedAt: workflowContext.workflowRunCreatedAt || Date.now(),
784
791
  failureUrl,
785
792
  retries,
786
793
  retryDelay,
@@ -884,7 +891,10 @@ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, disp
884
891
  path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
885
892
  method: "DELETE",
886
893
  parseResponseAsJson: false,
887
- body: JSON.stringify(result)
894
+ body: JSON.stringify(result),
895
+ // with the creation time, QStash deletes the run only if it is the same run.
896
+ // a late delete of a previous run can't delete a new run with the same id.
897
+ headers: workflowContext.workflowRunCreatedAt ? { [WORKFLOW_CREATED_AT_HEADER]: workflowContext.workflowRunCreatedAt.toString() } : void 0
888
898
  });
889
899
  await dispatchDebug?.("onInfo", {
890
900
  info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
@@ -961,6 +971,7 @@ ${atob(callbackMessage.body ?? "")}`
961
971
  const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
962
972
  const contentType = request.headers.get("Upstash-Workflow-ContentType");
963
973
  const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
974
+ const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
964
975
  if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
965
976
  throw new Error(
966
977
  `Missing info in callback message source header: ${JSON.stringify({
@@ -978,6 +989,7 @@ ${atob(callbackMessage.body ?? "")}`
978
989
  workflowConfig: {
979
990
  workflowRunId,
980
991
  workflowUrl,
992
+ workflowRunCreatedAt: Number(workflowRunCreatedAt ?? "0"),
981
993
  telemetry: telemetry2
982
994
  },
983
995
  invokeCount: Number(invokeCount)
@@ -1125,6 +1137,7 @@ var BaseLazyStep = class _BaseLazyStep {
1125
1137
  workflowConfig: {
1126
1138
  workflowRunId: context.workflowRunId,
1127
1139
  workflowUrl: context.url,
1140
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1128
1141
  useJSONContent: false,
1129
1142
  telemetry: telemetry2
1130
1143
  },
@@ -1452,6 +1465,8 @@ var LazyWaitEventStep = class extends BaseLazyStep {
1452
1465
  ) : {},
1453
1466
  // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
1454
1467
  "Upstash-Workflow-Runid": [context.workflowRunId],
1468
+ // same for WORKFLOW_CREATED_AT_HEADER, because Createdat -> CreatedAt:
1469
+ ...context.workflowRunCreatedAt ? { "Upstash-Workflow-Createdat": [context.workflowRunCreatedAt.toString()] } : {},
1455
1470
  [WORKFLOW_INIT_HEADER]: ["false"],
1456
1471
  [WORKFLOW_URL_HEADER]: [context.url],
1457
1472
  "Upstash-Workflow-CallType": ["step"]
@@ -1554,6 +1569,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
1554
1569
  workflowConfig: {
1555
1570
  workflowRunId: context.workflowRunId,
1556
1571
  workflowUrl: context.url,
1572
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1557
1573
  telemetry: telemetry2,
1558
1574
  useJSONContent: false
1559
1575
  },
@@ -1563,6 +1579,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1563
1579
  invokerHeaders[key] = value;
1564
1580
  });
1565
1581
  invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1582
+ if (context.workflowRunCreatedAt) {
1583
+ invokerHeaders["Upstash-Workflow-Createdat"] = context.workflowRunCreatedAt.toString();
1584
+ }
1566
1585
  const request = {
1567
1586
  body: stringifyBody(this.params.body),
1568
1587
  headers: Object.fromEntries(
@@ -1591,6 +1610,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1591
1610
  workflowConfig: {
1592
1611
  workflowRunId: getWorkflowRunId(workflowRunId),
1593
1612
  workflowUrl: newUrl,
1613
+ // the invoked run doesn't exist yet, so the creation time of the invoker run is sent.
1614
+ // it separates the invocations made by different runs of the invoker.
1615
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1594
1616
  retries,
1595
1617
  retryDelay,
1596
1618
  telemetry: telemetry2,
@@ -1755,6 +1777,9 @@ var WorkflowHeaders = class {
1755
1777
  [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1756
1778
  ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
1757
1779
  };
1780
+ if (this.workflowConfig.workflowRunCreatedAt) {
1781
+ this.headers.rawHeaders[WORKFLOW_CREATED_AT_HEADER] = this.workflowConfig.workflowRunCreatedAt.toString();
1782
+ }
1758
1783
  if (this.stepInfo?.lazyStep.stepType !== "Call") {
1759
1784
  this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1760
1785
  }
@@ -1914,6 +1939,7 @@ var submitParallelSteps = async ({
1914
1939
  workflowConfig: {
1915
1940
  workflowRunId: context.workflowRunId,
1916
1941
  workflowUrl: context.url,
1942
+ workflowRunCreatedAt: context.workflowRunCreatedAt,
1917
1943
  telemetry: telemetry2
1918
1944
  },
1919
1945
  invokeCount
@@ -3415,7 +3441,13 @@ var createRegionalHandler = (environment, receiverConfig, region, clientOptions)
3415
3441
  const client = new import_qstash11.Client({
3416
3442
  ...clientOptions,
3417
3443
  baseUrl: clientEnv.QSTASH_URL,
3418
- token: clientEnv.QSTASH_TOKEN
3444
+ token: clientEnv.QSTASH_TOKEN,
3445
+ // Mirror the receiver's dev-mode decision so a single QSTASH_DEV=true points
3446
+ // the client at the local dev server too. Without this the QStash Client
3447
+ // re-derives dev mode from its own process.env, which is blind to the
3448
+ // Cloudflare worker `env` binding that actually carries QSTASH_DEV there.
3449
+ // An explicit devMode in the user's client config still wins.
3450
+ devMode: clientOptions?.devMode ?? isQStashDevModeEnabled(environment)
3419
3451
  });
3420
3452
  const receiver = getReceiver(environment, receiverConfig, region);
3421
3453
  return { client, receiver };
@@ -3465,7 +3497,9 @@ var getQStashHandlers = ({
3465
3497
  client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash11.Client({
3466
3498
  ...qstashClientOption,
3467
3499
  baseUrl: environment.QSTASH_URL,
3468
- token: environment.QSTASH_TOKEN
3500
+ token: environment.QSTASH_TOKEN,
3501
+ // Mirror the receiver's dev-mode decision (see createRegionalHandler).
3502
+ devMode: qstashClientOption?.devMode ?? isQStashDevModeEnabled(environment)
3469
3503
  }),
3470
3504
  receiver: getReceiver(environment, receiverConfig)
3471
3505
  }
@@ -3804,7 +3838,8 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3804
3838
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3805
3839
  const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
3806
3840
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3807
- const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
3841
+ const workflowRunCreatedAtHeader = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
3842
+ const workflowRunCreatedAt = workflowRunCreatedAtHeader ? Number(workflowRunCreatedAtHeader) : isFirstInvocation ? Date.now() : 0;
3808
3843
  const workflowContext = new WorkflowContext({
3809
3844
  qstashClient: regionalClient,
3810
3845
  workflowRunId,
@@ -3817,7 +3852,7 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3817
3852
  invokeCount,
3818
3853
  label,
3819
3854
  retried,
3820
- workflowRunCreatedAt: Number(workflowRunCreatedAt),
3855
+ workflowRunCreatedAt,
3821
3856
  middlewareManager
3822
3857
  });
3823
3858
  const authCheck = await DisabledWorkflowContext.tryAuthentication(
@@ -3940,11 +3975,13 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3940
3975
  };
3941
3976
 
3942
3977
  // platforms/svelte.ts
3978
+ var import_qstash13 = require("@upstash/qstash");
3943
3979
  var telemetry = {
3944
3980
  sdk: SDK_TELEMETRY,
3945
3981
  framework: "svelte"
3946
3982
  };
3947
3983
  var serve = (routeFunction, options) => {
3984
+ void (0, import_qstash13.startDevServer)();
3948
3985
  const handler = async ({ request }) => {
3949
3986
  const { handler: serveHandler } = serveBase(
3950
3987
  routeFunction,
package/svelte.mjs CHANGED
@@ -2,14 +2,16 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-WEQGCISK.mjs";
5
+ } from "./chunk-DRHEHWMS.mjs";
6
6
 
7
7
  // platforms/svelte.ts
8
+ import { startDevServer } from "@upstash/qstash";
8
9
  var telemetry = {
9
10
  sdk: SDK_TELEMETRY,
10
11
  framework: "svelte"
11
12
  };
12
13
  var serve = (routeFunction, options) => {
14
+ void startDevServer();
13
15
  const handler = async ({ request }) => {
14
16
  const { handler: serveHandler } = serveBase(
15
17
  routeFunction,