@upstash/redis 1.35.0-canary → 1.35.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.
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // platforms/nodejs.ts
@@ -50,12 +40,57 @@ var UpstashError = class extends Error {
50
40
  var UrlError = class extends Error {
51
41
  constructor(url) {
52
42
  super(
53
- `Upstash Redis client was passed an invalid URL. You should pass the URL together with https. Received: "${url}". `
43
+ `Upstash Redis client was passed an invalid URL. You should pass a URL starting with https. Received: "${url}". `
54
44
  );
55
45
  this.name = "UrlError";
56
46
  }
57
47
  };
58
48
 
49
+ // pkg/util.ts
50
+ function parseRecursive(obj) {
51
+ const parsed = Array.isArray(obj) ? obj.map((o) => {
52
+ try {
53
+ return parseRecursive(o);
54
+ } catch {
55
+ return o;
56
+ }
57
+ }) : JSON.parse(obj);
58
+ if (typeof parsed === "number" && parsed.toString() !== obj) {
59
+ return obj;
60
+ }
61
+ return parsed;
62
+ }
63
+ function parseResponse(result) {
64
+ try {
65
+ return parseRecursive(result);
66
+ } catch {
67
+ return result;
68
+ }
69
+ }
70
+ function deserializeScanResponse(result) {
71
+ return [result[0], ...parseResponse(result.slice(1))];
72
+ }
73
+ function deserializeScanWithTypesResponse(result) {
74
+ const [cursor, keys] = result;
75
+ const parsedKeys = [];
76
+ for (let i = 0; i < keys.length; i += 2) {
77
+ parsedKeys.push({ key: keys[i], type: keys[i + 1] });
78
+ }
79
+ return [cursor, parsedKeys];
80
+ }
81
+ function mergeHeaders(...headers) {
82
+ const merged = {};
83
+ for (const header of headers) {
84
+ if (!header) continue;
85
+ for (const [key, value] of Object.entries(header)) {
86
+ if (value !== void 0 && value !== null) {
87
+ merged[key] = value;
88
+ }
89
+ }
90
+ }
91
+ return merged;
92
+ }
93
+
59
94
  // pkg/http.ts
60
95
  var HttpClient = class {
61
96
  baseUrl;
@@ -63,6 +98,7 @@ var HttpClient = class {
63
98
  options;
64
99
  readYourWrites;
65
100
  upstashSyncToken = "";
101
+ hasCredentials;
66
102
  retry;
67
103
  constructor(config) {
68
104
  this.options = {
@@ -76,15 +112,16 @@ var HttpClient = class {
76
112
  };
77
113
  this.upstashSyncToken = "";
78
114
  this.readYourWrites = config.readYourWrites ?? true;
79
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
115
+ this.baseUrl = (config.baseUrl || "").replace(/\/$/, "");
80
116
  const urlRegex = /^https?:\/\/[^\s#$./?].\S*$/;
81
- if (!urlRegex.test(this.baseUrl)) {
117
+ if (this.baseUrl && !urlRegex.test(this.baseUrl)) {
82
118
  throw new UrlError(this.baseUrl);
83
119
  }
84
120
  this.headers = {
85
121
  "Content-Type": "application/json",
86
122
  ...config.headers
87
123
  };
124
+ this.hasCredentials = Boolean(this.baseUrl && this.headers.authorization.split(" ")[1]);
88
125
  if (this.options.responseEncoding === "base64") {
89
126
  this.headers["Upstash-Encoding"] = "base64";
90
127
  }
@@ -102,20 +139,28 @@ var HttpClient = class {
102
139
  this.headers = merge(this.headers, "Upstash-Telemetry-Sdk", telemetry.sdk);
103
140
  }
104
141
  async request(req) {
142
+ const requestHeaders = mergeHeaders(this.headers, req.headers ?? {});
143
+ const requestUrl = [this.baseUrl, ...req.path ?? []].join("/");
144
+ const isEventStream = requestHeaders.Accept === "text/event-stream";
105
145
  const requestOptions = {
106
146
  //@ts-expect-error this should throw due to bun regression
107
147
  cache: this.options.cache,
108
148
  method: "POST",
109
- headers: this.headers,
149
+ headers: requestHeaders,
110
150
  body: JSON.stringify(req.body),
111
151
  keepalive: this.options.keepAlive,
112
152
  agent: this.options.agent,
113
- signal: this.options.signal,
153
+ signal: req.signal ?? this.options.signal,
114
154
  /**
115
155
  * Fastly specific
116
156
  */
117
157
  backend: this.options.backend
118
158
  };
159
+ if (!this.hasCredentials) {
160
+ console.warn(
161
+ "[Upstash Redis] Redis client was initialized without url or token. Failed to execute command."
162
+ );
163
+ }
119
164
  if (this.readYourWrites) {
120
165
  const newHeader = this.upstashSyncToken;
121
166
  this.headers["upstash-sync-token"] = newHeader;
@@ -124,7 +169,7 @@ var HttpClient = class {
124
169
  let error = null;
125
170
  for (let i = 0; i <= this.retry.attempts; i++) {
126
171
  try {
127
- res = await fetch([this.baseUrl, ...req.path ?? []].join("/"), requestOptions);
172
+ res = await fetch(requestUrl, requestOptions);
128
173
  break;
129
174
  } catch (error_) {
130
175
  if (this.options.signal?.aborted) {
@@ -139,20 +184,54 @@ var HttpClient = class {
139
184
  break;
140
185
  }
141
186
  error = error_;
142
- await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
187
+ if (i < this.retry.attempts) {
188
+ await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
189
+ }
143
190
  }
144
191
  }
145
192
  if (!res) {
146
193
  throw error ?? new Error("Exhausted all retries");
147
194
  }
148
- const body = await res.json();
149
195
  if (!res.ok) {
150
- throw new UpstashError(`${body.error}, command was: ${JSON.stringify(req.body)}`);
196
+ const body2 = await res.json();
197
+ throw new UpstashError(`${body2.error}, command was: ${JSON.stringify(req.body)}`);
151
198
  }
152
199
  if (this.readYourWrites) {
153
200
  const headers = res.headers;
154
201
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
155
202
  }
203
+ if (isEventStream && req && req.onMessage && res.body) {
204
+ const reader = res.body.getReader();
205
+ const decoder = new TextDecoder();
206
+ (async () => {
207
+ try {
208
+ while (true) {
209
+ const { value, done } = await reader.read();
210
+ if (done) break;
211
+ const chunk = decoder.decode(value);
212
+ const lines = chunk.split("\n");
213
+ for (const line of lines) {
214
+ if (line.startsWith("data: ")) {
215
+ const data = line.slice(6);
216
+ req.onMessage?.(data);
217
+ }
218
+ }
219
+ }
220
+ } catch (error2) {
221
+ if (error2 instanceof Error && error2.name === "AbortError") {
222
+ } else {
223
+ console.error("Stream reading error:", error2);
224
+ }
225
+ } finally {
226
+ try {
227
+ await reader.cancel();
228
+ } catch {
229
+ }
230
+ }
231
+ })();
232
+ return { result: 1 };
233
+ }
234
+ const body = await res.json();
156
235
  if (this.readYourWrites) {
157
236
  const headers = res.headers;
158
237
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -224,6 +303,23 @@ function merge(obj, key, value) {
224
303
  }
225
304
 
226
305
  // pkg/auto-pipeline.ts
306
+ var EXCLUDE_COMMANDS = /* @__PURE__ */ new Set([
307
+ "scan",
308
+ "keys",
309
+ "flushdb",
310
+ "flushall",
311
+ "dbsize",
312
+ "hscan",
313
+ "hgetall",
314
+ "hkeys",
315
+ "lrange",
316
+ "sscan",
317
+ "smembers",
318
+ "xrange",
319
+ "xrevrange",
320
+ "zscan",
321
+ "zrange"
322
+ ]);
227
323
  function createAutoPipelineProxy(_redis, json) {
228
324
  const redis = _redis;
229
325
  if (!redis.autoPipelineExecutor) {
@@ -238,7 +334,8 @@ function createAutoPipelineProxy(_redis, json) {
238
334
  return createAutoPipelineProxy(redis2, true);
239
335
  }
240
336
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
241
- if (commandInRedisButNotPipeline) {
337
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
338
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
242
339
  return redis2[command];
243
340
  }
244
341
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -282,7 +379,7 @@ var AutoPipelineExecutor = class {
282
379
  executeWithPipeline(pipeline);
283
380
  const pipelineDone = this.deferExecution().then(() => {
284
381
  if (!this.pipelinePromises.has(pipeline)) {
285
- const pipelinePromise = pipeline.exec();
382
+ const pipelinePromise = pipeline.exec({ keepErrors: true });
286
383
  this.pipelineCounter += 1;
287
384
  this.pipelinePromises.set(pipeline, pipelinePromise);
288
385
  this.activePipeline = null;
@@ -290,7 +387,11 @@ var AutoPipelineExecutor = class {
290
387
  return this.pipelinePromises.get(pipeline);
291
388
  });
292
389
  const results = await pipelineDone;
293
- return results[index];
390
+ const commandResult = results[index];
391
+ if (commandResult.error) {
392
+ throw new UpstashError(`Command failed: ${commandResult.error}`);
393
+ }
394
+ return commandResult.result;
294
395
  }
295
396
  async deferExecution() {
296
397
  await Promise.resolve();
@@ -298,31 +399,6 @@ var AutoPipelineExecutor = class {
298
399
  }
299
400
  };
300
401
 
301
- // pkg/util.ts
302
- function parseRecursive(obj) {
303
- const parsed = Array.isArray(obj) ? obj.map((o) => {
304
- try {
305
- return parseRecursive(o);
306
- } catch {
307
- return o;
308
- }
309
- }) : JSON.parse(obj);
310
- if (typeof parsed === "number" && parsed.toString() !== obj) {
311
- return obj;
312
- }
313
- return parsed;
314
- }
315
- function parseResponse(result) {
316
- try {
317
- return parseRecursive(result);
318
- } catch {
319
- return result;
320
- }
321
- }
322
- function deserializeScanResponse(result) {
323
- return [result[0], ...parseResponse(result.slice(1))];
324
- }
325
-
326
402
  // pkg/commands/command.ts
327
403
  var defaultSerializer = (c) => {
328
404
  switch (typeof c) {
@@ -340,6 +416,11 @@ var Command = class {
340
416
  command;
341
417
  serialize;
342
418
  deserialize;
419
+ headers;
420
+ path;
421
+ onMessage;
422
+ isStreaming;
423
+ signal;
343
424
  /**
344
425
  * Create a new command instance.
345
426
  *
@@ -349,6 +430,11 @@ var Command = class {
349
430
  this.serialize = defaultSerializer;
350
431
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
351
432
  this.command = command.map((c) => this.serialize(c));
433
+ this.headers = opts?.headers;
434
+ this.path = opts?.path;
435
+ this.onMessage = opts?.streamOptions?.onMessage;
436
+ this.isStreaming = opts?.streamOptions?.isStreaming ?? false;
437
+ this.signal = opts?.streamOptions?.signal;
352
438
  if (opts?.latencyLogging) {
353
439
  const originalExec = this.exec.bind(this);
354
440
  this.exec = async (client) => {
@@ -369,7 +455,12 @@ var Command = class {
369
455
  async exec(client) {
370
456
  const { result, error } = await client.request({
371
457
  body: this.command,
372
- upstashSyncToken: client.upstashSyncToken
458
+ path: this.path,
459
+ upstashSyncToken: client.upstashSyncToken,
460
+ headers: this.headers,
461
+ onMessage: this.onMessage,
462
+ isStreaming: this.isStreaming,
463
+ signal: this.signal
373
464
  });
374
465
  if (error) {
375
466
  throw new UpstashError(error);
@@ -497,6 +588,13 @@ var EchoCommand = class extends Command {
497
588
  }
498
589
  };
499
590
 
591
+ // pkg/commands/evalRo.ts
592
+ var EvalROCommand = class extends Command {
593
+ constructor([script, keys, args], opts) {
594
+ super(["eval_ro", script, keys.length, ...keys, ...args ?? []], opts);
595
+ }
596
+ };
597
+
500
598
  // pkg/commands/eval.ts
501
599
  var EvalCommand = class extends Command {
502
600
  constructor([script, keys, args], opts) {
@@ -504,6 +602,13 @@ var EvalCommand = class extends Command {
504
602
  }
505
603
  };
506
604
 
605
+ // pkg/commands/evalshaRo.ts
606
+ var EvalshaROCommand = class extends Command {
607
+ constructor([sha, keys, args], opts) {
608
+ super(["evalsha_ro", sha, keys.length, ...keys, ...args ?? []], opts);
609
+ }
610
+ };
611
+
507
612
  // pkg/commands/evalsha.ts
508
613
  var EvalshaCommand = class extends Command {
509
614
  constructor([sha, keys, args], opts) {
@@ -511,6 +616,14 @@ var EvalshaCommand = class extends Command {
511
616
  }
512
617
  };
513
618
 
619
+ // pkg/commands/exec.ts
620
+ var ExecCommand = class extends Command {
621
+ constructor(cmd, opts) {
622
+ const normalizedCmd = cmd.map((arg) => typeof arg === "string" ? arg : String(arg));
623
+ super(normalizedCmd, opts);
624
+ }
625
+ };
626
+
514
627
  // pkg/commands/exists.ts
515
628
  var ExistsCommand = class extends Command {
516
629
  constructor(cmd, opts) {
@@ -727,6 +840,27 @@ var GetDelCommand = class extends Command {
727
840
  }
728
841
  };
729
842
 
843
+ // pkg/commands/getex.ts
844
+ var GetExCommand = class extends Command {
845
+ constructor([key, opts], cmdOpts) {
846
+ const command = ["getex", key];
847
+ if (opts) {
848
+ if ("ex" in opts && typeof opts.ex === "number") {
849
+ command.push("ex", opts.ex);
850
+ } else if ("px" in opts && typeof opts.px === "number") {
851
+ command.push("px", opts.px);
852
+ } else if ("exat" in opts && typeof opts.exat === "number") {
853
+ command.push("exat", opts.exat);
854
+ } else if ("pxat" in opts && typeof opts.pxat === "number") {
855
+ command.push("pxat", opts.pxat);
856
+ } else if ("persist" in opts && opts.persist) {
857
+ command.push("persist");
858
+ }
859
+ }
860
+ super(command, cmdOpts);
861
+ }
862
+ };
863
+
730
864
  // pkg/commands/getrange.ts
731
865
  var GetRangeCommand = class extends Command {
732
866
  constructor(cmd, opts) {
@@ -755,6 +889,122 @@ var HExistsCommand = class extends Command {
755
889
  }
756
890
  };
757
891
 
892
+ // pkg/commands/hexpire.ts
893
+ var HExpireCommand = class extends Command {
894
+ constructor(cmd, opts) {
895
+ const [key, fields, seconds, option] = cmd;
896
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
897
+ super(
898
+ [
899
+ "hexpire",
900
+ key,
901
+ seconds,
902
+ ...option ? [option] : [],
903
+ "FIELDS",
904
+ fieldArray.length,
905
+ ...fieldArray
906
+ ],
907
+ opts
908
+ );
909
+ }
910
+ };
911
+
912
+ // pkg/commands/hexpireat.ts
913
+ var HExpireAtCommand = class extends Command {
914
+ constructor(cmd, opts) {
915
+ const [key, fields, timestamp, option] = cmd;
916
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
917
+ super(
918
+ [
919
+ "hexpireat",
920
+ key,
921
+ timestamp,
922
+ ...option ? [option] : [],
923
+ "FIELDS",
924
+ fieldArray.length,
925
+ ...fieldArray
926
+ ],
927
+ opts
928
+ );
929
+ }
930
+ };
931
+
932
+ // pkg/commands/hexpiretime.ts
933
+ var HExpireTimeCommand = class extends Command {
934
+ constructor(cmd, opts) {
935
+ const [key, fields] = cmd;
936
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
937
+ super(["hexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
938
+ }
939
+ };
940
+
941
+ // pkg/commands/hpersist.ts
942
+ var HPersistCommand = class extends Command {
943
+ constructor(cmd, opts) {
944
+ const [key, fields] = cmd;
945
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
946
+ super(["hpersist", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
947
+ }
948
+ };
949
+
950
+ // pkg/commands/hpexpire.ts
951
+ var HPExpireCommand = class extends Command {
952
+ constructor(cmd, opts) {
953
+ const [key, fields, milliseconds, option] = cmd;
954
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
955
+ super(
956
+ [
957
+ "hpexpire",
958
+ key,
959
+ milliseconds,
960
+ ...option ? [option] : [],
961
+ "FIELDS",
962
+ fieldArray.length,
963
+ ...fieldArray
964
+ ],
965
+ opts
966
+ );
967
+ }
968
+ };
969
+
970
+ // pkg/commands/hpexpireat.ts
971
+ var HPExpireAtCommand = class extends Command {
972
+ constructor(cmd, opts) {
973
+ const [key, fields, timestamp, option] = cmd;
974
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
975
+ super(
976
+ [
977
+ "hpexpireat",
978
+ key,
979
+ timestamp,
980
+ ...option ? [option] : [],
981
+ "FIELDS",
982
+ fieldArray.length,
983
+ ...fieldArray
984
+ ],
985
+ opts
986
+ );
987
+ }
988
+ };
989
+
990
+ // pkg/commands/hpexpiretime.ts
991
+ var HPExpireTimeCommand = class extends Command {
992
+ constructor(cmd, opts) {
993
+ const [key, fields] = cmd;
994
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
995
+ super(["hpexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
996
+ }
997
+ };
998
+
999
+ // pkg/commands/hpttl.ts
1000
+ var HPTtlCommand = class extends Command {
1001
+ constructor(cmd, opts) {
1002
+ const [key, fields] = cmd;
1003
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1004
+ super(["hpttl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1005
+ }
1006
+ };
1007
+
758
1008
  // pkg/commands/hget.ts
759
1009
  var HGetCommand = class extends Command {
760
1010
  constructor(cmd, opts) {
@@ -768,9 +1018,9 @@ function deserialize(result) {
768
1018
  return null;
769
1019
  }
770
1020
  const obj = {};
771
- while (result.length >= 2) {
772
- const key = result.shift();
773
- const value = result.shift();
1021
+ for (let i = 0; i < result.length; i += 2) {
1022
+ const key = result[i];
1023
+ const value = result[i + 1];
774
1024
  try {
775
1025
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
776
1026
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -854,9 +1104,9 @@ function deserialize3(result) {
854
1104
  return null;
855
1105
  }
856
1106
  const obj = {};
857
- while (result.length >= 2) {
858
- const key = result.shift();
859
- const value = result.shift();
1107
+ for (let i = 0; i < result.length; i += 2) {
1108
+ const key = result[i];
1109
+ const value = result[i + 1];
860
1110
  try {
861
1111
  obj[key] = JSON.parse(value);
862
1112
  } catch {
@@ -920,6 +1170,15 @@ var HStrLenCommand = class extends Command {
920
1170
  }
921
1171
  };
922
1172
 
1173
+ // pkg/commands/httl.ts
1174
+ var HTtlCommand = class extends Command {
1175
+ constructor(cmd, opts) {
1176
+ const [key, fields] = cmd;
1177
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1178
+ super(["httl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1179
+ }
1180
+ };
1181
+
923
1182
  // pkg/commands/hvals.ts
924
1183
  var HValsCommand = class extends Command {
925
1184
  constructor(cmd, opts) {
@@ -1039,6 +1298,14 @@ var JsonGetCommand = class extends Command {
1039
1298
  }
1040
1299
  };
1041
1300
 
1301
+ // pkg/commands/json_merge.ts
1302
+ var JsonMergeCommand = class extends Command {
1303
+ constructor(cmd, opts) {
1304
+ const command = ["JSON.MERGE", ...cmd];
1305
+ super(command, opts);
1306
+ }
1307
+ };
1308
+
1042
1309
  // pkg/commands/json_mget.ts
1043
1310
  var JsonMGetCommand = class extends Command {
1044
1311
  constructor(cmd, opts) {
@@ -1399,11 +1666,14 @@ var ScanCommand = class extends Command {
1399
1666
  if (typeof opts?.count === "number") {
1400
1667
  command.push("count", opts.count);
1401
1668
  }
1402
- if (opts?.type && opts.type.length > 0) {
1669
+ if (opts && "withType" in opts && opts.withType === true) {
1670
+ command.push("withtype");
1671
+ } else if (opts && "type" in opts && opts.type && opts.type.length > 0) {
1403
1672
  command.push("type", opts.type);
1404
1673
  }
1405
1674
  super(command, {
1406
- deserialize: deserializeScanResponse,
1675
+ // @ts-expect-error ignore types here
1676
+ deserialize: opts?.withType ? deserializeScanWithTypesResponse : deserializeScanResponse,
1407
1677
  ...cmdOpts
1408
1678
  });
1409
1679
  }
@@ -1829,15 +2099,15 @@ var XPendingCommand = class extends Command {
1829
2099
  function deserialize4(result) {
1830
2100
  const obj = {};
1831
2101
  for (const e of result) {
1832
- while (e.length >= 2) {
1833
- const streamId = e.shift();
1834
- const entries = e.shift();
2102
+ for (let i = 0; i < e.length; i += 2) {
2103
+ const streamId = e[i];
2104
+ const entries = e[i + 1];
1835
2105
  if (!(streamId in obj)) {
1836
2106
  obj[streamId] = {};
1837
2107
  }
1838
- while (entries.length >= 2) {
1839
- const field = entries.shift();
1840
- const value = entries.shift();
2108
+ for (let j = 0; j < entries.length; j += 2) {
2109
+ const field = entries[j];
2110
+ const value = entries[j + 1];
1841
2111
  try {
1842
2112
  obj[streamId][field] = JSON.parse(value);
1843
2113
  } catch {
@@ -1926,15 +2196,15 @@ var XRevRangeCommand = class extends Command {
1926
2196
  function deserialize5(result) {
1927
2197
  const obj = {};
1928
2198
  for (const e of result) {
1929
- while (e.length >= 2) {
1930
- const streamId = e.shift();
1931
- const entries = e.shift();
2199
+ for (let i = 0; i < e.length; i += 2) {
2200
+ const streamId = e[i];
2201
+ const entries = e[i + 1];
1932
2202
  if (!(streamId in obj)) {
1933
2203
  obj[streamId] = {};
1934
2204
  }
1935
- while (entries.length >= 2) {
1936
- const field = entries.shift();
1937
- const value = entries.shift();
2205
+ for (let j = 0; j < entries.length; j += 2) {
2206
+ const field = entries[j];
2207
+ const value = entries[j + 1];
1938
2208
  try {
1939
2209
  obj[streamId][field] = JSON.parse(value);
1940
2210
  } catch {
@@ -2193,6 +2463,183 @@ var ZUnionStoreCommand = class extends Command {
2193
2463
  }
2194
2464
  };
2195
2465
 
2466
+ // pkg/commands/psubscribe.ts
2467
+ var PSubscribeCommand = class extends Command {
2468
+ constructor(cmd, opts) {
2469
+ const sseHeaders = {
2470
+ Accept: "text/event-stream",
2471
+ "Cache-Control": "no-cache",
2472
+ Connection: "keep-alive"
2473
+ };
2474
+ super([], {
2475
+ ...opts,
2476
+ headers: sseHeaders,
2477
+ path: ["psubscribe", ...cmd],
2478
+ streamOptions: {
2479
+ isStreaming: true,
2480
+ onMessage: opts?.streamOptions?.onMessage,
2481
+ signal: opts?.streamOptions?.signal
2482
+ }
2483
+ });
2484
+ }
2485
+ };
2486
+
2487
+ // pkg/commands/subscribe.ts
2488
+ var Subscriber = class extends EventTarget {
2489
+ subscriptions;
2490
+ client;
2491
+ listeners;
2492
+ constructor(client, channels, isPattern = false) {
2493
+ super();
2494
+ this.client = client;
2495
+ this.subscriptions = /* @__PURE__ */ new Map();
2496
+ this.listeners = /* @__PURE__ */ new Map();
2497
+ for (const channel of channels) {
2498
+ if (isPattern) {
2499
+ this.subscribeToPattern(channel);
2500
+ } else {
2501
+ this.subscribeToChannel(channel);
2502
+ }
2503
+ }
2504
+ }
2505
+ subscribeToChannel(channel) {
2506
+ const controller = new AbortController();
2507
+ const command = new SubscribeCommand([channel], {
2508
+ streamOptions: {
2509
+ signal: controller.signal,
2510
+ onMessage: (data) => this.handleMessage(data, false)
2511
+ }
2512
+ });
2513
+ command.exec(this.client).catch((error) => {
2514
+ if (error.name !== "AbortError") {
2515
+ this.dispatchToListeners("error", error);
2516
+ }
2517
+ });
2518
+ this.subscriptions.set(channel, {
2519
+ command,
2520
+ controller,
2521
+ isPattern: false
2522
+ });
2523
+ }
2524
+ subscribeToPattern(pattern) {
2525
+ const controller = new AbortController();
2526
+ const command = new PSubscribeCommand([pattern], {
2527
+ streamOptions: {
2528
+ signal: controller.signal,
2529
+ onMessage: (data) => this.handleMessage(data, true)
2530
+ }
2531
+ });
2532
+ command.exec(this.client).catch((error) => {
2533
+ if (error.name !== "AbortError") {
2534
+ this.dispatchToListeners("error", error);
2535
+ }
2536
+ });
2537
+ this.subscriptions.set(pattern, {
2538
+ command,
2539
+ controller,
2540
+ isPattern: true
2541
+ });
2542
+ }
2543
+ handleMessage(data, isPattern) {
2544
+ const messageData = data.replace(/^data:\s*/, "");
2545
+ const firstCommaIndex = messageData.indexOf(",");
2546
+ const secondCommaIndex = messageData.indexOf(",", firstCommaIndex + 1);
2547
+ const thirdCommaIndex = isPattern ? messageData.indexOf(",", secondCommaIndex + 1) : -1;
2548
+ if (firstCommaIndex !== -1 && secondCommaIndex !== -1) {
2549
+ const type = messageData.slice(0, firstCommaIndex);
2550
+ if (isPattern && type === "pmessage" && thirdCommaIndex !== -1) {
2551
+ const pattern = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2552
+ const channel = messageData.slice(secondCommaIndex + 1, thirdCommaIndex);
2553
+ const messageStr = messageData.slice(thirdCommaIndex + 1);
2554
+ try {
2555
+ const message = JSON.parse(messageStr);
2556
+ this.dispatchToListeners("pmessage", { pattern, channel, message });
2557
+ this.dispatchToListeners(`pmessage:${pattern}`, { pattern, channel, message });
2558
+ } catch (error) {
2559
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2560
+ }
2561
+ } else {
2562
+ const channel = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2563
+ const messageStr = messageData.slice(secondCommaIndex + 1);
2564
+ try {
2565
+ if (type === "subscribe" || type === "psubscribe" || type === "unsubscribe" || type === "punsubscribe") {
2566
+ const count = Number.parseInt(messageStr);
2567
+ this.dispatchToListeners(type, count);
2568
+ } else {
2569
+ const message = JSON.parse(messageStr);
2570
+ this.dispatchToListeners(type, { channel, message });
2571
+ this.dispatchToListeners(`${type}:${channel}`, { channel, message });
2572
+ }
2573
+ } catch (error) {
2574
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2575
+ }
2576
+ }
2577
+ }
2578
+ }
2579
+ dispatchToListeners(type, data) {
2580
+ const listeners = this.listeners.get(type);
2581
+ if (listeners) {
2582
+ for (const listener of listeners) {
2583
+ listener(data);
2584
+ }
2585
+ }
2586
+ }
2587
+ on(type, listener) {
2588
+ if (!this.listeners.has(type)) {
2589
+ this.listeners.set(type, /* @__PURE__ */ new Set());
2590
+ }
2591
+ this.listeners.get(type)?.add(listener);
2592
+ }
2593
+ removeAllListeners() {
2594
+ this.listeners.clear();
2595
+ }
2596
+ async unsubscribe(channels) {
2597
+ if (channels) {
2598
+ for (const channel of channels) {
2599
+ const subscription = this.subscriptions.get(channel);
2600
+ if (subscription) {
2601
+ try {
2602
+ subscription.controller.abort();
2603
+ } catch {
2604
+ }
2605
+ this.subscriptions.delete(channel);
2606
+ }
2607
+ }
2608
+ } else {
2609
+ for (const subscription of this.subscriptions.values()) {
2610
+ try {
2611
+ subscription.controller.abort();
2612
+ } catch {
2613
+ }
2614
+ }
2615
+ this.subscriptions.clear();
2616
+ this.removeAllListeners();
2617
+ }
2618
+ }
2619
+ getSubscribedChannels() {
2620
+ return [...this.subscriptions.keys()];
2621
+ }
2622
+ };
2623
+ var SubscribeCommand = class extends Command {
2624
+ constructor(cmd, opts) {
2625
+ const sseHeaders = {
2626
+ Accept: "text/event-stream",
2627
+ "Cache-Control": "no-cache",
2628
+ Connection: "keep-alive"
2629
+ };
2630
+ super([], {
2631
+ ...opts,
2632
+ headers: sseHeaders,
2633
+ path: ["subscribe", ...cmd],
2634
+ streamOptions: {
2635
+ isStreaming: true,
2636
+ onMessage: opts?.streamOptions?.onMessage,
2637
+ signal: opts?.streamOptions?.signal
2638
+ }
2639
+ });
2640
+ }
2641
+ };
2642
+
2196
2643
  // pkg/commands/zdiffstore.ts
2197
2644
  var ZDiffStoreCommand = class extends Command {
2198
2645
  constructor(cmd, opts) {
@@ -2221,9 +2668,9 @@ var Pipeline = class {
2221
2668
  this.multiExec = opts.multiExec ?? false;
2222
2669
  if (this.commandOptions?.latencyLogging) {
2223
2670
  const originalExec = this.exec.bind(this);
2224
- this.exec = async () => {
2671
+ this.exec = async (options) => {
2225
2672
  const start = performance.now();
2226
- const result = await originalExec();
2673
+ const result = await (options ? originalExec(options) : originalExec());
2227
2674
  const end = performance.now();
2228
2675
  const loggerResult = (end - start).toFixed(2);
2229
2676
  console.log(
@@ -2233,19 +2680,7 @@ var Pipeline = class {
2233
2680
  };
2234
2681
  }
2235
2682
  }
2236
- /**
2237
- * Send the pipeline request to upstash.
2238
- *
2239
- * Returns an array with the results of all pipelined commands.
2240
- *
2241
- * If all commands are statically chained from start to finish, types are inferred. You can still define a return type manually if necessary though:
2242
- * ```ts
2243
- * const p = redis.pipeline()
2244
- * p.get("key")
2245
- * const result = p.exec<[{ greeting: string }]>()
2246
- * ```
2247
- */
2248
- exec = async () => {
2683
+ exec = async (options) => {
2249
2684
  if (this.commands.length === 0) {
2250
2685
  throw new Error("Pipeline is empty");
2251
2686
  }
@@ -2254,7 +2689,12 @@ var Pipeline = class {
2254
2689
  path,
2255
2690
  body: Object.values(this.commands).map((c) => c.command)
2256
2691
  });
2257
- return res.map(({ error, result }, i) => {
2692
+ return options?.keepErrors ? res.map(({ error, result }, i) => {
2693
+ return {
2694
+ error,
2695
+ result: this.commands[i].deserialize(result)
2696
+ };
2697
+ }) : res.map(({ error, result }, i) => {
2258
2698
  if (error) {
2259
2699
  throw new UpstashError(
2260
2700
  `Command ${i + 1} [ ${this.commands[i].command[0]} ] failed: ${error}`
@@ -2340,10 +2780,18 @@ var Pipeline = class {
2340
2780
  * @see https://redis.io/commands/echo
2341
2781
  */
2342
2782
  echo = (...args) => this.chain(new EchoCommand(args, this.commandOptions));
2783
+ /**
2784
+ * @see https://redis.io/commands/eval_ro
2785
+ */
2786
+ evalRo = (...args) => this.chain(new EvalROCommand(args, this.commandOptions));
2343
2787
  /**
2344
2788
  * @see https://redis.io/commands/eval
2345
2789
  */
2346
2790
  eval = (...args) => this.chain(new EvalCommand(args, this.commandOptions));
2791
+ /**
2792
+ * @see https://redis.io/commands/evalsha_ro
2793
+ */
2794
+ evalshaRo = (...args) => this.chain(new EvalshaROCommand(args, this.commandOptions));
2347
2795
  /**
2348
2796
  * @see https://redis.io/commands/evalsha
2349
2797
  */
@@ -2404,6 +2852,10 @@ var Pipeline = class {
2404
2852
  * @see https://redis.io/commands/getdel
2405
2853
  */
2406
2854
  getdel = (...args) => this.chain(new GetDelCommand(args, this.commandOptions));
2855
+ /**
2856
+ * @see https://redis.io/commands/getex
2857
+ */
2858
+ getex = (...args) => this.chain(new GetExCommand(args, this.commandOptions));
2407
2859
  /**
2408
2860
  * @see https://redis.io/commands/getrange
2409
2861
  */
@@ -2420,6 +2872,42 @@ var Pipeline = class {
2420
2872
  * @see https://redis.io/commands/hexists
2421
2873
  */
2422
2874
  hexists = (...args) => this.chain(new HExistsCommand(args, this.commandOptions));
2875
+ /**
2876
+ * @see https://redis.io/commands/hexpire
2877
+ */
2878
+ hexpire = (...args) => this.chain(new HExpireCommand(args, this.commandOptions));
2879
+ /**
2880
+ * @see https://redis.io/commands/hexpireat
2881
+ */
2882
+ hexpireat = (...args) => this.chain(new HExpireAtCommand(args, this.commandOptions));
2883
+ /**
2884
+ * @see https://redis.io/commands/hexpiretime
2885
+ */
2886
+ hexpiretime = (...args) => this.chain(new HExpireTimeCommand(args, this.commandOptions));
2887
+ /**
2888
+ * @see https://redis.io/commands/httl
2889
+ */
2890
+ httl = (...args) => this.chain(new HTtlCommand(args, this.commandOptions));
2891
+ /**
2892
+ * @see https://redis.io/commands/hpexpire
2893
+ */
2894
+ hpexpire = (...args) => this.chain(new HPExpireCommand(args, this.commandOptions));
2895
+ /**
2896
+ * @see https://redis.io/commands/hpexpireat
2897
+ */
2898
+ hpexpireat = (...args) => this.chain(new HPExpireAtCommand(args, this.commandOptions));
2899
+ /**
2900
+ * @see https://redis.io/commands/hpexpiretime
2901
+ */
2902
+ hpexpiretime = (...args) => this.chain(new HPExpireTimeCommand(args, this.commandOptions));
2903
+ /**
2904
+ * @see https://redis.io/commands/hpttl
2905
+ */
2906
+ hpttl = (...args) => this.chain(new HPTtlCommand(args, this.commandOptions));
2907
+ /**
2908
+ * @see https://redis.io/commands/hpersist
2909
+ */
2910
+ hpersist = (...args) => this.chain(new HPersistCommand(args, this.commandOptions));
2423
2911
  /**
2424
2912
  * @see https://redis.io/commands/hget
2425
2913
  */
@@ -2937,6 +3425,10 @@ var Pipeline = class {
2937
3425
  * @see https://redis.io/commands/json.get
2938
3426
  */
2939
3427
  get: (...args) => this.chain(new JsonGetCommand(args, this.commandOptions)),
3428
+ /**
3429
+ * @see https://redis.io/commands/json.merge
3430
+ */
3431
+ merge: (...args) => this.chain(new JsonMergeCommand(args, this.commandOptions)),
2940
3432
  /**
2941
3433
  * @see https://redis.io/commands/json.mget
2942
3434
  */
@@ -2990,27 +3482,43 @@ var Pipeline = class {
2990
3482
  };
2991
3483
 
2992
3484
  // pkg/script.ts
2993
- var import_enc_hex = __toESM(require("crypto-js/enc-hex.js"));
2994
- var import_sha1 = __toESM(require("crypto-js/sha1.js"));
3485
+ var import_uncrypto = require("uncrypto");
2995
3486
  var Script = class {
2996
3487
  script;
3488
+ /**
3489
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3490
+ * asynchronously. Do not use this property immidiately after the constructor.
3491
+ *
3492
+ * This property is only exposed for backwards compatibility and will be removed in the
3493
+ * future major release.
3494
+ */
2997
3495
  sha1;
2998
3496
  redis;
2999
3497
  constructor(redis, script) {
3000
3498
  this.redis = redis;
3001
- this.sha1 = this.digest(script);
3002
3499
  this.script = script;
3500
+ this.sha1 = "";
3501
+ void this.init(script);
3502
+ }
3503
+ /**
3504
+ * Initialize the script by computing its SHA-1 hash.
3505
+ */
3506
+ async init(script) {
3507
+ if (this.sha1) return;
3508
+ this.sha1 = await this.digest(script);
3003
3509
  }
3004
3510
  /**
3005
3511
  * Send an `EVAL` command to redis.
3006
3512
  */
3007
3513
  async eval(keys, args) {
3514
+ await this.init(this.script);
3008
3515
  return await this.redis.eval(this.script, keys, args);
3009
3516
  }
3010
3517
  /**
3011
3518
  * Calculates the sha1 hash of the script and then calls `EVALSHA`.
3012
3519
  */
3013
3520
  async evalsha(keys, args) {
3521
+ await this.init(this.script);
3014
3522
  return await this.redis.evalsha(this.sha1, keys, args);
3015
3523
  }
3016
3524
  /**
@@ -3020,6 +3528,7 @@ var Script = class {
3020
3528
  * Following calls will be able to use the cached script
3021
3529
  */
3022
3530
  async exec(keys, args) {
3531
+ await this.init(this.script);
3023
3532
  const res = await this.redis.evalsha(this.sha1, keys, args).catch(async (error) => {
3024
3533
  if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3025
3534
  return await this.redis.eval(this.script, keys, args);
@@ -3031,8 +3540,75 @@ var Script = class {
3031
3540
  /**
3032
3541
  * Compute the sha1 hash of the script and return its hex representation.
3033
3542
  */
3034
- digest(s) {
3035
- return import_enc_hex.default.stringify((0, import_sha1.default)(s));
3543
+ async digest(s) {
3544
+ const data = new TextEncoder().encode(s);
3545
+ const hashBuffer = await import_uncrypto.subtle.digest("SHA-1", data);
3546
+ const hashArray = [...new Uint8Array(hashBuffer)];
3547
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3548
+ }
3549
+ };
3550
+
3551
+ // pkg/scriptRo.ts
3552
+ var import_uncrypto2 = require("uncrypto");
3553
+ var ScriptRO = class {
3554
+ script;
3555
+ /**
3556
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3557
+ * asynchronously. Do not use this property immidiately after the constructor.
3558
+ *
3559
+ * This property is only exposed for backwards compatibility and will be removed in the
3560
+ * future major release.
3561
+ */
3562
+ sha1;
3563
+ redis;
3564
+ constructor(redis, script) {
3565
+ this.redis = redis;
3566
+ this.sha1 = "";
3567
+ this.script = script;
3568
+ void this.init(script);
3569
+ }
3570
+ async init(script) {
3571
+ if (this.sha1) return;
3572
+ this.sha1 = await this.digest(script);
3573
+ }
3574
+ /**
3575
+ * Send an `EVAL_RO` command to redis.
3576
+ */
3577
+ async evalRo(keys, args) {
3578
+ await this.init(this.script);
3579
+ return await this.redis.evalRo(this.script, keys, args);
3580
+ }
3581
+ /**
3582
+ * Calculates the sha1 hash of the script and then calls `EVALSHA_RO`.
3583
+ */
3584
+ async evalshaRo(keys, args) {
3585
+ await this.init(this.script);
3586
+ return await this.redis.evalshaRo(this.sha1, keys, args);
3587
+ }
3588
+ /**
3589
+ * Optimistically try to run `EVALSHA_RO` first.
3590
+ * If the script is not loaded in redis, it will fall back and try again with `EVAL_RO`.
3591
+ *
3592
+ * Following calls will be able to use the cached script
3593
+ */
3594
+ async exec(keys, args) {
3595
+ await this.init(this.script);
3596
+ const res = await this.redis.evalshaRo(this.sha1, keys, args).catch(async (error) => {
3597
+ if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3598
+ return await this.redis.evalRo(this.script, keys, args);
3599
+ }
3600
+ throw error;
3601
+ });
3602
+ return res;
3603
+ }
3604
+ /**
3605
+ * Compute the sha1 hash of the script and return its hex representation.
3606
+ */
3607
+ async digest(s) {
3608
+ const data = new TextEncoder().encode(s);
3609
+ const hashBuffer = await import_uncrypto2.subtle.digest("SHA-1", data);
3610
+ const hashArray = [...new Uint8Array(hashBuffer)];
3611
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3036
3612
  }
3037
3613
  };
3038
3614
 
@@ -3062,6 +3638,12 @@ var Redis = class {
3062
3638
  }
3063
3639
  this.enableAutoPipelining = opts?.enableAutoPipelining ?? true;
3064
3640
  }
3641
+ get readYourWritesSyncToken() {
3642
+ return this.client.upstashSyncToken;
3643
+ }
3644
+ set readYourWritesSyncToken(session) {
3645
+ this.client.upstashSyncToken = session;
3646
+ }
3065
3647
  get json() {
3066
3648
  return {
3067
3649
  /**
@@ -3104,6 +3686,10 @@ var Redis = class {
3104
3686
  * @see https://redis.io/commands/json.get
3105
3687
  */
3106
3688
  get: (...args) => new JsonGetCommand(args, this.opts).exec(this.client),
3689
+ /**
3690
+ * @see https://redis.io/commands/json.merge
3691
+ */
3692
+ merge: (...args) => new JsonMergeCommand(args, this.opts).exec(this.client),
3107
3693
  /**
3108
3694
  * @see https://redis.io/commands/json.mget
3109
3695
  */
@@ -3173,8 +3759,36 @@ var Redis = class {
3173
3759
  } catch {
3174
3760
  }
3175
3761
  };
3176
- createScript(script) {
3177
- return new Script(this, script);
3762
+ /**
3763
+ * Creates a new script.
3764
+ *
3765
+ * Scripts offer the ability to optimistically try to execute a script without having to send the
3766
+ * entire script to the server. If the script is loaded on the server, it tries again by sending
3767
+ * the entire script. Afterwards, the script is cached on the server.
3768
+ *
3769
+ * @param script - The script to create
3770
+ * @param opts - Optional options to pass to the script `{ readonly?: boolean }`
3771
+ * @returns A new script
3772
+ *
3773
+ * @example
3774
+ * ```ts
3775
+ * const redis = new Redis({...})
3776
+ *
3777
+ * const script = redis.createScript<string>("return ARGV[1];")
3778
+ * const arg1 = await script.eval([], ["Hello World"])
3779
+ * expect(arg1, "Hello World")
3780
+ * ```
3781
+ * @example
3782
+ * ```ts
3783
+ * const redis = new Redis({...})
3784
+ *
3785
+ * const script = redis.createScript<string>("return ARGV[1];", { readonly: true })
3786
+ * const arg1 = await script.evalRo([], ["Hello World"])
3787
+ * expect(arg1, "Hello World")
3788
+ * ```
3789
+ */
3790
+ createScript(script, opts) {
3791
+ return opts?.readonly ? new ScriptRO(this, script) : new Script(this, script);
3178
3792
  }
3179
3793
  /**
3180
3794
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3261,14 +3875,26 @@ var Redis = class {
3261
3875
  * @see https://redis.io/commands/echo
3262
3876
  */
3263
3877
  echo = (...args) => new EchoCommand(args, this.opts).exec(this.client);
3878
+ /**
3879
+ * @see https://redis.io/commands/eval_ro
3880
+ */
3881
+ evalRo = (...args) => new EvalROCommand(args, this.opts).exec(this.client);
3264
3882
  /**
3265
3883
  * @see https://redis.io/commands/eval
3266
3884
  */
3267
3885
  eval = (...args) => new EvalCommand(args, this.opts).exec(this.client);
3886
+ /**
3887
+ * @see https://redis.io/commands/evalsha_ro
3888
+ */
3889
+ evalshaRo = (...args) => new EvalshaROCommand(args, this.opts).exec(this.client);
3268
3890
  /**
3269
3891
  * @see https://redis.io/commands/evalsha
3270
3892
  */
3271
3893
  evalsha = (...args) => new EvalshaCommand(args, this.opts).exec(this.client);
3894
+ /**
3895
+ * Generic method to execute any Redis command.
3896
+ */
3897
+ exec = (args) => new ExecCommand(args, this.opts).exec(this.client);
3272
3898
  /**
3273
3899
  * @see https://redis.io/commands/exists
3274
3900
  */
@@ -3325,6 +3951,10 @@ var Redis = class {
3325
3951
  * @see https://redis.io/commands/getdel
3326
3952
  */
3327
3953
  getdel = (...args) => new GetDelCommand(args, this.opts).exec(this.client);
3954
+ /**
3955
+ * @see https://redis.io/commands/getex
3956
+ */
3957
+ getex = (...args) => new GetExCommand(args, this.opts).exec(this.client);
3328
3958
  /**
3329
3959
  * @see https://redis.io/commands/getrange
3330
3960
  */
@@ -3341,6 +3971,42 @@ var Redis = class {
3341
3971
  * @see https://redis.io/commands/hexists
3342
3972
  */
3343
3973
  hexists = (...args) => new HExistsCommand(args, this.opts).exec(this.client);
3974
+ /**
3975
+ * @see https://redis.io/commands/hexpire
3976
+ */
3977
+ hexpire = (...args) => new HExpireCommand(args, this.opts).exec(this.client);
3978
+ /**
3979
+ * @see https://redis.io/commands/hexpireat
3980
+ */
3981
+ hexpireat = (...args) => new HExpireAtCommand(args, this.opts).exec(this.client);
3982
+ /**
3983
+ * @see https://redis.io/commands/hexpiretime
3984
+ */
3985
+ hexpiretime = (...args) => new HExpireTimeCommand(args, this.opts).exec(this.client);
3986
+ /**
3987
+ * @see https://redis.io/commands/httl
3988
+ */
3989
+ httl = (...args) => new HTtlCommand(args, this.opts).exec(this.client);
3990
+ /**
3991
+ * @see https://redis.io/commands/hpexpire
3992
+ */
3993
+ hpexpire = (...args) => new HPExpireCommand(args, this.opts).exec(this.client);
3994
+ /**
3995
+ * @see https://redis.io/commands/hpexpireat
3996
+ */
3997
+ hpexpireat = (...args) => new HPExpireAtCommand(args, this.opts).exec(this.client);
3998
+ /**
3999
+ * @see https://redis.io/commands/hpexpiretime
4000
+ */
4001
+ hpexpiretime = (...args) => new HPExpireTimeCommand(args, this.opts).exec(this.client);
4002
+ /**
4003
+ * @see https://redis.io/commands/hpttl
4004
+ */
4005
+ hpttl = (...args) => new HPTtlCommand(args, this.opts).exec(this.client);
4006
+ /**
4007
+ * @see https://redis.io/commands/hpersist
4008
+ */
4009
+ hpersist = (...args) => new HPersistCommand(args, this.opts).exec(this.client);
3344
4010
  /**
3345
4011
  * @see https://redis.io/commands/hget
3346
4012
  */
@@ -3509,6 +4175,13 @@ var Redis = class {
3509
4175
  * @see https://redis.io/commands/psetex
3510
4176
  */
3511
4177
  psetex = (key, ttl, value) => new PSetEXCommand([key, ttl, value], this.opts).exec(this.client);
4178
+ /**
4179
+ * @see https://redis.io/commands/psubscribe
4180
+ */
4181
+ psubscribe = (patterns) => {
4182
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
4183
+ return new Subscriber(this.client, patternArray, true);
4184
+ };
3512
4185
  /**
3513
4186
  * @see https://redis.io/commands/pttl
3514
4187
  */
@@ -3545,10 +4218,9 @@ var Redis = class {
3545
4218
  * @see https://redis.io/commands/sadd
3546
4219
  */
3547
4220
  sadd = (key, member, ...members) => new SAddCommand([key, member, ...members], this.opts).exec(this.client);
3548
- /**
3549
- * @see https://redis.io/commands/scan
3550
- */
3551
- scan = (...args) => new ScanCommand(args, this.opts).exec(this.client);
4221
+ scan(cursor, opts) {
4222
+ return new ScanCommand([cursor, opts], this.opts).exec(this.client);
4223
+ }
3552
4224
  /**
3553
4225
  * @see https://redis.io/commands/scard
3554
4226
  */
@@ -3637,6 +4309,13 @@ var Redis = class {
3637
4309
  * @see https://redis.io/commands/strlen
3638
4310
  */
3639
4311
  strlen = (...args) => new StrLenCommand(args, this.opts).exec(this.client);
4312
+ /**
4313
+ * @see https://redis.io/commands/subscribe
4314
+ */
4315
+ subscribe = (channels) => {
4316
+ const channelArray = Array.isArray(channels) ? channels : [channels];
4317
+ return new Subscriber(this.client, channelArray);
4318
+ };
3640
4319
  /**
3641
4320
  * @see https://redis.io/commands/sunion
3642
4321
  */
@@ -3818,7 +4497,7 @@ var Redis = class {
3818
4497
  };
3819
4498
 
3820
4499
  // version.ts
3821
- var VERSION = "v1.35.0-canary";
4500
+ var VERSION = "v1.35.0";
3822
4501
 
3823
4502
  // platforms/nodejs.ts
3824
4503
  if (typeof atob === "undefined") {
@@ -3848,26 +4527,27 @@ var Redis2 = class _Redis extends Redis {
3848
4527
  return;
3849
4528
  }
3850
4529
  if (!configOrRequester.url) {
3851
- throw new Error(
4530
+ console.warn(
3852
4531
  `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.`
3853
4532
  );
4533
+ } else if (configOrRequester.url.startsWith(" ") || configOrRequester.url.endsWith(" ") || /\r|\n/.test(configOrRequester.url)) {
4534
+ console.warn(
4535
+ "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!"
4536
+ );
3854
4537
  }
3855
4538
  if (!configOrRequester.token) {
3856
- throw new Error(
4539
+ console.warn(
3857
4540
  `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.`
3858
4541
  );
3859
- }
3860
- if (configOrRequester.url.startsWith(" ") || configOrRequester.url.endsWith(" ") || /\r|\n/.test(configOrRequester.url)) {
3861
- console.warn("The redis url contains whitespace or newline, which can cause errors!");
3862
- }
3863
- if (configOrRequester.token.startsWith(" ") || configOrRequester.token.endsWith(" ") || /\r|\n/.test(configOrRequester.token)) {
3864
- console.warn("The redis token contains whitespace or newline, which can cause errors!");
4542
+ } else if (configOrRequester.token.startsWith(" ") || configOrRequester.token.endsWith(" ") || /\r|\n/.test(configOrRequester.token)) {
4543
+ console.warn(
4544
+ "[Upstash Redis] The redis token contains whitespace or newline, which can cause errors!"
4545
+ );
3865
4546
  }
3866
4547
  const client = new HttpClient({
3867
4548
  baseUrl: configOrRequester.url,
3868
4549
  retry: configOrRequester.retry,
3869
4550
  headers: { authorization: `Bearer ${configOrRequester.token}` },
3870
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
3871
4551
  agent: configOrRequester.agent,
3872
4552
  responseEncoding: configOrRequester.responseEncoding,
3873
4553
  cache: configOrRequester.cache ?? "no-store",
@@ -3905,16 +4585,18 @@ var Redis2 = class _Redis extends Redis {
3905
4585
  static fromEnv(config) {
3906
4586
  if (process.env === void 0) {
3907
4587
  throw new TypeError(
3908
- 'Unable to get environment variables, `process.env` is undefined. If you are deploying to cloudflare, please import from "@upstash/redis/cloudflare" instead'
4588
+ '[Upstash Redis] Unable to get environment variables, `process.env` is undefined. If you are deploying to cloudflare, please import from "@upstash/redis/cloudflare" instead'
3909
4589
  );
3910
4590
  }
3911
- const url = process.env.UPSTASH_REDIS_REST_URL;
4591
+ const url = process.env.UPSTASH_REDIS_REST_URL || process.env.KV_REST_API_URL;
3912
4592
  if (!url) {
3913
- throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_URL`");
4593
+ console.warn("[Upstash Redis] Unable to find environment variable: `UPSTASH_REDIS_REST_URL`");
3914
4594
  }
3915
- const token = process.env.UPSTASH_REDIS_REST_TOKEN;
4595
+ const token = process.env.UPSTASH_REDIS_REST_TOKEN || process.env.KV_REST_API_TOKEN;
3916
4596
  if (!token) {
3917
- throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`");
4597
+ console.warn(
4598
+ "[Upstash Redis] Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`"
4599
+ );
3918
4600
  }
3919
4601
  return new _Redis({ ...config, url, token });
3920
4602
  }