@upstash/workflow 1.0.0 → 1.1.0-rc

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/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@upstash/workflow","version":"v1.0.0","description":"Durable, Reliable and Performant Serverless Functions","main":"./index.js","module":"./index.mjs","types":"./index.d.ts","files":["./*"],"exports":{".":{"import":"./index.mjs","require":"./index.js"},"./dist/nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./h3":{"import":"./h3.mjs","require":"./h3.js"},"./svelte":{"import":"./svelte.mjs","require":"./svelte.js"},"./solidjs":{"import":"./solidjs.mjs","require":"./solidjs.js"},"./workflow":{"import":"./workflow.mjs","require":"./workflow.js"},"./hono":{"import":"./hono.mjs","require":"./hono.js"},"./cloudflare":{"import":"./cloudflare.mjs","require":"./cloudflare.js"},"./astro":{"import":"./astro.mjs","require":"./astro.js"},"./express":{"import":"./express.mjs","require":"./express.js"},"./tanstack":{"import":"./tanstack.mjs","require":"./tanstack.js"}},"scripts":{"build":"tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/","test":"bun test src","fmt":"prettier --write .","lint":"tsc && eslint \"{src,platforms}/**/*.{js,ts,tsx}\" --quiet --fix","check-exports":"bun run build && cd dist && attw -P"},"repository":{"type":"git","url":"git+https://github.com/upstash/workflow-ts.git"},"keywords":["upstash","qstash","workflow","serverless"],"author":"Cahid Arda Oz","license":"MIT","bugs":{"url":"https://github.com/upstash/workflow-ts/issues"},"homepage":"https://github.com/upstash/workflow-ts#readme","devDependencies":{"@commitlint/cli":"^19.5.0","@commitlint/config-conventional":"^19.5.0","@eslint/js":"^9.11.1","@solidjs/start":"^1.0.8","@sveltejs/kit":"^2.6.1","@types/bun":"^1.1.10","@types/express":"^5.0.3","astro":"^4.16.7","eslint":"^9.11.1","eslint-plugin-unicorn":"^55.0.0","express":"^5.1.0","globals":"^15.10.0","h3":"^1.12.0","hono":"^4.6.20","husky":"^9.1.6","next":"^14.2.14","prettier":"3.3.3","tsup":"^8.3.0","typescript":"^5.7.2","typescript-eslint":"^8.18.0"},"dependencies":{"@upstash/qstash":"^2.8.4"},"directories":{"example":"examples"},"peerDependencies":{"zod":"^3.25.0 || ^4.0.0"}}
1
+ {"name":"@upstash/workflow","version":"v1.1.0-rc","description":"Durable, Reliable and Performant Serverless Functions","main":"./index.js","module":"./index.mjs","types":"./index.d.ts","files":["./*"],"exports":{".":{"import":"./index.mjs","require":"./index.js"},"./dist/nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./h3":{"import":"./h3.mjs","require":"./h3.js"},"./svelte":{"import":"./svelte.mjs","require":"./svelte.js"},"./solidjs":{"import":"./solidjs.mjs","require":"./solidjs.js"},"./workflow":{"import":"./workflow.mjs","require":"./workflow.js"},"./hono":{"import":"./hono.mjs","require":"./hono.js"},"./cloudflare":{"import":"./cloudflare.mjs","require":"./cloudflare.js"},"./astro":{"import":"./astro.mjs","require":"./astro.js"},"./express":{"import":"./express.mjs","require":"./express.js"},"./tanstack":{"import":"./tanstack.mjs","require":"./tanstack.js"}},"scripts":{"build":"tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/","test":"bun test src","fmt":"prettier --write .","lint":"tsc && eslint \"{src,platforms}/**/*.{js,ts,tsx}\" --quiet --fix","check-exports":"bun run build && cd dist && attw -P"},"repository":{"type":"git","url":"git+https://github.com/upstash/workflow-ts.git"},"keywords":["upstash","qstash","workflow","serverless"],"author":"Cahid Arda Oz","license":"MIT","bugs":{"url":"https://github.com/upstash/workflow-ts/issues"},"homepage":"https://github.com/upstash/workflow-ts#readme","devDependencies":{"@commitlint/cli":"^19.5.0","@commitlint/config-conventional":"^19.5.0","@eslint/js":"^9.11.1","@solidjs/start":"^1.0.8","@sveltejs/kit":"^2.6.1","@types/bun":"^1.1.10","@types/express":"^5.0.3","astro":"^4.16.7","eslint":"^9.11.1","eslint-plugin-unicorn":"^55.0.0","express":"^5.1.0","globals":"^15.10.0","h3":"^1.12.0","hono":"^4.6.20","husky":"^9.1.6","next":"^14.2.14","prettier":"3.3.3","tsup":"^8.3.0","typescript":"^5.7.2","typescript-eslint":"^8.18.0"},"dependencies":{"@upstash/qstash":"2.9.0-rc"},"directories":{"example":"examples"},"peerDependencies":{"zod":"^3.25.0 || ^4.0.0"}}
@@ -1,4 +1,4 @@
1
- import { b as WorkflowServeOptions, a as RouteFunction, x as InvokableWorkflow } from './types-ByzQdZjb.mjs';
1
+ import { d as WorkflowServeOptions, c as RouteFunction, x as InvokableWorkflow } from './types-pEje3VEB.mjs';
2
2
 
