@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.
@@ -19,12 +19,57 @@ var UpstashError = class extends Error {
19
19
  var UrlError = class extends Error {
20
20
  constructor(url) {
21
21
  super(
22
- `Upstash Redis client was passed an invalid URL. You should pass the URL together with https. Received: "${url}". `
22
+ `Upstash Redis client was passed an invalid URL. You should pass a URL starting with https. Received: "${url}". `
23
23
  );
24
24
  this.name = "UrlError";
25
25
  }
26
26
  };
27
27
 
28
+ // pkg/util.ts
29
+ function parseRecursive(obj) {
30
+ const parsed = Array.isArray(obj) ? obj.map((o) => {
31
+ try {
32
+ return parseRecursive(o);
33
+ } catch {
34
+ return o;
35
+ }
36
+ }) : JSON.parse(obj);
37
+ if (typeof parsed === "number" && parsed.toString() !== obj) {
38
+ return obj;
39
+ }
40
+ return parsed;
41
+ }
42
+ function parseResponse(result) {
43
+ try {
44
+ return parseRecursive(result);
45
+ } catch {
46
+ return result;
47
+ }
48
+ }
49
+ function deserializeScanResponse(result) {
50
+ return [result[0], ...parseResponse(result.slice(1))];
51
+ }
52
+ function deserializeScanWithTypesResponse(result) {
53
+ const [cursor, keys] = result;
54
+ const parsedKeys = [];
55
+ for (let i = 0; i < keys.length; i += 2) {
56
+ parsedKeys.push({ key: keys[i], type: keys[i + 1] });
57
+ }
58
+ return [cursor, parsedKeys];
59
+ }
60
+ function mergeHeaders(...headers) {
61
+ const merged = {};
62
+ for (const header of headers) {
63
+ if (!header) continue;
64
+ for (const [key, value] of Object.entries(header)) {
65
+ if (value !== void 0 && value !== null) {
66
+ merged[key] = value;
67
+ }
68
+ }
69
+ }
70
+ return merged;
71
+ }
72
+
28
73
  // pkg/http.ts
29
74
  var HttpClient = class {
30
75
  baseUrl;
@@ -32,6 +77,7 @@ var HttpClient = class {
32
77
  options;
33
78
  readYourWrites;
34
79
  upstashSyncToken = "";
80
+ hasCredentials;
35
81
  retry;
36
82
  constructor(config) {
37
83
  this.options = {
@@ -45,15 +91,16 @@ var HttpClient = class {
45
91
  };
46
92
  this.upstashSyncToken = "";
47
93
  this.readYourWrites = config.readYourWrites ?? true;
48
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
94
+ this.baseUrl = (config.baseUrl || "").replace(/\/$/, "");
49
95
  const urlRegex = /^https?:\/\/[^\s#$./?].\S*$/;
50
- if (!urlRegex.test(this.baseUrl)) {
96
+ if (this.baseUrl && !urlRegex.test(this.baseUrl)) {
51
97
  throw new UrlError(this.baseUrl);
52
98
  }
53
99
  this.headers = {
54
100
  "Content-Type": "application/json",
55
101
  ...config.headers
56
102
  };
103
+ this.hasCredentials = Boolean(this.baseUrl && this.headers.authorization.split(" ")[1]);
57
104
  if (this.options.responseEncoding === "base64") {
58
105
  this.headers["Upstash-Encoding"] = "base64";
59
106
  }
@@ -71,20 +118,30 @@ var HttpClient = class {
71
118
  this.headers = merge(this.headers, "Upstash-Telemetry-Sdk", telemetry.sdk);
72
119
  }
73
120
  async request(req) {
121
+ const requestHeaders = mergeHeaders(this.headers, req.headers ?? {});
122
+ const requestUrl = [this.baseUrl, ...req.path ?? []].join("/");
123
+ const isEventStream = requestHeaders.Accept === "text/event-stream";
124
+ const signal = req.signal ?? this.options.signal;
125
+ const isSignalFunction = typeof signal === "function";
74
126
  const requestOptions = {
75
127
  //@ts-expect-error this should throw due to bun regression
76
128
  cache: this.options.cache,
77
129
  method: "POST",
78
- headers: this.headers,
130
+ headers: requestHeaders,
79
131
  body: JSON.stringify(req.body),
80
132
  keepalive: this.options.keepAlive,
81
133
  agent: this.options.agent,
82
- signal: this.options.signal,
134
+ signal: isSignalFunction ? signal() : signal,
83
135
  /**
84
136
  * Fastly specific
85
137
  */
86
138
  backend: this.options.backend
87
139
  };
140
+ if (!this.hasCredentials) {
141
+ console.warn(
142
+ "[Upstash Redis] Redis client was initialized without url or token. Failed to execute command."
143
+ );
144
+ }
88
145
  if (this.readYourWrites) {
89
146
  const newHeader = this.upstashSyncToken;
90
147
  this.headers["upstash-sync-token"] = newHeader;
@@ -93,35 +150,71 @@ var HttpClient = class {
93
150
  let error = null;
94
151
  for (let i = 0; i <= this.retry.attempts; i++) {
95
152
  try {
96
- res = await fetch([this.baseUrl, ...req.path ?? []].join("/"), requestOptions);
153
+ res = await fetch(requestUrl, requestOptions);
97
154
  break;
98
155
  } catch (error_) {
99
- if (this.options.signal?.aborted) {
156
+ if (requestOptions.signal?.aborted && isSignalFunction) {
157
+ throw error_;
158
+ } else if (requestOptions.signal?.aborted) {
100
159
  const myBlob = new Blob([
101
- JSON.stringify({ result: this.options.signal.reason ?? "Aborted" })
160
+ JSON.stringify({ result: requestOptions.signal.reason ?? "Aborted" })
102
161
  ]);
103
162
  const myOptions = {
104
163
  status: 200,
105
- statusText: this.options.signal.reason ?? "Aborted"
164
+ statusText: requestOptions.signal.reason ?? "Aborted"
106
165
  };
107
166
  res = new Response(myBlob, myOptions);
108
167
  break;
109
168
  }
110
169
  error = error_;
111
- await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
170
+ if (i < this.retry.attempts) {
171
+ await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
172
+ }
112
173
  }
113
174
  }
114
175
  if (!res) {
115
176
  throw error ?? new Error("Exhausted all retries");
116
177
  }
117
- const body = await res.json();
118
178
  if (!res.ok) {
119
- throw new UpstashError(`${body.error}, command was: ${JSON.stringify(req.body)}`);
179
+ const body2 = await res.json();
180
+ throw new UpstashError(`${body2.error}, command was: ${JSON.stringify(req.body)}`);
120
181
  }
121
182
  if (this.readYourWrites) {
122
183
  const headers = res.headers;
123
184
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
124
185
  }
186
+ if (isEventStream && req && req.onMessage && res.body) {
187
+ const reader = res.body.getReader();
188
+ const decoder = new TextDecoder();
189
+ (async () => {
190
+ try {
191
+ while (true) {
192
+ const { value, done } = await reader.read();
193
+ if (done) break;
194
+ const chunk = decoder.decode(value);
195
+ const lines = chunk.split("\n");
196
+ for (const line of lines) {
197
+ if (line.startsWith("data: ")) {
198
+ const data = line.slice(6);
199
+ req.onMessage?.(data);
200
+ }
201
+ }
202
+ }
203
+ } catch (error2) {
204
+ if (error2 instanceof Error && error2.name === "AbortError") {
205
+ } else {
206
+ console.error("Stream reading error:", error2);
207
+ }
208
+ } finally {
209
+ try {
210
+ await reader.cancel();
211
+ } catch {
212
+ }
213
+ }
214
+ })();
215
+ return { result: 1 };
216
+ }
217
+ const body = await res.json();
125
218
  if (this.readYourWrites) {
126
219
  const headers = res.headers;
127
220
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -192,31 +285,6 @@ function merge(obj, key, value) {
192
285
  return obj;
193
286
  }
194
287
 
195
- // pkg/util.ts
196
- function parseRecursive(obj) {
197
- const parsed = Array.isArray(obj) ? obj.map((o) => {
198
- try {
199
- return parseRecursive(o);
200
- } catch {
201
- return o;
202
- }
203
- }) : JSON.parse(obj);
204
- if (typeof parsed === "number" && parsed.toString() !== obj) {
205
- return obj;
206
- }
207
- return parsed;
208
- }
209
- function parseResponse(result) {
210
- try {
211
- return parseRecursive(result);
212
- } catch {
213
- return result;
214
- }
215
- }
216
- function deserializeScanResponse(result) {
217
- return [result[0], ...parseResponse(result.slice(1))];
218
- }
219
-
220
288
  // pkg/commands/command.ts
221
289
  var defaultSerializer = (c) => {
222
290
  switch (typeof c) {
@@ -234,6 +302,11 @@ var Command = class {
234
302
  command;
235
303
  serialize;
236
304
  deserialize;
305
+ headers;
306
+ path;
307
+ onMessage;
308
+ isStreaming;
309
+ signal;
237
310
  /**
238
311
  * Create a new command instance.
239
312
  *
@@ -243,6 +316,11 @@ var Command = class {
243
316
  this.serialize = defaultSerializer;
244
317
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
245
318
  this.command = command.map((c) => this.serialize(c));
319
+ this.headers = opts?.headers;
320
+ this.path = opts?.path;
321
+ this.onMessage = opts?.streamOptions?.onMessage;
322
+ this.isStreaming = opts?.streamOptions?.isStreaming ?? false;
323
+ this.signal = opts?.streamOptions?.signal;
246
324
  if (opts?.latencyLogging) {
247
325
  const originalExec = this.exec.bind(this);
248
326
  this.exec = async (client) => {
@@ -263,7 +341,12 @@ var Command = class {
263
341
  async exec(client) {
264
342
  const { result, error } = await client.request({
265
343
  body: this.command,
266
- upstashSyncToken: client.upstashSyncToken
344
+ path: this.path,
345
+ upstashSyncToken: client.upstashSyncToken,
346
+ headers: this.headers,
347
+ onMessage: this.onMessage,
348
+ isStreaming: this.isStreaming,
349
+ signal: this.signal
267
350
  });
268
351
  if (error) {
269
352
  throw new UpstashError(error);
@@ -281,9 +364,9 @@ function deserialize(result) {
281
364
  return null;
282
365
  }
283
366
  const obj = {};
284
- while (result.length >= 2) {
285
- const key = result.shift();
286
- const value = result.shift();
367
+ for (let i = 0; i < result.length; i += 2) {
368
+ const key = result[i];
369
+ const value = result[i + 1];
287
370
  try {
288
371
  obj[key] = JSON.parse(value);
289
372
  } catch {
@@ -425,6 +508,13 @@ var EchoCommand = class extends Command {
425
508
  }
426
509
  };
427
510
 
511
+ // pkg/commands/evalRo.ts
512
+ var EvalROCommand = class extends Command {
513
+ constructor([script, keys, args], opts) {
514
+ super(["eval_ro", script, keys.length, ...keys, ...args ?? []], opts);
515
+ }
516
+ };
517
+
428
518
  // pkg/commands/eval.ts
429
519
  var EvalCommand = class extends Command {
430
520
  constructor([script, keys, args], opts) {
@@ -432,6 +522,13 @@ var EvalCommand = class extends Command {
432
522
  }
433
523
  };
434
524
 
525
+ // pkg/commands/evalshaRo.ts
526
+ var EvalshaROCommand = class extends Command {
527
+ constructor([sha, keys, args], opts) {
528
+ super(["evalsha_ro", sha, keys.length, ...keys, ...args ?? []], opts);
529
+ }
530
+ };
531
+
435
532
  // pkg/commands/evalsha.ts
436
533
  var EvalshaCommand = class extends Command {
437
534
  constructor([sha, keys, args], opts) {
@@ -439,6 +536,14 @@ var EvalshaCommand = class extends Command {
439
536
  }
440
537
  };
441
538
 
539
+ // pkg/commands/exec.ts
540
+ var ExecCommand = class extends Command {
541
+ constructor(cmd, opts) {
542
+ const normalizedCmd = cmd.map((arg) => typeof arg === "string" ? arg : String(arg));
543
+ super(normalizedCmd, opts);
544
+ }
545
+ };
546
+
442
547
  // pkg/commands/exists.ts
443
548
  var ExistsCommand = class extends Command {
444
549
  constructor(cmd, opts) {
@@ -655,6 +760,27 @@ var GetDelCommand = class extends Command {
655
760
  }
656
761
  };
657
762
 
763
+ // pkg/commands/getex.ts
764
+ var GetExCommand = class extends Command {
765
+ constructor([key, opts], cmdOpts) {
766
+ const command = ["getex", key];
767
+ if (opts) {
768
+ if ("ex" in opts && typeof opts.ex === "number") {
769
+ command.push("ex", opts.ex);
770
+ } else if ("px" in opts && typeof opts.px === "number") {
771
+ command.push("px", opts.px);
772
+ } else if ("exat" in opts && typeof opts.exat === "number") {
773
+ command.push("exat", opts.exat);
774
+ } else if ("pxat" in opts && typeof opts.pxat === "number") {
775
+ command.push("pxat", opts.pxat);
776
+ } else if ("persist" in opts && opts.persist) {
777
+ command.push("persist");
778
+ }
779
+ }
780
+ super(command, cmdOpts);
781
+ }
782
+ };
783
+
658
784
  // pkg/commands/getrange.ts
659
785
  var GetRangeCommand = class extends Command {
660
786
  constructor(cmd, opts) {
@@ -683,6 +809,122 @@ var HExistsCommand = class extends Command {
683
809
  }
684
810
  };
685
811
 
812
+ // pkg/commands/hexpire.ts
813
+ var HExpireCommand = class extends Command {
814
+ constructor(cmd, opts) {
815
+ const [key, fields, seconds, option] = cmd;
816
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
817
+ super(
818
+ [
819
+ "hexpire",
820
+ key,
821
+ seconds,
822
+ ...option ? [option] : [],
823
+ "FIELDS",
824
+ fieldArray.length,
825
+ ...fieldArray
826
+ ],
827
+ opts
828
+ );
829
+ }
830
+ };
831
+
832
+ // pkg/commands/hexpireat.ts
833
+ var HExpireAtCommand = class extends Command {
834
+ constructor(cmd, opts) {
835
+ const [key, fields, timestamp, option] = cmd;
836
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
837
+ super(
838
+ [
839
+ "hexpireat",
840
+ key,
841
+ timestamp,
842
+ ...option ? [option] : [],
843
+ "FIELDS",
844
+ fieldArray.length,
845
+ ...fieldArray
846
+ ],
847
+ opts
848
+ );
849
+ }
850
+ };
851
+
852
+ // pkg/commands/hexpiretime.ts
853
+ var HExpireTimeCommand = class extends Command {
854
+ constructor(cmd, opts) {
855
+ const [key, fields] = cmd;
856
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
857
+ super(["hexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
858
+ }
859
+ };
860
+
861
+ // pkg/commands/hpersist.ts
862
+ var HPersistCommand = class extends Command {
863
+ constructor(cmd, opts) {
864
+ const [key, fields] = cmd;
865
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
866
+ super(["hpersist", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
867
+ }
868
+ };
869
+
870
+ // pkg/commands/hpexpire.ts
871
+ var HPExpireCommand = class extends Command {
872
+ constructor(cmd, opts) {
873
+ const [key, fields, milliseconds, option] = cmd;
874
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
875
+ super(
876
+ [
877
+ "hpexpire",
878
+ key,
879
+ milliseconds,
880
+ ...option ? [option] : [],
881
+ "FIELDS",
882
+ fieldArray.length,
883
+ ...fieldArray
884
+ ],
885
+ opts
886
+ );
887
+ }
888
+ };
889
+
890
+ // pkg/commands/hpexpireat.ts
891
+ var HPExpireAtCommand = class extends Command {
892
+ constructor(cmd, opts) {
893
+ const [key, fields, timestamp, option] = cmd;
894
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
895
+ super(
896
+ [
897
+ "hpexpireat",
898
+ key,
899
+ timestamp,
900
+ ...option ? [option] : [],
901
+ "FIELDS",
902
+ fieldArray.length,
903
+ ...fieldArray
904
+ ],
905
+ opts
906
+ );
907
+ }
908
+ };
909
+
910
+ // pkg/commands/hpexpiretime.ts
911
+ var HPExpireTimeCommand = class extends Command {
912
+ constructor(cmd, opts) {
913
+ const [key, fields] = cmd;
914
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
915
+ super(["hpexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
916
+ }
917
+ };
918
+
919
+ // pkg/commands/hpttl.ts
920
+ var HPTtlCommand = class extends Command {
921
+ constructor(cmd, opts) {
922
+ const [key, fields] = cmd;
923
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
924
+ super(["hpttl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
925
+ }
926
+ };
927
+
686
928
  // pkg/commands/hget.ts
687
929
  var HGetCommand = class extends Command {
688
930
  constructor(cmd, opts) {
@@ -696,9 +938,9 @@ function deserialize2(result) {
696
938
  return null;
697
939
  }
698
940
  const obj = {};
699
- while (result.length >= 2) {
700
- const key = result.shift();
701
- const value = result.shift();
941
+ for (let i = 0; i < result.length; i += 2) {
942
+ const key = result[i];
943
+ const value = result[i + 1];
702
944
  try {
703
945
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
704
946
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -814,6 +1056,15 @@ var HStrLenCommand = class extends Command {
814
1056
  }
815
1057
  };
816
1058
 
1059
+ // pkg/commands/httl.ts
1060
+ var HTtlCommand = class extends Command {
1061
+ constructor(cmd, opts) {
1062
+ const [key, fields] = cmd;
1063
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1064
+ super(["httl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1065
+ }
1066
+ };
1067
+
817
1068
  // pkg/commands/hvals.ts
818
1069
  var HValsCommand = class extends Command {
819
1070
  constructor(cmd, opts) {
@@ -933,6 +1184,14 @@ var JsonGetCommand = class extends Command {
933
1184
  }
934
1185
  };
935
1186
 
1187
+ // pkg/commands/json_merge.ts
1188
+ var JsonMergeCommand = class extends Command {
1189
+ constructor(cmd, opts) {
1190
+ const command = ["JSON.MERGE", ...cmd];
1191
+ super(command, opts);
1192
+ }
1193
+ };
1194
+
936
1195
  // pkg/commands/json_mget.ts
937
1196
  var JsonMGetCommand = class extends Command {
938
1197
  constructor(cmd, opts) {
@@ -1293,11 +1552,14 @@ var ScanCommand = class extends Command {
1293
1552
  if (typeof opts?.count === "number") {
1294
1553
  command.push("count", opts.count);
1295
1554
  }
1296
- if (opts?.type && opts.type.length > 0) {
1555
+ if (opts && "withType" in opts && opts.withType === true) {
1556
+ command.push("withtype");
1557
+ } else if (opts && "type" in opts && opts.type && opts.type.length > 0) {
1297
1558
  command.push("type", opts.type);
1298
1559
  }
1299
1560
  super(command, {
1300
- deserialize: deserializeScanResponse,
1561
+ // @ts-expect-error ignore types here
1562
+ deserialize: opts?.withType ? deserializeScanWithTypesResponse : deserializeScanResponse,
1301
1563
  ...cmdOpts
1302
1564
  });
1303
1565
  }
@@ -1723,15 +1985,15 @@ var XPendingCommand = class extends Command {
1723
1985
  function deserialize4(result) {
1724
1986
  const obj = {};
1725
1987
  for (const e of result) {
1726
- while (e.length >= 2) {
1727
- const streamId = e.shift();
1728
- const entries = e.shift();
1988
+ for (let i = 0; i < e.length; i += 2) {
1989
+ const streamId = e[i];
1990
+ const entries = e[i + 1];
1729
1991
  if (!(streamId in obj)) {
1730
1992
  obj[streamId] = {};
1731
1993
  }
1732
- while (entries.length >= 2) {
1733
- const field = entries.shift();
1734
- const value = entries.shift();
1994
+ for (let j = 0; j < entries.length; j += 2) {
1995
+ const field = entries[j];
1996
+ const value = entries[j + 1];
1735
1997
  try {
1736
1998
  obj[streamId][field] = JSON.parse(value);
1737
1999
  } catch {
@@ -1820,15 +2082,15 @@ var XRevRangeCommand = class extends Command {
1820
2082
  function deserialize5(result) {
1821
2083
  const obj = {};
1822
2084
  for (const e of result) {
1823
- while (e.length >= 2) {
1824
- const streamId = e.shift();
1825
- const entries = e.shift();
2085
+ for (let i = 0; i < e.length; i += 2) {
2086
+ const streamId = e[i];
2087
+ const entries = e[i + 1];
1826
2088
  if (!(streamId in obj)) {
1827
2089
  obj[streamId] = {};
1828
2090
  }
1829
- while (entries.length >= 2) {
1830
- const field = entries.shift();
1831
- const value = entries.shift();
2091
+ for (let j = 0; j < entries.length; j += 2) {
2092
+ const field = entries[j];
2093
+ const value = entries[j + 1];
1832
2094
  try {
1833
2095
  obj[streamId][field] = JSON.parse(value);
1834
2096
  } catch {
@@ -2115,9 +2377,9 @@ var Pipeline = class {
2115
2377
  this.multiExec = opts.multiExec ?? false;
2116
2378
  if (this.commandOptions?.latencyLogging) {
2117
2379
  const originalExec = this.exec.bind(this);
2118
- this.exec = async () => {
2380
+ this.exec = async (options) => {
2119
2381
  const start = performance.now();
2120
- const result = await originalExec();
2382
+ const result = await (options ? originalExec(options) : originalExec());
2121
2383
  const end = performance.now();
2122
2384
  const loggerResult = (end - start).toFixed(2);
2123
2385
  console.log(
@@ -2127,19 +2389,7 @@ var Pipeline = class {
2127
2389
  };
2128
2390
  }
2129
2391
  }
2130
- /**
2131
- * Send the pipeline request to upstash.
2132
- *
2133
- * Returns an array with the results of all pipelined commands.
2134
- *
2135
- * If all commands are statically chained from start to finish, types are inferred. You can still define a return type manually if necessary though:
2136
- * ```ts
2137
- * const p = redis.pipeline()
2138
- * p.get("key")
2139
- * const result = p.exec<[{ greeting: string }]>()
2140
- * ```
2141
- */
2142
- exec = async () => {
2392
+ exec = async (options) => {
2143
2393
  if (this.commands.length === 0) {
2144
2394
  throw new Error("Pipeline is empty");
2145
2395
  }
@@ -2148,7 +2398,12 @@ var Pipeline = class {
2148
2398
  path,
2149
2399
  body: Object.values(this.commands).map((c) => c.command)
2150
2400
  });
2151
- return res.map(({ error, result }, i) => {
2401
+ return options?.keepErrors ? res.map(({ error, result }, i) => {
2402
+ return {
2403
+ error,
2404
+ result: this.commands[i].deserialize(result)
2405
+ };
2406
+ }) : res.map(({ error, result }, i) => {
2152
2407
  if (error) {
2153
2408
  throw new UpstashError(
2154
2409
  `Command ${i + 1} [ ${this.commands[i].command[0]} ] failed: ${error}`
@@ -2234,10 +2489,18 @@ var Pipeline = class {
2234
2489
  * @see https://redis.io/commands/echo
2235
2490
  */
2236
2491
  echo = (...args) => this.chain(new EchoCommand(args, this.commandOptions));
2492
+ /**
2493
+ * @see https://redis.io/commands/eval_ro
2494
+ */
2495
+ evalRo = (...args) => this.chain(new EvalROCommand(args, this.commandOptions));
2237
2496
  /**
2238
2497
  * @see https://redis.io/commands/eval
2239
2498
  */
2240
2499
  eval = (...args) => this.chain(new EvalCommand(args, this.commandOptions));
2500
+ /**
2501
+ * @see https://redis.io/commands/evalsha_ro
2502
+ */
2503
+ evalshaRo = (...args) => this.chain(new EvalshaROCommand(args, this.commandOptions));
2241
2504
  /**
2242
2505
  * @see https://redis.io/commands/evalsha
2243
2506
  */
@@ -2298,6 +2561,10 @@ var Pipeline = class {
2298
2561
  * @see https://redis.io/commands/getdel
2299
2562
  */
2300
2563
  getdel = (...args) => this.chain(new GetDelCommand(args, this.commandOptions));
2564
+ /**
2565
+ * @see https://redis.io/commands/getex
2566
+ */
2567
+ getex = (...args) => this.chain(new GetExCommand(args, this.commandOptions));
2301
2568
  /**
2302
2569
  * @see https://redis.io/commands/getrange
2303
2570
  */
@@ -2314,6 +2581,42 @@ var Pipeline = class {
2314
2581
  * @see https://redis.io/commands/hexists
2315
2582
  */
2316
2583
  hexists = (...args) => this.chain(new HExistsCommand(args, this.commandOptions));
2584
+ /**
2585
+ * @see https://redis.io/commands/hexpire
2586
+ */
2587
+ hexpire = (...args) => this.chain(new HExpireCommand(args, this.commandOptions));
2588
+ /**
2589
+ * @see https://redis.io/commands/hexpireat
2590
+ */
2591
+ hexpireat = (...args) => this.chain(new HExpireAtCommand(args, this.commandOptions));
2592
+ /**
2593
+ * @see https://redis.io/commands/hexpiretime
2594
+ */
2595
+ hexpiretime = (...args) => this.chain(new HExpireTimeCommand(args, this.commandOptions));
2596
+ /**
2597
+ * @see https://redis.io/commands/httl
2598
+ */
2599
+ httl = (...args) => this.chain(new HTtlCommand(args, this.commandOptions));
2600
+ /**
2601
+ * @see https://redis.io/commands/hpexpire
2602
+ */
2603
+ hpexpire = (...args) => this.chain(new HPExpireCommand(args, this.commandOptions));
2604
+ /**
2605
+ * @see https://redis.io/commands/hpexpireat
2606
+ */
2607
+ hpexpireat = (...args) => this.chain(new HPExpireAtCommand(args, this.commandOptions));
2608
+ /**
2609
+ * @see https://redis.io/commands/hpexpiretime
2610
+ */
2611
+ hpexpiretime = (...args) => this.chain(new HPExpireTimeCommand(args, this.commandOptions));
2612
+ /**
2613
+ * @see https://redis.io/commands/hpttl
2614
+ */
2615
+ hpttl = (...args) => this.chain(new HPTtlCommand(args, this.commandOptions));
2616
+ /**
2617
+ * @see https://redis.io/commands/hpersist
2618
+ */
2619
+ hpersist = (...args) => this.chain(new HPersistCommand(args, this.commandOptions));
2317
2620
  /**
2318
2621
  * @see https://redis.io/commands/hget
2319
2622
  */
@@ -2831,6 +3134,10 @@ var Pipeline = class {
2831
3134
  * @see https://redis.io/commands/json.get
2832
3135
  */
2833
3136
  get: (...args) => this.chain(new JsonGetCommand(args, this.commandOptions)),
3137
+ /**
3138
+ * @see https://redis.io/commands/json.merge
3139
+ */
3140
+ merge: (...args) => this.chain(new JsonMergeCommand(args, this.commandOptions)),
2834
3141
  /**
2835
3142
  * @see https://redis.io/commands/json.mget
2836
3143
  */
@@ -2884,6 +3191,23 @@ var Pipeline = class {
2884
3191
  };
2885
3192
 
2886
3193
  // pkg/auto-pipeline.ts
3194
+ var EXCLUDE_COMMANDS = /* @__PURE__ */ new Set([
3195
+ "scan",
3196
+ "keys",
3197
+ "flushdb",
3198
+ "flushall",
3199
+ "dbsize",
3200
+ "hscan",
3201
+ "hgetall",
3202
+ "hkeys",
3203
+ "lrange",
3204
+ "sscan",
3205
+ "smembers",
3206
+ "xrange",
3207
+ "xrevrange",
3208
+ "zscan",
3209
+ "zrange"
3210
+ ]);
2887
3211
  function createAutoPipelineProxy(_redis, json) {
2888
3212
  const redis = _redis;
2889
3213
  if (!redis.autoPipelineExecutor) {
@@ -2898,7 +3222,8 @@ function createAutoPipelineProxy(_redis, json) {
2898
3222
  return createAutoPipelineProxy(redis2, true);
2899
3223
  }
2900
3224
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
2901
- if (commandInRedisButNotPipeline) {
3225
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
3226
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
2902
3227
  return redis2[command];
2903
3228
  }
2904
3229
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -2942,7 +3267,7 @@ var AutoPipelineExecutor = class {
2942
3267
  executeWithPipeline(pipeline);
2943
3268
  const pipelineDone = this.deferExecution().then(() => {
2944
3269
  if (!this.pipelinePromises.has(pipeline)) {
2945
- const pipelinePromise = pipeline.exec();
3270
+ const pipelinePromise = pipeline.exec({ keepErrors: true });
2946
3271
  this.pipelineCounter += 1;
2947
3272
  this.pipelinePromises.set(pipeline, pipelinePromise);
2948
3273
  this.activePipeline = null;
@@ -2950,7 +3275,11 @@ var AutoPipelineExecutor = class {
2950
3275
  return this.pipelinePromises.get(pipeline);
2951
3276
  });
2952
3277
  const results = await pipelineDone;
2953
- return results[index];
3278
+ const commandResult = results[index];
3279
+ if (commandResult.error) {
3280
+ throw new UpstashError(`Command failed: ${commandResult.error}`);
3281
+ }
3282
+ return commandResult.result;
2954
3283
  }
2955
3284
  async deferExecution() {
2956
3285
  await Promise.resolve();
@@ -2958,28 +3287,221 @@ var AutoPipelineExecutor = class {
2958
3287
  }
2959
3288
  };
2960
3289
 
3290
+ // pkg/commands/psubscribe.ts
3291
+ var PSubscribeCommand = class extends Command {
3292
+ constructor(cmd, opts) {
3293
+ const sseHeaders = {
3294
+ Accept: "text/event-stream",
3295
+ "Cache-Control": "no-cache",
3296
+ Connection: "keep-alive"
3297
+ };
3298
+ super([], {
3299
+ ...opts,
3300
+ headers: sseHeaders,
3301
+ path: ["psubscribe", ...cmd],
3302
+ streamOptions: {
3303
+ isStreaming: true,
3304
+ onMessage: opts?.streamOptions?.onMessage,
3305
+ signal: opts?.streamOptions?.signal
3306
+ }
3307
+ });
3308
+ }
3309
+ };
3310
+
3311
+ // pkg/commands/subscribe.ts
3312
+ var Subscriber = class extends EventTarget {
3313
+ subscriptions;
3314
+ client;
3315
+ listeners;
3316
+ constructor(client, channels, isPattern = false) {
3317
+ super();
3318
+ this.client = client;
3319
+ this.subscriptions = /* @__PURE__ */ new Map();
3320
+ this.listeners = /* @__PURE__ */ new Map();
3321
+ for (const channel of channels) {
3322
+ if (isPattern) {
3323
+ this.subscribeToPattern(channel);
3324
+ } else {
3325
+ this.subscribeToChannel(channel);
3326
+ }
3327
+ }
3328
+ }
3329
+ subscribeToChannel(channel) {
3330
+ const controller = new AbortController();
3331
+ const command = new SubscribeCommand([channel], {
3332
+ streamOptions: {
3333
+ signal: controller.signal,
3334
+ onMessage: (data) => this.handleMessage(data, false)
3335
+ }
3336
+ });
3337
+ command.exec(this.client).catch((error) => {
3338
+ if (error.name !== "AbortError") {
3339
+ this.dispatchToListeners("error", error);
3340
+ }
3341
+ });
3342
+ this.subscriptions.set(channel, {
3343
+ command,
3344
+ controller,
3345
+ isPattern: false
3346
+ });
3347
+ }
3348
+ subscribeToPattern(pattern) {
3349
+ const controller = new AbortController();
3350
+ const command = new PSubscribeCommand([pattern], {
3351
+ streamOptions: {
3352
+ signal: controller.signal,
3353
+ onMessage: (data) => this.handleMessage(data, true)
3354
+ }
3355
+ });
3356
+ command.exec(this.client).catch((error) => {
3357
+ if (error.name !== "AbortError") {
3358
+ this.dispatchToListeners("error", error);
3359
+ }
3360
+ });
3361
+ this.subscriptions.set(pattern, {
3362
+ command,
3363
+ controller,
3364
+ isPattern: true
3365
+ });
3366
+ }
3367
+ handleMessage(data, isPattern) {
3368
+ const messageData = data.replace(/^data:\s*/, "");
3369
+ const firstCommaIndex = messageData.indexOf(",");
3370
+ const secondCommaIndex = messageData.indexOf(",", firstCommaIndex + 1);
3371
+ const thirdCommaIndex = isPattern ? messageData.indexOf(",", secondCommaIndex + 1) : -1;
3372
+ if (firstCommaIndex !== -1 && secondCommaIndex !== -1) {
3373
+ const type = messageData.slice(0, firstCommaIndex);
3374
+ if (isPattern && type === "pmessage" && thirdCommaIndex !== -1) {
3375
+ const pattern = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
3376
+ const channel = messageData.slice(secondCommaIndex + 1, thirdCommaIndex);
3377
+ const messageStr = messageData.slice(thirdCommaIndex + 1);
3378
+ try {
3379
+ const message = JSON.parse(messageStr);
3380
+ this.dispatchToListeners("pmessage", { pattern, channel, message });
3381
+ this.dispatchToListeners(`pmessage:${pattern}`, { pattern, channel, message });
3382
+ } catch (error) {
3383
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
3384
+ }
3385
+ } else {
3386
+ const channel = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
3387
+ const messageStr = messageData.slice(secondCommaIndex + 1);
3388
+ try {
3389
+ if (type === "subscribe" || type === "psubscribe" || type === "unsubscribe" || type === "punsubscribe") {
3390
+ const count = Number.parseInt(messageStr);
3391
+ this.dispatchToListeners(type, count);
3392
+ } else {
3393
+ const message = JSON.parse(messageStr);
3394
+ this.dispatchToListeners(type, { channel, message });
3395
+ this.dispatchToListeners(`${type}:${channel}`, { channel, message });
3396
+ }
3397
+ } catch (error) {
3398
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
3399
+ }
3400
+ }
3401
+ }
3402
+ }
3403
+ dispatchToListeners(type, data) {
3404
+ const listeners = this.listeners.get(type);
3405
+ if (listeners) {
3406
+ for (const listener of listeners) {
3407
+ listener(data);
3408
+ }
3409
+ }
3410
+ }
3411
+ on(type, listener) {
3412
+ if (!this.listeners.has(type)) {
3413
+ this.listeners.set(type, /* @__PURE__ */ new Set());
3414
+ }
3415
+ this.listeners.get(type)?.add(listener);
3416
+ }
3417
+ removeAllListeners() {
3418
+ this.listeners.clear();
3419
+ }
3420
+ async unsubscribe(channels) {
3421
+ if (channels) {
3422
+ for (const channel of channels) {
3423
+ const subscription = this.subscriptions.get(channel);
3424
+ if (subscription) {
3425
+ try {
3426
+ subscription.controller.abort();
3427
+ } catch {
3428
+ }
3429
+ this.subscriptions.delete(channel);
3430
+ }
3431
+ }
3432
+ } else {
3433
+ for (const subscription of this.subscriptions.values()) {
3434
+ try {
3435
+ subscription.controller.abort();
3436
+ } catch {
3437
+ }
3438
+ }
3439
+ this.subscriptions.clear();
3440
+ this.removeAllListeners();
3441
+ }
3442
+ }
3443
+ getSubscribedChannels() {
3444
+ return [...this.subscriptions.keys()];
3445
+ }
3446
+ };
3447
+ var SubscribeCommand = class extends Command {
3448
+ constructor(cmd, opts) {
3449
+ const sseHeaders = {
3450
+ Accept: "text/event-stream",
3451
+ "Cache-Control": "no-cache",
3452
+ Connection: "keep-alive"
3453
+ };
3454
+ super([], {
3455
+ ...opts,
3456
+ headers: sseHeaders,
3457
+ path: ["subscribe", ...cmd],
3458
+ streamOptions: {
3459
+ isStreaming: true,
3460
+ onMessage: opts?.streamOptions?.onMessage,
3461
+ signal: opts?.streamOptions?.signal
3462
+ }
3463
+ });
3464
+ }
3465
+ };
3466
+
2961
3467
  // pkg/script.ts
2962
- import Hex from "crypto-js/enc-hex.js";
2963
- import sha1 from "crypto-js/sha1.js";
3468
+ import { subtle } from "uncrypto";
2964
3469
  var Script = class {
2965
3470
  script;
3471
+ /**
3472
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3473
+ * asynchronously. Do not use this property immidiately after the constructor.
3474
+ *
3475
+ * This property is only exposed for backwards compatibility and will be removed in the
3476
+ * future major release.
3477
+ */
2966
3478
  sha1;
2967
3479
  redis;
2968
3480
  constructor(redis, script) {
2969
3481
  this.redis = redis;
2970
- this.sha1 = this.digest(script);
2971
3482
  this.script = script;
3483
+ this.sha1 = "";
3484
+ void this.init(script);
3485
+ }
3486
+ /**
3487
+ * Initialize the script by computing its SHA-1 hash.
3488
+ */
3489
+ async init(script) {
3490
+ if (this.sha1) return;
3491
+ this.sha1 = await this.digest(script);
2972
3492
  }
2973
3493
  /**
2974
3494
  * Send an `EVAL` command to redis.
2975
3495
  */
2976
3496
  async eval(keys, args) {
3497
+ await this.init(this.script);
2977
3498
  return await this.redis.eval(this.script, keys, args);
2978
3499
  }
2979
3500
  /**
2980
3501
  * Calculates the sha1 hash of the script and then calls `EVALSHA`.
2981
3502
  */
2982
3503
  async evalsha(keys, args) {
3504
+ await this.init(this.script);
2983
3505
  return await this.redis.evalsha(this.sha1, keys, args);
2984
3506
  }
2985
3507
  /**
@@ -2989,6 +3511,7 @@ var Script = class {
2989
3511
  * Following calls will be able to use the cached script
2990
3512
  */
2991
3513
  async exec(keys, args) {
3514
+ await this.init(this.script);
2992
3515
  const res = await this.redis.evalsha(this.sha1, keys, args).catch(async (error) => {
2993
3516
  if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
2994
3517
  return await this.redis.eval(this.script, keys, args);
@@ -3000,8 +3523,75 @@ var Script = class {
3000
3523
  /**
3001
3524
  * Compute the sha1 hash of the script and return its hex representation.
3002
3525
  */
3003
- digest(s) {
3004
- return Hex.stringify(sha1(s));
3526
+ async digest(s) {
3527
+ const data = new TextEncoder().encode(s);
3528
+ const hashBuffer = await subtle.digest("SHA-1", data);
3529
+ const hashArray = [...new Uint8Array(hashBuffer)];
3530
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3531
+ }
3532
+ };
3533
+
3534
+ // pkg/scriptRo.ts
3535
+ import { subtle as subtle2 } from "uncrypto";
3536
+ var ScriptRO = class {
3537
+ script;
3538
+ /**
3539
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3540
+ * asynchronously. Do not use this property immidiately after the constructor.
3541
+ *
3542
+ * This property is only exposed for backwards compatibility and will be removed in the
3543
+ * future major release.
3544
+ */
3545
+ sha1;
3546
+ redis;
3547
+ constructor(redis, script) {
3548
+ this.redis = redis;
3549
+ this.sha1 = "";
3550
+ this.script = script;
3551
+ void this.init(script);
3552
+ }
3553
+ async init(script) {
3554
+ if (this.sha1) return;
3555
+ this.sha1 = await this.digest(script);
3556
+ }
3557
+ /**
3558
+ * Send an `EVAL_RO` command to redis.
3559
+ */
3560
+ async evalRo(keys, args) {
3561
+ await this.init(this.script);
3562
+ return await this.redis.evalRo(this.script, keys, args);
3563
+ }
3564
+ /**
3565
+ * Calculates the sha1 hash of the script and then calls `EVALSHA_RO`.
3566
+ */
3567
+ async evalshaRo(keys, args) {
3568
+ await this.init(this.script);
3569
+ return await this.redis.evalshaRo(this.sha1, keys, args);
3570
+ }
3571
+ /**
3572
+ * Optimistically try to run `EVALSHA_RO` first.
3573
+ * If the script is not loaded in redis, it will fall back and try again with `EVAL_RO`.
3574
+ *
3575
+ * Following calls will be able to use the cached script
3576
+ */
3577
+ async exec(keys, args) {
3578
+ await this.init(this.script);
3579
+ const res = await this.redis.evalshaRo(this.sha1, keys, args).catch(async (error) => {
3580
+ if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3581
+ return await this.redis.evalRo(this.script, keys, args);
3582
+ }
3583
+ throw error;
3584
+ });
3585
+ return res;
3586
+ }
3587
+ /**
3588
+ * Compute the sha1 hash of the script and return its hex representation.
3589
+ */
3590
+ async digest(s) {
3591
+ const data = new TextEncoder().encode(s);
3592
+ const hashBuffer = await subtle2.digest("SHA-1", data);
3593
+ const hashArray = [...new Uint8Array(hashBuffer)];
3594
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3005
3595
  }
3006
3596
  };
3007
3597
 
@@ -3031,6 +3621,12 @@ var Redis = class {
3031
3621
  }
3032
3622
  this.enableAutoPipelining = opts?.enableAutoPipelining ?? true;
3033
3623
  }
3624
+ get readYourWritesSyncToken() {
3625
+ return this.client.upstashSyncToken;
3626
+ }
3627
+ set readYourWritesSyncToken(session) {
3628
+ this.client.upstashSyncToken = session;
3629
+ }
3034
3630
  get json() {
3035
3631
  return {
3036
3632
  /**
@@ -3073,6 +3669,10 @@ var Redis = class {
3073
3669
  * @see https://redis.io/commands/json.get
3074
3670
  */
3075
3671
  get: (...args) => new JsonGetCommand(args, this.opts).exec(this.client),
3672
+ /**
3673
+ * @see https://redis.io/commands/json.merge
3674
+ */
3675
+ merge: (...args) => new JsonMergeCommand(args, this.opts).exec(this.client),
3076
3676
  /**
3077
3677
  * @see https://redis.io/commands/json.mget
3078
3678
  */
@@ -3142,8 +3742,36 @@ var Redis = class {
3142
3742
  } catch {
3143
3743
  }
3144
3744
  };
3145
- createScript(script) {
3146
- return new Script(this, script);
3745
+ /**
3746
+ * Creates a new script.
3747
+ *
3748
+ * Scripts offer the ability to optimistically try to execute a script without having to send the
3749
+ * entire script to the server. If the script is loaded on the server, it tries again by sending
3750
+ * the entire script. Afterwards, the script is cached on the server.
3751
+ *
3752
+ * @param script - The script to create
3753
+ * @param opts - Optional options to pass to the script `{ readonly?: boolean }`
3754
+ * @returns A new script
3755
+ *
3756
+ * @example
3757
+ * ```ts
3758
+ * const redis = new Redis({...})
3759
+ *
3760
+ * const script = redis.createScript<string>("return ARGV[1];")
3761
+ * const arg1 = await script.eval([], ["Hello World"])
3762
+ * expect(arg1, "Hello World")
3763
+ * ```
3764
+ * @example
3765
+ * ```ts
3766
+ * const redis = new Redis({...})
3767
+ *
3768
+ * const script = redis.createScript<string>("return ARGV[1];", { readonly: true })
3769
+ * const arg1 = await script.evalRo([], ["Hello World"])
3770
+ * expect(arg1, "Hello World")
3771
+ * ```
3772
+ */
3773
+ createScript(script, opts) {
3774
+ return opts?.readonly ? new ScriptRO(this, script) : new Script(this, script);
3147
3775
  }
3148
3776
  /**
3149
3777
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3230,14 +3858,26 @@ var Redis = class {
3230
3858
  * @see https://redis.io/commands/echo
3231
3859
  */
3232
3860
  echo = (...args) => new EchoCommand(args, this.opts).exec(this.client);
3861
+ /**
3862
+ * @see https://redis.io/commands/eval_ro
3863
+ */
3864
+ evalRo = (...args) => new EvalROCommand(args, this.opts).exec(this.client);
3233
3865
  /**
3234
3866
  * @see https://redis.io/commands/eval
3235
3867
  */
3236
3868
  eval = (...args) => new EvalCommand(args, this.opts).exec(this.client);
3869
+ /**
3870
+ * @see https://redis.io/commands/evalsha_ro
3871
+ */
3872
+ evalshaRo = (...args) => new EvalshaROCommand(args, this.opts).exec(this.client);
3237
3873
  /**
3238
3874
  * @see https://redis.io/commands/evalsha
3239
3875
  */
3240
3876
  evalsha = (...args) => new EvalshaCommand(args, this.opts).exec(this.client);
3877
+ /**
3878
+ * Generic method to execute any Redis command.
3879
+ */
3880
+ exec = (args) => new ExecCommand(args, this.opts).exec(this.client);
3241
3881
  /**
3242
3882
  * @see https://redis.io/commands/exists
3243
3883
  */
@@ -3294,6 +3934,10 @@ var Redis = class {
3294
3934
  * @see https://redis.io/commands/getdel
3295
3935
  */
3296
3936
  getdel = (...args) => new GetDelCommand(args, this.opts).exec(this.client);
3937
+ /**
3938
+ * @see https://redis.io/commands/getex
3939
+ */
3940
+ getex = (...args) => new GetExCommand(args, this.opts).exec(this.client);
3297
3941
  /**
3298
3942
  * @see https://redis.io/commands/getrange
3299
3943
  */
@@ -3310,6 +3954,42 @@ var Redis = class {
3310
3954
  * @see https://redis.io/commands/hexists
3311
3955
  */
3312
3956
  hexists = (...args) => new HExistsCommand(args, this.opts).exec(this.client);
3957
+ /**
3958
+ * @see https://redis.io/commands/hexpire
3959
+ */
3960
+ hexpire = (...args) => new HExpireCommand(args, this.opts).exec(this.client);
3961
+ /**
3962
+ * @see https://redis.io/commands/hexpireat
3963
+ */
3964
+ hexpireat = (...args) => new HExpireAtCommand(args, this.opts).exec(this.client);
3965
+ /**
3966
+ * @see https://redis.io/commands/hexpiretime
3967
+ */
3968
+ hexpiretime = (...args) => new HExpireTimeCommand(args, this.opts).exec(this.client);
3969
+ /**
3970
+ * @see https://redis.io/commands/httl
3971
+ */
3972
+ httl = (...args) => new HTtlCommand(args, this.opts).exec(this.client);
3973
+ /**
3974
+ * @see https://redis.io/commands/hpexpire
3975
+ */
3976
+ hpexpire = (...args) => new HPExpireCommand(args, this.opts).exec(this.client);
3977
+ /**
3978
+ * @see https://redis.io/commands/hpexpireat
3979
+ */
3980
+ hpexpireat = (...args) => new HPExpireAtCommand(args, this.opts).exec(this.client);
3981
+ /**
3982
+ * @see https://redis.io/commands/hpexpiretime
3983
+ */
3984
+ hpexpiretime = (...args) => new HPExpireTimeCommand(args, this.opts).exec(this.client);
3985
+ /**
3986
+ * @see https://redis.io/commands/hpttl
3987
+ */
3988
+ hpttl = (...args) => new HPTtlCommand(args, this.opts).exec(this.client);
3989
+ /**
3990
+ * @see https://redis.io/commands/hpersist
3991
+ */
3992
+ hpersist = (...args) => new HPersistCommand(args, this.opts).exec(this.client);
3313
3993
  /**
3314
3994
  * @see https://redis.io/commands/hget
3315
3995
  */
@@ -3478,6 +4158,13 @@ var Redis = class {
3478
4158
  * @see https://redis.io/commands/psetex
3479
4159
  */
3480
4160
  psetex = (key, ttl, value) => new PSetEXCommand([key, ttl, value], this.opts).exec(this.client);
4161
+ /**
4162
+ * @see https://redis.io/commands/psubscribe
4163
+ */
4164
+ psubscribe = (patterns) => {
4165
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
4166
+ return new Subscriber(this.client, patternArray, true);
4167
+ };
3481
4168
  /**
3482
4169
  * @see https://redis.io/commands/pttl
3483
4170
  */
@@ -3514,10 +4201,9 @@ var Redis = class {
3514
4201
  * @see https://redis.io/commands/sadd
3515
4202
  */
3516
4203
  sadd = (key, member, ...members) => new SAddCommand([key, member, ...members], this.opts).exec(this.client);
3517
- /**
3518
- * @see https://redis.io/commands/scan
3519
- */
3520
- scan = (...args) => new ScanCommand(args, this.opts).exec(this.client);
4204
+ scan(cursor, opts) {
4205
+ return new ScanCommand([cursor, opts], this.opts).exec(this.client);
4206
+ }
3521
4207
  /**
3522
4208
  * @see https://redis.io/commands/scard
3523
4209
  */
@@ -3606,6 +4292,13 @@ var Redis = class {
3606
4292
  * @see https://redis.io/commands/strlen
3607
4293
  */
3608
4294
  strlen = (...args) => new StrLenCommand(args, this.opts).exec(this.client);
4295
+ /**
4296
+ * @see https://redis.io/commands/subscribe
4297
+ */
4298
+ subscribe = (channels) => {
4299
+ const channelArray = Array.isArray(channels) ? channels : [channels];
4300
+ return new Subscriber(this.client, channelArray);
4301
+ };
3609
4302
  /**
3610
4303
  * @see https://redis.io/commands/sunion
3611
4304
  */
@@ -3787,7 +4480,7 @@ var Redis = class {
3787
4480
  };
3788
4481
 
3789
4482
  // version.ts
3790
- var VERSION = "v1.35.0-canary";
4483
+ var VERSION = "v1.35.1";
3791
4484
 
3792
4485
  export {
3793
4486
  error_exports,