@upstash/redis 1.35.0-canary → 1.35.1

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/fastly.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,30 @@ 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";
145
+ const signal = req.signal ?? this.options.signal;
146
+ const isSignalFunction = typeof signal === "function";
105
147
  const requestOptions = {
106
148
  //@ts-expect-error this should throw due to bun regression
107
149
  cache: this.options.cache,
108
150
  method: "POST",
109
- headers: this.headers,
151
+ headers: requestHeaders,
110
152
  body: JSON.stringify(req.body),
111
153
  keepalive: this.options.keepAlive,
112
154
  agent: this.options.agent,
113
- signal: this.options.signal,
155
+ signal: isSignalFunction ? signal() : signal,
114
156
  /**
115
157
  * Fastly specific
116
158
  */
117
159
  backend: this.options.backend
118
160
  };
161
+ if (!this.hasCredentials) {
162
+ console.warn(
163
+ "[Upstash Redis] Redis client was initialized without url or token. Failed to execute command."
164
+ );
165
+ }
119
166
  if (this.readYourWrites) {
120
167
  const newHeader = this.upstashSyncToken;
121
168
  this.headers["upstash-sync-token"] = newHeader;
@@ -124,35 +171,71 @@ var HttpClient = class {
124
171
  let error = null;
125
172
  for (let i = 0; i <= this.retry.attempts; i++) {
126
173
  try {
127
- res = await fetch([this.baseUrl, ...req.path ?? []].join("/"), requestOptions);
174
+ res = await fetch(requestUrl, requestOptions);
128
175
  break;
129
176
  } catch (error_) {
130
- if (this.options.signal?.aborted) {
177
+ if (requestOptions.signal?.aborted && isSignalFunction) {
178
+ throw error_;
179
+ } else if (requestOptions.signal?.aborted) {
131
180
  const myBlob = new Blob([
132
- JSON.stringify({ result: this.options.signal.reason ?? "Aborted" })
181
+ JSON.stringify({ result: requestOptions.signal.reason ?? "Aborted" })
133
182
  ]);
134
183
  const myOptions = {
135
184
  status: 200,
136
- statusText: this.options.signal.reason ?? "Aborted"
185
+ statusText: requestOptions.signal.reason ?? "Aborted"
137
186
  };
138
187
  res = new Response(myBlob, myOptions);
139
188
  break;
140
189
  }
141
190
  error = error_;
142
- await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
191
+ if (i < this.retry.attempts) {
192
+ await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
193
+ }
143
194
  }
144
195
  }
145
196
  if (!res) {
146
197
  throw error ?? new Error("Exhausted all retries");
147
198
  }
148
- const body = await res.json();
149
199
  if (!res.ok) {
150
- throw new UpstashError(`${body.error}, command was: ${JSON.stringify(req.body)}`);
200
+ const body2 = await res.json();
201
+ throw new UpstashError(`${body2.error}, command was: ${JSON.stringify(req.body)}`);
151
202
  }
152
203
  if (this.readYourWrites) {
153
204
  const headers = res.headers;
154
205
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
155
206
  }
207
+ if (isEventStream && req && req.onMessage && res.body) {
208
+ const reader = res.body.getReader();
209
+ const decoder = new TextDecoder();
210
+ (async () => {
211
+ try {
212
+ while (true) {
213
+ const { value, done } = await reader.read();
214
+ if (done) break;
215
+ const chunk = decoder.decode(value);
216
+ const lines = chunk.split("\n");
217
+ for (const line of lines) {
218
+ if (line.startsWith("data: ")) {
219
+ const data = line.slice(6);
220
+ req.onMessage?.(data);
221
+ }
222
+ }
223
+ }
224
+ } catch (error2) {
225
+ if (error2 instanceof Error && error2.name === "AbortError") {
226
+ } else {
227
+ console.error("Stream reading error:", error2);
228
+ }
229
+ } finally {
230
+ try {
231
+ await reader.cancel();
232
+ } catch {
233
+ }
234
+ }
235
+ })();
236
+ return { result: 1 };
237
+ }
238
+ const body = await res.json();
156
239
  if (this.readYourWrites) {
157
240
  const headers = res.headers;
158
241
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -224,6 +307,23 @@ function merge(obj, key, value) {
224
307
  }
225
308
 
226
309
  // pkg/auto-pipeline.ts
310
+ var EXCLUDE_COMMANDS = /* @__PURE__ */ new Set([
311
+ "scan",
312
+ "keys",
313
+ "flushdb",
314
+ "flushall",
315
+ "dbsize",
316
+ "hscan",
317
+ "hgetall",
318
+ "hkeys",
319
+ "lrange",
320
+ "sscan",
321
+ "smembers",
322
+ "xrange",
323
+ "xrevrange",
324
+ "zscan",
325
+ "zrange"
326
+ ]);
227
327
  function createAutoPipelineProxy(_redis, json) {
228
328
  const redis = _redis;
229
329
  if (!redis.autoPipelineExecutor) {
@@ -238,7 +338,8 @@ function createAutoPipelineProxy(_redis, json) {
238
338
  return createAutoPipelineProxy(redis2, true);
239
339
  }
240
340
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
241
- if (commandInRedisButNotPipeline) {
341
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
342
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
242
343
  return redis2[command];
243
344
  }
244
345
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -282,7 +383,7 @@ var AutoPipelineExecutor = class {
282
383
  executeWithPipeline(pipeline);
283
384
  const pipelineDone = this.deferExecution().then(() => {
284
385
  if (!this.pipelinePromises.has(pipeline)) {
285
- const pipelinePromise = pipeline.exec();
386
+ const pipelinePromise = pipeline.exec({ keepErrors: true });
286
387
  this.pipelineCounter += 1;
287
388
  this.pipelinePromises.set(pipeline, pipelinePromise);
288
389
  this.activePipeline = null;
@@ -290,7 +391,11 @@ var AutoPipelineExecutor = class {
290
391
  return this.pipelinePromises.get(pipeline);
291
392
  });
292
393
  const results = await pipelineDone;
293
- return results[index];
394
+ const commandResult = results[index];
395
+ if (commandResult.error) {
396
+ throw new UpstashError(`Command failed: ${commandResult.error}`);
397
+ }
398
+ return commandResult.result;
294
399
  }
295
400
  async deferExecution() {
296
401
  await Promise.resolve();
@@ -298,31 +403,6 @@ var AutoPipelineExecutor = class {
298
403
  }
299
404
  };
300
405
 
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
406
  // pkg/commands/command.ts
327
407
  var defaultSerializer = (c) => {
328
408
  switch (typeof c) {
@@ -340,6 +420,11 @@ var Command = class {
340
420
  command;
341
421
  serialize;
342
422
  deserialize;
423
+ headers;
424
+ path;
425
+ onMessage;
426
+ isStreaming;
427
+ signal;
343
428
  /**
344
429
  * Create a new command instance.
345
430
  *
@@ -349,6 +434,11 @@ var Command = class {
349
434
  this.serialize = defaultSerializer;
350
435
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
351
436
  this.command = command.map((c) => this.serialize(c));
437
+ this.headers = opts?.headers;
438
+ this.path = opts?.path;
439
+ this.onMessage = opts?.streamOptions?.onMessage;
440
+ this.isStreaming = opts?.streamOptions?.isStreaming ?? false;
441
+ this.signal = opts?.streamOptions?.signal;
352
442
  if (opts?.latencyLogging) {
353
443
  const originalExec = this.exec.bind(this);
354
444
  this.exec = async (client) => {
@@ -369,7 +459,12 @@ var Command = class {
369
459
  async exec(client) {
370
460
  const { result, error } = await client.request({
371
461
  body: this.command,
372
- upstashSyncToken: client.upstashSyncToken
462
+ path: this.path,
463
+ upstashSyncToken: client.upstashSyncToken,
464
+ headers: this.headers,
465
+ onMessage: this.onMessage,
466
+ isStreaming: this.isStreaming,
467
+ signal: this.signal
373
468
  });
374
469
  if (error) {
375
470
  throw new UpstashError(error);
@@ -497,6 +592,13 @@ var EchoCommand = class extends Command {
497
592
  }
498
593
  };
499
594
 
595
+ // pkg/commands/evalRo.ts
596
+ var EvalROCommand = class extends Command {
597
+ constructor([script, keys, args], opts) {
598
+ super(["eval_ro", script, keys.length, ...keys, ...args ?? []], opts);
599
+ }
600
+ };
601
+
500
602
  // pkg/commands/eval.ts
501
603
  var EvalCommand = class extends Command {
502
604
  constructor([script, keys, args], opts) {
@@ -504,6 +606,13 @@ var EvalCommand = class extends Command {
504
606
  }
505
607
  };
506
608
 
609
+ // pkg/commands/evalshaRo.ts
610
+ var EvalshaROCommand = class extends Command {
611
+ constructor([sha, keys, args], opts) {
612
+ super(["evalsha_ro", sha, keys.length, ...keys, ...args ?? []], opts);
613
+ }
614
+ };
615
+
507
616
  // pkg/commands/evalsha.ts
508
617
  var EvalshaCommand = class extends Command {
509
618
  constructor([sha, keys, args], opts) {
@@ -511,6 +620,14 @@ var EvalshaCommand = class extends Command {
511
620
  }
512
621
  };
513
622
 
623
+ // pkg/commands/exec.ts
624
+ var ExecCommand = class extends Command {
625
+ constructor(cmd, opts) {
626
+ const normalizedCmd = cmd.map((arg) => typeof arg === "string" ? arg : String(arg));
627
+ super(normalizedCmd, opts);
628
+ }
629
+ };
630
+
514
631
  // pkg/commands/exists.ts
515
632
  var ExistsCommand = class extends Command {
516
633
  constructor(cmd, opts) {
@@ -727,6 +844,27 @@ var GetDelCommand = class extends Command {
727
844
  }
728
845
  };
729
846
 
847
+ // pkg/commands/getex.ts
848
+ var GetExCommand = class extends Command {
849
+ constructor([key, opts], cmdOpts) {
850
+ const command = ["getex", key];
851
+ if (opts) {
852
+ if ("ex" in opts && typeof opts.ex === "number") {
853
+ command.push("ex", opts.ex);
854
+ } else if ("px" in opts && typeof opts.px === "number") {
855
+ command.push("px", opts.px);
856
+ } else if ("exat" in opts && typeof opts.exat === "number") {
857
+ command.push("exat", opts.exat);
858
+ } else if ("pxat" in opts && typeof opts.pxat === "number") {
859
+ command.push("pxat", opts.pxat);
860
+ } else if ("persist" in opts && opts.persist) {
861
+ command.push("persist");
862
+ }
863
+ }
864
+ super(command, cmdOpts);
865
+ }
866
+ };
867
+
730
868
  // pkg/commands/getrange.ts
731
869
  var GetRangeCommand = class extends Command {
732
870
  constructor(cmd, opts) {
@@ -755,6 +893,122 @@ var HExistsCommand = class extends Command {
755
893
  }
756
894
  };
757
895
 
896
+ // pkg/commands/hexpire.ts
897
+ var HExpireCommand = class extends Command {
898
+ constructor(cmd, opts) {
899
+ const [key, fields, seconds, option] = cmd;
900
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
901
+ super(
902
+ [
903
+ "hexpire",
904
+ key,
905
+ seconds,
906
+ ...option ? [option] : [],
907
+ "FIELDS",
908
+ fieldArray.length,
909
+ ...fieldArray
910
+ ],
911
+ opts
912
+ );
913
+ }
914
+ };
915
+
916
+ // pkg/commands/hexpireat.ts
917
+ var HExpireAtCommand = class extends Command {
918
+ constructor(cmd, opts) {
919
+ const [key, fields, timestamp, option] = cmd;
920
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
921
+ super(
922
+ [
923
+ "hexpireat",
924
+ key,
925
+ timestamp,
926
+ ...option ? [option] : [],
927
+ "FIELDS",
928
+ fieldArray.length,
929
+ ...fieldArray
930
+ ],
931
+ opts
932
+ );
933
+ }
934
+ };
935
+
936
+ // pkg/commands/hexpiretime.ts
937
+ var HExpireTimeCommand = class extends Command {
938
+ constructor(cmd, opts) {
939
+ const [key, fields] = cmd;
940
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
941
+ super(["hexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
942
+ }
943
+ };
944
+
945
+ // pkg/commands/hpersist.ts
946
+ var HPersistCommand = class extends Command {
947
+ constructor(cmd, opts) {
948
+ const [key, fields] = cmd;
949
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
950
+ super(["hpersist", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
951
+ }
952
+ };
953
+
954
+ // pkg/commands/hpexpire.ts
955
+ var HPExpireCommand = class extends Command {
956
+ constructor(cmd, opts) {
957
+ const [key, fields, milliseconds, option] = cmd;
958
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
959
+ super(
960
+ [
961
+ "hpexpire",
962
+ key,
963
+ milliseconds,
964
+ ...option ? [option] : [],
965
+ "FIELDS",
966
+ fieldArray.length,
967
+ ...fieldArray
968
+ ],
969
+ opts
970
+ );
971
+ }
972
+ };
973
+
974
+ // pkg/commands/hpexpireat.ts
975
+ var HPExpireAtCommand = class extends Command {
976
+ constructor(cmd, opts) {
977
+ const [key, fields, timestamp, option] = cmd;
978
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
979
+ super(
980
+ [
981
+ "hpexpireat",
982
+ key,
983
+ timestamp,
984
+ ...option ? [option] : [],
985
+ "FIELDS",
986
+ fieldArray.length,
987
+ ...fieldArray
988
+ ],
989
+ opts
990
+ );
991
+ }
992
+ };
993
+
994
+ // pkg/commands/hpexpiretime.ts
995
+ var HPExpireTimeCommand = class extends Command {
996
+ constructor(cmd, opts) {
997
+ const [key, fields] = cmd;
998
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
999
+ super(["hpexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1000
+ }
1001
+ };
1002
+
1003
+ // pkg/commands/hpttl.ts
1004
+ var HPTtlCommand = class extends Command {
1005
+ constructor(cmd, opts) {
1006
+ const [key, fields] = cmd;
1007
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1008
+ super(["hpttl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1009
+ }
1010
+ };
1011
+
758
1012
  // pkg/commands/hget.ts
759
1013
  var HGetCommand = class extends Command {
760
1014
  constructor(cmd, opts) {
@@ -768,9 +1022,9 @@ function deserialize(result) {
768
1022
  return null;
769
1023
  }
770
1024
  const obj = {};
771
- while (result.length >= 2) {
772
- const key = result.shift();
773
- const value = result.shift();
1025
+ for (let i = 0; i < result.length; i += 2) {
1026
+ const key = result[i];
1027
+ const value = result[i + 1];
774
1028
  try {
775
1029
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
776
1030
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -854,9 +1108,9 @@ function deserialize3(result) {
854
1108
  return null;
855
1109
  }
856
1110
  const obj = {};
857
- while (result.length >= 2) {
858
- const key = result.shift();
859
- const value = result.shift();
1111
+ for (let i = 0; i < result.length; i += 2) {
1112
+ const key = result[i];
1113
+ const value = result[i + 1];
860
1114
  try {
861
1115
  obj[key] = JSON.parse(value);
862
1116
  } catch {
@@ -920,6 +1174,15 @@ var HStrLenCommand = class extends Command {
920
1174
  }
921
1175
  };
922
1176
 
1177
+ // pkg/commands/httl.ts
1178
+ var HTtlCommand = class extends Command {
1179
+ constructor(cmd, opts) {
1180
+ const [key, fields] = cmd;
1181
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1182
+ super(["httl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1183
+ }
1184
+ };
1185
+
923
1186
  // pkg/commands/hvals.ts
924
1187
  var HValsCommand = class extends Command {
925
1188
  constructor(cmd, opts) {
@@ -1039,6 +1302,14 @@ var JsonGetCommand = class extends Command {
1039
1302
  }
1040
1303
  };
1041
1304
 
1305
+ // pkg/commands/json_merge.ts
1306
+ var JsonMergeCommand = class extends Command {
1307
+ constructor(cmd, opts) {
1308
+ const command = ["JSON.MERGE", ...cmd];
1309
+ super(command, opts);
1310
+ }
1311
+ };
1312
+
1042
1313
  // pkg/commands/json_mget.ts
1043
1314
  var JsonMGetCommand = class extends Command {
1044
1315
  constructor(cmd, opts) {
@@ -1399,11 +1670,14 @@ var ScanCommand = class extends Command {
1399
1670
  if (typeof opts?.count === "number") {
1400
1671
  command.push("count", opts.count);
1401
1672
  }
1402
- if (opts?.type && opts.type.length > 0) {
1673
+ if (opts && "withType" in opts && opts.withType === true) {
1674
+ command.push("withtype");
1675
+ } else if (opts && "type" in opts && opts.type && opts.type.length > 0) {
1403
1676
  command.push("type", opts.type);
1404
1677
  }
1405
1678
  super(command, {
1406
- deserialize: deserializeScanResponse,
1679
+ // @ts-expect-error ignore types here
1680
+ deserialize: opts?.withType ? deserializeScanWithTypesResponse : deserializeScanResponse,
1407
1681
  ...cmdOpts
1408
1682
  });
1409
1683
  }
@@ -1829,15 +2103,15 @@ var XPendingCommand = class extends Command {
1829
2103
  function deserialize4(result) {
1830
2104
  const obj = {};
1831
2105
  for (const e of result) {
1832
- while (e.length >= 2) {
1833
- const streamId = e.shift();
1834
- const entries = e.shift();
2106
+ for (let i = 0; i < e.length; i += 2) {
2107
+ const streamId = e[i];
2108
+ const entries = e[i + 1];
1835
2109
  if (!(streamId in obj)) {
1836
2110
  obj[streamId] = {};
1837
2111
  }
1838
- while (entries.length >= 2) {
1839
- const field = entries.shift();
1840
- const value = entries.shift();
2112
+ for (let j = 0; j < entries.length; j += 2) {
2113
+ const field = entries[j];
2114
+ const value = entries[j + 1];
1841
2115
  try {
1842
2116
  obj[streamId][field] = JSON.parse(value);
1843
2117
  } catch {
@@ -1926,15 +2200,15 @@ var XRevRangeCommand = class extends Command {
1926
2200
  function deserialize5(result) {
1927
2201
  const obj = {};
1928
2202
  for (const e of result) {
1929
- while (e.length >= 2) {
1930
- const streamId = e.shift();
1931
- const entries = e.shift();
2203
+ for (let i = 0; i < e.length; i += 2) {
2204
+ const streamId = e[i];
2205
+ const entries = e[i + 1];
1932
2206
  if (!(streamId in obj)) {
1933
2207
  obj[streamId] = {};
1934
2208
  }
1935
- while (entries.length >= 2) {
1936
- const field = entries.shift();
1937
- const value = entries.shift();
2209
+ for (let j = 0; j < entries.length; j += 2) {
2210
+ const field = entries[j];
2211
+ const value = entries[j + 1];
1938
2212
  try {
1939
2213
  obj[streamId][field] = JSON.parse(value);
1940
2214
  } catch {
@@ -2193,6 +2467,183 @@ var ZUnionStoreCommand = class extends Command {
2193
2467
  }
2194
2468
  };
2195
2469
 
2470
+ // pkg/commands/psubscribe.ts
2471
+ var PSubscribeCommand = class extends Command {
2472
+ constructor(cmd, opts) {
2473
+ const sseHeaders = {
2474
+ Accept: "text/event-stream",
2475
+ "Cache-Control": "no-cache",
2476
+ Connection: "keep-alive"
2477
+ };
2478
+ super([], {
2479
+ ...opts,
2480
+ headers: sseHeaders,
2481
+ path: ["psubscribe", ...cmd],
2482
+ streamOptions: {
2483
+ isStreaming: true,
2484
+ onMessage: opts?.streamOptions?.onMessage,
2485
+ signal: opts?.streamOptions?.signal
2486
+ }
2487
+ });
2488
+ }
2489
+ };
2490
+
2491
+ // pkg/commands/subscribe.ts
2492
+ var Subscriber = class extends EventTarget {
2493
+ subscriptions;
2494
+ client;
2495
+ listeners;
2496
+ constructor(client, channels, isPattern = false) {
2497
+ super();
2498
+ this.client = client;
2499
+ this.subscriptions = /* @__PURE__ */ new Map();
2500
+ this.listeners = /* @__PURE__ */ new Map();
2501
+ for (const channel of channels) {
2502
+ if (isPattern) {
2503
+ this.subscribeToPattern(channel);
2504
+ } else {
2505
+ this.subscribeToChannel(channel);
2506
+ }
2507
+ }
2508
+ }
2509
+ subscribeToChannel(channel) {
2510
+ const controller = new AbortController();
2511
+ const command = new SubscribeCommand([channel], {
2512
+ streamOptions: {
2513
+ signal: controller.signal,
2514
+ onMessage: (data) => this.handleMessage(data, false)
2515
+ }
2516
+ });
2517
+ command.exec(this.client).catch((error) => {
2518
+ if (error.name !== "AbortError") {
2519
+ this.dispatchToListeners("error", error);
2520
+ }
2521
+ });
2522
+ this.subscriptions.set(channel, {
2523
+ command,
2524
+ controller,
2525
+ isPattern: false
2526
+ });
2527
+ }
2528
+ subscribeToPattern(pattern) {
2529
+ const controller = new AbortController();
2530
+ const command = new PSubscribeCommand([pattern], {
2531
+ streamOptions: {
2532
+ signal: controller.signal,
2533
+ onMessage: (data) => this.handleMessage(data, true)
2534
+ }
2535
+ });
2536
+ command.exec(this.client).catch((error) => {
2537
+ if (error.name !== "AbortError") {
2538
+ this.dispatchToListeners("error", error);
2539
+ }
2540
+ });
2541
+ this.subscriptions.set(pattern, {
2542
+ command,
2543
+ controller,
2544
+ isPattern: true
2545
+ });
2546
+ }
2547
+ handleMessage(data, isPattern) {
2548
+ const messageData = data.replace(/^data:\s*/, "");
2549
+ const firstCommaIndex = messageData.indexOf(",");
2550
+ const secondCommaIndex = messageData.indexOf(",", firstCommaIndex + 1);
2551
+ const thirdCommaIndex = isPattern ? messageData.indexOf(",", secondCommaIndex + 1) : -1;
2552
+ if (firstCommaIndex !== -1 && secondCommaIndex !== -1) {
2553
+ const type = messageData.slice(0, firstCommaIndex);
2554
+ if (isPattern && type === "pmessage" && thirdCommaIndex !== -1) {
2555
+ const pattern = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2556
+ const channel = messageData.slice(secondCommaIndex + 1, thirdCommaIndex);
2557
+ const messageStr = messageData.slice(thirdCommaIndex + 1);
2558
+ try {
2559
+ const message = JSON.parse(messageStr);
2560
+ this.dispatchToListeners("pmessage", { pattern, channel, message });
2561
+ this.dispatchToListeners(`pmessage:${pattern}`, { pattern, channel, message });
2562
+ } catch (error) {
2563
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2564
+ }
2565
+ } else {
2566
+ const channel = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2567
+ const messageStr = messageData.slice(secondCommaIndex + 1);
2568
+ try {
2569
+ if (type === "subscribe" || type === "psubscribe" || type === "unsubscribe" || type === "punsubscribe") {
2570
+ const count = Number.parseInt(messageStr);
2571
+ this.dispatchToListeners(type, count);
2572
+ } else {
2573
+ const message = JSON.parse(messageStr);
2574
+ this.dispatchToListeners(type, { channel, message });
2575
+ this.dispatchToListeners(`${type}:${channel}`, { channel, message });
2576
+ }
2577
+ } catch (error) {
2578
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2579
+ }
2580
+ }
2581
+ }
2582
+ }
2583
+ dispatchToListeners(type, data) {
2584
+ const listeners = this.listeners.get(type);
2585
+ if (listeners) {
2586
+ for (const listener of listeners) {
2587
+ listener(data);
2588
+ }
2589
+ }
2590
+ }
2591
+ on(type, listener) {
2592
+ if (!this.listeners.has(type)) {
2593
+ this.listeners.set(type, /* @__PURE__ */ new Set());
2594
+ }
2595
+ this.listeners.get(type)?.add(listener);
2596
+ }
2597
+ removeAllListeners() {
2598
+ this.listeners.clear();
2599
+ }
2600
+ async unsubscribe(channels) {
2601
+ if (channels) {
2602
+ for (const channel of channels) {
2603
+ const subscription = this.subscriptions.get(channel);
2604
+ if (subscription) {
2605
+ try {
2606
+ subscription.controller.abort();
2607
+ } catch {
2608
+ }
2609
+ this.subscriptions.delete(channel);
2610
+ }
2611
+ }
2612
+ } else {
2613
+ for (const subscription of this.subscriptions.values()) {
2614
+ try {
2615
+ subscription.controller.abort();
2616
+ } catch {
2617
+ }
2618
+ }
2619
+ this.subscriptions.clear();
2620
+ this.removeAllListeners();
2621
+ }
2622
+ }
2623
+ getSubscribedChannels() {
2624
+ return [...this.subscriptions.keys()];
2625
+ }
2626
+ };
2627
+ var SubscribeCommand = class extends Command {
2628
+ constructor(cmd, opts) {
2629
+ const sseHeaders = {
2630
+ Accept: "text/event-stream",
2631
+ "Cache-Control": "no-cache",
2632
+ Connection: "keep-alive"
2633
+ };
2634
+ super([], {
2635
+ ...opts,
2636
+ headers: sseHeaders,
2637
+ path: ["subscribe", ...cmd],
2638
+ streamOptions: {
2639
+ isStreaming: true,
2640
+ onMessage: opts?.streamOptions?.onMessage,
2641
+ signal: opts?.streamOptions?.signal
2642
+ }
2643
+ });
2644
+ }
2645
+ };
2646
+
2196
2647
  // pkg/commands/zdiffstore.ts
2197
2648
  var ZDiffStoreCommand = class extends Command {
2198
2649
  constructor(cmd, opts) {
@@ -2221,9 +2672,9 @@ var Pipeline = class {
2221
2672
  this.multiExec = opts.multiExec ?? false;
2222
2673
  if (this.commandOptions?.latencyLogging) {
2223
2674
  const originalExec = this.exec.bind(this);
2224
- this.exec = async () => {
2675
+ this.exec = async (options) => {
2225
2676
  const start = performance.now();
2226
- const result = await originalExec();
2677
+ const result = await (options ? originalExec(options) : originalExec());
2227
2678
  const end = performance.now();
2228
2679
  const loggerResult = (end - start).toFixed(2);
2229
2680
  console.log(
@@ -2233,19 +2684,7 @@ var Pipeline = class {
2233
2684
  };
2234
2685
  }
2235
2686
  }
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 () => {
2687
+ exec = async (options) => {
2249
2688
  if (this.commands.length === 0) {
2250
2689
  throw new Error("Pipeline is empty");
2251
2690
  }
@@ -2254,7 +2693,12 @@ var Pipeline = class {
2254
2693
  path,
2255
2694
  body: Object.values(this.commands).map((c) => c.command)
2256
2695
  });
2257
- return res.map(({ error, result }, i) => {
2696
+ return options?.keepErrors ? res.map(({ error, result }, i) => {
2697
+ return {
2698
+ error,
2699
+ result: this.commands[i].deserialize(result)
2700
+ };
2701
+ }) : res.map(({ error, result }, i) => {
2258
2702
  if (error) {
2259
2703
  throw new UpstashError(
2260
2704
  `Command ${i + 1} [ ${this.commands[i].command[0]} ] failed: ${error}`
@@ -2340,10 +2784,18 @@ var Pipeline = class {
2340
2784
  * @see https://redis.io/commands/echo
2341
2785
  */
2342
2786
  echo = (...args) => this.chain(new EchoCommand(args, this.commandOptions));
2787
+ /**
2788
+ * @see https://redis.io/commands/eval_ro
2789
+ */
2790
+ evalRo = (...args) => this.chain(new EvalROCommand(args, this.commandOptions));
2343
2791
  /**
2344
2792
  * @see https://redis.io/commands/eval
2345
2793
  */
2346
2794
  eval = (...args) => this.chain(new EvalCommand(args, this.commandOptions));
2795
+ /**
2796
+ * @see https://redis.io/commands/evalsha_ro
2797
+ */
2798
+ evalshaRo = (...args) => this.chain(new EvalshaROCommand(args, this.commandOptions));
2347
2799
  /**
2348
2800
  * @see https://redis.io/commands/evalsha
2349
2801
  */
@@ -2404,6 +2856,10 @@ var Pipeline = class {
2404
2856
  * @see https://redis.io/commands/getdel
2405
2857
  */
2406
2858
  getdel = (...args) => this.chain(new GetDelCommand(args, this.commandOptions));
2859
+ /**
2860
+ * @see https://redis.io/commands/getex
2861
+ */
2862
+ getex = (...args) => this.chain(new GetExCommand(args, this.commandOptions));
2407
2863
  /**
2408
2864
  * @see https://redis.io/commands/getrange
2409
2865
  */
@@ -2420,6 +2876,42 @@ var Pipeline = class {
2420
2876
  * @see https://redis.io/commands/hexists
2421
2877
  */
2422
2878
  hexists = (...args) => this.chain(new HExistsCommand(args, this.commandOptions));
2879
+ /**
2880
+ * @see https://redis.io/commands/hexpire
2881
+ */
2882
+ hexpire = (...args) => this.chain(new HExpireCommand(args, this.commandOptions));
2883
+ /**
2884
+ * @see https://redis.io/commands/hexpireat
2885
+ */
2886
+ hexpireat = (...args) => this.chain(new HExpireAtCommand(args, this.commandOptions));
2887
+ /**
2888
+ * @see https://redis.io/commands/hexpiretime
2889
+ */
2890
+ hexpiretime = (...args) => this.chain(new HExpireTimeCommand(args, this.commandOptions));
2891
+ /**
2892
+ * @see https://redis.io/commands/httl
2893
+ */
2894
+ httl = (...args) => this.chain(new HTtlCommand(args, this.commandOptions));
2895
+ /**
2896
+ * @see https://redis.io/commands/hpexpire
2897
+ */
2898
+ hpexpire = (...args) => this.chain(new HPExpireCommand(args, this.commandOptions));
2899
+ /**
2900
+ * @see https://redis.io/commands/hpexpireat
2901
+ */
2902
+ hpexpireat = (...args) => this.chain(new HPExpireAtCommand(args, this.commandOptions));
2903
+ /**
2904
+ * @see https://redis.io/commands/hpexpiretime
2905
+ */
2906
+ hpexpiretime = (...args) => this.chain(new HPExpireTimeCommand(args, this.commandOptions));
2907
+ /**
2908
+ * @see https://redis.io/commands/hpttl
2909
+ */
2910
+ hpttl = (...args) => this.chain(new HPTtlCommand(args, this.commandOptions));
2911
+ /**
2912
+ * @see https://redis.io/commands/hpersist
2913
+ */
2914
+ hpersist = (...args) => this.chain(new HPersistCommand(args, this.commandOptions));
2423
2915
  /**
2424
2916
  * @see https://redis.io/commands/hget
2425
2917
  */
@@ -2937,6 +3429,10 @@ var Pipeline = class {
2937
3429
  * @see https://redis.io/commands/json.get
2938
3430
  */
2939
3431
  get: (...args) => this.chain(new JsonGetCommand(args, this.commandOptions)),
3432
+ /**
3433
+ * @see https://redis.io/commands/json.merge
3434
+ */
3435
+ merge: (...args) => this.chain(new JsonMergeCommand(args, this.commandOptions)),
2940
3436
  /**
2941
3437
  * @see https://redis.io/commands/json.mget
2942
3438
  */
@@ -2990,27 +3486,43 @@ var Pipeline = class {
2990
3486
  };
2991
3487
 
2992
3488
  // 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"));
3489
+ var import_uncrypto = require("uncrypto");
2995
3490
  var Script = class {
2996
3491
  script;
3492
+ /**
3493
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3494
+ * asynchronously. Do not use this property immidiately after the constructor.
3495
+ *
3496
+ * This property is only exposed for backwards compatibility and will be removed in the
3497
+ * future major release.
3498
+ */
2997
3499
  sha1;
2998
3500
  redis;
2999
3501
  constructor(redis, script) {
3000
3502
  this.redis = redis;
3001
- this.sha1 = this.digest(script);
3002
3503
  this.script = script;
3504
+ this.sha1 = "";
3505
+ void this.init(script);
3506
+ }
3507
+ /**
3508
+ * Initialize the script by computing its SHA-1 hash.
3509
+ */
3510
+ async init(script) {
3511
+ if (this.sha1) return;
3512
+ this.sha1 = await this.digest(script);
3003
3513
  }
3004
3514
  /**
3005
3515
  * Send an `EVAL` command to redis.
3006
3516
  */
3007
3517
  async eval(keys, args) {
3518
+ await this.init(this.script);
3008
3519
  return await this.redis.eval(this.script, keys, args);
3009
3520
  }
3010
3521
  /**
3011
3522
  * Calculates the sha1 hash of the script and then calls `EVALSHA`.
3012
3523
  */
3013
3524
  async evalsha(keys, args) {
3525
+ await this.init(this.script);
3014
3526
  return await this.redis.evalsha(this.sha1, keys, args);
3015
3527
  }
3016
3528
  /**
@@ -3020,6 +3532,7 @@ var Script = class {
3020
3532
  * Following calls will be able to use the cached script
3021
3533
  */
3022
3534
  async exec(keys, args) {
3535
+ await this.init(this.script);
3023
3536
  const res = await this.redis.evalsha(this.sha1, keys, args).catch(async (error) => {
3024
3537
  if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3025
3538
  return await this.redis.eval(this.script, keys, args);
@@ -3031,8 +3544,75 @@ var Script = class {
3031
3544
  /**
3032
3545
  * Compute the sha1 hash of the script and return its hex representation.
3033
3546
  */
3034
- digest(s) {
3035
- return import_enc_hex.default.stringify((0, import_sha1.default)(s));
3547
+ async digest(s) {
3548
+ const data = new TextEncoder().encode(s);
3549
+ const hashBuffer = await import_uncrypto.subtle.digest("SHA-1", data);
3550
+ const hashArray = [...new Uint8Array(hashBuffer)];
3551
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3552
+ }
3553
+ };
3554
+
3555
+ // pkg/scriptRo.ts
3556
+ var import_uncrypto2 = require("uncrypto");
3557
+ var ScriptRO = class {
3558
+ script;
3559
+ /**
3560
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3561
+ * asynchronously. Do not use this property immidiately after the constructor.
3562
+ *
3563
+ * This property is only exposed for backwards compatibility and will be removed in the
3564
+ * future major release.
3565
+ */
3566
+ sha1;
3567
+ redis;
3568
+ constructor(redis, script) {
3569
+ this.redis = redis;
3570
+ this.sha1 = "";
3571
+ this.script = script;
3572
+ void this.init(script);
3573
+ }
3574
+ async init(script) {
3575
+ if (this.sha1) return;
3576
+ this.sha1 = await this.digest(script);
3577
+ }
3578
+ /**
3579
+ * Send an `EVAL_RO` command to redis.
3580
+ */
3581
+ async evalRo(keys, args) {
3582
+ await this.init(this.script);
3583
+ return await this.redis.evalRo(this.script, keys, args);
3584
+ }
3585
+ /**
3586
+ * Calculates the sha1 hash of the script and then calls `EVALSHA_RO`.
3587
+ */
3588
+ async evalshaRo(keys, args) {
3589
+ await this.init(this.script);
3590
+ return await this.redis.evalshaRo(this.sha1, keys, args);
3591
+ }
3592
+ /**
3593
+ * Optimistically try to run `EVALSHA_RO` first.
3594
+ * If the script is not loaded in redis, it will fall back and try again with `EVAL_RO`.
3595
+ *
3596
+ * Following calls will be able to use the cached script
3597
+ */
3598
+ async exec(keys, args) {
3599
+ await this.init(this.script);
3600
+ const res = await this.redis.evalshaRo(this.sha1, keys, args).catch(async (error) => {
3601
+ if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3602
+ return await this.redis.evalRo(this.script, keys, args);
3603
+ }
3604
+ throw error;
3605
+ });
3606
+ return res;
3607
+ }
3608
+ /**
3609
+ * Compute the sha1 hash of the script and return its hex representation.
3610
+ */
3611
+ async digest(s) {
3612
+ const data = new TextEncoder().encode(s);
3613
+ const hashBuffer = await import_uncrypto2.subtle.digest("SHA-1", data);
3614
+ const hashArray = [...new Uint8Array(hashBuffer)];
3615
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3036
3616
  }
3037
3617
  };
3038
3618
 
@@ -3062,6 +3642,12 @@ var Redis = class {
3062
3642
  }
3063
3643
  this.enableAutoPipelining = opts?.enableAutoPipelining ?? true;
3064
3644
  }
3645
+ get readYourWritesSyncToken() {
3646
+ return this.client.upstashSyncToken;
3647
+ }
3648
+ set readYourWritesSyncToken(session) {
3649
+ this.client.upstashSyncToken = session;
3650
+ }
3065
3651
  get json() {
3066
3652
  return {
3067
3653
  /**
@@ -3104,6 +3690,10 @@ var Redis = class {
3104
3690
  * @see https://redis.io/commands/json.get
3105
3691
  */
3106
3692
  get: (...args) => new JsonGetCommand(args, this.opts).exec(this.client),
3693
+ /**
3694
+ * @see https://redis.io/commands/json.merge
3695
+ */
3696
+ merge: (...args) => new JsonMergeCommand(args, this.opts).exec(this.client),
3107
3697
  /**
3108
3698
  * @see https://redis.io/commands/json.mget
3109
3699
  */
@@ -3173,8 +3763,36 @@ var Redis = class {
3173
3763
  } catch {
3174
3764
  }
3175
3765
  };
3176
- createScript(script) {
3177
- return new Script(this, script);
3766
+ /**
3767
+ * Creates a new script.
3768
+ *
3769
+ * Scripts offer the ability to optimistically try to execute a script without having to send the
3770
+ * entire script to the server. If the script is loaded on the server, it tries again by sending
3771
+ * the entire script. Afterwards, the script is cached on the server.
3772
+ *
3773
+ * @param script - The script to create
3774
+ * @param opts - Optional options to pass to the script `{ readonly?: boolean }`
3775
+ * @returns A new script
3776
+ *
3777
+ * @example
3778
+ * ```ts
3779
+ * const redis = new Redis({...})
3780
+ *
3781
+ * const script = redis.createScript<string>("return ARGV[1];")
3782
+ * const arg1 = await script.eval([], ["Hello World"])
3783
+ * expect(arg1, "Hello World")
3784
+ * ```
3785
+ * @example
3786
+ * ```ts
3787
+ * const redis = new Redis({...})
3788
+ *
3789
+ * const script = redis.createScript<string>("return ARGV[1];", { readonly: true })
3790
+ * const arg1 = await script.evalRo([], ["Hello World"])
3791
+ * expect(arg1, "Hello World")
3792
+ * ```
3793
+ */
3794
+ createScript(script, opts) {
3795
+ return opts?.readonly ? new ScriptRO(this, script) : new Script(this, script);
3178
3796
  }
3179
3797
  /**
3180
3798
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3261,14 +3879,26 @@ var Redis = class {
3261
3879
  * @see https://redis.io/commands/echo
3262
3880
  */
3263
3881
  echo = (...args) => new EchoCommand(args, this.opts).exec(this.client);
3882
+ /**
3883
+ * @see https://redis.io/commands/eval_ro
3884
+ */
3885
+ evalRo = (...args) => new EvalROCommand(args, this.opts).exec(this.client);
3264
3886
  /**
3265
3887
  * @see https://redis.io/commands/eval
3266
3888
  */
3267
3889
  eval = (...args) => new EvalCommand(args, this.opts).exec(this.client);
3890
+ /**
3891
+ * @see https://redis.io/commands/evalsha_ro
3892
+ */
3893
+ evalshaRo = (...args) => new EvalshaROCommand(args, this.opts).exec(this.client);
3268
3894
  /**
3269
3895
  * @see https://redis.io/commands/evalsha
3270
3896
  */
3271
3897
  evalsha = (...args) => new EvalshaCommand(args, this.opts).exec(this.client);
3898
+ /**
3899
+ * Generic method to execute any Redis command.
3900
+ */
3901
+ exec = (args) => new ExecCommand(args, this.opts).exec(this.client);
3272
3902
  /**
3273
3903
  * @see https://redis.io/commands/exists
3274
3904
  */
@@ -3325,6 +3955,10 @@ var Redis = class {
3325
3955
  * @see https://redis.io/commands/getdel
3326
3956
  */
3327
3957
  getdel = (...args) => new GetDelCommand(args, this.opts).exec(this.client);
3958
+ /**
3959
+ * @see https://redis.io/commands/getex
3960
+ */
3961
+ getex = (...args) => new GetExCommand(args, this.opts).exec(this.client);
3328
3962
  /**
3329
3963
  * @see https://redis.io/commands/getrange
3330
3964
  */
@@ -3341,6 +3975,42 @@ var Redis = class {
3341
3975
  * @see https://redis.io/commands/hexists
3342
3976
  */
3343
3977
  hexists = (...args) => new HExistsCommand(args, this.opts).exec(this.client);
3978
+ /**
3979
+ * @see https://redis.io/commands/hexpire
3980
+ */
3981
+ hexpire = (...args) => new HExpireCommand(args, this.opts).exec(this.client);
3982
+ /**
3983
+ * @see https://redis.io/commands/hexpireat
3984
+ */
3985
+ hexpireat = (...args) => new HExpireAtCommand(args, this.opts).exec(this.client);
3986
+ /**
3987
+ * @see https://redis.io/commands/hexpiretime
3988
+ */
3989
+ hexpiretime = (...args) => new HExpireTimeCommand(args, this.opts).exec(this.client);
3990
+ /**
3991
+ * @see https://redis.io/commands/httl
3992
+ */
3993
+ httl = (...args) => new HTtlCommand(args, this.opts).exec(this.client);
3994
+ /**
3995
+ * @see https://redis.io/commands/hpexpire
3996
+ */
3997
+ hpexpire = (...args) => new HPExpireCommand(args, this.opts).exec(this.client);
3998
+ /**
3999
+ * @see https://redis.io/commands/hpexpireat
4000
+ */
4001
+ hpexpireat = (...args) => new HPExpireAtCommand(args, this.opts).exec(this.client);
4002
+ /**
4003
+ * @see https://redis.io/commands/hpexpiretime
4004
+ */
4005
+ hpexpiretime = (...args) => new HPExpireTimeCommand(args, this.opts).exec(this.client);
4006
+ /**
4007
+ * @see https://redis.io/commands/hpttl
4008
+ */
4009
+ hpttl = (...args) => new HPTtlCommand(args, this.opts).exec(this.client);
4010
+ /**
4011
+ * @see https://redis.io/commands/hpersist
4012
+ */
4013
+ hpersist = (...args) => new HPersistCommand(args, this.opts).exec(this.client);
3344
4014
  /**
3345
4015
  * @see https://redis.io/commands/hget
3346
4016
  */
@@ -3509,6 +4179,13 @@ var Redis = class {
3509
4179
  * @see https://redis.io/commands/psetex
3510
4180
  */
3511
4181
  psetex = (key, ttl, value) => new PSetEXCommand([key, ttl, value], this.opts).exec(this.client);
4182
+ /**
4183
+ * @see https://redis.io/commands/psubscribe
4184
+ */
4185
+ psubscribe = (patterns) => {
4186
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
4187
+ return new Subscriber(this.client, patternArray, true);
4188
+ };
3512
4189
  /**
3513
4190
  * @see https://redis.io/commands/pttl
3514
4191
  */
@@ -3545,10 +4222,9 @@ var Redis = class {
3545
4222
  * @see https://redis.io/commands/sadd
3546
4223
  */
3547
4224
  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);
4225
+ scan(cursor, opts) {
4226
+ return new ScanCommand([cursor, opts], this.opts).exec(this.client);
4227
+ }
3552
4228
  /**
3553
4229
  * @see https://redis.io/commands/scard
3554
4230
  */
@@ -3637,6 +4313,13 @@ var Redis = class {
3637
4313
  * @see https://redis.io/commands/strlen
3638
4314
  */
3639
4315
  strlen = (...args) => new StrLenCommand(args, this.opts).exec(this.client);
4316
+ /**
4317
+ * @see https://redis.io/commands/subscribe
4318
+ */
4319
+ subscribe = (channels) => {
4320
+ const channelArray = Array.isArray(channels) ? channels : [channels];
4321
+ return new Subscriber(this.client, channelArray);
4322
+ };
3640
4323
  /**
3641
4324
  * @see https://redis.io/commands/sunion
3642
4325
  */
@@ -3818,7 +4501,7 @@ var Redis = class {
3818
4501
  };
3819
4502
 
3820
4503
  // version.ts
3821
- var VERSION = "v1.35.0-canary";
4504
+ var VERSION = "v1.35.1";
3822
4505
 
3823
4506
  // platforms/fastly.ts
3824
4507
  var Redis2 = class extends Redis {
@@ -3836,20 +4519,22 @@ var Redis2 = class extends Redis {
3836
4519
  */
3837
4520
  constructor(config) {
3838
4521
  if (!config.url) {
3839
- throw new Error(
4522
+ console.warn(
3840
4523
  `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.`
3841
4524
  );
4525
+ } else if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) {
4526
+ console.warn(
4527
+ "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!"
4528
+ );
3842
4529
  }
3843
4530
  if (!config.token) {
3844
- throw new Error(
4531
+ console.warn(
3845
4532
  `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.`
3846
4533
  );
3847
- }
3848
- if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) {
3849
- console.warn("The redis url contains whitespace or newline, which can cause errors!");
3850
- }
3851
- if (config.token.startsWith(" ") || config.token.endsWith(" ") || /\r|\n/.test(config.token)) {
3852
- console.warn("The redis token contains whitespace or newline, which can cause errors!");
4534
+ } else if (config.token.startsWith(" ") || config.token.endsWith(" ") || /\r|\n/.test(config.token)) {
4535
+ console.warn(
4536
+ "[Upstash Redis] The redis token contains whitespace or newline, which can cause errors!"
4537
+ );
3853
4538
  }
3854
4539
  const client = new HttpClient({
3855
4540
  baseUrl: config.url,