@upstash/workflow 1.0.0 → 1.1.0-rc1

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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as StepType, R as RawStep, W as WorkflowMiddleware, a as RouteFunction, b as WorkflowServeOptions, T as Telemetry, N as NotifyResponse, c as Waiter } from './types-ByzQdZjb.js';
2
- export { A as AsyncStepFunction, C as CallResponse, v as CallSettings, D as DetailedFinishCondition, t as Duration, E as ExclusiveValidationOptions, o as FailureFunctionPayload, F as FinishCondition, H as HeaderParams, x as InvokableWorkflow, w as InvokeStepResponse, I as InvokeWorkflowRequest, L as LazyInvokeStepParams, s as NotifyStepResponse, P as ParallelCallState, p as RequiredExceptFields, l as Step, n as StepFunction, k as StepTypes, m as SyncStepFunction, u as WaitEventOptions, q as WaitRequest, r as WaitStepResponse, e as WorkflowAbort, i as WorkflowClient, h as WorkflowContext, d as WorkflowError, f as WorkflowNonRetryableError, j as WorkflowReceiver, g as WorkflowRetryAfterError } from './types-ByzQdZjb.js';
1
+ import { S as StepType, R as RawStep, W as WorkflowMiddleware, a as WorkflowClient, b as WorkflowReceiver, c as RouteFunction, d as WorkflowServeOptions, T as Telemetry, N as NotifyResponse, e as Waiter } from './types-pEje3VEB.js';
2
+ export { A as AsyncStepFunction, C as CallResponse, v as CallSettings, D as DetailedFinishCondition, t as Duration, E as ExclusiveValidationOptions, o as FailureFunctionPayload, F as FinishCondition, H as HeaderParams, x as InvokableWorkflow, w as InvokeStepResponse, I as InvokeWorkflowRequest, L as LazyInvokeStepParams, s as NotifyStepResponse, P as ParallelCallState, Q as QStashClientExtraConfig, p as RequiredExceptFields, l as Step, n as StepFunction, k as StepTypes, m as SyncStepFunction, u as WaitEventOptions, q as WaitRequest, r as WaitStepResponse, g as WorkflowAbort, j as WorkflowContext, f as WorkflowError, h as WorkflowNonRetryableError, i as WorkflowRetryAfterError } from './types-pEje3VEB.js';
3
3
  import { HTTPMethods, FlowControl, PublishRequest, Client as Client$1 } from '@upstash/qstash';
4
4
  import 'zod';
5
5
 
