@upstash/qstash 2.8.3 → 2.9.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/nextjs.mjs CHANGED
@@ -1,21 +1,16 @@
1
1
  import {
2
2
  Receiver,
3
3
  serve
4
- } from "./chunk-CR3B3DK3.mjs";
4
+ } from "./chunk-WOMVRJIB.mjs";
5
5
 
6
6
  // platforms/nextjs.ts
7
7
  var BAD_REQUEST = 400;
8
8
  function verifySignature(handler, config) {
9
9
  const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
10
- if (!currentSigningKey) {
11
- throw new Error(
12
- "currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
13
- );
14
- }
15
10
  const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
16
- if (!nextSigningKey) {
11
+ if (!currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
17
12
  throw new Error(
18
- "nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
13
+ "currentSigningKey and nextSigningKey are required, either in the config or as env variables (QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY)"
19
14
  );
20
15
  }
21
16
  const receiver = new Receiver({
@@ -33,6 +28,7 @@ function verifySignature(handler, config) {
33
28
  if (typeof signature !== "string") {
34
29
  throw new TypeError("`Upstash-Signature` header is not a string");
35
30
  }
31
+ const upstashRegion = request.headers["upstash-region"];
36
32
  const chunks = [];
37
33
  for await (const chunk of request) {
38
34
  chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
@@ -41,7 +37,8 @@ function verifySignature(handler, config) {
41
37
  const isValid = await receiver.verify({
42
38
  signature,
43
39
  body,
44
- clockTolerance: config?.clockTolerance
40
+ clockTolerance: config?.clockTolerance,
41
+ upstashRegion: typeof upstashRegion === "string" ? upstashRegion : void 0
45
42
  });
46
43
  if (!isValid) {
47
44
  response.status(BAD_REQUEST);
@@ -59,15 +56,10 @@ function verifySignature(handler, config) {
59
56
  }
60
57
  function verifySignatureEdge(handler, config) {
61
58
  const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
62
- if (!currentSigningKey) {
63
- throw new Error(
64
- "currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
65
- );
66
- }
67
59
  const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
68
- if (!nextSigningKey) {
60
+ if (!currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
69
61
  throw new Error(
70
- "nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
62
+ "currentSigningKey and nextSigningKey are required, either in the config or as env variables (QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY)"
71
63
  );
72
64
  }
73
65
  const receiver = new Receiver({
@@ -85,11 +77,13 @@ function verifySignatureEdge(handler, config) {
85
77
  if (typeof signature !== "string") {
86
78
  throw new TypeError("`Upstash-Signature` header is not a string");
87
79
  }
80
+ const upstashRegion = request.headers.get("upstash-region");
88
81
  const body = await requestClone.text();
89
82
  const isValid = await receiver.verify({
90
83
  signature,
91
84
  body,
92
- clockTolerance: config?.clockTolerance
85
+ clockTolerance: config?.clockTolerance,
86
+ upstashRegion: upstashRegion ?? void 0
93
87
  });
94
88
  if (!isValid) {
95
89
  return new Response(new TextEncoder().encode("invalid signature"), { status: 403 });
@@ -99,15 +93,10 @@ function verifySignatureEdge(handler, config) {
99
93
  }
100
94
  function verifySignatureAppRouter(handler, config) {
101
95
  const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
102
- if (!currentSigningKey) {
103
- throw new Error(
104
- "currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
105
- );
106
- }
107
96
  const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
108
- if (!nextSigningKey) {
97
+ if (!currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
109
98
  throw new Error(
110
- "nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
99
+ "currentSigningKey and nextSigningKey are required, either in the config or as env variables (QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY)"
111
100
  );
112
101
  }
113
102
  const receiver = new Receiver({
@@ -125,11 +114,13 @@ function verifySignatureAppRouter(handler, config) {
125
114
  if (typeof signature !== "string") {
126
115
  throw new TypeError("`Upstash-Signature` header is not a string");
127
116
  }
117
+ const upstashRegion = request.headers.get("upstash-region");
128
118
  const body = await requestClone.text();
129
119
  const isValid = await receiver.verify({
130
120
  signature,
131
121
  body,
132
- clockTolerance: config?.clockTolerance
122
+ clockTolerance: config?.clockTolerance,
123
+ upstashRegion: upstashRegion ?? void 0
133
124
  });
134
125
  if (!isValid) {
135
126
  return new Response(new TextEncoder().encode("invalid signature"), { status: 403 });
package/nuxt.js CHANGED
@@ -360,6 +360,93 @@ var H3Response = globalThis.Response;
360
360
  // src/receiver.ts
361
361
  var jose = __toESM(require("jose"));
362
362
  var import_crypto_js = __toESM(require("crypto-js"));
363
+
364
+ // src/client/utils.ts
365
+ function getSafeEnvironment() {
366
+ return typeof process === "undefined" ? {} : process.env;
367
+ }
368
+
369
+ // src/client/multi-region/utils.ts
370
+ var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
371
+ var getRegionFromEnvironment = (environment) => {
372
+ const region = environment.QSTASH_REGION;
373
+ return normalizeRegionHeader(region);
374
+ };
375
+ function readEnvironmentVariables(environmentVariables, environment, region) {
376
+ const result = {};
377
+ for (const variable of environmentVariables) {
378
+ const key = region ? `${region}_${variable}` : variable;
379
+ result[variable] = environment[key];
380
+ }
381
+ return result;
382
+ }
383
+ function readReceiverEnvironmentVariables(environment, region) {
384
+ return readEnvironmentVariables(
385
+ ["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
386
+ environment,
387
+ region
388
+ );
389
+ }
390
+ function normalizeRegionHeader(region) {
391
+ if (!region) {
392
+ return void 0;
393
+ }
394
+ region = region.replaceAll("-", "_").toUpperCase();
395
+ if (VALID_REGIONS.includes(region)) {
396
+ return region;
397
+ }
398
+ console.warn(
399
+ `[Upstash QStash] Invalid UPSTASH_REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
400
+ ", "
401
+ )}.`
402
+ );
403
+ return void 0;
404
+ }
405
+
406
+ // src/client/multi-region/incoming.ts
407
+ var getReceiverSigningKeys = ({
408
+ environment,
409
+ regionFromHeader,
410
+ config
411
+ }) => {
412
+ if (config?.currentSigningKey && config.nextSigningKey) {
413
+ return {
414
+ currentSigningKey: config.currentSigningKey,
415
+ nextSigningKey: config.nextSigningKey
416
+ };
417
+ }
418
+ const regionEnvironment = getRegionFromEnvironment(environment);
419
+ if (regionEnvironment) {
420
+ const regionHeader = normalizeRegionHeader(regionFromHeader);
421
+ if (regionHeader) {
422
+ const regionCreds = readReceiverEnvironmentVariables(environment, regionHeader);
423
+ if (regionCreds.QSTASH_CURRENT_SIGNING_KEY && regionCreds.QSTASH_NEXT_SIGNING_KEY) {
424
+ return {
425
+ currentSigningKey: regionCreds.QSTASH_CURRENT_SIGNING_KEY,
426
+ nextSigningKey: regionCreds.QSTASH_NEXT_SIGNING_KEY,
427
+ region: regionHeader
428
+ };
429
+ } else {
430
+ console.warn(
431
+ `[Upstash QStash] Signing keys not found for region "${regionHeader}". Falling back to default signing keys.`
432
+ );
433
+ }
434
+ } else {
435
+ console.warn(
436
+ `[Upstash QStash] Invalid UPSTASH_REGION header value: "${regionFromHeader}". Expected one of: EU-CENTRAL-1, US-EAST-1. Falling back to default signing keys.`
437
+ );
438
+ }
439
+ }
440
+ const defaultCreds = readReceiverEnvironmentVariables(environment);
441
+ if (defaultCreds.QSTASH_CURRENT_SIGNING_KEY && defaultCreds.QSTASH_NEXT_SIGNING_KEY) {
442
+ return {
443
+ currentSigningKey: defaultCreds.QSTASH_CURRENT_SIGNING_KEY,
444
+ nextSigningKey: defaultCreds.QSTASH_NEXT_SIGNING_KEY
445
+ };
446
+ }
447
+ };
448
+
449
+ // src/receiver.ts
363
450
  var SignatureError = class extends Error {
364
451
  constructor(message) {
365
452
  super(message);
@@ -370,8 +457,8 @@ var Receiver = class {
370
457
  currentSigningKey;
371
458
  nextSigningKey;
372
459
  constructor(config) {
373
- this.currentSigningKey = config.currentSigningKey;
374
- this.nextSigningKey = config.nextSigningKey;
460
+ this.currentSigningKey = config?.currentSigningKey;
461
+ this.nextSigningKey = config?.nextSigningKey;
375
462
  }
376
463
  /**
377
464
  * Verify the signature of a request.
@@ -383,11 +470,25 @@ var Receiver = class {
383
470
  * If that fails, the signature is invalid and a `SignatureError` is thrown.
384
471
  */
385
472
  async verify(request) {
473
+ const environment = getSafeEnvironment();
474
+ const signingKeys = getReceiverSigningKeys({
475
+ environment,
476
+ regionFromHeader: request.upstashRegion,
477
+ config: {
478
+ currentSigningKey: this.currentSigningKey,
479
+ nextSigningKey: this.nextSigningKey
480
+ }
481
+ });
482
+ if (!signingKeys) {
483
+ throw new Error(
484
+ "[Upstash QStash] No signing keys available for verification. See the warning above for more details."
485
+ );
486
+ }
386
487
  let payload;
387
488
  try {
388
- payload = await this.verifyWithKey(this.currentSigningKey, request);
489
+ payload = await this.verifyWithKey(signingKeys.currentSigningKey, request);
389
490
  } catch {
390
- payload = await this.verifyWithKey(this.nextSigningKey, request);
491
+ payload = await this.verifyWithKey(signingKeys.nextSigningKey, request);
391
492
  }
392
493
  this.verifyBodyAndUrl(payload, request);
393
494
  return true;
@@ -429,15 +530,10 @@ var import_neverthrow3 = require("neverthrow");
429
530
  // platforms/h3.ts
430
531
  var verifySignatureH3 = (handler, config) => {
431
532
  const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
432
- if (!currentSigningKey) {
433
- throw new Error(
434
- "currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
435
- );
436
- }
437
533
  const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
438
- if (!nextSigningKey) {
534
+ if (!currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
439
535
  throw new Error(
440
- "nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
536
+ "currentSigningKey and nextSigningKey are required, either in the config or as env variables (QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY)"
441
537
  );
442
538
  }
443
539
  const receiver = new Receiver({
@@ -452,11 +548,13 @@ var verifySignatureH3 = (handler, config) => {
452
548
  if (typeof signature !== "string") {
453
549
  throw new TypeError("`Upstash-Signature` header is not a string");
454
550
  }
551
+ const upstashRegion = getHeader(event, "upstash-region");
455
552
  const body = await readRawBody(event);
456
553
  const isValid = await receiver.verify({
457
554
  signature,
458
555
  body: JSON.stringify(body),
459
- clockTolerance: config?.clockTolerance
556
+ clockTolerance: config?.clockTolerance,
557
+ upstashRegion: typeof upstashRegion === "string" ? upstashRegion : void 0
460
558
  });
461
559
  if (!isValid) {
462
560
  return { status: 403, body: "invalid signature" };
package/nuxt.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  verifySignatureH3
3
- } from "./chunk-TLUU4FA7.mjs";
4
- import "./chunk-5ACKA46J.mjs";
5
- import "./chunk-CR3B3DK3.mjs";
3
+ } from "./chunk-PB5UCB6Z.mjs";
4
+ import "./chunk-NU64UBMT.mjs";
5
+ import "./chunk-WOMVRJIB.mjs";
6
6
 
7
7
  // platforms/nuxt.ts
8
8
  var verifySignatureNuxt = verifySignatureH3;
package/package.json CHANGED
@@ -1 +1 @@
1
- {"version":"v2.8.3","name":"@upstash/qstash","description":"Official Typescript client for QStash","author":"Andreas Thomas <dev@chronark.com>","license":"MIT","homepage":"https://github.com/upstash/sdk-qstash-ts#readme","repository":{"type":"git","url":"git+https://github.com/upstash/sdk-qstash-ts.git"},"bugs":{"url":"https://github.com/upstash/sdk-qstash-ts/issues"},"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"},"./nuxt":{"import":"./nuxt.mjs","require":"./nuxt.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"}},"keywords":["qstash","queue","events","serverless","upstash"],"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"},"devDependencies":{"@commitlint/cli":"^19.2.2","@commitlint/config-conventional":"^19.2.2","@eslint/eslintrc":"^3.1.0","@eslint/js":"^9.10.0","@solidjs/start":"^1.0.6","@sveltejs/kit":"^2.5.18","@types/bun":"^1.1.1","@types/crypto-js":"^4.2.0","@typescript-eslint/eslint-plugin":"^8.4.0","@typescript-eslint/parser":"^8.4.0","ai":"^3.1.28","bun-types":"^1.1.7","eslint":"^9.10.0","eslint-plugin-unicorn":"^51.0.1","h3":"^1.12.0","hono":"^4.5.8","husky":"^9.0.10","next":"^14.0.2","prettier":"^3.2.5","tsup":"latest","typescript":"^5.4.5","undici-types":"^6.16.0","vitest":"latest"},"dependencies":{"crypto-js":">=4.2.0","jose":"^5.2.3","neverthrow":"^7.0.1"}}
1
+ {"version":"v2.9.0-rc","name":"@upstash/qstash","description":"Official Typescript client for QStash","author":"Andreas Thomas <dev@chronark.com>","license":"MIT","homepage":"https://github.com/upstash/sdk-qstash-ts#readme","repository":{"type":"git","url":"git+https://github.com/upstash/sdk-qstash-ts.git"},"bugs":{"url":"https://github.com/upstash/sdk-qstash-ts/issues"},"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"},"./nuxt":{"import":"./nuxt.mjs","require":"./nuxt.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"}},"keywords":["qstash","queue","events","serverless","upstash"],"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"},"devDependencies":{"@commitlint/cli":"^19.2.2","@commitlint/config-conventional":"^19.2.2","@eslint/eslintrc":"^3.1.0","@eslint/js":"^9.10.0","@solidjs/start":"^1.0.6","@sveltejs/kit":"^2.5.18","@types/bun":"^1.1.1","@types/crypto-js":"^4.2.0","@typescript-eslint/eslint-plugin":"^8.4.0","@typescript-eslint/parser":"^8.4.0","bun-types":"^1.1.7","eslint":"^9.10.0","eslint-plugin-unicorn":"^51.0.1","h3":"^1.12.0","hono":"^4.5.8","husky":"^9.0.10","next":"^14.0.2","prettier":"^3.2.5","tsup":"latest","typescript":"^5.4.5","undici-types":"^6.16.0","vitest":"latest"},"dependencies":{"crypto-js":">=4.2.0","jose":"^5.2.3","neverthrow":"^7.0.1"}}
package/solidjs.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { APIHandler, APIEvent } from '@solidjs/start/server';
2
- import { a9 as RouteFunction, aa as WorkflowServeOptions } from './client-BpQp_dGA.mjs';
2
+ import { a9 as RouteFunction, aa as WorkflowServeOptions } from './client-BVG9vt90.mjs';
3
3
  import 'neverthrow';
4
4
 
5
5
  type VerifySignatureConfig = {
package/solidjs.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { APIHandler, APIEvent } from '@solidjs/start/server';
2
- import { a9 as RouteFunction, aa as WorkflowServeOptions } from './client-BpQp_dGA.js';
2
+ import { a9 as RouteFunction, aa as WorkflowServeOptions } from './client-BVG9vt90.js';
3
3
  import 'neverthrow';
4
4
 
5
5
  type VerifySignatureConfig = {