3
3
  type OmitOptionsInServeMany<TOptions> = Omit<TOptions, "env" | "url" | "schema" | "initialPayloadParser">;
4
4
  declare const serveManyBase: <THandler extends (...params: any[]) => any, TOptions extends OmitOptionsInServeMany<WorkflowServeOptions> = OmitOptionsInServeMany<WorkflowServeOptions>, TServeParams extends [routeFunction: RouteFunction<any, any>, options: TOptions] = [routeFunction: RouteFunction<any, any>, options: TOptions]>({ workflows, getUrl, serveMethod, options, }: {
@@ -1,4 +1,4 @@
1
- import { b as WorkflowServeOptions, a as RouteFunction, x as InvokableWorkflow } from './types-ByzQdZjb.js';
1
+ import { d as WorkflowServeOptions, c as RouteFunction, x as InvokableWorkflow } from './types-pEje3VEB.js';
2
2
 
3
3
  type OmitOptionsInServeMany<TOptions> = Omit<TOptions, "env" | "url" | "schema" | "initialPayloadParser">;
4
4
  declare const serveManyBase: <THandler extends (...params: any[]) => any, TOptions extends OmitOptionsInServeMany<WorkflowServeOptions> = OmitOptionsInServeMany<WorkflowServeOptions>, TServeParams extends [routeFunction: RouteFunction<any, any>, options: TOptions] = [routeFunction: RouteFunction<any, any>, options: TOptions]>({ workflows, getUrl, serveMethod, options, }: {
package/solidjs.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { APIEvent } from '@solidjs/start/server';
2
- import { a as RouteFunction, b as WorkflowServeOptions } from './types-ByzQdZjb.mjs';
2
+ import { c as RouteFunction, d as WorkflowServeOptions } from './types-pEje3VEB.mjs';
3
3
  import '@upstash/qstash';
4
4
  import 'zod';
5
5
 
package/solidjs.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { APIEvent } from '@solidjs/start/server';
2
- import { a as RouteFunction, b as WorkflowServeOptions } from './types-ByzQdZjb.js';
2
+ import { c as RouteFunction, d as WorkflowServeOptions } from './types-pEje3VEB.js';
3
3
  import '@upstash/qstash';
4
4
  import 'zod';
5
5
 
package/solidjs.js CHANGED
@@ -839,7 +839,7 @@ var recreateUserHeaders = (headers) => {
839
839
  const pairs = headers.entries();
840
840
  for (const [header, value] of pairs) {
841
841
  const headerLowerCase = header.toLowerCase();
842
- const isUserHeader = !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
842
+ const isUserHeader = headerLowerCase !== "upstash-region" && !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
843
843
  !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
844
844
  headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
845
845
  headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
@@ -3144,9 +3144,154 @@ var handleFailure = async ({
3144
3144
  }
3145
3145
  };
3146
3146
 
3147
- // src/serve/options.ts
3147
+ // src/serve/multi-region/handlers.ts
3148
3148
  var import_qstash10 = require("@upstash/qstash");
3149
- var import_qstash11 = require("@upstash/qstash");
3149
+
3150
+ // src/serve/multi-region/utils.ts
3151
+ var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
3152
+ var getRegionFromEnvironment = (environment) => {
3153
+ const region = environment.QSTASH_REGION;
3154
+ return normalizeRegionHeader(region);
3155
+ };
3156
+ function readEnvironmentVariables(environmentVariables, environment, region) {
3157
+ const result = {};
3158
+ for (const variable of environmentVariables) {
3159
+ const key = region ? `${region}_${variable}` : variable;
3160
+ result[variable] = environment[key];
3161
+ }
3162
+ return result;
3163
+ }
3164
+ function readClientEnvironmentVariables(environment, region) {
3165
+ return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
3166
+ }
3167
+ function readReceiverEnvironmentVariables(environment, region) {
3168
+ return readEnvironmentVariables(
3169
+ ["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
3170
+ environment,
3171
+ region
3172
+ );
3173
+ }
3174
+ function normalizeRegionHeader(region) {
3175
+ if (!region) {
3176
+ return void 0;
3177
+ }
3178
+ region = region.replaceAll("-", "_").toUpperCase();
3179
+ if (VALID_REGIONS.includes(region)) {
3180
+ return region;
3181
+ }
3182
+ console.warn(
3183
+ `[Upstash Workflow] Invalid UPSTASH-REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
3184
+ ", "
3185
+ )}.`
3186
+ );
3187
+ return void 0;
3188
+ }
3189
+
3190
+ // src/serve/multi-region/handlers.ts
3191
+ var getHandlersForRequest = (qstashHandlers, regionHeader, isFirstInvocation) => {
3192
+ if (qstashHandlers.mode === "single-region") {
3193
+ return qstashHandlers.handlers;
3194
+ }
3195
+ let targetRegion;
3196
+ if (isFirstInvocation) {
3197
+ targetRegion = qstashHandlers.defaultRegion;
3198
+ } else {
3199
+ const normalizedRegion = regionHeader ? normalizeRegionHeader(regionHeader) : void 0;
3200
+ targetRegion = normalizedRegion ?? qstashHandlers.defaultRegion;
3201
+ }
3202
+ const handler = qstashHandlers.handlers[targetRegion];
3203
+ if (!handler) {
3204
+ console.warn(
3205
+ `[Upstash Workflow] No handler found for region "${targetRegion}". Falling back to default region.`
3206
+ );
3207
+ return qstashHandlers.handlers[qstashHandlers.defaultRegion];
3208
+ }
3209
+ return handler;
3210
+ };
3211
+ var createRegionalHandler = (environment, receiverConfig, region, clientOptions) => {
3212
+ const clientEnv = readClientEnvironmentVariables(environment, region);
3213
+ const client = new import_qstash10.Client({
3214
+ ...clientOptions,
3215
+ baseUrl: clientEnv.QSTASH_URL,
3216
+ token: clientEnv.QSTASH_TOKEN
3217
+ });
3218
+ const receiver = getReceiver(environment, receiverConfig, region);
3219
+ return { client, receiver };
3220
+ };
3221
+ var shouldUseMultiRegionMode = (environment, qstashClientOption) => {
3222
+ const hasRegionEnv = Boolean(getRegionFromEnvironment(environment));
3223
+ if (hasRegionEnv && (!qstashClientOption || !("http" in qstashClientOption))) {
3224
+ return {
3225
+ isMultiRegion: true,
3226
+ defaultRegion: getRegionFromEnvironment(environment),
3227
+ clientOptions: qstashClientOption
3228
+ };
3229
+ } else {
3230
+ return { isMultiRegion: false };
3231
+ }
3232
+ };
3233
+ var getQStashHandlers = ({
3234
+ environment,
3235
+ qstashClientOption,
3236
+ receiverConfig
3237
+ }) => {
3238
+ const multiRegion = shouldUseMultiRegionMode(environment, qstashClientOption);
3239
+ if (multiRegion.isMultiRegion) {
3240
+ const regions = ["US_EAST_1", "EU_CENTRAL_1"];
3241
+ const handlers = {};
3242
+ for (const region of regions) {
3243
+ try {
3244
+ handlers[region] = createRegionalHandler(
3245
+ environment,
3246
+ receiverConfig,
3247
+ region,
3248
+ multiRegion.clientOptions
3249
+ );
3250
+ } catch (error) {
3251
+ console.warn(`[Upstash Workflow] Failed to create handler for region ${region}:`, error);
3252
+ }
3253
+ }
3254
+ return {
3255
+ mode: "multi-region",
3256
+ handlers,
3257
+ defaultRegion: multiRegion.defaultRegion
3258
+ };
3259
+ } else {
3260
+ return {
3261
+ mode: "single-region",
3262
+ handlers: {
3263
+ client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash10.Client({
3264
+ ...qstashClientOption,
3265
+ baseUrl: environment.QSTASH_URL,
3266
+ token: environment.QSTASH_TOKEN
3267
+ }),
3268
+ receiver: getReceiver(environment, receiverConfig)
3269
+ }
3270
+ };
3271
+ }
3272
+ };
3273
+ var getReceiver = (environment, receiverConfig, region) => {
3274
+ if (typeof receiverConfig === "string") {
3275
+ if (receiverConfig === "set-to-undefined") {
3276
+ return void 0;
3277
+ }
3278
+ const receiverEnv = readReceiverEnvironmentVariables(environment, region);
3279
+ return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash10.Receiver({
3280
+ currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
3281
+ nextSigningKey: receiverEnv.QSTASH_NEXT_SIGNING_KEY
3282
+ }) : void 0;
3283
+ } else {
3284
+ return receiverConfig;
3285
+ }
3286
+ };
3287
+ var getQStashHandlerOptions = (...params) => {
3288
+ const handlers = getQStashHandlers(...params);
3289
+ return {
3290
+ qstashHandlers: handlers,
3291
+ defaultReceiver: handlers.mode === "single-region" ? handlers.handlers.receiver : handlers.handlers[handlers.defaultRegion].receiver,
3292
+ defaultClient: handlers.mode === "single-region" ? handlers.handlers.client : handlers.handlers[handlers.defaultRegion].client
3293
+ };
3294
+ };
3150
3295
 
3151
3296
  // src/middleware/middleware.ts
3152
3297
  var WorkflowMiddleware = class {
@@ -3285,14 +3430,17 @@ var createResponseData = (workflowRunId, detailedFinishCondition) => {
3285
3430
  };
3286
3431
  var processOptions = (options, internalOptions) => {
3287
3432
  const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
3288
- const receiverEnvironmentVariablesSet = Boolean(
3289
- environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
3290
- );
3433
+ const {
3434
+ qstashHandlers,
3435
+ defaultClient: qstashClient,
3436
+ defaultReceiver: receiver
3437
+ } = getQStashHandlerOptions({
3438
+ environment,
3439
+ qstashClientOption: options?.qstashClient,
3440
+ receiverConfig: options && "receiver" in options ? options.receiver ? options.receiver : "set-to-undefined" : "not-set"
3441
+ });
3291
3442
  return {
3292
- qstashClient: options?.qstashClient ?? new import_qstash11.Client({
3293
- baseUrl: environment.QSTASH_URL,
3294
- token: environment.QSTASH_TOKEN
3295
- }),
3443
+ qstashClient,
3296
3444
  initialPayloadParser: (initialRequest) => {
3297
3445
  if (!initialRequest) {
3298
3446
  return void 0;
@@ -3307,10 +3455,7 @@ var processOptions = (options, internalOptions) => {
3307
3455
  throw error;
3308
3456
  }
3309
3457
  },
3310
- receiver: receiverEnvironmentVariablesSet ? new import_qstash10.Receiver({
3311
- currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
3312
- nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
3313
- }) : void 0,
3458
+ receiver,
3314
3459
  baseUrl: environment.UPSTASH_WORKFLOW_URL,
3315
3460
  env: environment,
3316
3461
  disableTelemetry: false,
@@ -3324,7 +3469,8 @@ var processOptions = (options, internalOptions) => {
3324
3469
  headers: responseData.headers
3325
3470
  });
3326
3471
  }),
3327
- useJSONContent: internalOptions?.useJSONContent ?? false
3472
+ useJSONContent: internalOptions?.useJSONContent ?? false,
3473
+ qstashHandlers
3328
3474
  }
3329
3475
  };
3330
3476
  };
@@ -3357,10 +3503,8 @@ var AUTH_FAIL_MESSAGE = `Failed to authenticate Workflow request. If this is une
3357
3503
  // src/serve/index.ts
3358
3504
  var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3359
3505
  const {
3360
- qstashClient,
3361
3506
  initialPayloadParser,
3362
3507
  url,
3363
- receiver,
3364
3508
  failureFunction,
3365
3509
  baseUrl,
3366
3510
  env,
@@ -3380,9 +3524,15 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3380
3524
  baseUrl,
3381
3525
  middlewareManager.dispatchDebug.bind(middlewareManager)
3382
3526
  );
3383
- const requestPayload = await getPayload(request) ?? "";
3384
- await verifyRequest(requestPayload, request.headers.get("upstash-signature"), receiver);
3385
3527
  const { isFirstInvocation, workflowRunId, unknownSdk } = validateRequest(request);
3528
+ const regionHeader = request.headers.get("upstash-region");
3529
+ const { client: regionalClient, receiver: regionalReceiver } = getHandlersForRequest(
3530
+ internal.qstashHandlers,
3531
+ regionHeader,
3532
+ isFirstInvocation
3533
+ );
3534
+ const requestPayload = await getPayload(request) ?? "";
3535
+ await verifyRequest(requestPayload, request.headers.get("upstash-signature"), regionalReceiver);
3386
3536
  middlewareManager.assignWorkflowRunId(workflowRunId);
3387
3537
  await middlewareManager.dispatchDebug("onInfo", {
3388
3538
  info: `Run id identified. isFirstInvocation: ${isFirstInvocation}, unknownSdk: ${unknownSdk}`
@@ -3392,7 +3542,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3392
3542
  isFirstInvocation,
3393
3543
  unknownSdk,
3394
3544
  workflowRunId,
3395
- requester: qstashClient.http,
3545
+ requester: regionalClient.http,
3396
3546
  messageId: request.headers.get("upstash-message-id"),
3397
3547
  dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
3398
3548
  });
@@ -3413,7 +3563,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3413
3563
  const failureCheck = await handleFailure({
3414
3564
  request,
3415
3565
  requestPayload,
3416
- qstashClient,
3566
+ qstashClient: regionalClient,
3417
3567
  initialPayloadParser,
3418
3568
  routeFunction,
3419
3569
  failureFunction,
@@ -3445,7 +3595,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3445
3595
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3446
3596
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3447
3597
  const workflowContext = new WorkflowContext({
3448
- qstashClient,
3598
+ qstashClient: regionalClient,
3449
3599
  workflowRunId,
3450
3600
  initialPayload: initialPayloadParser(rawInitialPayload),
3451
3601
  headers: recreateUserHeaders(request.headers),
@@ -3476,7 +3626,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
3476
3626
  const callReturnCheck = await handleThirdPartyCallResult({
3477
3627
  request,
3478
3628
  requestPayload: rawInitialPayload,
3479
- client: qstashClient,
3629
+ client: regionalClient,
3480
3630
  workflowUrl,
3481
3631
  telemetry,
3482
3632
  middlewareManager
package/solidjs.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase
4
- } from "./chunk-5GWDM6XJ.mjs";
4
+ } from "./chunk-2Z32SOYM.mjs";
5
5
 
6
6
  // platforms/solidjs.ts
7
7
  var serve = (routeFunction, options) => {
package/svelte.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _sveltejs_kit from '@sveltejs/kit';
2
2
  import { RequestHandler } from '@sveltejs/kit';
3
- import { a as RouteFunction, b as WorkflowServeOptions, x as InvokableWorkflow } from './types-ByzQdZjb.mjs';
4
- import { s as serveManyBase } from './serve-many-qpxb-yr-.mjs';
3
+ import { c as RouteFunction, d as WorkflowServeOptions, x as InvokableWorkflow } from './types-pEje3VEB.mjs';
4
+ import { s as serveManyBase } from './serve-many-DhB8-zPD.mjs';
5
5
  import '@upstash/qstash';
6
6
  import 'zod';
7
7
 
package/svelte.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _sveltejs_kit from '@sveltejs/kit';
2
2
  import { RequestHandler } from '@sveltejs/kit';
3
- import { a as RouteFunction, b as WorkflowServeOptions, x as InvokableWorkflow } from './types-ByzQdZjb.js';
4
- import { s as serveManyBase } from './serve-many-CFlNO2Iq.js';
3
+ import { c as RouteFunction, d as WorkflowServeOptions, x as InvokableWorkflow } from './types-pEje3VEB.js';
4
+ import { s as serveManyBase } from './serve-many-qnfynN1x.js';
5
5
  import '@upstash/qstash';
6
6
  import 'zod';
7
7
 
package/svelte.js CHANGED
@@ -841,7 +841,7 @@ var recreateUserHeaders = (headers) => {
841
841
  const pairs = headers.entries();
842
842
  for (const [header, value] of pairs) {
843
843
  const headerLowerCase = header.toLowerCase();
844
- const isUserHeader = !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
844
+ const isUserHeader = headerLowerCase !== "upstash-region" && !headerLowerCase.startsWith("upstash-workflow-") && // https://vercel.com/docs/edge-network/headers/request-headers#x-vercel-id
845
845
  !headerLowerCase.startsWith("x-vercel-") && !headerLowerCase.startsWith("x-forwarded-") && // https://blog.cloudflare.com/preventing-request-loops-using-cdn-loop/
846
846
  headerLowerCase !== "cf-connecting-ip" && headerLowerCase !== "cdn-loop" && headerLowerCase !== "cf-ew-via" && headerLowerCase !== "cf-ray" && // For Render https://render.com
847
847
  headerLowerCase !== "render-proxy-ttl" || headerLowerCase === WORKFLOW_LABEL_HEADER.toLocaleLowerCase();
@@ -3213,9 +3213,154 @@ var handleFailure = async ({
3213
3213
  }
3214
3214
  };
3215
3215
 
3216
- // src/serve/options.ts
3216
+ // src/serve/multi-region/handlers.ts
3217
3217
  var import_qstash10 = require("@upstash/qstash");
3218
- var import_qstash11 = require("@upstash/qstash");
3218
+
3219
+ // src/serve/multi-region/utils.ts
3220
+ var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
3221
+ var getRegionFromEnvironment = (environment) => {
3222
+ const region = environment.QSTASH_REGION;
3223
+ return normalizeRegionHeader(region);
3224
+ };
3225
+ function readEnvironmentVariables(environmentVariables, environment, region) {
3226
+ const result = {};
3227
+ for (const variable of environmentVariables) {
3228
+ const key = region ? `${region}_${variable}` : variable;
3229
+ result[variable] = environment[key];
3230
+ }
3231
+ return result;
3232
+ }
3233
+ function readClientEnvironmentVariables(environment, region) {
3234
+ return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
3235
+ }
3236
+ function readReceiverEnvironmentVariables(environment, region) {
3237
+ return readEnvironmentVariables(
3238
+ ["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
3239
+ environment,
3240
+ region
3241
+ );
3242
+ }
3243
+ function normalizeRegionHeader(region) {
3244
+ if (!region) {
3245
+ return void 0;
3246
+ }
3247
+ region = region.replaceAll("-", "_").toUpperCase();
3248
+ if (VALID_REGIONS.includes(region)) {
3249
+ return region;
3250
+ }
3251
+ console.warn(
3252
+ `[Upstash Workflow] Invalid UPSTASH-REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
3253
+ ", "
3254
+ )}.`
3255
+ );
3256
+ return void 0;
3257
+ }
3258
+
3259
+ // src/serve/multi-region/handlers.ts
3260
+ var getHandlersForRequest = (qstashHandlers, regionHeader, isFirstInvocation) => {
3261
+ if (qstashHandlers.mode === "single-region") {
3262
+ return qstashHandlers.handlers;
3263
+ }
3264
+ let targetRegion;
3265
+ if (isFirstInvocation) {
3266
+ targetRegion = qstashHandlers.defaultRegion;
3267
+ } else {
3268
+ const normalizedRegion = regionHeader ? normalizeRegionHeader(regionHeader) : void 0;
3269
+ targetRegion = normalizedRegion ?? qstashHandlers.defaultRegion;
3270
+ }
3271
+ const handler = qstashHandlers.handlers[targetRegion];
3272
+ if (!handler) {
3273
+ console.warn(
3274
+ `[Upstash Workflow] No handler found for region "${targetRegion}". Falling back to default region.`
3275
+ );
3276
+ return qstashHandlers.handlers[qstashHandlers.defaultRegion];
3277
+ }
3278
+ return handler;
3279
+ };
3280
+ var createRegionalHandler = (environment, receiverConfig, region, clientOptions) => {
3281
+ const clientEnv = readClientEnvironmentVariables(environment, region);
3282
+ const client = new import_qstash10.Client({
3283
+ ...clientOptions,
3284
+ baseUrl: clientEnv.QSTASH_URL,
3285
+ token: clientEnv.QSTASH_TOKEN
3286
+ });
3287
+ const receiver = getReceiver(environment, receiverConfig, region);
3288
+ return { client, receiver };
3289
+ };
3290
+ var shouldUseMultiRegionMode = (environment, qstashClientOption) => {
3291
+ const hasRegionEnv = Boolean(getRegionFromEnvironment(environment));
3292
+ if (hasRegionEnv && (!qstashClientOption || !("http" in qstashClientOption))) {
3293
+ return {
3294
+ isMultiRegion: true,
3295
+ defaultRegion: getRegionFromEnvironment(environment),
3296
+ clientOptions: qstashClientOption
3297
+ };
3298
+ } else {
3299
+ return { isMultiRegion: false };
3300
+ }
3301
+ };
3302
+ var getQStashHandlers = ({
3303
+ environment,
3304
+ qstashClientOption,
3305
+ receiverConfig
3306
+ }) => {
3307
+ const multiRegion = shouldUseMultiRegionMode(environment, qstashClientOption);
3308
+ if (multiRegion.isMultiRegion) {
3309
+ const regions = ["US_EAST_1", "EU_CENTRAL_1"];
3310
+ const handlers = {};
3311
+ for (const region of regions) {
3312
+ try {
3313
+ handlers[region] = createRegionalHandler(
3314
+ environment,
3315
+ receiverConfig,
3316
+ region,
3317
+ multiRegion.clientOptions
3318
+ );
3319
+ } catch (error) {
3320
+ console.warn(`[Upstash Workflow] Failed to create handler for region ${region}:`, error);
3321
+ }
3322
+ }
3323
+ return {
3324
+ mode: "multi-region",
3325
+ handlers,
3326
+ defaultRegion: multiRegion.defaultRegion
3327
+ };
3328
+ } else {
3329
+ return {
3330
+ mode: "single-region",
3331
+ handlers: {
3332
+ client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash10.Client({
3333
+ ...qstashClientOption,
3334
+ baseUrl: environment.QSTASH_URL,
3335
+ token: environment.QSTASH_TOKEN
3336
+ }),
3337
+ receiver: getReceiver(environment, receiverConfig)
3338
+ }
3339
+ };
3340
+ }
3341
+ };
3342
+ var getReceiver = (environment, receiverConfig, region) => {
3343
+ if (typeof receiverConfig === "string") {
3344
+ if (receiverConfig === "set-to-undefined") {
3345
+ return void 0;
3346
+ }
3347
+ const receiverEnv = readReceiverEnvironmentVariables(environment, region);
3348
+ return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash10.Receiver({
3349
+ currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
3350
+ nextSigningKey: receiverEnv.QSTASH_NEXT_SIGNING_KEY
3351
+ }) : void 0;
3352
+ } else {
3353
+ return receiverConfig;
3354
+ }
3355
+ };
3356
+ var getQStashHandlerOptions = (...params) => {
3357
+ const handlers = getQStashHandlers(...params);
3358
+ return {
3359
+ qstashHandlers: handlers,
3360
+ defaultReceiver: handlers.mode === "single-region" ? handlers.handlers.receiver : handlers.handlers[handlers.defaultRegion].receiver,
3361
+ defaultClient: handlers.mode === "single-region" ? handlers.handlers.client : handlers.handlers[handlers.defaultRegion].client
3362
+ };
3363
+ };
3219
3364
 
3220
3365
  // src/middleware/middleware.ts
3221
3366
  var WorkflowMiddleware = class {
@@ -3354,14 +3499,17 @@ var createResponseData = (workflowRunId, detailedFinishCondition) => {
3354
3499
  };
3355
3500
  var processOptions = (options, internalOptions) => {
3356
3501
  const environment = options?.env ?? (typeof process === "undefined" ? {} : process.env);
3357
- const receiverEnvironmentVariablesSet = Boolean(
3358
- environment.QSTASH_CURRENT_SIGNING_KEY && environment.QSTASH_NEXT_SIGNING_KEY
3359
- );
3502
+ const {
3503
+ qstashHandlers,
3504
+ defaultClient: qstashClient,
3505
+ defaultReceiver: receiver
3506
+ } = getQStashHandlerOptions({
3507
+ environment,
3508
+ qstashClientOption: options?.qstashClient,
3509
+ receiverConfig: options && "receiver" in options ? options.receiver ? options.receiver : "set-to-undefined" : "not-set"
3510
+ });
3360
3511
  return {
3361
- qstashClient: options?.qstashClient ?? new import_qstash11.Client({
3362
- baseUrl: environment.QSTASH_URL,
3363
- token: environment.QSTASH_TOKEN
3364
- }),
3512
+ qstashClient,
3365
3513
  initialPayloadParser: (initialRequest) => {
3366
3514
  if (!initialRequest) {
3367
3515
  return void 0;
@@ -3376,10 +3524,7 @@ var processOptions = (options, internalOptions) => {
3376
3524
  throw error;
3377
3525
  }
3378
3526
  },
3379
- receiver: receiverEnvironmentVariablesSet ? new import_qstash10.Receiver({
3380
- currentSigningKey: environment.QSTASH_CURRENT_SIGNING_KEY,
3381
- nextSigningKey: environment.QSTASH_NEXT_SIGNING_KEY
3382
- }) : void 0,
3527
+ receiver,
3383
3528
  baseUrl: environment.UPSTASH_WORKFLOW_URL,
3384
3529
  env: environment,
3385
3530
  disableTelemetry: false,
@@ -3393,7 +3538,8 @@ var processOptions = (options, internalOptions) => {
3393
3538
  headers: responseData.headers
3394
3539
  });
3395
3540
  }),
3396
- useJSONContent: internalOptions?.useJSONContent ?? false
3541
+ useJSONContent: internalOptions?.useJSONContent ?? false,
3542
+ qstashHandlers
3397
3543
  }
3398
3544
  };
3399
3545
  };
@@ -3426,10 +3572,8 @@ var AUTH_FAIL_MESSAGE = `Failed to authenticate Workflow request. If this is une
3426
3572
  // src/serve/index.ts
3427
3573
  var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3428
3574
  const {
3429
- qstashClient,
3430
3575
  initialPayloadParser,
3431
3576
  url,
3432
- receiver,
3433
3577
  failureFunction,
3434
3578
  baseUrl,
3435
3579
  env,
@@ -3449,9 +3593,15 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3449
3593
  baseUrl,
3450
3594
  middlewareManager.dispatchDebug.bind(middlewareManager)
3451
3595
  );
3452
- const requestPayload = await getPayload(request) ?? "";
3453
- await verifyRequest(requestPayload, request.headers.get("upstash-signature"), receiver);
3454
3596
  const { isFirstInvocation, workflowRunId, unknownSdk } = validateRequest(request);
3597
+ const regionHeader = request.headers.get("upstash-region");
3598
+ const { client: regionalClient, receiver: regionalReceiver } = getHandlersForRequest(
3599
+ internal.qstashHandlers,
3600
+ regionHeader,
3601
+ isFirstInvocation
3602
+ );
3603
+ const requestPayload = await getPayload(request) ?? "";
3604
+ await verifyRequest(requestPayload, request.headers.get("upstash-signature"), regionalReceiver);
3455
3605
  middlewareManager.assignWorkflowRunId(workflowRunId);
3456
3606
  await middlewareManager.dispatchDebug("onInfo", {
3457
3607
  info: `Run id identified. isFirstInvocation: ${isFirstInvocation}, unknownSdk: ${unknownSdk}`
@@ -3461,7 +3611,7 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3461
3611
  isFirstInvocation,
3462
3612
  unknownSdk,
3463
3613
  workflowRunId,
3464
- requester: qstashClient.http,
3614
+ requester: regionalClient.http,
3465
3615
  messageId: request.headers.get("upstash-message-id"),
3466
3616
  dispatchDebug: middlewareManager.dispatchDebug.bind(middlewareManager)
3467
3617
  });
@@ -3482,7 +3632,7 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3482
3632
  const failureCheck = await handleFailure({
3483
3633
  request,
3484
3634
  requestPayload,
3485
- qstashClient,
3635
+ qstashClient: regionalClient,
3486
3636
  initialPayloadParser,
3487
3637
  routeFunction,
3488
3638
  failureFunction,
@@ -3514,7 +3664,7 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3514
3664
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3515
3665
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3516
3666
  const workflowContext = new WorkflowContext({
3517
- qstashClient,
3667
+ qstashClient: regionalClient,
3518
3668
  workflowRunId,
3519
3669
  initialPayload: initialPayloadParser(rawInitialPayload),
3520
3670
  headers: recreateUserHeaders(request.headers),
@@ -3545,7 +3695,7 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3545
3695
  const callReturnCheck = await handleThirdPartyCallResult({
3546
3696
  request,
3547
3697
  requestPayload: rawInitialPayload,
3548
- client: qstashClient,
3698
+ client: regionalClient,
3549
3699
  workflowUrl,
3550
3700
  telemetry: telemetry2,
3551
3701
  middlewareManager
package/svelte.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-2Z32SOYM.mjs";
6
6
 
7
7
  // platforms/svelte.ts
8
8
  var telemetry = {
package/tanstack.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as RouteFunction, b as WorkflowServeOptions, E as ExclusiveValidationOptions, x as InvokableWorkflow } from './types-ByzQdZjb.mjs';
2
- import { s as serveManyBase } from './serve-many-qpxb-yr-.mjs';
1
+ import { c as RouteFunction, d as WorkflowServeOptions, E as ExclusiveValidationOptions, x as InvokableWorkflow } from './types-pEje3VEB.mjs';
2
+ import { s as serveManyBase } from './serve-many-DhB8-zPD.mjs';
3
3
  import '@upstash/qstash';
4
4
  import 'zod';
5
5
 
package/tanstack.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as RouteFunction, b as WorkflowServeOptions, E as ExclusiveValidationOptions, x as InvokableWorkflow } from './types-ByzQdZjb.js';
2
- import { s as serveManyBase } from './serve-many-CFlNO2Iq.js';
1
+ import { c as RouteFunction, d as WorkflowServeOptions, E as ExclusiveValidationOptions, x as InvokableWorkflow } from './types-pEje3VEB.js';
2
+ import { s as serveManyBase } from './serve-many-qnfynN1x.js';
3
3
  import '@upstash/qstash';
4
4
  import 'zod';
5
5