@@ -434,6 +434,27 @@ type DLQResumeRestartResponse = {
434
434
 
435
435
  declare const loggingMiddleware: WorkflowMiddleware<unknown, unknown>;
436
436
 
437
+ declare const VALID_REGIONS: readonly ["EU_CENTRAL_1", "US_EAST_1"];
438
+ type QStashRegion = (typeof VALID_REGIONS)[number];
439
+ /**
440
+ * Regional handler containing client and optional receiver
441
+ */
442
+ type RegionalHandler = {
443
+ client: WorkflowClient;
444
+ receiver?: WorkflowReceiver;
445
+ };
446
+ /**
447
+ * QStash handlers for single or multi-region mode
448
+ */
449
+ type QStashHandlers = {
450
+ mode: "single-region";
451
+ handlers: RegionalHandler;
452
+ } | {
453
+ mode: "multi-region";
454
+ handlers: Record<QStashRegion, RegionalHandler>;
455
+ defaultRegion: QStashRegion;
456
+ };
457
+
437
458
  type ResponseData = {
438
459
  text: string;
439
460
  status: number;
@@ -452,6 +473,10 @@ type InternalServeOptions<TResponse extends Response = Response> = {
452
473
  * in `triggerFirstInvocation`.
453
474
  */
454
475
  useJSONContent: boolean;
476
+ /**
477
+ * QStash handlers for single or multi-region mode
478
+ */
479
+ qstashHandlers: QStashHandlers;
455
480
  };
456
481
 
457
482
  /**
@@ -884,4 +909,4 @@ declare class Client {
884
909
  get dlq(): DLQ;
885
910
  }
886
911
 
887
- export { Client, type DLQResumeRestartOptions, type DLQResumeRestartResponse, NotifyResponse, RawStep, RouteFunction, type StepError, type StepLog, StepType, Telemetry, type TriggerOptions, Waiter, WorkflowMiddleware, type WorkflowRunLog, type WorkflowRunLogs, WorkflowServeOptions, loggingMiddleware, serve };
912
+ export { Client, type DLQResumeRestartOptions, type DLQResumeRestartResponse, NotifyResponse, RawStep, RouteFunction, type StepError, type StepLog, StepType, Telemetry, type TriggerOptions, Waiter, WorkflowClient, WorkflowMiddleware, WorkflowReceiver, type WorkflowRunLog, type WorkflowRunLogs, WorkflowServeOptions, loggingMiddleware, serve };
package/index.js CHANGED
@@ -855,7 +855,7 @@ var recreateUserHeaders = (headers) => {
855
855
  const pairs = headers.entries();
856
856
  for (const [header, value] of pairs) {
857
857
  const headerLowerCase = header.toLowerCase();
858
- const isUserHeader = !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
858
+ const isUserHeader = headerLowerCase !== "upstash-region" && !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
859
859
  !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
860
860
  headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
861
861
  headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
@@ -3160,9 +3160,154 @@ var handleFailure = async ({
3160
3160
  }
3161
3161
  };
3162
3162
 
3163
- // src/serve/options.ts
3163
+ // src/serve/multi-region/handlers.ts
3164
3164
  var import_qstash10 = require("@upstash/qstash");
3165
- var import_qstash11 = require("@upstash/qstash");
3165
+
3166
+ // src/serve/multi-region/utils.ts
3167
+ var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
3168
+ var getRegionFromEnvironment = (environment) => {
3169
+ const region = environment.QSTASH_REGION;
3170
+ return normalizeRegionHeader(region);
3171
+ };
3172
+ function readEnvironmentVariables(environmentVariables, environment, region) {
3173
+ const result = {};
3174
+ for (const variable of environmentVariables) {
3175
+ const key = region ? `${region}_${variable}` : variable;
3176
+ result[variable] = environment[key];
3177
+ }
3178
+ return result;
3179
+ }
3180
+ function readClientEnvironmentVariables(environment, region) {
3181
+ return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
3182
+ }
3183
+ function readReceiverEnvironmentVariables(environment, region) {
3184
+ return readEnvironmentVariables(
3185
+ ["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
3186
+ environment,
3187
+ region
3188
+ );
3189
+ }
3190
+ function normalizeRegionHeader(region) {
3191
+ if (!region) {
3192
+ return void 0;
3193
+ }
3194
+ region = region.replaceAll("-", "_").toUpperCase();
3195
+ if (VALID_REGIONS.includes(region)) {
3196
+ return region;
3197
+ }
3198
+ console.warn(
3199
+ `[Upstash Workflow] Invalid UPSTASH-REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
3200
+ ", "
3201
+ )}.`
3202
+ );
3203
+ return void 0;
3204
+ }
3205
+
3206
+ // src/serve/multi-region/handlers.ts
3207
+ var getHandlersForRequest = (qstashHandlers, regionHeader, isFirstInvocation) => {
3208
+ if (qstashHandlers.mode === "single-region") {
3209
+ return qstashHandlers.handlers;
3210
+ }
3211
+ let targetRegion;
3212
+ if (isFirstInvocation && !regionHeader) {
3213
+ targetRegion = qstashHandlers.defaultRegion;
3214
+ } else {
3215
+ const normalizedRegion = regionHeader ? normalizeRegionHeader(regionHeader) : void 0;
3216
+ targetRegion = normalizedRegion ?? qstashHandlers.defaultRegion;
3217
+ }
3218
+ const handler = qstashHandlers.handlers[targetRegion];
3219
+ if (!handler) {
3220
+ console.warn(
3221
+ `[Upstash Workflow] No handler found for region "${targetRegion}". Falling back to default region.`
3222
+ );
3223
+ return qstashHandlers.handlers[qstashHandlers.defaultRegion];
3224
+ }
3225
+ return handler;
3226
+ };
3227
+ var createRegionalHandler = (environment, receiverConfig, region, clientOptions) => {
3228
+ const clientEnv = readClientEnvironmentVariables(environment, region);
3229
+ const client = new import_qstash10.Client({
3230
+ ...clientOptions,
3231
+ baseUrl: clientEnv.QSTASH_URL,
3232
+ token: clientEnv.QSTASH_TOKEN
3233
+ });
3234
+ const receiver = getReceiver(environment, receiverConfig, region);
3235
+ return { client, receiver };
3236
+ };
3237
+ var shouldUseMultiRegionMode = (environment, qstashClientOption) => {
3238
+ const hasRegionEnv = Boolean(getRegionFromEnvironment(environment));
3239
+ if (hasRegionEnv && (!qstashClientOption || !("http" in qstashClientOption))) {
3240
+ return {
3241
+ isMultiRegion: true,
3242
+ defaultRegion: getRegionFromEnvironment(environment),
3243
+ clientOptions: qstashClientOption
3244
+ };
3245
+ } else {
3246
+ return { isMultiRegion: false };
3247
+ }
3248
+ };
3249
+ var getQStashHandlers = ({
3250
+ environment,
3251
+ qstashClientOption,
3252
+ receiverConfig
3253
+ }) => {
3254
+ const multiRegion = shouldUseMultiRegionMode(environment, qstashClientOption);
3255
+ if (multiRegion.isMultiRegion) {
3256
+ const regions = ["US_EAST_1", "EU_CENTRAL_1"];
3257
+ const handlers = {};
3258
+ for (const region of regions) {
3259
+ try {
3260
+ handlers[region] = createRegionalHandler(
3261
+ environment,
3262
+ receiverConfig,
3263
+ region,
3264
+ multiRegion.clientOptions
3265
+ );
3266
+ } catch (error) {
3267
+ console.warn(`[Upstash Workflow] Failed to create handler for region ${region}:`, error);
3268
+ }
3269
+ }
3270
+ return {
3271
+ mode: "multi-region",
3272
+ handlers,
3273
+ defaultRegion: multiRegion.defaultRegion
3274
+ };
3275
+ } else {
3276
+ return {
3277
+ mode: "single-region",
3278
+ handlers: {
3279
+ client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash10.Client({
3280
+ ...qstashClientOption,
3281
+ baseUrl: environment.QSTASH_URL,
3282
+ token: environment.QSTASH_TOKEN
3283
+ }),
3284
+ receiver: getReceiver(environment, receiverConfig)
3285
+ }
3286
+ };
3287
+ }
3288
+ };
3289
+ var getReceiver = (environment, receiverConfig, region) => {
3290
+ if (typeof receiverConfig === "string") {
3291
+ if (receiverConfig === "set-to-undefined") {
3292
+ return void 0;
3293
+ }
3294
+ const receiverEnv = readReceiverEnvironmentVariables(environment, region);
3295
+ return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash10.Receiver({
3296
+ currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
3297
+ nextSigningKey: receiverEnv.QSTASH_NEXT_SIGNING_KEY
3298
+ }) : void 0;
3299
+ } else {
3300
+ return receiverConfig;
3301
+ }
3302
+ };
3303
+ var getQStashHandlerOptions = (...params) => {
3304
+ const handlers = getQStashHandlers(...params);
3305
+ return {
3306
+ qstashHandlers: handlers,
3307
+ defaultReceiver: handlers.mode === "single-region" ? handlers.handlers.receiver : handlers.handlers[handlers.defaultRegion].receiver,
3308
+ defaultClient: handlers.mode === "single-region" ? handlers.handlers.client : handlers.handlers[handlers.defaultRegion].client
3309
+ };
3310
+ };
3166
3311
 
3167
3312
  // src/middleware/middleware.ts
3168
3313
  var WorkflowMiddleware = class {
@@ -3301,14 +3446,17 @@ var createResponseData = (workflowRunId, detailedFinishCondition) => {
3301
3446
  };
3302
3447
  var processOptions = (options, internalOptions) => {
3303
3448
  const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
3304
- const receiverEnvironmentVariablesSet = Boolean(
3305
- environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
3306
- );
3449
+ const {
3450
+ qstashHandlers,
3451
+ defaultClient: qstashClient,
3452
+ defaultReceiver: receiver
3453
+ } = getQStashHandlerOptions({
3454
+ environment,
3455
+ qstashClientOption: options?.qstashClient,
3456
+ receiverConfig: options && "receiver" in options ? options.receiver ? options.receiver : "set-to-undefined" : "not-set"
3457
+ });
3307
3458
  return {
3308
- qstashClient: options?.qstashClient ?? new import_qstash11.Client({
3309
- baseUrl: environment.QSTASH_URL,
3310
- token: environment.QSTASH_TOKEN
3311
- }),
3459
+ qstashClient,
3312
3460
  initialPayloadParser: (initialRequest) => {
3313
3461
  if (!initialRequest) {
3314
3462
  return void 0;
@@ -3323,10 +3471,7 @@ var processOptions = (options, internalOptions) => {
3323
3471
  throw error;
3324
3472
  }
3325
3473
  },
3326
- receiver: receiverEnvironmentVariablesSet ? new import_qstash10.Receiver({
3327
- currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
3328
- nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
3329
- }) : void 0,
3474
+ receiver,
3330
3475
  baseUrl: environment.UPSTASH_WORKFLOW_URL,
3331
3476
  env: environment,
3332
3477
  disableTelemetry: false,
@@ -3340,7 +3485,8 @@ var processOptions = (options, internalOptions) => {
3340
3485
  headers: responseData.headers
3341
3486
  });
3342
3487
  }),
3343
- useJSONContent: internalOptions?.useJSONContent ?? false
3488
+ useJSONContent: internalOptions?.useJSONContent ?? false,
3489
+ qstashHandlers
3344
3490
  }
3345
3491
  };
3346
3492
  };
@@ -3373,10 +3519,8 @@ var AUTH_FAIL_MESSAGE = `Failed to authenticate Workflow request. If this is une
3373
3519
  // src/serve/index.ts
3374
3520
  var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3375
3521
  const {
3376
- qstashClient,
3377
3522
  initialPayloadParser,
3378
3523
  url,
3379
- receiver,
3380
3524
  failureFunction,
3381
3525
  baseUrl,
3382
3526
  env,
@@ -3396,9 +3540,15 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3396
3540
  baseUrl,
3397
3541
  middlewareManager.dispatchDebug.bind(middlewareManager)
3398
3542
  );
3399
- const requestPayload = await getPayload(request) ?? "";
3400
- await verifyRequest(requestPayload, request.headers.get("upstash-signature"), receiver);
3401
3543
  const { isFirstInvocation, workflowRunId, unknownSdk } = validateRequest(request);
3544
+ const regionHeader = request.headers.get("upstash-region");
3545
+ const { client: regionalClient, receiver: regionalReceiver } = getHandlersForRequest(
3546
+ internal.qstashHandlers,
3547
+ regionHeader,
3548
+ isFirstInvocation
3549
+ );
3550
+ const requestPayload = await getPayload(request) ?? "";
3551
+ await verifyRequest(requestPayload, request.headers.get("upstash-signature"), regionalReceiver);
3402
3552
  middlewareManager.assignWorkflowRunId(workflowRunId);
3403
3553
  await middlewareManager.dispatchDebug("onInfo", {
3404
3554
  info: `Run id identified. isFirstInvocation: ${isFirstInvocation}, unknownSdk: ${unknownSdk}`
@@ -3408,7 +3558,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3408
3558
  isFirstInvocation,
3409
3559
  unknownSdk,
3410
3560
  workflowRunId,
3411
- requester: qstashClient.http,
3561
+ requester: regionalClient.http,
3412
3562
  messageId: request.headers.get("upstash-message-id"),
3413
3563
  dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
3414
3564
  });
@@ -3429,7 +3579,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3429
3579
  const failureCheck = await handleFailure({
3430
3580
  request,
3431
3581
  requestPayload,
3432
- qstashClient,
3582
+ qstashClient: regionalClient,
3433
3583
  initialPayloadParser,
3434
3584
  routeFunction,
3435
3585
  failureFunction,
@@ -3461,7 +3611,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3461
3611
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3462
3612
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3463
3613
  const workflowContext = new WorkflowContext({
3464
- qstashClient,
3614
+ qstashClient: regionalClient,
3465
3615
  workflowRunId,
3466
3616
  initialPayload: initialPayloadParser(rawInitialPayload),
3467
3617
  headers: recreateUserHeaders(request.headers),
@@ -3492,7 +3642,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3492
3642
  const callReturnCheck = await handleThirdPartyCallResult({
3493
3643
  request,
3494
3644
  requestPayload: rawInitialPayload,
3495
- client: qstashClient,
3645
+ client: regionalClient,
3496
3646
  workflowUrl,
3497
3647
  telemetry,
3498
3648
  middlewareManager
@@ -3601,7 +3751,7 @@ var serve = (routeFunction, options) => {
3601
3751
  };
3602
3752
 
3603
3753
  // src/client/index.ts
3604
- var import_qstash12 = require("@upstash/qstash");
3754
+ var import_qstash11 = require("@upstash/qstash");
3605
3755
 
3606
3756
  // src/client/dlq.ts
3607
3757
  var DLQ = class _DLQ {
@@ -3713,12 +3863,7 @@ var DLQ = class _DLQ {
3713
3863
  var Client4 = class {
3714
3864
  client;
3715
3865
  constructor(clientConfig) {
3716
- if (!clientConfig?.token) {
3717
- console.error(
3718
- "QStash token is required for Upstash Workflow!\n\nTo fix this:\n1. Get your token from the Upstash Console (https://console.upstash.com/qstash)\n2. Initialize the workflow client with:\n\n const client = new Client({\n token: '<YOUR_QSTASH_TOKEN>'\n });"
3719
- );
3720
- }
3721
- this.client = new import_qstash12.Client(clientConfig);
3866
+ this.client = new import_qstash11.Client(clientConfig);
3722
3867
  }
3723
3868
  /**
3724
3869
  * Cancel an ongoing workflow
package/index.mjs CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  prepareFlowControl,
16
16
  serve,
17
17
  triggerFirstInvocation
18
- } from "./chunk-5GWDM6XJ.mjs";
18
+ } from "./chunk-C5HFGF7Q.mjs";
19
19
 
20
20
  // src/client/index.ts
21
21
  import { Client as QStashClient } from "@upstash/qstash";
@@ -130,11 +130,6 @@ var DLQ = class _DLQ {
130
130
  var Client = class {
131
131
  client;
132
132
  constructor(clientConfig) {
133
- if (!clientConfig?.token) {
134
- console.error(
135
- "QStash token is required for Upstash Workflow!\n\nTo fix this:\n1. Get your token from the Upstash Console (https://console.upstash.com/qstash)\n2. Initialize the workflow client with:\n\n const client = new Client({\n token: '<YOUR_QSTASH_TOKEN>'\n });"
136
- );
137
- }
138
133
  this.client = new QStashClient(clientConfig);
139
134
  }
140
135
  /**
package/nextjs.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
2
- import { a as RouteFunction, b as WorkflowServeOptions, x as InvokableWorkflow } from './types-ByzQdZjb.mjs';
3
- import { s as serveManyBase } from './serve-many-qpxb-yr-.mjs';
2
+ import { c as RouteFunction, d as WorkflowServeOptions, x as InvokableWorkflow } from './types-pEje3VEB.mjs';
3
+ import { s as serveManyBase } from './serve-many-DhB8-zPD.mjs';
4
4
  import '@upstash/qstash';
5
5
  import 'zod';
6
6
 
package/nextjs.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
2
- import { a as RouteFunction, b as WorkflowServeOptions, x as InvokableWorkflow } from './types-ByzQdZjb.js';
3
- import { s as serveManyBase } from './serve-many-CFlNO2Iq.js';
2
+ import { c as RouteFunction, d as WorkflowServeOptions, x as InvokableWorkflow } from './types-pEje3VEB.js';
3
+ import { s as serveManyBase } from './serve-many-qnfynN1x.js';
4
4
  import '@upstash/qstash';
5
5
  import 'zod';
6
6
 
package/nextjs.js CHANGED
@@ -844,7 +844,7 @@ var recreateUserHeaders = (headers) => {
844
844
  const pairs = headers.entries();
845
845
  for (const [header, value] of pairs) {
846
846
  const headerLowerCase = header.toLowerCase();
847
- const isUserHeader = !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
847
+ const isUserHeader = headerLowerCase !== "upstash-region" && !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
848
848
  !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
849
849
  headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
850
850
  headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
@@ -3216,9 +3216,154 @@ var handleFailure = async ({
3216
3216
  }
3217
3217
  };
3218
3218
 
3219
- // src/serve/options.ts
3219
+ // src/serve/multi-region/handlers.ts
3220
3220
  var import_qstash10 = require("@upstash/qstash");
3221
- var import_qstash11 = require("@upstash/qstash");
3221
+
3222
+ // src/serve/multi-region/utils.ts
3223
+ var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
3224
+ var getRegionFromEnvironment = (environment) => {
3225
+ const region = environment.QSTASH_REGION;
3226
+ return normalizeRegionHeader(region);
3227
+ };
3228
+ function readEnvironmentVariables(environmentVariables, environment, region) {
3229
+ const result = {};
3230
+ for (const variable of environmentVariables) {
3231
+ const key = region ? `${region}_${variable}` : variable;
3232
+ result[variable] = environment[key];
3233
+ }
3234
+ return result;
3235
+ }
3236
+ function readClientEnvironmentVariables(environment, region) {
3237
+ return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
3238
+ }
3239
+ function readReceiverEnvironmentVariables(environment, region) {
3240
+ return readEnvironmentVariables(
3241
+ ["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
3242
+ environment,
3243
+ region
3244
+ );
3245
+ }
3246
+ function normalizeRegionHeader(region) {
3247
+ if (!region) {
3248
+ return void 0;
3249
+ }
3250
+ region = region.replaceAll("-", "_").toUpperCase();
3251
+ if (VALID_REGIONS.includes(region)) {
3252
+ return region;
3253
+ }
3254
+ console.warn(
3255
+ `[Upstash Workflow] Invalid UPSTASH-REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
3256
+ ", "
3257
+ )}.`
3258
+ );
3259
+ return void 0;
3260
+ }
3261
+
3262
+ // src/serve/multi-region/handlers.ts
3263
+ var getHandlersForRequest = (qstashHandlers, regionHeader, isFirstInvocation) => {
3264
+ if (qstashHandlers.mode === "single-region") {
3265
+ return qstashHandlers.handlers;
3266
+ }
3267
+ let targetRegion;
3268
+ if (isFirstInvocation && !regionHeader) {
3269
+ targetRegion = qstashHandlers.defaultRegion;
3270
+ } else {
3271
+ const normalizedRegion = regionHeader ? normalizeRegionHeader(regionHeader) : void 0;
3272
+ targetRegion = normalizedRegion ?? qstashHandlers.defaultRegion;
3273
+ }
3274
+ const handler = qstashHandlers.handlers[targetRegion];
3275
+ if (!handler) {
3276
+ console.warn(
3277
+ `[Upstash Workflow] No handler found for region "${targetRegion}". Falling back to default region.`
3278
+ );
3279
+ return qstashHandlers.handlers[qstashHandlers.defaultRegion];
3280
+ }
3281
+ return handler;
3282
+ };
3283
+ var createRegionalHandler = (environment, receiverConfig, region, clientOptions) => {
3284
+ const clientEnv = readClientEnvironmentVariables(environment, region);
3285
+ const client = new import_qstash10.Client({
3286
+ ...clientOptions,
3287
+ baseUrl: clientEnv.QSTASH_URL,
3288
+ token: clientEnv.QSTASH_TOKEN
3289
+ });
3290
+ const receiver = getReceiver(environment, receiverConfig, region);
3291
+ return { client, receiver };
3292
+ };
3293
+ var shouldUseMultiRegionMode = (environment, qstashClientOption) => {
3294
+ const hasRegionEnv = Boolean(getRegionFromEnvironment(environment));
3295
+ if (hasRegionEnv && (!qstashClientOption || !("http" in qstashClientOption))) {
3296
+ return {
3297
+ isMultiRegion: true,
3298
+ defaultRegion: getRegionFromEnvironment(environment),
3299
+ clientOptions: qstashClientOption
3300
+ };
3301
+ } else {
3302
+ return { isMultiRegion: false };
3303
+ }
3304
+ };
3305
+ var getQStashHandlers = ({
3306
+ environment,
3307
+ qstashClientOption,
3308
+ receiverConfig
3309
+ }) => {
3310
+ const multiRegion = shouldUseMultiRegionMode(environment, qstashClientOption);
3311
+ if (multiRegion.isMultiRegion) {
3312
+ const regions = ["US_EAST_1", "EU_CENTRAL_1"];
3313
+ const handlers = {};
3314
+ for (const region of regions) {
3315
+ try {
3316
+ handlers[region] = createRegionalHandler(
3317
+ environment,
3318
+ receiverConfig,
3319
+ region,
3320
+ multiRegion.clientOptions
3321
+ );
3322
+ } catch (error) {
3323
+ console.warn(`[Upstash Workflow] Failed to create handler for region ${region}:`, error);
3324
+ }
3325
+ }
3326
+ return {
3327
+ mode: "multi-region",
3328
+ handlers,
3329
+ defaultRegion: multiRegion.defaultRegion
3330
+ };
3331
+ } else {
3332
+ return {
3333
+ mode: "single-region",
3334
+ handlers: {
3335
+ client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash10.Client({
3336
+ ...qstashClientOption,
3337
+ baseUrl: environment.QSTASH_URL,
3338
+ token: environment.QSTASH_TOKEN
3339
+ }),
3340
+ receiver: getReceiver(environment, receiverConfig)
3341
+ }
3342
+ };
3343
+ }
3344
+ };
3345
+ var getReceiver = (environment, receiverConfig, region) => {
3346
+ if (typeof receiverConfig === "string") {
3347
+ if (receiverConfig === "set-to-undefined") {
3348
+ return void 0;
3349
+ }
3350
+ const receiverEnv = readReceiverEnvironmentVariables(environment, region);
3351
+ return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash10.Receiver({
3352
+ currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
3353
+ nextSigningKey: receiverEnv.QSTASH_NEXT_SIGNING_KEY
3354
+ }) : void 0;
3355
+ } else {
3356
+ return receiverConfig;
3357
+ }
3358
+ };
3359
+ var getQStashHandlerOptions = (...params) => {
3360
+ const handlers = getQStashHandlers(...params);
3361
+ return {
3362
+ qstashHandlers: handlers,
3363
+ defaultReceiver: handlers.mode === "single-region" ? handlers.handlers.receiver : handlers.handlers[handlers.defaultRegion].receiver,
3364
+ defaultClient: handlers.mode === "single-region" ? handlers.handlers.client : handlers.handlers[handlers.defaultRegion].client
3365
+ };
3366
+ };
3222
3367
 
3223
3368
  // src/middleware/middleware.ts
3224
3369
  var WorkflowMiddleware = class {
@@ -3357,14 +3502,17 @@ var createResponseData = (workflowRunId, detailedFinishCondition) => {
3357
3502
  };
3358
3503
  var processOptions = (options, internalOptions) => {
3359
3504
  const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
3360
- const receiverEnvironmentVariablesSet = Boolean(
3361
- environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
3362
- );
3505
+ const {
3506
+ qstashHandlers,
3507
+ defaultClient: qstashClient,
3508
+ defaultReceiver: receiver
3509
+ } = getQStashHandlerOptions({
3510
+ environment,
3511
+ qstashClientOption: options?.qstashClient,
3512
+ receiverConfig: options && "receiver" in options ? options.receiver ? options.receiver : "set-to-undefined" : "not-set"
3513
+ });
3363
3514
  return {
3364
- qstashClient: options?.qstashClient ?? new import_qstash11.Client({
3365
- baseUrl: environment.QSTASH_URL,
3366
- token: environment.QSTASH_TOKEN
3367
- }),
3515
+ qstashClient,
3368
3516
  initialPayloadParser: (initialRequest) => {
3369
3517
  if (!initialRequest) {
3370
3518
  return void 0;
@@ -3379,10 +3527,7 @@ var processOptions = (options, internalOptions) => {
3379
3527
  throw error;
3380
3528
  }
3381
3529
  },
3382
- receiver: receiverEnvironmentVariablesSet ? new import_qstash10.Receiver({
3383
- currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
3384
- nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
3385
- }) : void 0,
3530
+ receiver,
3386
3531
  baseUrl: environment.UPSTASH_WORKFLOW_URL,
3387
3532
  env: environment,
3388
3533
  disableTelemetry: false,
@@ -3396,7 +3541,8 @@ var processOptions = (options, internalOptions) => {
3396
3541
  headers: responseData.headers
3397
3542
  });
3398
3543
  }),
3399
- useJSONContent: internalOptions?.useJSONContent ?? false
3544
+ useJSONContent: internalOptions?.useJSONContent ?? false,
3545
+ qstashHandlers
3400
3546
  }
3401
3547
  };
3402
3548
  };
@@ -3429,10 +3575,8 @@ var AUTH_FAIL_MESSAGE = `Failed to authenticate Workflow request. If this is une
3429
3575
  // src/serve/index.ts
3430
3576
  var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3431
3577
  const {
3432
- qstashClient,
3433
3578
  initialPayloadParser,
3434
3579
  url,
3435
- receiver,
3436
3580
  failureFunction,
3437
3581
  baseUrl,
3438
3582
  env,
@@ -3452,9 +3596,15 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3452
3596
  baseUrl,
3453
3597
  middlewareManager.dispatchDebug.bind(middlewareManager)
3454
3598
  );
3455
- const requestPayload = await getPayload(request) ?? "";
3456
- await verifyRequest(requestPayload, request.headers.get("upstash-signature"), receiver);
3457
3599
  const { isFirstInvocation, workflowRunId, unknownSdk } = validateRequest(request);
3600
+ const regionHeader = request.headers.get("upstash-region");
3601
+ const { client: regionalClient, receiver: regionalReceiver } = getHandlersForRequest(
3602
+ internal.qstashHandlers,
3603
+ regionHeader,
3604
+ isFirstInvocation
3605
+ );
3606
+ const requestPayload = await getPayload(request) ?? "";
3607
+ await verifyRequest(requestPayload, request.headers.get("upstash-signature"), regionalReceiver);
3458
3608
  middlewareManager.assignWorkflowRunId(workflowRunId);
3459
3609
  await middlewareManager.dispatchDebug("onInfo", {
3460
3610
  info: `Run id identified. isFirstInvocation: ${isFirstInvocation}, unknownSdk: ${unknownSdk}`
@@ -3464,7 +3614,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3464
3614
  isFirstInvocation,
3465
3615
  unknownSdk,
3466
3616
  workflowRunId,
3467
- requester: qstashClient.http,
3617
+ requester: regionalClient.http,
3468
3618
  messageId: request.headers.get("upstash-message-id"),
3469
3619
  dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
3470
3620
  });
@@ -3485,7 +3635,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3485
3635
  const failureCheck = await handleFailure({
3486
3636
  request,
3487
3637
  requestPayload,
3488
- qstashClient,
3638
+ qstashClient: regionalClient,
3489
3639
  initialPayloadParser,
3490
3640
  routeFunction,
3491
3641
  failureFunction,
@@ -3517,7 +3667,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3517
3667
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3518
3668
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3519
3669
  const workflowContext = new WorkflowContext({
3520
- qstashClient,
3670
+ qstashClient: regionalClient,
3521
3671
  workflowRunId,
3522
3672
  initialPayload: initialPayloadParser(rawInitialPayload),
3523
3673
  headers: recreateUserHeaders(request.headers),
@@ -3548,7 +3698,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3548
3698
  const callReturnCheck = await handleThirdPartyCallResult({
3549
3699
  request,
3550
3700
  requestPayload: rawInitialPayload,
3551
- client: qstashClient,
3701
+ client: regionalClient,
3552
3702
  workflowUrl,
3553
3703
  telemetry,
3554
3704
  middlewareManager
package/nextjs.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-5GWDM6XJ.mjs";
5
+ } from "./chunk-C5HFGF7Q.mjs";
6
6
 
7
7
  // platforms/nextjs.ts
8
8
  var appTelemetry = {