@upstash/qstash 2.10.0 → 2.11.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/nextjs.js CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // platforms/nextjs.ts
31
31
  var nextjs_exports = {};
32
32
  __export(nextjs_exports, {
33
+ registerQStashDev: () => registerQStashDev,
33
34
  serve: () => serve2,
34
35
  servePagesRouter: () => servePagesRouter,
35
36
  verifySignature: () => verifySignature,
@@ -461,17 +462,23 @@ function normalizeCursor(response) {
461
462
  const cursor = response.cursor;
462
463
  return { ...response, cursor: cursor || void 0 };
463
464
  }
465
+ function _processGlobal() {
466
+ const proc = globalThis["process"];
467
+ return proc;
468
+ }
464
469
  function getRuntime() {
465
- if (typeof process === "object" && typeof process.versions == "object" && process.versions.bun)
466
- return `bun@${process.versions.bun}`;
470
+ const proc = _processGlobal();
471
+ if (proc?.versions?.bun)
472
+ return `bun@${proc.versions.bun}`;
467
473
  if (typeof EdgeRuntime === "string")
468
474
  return "edge-light";
469
- else if (typeof process === "object" && typeof process.version === "string")
470
- return `node@${process.version}`;
475
+ if (typeof proc?.version === "string")
476
+ return `node@${proc.version}`;
471
477
  return "";
472
478
  }
473
479
  function getSafeEnvironment() {
474
- return typeof process === "undefined" ? {} : process.env;
480
+ const proc = _processGlobal();
481
+ return proc?.env ?? {};
475
482
  }
476
483
 
477
484
  // src/client/multi-region/utils.ts
@@ -515,12 +522,451 @@ function normalizeRegionHeader(region) {
515
522
  return void 0;
516
523
  }
517
524
 
525
+ // src/dev-server/constants.ts
526
+ var DEFAULT_DEV_PORT = 8080;
527
+ var DEV_CREDENTIALS = {
528
+ token: "eyJVc2VySUQiOiJkZWZhdWx0VXNlciIsIlBhc3N3b3JkIjoiZGVmYXVsdFBhc3N3b3JkIn0=",
529
+ currentSigningKey: "sig_7kYjw48mhY7kAjqNGcy6cr29RJ6r",
530
+ nextSigningKey: "sig_5ZB6DVzB1wjE8S6rZ7eenA8Pdnhs"
531
+ };
532
+ var GITHUB_RELEASES_URL = "https://api.github.com/repos/upstash/qstash-cli/releases/latest";
533
+ var BINARY_URL_BASE = "https://artifacts.upstash.com/qstash/versions";
534
+ var CONSOLE_URL = "https://console.upstash.com/qstash/local-mode-user";
535
+ var DEV_PREFIX = "\x1B[2m[QStash Dev]\x1B[0m";
536
+ var CLI_PREFIX = "\x1B[2m[QStash CLI]\x1B[0m";
537
+ var _n = (m) => `node:${m}`;
538
+ var importHttp = () => import(
539
+ /* webpackIgnore: true */
540
+ _n("http")
541
+ );
542
+ var importHttps = () => import(
543
+ /* webpackIgnore: true */
544
+ _n("https")
545
+ );
546
+ var importFs = () => import(
547
+ /* webpackIgnore: true */
548
+ _n("fs")
549
+ );
550
+ var importChildProcess = () => import(
551
+ /* webpackIgnore: true */
552
+ _n("child_process")
553
+ );
554
+ var importOs = () => import(
555
+ /* webpackIgnore: true */
556
+ _n("os")
557
+ );
558
+
559
+ // src/dev-server/http.ts
560
+ var HTTP_OK = 200;
561
+ var HTTP_MULTI_CHOICE = 300;
562
+ var nativeGet = async (url, headers, timeoutMs) => {
563
+ const parsedUrl = new URL(url);
564
+ const httpModule = parsedUrl.protocol === "https:" ? await importHttps() : await importHttp();
565
+ return new Promise((resolve, reject) => {
566
+ const request = httpModule.get(url, { headers }, (response) => {
567
+ const chunks = [];
568
+ response.on("data", (chunk) => chunks.push(chunk));
569
+ response.on("end", () => {
570
+ const statusCode = response.statusCode ?? 0;
571
+ resolve({
572
+ ok: statusCode >= HTTP_OK && statusCode < HTTP_MULTI_CHOICE,
573
+ statusCode,
574
+ body: Buffer.concat(chunks)
575
+ });
576
+ });
577
+ response.on("error", reject);
578
+ });
579
+ if (timeoutMs) {
580
+ request.setTimeout(timeoutMs, () => {
581
+ request.destroy(new Error("Request timed out"));
582
+ });
583
+ }
584
+ request.on("error", reject);
585
+ });
586
+ };
587
+
588
+ // src/dev-server/health.ts
589
+ var HEALTH_CHECK_TIMEOUT_MS = 2e3;
590
+ var isDevServerRunning = async (baseUrl) => {
591
+ try {
592
+ const { ok: ok4, body } = await nativeGet(
593
+ `${baseUrl}/v2/keys`,
594
+ { Authorization: `Bearer ${DEV_CREDENTIALS.token}` },
595
+ HEALTH_CHECK_TIMEOUT_MS
596
+ );
597
+ if (!ok4)
598
+ return false;
599
+ const data = JSON.parse(body.toString());
600
+ return data.current === DEV_CREDENTIALS.currentSigningKey && data.next === DEV_CREDENTIALS.nextSigningKey;
601
+ } catch {
602
+ return false;
603
+ }
604
+ };
605
+ var _didLogUnreachable = false;
606
+ var checkDevServerReachable = async (baseUrl, runtime) => {
607
+ if (await pingEdge(baseUrl))
608
+ return;
609
+ if (!_didLogUnreachable) {
610
+ console.error(unreachableMessage(baseUrl, runtime));
611
+ _didLogUnreachable = true;
612
+ }
613
+ throw new Error(`${DEV_PREFIX} dev server unreachable at ${baseUrl}`);
614
+ };
615
+ var pingEdge = async (baseUrl) => {
616
+ try {
617
+ const controller = new AbortController();
618
+ const timeout = setTimeout(() => controller.abort(), HEALTH_CHECK_TIMEOUT_MS);
619
+ const response = await fetch(`${baseUrl}/v2/keys`, {
620
+ headers: { Authorization: `Bearer ${DEV_CREDENTIALS.token}` },
621
+ signal: controller.signal
622
+ });
623
+ clearTimeout(timeout);
624
+ return response.ok;
625
+ } catch {
626
+ return false;
627
+ }
628
+ };
629
+ var unreachableMessage = (baseUrl, runtime) => {
630
+ const port = new URL(baseUrl).port;
631
+ const manualStartCmd = `npx @upstash/qstash-cli dev --port ${port}`;
632
+ const header = `
633
+ ${DEV_PREFIX} The dev server is not running at ${baseUrl}.
634
+
635
+ `;
636
+ if (runtime === "cloudflare-workers") {
637
+ return header + `Cloudflare Workers cannot start the dev server automatically.
638
+ Start it manually before running wrangler dev:
639
+
640
+ ${manualStartCmd}
641
+ `;
642
+ }
643
+ return header + `Edge runtimes cannot start the dev server automatically.
644
+ Either:
645
+ 1. Add the instrumentation hook to start it with your app:
646
+
647
+ // instrumentation.ts
648
+ import { registerQStashDev } from "@upstash/qstash/nextjs";
649
+ export async function register() { await registerQStashDev(); }
650
+
651
+ 2. Or start it manually:
652
+
653
+ ${manualStartCmd}
654
+ `;
655
+ };
656
+
657
+ // src/dev-server/binary.ts
658
+ var ensureBinary = async () => {
659
+ const fs = await importFs();
660
+ const os = await importOs();
661
+ const cacheDirectory = await findCacheDirectory();
662
+ const isWindows = os.platform() === "win32";
663
+ const binaryName = isWindows ? "qstash.exe" : "qstash";
664
+ const binaryPath = `${cacheDirectory}/${binaryName}`;
665
+ const versionFile = `${cacheDirectory}/.version`;
666
+ let version;
667
+ try {
668
+ version = await fetchLatestVersion();
669
+ } catch (error) {
670
+ if (fs.existsSync(binaryPath)) {
671
+ const cachedVersion = fs.existsSync(versionFile) ? fs.readFileSync(versionFile, "utf8").trim() : "unknown";
672
+ console.log(`${DEV_PREFIX} Offline, using local v${cachedVersion}`);
673
+ return binaryPath;
674
+ }
675
+ throw error;
676
+ }
677
+ return downloadBinary(version, cacheDirectory);
678
+ };
679
+ var fetchLatestVersion = async () => {
680
+ const { ok: ok4, statusCode, body } = await nativeGet(GITHUB_RELEASES_URL, {
681
+ Accept: "application/vnd.github.v3+json",
682
+ "User-Agent": "upstash-qstash-js"
683
+ });
684
+ if (!ok4) {
685
+ throw new Error(`[QStash Dev] Failed to fetch latest version: HTTP ${statusCode}`);
686
+ }
687
+ const data = JSON.parse(body.toString());
688
+ return data.tag_name.replace(/^v/, "");
689
+ };
690
+ var findCacheDirectory = async () => {
691
+ const fs = await importFs();
692
+ const os = await importOs();
693
+ const home = os.homedir();
694
+ const platform = os.platform();
695
+ let base;
696
+ if (platform === "darwin") {
697
+ base = `${home}/Library/Caches/upstash`;
698
+ } else if (platform === "win32") {
699
+ base = `${process.env.LOCALAPPDATA ?? `${home}/AppData/Local`}/upstash`;
700
+ } else {
701
+ base = `${home}/.cache/upstash`;
702
+ }
703
+ const cacheDirectory = `${base}/qstash-dev`;
704
+ await fs.promises.mkdir(cacheDirectory, { recursive: true });
705
+ return cacheDirectory;
706
+ };
707
+ var downloadBinary = async (version, cacheDirectory) => {
708
+ const fs = await importFs();
709
+ const childProcess = await importChildProcess();
710
+ const os = await importOs();
711
+ const osPlatform = os.platform();
712
+ const isWindows = osPlatform === "win32";
713
+ const platform = isWindows ? "windows" : osPlatform === "darwin" ? "darwin" : "linux";
714
+ const arch = os.arch() === "arm64" ? "arm64" : "amd64";
715
+ const archiveName = `qstash-server_${version}_${platform}_${arch}`;
716
+ const binaryName = isWindows ? "qstash.exe" : "qstash";
717
+ const binaryPath = `${cacheDirectory}/${binaryName}`;
718
+ const versionFile = `${cacheDirectory}/.version`;
719
+ if (fs.existsSync(binaryPath) && fs.existsSync(versionFile)) {
720
+ const cachedVersion = fs.readFileSync(versionFile, "utf8").trim();
721
+ if (cachedVersion === version) {
722
+ return binaryPath;
723
+ }
724
+ }
725
+ await fs.promises.rm(cacheDirectory, { recursive: true, force: true });
726
+ await fs.promises.mkdir(cacheDirectory, { recursive: true });
727
+ const extension = isWindows ? "zip" : "tar.gz";
728
+ const archiveUrl = `${BINARY_URL_BASE}/${version}/${archiveName}.${extension}`;
729
+ console.log(`${DEV_PREFIX} Downloading dev server v${version}...`);
730
+ const { ok: ok4, statusCode, body } = await nativeGet(archiveUrl);
731
+ if (!ok4) {
732
+ throw new Error(`[QStash Dev] Failed to download binary: HTTP ${statusCode}`);
733
+ }
734
+ const archivePath = `${cacheDirectory}/${archiveName}.${extension}`;
735
+ await fs.promises.writeFile(archivePath, new Uint8Array(body));
736
+ childProcess.execFileSync("tar", ["-xf", archivePath, "-C", cacheDirectory], {
737
+ stdio: "pipe"
738
+ });
739
+ if (!isWindows) {
740
+ const EXECUTABLE_PERMISSION = 493;
741
+ await fs.promises.chmod(binaryPath, EXECUTABLE_PERMISSION);
742
+ }
743
+ await fs.promises.writeFile(versionFile, version);
744
+ await fs.promises.unlink(archivePath).catch(() => {
745
+ });
746
+ return binaryPath;
747
+ };
748
+
749
+ // src/dev-server/process.ts
750
+ var STARTUP_TIMEOUT_MS = 3e4;
751
+ var _proc = () => {
752
+ return globalThis["process"] ?? {};
753
+ };
754
+ var spawnServer = async (binaryPath, port, onUnexpectedExit) => {
755
+ const childProcess = await importChildProcess();
756
+ const child = await new Promise((resolve, reject) => {
757
+ const child2 = childProcess.spawn(binaryPath, ["dev", "--port", String(port)], {
758
+ stdio: ["ignore", "pipe", "pipe"]
759
+ });
760
+ const timeout = setTimeout(() => {
761
+ child2.kill();
762
+ reject(new Error("[QStash Dev] Server failed to start within 30 seconds"));
763
+ }, STARTUP_TIMEOUT_MS);
764
+ let startupOutput = "";
765
+ let started = false;
766
+ const bufferLine = (line) => {
767
+ if (!started)
768
+ startupOutput += `${line}
769
+ `;
770
+ };
771
+ forwardWithPrefix(child2.stdout, _proc().stdout, (line) => {
772
+ bufferLine(line);
773
+ if (!started && /runn+ing( at|\.)/i.test(line)) {
774
+ clearTimeout(timeout);
775
+ started = true;
776
+ resolve(child2);
777
+ }
778
+ });
779
+ forwardWithPrefix(child2.stderr, _proc().stderr, bufferLine);
780
+ child2.on("error", (error) => {
781
+ clearTimeout(timeout);
782
+ reject(new Error(`[QStash Dev] Failed to start server: ${error.message}`));
783
+ });
784
+ child2.on("close", (code, _signal) => {
785
+ if (started) {
786
+ onUnexpectedExit?.();
787
+ return;
788
+ }
789
+ clearTimeout(timeout);
790
+ reject(new Error(formatStartupError(code, startupOutput)));
791
+ });
792
+ });
793
+ registerCleanup(child);
794
+ child.unref?.();
795
+ child.stdout?.unref?.();
796
+ child.stderr?.unref?.();
797
+ };
798
+ var formatStartupError = (code, startupOutput) => {
799
+ const cleaned = startupOutput.replaceAll(/\u001B\[[\d;]*m/g, "").replaceAll(/^\d{1,2}:\d{2}(AM|PM)\s+\w{3}\s+/gm, "").trim();
800
+ if (/address already in use/i.test(cleaned)) {
801
+ const match = /:(\d+)\s*$/.exec(cleaned);
802
+ const portHint = match ? ` on port ${match[1]}` : "";
803
+ return `[QStash Dev] Port already in use${portHint}. Set QSTASH_DEV_PORT to use a different port, or stop the process holding it.`;
804
+ }
805
+ const codeSuffix = code ? ` with code ${code}` : "";
806
+ const detail = cleaned ? `: ${cleaned}` : "";
807
+ return `[QStash Dev] Server exited unexpectedly${codeSuffix}${detail}`;
808
+ };
809
+ var forwardWithPrefix = (source, destination, onLine) => {
810
+ if (!source)
811
+ return;
812
+ let buffer = "";
813
+ const flushLine = (line) => {
814
+ destination?.write(`${CLI_PREFIX} ${line}
815
+ `);
816
+ onLine(line);
817
+ };
818
+ source.on("data", (data) => {
819
+ buffer += data.toString();
820
+ let newlineIndex = buffer.indexOf("\n");
821
+ while (newlineIndex !== -1) {
822
+ flushLine(buffer.slice(0, newlineIndex));
823
+ buffer = buffer.slice(newlineIndex + 1);
824
+ newlineIndex = buffer.indexOf("\n");
825
+ }
826
+ });
827
+ source.on("end", () => {
828
+ if (buffer.length > 0) {
829
+ flushLine(buffer);
830
+ buffer = "";
831
+ }
832
+ });
833
+ source.on("error", () => {
834
+ });
835
+ };
836
+ var currentChild;
837
+ var processHandlersRegistered = false;
838
+ var killCurrentChild = () => {
839
+ if (!currentChild)
840
+ return;
841
+ try {
842
+ currentChild.kill("SIGTERM");
843
+ } catch {
844
+ }
845
+ currentChild = void 0;
846
+ };
847
+ var registerCleanup = (child) => {
848
+ currentChild = child;
849
+ if (!processHandlersRegistered) {
850
+ processHandlersRegistered = true;
851
+ const proc = _proc();
852
+ proc.on?.("exit", killCurrentChild);
853
+ proc.on?.("SIGINT", () => {
854
+ killCurrentChild();
855
+ proc.exit?.(0);
856
+ });
857
+ proc.on?.("SIGTERM", () => {
858
+ killCurrentChild();
859
+ proc.exit?.(0);
860
+ });
861
+ }
862
+ };
863
+
864
+ // src/dev-server/index.ts
865
+ var _processGlobal2 = () => {
866
+ const proc = globalThis["process"];
867
+ return proc;
868
+ };
869
+ var devServerPromise;
870
+ var ensureDevelopmentServer = (env, devMode) => {
871
+ if (!shouldUseDevelopmentMode(devMode, env))
872
+ return Promise.resolve();
873
+ const procEnv = _processGlobal2()?.env;
874
+ if (procEnv?.NEXT_PHASE === "phase-production-build")
875
+ return Promise.resolve();
876
+ if (procEnv?.NODE_ENV === "production")
877
+ return Promise.resolve();
878
+ const runtime = getRuntime2();
879
+ if (runtime !== "nodejs") {
880
+ return checkDevServerReachable(getDevUrl(env), runtime);
881
+ }
882
+ if (!devServerPromise) {
883
+ devServerPromise = startPipeline(env).catch((error) => {
884
+ devServerPromise = void 0;
885
+ throw error;
886
+ });
887
+ }
888
+ return devServerPromise;
889
+ };
890
+ var startPipeline = async (env) => {
891
+ const baseUrl = getDevUrl(env);
892
+ const port = new URL(baseUrl).port;
893
+ const consoleLink = `\x1B[36m${CONSOLE_URL}?port=${port}\x1B[0m`;
894
+ if (await isDevServerRunning(baseUrl)) {
895
+ console.log(
896
+ `${DEV_PREFIX} Server already running at ${baseUrl}
897
+ ${DEV_PREFIX} Console: ${consoleLink}`
898
+ );
899
+ return;
900
+ }
901
+ const binaryPath = await ensureBinary();
902
+ await spawnServer(binaryPath, port, () => {
903
+ devServerPromise = void 0;
904
+ });
905
+ };
906
+ var shouldUseDevelopmentMode = (devMode, env) => {
907
+ if (devMode !== void 0)
908
+ return devMode;
909
+ const value = env?.QSTASH_DEV ?? getProcessEnvironment("QSTASH_DEV");
910
+ if (value === void 0 || value === "" || value === "false" || value === "0")
911
+ return false;
912
+ if (value === "true" || value === "1")
913
+ return true;
914
+ throw new Error(`[QStash Dev] Invalid value for QSTASH_DEV in environment: ${value}`);
915
+ };
916
+ var getDevelopmentCredentials = (env) => {
917
+ return {
918
+ ...DEV_CREDENTIALS,
919
+ baseUrl: getDevUrl(env)
920
+ };
921
+ };
922
+ var getDevUrl = (env) => {
923
+ const portString = env?.QSTASH_DEV_PORT ?? getProcessEnvironment("QSTASH_DEV_PORT");
924
+ let port = DEFAULT_DEV_PORT;
925
+ if (portString) {
926
+ const parsed = Number.parseInt(portString, 10);
927
+ if (!Number.isNaN(parsed) && parsed > 0) {
928
+ port = parsed;
929
+ }
930
+ }
931
+ return `http://127.0.0.1:${port}`;
932
+ };
933
+ var getRuntime2 = () => {
934
+ if (typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers") {
935
+ return "cloudflare-workers";
936
+ }
937
+ const proc = _processGlobal2();
938
+ if (!proc) {
939
+ return "browser";
940
+ }
941
+ if (!proc.release?.name) {
942
+ return "edge";
943
+ }
944
+ return "nodejs";
945
+ };
946
+ var getProcessEnvironment = (key) => {
947
+ const proc = _processGlobal2();
948
+ return proc?.env ? proc.env[key] : void 0;
949
+ };
950
+
518
951
  // src/client/multi-region/incoming.ts
519
952
  var getReceiverSigningKeys = ({
520
953
  environment,
521
954
  regionFromHeader,
522
- config
955
+ config,
956
+ devMode
523
957
  }) => {
958
+ if (shouldUseDevelopmentMode(devMode, environment)) {
959
+ if (config?.currentSigningKey || config?.nextSigningKey) {
960
+ console.warn(
961
+ `${DEV_PREFIX} Dev mode is active. Ignoring signing keys from config. Set devMode: false to use your own keys.`
962
+ );
963
+ }
964
+ const developmentCreds = getDevelopmentCredentials(environment);
965
+ return {
966
+ currentSigningKey: developmentCreds.currentSigningKey,
967
+ nextSigningKey: developmentCreds.nextSigningKey
968
+ };
969
+ }
524
970
  if (config?.currentSigningKey && config.nextSigningKey) {
525
971
  return {
526
972
  currentSigningKey: config.currentSigningKey,
@@ -565,8 +1011,21 @@ var getClientCredentials = (clientCredentialConfig) => {
565
1011
  };
566
1012
  var resolveCredentials = ({
567
1013
  environment,
568
- config
1014
+ config,
1015
+ devMode
569
1016
  }) => {
1017
+ if (shouldUseDevelopmentMode(devMode, environment)) {
1018
+ if (config?.baseUrl || config?.token) {
1019
+ console.warn(
1020
+ `${DEV_PREFIX} Dev mode is active. Ignoring baseUrl/token from config. Set devMode: false to use your own credentials.`
1021
+ );
1022
+ }
1023
+ const developmentCreds = getDevelopmentCredentials(environment);
1024
+ return {
1025
+ baseUrl: developmentCreds.baseUrl,
1026
+ token: developmentCreds.token
1027
+ };
1028
+ }
570
1029
  if (config?.baseUrl && config.token) {
571
1030
  return {
572
1031
  baseUrl: config.baseUrl,
@@ -619,9 +1078,11 @@ var SignatureError = class extends Error {
619
1078
  var Receiver = class {
620
1079
  currentSigningKey;
621
1080
  nextSigningKey;
1081
+ devMode;
622
1082
  constructor(config) {
623
1083
  this.currentSigningKey = config?.currentSigningKey;
624
1084
  this.nextSigningKey = config?.nextSigningKey;
1085
+ this.devMode = config?.devMode;
625
1086
  }
626
1087
  /**
627
1088
  * Verify the signature of a request.
@@ -640,7 +1101,8 @@ var Receiver = class {
640
1101
  config: {
641
1102
  currentSigningKey: this.currentSigningKey,
642
1103
  nextSigningKey: this.nextSigningKey
643
- }
1104
+ },
1105
+ devMode: this.devMode
644
1106
  });
645
1107
  if (!signingKeys) {
646
1108
  throw new Error(
@@ -906,12 +1368,14 @@ var HttpClient = class {
906
1368
  baseUrl;
907
1369
  authorization;
908
1370
  options;
1371
+ devMode;
909
1372
  retry;
910
1373
  headers;
911
1374
  telemetryHeaders;
912
1375
  constructor(config) {
913
1376
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
914
1377
  this.authorization = config.authorization;
1378
+ this.devMode = config.devMode;
915
1379
  this.retry = // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
916
1380
  typeof config.retry === "boolean" && !config.retry ? {
917
1381
  attempts: 1,
@@ -924,6 +1388,7 @@ var HttpClient = class {
924
1388
  this.telemetryHeaders = config.telemetryHeaders;
925
1389
  }
926
1390
  async request(request) {
1391
+ await ensureDevelopmentServer(void 0, this.devMode);
927
1392
  const { response } = await this.requestWithBackoff(request);
928
1393
  if (request.parseResponseAsJson === false) {
929
1394
  return void 0;
@@ -931,6 +1396,7 @@ var HttpClient = class {
931
1396
  return await response.json();
932
1397
  }
933
1398
  async *requestStream(request) {
1399
+ await ensureDevelopmentServer(void 0, this.devMode);
934
1400
  const { response } = await this.requestWithBackoff(request);
935
1401
  if (!response.body) {
936
1402
  throw new Error("No response body");
@@ -1653,7 +2119,7 @@ var UrlGroups = class {
1653
2119
  };
1654
2120
 
1655
2121
  // version.ts
1656
- var VERSION = "2.10.0";
2122
+ var VERSION = "2.11.0";
1657
2123
 
1658
2124
  // src/client/client.ts
1659
2125
  var Client = class {
@@ -1661,7 +2127,14 @@ var Client = class {
1661
2127
  token;
1662
2128
  constructor(config) {
1663
2129
  const environment = getSafeEnvironment();
1664
- const { baseUrl, token } = getClientCredentials({ environment, config });
2130
+ const { baseUrl, token } = getClientCredentials({
2131
+ environment,
2132
+ config,
2133
+ devMode: config?.devMode
2134
+ });
2135
+ if (shouldUseDevelopmentMode(config?.devMode, environment)) {
2136
+ void ensureDevelopmentServer(environment, config?.devMode);
2137
+ }
1665
2138
  const enableTelemetry = environment.UPSTASH_DISABLE_TELEMETRY ? false : config?.enableTelemetry ?? true;
1666
2139
  const isCloudflare = typeof caches !== "undefined" && "default" in caches;
1667
2140
  const telemetryHeaders = new Headers(
@@ -1678,7 +2151,8 @@ var Client = class {
1678
2151
  //@ts-expect-error caused by undici and bunjs type overlap
1679
2152
  headers: prefixHeaders(new Headers(config?.headers ?? {})),
1680
2153
  //@ts-expect-error caused by undici and bunjs type overlap
1681
- telemetryHeaders
2154
+ telemetryHeaders,
2155
+ devMode: config?.devMode
1682
2156
  });
1683
2157
  this.token = token;
1684
2158
  }
@@ -3286,14 +3760,16 @@ var BAD_REQUEST = 400;
3286
3760
  function verifySignature(handler, config) {
3287
3761
  const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
3288
3762
  const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
3289
- if (!currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
3763
+ const devMode = shouldUseDevelopmentMode(config?.devMode, process.env);
3764
+ if (!devMode && !currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
3290
3765
  throw new Error(
3291
3766
  "currentSigningKey and nextSigningKey are required, either in the config or as env variables (QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY)"
3292
3767
  );
3293
3768
  }
3294
3769
  const receiver = new Receiver({
3295
3770
  currentSigningKey,
3296
- nextSigningKey
3771
+ nextSigningKey,
3772
+ devMode: config?.devMode
3297
3773
  });
3298
3774
  return async (request, response) => {
3299
3775
  const signature = request.headers["upstash-signature"];
@@ -3335,14 +3811,16 @@ function verifySignature(handler, config) {
3335
3811
  function verifySignatureEdge(handler, config) {
3336
3812
  const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
3337
3813
  const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
3338
- if (!currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
3814
+ const devMode = shouldUseDevelopmentMode(config?.devMode, process.env);
3815
+ if (!devMode && !currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
3339
3816
  throw new Error(
3340
3817
  "currentSigningKey and nextSigningKey are required, either in the config or as env variables (QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY)"
3341
3818
  );
3342
3819
  }
3343
3820
  const receiver = new Receiver({
3344
3821
  currentSigningKey,
3345
- nextSigningKey
3822
+ nextSigningKey,
3823
+ devMode: config?.devMode
3346
3824
  });
3347
3825
  return async (request, nfe) => {
3348
3826
  const requestClone = request.clone();
@@ -3372,14 +3850,16 @@ function verifySignatureEdge(handler, config) {
3372
3850
  function verifySignatureAppRouter(handler, config) {
3373
3851
  const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
3374
3852
  const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
3375
- if (!currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
3853
+ const devMode = shouldUseDevelopmentMode(config?.devMode, process.env);
3854
+ if (!devMode && !currentSigningKey && !nextSigningKey && !process.env.QSTASH_REGION) {
3376
3855
  throw new Error(
3377
3856
  "currentSigningKey and nextSigningKey are required, either in the config or as env variables (QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY)"
3378
3857
  );
3379
3858
  }
3380
3859
  const receiver = new Receiver({
3381
3860
  currentSigningKey,
3382
- nextSigningKey
3861
+ nextSigningKey,
3862
+ devMode: config?.devMode
3383
3863
  });
3384
3864
  return async (request, params) => {
3385
3865
  const requestClone = request.clone();
@@ -3437,8 +3917,16 @@ var servePagesRouter = (routeFunction, options) => {
3437
3917
  res.status(response.status).json(await response.json());
3438
3918
  };
3439
3919
  };
3920
+ async function registerQStashDev() {
3921
+ if (process.env.NODE_ENV === "production")
3922
+ return;
3923
+ if (process.env.NEXT_PHASE === "phase-production-build")
3924
+ return;
3925
+ await ensureDevelopmentServer(void 0, true);
3926
+ }
3440
3927
  // Annotate the CommonJS export names for ESM import in node:
3441
3928
  0 && (module.exports = {
3929
+ registerQStashDev,
3442
3930
  serve,
3443
3931
  servePagesRouter,
3444
3932
  verifySignature,